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