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