// Copyright (c) 2012 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. #include "base/debug/crash_logging.h" #include #include #include "base/debug/stack_trace.h" #include "base/format_macros.h" #include "base/logging.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" // Undef the macro so the preprocessor doesn't garble the constructor. #undef ScopedCrashKey namespace base { namespace debug { namespace { CrashKeyImplementation* g_crash_key_impl = nullptr; // Global map of crash key names to registration entries. typedef std::unordered_map CrashKeyMap; CrashKeyMap* g_crash_keys_ = nullptr; // The maximum length of a single chunk. size_t g_chunk_max_length_ = 0; // String used to format chunked key names. const char kChunkFormatString[] = "%s-%" PRIuS; // The functions that are called to actually set the key-value pairs in the // crash reportng system. SetCrashKeyValueFuncT g_set_key_func_ = nullptr; ClearCrashKeyValueFuncT g_clear_key_func_ = nullptr; // For a given |length|, computes the number of chunks a value of that size // will occupy. size_t NumChunksForLength(size_t length) { // Compute (length / g_chunk_max_length_), rounded up. return (length + g_chunk_max_length_ - 1) / g_chunk_max_length_; } // The longest max_length allowed by the system. const size_t kLargestValueAllowed = 2048; } // namespace CrashKeyString* AllocateCrashKeyString(const char name[], CrashKeySize value_length) { if (!g_crash_key_impl) return nullptr; return g_crash_key_impl->Allocate(name, value_length); } void SetCrashKeyString(CrashKeyString* crash_key, base::StringPiece value) { if (!g_crash_key_impl || !crash_key) return; g_crash_key_impl->Set(crash_key, value); } void ClearCrashKeyString(CrashKeyString* crash_key) { if (!g_crash_key_impl || !crash_key) return; g_crash_key_impl->Clear(crash_key); } void SetCrashKeyImplementation(std::unique_ptr impl) { delete g_crash_key_impl; g_crash_key_impl = impl.release(); } void SetCrashKeyValue(const base::StringPiece& key, const base::StringPiece& value) { if (!g_set_key_func_ || !g_crash_keys_) return; const CrashKey* crash_key = LookupCrashKey(key); DCHECK(crash_key) << "All crash keys must be registered before use " << "(key = " << key << ")"; // Handle the un-chunked case. if (!crash_key || crash_key->max_length <= g_chunk_max_length_) { g_set_key_func_(key, value); return; } // Unset the unused chunks. std::vector chunks = ChunkCrashKeyValue(*crash_key, value, g_chunk_max_length_); for (size_t i = chunks.size(); i < NumChunksForLength(crash_key->max_length); ++i) { g_clear_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1)); } // Set the chunked keys. for (size_t i = 0; i < chunks.size(); ++i) { g_set_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1), chunks[i]); } } void ClearCrashKey(const base::StringPiece& key) { if (!g_clear_key_func_ || !g_crash_keys_) return; const CrashKey* crash_key = LookupCrashKey(key); // Handle the un-chunked case. if (!crash_key || crash_key->max_length <= g_chunk_max_length_) { g_clear_key_func_(key); return; } for (size_t i = 0; i < NumChunksForLength(crash_key->max_length); ++i) { g_clear_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1)); } } void SetCrashKeyToStackTrace(const base::StringPiece& key, const StackTrace& trace) { size_t count = 0; const void* const* addresses = trace.Addresses(&count); SetCrashKeyFromAddresses(key, addresses, count); } void SetCrashKeyFromAddresses(const base::StringPiece& key, const void* const* addresses, size_t count) { std::string value = ""; if (addresses && count) { const size_t kBreakpadValueMax = 255; std::vector hex_backtrace; size_t length = 0; for (size_t i = 0; i < count; ++i) { std::string s = base::StringPrintf("%p", addresses[i]); length += s.length() + 1; if (length > kBreakpadValueMax) break; hex_backtrace.push_back(s); } value = base::JoinString(hex_backtrace, " "); // Warn if this exceeds the breakpad limits. DCHECK_LE(value.length(), kBreakpadValueMax); } SetCrashKeyValue(key, value); } ScopedCrashKey::ScopedCrashKey(const base::StringPiece& key, const base::StringPiece& value) : key_(key.as_string()) { SetCrashKeyValue(key, value); } ScopedCrashKey::~ScopedCrashKey() { ClearCrashKey(key_); } size_t InitCrashKeys(const CrashKey* const keys, size_t count, size_t chunk_max_length) { DCHECK(!g_crash_keys_) << "Crash logging may only be initialized once"; if (!keys) { delete g_crash_keys_; g_crash_keys_ = nullptr; return 0; } g_crash_keys_ = new CrashKeyMap; g_chunk_max_length_ = chunk_max_length; size_t total_keys = 0; for (size_t i = 0; i < count; ++i) { g_crash_keys_->insert(std::make_pair(keys[i].key_name, keys[i])); total_keys += NumChunksForLength(keys[i].max_length); DCHECK_LT(keys[i].max_length, kLargestValueAllowed); } DCHECK_EQ(count, g_crash_keys_->size()) << "Duplicate crash keys were registered"; return total_keys; } const CrashKey* LookupCrashKey(const base::StringPiece& key) { if (!g_crash_keys_) return nullptr; CrashKeyMap::const_iterator it = g_crash_keys_->find(key.as_string()); if (it == g_crash_keys_->end()) return nullptr; return &(it->second); } void SetCrashKeyReportingFunctions( SetCrashKeyValueFuncT set_key_func, ClearCrashKeyValueFuncT clear_key_func) { g_set_key_func_ = set_key_func; g_clear_key_func_ = clear_key_func; } std::vector ChunkCrashKeyValue(const CrashKey& crash_key, const base::StringPiece& value, size_t chunk_max_length) { std::string value_string = value.substr(0, crash_key.max_length).as_string(); std::vector chunks; for (size_t offset = 0; offset < value_string.length(); ) { std::string chunk = value_string.substr(offset, chunk_max_length); chunks.push_back(chunk); offset += chunk.length(); } return chunks; } void ResetCrashLoggingForTesting() { delete g_crash_keys_; g_crash_keys_ = nullptr; g_chunk_max_length_ = 0; g_set_key_func_ = nullptr; g_clear_key_func_ = nullptr; } } // namespace debug } // namespace base