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/strings/string_util.h"
14 #include "base/strings/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
) {
49 if (!g_set_key_func_
|| !g_crash_keys_
)
52 const CrashKey
* crash_key
= LookupCrashKey(key
);
54 DCHECK(crash_key
) << "All crash keys must be registered before use "
55 << "(key = " << key
<< ")";
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
);
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
);
69 g_clear_key_func_(base::StringPrintf(kChunkFormatString
, key
.data(), i
+1));
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),
79 void ClearCrashKey(const base::StringPiece
& key
) {
80 if (!g_clear_key_func_
|| !g_crash_keys_
)
83 const CrashKey
* crash_key
= LookupCrashKey(key
);
85 // Handle the un-chunked case.
86 if (!crash_key
|| crash_key
->max_length
<= g_chunk_max_length_
) {
87 g_clear_key_func_(key
);
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));
96 void SetCrashKeyToStackTrace(const base::StringPiece
& key
,
97 const StackTrace
& trace
) {
99 const void* const* addresses
= trace
.Addresses(&count
);
100 SetCrashKeyFromAddresses(key
, addresses
, count
);
103 void SetCrashKeyFromAddresses(const base::StringPiece
& key
,
104 const void* const* addresses
,
106 std::string value
= "<null>";
107 if (addresses
&& count
) {
108 const size_t kBreakpadValueMax
= 255;
110 std::vector
<std::string
> hex_backtrace
;
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
)
118 hex_backtrace
.push_back(s
);
121 value
= JoinString(hex_backtrace
, ' ');
123 // Warn if this exceeds the breakpad limits.
124 DCHECK_LE(value
.length(), kBreakpadValueMax
);
127 SetCrashKeyValue(key
, value
);
130 ScopedCrashKey::ScopedCrashKey(const base::StringPiece
& key
,
131 const base::StringPiece
& value
)
132 : key_(key
.as_string()) {
133 SetCrashKeyValue(key
, value
);
136 ScopedCrashKey::~ScopedCrashKey() {
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";
144 delete g_crash_keys_
;
145 g_crash_keys_
= NULL
;
149 g_crash_keys_
= new CrashKeyMap
;
150 g_chunk_max_length_
= chunk_max_length
;
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
);
158 DCHECK_EQ(count
, g_crash_keys_
->size())
159 << "Duplicate crash keys were registered";
164 const CrashKey
* LookupCrashKey(const base::StringPiece
& key
) {
167 CrashKeyMap::const_iterator it
= g_crash_keys_
->find(key
.as_string());
168 if (it
== g_crash_keys_
->end())
170 return &(it
->second
);
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
;
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();
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
;