1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/debug/crash_logging.h" 6 7#include <cmath> 8#include <map> 9 10#include "base/debug/stack_trace.h" 11#include "base/format_macros.h" 12#include "base/logging.h" 13#include "base/strings/string_util.h" 14#include "base/strings/stringprintf.h" 15 16namespace base { 17namespace debug { 18 19namespace { 20 21// Global map of crash key names to registration entries. 22typedef std::map<base::StringPiece, CrashKey> CrashKeyMap; 23CrashKeyMap* g_crash_keys_ = NULL; 24 25// The maximum length of a single chunk. 26size_t g_chunk_max_length_ = 0; 27 28// String used to format chunked key names. 29const char kChunkFormatString[] = "%s-%" PRIuS; 30 31// The functions that are called to actually set the key-value pairs in the 32// crash reportng system. 33SetCrashKeyValueFuncT g_set_key_func_ = NULL; 34ClearCrashKeyValueFuncT g_clear_key_func_ = NULL; 35 36// For a given |length|, computes the number of chunks a value of that size 37// will occupy. 38size_t NumChunksForLength(size_t length) { 39 return std::ceil(length / static_cast<float>(g_chunk_max_length_)); 40} 41 42// The longest max_length allowed by the system. 43const size_t kLargestValueAllowed = 1024; 44 45} // namespace 46 47void SetCrashKeyValue(const base::StringPiece& key, 48 const base::StringPiece& value) { 49 if (!g_set_key_func_ || !g_crash_keys_) 50 return; 51 52 const CrashKey* crash_key = LookupCrashKey(key); 53 54 DCHECK(crash_key) << "All crash keys must be registered before use " 55 << "(key = " << key << ")"; 56 57 // Handle the un-chunked case. 58 if (!crash_key || crash_key->max_length <= g_chunk_max_length_) { 59 g_set_key_func_(key, value); 60 return; 61 } 62 63 // Unset the unused chunks. 64 std::vector<std::string> chunks = 65 ChunkCrashKeyValue(*crash_key, value, g_chunk_max_length_); 66 for (size_t i = chunks.size(); 67 i < NumChunksForLength(crash_key->max_length); 68 ++i) { 69 g_clear_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1)); 70 } 71 72 // Set the chunked keys. 73 for (size_t i = 0; i < chunks.size(); ++i) { 74 g_set_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1), 75 chunks[i]); 76 } 77} 78 79void ClearCrashKey(const base::StringPiece& key) { 80 if (!g_clear_key_func_ || !g_crash_keys_) 81 return; 82 83 const CrashKey* crash_key = LookupCrashKey(key); 84 85 // Handle the un-chunked case. 86 if (!crash_key || crash_key->max_length <= g_chunk_max_length_) { 87 g_clear_key_func_(key); 88 return; 89 } 90 91 for (size_t i = 0; i < NumChunksForLength(crash_key->max_length); ++i) { 92 g_clear_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1)); 93 } 94} 95 96void SetCrashKeyToStackTrace(const base::StringPiece& key, 97 const StackTrace& trace) { 98 size_t count = 0; 99 const void* const* addresses = trace.Addresses(&count); 100 SetCrashKeyFromAddresses(key, addresses, count); 101} 102 103void SetCrashKeyFromAddresses(const base::StringPiece& key, 104 const void* const* addresses, 105 size_t count) { 106 std::string value = "<null>"; 107 if (addresses && count) { 108 const size_t kBreakpadValueMax = 255; 109 110 std::vector<std::string> hex_backtrace; 111 size_t length = 0; 112 113 for (size_t i = 0; i < count; ++i) { 114 std::string s = base::StringPrintf("%p", addresses[i]); 115 length += s.length() + 1; 116 if (length > kBreakpadValueMax) 117 break; 118 hex_backtrace.push_back(s); 119 } 120 121 value = JoinString(hex_backtrace, ' '); 122 123 // Warn if this exceeds the breakpad limits. 124 DCHECK_LE(value.length(), kBreakpadValueMax); 125 } 126 127 SetCrashKeyValue(key, value); 128} 129 130ScopedCrashKey::ScopedCrashKey(const base::StringPiece& key, 131 const base::StringPiece& value) 132 : key_(key.as_string()) { 133 SetCrashKeyValue(key, value); 134} 135 136ScopedCrashKey::~ScopedCrashKey() { 137 ClearCrashKey(key_); 138} 139 140size_t InitCrashKeys(const CrashKey* const keys, size_t count, 141 size_t chunk_max_length) { 142 DCHECK(!g_crash_keys_) << "Crash logging may only be initialized once"; 143 if (!keys) { 144 delete g_crash_keys_; 145 g_crash_keys_ = NULL; 146 return 0; 147 } 148 149 g_crash_keys_ = new CrashKeyMap; 150 g_chunk_max_length_ = chunk_max_length; 151 152 size_t total_keys = 0; 153 for (size_t i = 0; i < count; ++i) { 154 g_crash_keys_->insert(std::make_pair(keys[i].key_name, keys[i])); 155 total_keys += NumChunksForLength(keys[i].max_length); 156 DCHECK_LT(keys[i].max_length, kLargestValueAllowed); 157 } 158 DCHECK_EQ(count, g_crash_keys_->size()) 159 << "Duplicate crash keys were registered"; 160 161 return total_keys; 162} 163 164const CrashKey* LookupCrashKey(const base::StringPiece& key) { 165 if (!g_crash_keys_) 166 return NULL; 167 CrashKeyMap::const_iterator it = g_crash_keys_->find(key.as_string()); 168 if (it == g_crash_keys_->end()) 169 return NULL; 170 return &(it->second); 171} 172 173void SetCrashKeyReportingFunctions( 174 SetCrashKeyValueFuncT set_key_func, 175 ClearCrashKeyValueFuncT clear_key_func) { 176 g_set_key_func_ = set_key_func; 177 g_clear_key_func_ = clear_key_func; 178} 179 180std::vector<std::string> ChunkCrashKeyValue(const CrashKey& crash_key, 181 const base::StringPiece& value, 182 size_t chunk_max_length) { 183 std::string value_string = value.substr(0, crash_key.max_length).as_string(); 184 std::vector<std::string> chunks; 185 for (size_t offset = 0; offset < value_string.length(); ) { 186 std::string chunk = value_string.substr(offset, chunk_max_length); 187 chunks.push_back(chunk); 188 offset += chunk.length(); 189 } 190 return chunks; 191} 192 193void ResetCrashLoggingForTesting() { 194 delete g_crash_keys_; 195 g_crash_keys_ = NULL; 196 g_chunk_max_length_ = 0; 197 g_set_key_func_ = NULL; 198 g_clear_key_func_ = NULL; 199} 200 201} // namespace debug 202} // namespace base 203