blob: c96108a18b38d3efe007d574e248c400afa6d30b [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// 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 "base/debug/stack_trace.h"
#include "base/macros.h"
#include "base/strings/string_piece.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.
#if BUILDFLAG(USE_CRASHPAD_ANNOTATION)
#include "third_party/crashpad/crashpad/client/annotation.h" // nogncheck
#endif
namespace crash_reporter {
class CrashKeyBreakpadTest;
// 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:
namespace internal {
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) {}
void Set(base::StringPiece 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.
size_t* index_array_;
size_t index_array_count_;
DISALLOW_COPY_AND_ASSIGN(CrashKeyStringImpl);
};
// 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 CrashKeyString : 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 CrashKeyString(const char name[])
: internal::CrashKeyStringImpl(name, indexes_.data, chunk_count) {}
constexpr CrashKeyString(const char name[], Tag tag) : CrashKeyString(name) {}
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_;
DISALLOW_COPY_AND_ASSIGN(CrashKeyString);
};
#endif
// 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 ScopedCrashKeyString {
public:
#if BUILDFLAG(USE_CRASHPAD_ANNOTATION)
using CrashKeyType = crashpad::Annotation;
#else
using CrashKeyType = internal::CrashKeyStringImpl;
#endif
template <class T>
ScopedCrashKeyString(T* crash_key, base::StringPiece value)
: crash_key_(crash_key) {
crash_key->Set(value);
}
~ScopedCrashKeyString() { crash_key_->Clear(); }
private:
CrashKeyType* const crash_key_;
DISALLOW_COPY_AND_ASSIGN(ScopedCrashKeyString);
};
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.
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.
CRASH_KEY_EXPORT std::string GetCrashKeyValue(const std::string& key_name);
// Resets crash key state and, depending on the platform, de-initializes
// the system.
CRASH_KEY_EXPORT void ResetCrashKeysForTesting();
#endif
} // namespace crash_reporter
#endif // COMPONENTS_CRASH_CORE_COMMON_CRASH_KEY_H_