• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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