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.
5 #include "base/debug/crash_logging.h"
10 #include "base/debug/stack_trace.h"
11 #include "base/format_macros.h"
12 #include "base/logging.h"
13 #include "base/string_util.h"
14 #include "base/stringprintf.h"
21 // Global map of crash key names to registration entries.
22 typedef std::map
<base::StringPiece
, CrashKey
> CrashKeyMap
;
23 CrashKeyMap
* g_crash_keys_
= NULL
;
25 // The maximum length of a single chunk.
26 size_t g_chunk_max_length_
= 0;
28 // String used to format chunked key names.
29 const char kChunkFormatString
[] = "%s-%" PRIuS
;
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
;
36 // For a given |length|, computes the number of chunks a value of that size
38 size_t NumChunksForLength(size_t length
) {
39 return std::ceil(length
/ static_cast<float>(g_chunk_max_length_
));
42 // The longest max_length allowed by the system.
43 const size_t kLargestValueAllowed
= 1024;
47 void SetCrashKeyValue(const base::StringPiece
& key
,
48 const base::StringPiece
& value
) {
52 const CrashKey
* crash_key
= LookupCrashKey(key
);
54 // TODO(rsesek): Do this:
55 //DCHECK(crash_key) << "All crash keys must be registered before use "
56 // << "(key = " << key << ")";
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
);
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
);
70 g_clear_key_func_(base::StringPrintf(kChunkFormatString
, key
.data(), i
+1));
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),
80 void ClearCrashKey(const base::StringPiece
& key
) {
81 if (!g_clear_key_func_
)
84 const CrashKey
* crash_key
= LookupCrashKey(key
);
86 // Handle the un-chunked case.
87 if (!crash_key
|| crash_key
->max_length
<= g_chunk_max_length_
) {
88 g_clear_key_func_(key
);
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));
97 void SetCrashKeyToStackTrace(const base::StringPiece
& key
,
98 const StackTrace
& trace
) {
100 const void* const* addresses
= trace
.Addresses(&count
);
101 SetCrashKeyFromAddresses(key
, addresses
, count
);
104 void SetCrashKeyFromAddresses(const base::StringPiece
& key
,
105 const void* const* addresses
,
107 std::string value
= "<null>";
108 if (addresses
&& count
) {
109 const size_t kBreakpadValueMax
= 255;
111 std::vector
<std::string
> hex_backtrace
;
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
)
119 hex_backtrace
.push_back(s
);
122 value
= JoinString(hex_backtrace
, ' ');
124 // Warn if this exceeds the breakpad limits.
125 DCHECK_LE(value
.length(), kBreakpadValueMax
);
128 SetCrashKeyValue(key
, value
);
131 ScopedCrashKey::ScopedCrashKey(const base::StringPiece
& key
,
132 const base::StringPiece
& value
)
133 : key_(key
.as_string()) {
134 SetCrashKeyValue(key
, value
);
137 ScopedCrashKey::~ScopedCrashKey() {
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";
145 delete g_crash_keys_
;
146 g_crash_keys_
= NULL
;
150 g_crash_keys_
= new CrashKeyMap
;
151 g_chunk_max_length_
= chunk_max_length
;
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
);
159 DCHECK_EQ(count
, g_crash_keys_
->size())
160 << "Duplicate crash keys were registered";
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())
169 return &(it
->second
);
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
;
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();
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
;