1 /* Copyright 2021 The TensorFlow Authors. All Rights Reserved.
2
3 Licensed under the Apache License, Version 2.0 (the "License");
4 you may not use this file except in compliance with the License.
5 You may obtain a copy of the License at
6
7 http://www.apache.org/licenses/LICENSE-2.0
8
9 Unless required by applicable law or agreed to in writing, software
10 distributed under the License is distributed on an "AS IS" BASIS,
11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 See the License for the specific language governing permissions and
13 limitations under the License.
14 ==============================================================================*/
15 #include "tensorflow/lite/experimental/acceleration/mini_benchmark/runner.h"
16
17 #include <dlfcn.h>
18 #include <signal.h>
19
20 #include <cstddef>
21 #include <fstream>
22 #include <memory>
23 #include <string>
24
25 #include <gmock/gmock.h>
26 #include <gtest/gtest.h>
27 #include "flatbuffers/flatbuffers.h" // from @flatbuffers
28 #include "tensorflow/lite/schema/mutable/schema_generated.h"
29 #ifdef __ANDROID__
30 #include "tensorflow/lite/experimental/acceleration/mini_benchmark/embedded_runner_executable.h"
31 #include "tensorflow/lite/experimental/acceleration/mini_benchmark/embedded_runner_unit_test_entry_points.h"
32 #include "tensorflow/lite/experimental/acceleration/mini_benchmark/mini_benchmark_test_helper.h"
33 #endif // __ANDROID__
34 #include "tensorflow/lite/experimental/acceleration/mini_benchmark/status_codes.h"
35
36 extern "C" {
FunctionInBinary(int argc,char ** argv)37 int FunctionInBinary(int argc, char** argv) { return 2; }
38 } // extern "C"
39
40 namespace tflite {
41 namespace acceleration {
42 namespace {
43
44 typedef int (*EntryPoint)(int, char**);
45 using flatbuffers::FlatBufferBuilder;
46
47 struct RunnerTest : ::testing::Test {
48 protected:
LoadEntryPointModuletflite::acceleration::__anon62bcdf510111::RunnerTest49 void* LoadEntryPointModule() {
50 void* module =
51 dlopen(entry_point_file.c_str(), RTLD_NOW | RTLD_LOCAL | RTLD_NODELETE);
52 EXPECT_TRUE(module) << dlerror();
53 return module;
54 }
55
Loadtflite::acceleration::__anon62bcdf510111::RunnerTest56 EntryPoint Load(const char* name) {
57 #ifdef __ANDROID__
58 void* module = LoadEntryPointModule();
59 if (!module) {
60 return nullptr;
61 }
62 #else // !__ANDROID__
63 auto module = RTLD_DEFAULT;
64 #endif // __ANDROID__
65 void* symbol = dlsym(module, name);
66 return reinterpret_cast<EntryPoint>(symbol);
67 }
68
SetUptflite::acceleration::__anon62bcdf510111::RunnerTest69 void SetUp() override {
70 #ifdef __ANDROID__
71 // We extract the test files here as that's the only way to get the right
72 // architecture when building tests for multiple architectures.
73 entry_point_file = MiniBenchmarkTestHelper::DumpToTempFile(
74 "librunner_unit_test_entry_points.so",
75 g_tflite_acceleration_embedded_runner_unit_test_entry_points,
76 g_tflite_acceleration_embedded_runner_unit_test_entry_points_len);
77 ASSERT_TRUE(!entry_point_file.empty());
78 #endif // __ANDROID__
79 }
80
Inittflite::acceleration::__anon62bcdf510111::RunnerTest81 void Init(const char* symbol_name) {
82 EntryPoint function = Load(symbol_name);
83 ASSERT_TRUE(function);
84 runner = std::make_unique<ProcessRunner>(::testing::TempDir(), symbol_name,
85 function);
86 ASSERT_EQ(runner->Init(), kMinibenchmarkSuccess);
87 }
88
CreateTestModeltflite::acceleration::__anon62bcdf510111::RunnerTest89 FlatBufferBuilder CreateTestModel() {
90 ModelT model;
91 model.description = "test";
92 flatbuffers::FlatBufferBuilder fbb;
93 fbb.Finish(CreateModel(fbb, &model));
94 return fbb;
95 }
96 int exitcode = 0;
97 int signal = 0;
98 std::string output;
99 std::unique_ptr<ProcessRunner> runner;
100 std::string entry_point_file;
101 };
102
103 // These tests are only for Android. They are also disabled on 64-bit arm
104 // because the 64-bit arm emulator doesn't have a shell that works with popen().
105 // These tests are run on x86 emulators.
106 #if !defined(__aarch64__)
TEST_F(RunnerTest,LoadSymbol)107 TEST_F(RunnerTest, LoadSymbol) {
108 EntryPoint JustReturnZero = Load("JustReturnZero");
109 ASSERT_TRUE(JustReturnZero);
110 #ifdef __ANDROID__
111 Dl_info dl_info;
112 int status = dladdr(reinterpret_cast<void*>(JustReturnZero), &dl_info);
113 ASSERT_TRUE(status) << dlerror();
114 ASSERT_TRUE(dl_info.dli_fname) << dlerror();
115
116 void* this_module =
117 dlopen(dl_info.dli_fname, RTLD_NOW | RTLD_LOCAL | RTLD_NODELETE);
118 ASSERT_TRUE(this_module);
119 void* symbol = dlsym(this_module, "JustReturnZero");
120 EXPECT_TRUE(symbol);
121 #endif // __ANDROID__
122 }
123
TEST_F(RunnerTest,JustReturnZero)124 TEST_F(RunnerTest, JustReturnZero) {
125 ASSERT_NO_FATAL_FAILURE(Init("JustReturnZero"));
126 EXPECT_EQ(runner->Run(nullptr, {}, &output, &exitcode, &signal),
127 kMinibenchmarkCommandFailed);
128 EXPECT_EQ(exitcode, 0);
129 EXPECT_EQ(signal, 0);
130 EXPECT_EQ(output, "");
131 }
132
TEST_F(RunnerTest,ReturnOne)133 TEST_F(RunnerTest, ReturnOne) {
134 ASSERT_NO_FATAL_FAILURE(Init("ReturnOne"));
135 EXPECT_EQ(runner->Run(nullptr, {}, &output, &exitcode, &signal),
136 kMinibenchmarkCommandFailed);
137 EXPECT_EQ(exitcode, 1);
138 EXPECT_EQ(signal, 0);
139 EXPECT_EQ(output, "");
140 }
141
TEST_F(RunnerTest,ReturnSuccess)142 TEST_F(RunnerTest, ReturnSuccess) {
143 ASSERT_NO_FATAL_FAILURE(Init("ReturnSuccess"));
144 EXPECT_EQ(runner->Run(nullptr, {}, &output, &exitcode, &signal),
145 kMinibenchmarkSuccess);
146 EXPECT_EQ(exitcode, kMinibenchmarkSuccess);
147 EXPECT_EQ(signal, 0);
148 EXPECT_EQ(output, "");
149 }
150
TEST_F(RunnerTest,NullFunctionPointer)151 TEST_F(RunnerTest, NullFunctionPointer) {
152 ProcessRunner runner("foo", "bar", nullptr);
153 EXPECT_EQ(runner.Init(), kMinibenchmarkPreconditionNotMet);
154 EXPECT_EQ(runner.Run(nullptr, {}, &output, &exitcode, &signal),
155 kMinibenchmarkPreconditionNotMet);
156 }
157
158 #ifdef __ANDROID__
TEST_F(RunnerTest,SigKill)159 TEST_F(RunnerTest, SigKill) {
160 ASSERT_NO_FATAL_FAILURE(Init("SigKill"));
161 EXPECT_EQ(runner->Run(nullptr, {}, &output, &exitcode, &signal),
162 kMinibenchmarkCommandFailed);
163 EXPECT_EQ(exitcode, 0);
164 EXPECT_EQ(signal, SIGKILL);
165 EXPECT_EQ(output, "");
166 }
167
TEST_F(RunnerTest,WriteOk)168 TEST_F(RunnerTest, WriteOk) {
169 ASSERT_NO_FATAL_FAILURE(Init("WriteOk"));
170 EXPECT_EQ(runner->Run(nullptr, {}, &output, &exitcode, &signal),
171 kMinibenchmarkSuccess);
172 EXPECT_EQ(exitcode, kMinibenchmarkSuccess);
173 EXPECT_EQ(signal, 0);
174 EXPECT_EQ(output, "ok\n");
175 }
176
TEST_F(RunnerTest,Write10kChars)177 TEST_F(RunnerTest, Write10kChars) {
178 ASSERT_NO_FATAL_FAILURE(Init("Write10kChars"));
179 EXPECT_EQ(runner->Run(nullptr, {}, &output, &exitcode, &signal),
180 kMinibenchmarkSuccess);
181 EXPECT_EQ(exitcode, kMinibenchmarkSuccess);
182 EXPECT_EQ(signal, 0);
183 EXPECT_EQ(output.size(), 10000);
184 }
185
TEST_F(RunnerTest,ArgsArePassed)186 TEST_F(RunnerTest, ArgsArePassed) {
187 ASSERT_NO_FATAL_FAILURE(Init("WriteArgs"));
188 EXPECT_EQ(
189 runner->Run(nullptr, {"foo", "bar", "baz"}, &output, &exitcode, &signal),
190 kMinibenchmarkSuccess);
191 EXPECT_EQ(exitcode, kMinibenchmarkSuccess);
192 EXPECT_EQ(signal, 0);
193 EXPECT_EQ(output, "foo\nbar\nbaz\n");
194 }
195
TEST_F(RunnerTest,SymbolLookupFailed)196 TEST_F(RunnerTest, SymbolLookupFailed) {
197 ProcessRunner runner(::testing::TempDir(), "FunctionInBinary",
198 FunctionInBinary);
199 EXPECT_EQ(runner.Init(), kMinibenchmarkSuccess);
200 EXPECT_EQ(runner.Run(nullptr, {}, &output, &exitcode, &signal),
201 kMinibenchmarkCommandFailed)
202 << output;
203 EXPECT_EQ(exitcode, kMinibenchmarkRunnerMainSymbolLookupFailed) << output;
204 }
205
TEST_F(RunnerTest,ReadModelFromPipe)206 TEST_F(RunnerTest, ReadModelFromPipe) {
207 ASSERT_NO_FATAL_FAILURE(Init("ReadFromPipe"));
208 FlatBufferBuilder model = CreateTestModel();
209 EXPECT_EQ(runner->Run(&model, {}, &output, &exitcode, &signal),
210 kMinibenchmarkSuccess);
211 EXPECT_EQ(exitcode, kMinibenchmarkSuccess);
212 EXPECT_EQ(signal, 0);
213 EXPECT_EQ(output,
214 std::string((char*)model.GetBufferPointer(), model.GetSize()));
215 }
216
217 #else // __ANDROID__
218
TEST_F(RunnerTest,ReadModelFromPipeNonAndroid)219 TEST_F(RunnerTest, ReadModelFromPipeNonAndroid) {
220 ASSERT_NO_FATAL_FAILURE(Init("ReadFromPipeInProcess"));
221 FlatBufferBuilder model = CreateTestModel();
222 EXPECT_EQ(runner->Run(&model, {}, &output, &exitcode, &signal),
223 kMinibenchmarkSuccess);
224 }
225
226 #endif // __ANDROID__
227 #endif // !__aarch64__
228
229 } // namespace
230 } // namespace acceleration
231 } // namespace tflite
232