| // Copyright 2016 The Chromium Embedded Framework Authors. Portions copyright |
| // 2016 The Chromium Authors. All rights reserved. Use of this source code is |
| // governed by a BSD-style license that can be found in the LICENSE file. |
| |
| #include "libcef/common/crash_reporter_client.h" |
| |
| #include <utility> |
| |
| #if defined(OS_WIN) |
| #include <windows.h> |
| #endif |
| |
| #include "base/environment.h" |
| #include "base/logging.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string16.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_split.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "components/crash/core/common/crash_key.h" |
| #include "services/service_manager/embedder/switches.h" |
| #include "third_party/crashpad/crashpad/client/annotation.h" |
| |
| #if defined(OS_MACOSX) |
| #include "libcef/common/util_mac.h" |
| #endif |
| |
| #if defined(OS_POSIX) |
| // Don't use CommandLine, FilePath or PathService on Windows. FilePath has |
| // dependencies outside of kernel32, which is disallowed by chrome_elf. |
| // CommandLine and PathService depend on global state that will not be |
| // initialized at the time the CefCrashReporterClient object is created. |
| #include "base/command_line.h" |
| #include "base/environment.h" |
| #include "base/files/file_path.h" |
| #include "base/path_service.h" |
| #include "chrome/common/chrome_paths.h" |
| #endif |
| |
| #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| #include "content/public/common/content_switches.h" |
| #include "libcef/common/cef_crash_report_utils.h" |
| #endif |
| |
| #if defined(OS_WIN) |
| #include "base/debug/leak_annotations.h" |
| #include "chrome/install_static/install_util.h" |
| #include "components/crash/core/app/crashpad.h" |
| #endif |
| |
| namespace { |
| |
| #if defined(OS_WIN) |
| typedef base::string16 PathString; |
| const char kPathSep = '\\'; |
| #else |
| typedef std::string PathString; |
| #endif |
| |
| PathString GetCrashConfigPath() { |
| #if defined(OS_WIN) |
| // Start with the path to the running executable. |
| wchar_t module_path[MAX_PATH]; |
| if (GetModuleFileName(nullptr, module_path, MAX_PATH) == 0) |
| return PathString(); |
| |
| PathString config_path = module_path; |
| |
| // Remove the executable file name. |
| PathString::size_type last_backslash = |
| config_path.rfind(kPathSep, config_path.size()); |
| if (last_backslash != PathString::npos) |
| config_path.erase(last_backslash + 1); |
| |
| config_path += L"crash_reporter.cfg"; |
| return config_path; |
| #elif defined(OS_POSIX) |
| base::FilePath config_path; |
| |
| #if defined(OS_MACOSX) |
| // Start with the path to the main app Resources directory. May be empty if |
| // not running in an app bundle. |
| config_path = util_mac::GetMainResourcesDirectory(); |
| #endif |
| |
| if (config_path.empty()) { |
| // Start with the path to the running executable. |
| if (!base::PathService::Get(base::DIR_EXE, &config_path)) |
| return PathString(); |
| } |
| |
| return config_path.Append(FILE_PATH_LITERAL("crash_reporter.cfg")).value(); |
| #endif // defined(OS_POSIX) |
| } |
| |
| #if defined(OS_WIN) |
| |
| // On Windows, FAT32 and NTFS both limit filenames to a maximum of 255 |
| // characters. On POSIX systems, the typical filename length limit is 255 |
| // character units. HFS+'s limit is actually 255 Unicode characters using |
| // Apple's modification of Normalization Form D, but the differences aren't |
| // really worth dealing with here. |
| const unsigned maxFilenameLength = 255; |
| |
| const char kInvalidFileChars[] = "<>:\"/\\|?*"; |
| |
| bool isInvalidFileCharacter(unsigned char c) { |
| if (c < ' ' || c == 0x7F) |
| return true; |
| for (size_t i = 0; i < sizeof(kInvalidFileChars); ++i) { |
| if (c == kInvalidFileChars[i]) |
| return true; |
| } |
| return false; |
| } |
| |
| bool isAbsolutePath(const std::string& s) { |
| // Check for local paths (beginning with "c:\") and network paths |
| // (beginning with "\\"). |
| return s.length() > 2 && |
| ((isalpha(s[0]) && s[1] == ':' && s[2] == kPathSep) || |
| (s[0] == kPathSep && s[1] == kPathSep)); |
| } |
| |
| std::string extractAbsolutePathStart(std::string& s) { |
| if (!isAbsolutePath(s)) |
| return std::string(); |
| |
| std::string start; |
| if (s[0] == kPathSep) { |
| // Network path. |
| start = s.substr(0, 2); |
| s = s.substr(2); |
| } else { |
| // Local path. |
| start = s.substr(0, 3); |
| s = s.substr(3); |
| } |
| return start; |
| } |
| |
| std::string sanitizePathComponentPart(const std::string& s) { |
| if (s.empty()) |
| return std::string(); |
| |
| std::string result; |
| result.reserve(s.length()); |
| for (size_t i = 0; i < s.length(); ++i) { |
| if (!isInvalidFileCharacter(s[i])) |
| result.push_back(s[i]); |
| } |
| return result; |
| } |
| |
| std::string sanitizePathComponent(const std::string& s) { |
| std::string name, ext; |
| |
| // Separate name and extension, if any. |
| std::string::size_type pos = s.rfind('.'); |
| if (pos != std::string::npos) { |
| name = s.substr(0, pos); |
| ext = s.substr(pos + 1); |
| } else { |
| name = s; |
| } |
| |
| // Remove invalid characters. |
| name = sanitizePathComponentPart(name); |
| ext = sanitizePathComponentPart(ext); |
| |
| // Remove a ridiculously-long extension. |
| if (ext.length() >= maxFilenameLength) |
| ext = std::string(); |
| |
| // Truncate an overly-long filename, reserving one character for a dot. |
| std::string::size_type max_name_len = maxFilenameLength - ext.length() - 1; |
| if (name.length() > max_name_len) |
| name = name.substr(0, max_name_len); |
| |
| return ext.empty() ? name : name + "." + ext; |
| } |
| |
| std::string sanitizePath(const std::string& s) { |
| std::string path = s; |
| |
| // Extract the absolute path start component, if any (e.g. "c:\" on Windows). |
| std::string result = extractAbsolutePathStart(path); |
| result.reserve(s.length()); |
| |
| std::vector<std::string> parts = |
| base::SplitString(path, std::string() + kPathSep, base::KEEP_WHITESPACE, |
| base::SPLIT_WANT_NONEMPTY); |
| for (size_t i = 0; i < parts.size(); ++i) { |
| std::string part = parts[i]; |
| if (part != "." && part != "..") |
| part = sanitizePathComponent(part); |
| if (!result.empty() && result[result.length() - 1] != kPathSep) |
| result += kPathSep; |
| result += part; |
| } |
| |
| return result; |
| } |
| |
| std::string joinPath(const std::string& s1, const std::string& s2) { |
| if (s1.empty() && s2.empty()) |
| return std::string(); |
| if (s1.empty()) |
| return s2; |
| if (s2.empty()) |
| return s1; |
| |
| // Don't try to join absolute paths on Windows. |
| // Skip this check on POSIX where it's more difficult to differentiate. |
| if (isAbsolutePath(s2)) |
| return s2; |
| |
| std::string result = s1; |
| if (result[result.size() - 1] != kPathSep) |
| result += kPathSep; |
| if (s2[0] == kPathSep) |
| result += s2.substr(1); |
| else |
| result += s2; |
| return result; |
| } |
| |
| // This will only be non-nullptr in the chrome_elf address space. |
| CefCrashReporterClient* g_crash_reporter_client = nullptr; |
| |
| #endif // defined(OS_WIN) |
| |
| const char kKeyMapDelim = ','; |
| |
| std::string NormalizeCrashKey(const base::StringPiece& key) { |
| std::string str = key.as_string(); |
| std::replace(str.begin(), str.end(), kKeyMapDelim, '-'); |
| if (str.length() > crashpad::Annotation::kNameMaxLength) { |
| return str.substr(0, crashpad::Annotation::kNameMaxLength); |
| } |
| return str; |
| } |
| |
| void ParseURL(const std::string& value, std::string* url) { |
| if (value.find("http://") == 0 || value.find("https://") == 0) { |
| *url = value; |
| if (url->rfind('/') <= 8) { |
| // Make sure the URL includes a path component. Otherwise, crash |
| // upload will fail on older Windows versions due to |
| // https://crbug.com/826564. |
| *url += "/"; |
| } |
| } |
| } |
| |
| bool ParseBool(const std::string& value) { |
| return base::EqualsCaseInsensitiveASCII(value, "true") || value == "1"; |
| } |
| |
| int ParseZeroBasedInt(const std::string& value) { |
| int int_val; |
| if (base::StringToInt(value, &int_val) && int_val > 0) |
| return int_val; |
| return 0; |
| } |
| |
| } // namespace |
| |
| #if defined(OS_WIN) |
| |
| extern "C" { |
| |
| // Export functions from chrome_elf that are required by crash_reporting.cc |
| |
| int __declspec(dllexport) __cdecl SetCrashKeyValueImpl(const char* key, |
| size_t key_size, |
| const char* value, |
| size_t value_size) { |
| if (g_crash_reporter_client) { |
| return g_crash_reporter_client->SetCrashKeyValue( |
| base::StringPiece(key, key_size), base::StringPiece(value, value_size)); |
| } |
| return 0; |
| } |
| |
| int __declspec(dllexport) __cdecl IsCrashReportingEnabledImpl() { |
| return g_crash_reporter_client && |
| g_crash_reporter_client->HasCrashConfigFile(); |
| } |
| |
| } // extern "C" |
| |
| // The below functions were deleted from chrome/install_static/install_util.cc |
| // in https://crbug.com/565446#c17. |
| |
| constexpr wchar_t kUserDataDirname[] = L"User Data"; |
| |
| // Populates |result| with the default User Data directory for the current |
| // user.This may be overidden by a command line option. Returns false if all |
| // attempts at locating a User Data directory fail. |
| bool GetDefaultUserDataDirectory(std::wstring* result, |
| const std::wstring& install_sub_directory) { |
| // This environment variable should be set on Windows Vista and later |
| // (https://msdn.microsoft.com/library/windows/desktop/dd378457.aspx). |
| std::wstring user_data_dir = |
| install_static::GetEnvironmentString16(L"LOCALAPPDATA"); |
| |
| if (user_data_dir.empty()) { |
| // LOCALAPPDATA was not set; fallback to the temporary files path. |
| DWORD size = ::GetTempPath(0, nullptr); |
| if (!size) |
| return false; |
| user_data_dir.resize(size + 1); |
| size = ::GetTempPath(size + 1, &user_data_dir[0]); |
| if (!size || size >= user_data_dir.size()) |
| return false; |
| user_data_dir.resize(size); |
| } |
| |
| result->swap(user_data_dir); |
| if ((*result)[result->length() - 1] != L'\\') |
| result->push_back(L'\\'); |
| result->append(install_sub_directory); |
| result->push_back(L'\\'); |
| result->append(kUserDataDirname); |
| return true; |
| } |
| |
| // Populates |crash_dir| with the default crash dump location regardless of |
| // whether DIR_USER_DATA or DIR_CRASH_DUMPS has been overridden. |
| bool GetDefaultCrashDumpLocation(std::wstring* crash_dir, |
| const std::wstring& install_sub_directory) { |
| // In order to be able to start crash handling very early, we do not rely on |
| // chrome's PathService entries (for DIR_CRASH_DUMPS) being available on |
| // Windows. See https://crbug.com/564398. |
| if (!GetDefaultUserDataDirectory(crash_dir, install_sub_directory)) |
| return false; |
| |
| // We have to make sure the user data dir exists on first run. See |
| // http://crbug.com/591504. |
| if (!install_static::RecursiveDirectoryCreate(*crash_dir)) |
| return false; |
| crash_dir->append(L"\\Crashpad"); |
| return true; |
| } |
| |
| #endif // OS_WIN |
| |
| CefCrashReporterClient::CefCrashReporterClient() {} |
| CefCrashReporterClient::~CefCrashReporterClient() {} |
| |
| // Be aware that logging is not initialized at the time this method is called. |
| bool CefCrashReporterClient::ReadCrashConfigFile() { |
| if (has_crash_config_file_) |
| return true; |
| |
| PathString config_path = GetCrashConfigPath(); |
| if (config_path.empty()) |
| return false; |
| |
| #if defined(OS_WIN) |
| FILE* fp = _wfopen(config_path.c_str(), L"r"); |
| #else |
| FILE* fp = fopen(config_path.c_str(), "r"); |
| #endif |
| if (!fp) |
| return false; |
| |
| char line[1000]; |
| |
| size_t small_index = 0; |
| size_t medium_index = 0; |
| size_t large_index = 0; |
| std::string map_keys; |
| |
| enum section { |
| kNoSection, |
| kConfigSection, |
| kCrashKeysSection, |
| } current_section = kNoSection; |
| |
| while (fgets(line, sizeof(line) - 1, fp) != nullptr) { |
| std::string str = line; |
| base::TrimString(str, base::kWhitespaceASCII, &str); |
| if (str.empty() || str[0] == '#') |
| continue; |
| |
| if (str == "[Config]") { |
| current_section = kConfigSection; |
| continue; |
| } else if (str == "[CrashKeys]") { |
| current_section = kCrashKeysSection; |
| continue; |
| } else if (str[0] == '[') { |
| current_section = kNoSection; |
| continue; |
| } |
| |
| if (current_section == kNoSection) |
| continue; |
| |
| size_t div = str.find('='); |
| if (div == std::string::npos) |
| continue; |
| |
| std::string name_str = str.substr(0, div); |
| base::TrimString(name_str, base::kWhitespaceASCII, &name_str); |
| std::string val_str = str.substr(div + 1); |
| base::TrimString(val_str, base::kWhitespaceASCII, &val_str); |
| if (name_str.empty()) |
| continue; |
| |
| if (current_section == kConfigSection) { |
| if (name_str == "ServerURL") { |
| ParseURL(val_str, &server_url_); |
| } else if (name_str == "ProductName") { |
| product_name_ = val_str; |
| } else if (name_str == "ProductVersion") { |
| product_version_ = val_str; |
| } else if (name_str == "RateLimitEnabled") { |
| rate_limit_ = ParseBool(val_str); |
| } else if (name_str == "MaxUploadsPerDay") { |
| max_uploads_ = ParseZeroBasedInt(val_str); |
| } else if (name_str == "MaxDatabaseSizeInMb") { |
| max_db_size_ = ParseZeroBasedInt(val_str); |
| } else if (name_str == "MaxDatabaseAgeInDays") { |
| max_db_age_ = ParseZeroBasedInt(val_str); |
| } |
| #if defined(OS_WIN) |
| else if (name_str == "ExternalHandler") { |
| if (!val_str.empty()) |
| external_handler_ = sanitizePath(val_str); |
| } else if (name_str == "AppName") { |
| if (!val_str.empty()) { |
| val_str = sanitizePathComponent(val_str); |
| if (!val_str.empty()) |
| app_name_ = val_str; |
| } |
| } |
| #elif defined(OS_MACOSX) |
| else if (name_str == "BrowserCrashForwardingEnabled") { |
| enable_browser_crash_forwarding_ = ParseBool(val_str); |
| } |
| #endif |
| } else if (current_section == kCrashKeysSection) { |
| // Skip duplicate definitions. |
| if (!crash_keys_.empty() && |
| crash_keys_.find(name_str) != crash_keys_.end()) { |
| continue; |
| } |
| |
| KeySize size; |
| size_t index; |
| char group; |
| if (val_str == "small") { |
| size = SMALL_SIZE; |
| index = small_index++; |
| group = 'S'; |
| } else if (val_str == "medium") { |
| size = MEDIUM_SIZE; |
| index = medium_index++; |
| group = 'M'; |
| } else if (val_str == "large") { |
| size = LARGE_SIZE; |
| index = large_index++; |
| group = 'L'; |
| } else { |
| continue; |
| } |
| |
| name_str = NormalizeCrashKey(name_str); |
| crash_keys_.insert(std::make_pair(name_str, std::make_pair(size, index))); |
| |
| const std::string& key = |
| std::string(1, group) + "-" + std::string(1, 'A' + index); |
| if (!map_keys.empty()) { |
| map_keys.append(std::string(1, kKeyMapDelim)); |
| } |
| map_keys.append(key + "=" + name_str); |
| } |
| } |
| |
| fclose(fp); |
| |
| if (!map_keys.empty()) { |
| // Split |map_keys| across multiple Annotations if necessary. |
| // Must match the logic in crash_report_utils::FilterParameters. |
| using IDKey = |
| crash_reporter::CrashKeyString<crashpad::Annotation::kValueMaxSize>; |
| static IDKey ids[] = { |
| {"K-A", IDKey::Tag::kArray}, |
| {"K-B", IDKey::Tag::kArray}, |
| {"K-C", IDKey::Tag::kArray}, |
| }; |
| |
| // Make sure we can fit all possible name/value pairs. |
| static_assert(base::size(ids) * crashpad::Annotation::kValueMaxSize >= |
| 3 * 26 /* sizes (small, medium, large) * slots (A to Z) */ |
| * (3 + 2 /* key size ("S-A") + delim size ("=,") */ |
| + crashpad::Annotation::kNameMaxLength), |
| "Not enough storage for key map"); |
| |
| size_t offset = 0; |
| for (size_t i = 0; i < base::size(ids); ++i) { |
| size_t length = std::min(map_keys.size() - offset, |
| crashpad::Annotation::kValueMaxSize); |
| ids[i].Set(base::StringPiece(map_keys.data() + offset, length)); |
| offset += length; |
| if (offset >= map_keys.size()) |
| break; |
| } |
| } |
| |
| // Allow override of some values via environment variables. |
| { |
| std::unique_ptr<base::Environment> env(base::Environment::Create()); |
| std::string val_str; |
| |
| if (env->GetVar("CEF_CRASH_REPORTER_SERVER_URL", &val_str)) { |
| ParseURL(val_str, &server_url_); |
| } |
| if (env->GetVar("CEF_CRASH_REPORTER_RATE_LIMIT_ENABLED", &val_str)) { |
| rate_limit_ = ParseBool(val_str); |
| } |
| } |
| |
| has_crash_config_file_ = true; |
| return true; |
| } |
| |
| bool CefCrashReporterClient::HasCrashConfigFile() const { |
| return has_crash_config_file_; |
| } |
| |
| #if defined(OS_WIN) |
| |
| // static |
| void CefCrashReporterClient::InitializeCrashReportingForProcess() { |
| if (g_crash_reporter_client) |
| return; |
| |
| g_crash_reporter_client = new CefCrashReporterClient(); |
| ANNOTATE_LEAKING_OBJECT_PTR(g_crash_reporter_client); |
| |
| if (!g_crash_reporter_client->ReadCrashConfigFile()) |
| return; |
| |
| std::wstring process_type = install_static::GetSwitchValueFromCommandLine( |
| ::GetCommandLineW(), install_static::kProcessType); |
| if (process_type != install_static::kCrashpadHandler) { |
| crash_reporter::SetCrashReporterClient(g_crash_reporter_client); |
| |
| // If |embedded_handler| is true then we launch another instance of the main |
| // executable as the crashpad-handler process. |
| const bool embedded_handler = |
| !g_crash_reporter_client->HasCrashExternalHandler(); |
| if (embedded_handler) { |
| crash_reporter::InitializeCrashpadWithEmbeddedHandler( |
| process_type.empty(), install_static::UTF16ToUTF8(process_type), |
| std::string(), base::FilePath()); |
| } else { |
| crash_reporter::InitializeCrashpad( |
| process_type.empty(), install_static::UTF16ToUTF8(process_type)); |
| } |
| } |
| } |
| |
| bool CefCrashReporterClient::GetAlternativeCrashDumpLocation( |
| base::string16* crash_dir) { |
| // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate |
| // location to write breakpad crash dumps can be set. |
| *crash_dir = |
| install_static::GetEnvironmentString16(L"BREAKPAD_DUMP_LOCATION"); |
| return !crash_dir->empty(); |
| } |
| |
| void CefCrashReporterClient::GetProductNameAndVersion( |
| const base::string16& exe_path, |
| base::string16* product_name, |
| base::string16* version, |
| base::string16* special_build, |
| base::string16* channel_name) { |
| *product_name = base::ASCIIToUTF16(product_name_); |
| *version = base::ASCIIToUTF16(product_version_); |
| *special_build = base::string16(); |
| *channel_name = base::string16(); |
| } |
| |
| bool CefCrashReporterClient::GetCrashDumpLocation(base::string16* crash_dir) { |
| // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate |
| // location to write breakpad crash dumps can be set. |
| if (GetAlternativeCrashDumpLocation(crash_dir)) |
| return true; |
| |
| return GetDefaultCrashDumpLocation(crash_dir, base::UTF8ToUTF16(app_name_)); |
| } |
| |
| bool CefCrashReporterClient::GetCrashMetricsLocation( |
| base::string16* metrics_dir) { |
| return GetDefaultUserDataDirectory(metrics_dir, base::UTF8ToUTF16(app_name_)); |
| } |
| |
| #elif defined(OS_POSIX) |
| |
| void CefCrashReporterClient::GetProductNameAndVersion(const char** product_name, |
| const char** version) { |
| *product_name = product_name_.c_str(); |
| *version = product_version_.c_str(); |
| } |
| |
| void CefCrashReporterClient::GetProductNameAndVersion(std::string* product_name, |
| std::string* version, |
| std::string* channel) { |
| *product_name = product_name_; |
| *version = product_version_; |
| } |
| |
| #if !defined(OS_MACOSX) |
| |
| base::FilePath CefCrashReporterClient::GetReporterLogFilename() { |
| return base::FilePath(FILE_PATH_LITERAL("uploads.log")); |
| } |
| |
| bool CefCrashReporterClient::EnableBreakpadForProcess( |
| const std::string& process_type) { |
| return process_type == switches::kRendererProcess || |
| process_type == switches::kPpapiPluginProcess || |
| process_type == service_manager::switches::kZygoteProcess || |
| process_type == switches::kGpuProcess; |
| } |
| |
| #endif // !defined(OS_MACOSX) |
| |
| bool CefCrashReporterClient::GetCrashDumpLocation(base::FilePath* crash_dir) { |
| // By setting the BREAKPAD_DUMP_LOCATION environment variable, an alternate |
| // location to write breakpad crash dumps can be set. |
| std::unique_ptr<base::Environment> env(base::Environment::Create()); |
| std::string alternate_crash_dump_location; |
| if (env->GetVar("BREAKPAD_DUMP_LOCATION", &alternate_crash_dump_location)) { |
| base::FilePath crash_dumps_dir_path = |
| base::FilePath::FromUTF8Unsafe(alternate_crash_dump_location); |
| base::PathService::Override(chrome::DIR_CRASH_DUMPS, crash_dumps_dir_path); |
| } |
| return base::PathService::Get(chrome::DIR_CRASH_DUMPS, crash_dir); |
| } |
| |
| #endif // !defined(OS_POSIX) |
| |
| bool CefCrashReporterClient::GetCollectStatsConsent() { |
| return true; |
| } |
| |
| bool CefCrashReporterClient::GetCollectStatsInSample() { |
| return true; |
| } |
| |
| #if defined(OS_WIN) || defined(OS_MACOSX) |
| bool CefCrashReporterClient::ReportingIsEnforcedByPolicy( |
| bool* crashpad_enabled) { |
| *crashpad_enabled = true; |
| return true; |
| } |
| #endif |
| |
| #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| bool CefCrashReporterClient::IsRunningUnattended() { |
| // Crash upload will only be enabled with Breakpad on Linux if this method |
| // returns false. |
| return false; |
| } |
| #endif |
| |
| std::string CefCrashReporterClient::GetCrashServerURL() { |
| return server_url_; |
| } |
| |
| // See HandlerMain() in third_party/crashpad/crashpad/handler/handler_main.cc |
| // for supported arguments. |
| void CefCrashReporterClient::GetCrashOptionalArguments( |
| std::vector<std::string>* arguments) { |
| if (!rate_limit_) |
| arguments->push_back(std::string("--no-rate-limit")); |
| |
| if (max_uploads_ > 0) { |
| arguments->push_back(std::string("--max-uploads=") + |
| base::NumberToString(max_uploads_)); |
| } |
| |
| if (max_db_size_ > 0) { |
| arguments->push_back(std::string("--max-db-size=") + |
| base::NumberToString(max_db_size_)); |
| } |
| |
| if (max_db_age_ > 0) { |
| arguments->push_back(std::string("--max-db-age=") + |
| base::NumberToString(max_db_age_)); |
| } |
| } |
| |
| #if defined(OS_WIN) |
| |
| base::string16 CefCrashReporterClient::GetCrashExternalHandler( |
| const base::string16& exe_dir) { |
| if (external_handler_.empty()) |
| return CrashReporterClient::GetCrashExternalHandler(exe_dir); |
| if (isAbsolutePath(external_handler_)) |
| return base::UTF8ToUTF16(external_handler_); |
| return base::UTF8ToWide( |
| joinPath(base::UTF16ToUTF8(exe_dir), external_handler_)); |
| } |
| |
| bool CefCrashReporterClient::HasCrashExternalHandler() const { |
| return !external_handler_.empty(); |
| } |
| |
| #endif // defined(OS_WIN) |
| |
| #if defined(OS_MACOSX) |
| bool CefCrashReporterClient::EnableBrowserCrashForwarding() { |
| return enable_browser_crash_forwarding_; |
| } |
| #endif |
| |
| #if defined(OS_POSIX) && !defined(OS_MACOSX) |
| CefCrashReporterClient::ParameterMap CefCrashReporterClient::FilterParameters( |
| const ParameterMap& parameters) { |
| return crash_report_utils::FilterParameters(parameters); |
| } |
| #endif |
| |
| // The new Crashpad Annotation API requires that annotations be declared using |
| // static storage. We work around this limitation by defining a fixed amount of |
| // storage for each key size and later substituting the actual key name during |
| // crash dump processing. |
| |
| #define IDKEY(name) \ |
| { name, IDKey::Tag::kArray } |
| |
| #define IDKEY_ENTRIES(n) \ |
| IDKEY(n "-A"), IDKEY(n "-B"), IDKEY(n "-C"), IDKEY(n "-D"), IDKEY(n "-E"), \ |
| IDKEY(n "-F"), IDKEY(n "-G"), IDKEY(n "-H"), IDKEY(n "-I"), \ |
| IDKEY(n "-J"), IDKEY(n "-K"), IDKEY(n "-L"), IDKEY(n "-M"), \ |
| IDKEY(n "-N"), IDKEY(n "-O"), IDKEY(n "-P"), IDKEY(n "-Q"), \ |
| IDKEY(n "-R"), IDKEY(n "-S"), IDKEY(n "-T"), IDKEY(n "-U"), \ |
| IDKEY(n "-V"), IDKEY(n "-W"), IDKEY(n "-X"), IDKEY(n "-Y"), \ |
| IDKEY(n "-Z") |
| |
| #define IDKEY_FUNCTION(name, size_) \ |
| static_assert(size_ <= crashpad::Annotation::kValueMaxSize, \ |
| "Annotation size is too large."); \ |
| bool Set##name##Annotation(size_t index, const base::StringPiece& value) { \ |
| using IDKey = crash_reporter::CrashKeyString<size_>; \ |
| static IDKey ids[] = {IDKEY_ENTRIES(#name)}; \ |
| if (index < base::size(ids)) { \ |
| if (value.empty()) { \ |
| ids[index].Clear(); \ |
| } else { \ |
| ids[index].Set(value); \ |
| } \ |
| return true; \ |
| } \ |
| return false; \ |
| } |
| |
| // The first argument must be kept synchronized with the logic in |
| // CefCrashReporterClient::ReadCrashConfigFile and |
| // crash_report_utils::FilterParameters. |
| IDKEY_FUNCTION(S, 64) |
| IDKEY_FUNCTION(M, 256) |
| IDKEY_FUNCTION(L, 1024) |
| |
| bool CefCrashReporterClient::SetCrashKeyValue(const base::StringPiece& key, |
| const base::StringPiece& value) { |
| if (key.empty() || crash_keys_.empty()) |
| return false; |
| |
| KeyMap::const_iterator it = crash_keys_.find(NormalizeCrashKey(key)); |
| if (it == crash_keys_.end()) |
| return false; |
| |
| const KeySize size = it->second.first; |
| const size_t index = it->second.second; |
| |
| base::AutoLock lock_scope(crash_key_lock_); |
| |
| switch (size) { |
| case SMALL_SIZE: |
| return SetSAnnotation(index, value); |
| case MEDIUM_SIZE: |
| return SetMAnnotation(index, value); |
| case LARGE_SIZE: |
| return SetLAnnotation(index, value); |
| } |
| |
| return false; |
| } |