1 // Copyright (c) 2013 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 "chrome/common/crash_keys.h"
7 #include "base/command_line.h"
8 #include "base/format_macros.h"
9 #include "base/logging.h"
10 #include "base/strings/string_split.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/common/chrome_switches.h"
15 #include "content/public/common/content_switches.h"
16 #include "ipc/ipc_switches.h"
18 #if defined(OS_MACOSX)
19 #include "breakpad/src/common/simple_string_dictionary.h"
21 #include "breakpad/src/client/windows/common/ipc_protocol.h"
22 #elif defined(OS_CHROMEOS)
23 #include "chrome/common/chrome_switches.h"
24 #include "gpu/command_buffer/service/gpu_switches.h"
25 #include "ui/gl/gl_switches.h"
28 namespace crash_keys
{
30 // A small crash key, guaranteed to never be split into multiple pieces.
31 const size_t kSmallSize
= 63;
33 // A medium crash key, which will be chunked on certain platforms but not
34 // others. Guaranteed to never be more than four chunks.
35 const size_t kMediumSize
= kSmallSize
* 4;
37 // A large crash key, which will be chunked on all platforms. This should be
39 const size_t kLargeSize
= kSmallSize
* 16;
41 // The maximum lengths specified by breakpad include the trailing NULL, so
42 // the actual length of the string is one less.
43 #if defined(OS_MACOSX)
44 static const size_t kSingleChunkLength
=
45 google_breakpad::SimpleStringDictionary::value_size
- 1;
47 static const size_t kSingleChunkLength
=
48 google_breakpad::CustomInfoEntry::kValueMaxLength
- 1;
50 static const size_t kSingleChunkLength
= 63;
53 // Guarantees for crash key sizes.
54 static_assert(kSmallSize
<= kSingleChunkLength
,
55 "crash key chunk size too small");
56 #if defined(OS_MACOSX)
57 static_assert(kMediumSize
<= kSingleChunkLength
,
58 "mac has medium size crash key chunks");
61 #if defined(OS_MACOSX)
62 // Crashpad owns the "guid" key. Chrome's metrics client ID is a separate ID
63 // carried in a distinct "metrics_client_id" field.
64 const char kMetricsClientId
[] = "metrics_client_id";
66 const char kClientId
[] = "guid";
69 const char kChannel
[] = "channel";
71 const char kActiveURL
[] = "url-chunk";
73 const char kFontKeyName
[] = "font_key_name";
75 const char kSwitch
[] = "switch-%" PRIuS
;
76 const char kNumSwitches
[] = "num-switches";
78 const char kNumVariations
[] = "num-experiments";
79 const char kVariations
[] = "variations";
81 const char kExtensionID
[] = "extension-%" PRIuS
;
82 const char kNumExtensionsCount
[] = "num-extensions";
84 const char kShutdownType
[] = "shutdown-type";
86 #if !defined(OS_ANDROID)
87 const char kGPUVendorID
[] = "gpu-venid";
88 const char kGPUDeviceID
[] = "gpu-devid";
90 const char kGPUDriverVersion
[] = "gpu-driver";
91 const char kGPUPixelShaderVersion
[] = "gpu-psver";
92 const char kGPUVertexShaderVersion
[] = "gpu-vsver";
93 #if defined(OS_MACOSX)
94 const char kGPUGLVersion
[] = "gpu-glver";
95 #elif defined(OS_POSIX)
96 const char kGPUVendor
[] = "gpu-gl-vendor";
97 const char kGPURenderer
[] = "gpu-gl-renderer";
100 const char kPrinterInfo
[] = "prn-info-%" PRIuS
;
102 #if defined(OS_CHROMEOS)
103 const char kNumberOfUsers
[] = "num-users";
106 #if defined(OS_MACOSX)
109 const char kFirstNSException
[] = "firstexception";
110 const char kFirstNSExceptionTrace
[] = "firstexception_bt";
112 const char kLastNSException
[] = "lastexception";
113 const char kLastNSExceptionTrace
[] = "lastexception_bt";
115 const char kNSException
[] = "nsexception";
116 const char kNSExceptionTrace
[] = "nsexception_bt";
118 const char kSendAction
[] = "sendaction";
120 const char kZombie
[] = "zombie";
121 const char kZombieTrace
[] = "zombie_dealloc_bt";
127 const char kKaskoGuid
[] = "kasko-guid";
128 const char kKaskoEquivalentGuid
[] = "kasko-equivalent-guid";
131 const char kBug464926CrashKey
[] = "bug-464926-info";
133 const char kViewCount
[] = "view-count";
135 size_t RegisterChromeCrashKeys() {
136 // The following keys may be chunked by the underlying crash logging system,
137 // but ultimately constitute a single key-value pair.
138 base::debug::CrashKey fixed_keys
[] = {
139 #if defined(OS_MACOSX)
140 { kMetricsClientId
, kSmallSize
},
142 { kClientId
, kSmallSize
},
144 { kChannel
, kSmallSize
},
145 { kActiveURL
, kLargeSize
},
146 { kNumSwitches
, kSmallSize
},
147 { kNumVariations
, kSmallSize
},
148 { kVariations
, kLargeSize
},
149 { kNumExtensionsCount
, kSmallSize
},
150 { kShutdownType
, kSmallSize
},
151 #if !defined(OS_ANDROID)
152 { kGPUVendorID
, kSmallSize
},
153 { kGPUDeviceID
, kSmallSize
},
155 { kGPUDriverVersion
, kSmallSize
},
156 { kGPUPixelShaderVersion
, kSmallSize
},
157 { kGPUVertexShaderVersion
, kSmallSize
},
158 #if defined(OS_MACOSX)
159 { kGPUGLVersion
, kSmallSize
},
160 #elif defined(OS_POSIX)
161 { kGPUVendor
, kSmallSize
},
162 { kGPURenderer
, kSmallSize
},
166 { "discardable-memory-allocated", kSmallSize
},
167 { "discardable-memory-free", kSmallSize
},
168 { kFontKeyName
, kSmallSize
},
169 { "ppapi_path", kMediumSize
},
170 { "subresource_url", kLargeSize
},
171 { "total-discardable-memory-allocated", kSmallSize
},
172 #if defined(OS_CHROMEOS)
173 { kNumberOfUsers
, kSmallSize
},
175 #if defined(OS_MACOSX)
176 { mac::kFirstNSException
, kMediumSize
},
177 { mac::kFirstNSExceptionTrace
, kMediumSize
},
178 { mac::kLastNSException
, kMediumSize
},
179 { mac::kLastNSExceptionTrace
, kMediumSize
},
180 { mac::kNSException
, kMediumSize
},
181 { mac::kNSExceptionTrace
, kMediumSize
},
182 { mac::kSendAction
, kMediumSize
},
183 { mac::kZombie
, kMediumSize
},
184 { mac::kZombieTrace
, kMediumSize
},
186 { "channel_error_bt", kMediumSize
},
187 { "remove_route_bt", kMediumSize
},
188 { "rwhvm_window", kMediumSize
},
190 { "VideoCaptureDeviceQTKit", kSmallSize
},
193 { kKaskoGuid
, kSmallSize
},
194 { kKaskoEquivalentGuid
, kSmallSize
},
196 { kBug464926CrashKey
, kSmallSize
},
197 { kViewCount
, kSmallSize
},
200 // This dynamic set of keys is used for sets of key value pairs when gathering
201 // a collection of data, like command line switches or extension IDs.
202 std::vector
<base::debug::CrashKey
> keys(
203 fixed_keys
, fixed_keys
+ arraysize(fixed_keys
));
205 // Register the switches.
207 // The fixed_keys names are string constants. Use static storage for
208 // formatted key names as well, since they will persist for the duration of
210 static char formatted_keys
[kSwitchesMaxCount
][sizeof(kSwitch
) + 1] =
212 const size_t formatted_key_len
= sizeof(formatted_keys
[0]);
213 for (size_t i
= 0; i
< kSwitchesMaxCount
; ++i
) {
214 // Name the keys using 1-based indexing.
215 int n
= base::snprintf(
216 formatted_keys
[i
], formatted_key_len
, kSwitch
, i
+ 1);
218 base::debug::CrashKey crash_key
= { formatted_keys
[i
], kSmallSize
};
219 keys
.push_back(crash_key
);
223 // Register the extension IDs.
225 static char formatted_keys
[kExtensionIDMaxCount
][sizeof(kExtensionID
) + 1] =
227 const size_t formatted_key_len
= sizeof(formatted_keys
[0]);
228 for (size_t i
= 0; i
< kExtensionIDMaxCount
; ++i
) {
229 int n
= base::snprintf(
230 formatted_keys
[i
], formatted_key_len
, kExtensionID
, i
+ 1);
232 base::debug::CrashKey crash_key
= { formatted_keys
[i
], kSmallSize
};
233 keys
.push_back(crash_key
);
237 // Register the printer info.
239 static char formatted_keys
[kPrinterInfoCount
][sizeof(kPrinterInfo
) + 1] =
241 const size_t formatted_key_len
= sizeof(formatted_keys
[0]);
242 for (size_t i
= 0; i
< kPrinterInfoCount
; ++i
) {
243 // Key names are 1-indexed.
244 int n
= base::snprintf(
245 formatted_keys
[i
], formatted_key_len
, kPrinterInfo
, i
+ 1);
247 base::debug::CrashKey crash_key
= { formatted_keys
[i
], kSmallSize
};
248 keys
.push_back(crash_key
);
252 return base::debug::InitCrashKeys(&keys
.at(0), keys
.size(),
256 void SetMetricsClientIdFromGUID(const std::string
& metrics_client_guid
) {
257 std::string
stripped_guid(metrics_client_guid
);
258 // Remove all instance of '-' char from the GUID. So BCD-WXY becomes BCDWXY.
259 ReplaceSubstringsAfterOffset(&stripped_guid
, 0, "-", "");
260 if (stripped_guid
.empty())
263 #if defined(OS_MACOSX)
264 // The crash client ID is maintained by Crashpad and is distinct from the
265 // metrics client ID, which is carried in its own key.
266 base::debug::SetCrashKeyValue(kMetricsClientId
, stripped_guid
);
268 // The crash client ID is set by the application when Breakpad is in use.
269 // The same ID as the metrics client ID is used.
270 base::debug::SetCrashKeyValue(kClientId
, stripped_guid
);
274 void ClearMetricsClientId() {
275 #if defined(OS_MACOSX)
276 // Crashpad always monitors for crashes, but doesn't upload them when
277 // crash reporting is disabled. The preference to upload crash reports is
278 // linked to the preference for metrics reporting. When metrics reporting is
279 // disabled, don't put the metrics client ID into crash dumps. This way, crash
280 // reports that are saved but not uploaded will not have a metrics client ID
281 // from the time that metrics reporting was disabled even if they are uploaded
282 // by user action at a later date.
284 // Breakpad cannot be enabled or disabled without an application restart, and
285 // it needs to use the metrics client ID as its stable crash client ID, so
286 // leave its client ID intact even when metrics reporting is disabled while
287 // the application is running.
288 base::debug::ClearCrashKey(kMetricsClientId
);
292 static bool IsBoringSwitch(const std::string
& flag
) {
293 static const char* const kIgnoreSwitches
[] = {
294 switches::kEnableLogging
,
295 switches::kFlagSwitchesBegin
,
296 switches::kFlagSwitchesEnd
,
297 switches::kLoggingLevel
,
299 // This file is linked into both chrome.dll and chrome.exe. However //ipc
300 // is only in the .dll, so this needs to be a literal rather than the
302 "channel", // switches::kProcessChannelID
304 switches::kProcessChannelID
,
306 switches::kProcessType
,
310 switches::kForceFieldTrials
,
311 switches::kPluginPath
,
312 #elif defined(OS_MACOSX)
313 switches::kMetricsClientID
,
314 #elif defined(OS_CHROMEOS)
315 switches::kPpapiFlashArgs
,
316 switches::kPpapiFlashPath
,
317 switches::kRegisterPepperPlugins
,
318 switches::kUIPrioritizeInGpuProcess
,
320 switches::kUserDataDir
,
321 // Cros/CC flgas are specified as raw strings to avoid dependency.
322 "child-wallpaper-large",
323 "child-wallpaper-small",
324 "default-wallpaper-large",
325 "default-wallpaper-small",
326 "guest-wallpaper-large",
327 "guest-wallpaper-small",
328 "enterprise-enable-forced-re-enrollment",
329 "enterprise-enrollment-initial-modulus",
330 "enterprise-enrollment-modulus-limit",
333 "max-tiles-for-interest-area",
334 "max-unused-resource-memory-usage-percentage",
335 "termination-message-file",
341 // Just about everything has this, don't bother.
342 if (StartsWithASCII(flag
, "/prefetch:", true))
346 if (!StartsWithASCII(flag
, "--", true))
348 size_t end
= flag
.find("=");
349 size_t len
= (end
== std::string::npos
) ? flag
.length() - 2 : end
- 2;
350 for (size_t i
= 0; i
< arraysize(kIgnoreSwitches
); ++i
) {
351 if (flag
.compare(2, len
, kIgnoreSwitches
[i
]) == 0)
357 void SetSwitchesFromCommandLine(const base::CommandLine
* command_line
) {
358 DCHECK(command_line
);
362 const base::CommandLine::StringVector
& argv
= command_line
->argv();
364 // Set the number of switches in case size > kNumSwitches.
365 base::debug::SetCrashKeyValue(kNumSwitches
,
366 base::StringPrintf("%" PRIuS
, argv
.size() - 1));
368 size_t key_i
= 1; // Key names are 1-indexed.
370 // Go through the argv, skipping the exec path.
371 for (size_t i
= 1; i
< argv
.size(); ++i
) {
373 std::string switch_str
= base::WideToUTF8(argv
[i
]);
375 std::string switch_str
= argv
[i
];
378 // Skip uninteresting switches.
379 if (IsBoringSwitch(switch_str
))
382 // Stop if there are too many switches.
383 if (i
> crash_keys::kSwitchesMaxCount
)
386 std::string key
= base::StringPrintf(kSwitch
, key_i
++);
387 base::debug::SetCrashKeyValue(key
, switch_str
);
390 // Clear any remaining switches.
391 for (; key_i
<= kSwitchesMaxCount
; ++key_i
) {
392 base::debug::ClearCrashKey(base::StringPrintf(kSwitch
, key_i
));
396 void SetVariationsList(const std::vector
<std::string
>& variations
) {
397 base::debug::SetCrashKeyValue(kNumVariations
,
398 base::StringPrintf("%" PRIuS
, variations
.size()));
400 std::string variations_string
;
401 variations_string
.reserve(kLargeSize
);
403 for (size_t i
= 0; i
< variations
.size(); ++i
) {
404 const std::string
& variation
= variations
[i
];
405 // Do not truncate an individual experiment.
406 if (variations_string
.size() + variation
.size() >= kLargeSize
)
408 variations_string
+= variation
;
409 variations_string
+= ",";
412 base::debug::SetCrashKeyValue(kVariations
, variations_string
);
415 void SetActiveExtensions(const std::set
<std::string
>& extensions
) {
416 base::debug::SetCrashKeyValue(kNumExtensionsCount
,
417 base::StringPrintf("%" PRIuS
, extensions
.size()));
419 std::set
<std::string
>::const_iterator it
= extensions
.begin();
420 for (size_t i
= 0; i
< kExtensionIDMaxCount
; ++i
) {
421 std::string key
= base::StringPrintf(kExtensionID
, i
+ 1);
422 if (it
== extensions
.end()) {
423 base::debug::ClearCrashKey(key
);
425 base::debug::SetCrashKeyValue(key
, *it
);
431 ScopedPrinterInfo::ScopedPrinterInfo(const base::StringPiece
& data
) {
432 std::vector
<std::string
> info
;
433 base::SplitString(data
.as_string(), ';', &info
);
434 for (size_t i
= 0; i
< kPrinterInfoCount
; ++i
) {
435 std::string key
= base::StringPrintf(kPrinterInfo
, i
+ 1);
439 base::debug::SetCrashKeyValue(key
, value
);
443 ScopedPrinterInfo::~ScopedPrinterInfo() {
444 for (size_t i
= 0; i
< kPrinterInfoCount
; ++i
) {
445 std::string key
= base::StringPrintf(kPrinterInfo
, i
+ 1);
446 base::debug::ClearCrashKey(key
);
450 } // namespace crash_keys