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>
toString(const T & obj)122 inline std::string toString(const T& obj) {
123 return std::to_string(obj);
124 }
125
126 template <typename T>
joinStr(const std::string & joint,const std::vector<T> & items)127 inline std::string joinStr(const std::string& joint, const std::vector<T>& items) {
128 std::stringstream ss;
129 for (uint32_t i = 0; i < items.size(); i++) {
130 if (i == 0) {
131 ss << toString(items[i]);
132 } else {
133 ss << joint << toString(items[i]);
134 }
135 }
136 return ss.str();
137 }
138
139 template <typename T, class Function>
joinStr(const std::string & joint,const std::vector<T> & items,Function str)140 inline std::string joinStr(const std::string& joint, const std::vector<T>& items, Function str) {
141 std::stringstream ss;
142 for (uint32_t i = 0; i < items.size(); i++) {
143 if (i != 0) ss << joint;
144 ss << str(items[i]);
145 }
146 return ss.str();
147 }
148
149 template <typename T>
joinStr(const std::string & joint,int limit,const std::vector<T> & items)150 inline std::string joinStr(const std::string& joint, int limit, const std::vector<T>& items) {
151 if (items.size() > static_cast<size_t>(limit)) {
152 std::vector<T> topMax(items.begin(), items.begin() + limit);
153 return joinStr(joint, topMax) + ", (" + toString(items.size() - limit) + " ommited), " +
154 toString(items.back());
155 } else {
156 return joinStr(joint, items);
157 }
158 }
159
160 static const char* kLifeTimeNames[6] = {
161 "TEMPORARY_VARIABLE", "SUBGRAPH_INPUT", "SUBGRAPH_OUTPUT",
162 "CONSTANT_COPY", "CONSTANT_REFERENCE", "NO_VALUE",
163 };
164
165 static const bool kScalarDataType[]{
166 true, // ANEURALNETWORKS_FLOAT32
167 true, // ANEURALNETWORKS_INT32
168 true, // ANEURALNETWORKS_UINT32
169 false, // ANEURALNETWORKS_TENSOR_FLOAT32
170 false, // ANEURALNETWORKS_TENSOR_INT32
171 false, // ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
172 true, // ANEURALNETWORKS_BOOL
173 false, // ANEURALNETWORKS_TENSOR_QUANT16_SYMM
174 false, // ANEURALNETWORKS_TENSOR_FLOAT16
175 false, // ANEURALNETWORKS_TENSOR_BOOL8
176 true, // ANEURALNETWORKS_FLOAT16
177 false, // ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL
178 false, // ANEURALNETWORKS_TENSOR_QUANT16_ASYMM
179 false, // ANEURALNETWORKS_TENSOR_QUANT8_SYMM
180 false, // ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED
181 };
182
183 static const uint32_t kSizeOfDataType[]{
184 4, // ANEURALNETWORKS_FLOAT32
185 4, // ANEURALNETWORKS_INT32
186 4, // ANEURALNETWORKS_UINT32
187 4, // ANEURALNETWORKS_TENSOR_FLOAT32
188 4, // ANEURALNETWORKS_TENSOR_INT32
189 1, // ANEURALNETWORKS_TENSOR_QUANT8_ASYMM
190 1, // ANEURALNETWORKS_BOOL
191 2, // ANEURALNETWORKS_TENSOR_QUANT16_SYMM
192 2, // ANEURALNETWORKS_TENSOR_FLOAT16
193 1, // ANEURALNETWORKS_TENSOR_BOOL8
194 2, // ANEURALNETWORKS_FLOAT16
195 1, // ANEURALNETWORKS_TENSOR_QUANT8_SYMM_PER_CHANNEL
196 2, // ANEURALNETWORKS_TENSOR_QUANT16_ASYMM
197 1, // ANEURALNETWORKS_TENSOR_QUANT8_SYMM
198 1, // ANEURALNETWORKS_TENSOR_QUANT8_ASYMM_SIGNED
199 };
200
201 template <>
202 inline std::string toString<RandomVariableType>(const RandomVariableType& type) {
203 static const std::string typeNames[] = {"FREE", "CONST", "OP"};
204 return typeNames[static_cast<int>(type)];
205 }
206
alignedString(std::string str,int width)207 inline std::string alignedString(std::string str, int width) {
208 str.push_back(':');
209 str.resize(width + 1, ' ');
210 return str;
211 }
212
213 template <>
214 inline std::string toString<RandomVariableRange>(const RandomVariableRange& range) {
215 return "[" + joinStr(", ", 20, range.getChoices()) + "]";
216 }
217
218 template <>
219 inline std::string toString<RandomOperandType>(const RandomOperandType& type) {
220 static const std::string typeNames[] = {"Input", "Output", "Internal", "Parameter", "No Value"};
221 return typeNames[static_cast<int>(type)];
222 }
223
224 template <>
225 inline std::string toString<RandomVariableNode>(const RandomVariableNode& var) {
226 std::stringstream ss;
227 ss << "var" << var->index << " = ";
228 switch (var->type) {
229 case RandomVariableType::FREE:
230 ss << "FREE " << toString(var->range);
231 break;
232 case RandomVariableType::CONST:
233 ss << "CONST " << toString(var->value);
234 break;
235 case RandomVariableType::OP:
236 ss << "var" << var->parent1->index << " " << var->op->getName();
237 if (var->parent2 != nullptr) ss << " var" << var->parent2->index;
238 ss << ", " << toString(var->range);
239 break;
240 default:
241 NN_FUZZER_CHECK(false);
242 }
243 ss << ", timestamp = " << var->timestamp;
244 return ss.str();
245 }
246
247 template <>
248 inline std::string toString<RandomVariable>(const RandomVariable& var) {
249 return "var" + std::to_string(var.get()->index);
250 }
251
252 template <>
253 inline std::string toString<RandomOperand>(const RandomOperand& op) {
254 return toString(op.type) + ", dimension = [" +
255 joinStr(", ", op.dimensions,
256 [](const RandomVariable& var) { return std::to_string(var.getValue()); }) +
257 "], scale = " + toString(op.scale) + " , zero_point = " + toString(op.zeroPoint);
258 }
259
260 // This class is a workaround for two issues our code relies on:
261 // 1. sizeof(bool) is implementation defined.
262 // 2. vector<bool> does not allow direct pointer access via the data() method.
263 class bool8 {
264 public:
bool8()265 bool8() : mValue() {}
bool8(bool value)266 /* implicit */ bool8(bool value) : mValue(value) {}
267 inline operator bool() const { return mValue != 0; }
268
269 private:
270 uint8_t mValue;
271 };
272 static_assert(sizeof(bool8) == 1, "size of bool8 must be 8 bits");
273
274 struct RandomNumberGenerator {
275 static std::mt19937 generator;
276 };
277
getBernoulli(double p)278 inline bool getBernoulli(double p) {
279 std::bernoulli_distribution dis(p);
280 return dis(RandomNumberGenerator::generator);
281 }
282
283 template <typename T>
284 inline constexpr bool nnIsFloat = std::is_floating_point_v<T> || std::is_same_v<T, _Float16>;
285
286 // getUniform for floating point values operates on a open interval (lower, upper).
287 // This is important for generating a scale that is greater than but not equal to a lower bound.
288 template <typename T>
getUniform(T lower,T upper)289 inline std::enable_if_t<nnIsFloat<T>, T> getUniform(T lower, T upper) {
290 float nextLower = std::nextafter(static_cast<float>(lower), std::numeric_limits<float>::max());
291 std::uniform_real_distribution<float> dis(nextLower, upper);
292 return dis(RandomNumberGenerator::generator);
293 }
294 template <typename T>
getUniformNonZero(T lower,T upper,T zeroPoint)295 inline std::enable_if_t<nnIsFloat<T>, T> getUniformNonZero(T lower, T upper, T zeroPoint) {
296 if (upper >= zeroPoint) {
297 upper = std::nextafter(static_cast<float>(upper), std::numeric_limits<float>::min());
298 }
299 std::uniform_real_distribution<float> dis(lower, upper);
300 const float value = dis(RandomNumberGenerator::generator);
301 return value >= zeroPoint ? std::nextafter(value, std::numeric_limits<float>::max()) : value;
302 }
303
304 // getUniform for integers operates on a closed interval [lower, upper].
305 // This is important that 255 should be included as a valid candidate for QUANT8_ASYMM values.
306 template <typename T>
getUniform(T lower,T upper)307 inline std::enable_if_t<std::is_integral_v<T>, T> getUniform(T lower, T upper) {
308 std::uniform_int_distribution<T> dis(lower, upper);
309 return dis(RandomNumberGenerator::generator);
310 }
311 template <typename T>
getUniformNonZero(T lower,T upper,T zeroPoint)312 inline std::enable_if_t<std::is_integral_v<T>, T> getUniformNonZero(T lower, T upper, T zeroPoint) {
313 if (upper >= zeroPoint) upper--;
314 std::uniform_int_distribution<T> dis(lower, upper);
315 const T value = dis(RandomNumberGenerator::generator);
316 return value >= zeroPoint ? value + 1 : value;
317 }
318
319 template <typename T>
getRandomChoice(const std::vector<T> & choices)320 inline const T& getRandomChoice(const std::vector<T>& choices) {
321 NN_FUZZER_CHECK(!choices.empty()) << "Empty choices!";
322 std::uniform_int_distribution<size_t> dis(0, choices.size() - 1);
323 size_t i = dis(RandomNumberGenerator::generator);
324 return choices[i];
325 }
326
327 template <typename T>
randomShuffle(std::vector<T> * vec)328 inline void randomShuffle(std::vector<T>* vec) {
329 std::shuffle(vec->begin(), vec->end(), RandomNumberGenerator::generator);
330 }
331
332 } // namespace fuzzing_test
333 } // namespace nn
334 } // namespace android
335
336 #endif // ANDROID_FRAMEWORKS_ML_NN_RUNTIME_TEST_FUZZING_RANDOM_GRAPH_GENERATOR_UTILS_H
337