• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
NumChunksForLength(size_t length)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 
SetCrashKeyValue(const base::StringPiece & key,const base::StringPiece & value)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 
ClearCrashKey(const base::StringPiece & key)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 
SetCrashKeyToStackTrace(const base::StringPiece & key,const StackTrace & trace)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 
SetCrashKeyFromAddresses(const base::StringPiece & key,const void * const * addresses,size_t count)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 
ScopedCrashKey(const base::StringPiece & key,const base::StringPiece & value)130 ScopedCrashKey::ScopedCrashKey(const base::StringPiece& key,
131                                const base::StringPiece& value)
132     : key_(key.as_string()) {
133   SetCrashKeyValue(key, value);
134 }
135 
~ScopedCrashKey()136 ScopedCrashKey::~ScopedCrashKey() {
137   ClearCrashKey(key_);
138 }
139 
InitCrashKeys(const CrashKey * const keys,size_t count,size_t chunk_max_length)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 
LookupCrashKey(const base::StringPiece & key)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 
SetCrashKeyReportingFunctions(SetCrashKeyValueFuncT set_key_func,ClearCrashKeyValueFuncT clear_key_func)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 
ChunkCrashKeyValue(const CrashKey & crash_key,const base::StringPiece & value,size_t chunk_max_length)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 
ResetCrashLoggingForTesting()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