chrome_unwinder_android_unittest.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/profiler/chrome_unwinder_android.h"
  5. #include "base/android/library_loader/anchor_functions.h"
  6. #include "base/files/file_util.h"
  7. #include "base/profiler/profile_builder.h"
  8. #include "base/profiler/stack_buffer.h"
  9. #include "base/profiler/stack_copier_signal.h"
  10. #include "base/profiler/stack_sampling_profiler_test_util.h"
  11. #include "base/profiler/thread_delegate_posix.h"
  12. #include "base/test/gtest_util.h"
  13. #include "build/build_config.h"
  14. #include "testing/gtest/include/gtest/gtest.h"
  15. namespace base {
  16. namespace {
  17. // Input is generated from the CFI file:
  18. // STACK CFI INIT 100 100
  19. // STACK CFI 1010 .cfa: sp 8 + .ra: .cfa -4 + ^
  20. const uint16_t cfi_data[] = {
  21. // UNW_INDEX size
  22. 0x02,
  23. 0x0,
  24. // UNW_INDEX function_addresses (4 byte rows).
  25. 0x100,
  26. 0x0,
  27. 0x200,
  28. 0x0,
  29. // UNW_INDEX entry_data_indices (2 byte rows).
  30. 0x0,
  31. 0xffff,
  32. // UNW_DATA table.
  33. 0x1,
  34. 0x10,
  35. 0x9,
  36. };
  37. // Utility function to add a single native module during test setup. Returns
  38. // a pointer to the provided module.
  39. const ModuleCache::Module* AddNativeModule(
  40. ModuleCache* cache,
  41. std::unique_ptr<const ModuleCache::Module> module) {
  42. const ModuleCache::Module* module_ptr = module.get();
  43. cache->AddCustomNativeModule(std::move(module));
  44. return module_ptr;
  45. }
  46. ArmCFITable::FrameEntry MakeFrameEntry(uint16_t cfa_offset,
  47. uint16_t ra_offset) {
  48. return ArmCFITable::FrameEntry{
  49. static_cast<uint16_t>(cfa_offset * sizeof(uintptr_t)),
  50. static_cast<uint16_t>(ra_offset * sizeof(uintptr_t))};
  51. }
  52. } // namespace
  53. // Tests unwind step under normal operation.
  54. TEST(ChromeUnwinderAndroidTest, Step) {
  55. const std::vector<uintptr_t> stack_buffer = {
  56. 0xFFFF, 0xFFFF, 0xFFFF, 0x1111, 0xFFFF, 0x2222,
  57. };
  58. const uintptr_t stack_top =
  59. reinterpret_cast<uintptr_t>(stack_buffer.data() + stack_buffer.size());
  60. RegisterContext context;
  61. RegisterContextInstructionPointer(&context) = 0xBEEF;
  62. RegisterContextStackPointer(&context) =
  63. reinterpret_cast<uintptr_t>(stack_buffer.data());
  64. EXPECT_TRUE(ChromeUnwinderAndroid::StepForTesting(&context, stack_top,
  65. MakeFrameEntry(4, 1)));
  66. EXPECT_EQ(RegisterContextInstructionPointer(&context), 0x1111U);
  67. EXPECT_EQ(RegisterContextStackPointer(&context),
  68. reinterpret_cast<uintptr_t>(stack_buffer.data() + 4));
  69. EXPECT_TRUE(ChromeUnwinderAndroid::StepForTesting(&context, stack_top,
  70. MakeFrameEntry(1, 0)));
  71. EXPECT_EQ(RegisterContextInstructionPointer(&context), 0x2222U);
  72. EXPECT_EQ(RegisterContextStackPointer(&context),
  73. reinterpret_cast<uintptr_t>(stack_buffer.data() + 5));
  74. }
  75. // Tests unwind step using immediate return address.
  76. TEST(ChromeUnwinderAndroidTest, StepImmediate) {
  77. const std::vector<uintptr_t> stack_buffer = {0xFFFF, 0xFFFF};
  78. const uintptr_t stack_top =
  79. reinterpret_cast<uintptr_t>(stack_buffer.data() + stack_buffer.size());
  80. RegisterContext context;
  81. RegisterContextInstructionPointer(&context) = 0xBEEF;
  82. RegisterContextStackPointer(&context) =
  83. reinterpret_cast<uintptr_t>(stack_buffer.data());
  84. context.arm_lr = 0x4444;
  85. EXPECT_TRUE(ChromeUnwinderAndroid::StepForTesting(&context, stack_top,
  86. MakeFrameEntry(0, 0)));
  87. EXPECT_EQ(RegisterContextInstructionPointer(&context), 0x4444U);
  88. EXPECT_EQ(RegisterContextStackPointer(&context),
  89. reinterpret_cast<uintptr_t>(stack_buffer.data()));
  90. }
  91. // Tests that unwinding fails if immediate return address is the current
  92. // instruction.
  93. TEST(ChromeUnwinderAndroidTest, StepImmediateFail) {
  94. const std::vector<uintptr_t> stack_buffer = {0xFFFF, 0xFFFF};
  95. const uintptr_t stack_top =
  96. reinterpret_cast<uintptr_t>(stack_buffer.data() + stack_buffer.size());
  97. RegisterContext context;
  98. RegisterContextInstructionPointer(&context) = 0x1111;
  99. RegisterContextStackPointer(&context) =
  100. reinterpret_cast<uintptr_t>(stack_buffer.data());
  101. context.arm_lr = 0x1111;
  102. EXPECT_FALSE(ChromeUnwinderAndroid::StepForTesting(&context, stack_top,
  103. MakeFrameEntry(0, 0)));
  104. }
  105. // Tests that unwinding fails if stack is invalid.
  106. TEST(ChromeUnwinderAndroidTest, StepInvalidStack) {
  107. const std::vector<uintptr_t> stack_buffer = {0xFFFF};
  108. const uintptr_t stack_top =
  109. reinterpret_cast<uintptr_t>(stack_buffer.data() + stack_buffer.size());
  110. RegisterContext context;
  111. EXPECT_CHECK_DEATH({
  112. RegisterContextStackPointer(&context) = 0;
  113. ChromeUnwinderAndroid::StepForTesting(&context, stack_top,
  114. MakeFrameEntry(1, 0));
  115. });
  116. EXPECT_CHECK_DEATH({
  117. RegisterContextStackPointer(&context) = reinterpret_cast<uintptr_t>(
  118. stack_buffer.data() + stack_buffer.size() + 1);
  119. ChromeUnwinderAndroid::StepForTesting(&context, stack_top,
  120. MakeFrameEntry(1, 0));
  121. });
  122. }
  123. // Tests that unwinding fails if the frame entry is out of bounds.
  124. TEST(ChromeUnwinderAndroidTest, StepOutOfBounds) {
  125. RegisterContext context;
  126. RegisterContextInstructionPointer(&context) = 0xBEEF;
  127. constexpr uintptr_t kOverflowOffset = 8;
  128. constexpr size_t kStackSize = 4;
  129. // It's fine to use a fake stack pointer since the stack won't be
  130. // dereferenced. Purposely underflow so that sp + |kOverflowOffset| overflows.
  131. RegisterContextStackPointer(&context) = 0 - kOverflowOffset;
  132. const uintptr_t stack_top =
  133. RegisterContextStackPointer(&context) + kStackSize;
  134. // ra_offset exceeds cfa_offset.
  135. EXPECT_FALSE(ChromeUnwinderAndroid::StepForTesting(&context, stack_top,
  136. MakeFrameEntry(1, 2)));
  137. EXPECT_FALSE(ChromeUnwinderAndroid::StepForTesting(
  138. &context, stack_top, MakeFrameEntry(1, kOverflowOffset)));
  139. // cfa_offset exceeds |stack_top|.
  140. EXPECT_FALSE(ChromeUnwinderAndroid::StepForTesting(
  141. &context, stack_top, MakeFrameEntry(kStackSize, 0)));
  142. // sp + cfa_offset overflows.
  143. EXPECT_FALSE(ChromeUnwinderAndroid::StepForTesting(
  144. &context, stack_top, MakeFrameEntry(kOverflowOffset, 0)));
  145. }
  146. TEST(ChromeUnwinderAndroidTest, StepUnderflows) {
  147. RegisterContext context;
  148. RegisterContextInstructionPointer(&context) = 0xBEEF;
  149. // It's fine to use a fake stack pointer since the stack won't be
  150. // dereferenced.
  151. RegisterContextStackPointer(&context) = 2;
  152. const uintptr_t stack_top = RegisterContextStackPointer(&context) + 4;
  153. // sp + cfa_offset - ra_offset underflows.
  154. EXPECT_FALSE(ChromeUnwinderAndroid::StepForTesting(&context, stack_top,
  155. MakeFrameEntry(1, 4)));
  156. }
  157. TEST(ChromeUnwinderAndroidTest, CanUnwindFrom) {
  158. auto cfi_table = ArmCFITable::Parse(
  159. {reinterpret_cast<const uint8_t*>(cfi_data), sizeof(cfi_data)});
  160. auto chrome_module = std::make_unique<TestModule>(0x1000, 0x500);
  161. auto non_chrome_module = std::make_unique<TestModule>(0x2000, 0x500);
  162. ModuleCache module_cache;
  163. ChromeUnwinderAndroid unwinder(cfi_table.get(),
  164. chrome_module->GetBaseAddress());
  165. unwinder.Initialize(&module_cache);
  166. EXPECT_TRUE(unwinder.CanUnwindFrom({0x1100, chrome_module.get()}));
  167. EXPECT_FALSE(unwinder.CanUnwindFrom({0x2100, non_chrome_module.get()}));
  168. }
  169. TEST(ChromeUnwinderAndroidTest, TryUnwind) {
  170. auto cfi_table = ArmCFITable::Parse(
  171. {reinterpret_cast<const uint8_t*>(cfi_data), sizeof(cfi_data)});
  172. ModuleCache module_cache;
  173. const ModuleCache::Module* chrome_module = AddNativeModule(
  174. &module_cache, std::make_unique<TestModule>(0x1000, 0x500));
  175. ChromeUnwinderAndroid unwinder(cfi_table.get(),
  176. chrome_module->GetBaseAddress());
  177. unwinder.Initialize(&module_cache);
  178. std::vector<uintptr_t> stack_buffer = {
  179. 0xFFFF,
  180. // .cfa: sp 8 + .ra: .cfa -4 + ^
  181. 0x2000,
  182. 0xFFFF,
  183. };
  184. uintptr_t stack_top =
  185. reinterpret_cast<uintptr_t>(stack_buffer.data() + stack_buffer.size());
  186. std::vector<Frame> stack;
  187. stack.emplace_back(0x1100, chrome_module);
  188. RegisterContext context;
  189. RegisterContextInstructionPointer(&context) = 0x1100;
  190. RegisterContextStackPointer(&context) =
  191. reinterpret_cast<uintptr_t>(stack_buffer.data());
  192. context.arm_lr = 0x11AA;
  193. EXPECT_EQ(UnwindResult::kUnrecognizedFrame,
  194. unwinder.TryUnwind(&context, stack_top, &stack));
  195. EXPECT_EQ(std::vector<Frame>({{0x1100, chrome_module},
  196. {0x11AA, chrome_module},
  197. {0x2000, nullptr}}),
  198. stack);
  199. }
  200. TEST(ChromeUnwinderAndroidTest, TryUnwindAbort) {
  201. auto cfi_table = ArmCFITable::Parse(
  202. {reinterpret_cast<const uint8_t*>(cfi_data), sizeof(cfi_data)});
  203. ASSERT_TRUE(cfi_table);
  204. ModuleCache module_cache;
  205. const ModuleCache::Module* chrome_module = AddNativeModule(
  206. &module_cache, std::make_unique<TestModule>(0x1000, 0x500));
  207. ChromeUnwinderAndroid unwinder(cfi_table.get(),
  208. chrome_module->GetBaseAddress());
  209. unwinder.Initialize(&module_cache);
  210. std::vector<uintptr_t> stack_buffer = {
  211. 0xFFFF,
  212. };
  213. uintptr_t stack_top =
  214. reinterpret_cast<uintptr_t>(stack_buffer.data() + stack_buffer.size());
  215. std::vector<Frame> stack;
  216. stack.emplace_back(0x1100, chrome_module);
  217. RegisterContext context;
  218. RegisterContextInstructionPointer(&context) = 0x1100;
  219. RegisterContextStackPointer(&context) =
  220. reinterpret_cast<uintptr_t>(stack_buffer.data());
  221. context.arm_lr = 0x1100;
  222. // Aborted because ra == pc.
  223. EXPECT_EQ(UnwindResult::kAborted,
  224. unwinder.TryUnwind(&context, stack_top, &stack));
  225. EXPECT_EQ(std::vector<Frame>({{0x1100, chrome_module}}), stack);
  226. }
  227. TEST(ChromeUnwinderAndroidTest, TryUnwindNoData) {
  228. auto cfi_table = ArmCFITable::Parse(
  229. {reinterpret_cast<const uint8_t*>(cfi_data), sizeof(cfi_data)});
  230. ModuleCache module_cache;
  231. const ModuleCache::Module* chrome_module = AddNativeModule(
  232. &module_cache, std::make_unique<TestModule>(0x1000, 0x500));
  233. ChromeUnwinderAndroid unwinder(cfi_table.get(),
  234. chrome_module->GetBaseAddress());
  235. unwinder.Initialize(&module_cache);
  236. std::vector<uintptr_t> stack_buffer = {0xFFFF};
  237. uintptr_t stack_top =
  238. reinterpret_cast<uintptr_t>(stack_buffer.data() + stack_buffer.size());
  239. std::vector<Frame> stack;
  240. stack.emplace_back(0x1200, chrome_module);
  241. RegisterContext context;
  242. RegisterContextInstructionPointer(&context) = 0xBEEF;
  243. RegisterContextStackPointer(&context) =
  244. reinterpret_cast<uintptr_t>(stack_buffer.data());
  245. context.arm_lr = 0x12AA;
  246. // Unwinding will first use arm_lr as fallback because there's no unwind info
  247. // for the instruction pointer, and then abort.
  248. EXPECT_EQ(UnwindResult::kAborted,
  249. unwinder.TryUnwind(&context, stack_top, &stack));
  250. EXPECT_EQ(
  251. std::vector<Frame>({{0x1200, chrome_module}, {0x12AA, chrome_module}}),
  252. stack);
  253. }
  254. } // namespace base