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 <regex>
18 #include <sstream>
19 #include <string>
20 #include <vector>
21
22 #include <sys/wait.h>
23 #include <unistd.h>
24
25 #include "common_runtime_test.h"
26
27 #include "base/logging.h"
28 #include "base/macros.h"
29 #include "base/unix_file/fd_file.h"
30 #include "dex_file-inl.h"
31 #include "jit/profile_compilation_info.h"
32 #include "method_reference.h"
33 #include "runtime.h"
34 #include "utils.h"
35
36 namespace art {
37
38 struct ImageSizes {
39 size_t art_size = 0;
40 size_t oat_size = 0;
41 size_t vdex_size = 0;
42 };
43
operator <<(std::ostream & os,const ImageSizes & sizes)44 std::ostream& operator<<(std::ostream& os, const ImageSizes& sizes) {
45 os << "art=" << sizes.art_size << " oat=" << sizes.oat_size << " vdex=" << sizes.vdex_size;
46 return os;
47 }
48
49 class Dex2oatImageTest : public CommonRuntimeTest {
50 public:
TearDown()51 virtual void TearDown() OVERRIDE {}
52
53 protected:
54 // Visitors take method and type references
55 template <typename MethodVisitor, typename ClassVisitor>
VisitLibcoreDexes(const MethodVisitor & method_visitor,const ClassVisitor & class_visitor,size_t method_frequency=1,size_t class_frequency=1)56 void VisitLibcoreDexes(const MethodVisitor& method_visitor,
57 const ClassVisitor& class_visitor,
58 size_t method_frequency = 1,
59 size_t class_frequency = 1) {
60 size_t method_counter = 0;
61 size_t class_counter = 0;
62 for (std::string dex : GetLibCoreDexFileNames()) {
63 std::vector<std::unique_ptr<const DexFile>> dex_files;
64 std::string error_msg;
65 CHECK(DexFile::Open(dex.c_str(), dex, /*verify_checksum*/ false, &error_msg, &dex_files))
66 << error_msg;
67 for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
68 for (size_t i = 0; i < dex_file->NumMethodIds(); ++i) {
69 if (++method_counter % method_frequency == 0) {
70 method_visitor(MethodReference(dex_file.get(), i));
71 }
72 }
73 for (size_t i = 0; i < dex_file->NumTypeIds(); ++i) {
74 if (++class_counter % class_frequency == 0) {
75 class_visitor(TypeReference(dex_file.get(), dex::TypeIndex(i)));
76 }
77 }
78 }
79 }
80 }
81
WriteLine(File * file,std::string line)82 static void WriteLine(File* file, std::string line) {
83 line += '\n';
84 EXPECT_TRUE(file->WriteFully(&line[0], line.length()));
85 }
86
GenerateClasses(File * out_file,size_t frequency=1)87 void GenerateClasses(File* out_file, size_t frequency = 1) {
88 VisitLibcoreDexes(VoidFunctor(),
89 [out_file](TypeReference ref) {
90 WriteLine(out_file,
91 ref.dex_file->PrettyType(ref.type_index));
92 }, frequency, frequency);
93 EXPECT_EQ(out_file->Flush(), 0);
94 }
95
GenerateMethods(File * out_file,size_t frequency=1)96 void GenerateMethods(File* out_file, size_t frequency = 1) {
97 VisitLibcoreDexes([out_file](MethodReference ref) {
98 WriteLine(out_file,
99 ref.dex_file->PrettyMethod(ref.dex_method_index));
100 }, VoidFunctor(), frequency, frequency);
101 EXPECT_EQ(out_file->Flush(), 0);
102 }
103
AddRuntimeArg(std::vector<std::string> & args,const std::string & arg)104 void AddRuntimeArg(std::vector<std::string>& args, const std::string& arg) {
105 args.push_back("--runtime-arg");
106 args.push_back(arg);
107 }
108
CompileImageAndGetSizes(const std::vector<std::string> & extra_args)109 ImageSizes CompileImageAndGetSizes(const std::vector<std::string>& extra_args) {
110 ImageSizes ret;
111 ScratchFile scratch;
112 std::string scratch_dir = scratch.GetFilename();
113 while (!scratch_dir.empty() && scratch_dir.back() != '/') {
114 scratch_dir.pop_back();
115 }
116 CHECK(!scratch_dir.empty()) << "No directory " << scratch.GetFilename();
117 std::string error_msg;
118 if (!CompileBootImage(extra_args, scratch.GetFilename(), &error_msg)) {
119 LOG(ERROR) << "Failed to compile image " << scratch.GetFilename() << error_msg;
120 }
121 std::string art_file = scratch.GetFilename() + ".art";
122 std::string oat_file = scratch.GetFilename() + ".oat";
123 std::string vdex_file = scratch.GetFilename() + ".vdex";
124 ret.art_size = GetFileSizeBytes(art_file);
125 ret.oat_size = GetFileSizeBytes(oat_file);
126 ret.vdex_size = GetFileSizeBytes(vdex_file);
127 CHECK_GT(ret.art_size, 0u) << art_file;
128 CHECK_GT(ret.oat_size, 0u) << oat_file;
129 CHECK_GT(ret.vdex_size, 0u) << vdex_file;
130 scratch.Close();
131 // Clear image files since we compile the image multiple times and don't want to leave any
132 // artifacts behind.
133 ClearDirectory(scratch_dir.c_str(), /*recursive*/ false);
134 return ret;
135 }
136
CompileBootImage(const std::vector<std::string> & extra_args,const std::string & image_file_name_prefix,std::string * error_msg)137 bool CompileBootImage(const std::vector<std::string>& extra_args,
138 const std::string& image_file_name_prefix,
139 std::string* error_msg) {
140 Runtime* const runtime = Runtime::Current();
141 std::vector<std::string> argv;
142 argv.push_back(runtime->GetCompilerExecutable());
143 AddRuntimeArg(argv, "-Xms64m");
144 AddRuntimeArg(argv, "-Xmx64m");
145 std::vector<std::string> dex_files = GetLibCoreDexFileNames();
146 for (const std::string& dex_file : dex_files) {
147 argv.push_back("--dex-file=" + dex_file);
148 argv.push_back("--dex-location=" + dex_file);
149 }
150 if (runtime->IsJavaDebuggable()) {
151 argv.push_back("--debuggable");
152 }
153 runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
154
155 AddRuntimeArg(argv, "-Xverify:softfail");
156
157 if (!kIsTargetBuild) {
158 argv.push_back("--host");
159 }
160
161 ScratchFile file;
162 const std::string image_prefix = file.GetFilename();
163
164 argv.push_back("--image=" + image_file_name_prefix + ".art");
165 argv.push_back("--oat-file=" + image_file_name_prefix + ".oat");
166 argv.push_back("--oat-location=" + image_file_name_prefix + ".oat");
167 argv.push_back("--base=0x60000000");
168
169 std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
170 argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
171
172 // We must set --android-root.
173 const char* android_root = getenv("ANDROID_ROOT");
174 CHECK(android_root != nullptr);
175 argv.push_back("--android-root=" + std::string(android_root));
176 argv.insert(argv.end(), extra_args.begin(), extra_args.end());
177
178 return RunDex2Oat(argv, error_msg);
179 }
180
RunDex2Oat(const std::vector<std::string> & args,std::string * error_msg)181 int RunDex2Oat(const std::vector<std::string>& args, std::string* error_msg) {
182 int link[2];
183
184 if (pipe(link) == -1) {
185 return false;
186 }
187
188 pid_t pid = fork();
189 if (pid == -1) {
190 return false;
191 }
192
193 if (pid == 0) {
194 // We need dex2oat to actually log things.
195 setenv("ANDROID_LOG_TAGS", "*:f", 1);
196 dup2(link[1], STDERR_FILENO);
197 close(link[0]);
198 close(link[1]);
199 std::vector<const char*> c_args;
200 for (const std::string& str : args) {
201 c_args.push_back(str.c_str());
202 }
203 c_args.push_back(nullptr);
204 execv(c_args[0], const_cast<char* const*>(c_args.data()));
205 exit(1);
206 UNREACHABLE();
207 } else {
208 close(link[1]);
209 char buffer[128];
210 memset(buffer, 0, 128);
211 ssize_t bytes_read = 0;
212
213 while (TEMP_FAILURE_RETRY(bytes_read = read(link[0], buffer, 128)) > 0) {
214 *error_msg += std::string(buffer, bytes_read);
215 }
216 close(link[0]);
217 int status = -1;
218 if (waitpid(pid, &status, 0) != -1) {
219 return (status == 0);
220 }
221 return false;
222 }
223 }
224 };
225
TEST_F(Dex2oatImageTest,TestModesAndFilters)226 TEST_F(Dex2oatImageTest, TestModesAndFilters) {
227 if (kIsTargetBuild) {
228 // This test is too slow for target builds.
229 return;
230 }
231 ImageSizes base_sizes = CompileImageAndGetSizes({});
232 ImageSizes image_classes_sizes;
233 ImageSizes compiled_classes_sizes;
234 ImageSizes compiled_all_classes_sizes;
235 ImageSizes compiled_methods_sizes;
236 ImageSizes compiled_all_methods_sizes;
237 ImageSizes profile_sizes;
238 std::cout << "Base compile sizes " << base_sizes << std::endl;
239 // Test image classes
240 {
241 ScratchFile classes;
242 GenerateClasses(classes.GetFile(), /*frequency*/ 1u);
243 image_classes_sizes = CompileImageAndGetSizes(
244 {"--image-classes=" + classes.GetFilename()});
245 classes.Close();
246 std::cout << "Image classes sizes " << image_classes_sizes << std::endl;
247 // Putting all classes as image classes should increase art size
248 EXPECT_GE(image_classes_sizes.art_size, base_sizes.art_size);
249 // Sanity check that dex is the same size.
250 EXPECT_EQ(image_classes_sizes.vdex_size, base_sizes.vdex_size);
251 }
252 // Test compiled classes with all the classes.
253 {
254 ScratchFile classes;
255 // Only compile every even class.
256 GenerateClasses(classes.GetFile(), /*frequency*/ 1u);
257 compiled_all_classes_sizes = CompileImageAndGetSizes(
258 {"--compiled-classes=" + classes.GetFilename()});
259 classes.Close();
260 std::cout << "Compiled all classes sizes " << compiled_all_classes_sizes << std::endl;
261 // Check that oat size is smaller since we didn't compile everything.
262 EXPECT_EQ(compiled_all_classes_sizes.art_size, base_sizes.art_size);
263 // TODO(mathieuc): Find a reliable way to check compiled code.
264 // EXPECT_EQ(compiled_all_classes_sizes.oat_size, base_sizes.oat_size);
265 EXPECT_EQ(compiled_all_classes_sizes.vdex_size, base_sizes.vdex_size);
266 }
267 // Test compiled classes.
268 {
269 ScratchFile classes;
270 // Only compile every even class.
271 GenerateClasses(classes.GetFile(), /*frequency*/ 2u);
272 compiled_classes_sizes = CompileImageAndGetSizes(
273 {"--image-classes=" + classes.GetFilename(),
274 "--compiled-classes=" + classes.GetFilename()});
275 classes.Close();
276 std::cout << "Compiled classes sizes " << compiled_classes_sizes << std::endl;
277 // Check that oat size is smaller since we didn't compile everything.
278 // TODO(mathieuc): Find a reliable way to check compiled code.
279 // EXPECT_LT(compiled_classes_sizes.oat_size, base_sizes.oat_size);
280 // Art file should be smaller than image classes version since we included fewer classes in the
281 // list.
282 EXPECT_LT(compiled_classes_sizes.art_size, image_classes_sizes.art_size);
283 }
284 // Test compiled methods.
285 {
286 ScratchFile methods;
287 // Only compile every even class.
288 GenerateMethods(methods.GetFile(), /*frequency*/ 1u);
289 compiled_all_methods_sizes = CompileImageAndGetSizes(
290 {"--compiled-methods=" + methods.GetFilename()});
291 methods.Close();
292 std::cout << "Compiled all methods sizes " << compiled_all_methods_sizes << std::endl;
293 EXPECT_EQ(compiled_all_classes_sizes.art_size, base_sizes.art_size);
294 // TODO(mathieuc): Find a reliable way to check compiled code. b/63746626
295 // EXPECT_EQ(compiled_all_classes_sizes.oat_size, base_sizes.oat_size);
296 EXPECT_EQ(compiled_all_classes_sizes.vdex_size, base_sizes.vdex_size);
297 }
298 static size_t kMethodFrequency = 3;
299 static size_t kTypeFrequency = 4;
300 // Test compiling fewer methods and classes.
301 {
302 ScratchFile methods;
303 ScratchFile classes;
304 // Only compile every even class.
305 GenerateMethods(methods.GetFile(), kMethodFrequency);
306 GenerateClasses(classes.GetFile(), kTypeFrequency);
307 compiled_methods_sizes = CompileImageAndGetSizes(
308 {"--image-classes=" + classes.GetFilename(),
309 "--compiled-methods=" + methods.GetFilename()});
310 methods.Close();
311 classes.Close();
312 std::cout << "Compiled fewer methods sizes " << compiled_methods_sizes << std::endl;
313 }
314 // Cross verify profile based image against image-classes and compiled-methods to make sure it
315 // matches.
316 {
317 ProfileCompilationInfo profile;
318 VisitLibcoreDexes([&profile](MethodReference ref) {
319 EXPECT_TRUE(profile.AddMethodIndex(ProfileCompilationInfo::MethodHotness::kFlagHot, ref));
320 }, [&profile](TypeReference ref) {
321 EXPECT_TRUE(profile.AddClassesForDex(ref.dex_file, &ref.type_index, &ref.type_index + 1));
322 }, kMethodFrequency, kTypeFrequency);
323 ScratchFile profile_file;
324 profile.Save(profile_file.GetFile()->Fd());
325 EXPECT_EQ(profile_file.GetFile()->Flush(), 0);
326 profile_sizes = CompileImageAndGetSizes(
327 {"--profile-file=" + profile_file.GetFilename(),
328 "--compiler-filter=speed-profile"});
329 profile_file.Close();
330 std::cout << "Profile sizes " << profile_sizes << std::endl;
331 // Since there is some difference between profile vs image + methods due to layout, check that
332 // the range is within expected margins (+-5%).
333 const double kRatio = 0.95;
334 EXPECT_LE(profile_sizes.art_size * kRatio, compiled_methods_sizes.art_size);
335 // TODO(mathieuc): Find a reliable way to check compiled code. b/63746626
336 // EXPECT_LE(profile_sizes.oat_size * kRatio, compiled_methods_sizes.oat_size);
337 EXPECT_LE(profile_sizes.vdex_size * kRatio, compiled_methods_sizes.vdex_size);
338 EXPECT_GE(profile_sizes.art_size / kRatio, compiled_methods_sizes.art_size);
339 // TODO(mathieuc): Find a reliable way to check compiled code. b/63746626
340 // EXPECT_GE(profile_sizes.oat_size / kRatio, compiled_methods_sizes.oat_size);
341 EXPECT_GE(profile_sizes.vdex_size / kRatio, compiled_methods_sizes.vdex_size);
342 }
343 // Test dirty image objects.
344 {
345 ScratchFile classes;
346 GenerateClasses(classes.GetFile(), /*frequency*/ 1u);
347 image_classes_sizes = CompileImageAndGetSizes(
348 {"--dirty-image-objects=" + classes.GetFilename()});
349 classes.Close();
350 std::cout << "Dirty image object sizes " << image_classes_sizes << std::endl;
351 }
352 }
353
354 } // namespace art
355