blob: db681f4cb67ce49d532ff4b26be11d76ca736d5b [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
70 // Cannot sizeof the actual arrays so hardcode the values here.
71 // They should not change anyway.
72 static constexpr int kProfileMagicSize = 4;
73 static constexpr int kProfileVersionSize = 4;
Calin Juravle877fd962016-01-05 14:29:29 +000074};
75
76TEST_F(ProfileCompilationInfoTest, SaveArtMethods) {
77 ScratchFile profile;
78
79 Thread* self = Thread::Current();
80 jobject class_loader;
81 {
82 ScopedObjectAccess soa(self);
83 class_loader = LoadDex("ProfileTestMultiDex");
84 }
85 ASSERT_NE(class_loader, nullptr);
86
87 // Save virtual methods from Main.
Mathieu Chartier8913fc12015-12-09 16:38:30 -080088 std::set<DexCacheResolvedClasses> resolved_classes;
Calin Juravle877fd962016-01-05 14:29:29 +000089 std::vector<ArtMethod*> main_methods = GetVirtualMethods(class_loader, "LMain;");
Mathieu Chartier8913fc12015-12-09 16:38:30 -080090 ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(),
91 main_methods,
92 resolved_classes));
Calin Juravle877fd962016-01-05 14:29:29 +000093
94 // Check that what we saved is in the profile.
95 ProfileCompilationInfo info1;
96 ASSERT_TRUE(info1.Load(GetFd(profile)));
97 ASSERT_EQ(info1.GetNumberOfMethods(), main_methods.size());
98 {
99 ScopedObjectAccess soa(self);
100 for (ArtMethod* m : main_methods) {
101 ASSERT_TRUE(info1.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())));
102 }
103 }
104
105 // Save virtual methods from Second.
106 std::vector<ArtMethod*> second_methods = GetVirtualMethods(class_loader, "LSecond;");
Mathieu Chartier8913fc12015-12-09 16:38:30 -0800107 ASSERT_TRUE(ProfileCompilationInfo::SaveProfilingInfo(profile.GetFilename(),
108 second_methods,
109 resolved_classes));
Calin Juravle877fd962016-01-05 14:29:29 +0000110
111 // Check that what we saved is in the profile (methods form Main and Second).
112 ProfileCompilationInfo info2;
113 ASSERT_TRUE(profile.GetFile()->ResetOffset());
114 ASSERT_TRUE(info2.Load(GetFd(profile)));
115 ASSERT_EQ(info2.GetNumberOfMethods(), main_methods.size() + second_methods.size());
116 {
117 ScopedObjectAccess soa(self);
118 for (ArtMethod* m : main_methods) {
119 ASSERT_TRUE(info2.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())));
120 }
121 for (ArtMethod* m : second_methods) {
122 ASSERT_TRUE(info2.ContainsMethod(MethodReference(m->GetDexFile(), m->GetDexMethodIndex())));
123 }
124 }
125}
126
127TEST_F(ProfileCompilationInfoTest, SaveFd) {
128 ScratchFile profile;
129
130 ProfileCompilationInfo saved_info;
131 // Save a few methods.
132 for (uint16_t i = 0; i < 10; i++) {
Calin Juravle85f7bf32016-03-18 16:23:40 +0000133 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
134 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
Calin Juravle877fd962016-01-05 14:29:29 +0000135 }
136 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
137 ASSERT_EQ(0, profile.GetFile()->Flush());
138
139 // Check that we get back what we saved.
140 ProfileCompilationInfo loaded_info;
141 ASSERT_TRUE(profile.GetFile()->ResetOffset());
142 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
143 ASSERT_TRUE(loaded_info.Equals(saved_info));
144
145 // Save more methods.
146 for (uint16_t i = 0; i < 100; i++) {
Calin Juravle85f7bf32016-03-18 16:23:40 +0000147 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
148 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
149 ASSERT_TRUE(AddMethod("dex_location3", /* checksum */ 3, /* method_idx */ i, &saved_info));
Calin Juravle877fd962016-01-05 14:29:29 +0000150 }
151 ASSERT_TRUE(profile.GetFile()->ResetOffset());
152 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
153 ASSERT_EQ(0, profile.GetFile()->Flush());
154
155 // Check that we get back everything we saved.
156 ProfileCompilationInfo loaded_info2;
157 ASSERT_TRUE(profile.GetFile()->ResetOffset());
158 ASSERT_TRUE(loaded_info2.Load(GetFd(profile)));
159 ASSERT_TRUE(loaded_info2.Equals(saved_info));
160}
161
Calin Juravle85f7bf32016-03-18 16:23:40 +0000162TEST_F(ProfileCompilationInfoTest, AddMethodsAndClassesFail) {
Calin Juravle877fd962016-01-05 14:29:29 +0000163 ScratchFile profile;
164
165 ProfileCompilationInfo info;
Calin Juravle85f7bf32016-03-18 16:23:40 +0000166 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info));
Calin Juravle877fd962016-01-05 14:29:29 +0000167 // Trying to add info for an existing file but with a different checksum.
Calin Juravle85f7bf32016-03-18 16:23:40 +0000168 ASSERT_FALSE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info));
Calin Juravle877fd962016-01-05 14:29:29 +0000169}
170
Calin Juravle85f7bf32016-03-18 16:23:40 +0000171TEST_F(ProfileCompilationInfoTest, MergeFail) {
Calin Juravle877fd962016-01-05 14:29:29 +0000172 ScratchFile profile;
173
174 ProfileCompilationInfo info1;
Calin Juravle85f7bf32016-03-18 16:23:40 +0000175 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 1, /* method_idx */ 1, &info1));
Calin Juravle877fd962016-01-05 14:29:29 +0000176 // Use the same file, change the checksum.
177 ProfileCompilationInfo info2;
Calin Juravle85f7bf32016-03-18 16:23:40 +0000178 ASSERT_TRUE(AddMethod("dex_location", /* checksum */ 2, /* method_idx */ 2, &info2));
Calin Juravle877fd962016-01-05 14:29:29 +0000179
Calin Juravle85f7bf32016-03-18 16:23:40 +0000180 ASSERT_FALSE(info1.MergeWith(info2));
Calin Juravle877fd962016-01-05 14:29:29 +0000181}
182
Calin Juravleb8697b12016-03-21 14:37:55 +0000183TEST_F(ProfileCompilationInfoTest, SaveMaxMethods) {
184 ScratchFile profile;
185
186 ProfileCompilationInfo saved_info;
187 // Save the maximum number of methods
188 for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
189 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
190 ASSERT_TRUE(AddMethod("dex_location2", /* checksum */ 2, /* method_idx */ i, &saved_info));
191 }
192 // Save the maximum number of classes
193 for (uint16_t i = 0; i < std::numeric_limits<uint16_t>::max(); i++) {
194 ASSERT_TRUE(AddClass("dex_location1", /* checksum */ 1, /* class_idx */ i, &saved_info));
195 ASSERT_TRUE(AddClass("dex_location2", /* checksum */ 2, /* class_idx */ i, &saved_info));
196 }
197
198 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
199 ASSERT_EQ(0, profile.GetFile()->Flush());
200
201 // Check that we get back what we saved.
202 ProfileCompilationInfo loaded_info;
203 ASSERT_TRUE(profile.GetFile()->ResetOffset());
204 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
205 ASSERT_TRUE(loaded_info.Equals(saved_info));
206}
207
208TEST_F(ProfileCompilationInfoTest, SaveEmpty) {
209 ScratchFile profile;
210
211 ProfileCompilationInfo saved_info;
212 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
213 ASSERT_EQ(0, profile.GetFile()->Flush());
214
215 // Check that we get back what we saved.
216 ProfileCompilationInfo loaded_info;
217 ASSERT_TRUE(profile.GetFile()->ResetOffset());
218 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
219 ASSERT_TRUE(loaded_info.Equals(saved_info));
220}
221
222TEST_F(ProfileCompilationInfoTest, LoadEmpty) {
223 ScratchFile profile;
224
225 ProfileCompilationInfo empyt_info;
226
227 ProfileCompilationInfo loaded_info;
228 ASSERT_TRUE(profile.GetFile()->ResetOffset());
229 ASSERT_TRUE(loaded_info.Load(GetFd(profile)));
230 ASSERT_TRUE(loaded_info.Equals(empyt_info));
231}
232
233TEST_F(ProfileCompilationInfoTest, BadMagic) {
234 ScratchFile profile;
235 uint8_t buffer[] = { 1, 2, 3, 4 };
236 ASSERT_TRUE(profile.GetFile()->WriteFully(buffer, sizeof(buffer)));
237 ProfileCompilationInfo loaded_info;
238 ASSERT_TRUE(profile.GetFile()->ResetOffset());
239 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
240}
241
242TEST_F(ProfileCompilationInfoTest, BadVersion) {
243 ScratchFile profile;
244
245 ASSERT_TRUE(profile.GetFile()->WriteFully(
246 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
247 uint8_t version[] = { 'v', 'e', 'r', 's', 'i', 'o', 'n' };
248 ASSERT_TRUE(profile.GetFile()->WriteFully(version, sizeof(version)));
249 ASSERT_EQ(0, profile.GetFile()->Flush());
250
251 ProfileCompilationInfo loaded_info;
252 ASSERT_TRUE(profile.GetFile()->ResetOffset());
253 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
254}
255
256TEST_F(ProfileCompilationInfoTest, Incomplete) {
257 ScratchFile profile;
258 ASSERT_TRUE(profile.GetFile()->WriteFully(
259 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
260 ASSERT_TRUE(profile.GetFile()->WriteFully(
261 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
262 // Write that we have at least one line.
263 uint8_t line_number[] = { 0, 1 };
264 ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
265 ASSERT_EQ(0, profile.GetFile()->Flush());
266
267 ProfileCompilationInfo loaded_info;
268 ASSERT_TRUE(profile.GetFile()->ResetOffset());
269 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
270}
271
272TEST_F(ProfileCompilationInfoTest, TooLongDexLocation) {
273 ScratchFile profile;
274 ASSERT_TRUE(profile.GetFile()->WriteFully(
275 ProfileCompilationInfo::kProfileMagic, kProfileMagicSize));
276 ASSERT_TRUE(profile.GetFile()->WriteFully(
277 ProfileCompilationInfo::kProfileVersion, kProfileVersionSize));
278 // Write that we have at least one line.
279 uint8_t line_number[] = { 0, 1 };
280 ASSERT_TRUE(profile.GetFile()->WriteFully(line_number, sizeof(line_number)));
281
282 // dex_location_size, methods_size, classes_size, checksum.
283 // Dex location size is too big and should be rejected.
284 uint8_t line[] = { 255, 255, 0, 1, 0, 1, 0, 0, 0, 0 };
285 ASSERT_TRUE(profile.GetFile()->WriteFully(line, sizeof(line)));
286 ASSERT_EQ(0, profile.GetFile()->Flush());
287
288 ProfileCompilationInfo loaded_info;
289 ASSERT_TRUE(profile.GetFile()->ResetOffset());
290 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
291}
292
293TEST_F(ProfileCompilationInfoTest, UnexpectedContent) {
294 ScratchFile profile;
295
296 ProfileCompilationInfo saved_info;
297 // Save the maximum number of methods
298 for (uint16_t i = 0; i < 10; i++) {
299 ASSERT_TRUE(AddMethod("dex_location1", /* checksum */ 1, /* method_idx */ i, &saved_info));
300 }
301 ASSERT_TRUE(saved_info.Save(GetFd(profile)));
302
303 uint8_t random_data[] = { 1, 2, 3};
304 ASSERT_TRUE(profile.GetFile()->WriteFully(random_data, sizeof(random_data)));
305
306 ASSERT_EQ(0, profile.GetFile()->Flush());
307
308 // Check that we fail because of unexpected data at the end of the file.
309 ProfileCompilationInfo loaded_info;
310 ASSERT_TRUE(profile.GetFile()->ResetOffset());
311 ASSERT_FALSE(loaded_info.Load(GetFd(profile)));
312}
313
Calin Juravle877fd962016-01-05 14:29:29 +0000314} // namespace art