Skip to content

Commit

Permalink
Send a crash report when a hung process is detected.
Browse files Browse the repository at this point in the history
BUG=478209

Committed: https://crrev.com/8431e8f39edd5d7bff793f446035207fee6dacd1
Cr-Commit-Position: refs/heads/master@{#326559}

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

Cr-Commit-Position: refs/heads/master@{#326617}
  • Loading branch information
erikwright authored and Commit bot committed Apr 23, 2015
1 parent d6ac192 commit 25a81c2
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 9 deletions.
5 changes: 4 additions & 1 deletion chrome/app/client_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@ int MainDllLoader::Launch(HINSTANCE instance) {
if (!PathService::Get(chrome::DIR_WATCHER_DATA, &watcher_data_directory))
return chrome::RESULT_CODE_MISSING_DATA;

base::string16 channel_name = GoogleUpdateSettings::GetChromeChannel(
!InstallUtil::IsPerUserInstall(cmd_line.GetProgram()));

// Intentionally leaked.
HMODULE watcher_dll = Load(&version, &file);
if (!watcher_dll)
Expand All @@ -217,7 +220,7 @@ int MainDllLoader::Launch(HINSTANCE instance) {
return watcher_main(chrome::kBrowserExitCodesRegistryPath,
parent_process.Take(), on_initialized_event.Take(),
watcher_data_directory.value().c_str(),
message_window_name.c_str());
message_window_name.c_str(), channel_name.c_str());
}

// Initialize the sandbox services.
Expand Down
1 change: 1 addition & 0 deletions chrome/chrome_watcher/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ shared_library("chrome_watcher") {
deps = [
":chrome_watcher_resources",
":client",
"//chrome/installer/util",
"//base",
"//components/browser_watcher",
]
Expand Down
1 change: 1 addition & 0 deletions chrome/chrome_watcher/DEPS
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
include_rules = [
"+base",
"+chrome/installer/util",
"+components/browser_watcher",
"+syzygy/kasko/api",
]
1 change: 1 addition & 0 deletions chrome/chrome_watcher/chrome_watcher.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
'dependencies': [
'chrome_watcher_client',
'chrome_watcher_resources',
'installer_util',
'../base/base.gyp:base',
'../components/components.gyp:browser_watcher',
],
Expand Down
80 changes: 73 additions & 7 deletions chrome/chrome_watcher/chrome_watcher_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/file_version_info.h"
#include "base/files/file_path.h"
#include "base/logging_win.h"
#include "base/macros.h"
Expand All @@ -17,12 +18,17 @@
#include "base/process/process.h"
#include "base/run_loop.h"
#include "base/sequenced_task_runner.h"
#include "base/strings/string16.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/template_util.h"
#include "base/threading/thread.h"
#include "base/time/time.h"
#include "base/win/scoped_handle.h"
#include "chrome/chrome_watcher/chrome_watcher_main_api.h"
#include "chrome/installer/util/util_constants.h"
#include "components/browser_watcher/endsession_watcher_window_win.h"
#include "components/browser_watcher/exit_code_watcher_win.h"
#include "components/browser_watcher/exit_funnel_win.h"
Expand Down Expand Up @@ -201,6 +207,7 @@ void BrowserMonitor::BrowserExited() {
void OnWindowEvent(
const base::string16& registry_path,
base::Process process,
const base::Callback<void(const base::Process&)>& on_hung_callback,
browser_watcher::WindowHangMonitor::WindowEvent window_event) {
browser_watcher::ExitFunnel exit_funnel;
if (exit_funnel.Init(registry_path.c_str(), process.Handle())) {
Expand All @@ -210,6 +217,8 @@ void OnWindowEvent(
break;
case browser_watcher::WindowHangMonitor::WINDOW_HUNG:
exit_funnel.RecordEvent(L"MessageWindowHung");
if (!on_hung_callback.is_null())
on_hung_callback.Run(process);
break;
case browser_watcher::WindowHangMonitor::WINDOW_VANISHED:
exit_funnel.RecordEvent(L"MessageWindowVanished");
Expand All @@ -221,6 +230,53 @@ void OnWindowEvent(
}
}

#ifdef KASKO
void DumpHungBrowserProcess(const base::string16& channel,
const base::Process& process) {
// TODO(erikwright): Rather than recreating these crash keys here, it would be
// ideal to read them directly from the browser process.

// This is looking up the version of chrome_watcher.dll, which is equivalent
// for our purposes to chrome.dll.
scoped_ptr<FileVersionInfo> version_info(
FileVersionInfo::CreateFileVersionInfoForModule(
reinterpret_cast<HMODULE>(&__ImageBase)));
using CrashKeyStrings = std::pair<base::string16, base::string16>;
std::vector<CrashKeyStrings> crash_key_strings;
if (version_info.get()) {
crash_key_strings.push_back(
CrashKeyStrings(L"prod", version_info->product_short_name()));
base::string16 version = version_info->product_version();
if (!version_info->is_official_build())
version.append(base::ASCIIToUTF16("-devel"));
crash_key_strings.push_back(CrashKeyStrings(L"ver", version));
} else {
// No version info found. Make up the values.
crash_key_strings.push_back(CrashKeyStrings(L"prod", L"Chrome"));
crash_key_strings.push_back(CrashKeyStrings(L"ver", L"0.0.0.0-devel"));
}
crash_key_strings.push_back(CrashKeyStrings(L"channel", channel));
crash_key_strings.push_back(CrashKeyStrings(L"plat", L"Win32"));
crash_key_strings.push_back(CrashKeyStrings(L"ptype", L"browser"));
crash_key_strings.push_back(
CrashKeyStrings(L"pid", base::IntToString16(process.Pid())));
crash_key_strings.push_back(CrashKeyStrings(L"hung-process", L"1"));

std::vector<const base::char16*> key_buffers;
std::vector<const base::char16*> value_buffers;
for (auto& strings : crash_key_strings) {
key_buffers.push_back(strings.first.c_str());
value_buffers.push_back(strings.second.c_str());
}
key_buffers.push_back(nullptr);
value_buffers.push_back(nullptr);
// TODO(erikwright): Make the dump-type channel-dependent.
kasko::api::SendReportForProcess(process.Handle(),
kasko::api::LARGER_DUMP_TYPE,
key_buffers.data(), value_buffers.data());
}
#endif // KASKO

} // namespace

// The main entry point to the watcher, declared as extern "C" to avoid name
Expand All @@ -229,7 +285,8 @@ extern "C" int WatcherMain(const base::char16* registry_path,
HANDLE process_handle,
HANDLE on_initialized_event_handle,
const base::char16* browser_data_directory,
const base::char16* message_window_name) {
const base::char16* message_window_name,
const base::char16* channel_name) {
base::Process process(process_handle);
base::win::ScopedHandle on_initialized_event(on_initialized_event_handle);

Expand All @@ -244,6 +301,8 @@ extern "C" int WatcherMain(const base::char16* registry_path,
// chrome.exe in order to report its exit status.
::SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY);

base::Callback<void(const base::Process&)> on_hung_callback;

#ifdef KASKO
bool launched_kasko = kasko::api::InitializeReporter(
GetKaskoEndpoint(process.Pid()).c_str(),
Expand All @@ -256,6 +315,10 @@ extern "C" int WatcherMain(const base::char16* registry_path,
.Append(kPermanentlyFailedReportsSubdir)
.value()
.c_str());
if (launched_kasko &&
base::StringPiece16(channel_name) == installer::kChromeChannelCanary) {
on_hung_callback = base::Bind(&DumpHungBrowserProcess, channel_name);
}
#endif // KASKO

// Run a UI message loop on the main thread.
Expand All @@ -269,13 +332,16 @@ extern "C" int WatcherMain(const base::char16* registry_path,
return 1;
}

browser_watcher::WindowHangMonitor hang_monitor(
base::TimeDelta::FromSeconds(60), base::TimeDelta::FromSeconds(20),
base::Bind(&OnWindowEvent, registry_path,
base::Passed(process.Duplicate())));
hang_monitor.Initialize(process.Duplicate(), message_window_name);
{
// Scoped to force |hang_monitor| destruction before Kasko is shut down.
browser_watcher::WindowHangMonitor hang_monitor(
base::TimeDelta::FromSeconds(60), base::TimeDelta::FromSeconds(20),
base::Bind(&OnWindowEvent, registry_path,
base::Passed(process.Duplicate()), on_hung_callback));
hang_monitor.Initialize(process.Duplicate(), message_window_name);

run_loop.Run();
run_loop.Run();
}

#ifdef KASKO
if (launched_kasko)
Expand Down
5 changes: 4 additions & 1 deletion chrome/chrome_watcher/chrome_watcher_main_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,15 @@ extern const base::FilePath::CharType kPermanentlyFailedReportsSubdir[];
// reports. |on_initialized_event| will be signaled once the watcher process is
// fully initialized. Takes ownership of |parent_process| and
// |on_initialized_event|.
// |channel_name| is the current Chrome distribution channel (one of
// installer::kChromeChannelXXX).
typedef int (*ChromeWatcherMainFunction)(
const base::char16* registry_path,
HANDLE parent_process,
HANDLE on_initialized_event,
const base::char16* browser_data_directory,
const base::char16* message_window_name);
const base::char16* message_window_name,
const base::char16* channel_name);

// Returns an RPC endpoint name for the identified client process. This method
// may be invoked in both the client and the watcher process with the PID of the
Expand Down

0 comments on commit 25a81c2

Please sign in to comment.