1 // Copyright 2012 The Chromium Authors 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_DEBUG_CRASH_LOGGING_H_ 6 #define BASE_DEBUG_CRASH_LOGGING_H_ 7 8 #include <stddef.h> 9 10 #include <iosfwd> 11 #include <memory> 12 #include <string_view> 13 #include <type_traits> 14 15 #include "base/base_export.h" 16 #include "base/memory/raw_ptr.h" 17 #include "base/strings/string_number_conversions.h" 18 19 namespace base { 20 namespace debug { 21 22 // A crash key is an annotation that is carried along with a crash report, to 23 // provide additional debugging information beyond a stack trace. Crash keys 24 // have a name and a string value. 25 // 26 // The preferred API is //components/crash/core/common:crash_key, however not 27 // all clients can hold a direct dependency on that target. The API provided 28 // in this file indirects the dependency and adds some convenience helpers that 29 // make the API a bit less clunky. 30 // 31 // TODO(dcheng): Some of the nicer APIs should probably be upstreamed into 32 // //components/crash. 33 // 34 // Preferred usage when a crash key value only needs to be set within a scope: 35 // 36 // SCOPED_CRASH_KEY_STRING32("category", "name", "value"); 37 // base::debug::DumpWithoutCrashing(); 38 // 39 // If the crash key is pre-allocated elsewhere, but the value only needs to be 40 // set within a scope: 41 // 42 // base::debug::ScopedCrashKeyString scoper( 43 // GetCrashKeyForComponent(), 44 // "value"); 45 // 46 // Otherwise, if the crash key needs to persist (e.g. the actual crash dump is 47 // triggered some time later asynchronously): 48 // 49 // static auto* const crash_key = base::debug::AllocateCrashKeyString( 50 // "name", base::debug::CrashKeySize::Size32); 51 // base::debug::SetCrashKeyString(crash_key, "value"); 52 // 53 // // Do other work before calling `base::debug::DumpWithoutCrashing()` later. 54 // 55 // ***WARNING*** 56 // 57 // Do *not* write this: 58 // 59 // base::debug::SetCrashKeyString( 60 // base::debug::AllocateCrashKeyString( 61 // "name", base::debug::CrashKeySize::Size32), 62 // "value"); 63 // 64 // As this will leak a heap allocation every time the crash key is set! 65 66 // The maximum length for a crash key's value must be one of the following 67 // pre-determined values. 68 enum class CrashKeySize { 69 Size32 = 32, 70 Size64 = 64, 71 Size256 = 256, 72 Size1024 = 1024, 73 }; 74 75 struct CrashKeyString; 76 77 // Allocates a new crash key with the specified |name| with storage for a 78 // value up to length |size|. This will return null if the crash key system is 79 // not initialized. 80 // 81 // Note: this internally allocates, so the returned pointer should always 82 // be cached in a variable with static storage duration, e.g.: 83 // static auto* const crash_key = base::debug::AllocateCrashKeyString(...); 84 BASE_EXPORT CrashKeyString* AllocateCrashKeyString(const char name[], 85 CrashKeySize size); 86 87 // Stores |value| into the specified |crash_key|. The |crash_key| may be null 88 // if AllocateCrashKeyString() returned null. If |value| is longer than the 89 // size with which the key was allocated, it will be truncated. 90 BASE_EXPORT void SetCrashKeyString(CrashKeyString* crash_key, 91 std::string_view value); 92 93 // Clears any value that was stored in |crash_key|. The |crash_key| may be 94 // null. 95 BASE_EXPORT void ClearCrashKeyString(CrashKeyString* crash_key); 96 97 // Outputs current (i.e. allocated and non-empty) crash keys to `out`. 98 BASE_EXPORT void OutputCrashKeysToStream(std::ostream& out); 99 100 // A scoper that sets the specified key to value for the lifetime of the 101 // object, and clears it on destruction. 102 class BASE_EXPORT [[nodiscard]] ScopedCrashKeyString { 103 public: 104 ScopedCrashKeyString(CrashKeyString* crash_key, std::string_view value); 105 ScopedCrashKeyString(ScopedCrashKeyString&& other); 106 ~ScopedCrashKeyString(); 107 108 // Disallow copy and assign. 109 ScopedCrashKeyString(const ScopedCrashKeyString&) = delete; 110 ScopedCrashKeyString& operator=(const ScopedCrashKeyString&) = delete; 111 112 // Disallow move assign to keep the time at which the crash key is cleared 113 // easy to reason about. Assigning over an existing instance would 114 // automatically clear the key instead of at the destruction of the object. 115 ScopedCrashKeyString& operator=(ScopedCrashKeyString&&) = delete; 116 117 private: 118 raw_ptr<CrashKeyString> crash_key_; 119 }; 120 121 // Internal helpers for the SCOPED_CRASH_KEY_... helper macros defined below. 122 // 123 // The first static_assert that checks the length of |key_name| is a 124 // compile-time equivalent of the DCHECK in 125 // crash_reporter::internal::CrashKeyStringImpl::Set that restricts the name of 126 // a crash key to 40 characters. 127 // 128 // The second static_assert that checks for reserved characters is a compile 129 // time equivalent of the DCHECK in base::debug::AllocateCrashKeyString. 130 #define SCOPED_CRASH_KEY_STRING_INTERNAL2(category, name, nonce, data, \ 131 key_size) \ 132 static_assert(::std::size(category "-" name) < 40, \ 133 "Crash key names must be shorter than 40 characters."); \ 134 static_assert(::std::string_view(category "-" name).find(':') == \ 135 ::base::StringPiece::npos, \ 136 "Crash key names must not contain the ':' character."); \ 137 ::base::debug::ScopedCrashKeyString scoped_crash_key_helper##nonce( \ 138 [] { \ 139 static auto* const key = ::base::debug::AllocateCrashKeyString( \ 140 category "-" name, key_size); \ 141 return key; \ 142 }(), \ 143 (data)) 144 145 // This indirection is needed to expand __COUNTER__. 146 #define SCOPED_CRASH_KEY_STRING_INTERNAL(category, name, nonce, data, \ 147 key_size) \ 148 SCOPED_CRASH_KEY_STRING_INTERNAL2(category, name, nonce, data, key_size) 149 150 // Helper macros for putting a local variable crash key on the stack before 151 // causing a crash or calling CrashWithoutDumping(). `category` and `name` 152 // should be string literals. 153 // 154 // SCOPED_CRASH_KEY_STRING32("MyCategory", "key_name", "value"); 155 // 156 // will set the crash key annotation named "MyCategory-key_name" to "value" 157 // while in scope. 158 #define SCOPED_CRASH_KEY_STRING32(category, name, data) \ 159 SCOPED_CRASH_KEY_STRING_INTERNAL(category, name, __COUNTER__, (data), \ 160 ::base::debug::CrashKeySize::Size32) 161 162 #define SCOPED_CRASH_KEY_STRING64(category, name, data) \ 163 SCOPED_CRASH_KEY_STRING_INTERNAL(category, name, __COUNTER__, (data), \ 164 ::base::debug::CrashKeySize::Size64) 165 166 #define SCOPED_CRASH_KEY_STRING256(category, name, data) \ 167 SCOPED_CRASH_KEY_STRING_INTERNAL(category, name, __COUNTER__, (data), \ 168 ::base::debug::CrashKeySize::Size256) 169 170 #define SCOPED_CRASH_KEY_STRING1024(category, name, data) \ 171 SCOPED_CRASH_KEY_STRING_INTERNAL(category, name, __COUNTER__, (data), \ 172 ::base::debug::CrashKeySize::Size1024) 173 174 #define SCOPED_CRASH_KEY_BOOL(category, name, data) \ 175 static_assert(std::is_same_v<std::decay_t<decltype(data)>, bool>, \ 176 "SCOPED_CRASH_KEY_BOOL must be passed a boolean value."); \ 177 SCOPED_CRASH_KEY_STRING32(category, name, (data) ? "true" : "false") 178 179 #define SCOPED_CRASH_KEY_NUMBER(category, name, data) \ 180 SCOPED_CRASH_KEY_STRING32(category, name, ::base::NumberToString(data)) 181 182 //////////////////////////////////////////////////////////////////////////////// 183 // The following declarations are used to initialize the crash key system 184 // in //base by providing implementations for the above functions. 185 186 // The virtual interface that provides the implementation for the crash key 187 // API. This is implemented by a higher-layer component, and the instance is 188 // set using the function below. 189 class CrashKeyImplementation { 190 public: 191 virtual ~CrashKeyImplementation() = default; 192 193 virtual CrashKeyString* Allocate(const char name[], CrashKeySize size) = 0; 194 virtual void Set(CrashKeyString* crash_key, std::string_view value) = 0; 195 virtual void Clear(CrashKeyString* crash_key) = 0; 196 virtual void OutputCrashKeysToStream(std::ostream& out) = 0; 197 }; 198 199 // Initializes the crash key system in base by replacing the existing 200 // implementation, if it exists, with |impl|. The |impl| is copied into base. 201 BASE_EXPORT void SetCrashKeyImplementation( 202 std::unique_ptr<CrashKeyImplementation> impl); 203 204 // The base structure for a crash key, storing the allocation metadata. 205 struct CrashKeyString { CrashKeyStringCrashKeyString206 constexpr CrashKeyString(const char name[], CrashKeySize size) 207 : name(name), size(size) {} 208 const char* const name; 209 const CrashKeySize size; 210 }; 211 212 } // namespace debug 213 } // namespace base 214 215 #endif // BASE_DEBUG_CRASH_LOGGING_H_ 216