1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220
|
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/common/crash_keys.h"
#include <array>
#include <deque>
#include <string_view>
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/format_macros.h"
#include "base/no_destructor.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "chrome/common/chrome_switches.h"
#include "components/crash/core/common/crash_key.h"
#include "components/crash/core/common/crash_keys.h"
#include "components/webui/flags/flags_ui_switches.h"
#include "content/public/common/content_switches.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "components/crash/core/app/crash_switches.h"
#include "gpu/command_buffer/service/gpu_switches.h"
#include "ui/gl/gl_switches.h"
#endif
namespace crash_keys {
namespace {
constexpr std::string_view kStringAnnotationsSwitch = "string-annotations";
// A convenient wrapper around a crash key and its name.
//
// The CrashKey contract requires that CrashKeyStrings are never
// moved, copied, or deleted (see
// third_party/crashpad/crashpad/client/annotation.h); since this class holds
// a CrashKeyString, it likewise cannot be moved, copied, or deleted.
class CrashKeyWithName {
public:
explicit CrashKeyWithName(std::string name)
: name_(std::move(name)), crash_key_(name_.c_str()) {}
CrashKeyWithName(const CrashKeyWithName&) = delete;
CrashKeyWithName& operator=(const CrashKeyWithName&) = delete;
CrashKeyWithName(CrashKeyWithName&&) = delete;
CrashKeyWithName& operator=(CrashKeyWithName&&) = delete;
~CrashKeyWithName() = delete;
std::string_view Name() const { return name_; }
std::string_view Value() const { return crash_key_.value(); }
void Clear() { crash_key_.Clear(); }
void Set(std::string_view value) { crash_key_.Set(value); }
private:
std::string name_;
crash_reporter::CrashKeyString<64> crash_key_;
};
void SplitAndPopulateCrashKeys(std::deque<CrashKeyWithName>& crash_keys,
std::string_view comma_separated_feature_list,
std::string crash_key_name_prefix) {
// Crash keys are indestructable so we can not simply empty the deque.
// Instead we must keep the previous crash keys alive and clear their values.
for (CrashKeyWithName& crash_key : crash_keys)
crash_key.Clear();
auto features =
base::SplitString(comma_separated_feature_list, ",",
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (size_t i = 0; i < features.size(); i++) {
if (crash_keys.size() <= i) {
crash_keys.emplace_back(base::StringPrintf(
"%s-%" PRIuS, crash_key_name_prefix.c_str(), i + 1));
}
CrashKeyWithName& crash_key = crash_keys[i];
crash_key.Set(features[i]);
}
}
// --enable-features and --disable-features often contain a long list not
// fitting into 64 bytes, hiding important information when analysing crashes.
// Therefore they are separated out in a list of CrashKeys, one for each enabled
// or disabled feature.
// They are also excluded from the default "switches".
void HandleEnableDisableFeatures(const base::CommandLine& command_line) {
static base::NoDestructor<std::deque<CrashKeyWithName>>
enabled_features_crash_keys;
static base::NoDestructor<std::deque<CrashKeyWithName>>
disabled_features_crash_keys;
SplitAndPopulateCrashKeys(
*enabled_features_crash_keys,
command_line.GetSwitchValueASCII(switches::kEnableFeatures),
"commandline-enabled-feature");
SplitAndPopulateCrashKeys(
*disabled_features_crash_keys,
command_line.GetSwitchValueASCII(switches::kDisableFeatures),
"commandline-disabled-feature");
}
// Return true if we DON'T want to upload this flag to the crash server.
bool IsBoringSwitch(const std::string& flag) {
static const auto kIgnoreSwitches = std::to_array<std::string_view>({
kStringAnnotationsSwitch,
switches::kEnableLogging,
switches::kFlagSwitchesBegin,
switches::kFlagSwitchesEnd,
switches::kLoggingLevel,
switches::kProcessType,
switches::kV,
switches::kVModule,
// This is a serialized buffer which won't fit in the default 64 bytes
// anyways. Should be switches::kGpuPreferences but we run into linking
// errors on Windows if we try to use that directly.
"gpu-preferences",
switches::kEnableFeatures,
switches::kDisableFeatures,
#if BUILDFLAG(IS_MAC)
switches::kMetricsClientID,
#elif BUILDFLAG(IS_CHROMEOS)
// --crash-loop-before is a "boring" switch because it is redundant;
// crash_reporter separately informs the crash server if it is doing
// crash-loop handling.
crash_reporter::switches::kCrashLoopBefore,
switches::kRegisterPepperPlugins,
switches::kUseGL,
switches::kUserDataDir,
// Cros/CC flags are specified as raw strings to avoid dependency.
"child-wallpaper-large",
"child-wallpaper-small",
"default-wallpaper-large",
"default-wallpaper-small",
"guest-wallpaper-large",
"guest-wallpaper-small",
"enterprise-enable-forced-re-enrollment",
"enterprise-enable-forced-re-enrollment-on-flex",
"enterprise-enrollment-initial-modulus",
"enterprise-enrollment-modulus-limit",
"login-profile",
"login-user",
"use-cras",
#endif
});
#if BUILDFLAG(IS_WIN)
// Just about everything has this, don't bother.
if (base::StartsWith(flag, "/prefetch:", base::CompareCase::SENSITIVE))
return true;
#endif
if (!base::StartsWith(flag, "--", base::CompareCase::SENSITIVE))
return false;
size_t end = flag.find("=");
size_t len = (end == std::string::npos) ? flag.length() - 2 : end - 2;
for (size_t i = 0; i < std::size(kIgnoreSwitches); ++i) {
if (flag.compare(2, len, kIgnoreSwitches[i]) == 0)
return true;
}
return false;
}
std::deque<CrashKeyWithName>& GetCommandLineStringAnnotations() {
static base::NoDestructor<std::deque<CrashKeyWithName>>
command_line_string_annotations;
return *command_line_string_annotations;
}
void SetStringAnnotations(const base::CommandLine& command_line) {
// This is only meant to be used to pass annotations from the browser to
// children and not to be used on the browser command line.
if (!command_line.HasSwitch(switches::kProcessType)) {
return;
}
base::StringPairs annotations;
if (!base::SplitStringIntoKeyValuePairs(
command_line.GetSwitchValueASCII(kStringAnnotationsSwitch), '=', ',',
&annotations)) {
return;
}
for (const auto& [key, value] : annotations) {
GetCommandLineStringAnnotations().emplace_back(key).Set(value);
}
}
} // namespace
void AllocateCrashKeyInBrowserAndChildren(std::string_view key,
std::string_view value) {
GetCommandLineStringAnnotations().emplace_back(std::string(key)).Set(value);
}
void AppendStringAnnotationsCommandLineSwitch(base::CommandLine* command_line) {
std::string string_annotations;
for (const auto& crash_key : GetCommandLineStringAnnotations()) {
if (!string_annotations.empty()) {
string_annotations.push_back(',');
}
string_annotations = base::StrCat(
{string_annotations, crash_key.Name(), "=", crash_key.Value()});
}
if (string_annotations.empty()) {
return;
}
command_line->AppendSwitchASCII(kStringAnnotationsSwitch, string_annotations);
}
void SetCrashKeysFromCommandLine(const base::CommandLine& command_line) {
SetStringAnnotations(command_line);
HandleEnableDisableFeatures(command_line);
SetSwitchesFromCommandLine(command_line, &IsBoringSwitch);
}
} // namespace crash_keys
|