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 // The maximum lengths specified by breakpad include the trailing NULL, so
31 // the actual length of the string is one less.
32 #if defined(OS_MACOSX)
33 static const size_t kSingleChunkLength
=
34 google_breakpad::SimpleStringDictionary::value_size
- 1;
36 static const size_t kSingleChunkLength
=
37 google_breakpad::CustomInfoEntry::kValueMaxLength
- 1;
39 static const size_t kSingleChunkLength
= 63;
42 // Guarantees for crash key sizes.
43 static_assert(kSmallSize
<= kSingleChunkLength
,
44 "crash key chunk size too small");
45 #if defined(OS_MACOSX)
46 static_assert(kMediumSize
<= kSingleChunkLength
,
47 "mac has medium size crash key chunks");
50 const char kActiveURL
[] = "url-chunk";
52 const char kFontKeyName
[] = "font_key_name";
54 const char kSwitch
[] = "switch-%" PRIuS
;
55 const char kNumSwitches
[] = "num-switches";
57 const char kExtensionID
[] = "extension-%" PRIuS
;
58 const char kNumExtensionsCount
[] = "num-extensions";
60 const char kShutdownType
[] = "shutdown-type";
62 #if !defined(OS_ANDROID)
63 const char kGPUVendorID
[] = "gpu-venid";
64 const char kGPUDeviceID
[] = "gpu-devid";
66 const char kGPUDriverVersion
[] = "gpu-driver";
67 const char kGPUPixelShaderVersion
[] = "gpu-psver";
68 const char kGPUVertexShaderVersion
[] = "gpu-vsver";
69 #if defined(OS_MACOSX)
70 const char kGPUGLVersion
[] = "gpu-glver";
71 #elif defined(OS_POSIX)
72 const char kGPUVendor
[] = "gpu-gl-vendor";
73 const char kGPURenderer
[] = "gpu-gl-renderer";
76 const char kPrinterInfo
[] = "prn-info-%" PRIuS
;
78 #if defined(OS_CHROMEOS)
79 const char kNumberOfUsers
[] = "num-users";
82 #if defined(OS_MACOSX)
85 const char kFirstNSException
[] = "firstexception";
86 const char kFirstNSExceptionTrace
[] = "firstexception_bt";
88 const char kLastNSException
[] = "lastexception";
89 const char kLastNSExceptionTrace
[] = "lastexception_bt";
91 const char kNSException
[] = "nsexception";
92 const char kNSExceptionTrace
[] = "nsexception_bt";
94 const char kSendAction
[] = "sendaction";
100 const char kKaskoGuid
[] = "kasko-guid";
101 const char kKaskoEquivalentGuid
[] = "kasko-equivalent-guid";
104 const char kViewCount
[] = "view-count";
106 size_t RegisterChromeCrashKeys() {
107 // The following keys may be chunked by the underlying crash logging system,
108 // but ultimately constitute a single key-value pair.
109 base::debug::CrashKey fixed_keys
[] = {
110 #if defined(OS_MACOSX)
111 { kMetricsClientId
, kSmallSize
},
113 { kClientId
, kSmallSize
},
115 { kChannel
, kSmallSize
},
116 { kActiveURL
, kLargeSize
},
117 { kNumSwitches
, kSmallSize
},
118 { kNumVariations
, kSmallSize
},
119 { kVariations
, kLargeSize
},
120 { kNumExtensionsCount
, kSmallSize
},
121 { kShutdownType
, kSmallSize
},
122 #if !defined(OS_ANDROID)
123 { kGPUVendorID
, kSmallSize
},
124 { kGPUDeviceID
, kSmallSize
},
126 { kGPUDriverVersion
, kSmallSize
},
127 { kGPUPixelShaderVersion
, kSmallSize
},
128 { kGPUVertexShaderVersion
, kSmallSize
},
129 #if defined(OS_MACOSX)
130 { kGPUGLVersion
, kSmallSize
},
131 #elif defined(OS_POSIX)
132 { kGPUVendor
, kSmallSize
},
133 { kGPURenderer
, kSmallSize
},
137 { "discardable-memory-allocated", kSmallSize
},
138 { "discardable-memory-free", kSmallSize
},
139 { kFontKeyName
, kSmallSize
},
140 { "ppapi_path", kMediumSize
},
141 { "subresource_url", kLargeSize
},
142 { "total-discardable-memory-allocated", kSmallSize
},
143 #if defined(OS_CHROMEOS)
144 { kNumberOfUsers
, kSmallSize
},
146 #if defined(OS_MACOSX)
147 { mac::kFirstNSException
, kMediumSize
},
148 { mac::kFirstNSExceptionTrace
, kMediumSize
},
149 { mac::kLastNSException
, kMediumSize
},
150 { mac::kLastNSExceptionTrace
, kMediumSize
},
151 { mac::kNSException
, kMediumSize
},
152 { mac::kNSExceptionTrace
, kMediumSize
},
153 { mac::kSendAction
, kMediumSize
},
154 { mac::kZombie
, kMediumSize
},
155 { mac::kZombieTrace
, kMediumSize
},
157 { "channel_error_bt", kMediumSize
},
158 { "remove_route_bt", kMediumSize
},
159 { "rwhvm_window", kMediumSize
},
161 { "VideoCaptureDeviceQTKit", kSmallSize
},
164 { kKaskoGuid
, kSmallSize
},
165 { kKaskoEquivalentGuid
, kSmallSize
},
167 { kBug464926CrashKey
, kSmallSize
},
168 { kViewCount
, kSmallSize
},
171 // This dynamic set of keys is used for sets of key value pairs when gathering
172 // a collection of data, like command line switches or extension IDs.
173 std::vector
<base::debug::CrashKey
> keys(
174 fixed_keys
, fixed_keys
+ arraysize(fixed_keys
));
176 // Register the switches.
178 // The fixed_keys names are string constants. Use static storage for
179 // formatted key names as well, since they will persist for the duration of
181 static char formatted_keys
[kSwitchesMaxCount
][sizeof(kSwitch
) + 1] =
183 const size_t formatted_key_len
= sizeof(formatted_keys
[0]);
184 for (size_t i
= 0; i
< kSwitchesMaxCount
; ++i
) {
185 // Name the keys using 1-based indexing.
186 int n
= base::snprintf(
187 formatted_keys
[i
], formatted_key_len
, kSwitch
, i
+ 1);
189 base::debug::CrashKey crash_key
= { formatted_keys
[i
], kSmallSize
};
190 keys
.push_back(crash_key
);
194 // Register the extension IDs.
196 static char formatted_keys
[kExtensionIDMaxCount
][sizeof(kExtensionID
) + 1] =
198 const size_t formatted_key_len
= sizeof(formatted_keys
[0]);
199 for (size_t i
= 0; i
< kExtensionIDMaxCount
; ++i
) {
200 int n
= base::snprintf(
201 formatted_keys
[i
], formatted_key_len
, kExtensionID
, i
+ 1);
203 base::debug::CrashKey crash_key
= { formatted_keys
[i
], kSmallSize
};
204 keys
.push_back(crash_key
);
208 // Register the printer info.
210 static char formatted_keys
[kPrinterInfoCount
][sizeof(kPrinterInfo
) + 1] =
212 const size_t formatted_key_len
= sizeof(formatted_keys
[0]);
213 for (size_t i
= 0; i
< kPrinterInfoCount
; ++i
) {
214 // Key names are 1-indexed.
215 int n
= base::snprintf(
216 formatted_keys
[i
], formatted_key_len
, kPrinterInfo
, i
+ 1);
218 base::debug::CrashKey crash_key
= { formatted_keys
[i
], kSmallSize
};
219 keys
.push_back(crash_key
);
223 return base::debug::InitCrashKeys(&keys
.at(0), keys
.size(),
227 static bool IsBoringSwitch(const std::string
& flag
) {
228 static const char* const kIgnoreSwitches
[] = {
229 switches::kEnableLogging
,
230 switches::kFlagSwitchesBegin
,
231 switches::kFlagSwitchesEnd
,
232 switches::kLoggingLevel
,
234 // This file is linked into both chrome.dll and chrome.exe. However //ipc
235 // is only in the .dll, so this needs to be a literal rather than the
237 "channel", // switches::kProcessChannelID
239 switches::kProcessChannelID
,
241 switches::kProcessType
,
245 switches::kForceFieldTrials
,
246 switches::kPluginPath
,
247 #elif defined(OS_MACOSX)
248 switches::kMetricsClientID
,
249 #elif defined(OS_CHROMEOS)
250 switches::kPpapiFlashArgs
,
251 switches::kPpapiFlashPath
,
252 switches::kRegisterPepperPlugins
,
253 switches::kUIPrioritizeInGpuProcess
,
255 switches::kUserDataDir
,
256 // Cros/CC flags are specified as raw strings to avoid dependency.
257 "child-wallpaper-large",
258 "child-wallpaper-small",
259 "default-wallpaper-large",
260 "default-wallpaper-small",
261 "guest-wallpaper-large",
262 "guest-wallpaper-small",
263 "enterprise-enable-forced-re-enrollment",
264 "enterprise-enrollment-initial-modulus",
265 "enterprise-enrollment-modulus-limit",
268 "max-unused-resource-memory-usage-percentage",
269 "termination-message-file",
275 // Just about everything has this, don't bother.
276 if (base::StartsWith(flag
, "/prefetch:", base::CompareCase::SENSITIVE
))
280 if (!base::StartsWith(flag
, "--", base::CompareCase::SENSITIVE
))
282 size_t end
= flag
.find("=");
283 size_t len
= (end
== std::string::npos
) ? flag
.length() - 2 : end
- 2;
284 for (size_t i
= 0; i
< arraysize(kIgnoreSwitches
); ++i
) {
285 if (flag
.compare(2, len
, kIgnoreSwitches
[i
]) == 0)
291 void SetSwitchesFromCommandLine(const base::CommandLine
* command_line
) {
292 DCHECK(command_line
);
296 const base::CommandLine::StringVector
& argv
= command_line
->argv();
298 // Set the number of switches in case size > kNumSwitches.
299 base::debug::SetCrashKeyValue(kNumSwitches
,
300 base::StringPrintf("%" PRIuS
, argv
.size() - 1));
302 size_t key_i
= 1; // Key names are 1-indexed.
304 // Go through the argv, skipping the exec path.
305 for (size_t i
= 1; i
< argv
.size(); ++i
) {
307 std::string switch_str
= base::WideToUTF8(argv
[i
]);
309 std::string switch_str
= argv
[i
];
312 // Skip uninteresting switches.
313 if (IsBoringSwitch(switch_str
))
316 // Stop if there are too many switches.
317 if (i
> crash_keys::kSwitchesMaxCount
)
320 std::string key
= base::StringPrintf(kSwitch
, key_i
++);
321 base::debug::SetCrashKeyValue(key
, switch_str
);
324 // Clear any remaining switches.
325 for (; key_i
<= kSwitchesMaxCount
; ++key_i
) {
326 base::debug::ClearCrashKey(base::StringPrintf(kSwitch
, key_i
));
330 void SetActiveExtensions(const std::set
<std::string
>& extensions
) {
331 base::debug::SetCrashKeyValue(kNumExtensionsCount
,
332 base::StringPrintf("%" PRIuS
, extensions
.size()));
334 std::set
<std::string
>::const_iterator it
= extensions
.begin();
335 for (size_t i
= 0; i
< kExtensionIDMaxCount
; ++i
) {
336 std::string key
= base::StringPrintf(kExtensionID
, i
+ 1);
337 if (it
== extensions
.end()) {
338 base::debug::ClearCrashKey(key
);
340 base::debug::SetCrashKeyValue(key
, *it
);
346 ScopedPrinterInfo::ScopedPrinterInfo(const base::StringPiece
& data
) {
347 std::vector
<std::string
> info
= base::SplitString(
348 data
.as_string(), ";", base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
349 for (size_t i
= 0; i
< kPrinterInfoCount
; ++i
) {
350 std::string key
= base::StringPrintf(kPrinterInfo
, i
+ 1);
354 base::debug::SetCrashKeyValue(key
, value
);
358 ScopedPrinterInfo::~ScopedPrinterInfo() {
359 for (size_t i
= 0; i
< kPrinterInfoCount
; ++i
) {
360 std::string key
= base::StringPrintf(kPrinterInfo
, i
+ 1);
361 base::debug::ClearCrashKey(key
);
365 } // namespace crash_keys