blob: efaea841600ea8160dd4247d69685a6bd7a9118e [file] [log] [blame]
David Sehr1fed3432018-05-29 13:19:47 -07001/*
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#include <stdio.h>
19
20#include "art_method-inl.h"
21#include "base/unix_file/fd_file.h"
22#include "class_linker-inl.h"
23#include "common_runtime_test.h"
24#include "dex/dex_file.h"
25#include "dex/dex_file_loader.h"
26#include "dex/method_reference.h"
27#include "dex/type_reference.h"
28#include "handle_scope-inl.h"
29#include "linear_alloc.h"
30#include "mirror/class-inl.h"
31#include "mirror/class_loader.h"
32#include "profile/profile_compilation_info.h"
33#include "scoped_thread_state_change-inl.h"
David Sehr1fed3432018-05-29 13:19:47 -070034
35namespace art {
36
37using Hotness = ProfileCompilationInfo::MethodHotness;
38
David Sehr1fed3432018-05-29 13:19:47 -070039class ProfileCompilationInfoTest : public CommonRuntimeTest {
40 public:
Roland Levillainbbc6e7e2018-08-24 16:58:47 +010041 void PostRuntimeCreate() override {
David Sehr1fed3432018-05-29 13:19:47 -070042 allocator_.reset(new ArenaAllocator(Runtime::Current()->GetArenaPool()));
43 }
44
45 protected:
46 std::vector<ArtMethod*> GetVirtualMethods(jobject class_loader,
47 const std::string& clazz) {
48 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
49 Thread* self = Thread::Current();
50 ScopedObjectAccess soa(self);
51 StackHandleScope<1> hs(self);
52 Handle<mirror::ClassLoader> h_loader(
53 hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader()));
54 ObjPtr<mirror::Class> klass = class_linker->FindClass(self, clazz.c_str(), h_loader);
55
56 const auto pointer_size = class_linker->GetImagePointerSize();
57 std::vector<ArtMethod*> methods;
58 for (auto& m : klass->GetVirtualMethods(pointer_size)) {
59 methods.push_back(&m);
60 }
61 return methods;
62 }
63
David Sehr1fed3432018-05-29 13:19:47 -070064 uint32_t GetFd(const ScratchFile& file) {
65 return static_cast<uint32_t>(file.GetFd());
66 }
67
68 bool SaveProfilingInfo(
69 const std::string& filename,
70 const std::vector<ArtMethod*>& methods,
David Sehr1fed3432018-05-29 13:19:47 -070071 Hotness::Flag flags) {
72 ProfileCompilationInfo info;
73 std::vector<ProfileMethodInfo> profile_methods;
74 ScopedObjectAccess soa(Thread::Current());
75 for (ArtMethod* method : methods) {
76 profile_methods.emplace_back(
77 MethodReference(method->GetDexFile(), method->GetDexMethodIndex()));
78 }
Calin Juravlea6c9b782019-09-16 18:57:26 -070079 if (!info.AddMethods(profile_methods, flags)) {
David Sehr1fed3432018-05-29 13:19:47 -070080 return false;
81 }
82 if (info.GetNumberOfMethods() != profile_methods.size()) {
83 return false;
84 }
85 ProfileCompilationInfo file_profile;
86 if (!file_profile.Load(filename, false)) {
87 return false;
88 }
89 if (!info.MergeWith(file_profile)) {
90 return false;
91 }
92
93 return info.Save(filename, nullptr);
94 }
95
96 // Saves the given art methods to a profile backed by 'filename' and adds
97 // some fake inline caches to it. The added inline caches are returned in
98 // the out map `profile_methods_map`.
99 bool SaveProfilingInfoWithFakeInlineCaches(
100 const std::string& filename,
101 const std::vector<ArtMethod*>& methods,
102 Hotness::Flag flags,
103 /*out*/ SafeMap<ArtMethod*, ProfileMethodInfo>* profile_methods_map) {
104 ProfileCompilationInfo info;
105 std::vector<ProfileMethodInfo> profile_methods;
106 ScopedObjectAccess soa(Thread::Current());
107 for (ArtMethod* method : methods) {
108 std::vector<ProfileMethodInfo::ProfileInlineCache> caches;
109 // Monomorphic
110 for (uint16_t dex_pc = 0; dex_pc < 11; dex_pc++) {
111 std::vector<TypeReference> classes;
112 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(0));
113 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
114 }
115 // Polymorphic
116 for (uint16_t dex_pc = 11; dex_pc < 22; dex_pc++) {
117 std::vector<TypeReference> classes;
118 for (uint16_t k = 0; k < InlineCache::kIndividualCacheSize / 2; k++) {
119 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
120 }
121 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
122 }
123 // Megamorphic
124 for (uint16_t dex_pc = 22; dex_pc < 33; dex_pc++) {
125 std::vector<TypeReference> classes;
126 for (uint16_t k = 0; k < 2 * InlineCache::kIndividualCacheSize; k++) {
127 classes.emplace_back(method->GetDexFile(), dex::TypeIndex(k));
128 }
129 caches.emplace_back(dex_pc, /*is_missing_types*/false, classes);
130 }
131 // Missing types
132 for (uint16_t dex_pc = 33; dex_pc < 44; dex_pc++) {
133 std::vector<TypeReference> classes;
134 caches.emplace_back(dex_pc, /*is_missing_types*/true, classes);
135 }
136 ProfileMethodInfo pmi(MethodReference(method->GetDexFile(),
137 method->GetDexMethodIndex()),
138 caches);
139 profile_methods.push_back(pmi);
140 profile_methods_map->Put(method, pmi);
141 }
142
143 if (!info.AddMethods(profile_methods, flags)
144 || info.GetNumberOfMethods() != profile_methods.size()) {
145 return false;
146 }
147 return info.Save(filename, nullptr);
148 }
149
150 // Creates an inline cache which will be destructed at the end of the test.
151 ProfileCompilationInfo::InlineCacheMap* CreateInlineCacheMap() {
152 used_inline_caches.emplace_back(new ProfileCompilationInfo::InlineCacheMap(
153 std::less<uint16_t>(), allocator_->Adapter(kArenaAllocProfile)));
154 return used_inline_caches.back().get();
155 }
156
157 ProfileCompilationInfo::OfflineProfileMethodInfo ConvertProfileMethodInfo(
158 const ProfileMethodInfo& pmi) {
159 ProfileCompilationInfo::InlineCacheMap* ic_map = CreateInlineCacheMap();
160 ProfileCompilationInfo::OfflineProfileMethodInfo offline_pmi(ic_map);
161 SafeMap<DexFile*, uint8_t> dex_map; // dex files to profile index
162 for (const auto& inline_cache : pmi.inline_caches) {
163 ProfileCompilationInfo::DexPcData& dex_pc_data =
164 ic_map->FindOrAdd(
165 inline_cache.dex_pc, ProfileCompilationInfo::DexPcData(allocator_.get()))->second;
166 if (inline_cache.is_missing_types) {
167 dex_pc_data.SetIsMissingTypes();
168 }
169 for (const auto& class_ref : inline_cache.classes) {
170 uint8_t dex_profile_index = dex_map.FindOrAdd(const_cast<DexFile*>(class_ref.dex_file),
171 static_cast<uint8_t>(dex_map.size()))->second;
172 dex_pc_data.AddClass(dex_profile_index, class_ref.TypeIndex());
173 if (dex_profile_index >= offline_pmi.dex_references.size()) {
174 // This is a new dex.
Vladimir Markoc2f46932021-03-05 10:55:55 +0000175 const std::string& location = class_ref.dex_file->GetLocation();
176 std::string dex_key = ProfileCompilationInfo::GetProfileDexFileBaseKey(location);
177 // The `dex_key` is a temporary that shall cease to exist soon. Create a view
178 // using the dex file's location as the backing data.
179 CHECK(EndsWith(location, dex_key));
180 size_t dex_key_start = location.size() - dex_key.size();
181 std::string_view dex_key_view(location.data() + dex_key_start, dex_key.size());
182 offline_pmi.dex_references.emplace_back(dex_key_view,
David Sehr1fed3432018-05-29 13:19:47 -0700183 class_ref.dex_file->GetLocationChecksum(),
184 class_ref.dex_file->NumMethodIds());
185 }
186 }
187 }
188 return offline_pmi;
189 }
190
191 // Cannot sizeof the actual arrays so hard code the values here.
192 // They should not change anyway.
193 static constexpr int kProfileMagicSize = 4;
194 static constexpr int kProfileVersionSize = 4;
195
196 std::unique_ptr<ArenaAllocator> allocator_;
197
198 // Cache of inline caches generated during tests.
199 // This makes it easier to pass data between different utilities and ensure that
200 // caches are destructed at the end of the test.
201 std::vector<std::unique_ptr<ProfileCompilationInfo::InlineCacheMap>> used_inline_caches;
202};
203
204TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
205 ScratchFile profile;
206
207 Thread* self = Thread::Current();
208 jobject class_loader;
209 {
210 ScopedObjectAccess soa(self);
211 class_loader = LoadDex("ProfileTestMultiDex");
212 }
213 ASSERT_NE(class_loader, nullptr);
214
215 // Save virtual methods from Main.
David Sehr1fed3432018-05-29 13:19:47 -0700216 std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
217 ASSERT_TRUE(SaveProfilingInfo(
Calin Juravlea6c9b782019-09-16 18:57:26 -0700218 profile.GetFilename(),
219 main_methods,
220 static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagPostStartup)));
David Sehr1fed3432018-05-29 13:19:47 -0700221
222 // Check that what we saved is in the profile.
223 ProfileCompilationInfo info1;
224 ASSERT_TRUE(info1.Load(GetFd(profile)));
225 ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size());
226 {
227 ScopedObjectAccess soa(self);
228 for (ArtMethod* m : main_methods) {
229 Hotness h = info1.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
230 ASSERT_TRUE(h.IsHot());
231 ASSERT_TRUE(h.IsPostStartup());
232 }
233 }
234
235 // Save virtual methods from Second.
236 std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
237 ASSERT_TRUE(SaveProfilingInfo(
Calin Juravlea6c9b782019-09-16 18:57:26 -0700238 profile.GetFilename(),
239 second_methods,
240 static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup)));
David Sehr1fed3432018-05-29 13:19:47 -0700241
242 // Check that what we saved is in the profile (methods form Main and Second).
243 ProfileCompilationInfo info2;
244 ASSERT_TRUE(profile.GetFile()->ResetOffset());
245 ASSERT_TRUE(info2.Load(GetFd(profile)));
246 ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size());
247 {
248 ScopedObjectAccess soa(self);
249 for (ArtMethod* m : main_methods) {
250 Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
251 ASSERT_TRUE(h.IsHot());
252 ASSERT_TRUE(h.IsPostStartup());
253 }
254 for (ArtMethod* m : second_methods) {
255 Hotness h = info2.GetMethodHotness(MethodReference(m->GetDexFile(), m->GetDexMethodIndex()));
256 ASSERT_TRUE(h.IsHot());
257 ASSERT_TRUE(h.IsStartup());
258 }
259 }
260}
261
262TEST_F(ProfileCompilationInfoTest, SaveArtMethodsWithInlineCaches) {
263 ScratchFile profile;
264
265 Thread* self = Thread::Current();
266 jobject class_loader;
267 {
268 ScopedObjectAccess soa(self);
269 class_loader = LoadDex("ProfileTestMultiDex");
270 }
271 ASSERT_NE(class_loader, nullptr);
272
273 // Save virtual methods from Main.
David Sehr1fed3432018-05-29 13:19:47 -0700274 std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
275
276 SafeMap<ArtMethod*, ProfileMethodInfo> profile_methods_map;
277 ASSERT_TRUE(SaveProfilingInfoWithFakeInlineCaches(
Calin Juravlea6c9b782019-09-16 18:57:26 -0700278 profile.GetFilename(),
279 main_methods,
280 static_cast<Hotness::Flag>(Hotness::kFlagHot | Hotness::kFlagStartup),
281 &profile_methods_map));
David Sehr1fed3432018-05-29 13:19:47 -0700282
283 // Check that what we saved is in the profile.
284 ProfileCompilationInfo info;
285 ASSERT_TRUE(info.Load(GetFd(profile)));
286 ASSERT_EQ(info.GetNumberOfMethods(), main_methods.size());
287 {
288 ScopedObjectAccess soa(self);
289 for (ArtMethod* m : main_methods) {
Calin Juravle4ad95212019-09-23 23:39:41 -0400290 MethodReference method_ref(m->GetDexFile(), m->GetDexMethodIndex());
291 Hotness h = info.GetMethodHotness(method_ref);
David Sehr1fed3432018-05-29 13:19:47 -0700292 ASSERT_TRUE(h.IsHot());
293 ASSERT_TRUE(h.IsStartup());
294 const ProfileMethodInfo& pmi = profile_methods_map.find(m)->second;
295 std::unique_ptr<ProfileCompilationInfo::OfflineProfileMethodInfo> offline_pmi =
Calin Juravle4ad95212019-09-23 23:39:41 -0400296 info.GetHotMethodInfo(method_ref);
David Sehr1fed3432018-05-29 13:19:47 -0700297 ASSERT_TRUE(offline_pmi != nullptr);
298 ProfileCompilationInfo::OfflineProfileMethodInfo converted_pmi =
299 ConvertProfileMethodInfo(pmi);
300 ASSERT_EQ(converted_pmi, *offline_pmi);
301 }
302 }
303}
304
305} // namespace art