• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_FUZZING_RANDOM_GRAPH_GENERATOR_UTILS_H
18 #define ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_FUZZING_RANDOM_GRAPH_GENERATOR_UTILS_H
19 
20 #include <android/log.h>
21 
22 #include <chrono>
23 #include <fstream>
24 #include <limits>
25 #include <memory>
26 #include <random>
27 #include <sstream>
28 #include <string>
29 #include <vector>
30 
31 #include "RandomGraphGenerator.h"
32 #include "RandomVariable.h"
33 #include "TestHarness.h"
34 #include "TestNeuralNetworksWrapper.h"
35 
36 namespace android {
37 namespace nn {
38 namespace fuzzing_test {
39 
40 #define NN_FUZZER_LOG_INIT(filename) Logger::get()->init((filename))
41 #define NN_FUZZER_LOG_WRITE_FATAL_TO_SYSLOG(logTag) \
42     LoggerStream::writeAbortMessageToSystemLog(logTag)
43 #define NN_FUZZER_LOG_CLOSE Logger::get()->close()
44 #define NN_FUZZER_LOG              \
45     if (!Logger::get()->enabled()) \
46         ;                          \
47     else                           \
48         LoggerStream(false) << alignedString(__FUNCTION__, 20)
49 #define NN_FUZZER_CHECK(condition)                                                             \
50     if ((condition))                                                                           \
51         ;                                                                                      \
52     else                                                                                       \
53         LoggerStream(true) << alignedString(__FUNCTION__, 20) << "Check failed " << #condition \
54                            << ": "
55 
56 // A Singleton manages the global configurations of logging.
57 class Logger {
58    public:
get()59     static Logger* get() {
60         static Logger instance;
61         return &instance;
62     }
init(const std::string & filename)63     void init(const std::string& filename) {
64         os.open(filename);
65         mStart = std::chrono::high_resolution_clock::now();
66     }
enabled()67     bool enabled() { return os.is_open(); }
close()68     void close() {
69         if (os.is_open()) os.close();
70     }
log(const std::string & str)71     void log(const std::string& str) {
72         if (os.is_open()) os << getElapsedTime() << str << std::flush;
73     }
74 
75    private:
76     Logger() = default;
77     Logger(const Logger&) = delete;
78     Logger& operator=(const Logger&) = delete;
79     std::string getElapsedTime();
80     std::ofstream os;
81     std::chrono::time_point<std::chrono::high_resolution_clock> mStart;
82 };
83 
84 // Controls logging of a single line.
85 class LoggerStream {
86    public:
LoggerStream(bool abortAfterLog)87     LoggerStream(bool abortAfterLog) : mAbortAfterLog(abortAfterLog) {}
~LoggerStream()88     ~LoggerStream() {
89         Logger::get()->log(ss.str() + '\n');
90         if (mAbortAfterLog) {
91             if (LoggerStream::mWriteAbortMessageToSystemLog) {
92                 __android_log_print(ANDROID_LOG_FATAL, mLogTag.c_str(), "%s", ss.str().c_str());
93             } else {
94                 std::cout << ss.str() << std::endl;
95             }
96             abort();
97         }
98     }
99 
100     template <typename T>
101     LoggerStream& operator<<(const T& str) {
102         ss << str;
103         return *this;
104     }
105 
writeAbortMessageToSystemLog(const std::string & logTag)106     static void writeAbortMessageToSystemLog(const std::string& logTag) {
107         LoggerStream::mWriteAbortMessageToSystemLog = true;
108         LoggerStream::mLogTag = logTag;
109     }
110 
111    private:
112     LoggerStream(const LoggerStream&) = delete;
113     LoggerStream& operator=(const LoggerStream&) = delete;
114     std::stringstream ss;
115     bool mAbortAfterLog;
116 
117     static bool mWriteAbortMessageToSystemLog;
118     static std::string mLogTag;
119 };
120 
121 template <typename T>
joinStr(const std::string & joint,const std::vector<T> & items)122 inline std::string joinStr(const std::string& joint, const std::vector<T>& items) {
123     std::stringstream ss;
124     for (uint32_t i = 0; i < items.size(); i++) {
125         if (i == 0) {
126             ss << items[i];
127         } else {
128             ss << joint << items[i];
129         }
130     }
131     return ss.str();
132 }
133 
134 template <typename T, class Function>
joinStr(const std::string & joint,const std::vector<T> & items,Function str)135 inline std::string joinStr(const std::string& joint, const std::vector<T>& items, Function str) {
136     std::stringstream ss;
137     for (uint32_t i = 0; i < items.size(); i++) {
138         if (i != 0) ss << joint;
139         ss << str(items[i]);
140     }
141     return ss.str();
142 }
143 
144 template <typename T>
joinStr(const std::string & joint,int limit,const std::vector<T> & items)145 inline std::string joinStr(const std::string& joint, int limit, const std::vector<T>& items) {
146     if (items.size() > static_cast<size_t>(limit)) {
147         std::vector<T> topMax(items.begin(), items.begin() + limit);
148         std::stringstream ss;
149         ss << joinStr(joint, topMax) << ", (" << (items.size() - limit) << " omitted), "
150            << items.back();
151         return ss.str();
152     } else {
153         return joinStr(joint, items);
154     }
155 }
156 
157 static const bool kScalarDataType[]{
158         true,   // ANEURALNETWORKS_FLOAT32
159         true,   // ANEURALNETWORKS_INT32
160         true,   // ANEURALNETWORKS_UINT32
161         false,  // ANEURALNETWORKS_TENSOR_FLOAT32
162         false,  // ANEURALNETWORKS_TENSOR_INT32
163         false,  // ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
164         true,   // ANEURALNETWORKS_BOOL
165         false,  // ANEURALNETWORKS_TENSOR_QUANT16_SYMM
166         false,  // ANEURALNETWORKS_TENSOR_FLOAT16
167         false,  // ANEURALNETWORKS_TENSOR_BOOL8
168         true,   // ANEURALNETWORKS_FLOAT16
169         false,  // ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL
170         false,  // ANEURALNETWORKS_TENSOR_QUANT16_ASYMM
171         false,  // ANEURALNETWORKS_TENSOR_QUANT8_SYMM
172         false,  // ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED
173 };
174 
175 static const uint32_t kSizeOfDataType[]{
176         4,  // ANEURALNETWORKS_FLOAT32
177         4,  // ANEURALNETWORKS_INT32
178         4,  // ANEURALNETWORKS_UINT32
179         4,  // ANEURALNETWORKS_TENSOR_FLOAT32
180         4,  // ANEURALNETWORKS_TENSOR_INT32
181         1,  // ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
182         1,  // ANEURALNETWORKS_BOOL
183         2,  // ANEURALNETWORKS_TENSOR_QUANT16_SYMM
184         2,  // ANEURALNETWORKS_TENSOR_FLOAT16
185         1,  // ANEURALNETWORKS_TENSOR_BOOL8
186         2,  // ANEURALNETWORKS_FLOAT16
187         1,  // ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL
188         2,  // ANEURALNETWORKS_TENSOR_QUANT16_ASYMM
189         1,  // ANEURALNETWORKS_TENSOR_QUANT8_SYMM
190         1,  // ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED
191 };
192 
193 inline std::ostream& operator<<(std::ostream& os, const RandomVariableType& type) {
194     static const std::string typeNames[] = {"FREE", "CONST", "OP"};
195     return os << typeNames[static_cast<int>(type)];
196 }
197 
alignedString(std::string str,int width)198 inline std::string alignedString(std::string str, int width) {
199     str.push_back(':');
200     str.resize(width + 1, ' ');
201     return str;
202 }
203 
204 inline std::ostream& operator<<(std::ostream& os, const RandomVariableRange& range) {
205     return os << "[" + joinStr(", ", 20, range.getChoices()) + "]";
206 }
207 
208 inline std::ostream& operator<<(std::ostream& os, const RandomOperandType& type) {
209     static const std::string typeNames[] = {"Input", "Output", "Internal", "Parameter", "No Value"};
210     return os << typeNames[static_cast<int>(type)];
211 }
212 
213 inline std::ostream& operator<<(std::ostream& os, const RandomVariableNode& var) {
214     os << "var" << var->index << " = ";
215     switch (var->type) {
216         case RandomVariableType::FREE:
217             os << "FREE " << var->range;
218             break;
219         case RandomVariableType::CONST:
220             os << "CONST " << var->value;
221             break;
222         case RandomVariableType::OP:
223             os << "var" << var->parent1->index << " " << var->op->getName();
224             if (var->parent2 != nullptr) os << " var" << var->parent2->index;
225             os << ", " << var->range;
226             break;
227         default:
228             NN_FUZZER_CHECK(false);
229     }
230     os << ", timestamp = " << var->timestamp;
231     return os;
232 }
233 
234 inline std::ostream& operator<<(std::ostream& os, const RandomVariable& var) {
235     return os << "var" + std::to_string(var.get()->index);
236 }
237 
238 inline std::ostream& operator<<(std::ostream& os, const RandomOperand& op) {
239     return os << op.type << ", dimension = ["
240               << joinStr(", ", op.dimensions,
241                          [](const RandomVariable& var) { return std::to_string(var.getValue()); })
242               << "], scale = " << op.scale << " , zero_point = " << op.zeroPoint;
243 }
244 
245 // This class is a workaround for two issues our code relies on:
246 // 1. sizeof(bool) is implementation defined.
247 // 2. vector<bool> does not allow direct pointer access via the data() method.
248 class bool8 {
249    public:
bool8()250     bool8() : mValue() {}
bool8(bool value)251     /* implicit */ bool8(bool value) : mValue(value) {}
252     inline operator bool() const { return mValue != 0; }
253 
254    private:
255     uint8_t mValue;
256 };
257 static_assert(sizeof(bool8) == 1, "size of bool8 must be 8 bits");
258 
259 struct RandomNumberGenerator {
260     static std::mt19937 generator;
261 };
262 
getBernoulli(double p)263 inline bool getBernoulli(double p) {
264     std::bernoulli_distribution dis(p);
265     return dis(RandomNumberGenerator::generator);
266 }
267 
268 template <typename T>
269 inline constexpr bool nnIsFloat = std::is_floating_point_v<T> || std::is_same_v<T, _Float16>;
270 
271 // getUniform for floating point values operates on a open interval (lower, upper).
272 // This is important for generating a scale that is greater than but not equal to a lower bound.
273 template <typename T>
getUniform(T lower,T upper)274 inline std::enable_if_t<nnIsFloat<T>, T> getUniform(T lower, T upper) {
275     float nextLower = std::nextafter(static_cast<float>(lower), std::numeric_limits<float>::max());
276     std::uniform_real_distribution<float> dis(nextLower, upper);
277     return dis(RandomNumberGenerator::generator);
278 }
279 template <typename T>
getUniformNonZero(T lower,T upper,T zeroPoint)280 inline std::enable_if_t<nnIsFloat<T>, T> getUniformNonZero(T lower, T upper, T zeroPoint) {
281     if (upper >= zeroPoint) {
282         upper = std::nextafter(static_cast<float>(upper), std::numeric_limits<float>::min());
283     }
284     std::uniform_real_distribution<float> dis(lower, upper);
285     const float value = dis(RandomNumberGenerator::generator);
286     return value >= zeroPoint ? std::nextafter(value, std::numeric_limits<float>::max()) : value;
287 }
288 
289 // getUniform for integers operates on a closed interval [lower, upper].
290 // This is important that 255 should be included as a valid candidate for QUANT8_ASYMM values.
291 template <typename T>
getUniform(T lower,T upper)292 inline std::enable_if_t<std::is_integral_v<T>, T> getUniform(T lower, T upper) {
293     std::uniform_int_distribution<T> dis(lower, upper);
294     return dis(RandomNumberGenerator::generator);
295 }
296 template <typename T>
getUniformNonZero(T lower,T upper,T zeroPoint)297 inline std::enable_if_t<std::is_integral_v<T>, T> getUniformNonZero(T lower, T upper, T zeroPoint) {
298     if (upper >= zeroPoint) upper--;
299     std::uniform_int_distribution<T> dis(lower, upper);
300     const T value = dis(RandomNumberGenerator::generator);
301     return value >= zeroPoint ? value + 1 : value;
302 }
303 
304 template <typename T>
getRandomChoice(const std::vector<T> & choices)305 inline const T& getRandomChoice(const std::vector<T>& choices) {
306     NN_FUZZER_CHECK(!choices.empty()) << "Empty choices!";
307     std::uniform_int_distribution<size_t> dis(0, choices.size() - 1);
308     size_t i = dis(RandomNumberGenerator::generator);
309     return choices[i];
310 }
311 
312 template <typename T>
randomShuffle(std::vector<T> * vec)313 inline void randomShuffle(std::vector<T>* vec) {
314     std::shuffle(vec->begin(), vec->end(), RandomNumberGenerator::generator);
315 }
316 
317 }  // namespace fuzzing_test
318 }  // namespace nn
319 }  // namespace android
320 
321 #endif  // ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_FUZZING_RANDOM_GRAPH_GENERATOR_UTILS_H
322