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