• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 <stdlib.h>
20 
21 #include <string>
22 #include <vector>
23 
24 #include "record_file.h"
25 #include "report_utils.h"
26 #include "thread_tree.h"
27 
28 using namespace simpleperf;
29 
TEST(ProguardMappingRetrace,smoke)30 TEST(ProguardMappingRetrace, smoke) {
31   TemporaryFile tmpfile;
32   close(tmpfile.release());
33   ASSERT_TRUE(
34       android::base::WriteStringToFile("original.class.A -> A:\n"
35                                        "\n"
36                                        "    void method_a() -> a\n"
37                                        "    void method_b() -> b\n"
38                                        "      # {\"id\":\"com.android.tools.r8.synthesized\"}\n"
39                                        "      # some other comments\n"
40                                        "    void original.class.M.method_c() -> c\n"
41                                        "    void original.class.A.method_d() -> d\n"
42                                        "original.class.B -> B:\n"
43                                        "# some other comments\n"
44                                        "original.class.C -> C:\n"
45                                        "# {\'id\':\'com.android.tools.r8.synthesized\'}\n",
46                                        tmpfile.path));
47   ProguardMappingRetrace retrace;
48   ASSERT_TRUE(retrace.AddProguardMappingFile(tmpfile.path));
49   std::string original_name;
50   bool synthesized;
51   ASSERT_TRUE(retrace.DeObfuscateJavaMethods("A.a", &original_name, &synthesized));
52   ASSERT_EQ(original_name, "original.class.A.method_a");
53   ASSERT_FALSE(synthesized);
54   ASSERT_TRUE(retrace.DeObfuscateJavaMethods("A.b", &original_name, &synthesized));
55   ASSERT_EQ(original_name, "original.class.A.method_b");
56   ASSERT_TRUE(synthesized);
57   ASSERT_TRUE(retrace.DeObfuscateJavaMethods("A.c", &original_name, &synthesized));
58   ASSERT_EQ(original_name, "original.class.M.method_c");
59   ASSERT_FALSE(synthesized);
60   ASSERT_TRUE(retrace.DeObfuscateJavaMethods("A.d", &original_name, &synthesized));
61   ASSERT_EQ(original_name, "original.class.A.method_d");
62   ASSERT_FALSE(synthesized);
63   ASSERT_TRUE(retrace.DeObfuscateJavaMethods("B.b", &original_name, &synthesized));
64   ASSERT_EQ(original_name, "original.class.B.b");
65   ASSERT_FALSE(synthesized);
66   ASSERT_TRUE(retrace.DeObfuscateJavaMethods("C.c", &original_name, &synthesized));
67   ASSERT_EQ(original_name, "original.class.C.c");
68   ASSERT_TRUE(synthesized);
69 }
70 
71 class CallChainReportBuilderTest : public testing::Test {
72  protected:
SetUp()73   virtual void SetUp() {
74     // To test different options for CallChainReportBuilder, we create a fake thread, add fake
75     // libraries used by the thread, and provide fake symbols in each library. We need four
76     // types of libraries: native, interpreter, jit cache and dex file.
77     thread_tree.SetThreadName(1, 1, "thread1");
78     thread = thread_tree.FindThread(1);
79 
80     // Add symbol info for the native library.
81     SetSymbols(fake_native_lib_path, DSO_ELF_FILE,
82                {
83                    Symbol("native_func1", 0x0, 0x100),
84                    Symbol("art_jni_trampoline", 0x100, 0x100),
85                });
86 
87     // Add symbol info for the interpreter library.
88     SetSymbols(
89         fake_interpreter_path, DSO_ELF_FILE,
90         {
91             Symbol("art_func1", 0x0, 0x100),
92             Symbol("art_func2", 0x100, 0x100),
93             Symbol("_ZN3artL13Method_invokeEP7_JNIEnvP8_jobjectS3_P13_jobjectArray", 0x200, 0x100),
94             Symbol("art_quick_generic_jni_trampoline", 0x300, 0x100),
95         });
96 
97     // Add symbol info for the dex file.
98     SetSymbols(fake_dex_file_path, DSO_DEX_FILE,
99                {
100                    Symbol("java_method1", 0x0, 0x100),
101                    Symbol("java_method2", 0x100, 0x100),
102                    Symbol("obfuscated_class.obfuscated_java_method", 0x200, 0x100),
103                });
104 
105     // Add symbol info for the jit cache.
106     SetSymbols(fake_jit_cache_path, DSO_ELF_FILE,
107                {
108                    Symbol("java_method2", 0x3000, 0x100),
109                    Symbol("java_method3", 0x3100, 0x100),
110                    Symbol("obfuscated_class.obfuscated_java_method2", 0x3200, 0x100),
111                    Symbol("obfuscated_class.java_method4", 0x3300, 0x100),
112                });
113 
114     // Add map layout for libraries used in the thread:
115     // 0x0000 - 0x1000 is mapped to the native library.
116     // 0x1000 - 0x2000 is mapped to the interpreter library.
117     // 0x2000 - 0x3000 is mapped to the dex file.
118     // 0x3000 - 0x4000 is mapped to the jit cache.
119     thread_tree.AddThreadMap(1, 1, 0x0, 0x1000, 0x0, fake_native_lib_path);
120     thread_tree.AddThreadMap(1, 1, 0x1000, 0x1000, 0x0, fake_interpreter_path);
121     thread_tree.AddThreadMap(1, 1, 0x2000, 0x1000, 0x0, fake_dex_file_path);
122     thread_tree.AddThreadMap(1, 1, 0x3000, 0x1000, 0x0, fake_jit_cache_path,
123                              map_flags::PROT_JIT_SYMFILE_MAP);
124   }
125 
SetSymbols(const std::string & path,DsoType dso_type,const std::vector<Symbol> & symbols)126   void SetSymbols(const std::string& path, DsoType dso_type, const std::vector<Symbol>& symbols) {
127     FileFeature file;
128     file.path = path;
129     file.type = dso_type;
130     file.min_vaddr = file.file_offset_of_min_vaddr = 0;
131     file.symbols = symbols;
132     thread_tree.AddDsoInfo(file);
133   }
134 
135   ThreadTree thread_tree;
136   const ThreadEntry* thread;
137   const std::string fake_native_lib_path = "fake_dir/fake_native_lib.so";
138   const std::string fake_interpreter_path = "fake_dir/libart.so";
139   const std::string fake_dex_file_path = "fake_dir/framework.jar";
140   const std::string fake_jit_cache_path = "fake_jit_app_cache:0";
141 
142   const std::vector<uint64_t> fake_ips = {
143       0x1000,  // art_func1
144       0x1100,  // art_func2
145       0x2000,  // java_method1 in dex file
146       0x1000,  // art_func1
147       0x1100,  // art_func2
148       0x3000,  // java_method2 in jit cache
149       0x1000,  // art_func1
150       0x1100,  // art_func2
151   };
152 };
153 
TEST_F(CallChainReportBuilderTest,default_option)154 TEST_F(CallChainReportBuilderTest, default_option) {
155   // Test default option: remove_art_frame = true, convert_jit_frame = true.
156   // The callchain shouldn't include interpreter frames. And the JIT frame is
157   // converted to a dex frame.
158   CallChainReportBuilder builder(thread_tree);
159   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
160   ASSERT_EQ(entries.size(), 2);
161   ASSERT_EQ(entries[0].ip, 0x2000);
162   ASSERT_STREQ(entries[0].symbol->Name(), "java_method1");
163   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
164   ASSERT_EQ(entries[0].vaddr_in_file, 0);
165   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
166   ASSERT_EQ(entries[1].ip, 0x3000);
167   ASSERT_STREQ(entries[1].symbol->Name(), "java_method2");
168   ASSERT_EQ(entries[1].dso->Path(), fake_dex_file_path);
169   ASSERT_EQ(entries[1].vaddr_in_file, 0x100);
170   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
171 }
172 
TEST_F(CallChainReportBuilderTest,not_convert_jit_frame)173 TEST_F(CallChainReportBuilderTest, not_convert_jit_frame) {
174   // Test option: remove_art_frame = true, convert_jit_frame = false.
175   // The callchain shouldn't include interpreter frames. And the JIT frame isn't
176   // converted to a dex frame.
177   CallChainReportBuilder builder(thread_tree);
178   builder.SetConvertJITFrame(false);
179   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
180   ASSERT_EQ(entries.size(), 2);
181   ASSERT_EQ(entries[0].ip, 0x2000);
182   ASSERT_STREQ(entries[0].symbol->Name(), "java_method1");
183   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
184   ASSERT_EQ(entries[0].vaddr_in_file, 0);
185   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
186   ASSERT_EQ(entries[1].ip, 0x3000);
187   ASSERT_STREQ(entries[1].symbol->Name(), "java_method2");
188   ASSERT_EQ(entries[1].dso->Path(), fake_jit_cache_path);
189   ASSERT_EQ(entries[1].vaddr_in_file, 0x3000);
190   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
191 }
192 
TEST_F(CallChainReportBuilderTest,not_remove_art_frame)193 TEST_F(CallChainReportBuilderTest, not_remove_art_frame) {
194   // Test option: remove_art_frame = false, convert_jit_frame = true.
195   // The callchain should include interpreter frames. And the JIT frame is
196   // converted to a dex frame.
197   CallChainReportBuilder builder(thread_tree);
198   builder.SetRemoveArtFrame(false);
199   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
200   ASSERT_EQ(entries.size(), 8);
201   for (size_t i : {0, 3, 6}) {
202     ASSERT_EQ(entries[i].ip, 0x1000);
203     ASSERT_STREQ(entries[i].symbol->Name(), "art_func1");
204     ASSERT_EQ(entries[i].dso->Path(), fake_interpreter_path);
205     ASSERT_EQ(entries[i].vaddr_in_file, 0);
206     ASSERT_EQ(entries[i].execution_type, CallChainExecutionType::ART_METHOD);
207     ASSERT_EQ(entries[i + 1].ip, 0x1100);
208     ASSERT_STREQ(entries[i + 1].symbol->Name(), "art_func2");
209     ASSERT_EQ(entries[i + 1].dso->Path(), fake_interpreter_path);
210     ASSERT_EQ(entries[i + 1].vaddr_in_file, 0x100);
211     ASSERT_EQ(entries[i + 1].execution_type, CallChainExecutionType::ART_METHOD);
212   }
213   ASSERT_EQ(entries[2].ip, 0x2000);
214   ASSERT_STREQ(entries[2].symbol->Name(), "java_method1");
215   ASSERT_EQ(entries[2].dso->Path(), fake_dex_file_path);
216   ASSERT_EQ(entries[2].vaddr_in_file, 0);
217   ASSERT_EQ(entries[2].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
218   ASSERT_EQ(entries[5].ip, 0x3000);
219   ASSERT_STREQ(entries[5].symbol->Name(), "java_method2");
220   ASSERT_EQ(entries[5].dso->Path(), fake_dex_file_path);
221   ASSERT_EQ(entries[5].vaddr_in_file, 0x100);
222   ASSERT_EQ(entries[5].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
223 }
224 
TEST_F(CallChainReportBuilderTest,remove_jit_frame_called_by_dex_frame)225 TEST_F(CallChainReportBuilderTest, remove_jit_frame_called_by_dex_frame) {
226   // Test option: remove_art_frame = true, convert_jit_frame = true.
227   // The callchain should remove the JIT frame called by a dex frame having the same symbol name.
228   std::vector<uint64_t> fake_ips = {
229       0x3000,  // java_method2 in jit cache
230       0x1000,  // art_func1
231       0x1100,  // art_func2
232       0x2100,  // java_method2 in dex file
233       0x1000,  // art_func1
234   };
235   CallChainReportBuilder builder(thread_tree);
236   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
237   ASSERT_EQ(entries.size(), 1);
238   ASSERT_EQ(entries[0].ip, 0x2100);
239   ASSERT_STREQ(entries[0].symbol->Name(), "java_method2");
240   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
241   ASSERT_EQ(entries[0].vaddr_in_file, 0x100);
242   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
243 }
244 
TEST_F(CallChainReportBuilderTest,remove_art_frame_only_near_jvm_method)245 TEST_F(CallChainReportBuilderTest, remove_art_frame_only_near_jvm_method) {
246   // Test option: remove_art_frame = true, convert_jit_frame = true.
247   // The callchain should not remove ART symbols not near a JVM method.
248   std::vector<uint64_t> fake_ips = {
249       0x1000,  // art_func1
250       0x0,     // native_func1
251       0x2000,  // java_method1 in dex file
252       0x0,     // native_func1
253       0x1000,  // art_func1
254   };
255   CallChainReportBuilder builder(thread_tree);
256   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
257   ASSERT_EQ(entries.size(), 5);
258   for (size_t i : {0, 4}) {
259     ASSERT_EQ(entries[i].ip, 0x1000);
260     ASSERT_STREQ(entries[i].symbol->Name(), "art_func1");
261     ASSERT_EQ(entries[i].dso->Path(), fake_interpreter_path);
262     ASSERT_EQ(entries[i].vaddr_in_file, 0);
263     ASSERT_EQ(entries[i].execution_type, CallChainExecutionType::NATIVE_METHOD);
264   }
265   for (size_t i : {1, 3}) {
266     ASSERT_EQ(entries[i].ip, 0x0);
267     ASSERT_STREQ(entries[i].symbol->Name(), "native_func1");
268     ASSERT_EQ(entries[i].dso->Path(), fake_native_lib_path);
269     ASSERT_EQ(entries[i].vaddr_in_file, 0);
270     ASSERT_EQ(entries[i].execution_type, CallChainExecutionType::NATIVE_METHOD);
271   }
272 
273   ASSERT_EQ(entries[2].ip, 0x2000);
274   ASSERT_STREQ(entries[2].symbol->Name(), "java_method1");
275   ASSERT_EQ(entries[2].dso->Path(), fake_dex_file_path);
276   ASSERT_EQ(entries[2].vaddr_in_file, 0x0);
277   ASSERT_EQ(entries[2].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
278 }
279 
TEST_F(CallChainReportBuilderTest,keep_art_jni_method)280 TEST_F(CallChainReportBuilderTest, keep_art_jni_method) {
281   // Test option: remove_art_frame = true.
282   // The callchain should remove art_jni_trampoline, but keep jni methods.
283   std::vector<uint64_t> fake_ips = {
284       0x1200,  // art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)
285       0x100,   // art_jni_trampoline
286       0x2000,  // java_method1 in dex file
287       0x1200,  // art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)
288       0x1300,  // art_quick_generic_jni_trampoline
289   };
290   CallChainReportBuilder builder(thread_tree);
291   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
292   ASSERT_EQ(entries.size(), 3);
293   for (size_t i : {0, 2}) {
294     ASSERT_EQ(entries[i].ip, 0x1200);
295     ASSERT_STREQ(entries[i].symbol->DemangledName(),
296                  "art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)");
297     ASSERT_EQ(entries[i].dso->Path(), fake_interpreter_path);
298     ASSERT_EQ(entries[i].vaddr_in_file, 0x200);
299     ASSERT_EQ(entries[i].execution_type, CallChainExecutionType::NATIVE_METHOD);
300   }
301   ASSERT_EQ(entries[1].ip, 0x2000);
302   ASSERT_STREQ(entries[1].symbol->Name(), "java_method1");
303   ASSERT_EQ(entries[1].dso->Path(), fake_dex_file_path);
304   ASSERT_EQ(entries[1].vaddr_in_file, 0x0);
305   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
306 }
307 
TEST_F(CallChainReportBuilderTest,add_proguard_mapping_file)308 TEST_F(CallChainReportBuilderTest, add_proguard_mapping_file) {
309   std::vector<uint64_t> fake_ips = {
310       0x2200,  // 2200,  // obfuscated_class.obfuscated_java_method
311       0x3200,  // 3200,  // obfuscated_class.obfuscated_java_method2
312       0x3300,  // 3300,  // obfuscated_class.java_method4
313   };
314   CallChainReportBuilder builder(thread_tree);
315   // Symbol names aren't changed when not given proguard mapping files.
316   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
317   ASSERT_EQ(entries.size(), 3);
318   ASSERT_EQ(entries[0].ip, 0x2200);
319   ASSERT_STREQ(entries[0].symbol->DemangledName(), "obfuscated_class.obfuscated_java_method");
320   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
321   ASSERT_EQ(entries[0].vaddr_in_file, 0x200);
322   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
323   ASSERT_EQ(entries[1].ip, 0x3200);
324   ASSERT_STREQ(entries[1].symbol->DemangledName(), "obfuscated_class.obfuscated_java_method2");
325   ASSERT_EQ(entries[1].dso->Path(), fake_jit_cache_path);
326   ASSERT_EQ(entries[1].vaddr_in_file, 0x3200);
327   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
328   ASSERT_EQ(entries[2].ip, 0x3300);
329   ASSERT_STREQ(entries[2].symbol->DemangledName(), "obfuscated_class.java_method4");
330   ASSERT_EQ(entries[2].dso->Path(), fake_jit_cache_path);
331   ASSERT_EQ(entries[2].vaddr_in_file, 0x3300);
332   ASSERT_EQ(entries[2].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
333 
334   // Symbol names are changed when given a proguard mapping file.
335   TemporaryFile tmpfile;
336   close(tmpfile.release());
337   ASSERT_TRUE(android::base::WriteStringToFile(
338       "android.support.v4.app.RemoteActionCompatParcelizer -> obfuscated_class:\n"
339       "    13:13:androidx.core.app.RemoteActionCompat read(androidx.versionedparcelable.Versioned"
340       "Parcel) -> obfuscated_java_method\n"
341       "    13:13:androidx.core.app.RemoteActionCompat "
342       "android.support.v4.app.RemoteActionCompatParcelizer.read2(androidx.versionedparcelable."
343       "VersionedParcel) -> obfuscated_java_method2",
344       tmpfile.path));
345   builder.AddProguardMappingFile(tmpfile.path);
346   entries = builder.Build(thread, fake_ips, 0);
347   ASSERT_EQ(entries.size(), 3);
348   ASSERT_EQ(entries[0].ip, 0x2200);
349   ASSERT_STREQ(entries[0].symbol->DemangledName(),
350                "android.support.v4.app.RemoteActionCompatParcelizer.read");
351   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
352   ASSERT_EQ(entries[0].vaddr_in_file, 0x200);
353   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
354   ASSERT_EQ(entries[1].ip, 0x3200);
355   ASSERT_STREQ(entries[1].symbol->DemangledName(),
356                "android.support.v4.app.RemoteActionCompatParcelizer.read2");
357   ASSERT_EQ(entries[1].dso->Path(), fake_jit_cache_path);
358   ASSERT_EQ(entries[1].vaddr_in_file, 0x3200);
359   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
360   ASSERT_STREQ(entries[2].symbol->DemangledName(),
361                "android.support.v4.app.RemoteActionCompatParcelizer.java_method4");
362   ASSERT_EQ(entries[2].dso->Path(), fake_jit_cache_path);
363   ASSERT_EQ(entries[2].vaddr_in_file, 0x3300);
364   ASSERT_EQ(entries[2].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
365 }
366 
TEST_F(CallChainReportBuilderTest,not_remove_synthesized_frame_by_default)367 TEST_F(CallChainReportBuilderTest, not_remove_synthesized_frame_by_default) {
368   std::vector<uint64_t> fake_ips = {
369       0x2200,  // 2200,  // obfuscated_class.obfuscated_java_method
370       0x3200,  // 3200,  // obfuscated_class.obfuscated_java_method2
371   };
372 
373   TemporaryFile tmpfile;
374   ASSERT_TRUE(android::base::WriteStringToFile(
375       "android.support.v4.app.RemoteActionCompatParcelizer -> obfuscated_class:\n"
376       "    13:13:androidx.core.app.RemoteActionCompat read(androidx.versionedparcelable.Versioned"
377       "Parcel) -> obfuscated_java_method\n"
378       "      # {\"id\":\"com.android.tools.r8.synthesized\"}\n"
379       "    13:13:androidx.core.app.RemoteActionCompat "
380       "android.support.v4.app.RemoteActionCompatParcelizer.read2(androidx.versionedparcelable."
381       "VersionedParcel) -> obfuscated_java_method2",
382       tmpfile.path));
383 
384   // By default, synthesized frames are kept.
385   CallChainReportBuilder builder(thread_tree);
386   builder.AddProguardMappingFile(tmpfile.path);
387   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
388   ASSERT_EQ(entries.size(), 2);
389   ASSERT_EQ(entries[0].ip, 0x2200);
390   ASSERT_STREQ(entries[0].symbol->DemangledName(),
391                "android.support.v4.app.RemoteActionCompatParcelizer.read");
392   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
393   ASSERT_EQ(entries[0].vaddr_in_file, 0x200);
394   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
395   ASSERT_EQ(entries[1].ip, 0x3200);
396   ASSERT_STREQ(entries[1].symbol->DemangledName(),
397                "android.support.v4.app.RemoteActionCompatParcelizer.read2");
398   ASSERT_EQ(entries[1].dso->Path(), fake_jit_cache_path);
399   ASSERT_EQ(entries[1].vaddr_in_file, 0x3200);
400   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
401 }
402 
TEST_F(CallChainReportBuilderTest,remove_synthesized_frame_with_env_variable)403 TEST_F(CallChainReportBuilderTest, remove_synthesized_frame_with_env_variable) {
404   // Windows doesn't support setenv and unsetenv. So don't test on it.
405 #if !defined(__WIN32)
406   std::vector<uint64_t> fake_ips = {
407       0x2200,  // 2200,  // obfuscated_class.obfuscated_java_method
408       0x3200,  // 3200,  // obfuscated_class.obfuscated_java_method2
409   };
410 
411   TemporaryFile tmpfile;
412   ASSERT_TRUE(android::base::WriteStringToFile(
413       "android.support.v4.app.RemoteActionCompatParcelizer -> obfuscated_class:\n"
414       "    13:13:androidx.core.app.RemoteActionCompat read(androidx.versionedparcelable.Versioned"
415       "Parcel) -> obfuscated_java_method\n"
416       "      # {\"id\":\"com.android.tools.r8.synthesized\"}\n"
417       "    13:13:androidx.core.app.RemoteActionCompat "
418       "android.support.v4.app.RemoteActionCompatParcelizer.read2(androidx.versionedparcelable."
419       "VersionedParcel) -> obfuscated_java_method2",
420       tmpfile.path));
421 
422   // With environment variable set, synthesized frames are removed.
423   ASSERT_EQ(setenv("REMOVE_R8_SYNTHESIZED_FRAME", "1", 1), 0);
424   CallChainReportBuilder builder(thread_tree);
425   ASSERT_EQ(unsetenv("REMOVE_R8_SYNTHESIZED_FRAME"), 0);
426   builder.AddProguardMappingFile(tmpfile.path);
427   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
428   ASSERT_EQ(entries.size(), 1);
429   ASSERT_EQ(entries[0].ip, 0x3200);
430   ASSERT_STREQ(entries[0].symbol->DemangledName(),
431                "android.support.v4.app.RemoteActionCompatParcelizer.read2");
432   ASSERT_EQ(entries[0].dso->Path(), fake_jit_cache_path);
433   ASSERT_EQ(entries[0].vaddr_in_file, 0x3200);
434   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
435 #endif  // !defined(__WIN32)
436 }
437 
TEST_F(CallChainReportBuilderTest,add_proguard_mapping_file_for_jit_method_with_signature)438 TEST_F(CallChainReportBuilderTest, add_proguard_mapping_file_for_jit_method_with_signature) {
439   std::vector<uint64_t> fake_ips = {
440       0x3200,  // 3200,  // void ctep.v(cteo, ctgc, ctbn)
441   };
442   SetSymbols(fake_jit_cache_path, DSO_ELF_FILE,
443              {Symbol("void ctep.v(cteo, ctgc, ctbn)", 0x3200, 0x100)});
444   CallChainReportBuilder builder(thread_tree);
445   TemporaryFile tmpfile;
446   close(tmpfile.release());
447   ASSERT_TRUE(android::base::WriteStringToFile(
448       "android.support.v4.app.RemoteActionCompatParcelizer -> ctep:\n"
449       "    13:13:androidx.core.app.RemoteActionCompat read(androidx.versionedparcelable.Versioned"
450       "Parcel) -> v\n",
451       tmpfile.path));
452   builder.AddProguardMappingFile(tmpfile.path);
453   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
454   ASSERT_EQ(entries.size(), 1);
455   ASSERT_EQ(entries[0].ip, 0x3200);
456   ASSERT_STREQ(entries[0].symbol->DemangledName(),
457                "android.support.v4.app.RemoteActionCompatParcelizer.read");
458   ASSERT_EQ(entries[0].dso->Path(), fake_jit_cache_path);
459   ASSERT_EQ(entries[0].vaddr_in_file, 0x3200);
460   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
461 }
462 
TEST_F(CallChainReportBuilderTest,add_proguard_mapping_file_for_compiled_java_method_with_signature)463 TEST_F(CallChainReportBuilderTest,
464        add_proguard_mapping_file_for_compiled_java_method_with_signature) {
465   TemporaryFile tmpfile;
466   close(tmpfile.release());
467   ASSERT_TRUE(android::base::WriteStringToFile(
468       "android.support.v4.app.RemoteActionCompatParcelizer -> ctep:\n"
469       "    13:13:androidx.core.app.RemoteActionCompat read(androidx.versionedparcelable.Versioned"
470       "Parcel) -> v\n",
471       tmpfile.path));
472 
473   for (const char* suffix : {".odex", ".oat", ".dex"}) {
474     std::string compiled_java_path = "compiled_java" + std::string(suffix);
475     SetSymbols(compiled_java_path, DSO_ELF_FILE,
476                {Symbol("void ctep.v(cteo, ctgc, ctbn)", 0x0, 0x100)});
477     thread_tree.AddThreadMap(1, 1, 0x4000, 0x1000, 0x0, compiled_java_path);
478     std::vector<uint64_t> fake_ips = {
479         0x4000,  // 4000,  // void ctep.v(cteo, ctgc, ctbn)
480     };
481 
482     CallChainReportBuilder builder(thread_tree);
483     builder.AddProguardMappingFile(tmpfile.path);
484     std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
485     ASSERT_EQ(entries.size(), 1);
486     ASSERT_EQ(entries[0].ip, 0x4000);
487     ASSERT_STREQ(entries[0].symbol->DemangledName(),
488                  "android.support.v4.app.RemoteActionCompatParcelizer.read");
489     ASSERT_EQ(entries[0].dso->Path(), compiled_java_path);
490     ASSERT_EQ(entries[0].vaddr_in_file, 0x0);
491     ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::NATIVE_METHOD);
492   }
493 }
494 
TEST_F(CallChainReportBuilderTest,convert_jit_frame_for_jit_method_with_signature)495 TEST_F(CallChainReportBuilderTest, convert_jit_frame_for_jit_method_with_signature) {
496   std::vector<uint64_t> fake_ips = {
497       0x2200,  // 2200,  // ctep.v
498       0x3200,  // 3200,  // void ctep.v(cteo, ctgc, ctbn)
499   };
500   SetSymbols(fake_dex_file_path, DSO_DEX_FILE, {Symbol("ctep.v", 0x200, 0x100)});
501   SetSymbols(fake_jit_cache_path, DSO_ELF_FILE,
502              {Symbol("void ctep.v(cteo, ctgc, ctbn)", 0x3200, 0x100)});
503   CallChainReportBuilder builder(thread_tree);
504   // Test if we can convert jit method with signature.
505   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
506   ASSERT_EQ(entries.size(), 2);
507   ASSERT_EQ(entries[0].ip, 0x2200);
508   ASSERT_STREQ(entries[0].symbol->DemangledName(), "ctep.v");
509   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
510   ASSERT_EQ(entries[0].vaddr_in_file, 0x200);
511   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
512   ASSERT_EQ(entries[1].ip, 0x3200);
513   ASSERT_STREQ(entries[1].symbol->DemangledName(), "ctep.v");
514   ASSERT_EQ(entries[1].dso->Path(), fake_dex_file_path);
515   ASSERT_EQ(entries[1].vaddr_in_file, 0x200);
516   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
517 
518   // Test adding proguard mapping file.
519   TemporaryFile tmpfile;
520   close(tmpfile.release());
521   ASSERT_TRUE(android::base::WriteStringToFile(
522       "android.support.v4.app.RemoteActionCompatParcelizer -> ctep:\n"
523       "    13:13:androidx.core.app.RemoteActionCompat read(androidx.versionedparcelable.Versioned"
524       "Parcel) -> v\n",
525       tmpfile.path));
526   builder.AddProguardMappingFile(tmpfile.path);
527   entries = builder.Build(thread, fake_ips, 0);
528   ASSERT_EQ(entries.size(), 2);
529   ASSERT_EQ(entries[0].ip, 0x2200);
530   ASSERT_STREQ(entries[0].symbol->DemangledName(),
531                "android.support.v4.app.RemoteActionCompatParcelizer.read");
532   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
533   ASSERT_EQ(entries[0].vaddr_in_file, 0x200);
534   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
535   ASSERT_EQ(entries[1].ip, 0x3200);
536   ASSERT_STREQ(entries[1].symbol->DemangledName(),
537                "android.support.v4.app.RemoteActionCompatParcelizer.read");
538   ASSERT_EQ(entries[1].dso->Path(), fake_dex_file_path);
539   ASSERT_EQ(entries[1].vaddr_in_file, 0x200);
540   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
541 }
542 
543 class ThreadReportBuilderTest : public testing::Test {
544  protected:
SetUp()545   virtual void SetUp() {
546     thread_tree.SetThreadName(1, 1, "thread1");
547     thread_tree.SetThreadName(1, 2, "thread-pool1");
548     thread_tree.SetThreadName(1, 3, "thread-pool2");
549   }
550 
IsReportEqual(const ThreadReport & report1,const ThreadReport & report2)551   bool IsReportEqual(const ThreadReport& report1, const ThreadReport& report2) {
552     return report1.pid == report2.pid && report1.tid == report2.tid &&
553            strcmp(report1.thread_name, report2.thread_name) == 0;
554   }
555 
556   ThreadTree thread_tree;
557 };
558 
TEST_F(ThreadReportBuilderTest,no_setting)559 TEST_F(ThreadReportBuilderTest, no_setting) {
560   ThreadReportBuilder builder;
561   ThreadEntry* thread = thread_tree.FindThread(1);
562   ThreadReport report = builder.Build(*thread);
563   ASSERT_TRUE(IsReportEqual(report, ThreadReport(1, 1, "thread1")));
564 }
565 
TEST_F(ThreadReportBuilderTest,aggregate_threads)566 TEST_F(ThreadReportBuilderTest, aggregate_threads) {
567   ThreadReportBuilder builder;
568   ASSERT_TRUE(builder.AggregateThreads({"thread-pool.*"}));
569   ThreadEntry* thread = thread_tree.FindThread(1);
570   ThreadReport report = builder.Build(*thread);
571   ASSERT_TRUE(IsReportEqual(report, ThreadReport(1, 1, "thread1")));
572   thread = thread_tree.FindThread(2);
573   report = builder.Build(*thread);
574   ASSERT_TRUE(IsReportEqual(report, ThreadReport(1, 2, "thread-pool.*")));
575   thread = thread_tree.FindThread(3);
576   report = builder.Build(*thread);
577   ASSERT_TRUE(IsReportEqual(report, ThreadReport(1, 2, "thread-pool.*")));
578 }
579 
TEST_F(ThreadReportBuilderTest,aggregate_threads_bad_regex)580 TEST_F(ThreadReportBuilderTest, aggregate_threads_bad_regex) {
581   ThreadReportBuilder builder;
582   ASSERT_FALSE(builder.AggregateThreads({"?thread-pool*"}));
583 }
584