Robert Sesek | 21f5a44 | 2018-01-05 19:29:07 | [diff] [blame] | 1 | # Debugging with Crash Keys |
| 2 | |
| 3 | Chrome is client-side software, which means that sometimes there are bugs that |
| 4 | can occur only on users' machines ("in production") that cannot be reproduced by |
| 5 | test or software engineering. When this happens, it's often helpful to gather bug- |
| 6 | specific data from production to help pinpoint the cause of the crash. The crash |
| 7 | key logging system is a generic method to help do that. |
| 8 | |
| 9 | [TOC] |
| 10 | |
| 11 | ## High-Level Overview |
| 12 | |
| 13 | The core of the crash key logging system is in [//components/crash/core/common/crash_key.h](https://cs.chromium.org/chromium/src/components/crash/core/common/crash_key.h), |
| 14 | which declares a `crash_reporter::CrashKeyString` class. Every crash key has |
| 15 | an associated value maximum length and a string name to identify it. The maximum |
| 16 | length is specified as a template parameter in order to allocate that amount of |
| 17 | space for the value up-front. When a process is crashing, memory corruption can |
| 18 | make it unsafe to call into the system allocator, so pre-allocating space for |
| 19 | the value defends against that. |
| 20 | |
| 21 | When a crash key is set, the specified value is copied to its internal storage. |
| 22 | And if the process subsequently crashes, the name-value tuple is uploaded as |
| 23 | POST form-multipart data when the crash report minidump is uploaded to the |
| 24 | Google crash reporting system. (The data therefore are only accessible to those |
| 25 | with access to crash reports internally at Google). For platforms that use |
| 26 | [Crashpad](https://crashpad.chromium.org) as the crash reporting platform, the |
| 27 | crash keys are also stored in the minidump file itself. For platforms that use |
| 28 | Breakpad, the keys are only available at upload. |
| 29 | |
| 30 | The crash key system is used to report some common pieces of data, not just |
| 31 | things that happen in exceptional cases: the URL of the webpage, command line |
| 32 | switches, active extension IDs, GPU vendor information, experiment/variations |
| 33 | information, etc. |
| 34 | |
Fergal Daly | 3f51b30 | 2020-03-04 16:05:31 | [diff] [blame] | 35 | ## Redaction |
| 36 | |
Nate Fischer | 80c1af7df | 2024-01-31 01:29:40 | [diff] [blame] | 37 | Beware that certain on certain platforms (e.g. Android WebView) we |
Fergal Daly | 3f51b30 | 2020-03-04 16:05:31 | [diff] [blame] | 38 | [sanitize the stack in the dump](https://cs.chromium.org/chromium/src/third_party/crashpad/crashpad/snapshot/sanitized/memory_snapshot_sanitized.h) |
Oksana Zhuravlova | 04b1bd41 | 2021-03-18 21:30:27 | [diff] [blame] | 39 | and only crash keys on an |
| 40 | [allowlist](https://cs.chromium.org/chromium/src/android_webview/common/crash_reporter/crash_keys.cc) |
Fergal Daly | 3f51b30 | 2020-03-04 16:05:31 | [diff] [blame] | 41 | will be captured. |
| 42 | |
Robert Sesek | 21f5a44 | 2018-01-05 19:29:07 | [diff] [blame] | 43 | ## Getting Started with a Single Key-Value Pair |
| 44 | |
| 45 | Imagine you are investigating a crash, and you want to know the value of some |
| 46 | variable when the crash occurs; the crash key logging system enables you to do |
| 47 | just that. |
| 48 | |
| 49 | #### 1. Declare the Crash Key |
| 50 | |
| 51 | A crash key must be allocated using static storage duration, so that there is |
| 52 | space for the value to be set. This can be done as a static variable in the |
| 53 | global or function scope, or in an anonymous namespace: |
| 54 | |
| 55 | static crash_reporter::CrashKeyString<32> crash_key_one("one"); |
| 56 | |
| 57 | namespace { |
| 58 | crash_reporter::CrashKeyString<64> crash_key_two("two"); |
| 59 | } |
| 60 | |
| 61 | void DoSomething(const std::string& arg) { |
| 62 | static crash_reporter::CrashKeyString<8> three("three"); |
| 63 | crash_key_two.Set(arg); |
| 64 | three.Set("true"); |
| 65 | } |
| 66 | |
| 67 | The template argument specifies the maximum length a value can be, and it |
| 68 | should include space for a trailing NUL byte. Values must be C-strings and |
| 69 | cannot have embedded NULs. The constructor argument is the name of the |
| 70 | crash key, and it is what you will use to identify your data in uploaded |
| 71 | crash reports. |
| 72 | |
Caleb Raitto | a5a95f9 | 2022-06-16 16:10:53 | [diff] [blame] | 73 | Note that crash key names are global and must not conflict with the |
| 74 | name of any other crash key in Chrome. |
| 75 | |
Robert Sesek | 21f5a44 | 2018-01-05 19:29:07 | [diff] [blame] | 76 | If you need to declare an array of crash keys (e.g., for recording N values |
| 77 | of an array), you can use a constructor tag to avoid warnings about `explicit`: |
| 78 | |
| 79 | static ArrayItemKey = crash_reporter::CrashKeyString<32>; |
| 80 | static ArrayItemKey crash_keys[] = { |
| 81 | {"array-item-1", ArrayItemKey::Tag::kArray}, |
| 82 | {"array-item-2", ArrayItemKey::Tag::kArray}, |
| 83 | {"array-item-3", ArrayItemKey::Tag::kArray}, |
| 84 | {"array-item-4", ArrayItemKey::Tag::kArray}, |
| 85 | }; |
| 86 | |
| 87 | The crash key system will require your target to have a dependency on |
| 88 | `//components/crash/core/common:crash_key`. If you encounter link errors for |
| 89 | unresolved symbols to `crashpad::Annotation::SetSize(unsigned int)`, adding |
| 90 | the dependency will resolve them. |
| 91 | |
| 92 | #### 2. Set the Crash Key |
| 93 | |
| 94 | After a key has been allocated, its `Set(base::StringPiece)` and |
| 95 | `Clear()` methods can be used to record and clear a value. In addition, |
| 96 | crash_key.h provides a `ScopedCrashKeyString` class to set the value for the |
| 97 | duration of a scope and clear it upon exiting. |
| 98 | |
| 99 | #### 3. Seeing the Data |
| 100 | |
| 101 | Using <http://go/crash> (internal only), find the crash report signature related |
| 102 | to your bug, and click on the "N of M" reports link to drill down to |
| 103 | report-specific information. From there, select a report and go to the |
| 104 | "Product Data" section to view all the crash key-value pairs. |
| 105 | |
| 106 | ## Dealing with DEPS |
| 107 | |
| 108 | Not all targets in the Chromium source tree are permitted to depend on the |
| 109 | `//components/crash/core/common:crash_key` target due to DEPS file |
| 110 | `include_rules`. |
| 111 | |
| 112 | If the crash key being added is only a temporary debugging aid to track down a |
| 113 | crash, consider adding the dependency temporarily and removing it when done. |
| 114 | A specific include rule can be added for crash_key.h: |
| 115 | |
| 116 | # DEPS |
| 117 | include_rules = [ |
| 118 | '+components/crash/core/common/crash_key.h', |
| 119 | ] |
| 120 | |
| 121 | Then simply remove it (and the BUILD.gn dependency) once the crash is resolved |
| 122 | and the crash key deleted. |
| 123 | |
| 124 | If this crash key is more permanent, then there is an alternate API in //base |
| 125 | that can be used. This API is used by the //content module to set its permanent |
| 126 | crash key information. Note however that the base-level API is more limited in |
| 127 | terms of features and flexibility. See the header documentation in |
| 128 | [//base/debug/crash_logging.h](https://cs.chromium.org/chromium/src/base/debug/crash_logging.h) |
| 129 | for usage examples. |
| 130 | |
| 131 | ## Advanced Topics: Stack Traces |
| 132 | |
| 133 | Now imagine a scenario where you have a use-after-free. The crash reports coming |
| 134 | in do not indicate where the object being used was initially freed, however, |
| 135 | just where it is later being dereferenced. To make debugging easier, it would be |
| 136 | nice to have the stack trace of the destructor, and the crash key system works |
| 137 | for that, too. |
| 138 | |
| 139 | #### 1. Declare the Crash Key |
| 140 | |
| 141 | Declaring the crash key is no different than written above, though special |
| 142 | attention should be paid to the maximum size argument, which will affect the |
| 143 | number of stack frames that are recorded. Typically a value of _1024_ is |
| 144 | recommended. |
| 145 | |
| 146 | #### 2. Set the Crash Key |
| 147 | |
| 148 | To set a stack trace to a crash key, use the `SetCrashKeyStringToStackTrace()` |
| 149 | function in crash_logging.h: |
| 150 | |
| 151 | Usemeafterfree::~Usemeafterfree() { |
Caleb Raitto | a5a95f9 | 2022-06-16 16:10:53 | [diff] [blame] | 152 | static crash_reporter::CrashKeyString<1024> trace_key( |
| 153 | "useme-after-free-uaf-dtor-trace"); |
Robert Sesek | 21f5a44 | 2018-01-05 19:29:07 | [diff] [blame] | 154 | crash_reporter::SetCrashKeyStringToStackTrace(&trace_key, |
| 155 | base::debug::StackTrace()); |
| 156 | } |
| 157 | |
| 158 | #### 3. Seeing the Data |
| 159 | |
| 160 | Unlike with the previous example, a stack trace will just be a string of |
| 161 | hexadecimal addresses. To turn the addresses back into symbols use, |
| 162 | <http://go/crsym> (internal instance of <https://github.com/chromium/crsym/>). |
| 163 | Using the **Crash Key** input type, give it a crash report ID and the name of |
| 164 | your crash key. Crsym will then fetch the symbol data from the internal crash |
| 165 | processing backends and return a formatted, symbolized stack trace. |