1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
|
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_CRASH_CORE_COMMON_CRASH_KEY_H_
#define COMPONENTS_CRASH_CORE_COMMON_CRASH_KEY_H_
#include <stdint.h>
#include <string>
#include <string_view>
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "build/build_config.h"
#include "components/crash/core/common/crash_buildflags.h"
#include "components/crash/core/common/crash_export.h"
// The crash key interface exposed by this file is the same as the Crashpad
// Annotation interface. Because not all platforms use Crashpad yet, a
// source-compatible interface is provided on top of the older Breakpad
// storage mechanism.
//
// See https://cs.chromium.org/chromium/src/docs/debugging_with_crash_keys.md
// for more information on using this.
#if BUILDFLAG(USE_CRASHPAD_ANNOTATION)
#include "third_party/crashpad/crashpad/client/annotation.h" // nogncheck
#endif
namespace base {
namespace debug {
class StackTrace;
} // namespace debug
} // namespace base
namespace crash_reporter {
// A CrashKeyString stores a name-value pair that will be recorded within a
// crash report.
//
// The crash key name must be a constant string expression, and the value
// should be unique and identifying. The maximum size for the value is
// specified as the template argument, and values greater than this are
// truncated. When specifying a value size, space should be left for the
// `NUL` byte. Crash keys should be declared with static storage duration.
//
// Examples:
// \code
// // This crash key is only set in one function:
// void DidNavigate(const GURL& gurl) {
// static crash_reporter::CrashKeyString<256> url_key("url");
// url_key.Set(gurl.ToString());
// }
//
// // This crash key can be set/cleared across different functions:
// namespace {
// crash_reporter::CrashKeyString<32> g_operation_id("operation-req-id");
// }
//
// void OnStartingOperation(const std::string& request_id) {
// g_operation_id.Set(request_id);
// }
//
// void OnEndingOperation() {
// g_operation_id.Clear()
// }
// \endcode
#if BUILDFLAG(USE_CRASHPAD_ANNOTATION)
template <crashpad::Annotation::ValueSizeType MaxLength>
using CrashKeyString = crashpad::StringAnnotation<MaxLength>;
#else // Crashpad-compatible crash key interface:
class CrashKeyBreakpadTest;
namespace internal {
constexpr size_t kCrashKeyStorageKeySize = 40;
constexpr size_t kCrashKeyStorageNumEntries = 200;
constexpr size_t kCrashKeyStorageValueSize = 128;
// Base implementation of a CrashKeyString for non-Crashpad clients. A separate
// base class is used to avoid inlining complex logic into the public template
// API.
class CRASH_KEY_EXPORT CrashKeyStringImpl {
public:
constexpr explicit CrashKeyStringImpl(const char name[],
size_t* index_array,
size_t index_array_count)
: name_(name),
index_array_(index_array),
index_array_count_(index_array_count) {}
CrashKeyStringImpl(const CrashKeyStringImpl&) = delete;
CrashKeyStringImpl& operator=(const CrashKeyStringImpl&) = delete;
void Set(std::string_view value);
void Clear();
bool is_set() const;
private:
friend class crash_reporter::CrashKeyBreakpadTest;
// The name of the crash key.
const char* const name_;
// If the crash key is set, this is the index into the storage that can be
// used to set/clear the key without requiring a linear scan of the storage
// table. This will be |num_entries| if unset.
// RAW_PTR_EXCLUSION: #global-scope
RAW_PTR_EXCLUSION size_t* index_array_;
size_t index_array_count_;
};
// This type creates a C array that is initialized with a specific default
// value, rather than the standard zero-initialized default.
template <typename T,
size_t TotalSize,
T DefaultValue,
size_t Count,
T... Values>
struct InitializedArrayImpl {
using Type = typename InitializedArrayImpl<T,
TotalSize,
DefaultValue,
Count - 1,
DefaultValue,
Values...>::Type;
};
template <typename T, size_t TotalSize, T DefaultValue, T... Values>
struct InitializedArrayImpl<T, TotalSize, DefaultValue, 0, Values...> {
using Type = InitializedArrayImpl<T, TotalSize, DefaultValue, 0, Values...>;
T data[TotalSize]{Values...};
};
template <typename T, size_t ArraySize, T DefaultValue>
using InitializedArray =
typename InitializedArrayImpl<T, ArraySize, DefaultValue, ArraySize>::Type;
} // namespace internal
template <uint32_t MaxLength>
class CrashKeyStringBreakpad : public internal::CrashKeyStringImpl {
public:
constexpr static size_t chunk_count =
(MaxLength / internal::kCrashKeyStorageValueSize) + 1;
// A constructor tag that can be used to initialize a C array of crash keys.
enum class Tag { kArray };
constexpr explicit CrashKeyStringBreakpad(const char name[])
: internal::CrashKeyStringImpl(name, indexes_.data, chunk_count) {}
constexpr CrashKeyStringBreakpad(const char name[], Tag tag)
: CrashKeyStringBreakpad(name) {}
CrashKeyStringBreakpad(const CrashKeyStringBreakpad&) = delete;
CrashKeyStringBreakpad& operator=(const CrashKeyStringBreakpad&) = delete;
private:
// Indexes into the TransitionalCrashKeyStorage for when a value is set.
// See the comment in CrashKeyStringImpl for details.
// An unset index in the storage is represented by a sentinel value, which
// is the total number of entries. This will initialize the array with
// that sentinel value as a compile-time expression.
internal::InitializedArray<size_t,
chunk_count,
internal::kCrashKeyStorageNumEntries>
indexes_;
};
template <uint32_t MaxLength>
using CrashKeyString = CrashKeyStringBreakpad<MaxLength>;
#endif // BUILDFLAG(USE_CRASHPAD_ANNOTATION)
// This scoper clears the specified annotation's value when it goes out of
// scope.
//
// Example:
// void DoSomething(const std::string& data) {
// static crash_reporter::CrashKeyString<32> crash_key("DoSomething-data");
// crash_reporter::ScopedCrashKeyString auto_clear(&crash_key, data);
//
// DoSomethignImpl(data);
// }
class [[nodiscard]] ScopedCrashKeyString {
public:
#if BUILDFLAG(USE_CRASHPAD_ANNOTATION)
using CrashKeyType = crashpad::Annotation;
#else
using CrashKeyType = internal::CrashKeyStringImpl;
#endif
template <class T>
ScopedCrashKeyString(T* crash_key, std::string_view value)
: crash_key_(crash_key) {
crash_key->Set(value);
}
ScopedCrashKeyString(const ScopedCrashKeyString&) = delete;
ScopedCrashKeyString& operator=(const ScopedCrashKeyString&) = delete;
~ScopedCrashKeyString() { crash_key_->Clear(); }
private:
const raw_ptr<CrashKeyType> crash_key_;
};
namespace internal {
// Formats a stack trace into a string whose length will not exceed
// |max_length|. This function ensures no addresses are truncated when
// being formatted.
CRASH_KEY_EXPORT std::string FormatStackTrace(
const base::debug::StackTrace& trace,
size_t max_length);
} // namespace internal
// Formats a base::debug::StackTrace as a string of space-separated hexadecimal
// numbers and stores it in a CrashKeyString.
// TODO(rsesek): When all clients use Crashpad, traces should become a first-
// class Annotation type rather than being forced through string conversion.
template <uint32_t Size>
void SetCrashKeyStringToStackTrace(CrashKeyString<Size>* key,
const base::debug::StackTrace& trace) {
std::string trace_string = internal::FormatStackTrace(trace, Size);
key->Set(trace_string);
}
// Initializes the crash key subsystem if it is required. Calling this multiple
// times is safe (though not thread-safe) and will not result in data loss from
// crash keys set prior to the last initialization.
CRASH_KEY_EXPORT void InitializeCrashKeys();
#if defined(UNIT_TEST) || defined(CRASH_CORE_COMMON_IMPLEMENTATION)
// Returns a value for the crash key named |key_name|. For Crashpad-based
// clients, this returns the first instance found of the name. On Breakpad
// clients, oversized crash key values (those longer than
// |kCrashKeyStorageValueSize| - 1) are stored in chunks and must be retrieved
// piecewise, using syntax <key name>__1, <key name>__2, etc.
// Note: In a component build, this will only retrieve crash keys for the
// current component.
CRASH_KEY_EXPORT std::string GetCrashKeyValue(const std::string& key_name);
// Initializes the crash key subsystem with testing configuration if it is
// required.
CRASH_KEY_EXPORT void InitializeCrashKeysForTesting();
// Resets crash key state and, depending on the platform, de-initializes
// the system.
// WARNING: this does not work on Breakpad, which is used by Chrome on Linux
// (crbug.com/1041106).
CRASH_KEY_EXPORT void ResetCrashKeysForTesting();
#endif
} // namespace crash_reporter
#endif // COMPONENTS_CRASH_CORE_COMMON_CRASH_KEY_H_
|