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 16 namespace base { 17 namespace debug { 18 19 namespace { 20 21 // Global map of crash key names to registration entries. 22 typedef std::map<base::StringPiece, CrashKey> CrashKeyMap; 23 CrashKeyMap* g_crash_keys_ = NULL; 24 25 // The maximum length of a single chunk. 26 size_t g_chunk_max_length_ = 0; 27 28 // String used to format chunked key names. 29 const char kChunkFormatString[] = "%s-%" PRIuS; 30 31 // The functions that are called to actually set the key-value pairs in the 32 // crash reportng system. 33 SetCrashKeyValueFuncT g_set_key_func_ = NULL; 34 ClearCrashKeyValueFuncT g_clear_key_func_ = NULL; 35 36 // For a given |length|, computes the number of chunks a value of that size 37 // will occupy. 38 size_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. 43 const size_t kLargestValueAllowed = 1024; 44 45 } // namespace 46 47 void 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 79 void 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 96 void 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 103 void 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 130 ScopedCrashKey::ScopedCrashKey(const base::StringPiece& key, 131 const base::StringPiece& value) 132 : key_(key.as_string()) { 133 SetCrashKeyValue(key, value); 134 } 135 136 ScopedCrashKey::~ScopedCrashKey() { 137 ClearCrashKey(key_); 138 } 139 140 size_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 164 const 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 173 void 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 180 std::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 193 void 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