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