cfi_backtrace_android.h 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. // Copyright 2018 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. #ifndef BASE_TRACE_EVENT_CFI_BACKTRACE_ANDROID_H_
  5. #define BASE_TRACE_EVENT_CFI_BACKTRACE_ANDROID_H_
  6. #include <stddef.h>
  7. #include <stdint.h>
  8. #include <memory>
  9. #include "base/base_export.h"
  10. #include "base/debug/debugging_buildflags.h"
  11. #include "base/files/memory_mapped_file.h"
  12. #include "base/gtest_prod_util.h"
  13. #include "base/memory/raw_ptr.h"
  14. #include "base/threading/thread_local_storage.h"
  15. namespace base {
  16. namespace trace_event {
  17. // This class is used to unwind stack frames in the current thread. The unwind
  18. // information (dwarf debug info) is stripped from the chrome binary and we do
  19. // not build with exception tables (ARM EHABI) in release builds. So, we use a
  20. // custom unwind table which is generated and added to specific android builds,
  21. // when add_unwind_tables_in_apk build option is specified. This unwind table
  22. // contains information for unwinding stack frames when the functions calls are
  23. // from lib[mono]chrome.so. The file is added as an asset to the apk and the
  24. // table is used to unwind stack frames for profiling. This class implements
  25. // methods to read and parse the unwind table and unwind stack frames using this
  26. // data.
  27. class BASE_EXPORT CFIBacktraceAndroid {
  28. public:
  29. // Creates and initializes by memory mapping the unwind tables from apk assets
  30. // on first call.
  31. static CFIBacktraceAndroid* GetInitializedInstance();
  32. // Returns true if the given program counter |pc| is mapped in chrome library.
  33. static bool is_chrome_address(uintptr_t pc);
  34. // Returns the start and end address of the current library.
  35. static uintptr_t executable_start_addr();
  36. static uintptr_t executable_end_addr();
  37. // Returns true if stack unwinding is possible using CFI unwind tables in apk.
  38. // There is no need to check this before each unwind call. Will always return
  39. // the same value based on CFI tables being present in the binary.
  40. bool can_unwind_stack_frames() const { return can_unwind_stack_frames_; }
  41. // Returns the program counters by unwinding stack in the current thread in
  42. // order of latest call frame first. Unwinding works only if
  43. // can_unwind_stack_frames() returns true. This function allocates memory from
  44. // heap for cache on the first call of the calling thread, unless
  45. // AllocateCacheForCurrentThread() is called from the thread. For each stack
  46. // frame, this method searches through the unwind table mapped in memory to
  47. // find the unwind information for function and walks the stack to find all
  48. // the return address. This only works until the last function call from the
  49. // chrome.so. We do not have unwind information to unwind beyond any frame
  50. // outside of chrome.so. Calls to Unwind() are thread safe and lock free, once
  51. // Initialize() returns success.
  52. size_t Unwind(const void** out_trace, size_t max_depth);
  53. // Same as above function, but starts from a given program counter |pc|,
  54. // stack pointer |sp| and link register |lr|. This can be from current thread
  55. // or any other thread. But the caller must make sure that the thread's stack
  56. // segment is not racy to read.
  57. size_t Unwind(uintptr_t pc,
  58. uintptr_t sp,
  59. uintptr_t lr,
  60. const void** out_trace,
  61. size_t max_depth);
  62. // Allocates memory for CFI cache for the current thread so that Unwind()
  63. // calls are safe for signal handlers.
  64. void AllocateCacheForCurrentThread();
  65. // The CFI information that correspond to an instruction.
  66. struct CFIRow {
  67. bool operator==(const CFIBacktraceAndroid::CFIRow& o) const {
  68. return cfa_offset == o.cfa_offset && ra_offset == o.ra_offset;
  69. }
  70. // The offset of the call frame address of previous function from the
  71. // current stack pointer. Rule for unwinding SP: SP_prev = SP_cur +
  72. // cfa_offset.
  73. uint16_t cfa_offset = 0;
  74. // The offset of location of return address from the previous call frame
  75. // address. Rule for unwinding PC: PC_prev = * (SP_prev - ra_offset).
  76. uint16_t ra_offset = 0;
  77. };
  78. // Finds the CFI row for the given |func_addr| in terms of offset from
  79. // the start of the current binary. Concurrent calls are thread safe.
  80. bool FindCFIRowForPC(uintptr_t func_addr, CFIRow* out);
  81. private:
  82. FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestCFICache);
  83. FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestFindCFIRow);
  84. FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestUnwinding);
  85. // A simple cache that stores entries in table using prime modulo hashing.
  86. // This cache with 500 entries already gives us 95% hit rate, and fits in a
  87. // single system page (usually 4KiB). Using a thread local cache for each
  88. // thread gives us 30% improvements on performance of heap profiling.
  89. class CFICache {
  90. public:
  91. // Add new item to the cache. It replaces an existing item with same hash.
  92. // Constant time operation.
  93. void Add(uintptr_t address, CFIRow cfi);
  94. // Finds the given address and fills |cfi| with the info for the address.
  95. // returns true if found, otherwise false. Assumes |address| is never 0.
  96. bool Find(uintptr_t address, CFIRow* cfi);
  97. private:
  98. FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestCFICache);
  99. // Size is the highest prime which fits the cache in a single system page,
  100. // usually 4KiB. A prime is chosen to make sure addresses are hashed evenly.
  101. static const int kLimit = 509;
  102. struct AddrAndCFI {
  103. uintptr_t address;
  104. CFIRow cfi;
  105. };
  106. AddrAndCFI cache_[kLimit] = {};
  107. };
  108. static_assert(sizeof(CFIBacktraceAndroid::CFICache) < 4096,
  109. "The cache does not fit in a single page.");
  110. CFIBacktraceAndroid();
  111. ~CFIBacktraceAndroid();
  112. // Initializes unwind tables using the CFI asset file in the apk if present.
  113. // Also stores the limits of mapped region of the lib[mono]chrome.so binary,
  114. // since the unwind is only feasible for addresses within the .so file. Once
  115. // initialized, the memory map of the unwind table is never cleared since we
  116. // cannot guarantee that all the threads are done using the memory map when
  117. // heap profiling is turned off. But since we keep the memory map is clean,
  118. // the system can choose to evict the unused pages when needed. This would
  119. // still reduce the total amount of address space available in process.
  120. void Initialize();
  121. // Finds the UNW_INDEX and UNW_DATA tables in from the CFI file memory map.
  122. void ParseCFITables();
  123. CFICache* GetThreadLocalCFICache();
  124. // The start address of the memory mapped unwind table asset file. Unique ptr
  125. // because it is replaced in tests.
  126. std::unique_ptr<MemoryMappedFile> cfi_mmap_;
  127. // The UNW_INDEX table: Start address of the function address column. The
  128. // memory segment corresponding to this column is treated as an array of
  129. // uintptr_t.
  130. raw_ptr<const uintptr_t> unw_index_function_col_ = nullptr;
  131. // The UNW_INDEX table: Start address of the index column. The memory segment
  132. // corresponding to this column is treated as an array of uint16_t.
  133. raw_ptr<const uint16_t> unw_index_indices_col_ = nullptr;
  134. // The number of rows in UNW_INDEX table.
  135. size_t unw_index_row_count_ = 0;
  136. // The start address of UNW_DATA table.
  137. raw_ptr<const uint16_t> unw_data_start_addr_ = nullptr;
  138. bool can_unwind_stack_frames_ = false;
  139. ThreadLocalStorage::Slot thread_local_cfi_cache_;
  140. };
  141. } // namespace trace_event
  142. } // namespace base
  143. #endif // BASE_TRACE_EVENT_CFI_BACKTRACE_ANDROID_H_