Commit caebd1b9 authored by siggi@chromium.org's avatar siggi@chromium.org

Move CrashKeysWin to a separate file and add first tests.

R=rsesek@chromium.org
BUG=378916

Review URL: https://codereview.chromium.org/327853002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@276854 0039d316-1c4b-4281-b951-d872f2087c98
parent 5b081a21
...@@ -3,42 +3,54 @@ ...@@ -3,42 +3,54 @@
# found in the LICENSE file. # found in the LICENSE file.
{ {
'target_defaults': {
'variables': {
'breakpad_component_target': 0,
},
'target_conditions': [
['breakpad_component_target==1', {
'defines': ['BREAKPAD_IMPLEMENTATION'],
'sources': [
'breakpad/app/breakpad_client.cc',
'breakpad/app/breakpad_client.h',
'breakpad/app/breakpad_linux.cc',
'breakpad/app/breakpad_linux.h',
'breakpad/app/breakpad_linux_impl.h',
'breakpad/app/breakpad_mac.h',
'breakpad/app/breakpad_mac.mm',
'breakpad/app/breakpad_win.cc',
'breakpad/app/breakpad_win.h',
'breakpad/app/hard_error_handler_win.cc',
'breakpad/app/hard_error_handler_win.h',
],
}],
],
},
'targets': [ 'targets': [
{ {
'target_name': 'breakpad_component_lib',
'type': 'static_library',
'sources': [
'breakpad/app/breakpad_client.cc',
'breakpad/app/breakpad_client.h',
'breakpad/app/crash_keys_win.cc',
'breakpad/app/crash_keys_win.h',
],
'include_dirs': [
'..',
'../breakpad/src',
],
},
{
'variables': {
'conditions': [
['OS == "ios" ', {
# On IOS there are no files compiled into the library, and we
# can't have libraries with zero objects.
'breakpad_component_target_type%': 'none',
}, {
'breakpad_component_target_type%': 'static_library',
}],
],
},
# Note: if you depend on this target, you need to either link in # Note: if you depend on this target, you need to either link in
# content.gyp:content_common, or add # content.gyp:content_common, or add
# content/public/common/content_switches.cc to your sources. # content/public/common/content_switches.cc to your sources.
'target_name': 'breakpad_component', 'target_name': 'breakpad_component',
'type': 'static_library', 'type': '<(breakpad_component_target_type)',
'variables': { 'sources': [
'breakpad_component_target': 1, 'breakpad/app/breakpad_linux.cc',
}, 'breakpad/app/breakpad_linux.h',
'breakpad/app/breakpad_linux_impl.h',
'breakpad/app/breakpad_mac.h',
'breakpad/app/breakpad_mac.mm',
'breakpad/app/breakpad_win.cc',
'breakpad/app/breakpad_win.h',
'breakpad/app/hard_error_handler_win.cc',
'breakpad/app/hard_error_handler_win.h',
],
'dependencies': [ 'dependencies': [
'breakpad_component_lib',
'../base/base.gyp:base', '../base/base.gyp:base',
], ],
'defines': ['BREAKPAD_IMPLEMENTATION'],
'conditions': [ 'conditions': [
['OS=="mac"', { ['OS=="mac"', {
'dependencies': [ 'dependencies': [
...@@ -71,6 +83,18 @@ ...@@ -71,6 +83,18 @@
}], }],
], ],
}, },
{
'target_name': 'breakpad_test_support',
'type': 'none',
'dependencies': [
'breakpad_component_lib',
],
'direct_dependent_settings': {
'include_dirs' : [
'../breakpad/src',
],
}
},
], ],
'conditions': [ 'conditions': [
['OS=="win"', { ['OS=="win"', {
...@@ -98,11 +122,25 @@ ...@@ -98,11 +122,25 @@
# content/public/common/content_switches.cc to your sources. # content/public/common/content_switches.cc to your sources.
'target_name': 'breakpad_win64', 'target_name': 'breakpad_win64',
'type': 'static_library', 'type': 'static_library',
'variables': { 'sources': [
'breakpad_component_target': 1, 'breakpad/app/breakpad_client.cc',
}, 'breakpad/app/breakpad_client.h',
'breakpad/app/breakpad_linux.cc',
'breakpad/app/breakpad_linux.h',
'breakpad/app/breakpad_linux_impl.h',
'breakpad/app/breakpad_mac.h',
'breakpad/app/breakpad_mac.mm',
'breakpad/app/breakpad_win.cc',
'breakpad/app/breakpad_win.h',
# TODO(siggi): test the x64 version too.
'breakpad/app/crash_keys_win.cc',
'breakpad/app/crash_keys_win.h',
'breakpad/app/hard_error_handler_win.cc',
'breakpad/app/hard_error_handler_win.h',
],
'defines': [ 'defines': [
'COMPILE_CONTENT_STATICALLY', 'COMPILE_CONTENT_STATICALLY',
'BREAKPAD_IMPLEMENTATION',
], ],
'dependencies': [ 'dependencies': [
'../base/base.gyp:base_win64', '../base/base.gyp:base_win64',
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include "base/win/win_util.h" #include "base/win/win_util.h"
#include "breakpad/src/client/windows/handler/exception_handler.h" #include "breakpad/src/client/windows/handler/exception_handler.h"
#include "components/breakpad/app/breakpad_client.h" #include "components/breakpad/app/breakpad_client.h"
#include "components/breakpad/app/crash_keys_win.h"
#include "components/breakpad/app/hard_error_handler_win.h" #include "components/breakpad/app/hard_error_handler_win.h"
#include "content/public/common/result_codes.h" #include "content/public/common/result_codes.h"
#include "sandbox/win/src/nt_internals.h" #include "sandbox/win/src/nt_internals.h"
...@@ -46,55 +47,6 @@ ...@@ -46,55 +47,6 @@
namespace breakpad { namespace breakpad {
// Manages the breakpad key/value pair stash, there may only be one instance
// of this class per process at one time.
class CrashKeysWin {
public:
CrashKeysWin();
~CrashKeysWin();
// May only be called once.
google_breakpad::CustomClientInfo*
GetCustomInfo(const std::wstring& exe_path, const std::wstring& type);
void SetCrashKeyValue(const std::wstring& key, const std::wstring& value);
void ClearCrashKeyValue(const std::wstring& key);
static CrashKeysWin* keeper() { return keeper_; }
private:
// One-time initialization of private key/value pairs.
void SetPluginPath(const std::wstring& path);
void SetBreakpadDumpPath();
// Must not be resized after GetCustomInfo is invoked.
std::vector<google_breakpad::CustomInfoEntry> custom_entries_;
typedef std::map<std::wstring, google_breakpad::CustomInfoEntry*>
DynamicEntriesMap;
base::Lock lock_;
// Keeps track of the next index for a new dynamic entry.
size_t dynamic_keys_offset_; // Under lock_.
// Maintains key->entry information for dynamic key/value entries
// in custom_entries_.
DynamicEntriesMap dynamic_entries_; // Under lock_.
// Stores the sole instance of this class allowed per process.
static CrashKeysWin* keeper_;
};
CrashKeysWin* CrashKeysWin::keeper_;
CrashKeysWin::CrashKeysWin() : dynamic_keys_offset_(0) {
DCHECK_EQ(static_cast<CrashKeysWin*>(NULL), keeper_);
keeper_ = this;
}
CrashKeysWin::~CrashKeysWin() {
DCHECK_EQ(this, keeper_);
keeper_ = NULL;
}
namespace { namespace {
// Minidump with stacks, PEB, TEB, and unloaded module list. // Minidump with stacks, PEB, TEB, and unloaded module list.
...@@ -134,14 +86,6 @@ typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle, ...@@ -134,14 +86,6 @@ typedef NTSTATUS (WINAPI* NtTerminateProcessPtr)(HANDLE ProcessHandle,
NTSTATUS ExitStatus); NTSTATUS ExitStatus);
char* g_real_terminate_process_stub = NULL; char* g_real_terminate_process_stub = NULL;
// Allow for 256 dynamic entries in addition to the fixed set of entries
// set up in this file.
const size_t kMaxDynamicEntries = 256;
// Maximum length for plugin path to include in plugin crash reports.
const size_t kMaxPluginPathLength = 256;
} // namespace } // namespace
// Dumps the current process memory. // Dumps the current process memory.
...@@ -201,43 +145,6 @@ InjectDumpForHangDebugging(HANDLE process) { ...@@ -201,43 +145,6 @@ InjectDumpForHangDebugging(HANDLE process) {
0, 0, NULL); 0, 0, NULL);
} }
// Appends the plugin path to |g_custom_entries|.
void CrashKeysWin::SetPluginPath(const std::wstring& path) {
if (path.size() > kMaxPluginPathLength) {
// If the path is too long, truncate from the start rather than the end,
// since we want to be able to recover the DLL name.
SetPluginPath(path.substr(path.size() - kMaxPluginPathLength));
return;
}
// The chunk size without terminator.
const size_t kChunkSize = static_cast<size_t>(
google_breakpad::CustomInfoEntry::kValueMaxLength - 1);
int chunk_index = 0;
size_t chunk_start = 0; // Current position inside |path|
for (chunk_start = 0; chunk_start < path.size(); chunk_index++) {
size_t chunk_length = std::min(kChunkSize, path.size() - chunk_start);
custom_entries_.push_back(google_breakpad::CustomInfoEntry(
base::StringPrintf(L"plugin-path-chunk-%i", chunk_index + 1).c_str(),
path.substr(chunk_start, chunk_length).c_str()));
chunk_start += chunk_length;
}
}
// Appends the breakpad dump path to |g_custom_entries|.
void CrashKeysWin::SetBreakpadDumpPath() {
base::FilePath crash_dumps_dir_path;
if (GetBreakpadClient()->GetAlternativeCrashDumpLocation(
&crash_dumps_dir_path)) {
custom_entries_.push_back(google_breakpad::CustomInfoEntry(
L"breakpad-dump-location", crash_dumps_dir_path.value().c_str()));
}
}
// Returns a string containing a list of all modifiers for the loaded profile. // Returns a string containing a list of all modifiers for the loaded profile.
std::wstring GetProfileType() { std::wstring GetProfileType() {
std::wstring profile_type; std::wstring profile_type;
...@@ -267,79 +174,6 @@ std::wstring GetProfileType() { ...@@ -267,79 +174,6 @@ std::wstring GetProfileType() {
return profile_type; return profile_type;
} }
// Returns the custom info structure based on the dll in parameter and the
// process type.
google_breakpad::CustomClientInfo*
CrashKeysWin::GetCustomInfo(const std::wstring& exe_path,
const std::wstring& type) {
base::string16 version, product;
base::string16 special_build;
base::string16 channel_name;
GetBreakpadClient()->GetProductNameAndVersion(
base::FilePath(exe_path),
&product,
&version,
&special_build,
&channel_name);
// We only expect this method to be called once per process.
// Common enties
custom_entries_.push_back(
google_breakpad::CustomInfoEntry(L"ver", version.c_str()));
custom_entries_.push_back(
google_breakpad::CustomInfoEntry(L"prod", product.c_str()));
custom_entries_.push_back(
google_breakpad::CustomInfoEntry(L"plat", L"Win32"));
custom_entries_.push_back(
google_breakpad::CustomInfoEntry(L"ptype", type.c_str()));
custom_entries_.push_back(google_breakpad::CustomInfoEntry(
L"pid", base::StringPrintf(L"%d", ::GetCurrentProcessId()).c_str()));
custom_entries_.push_back(google_breakpad::CustomInfoEntry(
L"channel", channel_name.c_str()));
custom_entries_.push_back(google_breakpad::CustomInfoEntry(
L"profile-type", GetProfileType().c_str()));
if (!special_build.empty()) {
custom_entries_.push_back(google_breakpad::CustomInfoEntry(
L"special", special_build.c_str()));
}
if (type == L"plugin" || type == L"ppapi") {
std::wstring plugin_path =
CommandLine::ForCurrentProcess()->GetSwitchValueNative("plugin-path");
if (!plugin_path.empty())
SetPluginPath(plugin_path);
}
// Check whether configuration management controls crash reporting.
bool crash_reporting_enabled = true;
bool controlled_by_policy = GetBreakpadClient()->ReportingIsEnforcedByPolicy(
&crash_reporting_enabled);
const CommandLine& command = *CommandLine::ForCurrentProcess();
bool use_crash_service =
!controlled_by_policy && (command.HasSwitch(switches::kNoErrorDialogs) ||
GetBreakpadClient()->IsRunningUnattended());
if (use_crash_service)
SetBreakpadDumpPath();
// Create space for dynamic ad-hoc keys. The names and values are set using
// the API defined in base/debug/crash_logging.h.
dynamic_keys_offset_ = custom_entries_.size();
for (size_t i = 0; i < kMaxDynamicEntries; ++i) {
// The names will be mutated as they are set. Un-numbered since these are
// merely placeholders. The name cannot be empty because Breakpad's
// HTTPUpload will interpret that as an invalid parameter.
custom_entries_.push_back(
google_breakpad::CustomInfoEntry(L"unspecified-crash-key", L""));
}
static google_breakpad::CustomClientInfo custom_client_info;
custom_client_info.entries = &custom_entries_.front();
custom_client_info.count = custom_entries_.size();
return &custom_client_info;
}
namespace { namespace {
// This callback is used when we want to get a dump without crashing the // This callback is used when we want to get a dump without crashing the
...@@ -433,35 +267,6 @@ long WINAPI ServiceExceptionFilter(EXCEPTION_POINTERS* info) { ...@@ -433,35 +267,6 @@ long WINAPI ServiceExceptionFilter(EXCEPTION_POINTERS* info) {
} // namespace } // namespace
void CrashKeysWin::SetCrashKeyValue(
const std::wstring& key, const std::wstring& value) {
// CustomInfoEntry limits the length of key and value. If they exceed
// their maximum length the underlying string handling functions raise
// an exception and prematurely trigger a crash. Truncate here.
std::wstring safe_key(std::wstring(key).substr(
0, google_breakpad::CustomInfoEntry::kNameMaxLength - 1));
std::wstring safe_value(std::wstring(value).substr(
0, google_breakpad::CustomInfoEntry::kValueMaxLength - 1));
// If we already have a value for this key, update it; otherwise, insert
// the new value if we have not exhausted the pre-allocated slots for dynamic
// entries.
base::AutoLock lock(lock_);
DynamicEntriesMap::iterator it = dynamic_entries_.find(safe_key);
google_breakpad::CustomInfoEntry* entry = NULL;
if (it == dynamic_entries_.end()) {
if (dynamic_entries_.size() >= kMaxDynamicEntries)
return;
entry = &custom_entries_[dynamic_keys_offset_++];
dynamic_entries_.insert(std::make_pair(safe_key, entry));
} else {
entry = it->second;
}
entry->set(safe_key.data(), safe_value.data());
}
// NOTE: This function is used by SyzyASAN to annotate crash reports. If you // NOTE: This function is used by SyzyASAN to annotate crash reports. If you
// change the name or signature of this function you will break SyzyASAN // change the name or signature of this function you will break SyzyASAN
// instrumented releases of Chrome. Please contact syzygy-team@chromium.org // instrumented releases of Chrome. Please contact syzygy-team@chromium.org
...@@ -477,17 +282,6 @@ extern "C" void __declspec(dllexport) __cdecl SetCrashKeyValueImpl( ...@@ -477,17 +282,6 @@ extern "C" void __declspec(dllexport) __cdecl SetCrashKeyValueImpl(
keeper->SetCrashKeyValue(key, value); keeper->SetCrashKeyValue(key, value);
} }
void CrashKeysWin::ClearCrashKeyValue(const std::wstring& key) {
base::AutoLock lock(lock_);
std::wstring key_string(key);
DynamicEntriesMap::iterator it = dynamic_entries_.find(key_string);
if (it == dynamic_entries_.end())
return;
it->second->set_value(NULL);
}
extern "C" void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl( extern "C" void __declspec(dllexport) __cdecl ClearCrashKeyValueImpl(
const wchar_t* key) { const wchar_t* key) {
CrashKeysWin* keeper = CrashKeysWin::keeper(); CrashKeysWin* keeper = CrashKeysWin::keeper();
...@@ -709,7 +503,9 @@ void InitCrashReporter(const std::string& process_type_switch) { ...@@ -709,7 +503,9 @@ void InitCrashReporter(const std::string& process_type_switch) {
CrashKeysWin* keeper = new CrashKeysWin(); CrashKeysWin* keeper = new CrashKeysWin();
google_breakpad::CustomClientInfo* custom_info = google_breakpad::CustomClientInfo* custom_info =
keeper->GetCustomInfo(exe_path, process_type); keeper->GetCustomInfo(exe_path, process_type,
GetProfileType(), CommandLine::ForCurrentProcess(),
GetBreakpadClient());
google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL; google_breakpad::ExceptionHandler::MinidumpCallback callback = NULL;
LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL; LPTOP_LEVEL_EXCEPTION_FILTER default_filter = NULL;
......
// Copyright 2014 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 "components/breakpad/app/crash_keys_win.h"
#include <algorithm>
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "components/breakpad/app/breakpad_client.h"
namespace breakpad {
namespace {
const size_t kMaxPluginPathLength = 256;
const size_t kMaxDynamicEntries = 256;
} // namespace
CrashKeysWin* CrashKeysWin::keeper_;
CrashKeysWin::CrashKeysWin() : dynamic_keys_offset_(0) {
DCHECK_EQ(static_cast<CrashKeysWin*>(NULL), keeper_);
keeper_ = this;
}
CrashKeysWin::~CrashKeysWin() {
DCHECK_EQ(this, keeper_);
keeper_ = NULL;
}
// Appends the plugin path to |g_custom_entries|.
void CrashKeysWin::SetPluginPath(const std::wstring& path) {
if (path.size() > kMaxPluginPathLength) {
// If the path is too long, truncate from the start rather than the end,
// since we want to be able to recover the DLL name.
SetPluginPath(path.substr(path.size() - kMaxPluginPathLength));
return;
}
// The chunk size without terminator.
const size_t kChunkSize = static_cast<size_t>(
google_breakpad::CustomInfoEntry::kValueMaxLength - 1);
int chunk_index = 0;
size_t chunk_start = 0; // Current position inside |path|
for (chunk_start = 0; chunk_start < path.size(); chunk_index++) {
size_t chunk_length = std::min(kChunkSize, path.size() - chunk_start);
custom_entries_.push_back(google_breakpad::CustomInfoEntry(
base::StringPrintf(L"plugin-path-chunk-%i", chunk_index + 1).c_str(),
path.substr(chunk_start, chunk_length).c_str()));
chunk_start += chunk_length;
}
}
// Appends the breakpad dump path to |g_custom_entries|.
void CrashKeysWin::SetBreakpadDumpPath(BreakpadClient* breakpad_client) {
base::FilePath crash_dumps_dir_path;
if (breakpad_client->GetAlternativeCrashDumpLocation(
&crash_dumps_dir_path)) {
custom_entries_.push_back(google_breakpad::CustomInfoEntry(
L"breakpad-dump-location", crash_dumps_dir_path.value().c_str()));
}
}
// Returns the custom info structure based on the dll in parameter and the
// process type.
google_breakpad::CustomClientInfo*
CrashKeysWin::GetCustomInfo(const std::wstring& exe_path,
const std::wstring& type,
const std::wstring& profile_type,
base::CommandLine* cmd_line,
BreakpadClient* breakpad_client) {
base::string16 version, product;
base::string16 special_build;
base::string16 channel_name;
breakpad_client->GetProductNameAndVersion(
base::FilePath(exe_path),
&product,
&version,
&special_build,
&channel_name);
// We only expect this method to be called once per process.
// Common enties
custom_entries_.push_back(
google_breakpad::CustomInfoEntry(L"ver",
base::UTF16ToWide(version).c_str()));
custom_entries_.push_back(
google_breakpad::CustomInfoEntry(L"prod",
base::UTF16ToWide(product).c_str()));
custom_entries_.push_back(
google_breakpad::CustomInfoEntry(L"plat", L"Win32"));
custom_entries_.push_back(
google_breakpad::CustomInfoEntry(L"ptype", type.c_str()));
custom_entries_.push_back(google_breakpad::CustomInfoEntry(
L"pid", base::StringPrintf(L"%d", ::GetCurrentProcessId()).c_str()));
custom_entries_.push_back(google_breakpad::CustomInfoEntry(
L"channel", base::UTF16ToWide(channel_name).c_str()));
custom_entries_.push_back(google_breakpad::CustomInfoEntry(
L"profile-type", profile_type.c_str()));
if (!special_build.empty())
custom_entries_.push_back(google_breakpad::CustomInfoEntry(
L"special", base::UTF16ToWide(special_build).c_str()));
if (type == L"plugin" || type == L"ppapi") {
std::wstring plugin_path = cmd_line->GetSwitchValueNative("plugin-path");
if (!plugin_path.empty())
SetPluginPath(plugin_path);
}
// Check whether configuration management controls crash reporting.
bool crash_reporting_enabled = true;
bool controlled_by_policy = breakpad_client->ReportingIsEnforcedByPolicy(
&crash_reporting_enabled);
bool use_crash_service = !controlled_by_policy &&
(cmd_line->HasSwitch(switches::kNoErrorDialogs) ||
breakpad_client->IsRunningUnattended());
if (use_crash_service)
SetBreakpadDumpPath(breakpad_client);
// Create space for dynamic ad-hoc keys. The names and values are set using
// the API defined in base/debug/crash_logging.h.
dynamic_keys_offset_ = custom_entries_.size();
for (size_t i = 0; i < kMaxDynamicEntries; ++i) {
// The names will be mutated as they are set. Un-numbered since these are
// merely placeholders. The name cannot be empty because Breakpad's
// HTTPUpload will interpret that as an invalid parameter.
custom_entries_.push_back(
google_breakpad::CustomInfoEntry(L"unspecified-crash-key", L""));
}
static google_breakpad::CustomClientInfo custom_client_info;
custom_client_info.entries = &custom_entries_.front();
custom_client_info.count = custom_entries_.size();
return &custom_client_info;
}
void CrashKeysWin::SetCrashKeyValue(
const std::wstring& key, const std::wstring& value) {
// CustomInfoEntry limits the length of key and value. If they exceed
// their maximum length the underlying string handling functions raise
// an exception and prematurely trigger a crash. Truncate here.
std::wstring safe_key(std::wstring(key).substr(
0, google_breakpad::CustomInfoEntry::kNameMaxLength - 1));
std::wstring safe_value(std::wstring(value).substr(
0, google_breakpad::CustomInfoEntry::kValueMaxLength - 1));
// If we already have a value for this key, update it; otherwise, insert
// the new value if we have not exhausted the pre-allocated slots for dynamic
// entries.
base::AutoLock lock(lock_);
DynamicEntriesMap::iterator it = dynamic_entries_.find(safe_key);
google_breakpad::CustomInfoEntry* entry = NULL;
if (it == dynamic_entries_.end()) {
if (dynamic_entries_.size() >= kMaxDynamicEntries)
return;
entry = &custom_entries_[dynamic_keys_offset_++];
dynamic_entries_.insert(std::make_pair(safe_key, entry));
} else {
entry = it->second;
}
entry->set(safe_key.data(), safe_value.data());
}
void CrashKeysWin::ClearCrashKeyValue(const std::wstring& key) {
base::AutoLock lock(lock_);
std::wstring key_string(key);
DynamicEntriesMap::iterator it = dynamic_entries_.find(key_string);
if (it == dynamic_entries_.end())
return;
it->second->set_value(NULL);
}
} // namespace breakpad
// Copyright 2014 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 <map>
#include <string>
#include <vector>
#include "base/macros.h"
#include "base/synchronization/lock.h"
#include "breakpad/src/client/windows/common/ipc_protocol.h"
#include "breakpad/src/client/windows/handler/exception_handler.h"
namespace base {
class CommandLine;
} // namespace base
namespace breakpad {
class BreakpadClient;
// Manages the breakpad key/value pair stash, there may only be one instance
// of this class per process at one time.
class CrashKeysWin {
public:
CrashKeysWin();
~CrashKeysWin();
// May only be called once.
// |exe_path| is the path to the executable running, which may be used
// to figure out whether this is a user or system install.
// |type| is the process type, or mode this process is running in e.g.
// something like "browser" or "renderer".
// |profile_type| is a string describing the kind of the user's Windows
// profile, e.g. "mandatory", or "roaming" or similar.
// |cmd_line| is the current process' command line consulted for explicit
// crash reporting flags.
// |breakpad_client| is consulted for crash reporting settings.
google_breakpad::CustomClientInfo* GetCustomInfo(
const std::wstring& exe_path,
const std::wstring& type,
const std::wstring& profile_type,
base::CommandLine* cmd_line,
BreakpadClient* breakpad_client);
void SetCrashKeyValue(const std::wstring& key, const std::wstring& value);
void ClearCrashKeyValue(const std::wstring& key);
static CrashKeysWin* keeper() { return keeper_; }
private:
// One-time initialization of private key/value pairs.
void SetPluginPath(const std::wstring& path);
void SetBreakpadDumpPath(BreakpadClient* breakpad_client);
// Must not be resized after GetCustomInfo is invoked.
std::vector<google_breakpad::CustomInfoEntry> custom_entries_;
typedef std::map<std::wstring, google_breakpad::CustomInfoEntry*>
DynamicEntriesMap;
base::Lock lock_;
// Keeps track of the next index for a new dynamic entry.
size_t dynamic_keys_offset_; // Under lock_.
// Maintains key->entry information for dynamic key/value entries
// in custom_entries_.
DynamicEntriesMap dynamic_entries_; // Under lock_.
// Stores the sole instance of this class allowed per process.
static CrashKeysWin* keeper_;
DISALLOW_COPY_AND_ASSIGN(CrashKeysWin);
};
} // namespace breakpad
// Copyright 2014 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 "components/breakpad/app/crash_keys_win.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/strings/stringprintf.h"
#include "components/breakpad/app/breakpad_client.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace breakpad {
using testing::_;
using testing::DoAll;
using testing::Return;
using testing::SetArgPointee;
class MockBreakpadClient : public BreakpadClient {
public:
MOCK_METHOD1(GetAlternativeCrashDumpLocation,
bool(base::FilePath* crash_dir));
MOCK_METHOD5(GetProductNameAndVersion, void(const base::FilePath& exe_path,
base::string16* product_name,
base::string16* version,
base::string16* special_build,
base::string16* channel_name));
MOCK_METHOD3(ShouldShowRestartDialog, bool(base::string16* title,
base::string16* message,
bool* is_rtl_locale));
MOCK_METHOD0(AboutToRestart, bool());
MOCK_METHOD1(GetDeferredUploadsSupported, bool(bool is_per_user_install));
MOCK_METHOD1(GetIsPerUserInstall, bool(const base::FilePath& exe_path));
MOCK_METHOD1(GetShouldDumpLargerDumps, bool(bool is_per_user_install));
MOCK_METHOD0(GetResultCodeRespawnFailed, int());
MOCK_METHOD0(InitBrowserCrashDumpsRegKey, void());
MOCK_METHOD1(RecordCrashDumpAttempt, void(bool is_real_crash));
MOCK_METHOD2(GetProductNameAndVersion, void(std::string* product_name,
std::string* version));
MOCK_METHOD0(GetReporterLogFilename, base::FilePath());
MOCK_METHOD1(GetCrashDumpLocation, bool(base::FilePath* crash_dir));
MOCK_METHOD0(RegisterCrashKeys, size_t());
MOCK_METHOD0(IsRunningUnattended, bool());
MOCK_METHOD0(GetCollectStatsConsent, bool());
MOCK_METHOD1(ReportingIsEnforcedByPolicy, bool(bool* breakpad_enabled));
MOCK_METHOD0(GetAndroidMinidumpDescriptor, int());
#if defined(OS_MACOSX)
MOCK_METHOD1(InstallAdditionalFilters, void(BreakpadRef breakpad));
#endif
MOCK_METHOD1(EnableBreakpadForProcess, bool(const std::string& process_type));
};
class CrashKeysWinTest : public testing::Test {
public:
size_t CountKeyValueOccurences(
const google_breakpad::CustomClientInfo* client_info,
const wchar_t* key, const wchar_t* value);
protected:
testing::StrictMock<MockBreakpadClient> breakpad_client_;
};
size_t CrashKeysWinTest::CountKeyValueOccurences(
const google_breakpad::CustomClientInfo* client_info,
const wchar_t* key, const wchar_t* value) {
size_t occurrences = 0;
for (size_t i = 0; i < client_info->count; ++i) {
if (wcscmp(client_info->entries[i].name, key) == 0 &&
wcscmp(client_info->entries[i].value, value) == 0) {
++occurrences;
}
}
return occurrences;
}
TEST_F(CrashKeysWinTest, RecordsSelf) {
ASSERT_EQ(static_cast<CrashKeysWin*>(NULL), CrashKeysWin::keeper());
{
CrashKeysWin crash_keys;
ASSERT_EQ(&crash_keys, CrashKeysWin::keeper());
}
ASSERT_EQ(static_cast<CrashKeysWin*>(NULL), CrashKeysWin::keeper());
}
// Tests the crash keys set up for the most common official build consumer
// scenario. No policy controls, not running unattended and no explicit
// switches.
TEST_F(CrashKeysWinTest, OfficialLikeKeys) {
CrashKeysWin crash_keys;
const base::FilePath kExePath(L"C:\\temp\\exe_path.exe");
// The exe path ought to get passed through to the breakpad client.
EXPECT_CALL(breakpad_client_, GetProductNameAndVersion(kExePath, _, _, _, _))
.WillRepeatedly(DoAll(
SetArgPointee<1>(L"SomeProdName"),
SetArgPointee<2>(L"1.2.3.4"),
SetArgPointee<3>(L""),
SetArgPointee<4>(L"-devm")));
EXPECT_CALL(breakpad_client_, GetAlternativeCrashDumpLocation(_))
.WillRepeatedly(DoAll(
SetArgPointee<0>(base::FilePath(L"C:\\temp")),
Return(false)));
EXPECT_CALL(breakpad_client_, ReportingIsEnforcedByPolicy(_))
.WillRepeatedly(Return(false));
EXPECT_CALL(breakpad_client_, IsRunningUnattended())
.WillRepeatedly(Return(false));
// Provide an empty command line.
base::CommandLine cmd_line(base::CommandLine::NO_PROGRAM);
google_breakpad::CustomClientInfo* info =
crash_keys.GetCustomInfo(kExePath.value(),
L"made_up_type",
L"temporary",
&cmd_line,
&breakpad_client_);
ASSERT_TRUE(info != NULL);
ASSERT_TRUE(info->entries != NULL);
// We expect 7 fixed keys and a "freeboard" of 256 keys for dynamic entries.
EXPECT_EQ(256U + 7U, info->count);
EXPECT_EQ(1, CountKeyValueOccurences(info, L"ver", L"1.2.3.4"));
EXPECT_EQ(1, CountKeyValueOccurences(info, L"prod", L"SomeProdName"));
EXPECT_EQ(1, CountKeyValueOccurences(info, L"plat", L"Win32"));
EXPECT_EQ(1, CountKeyValueOccurences(info, L"ptype", L"made_up_type"));
std::wstring pid_str(base::StringPrintf(L"%d", ::GetCurrentProcessId()));
EXPECT_EQ(1, CountKeyValueOccurences(info, L"pid", pid_str.c_str()));
EXPECT_EQ(1, CountKeyValueOccurences(info, L"channel", L"-devm"));
EXPECT_EQ(1, CountKeyValueOccurences(info, L"profile-type", L"temporary"));
EXPECT_EQ(256, CountKeyValueOccurences(info, L"unspecified-crash-key", L""));
}
} // namespace breakpad
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
'bookmarks/browser/bookmark_index_unittest.cc', 'bookmarks/browser/bookmark_index_unittest.cc',
'bookmarks/browser/bookmark_model_unittest.cc', 'bookmarks/browser/bookmark_model_unittest.cc',
'bookmarks/browser/bookmark_utils_unittest.cc', 'bookmarks/browser/bookmark_utils_unittest.cc',
'breakpad/app/crash_keys_win_unittest.cc',
'captive_portal/captive_portal_detector_unittest.cc', 'captive_portal/captive_portal_detector_unittest.cc',
'cloud_devices/common/cloud_devices_urls_unittest.cc', 'cloud_devices/common/cloud_devices_urls_unittest.cc',
'cloud_devices/common/printer_description_unittest.cc', 'cloud_devices/common/printer_description_unittest.cc',
...@@ -227,6 +228,9 @@ ...@@ -227,6 +228,9 @@
'components_strings.gyp:components_strings', 'components_strings.gyp:components_strings',
'../third_party/libphonenumber/libphonenumber.gyp:libphonenumber', '../third_party/libphonenumber/libphonenumber.gyp:libphonenumber',
# Dependencies of breakpad
'components.gyp:breakpad_test_support',
# Dependencies of bookmarks # Dependencies of bookmarks
'components.gyp:bookmarks_browser', 'components.gyp:bookmarks_browser',
'components.gyp:bookmarks_test_support', 'components.gyp:bookmarks_test_support',
...@@ -257,7 +261,7 @@ ...@@ -257,7 +261,7 @@
# Dependencies of feedback # Dependencies of feedback
'components.gyp:feedback_component', 'components.gyp:feedback_component',
# Dependencies of gcm # Dependencies of gcm
'components.gyp:gcm_driver', 'components.gyp:gcm_driver',
'components.gyp:gcm_driver_test_support', 'components.gyp:gcm_driver_test_support',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment