blob: ad9af701740d4525dd8733d8f6e0a8b9f2631f8e [file] [log] [blame]
Calin Juravle877fd962016-01-05 14:29:29 +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
19#include "base/unix_file/fd_file.h"
20#include "art_method-inl.h"
21#include "class_linker-inl.h"
22#include "common_runtime_test.h"
23#include "dex_file.h"
24#include "mirror/class-inl.h"
25#include "mirror/class_loader.h"
26#include "handle_scope-inl.h"
27#include "jit/offline_profiling_info.h"
28#include "scoped_thread_state_change.h"
29
30namespace art {
31
32class ProfileCompilationInfoTest : public CommonRuntimeTest {
33 protected:
34 std::vector<ArtMethod*> GetVirtualMethods(jobject class_loader,
35 const std::string& clazz) {
36 ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
37 Thread* self = Thread::Current();
38 ScopedObjectAccess soa(self);
39 StackHandleScope<1> hs(self);
40 Handle<mirror::ClassLoader> h_loader(hs.NewHandle(
41 reinterpret_cast<mirror::ClassLoader*>(self->DecodeJObject(class_loader))));
42 mirror::Class* klass = class_linker->FindClass(self, clazz.c_str(), h_loader);
43
44 const auto pointer_size = class_linker->GetImagePointerSize();
45 std::vector<ArtMethod*> methods;
46 for (auto& m : klass->GetVirtualMethods(pointer_size)) {
47 methods.push_back(&m);
48 }
49 return methods;
50 }
51
Calin Juravle85f7bf32016-03-18 16:23:40 +000052 bool AddMethod(const std::string& dex_location,
Calin Juravleb8697b12016-03-21 14:37:55 +000053 uint32_t checksum,
54 uint16_t method_index,
55 ProfileCompilationInfo* info) {
Mathieu Chartier8913fc12015-12-09 16:38:30 -080056 return info->AddMethodIndex(dex_location, checksum, method_index);
Calin Juravle877fd962016-01-05 14:29:29 +000057 }
58
Calin Juravleb8697b12016-03-21 14:37:55 +000059 bool AddClass(const std::string& dex_location,
60 uint32_t checksum,
61 uint16_t class_index,
62 ProfileCompilationInfo* info) {
63 return info->AddMethodIndex(dex_location, checksum, class_index);
64 }
65
Calin Juravle877fd962016-01-05 14:29:29 +000066 uint32_t GetFd(const ScratchFile& file) {
67 return static_cast<uint32_t>(file.GetFd());
68 }
Calin Juravleb8697b12016-03-21 14:37:55 +000069
Calin Juravlefe297a92016-03-24 20:33:22 +000070 bool SaveProfilingInfo(
71 const std::string& filename,
72 const std::vector<ArtMethod*>& methods,
73 const std::set<DexCacheResolvedClasses>& resolved_classes) {
74 ProfileCompilationInfo info;
75 if (!info.AddMethodsAndClasses(methods, resolved_classes)) {
76 return false;
77 }
78 return info.MergeAndSave(filename, nullptr, false);
79 }
80
Calin Juravleb8697b12016-03-21 14:37:55 +000081 // Cannot sizeof the actual arrays so hardcode the values here.
82 // They should not change anyway.
83 static constexpr int kProfileMagicSize = 4;
84 static constexpr int kProfileVersionSize = 4;
Calin Juravle877fd962016-01-05 14:29:29 +000085};
86
87TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
88 ScratchFile profile;
89
90 Thread* self = Thread::Current();
91 jobject class_loader;
92 {
93 ScopedObjectAccess soa(self);
94 class_loader = LoadDex("ProfileTestMultiDex");
95 }
96 ASSERT_NE(class_loader, nullptr);
97
98 // Save virtual methods from Main.
Mathieu Chartier8913fc12015-12-09 16:38:30 -080099 std::set<DexCacheResolvedClasses> resolved_classes;
Calin Juravle877fd962016-01-05 14:29:29 +0000100 std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
Calin Juravlefe297a92016-03-24 20:33:22 +0000101 ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), main_methods, resolved_classes));
Calin Juravle877fd962016-01-05 14:29:29 +0000102
103 // Check that what we saved is in the profile.
104 ProfileCompilationInfo info1;
105 ASSERT_TRUE(info1.Load(GetFd(profile)));
106 ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size());
107 {
108 ScopedObjectAccess soa(self);
109 for (ArtMethod* m : main_methods) {
110 ASSERT_TRUE(info1.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())));
111 }
112 }
113
114 // Save virtual methods from Second.
115 std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
Calin Juravlefe297a92016-03-24 20:33:22 +0000116 ASSERT_TRUE(SaveProfilingInfo(profile.GetFilename(), second_methods, resolved_classes));
Calin Juravle877fd962016-01-05 14:29:29 +0000117
118 // Check that what we saved is in the profile (methods form Main and Second).
119 ProfileCompilationInfo info2;
120 ASSERT_TRUE(profile.GetFile()->ResetOffset());
121 ASSERT_TRUE(info2.Load(GetFd(profile)));
122 ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size());
123 {
124 ScopedObjectAccess soa(self);
125 for (ArtMethod* m : main_methods) {
126 ASSERT_TRUE(info2.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())));
127 }
128 for (ArtMethod* m : second_methods) {
129 ASSERT_TRUE(info2.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())));
130 }
131 }
132}
133
134TEST_F(ProfileCompilationInfoTest, SaveFd) {
135 ScratchFile profile;
136
137 ProfileCompilationInfo saved_info;
138 // Save a few methods.
139 for (uint16_t i = 0; i < 10; i++) {
Calin Juravle85f7bf32016-03-18 16:23:40 +0000140 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
141 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
Calin Juravle877fd962016-01-05 14:29:29 +0000142 }
143 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
144 ASSERT_EQ(0, profile.GetFile()->Flush());
145
146 // Check that we get back what we saved.
147 ProfileCompilationInfo loaded_info;
148 ASSERT_TRUE(profile.GetFile()->ResetOffset());
149 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
150 ASSERT_TRUE(loaded_info.Equals(saved_info));
151
152 // Save more methods.
153 for (uint16_t i = 0; i < 100; i++) {
Calin Juravle85f7bf32016-03-18 16:23:40 +0000154 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
155 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
156 ASSERT_TRUE(AddMethod("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info));
Calin Juravle877fd962016-01-05 14:29:29 +0000157 }
158 ASSERT_TRUE(profile.GetFile()->ResetOffset());
159 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
160 ASSERT_EQ(0, profile.GetFile()->Flush());
161
162 // Check that we get back everything we saved.
163 ProfileCompilationInfo loaded_info2;
164 ASSERT_TRUE(profile.GetFile()->ResetOffset());
165 ASSERT_TRUE(loaded_info2.Load(GetFd(profile)));
166 ASSERT_TRUE(loaded_info2.Equals(saved_info));
167}
168
Calin Juravle85f7bf32016-03-18 16:23:40 +0000169TEST_F(ProfileCompilationInfoTest, AddMethodsAndClassesFail) {
Calin Juravle877fd962016-01-05 14:29:29 +0000170 ScratchFile profile;
171
172 ProfileCompilationInfo info;
Calin Juravle85f7bf32016-03-18 16:23:40 +0000173 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info));
Calin Juravle877fd962016-01-05 14:29:29 +0000174 // Trying to add info for an existing file but with a different checksum.
Calin Juravle85f7bf32016-03-18 16:23:40 +0000175 ASSERT_FALSE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info));
Calin Juravle877fd962016-01-05 14:29:29 +0000176}
177
Calin Juravle85f7bf32016-03-18 16:23:40 +0000178TEST_F(ProfileCompilationInfoTest, MergeFail) {
Calin Juravle877fd962016-01-05 14:29:29 +0000179 ScratchFile profile;
180
181 ProfileCompilationInfo info1;
Calin Juravle85f7bf32016-03-18 16:23:40 +0000182 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
Calin Juravle877fd962016-01-05 14:29:29 +0000183 // Use the same file, change the checksum.
184 ProfileCompilationInfo info2;
Calin Juravle85f7bf32016-03-18 16:23:40 +0000185 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
Calin Juravle877fd962016-01-05 14:29:29 +0000186
Calin Juravle85f7bf32016-03-18 16:23:40 +0000187 ASSERT_FALSE(info1.MergeWith(info2));
Calin Juravle877fd962016-01-05 14:29:29 +0000188}
189
Calin Juravleb8697b12016-03-21 14:37:55 +0000190TEST_F(ProfileCompilationInfoTest, SaveMaxMethods) {
191 ScratchFile profile;
192
193 ProfileCompilationInfo saved_info;
194 // Save the maximum number of methods
195 for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
196 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
197 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
198 }
199 // Save the maximum number of classes
200 for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
201 ASSERT_TRUE(AddClass("dex_location1", /* checksum */ 1, /* class_idx */ i, &saved_info));
202 ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, /* class_idx */ i, &saved_info));
203 }
204
205 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
206 ASSERT_EQ(0, profile.GetFile()->Flush());
207
208 // Check that we get back what we saved.
209 ProfileCompilationInfo loaded_info;
210 ASSERT_TRUE(profile.GetFile()->ResetOffset());
211 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
212 ASSERT_TRUE(loaded_info.Equals(saved_info));
213}
214
215TEST_F(ProfileCompilationInfoTest, SaveEmpty) {
216 ScratchFile profile;
217
218 ProfileCompilationInfo saved_info;
219 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
220 ASSERT_EQ(0, profile.GetFile()->Flush());
221
222 // Check that we get back what we saved.
223 ProfileCompilationInfo loaded_info;
224 ASSERT_TRUE(profile.GetFile()->ResetOffset());
225 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
226 ASSERT_TRUE(loaded_info.Equals(saved_info));
227}
228
229TEST_F(ProfileCompilationInfoTest, LoadEmpty) {
230 ScratchFile profile;
231
232 ProfileCompilationInfo empyt_info;
233
234 ProfileCompilationInfo loaded_info;
235 ASSERT_TRUE(profile.GetFile()->ResetOffset());
236 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
237 ASSERT_TRUE(loaded_info.Equals(empyt_info));
238}
239
240TEST_F(ProfileCompilationInfoTest, BadMagic) {
241 ScratchFile profile;
242 uint8_t buffer[] = { 1, 2, 3, 4 };
243 ASSERT_TRUE(profile.GetFile()->WriteFully(buffer, sizeof(buffer)));
244 ProfileCompilationInfo loaded_info;
245 ASSERT_TRUE(profile.GetFile()->ResetOffset());
246 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
247}
248
249TEST_F(ProfileCompilationInfoTest, BadVersion) {
250 ScratchFile profile;
251
252 ASSERT_TRUE(profile.GetFile()->WriteFully(
253 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
254 uint8_t version[] = { 'v', 'e', 'r', 's', 'i', 'o', 'n' };
255 ASSERT_TRUE(profile.GetFile()->WriteFully(version, sizeof(version)));
256 ASSERT_EQ(0, profile.GetFile()->Flush());
257
258 ProfileCompilationInfo loaded_info;
259 ASSERT_TRUE(profile.GetFile()->ResetOffset());
260 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
261}
262
263TEST_F(ProfileCompilationInfoTest, Incomplete) {
264 ScratchFile profile;
265 ASSERT_TRUE(profile.GetFile()->WriteFully(
266 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
267 ASSERT_TRUE(profile.GetFile()->WriteFully(
268 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
269 // Write that we have at least one line.
270 uint8_t line_number[] = { 0, 1 };
271 ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
272 ASSERT_EQ(0, profile.GetFile()->Flush());
273
274 ProfileCompilationInfo loaded_info;
275 ASSERT_TRUE(profile.GetFile()->ResetOffset());
276 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
277}
278
279TEST_F(ProfileCompilationInfoTest, TooLongDexLocation) {
280 ScratchFile profile;
281 ASSERT_TRUE(profile.GetFile()->WriteFully(
282 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
283 ASSERT_TRUE(profile.GetFile()->WriteFully(
284 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
285 // Write that we have at least one line.
286 uint8_t line_number[] = { 0, 1 };
287 ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
288
289 // dex_location_size, methods_size, classes_size, checksum.
290 // Dex location size is too big and should be rejected.
291 uint8_t line[] = { 255, 255, 0, 1, 0, 1, 0, 0, 0, 0 };
292 ASSERT_TRUE(profile.GetFile()->WriteFully(line, sizeof(line)));
293 ASSERT_EQ(0, profile.GetFile()->Flush());
294
295 ProfileCompilationInfo loaded_info;
296 ASSERT_TRUE(profile.GetFile()->ResetOffset());
297 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
298}
299
300TEST_F(ProfileCompilationInfoTest, UnexpectedContent) {
301 ScratchFile profile;
302
303 ProfileCompilationInfo saved_info;
304 // Save the maximum number of methods
305 for (uint16_t i = 0; i < 10; i++) {
306 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
307 }
308 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
309
310 uint8_t random_data[] = { 1, 2, 3};
311 ASSERT_TRUE(profile.GetFile()->WriteFully(random_data, sizeof(random_data)));
312
313 ASSERT_EQ(0, profile.GetFile()->Flush());
314
315 // Check that we fail because of unexpected data at the end of the file.
316 ProfileCompilationInfo loaded_info;
317 ASSERT_TRUE(profile.GetFile()->ResetOffset());
318 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
319}
320
Calin Juravle877fd962016-01-05 14:29:29 +0000321} // namespace art