• 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 <string>
20 #include <vector>
21 
22 #include "record_file.h"
23 #include "report_utils.h"
24 #include "thread_tree.h"
25 
26 using namespace simpleperf;
27 
28 class CallChainReportBuilderTest : public testing::Test {
29  protected:
SetUp()30   virtual void SetUp() {
31     // To test different options for CallChainReportBuilder, we create a fake thread, add fake
32     // libraries used by the thread, and provide fake symbols in each library. We need four
33     // types of libraries: native, interpreter, jit cache and dex file.
34     thread_tree.SetThreadName(1, 1, "thread1");
35     thread = thread_tree.FindThread(1);
36 
37     // Add symbol info for the native library.
38     SetSymbols(fake_native_lib_path, DSO_ELF_FILE,
39                {
40                    Symbol("native_func1", 0x0, 0x100),
41                    Symbol("art_jni_trampoline", 0x100, 0x100),
42                });
43 
44     // Add symbol info for the interpreter library.
45     SetSymbols(
46         fake_interpreter_path, DSO_ELF_FILE,
47         {
48             Symbol("art_func1", 0x0, 0x100),
49             Symbol("art_func2", 0x100, 0x100),
50             Symbol("_ZN3artL13Method_invokeEP7_JNIEnvP8_jobjectS3_P13_jobjectArray", 0x200, 0x100),
51             Symbol("art_quick_generic_jni_trampoline", 0x300, 0x100),
52         });
53 
54     // Add symbol info for the dex file.
55     SetSymbols(fake_dex_file_path, DSO_DEX_FILE,
56                {
57                    Symbol("java_method1", 0x0, 0x100),
58                    Symbol("java_method2", 0x100, 0x100),
59                    Symbol("obfuscated_class.obfuscated_java_method", 0x200, 0x100),
60                });
61 
62     // Add symbol info for the jit cache.
63     SetSymbols(fake_jit_cache_path, DSO_ELF_FILE,
64                {
65                    Symbol("java_method2", 0x3000, 0x100),
66                    Symbol("java_method3", 0x3100, 0x100),
67                    Symbol("obfuscated_class.obfuscated_java_method2", 0x3200, 0x100),
68                });
69 
70     // Add map layout for libraries used in the thread:
71     // 0x0000 - 0x1000 is mapped to the native library.
72     // 0x1000 - 0x2000 is mapped to the interpreter library.
73     // 0x2000 - 0x3000 is mapped to the dex file.
74     // 0x3000 - 0x4000 is mapped to the jit cache.
75     thread_tree.AddThreadMap(1, 1, 0x0, 0x1000, 0x0, fake_native_lib_path);
76     thread_tree.AddThreadMap(1, 1, 0x1000, 0x1000, 0x0, fake_interpreter_path);
77     thread_tree.AddThreadMap(1, 1, 0x2000, 0x1000, 0x0, fake_dex_file_path);
78     thread_tree.AddThreadMap(1, 1, 0x3000, 0x1000, 0x0, fake_jit_cache_path,
79                              map_flags::PROT_JIT_SYMFILE_MAP);
80   }
81 
SetSymbols(const std::string & path,DsoType dso_type,const std::vector<Symbol> & symbols)82   void SetSymbols(const std::string& path, DsoType dso_type, const std::vector<Symbol>& symbols) {
83     FileFeature file;
84     file.path = path;
85     file.type = dso_type;
86     file.min_vaddr = file.file_offset_of_min_vaddr = 0;
87     file.symbols = symbols;
88     thread_tree.AddDsoInfo(file);
89   }
90 
91   ThreadTree thread_tree;
92   const ThreadEntry* thread;
93   const std::string fake_native_lib_path = "fake_dir/fake_native_lib.so";
94   const std::string fake_interpreter_path = "fake_dir/libart.so";
95   const std::string fake_dex_file_path = "fake_dir/framework.jar";
96   const std::string fake_jit_cache_path = "fake_jit_app_cache:0";
97 
98   const std::vector<uint64_t> fake_ips = {
99       0x1000,  // art_func1
100       0x1100,  // art_func2
101       0x2000,  // java_method1 in dex file
102       0x1000,  // art_func1
103       0x1100,  // art_func2
104       0x3000,  // java_method2 in jit cache
105       0x1000,  // art_func1
106       0x1100,  // art_func2
107   };
108 };
109 
TEST_F(CallChainReportBuilderTest,default_option)110 TEST_F(CallChainReportBuilderTest, default_option) {
111   // Test default option: remove_art_frame = true, convert_jit_frame = true.
112   // The callchain shouldn't include interpreter frames. And the JIT frame is
113   // converted to a dex frame.
114   CallChainReportBuilder builder(thread_tree);
115   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
116   ASSERT_EQ(entries.size(), 2);
117   ASSERT_EQ(entries[0].ip, 0x2000);
118   ASSERT_STREQ(entries[0].symbol->Name(), "java_method1");
119   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
120   ASSERT_EQ(entries[0].vaddr_in_file, 0);
121   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
122   ASSERT_EQ(entries[1].ip, 0x3000);
123   ASSERT_STREQ(entries[1].symbol->Name(), "java_method2");
124   ASSERT_EQ(entries[1].dso->Path(), fake_dex_file_path);
125   ASSERT_EQ(entries[1].vaddr_in_file, 0x100);
126   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
127 }
128 
TEST_F(CallChainReportBuilderTest,not_convert_jit_frame)129 TEST_F(CallChainReportBuilderTest, not_convert_jit_frame) {
130   // Test option: remove_art_frame = true, convert_jit_frame = false.
131   // The callchain shouldn't include interpreter frames. And the JIT frame isn't
132   // converted to a dex frame.
133   CallChainReportBuilder builder(thread_tree);
134   builder.SetConvertJITFrame(false);
135   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
136   ASSERT_EQ(entries.size(), 2);
137   ASSERT_EQ(entries[0].ip, 0x2000);
138   ASSERT_STREQ(entries[0].symbol->Name(), "java_method1");
139   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
140   ASSERT_EQ(entries[0].vaddr_in_file, 0);
141   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
142   ASSERT_EQ(entries[1].ip, 0x3000);
143   ASSERT_STREQ(entries[1].symbol->Name(), "java_method2");
144   ASSERT_EQ(entries[1].dso->Path(), fake_jit_cache_path);
145   ASSERT_EQ(entries[1].vaddr_in_file, 0x3000);
146   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
147 }
148 
TEST_F(CallChainReportBuilderTest,not_remove_art_frame)149 TEST_F(CallChainReportBuilderTest, not_remove_art_frame) {
150   // Test option: remove_art_frame = false, convert_jit_frame = true.
151   // The callchain should include interpreter frames. And the JIT frame is
152   // converted to a dex frame.
153   CallChainReportBuilder builder(thread_tree);
154   builder.SetRemoveArtFrame(false);
155   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
156   ASSERT_EQ(entries.size(), 8);
157   for (size_t i : {0, 3, 6}) {
158     ASSERT_EQ(entries[i].ip, 0x1000);
159     ASSERT_STREQ(entries[i].symbol->Name(), "art_func1");
160     ASSERT_EQ(entries[i].dso->Path(), fake_interpreter_path);
161     ASSERT_EQ(entries[i].vaddr_in_file, 0);
162     ASSERT_EQ(entries[i].execution_type, CallChainExecutionType::ART_METHOD);
163     ASSERT_EQ(entries[i + 1].ip, 0x1100);
164     ASSERT_STREQ(entries[i + 1].symbol->Name(), "art_func2");
165     ASSERT_EQ(entries[i + 1].dso->Path(), fake_interpreter_path);
166     ASSERT_EQ(entries[i + 1].vaddr_in_file, 0x100);
167     ASSERT_EQ(entries[i + 1].execution_type, CallChainExecutionType::ART_METHOD);
168   }
169   ASSERT_EQ(entries[2].ip, 0x2000);
170   ASSERT_STREQ(entries[2].symbol->Name(), "java_method1");
171   ASSERT_EQ(entries[2].dso->Path(), fake_dex_file_path);
172   ASSERT_EQ(entries[2].vaddr_in_file, 0);
173   ASSERT_EQ(entries[2].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
174   ASSERT_EQ(entries[5].ip, 0x3000);
175   ASSERT_STREQ(entries[5].symbol->Name(), "java_method2");
176   ASSERT_EQ(entries[5].dso->Path(), fake_dex_file_path);
177   ASSERT_EQ(entries[5].vaddr_in_file, 0x100);
178   ASSERT_EQ(entries[5].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
179 }
180 
TEST_F(CallChainReportBuilderTest,remove_jit_frame_called_by_dex_frame)181 TEST_F(CallChainReportBuilderTest, remove_jit_frame_called_by_dex_frame) {
182   // Test option: remove_art_frame = true, convert_jit_frame = true.
183   // The callchain should remove the JIT frame called by a dex frame having the same symbol name.
184   std::vector<uint64_t> fake_ips = {
185       0x3000,  // java_method2 in jit cache
186       0x1000,  // art_func1
187       0x1100,  // art_func2
188       0x2100,  // java_method2 in dex file
189       0x1000,  // art_func1
190   };
191   CallChainReportBuilder builder(thread_tree);
192   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
193   ASSERT_EQ(entries.size(), 1);
194   ASSERT_EQ(entries[0].ip, 0x2100);
195   ASSERT_STREQ(entries[0].symbol->Name(), "java_method2");
196   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
197   ASSERT_EQ(entries[0].vaddr_in_file, 0x100);
198   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
199 }
200 
TEST_F(CallChainReportBuilderTest,remove_art_frame_only_near_jvm_method)201 TEST_F(CallChainReportBuilderTest, remove_art_frame_only_near_jvm_method) {
202   // Test option: remove_art_frame = true, convert_jit_frame = true.
203   // The callchain should not remove ART symbols not near a JVM method.
204   std::vector<uint64_t> fake_ips = {
205       0x1000,  // art_func1
206       0x0,     // native_func1
207       0x2000,  // java_method1 in dex file
208       0x0,     // native_func1
209       0x1000,  // art_func1
210   };
211   CallChainReportBuilder builder(thread_tree);
212   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
213   ASSERT_EQ(entries.size(), 5);
214   for (size_t i : {0, 4}) {
215     ASSERT_EQ(entries[i].ip, 0x1000);
216     ASSERT_STREQ(entries[i].symbol->Name(), "art_func1");
217     ASSERT_EQ(entries[i].dso->Path(), fake_interpreter_path);
218     ASSERT_EQ(entries[i].vaddr_in_file, 0);
219     ASSERT_EQ(entries[i].execution_type, CallChainExecutionType::NATIVE_METHOD);
220   }
221   for (size_t i : {1, 3}) {
222     ASSERT_EQ(entries[i].ip, 0x0);
223     ASSERT_STREQ(entries[i].symbol->Name(), "native_func1");
224     ASSERT_EQ(entries[i].dso->Path(), fake_native_lib_path);
225     ASSERT_EQ(entries[i].vaddr_in_file, 0);
226     ASSERT_EQ(entries[i].execution_type, CallChainExecutionType::NATIVE_METHOD);
227   }
228 
229   ASSERT_EQ(entries[2].ip, 0x2000);
230   ASSERT_STREQ(entries[2].symbol->Name(), "java_method1");
231   ASSERT_EQ(entries[2].dso->Path(), fake_dex_file_path);
232   ASSERT_EQ(entries[2].vaddr_in_file, 0x0);
233   ASSERT_EQ(entries[2].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
234 }
235 
TEST_F(CallChainReportBuilderTest,keep_art_jni_method)236 TEST_F(CallChainReportBuilderTest, keep_art_jni_method) {
237   // Test option: remove_art_frame = true.
238   // The callchain should remove art_jni_trampoline, but keep jni methods.
239   std::vector<uint64_t> fake_ips = {
240       0x1200,  // art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)
241       0x100,   // art_jni_trampoline
242       0x2000,  // java_method1 in dex file
243       0x1200,  // art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)
244       0x1300,  // art_quick_generic_jni_trampoline
245   };
246   CallChainReportBuilder builder(thread_tree);
247   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
248   ASSERT_EQ(entries.size(), 3);
249   for (size_t i : {0, 2}) {
250     ASSERT_EQ(entries[i].ip, 0x1200);
251     ASSERT_STREQ(entries[i].symbol->DemangledName(),
252                  "art::Method_invoke(_JNIEnv*, _jobject*, _jobject*, _jobjectArray*)");
253     ASSERT_EQ(entries[i].dso->Path(), fake_interpreter_path);
254     ASSERT_EQ(entries[i].vaddr_in_file, 0x200);
255     ASSERT_EQ(entries[i].execution_type, CallChainExecutionType::NATIVE_METHOD);
256   }
257   ASSERT_EQ(entries[1].ip, 0x2000);
258   ASSERT_STREQ(entries[1].symbol->Name(), "java_method1");
259   ASSERT_EQ(entries[1].dso->Path(), fake_dex_file_path);
260   ASSERT_EQ(entries[1].vaddr_in_file, 0x0);
261   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
262 }
263 
TEST_F(CallChainReportBuilderTest,add_proguard_mapping_file)264 TEST_F(CallChainReportBuilderTest, add_proguard_mapping_file) {
265   std::vector<uint64_t> fake_ips = {
266       0x2200,  // 2200,  // obfuscated_class.obfuscated_java_method
267       0x3200,  // 3200,  // obfuscated_class.obfuscated_java_method2
268   };
269   CallChainReportBuilder builder(thread_tree);
270   // Symbol names aren't changed when not given proguard mapping files.
271   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
272   ASSERT_EQ(entries.size(), 2);
273   ASSERT_EQ(entries[0].ip, 0x2200);
274   ASSERT_STREQ(entries[0].symbol->DemangledName(), "obfuscated_class.obfuscated_java_method");
275   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
276   ASSERT_EQ(entries[0].vaddr_in_file, 0x200);
277   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
278   ASSERT_EQ(entries[1].ip, 0x3200);
279   ASSERT_STREQ(entries[1].symbol->DemangledName(), "obfuscated_class.obfuscated_java_method2");
280   ASSERT_EQ(entries[1].dso->Path(), fake_jit_cache_path);
281   ASSERT_EQ(entries[1].vaddr_in_file, 0x3200);
282   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
283 
284   // Symbol names are changed when given a proguard mapping file.
285   TemporaryFile tmpfile;
286   close(tmpfile.release());
287   ASSERT_TRUE(android::base::WriteStringToFile(
288       "android.support.v4.app.RemoteActionCompatParcelizer -> obfuscated_class:\n"
289       "    13:13:androidx.core.app.RemoteActionCompat read(androidx.versionedparcelable.Versioned"
290       "Parcel) -> obfuscated_java_method\n"
291       "    13:13:androidx.core.app.RemoteActionCompat "
292       "android.support.v4.app.RemoteActionCompatParcelizer.read2(androidx.versionedparcelable."
293       "VersionedParcel) -> obfuscated_java_method2",
294       tmpfile.path));
295   builder.AddProguardMappingFile(tmpfile.path);
296   entries = builder.Build(thread, fake_ips, 0);
297   ASSERT_EQ(entries.size(), 2);
298   ASSERT_EQ(entries[0].ip, 0x2200);
299   ASSERT_STREQ(entries[0].symbol->DemangledName(),
300                "android.support.v4.app.RemoteActionCompatParcelizer.read");
301   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
302   ASSERT_EQ(entries[0].vaddr_in_file, 0x200);
303   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
304   ASSERT_EQ(entries[1].ip, 0x3200);
305   ASSERT_STREQ(entries[1].symbol->DemangledName(),
306                "android.support.v4.app.RemoteActionCompatParcelizer.read2");
307   ASSERT_EQ(entries[1].dso->Path(), fake_jit_cache_path);
308   ASSERT_EQ(entries[1].vaddr_in_file, 0x3200);
309   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
310 }
311 
TEST_F(CallChainReportBuilderTest,add_proguard_mapping_file_for_jit_method_with_signature)312 TEST_F(CallChainReportBuilderTest, add_proguard_mapping_file_for_jit_method_with_signature) {
313   std::vector<uint64_t> fake_ips = {
314       0x3200,  // 3200,  // void ctep.v(cteo, ctgc, ctbn)
315   };
316   SetSymbols(fake_jit_cache_path, DSO_ELF_FILE,
317              {Symbol("void ctep.v(cteo, ctgc, ctbn)", 0x3200, 0x100)});
318   CallChainReportBuilder builder(thread_tree);
319   TemporaryFile tmpfile;
320   close(tmpfile.release());
321   ASSERT_TRUE(android::base::WriteStringToFile(
322       "android.support.v4.app.RemoteActionCompatParcelizer -> ctep:\n"
323       "    13:13:androidx.core.app.RemoteActionCompat read(androidx.versionedparcelable.Versioned"
324       "Parcel) -> v\n",
325       tmpfile.path));
326   builder.AddProguardMappingFile(tmpfile.path);
327   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
328   ASSERT_EQ(entries.size(), 1);
329   ASSERT_EQ(entries[0].ip, 0x3200);
330   ASSERT_STREQ(entries[0].symbol->DemangledName(),
331                "android.support.v4.app.RemoteActionCompatParcelizer.read");
332   ASSERT_EQ(entries[0].dso->Path(), fake_jit_cache_path);
333   ASSERT_EQ(entries[0].vaddr_in_file, 0x3200);
334   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
335 }
336 
TEST_F(CallChainReportBuilderTest,add_proguard_mapping_file_for_compiled_java_method_with_signature)337 TEST_F(CallChainReportBuilderTest,
338        add_proguard_mapping_file_for_compiled_java_method_with_signature) {
339   TemporaryFile tmpfile;
340   close(tmpfile.release());
341   ASSERT_TRUE(android::base::WriteStringToFile(
342       "android.support.v4.app.RemoteActionCompatParcelizer -> ctep:\n"
343       "    13:13:androidx.core.app.RemoteActionCompat read(androidx.versionedparcelable.Versioned"
344       "Parcel) -> v\n",
345       tmpfile.path));
346 
347   for (const char* suffix : {".odex", ".oat", ".dex"}) {
348     std::string compiled_java_path = "compiled_java" + std::string(suffix);
349     SetSymbols(compiled_java_path, DSO_ELF_FILE,
350                {Symbol("void ctep.v(cteo, ctgc, ctbn)", 0x0, 0x100)});
351     thread_tree.AddThreadMap(1, 1, 0x4000, 0x1000, 0x0, compiled_java_path);
352     std::vector<uint64_t> fake_ips = {
353         0x4000,  // 4000,  // void ctep.v(cteo, ctgc, ctbn)
354     };
355 
356     CallChainReportBuilder builder(thread_tree);
357     builder.AddProguardMappingFile(tmpfile.path);
358     std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
359     ASSERT_EQ(entries.size(), 1);
360     ASSERT_EQ(entries[0].ip, 0x4000);
361     ASSERT_STREQ(entries[0].symbol->DemangledName(),
362                  "android.support.v4.app.RemoteActionCompatParcelizer.read");
363     ASSERT_EQ(entries[0].dso->Path(), compiled_java_path);
364     ASSERT_EQ(entries[0].vaddr_in_file, 0x0);
365     ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::NATIVE_METHOD);
366   }
367 }
368 
TEST_F(CallChainReportBuilderTest,convert_jit_frame_for_jit_method_with_signature)369 TEST_F(CallChainReportBuilderTest, convert_jit_frame_for_jit_method_with_signature) {
370   std::vector<uint64_t> fake_ips = {
371       0x2200,  // 2200,  // ctep.v
372       0x3200,  // 3200,  // void ctep.v(cteo, ctgc, ctbn)
373   };
374   SetSymbols(fake_dex_file_path, DSO_DEX_FILE, {Symbol("ctep.v", 0x200, 0x100)});
375   SetSymbols(fake_jit_cache_path, DSO_ELF_FILE,
376              {Symbol("void ctep.v(cteo, ctgc, ctbn)", 0x3200, 0x100)});
377   CallChainReportBuilder builder(thread_tree);
378   // Test if we can convert jit method with signature.
379   std::vector<CallChainReportEntry> entries = builder.Build(thread, fake_ips, 0);
380   ASSERT_EQ(entries.size(), 2);
381   ASSERT_EQ(entries[0].ip, 0x2200);
382   ASSERT_STREQ(entries[0].symbol->DemangledName(), "ctep.v");
383   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
384   ASSERT_EQ(entries[0].vaddr_in_file, 0x200);
385   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
386   ASSERT_EQ(entries[1].ip, 0x3200);
387   ASSERT_STREQ(entries[1].symbol->DemangledName(), "ctep.v");
388   ASSERT_EQ(entries[1].dso->Path(), fake_dex_file_path);
389   ASSERT_EQ(entries[1].vaddr_in_file, 0x200);
390   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
391 
392   // Test adding proguard mapping file.
393   TemporaryFile tmpfile;
394   close(tmpfile.release());
395   ASSERT_TRUE(android::base::WriteStringToFile(
396       "android.support.v4.app.RemoteActionCompatParcelizer -> ctep:\n"
397       "    13:13:androidx.core.app.RemoteActionCompat read(androidx.versionedparcelable.Versioned"
398       "Parcel) -> v\n",
399       tmpfile.path));
400   builder.AddProguardMappingFile(tmpfile.path);
401   entries = builder.Build(thread, fake_ips, 0);
402   ASSERT_EQ(entries.size(), 2);
403   ASSERT_EQ(entries[0].ip, 0x2200);
404   ASSERT_STREQ(entries[0].symbol->DemangledName(),
405                "android.support.v4.app.RemoteActionCompatParcelizer.read");
406   ASSERT_EQ(entries[0].dso->Path(), fake_dex_file_path);
407   ASSERT_EQ(entries[0].vaddr_in_file, 0x200);
408   ASSERT_EQ(entries[0].execution_type, CallChainExecutionType::INTERPRETED_JVM_METHOD);
409   ASSERT_EQ(entries[1].ip, 0x3200);
410   ASSERT_STREQ(entries[1].symbol->DemangledName(),
411                "android.support.v4.app.RemoteActionCompatParcelizer.read");
412   ASSERT_EQ(entries[1].dso->Path(), fake_dex_file_path);
413   ASSERT_EQ(entries[1].vaddr_in_file, 0x200);
414   ASSERT_EQ(entries[1].execution_type, CallChainExecutionType::JIT_JVM_METHOD);
415 }
416