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