xref: /aosp_15_r20/external/cronet/base/debug/crash_logging.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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