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    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.
87TEST_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.
98TEST_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.
110TEST_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.
122TEST_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.
131TEST_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.
148TEST_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.
163TEST_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.
182TEST_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.
197TEST_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.
209TEST_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.
223TEST_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.
245TEST_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.
257TEST_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.
273TEST_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..
285TEST_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.
297TEST_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.
305TEST_F(DexoptAnalyzerTest, ShortDexLocation) {
306  std::string dex_location = "/xx";
307
308  Verify(dex_location, CompilerFilter::kSpeed);
309}
310
311}  // namespace art
312