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