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::kVerify);
122 Verify(dex_location, CompilerFilter::kSpeedProfile);
123 Verify(dex_location, CompilerFilter::kSpeed,
124 ProfileAnalysisResult::kDontOptimizeSmallDelta, false, nullptr);
125 }
126
127 // Case: We have a DEX file and up-to-date ODEX file for it.
TEST_F(DexoptAnalyzerTest,OatUpToDate)128 TEST_F(DexoptAnalyzerTest, OatUpToDate) {
129 std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
130 std::string odex_location = GetOdexDir() + "/OatUpToDate.odex";
131 Copy(GetDexSrc1(), dex_location);
132 GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
133
134 Verify(dex_location, CompilerFilter::kSpeed);
135 Verify(dex_location, CompilerFilter::kVerify);
136 Verify(dex_location, CompilerFilter::kEverything);
137 Verify(dex_location, CompilerFilter::kSpeed,
138 ProfileAnalysisResult::kDontOptimizeSmallDelta, false, nullptr);
139 }
140
141 // Case: We have a DEX file and speed-profile ODEX file for it.
TEST_F(DexoptAnalyzerTest,ProfileOatUpToDate)142 TEST_F(DexoptAnalyzerTest, ProfileOatUpToDate) {
143 std::string dex_location = GetScratchDir() + "/ProfileOatUpToDate.jar";
144 std::string odex_location = GetOdexDir() + "/ProfileOatUpToDate.odex";
145 Copy(GetDexSrc1(), dex_location);
146 GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeedProfile);
147
148 Verify(dex_location, CompilerFilter::kSpeedProfile,
149 ProfileAnalysisResult::kDontOptimizeSmallDelta);
150 Verify(dex_location, CompilerFilter::kVerify, ProfileAnalysisResult::kDontOptimizeSmallDelta);
151 Verify(dex_location, CompilerFilter::kSpeedProfile, ProfileAnalysisResult::kOptimize);
152 Verify(dex_location, CompilerFilter::kVerify, ProfileAnalysisResult::kOptimize);
153 }
154
155 // Case: We have a DEX file, verify odex file for it, and we ask if it's up to date
156 // when the profiles are empty or full.
TEST_F(DexoptAnalyzerTest,VerifyAndEmptyProfiles)157 TEST_F(DexoptAnalyzerTest, VerifyAndEmptyProfiles) {
158 std::string dex_location = GetScratchDir() + "/VerifyAndEmptyProfiles.jar";
159 std::string odex_location = GetOdexDir() + "/VerifyAndEmptyProfiles.odex";
160 Copy(GetDexSrc1(), dex_location);
161
162 GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kVerify);
163
164 // If we want to speed-profile something that was verified, do it even if
165 // the profile analysis returns kDontOptimizeSmallDelta (it means that we do have profile data,
166 // so a transition verify -> speed-profile is still worth).
167 ASSERT_EQ(
168 static_cast<int>(ReturnCode::kDex2OatForFilterOdex),
169 Analyze(dex_location, CompilerFilter::kSpeedProfile,
170 ProfileAnalysisResult::kDontOptimizeSmallDelta, "PCL[]"));
171 // If we want to speed-profile something that was verified but the profiles are empty,
172 // don't do it - there will be no gain.
173 ASSERT_EQ(
174 static_cast<int>(ReturnCode::kNoDexOptNeeded),
175 Analyze(dex_location, CompilerFilter::kSpeedProfile,
176 ProfileAnalysisResult::kDontOptimizeEmptyProfiles, "PCL[]"));
177 // Standard case where we need to re-compile a speed-profile because of sufficient new
178 // information in the profile.
179 ASSERT_EQ(
180 static_cast<int>(ReturnCode::kDex2OatForFilterOdex),
181 Analyze(dex_location, CompilerFilter::kSpeedProfile,
182 ProfileAnalysisResult::kOptimize, "PCL[]"));
183 }
184
TEST_F(DexoptAnalyzerTest,Downgrade)185 TEST_F(DexoptAnalyzerTest, Downgrade) {
186 std::string dex_location = GetScratchDir() + "/Downgrade.jar";
187 std::string odex_location = GetOdexDir() + "/Downgrade.odex";
188 Copy(GetDexSrc1(), dex_location);
189 GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kVerify);
190
191 Verify(dex_location, CompilerFilter::kSpeedProfile,
192 ProfileAnalysisResult::kDontOptimizeSmallDelta, true);
193 Verify(dex_location, CompilerFilter::kVerify,
194 ProfileAnalysisResult::kDontOptimizeSmallDelta, true);
195 }
196
197 // Case: We have a MultiDEX file and up-to-date ODEX file for it.
TEST_F(DexoptAnalyzerTest,MultiDexOatUpToDate)198 TEST_F(DexoptAnalyzerTest, MultiDexOatUpToDate) {
199 std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar";
200 std::string odex_location = GetOdexDir() + "/MultiDexOatUpToDate.odex";
201
202 Copy(GetMultiDexSrc1(), dex_location);
203 GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
204
205 Verify(dex_location, CompilerFilter::kSpeed, ProfileAnalysisResult::kDontOptimizeSmallDelta);
206 }
207
208 // Case: We have a MultiDEX file where the secondary dex file is out of date.
TEST_F(DexoptAnalyzerTest,MultiDexSecondaryOutOfDate)209 TEST_F(DexoptAnalyzerTest, MultiDexSecondaryOutOfDate) {
210 std::string dex_location = GetScratchDir() + "/MultiDexSecondaryOutOfDate.jar";
211 std::string odex_location = GetOdexDir() + "/MultiDexSecondaryOutOfDate.odex";
212
213 // Compile code for GetMultiDexSrc1.
214 Copy(GetMultiDexSrc1(), dex_location);
215 GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
216
217 // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum
218 // is out of date.
219 Copy(GetMultiDexSrc2(), dex_location);
220
221 Verify(dex_location, CompilerFilter::kSpeed, ProfileAnalysisResult::kDontOptimizeSmallDelta);
222 }
223
224 // Case: We have a DEX file and an ODEX file out of date with respect to the
225 // dex checksum.
TEST_F(DexoptAnalyzerTest,OatDexOutOfDate)226 TEST_F(DexoptAnalyzerTest, OatDexOutOfDate) {
227 std::string dex_location = GetScratchDir() + "/OatDexOutOfDate.jar";
228 std::string odex_location = GetOdexDir() + "/OatDexOutOfDate.odex";
229
230 // We create a dex, generate an oat for it, then overwrite the dex with a
231 // different dex to make the oat out of date.
232 Copy(GetDexSrc1(), dex_location);
233 GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
234 Copy(GetDexSrc2(), dex_location);
235
236 Verify(dex_location, CompilerFilter::kSpeed);
237 }
238
239 // Case: We have a DEX file and an ODEX file out of date with respect to the
240 // boot image.
TEST_F(DexoptAnalyzerTest,OatImageOutOfDate)241 TEST_F(DexoptAnalyzerTest, OatImageOutOfDate) {
242 std::string dex_location = GetScratchDir() + "/OatImageOutOfDate.jar";
243 std::string odex_location = GetOdexDir() + "/OatImageOutOfDate.odex";
244
245 Copy(GetDexSrc1(), dex_location);
246 GenerateOatForTest(dex_location,
247 odex_location,
248 CompilerFilter::kSpeed,
249 /*with_alternate_image=*/true);
250
251 Verify(dex_location, CompilerFilter::kVerify);
252 Verify(dex_location, CompilerFilter::kSpeed);
253 }
254
255 // Case: We have a DEX file and an ODEX file, but no OAT file.
TEST_F(DexoptAnalyzerTest,DexOdexNoOat)256 TEST_F(DexoptAnalyzerTest, DexOdexNoOat) {
257 std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
258 std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
259
260 Copy(GetDexSrc1(), dex_location);
261 GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
262
263 Verify(dex_location, CompilerFilter::kSpeed);
264 Verify(dex_location, CompilerFilter::kEverything);
265 }
266
267 // Case: We have a stripped (or resource-only) DEX file, no ODEX file and no
268 // OAT file. Expect: The status is kNoDexOptNeeded.
TEST_F(DexoptAnalyzerTest,ResourceOnlyDex)269 TEST_F(DexoptAnalyzerTest, ResourceOnlyDex) {
270 std::string dex_location = GetScratchDir() + "/ResourceOnlyDex.jar";
271
272 Copy(GetResourceOnlySrc1(), dex_location);
273
274 Verify(dex_location, CompilerFilter::kSpeed);
275 Verify(dex_location, CompilerFilter::kVerify);
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: Non-standard extension for dex file.
TEST_F(DexoptAnalyzerTest,LongDexExtension)295 TEST_F(DexoptAnalyzerTest, LongDexExtension) {
296 std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
297 Copy(GetDexSrc1(), dex_location);
298
299 Verify(dex_location, CompilerFilter::kSpeed);
300 }
301
302 // Case: Very short, non-existent Dex location.
TEST_F(DexoptAnalyzerTest,ShortDexLocation)303 TEST_F(DexoptAnalyzerTest, ShortDexLocation) {
304 std::string dex_location = "/xx";
305
306 Verify(dex_location, CompilerFilter::kSpeed);
307 }
308
309 // Case: We have a DEX file and up-to-date OAT file for it, and we check with
310 // a class loader context.
TEST_F(DexoptAnalyzerTest,ClassLoaderContext)311 TEST_F(DexoptAnalyzerTest, ClassLoaderContext) {
312 std::string dex_location1 = GetScratchDir() + "/DexToAnalyze.jar";
313 std::string odex_location1 = GetOdexDir() + "/DexToAnalyze.odex";
314 std::string dex_location2 = GetScratchDir() + "/DexInContext.jar";
315 Copy(GetDexSrc1(), dex_location1);
316 Copy(GetDexSrc2(), dex_location2);
317
318 std::string class_loader_context = "PCL[" + dex_location2 + "]";
319 std::string class_loader_context_option = "--class-loader-context=PCL[" + dex_location2 + "]";
320
321 // Generate the odex to get the class loader context also open the dex files.
322 GenerateOdexForTest(dex_location1, odex_location1, CompilerFilter::kSpeed, /* compilation_reason= */ nullptr, /* extra_args= */ { class_loader_context_option });
323
324 Verify(dex_location1, CompilerFilter::kSpeed, ProfileAnalysisResult::kDontOptimizeSmallDelta,
325 false, class_loader_context.c_str());
326 }
327
328 } // namespace dexoptanalyzer
329 } // namespace art
330