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