Skip to content

Commit

Permalink
handler: Add user extensibility stream call-out.
Browse files Browse the repository at this point in the history
Bug: crashpad:167
Test: Add crashpad_handler_test.
Change-Id: I79b0b71dc4f61e6dce6bc10083e2f924dc83c940
Reviewed-on: https://chromium-review.googlesource.com/463746
Commit-Queue: Sigurður Ásgeirsson <[email protected]>
Reviewed-by: Mark Mentovai <[email protected]>
  • Loading branch information
sigurasg authored and Commit Bot committed Apr 11, 2017
1 parent a5d8137 commit 30385d4
Show file tree
Hide file tree
Showing 26 changed files with 682 additions and 82 deletions.
5 changes: 5 additions & 0 deletions build/run_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ def main(args):
'crashpad_test_test',
'crashpad_util_test',
]

if sys.platform == 'win32':
tests.append('crashpad_handler_test')
tests = sorted(tests)

for test in tests:
print '-' * 80
print test
Expand Down
1 change: 1 addition & 0 deletions crashpad.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
'client/client_test.gyp:*',
'compat/compat.gyp:*',
'handler/handler.gyp:*',
'handler/handler_test.gyp:*',
'minidump/minidump.gyp:*',
'minidump/minidump_test.gyp:*',
'snapshot/snapshot.gyp:*',
Expand Down
132 changes: 132 additions & 0 deletions handler/crashpad_handler_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
// Copyright 2017 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <windows.h>
#include <dbghelp.h>
#include <string.h>

#include <map>
#include <string>
#include <vector>

#include "base/files/file_path.h"
#include "client/crash_report_database.h"
#include "client/crashpad_client.h"
#include "gtest/gtest.h"
#include "minidump/test/minidump_file_writer_test_util.h"
#include "test/test_paths.h"
#include "test/win/win_multiprocess_with_temp_dir.h"
#include "util/file/file_reader.h"

namespace crashpad {
namespace test {
namespace {

void StartAndCrashWithExtendedHandler(const base::FilePath& temp_dir) {
base::FilePath handler_path = TestPaths::Executable().DirName().Append(
FILE_PATH_LITERAL("crashpad_handler_test_extended_handler.exe"));

CrashpadClient client;
ASSERT_TRUE(client.StartHandler(handler_path,
temp_dir,
base::FilePath(),
"",
std::map<std::string, std::string>(),
std::vector<std::string>(),
false,
false));

__debugbreak();
}

class CrashWithExtendedHandler final : public WinMultiprocessWithTempDir {
public:
CrashWithExtendedHandler() : WinMultiprocessWithTempDir() {}
~CrashWithExtendedHandler() {}

private:
void ValidateGeneratedDump();

void WinMultiprocessParent() override {
SetExpectedChildExitCode(EXCEPTION_BREAKPOINT);
}

void WinMultiprocessChild() override {
StartAndCrashWithExtendedHandler(GetTempDirPath());
}

void WinMultiprocessParentAfterChild(HANDLE child) override {
// At this point the child has exited, which means the crash report should
// have been written.
ValidateGeneratedDump();

// Delegate the cleanup to the superclass.
WinMultiprocessWithTempDir::WinMultiprocessParentAfterChild(child);
}
};

void CrashWithExtendedHandler::ValidateGeneratedDump() {
// Open the database and find the sole dump that should have been created.
std::unique_ptr<CrashReportDatabase> database(
CrashReportDatabase::Initialize(GetTempDirPath()));
ASSERT_TRUE(database);

std::vector<CrashReportDatabase::Report> reports;
ASSERT_EQ(database->GetCompletedReports(&reports),
CrashReportDatabase::kNoError);
ASSERT_EQ(reports.size(), 1u);

// Open the dump and validate that it has the extension stream with the
// expected contents.
FileReader reader;
ASSERT_TRUE(reader.Open(reports[0].file_path));

// Read the header.
MINIDUMP_HEADER header = {};
ASSERT_TRUE(reader.ReadExactly(&header, sizeof(header)));

// Read the directory.
std::vector<MINIDUMP_DIRECTORY> directory(header.NumberOfStreams);
ASSERT_TRUE(reader.SeekSet(header.StreamDirectoryRva));
ASSERT_TRUE(reader.ReadExactly(directory.data(),
directory.size() * sizeof(directory[0])));

// Search for the extension stream.
size_t found_extension_streams = 0;
for (const auto& entry : directory) {
if (entry.StreamType == 0xCAFEBABE) {
++found_extension_streams;

ASSERT_TRUE(reader.SeekSet(entry.Location.Rva));

std::vector<char> data;
data.resize(entry.Location.DataSize);

ASSERT_TRUE(reader.ReadExactly(data.data(), data.size()));

static const char kExpectedData[] = "Injected extension stream!";
EXPECT_EQ(memcmp(kExpectedData, data.data(), sizeof(kExpectedData)), 0);
}
}

EXPECT_EQ(found_extension_streams, 1u);
}

TEST(CrashpadHandler, ExtensibilityCalloutsWork) {
WinMultiprocessWithTempDir::Run<CrashWithExtendedHandler>();
}

} // namespace
} // namespace test
} // namespace crashpad
70 changes: 70 additions & 0 deletions handler/crashpad_handler_test_extended_handler.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright 2017 The Crashpad Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "build/build_config.h"
#include "handler/handler_main.h"
#include "minidump/test/minidump_user_extension_stream_util.h"
#include "tools/tool_support.h"

#if defined(OS_WIN)
#include <windows.h>
#endif

namespace {

class TestUserStreamDataSource : public crashpad::UserStreamDataSource {
public:
TestUserStreamDataSource() {}

std::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>
ProduceStreamData(crashpad::ProcessSnapshot* process_snapshot) override;

private:
DISALLOW_COPY_AND_ASSIGN(TestUserStreamDataSource);
};

std::unique_ptr<crashpad::MinidumpUserExtensionStreamDataSource>
TestUserStreamDataSource::ProduceStreamData(
crashpad::ProcessSnapshot* process_snapshot) {
static const char kTestData[] = "Injected extension stream!";

return base::WrapUnique(new crashpad::test::BufferExtensionStreamDataSource(
0xCAFEBABE, kTestData, sizeof(kTestData)));
}

int ExtendedHandlerMain(int argc, char* argv[]) {
crashpad::UserStreamDataSources user_stream_data_sources;
user_stream_data_sources.push_back(
base::WrapUnique(new TestUserStreamDataSource()));

return crashpad::HandlerMain(argc, argv, &user_stream_data_sources);
}

} // namespace

#if defined(OS_MACOSX)

int main(int argc, char* argv[]) {
return ExtendedHandlerMain(argc, argv);
}

#elif defined(OS_WIN)

int wmain(int argc, wchar_t* argv[]) {
return crashpad::ToolSupport::Wmain(argc, argv, &ExtendedHandlerMain);
}

#endif // OS_MACOSX
2 changes: 2 additions & 0 deletions handler/handler.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
'mac/exception_handler_server.h',
'prune_crash_reports_thread.cc',
'prune_crash_reports_thread.h',
'user_stream_data_source.cc',
'user_stream_data_source.h',
'win/crash_report_exception_handler.cc',
'win/crash_report_exception_handler.h',
],
Expand Down
10 changes: 7 additions & 3 deletions handler/handler_main.cc
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,9 @@ void MonitorSelf(const Options& options) {

} // namespace

int HandlerMain(int argc, char* argv[]) {
int HandlerMain(int argc,
char* argv[],
const UserStreamDataSources* user_stream_sources) {
InstallCrashHandler();
CallMetricsRecordNormalExit metrics_record_normal_exit;

Expand Down Expand Up @@ -727,8 +729,10 @@ int HandlerMain(int argc, char* argv[]) {
PruneCondition::GetDefault());
prune_thread.Start();

CrashReportExceptionHandler exception_handler(
database.get(), &upload_thread, &options.annotations);
CrashReportExceptionHandler exception_handler(database.get(),
&upload_thread,
&options.annotations,
user_stream_sources);

#if defined(OS_WIN)
if (options.initial_client_data.IsValid()) {
Expand Down
11 changes: 10 additions & 1 deletion handler/handler_main.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,22 @@
#ifndef CRASHPAD_HANDLER_HANDLER_MAIN_H_
#define CRASHPAD_HANDLER_HANDLER_MAIN_H_

#include "handler/user_stream_data_source.h"

namespace crashpad {

//! \brief The `main()` of the `crashpad_handler` binary.
//!
//! This is exposed so that `crashpad_handler` can be embedded into another
//! binary, but called and used as if it were a standalone executable.
int HandlerMain(int argc, char* argv[]);
//!
//! \param[in] user_stream_sources An optional vector containing the
//! extensibility data sources to call on crash. Each time a minidump is
//! created, the sources are called in turn. Any streams returned are added
//! to the minidump.
int HandlerMain(int argc,
char* argv[],
const UserStreamDataSources* user_stream_sources);

} // namespace crashpad

Expand Down
64 changes: 64 additions & 0 deletions handler/handler_test.gyp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Copyright 2017 The Crashpad Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

{
'includes': [
'../build/crashpad.gypi',
],
'targets': [
{
'target_name': 'crashpad_handler_test_extended_handler',
'type': 'executable',
'dependencies': [
'../compat/compat.gyp:crashpad_compat',
'../minidump/minidump_test.gyp:crashpad_minidump_test_lib',
'../third_party/mini_chromium/mini_chromium.gyp:base',
'../tools/tools.gyp:crashpad_tool_support',
'handler.gyp:crashpad_handler_lib',
],
'include_dirs': [
'..',
],
'sources': [
'crashpad_handler_test_extended_handler.cc',
],
},
],
'conditions': [
['OS=="win"', {
'targets': [{
# The handler is only tested on Windows for now.
'target_name': 'crashpad_handler_test',
'type': 'executable',
'dependencies': [
'crashpad_handler_test_extended_handler',
'handler.gyp:crashpad_handler_lib',
'../client/client.gyp:crashpad_client',
'../compat/compat.gyp:crashpad_compat',
'../test/test.gyp:crashpad_gtest_main',
'../test/test.gyp:crashpad_test',
'../third_party/gtest/gtest.gyp:gtest',
'../third_party/mini_chromium/mini_chromium.gyp:base',
'../util/util.gyp:crashpad_util',
],
'include_dirs': [
'..',
],
'sources': [
'crashpad_handler_test.cc',
],
}],
}],
],
}
11 changes: 8 additions & 3 deletions handler/mac/crash_report_exception_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "base/strings/stringprintf.h"
#include "client/settings.h"
#include "minidump/minidump_file_writer.h"
#include "minidump/minidump_user_extension_stream_data_source.h"
#include "snapshot/crashpad_info_client_options.h"
#include "snapshot/mac/process_snapshot_mac.h"
#include "util/file/file_writer.h"
Expand All @@ -41,11 +42,12 @@ namespace crashpad {
CrashReportExceptionHandler::CrashReportExceptionHandler(
CrashReportDatabase* database,
CrashReportUploadThread* upload_thread,
const std::map<std::string, std::string>* process_annotations)
const std::map<std::string, std::string>* process_annotations,
const UserStreamDataSources* user_stream_data_sources)
: database_(database),
upload_thread_(upload_thread),
process_annotations_(process_annotations) {
}
process_annotations_(process_annotations),
user_stream_data_sources_(user_stream_data_sources) {}

CrashReportExceptionHandler::~CrashReportExceptionHandler() {
}
Expand Down Expand Up @@ -169,6 +171,9 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(

MinidumpFileWriter minidump;
minidump.InitializeFromSnapshot(&process_snapshot);
AddUserExtensionStreams(
user_stream_data_sources_, &process_snapshot, &minidump);

if (!minidump.WriteEverything(&file_writer)) {
Metrics::ExceptionCaptureResult(
Metrics::CaptureResult::kMinidumpWriteFailed);
Expand Down
Loading

0 comments on commit 30385d4

Please sign in to comment.