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/installer/util/google_update_settings.h"
16 #if defined(OS_MACOSX)
17 #include "breakpad/src/common/simple_string_dictionary.h"
19 #include "breakpad/src/client/windows/common/ipc_protocol.h"
20 #elif defined(OS_CHROMEOS)
21 #include "chrome/common/chrome_switches.h"
22 #include "gpu/command_buffer/service/gpu_switches.h"
23 #include "ui/gl/gl_switches.h"
26 namespace crash_keys
{
28 // A small crash key, guaranteed to never be split into multiple pieces.
29 const size_t kSmallSize
= 63;
31 // A medium crash key, which will be chunked on certain platforms but not
32 // others. Guaranteed to never be more than four chunks.
33 const size_t kMediumSize
= kSmallSize
* 4;
35 // A large crash key, which will be chunked on all platforms. This should be
37 const size_t kLargeSize
= kSmallSize
* 16;
39 // The maximum lengths specified by breakpad include the trailing NULL, so
40 // the actual length of the string is one less.
41 #if defined(OS_MACOSX)
42 static const size_t kSingleChunkLength
=
43 google_breakpad::SimpleStringDictionary::value_size
- 1;
45 static const size_t kSingleChunkLength
=
46 google_breakpad::CustomInfoEntry::kValueMaxLength
- 1;
48 static const size_t kSingleChunkLength
= 63;
51 // Guarantees for crash key sizes.
52 COMPILE_ASSERT(kSmallSize
<= kSingleChunkLength
,
53 crash_key_chunk_size_too_small
);
54 #if defined(OS_MACOSX)
55 COMPILE_ASSERT(kMediumSize
<= kSingleChunkLength
,
56 mac_has_medium_size_crash_key_chunks
);
59 const char kClientID
[] = "guid";
61 const char kChannel
[] = "channel";
63 const char kActiveURL
[] = "url-chunk";
65 const char kSwitch
[] = "switch-%" PRIuS
;
66 const char kNumSwitches
[] = "num-switches";
68 const char kNumVariations
[] = "num-experiments";
69 const char kVariations
[] = "variations";
71 const char kExtensionID
[] = "extension-%" PRIuS
;
72 const char kNumExtensionsCount
[] = "num-extensions";
74 const char kNumberOfViews
[] = "num-views";
76 const char kShutdownType
[] = "shutdown-type";
78 #if !defined(OS_ANDROID)
79 const char kGPUVendorID
[] = "gpu-venid";
80 const char kGPUDeviceID
[] = "gpu-devid";
82 const char kGPUDriverVersion
[] = "gpu-driver";
83 const char kGPUPixelShaderVersion
[] = "gpu-psver";
84 const char kGPUVertexShaderVersion
[] = "gpu-vsver";
85 #if defined(OS_MACOSX)
86 const char kGPUGLVersion
[] = "gpu-glver";
87 #elif defined(OS_POSIX)
88 const char kGPUVendor
[] = "gpu-gl-vendor";
89 const char kGPURenderer
[] = "gpu-gl-renderer";
92 const char kPrinterInfo
[] = "prn-info-%" PRIuS
;
94 #if defined(OS_CHROMEOS)
95 const char kNumberOfUsers
[] = "num-users";
98 #if defined(OS_MACOSX)
101 const char kFirstNSException
[] = "firstexception";
102 const char kFirstNSExceptionTrace
[] = "firstexception_bt";
104 const char kLastNSException
[] = "lastexception";
105 const char kLastNSExceptionTrace
[] = "lastexception_bt";
107 const char kNSException
[] = "nsexception";
108 const char kNSExceptionTrace
[] = "nsexception_bt";
110 const char kSendAction
[] = "sendaction";
112 const char kZombie
[] = "zombie";
113 const char kZombieTrace
[] = "zombie_dealloc_bt";
118 size_t RegisterChromeCrashKeys() {
119 // The following keys may be chunked by the underlying crash logging system,
120 // but ultimately constitute a single key-value pair.
121 base::debug::CrashKey fixed_keys
[] = {
122 { kClientID
, kSmallSize
},
123 { kChannel
, kSmallSize
},
124 { kActiveURL
, kLargeSize
},
125 { kNumSwitches
, kSmallSize
},
126 { kNumVariations
, kSmallSize
},
127 { kVariations
, kLargeSize
},
128 { kNumExtensionsCount
, kSmallSize
},
129 { kNumberOfViews
, kSmallSize
},
130 { kShutdownType
, kSmallSize
},
131 #if !defined(OS_ANDROID)
132 { kGPUVendorID
, kSmallSize
},
133 { kGPUDeviceID
, kSmallSize
},
135 { kGPUDriverVersion
, kSmallSize
},
136 { kGPUPixelShaderVersion
, kSmallSize
},
137 { kGPUVertexShaderVersion
, kSmallSize
},
138 #if defined(OS_MACOSX)
139 { kGPUGLVersion
, kSmallSize
},
140 #elif defined(OS_POSIX)
141 { kGPUVendor
, kSmallSize
},
142 { kGPURenderer
, kSmallSize
},
146 { "dm-usage", kSmallSize
},
148 { "ppapi_path", kMediumSize
},
149 { "subresource_url", kLargeSize
},
150 #if defined(OS_CHROMEOS)
151 { kNumberOfUsers
, kSmallSize
},
153 #if defined(OS_MACOSX)
154 { mac::kFirstNSException
, kMediumSize
},
155 { mac::kFirstNSExceptionTrace
, kMediumSize
},
156 { mac::kLastNSException
, kMediumSize
},
157 { mac::kLastNSExceptionTrace
, kMediumSize
},
158 { mac::kNSException
, kMediumSize
},
159 { mac::kNSExceptionTrace
, kMediumSize
},
160 { mac::kSendAction
, kMediumSize
},
161 { mac::kZombie
, kMediumSize
},
162 { mac::kZombieTrace
, kMediumSize
},
164 { "channel_error_bt", kMediumSize
},
165 { "remove_route_bt", kMediumSize
},
166 { "rwhvm_window", kMediumSize
},
168 { "VideoCaptureDeviceQTKit", kSmallSize
},
172 // This dynamic set of keys is used for sets of key value pairs when gathering
173 // a collection of data, like command line switches or extension IDs.
174 std::vector
<base::debug::CrashKey
> keys(
175 fixed_keys
, fixed_keys
+ arraysize(fixed_keys
));
177 // Register the switches.
179 // The fixed_keys names are string constants. Use static storage for
180 // formatted key names as well, since they will persist for the duration of
182 static char formatted_keys
[kSwitchesMaxCount
][sizeof(kSwitch
) + 1] =
184 const size_t formatted_key_len
= sizeof(formatted_keys
[0]);
185 for (size_t i
= 0; i
< kSwitchesMaxCount
; ++i
) {
186 // Name the keys using 1-based indexing.
187 int n
= base::snprintf(
188 formatted_keys
[i
], formatted_key_len
, kSwitch
, i
+ 1);
190 base::debug::CrashKey crash_key
= { formatted_keys
[i
], kSmallSize
};
191 keys
.push_back(crash_key
);
195 // Register the extension IDs.
197 static char formatted_keys
[kExtensionIDMaxCount
][sizeof(kExtensionID
) + 1] =
199 const size_t formatted_key_len
= sizeof(formatted_keys
[0]);
200 for (size_t i
= 0; i
< kExtensionIDMaxCount
; ++i
) {
201 int n
= base::snprintf(
202 formatted_keys
[i
], formatted_key_len
, kExtensionID
, i
+ 1);
204 base::debug::CrashKey crash_key
= { formatted_keys
[i
], kSmallSize
};
205 keys
.push_back(crash_key
);
209 // Register the printer info.
211 static char formatted_keys
[kPrinterInfoCount
][sizeof(kPrinterInfo
) + 1] =
213 const size_t formatted_key_len
= sizeof(formatted_keys
[0]);
214 for (size_t i
= 0; i
< kPrinterInfoCount
; ++i
) {
215 // Key names are 1-indexed.
216 int n
= base::snprintf(
217 formatted_keys
[i
], formatted_key_len
, kPrinterInfo
, i
+ 1);
219 base::debug::CrashKey crash_key
= { formatted_keys
[i
], kSmallSize
};
220 keys
.push_back(crash_key
);
224 return base::debug::InitCrashKeys(&keys
.at(0), keys
.size(),
228 void SetClientID(const std::string
& client_id
) {
229 std::string
guid(client_id
);
230 // Remove all instance of '-' char from the GUID. So BCD-WXY becomes BCDWXY.
231 ReplaceSubstringsAfterOffset(&guid
, 0, "-", "");
235 base::debug::SetCrashKeyValue(kClientID
, guid
);
236 GoogleUpdateSettings::SetMetricsId(guid
);
239 static bool IsBoringSwitch(const std::string
& flag
) {
241 return StartsWithASCII(flag
, "--channel=", true) ||
243 // No point to including this since we already have a ptype field.
244 StartsWithASCII(flag
, "--type=", true) ||
246 // Not particularly interesting
247 StartsWithASCII(flag
, "--flash-broker=", true) ||
249 // Just about everything has this, don't bother.
250 StartsWithASCII(flag
, "/prefetch:", true) ||
252 // We handle the plugin path separately since it is usually too big
253 // to fit in the switches (limited to 63 characters).
254 StartsWithASCII(flag
, "--plugin-path=", true) ||
256 // This is too big so we end up truncating it anyway.
257 StartsWithASCII(flag
, "--force-fieldtrials=", true) ||
259 // These surround the flags that were added by about:flags, it lets
260 // you distinguish which flags were added manually via the command
261 // line versus those added through about:flags. For the most part
262 // we don't care how an option was enabled, so we strip these.
263 // (If you need to know can always look at the PEB).
264 flag
== "--flag-switches-begin" ||
265 flag
== "--flag-switches-end";
266 #elif defined(OS_CHROMEOS)
267 static const char* kIgnoreSwitches
[] = {
268 ::switches::kEnableCompositingForFixedPosition
,
269 ::switches::kEnableImplSidePainting
,
270 ::switches::kEnableLogging
,
271 ::switches::kFlagSwitchesBegin
,
272 ::switches::kFlagSwitchesEnd
,
273 ::switches::kLoggingLevel
,
274 ::switches::kPpapiFlashArgs
,
275 ::switches::kPpapiFlashPath
,
276 ::switches::kRegisterPepperPlugins
,
277 ::switches::kUIPrioritizeInGpuProcess
,
279 ::switches::kUserDataDir
,
281 ::switches::kVModule
,
282 // Cros/CC flgas are specified as raw strings to avoid dependency.
283 "ash-default-wallpaper-large",
284 "ash-default-wallpaper-small",
285 "ash-guest-wallpaper-large",
286 "ash-guest-wallpaper-small",
287 "enterprise-enable-forced-re-enrollment",
288 "enterprise-enrollment-initial-modulus",
289 "enterprise-enrollment-modulus-limit",
292 "max-tiles-for-interest-area",
293 "max-unused-resource-memory-usage-percentage",
294 "termination-message-file",
297 if (!StartsWithASCII(flag
, "--", true))
299 std::size_t end
= flag
.find("=");
300 int len
= (end
== std::string::npos
) ? flag
.length() - 2 : end
- 2;
301 for (size_t i
= 0; i
< arraysize(kIgnoreSwitches
); ++i
) {
302 if (flag
.compare(2, len
, kIgnoreSwitches
[i
]) == 0)
311 void SetSwitchesFromCommandLine(const CommandLine
* command_line
) {
312 DCHECK(command_line
);
316 const CommandLine::StringVector
& argv
= command_line
->argv();
318 // Set the number of switches in case size > kNumSwitches.
319 base::debug::SetCrashKeyValue(kNumSwitches
,
320 base::StringPrintf("%" PRIuS
, argv
.size() - 1));
322 size_t key_i
= 1; // Key names are 1-indexed.
324 // Go through the argv, skipping the exec path.
325 for (size_t i
= 1; i
< argv
.size(); ++i
) {
327 std::string switch_str
= base::WideToUTF8(argv
[i
]);
329 std::string switch_str
= argv
[i
];
332 // Skip uninteresting switches.
333 if (IsBoringSwitch(switch_str
))
336 // Stop if there are too many switches.
337 if (i
> crash_keys::kSwitchesMaxCount
)
340 std::string key
= base::StringPrintf(kSwitch
, key_i
++);
341 base::debug::SetCrashKeyValue(key
, switch_str
);
344 // Clear any remaining switches.
345 for (; key_i
<= kSwitchesMaxCount
; ++key_i
) {
346 base::debug::ClearCrashKey(base::StringPrintf(kSwitch
, key_i
));
350 void SetVariationsList(const std::vector
<std::string
>& variations
) {
351 base::debug::SetCrashKeyValue(kNumVariations
,
352 base::StringPrintf("%" PRIuS
, variations
.size()));
354 std::string variations_string
;
355 variations_string
.reserve(kLargeSize
);
357 for (size_t i
= 0; i
< variations
.size(); ++i
) {
358 const std::string
& variation
= variations
[i
];
359 // Do not truncate an individual experiment.
360 if (variations_string
.size() + variation
.size() >= kLargeSize
)
362 variations_string
+= variation
;
363 variations_string
+= ",";
366 base::debug::SetCrashKeyValue(kVariations
, variations_string
);
369 void SetActiveExtensions(const std::set
<std::string
>& extensions
) {
370 base::debug::SetCrashKeyValue(kNumExtensionsCount
,
371 base::StringPrintf("%" PRIuS
, extensions
.size()));
373 std::set
<std::string
>::const_iterator it
= extensions
.begin();
374 for (size_t i
= 0; i
< kExtensionIDMaxCount
; ++i
) {
375 std::string key
= base::StringPrintf(kExtensionID
, i
+ 1);
376 if (it
== extensions
.end()) {
377 base::debug::ClearCrashKey(key
);
379 base::debug::SetCrashKeyValue(key
, *it
);
385 ScopedPrinterInfo::ScopedPrinterInfo(const base::StringPiece
& data
) {
386 std::vector
<std::string
> info
;
387 base::SplitString(data
.as_string(), ';', &info
);
388 for (size_t i
= 0; i
< kPrinterInfoCount
; ++i
) {
389 std::string key
= base::StringPrintf(kPrinterInfo
, i
+ 1);
393 base::debug::SetCrashKeyValue(key
, value
);
397 ScopedPrinterInfo::~ScopedPrinterInfo() {
398 for (size_t i
= 0; i
< kPrinterInfoCount
; ++i
) {
399 std::string key
= base::StringPrintf(kPrinterInfo
, i
+ 1);
400 base::debug::ClearCrashKey(key
);
404 } // namespace crash_keys