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