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 #include <android-base/logging.h>
18
19 #include <cstdlib>
20 #include <optional>
21 #include <utility>
22
23 #include "Converter.h"
24 #include "Model.pb.h"
25 #include "NeuralNetworksWrapper.h"
26 #include "TestHarness.h"
27 #include "src/libfuzzer/libfuzzer_macro.h"
28
29 namespace {
30
31 using ::android::nn::fuzz::convertToTestModel;
32 using ::android_nn_fuzz::Test;
33 using ::test_helper::TestModel;
34 using namespace ::android::nn::wrapper;
35 using namespace test_helper;
36
getOperandType(const TestOperand & op)37 OperandType getOperandType(const TestOperand& op) {
38 auto dims = op.dimensions;
39 if (op.type == TestOperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) {
40 return OperandType(
41 static_cast<Type>(op.type), dims,
42 SymmPerChannelQuantParams(op.channelQuant.scales, op.channelQuant.channelDim));
43 } else {
44 return OperandType(static_cast<Type>(op.type), dims, op.scale, op.zeroPoint);
45 }
46 }
47
CreateModel(const TestModel & testModel)48 std::optional<Model> CreateModel(const TestModel& testModel) {
49 Model model;
50
51 // Operands.
52 // TODO(b/148605565): Add control flow support
53 CHECK_EQ(testModel.referenced.size(), 0u) << "Subgraphs not supported";
54 for (const auto& operand : testModel.main.operands) {
55 auto type = getOperandType(operand);
56 auto index = model.addOperand(&type);
57
58 switch (operand.lifetime) {
59 case TestOperandLifeTime::CONSTANT_COPY:
60 case TestOperandLifeTime::CONSTANT_REFERENCE:
61 model.setOperandValue(index, operand.data.get<void>(), operand.data.size());
62 break;
63 case TestOperandLifeTime::NO_VALUE:
64 model.setOperandValue(index, nullptr, 0);
65 break;
66 case TestOperandLifeTime::SUBGRAPH: {
67 CHECK(false);
68 } break;
69 case TestOperandLifeTime::SUBGRAPH_INPUT:
70 case TestOperandLifeTime::SUBGRAPH_OUTPUT:
71 case TestOperandLifeTime::TEMPORARY_VARIABLE:
72 // Nothing to do here.
73 break;
74 }
75 if (!model.isValid()) return std::nullopt;
76 }
77
78 // Operations.
79 CHECK_EQ(testModel.referenced.size(), 0u) << "Subgraphs not supported";
80 for (const auto& operation : testModel.main.operations) {
81 model.addOperation(static_cast<int>(operation.type), operation.inputs, operation.outputs);
82 if (!model.isValid()) return std::nullopt;
83 }
84
85 // Inputs and outputs.
86 model.identifyInputsAndOutputs(testModel.main.inputIndexes, testModel.main.outputIndexes);
87 if (!model.isValid()) return std::nullopt;
88
89 // Relaxed computation.
90 model.relaxComputationFloat32toFloat16(testModel.isRelaxed);
91 if (!model.isValid()) return std::nullopt;
92
93 if (model.finish() != Result::NO_ERROR) {
94 return std::nullopt;
95 }
96
97 return model;
98 }
99
CreateCompilation(const Model & model)100 std::optional<Compilation> CreateCompilation(const Model& model) {
101 Compilation compilation(&model);
102 if (compilation.finish() != Result::NO_ERROR) {
103 return std::nullopt;
104 }
105 return compilation;
106 }
107
CreateExecution(const Compilation & compilation,const TestModel & testModel)108 std::optional<Execution> CreateExecution(const Compilation& compilation,
109 const TestModel& testModel) {
110 Execution execution(&compilation);
111
112 // Model inputs.
113 for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
114 const auto& operand = testModel.main.operands[testModel.main.inputIndexes[i]];
115 if (execution.setInput(i, operand.data.get<void>(), operand.data.size()) !=
116 Result::NO_ERROR) {
117 return std::nullopt;
118 }
119 }
120
121 // Model outputs.
122 for (uint32_t i = 0; i < testModel.main.outputIndexes.size(); i++) {
123 const auto& operand = testModel.main.operands[testModel.main.outputIndexes[i]];
124 if (execution.setOutput(i, const_cast<void*>(operand.data.get<void>()),
125 operand.data.size()) != Result::NO_ERROR) {
126 return std::nullopt;
127 }
128 }
129
130 return execution;
131 }
132
runTest(const TestModel & testModel)133 void runTest(const TestModel& testModel) {
134 // set up model
135 auto model = CreateModel(testModel);
136 if (!model.has_value()) {
137 return;
138 }
139
140 // set up compilation
141 auto compilation = CreateCompilation(*model);
142 if (!compilation.has_value()) {
143 return;
144 }
145
146 // set up execution
147 auto execution = CreateExecution(*compilation, testModel);
148 if (!execution.has_value()) {
149 return;
150 }
151
152 // perform execution
153 execution->compute();
154 }
155
156 } // anonymous namespace
157
DEFINE_PROTO_FUZZER(const Test & model)158 DEFINE_PROTO_FUZZER(const Test& model) {
159 const TestModel testModel = convertToTestModel(model);
160 runTest(testModel);
161 }
162