xref: /aosp_15_r20/external/cronet/base/profiler/chrome_unwinder_android_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/profiler/chrome_unwinder_android.h"
6 
7 #include "base/memory/aligned_memory.h"
8 #include "base/profiler/chrome_unwind_info_android.h"
9 #include "base/profiler/stack_sampling_profiler_test_util.h"
10 #include "base/ranges/algorithm.h"
11 #include "base/test/gtest_util.h"
12 #include "build/build_config.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14 
15 namespace base {
16 
TEST(ChromeAndroidUnwindInstructionTest,TestSmallStackPointerIncrementMinValue)17 TEST(ChromeAndroidUnwindInstructionTest,
18      TestSmallStackPointerIncrementMinValue) {
19   RegisterContext thread_context = {};
20   const uint8_t instruction = 0b00000000;
21   const uint8_t* current_instruction = &instruction;
22   thread_context.arm_sp = 0x10000000;
23   bool pc_was_updated = false;
24   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
25                                      &thread_context),
26             UnwindInstructionResult::kInstructionPending);
27   EXPECT_FALSE(pc_was_updated);
28   ASSERT_EQ(current_instruction, &instruction + 1);
29   EXPECT_EQ(0x10000004ul, thread_context.arm_sp);
30 }
31 
TEST(ChromeAndroidUnwindInstructionTest,TestSmallStackPointerIncrementMidValue)32 TEST(ChromeAndroidUnwindInstructionTest,
33      TestSmallStackPointerIncrementMidValue) {
34   // xxxxxx = 4; vsp = vsp + (4 << 2) + 4 = vsp + 16 + 4 = vsp + 0x14.
35   RegisterContext thread_context = {};
36   const uint8_t instruction = 0b00000100;
37   const uint8_t* current_instruction = &instruction;
38   thread_context.arm_sp = 0x10000000;
39   bool pc_was_updated = false;
40   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
41                                      &thread_context),
42             UnwindInstructionResult::kInstructionPending);
43   EXPECT_FALSE(pc_was_updated);
44   ASSERT_EQ(current_instruction, &instruction + 1);
45   EXPECT_EQ(0x10000014ul, thread_context.arm_sp);
46 }
47 
TEST(ChromeAndroidUnwindInstructionTest,TestSmallStackPointerIncrementMaxValue)48 TEST(ChromeAndroidUnwindInstructionTest,
49      TestSmallStackPointerIncrementMaxValue) {
50   RegisterContext thread_context = {};
51   const uint8_t instruction = 0b00111111;
52   const uint8_t* current_instruction = &instruction;
53   thread_context.arm_sp = 0x10000000;
54   bool pc_was_updated = false;
55   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
56                                      &thread_context),
57             UnwindInstructionResult::kInstructionPending);
58   EXPECT_FALSE(pc_was_updated);
59   ASSERT_EQ(current_instruction, &instruction + 1);
60   EXPECT_EQ(0x10000100ul, thread_context.arm_sp);
61 }
62 
TEST(ChromeAndroidUnwindInstructionTest,TestSmallStackPointerIncrementOverflow)63 TEST(ChromeAndroidUnwindInstructionTest,
64      TestSmallStackPointerIncrementOverflow) {
65   RegisterContext thread_context = {};
66   const uint8_t instruction = 0b00111111;
67   const uint8_t* current_instruction = &instruction;
68   thread_context.arm_sp = 0xffffffff;
69   bool pc_was_updated = false;
70   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
71                                      &thread_context),
72             UnwindInstructionResult::kAborted);
73   ASSERT_EQ(current_instruction, &instruction + 1);
74   EXPECT_EQ(0xffffffff, thread_context.arm_sp);
75 }
76 
TEST(ChromeAndroidUnwindInstructionTest,TestSmallStackPointerDecrementMinValue)77 TEST(ChromeAndroidUnwindInstructionTest,
78      TestSmallStackPointerDecrementMinValue) {
79   RegisterContext thread_context = {};
80   const uint8_t instruction = 0b01000000;
81   const uint8_t* current_instruction = &instruction;
82   thread_context.arm_sp = 0x10000000;
83   bool pc_was_updated = false;
84   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
85                                      &thread_context),
86             UnwindInstructionResult::kInstructionPending);
87   EXPECT_FALSE(pc_was_updated);
88   ASSERT_EQ(current_instruction, &instruction + 1);
89   EXPECT_EQ(0x0ffffffcul, thread_context.arm_sp);
90 }
91 
TEST(ChromeAndroidUnwindInstructionTest,TestSmallStackPointerDecrementMidValue)92 TEST(ChromeAndroidUnwindInstructionTest,
93      TestSmallStackPointerDecrementMidValue) {
94   // xxxxxx = 4; vsp = vsp - (4 << 2) - 4 = vsp - 16 - 4 = vsp - 0x14.
95   RegisterContext thread_context = {};
96   const uint8_t instruction = 0b01000100;
97   const uint8_t* current_instruction = &instruction;
98   thread_context.arm_sp = 0x10000000;
99   bool pc_was_updated = false;
100   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
101                                      &thread_context),
102             UnwindInstructionResult::kInstructionPending);
103   EXPECT_FALSE(pc_was_updated);
104   ASSERT_EQ(current_instruction, &instruction + 1);
105   EXPECT_EQ(0x0fffffecul, thread_context.arm_sp);
106 }
107 
TEST(ChromeAndroidUnwindInstructionTest,TestSmallStackPointerDecrementMaxValue)108 TEST(ChromeAndroidUnwindInstructionTest,
109      TestSmallStackPointerDecrementMaxValue) {
110   RegisterContext thread_context = {};
111   const uint8_t instruction = 0b01111111;
112   const uint8_t* current_instruction = &instruction;
113   thread_context.arm_sp = 0x10000000;
114   bool pc_was_updated = false;
115   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
116                                      &thread_context),
117             UnwindInstructionResult::kInstructionPending);
118   EXPECT_FALSE(pc_was_updated);
119   ASSERT_EQ(current_instruction, &instruction + 1);
120   EXPECT_EQ(0x0fffff00ul, thread_context.arm_sp);
121 }
122 
TEST(ChromeAndroidUnwindInstructionTest,TestSmallStackPointerDecrementUnderflow)123 TEST(ChromeAndroidUnwindInstructionTest,
124      TestSmallStackPointerDecrementUnderflow) {
125   RegisterContext thread_context = {};
126   const uint8_t instruction = 0b01111111;
127   const uint8_t* current_instruction = &instruction;
128   thread_context.arm_sp = 0x00000000;
129   bool pc_was_updated = false;
130   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
131                                      &thread_context),
132             UnwindInstructionResult::kAborted);
133   EXPECT_FALSE(pc_was_updated);
134   ASSERT_EQ(current_instruction, &instruction + 1);
135   EXPECT_EQ(0x0ul, thread_context.arm_sp);
136 }
137 
138 using ChromeAndroidUnwindSetStackPointerFromRegisterValueTest =
139     ::testing::TestWithParam<uint8_t>;
140 
141 INSTANTIATE_TEST_SUITE_P(
142     All,
143     ChromeAndroidUnwindSetStackPointerFromRegisterValueTest,
144     // The function should set all registers except
145     // - callee saved registers (r0, r1, r2, r3)
146     // - sp (r13)
147     // - pc (r15)
148     ::testing::Values(4, 5, 6, 7, 8, 9, 10, 11, 12, 14));
149 
TEST_P(ChromeAndroidUnwindSetStackPointerFromRegisterValueTest,TestSetStackPointerFromRegisterValue)150 TEST_P(ChromeAndroidUnwindSetStackPointerFromRegisterValueTest,
151        TestSetStackPointerFromRegisterValue) {
152   const uint8_t register_index = GetParam();
153 
154   RegisterContext thread_context = {};
155   thread_context.arm_r0 = 100;
156   thread_context.arm_r1 = 101;
157   thread_context.arm_r2 = 102;
158   thread_context.arm_r3 = 103;
159   thread_context.arm_r4 = 104;
160   thread_context.arm_r5 = 105;
161   thread_context.arm_r6 = 106;
162   thread_context.arm_r7 = 107;
163   thread_context.arm_r8 = 108;
164   thread_context.arm_r9 = 109;
165   thread_context.arm_r10 = 110;
166   thread_context.arm_fp = 111;  // r11
167   thread_context.arm_ip = 112;  // r12
168   thread_context.arm_lr = 114;  // r14
169 
170   const uint8_t instruction = 0b10010000 + register_index;
171   const uint8_t* current_instruction = &instruction;
172   bool pc_was_updated = false;
173   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
174                                      &thread_context),
175             UnwindInstructionResult::kInstructionPending);
176   EXPECT_FALSE(pc_was_updated);
177   ASSERT_EQ(current_instruction, &instruction + 1);
178   EXPECT_EQ(100ul + register_index, thread_context.arm_sp);
179 }
180 
TEST(ChromeAndroidUnwindInstructionTest,TestCompleteWithNoPriorPCUpdate)181 TEST(ChromeAndroidUnwindInstructionTest, TestCompleteWithNoPriorPCUpdate) {
182   RegisterContext thread_context = {};
183   thread_context.arm_lr = 114;  // r14
184   thread_context.arm_pc = 115;  // r15
185   const uint8_t instruction = 0b10110000;
186   const uint8_t* current_instruction = &instruction;
187   bool pc_was_updated = false;
188   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
189                                      &thread_context),
190             UnwindInstructionResult::kCompleted);
191   ASSERT_EQ(current_instruction, &instruction + 1);
192   EXPECT_EQ(114ul, thread_context.arm_pc);
193 }
194 
TEST(ChromeAndroidUnwindInstructionTest,TestCompleteWithPriorPCUpdate)195 TEST(ChromeAndroidUnwindInstructionTest, TestCompleteWithPriorPCUpdate) {
196   RegisterContext thread_context = {};
197   thread_context.arm_lr = 114;  // r14
198   thread_context.arm_pc = 115;  // r15
199   const uint8_t instruction = 0b10110000;
200   const uint8_t* current_instruction = &instruction;
201   bool pc_was_updated = true;
202   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
203                                      &thread_context),
204             UnwindInstructionResult::kCompleted);
205   ASSERT_EQ(current_instruction, &instruction + 1);
206   EXPECT_EQ(115ul, thread_context.arm_pc);
207 }
208 
TEST(ChromeAndroidUnwindInstructionTest,TestPopDiscontinuousRegistersIncludingPC)209 TEST(ChromeAndroidUnwindInstructionTest,
210      TestPopDiscontinuousRegistersIncludingPC) {
211   RegisterContext thread_context = {};
212 
213   thread_context.arm_r0 = 100;
214   thread_context.arm_r1 = 101;
215   thread_context.arm_r2 = 102;
216   thread_context.arm_r3 = 103;
217   thread_context.arm_r4 = 104;
218   thread_context.arm_r5 = 105;
219   thread_context.arm_r6 = 106;
220   thread_context.arm_r7 = 107;
221   thread_context.arm_r8 = 108;
222   thread_context.arm_r9 = 109;
223   thread_context.arm_r10 = 110;
224   thread_context.arm_fp = 111;
225   thread_context.arm_ip = 112;
226   thread_context.arm_lr = 113;
227   thread_context.arm_pc = 114;
228 
229   // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}.
230   const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
231 
232   thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
233   // Pop r15, r12, r8, r4.
234   const uint8_t instruction[] = {0b10001001, 0b00010001};
235   const uint8_t* current_instruction = instruction;
236 
237   bool pc_was_updated = false;
238   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
239                                      &thread_context),
240             UnwindInstructionResult::kInstructionPending);
241   EXPECT_TRUE(pc_was_updated);
242   ASSERT_EQ(current_instruction, instruction + 2);
243   EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 4), thread_context.arm_sp);
244 
245   EXPECT_EQ(100ul, thread_context.arm_r0);
246   EXPECT_EQ(101ul, thread_context.arm_r1);
247   EXPECT_EQ(102ul, thread_context.arm_r2);
248   EXPECT_EQ(103ul, thread_context.arm_r3);
249   EXPECT_EQ(1ul, thread_context.arm_r4);
250   EXPECT_EQ(105ul, thread_context.arm_r5);
251   EXPECT_EQ(106ul, thread_context.arm_r6);
252   EXPECT_EQ(107ul, thread_context.arm_r7);
253   EXPECT_EQ(2ul, thread_context.arm_r8);
254   EXPECT_EQ(109ul, thread_context.arm_r9);
255   EXPECT_EQ(110ul, thread_context.arm_r10);
256   EXPECT_EQ(111ul, thread_context.arm_fp);
257   EXPECT_EQ(3ul, thread_context.arm_ip);
258   EXPECT_EQ(113ul, thread_context.arm_lr);
259   EXPECT_EQ(4ul, thread_context.arm_pc);
260 }
261 
TEST(ChromeAndroidUnwindInstructionTest,TestPopDiscontinuousRegisters)262 TEST(ChromeAndroidUnwindInstructionTest, TestPopDiscontinuousRegisters) {
263   RegisterContext thread_context = {};
264 
265   thread_context.arm_r0 = 100;
266   thread_context.arm_r1 = 101;
267   thread_context.arm_r2 = 102;
268   thread_context.arm_r3 = 103;
269   thread_context.arm_r4 = 104;
270   thread_context.arm_r5 = 105;
271   thread_context.arm_r6 = 106;
272   thread_context.arm_r7 = 107;
273   thread_context.arm_r8 = 108;
274   thread_context.arm_r9 = 109;
275   thread_context.arm_r10 = 110;
276   thread_context.arm_fp = 111;
277   thread_context.arm_ip = 112;
278   thread_context.arm_lr = 113;
279   thread_context.arm_pc = 114;
280 
281   // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}.
282   const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
283 
284   thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
285   // Pop r12, r8, r4.
286   const uint8_t instruction[] = {0b10000001, 0b00010001};
287   const uint8_t* current_instruction = instruction;
288 
289   bool pc_was_updated = false;
290   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
291                                      &thread_context),
292             UnwindInstructionResult::kInstructionPending);
293   EXPECT_FALSE(pc_was_updated);
294   ASSERT_EQ(current_instruction, instruction + 2);
295   EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 3), thread_context.arm_sp);
296 
297   EXPECT_EQ(100ul, thread_context.arm_r0);
298   EXPECT_EQ(101ul, thread_context.arm_r1);
299   EXPECT_EQ(102ul, thread_context.arm_r2);
300   EXPECT_EQ(103ul, thread_context.arm_r3);
301   EXPECT_EQ(1ul, thread_context.arm_r4);
302   EXPECT_EQ(105ul, thread_context.arm_r5);
303   EXPECT_EQ(106ul, thread_context.arm_r6);
304   EXPECT_EQ(107ul, thread_context.arm_r7);
305   EXPECT_EQ(2ul, thread_context.arm_r8);
306   EXPECT_EQ(109ul, thread_context.arm_r9);
307   EXPECT_EQ(110ul, thread_context.arm_r10);
308   EXPECT_EQ(111ul, thread_context.arm_fp);
309   EXPECT_EQ(3ul, thread_context.arm_ip);
310   EXPECT_EQ(113ul, thread_context.arm_lr);
311   EXPECT_EQ(114ul, thread_context.arm_pc);
312 }
313 
TEST(ChromeAndroidUnwindInstructionTest,TestPopDiscontinuousRegistersOverflow)314 TEST(ChromeAndroidUnwindInstructionTest,
315      TestPopDiscontinuousRegistersOverflow) {
316   RegisterContext thread_context = {};
317 
318   thread_context.arm_r0 = 100;
319   thread_context.arm_r1 = 101;
320   thread_context.arm_r2 = 102;
321   thread_context.arm_r3 = 103;
322   thread_context.arm_r4 = 104;
323   thread_context.arm_r5 = 105;
324   thread_context.arm_r6 = 106;
325   thread_context.arm_r7 = 107;
326   thread_context.arm_r8 = 108;
327   thread_context.arm_r9 = 109;
328   thread_context.arm_r10 = 110;
329   thread_context.arm_fp = 111;
330   thread_context.arm_ip = 112;
331   thread_context.arm_lr = 113;
332   thread_context.arm_pc = 114;
333 
334   // Pop up to 12 integer registers under masks {r15-r12}, {r11-r4}.
335   thread_context.arm_sp = 0xffffffff;
336   // Pop r15, r12, r8, r4.
337   const uint8_t instruction[] = {0b10001001, 0b00010001};
338   const uint8_t* current_instruction = instruction;
339 
340   bool pc_was_updated = false;
341   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
342                                      &thread_context),
343             UnwindInstructionResult::kAborted);
344   EXPECT_FALSE(pc_was_updated);
345   ASSERT_EQ(current_instruction, instruction + 2);
346   EXPECT_EQ(0xffffffff, thread_context.arm_sp);
347 
348   EXPECT_EQ(100ul, thread_context.arm_r0);
349   EXPECT_EQ(101ul, thread_context.arm_r1);
350   EXPECT_EQ(102ul, thread_context.arm_r2);
351   EXPECT_EQ(103ul, thread_context.arm_r3);
352   EXPECT_EQ(104ul, thread_context.arm_r4);
353   EXPECT_EQ(105ul, thread_context.arm_r5);
354   EXPECT_EQ(106ul, thread_context.arm_r6);
355   EXPECT_EQ(107ul, thread_context.arm_r7);
356   EXPECT_EQ(108ul, thread_context.arm_r8);
357   EXPECT_EQ(109ul, thread_context.arm_r9);
358   EXPECT_EQ(110ul, thread_context.arm_r10);
359   EXPECT_EQ(111ul, thread_context.arm_fp);
360   EXPECT_EQ(112ul, thread_context.arm_ip);
361   EXPECT_EQ(113ul, thread_context.arm_lr);
362   EXPECT_EQ(114ul, thread_context.arm_pc);
363 }
364 
TEST(ChromeAndroidUnwindInstructionTest,TestRefuseToUnwind)365 TEST(ChromeAndroidUnwindInstructionTest, TestRefuseToUnwind) {
366   RegisterContext thread_context = {};
367 
368   const uint8_t instruction[] = {0b10000000, 0b0};
369   const uint8_t* current_instruction = instruction;
370 
371   bool pc_was_updated = false;
372   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
373                                      &thread_context),
374             UnwindInstructionResult::kAborted);
375   EXPECT_FALSE(pc_was_updated);
376   ASSERT_EQ(current_instruction, instruction + 2);
377 }
378 
TEST(ChromeAndroidUnwindInstructionTest,TestPopRegistersIncludingR14MinRegisters)379 TEST(ChromeAndroidUnwindInstructionTest,
380      TestPopRegistersIncludingR14MinRegisters) {
381   RegisterContext thread_context = {};
382 
383   thread_context.arm_r0 = 100;
384   thread_context.arm_r1 = 101;
385   thread_context.arm_r2 = 102;
386   thread_context.arm_r3 = 103;
387   thread_context.arm_r4 = 104;
388   thread_context.arm_r5 = 105;
389   thread_context.arm_r6 = 106;
390   thread_context.arm_r7 = 107;
391   thread_context.arm_r8 = 108;
392   thread_context.arm_r9 = 109;
393   thread_context.arm_r10 = 110;
394   thread_context.arm_fp = 111;
395   thread_context.arm_ip = 112;
396   thread_context.arm_lr = 113;
397 
398   // Popping r4 - r[4 + nnn], r14, at most 9 registers.
399   // r14 = lr
400   const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
401 
402   thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
403   const uint8_t instruction = 0b10101000;
404   const uint8_t* current_instruction = &instruction;
405   bool pc_was_updated = false;
406   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
407                                      &thread_context),
408             UnwindInstructionResult::kInstructionPending);
409   EXPECT_FALSE(pc_was_updated);
410   ASSERT_EQ(current_instruction, &instruction + 1);
411   EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 2), thread_context.arm_sp);
412 
413   EXPECT_EQ(100ul, thread_context.arm_r0);
414   EXPECT_EQ(101ul, thread_context.arm_r1);
415   EXPECT_EQ(102ul, thread_context.arm_r2);
416   EXPECT_EQ(103ul, thread_context.arm_r3);
417   EXPECT_EQ(1ul, thread_context.arm_r4);
418   EXPECT_EQ(105ul, thread_context.arm_r5);
419   EXPECT_EQ(106ul, thread_context.arm_r6);
420   EXPECT_EQ(107ul, thread_context.arm_r7);
421   EXPECT_EQ(108ul, thread_context.arm_r8);
422   EXPECT_EQ(109ul, thread_context.arm_r9);
423   EXPECT_EQ(110ul, thread_context.arm_r10);
424   EXPECT_EQ(111ul, thread_context.arm_fp);
425   EXPECT_EQ(112ul, thread_context.arm_ip);
426   EXPECT_EQ(2ul, thread_context.arm_lr);
427 }
428 
TEST(ChromeAndroidUnwindInstructionTest,TestPopRegistersIncludingR14MidRegisters)429 TEST(ChromeAndroidUnwindInstructionTest,
430      TestPopRegistersIncludingR14MidRegisters) {
431   RegisterContext thread_context = {};
432 
433   thread_context.arm_r0 = 100;
434   thread_context.arm_r1 = 101;
435   thread_context.arm_r2 = 102;
436   thread_context.arm_r3 = 103;
437   thread_context.arm_r4 = 104;
438   thread_context.arm_r5 = 105;
439   thread_context.arm_r6 = 106;
440   thread_context.arm_r7 = 107;
441   thread_context.arm_r8 = 108;
442   thread_context.arm_r9 = 109;
443   thread_context.arm_r10 = 110;
444   thread_context.arm_fp = 111;
445   thread_context.arm_ip = 112;
446   thread_context.arm_lr = 113;
447 
448   // Popping r4 - r[4 + nnn], r14, at most 9 registers.
449   // r14 = lr
450   const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
451 
452   thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
453   const uint8_t instruction = 0b10101100;  // Pop r4-r8, r14.
454   const uint8_t* current_instruction = &instruction;
455   bool pc_was_updated = false;
456   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
457                                      &thread_context),
458             UnwindInstructionResult::kInstructionPending);
459   EXPECT_FALSE(pc_was_updated);
460   ASSERT_EQ(current_instruction, &instruction + 1);
461   EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 6), thread_context.arm_sp);
462 
463   EXPECT_EQ(100ul, thread_context.arm_r0);
464   EXPECT_EQ(101ul, thread_context.arm_r1);
465   EXPECT_EQ(102ul, thread_context.arm_r2);
466   EXPECT_EQ(103ul, thread_context.arm_r3);
467   EXPECT_EQ(1ul, thread_context.arm_r4);
468   EXPECT_EQ(2ul, thread_context.arm_r5);
469   EXPECT_EQ(3ul, thread_context.arm_r6);
470   EXPECT_EQ(4ul, thread_context.arm_r7);
471   EXPECT_EQ(5ul, thread_context.arm_r8);
472   EXPECT_EQ(109ul, thread_context.arm_r9);
473   EXPECT_EQ(110ul, thread_context.arm_r10);
474   EXPECT_EQ(111ul, thread_context.arm_fp);
475   EXPECT_EQ(112ul, thread_context.arm_ip);
476   EXPECT_EQ(6ul, thread_context.arm_lr);
477 }
478 
TEST(ChromeAndroidUnwindInstructionTest,TestPopRegistersIncludingR14MaxRegisters)479 TEST(ChromeAndroidUnwindInstructionTest,
480      TestPopRegistersIncludingR14MaxRegisters) {
481   RegisterContext thread_context = {};
482 
483   thread_context.arm_r0 = 100;
484   thread_context.arm_r1 = 101;
485   thread_context.arm_r2 = 102;
486   thread_context.arm_r3 = 103;
487   thread_context.arm_r4 = 104;
488   thread_context.arm_r5 = 105;
489   thread_context.arm_r6 = 106;
490   thread_context.arm_r7 = 107;
491   thread_context.arm_r8 = 108;
492   thread_context.arm_r9 = 109;
493   thread_context.arm_r10 = 110;
494   thread_context.arm_fp = 111;
495   thread_context.arm_ip = 112;
496   thread_context.arm_lr = 113;
497 
498   // Popping r4 - r[4 + nnn], r14, at most 9 registers.
499   // r14 = lr
500   const uintptr_t stack[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
501 
502   thread_context.arm_sp = reinterpret_cast<uintptr_t>(&stack[0]);
503   const uint8_t instruction = 0b10101111;  // Pop r4 - r11, r14.
504   const uint8_t* current_instruction = &instruction;
505   bool pc_was_updated = false;
506   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
507                                      &thread_context),
508             UnwindInstructionResult::kInstructionPending);
509   EXPECT_FALSE(pc_was_updated);
510   ASSERT_EQ(current_instruction, &instruction + 1);
511   EXPECT_EQ(reinterpret_cast<uintptr_t>(&stack[0] + 9), thread_context.arm_sp);
512 
513   EXPECT_EQ(100ul, thread_context.arm_r0);
514   EXPECT_EQ(101ul, thread_context.arm_r1);
515   EXPECT_EQ(102ul, thread_context.arm_r2);
516   EXPECT_EQ(103ul, thread_context.arm_r3);
517   EXPECT_EQ(1ul, thread_context.arm_r4);
518   EXPECT_EQ(2ul, thread_context.arm_r5);
519   EXPECT_EQ(3ul, thread_context.arm_r6);
520   EXPECT_EQ(4ul, thread_context.arm_r7);
521   EXPECT_EQ(5ul, thread_context.arm_r8);
522   EXPECT_EQ(6ul, thread_context.arm_r9);
523   EXPECT_EQ(7ul, thread_context.arm_r10);
524   EXPECT_EQ(8ul, thread_context.arm_fp);
525   EXPECT_EQ(112ul, thread_context.arm_ip);
526   EXPECT_EQ(9ul, thread_context.arm_lr);
527 }
528 
TEST(ChromeAndroidUnwindInstructionTest,TestPopRegistersIncludingR14Overflow)529 TEST(ChromeAndroidUnwindInstructionTest, TestPopRegistersIncludingR14Overflow) {
530   RegisterContext thread_context = {};
531 
532   thread_context.arm_r0 = 100;
533   thread_context.arm_r1 = 101;
534   thread_context.arm_r2 = 102;
535   thread_context.arm_r3 = 103;
536   thread_context.arm_r4 = 104;
537   thread_context.arm_r5 = 105;
538   thread_context.arm_r6 = 106;
539   thread_context.arm_r7 = 107;
540   thread_context.arm_r8 = 108;
541   thread_context.arm_r9 = 109;
542   thread_context.arm_r10 = 110;
543   thread_context.arm_fp = 111;
544   thread_context.arm_ip = 112;
545   thread_context.arm_lr = 113;
546 
547   // Popping r4 - r[4 + nnn], r14, at most 9 registers.
548   // r14 = lr
549   thread_context.arm_sp = 0xffffffff;
550   const uint8_t instruction = 0b10101111;  // Pop r4 - r11, r14.
551   const uint8_t* current_instruction = &instruction;
552   bool pc_was_updated = false;
553   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
554                                      &thread_context),
555             UnwindInstructionResult::kAborted);
556   EXPECT_FALSE(pc_was_updated);
557   ASSERT_EQ(current_instruction, &instruction + 1);
558   EXPECT_EQ(0xffffffff, thread_context.arm_sp);
559 
560   EXPECT_EQ(100ul, thread_context.arm_r0);
561   EXPECT_EQ(101ul, thread_context.arm_r1);
562   EXPECT_EQ(102ul, thread_context.arm_r2);
563   EXPECT_EQ(103ul, thread_context.arm_r3);
564   EXPECT_EQ(104ul, thread_context.arm_r4);
565   EXPECT_EQ(105ul, thread_context.arm_r5);
566   EXPECT_EQ(106ul, thread_context.arm_r6);
567   EXPECT_EQ(107ul, thread_context.arm_r7);
568   EXPECT_EQ(108ul, thread_context.arm_r8);
569   EXPECT_EQ(109ul, thread_context.arm_r9);
570   EXPECT_EQ(110ul, thread_context.arm_r10);
571   EXPECT_EQ(111ul, thread_context.arm_fp);
572   EXPECT_EQ(112ul, thread_context.arm_ip);
573   EXPECT_EQ(113ul, thread_context.arm_lr);
574 }
575 
TEST(ChromeAndroidUnwindInstructionTest,TestBigStackPointerIncrementMinValue)576 TEST(ChromeAndroidUnwindInstructionTest, TestBigStackPointerIncrementMinValue) {
577   RegisterContext thread_context = {};
578   thread_context.arm_sp = 0x10000000;
579 
580   const uint8_t increment_0[] = {
581       0b10110010,
582       0b00000000,
583   };
584   const uint8_t* current_instruction = &increment_0[0];
585   bool pc_was_updated = false;
586   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
587                                      &thread_context),
588             UnwindInstructionResult::kInstructionPending);
589   EXPECT_FALSE(pc_was_updated);
590   ASSERT_EQ(current_instruction, increment_0 + sizeof(increment_0));
591   // vsp + 0x204 + (0 << 2)
592   // = vsp + 0x204
593   EXPECT_EQ(0x10000204ul, thread_context.arm_sp);
594 }
595 
TEST(ChromeAndroidUnwindInstructionTest,TestBigStackPointerIncrementMidValue)596 TEST(ChromeAndroidUnwindInstructionTest, TestBigStackPointerIncrementMidValue) {
597   RegisterContext thread_context = {};
598   thread_context.arm_sp = 0x10000000;
599 
600   const uint8_t increment_4[] = {
601       0b10110010,
602       0b00000100,
603   };
604   const uint8_t* current_instruction = &increment_4[0];
605 
606   // vsp + 0x204 + (4 << 2)
607   // = vsp + 0x204 + 0x10
608   // = vsp + 0x214
609   bool pc_was_updated = false;
610   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
611                                      &thread_context),
612             UnwindInstructionResult::kInstructionPending);
613   EXPECT_FALSE(pc_was_updated);
614   ASSERT_EQ(current_instruction, increment_4 + sizeof(increment_4));
615   EXPECT_EQ(0x10000214ul, thread_context.arm_sp);
616 }
617 
TEST(ChromeAndroidUnwindInstructionTest,TestBigStackPointerIncrementLargeValue)618 TEST(ChromeAndroidUnwindInstructionTest,
619      TestBigStackPointerIncrementLargeValue) {
620   RegisterContext thread_context = {};
621   thread_context.arm_sp = 0x10000000;
622 
623   const uint8_t increment_128[] = {
624       0b10110010,
625       0b10000000,
626       0b00000001,
627   };
628   const uint8_t* current_instruction = &increment_128[0];
629   // vsp + 0x204 + (128 << 2)
630   // = vsp + 0x204 + 512
631   // = vsp + 0x204 + 0x200
632   // = vsp + 0x404
633   bool pc_was_updated = false;
634   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
635                                      &thread_context),
636             UnwindInstructionResult::kInstructionPending);
637   EXPECT_FALSE(pc_was_updated);
638   ASSERT_EQ(current_instruction, increment_128 + sizeof(increment_128));
639   EXPECT_EQ(0x10000404ul, thread_context.arm_sp);
640 }
641 
TEST(ChromeAndroidUnwindInstructionTest,TestBigStackPointerIncrementOverflow)642 TEST(ChromeAndroidUnwindInstructionTest, TestBigStackPointerIncrementOverflow) {
643   RegisterContext thread_context = {};
644   thread_context.arm_sp = 0xffffffff;
645 
646   const uint8_t increment_overflow[] = {
647       0b10110010,
648       0b10000000,
649       0b00000001,
650   };  // ULEB128 = 128
651   const uint8_t* current_instruction = &increment_overflow[0];
652   bool pc_was_updated = false;
653   ASSERT_EQ(ExecuteUnwindInstruction(current_instruction, pc_was_updated,
654                                      &thread_context),
655             UnwindInstructionResult::kAborted);
656   EXPECT_FALSE(pc_was_updated);
657   ASSERT_EQ(current_instruction,
658             increment_overflow + sizeof(increment_overflow));
659   EXPECT_EQ(0xfffffffful, thread_context.arm_sp);
660 }
661 
TEST(ChromeUnwinderAndroidTest,TestFunctionOffsetTableLookupExactMatchingOffset)662 TEST(ChromeUnwinderAndroidTest,
663      TestFunctionOffsetTableLookupExactMatchingOffset) {
664   const uint8_t function_offset_table[] = {
665       // Function 1: [(130, 2), (128, 3), (0, 4)]
666       // offset = 130
667       0b10000010,
668       0b00000001,
669       // unwind index = 2
670       0b00000010,
671       // offset = 128
672       0b10000000,
673       0b00000001,
674       // unwind index = 3
675       0b00000011,
676       // offset = 0
677       0b00000000,
678       // unwind index = 4
679       0b00000100,
680   };
681 
682   EXPECT_EQ(3ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
683                      &function_offset_table[0],
684                      /* instruction_offset_from_function_start */ 128));
685 }
686 
TEST(ChromeUnwinderAndroidTest,TestFunctionOffsetTableLookupNonExactMatchingOffset)687 TEST(ChromeUnwinderAndroidTest,
688      TestFunctionOffsetTableLookupNonExactMatchingOffset) {
689   const uint8_t function_offset_table[] = {
690       // Function 1: [(130, 2), (128, 3), (0, 4)]
691       // offset = 130
692       0b10000010,
693       0b00000001,
694       // unwind index = 2
695       0b00000010,
696       // offset = 128
697       0b10000000,
698       0b00000001,
699       // unwind index = 3
700       0b00000011,
701       // offset = 0
702       0b00000000,
703       // unwind index = 4
704       0b00000100,
705   };
706 
707   EXPECT_EQ(3ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
708                      &function_offset_table[0],
709                      /* instruction_offset_from_function_start */ 129));
710 }
711 
TEST(ChromeUnwinderAndroidTest,TestFunctionOffsetTableLookupZeroOffset)712 TEST(ChromeUnwinderAndroidTest, TestFunctionOffsetTableLookupZeroOffset) {
713   const uint8_t function_offset_table[] = {
714       // Function 1: [(130, 2), (128, 3), (0, 4)]
715       // offset = 130
716       0b10000010,
717       0b00000001,
718       // unwind index = 2
719       0b00000010,
720       // offset = 128
721       0b10000000,
722       0b00000001,
723       // unwind index = 3
724       0b00000011,
725       // offset = 0
726       0b00000000,
727       // unwind index = 4
728       0b00000100,
729   };
730 
731   EXPECT_EQ(4ul, GetFirstUnwindInstructionIndexFromFunctionOffsetTableEntry(
732                      &function_offset_table[0],
733                      /* instruction_offset_from_function_start */ 0));
734 }
735 
TEST(ChromeUnwinderAndroidTest,TestAddressTableLookupEntryInPage)736 TEST(ChromeUnwinderAndroidTest, TestAddressTableLookupEntryInPage) {
737   const uint32_t page_start_instructions[] = {0, 2};
738   const FunctionTableEntry function_offset_table_indices[] = {
739       // Page 0
740       {
741           /* function_start_address_page_instruction_offset */ 0,
742           /* function_offset_table_byte_index */ 20,
743       },
744       {
745           /* function_start_address_page_instruction_offset */ 4,
746           /* function_offset_table_byte_index */ 40,
747       },
748       // Page 1
749       {
750           /* function_start_address_page_instruction_offset */ 6,
751           /* function_offset_table_byte_index */ 70,
752       },
753   };
754 
755   {
756     const uint32_t page_number = 0;
757     const uint32_t page_instruction_offset = 4;
758     const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
759         page_start_instructions, function_offset_table_indices,
760         /* instruction_offset */ (page_instruction_offset << 1) +
761             (page_number << 17));
762     ASSERT_NE(std::nullopt, entry_found);
763     EXPECT_EQ(0, entry_found->instruction_offset_from_function_start);
764     EXPECT_EQ(40ul, entry_found->function_offset_table_byte_index);
765   }
766 
767   {
768     const uint32_t page_number = 0;
769     const uint32_t page_instruction_offset = 50;
770     const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
771         page_start_instructions, function_offset_table_indices,
772         /* instruction_offset */ (page_instruction_offset << 1) +
773             (page_number << 17));
774     ASSERT_NE(std::nullopt, entry_found);
775     EXPECT_EQ(46, entry_found->instruction_offset_from_function_start);
776     EXPECT_EQ(40ul, entry_found->function_offset_table_byte_index);
777   }
778 
779   // Lookup last instruction in last function.
780   {
781     const uint32_t page_number = 1;
782     const uint32_t page_instruction_offset = 0xffff;
783     const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
784         page_start_instructions, function_offset_table_indices,
785         /* instruction_offset */ (page_instruction_offset << 1) +
786             (page_number << 17));
787     ASSERT_NE(std::nullopt, entry_found);
788     // 0xffff - 6 = 0xfff9.
789     EXPECT_EQ(0xfff9, entry_found->instruction_offset_from_function_start);
790     EXPECT_EQ(70ul, entry_found->function_offset_table_byte_index);
791   }
792 }
793 
TEST(ChromeUnwinderAndroidTest,TestAddressTableLookupEmptyPage)794 TEST(ChromeUnwinderAndroidTest, TestAddressTableLookupEmptyPage) {
795   const uint32_t page_start_instructions[] = {0, 1, 1};
796   const FunctionTableEntry function_offset_table_indices[] = {
797       // Page 0
798       {
799           /* function_start_address_page_instruction_offset */ 0,
800           /* function_offset_table_byte_index */ 20,
801       },
802       // Page 1 is empty
803       // Page 2
804       {
805           /* function_start_address_page_instruction_offset */ 6,
806           /* function_offset_table_byte_index */ 70,
807       },
808   };
809 
810   const uint32_t page_number = 1;
811   const uint32_t page_instruction_offset = 4;
812   const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
813       page_start_instructions, function_offset_table_indices,
814       /* instruction_offset */ (page_instruction_offset << 1) +
815           (page_number << 17));
816   ASSERT_NE(std::nullopt, entry_found);
817   EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
818   EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
819 }
820 
TEST(ChromeUnwinderAndroidTest,TestAddressTableLookupInvalidIntructionOffset)821 TEST(ChromeUnwinderAndroidTest, TestAddressTableLookupInvalidIntructionOffset) {
822   const uint32_t page_start_instructions[] = {0, 1};
823   const FunctionTableEntry function_offset_table_indices[] = {
824       // Page 0
825       // This function spans from page 0 offset 0 to page 1 offset 5.
826       {
827           /* function_start_address_page_instruction_offset */ 0,
828           /* function_offset_table_byte_index */ 20,
829       },
830       // Page 1
831       {
832           /* function_start_address_page_instruction_offset */ 6,
833           /* function_offset_table_byte_index */ 70,
834       },
835   };
836 
837   // Instruction offset lies after last page on page table.
838   {
839     const uint32_t page_number = 50;
840     const uint32_t page_instruction_offset = 6;
841     const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
842         page_start_instructions, function_offset_table_indices,
843         /* instruction_offset */ (page_instruction_offset << 1) +
844             (page_number << 17));
845     ASSERT_EQ(std::nullopt, entry_found);
846   }
847   {
848     const uint32_t page_number = 2;
849     const uint32_t page_instruction_offset = 0;
850     const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
851         page_start_instructions, function_offset_table_indices,
852         /* instruction_offset */ (page_instruction_offset << 1) +
853             (page_number << 17));
854     ASSERT_EQ(std::nullopt, entry_found);
855   }
856 }
857 
TEST(ChromeUnwinderAndroidTest,TestAddressTableLookupOnSecondPageOfFunctionSpanningPageBoundary)858 TEST(ChromeUnwinderAndroidTest,
859      TestAddressTableLookupOnSecondPageOfFunctionSpanningPageBoundary) {
860   const uint32_t page_start_instructions[] = {0, 1, 2};
861   const FunctionTableEntry function_offset_table_indices[] = {
862       // Page 0
863       {
864           /* function_start_address_page_instruction_offset */ 0,
865           /* function_offset_table_byte_index */ 20,
866       },
867       // Page 1
868       {
869           /* function_start_address_page_instruction_offset */ 6,
870           /* function_offset_table_byte_index */ 70,
871       },
872       // Page 2
873       {
874           /* function_start_address_page_instruction_offset */ 10,
875           /* function_offset_table_byte_index */ 80,
876       }};
877 
878   const uint32_t page_number = 1;
879   const uint32_t page_instruction_offset = 4;
880   const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
881       page_start_instructions, function_offset_table_indices,
882       /* instruction_offset */ (page_instruction_offset << 1) +
883           (page_number << 17));
884   ASSERT_NE(std::nullopt, entry_found);
885   EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
886   EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
887 }
888 
TEST(ChromeUnwinderAndroidTest,TestAddressTableLookupWithinFunctionSpanningMultiplePages)889 TEST(ChromeUnwinderAndroidTest,
890      TestAddressTableLookupWithinFunctionSpanningMultiplePages) {
891   const uint32_t page_start_instructions[] = {0, 1, 1, 1};
892   const FunctionTableEntry function_offset_table_indices[] = {
893       // Page 0
894       // This function spans from page 0 offset 0 to page 3 offset 5.
895       {
896           /* function_start_address_page_instruction_offset */ 0,
897           /* function_offset_table_byte_index */ 20,
898       },
899       // Page 1 is empty
900       // Page 2 is empty
901       // Page 3
902       {
903           /* function_start_address_page_instruction_offset */ 6,
904           /* function_offset_table_byte_index */ 70,
905       },
906   };
907 
908   {
909     const uint32_t page_number = 0;
910     const uint32_t page_instruction_offset = 4;
911     const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
912         page_start_instructions, function_offset_table_indices,
913         /* instruction_offset */ (page_instruction_offset << 1) +
914             (page_number << 17));
915     ASSERT_NE(std::nullopt, entry_found);
916     EXPECT_EQ(0x4, entry_found->instruction_offset_from_function_start);
917     EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
918   }
919   {
920     const uint32_t page_number = 1;
921     const uint32_t page_instruction_offset = 4;
922     const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
923         page_start_instructions, function_offset_table_indices,
924         /* instruction_offset */ (page_instruction_offset << 1) +
925             (page_number << 17));
926     ASSERT_NE(std::nullopt, entry_found);
927     EXPECT_EQ(0x10004, entry_found->instruction_offset_from_function_start);
928     EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
929   }
930   {
931     const uint32_t page_number = 2;
932     const uint32_t page_instruction_offset = 4;
933     const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
934         page_start_instructions, function_offset_table_indices,
935         /* instruction_offset */ (page_instruction_offset << 1) +
936             (page_number << 17));
937     ASSERT_NE(std::nullopt, entry_found);
938     EXPECT_EQ(0x20004, entry_found->instruction_offset_from_function_start);
939     EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
940   }
941   {
942     const uint32_t page_number = 3;
943     const uint32_t page_instruction_offset = 4;
944     const auto entry_found = GetFunctionTableIndexFromInstructionOffset(
945         page_start_instructions, function_offset_table_indices,
946         /* instruction_offset */ (page_instruction_offset << 1) +
947             (page_number << 17));
948     ASSERT_NE(std::nullopt, entry_found);
949     EXPECT_EQ(0x30004, entry_found->instruction_offset_from_function_start);
950     EXPECT_EQ(20ul, entry_found->function_offset_table_byte_index);
951   }
952 }
953 
954 // Utility function to add a single native module during test setup. Returns
955 // a pointer to the provided module.
AddNativeModule(ModuleCache * cache,std::unique_ptr<const ModuleCache::Module> module)956 const ModuleCache::Module* AddNativeModule(
957     ModuleCache* cache,
958     std::unique_ptr<const ModuleCache::Module> module) {
959   const ModuleCache::Module* module_ptr = module.get();
960   cache->AddCustomNativeModule(std::move(module));
961   return module_ptr;
962 }
963 
TEST(ChromeUnwinderAndroidTest,CanUnwindFrom)964 TEST(ChromeUnwinderAndroidTest, CanUnwindFrom) {
965   const uint32_t page_table[] = {0};
966   const FunctionTableEntry function_table[] = {{0, 0}};
967   const uint8_t function_offset_table[] = {0};
968   const uint8_t unwind_instruction_table[] = {0};
969   auto dummy_unwind_info = ChromeUnwindInfoAndroid{
970       unwind_instruction_table,
971       function_offset_table,
972       function_table,
973       page_table,
974   };
975 
976   auto chrome_module = std::make_unique<TestModule>(0x1000, 0x500);
977   auto non_chrome_module = std::make_unique<TestModule>(0x2000, 0x500);
978 
979   ModuleCache module_cache;
980   ChromeUnwinderAndroid unwinder(dummy_unwind_info,
981                                  chrome_module->GetBaseAddress(),
982                                  /* text_section_start_address */
983                                  chrome_module->GetBaseAddress() + 4);
984   unwinder.Initialize(&module_cache);
985 
986   EXPECT_TRUE(unwinder.CanUnwindFrom({0x1100, chrome_module.get()}));
987   EXPECT_TRUE(unwinder.CanUnwindFrom({0x1000, chrome_module.get()}));
988   EXPECT_FALSE(unwinder.CanUnwindFrom({0x2100, non_chrome_module.get()}));
989   EXPECT_FALSE(unwinder.CanUnwindFrom({0x400, nullptr}));
990 }
991 
992 namespace {
ExpectFramesEq(const std::vector<Frame> & expected,const std::vector<Frame> & actual)993 void ExpectFramesEq(const std::vector<Frame>& expected,
994                     const std::vector<Frame>& actual) {
995   EXPECT_EQ(actual.size(), expected.size());
996   if (actual.size() != expected.size())
997     return;
998 
999   for (size_t i = 0; i < actual.size(); i++) {
1000     EXPECT_EQ(expected[i].module, actual[i].module);
1001     EXPECT_EQ(expected[i].instruction_pointer, actual[i].instruction_pointer);
1002   }
1003 }
1004 
1005 class AlignedStackMemory {
1006  public:
AlignedStackMemory(std::initializer_list<uintptr_t> values)1007   AlignedStackMemory(std::initializer_list<uintptr_t> values)
1008       : size_(values.size()),
1009         stack_memory_(static_cast<uintptr_t*>(
1010             AlignedAlloc(size_ * sizeof(uintptr_t), 2 * sizeof(uintptr_t)))) {
1011     DCHECK_EQ(size_ % 2, 0u);
1012     ranges::copy(values, stack_memory_.get());
1013   }
1014 
stack_start_address() const1015   uintptr_t stack_start_address() const {
1016     return reinterpret_cast<uintptr_t>(stack_memory_.get());
1017   }
1018 
stack_end_address() const1019   uintptr_t stack_end_address() const {
1020     return reinterpret_cast<uintptr_t>(stack_memory_.get() + size_);
1021   }
1022 
1023  private:
1024   const uintptr_t size_;
1025   const std::unique_ptr<uintptr_t, AlignedFreeDeleter> stack_memory_;
1026 };
1027 
1028 }  // namespace
1029 
TEST(ChromeUnwinderAndroidTest,TryUnwind)1030 TEST(ChromeUnwinderAndroidTest, TryUnwind) {
1031   const uint32_t page_table[] = {0, 2};
1032   const size_t number_of_pages = std::size(page_table);
1033   const size_t page_size = 1 << 17;
1034 
1035   const FunctionTableEntry function_table[] = {
1036       // Page 0.
1037       {0, 0},     // Function 0.
1038       {0x10, 4},  // Function 1. The function to unwind 2 times.
1039       // Page 1.
1040       {0x5, 8},    // Function 2.
1041       {0x20, 12},  // Function 3.
1042   };
1043   const uint8_t function_offset_table[] = {
1044       // Function 0.
1045       0x2,
1046       0,
1047       0x0,
1048       1,
1049       // Function 1.
1050       0x7f,
1051       0,
1052       0x0,
1053       1,
1054       // Function 2.
1055       0x78,
1056       0,
1057       0x0,
1058       1,
1059       // Function 3.
1060       0x2,
1061       0,
1062       0x0,
1063       1,
1064   };
1065   const uint8_t unwind_instruction_table[] = {
1066       // Offset 0: Pop r4, r14 from stack top.
1067       // Need to pop 2 registers to keep SP aligned.
1068       0b10101000,
1069       // Offset 1: COMPLETE.
1070       0b10110000,
1071   };
1072 
1073   auto unwind_info = ChromeUnwindInfoAndroid{
1074       unwind_instruction_table,
1075       function_offset_table,
1076       function_table,
1077       page_table,
1078   };
1079 
1080   ModuleCache module_cache;
1081   const ModuleCache::Module* chrome_module = AddNativeModule(
1082       &module_cache, std::make_unique<TestModule>(
1083                          0x1000, number_of_pages * page_size, "ChromeModule"));
1084 
1085   uintptr_t text_section_start_address = 0x1100;
1086   ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1087                                  text_section_start_address);
1088 
1089   unwinder.Initialize(&module_cache);
1090 
1091   // Both first_pc and second_pc lie in Function 1's address range.
1092   uintptr_t first_pc = text_section_start_address + 0x20;
1093   uintptr_t second_pc = text_section_start_address + page_size + 0x4;
1094   // third_pc lies outside chrome_module's address range.
1095   uintptr_t third_pc = text_section_start_address + 3 * page_size;
1096 
1097   AlignedStackMemory stack_memory = {
1098       0x0,
1099       third_pc,
1100       0xFFFF,
1101       0xFFFF,
1102   };
1103 
1104   std::vector<Frame> unwound_frames = {{first_pc, chrome_module}};
1105   RegisterContext context;
1106   RegisterContextInstructionPointer(&context) = first_pc;
1107   RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1108   context.arm_lr = second_pc;
1109 
1110   EXPECT_EQ(UnwindResult::kUnrecognizedFrame,
1111             unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1112                                &unwound_frames));
1113   ExpectFramesEq(std::vector<Frame>({{first_pc, chrome_module},
1114                                      {second_pc, chrome_module},
1115                                      {third_pc, nullptr}}),
1116                  unwound_frames);
1117 }
1118 
TEST(ChromeUnwinderAndroidTest,TryUnwindInfiniteLoopSingleFrame)1119 TEST(ChromeUnwinderAndroidTest, TryUnwindInfiniteLoopSingleFrame) {
1120   const uint32_t page_table[] = {0, 2};
1121   const size_t number_of_pages = std::size(page_table);
1122   const size_t page_size = 1 << 17;
1123 
1124   const FunctionTableEntry function_table[] = {
1125       // Page 0.
1126       {0x0, 0},   // Refuse to unwind filler function.
1127       {0x10, 2},  // Function 0. The function to unwind.
1128       // Page 1.
1129       {0x5, 0},  // Refuse to unwind filler function.
1130   };
1131   const uint8_t function_offset_table[] = {
1132       // Refuse to unwind filler function.
1133       0x0,
1134       0,
1135       // Function 0.
1136       0x0,
1137       2,
1138   };
1139   const uint8_t unwind_instruction_table[] = {
1140       // Offset 0: REFUSE_TO_UNWIND.
1141       0b10000000,
1142       0b00000000,
1143       // Offset 2: COMPLETE.
1144       0b10110000,
1145   };
1146 
1147   auto unwind_info = ChromeUnwindInfoAndroid{
1148       unwind_instruction_table,
1149       function_offset_table,
1150       function_table,
1151       page_table,
1152   };
1153 
1154   ModuleCache module_cache;
1155   const ModuleCache::Module* chrome_module = AddNativeModule(
1156       &module_cache, std::make_unique<TestModule>(
1157                          0x1000, number_of_pages * page_size, "ChromeModule"));
1158 
1159   uintptr_t text_section_start_address = 0x1100;
1160   ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1161                                  text_section_start_address);
1162 
1163   unwinder.Initialize(&module_cache);
1164   uintptr_t pc = text_section_start_address + 0x20;
1165 
1166   AlignedStackMemory stack_memory = {
1167       0xFFFF,
1168       0xFFFF,
1169   };
1170 
1171   std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1172   RegisterContext context;
1173   RegisterContextInstructionPointer(&context) = pc;
1174   RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1175 
1176   // Set lr = pc so that both sp and pc stays the same after first round of
1177   // unwind.
1178   context.arm_lr = pc;
1179 
1180   EXPECT_EQ(UnwindResult::kAborted,
1181             unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1182                                &unwound_frames));
1183   ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1184 }
1185 
TEST(ChromeUnwinderAndroidTest,TryUnwindInfiniteLoopMultipleFrames)1186 TEST(ChromeUnwinderAndroidTest, TryUnwindInfiniteLoopMultipleFrames) {
1187   // This test aims to produce a scenario, where after the unwind of a number
1188   // of frames, the sp and pc get to their original state before the unwind.
1189 
1190   // Function 1 (pc1, sp1):
1191   // - set pc = lr(pc2)
1192   // Function 2 (pc2, sp1):
1193   // - pop r14(pc2), r15(pc1) off stack
1194   // - vsp = r4 (reset vsp to frame initial vsp)
1195 
1196   const uint32_t page_table[] = {0, 3};
1197   const size_t number_of_pages = std::size(page_table);
1198   const size_t page_size = 1 << 17;
1199 
1200   const FunctionTableEntry function_table[] = {
1201       // Page 0.
1202       {0x0, 0},    // Refuse to unwind filler function.
1203       {0x10, 2},   // Function 1. The function to unwind.
1204       {0x100, 2},  // Function 2. The function to unwind.
1205       // Page 1.
1206       {0x5, 0},  // Refuse to unwind filler function.
1207   };
1208   const uint8_t function_offset_table[] = {
1209       // Refuse to unwind filler function.
1210       0x0,
1211       0,
1212       // Function 0.
1213       0x0,
1214       2,
1215       // Function 1.
1216       0x2,
1217       3,
1218       0x1,
1219       5,
1220       0x0,
1221       6,
1222   };
1223   const uint8_t unwind_instruction_table[] = {
1224       // Offset 0: REFUSE_TO_UNWIND.
1225       0b10000000,
1226       0b00000000,
1227       // Offset 2: COMPLETE.
1228       0b10110000,
1229       // Offset 3: POP r14, r15 off the stack.
1230       0b10001100,
1231       0b00000000,
1232       // Offset 5: vsp = r4.
1233       0b10010100,
1234       // Offset 6: COMPLETE.
1235       0b10110000,
1236   };
1237 
1238   auto unwind_info = ChromeUnwindInfoAndroid{
1239       unwind_instruction_table,
1240       function_offset_table,
1241       function_table,
1242       page_table,
1243   };
1244 
1245   ModuleCache module_cache;
1246   const ModuleCache::Module* chrome_module = AddNativeModule(
1247       &module_cache, std::make_unique<TestModule>(
1248                          0x1000, number_of_pages * page_size, "ChromeModule"));
1249 
1250   uintptr_t text_section_start_address = 0x1100;
1251   ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1252                                  text_section_start_address);
1253 
1254   unwinder.Initialize(&module_cache);
1255   uintptr_t first_pc = text_section_start_address + 0x20;    // Function 1.
1256   uintptr_t second_pc = text_section_start_address + 0x110;  // Function 2.
1257 
1258   AlignedStackMemory stack_memory = {
1259       second_pc,
1260       first_pc,
1261       0xFFFF,
1262       0xFFFF,
1263   };
1264 
1265   std::vector<Frame> unwound_frames = {{first_pc, chrome_module}};
1266   RegisterContext context;
1267   RegisterContextInstructionPointer(&context) = first_pc;
1268   RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1269 
1270   context.arm_lr = second_pc;
1271   context.arm_r4 = stack_memory.stack_start_address();
1272 
1273   EXPECT_EQ(UnwindResult::kAborted,
1274             unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1275                                &unwound_frames));
1276   ExpectFramesEq(std::vector<Frame>(
1277                      {{first_pc, chrome_module}, {second_pc, chrome_module}}),
1278                  unwound_frames);
1279 }
1280 
TEST(ChromeUnwinderAndroidTest,TryUnwindUnalignedSPFrameUnwind)1281 TEST(ChromeUnwinderAndroidTest, TryUnwindUnalignedSPFrameUnwind) {
1282   // SP should be 2-uintptr_t aligned before/after each frame unwind.
1283   const uint32_t page_table[] = {0, 2};
1284   const size_t number_of_pages = std::size(page_table);
1285   const size_t page_size = 1 << 17;
1286 
1287   const FunctionTableEntry function_table[] = {
1288       // Page 0.
1289       {0x0, 0},   // Refuse to unwind filler function.
1290       {0x10, 2},  // Function 0. The function to unwind.
1291       // Page 1.
1292       {0x5, 0},  // Refuse to unwind filler function.
1293   };
1294   const uint8_t function_offset_table[] = {
1295       // Refuse to unwind filler function.
1296       0x0,
1297       0,
1298       // Function 0.
1299       0x0,
1300       2,
1301   };
1302   const uint8_t unwind_instruction_table[] = {
1303       // Offset 0: REFUSE_TO_UNWIND.
1304       0b10000000,
1305       0b00000000,
1306       // Offset 2: COMPLETE.
1307       0b10110000,
1308   };
1309 
1310   auto unwind_info = ChromeUnwindInfoAndroid{
1311       unwind_instruction_table,
1312       function_offset_table,
1313       function_table,
1314       page_table,
1315   };
1316 
1317   ModuleCache module_cache;
1318   const ModuleCache::Module* chrome_module = AddNativeModule(
1319       &module_cache, std::make_unique<TestModule>(
1320                          0x1000, number_of_pages * page_size, "ChromeModule"));
1321 
1322   uintptr_t text_section_start_address = 0x1100;
1323   ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1324                                  text_section_start_address);
1325 
1326   unwinder.Initialize(&module_cache);
1327   uintptr_t pc = text_section_start_address + 0x20;
1328 
1329   AlignedStackMemory stack_memory = {
1330       0xFFFF,
1331       0xFFFF,
1332   };
1333 
1334   std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1335   RegisterContext context;
1336   RegisterContextInstructionPointer(&context) = pc;
1337   // Make stack memory not aligned to 2 * sizeof(uintptr_t);
1338   RegisterContextStackPointer(&context) =
1339       stack_memory.stack_start_address() + sizeof(uintptr_t);
1340 
1341   // The address is outside chrome module, which will result the unwind to
1342   // stop with result kUnrecognizedFrame if SP alignment issue was not detected.
1343   context.arm_lr =
1344       text_section_start_address + (number_of_pages + 1) * page_size;
1345 
1346   EXPECT_EQ(UnwindResult::kAborted,
1347             unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1348                                &unwound_frames));
1349   ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1350 }
1351 
TEST(ChromeUnwinderAndroidTest,TryUnwindUnalignedSPInstructionUnwind)1352 TEST(ChromeUnwinderAndroidTest, TryUnwindUnalignedSPInstructionUnwind) {
1353   // SP should be uintptr_t aligned before/after each unwind instruction
1354   // execution.
1355 
1356   const uint32_t page_table[] = {0, 2};
1357   const size_t number_of_pages = std::size(page_table);
1358   const size_t page_size = 1 << 17;
1359 
1360   const FunctionTableEntry function_table[] = {
1361       // Page 0.
1362       {0x0, 0},   // Refuse to unwind filler function.
1363       {0x10, 2},  // Function 0. The function to unwind.
1364       // Page 1.
1365       {0x5, 0},  // Refuse to unwind filler function.
1366   };
1367   const uint8_t function_offset_table[] = {
1368       // Refuse to unwind filler function.
1369       0x0,
1370       0,
1371       // Function 0.
1372       0x0,
1373       2,
1374   };
1375   const uint8_t unwind_instruction_table[] = {
1376       // Offset 0: REFUSE_TO_UNWIND.
1377       0b10000000, 0b00000000,
1378       // Offset 2:
1379       0b10010100,  // vsp = r4, where r4 = stack + (sizeof(uintptr_t) / 2)
1380       0b10110000,  // COMPLETE.
1381   };
1382 
1383   auto unwind_info = ChromeUnwindInfoAndroid{
1384       unwind_instruction_table,
1385       function_offset_table,
1386       function_table,
1387       page_table,
1388   };
1389 
1390   ModuleCache module_cache;
1391   const ModuleCache::Module* chrome_module = AddNativeModule(
1392       &module_cache, std::make_unique<TestModule>(
1393                          0x1000, number_of_pages * page_size, "ChromeModule"));
1394 
1395   uintptr_t text_section_start_address = 0x1100;
1396   ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1397                                  text_section_start_address);
1398 
1399   unwinder.Initialize(&module_cache);
1400   uintptr_t pc = text_section_start_address + 0x20;
1401 
1402   AlignedStackMemory stack_memory = {
1403       0xFFFF,
1404       0xFFFF,
1405   };
1406 
1407   std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1408   RegisterContext context;
1409   RegisterContextInstructionPointer(&context) = pc;
1410   RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1411 
1412   // The address is outside chrome module, which will result the unwind to
1413   // stop with result kUnrecognizedFrame if SP alignment issue was not detected.
1414   context.arm_lr =
1415       text_section_start_address + (number_of_pages + 1) * page_size;
1416 
1417   context.arm_r4 = stack_memory.stack_start_address() + sizeof(uintptr_t) / 2;
1418 
1419   EXPECT_EQ(UnwindResult::kAborted,
1420             unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1421                                &unwound_frames));
1422   ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1423 }
1424 
TEST(ChromeUnwinderAndroidTest,TryUnwindSPOverflow)1425 TEST(ChromeUnwinderAndroidTest, TryUnwindSPOverflow) {
1426   const uint32_t page_table[] = {0, 2};
1427   const size_t number_of_pages = std::size(page_table);
1428   const size_t page_size = 1 << 17;
1429 
1430   const FunctionTableEntry function_table[] = {
1431       // Page 0.
1432       {0x0, 0},   // Refuse to unwind filler function.
1433       {0x10, 2},  // Function 0. The function to unwind.
1434       // Page 1.
1435       {0x5, 0},  // Refuse to unwind filler function.
1436   };
1437   const uint8_t function_offset_table[] = {
1438       // Refuse to unwind filler function.
1439       0x0,
1440       0,
1441       // Function 0.
1442       0x0,
1443       2,
1444   };
1445   const uint8_t unwind_instruction_table[] = {
1446       // Offset 0: REFUSE_TO_UNWIND.
1447       0b10000000, 0b00000000,
1448       // Offset 2.
1449       0b10010100,  // vsp = r4.
1450       0b10101000,  // Pop r4, r14.
1451       0b10110000,  // COMPLETE.
1452   };
1453 
1454   auto unwind_info = ChromeUnwindInfoAndroid{
1455       unwind_instruction_table,
1456       function_offset_table,
1457       function_table,
1458       page_table,
1459   };
1460 
1461   ModuleCache module_cache;
1462   const ModuleCache::Module* chrome_module = AddNativeModule(
1463       &module_cache, std::make_unique<TestModule>(
1464                          0x1000, number_of_pages * page_size, "ChromeModule"));
1465 
1466   uintptr_t text_section_start_address = 0x1100;
1467   ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1468                                  text_section_start_address);
1469 
1470   unwinder.Initialize(&module_cache);
1471   uintptr_t pc = text_section_start_address + 0x20;
1472 
1473   AlignedStackMemory stack_memory = {
1474       0xFFFF,
1475       0xFFFF,
1476   };
1477   std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1478   RegisterContext context;
1479   RegisterContextInstructionPointer(&context) = pc;
1480   RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1481 
1482   // Setting vsp = 0xffffffff should cause SP overflow.
1483   context.arm_r4 = 0xffffffff;
1484 
1485   // The address is outside chrome module, which will result the unwind to
1486   // stop with result kUnrecognizedFrame if the unwinder did not abort for other
1487   // reasons.
1488   context.arm_lr =
1489       text_section_start_address + (number_of_pages + 1) * page_size;
1490 
1491   EXPECT_EQ(UnwindResult::kAborted,
1492             unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1493                                &unwound_frames));
1494   ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1495 }
1496 
TEST(ChromeUnwinderAndroidTest,TryUnwindNullSP)1497 TEST(ChromeUnwinderAndroidTest, TryUnwindNullSP) {
1498   const uint32_t page_table[] = {0, 2};
1499   const size_t number_of_pages = std::size(page_table);
1500   const size_t page_size = 1 << 17;
1501 
1502   const FunctionTableEntry function_table[] = {
1503       // Page 0.
1504       {0x0, 0},   // Refuse to unwind filler function.
1505       {0x10, 2},  // Function 0. The function to unwind.
1506       // Page 1.
1507       {0x5, 0},  // Refuse to unwind filler function.
1508   };
1509   const uint8_t function_offset_table[] = {
1510       // Refuse to unwind filler function.
1511       0x0,
1512       0,
1513       // Function 0.
1514       0x0,
1515       2,
1516   };
1517   const uint8_t unwind_instruction_table[] = {
1518       // Offset 0: REFUSE_TO_UNWIND.
1519       0b10000000, 0b00000000,
1520       // Offset 2.
1521       0b10010100,  // vsp = r4.
1522       0b10101000,  // Pop r4, r14.
1523       0b10110000,  // COMPLETE.
1524   };
1525 
1526   auto unwind_info = ChromeUnwindInfoAndroid{
1527       unwind_instruction_table,
1528       function_offset_table,
1529       function_table,
1530       page_table,
1531   };
1532 
1533   ModuleCache module_cache;
1534   const ModuleCache::Module* chrome_module = AddNativeModule(
1535       &module_cache, std::make_unique<TestModule>(
1536                          0x1000, number_of_pages * page_size, "ChromeModule"));
1537 
1538   uintptr_t text_section_start_address = 0x1100;
1539   ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1540                                  text_section_start_address);
1541 
1542   unwinder.Initialize(&module_cache);
1543   uintptr_t pc = text_section_start_address + 0x20;
1544 
1545   AlignedStackMemory stack_memory = {
1546       0xFFFF,
1547       0xFFFF,
1548   };
1549   std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1550   RegisterContext context;
1551   RegisterContextInstructionPointer(&context) = pc;
1552   RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1553 
1554   // Setting vsp = 0x0 should cause the unwinder to abort.
1555   context.arm_r4 = 0x0;
1556 
1557   // The address is outside chrome module, which will result the unwind to
1558   // stop with result kUnrecognizedFrame if the unwinder did not abort for other
1559   // reasons.
1560   context.arm_lr =
1561       text_section_start_address + (number_of_pages + 1) * page_size;
1562 
1563   EXPECT_EQ(UnwindResult::kAborted,
1564             unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1565                                &unwound_frames));
1566   ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1567 }
1568 
TEST(ChromeUnwinderAndroidTest,TryUnwindInvalidSPOperation)1569 TEST(ChromeUnwinderAndroidTest, TryUnwindInvalidSPOperation) {
1570   // This test aims to verify that for each unwind instruction executed, it is
1571   // always true that sp > frame initial sp.
1572 
1573   const uint32_t page_table[] = {0, 2};
1574   const size_t number_of_pages = std::size(page_table);
1575   const size_t page_size = 1 << 17;
1576 
1577   const FunctionTableEntry function_table[] = {
1578       // Page 0.
1579       {0x0, 0},   // Refuse to unwind filler function.
1580       {0x10, 2},  // Function 0. The function to unwind.
1581       // Page 1.
1582       {0x5, 0},  // Refuse to unwind filler function.
1583   };
1584   const uint8_t function_offset_table[] = {
1585       // Refuse to unwind filler function.
1586       0x0,
1587       0,
1588       // Function 0.
1589       0x0,
1590       2,
1591   };
1592   const uint8_t unwind_instruction_table[] = {
1593       // Offset 0: REFUSE_TO_UNWIND.
1594       0b10000000, 0b00000000,
1595       // Offset 2.
1596       0b10010100,  // vsp = r4 (r4 < frame initial sp).
1597       0b10010101,  // vsp = r5 (r5 > frame initial sp).
1598       0b10110000,  // COMPLETE.
1599   };
1600 
1601   auto unwind_info = ChromeUnwindInfoAndroid{
1602       unwind_instruction_table,
1603       function_offset_table,
1604       function_table,
1605       page_table,
1606   };
1607 
1608   ModuleCache module_cache;
1609   const ModuleCache::Module* chrome_module = AddNativeModule(
1610       &module_cache, std::make_unique<TestModule>(
1611                          0x1000, number_of_pages * page_size, "ChromeModule"));
1612 
1613   uintptr_t text_section_start_address = 0x1100;
1614   ChromeUnwinderAndroid unwinder(unwind_info, chrome_module->GetBaseAddress(),
1615                                  text_section_start_address);
1616 
1617   unwinder.Initialize(&module_cache);
1618   uintptr_t pc = text_section_start_address + 0x20;
1619 
1620   AlignedStackMemory stack_memory = {
1621       0xFFFF,
1622       0xFFFF,
1623   };
1624   std::vector<Frame> unwound_frames = {{pc, chrome_module}};
1625   RegisterContext context;
1626   RegisterContextInstructionPointer(&context) = pc;
1627   RegisterContextStackPointer(&context) = stack_memory.stack_start_address();
1628 
1629   context.arm_r4 = stack_memory.stack_start_address() - 2 * sizeof(uintptr_t);
1630   context.arm_r5 = stack_memory.stack_start_address() + 2 * sizeof(uintptr_t);
1631 
1632   // The address is outside chrome module, which will result the unwind to
1633   // stop with result kUnrecognizedFrame if the unwinder did not abort for other
1634   // reasons.
1635   context.arm_lr =
1636       text_section_start_address + (number_of_pages + 1) * page_size;
1637 
1638   EXPECT_EQ(UnwindResult::kAborted,
1639             unwinder.TryUnwind(&context, stack_memory.stack_end_address(),
1640                                &unwound_frames));
1641   ExpectFramesEq(std::vector<Frame>({{pc, chrome_module}}), unwound_frames);
1642 }
1643 
1644 }  // namespace base
1645