blob: c78d34ebb300284d75ff98fdf7c4dcc44224b714 [file] [log] [blame]
Calin Juravle2e2db782016-02-23 12:00:03 +00001/*
2 * Copyright (C) 2016 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
Calin Juravlee0ac1152017-02-13 19:03:47 -080019#include "art_method-inl.h"
Calin Juravle2e2db782016-02-23 12:00:03 +000020#include "base/unix_file/fd_file.h"
21#include "common_runtime_test.h"
David Sehr97c381e2017-02-01 15:09:58 -080022#include "exec_utils.h"
Calin Juravle33083d62017-01-18 15:29:12 -080023#include "jit/profile_compilation_info.h"
Calin Juravlecc3171a2017-05-19 16:47:53 -070024#include "linear_alloc.h"
Calin Juravlee0ac1152017-02-13 19:03:47 -080025#include "mirror/class-inl.h"
Mathieu Chartierd808e8b2017-03-21 13:37:41 -070026#include "obj_ptr-inl.h"
Calin Juravlee0ac1152017-02-13 19:03:47 -080027#include "profile_assistant.h"
28#include "scoped_thread_state_change-inl.h"
Calin Juravle2e2db782016-02-23 12:00:03 +000029#include "utils.h"
30
31namespace art {
32
Mathieu Chartierea650f32017-05-24 12:04:13 -070033static constexpr size_t kMaxMethodIds = 65535;
34
Calin Juravle2e2db782016-02-23 12:00:03 +000035class ProfileAssistantTest : public CommonRuntimeTest {
Calin Juravlecc3171a2017-05-19 16:47:53 -070036 public:
Calin Juravlee6f87cc2017-05-24 17:41:05 -070037 void PostRuntimeCreate() OVERRIDE {
Calin Juravlecc3171a2017-05-19 16:47:53 -070038 arena_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
39 }
40
Calin Juravle2e2db782016-02-23 12:00:03 +000041 protected:
42 void SetupProfile(const std::string& id,
43 uint32_t checksum,
44 uint16_t number_of_methods,
Calin Juravlec824b512016-03-29 20:33:33 +010045 uint16_t number_of_classes,
Calin Juravle2e2db782016-02-23 12:00:03 +000046 const ScratchFile& profile,
47 ProfileCompilationInfo* info,
Calin Juravlecea9e9d2017-03-23 19:04:59 -070048 uint16_t start_method_index = 0,
49 bool reverse_dex_write_order = false) {
Calin Juravle2e2db782016-02-23 12:00:03 +000050 std::string dex_location1 = "location1" + id;
51 uint32_t dex_location_checksum1 = checksum;
52 std::string dex_location2 = "location2" + id;
53 uint32_t dex_location_checksum2 = 10 * checksum;
54 for (uint16_t i = start_method_index; i < start_method_index + number_of_methods; i++) {
Calin Juravlecea9e9d2017-03-23 19:04:59 -070055 // reverse_dex_write_order controls the order in which the dex files will be added to
56 // the profile and thus written to disk.
57 ProfileCompilationInfo::OfflineProfileMethodInfo pmi =
58 GetOfflineProfileMethodInfo(dex_location1, dex_location_checksum1,
59 dex_location2, dex_location_checksum2);
60 if (reverse_dex_write_order) {
Mathieu Chartierea650f32017-05-24 12:04:13 -070061 ASSERT_TRUE(info->AddMethod(dex_location2, dex_location_checksum2, i, kMaxMethodIds, pmi));
62 ASSERT_TRUE(info->AddMethod(dex_location1, dex_location_checksum1, i, kMaxMethodIds, pmi));
Calin Juravlecea9e9d2017-03-23 19:04:59 -070063 } else {
Mathieu Chartierea650f32017-05-24 12:04:13 -070064 ASSERT_TRUE(info->AddMethod(dex_location1, dex_location_checksum1, i, kMaxMethodIds, pmi));
65 ASSERT_TRUE(info->AddMethod(dex_location2, dex_location_checksum2, i, kMaxMethodIds, pmi));
Calin Juravlecea9e9d2017-03-23 19:04:59 -070066 }
Calin Juravle2e2db782016-02-23 12:00:03 +000067 }
Calin Juravlec824b512016-03-29 20:33:33 +010068 for (uint16_t i = 0; i < number_of_classes; i++) {
Mathieu Chartierea650f32017-05-24 12:04:13 -070069 ASSERT_TRUE(info->AddClassIndex(dex_location1,
70 dex_location_checksum1,
71 dex::TypeIndex(i),
72 kMaxMethodIds));
Calin Juravlec824b512016-03-29 20:33:33 +010073 }
74
Calin Juravle2e2db782016-02-23 12:00:03 +000075 ASSERT_TRUE(info->Save(GetFd(profile)));
76 ASSERT_EQ(0, profile.GetFile()->Flush());
77 ASSERT_TRUE(profile.GetFile()->ResetOffset());
78 }
79
Mathieu Chartier28b5c582017-06-06 14:12:50 -070080 void SetupBasicProfile(const std::string& id,
81 uint32_t checksum,
82 uint16_t number_of_methods,
83 const std::vector<uint32_t> hot_methods,
84 const std::vector<uint32_t> startup_methods,
85 const std::vector<uint32_t> post_startup_methods,
86 const ScratchFile& profile,
87 ProfileCompilationInfo* info) {
88 std::string dex_location = "location1" + id;
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -070089 using Hotness = ProfileCompilationInfo::MethodHotness;
Mathieu Chartier28b5c582017-06-06 14:12:50 -070090 for (uint32_t idx : hot_methods) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -070091 info->AddMethodIndex(Hotness::kFlagHot, dex_location, checksum, idx, number_of_methods);
Mathieu Chartier28b5c582017-06-06 14:12:50 -070092 }
93 for (uint32_t idx : startup_methods) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -070094 info->AddMethodIndex(Hotness::kFlagStartup, dex_location, checksum, idx, number_of_methods);
Mathieu Chartier28b5c582017-06-06 14:12:50 -070095 }
96 for (uint32_t idx : post_startup_methods) {
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -070097 info->AddMethodIndex(Hotness::kFlagPostStartup,
98 dex_location,
99 checksum,
100 idx,
101 number_of_methods);
Mathieu Chartier28b5c582017-06-06 14:12:50 -0700102 }
103 ASSERT_TRUE(info->Save(GetFd(profile)));
104 ASSERT_EQ(0, profile.GetFile()->Flush());
105 ASSERT_TRUE(profile.GetFile()->ResetOffset());
106 }
107
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700108 // Creates an inline cache which will be destructed at the end of the test.
109 ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() {
110 used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap(
111 std::less<uint16_t>(), arena_->Adapter(kArenaAllocProfile)));
112 return used_inline_caches.back().get();
113 }
114
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700115 ProfileCompilationInfo::OfflineProfileMethodInfo GetOfflineProfileMethodInfo(
116 const std::string& dex_location1, uint32_t dex_checksum1,
117 const std::string& dex_location2, uint32_t dex_checksum2) {
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700118 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
119 ProfileCompilationInfo::OfflineProfileMethodInfo pmi(ic_map);
Mathieu Chartierea650f32017-05-24 12:04:13 -0700120 pmi.dex_references.emplace_back(dex_location1, dex_checksum1, kMaxMethodIds);
121 pmi.dex_references.emplace_back(dex_location2, dex_checksum2, kMaxMethodIds);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700122
123 // Monomorphic
124 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700125 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700126 dex_pc_data.AddClass(0, dex::TypeIndex(0));
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700127 ic_map->Put(dex_pc, dex_pc_data);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700128 }
129 // Polymorphic
130 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700131 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700132 dex_pc_data.AddClass(0, dex::TypeIndex(0));
133 dex_pc_data.AddClass(1, dex::TypeIndex(1));
134
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700135 ic_map->Put(dex_pc, dex_pc_data);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700136 }
137 // Megamorphic
138 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700139 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700140 dex_pc_data.SetIsMegamorphic();
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700141 ic_map->Put(dex_pc, dex_pc_data);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700142 }
143 // Missing types
144 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700145 ProfileCompilationInfo::DexPcData dex_pc_data(arena_.get());
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700146 dex_pc_data.SetIsMissingTypes();
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700147 ic_map->Put(dex_pc, dex_pc_data);
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700148 }
149
150 return pmi;
151 }
152
Calin Juravle2e2db782016-02-23 12:00:03 +0000153 int GetFd(const ScratchFile& file) const {
154 return static_cast<int>(file.GetFd());
155 }
156
157 void CheckProfileInfo(ScratchFile& file, const ProfileCompilationInfo& info) {
158 ProfileCompilationInfo file_info;
159 ASSERT_TRUE(file.GetFile()->ResetOffset());
160 ASSERT_TRUE(file_info.Load(GetFd(file)));
161 ASSERT_TRUE(file_info.Equals(info));
162 }
163
Calin Juravle7bcdb532016-06-07 16:14:47 +0100164 std::string GetProfmanCmd() {
Calin Juravle2e2db782016-02-23 12:00:03 +0000165 std::string file_path = GetTestAndroidRoot();
Calin Juravlede4fb632016-02-23 16:53:30 +0000166 file_path += "/bin/profman";
Calin Juravle2e2db782016-02-23 12:00:03 +0000167 if (kIsDebugBuild) {
168 file_path += "d";
169 }
Calin Juravle7bcdb532016-06-07 16:14:47 +0100170 EXPECT_TRUE(OS::FileExists(file_path.c_str()))
171 << file_path << " should be a valid file path";
172 return file_path;
173 }
Mathieu Chartierf70fe3d2017-06-21 15:24:02 -0700174
Calin Juravle7bcdb532016-06-07 16:14:47 +0100175 // Runs test with given arguments.
176 int ProcessProfiles(const std::vector<int>& profiles_fd, int reference_profile_fd) {
177 std::string profman_cmd = GetProfmanCmd();
Calin Juravle2e2db782016-02-23 12:00:03 +0000178 std::vector<std::string> argv_str;
Calin Juravle7bcdb532016-06-07 16:14:47 +0100179 argv_str.push_back(profman_cmd);
Calin Juravle2e2db782016-02-23 12:00:03 +0000180 for (size_t k = 0; k < profiles_fd.size(); k++) {
181 argv_str.push_back("--profile-file-fd=" + std::to_string(profiles_fd[k]));
182 }
183 argv_str.push_back("--reference-profile-file-fd=" + std::to_string(reference_profile_fd));
184
185 std::string error;
186 return ExecAndReturnCode(argv_str, &error);
187 }
Calin Juravle7bcdb532016-06-07 16:14:47 +0100188
189 bool GenerateTestProfile(const std::string& filename) {
190 std::string profman_cmd = GetProfmanCmd();
191 std::vector<std::string> argv_str;
192 argv_str.push_back(profman_cmd);
193 argv_str.push_back("--generate-test-profile=" + filename);
194 std::string error;
195 return ExecAndReturnCode(argv_str, &error);
196 }
David Sehr7c80f2d2017-02-07 16:47:58 -0800197
Jeff Haof0a31f82017-03-27 15:50:37 -0700198 bool GenerateTestProfileWithInputDex(const std::string& filename) {
199 std::string profman_cmd = GetProfmanCmd();
200 std::vector<std::string> argv_str;
201 argv_str.push_back(profman_cmd);
202 argv_str.push_back("--generate-test-profile=" + filename);
203 argv_str.push_back("--generate-test-profile-seed=0");
204 argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
205 argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]);
206 std::string error;
207 return ExecAndReturnCode(argv_str, &error);
208 }
209
Calin Juravlee0ac1152017-02-13 19:03:47 -0800210 bool CreateProfile(std::string profile_file_contents,
211 const std::string& filename,
212 const std::string& dex_location) {
David Sehr7c80f2d2017-02-07 16:47:58 -0800213 ScratchFile class_names_file;
214 File* file = class_names_file.GetFile();
Calin Juravlee0ac1152017-02-13 19:03:47 -0800215 EXPECT_TRUE(file->WriteFully(profile_file_contents.c_str(), profile_file_contents.length()));
David Sehr7c80f2d2017-02-07 16:47:58 -0800216 EXPECT_EQ(0, file->Flush());
217 EXPECT_TRUE(file->ResetOffset());
218 std::string profman_cmd = GetProfmanCmd();
219 std::vector<std::string> argv_str;
220 argv_str.push_back(profman_cmd);
221 argv_str.push_back("--create-profile-from=" + class_names_file.GetFilename());
222 argv_str.push_back("--reference-profile-file=" + filename);
Calin Juravlee0ac1152017-02-13 19:03:47 -0800223 argv_str.push_back("--apk=" + dex_location);
224 argv_str.push_back("--dex-location=" + dex_location);
David Sehr7c80f2d2017-02-07 16:47:58 -0800225 std::string error;
226 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
227 return true;
228 }
229
Mathieu Chartier28b5c582017-06-06 14:12:50 -0700230 bool RunProfman(const std::string& filename,
231 std::vector<std::string>& extra_args,
232 std::string* output) {
233 ScratchFile output_file;
David Sehr7c80f2d2017-02-07 16:47:58 -0800234 std::string profman_cmd = GetProfmanCmd();
235 std::vector<std::string> argv_str;
236 argv_str.push_back(profman_cmd);
Mathieu Chartier28b5c582017-06-06 14:12:50 -0700237 argv_str.insert(argv_str.end(), extra_args.begin(), extra_args.end());
David Sehr7c80f2d2017-02-07 16:47:58 -0800238 argv_str.push_back("--profile-file=" + filename);
239 argv_str.push_back("--apk=" + GetLibCoreDexFileNames()[0]);
Calin Juravlee0ac1152017-02-13 19:03:47 -0800240 argv_str.push_back("--dex-location=" + GetLibCoreDexFileNames()[0]);
Mathieu Chartier28b5c582017-06-06 14:12:50 -0700241 argv_str.push_back("--dump-output-to-fd=" + std::to_string(GetFd(output_file)));
David Sehr7c80f2d2017-02-07 16:47:58 -0800242 std::string error;
243 EXPECT_EQ(ExecAndReturnCode(argv_str, &error), 0);
Mathieu Chartier28b5c582017-06-06 14:12:50 -0700244 File* file = output_file.GetFile();
David Sehr7c80f2d2017-02-07 16:47:58 -0800245 EXPECT_EQ(0, file->Flush());
246 EXPECT_TRUE(file->ResetOffset());
247 int64_t length = file->GetLength();
248 std::unique_ptr<char[]> buf(new char[length]);
249 EXPECT_EQ(file->Read(buf.get(), length, 0), length);
Mathieu Chartier28b5c582017-06-06 14:12:50 -0700250 *output = std::string(buf.get(), length);
David Sehr7c80f2d2017-02-07 16:47:58 -0800251 return true;
252 }
253
Mathieu Chartier28b5c582017-06-06 14:12:50 -0700254 bool DumpClassesAndMethods(const std::string& filename, std::string* file_contents) {
255 std::vector<std::string> extra_args;
256 extra_args.push_back("--dump-classes-and-methods");
257 return RunProfman(filename, extra_args, file_contents);
258 }
259
260 bool DumpOnly(const std::string& filename, std::string* file_contents) {
261 std::vector<std::string> extra_args;
262 extra_args.push_back("--dump-only");
263 return RunProfman(filename, extra_args, file_contents);
264 }
265
Mathieu Chartierd808e8b2017-03-21 13:37:41 -0700266 bool CreateAndDump(const std::string& input_file_contents,
267 std::string* output_file_contents) {
David Sehr7c80f2d2017-02-07 16:47:58 -0800268 ScratchFile profile_file;
Calin Juravlee0ac1152017-02-13 19:03:47 -0800269 EXPECT_TRUE(CreateProfile(input_file_contents,
270 profile_file.GetFilename(),
271 GetLibCoreDexFileNames()[0]));
David Sehr7c80f2d2017-02-07 16:47:58 -0800272 profile_file.GetFile()->ResetOffset();
Mathieu Chartier34067262017-04-06 13:55:46 -0700273 EXPECT_TRUE(DumpClassesAndMethods(profile_file.GetFilename(), output_file_contents));
David Sehr7c80f2d2017-02-07 16:47:58 -0800274 return true;
275 }
Calin Juravlee0ac1152017-02-13 19:03:47 -0800276
277 mirror::Class* GetClass(jobject class_loader, const std::string& clazz) {
278 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
279 Thread* self = Thread::Current();
280 ScopedObjectAccess soa(self);
281 StackHandleScope<1> hs(self);
282 Handle<mirror::ClassLoader> h_loader(
Mathieu Chartierd808e8b2017-03-21 13:37:41 -0700283 hs.NewHandle(ObjPtr<mirror::ClassLoader>::DownCast(self->DecodeJObject(class_loader))));
Calin Juravlee0ac1152017-02-13 19:03:47 -0800284 return class_linker->FindClass(self, clazz.c_str(), h_loader);
285 }
286
287 ArtMethod* GetVirtualMethod(jobject class_loader,
288 const std::string& clazz,
289 const std::string& name) {
290 mirror::Class* klass = GetClass(class_loader, clazz);
291 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
292 const auto pointer_size = class_linker->GetImagePointerSize();
293 ArtMethod* method = nullptr;
294 Thread* self = Thread::Current();
295 ScopedObjectAccess soa(self);
296 for (auto& m : klass->GetVirtualMethods(pointer_size)) {
297 if (name == m.GetName()) {
298 EXPECT_TRUE(method == nullptr);
299 method = &m;
300 }
301 }
302 return method;
303 }
304
305 // Verify that given method has the expected inline caches and nothing else.
306 void AssertInlineCaches(ArtMethod* method,
307 const std::set<mirror::Class*>& expected_clases,
308 const ProfileCompilationInfo& info,
Calin Juravle589e71e2017-03-03 16:05:05 -0800309 bool is_megamorphic,
310 bool is_missing_types)
Calin Juravlee0ac1152017-02-13 19:03:47 -0800311 REQUIRES_SHARED(Locks::mutator_lock_) {
Calin Juravlecc3171a2017-05-19 16:47:53 -0700312 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi =
313 info.GetMethod(method->GetDexFile()->GetLocation(),
314 method->GetDexFile()->GetLocationChecksum(),
315 method->GetDexMethodIndex());
316 ASSERT_TRUE(pmi != nullptr);
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700317 ASSERT_EQ(pmi->inline_caches->size(), 1u);
318 const ProfileCompilationInfo::DexPcData& dex_pc_data = pmi->inline_caches->begin()->second;
Calin Juravlee0ac1152017-02-13 19:03:47 -0800319
Calin Juravle589e71e2017-03-03 16:05:05 -0800320 ASSERT_EQ(dex_pc_data.is_megamorphic, is_megamorphic);
321 ASSERT_EQ(dex_pc_data.is_missing_types, is_missing_types);
Calin Juravlee0ac1152017-02-13 19:03:47 -0800322 ASSERT_EQ(expected_clases.size(), dex_pc_data.classes.size());
323 size_t found = 0;
324 for (mirror::Class* it : expected_clases) {
325 for (const auto& class_ref : dex_pc_data.classes) {
326 ProfileCompilationInfo::DexReference dex_ref =
Calin Juravlecc3171a2017-05-19 16:47:53 -0700327 pmi->dex_references[class_ref.dex_profile_index];
Calin Juravlee0ac1152017-02-13 19:03:47 -0800328 if (dex_ref.MatchesDex(&(it->GetDexFile())) &&
329 class_ref.type_index == it->GetDexTypeIndex()) {
330 found++;
331 }
332 }
333 }
334
335 ASSERT_EQ(expected_clases.size(), found);
336 }
Calin Juravlecc3171a2017-05-19 16:47:53 -0700337
338 std::unique_ptr<ArenaAllocator> arena_;
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700339
340 // Cache of inline caches generated during tests.
341 // This makes it easier to pass data between different utilities and ensure that
342 // caches are destructed at the end of the test.
343 std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
Calin Juravle2e2db782016-02-23 12:00:03 +0000344};
345
346TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferences) {
347 ScratchFile profile1;
348 ScratchFile profile2;
349 ScratchFile reference_profile;
350
351 std::vector<int> profile_fds({
352 GetFd(profile1),
353 GetFd(profile2)});
354 int reference_profile_fd = GetFd(reference_profile);
355
356 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
357 ProfileCompilationInfo info1;
Calin Juravlec824b512016-03-29 20:33:33 +0100358 SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
Calin Juravle2e2db782016-02-23 12:00:03 +0000359 ProfileCompilationInfo info2;
Calin Juravlec824b512016-03-29 20:33:33 +0100360 SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
Calin Juravle2e2db782016-02-23 12:00:03 +0000361
362 // We should advise compilation.
363 ASSERT_EQ(ProfileAssistant::kCompile,
364 ProcessProfiles(profile_fds, reference_profile_fd));
365 // The resulting compilation info must be equal to the merge of the inputs.
366 ProfileCompilationInfo result;
367 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
368 ASSERT_TRUE(result.Load(reference_profile_fd));
369
370 ProfileCompilationInfo expected;
Calin Juravle67265462016-03-18 16:23:40 +0000371 ASSERT_TRUE(expected.MergeWith(info1));
372 ASSERT_TRUE(expected.MergeWith(info2));
Calin Juravle2e2db782016-02-23 12:00:03 +0000373 ASSERT_TRUE(expected.Equals(result));
374
375 // The information from profiles must remain the same.
376 CheckProfileInfo(profile1, info1);
377 CheckProfileInfo(profile2, info2);
378}
379
Calin Juravlec824b512016-03-29 20:33:33 +0100380// TODO(calin): Add more tests for classes.
381TEST_F(ProfileAssistantTest, AdviseCompilationEmptyReferencesBecauseOfClasses) {
382 ScratchFile profile1;
383 ScratchFile reference_profile;
384
385 std::vector<int> profile_fds({
386 GetFd(profile1)});
387 int reference_profile_fd = GetFd(reference_profile);
388
389 const uint16_t kNumberOfClassesToEnableCompilation = 100;
390 ProfileCompilationInfo info1;
391 SetupProfile("p1", 1, 0, kNumberOfClassesToEnableCompilation, profile1, &info1);
392
393 // We should advise compilation.
394 ASSERT_EQ(ProfileAssistant::kCompile,
395 ProcessProfiles(profile_fds, reference_profile_fd));
396 // The resulting compilation info must be equal to the merge of the inputs.
397 ProfileCompilationInfo result;
398 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
399 ASSERT_TRUE(result.Load(reference_profile_fd));
400
401 ProfileCompilationInfo expected;
402 ASSERT_TRUE(expected.MergeWith(info1));
403 ASSERT_TRUE(expected.Equals(result));
404
405 // The information from profiles must remain the same.
406 CheckProfileInfo(profile1, info1);
407}
408
Calin Juravle2e2db782016-02-23 12:00:03 +0000409TEST_F(ProfileAssistantTest, AdviseCompilationNonEmptyReferences) {
410 ScratchFile profile1;
411 ScratchFile profile2;
412 ScratchFile reference_profile;
413
414 std::vector<int> profile_fds({
415 GetFd(profile1),
416 GetFd(profile2)});
417 int reference_profile_fd = GetFd(reference_profile);
418
419 // The new profile info will contain the methods with indices 0-100.
420 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
421 ProfileCompilationInfo info1;
Calin Juravlec824b512016-03-29 20:33:33 +0100422 SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
Calin Juravle2e2db782016-02-23 12:00:03 +0000423 ProfileCompilationInfo info2;
Calin Juravlec824b512016-03-29 20:33:33 +0100424 SetupProfile("p2", 2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
Calin Juravle2e2db782016-02-23 12:00:03 +0000425
426
427 // The reference profile info will contain the methods with indices 50-150.
428 const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
429 ProfileCompilationInfo reference_info;
Calin Juravlec824b512016-03-29 20:33:33 +0100430 SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
Calin Juravle2e2db782016-02-23 12:00:03 +0000431 &reference_info, kNumberOfMethodsToEnableCompilation / 2);
432
433 // We should advise compilation.
434 ASSERT_EQ(ProfileAssistant::kCompile,
435 ProcessProfiles(profile_fds, reference_profile_fd));
436
437 // The resulting compilation info must be equal to the merge of the inputs
438 ProfileCompilationInfo result;
439 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
440 ASSERT_TRUE(result.Load(reference_profile_fd));
441
442 ProfileCompilationInfo expected;
Calin Juravle67265462016-03-18 16:23:40 +0000443 ASSERT_TRUE(expected.MergeWith(info1));
444 ASSERT_TRUE(expected.MergeWith(info2));
445 ASSERT_TRUE(expected.MergeWith(reference_info));
Calin Juravle2e2db782016-02-23 12:00:03 +0000446 ASSERT_TRUE(expected.Equals(result));
447
448 // The information from profiles must remain the same.
449 CheckProfileInfo(profile1, info1);
450 CheckProfileInfo(profile2, info2);
451}
452
453TEST_F(ProfileAssistantTest, DoNotAdviseCompilation) {
454 ScratchFile profile1;
455 ScratchFile profile2;
456 ScratchFile reference_profile;
457
458 std::vector<int> profile_fds({
459 GetFd(profile1),
460 GetFd(profile2)});
461 int reference_profile_fd = GetFd(reference_profile);
462
463 const uint16_t kNumberOfMethodsToSkipCompilation = 1;
464 ProfileCompilationInfo info1;
Calin Juravlec824b512016-03-29 20:33:33 +0100465 SetupProfile("p1", 1, kNumberOfMethodsToSkipCompilation, 0, profile1, &info1);
Calin Juravle2e2db782016-02-23 12:00:03 +0000466 ProfileCompilationInfo info2;
Calin Juravlec824b512016-03-29 20:33:33 +0100467 SetupProfile("p2", 2, kNumberOfMethodsToSkipCompilation, 0, profile2, &info2);
Calin Juravle2e2db782016-02-23 12:00:03 +0000468
469 // We should not advise compilation.
470 ASSERT_EQ(ProfileAssistant::kSkipCompilation,
471 ProcessProfiles(profile_fds, reference_profile_fd));
472
473 // The information from profiles must remain the same.
474 ProfileCompilationInfo file_info1;
475 ASSERT_TRUE(profile1.GetFile()->ResetOffset());
476 ASSERT_TRUE(file_info1.Load(GetFd(profile1)));
477 ASSERT_TRUE(file_info1.Equals(info1));
478
479 ProfileCompilationInfo file_info2;
480 ASSERT_TRUE(profile2.GetFile()->ResetOffset());
481 ASSERT_TRUE(file_info2.Load(GetFd(profile2)));
482 ASSERT_TRUE(file_info2.Equals(info2));
483
484 // Reference profile files must remain empty.
485 ASSERT_EQ(0, reference_profile.GetFile()->GetLength());
486
487 // The information from profiles must remain the same.
488 CheckProfileInfo(profile1, info1);
489 CheckProfileInfo(profile2, info2);
490}
491
492TEST_F(ProfileAssistantTest, FailProcessingBecauseOfProfiles) {
493 ScratchFile profile1;
494 ScratchFile profile2;
495 ScratchFile reference_profile;
496
497 std::vector<int> profile_fds({
498 GetFd(profile1),
499 GetFd(profile2)});
500 int reference_profile_fd = GetFd(reference_profile);
501
502 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
503 // Assign different hashes for the same dex file. This will make merging of information to fail.
504 ProfileCompilationInfo info1;
Calin Juravlec824b512016-03-29 20:33:33 +0100505 SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
Calin Juravle2e2db782016-02-23 12:00:03 +0000506 ProfileCompilationInfo info2;
Calin Juravlec824b512016-03-29 20:33:33 +0100507 SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, 0, profile2, &info2);
Calin Juravle2e2db782016-02-23 12:00:03 +0000508
509 // We should fail processing.
510 ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
511 ProcessProfiles(profile_fds, reference_profile_fd));
512
513 // The information from profiles must remain the same.
514 CheckProfileInfo(profile1, info1);
515 CheckProfileInfo(profile2, info2);
516
517 // Reference profile files must still remain empty.
518 ASSERT_EQ(0, reference_profile.GetFile()->GetLength());
519}
520
521TEST_F(ProfileAssistantTest, FailProcessingBecauseOfReferenceProfiles) {
522 ScratchFile profile1;
523 ScratchFile reference_profile;
524
525 std::vector<int> profile_fds({
526 GetFd(profile1)});
527 int reference_profile_fd = GetFd(reference_profile);
528
529 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
530 // Assign different hashes for the same dex file. This will make merging of information to fail.
531 ProfileCompilationInfo info1;
Calin Juravlec824b512016-03-29 20:33:33 +0100532 SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1);
Calin Juravle2e2db782016-02-23 12:00:03 +0000533 ProfileCompilationInfo reference_info;
Calin Juravlec824b512016-03-29 20:33:33 +0100534 SetupProfile("p1", 2, kNumberOfMethodsToEnableCompilation, 0, reference_profile, &reference_info);
Calin Juravle2e2db782016-02-23 12:00:03 +0000535
536 // We should not advise compilation.
537 ASSERT_TRUE(profile1.GetFile()->ResetOffset());
538 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
539 ASSERT_EQ(ProfileAssistant::kErrorBadProfiles,
540 ProcessProfiles(profile_fds, reference_profile_fd));
541
542 // The information from profiles must remain the same.
543 CheckProfileInfo(profile1, info1);
544}
545
Calin Juravle7bcdb532016-06-07 16:14:47 +0100546TEST_F(ProfileAssistantTest, TestProfileGeneration) {
547 ScratchFile profile;
548 // Generate a test profile.
549 GenerateTestProfile(profile.GetFilename());
550
551 // Verify that the generated profile is valid and can be loaded.
552 ASSERT_TRUE(profile.GetFile()->ResetOffset());
553 ProfileCompilationInfo info;
554 ASSERT_TRUE(info.Load(GetFd(profile)));
555}
556
Jeff Haof0a31f82017-03-27 15:50:37 -0700557TEST_F(ProfileAssistantTest, TestProfileGenerationWithIndexDex) {
558 ScratchFile profile;
559 // Generate a test profile passing in a dex file as reference.
560 GenerateTestProfileWithInputDex(profile.GetFilename());
561
562 // Verify that the generated profile is valid and can be loaded.
563 ASSERT_TRUE(profile.GetFile()->ResetOffset());
564 ProfileCompilationInfo info;
565 ASSERT_TRUE(info.Load(GetFd(profile)));
566}
567
David Sehr7c80f2d2017-02-07 16:47:58 -0800568TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) {
569 // Class names put here need to be in sorted order.
570 std::vector<std::string> class_names = {
Mathieu Chartierea650f32017-05-24 12:04:13 -0700571 "HLjava/lang/Object;-><init>()V",
Calin Juravlee0ac1152017-02-13 19:03:47 -0800572 "Ljava/lang/Comparable;",
573 "Ljava/lang/Math;",
Mathieu Chartier34067262017-04-06 13:55:46 -0700574 "Ljava/lang/Object;",
Mathieu Chartierea650f32017-05-24 12:04:13 -0700575 "SPLjava/lang/Comparable;->compareTo(Ljava/lang/Object;)I",
David Sehr7c80f2d2017-02-07 16:47:58 -0800576 };
Mathieu Chartier34067262017-04-06 13:55:46 -0700577 std::string file_contents;
David Sehr7c80f2d2017-02-07 16:47:58 -0800578 for (std::string& class_name : class_names) {
Mathieu Chartier34067262017-04-06 13:55:46 -0700579 file_contents += class_name + std::string("\n");
David Sehr7c80f2d2017-02-07 16:47:58 -0800580 }
581 std::string output_file_contents;
Mathieu Chartier34067262017-04-06 13:55:46 -0700582 ASSERT_TRUE(CreateAndDump(file_contents, &output_file_contents));
583 ASSERT_EQ(output_file_contents, file_contents);
David Sehr7c80f2d2017-02-07 16:47:58 -0800584}
585
Mathieu Chartierd808e8b2017-03-21 13:37:41 -0700586TEST_F(ProfileAssistantTest, TestProfileCreationGenerateMethods) {
587 // Class names put here need to be in sorted order.
588 std::vector<std::string> class_names = {
589 "Ljava/lang/Math;->*",
590 };
591 std::string input_file_contents;
592 std::string expected_contents;
593 for (std::string& class_name : class_names) {
594 input_file_contents += class_name + std::string("\n");
595 expected_contents += DescriptorToDot(class_name.c_str()) +
596 std::string("\n");
597 }
598 std::string output_file_contents;
599 ScratchFile profile_file;
600 EXPECT_TRUE(CreateProfile(input_file_contents,
601 profile_file.GetFilename(),
602 GetLibCoreDexFileNames()[0]));
603 ProfileCompilationInfo info;
604 profile_file.GetFile()->ResetOffset();
605 ASSERT_TRUE(info.Load(GetFd(profile_file)));
606 // Verify that the profile has matching methods.
607 ScopedObjectAccess soa(Thread::Current());
608 ObjPtr<mirror::Class> klass = GetClass(nullptr, "Ljava/lang/Math;");
609 ASSERT_TRUE(klass != nullptr);
610 size_t method_count = 0;
611 for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) {
612 if (!method.IsCopied() && method.GetCodeItem() != nullptr) {
613 ++method_count;
Calin Juravlecc3171a2017-05-19 16:47:53 -0700614 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi =
615 info.GetMethod(method.GetDexFile()->GetLocation(),
616 method.GetDexFile()->GetLocationChecksum(),
617 method.GetDexMethodIndex());
Mathieu Chartierbbe3a5e2017-06-13 16:36:17 -0700618 ASSERT_TRUE(pmi != nullptr) << method.PrettyMethod();
Mathieu Chartierd808e8b2017-03-21 13:37:41 -0700619 }
620 }
621 EXPECT_GT(method_count, 0u);
622}
623
Mathieu Chartier2f794552017-06-19 10:58:08 -0700624TEST_F(ProfileAssistantTest, TestBootImageProfile) {
625 const std::string core_dex = GetLibCoreDexFileNames()[0];
626
627 std::vector<ScratchFile> profiles;
628
629 // In image with enough clean occurrences.
630 const std::string kCleanClass = "Ljava/lang/CharSequence;";
631 // In image with enough dirty occurrences.
632 const std::string kDirtyClass = "Ljava/lang/Object;";
633 // Not in image becauseof not enough occurrences.
634 const std::string kUncommonCleanClass = "Ljava/lang/Process;";
635 const std::string kUncommonDirtyClass = "Ljava/lang/Package;";
636 // Method that is hot.
637 // Also adds the class through inference since it is in each dex.
638 const std::string kHotMethod = "Ljava/lang/Comparable;->compareTo(Ljava/lang/Object;)I";
639 // Method that doesn't add the class since its only in one profile. Should still show up in the
640 // boot profile.
641 const std::string kOtherMethod = "Ljava/util/HashMap;-><init>()V";
Mathieu Chartier8eecddf2017-07-12 16:05:54 -0700642 // Method that gets marked as hot since it's in multiple profiles.
643 const std::string kMultiMethod = "Ljava/util/ArrayList;->clear()V";
Mathieu Chartier2f794552017-06-19 10:58:08 -0700644
645 // Thresholds for this test.
646 static const size_t kDirtyThreshold = 3;
647 static const size_t kCleanThreshold = 2;
Mathieu Chartier8eecddf2017-07-12 16:05:54 -0700648 static const size_t kMethodThreshold = 2;
Mathieu Chartier2f794552017-06-19 10:58:08 -0700649
650 // Create a bunch of boot profiles.
651 std::string dex1 =
652 kCleanClass + "\n" +
653 kDirtyClass + "\n" +
654 kUncommonCleanClass + "\n" +
655 "H" + kHotMethod + "\n" +
656 kUncommonDirtyClass;
657 profiles.emplace_back(ScratchFile());
658 EXPECT_TRUE(CreateProfile(dex1, profiles.back().GetFilename(), core_dex));
659
660 // Create a bunch of boot profiles.
661 std::string dex2 =
662 kCleanClass + "\n" +
663 kDirtyClass + "\n" +
664 "P" + kHotMethod + "\n" +
Mathieu Chartier8eecddf2017-07-12 16:05:54 -0700665 "P" + kMultiMethod + "\n" +
Mathieu Chartier2f794552017-06-19 10:58:08 -0700666 kUncommonDirtyClass;
667 profiles.emplace_back(ScratchFile());
668 EXPECT_TRUE(CreateProfile(dex2, profiles.back().GetFilename(), core_dex));
669
670 // Create a bunch of boot profiles.
671 std::string dex3 =
672 "S" + kHotMethod + "\n" +
673 "P" + kOtherMethod + "\n" +
Mathieu Chartier8eecddf2017-07-12 16:05:54 -0700674 "P" + kMultiMethod + "\n" +
Mathieu Chartier2f794552017-06-19 10:58:08 -0700675 kDirtyClass + "\n";
676 profiles.emplace_back(ScratchFile());
677 EXPECT_TRUE(CreateProfile(dex3, profiles.back().GetFilename(), core_dex));
678
679 // Generate the boot profile.
680 ScratchFile out_profile;
681 std::vector<std::string> args;
682 args.push_back(GetProfmanCmd());
683 args.push_back("--generate-boot-image-profile");
684 args.push_back("--boot-image-class-threshold=" + std::to_string(kDirtyThreshold));
685 args.push_back("--boot-image-clean-class-threshold=" + std::to_string(kCleanThreshold));
Mathieu Chartier8eecddf2017-07-12 16:05:54 -0700686 args.push_back("--boot-image-sampled-method-threshold=" + std::to_string(kMethodThreshold));
Mathieu Chartier2f794552017-06-19 10:58:08 -0700687 args.push_back("--reference-profile-file=" + out_profile.GetFilename());
688 args.push_back("--apk=" + core_dex);
689 args.push_back("--dex-location=" + core_dex);
690 for (const ScratchFile& profile : profiles) {
691 args.push_back("--profile-file=" + profile.GetFilename());
692 }
693 std::string error;
694 EXPECT_EQ(ExecAndReturnCode(args, &error), 0) << error;
695 ASSERT_EQ(0, out_profile.GetFile()->Flush());
696 ASSERT_TRUE(out_profile.GetFile()->ResetOffset());
697
698 // Verify the boot profile contents.
699 std::string output_file_contents;
700 EXPECT_TRUE(DumpClassesAndMethods(out_profile.GetFilename(), &output_file_contents));
701 // Common classes, should be in the classes of the profile.
702 EXPECT_NE(output_file_contents.find(kCleanClass + "\n"), std::string::npos)
703 << output_file_contents;
704 EXPECT_NE(output_file_contents.find(kDirtyClass + "\n"), std::string::npos)
705 << output_file_contents;
706 // Uncommon classes, should not fit preloaded class criteria and should not be in the profile.
707 EXPECT_EQ(output_file_contents.find(kUncommonCleanClass + "\n"), std::string::npos)
708 << output_file_contents;
709 EXPECT_EQ(output_file_contents.find(kUncommonDirtyClass + "\n"), std::string::npos)
710 << output_file_contents;
711 // Inferred class from a method common to all three profiles.
712 EXPECT_NE(output_file_contents.find("Ljava/lang/Comparable;\n"), std::string::npos)
713 << output_file_contents;
714 // Aggregated methods hotness information.
715 EXPECT_NE(output_file_contents.find("HSP" + kHotMethod), std::string::npos)
716 << output_file_contents;
Mathieu Chartier8eecddf2017-07-12 16:05:54 -0700717 EXPECT_NE(output_file_contents.find("P" + kOtherMethod), std::string::npos)
Mathieu Chartier2f794552017-06-19 10:58:08 -0700718 << output_file_contents;
719 // Not inferred class, method is only in one profile.
720 EXPECT_EQ(output_file_contents.find("Ljava/util/HashMap;\n"), std::string::npos)
721 << output_file_contents;
Mathieu Chartier8eecddf2017-07-12 16:05:54 -0700722 // Test the sampled methods that became hot.
723 // Other method is in only one profile, it should not become hot.
724 EXPECT_EQ(output_file_contents.find("HP" + kOtherMethod), std::string::npos)
725 << output_file_contents;
726 // Multi method is in at least two profiles, it should become hot.
727 EXPECT_NE(output_file_contents.find("HP" + kMultiMethod), std::string::npos)
728 << output_file_contents;
Mathieu Chartier2f794552017-06-19 10:58:08 -0700729}
730
David Sehr7c80f2d2017-02-07 16:47:58 -0800731TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) {
732 // Class names put here need to be in sorted order.
733 std::vector<std::string> class_names = {
Calin Juravlee0ac1152017-02-13 19:03:47 -0800734 "Ldoesnt/match/this/one;",
735 "Ljava/lang/Comparable;",
736 "Ljava/lang/Object;"
David Sehr7c80f2d2017-02-07 16:47:58 -0800737 };
738 std::string input_file_contents;
739 for (std::string& class_name : class_names) {
740 input_file_contents += class_name + std::string("\n");
741 }
742 std::string output_file_contents;
743 ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
744 std::string expected_contents =
Mathieu Chartier34067262017-04-06 13:55:46 -0700745 class_names[1] + std::string("\n") +
746 class_names[2] + std::string("\n");
David Sehr7c80f2d2017-02-07 16:47:58 -0800747 ASSERT_EQ(output_file_contents, expected_contents);
748}
749
750TEST_F(ProfileAssistantTest, TestProfileCreationNoneMatched) {
751 // Class names put here need to be in sorted order.
752 std::vector<std::string> class_names = {
Calin Juravlee0ac1152017-02-13 19:03:47 -0800753 "Ldoesnt/match/this/one;",
754 "Ldoesnt/match/this/one/either;",
755 "Lnor/this/one;"
David Sehr7c80f2d2017-02-07 16:47:58 -0800756 };
757 std::string input_file_contents;
758 for (std::string& class_name : class_names) {
759 input_file_contents += class_name + std::string("\n");
760 }
761 std::string output_file_contents;
762 ASSERT_TRUE(CreateAndDump(input_file_contents, &output_file_contents));
763 std::string expected_contents("");
764 ASSERT_EQ(output_file_contents, expected_contents);
765}
766
Calin Juravlee0ac1152017-02-13 19:03:47 -0800767TEST_F(ProfileAssistantTest, TestProfileCreateInlineCache) {
768 // Create the profile content.
769 std::vector<std::string> methods = {
770 "LTestInline;->inlineMonomorphic(LSuper;)I+LSubA;",
771 "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;",
772 "LTestInline;->inlineMegamorphic(LSuper;)I+LSubA;,LSubB;,LSubC;,LSubD;,LSubE;",
Calin Juravle589e71e2017-03-03 16:05:05 -0800773 "LTestInline;->inlineMissingTypes(LSuper;)I+missing_types",
Calin Juravlee0ac1152017-02-13 19:03:47 -0800774 "LTestInline;->noInlineCache(LSuper;)I"
775 };
776 std::string input_file_contents;
777 for (std::string& m : methods) {
778 input_file_contents += m + std::string("\n");
779 }
780
781 // Create the profile and save it to disk.
782 ScratchFile profile_file;
783 ASSERT_TRUE(CreateProfile(input_file_contents,
784 profile_file.GetFilename(),
785 GetTestDexFileName("ProfileTestMultiDex")));
786
787 // Load the profile from disk.
788 ProfileCompilationInfo info;
789 profile_file.GetFile()->ResetOffset();
790 ASSERT_TRUE(info.Load(GetFd(profile_file)));
791
792 // Load the dex files and verify that the profile contains the expected methods info.
793 ScopedObjectAccess soa(Thread::Current());
794 jobject class_loader = LoadDex("ProfileTestMultiDex");
795 ASSERT_NE(class_loader, nullptr);
796
797 mirror::Class* sub_a = GetClass(class_loader, "LSubA;");
798 mirror::Class* sub_b = GetClass(class_loader, "LSubB;");
799 mirror::Class* sub_c = GetClass(class_loader, "LSubC;");
800
801 ASSERT_TRUE(sub_a != nullptr);
802 ASSERT_TRUE(sub_b != nullptr);
803 ASSERT_TRUE(sub_c != nullptr);
804
805 {
806 // Verify that method inlineMonomorphic has the expected inline caches and nothing else.
807 ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader,
808 "LTestInline;",
809 "inlineMonomorphic");
810 ASSERT_TRUE(inline_monomorphic != nullptr);
811 std::set<mirror::Class*> expected_monomorphic;
812 expected_monomorphic.insert(sub_a);
Calin Juravle589e71e2017-03-03 16:05:05 -0800813 AssertInlineCaches(inline_monomorphic,
814 expected_monomorphic,
815 info,
816 /*megamorphic*/false,
817 /*missing_types*/false);
Calin Juravlee0ac1152017-02-13 19:03:47 -0800818 }
819
820 {
821 // Verify that method inlinePolymorphic has the expected inline caches and nothing else.
822 ArtMethod* inline_polymorhic = GetVirtualMethod(class_loader,
823 "LTestInline;",
824 "inlinePolymorphic");
825 ASSERT_TRUE(inline_polymorhic != nullptr);
826 std::set<mirror::Class*> expected_polymorphic;
827 expected_polymorphic.insert(sub_a);
828 expected_polymorphic.insert(sub_b);
829 expected_polymorphic.insert(sub_c);
Calin Juravle589e71e2017-03-03 16:05:05 -0800830 AssertInlineCaches(inline_polymorhic,
831 expected_polymorphic,
832 info,
833 /*megamorphic*/false,
834 /*missing_types*/false);
Calin Juravlee0ac1152017-02-13 19:03:47 -0800835 }
836
837 {
838 // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
839 ArtMethod* inline_megamorphic = GetVirtualMethod(class_loader,
840 "LTestInline;",
841 "inlineMegamorphic");
842 ASSERT_TRUE(inline_megamorphic != nullptr);
843 std::set<mirror::Class*> expected_megamorphic;
Calin Juravle589e71e2017-03-03 16:05:05 -0800844 AssertInlineCaches(inline_megamorphic,
845 expected_megamorphic,
846 info,
847 /*megamorphic*/true,
848 /*missing_types*/false);
849 }
850
851 {
852 // Verify that method inlineMegamorphic has the expected inline caches and nothing else.
853 ArtMethod* inline_missing_types = GetVirtualMethod(class_loader,
854 "LTestInline;",
855 "inlineMissingTypes");
856 ASSERT_TRUE(inline_missing_types != nullptr);
857 std::set<mirror::Class*> expected_missing_Types;
858 AssertInlineCaches(inline_missing_types,
859 expected_missing_Types,
860 info,
861 /*megamorphic*/false,
862 /*missing_types*/true);
Calin Juravlee0ac1152017-02-13 19:03:47 -0800863 }
864
865 {
866 // Verify that method noInlineCache has no inline caches in the profile.
867 ArtMethod* no_inline_cache = GetVirtualMethod(class_loader, "LTestInline;", "noInlineCache");
868 ASSERT_TRUE(no_inline_cache != nullptr);
Calin Juravlecc3171a2017-05-19 16:47:53 -0700869 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi_no_inline_cache =
870 info.GetMethod(no_inline_cache->GetDexFile()->GetLocation(),
871 no_inline_cache->GetDexFile()->GetLocationChecksum(),
872 no_inline_cache->GetDexMethodIndex());
873 ASSERT_TRUE(pmi_no_inline_cache != nullptr);
Calin Juravlee6f87cc2017-05-24 17:41:05 -0700874 ASSERT_TRUE(pmi_no_inline_cache->inline_caches->empty());
Calin Juravlee0ac1152017-02-13 19:03:47 -0800875 }
876}
877
Calin Juravlecea9e9d2017-03-23 19:04:59 -0700878TEST_F(ProfileAssistantTest, MergeProfilesWithDifferentDexOrder) {
879 ScratchFile profile1;
880 ScratchFile reference_profile;
881
882 std::vector<int> profile_fds({GetFd(profile1)});
883 int reference_profile_fd = GetFd(reference_profile);
884
885 // The new profile info will contain the methods with indices 0-100.
886 const uint16_t kNumberOfMethodsToEnableCompilation = 100;
887 ProfileCompilationInfo info1;
888 SetupProfile("p1", 1, kNumberOfMethodsToEnableCompilation, 0, profile1, &info1,
889 /*start_method_index*/0, /*reverse_dex_write_order*/false);
890
891 // The reference profile info will contain the methods with indices 50-150.
892 // When setting up the profile reverse the order in which the dex files
893 // are added to the profile. This will verify that profman merges profiles
894 // with a different dex order correctly.
895 const uint16_t kNumberOfMethodsAlreadyCompiled = 100;
896 ProfileCompilationInfo reference_info;
897 SetupProfile("p1", 1, kNumberOfMethodsAlreadyCompiled, 0, reference_profile,
898 &reference_info, kNumberOfMethodsToEnableCompilation / 2, /*reverse_dex_write_order*/true);
899
900 // We should advise compilation.
901 ASSERT_EQ(ProfileAssistant::kCompile,
902 ProcessProfiles(profile_fds, reference_profile_fd));
903
904 // The resulting compilation info must be equal to the merge of the inputs.
905 ProfileCompilationInfo result;
906 ASSERT_TRUE(reference_profile.GetFile()->ResetOffset());
907 ASSERT_TRUE(result.Load(reference_profile_fd));
908
909 ProfileCompilationInfo expected;
910 ASSERT_TRUE(expected.MergeWith(reference_info));
911 ASSERT_TRUE(expected.MergeWith(info1));
912 ASSERT_TRUE(expected.Equals(result));
913
914 // The information from profile must remain the same.
915 CheckProfileInfo(profile1, info1);
916}
917
Calin Juravle08556882017-05-26 16:40:45 -0700918TEST_F(ProfileAssistantTest, TestProfileCreateWithInvalidData) {
919 // Create the profile content.
920 std::vector<std::string> profile_methods = {
921 "LTestInline;->inlineMonomorphic(LSuper;)I+invalid_class",
922 "LTestInline;->invalid_method",
923 "invalid_class"
924 };
925 std::string input_file_contents;
926 for (std::string& m : profile_methods) {
927 input_file_contents += m + std::string("\n");
928 }
929
930 // Create the profile and save it to disk.
931 ScratchFile profile_file;
932 std::string dex_filename = GetTestDexFileName("ProfileTestMultiDex");
933 ASSERT_TRUE(CreateProfile(input_file_contents,
934 profile_file.GetFilename(),
935 dex_filename));
936
937 // Load the profile from disk.
938 ProfileCompilationInfo info;
939 profile_file.GetFile()->ResetOffset();
940 ASSERT_TRUE(info.Load(GetFd(profile_file)));
941
942 // Load the dex files and verify that the profile contains the expected methods info.
943 ScopedObjectAccess soa(Thread::Current());
944 jobject class_loader = LoadDex("ProfileTestMultiDex");
945 ASSERT_NE(class_loader, nullptr);
946
947 ArtMethod* inline_monomorphic = GetVirtualMethod(class_loader,
948 "LTestInline;",
949 "inlineMonomorphic");
950 const DexFile* dex_file = inline_monomorphic->GetDexFile();
951
952 // Verify that the inline cache contains the invalid type.
953 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> pmi =
954 info.GetMethod(dex_file->GetLocation(),
955 dex_file->GetLocationChecksum(),
956 inline_monomorphic->GetDexMethodIndex());
957 ASSERT_TRUE(pmi != nullptr);
958 ASSERT_EQ(pmi->inline_caches->size(), 1u);
959 const ProfileCompilationInfo::DexPcData& dex_pc_data = pmi->inline_caches->begin()->second;
960 dex::TypeIndex invalid_class_index(std::numeric_limits<uint16_t>::max() - 1);
961 ASSERT_EQ(1u, dex_pc_data.classes.size());
962 ASSERT_EQ(invalid_class_index, dex_pc_data.classes.begin()->type_index);
963
964 // Verify that the start-up classes contain the invalid class.
965 std::set<dex::TypeIndex> classes;
Mathieu Chartierea650f32017-05-24 12:04:13 -0700966 std::set<uint16_t> hot_methods;
967 std::set<uint16_t> startup_methods;
968 std::set<uint16_t> post_start_methods;
969 ASSERT_TRUE(info.GetClassesAndMethods(*dex_file,
970 &classes,
971 &hot_methods,
972 &startup_methods,
973 &post_start_methods));
Calin Juravle08556882017-05-26 16:40:45 -0700974 ASSERT_EQ(1u, classes.size());
975 ASSERT_TRUE(classes.find(invalid_class_index) != classes.end());
976
977 // Verify that the invalid method is in the profile.
Mathieu Chartierea650f32017-05-24 12:04:13 -0700978 ASSERT_EQ(2u, hot_methods.size());
Calin Juravle08556882017-05-26 16:40:45 -0700979 uint16_t invalid_method_index = std::numeric_limits<uint16_t>::max() - 1;
Mathieu Chartierea650f32017-05-24 12:04:13 -0700980 ASSERT_TRUE(hot_methods.find(invalid_method_index) != hot_methods.end());
Calin Juravle08556882017-05-26 16:40:45 -0700981}
982
Mathieu Chartier28b5c582017-06-06 14:12:50 -0700983TEST_F(ProfileAssistantTest, DumpOnly) {
984 ScratchFile profile;
985
986 const uint32_t kNumberOfMethods = 64;
987 std::vector<uint32_t> hot_methods;
988 std::vector<uint32_t> startup_methods;
989 std::vector<uint32_t> post_startup_methods;
990 for (size_t i = 0; i < kNumberOfMethods; ++i) {
991 if (i % 2 == 0) {
992 hot_methods.push_back(i);
993 }
994 if (i % 3 == 1) {
995 startup_methods.push_back(i);
996 }
997 if (i % 4 == 2) {
998 post_startup_methods.push_back(i);
999 }
1000 }
1001 EXPECT_GT(hot_methods.size(), 0u);
1002 EXPECT_GT(startup_methods.size(), 0u);
1003 EXPECT_GT(post_startup_methods.size(), 0u);
1004 ProfileCompilationInfo info1;
1005 SetupBasicProfile("p1",
1006 1,
1007 kNumberOfMethods,
1008 hot_methods,
1009 startup_methods,
1010 post_startup_methods,
1011 profile,
1012 &info1);
1013 std::string output;
1014 DumpOnly(profile.GetFilename(), &output);
1015 const size_t hot_offset = output.find("hot methods:");
1016 const size_t startup_offset = output.find("startup methods:");
1017 const size_t post_startup_offset = output.find("post startup methods:");
1018 const size_t classes_offset = output.find("classes:");
1019 ASSERT_NE(hot_offset, std::string::npos);
1020 ASSERT_NE(startup_offset, std::string::npos);
1021 ASSERT_NE(post_startup_offset, std::string::npos);
1022 ASSERT_LT(hot_offset, startup_offset);
1023 ASSERT_LT(startup_offset, post_startup_offset);
1024 // Check the actual contents of the dump by looking at the offsets of the methods.
1025 for (uint32_t m : hot_methods) {
1026 const size_t pos = output.find(std::to_string(m) + "[],", hot_offset);
1027 ASSERT_NE(pos, std::string::npos);
1028 EXPECT_LT(pos, startup_offset);
1029 }
1030 for (uint32_t m : startup_methods) {
1031 const size_t pos = output.find(std::to_string(m) + ",", startup_offset);
1032 ASSERT_NE(pos, std::string::npos);
1033 EXPECT_LT(pos, post_startup_offset);
1034 }
1035 for (uint32_t m : post_startup_methods) {
1036 const size_t pos = output.find(std::to_string(m) + ",", post_startup_offset);
1037 ASSERT_NE(pos, std::string::npos);
1038 EXPECT_LT(pos, classes_offset);
1039 }
1040}
1041
Calin Juravle2e2db782016-02-23 12:00:03 +00001042} // namespace art