crash_logging.cc revision 5e3f23d412006dc4db4e659864679f29341e113f
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_)
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
80void 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
97void 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
104void 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
131ScopedCrashKey::ScopedCrashKey(const base::StringPiece& key,
132                               const base::StringPiece& value)
133    : key_(key.as_string()) {
134  SetCrashKeyValue(key, value);
135}
136
137ScopedCrashKey::~ScopedCrashKey() {
138  ClearCrashKey(key_);
139}
140
141size_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
165const 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
172void 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
179std::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
192void 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