• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 ART_OATDUMP_OATDUMP_TEST_H_
18 #define ART_OATDUMP_OATDUMP_TEST_H_
19 
20 #include <sstream>
21 #include <string>
22 #include <vector>
23 
24 #include "android-base/strings.h"
25 
26 #include "arch/instruction_set.h"
27 #include "base/file_utils.h"
28 #include "base/os.h"
29 #include "base/unix_file/fd_file.h"
30 #include "base/utils.h"
31 #include "common_runtime_test.h"
32 #include "exec_utils.h"
33 #include "gc/heap.h"
34 #include "gc/space/image_space.h"
35 
36 #include <sys/types.h>
37 #include <unistd.h>
38 
39 namespace art {
40 
41 class OatDumpTest : public CommonRuntimeTest {
42  protected:
SetUp()43   virtual void SetUp() {
44     CommonRuntimeTest::SetUp();
45     core_art_location_ = GetCoreArtLocation();
46     core_oat_location_ = GetSystemImageFilename(GetCoreOatLocation().c_str(), kRuntimeISA);
47     tmp_dir_ = GetScratchDir();
48   }
49 
TearDown()50   virtual void TearDown() {
51     ClearDirectory(tmp_dir_.c_str(), /*recursive*/ false);
52     ASSERT_EQ(rmdir(tmp_dir_.c_str()), 0);
53     CommonRuntimeTest::TearDown();
54   }
55 
GetScratchDir()56   std::string GetScratchDir() {
57     // ANDROID_DATA needs to be set
58     CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA"));
59     std::string dir = getenv("ANDROID_DATA");
60     dir += "/oatdump-tmp-dir-XXXXXX";
61     if (mkdtemp(&dir[0]) == nullptr) {
62       PLOG(FATAL) << "mkdtemp(\"" << &dir[0] << "\") failed";
63     }
64     return dir;
65   }
66 
67   // Linking flavor.
68   enum Flavor {
69     kDynamic,  // oatdump(d), dex2oat(d)
70     kStatic,   // oatdump(d)s, dex2oat(d)s
71   };
72 
73   // Returns path to the oatdump/dex2oat/dexdump binary.
GetExecutableFilePath(const char * name,bool is_debug,bool is_static)74   std::string GetExecutableFilePath(const char* name, bool is_debug, bool is_static) {
75     std::string root = GetTestAndroidRoot();
76     root += "/bin/";
77     root += name;
78     if (is_debug) {
79       root += "d";
80     }
81     if (is_static) {
82       root += "s";
83     }
84     return root;
85   }
86 
GetExecutableFilePath(Flavor flavor,const char * name)87   std::string GetExecutableFilePath(Flavor flavor, const char* name) {
88     return GetExecutableFilePath(name, kIsDebugBuild, flavor == kStatic);
89   }
90 
91   enum Mode {
92     kModeOat,
93     kModeOatWithBootImage,
94     kModeArt,
95     kModeSymbolize,
96   };
97 
98   // Display style.
99   enum Display {
100     kListOnly,
101     kListAndCode
102   };
103 
GetAppBaseName()104   std::string GetAppBaseName() {
105     // Use ProfileTestMultiDex as it contains references to boot image strings
106     // that shall use different code for PIC and non-PIC.
107     return "ProfileTestMultiDex";
108   }
109 
GetAppOdexName()110   std::string GetAppOdexName() {
111     return tmp_dir_ + "/" + GetAppBaseName() + ".odex";
112   }
113 
GenerateAppOdexFile(Flavor flavor,const std::vector<std::string> & args,std::string * error_msg)114   bool GenerateAppOdexFile(Flavor flavor,
115                            const std::vector<std::string>& args,
116                            /*out*/ std::string* error_msg) {
117     std::string dex2oat_path = GetExecutableFilePath(flavor, "dex2oat");
118     std::vector<std::string> exec_argv = {
119         dex2oat_path,
120         "--runtime-arg",
121         "-Xms64m",
122         "--runtime-arg",
123         "-Xmx512m",
124         "--runtime-arg",
125         "-Xnorelocate",
126         "--boot-image=" + GetCoreArtLocation(),
127         "--instruction-set=" + std::string(GetInstructionSetString(kRuntimeISA)),
128         "--dex-file=" + GetTestDexFileName(GetAppBaseName().c_str()),
129         "--oat-file=" + GetAppOdexName(),
130         "--compiler-filter=speed"
131     };
132     exec_argv.insert(exec_argv.end(), args.begin(), args.end());
133 
134     return ForkAndExecAndWait(exec_argv, error_msg);
135   }
136 
137   // Run the test with custom arguments.
Exec(Flavor flavor,Mode mode,const std::vector<std::string> & args,Display display,std::string * error_msg)138   bool Exec(Flavor flavor,
139             Mode mode,
140             const std::vector<std::string>& args,
141             Display display,
142             /*out*/ std::string* error_msg) {
143     std::string file_path = GetExecutableFilePath(flavor, "oatdump");
144 
145     EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
146 
147     // ScratchFile scratch;
148     std::vector<std::string> exec_argv = { file_path };
149     std::vector<std::string> expected_prefixes;
150     if (mode == kModeSymbolize) {
151       exec_argv.push_back("--symbolize=" + core_oat_location_);
152       exec_argv.push_back("--output=" + core_oat_location_ + ".symbolize");
153     } else {
154       expected_prefixes.push_back("Dex file data for");
155       expected_prefixes.push_back("Num string ids:");
156       expected_prefixes.push_back("Num field ids:");
157       expected_prefixes.push_back("Num method ids:");
158       expected_prefixes.push_back("LOCATION:");
159       expected_prefixes.push_back("MAGIC:");
160       expected_prefixes.push_back("DEX FILE COUNT:");
161       if (display == kListAndCode) {
162         // Code and dex code do not show up if list only.
163         expected_prefixes.push_back("DEX CODE:");
164         expected_prefixes.push_back("CODE:");
165         expected_prefixes.push_back("CodeInfoEncoding");
166         expected_prefixes.push_back("CodeInfoInlineInfo");
167       }
168       if (mode == kModeArt) {
169         exec_argv.push_back("--image=" + core_art_location_);
170         exec_argv.push_back("--instruction-set=" + std::string(
171             GetInstructionSetString(kRuntimeISA)));
172         expected_prefixes.push_back("IMAGE LOCATION:");
173         expected_prefixes.push_back("IMAGE BEGIN:");
174         expected_prefixes.push_back("kDexCaches:");
175       } else if (mode == kModeOatWithBootImage) {
176         exec_argv.push_back("--boot-image=" + GetCoreArtLocation());
177         exec_argv.push_back("--instruction-set=" + std::string(
178             GetInstructionSetString(kRuntimeISA)));
179         exec_argv.push_back("--oat-file=" + GetAppOdexName());
180       } else {
181         CHECK_EQ(static_cast<size_t>(mode), static_cast<size_t>(kModeOat));
182         exec_argv.push_back("--oat-file=" + core_oat_location_);
183       }
184     }
185     exec_argv.insert(exec_argv.end(), args.begin(), args.end());
186 
187     pid_t pid;
188     int pipe_fd;
189     bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg);
190     if (result) {
191       static const size_t kLineMax = 256;
192       char line[kLineMax] = {};
193       size_t line_len = 0;
194       size_t total = 0;
195       std::vector<bool> found(expected_prefixes.size(), false);
196       while (true) {
197         while (true) {
198           size_t spaces = 0;
199           // Trim spaces at the start of the line.
200           for (; spaces < line_len && isspace(line[spaces]); ++spaces) {}
201           if (spaces > 0) {
202             line_len -= spaces;
203             memmove(&line[0], &line[spaces], line_len);
204           }
205           ssize_t bytes_read =
206               TEMP_FAILURE_RETRY(read(pipe_fd, &line[line_len], kLineMax - line_len));
207           if (bytes_read <= 0) {
208             break;
209           }
210           line_len += bytes_read;
211           total += bytes_read;
212         }
213         if (line_len == 0) {
214           break;
215         }
216         // Check contents.
217         for (size_t i = 0; i < expected_prefixes.size(); ++i) {
218           const std::string& expected = expected_prefixes[i];
219           if (!found[i] &&
220               line_len >= expected.length() &&
221               memcmp(line, expected.c_str(), expected.length()) == 0) {
222             found[i] = true;
223           }
224         }
225         // Skip to next line.
226         size_t next_line = 0;
227         for (; next_line + 1 < line_len && line[next_line] != '\n'; ++next_line) {}
228         line_len -= next_line + 1;
229         memmove(&line[0], &line[next_line + 1], line_len);
230       }
231       if (mode == kModeSymbolize) {
232         EXPECT_EQ(total, 0u);
233       } else {
234         EXPECT_GT(total, 0u);
235       }
236       LOG(INFO) << "Processed bytes " << total;
237       close(pipe_fd);
238       int status = 0;
239       if (waitpid(pid, &status, 0) != -1) {
240         result = (status == 0);
241       }
242 
243       for (size_t i = 0; i < expected_prefixes.size(); ++i) {
244         if (!found[i]) {
245           LOG(ERROR) << "Did not find prefix " << expected_prefixes[i];
246           result = false;
247         }
248       }
249     }
250 
251     return result;
252   }
253 
ForkAndExec(const std::vector<std::string> & exec_argv,pid_t * pid,int * pipe_fd,std::string * error_msg)254   bool ForkAndExec(const std::vector<std::string>& exec_argv,
255                    /*out*/ pid_t* pid,
256                    /*out*/ int* pipe_fd,
257                    /*out*/ std::string* error_msg) {
258     int link[2];
259     if (pipe(link) == -1) {
260       *error_msg = strerror(errno);
261       return false;
262     }
263 
264     *pid = fork();
265     if (*pid == -1) {
266       *error_msg = strerror(errno);
267       close(link[0]);
268       close(link[1]);
269       return false;
270     }
271 
272     if (*pid == 0) {
273       dup2(link[1], STDOUT_FILENO);
274       close(link[0]);
275       close(link[1]);
276       // change process groups, so we don't get reaped by ProcessManager
277       setpgid(0, 0);
278       // Use execv here rather than art::Exec to avoid blocking on waitpid here.
279       std::vector<char*> argv;
280       for (size_t i = 0; i < exec_argv.size(); ++i) {
281         argv.push_back(const_cast<char*>(exec_argv[i].c_str()));
282       }
283       argv.push_back(nullptr);
284       UNUSED(execv(argv[0], &argv[0]));
285       const std::string command_line(android::base::Join(exec_argv, ' '));
286       PLOG(ERROR) << "Failed to execv(" << command_line << ")";
287       // _exit to avoid atexit handlers in child.
288       _exit(1);
289       UNREACHABLE();
290     } else {
291       close(link[1]);
292       *pipe_fd = link[0];
293       return true;
294     }
295   }
296 
ForkAndExecAndWait(const std::vector<std::string> & exec_argv,std::string * error_msg)297   bool ForkAndExecAndWait(const std::vector<std::string>& exec_argv,
298                           /*out*/ std::string* error_msg) {
299     pid_t pid;
300     int pipe_fd;
301     bool result = ForkAndExec(exec_argv, &pid, &pipe_fd, error_msg);
302     if (result) {
303       close(pipe_fd);
304       int status = 0;
305       if (waitpid(pid, &status, 0) != -1) {
306         result = (status == 0);
307       }
308     }
309     return result;
310   }
311 
312   std::string tmp_dir_;
313 
314  private:
315   std::string core_art_location_;
316   std::string core_oat_location_;
317 };
318 
319 }  // namespace art
320 
321 #endif  // ART_OATDUMP_OATDUMP_TEST_H_
322