• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 <string>
18 #include <vector>
19 #include <sstream>
20 
21 #include "common_runtime_test.h"
22 
23 #include "base/logging.h"
24 #include "base/macros.h"
25 #include "base/stringprintf.h"
26 #include "dex2oat_environment_test.h"
27 #include "oat.h"
28 #include "oat_file.h"
29 #include "utils.h"
30 
31 #include <sys/wait.h>
32 #include <unistd.h>
33 
34 namespace art {
35 
36 class Dex2oatTest : public Dex2oatEnvironmentTest {
37  public:
TearDown()38   virtual void TearDown() OVERRIDE {
39     Dex2oatEnvironmentTest::TearDown();
40 
41     output_ = "";
42     error_msg_ = "";
43     success_ = false;
44   }
45 
46  protected:
GenerateOdexForTest(const std::string & dex_location,const std::string & odex_location,CompilerFilter::Filter filter,const std::vector<std::string> & extra_args={},bool expect_success=true)47   void GenerateOdexForTest(const std::string& dex_location,
48                            const std::string& odex_location,
49                            CompilerFilter::Filter filter,
50                            const std::vector<std::string>& extra_args = {},
51                            bool expect_success = true) {
52     std::vector<std::string> args;
53     args.push_back("--dex-file=" + dex_location);
54     args.push_back("--oat-file=" + odex_location);
55     args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
56     args.push_back("--runtime-arg");
57     args.push_back("-Xnorelocate");
58 
59     args.insert(args.end(), extra_args.begin(), extra_args.end());
60 
61     std::string error_msg;
62     bool success = Dex2Oat(args, &error_msg);
63 
64     if (expect_success) {
65       ASSERT_TRUE(success) << error_msg;
66 
67       // Verify the odex file was generated as expected.
68       std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
69                                                        odex_location.c_str(),
70                                                        nullptr,
71                                                        nullptr,
72                                                        false,
73                                                        /*low_4gb*/false,
74                                                        dex_location.c_str(),
75                                                        &error_msg));
76       ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
77 
78       CheckFilter(filter, odex_file->GetCompilerFilter());
79     } else {
80       ASSERT_FALSE(success) << output_;
81 
82       error_msg_ = error_msg;
83 
84       // Verify there's no loadable odex file.
85       std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
86                                                        odex_location.c_str(),
87                                                        nullptr,
88                                                        nullptr,
89                                                        false,
90                                                        /*low_4gb*/false,
91                                                        dex_location.c_str(),
92                                                        &error_msg));
93       ASSERT_TRUE(odex_file.get() == nullptr);
94     }
95   }
96 
97   // Check the input compiler filter against the generated oat file's filter. Mayb be overridden
98   // in subclasses when equality is not expected.
CheckFilter(CompilerFilter::Filter expected,CompilerFilter::Filter actual)99   virtual void CheckFilter(CompilerFilter::Filter expected, CompilerFilter::Filter actual) {
100     EXPECT_EQ(expected, actual);
101   }
102 
Dex2Oat(const std::vector<std::string> & dex2oat_args,std::string * error_msg)103   bool Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* error_msg) {
104     Runtime* runtime = Runtime::Current();
105 
106     const std::vector<gc::space::ImageSpace*>& image_spaces =
107         runtime->GetHeap()->GetBootImageSpaces();
108     if (image_spaces.empty()) {
109       *error_msg = "No image location found for Dex2Oat.";
110       return false;
111     }
112     std::string image_location = image_spaces[0]->GetImageLocation();
113 
114     std::vector<std::string> argv;
115     argv.push_back(runtime->GetCompilerExecutable());
116     argv.push_back("--runtime-arg");
117     argv.push_back("-classpath");
118     argv.push_back("--runtime-arg");
119     std::string class_path = runtime->GetClassPathString();
120     if (class_path == "") {
121       class_path = OatFile::kSpecialSharedLibrary;
122     }
123     argv.push_back(class_path);
124     if (runtime->IsDebuggable()) {
125       argv.push_back("--debuggable");
126     }
127     runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
128 
129     if (!runtime->IsVerificationEnabled()) {
130       argv.push_back("--compiler-filter=verify-none");
131     }
132 
133     if (runtime->MustRelocateIfPossible()) {
134       argv.push_back("--runtime-arg");
135       argv.push_back("-Xrelocate");
136     } else {
137       argv.push_back("--runtime-arg");
138       argv.push_back("-Xnorelocate");
139     }
140 
141     if (!kIsTargetBuild) {
142       argv.push_back("--host");
143     }
144 
145     argv.push_back("--boot-image=" + image_location);
146 
147     std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
148     argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
149 
150     argv.insert(argv.end(), dex2oat_args.begin(), dex2oat_args.end());
151 
152     // We must set --android-root.
153     const char* android_root = getenv("ANDROID_ROOT");
154     CHECK(android_root != nullptr);
155     argv.push_back("--android-root=" + std::string(android_root));
156 
157     std::string command_line(Join(argv, ' '));
158 
159     // We need to fix up the '&' being used for "do not check classpath."
160     size_t ampersand = command_line.find(" &");
161     CHECK_NE(ampersand, std::string::npos);
162     command_line = command_line.replace(ampersand, 2, " \\&");
163 
164     command_line += " 2>&1";
165 
166     // We need dex2oat to actually log things.
167     setenv("ANDROID_LOG_TAGS", "*:d", 1);
168 
169     FILE* pipe = popen(command_line.c_str(), "r");
170 
171     setenv("ANDROID_LOG_TAGS", "*:e", 1);
172 
173     if (pipe == nullptr) {
174       success_ = false;
175     } else {
176       char buffer[128];
177 
178       while (fgets(buffer, 128, pipe) != nullptr) {
179         output_ += buffer;
180       }
181 
182       int result = pclose(pipe);
183       success_ = result == 0;
184     }
185     return success_;
186   }
187 
188   std::string output_ = "";
189   std::string error_msg_ = "";
190   bool success_ = false;
191 };
192 
193 class Dex2oatSwapTest : public Dex2oatTest {
194  protected:
RunTest(bool use_fd,bool expect_use,const std::vector<std::string> & extra_args={})195   void RunTest(bool use_fd, bool expect_use, const std::vector<std::string>& extra_args = {}) {
196     std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar";
197     std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex";
198 
199     Copy(GetDexSrc1(), dex_location);
200 
201     std::vector<std::string> copy(extra_args);
202 
203     std::unique_ptr<ScratchFile> sf;
204     if (use_fd) {
205       sf.reset(new ScratchFile());
206       copy.push_back(StringPrintf("--swap-fd=%d", sf->GetFd()));
207     } else {
208       std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap";
209       copy.push_back("--swap-file=" + swap_location);
210     }
211     GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed, copy);
212 
213     CheckValidity();
214     ASSERT_TRUE(success_);
215     CheckResult(expect_use);
216   }
217 
CheckResult(bool expect_use)218   void CheckResult(bool expect_use) {
219     if (kIsTargetBuild) {
220       CheckTargetResult(expect_use);
221     } else {
222       CheckHostResult(expect_use);
223     }
224   }
225 
CheckTargetResult(bool expect_use ATTRIBUTE_UNUSED)226   void CheckTargetResult(bool expect_use ATTRIBUTE_UNUSED) {
227     // TODO: Ignore for now, as we won't capture any output (it goes to the logcat). We may do
228     //       something for variants with file descriptor where we can control the lifetime of
229     //       the swap file and thus take a look at it.
230   }
231 
CheckHostResult(bool expect_use)232   void CheckHostResult(bool expect_use) {
233     if (!kIsTargetBuild) {
234       if (expect_use) {
235         EXPECT_NE(output_.find("Large app, accepted running with swap."), std::string::npos)
236             << output_;
237       } else {
238         EXPECT_EQ(output_.find("Large app, accepted running with swap."), std::string::npos)
239             << output_;
240       }
241     }
242   }
243 
244   // Check whether the dex2oat run was really successful.
CheckValidity()245   void CheckValidity() {
246     if (kIsTargetBuild) {
247       CheckTargetValidity();
248     } else {
249       CheckHostValidity();
250     }
251   }
252 
CheckTargetValidity()253   void CheckTargetValidity() {
254     // TODO: Ignore for now, as we won't capture any output (it goes to the logcat). We may do
255     //       something for variants with file descriptor where we can control the lifetime of
256     //       the swap file and thus take a look at it.
257   }
258 
259   // On the host, we can get the dex2oat output. Here, look for "dex2oat took."
CheckHostValidity()260   void CheckHostValidity() {
261     EXPECT_NE(output_.find("dex2oat took"), std::string::npos) << output_;
262   }
263 };
264 
TEST_F(Dex2oatSwapTest,DoNotUseSwapDefaultSingleSmall)265 TEST_F(Dex2oatSwapTest, DoNotUseSwapDefaultSingleSmall) {
266   RunTest(false /* use_fd */, false /* expect_use */);
267   RunTest(true /* use_fd */, false /* expect_use */);
268 }
269 
TEST_F(Dex2oatSwapTest,DoNotUseSwapSingle)270 TEST_F(Dex2oatSwapTest, DoNotUseSwapSingle) {
271   RunTest(false /* use_fd */, false /* expect_use */, { "--swap-dex-size-threshold=0" });
272   RunTest(true /* use_fd */, false /* expect_use */, { "--swap-dex-size-threshold=0" });
273 }
274 
TEST_F(Dex2oatSwapTest,DoNotUseSwapSmall)275 TEST_F(Dex2oatSwapTest, DoNotUseSwapSmall) {
276   RunTest(false /* use_fd */, false /* expect_use */, { "--swap-dex-count-threshold=0" });
277   RunTest(true /* use_fd */, false /* expect_use */, { "--swap-dex-count-threshold=0" });
278 }
279 
TEST_F(Dex2oatSwapTest,DoUseSwapSingleSmall)280 TEST_F(Dex2oatSwapTest, DoUseSwapSingleSmall) {
281   RunTest(false /* use_fd */,
282           true /* expect_use */,
283           { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" });
284   RunTest(true /* use_fd */,
285           true /* expect_use */,
286           { "--swap-dex-size-threshold=0", "--swap-dex-count-threshold=0" });
287 }
288 
289 class Dex2oatVeryLargeTest : public Dex2oatTest {
290  protected:
CheckFilter(CompilerFilter::Filter input ATTRIBUTE_UNUSED,CompilerFilter::Filter result ATTRIBUTE_UNUSED)291   void CheckFilter(CompilerFilter::Filter input ATTRIBUTE_UNUSED,
292                    CompilerFilter::Filter result ATTRIBUTE_UNUSED) OVERRIDE {
293     // Ignore, we'll do our own checks.
294   }
295 
RunTest(CompilerFilter::Filter filter,bool expect_large,const std::vector<std::string> & extra_args={})296   void RunTest(CompilerFilter::Filter filter,
297                bool expect_large,
298                const std::vector<std::string>& extra_args = {}) {
299     std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
300     std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
301 
302     Copy(GetDexSrc1(), dex_location);
303 
304     std::vector<std::string> copy(extra_args);
305 
306     GenerateOdexForTest(dex_location, odex_location, filter, copy);
307 
308     CheckValidity();
309     ASSERT_TRUE(success_);
310     CheckResult(dex_location, odex_location, filter, expect_large);
311   }
312 
CheckResult(const std::string & dex_location,const std::string & odex_location,CompilerFilter::Filter filter,bool expect_large)313   void CheckResult(const std::string& dex_location,
314                    const std::string& odex_location,
315                    CompilerFilter::Filter filter,
316                    bool expect_large) {
317     // Host/target independent checks.
318     std::string error_msg;
319     std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
320                                                      odex_location.c_str(),
321                                                      nullptr,
322                                                      nullptr,
323                                                      false,
324                                                      /*low_4gb*/false,
325                                                      dex_location.c_str(),
326                                                      &error_msg));
327     ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
328     if (expect_large) {
329       // Note: we cannot check the following:
330       //   EXPECT_TRUE(CompilerFilter::IsAsGoodAs(CompilerFilter::kVerifyAtRuntime,
331       //                                          odex_file->GetCompilerFilter()));
332       // The reason is that the filter override currently happens when the dex files are
333       // loaded in dex2oat, which is after the oat file has been started. Thus, the header
334       // store cannot be changed, and the original filter is set in stone.
335 
336       for (const OatDexFile* oat_dex_file : odex_file->GetOatDexFiles()) {
337         std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&error_msg);
338         ASSERT_TRUE(dex_file != nullptr);
339         uint32_t class_def_count = dex_file->NumClassDefs();
340         ASSERT_LT(class_def_count, std::numeric_limits<uint16_t>::max());
341         for (uint16_t class_def_index = 0; class_def_index < class_def_count; ++class_def_index) {
342           OatFile::OatClass oat_class = oat_dex_file->GetOatClass(class_def_index);
343           EXPECT_EQ(oat_class.GetType(), OatClassType::kOatClassNoneCompiled);
344         }
345       }
346 
347       // If the input filter was "below," it should have been used.
348       if (!CompilerFilter::IsAsGoodAs(CompilerFilter::kVerifyAtRuntime, filter)) {
349         EXPECT_EQ(odex_file->GetCompilerFilter(), filter);
350       }
351     } else {
352       EXPECT_EQ(odex_file->GetCompilerFilter(), filter);
353     }
354 
355     // Host/target dependent checks.
356     if (kIsTargetBuild) {
357       CheckTargetResult(expect_large);
358     } else {
359       CheckHostResult(expect_large);
360     }
361   }
362 
CheckTargetResult(bool expect_large ATTRIBUTE_UNUSED)363   void CheckTargetResult(bool expect_large ATTRIBUTE_UNUSED) {
364     // TODO: Ignore for now. May do something for fd things.
365   }
366 
CheckHostResult(bool expect_large)367   void CheckHostResult(bool expect_large) {
368     if (!kIsTargetBuild) {
369       if (expect_large) {
370         EXPECT_NE(output_.find("Very large app, downgrading to verify-at-runtime."),
371                   std::string::npos)
372             << output_;
373       } else {
374         EXPECT_EQ(output_.find("Very large app, downgrading to verify-at-runtime."),
375                   std::string::npos)
376             << output_;
377       }
378     }
379   }
380 
381   // Check whether the dex2oat run was really successful.
CheckValidity()382   void CheckValidity() {
383     if (kIsTargetBuild) {
384       CheckTargetValidity();
385     } else {
386       CheckHostValidity();
387     }
388   }
389 
CheckTargetValidity()390   void CheckTargetValidity() {
391     // TODO: Ignore for now.
392   }
393 
394   // On the host, we can get the dex2oat output. Here, look for "dex2oat took."
CheckHostValidity()395   void CheckHostValidity() {
396     EXPECT_NE(output_.find("dex2oat took"), std::string::npos) << output_;
397   }
398 };
399 
TEST_F(Dex2oatVeryLargeTest,DontUseVeryLarge)400 TEST_F(Dex2oatVeryLargeTest, DontUseVeryLarge) {
401   RunTest(CompilerFilter::kVerifyNone, false);
402   RunTest(CompilerFilter::kVerifyAtRuntime, false);
403   RunTest(CompilerFilter::kInterpretOnly, false);
404   RunTest(CompilerFilter::kSpeed, false);
405 
406   RunTest(CompilerFilter::kVerifyNone, false, { "--very-large-app-threshold=1000000" });
407   RunTest(CompilerFilter::kVerifyAtRuntime, false, { "--very-large-app-threshold=1000000" });
408   RunTest(CompilerFilter::kInterpretOnly, false, { "--very-large-app-threshold=1000000" });
409   RunTest(CompilerFilter::kSpeed, false, { "--very-large-app-threshold=1000000" });
410 }
411 
TEST_F(Dex2oatVeryLargeTest,UseVeryLarge)412 TEST_F(Dex2oatVeryLargeTest, UseVeryLarge) {
413   RunTest(CompilerFilter::kVerifyNone, false, { "--very-large-app-threshold=100" });
414   RunTest(CompilerFilter::kVerifyAtRuntime, false, { "--very-large-app-threshold=100" });
415   RunTest(CompilerFilter::kInterpretOnly, true, { "--very-large-app-threshold=100" });
416   RunTest(CompilerFilter::kSpeed, true, { "--very-large-app-threshold=100" });
417 }
418 
419 }  // namespace art
420