• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 <gtest/gtest.h>
18 
19 #include <string>
20 
21 #include "arch/instruction_set.h"
22 #include "base/compiler_filter.h"
23 #include "dexopt_test.h"
24 #include "dexoptanalyzer.h"
25 
26 namespace art {
27 namespace dexoptanalyzer {
28 
29 class DexoptAnalyzerTest : public DexoptTest {
30  protected:
GetDexoptAnalyzerCmd()31   std::string GetDexoptAnalyzerCmd() {
32     std::string file_path = GetArtBinDir() + "/dexoptanalyzer";
33     if (kIsDebugBuild) {
34       file_path += 'd';
35     }
36     EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
37     return file_path;
38   }
39 
Analyze(const std::string & dex_file,CompilerFilter::Filter compiler_filter,ProfileAnalysisResult profile_analysis_result,const char * class_loader_context,bool downgrade=false)40   int Analyze(const std::string& dex_file,
41               CompilerFilter::Filter compiler_filter,
42               ProfileAnalysisResult profile_analysis_result,
43               const char* class_loader_context,
44               bool downgrade = false) {
45     std::string dexoptanalyzer_cmd = GetDexoptAnalyzerCmd();
46     std::vector<std::string> argv_str;
47     argv_str.push_back(dexoptanalyzer_cmd);
48     argv_str.push_back("--dex-file=" + dex_file);
49     argv_str.push_back("--isa=" + std::string(GetInstructionSetString(kRuntimeISA)));
50     argv_str.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(compiler_filter));
51     argv_str.push_back("--profile-analysis-result=" +
52         std::to_string(static_cast<int>(profile_analysis_result)));
53     if (downgrade) {
54       argv_str.push_back("--downgrade");
55     }
56 
57     argv_str.push_back("--runtime-arg");
58     argv_str.push_back(GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames()));
59     argv_str.push_back("--runtime-arg");
60     argv_str.push_back(GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations()));
61     argv_str.push_back("--image=" + GetImageLocation());
62     argv_str.push_back("--android-data=" + android_data_);
63     if (class_loader_context != nullptr) {
64       argv_str.push_back("--class-loader-context=" + std::string(class_loader_context));
65     }
66 
67     std::string error;
68     return ExecAndReturnCode(argv_str, &error);
69   }
70 
DexoptanalyzerToOatFileAssistant(int dexoptanalyzerResult)71   int DexoptanalyzerToOatFileAssistant(int dexoptanalyzerResult) {
72     switch (dexoptanalyzerResult) {
73       case 0: return OatFileAssistant::kNoDexOptNeeded;
74       case 1: return OatFileAssistant::kDex2OatFromScratch;
75       case 2: return OatFileAssistant::kDex2OatForBootImage;
76       case 3: return OatFileAssistant::kDex2OatForFilter;
77       case 4: return -OatFileAssistant::kDex2OatForBootImage;
78       case 5: return -OatFileAssistant::kDex2OatForFilter;
79       default: return dexoptanalyzerResult;
80     }
81   }
82 
83   // Verify that the output of dexoptanalyzer for the given arguments is the same
84   // as the output of OatFileAssistant::GetDexOptNeeded.
Verify(const std::string & dex_file,CompilerFilter::Filter compiler_filter,ProfileAnalysisResult profile_analysis_result=ProfileAnalysisResult::kDontOptimizeSmallDelta,bool downgrade=false,const char * class_loader_context="PCL[]")85   void Verify(const std::string& dex_file,
86               CompilerFilter::Filter compiler_filter,
87               ProfileAnalysisResult profile_analysis_result =
88                   ProfileAnalysisResult::kDontOptimizeSmallDelta,
89               bool downgrade = false,
90               const char* class_loader_context = "PCL[]") {
91     std::unique_ptr<ClassLoaderContext> context = class_loader_context == nullptr
92         ? nullptr
93         : ClassLoaderContext::Create(class_loader_context);
94     if (context != nullptr) {
95       std::vector<int> context_fds;
96       ASSERT_TRUE(context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true));
97     }
98 
99     int dexoptanalyzerResult = Analyze(
100         dex_file, compiler_filter, profile_analysis_result, class_loader_context, downgrade);
101     dexoptanalyzerResult = DexoptanalyzerToOatFileAssistant(dexoptanalyzerResult);
102     OatFileAssistant oat_file_assistant(dex_file.c_str(),
103                                         kRuntimeISA,
104                                         context.get(),
105                                         /*load_executable=*/ false);
106     bool assume_profile_changed = profile_analysis_result == ProfileAnalysisResult::kOptimize;
107     int assistantResult = oat_file_assistant.GetDexOptNeeded(
108         compiler_filter, assume_profile_changed, downgrade);
109     EXPECT_EQ(assistantResult, dexoptanalyzerResult);
110   }
111 };
112 
113 // The tests below exercise the same test case from oat_file_assistant_test.cc.
114 
115 // Case: We have a DEX file, but no ODEX file for it.
TEST_F(DexoptAnalyzerTest,DexNoOat)116 TEST_F(DexoptAnalyzerTest, DexNoOat) {
117   std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
118   Copy(GetDexSrc1(), dex_location);
119 
120   Verify(dex_location, CompilerFilter::kSpeed);
121   Verify(dex_location, CompilerFilter::kExtract);
122   Verify(dex_location, CompilerFilter::kVerify);
123   Verify(dex_location, CompilerFilter::kSpeedProfile);
124   Verify(dex_location, CompilerFilter::kSpeed,
125       ProfileAnalysisResult::kDontOptimizeSmallDelta, false, nullptr);
126 }
127 
128 // Case: We have a DEX file and up-to-date ODEX file for it.
TEST_F(DexoptAnalyzerTest,OatUpToDate)129 TEST_F(DexoptAnalyzerTest, OatUpToDate) {
130   std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
131   std::string odex_location = GetOdexDir() + "/OatUpToDate.odex";
132   Copy(GetDexSrc1(), dex_location);
133   GenerateOdexForTest(dex_location.c_str(), odex_location.c_str(), CompilerFilter::kSpeed);
134 
135   Verify(dex_location, CompilerFilter::kSpeed);
136   Verify(dex_location, CompilerFilter::kVerify);
137   Verify(dex_location, CompilerFilter::kExtract);
138   Verify(dex_location, CompilerFilter::kEverything);
139   Verify(dex_location, CompilerFilter::kSpeed,
140       ProfileAnalysisResult::kDontOptimizeSmallDelta, false, nullptr);
141 }
142 
143 // Case: We have a DEX file and speed-profile ODEX file for it.
TEST_F(DexoptAnalyzerTest,ProfileOatUpToDate)144 TEST_F(DexoptAnalyzerTest, ProfileOatUpToDate) {
145   std::string dex_location = GetScratchDir() + "/ProfileOatUpToDate.jar";
146   std::string odex_location = GetOdexDir() + "/ProfileOatUpToDate.odex";
147   Copy(GetDexSrc1(), dex_location);
148   GenerateOdexForTest(dex_location.c_str(), odex_location.c_str(), CompilerFilter::kSpeedProfile);
149 
150   Verify(dex_location, CompilerFilter::kSpeedProfile,
151       ProfileAnalysisResult::kDontOptimizeSmallDelta);
152   Verify(dex_location, CompilerFilter::kVerify, ProfileAnalysisResult::kDontOptimizeSmallDelta);
153   Verify(dex_location, CompilerFilter::kSpeedProfile, ProfileAnalysisResult::kOptimize);
154   Verify(dex_location, CompilerFilter::kVerify, ProfileAnalysisResult::kOptimize);
155 }
156 
157 // Case: We have a DEX file, verify odex file for it, and we ask if it's up to date
158 // when the profiles are empty or full.
TEST_F(DexoptAnalyzerTest,VerifyAndEmptyProfiles)159 TEST_F(DexoptAnalyzerTest, VerifyAndEmptyProfiles) {
160   std::string dex_location = GetScratchDir() + "/VerifyAndEmptyProfiles.jar";
161   std::string odex_location = GetOdexDir() + "/VerifyAndEmptyProfiles.odex";
162   Copy(GetDexSrc1(), dex_location);
163 
164   GenerateOdexForTest(dex_location.c_str(), odex_location.c_str(), CompilerFilter::kVerify);
165 
166   // If we want to speed-profile something that was verified, do it even if
167   // the profile analysis returns kDontOptimizeSmallDelta (it means that we do have profile data,
168   // so a transition verify -> speed-profile is still worth).
169   ASSERT_EQ(
170       static_cast<int>(ReturnCode::kDex2OatForFilterOdex),
171       Analyze(dex_location, CompilerFilter::kSpeedProfile,
172           ProfileAnalysisResult::kDontOptimizeSmallDelta, "PCL[]"));
173   // If we want to speed-profile something that was verified but the profiles are empty,
174   // don't do it - there will be no gain.
175   ASSERT_EQ(
176       static_cast<int>(ReturnCode::kNoDexOptNeeded),
177       Analyze(dex_location, CompilerFilter::kSpeedProfile,
178           ProfileAnalysisResult::kDontOptimizeEmptyProfiles, "PCL[]"));
179   // Standard case where we need to re-compile a speed-profile because of sufficient new
180   // information in the profile.
181   ASSERT_EQ(
182       static_cast<int>(ReturnCode::kDex2OatForFilterOdex),
183       Analyze(dex_location, CompilerFilter::kSpeedProfile,
184           ProfileAnalysisResult::kOptimize, "PCL[]"));
185 }
186 
TEST_F(DexoptAnalyzerTest,Downgrade)187 TEST_F(DexoptAnalyzerTest, Downgrade) {
188   std::string dex_location = GetScratchDir() + "/Downgrade.jar";
189   std::string odex_location = GetOdexDir() + "/Downgrade.odex";
190   Copy(GetDexSrc1(), dex_location);
191   GenerateOdexForTest(dex_location.c_str(), odex_location.c_str(), CompilerFilter::kVerify);
192 
193   Verify(dex_location, CompilerFilter::kSpeedProfile,
194       ProfileAnalysisResult::kDontOptimizeSmallDelta, true);
195   Verify(dex_location, CompilerFilter::kVerify,
196       ProfileAnalysisResult::kDontOptimizeSmallDelta, true);
197   Verify(dex_location, CompilerFilter::kExtract,
198       ProfileAnalysisResult::kDontOptimizeSmallDelta, true);
199 }
200 
201 // Case: We have a MultiDEX file and up-to-date ODEX file for it.
TEST_F(DexoptAnalyzerTest,MultiDexOatUpToDate)202 TEST_F(DexoptAnalyzerTest, MultiDexOatUpToDate) {
203   std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar";
204   std::string odex_location = GetOdexDir() + "/MultiDexOatUpToDate.odex";
205 
206   Copy(GetMultiDexSrc1(), dex_location);
207   GenerateOdexForTest(dex_location.c_str(), odex_location.c_str(), CompilerFilter::kSpeed);
208 
209   Verify(dex_location, CompilerFilter::kSpeed, ProfileAnalysisResult::kDontOptimizeSmallDelta);
210 }
211 
212 // Case: We have a MultiDEX file where the secondary dex file is out of date.
TEST_F(DexoptAnalyzerTest,MultiDexSecondaryOutOfDate)213 TEST_F(DexoptAnalyzerTest, MultiDexSecondaryOutOfDate) {
214   std::string dex_location = GetScratchDir() + "/MultiDexSecondaryOutOfDate.jar";
215   std::string odex_location = GetOdexDir() + "/MultiDexSecondaryOutOfDate.odex";
216 
217   // Compile code for GetMultiDexSrc1.
218   Copy(GetMultiDexSrc1(), dex_location);
219   GenerateOdexForTest(dex_location.c_str(), odex_location.c_str(), CompilerFilter::kSpeed);
220 
221   // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum
222   // is out of date.
223   Copy(GetMultiDexSrc2(), dex_location);
224 
225   Verify(dex_location, CompilerFilter::kSpeed, ProfileAnalysisResult::kDontOptimizeSmallDelta);
226 }
227 
228 // Case: We have a DEX file and an ODEX file out of date with respect to the
229 // dex checksum.
TEST_F(DexoptAnalyzerTest,OatDexOutOfDate)230 TEST_F(DexoptAnalyzerTest, OatDexOutOfDate) {
231   std::string dex_location = GetScratchDir() + "/OatDexOutOfDate.jar";
232   std::string odex_location = GetOdexDir() + "/OatDexOutOfDate.odex";
233 
234   // We create a dex, generate an oat for it, then overwrite the dex with a
235   // different dex to make the oat out of date.
236   Copy(GetDexSrc1(), dex_location);
237   GenerateOdexForTest(dex_location.c_str(), odex_location.c_str(), CompilerFilter::kSpeed);
238   Copy(GetDexSrc2(), dex_location);
239 
240   Verify(dex_location, CompilerFilter::kExtract);
241   Verify(dex_location, CompilerFilter::kSpeed);
242 }
243 
244 // Case: We have a DEX file and an ODEX file out of date with respect to the
245 // boot image.
TEST_F(DexoptAnalyzerTest,OatImageOutOfDate)246 TEST_F(DexoptAnalyzerTest, OatImageOutOfDate) {
247   std::string dex_location = GetScratchDir() + "/OatImageOutOfDate.jar";
248   std::string odex_location = GetOdexDir() + "/OatImageOutOfDate.odex";
249 
250   Copy(GetDexSrc1(), dex_location);
251   GenerateOatForTest(dex_location.c_str(),
252                      odex_location.c_str(),
253                      CompilerFilter::kSpeed,
254                      /*with_alternate_image=*/true);
255 
256   Verify(dex_location, CompilerFilter::kExtract);
257   Verify(dex_location, CompilerFilter::kVerify);
258   Verify(dex_location, CompilerFilter::kSpeed);
259 }
260 
261 // Case: We have a DEX file and a verify-at-runtime OAT file out of date with
262 // respect to the boot image.
263 // It shouldn't matter that the OAT file is out of date, because it is
264 // verify-at-runtime.
TEST_F(DexoptAnalyzerTest,OatVerifyAtRuntimeImageOutOfDate)265 TEST_F(DexoptAnalyzerTest, OatVerifyAtRuntimeImageOutOfDate) {
266   std::string dex_location = GetScratchDir() + "/OatVerifyAtRuntimeImageOutOfDate.jar";
267   std::string odex_location = GetOdexDir() + "/OatVerifyAtRuntimeImageOutOfDate.odex";
268 
269   Copy(GetDexSrc1(), dex_location);
270   GenerateOatForTest(dex_location.c_str(),
271                      odex_location.c_str(),
272                      CompilerFilter::kExtract,
273                      /*with_alternate_image=*/true);
274 
275   Verify(dex_location, CompilerFilter::kExtract);
276   Verify(dex_location, CompilerFilter::kVerify);
277 }
278 
279 // Case: We have a DEX file and an ODEX file, but no OAT file.
TEST_F(DexoptAnalyzerTest,DexOdexNoOat)280 TEST_F(DexoptAnalyzerTest, DexOdexNoOat) {
281   std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
282   std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
283 
284   Copy(GetDexSrc1(), dex_location);
285   GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
286 
287   Verify(dex_location, CompilerFilter::kExtract);
288   Verify(dex_location, CompilerFilter::kSpeed);
289   Verify(dex_location, CompilerFilter::kEverything);
290 }
291 
292 // Case: We have a stripped (or resource-only) DEX file, no ODEX file and no
293 // OAT file. Expect: The status is kNoDexOptNeeded.
TEST_F(DexoptAnalyzerTest,ResourceOnlyDex)294 TEST_F(DexoptAnalyzerTest, ResourceOnlyDex) {
295   std::string dex_location = GetScratchDir() + "/ResourceOnlyDex.jar";
296 
297   Copy(GetResourceOnlySrc1(), dex_location);
298 
299   Verify(dex_location, CompilerFilter::kSpeed);
300   Verify(dex_location, CompilerFilter::kExtract);
301   Verify(dex_location, CompilerFilter::kVerify);
302 }
303 
304 // Case: We have a DEX file, an ODEX file and an OAT file.
TEST_F(DexoptAnalyzerTest,OdexOatOverlap)305 TEST_F(DexoptAnalyzerTest, OdexOatOverlap) {
306   std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
307   std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
308   std::string oat_location = GetOdexDir() + "/OdexOatOverlap.oat";
309 
310   Copy(GetDexSrc1(), dex_location);
311   GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
312 
313   // Create the oat file by copying the odex so they are located in the same
314   // place in memory.
315   Copy(odex_location, oat_location);
316 
317   Verify(dex_location, CompilerFilter::kSpeed);
318 }
319 
320 // Case: We have a DEX file and a VerifyAtRuntime ODEX file, but no OAT file..
TEST_F(DexoptAnalyzerTest,DexVerifyAtRuntimeOdexNoOat)321 TEST_F(DexoptAnalyzerTest, DexVerifyAtRuntimeOdexNoOat) {
322   std::string dex_location = GetScratchDir() + "/DexVerifyAtRuntimeOdexNoOat.jar";
323   std::string odex_location = GetOdexDir() + "/DexVerifyAtRuntimeOdexNoOat.odex";
324 
325   Copy(GetDexSrc1(), dex_location);
326   GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kExtract);
327 
328   Verify(dex_location, CompilerFilter::kExtract);
329   Verify(dex_location, CompilerFilter::kSpeed);
330 }
331 
332 // Case: Non-standard extension for dex file.
TEST_F(DexoptAnalyzerTest,LongDexExtension)333 TEST_F(DexoptAnalyzerTest, LongDexExtension) {
334   std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
335   Copy(GetDexSrc1(), dex_location);
336 
337   Verify(dex_location, CompilerFilter::kSpeed);
338 }
339 
340 // Case: Very short, non-existent Dex location.
TEST_F(DexoptAnalyzerTest,ShortDexLocation)341 TEST_F(DexoptAnalyzerTest, ShortDexLocation) {
342   std::string dex_location = "/xx";
343 
344   Verify(dex_location, CompilerFilter::kSpeed);
345 }
346 
347 // Case: We have a DEX file and up-to-date OAT file for it, and we check with
348 // a class loader context.
TEST_F(DexoptAnalyzerTest,ClassLoaderContext)349 TEST_F(DexoptAnalyzerTest, ClassLoaderContext) {
350   std::string dex_location1 = GetScratchDir() + "/DexToAnalyze.jar";
351   std::string odex_location1 = GetOdexDir() + "/DexToAnalyze.odex";
352   std::string dex_location2 = GetScratchDir() + "/DexInContext.jar";
353   Copy(GetDexSrc1(), dex_location1);
354   Copy(GetDexSrc2(), dex_location2);
355 
356   std::string class_loader_context = "PCL[" + dex_location2 + "]";
357   std::string class_loader_context_option = "--class-loader-context=PCL[" + dex_location2 + "]";
358 
359   // Generate the odex to get the class loader context also open the dex files.
360   GenerateOdexForTest(dex_location1, odex_location1, CompilerFilter::kSpeed, /* compilation_reason= */ nullptr, /* extra_args= */ { class_loader_context_option });
361 
362   Verify(dex_location1, CompilerFilter::kSpeed, ProfileAnalysisResult::kDontOptimizeSmallDelta,
363       false, class_loader_context.c_str());
364 }
365 
366 }  // namespace dexoptanalyzer
367 }  // namespace art
368