// 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 <cmath> #include <map> #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" namespace base { namespace debug { namespace { // Global map of crash key names to registration entries. typedef std::map<base::StringPiece, CrashKey> CrashKeyMap; CrashKeyMap* g_crash_keys_ = NULL; // 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_ = NULL; ClearCrashKeyValueFuncT g_clear_key_func_ = NULL; // For a given |length|, computes the number of chunks a value of that size // will occupy. size_t NumChunksForLength(size_t length) { return std::ceil(length / static_cast<float>(g_chunk_max_length_)); } // The longest max_length allowed by the system. const size_t kLargestValueAllowed = 1024; } // namespace 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<std::string> 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 = "<null>"; if (addresses && count) { const size_t kBreakpadValueMax = 255; std::vector<std::string> 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 = 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_ = NULL; 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 NULL; CrashKeyMap::const_iterator it = g_crash_keys_->find(key.as_string()); if (it == g_crash_keys_->end()) return NULL; 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<std::string> 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<std::string> 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_ = NULL; g_chunk_max_length_ = 0; g_set_key_func_ = NULL; g_clear_key_func_ = NULL; } } // namespace debug } // namespace base