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_) 50 return; 51 52 const CrashKey* crash_key = LookupCrashKey(key); 53 54 // TODO(rsesek): Do this: 55 //DCHECK(crash_key) << "All crash keys must be registered before use " 56 // << "(key = " << key << ")"; 57 58 // Handle the un-chunked case. 59 if (!crash_key || crash_key->max_length <= g_chunk_max_length_) { 60 g_set_key_func_(key, value); 61 return; 62 } 63 64 // Unset the unused chunks. 65 std::vector<std::string> chunks = 66 ChunkCrashKeyValue(*crash_key, value, g_chunk_max_length_); 67 for (size_t i = chunks.size(); 68 i < NumChunksForLength(crash_key->max_length); 69 ++i) { 70 g_clear_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1)); 71 } 72 73 // Set the chunked keys. 74 for (size_t i = 0; i < chunks.size(); ++i) { 75 g_set_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1), 76 chunks[i]); 77 } 78 } 79 80 void ClearCrashKey(const base::StringPiece& key) { 81 if (!g_clear_key_func_) 82 return; 83 84 const CrashKey* crash_key = LookupCrashKey(key); 85 86 // Handle the un-chunked case. 87 if (!crash_key || crash_key->max_length <= g_chunk_max_length_) { 88 g_clear_key_func_(key); 89 return; 90 } 91 92 for (size_t i = 0; i < NumChunksForLength(crash_key->max_length); ++i) { 93 g_clear_key_func_(base::StringPrintf(kChunkFormatString, key.data(), i+1)); 94 } 95 } 96 97 void SetCrashKeyToStackTrace(const base::StringPiece& key, 98 const StackTrace& trace) { 99 size_t count = 0; 100 const void* const* addresses = trace.Addresses(&count); 101 SetCrashKeyFromAddresses(key, addresses, count); 102 } 103 104 void SetCrashKeyFromAddresses(const base::StringPiece& key, 105 const void* const* addresses, 106 size_t count) { 107 std::string value = "<null>"; 108 if (addresses && count) { 109 const size_t kBreakpadValueMax = 255; 110 111 std::vector<std::string> hex_backtrace; 112 size_t length = 0; 113 114 for (size_t i = 0; i < count; ++i) { 115 std::string s = base::StringPrintf("%p", addresses[i]); 116 length += s.length() + 1; 117 if (length > kBreakpadValueMax) 118 break; 119 hex_backtrace.push_back(s); 120 } 121 122 value = JoinString(hex_backtrace, ' '); 123 124 // Warn if this exceeds the breakpad limits. 125 DCHECK_LE(value.length(), kBreakpadValueMax); 126 } 127 128 SetCrashKeyValue(key, value); 129 } 130 131 ScopedCrashKey::ScopedCrashKey(const base::StringPiece& key, 132 const base::StringPiece& value) 133 : key_(key.as_string()) { 134 SetCrashKeyValue(key, value); 135 } 136 137 ScopedCrashKey::~ScopedCrashKey() { 138 ClearCrashKey(key_); 139 } 140 141 size_t InitCrashKeys(const CrashKey* const keys, size_t count, 142 size_t chunk_max_length) { 143 DCHECK(!g_crash_keys_) << "Crash logging may only be initialized once"; 144 if (!keys) { 145 delete g_crash_keys_; 146 g_crash_keys_ = NULL; 147 return 0; 148 } 149 150 g_crash_keys_ = new CrashKeyMap; 151 g_chunk_max_length_ = chunk_max_length; 152 153 size_t total_keys = 0; 154 for (size_t i = 0; i < count; ++i) { 155 g_crash_keys_->insert(std::make_pair(keys[i].key_name, keys[i])); 156 total_keys += NumChunksForLength(keys[i].max_length); 157 DCHECK_LT(keys[i].max_length, kLargestValueAllowed); 158 } 159 DCHECK_EQ(count, g_crash_keys_->size()) 160 << "Duplicate crash keys were registered"; 161 162 return total_keys; 163 } 164 165 const CrashKey* LookupCrashKey(const base::StringPiece& key) { 166 CrashKeyMap::const_iterator it = g_crash_keys_->find(key.as_string()); 167 if (it == g_crash_keys_->end()) 168 return NULL; 169 return &(it->second); 170 } 171 172 void SetCrashKeyReportingFunctions( 173 SetCrashKeyValueFuncT set_key_func, 174 ClearCrashKeyValueFuncT clear_key_func) { 175 g_set_key_func_ = set_key_func; 176 g_clear_key_func_ = clear_key_func; 177 } 178 179 std::vector<std::string> ChunkCrashKeyValue(const CrashKey& crash_key, 180 const base::StringPiece& value, 181 size_t chunk_max_length) { 182 std::string value_string = value.substr(0, crash_key.max_length).as_string(); 183 std::vector<std::string> chunks; 184 for (size_t offset = 0; offset < value_string.length(); ) { 185 std::string chunk = value_string.substr(offset, chunk_max_length); 186 chunks.push_back(chunk); 187 offset += chunk.length(); 188 } 189 return chunks; 190 } 191 192 void ResetCrashLoggingForTesting() { 193 delete g_crash_keys_; 194 g_crash_keys_ = NULL; 195 g_chunk_max_length_ = 0; 196 g_set_key_func_ = NULL; 197 g_clear_key_func_ = NULL; 198 } 199 200 } // namespace debug 201 } // namespace base 202