• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 stack unwinding is possible using CFI unwind tables in apk.
39   // There is no need to check this before each unwind call. Will always return
40   // the same value based on CFI tables being present in the binary.
can_unwind_stack_frames()41   bool can_unwind_stack_frames() const { return can_unwind_stack_frames_; }
42 
43   // Returns the program counters by unwinding stack in the current thread in
44   // order of latest call frame first. Unwinding works only if
45   // can_unwind_stack_frames() returns true. This function allocates memory from
46   // heap for caches. For each stack frame, this method searches through the
47   // unwind table mapped in memory to find the unwind information for function
48   // and walks the stack to find all the return address. This only works until
49   // the last function call from the chrome.so. We do not have unwind
50   // information to unwind beyond any frame outside of chrome.so. Calls to
51   // Unwind() are thread safe and lock free, once Initialize() returns success.
52   size_t Unwind(const void** out_trace, size_t max_depth);
53 
54  private:
55   FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestCFICache);
56   FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestFindCFIRow);
57   FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestUnwinding);
58 
59   // The CFI information that correspond to an instruction.
60   struct CFIRow {
61     bool operator==(const CFIBacktraceAndroid::CFIRow& o) const {
62       return cfa_offset == o.cfa_offset && ra_offset == o.ra_offset;
63     }
64 
65     // The offset of the call frame address of previous function from the
66     // current stack pointer. Rule for unwinding SP: SP_prev = SP_cur +
67     // cfa_offset.
68     uint16_t cfa_offset = 0;
69     // The offset of location of return address from the previous call frame
70     // address. Rule for unwinding PC: PC_prev = * (SP_prev - ra_offset).
71     uint16_t ra_offset = 0;
72   };
73 
74   // A simple cache that stores entries in table using prime modulo hashing.
75   // This cache with 500 entries already gives us 95% hit rate, and fits in a
76   // single system page (usually 4KiB). Using a thread local cache for each
77   // thread gives us 30% improvements on performance of heap profiling.
78   class CFICache {
79    public:
80     // Add new item to the cache. It replaces an existing item with same hash.
81     // Constant time operation.
82     void Add(uintptr_t address, CFIRow cfi);
83 
84     // Finds the given address and fills |cfi| with the info for the address.
85     // returns true if found, otherwise false. Assumes |address| is never 0.
86     bool Find(uintptr_t address, CFIRow* cfi);
87 
88    private:
89     FRIEND_TEST_ALL_PREFIXES(CFIBacktraceAndroidTest, TestCFICache);
90 
91     // Size is the highest prime which fits the cache in a single system page,
92     // usually 4KiB. A prime is chosen to make sure addresses are hashed evenly.
93     static const int kLimit = 509;
94 
95     struct AddrAndCFI {
96       uintptr_t address;
97       CFIRow cfi;
98     };
99     AddrAndCFI cache_[kLimit] = {};
100   };
101 
102   static_assert(sizeof(CFIBacktraceAndroid::CFICache) < 4096,
103                 "The cache does not fit in a single page.");
104 
105   CFIBacktraceAndroid();
106   ~CFIBacktraceAndroid();
107 
108   // Initializes unwind tables using the CFI asset file in the apk if present.
109   // Also stores the limits of mapped region of the lib[mono]chrome.so binary,
110   // since the unwind is only feasible for addresses within the .so file. Once
111   // initialized, the memory map of the unwind table is never cleared since we
112   // cannot guarantee that all the threads are done using the memory map when
113   // heap profiling is turned off. But since we keep the memory map is clean,
114   // the system can choose to evict the unused pages when needed. This would
115   // still reduce the total amount of address space available in process.
116   void Initialize();
117 
118   // Finds the UNW_INDEX and UNW_DATA tables in from the CFI file memory map.
119   void ParseCFITables();
120 
121   // Finds the CFI row for the given |func_addr| in terms of offset from
122   // the start of the current binary.
123   bool FindCFIRowForPC(uintptr_t func_addr, CFIRow* out);
124 
125   CFICache* GetThreadLocalCFICache();
126 
127   // Details about the memory mapped region which contains the libchrome.so
128   // library file.
129   uintptr_t executable_start_addr_ = 0;
130   uintptr_t executable_end_addr_ = 0;
131 
132   // The start address of the memory mapped unwind table asset file. Unique ptr
133   // because it is replaced in tests.
134   std::unique_ptr<MemoryMappedFile> cfi_mmap_;
135 
136   // The UNW_INDEX table: Start address of the function address column. The
137   // memory segment corresponding to this column is treated as an array of
138   // uintptr_t.
139   const uintptr_t* unw_index_function_col_ = nullptr;
140   // The UNW_INDEX table: Start address of the index column. The memory segment
141   // corresponding to this column is treated as an array of uint16_t.
142   const uint16_t* unw_index_indices_col_ = nullptr;
143   // The number of rows in UNW_INDEX table.
144   size_t unw_index_row_count_ = 0;
145 
146   // The start address of UNW_DATA table.
147   const uint16_t* unw_data_start_addr_ = nullptr;
148 
149   bool can_unwind_stack_frames_ = false;
150 
151   ThreadLocalStorage::Slot thread_local_cfi_cache_;
152 };
153 
154 }  // namespace trace_event
155 }  // namespace base
156 
157 #endif  // BASE_TRACE_EVENT_CFI_BACKTRACE_ANDROID_H_
158