Allow exception forwarding to the system’s native crash reporter to be

disabled.

ClientInfo::set_system_crash_reporter_forwarding() can be used to
disable forwarding. The first module that is found with a non-default
value in this field will dictate whether forwarding is enabled or
disabled. It is possible to enable or disable reporting with this call,
as well as reset it to default, which will allow later modules a chance
to influence the behavior.

ClientInfo::set_crashpad_handler_behavior() is also provided, which can
be used to disable Crashpad’s handling of the exception. Most users
should not call this, but should use Settings::SetUploadsEnabled()
instead.

TEST=crashpad_snapshot_test \
         CrashpadInfoClientOptions.*:MachOImageReader.Self_DyldImages; \
     run_with_crashpad --handler crashpad_handler \
         -a --database=/tmp/crashpad_db \
         -a --url=https://clients2.google.com/cr/staging_report \
         -a --annotation=prod=crashpad \
         -a --annotation=ver=0.7.0 \
         crashy_program

R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/997713002
This commit is contained in:
Mark Mentovai 2015-03-11 17:07:11 -04:00
parent f0ee5f0efe
commit 9b7ff0ea5a
20 changed files with 639 additions and 68 deletions

View File

@ -85,6 +85,8 @@ CrashpadInfo::CrashpadInfo()
: signature_(kSignature),
size_(sizeof(*this)),
version_(kCrashpadInfoVersion),
crashpad_handler_behavior_(TriState::kUnset),
system_crash_reporter_forwarding_(TriState::kUnset),
padding_0_(0),
simple_annotations_(nullptr) {
}

View File

@ -20,6 +20,7 @@
#include <stdint.h>
#include "client/simple_string_dictionary.h"
#include "util/misc/tri_state.h"
namespace crashpad {
@ -41,6 +42,43 @@ struct CrashpadInfo {
simple_annotations_ = simple_annotations;
}
//! \brief Enables or disables Crashpad handler processing.
//!
//! When handling an exception, the Crashpad handler will scan all modules in
//! a process. The first one that has a CrashpadInfo structure populated with
//! a value other than #kUnset for this field will dictate whether the handler
//! is functional or not. If all modules with a CrashpadInfo structure specify
//! #kUnset, the handler will be enabled. If disabled, the Crashpad handler
//! will still run and receive exceptions, but will not take any action on an
//! exception on its own behalf, except for the action necessary to determine
//! that it has been disabled.
//!
//! The Crashpad handler should not normally be disabled. More commonly, it
//! is appropraite to disable crash report upload by calling
//! Settings::SetUploadsEnabled().
void set_crashpad_handler_behavior(TriState crashpad_handler_behavior) {
crashpad_handler_behavior_ = crashpad_handler_behavior;
}
//! \brief Enables or disables Crashpad forwarding of exceptions to the
//! systems crash reporter.
//!
//! When handling an exception, the Crashpad handler will scan all modules in
//! a process. The first one that has a CrashpadInfo structure populated with
//! a value other than #kUnset for this field will dictate whether the
//! exception is forwarded to the systems crash reporter. If all modules with
//! a CrashpadInfo structure specify #kUnset, forwarding will be enabled.
//! Unless disabled, forwarding may still occur if the Crashpad handler is
//! disabled by SetCrashpadHandlerState(). Even when forwarding is enabled,
//! the Crashpad handler may choose not to forward all exceptions to the
//! systems crash reporter in cases where it has reason to believe that the
//! systems crash reporter would not normally have handled the exception in
//! Crashpads absence.
void set_system_crash_reporter_forwarding(
TriState system_crash_reporter_forwarding) {
system_crash_reporter_forwarding_ = system_crash_reporter_forwarding;
}
static const uint32_t kSignature = 'CPad';
private:
@ -57,7 +95,9 @@ struct CrashpadInfo {
uint32_t signature_; // kSignature
uint32_t size_; // The size of the entire CrashpadInfo structure.
uint32_t version_; // kVersion
uint32_t padding_0_;
TriState crashpad_handler_behavior_;
TriState system_crash_reporter_forwarding_;
uint16_t padding_0_;
SimpleStringDictionary* simple_annotations_; // weak
#if defined(__clang__)

View File

@ -22,12 +22,14 @@
#include "base/mac/mach_logging.h"
#include "base/strings/stringprintf.h"
#include "minidump/minidump_file_writer.h"
#include "snapshot/mac/crashpad_info_client_options.h"
#include "snapshot/mac/process_snapshot_mac.h"
#include "util/file/file_writer.h"
#include "util/mach/exc_client_variants.h"
#include "util/mach/exception_behaviors.h"
#include "util/mach/mach_extensions.h"
#include "util/mach/scoped_task_suspend.h"
#include "util/misc/tri_state.h"
#include "util/misc/uuid.h"
namespace crashpad {
@ -116,49 +118,55 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
return KERN_FAILURE;
}
if (!process_snapshot.InitializeException(thread,
exception,
code,
code_count,
*flavor,
old_state,
old_state_count)) {
return KERN_FAILURE;
CrashpadInfoClientOptions client_options;
process_snapshot.GetCrashpadOptions(&client_options);
if (client_options.crashpad_handler_behavior != TriState::kDisabled) {
if (!process_snapshot.InitializeException(thread,
exception,
code,
code_count,
*flavor,
old_state,
old_state_count)) {
return KERN_FAILURE;
}
process_snapshot.SetAnnotationsSimpleMap(*process_annotations_);
CrashReportDatabase::NewReport* new_report;
CrashReportDatabase::OperationStatus database_status =
database_->PrepareNewCrashReport(&new_report);
if (database_status != CrashReportDatabase::kNoError) {
return KERN_FAILURE;
}
CallErrorWritingCrashReport call_error_writing_crash_report(database_,
new_report);
WeakFileHandleFileWriter file_writer(new_report->handle);
MinidumpFileWriter minidump;
minidump.InitializeFromSnapshot(&process_snapshot);
if (!minidump.WriteEverything(&file_writer)) {
return KERN_FAILURE;
}
call_error_writing_crash_report.Disarm();
UUID uuid;
database_status = database_->FinishedWritingCrashReport(new_report, &uuid);
if (database_status != CrashReportDatabase::kNoError) {
return KERN_FAILURE;
}
upload_thread_->ReportPending();
}
process_snapshot.SetAnnotationsSimpleMap(*process_annotations_);
CrashReportDatabase::NewReport* new_report;
CrashReportDatabase::OperationStatus database_status =
database_->PrepareNewCrashReport(&new_report);
if (database_status != CrashReportDatabase::kNoError) {
return KERN_FAILURE;
}
CallErrorWritingCrashReport call_error_writing_crash_report(database_,
new_report);
WeakFileHandleFileWriter file_writer(new_report->handle);
MinidumpFileWriter minidump;
minidump.InitializeFromSnapshot(&process_snapshot);
if (!minidump.WriteEverything(&file_writer)) {
return KERN_FAILURE;
}
call_error_writing_crash_report.Disarm();
UUID uuid;
database_status = database_->FinishedWritingCrashReport(new_report, &uuid);
if (database_status != CrashReportDatabase::kNoError) {
return KERN_FAILURE;
}
upload_thread_->ReportPending();
if (exception == EXC_CRASH ||
exception == EXC_RESOURCE ||
exception == EXC_GUARD) {
if (client_options.system_crash_reporter_forwarding != TriState::kDisabled &&
(exception == EXC_CRASH ||
exception == EXC_RESOURCE ||
exception == EXC_GUARD)) {
// Dont forward simulated exceptions such as kMachExceptionSimulated to the
// system crash reporter. Only forward the types of exceptions that it would
// receive under normal conditions. Although the system crash reporter is

View File

@ -0,0 +1,44 @@
// Copyright 2015 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 "snapshot/mac/crashpad_info_client_options.h"
#include "base/logging.h"
#include "client/crashpad_info.h"
namespace crashpad {
// static
TriState CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
uint8_t crashpad_info_tri_state) {
switch (crashpad_info_tri_state) {
case static_cast<uint8_t>(TriState::kUnset):
return TriState::kUnset;
case static_cast<uint8_t>(TriState::kEnabled):
return TriState::kEnabled;
case static_cast<uint8_t>(TriState::kDisabled):
return TriState::kDisabled;
default:
LOG(WARNING) << "unknown TriState "
<< static_cast<int>(crashpad_info_tri_state);
return TriState::kUnset;
}
}
CrashpadInfoClientOptions::CrashpadInfoClientOptions()
: crashpad_handler_behavior(TriState::kUnset),
system_crash_reporter_forwarding(TriState::kUnset) {
}
} // namespace crashpad

View File

@ -0,0 +1,64 @@
// Copyright 2015 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.
#ifndef CRASHPAD_SNAPSHOT_MAC_CRASHPAD_INFO_CLIENT_OPTIONS_H_
#define CRASHPAD_SNAPSHOT_MAC_CRASHPAD_INFO_CLIENT_OPTIONS_H_
#include "util/misc/tri_state.h"
namespace crashpad {
//! \brief Options represented in a clients CrashpadInfo structure.
//!
//! The CrashpadInfo structure is not suitable to expose client options
//! in a generic way at the snapshot level. This structure duplicates
//! option-related fields from the client structure for general use within the
//! snapshot layer and by users of this layer.
//!
//! For objects of this type corresponding to a module, option values are taken
//! from the modules CrashpadInfo structure directly. If the module has no such
//! such structure, option values appear unset.
//!
//! For objects of this type corresponding to an entire process, option values
//! are taken from the CrashpadInfo structures of modules within the process.
//! The first module found with a set value (enabled or disabled) will provide
//! an option value for the process. Different modules may provide values for
//! different options. If no module in the process sets a value for an option,
//! the option will appear unset for the process. If no module in the process
//! has a CrashpadInfo structure, all option values will appear unset.
struct CrashpadInfoClientOptions {
public:
//! \brief Converts `uint8_t` value to a TriState value.
//!
//! The process_types layer exposes TriState as a `uint8_t` rather than an
//! enum type. This function converts these values into the equivalent enum
//! values used in the snapshot layer.
//!
//! \return The TriState equivalent of \a crashpad_info_tri_state, if it is a
//! valid TriState value. Otherwise, logs a warning and returns
//! TriState::kUnset.
static TriState TriStateFromCrashpadInfo(uint8_t crashpad_info_tri_state);
CrashpadInfoClientOptions();
//! \sa CrashpadInfo::set_crashpad_handler_behavior()
TriState crashpad_handler_behavior;
//! \sa CrashpadInfo::set_system_crash_reporter_forwarding()
TriState system_crash_reporter_forwarding;
};
} // namespace crashpad
#endif // CRASHPAD_SNAPSHOT_MAC_CRASHPAD_INFO_CLIENT_OPTIONS_H_

View File

@ -0,0 +1,205 @@
// Copyright 2015 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 "snapshot/mac/crashpad_info_client_options.h"
#include <dlfcn.h>
#include "base/files/file_path.h"
#include "client/crashpad_info.h"
#include "gtest/gtest.h"
#include "snapshot/mac/process_snapshot_mac.h"
#include "util/test/paths.h"
namespace crashpad {
namespace test {
namespace {
TEST(CrashpadInfoClientOptions, TriStateFromCrashpadInfo) {
EXPECT_EQ(TriState::kUnset,
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(0));
EXPECT_EQ(TriState::kEnabled,
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(1));
EXPECT_EQ(TriState::kDisabled,
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(2));
// These will produce log messages but should result in kUnset being returned.
EXPECT_EQ(TriState::kUnset,
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(3));
EXPECT_EQ(TriState::kUnset,
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(4));
EXPECT_EQ(TriState::kUnset,
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(0xff));
}
class ScopedUnsetCrashpadInfoOptions {
public:
explicit ScopedUnsetCrashpadInfoOptions(CrashpadInfo* crashpad_info)
: crashpad_info_(crashpad_info) {
}
~ScopedUnsetCrashpadInfoOptions() {
crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset);
crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset);
}
private:
CrashpadInfo* crashpad_info_;
DISALLOW_COPY_AND_ASSIGN(ScopedUnsetCrashpadInfoOptions);
};
TEST(CrashpadInfoClientOptions, OneModule) {
// Make sure that the initial state has all values unset.
ProcessSnapshotMac process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
CrashpadInfoClientOptions options;
process_snapshot.GetCrashpadOptions(&options);
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
ASSERT_TRUE(crashpad_info);
{
ScopedUnsetCrashpadInfoOptions unset(crashpad_info);
crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled);
process_snapshot.GetCrashpadOptions(&options);
EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
}
{
ScopedUnsetCrashpadInfoOptions unset(crashpad_info);
crashpad_info->set_system_crash_reporter_forwarding(TriState::kDisabled);
process_snapshot.GetCrashpadOptions(&options);
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding);
}
}
class ScopedDlHandle {
public:
explicit ScopedDlHandle(void* dl_handle)
: dl_handle_(dl_handle) {
}
~ScopedDlHandle() {
if (dl_handle_) {
if (dlclose(dl_handle_) != 0) {
LOG(ERROR) << "dlclose: " << dlerror();
}
}
}
bool valid() const { return dl_handle_ != nullptr; }
template <typename T>
T LookUpSymbol(const char* symbol_name) {
return reinterpret_cast<T>(dlsym(dl_handle_, symbol_name));
}
private:
void* dl_handle_;
DISALLOW_COPY_AND_ASSIGN(ScopedDlHandle);
};
TEST(CrashpadInfoClientOptions, TwoModules) {
// Open the module, which has its own CrashpadInfo structure.
base::FilePath module_path =
Paths::Executable().DirName().Append("crashpad_snapshot_test_module.so");
ScopedDlHandle dl_handle(
dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL));
ASSERT_TRUE(dl_handle.valid()) << "dlopen " << module_path.value() << ": "
<< dlerror();
// Get the function pointer from the module. This wraps GetCrashpadInfo(), but
// because it runs in the module, it returns the remote modules CrashpadInfo
// structure.
CrashpadInfo* (*TestModule_GetCrashpadInfo)() =
dl_handle.LookUpSymbol<CrashpadInfo* (*)()>("TestModule_GetCrashpadInfo");
ASSERT_TRUE(TestModule_GetCrashpadInfo);
// Make sure that the initial state has all values unset.
ProcessSnapshotMac process_snapshot;
ASSERT_TRUE(process_snapshot.Initialize(mach_task_self()));
CrashpadInfoClientOptions options;
process_snapshot.GetCrashpadOptions(&options);
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
// Get both CrashpadInfo structures.
CrashpadInfo* local_crashpad_info = CrashpadInfo::GetCrashpadInfo();
ASSERT_TRUE(local_crashpad_info);
CrashpadInfo* remote_crashpad_info = TestModule_GetCrashpadInfo();
ASSERT_TRUE(remote_crashpad_info);
{
ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info);
ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info);
// When only one module sets a value, it applies to the entire process.
remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled);
process_snapshot.GetCrashpadOptions(&options);
EXPECT_EQ(TriState::kEnabled, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
// When more than one module sets a value, the first one in the module list
// applies to the process. The local module should appear before the remote
// module, because the local module loaded the remote module.
local_crashpad_info->set_crashpad_handler_behavior(TriState::kDisabled);
process_snapshot.GetCrashpadOptions(&options);
EXPECT_EQ(TriState::kDisabled, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kUnset, options.system_crash_reporter_forwarding);
}
{
ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info);
ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info);
// When only one module sets a value, it applies to the entire process.
remote_crashpad_info->set_system_crash_reporter_forwarding(
TriState::kDisabled);
process_snapshot.GetCrashpadOptions(&options);
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kDisabled, options.system_crash_reporter_forwarding);
// When more than one module sets a value, the first one in the module list
// applies to the process. The local module should appear before the remote
// module, because the local module loaded the remote module.
local_crashpad_info->set_system_crash_reporter_forwarding(
TriState::kEnabled);
process_snapshot.GetCrashpadOptions(&options);
EXPECT_EQ(TriState::kUnset, options.crashpad_handler_behavior);
EXPECT_EQ(TriState::kEnabled, options.system_crash_reporter_forwarding);
}
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -0,0 +1,35 @@
// Copyright 2015 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 "client/crashpad_info.h"
#define EXPORT __attribute__((visibility("default")))
extern "C" {
// Returns the modules CrashpadInfo structure. Assuming that this file is built
// into a loadable_module with a distinct static copy of the Crashpad client
// library from the copy built into the loader of this loadable_module, this
// will return a different CrashpadInfo structure than the one that the loader
// uses. Having an extra CrashpadInfo structure makes it possible to test
// behaviors that are relevant in the presence of multiple Crashpad
// client-enabled modules.
//
// This function is used by the CrashpadInfoClientOptions.TwoModules test in
// crashpad_info_client_options_test.cc.
EXPORT crashpad::CrashpadInfo* TestModule_GetCrashpadInfo() {
return crashpad::CrashpadInfo::GetCrashpadInfo();
}
} // extern "C"

View File

@ -22,7 +22,6 @@
#include "client/simple_string_dictionary.h"
#include "snapshot/mac/mach_o_image_reader.h"
#include "snapshot/mac/process_reader.h"
#include "snapshot/mac/process_types.h"
#include "util/mach/task_memory.h"
#include "util/stdlib/strnlen.h"
@ -138,31 +137,8 @@ void MachOImageAnnotationsReader::ReadDyldErrorStringAnnotation(
void MachOImageAnnotationsReader::ReadCrashpadSimpleAnnotations(
std::map<std::string, std::string>* simple_map_annotations) const {
mach_vm_address_t crashpad_info_address;
const process_types::section* crashpad_info_section =
image_reader_->GetSectionByName(
SEG_DATA, "__crashpad_info", &crashpad_info_address);
if (!crashpad_info_section) {
return;
}
process_types::CrashpadInfo crashpad_info;
if (crashpad_info_section->size <
crashpad_info.ExpectedSize(process_reader_)) {
LOG(WARNING) << "small crashpad info section size "
<< crashpad_info_section->size << " in " << name_;
return;
}
if (!crashpad_info.Read(process_reader_, crashpad_info_address)) {
LOG(WARNING) << "could not read crashpad info from " << name_;
return;
}
if (crashpad_info.signature != CrashpadInfo::kSignature ||
crashpad_info.size != crashpad_info_section->size ||
crashpad_info.version < 1) {
LOG(WARNING) << "unexpected crashpad info data in " << name_;
if (!image_reader_->GetCrashpadInfo(&crashpad_info)) {
return;
}

View File

@ -20,6 +20,7 @@
#include <vector>
#include "base/basictypes.h"
#include "snapshot/mac/process_types.h"
namespace crashpad {

View File

@ -23,6 +23,7 @@
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "client/crashpad_info.h"
#include "snapshot/mac/mach_o_image_segment_reader.h"
#include "snapshot/mac/mach_o_image_symbol_table_reader.h"
#include "snapshot/mac/process_reader.h"
@ -454,6 +455,39 @@ void MachOImageReader::UUID(crashpad::UUID* uuid) const {
memcpy(uuid, &uuid_, sizeof(uuid_));
}
bool MachOImageReader::GetCrashpadInfo(
process_types::CrashpadInfo* crashpad_info) const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
mach_vm_address_t crashpad_info_address;
const process_types::section* crashpad_info_section =
GetSectionByName(SEG_DATA, "__crashpad_info", &crashpad_info_address);
if (!crashpad_info_section) {
return false;
}
if (crashpad_info_section->size <
crashpad_info->ExpectedSize(process_reader_)) {
LOG(WARNING) << "small crashpad info section size "
<< crashpad_info_section->size << module_info_;
return false;
}
if (!crashpad_info->Read(process_reader_, crashpad_info_address)) {
LOG(WARNING) << "could not read crashpad info" << module_info_;
return false;
}
if (crashpad_info->signature != CrashpadInfo::kSignature ||
crashpad_info->size != crashpad_info_section->size ||
crashpad_info->version < 1) {
LOG(WARNING) << "unexpected crashpad info data" << module_info_;
return false;
}
return true;
}
template <typename T>
bool MachOImageReader::ReadLoadCommand(mach_vm_address_t load_command_address,
const std::string& load_command_info,

View File

@ -265,6 +265,13 @@ class MachOImageReader {
//! be empty.
std::string DylinkerName() const { return dylinker_name_; }
//! \brief Obtains the modules CrashpadInfo structure.
//!
//! \return `true` on success, `false` on failure. If the module does not have
//! a `__crashpad_info` section, this will return `false` without logging
//! any messages. Other failures will result in messages being logged.
bool GetCrashpadInfo(process_types::CrashpadInfo* crashpad_info) const;
private:
// A generic helper routine for the other Read*Command() methods.
template <typename T>

View File

@ -26,6 +26,7 @@
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "client/crashpad_info.h"
#include "gtest/gtest.h"
#include "snapshot/mac/mach_o_image_segment_reader.h"
#include "snapshot/mac/process_reader.h"
@ -532,6 +533,8 @@ TEST(MachOImageReader, Self_DyldImages) {
uint32_t count = _dyld_image_count();
ASSERT_GE(count, 1u);
size_t modules_with_crashpad_info = 0;
for (uint32_t index = 0; index < count; ++index) {
const char* image_name = _dyld_get_image_name(index);
SCOPED_TRACE(base::StringPrintf("index %u, image %s", index, image_name));
@ -560,8 +563,15 @@ TEST(MachOImageReader, Self_DyldImages) {
mach_header, image_address, image_slide, &image_reader, false));
ASSERT_NO_FATAL_FAILURE(ExpectSymbolTable(mach_header, &image_reader));
process_types::CrashpadInfo crashpad_info;
if (image_reader.GetCrashpadInfo(&crashpad_info)) {
++modules_with_crashpad_info;
}
}
EXPECT_GE(modules_with_crashpad_info, 1u);
// Now that all of the modules have been verified, make sure that dyld itself
// can be read properly too.
const struct dyld_all_image_infos* dyld_image_infos =

View File

@ -20,6 +20,7 @@
#include "base/strings/stringprintf.h"
#include "snapshot/mac/mach_o_image_annotations_reader.h"
#include "snapshot/mac/mach_o_image_reader.h"
#include "util/misc/tri_state.h"
#include "util/misc/uuid.h"
#include "util/stdlib/strnlen.h"
@ -55,6 +56,25 @@ bool ModuleSnapshotMac::Initialize(
return true;
}
void ModuleSnapshotMac::GetCrashpadOptions(CrashpadInfoClientOptions* options) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
process_types::CrashpadInfo crashpad_info;
if (!mach_o_image_reader_->GetCrashpadInfo(&crashpad_info)) {
options->crashpad_handler_behavior = TriState::kUnset;
options->system_crash_reporter_forwarding = TriState::kUnset;
return;
}
options->crashpad_handler_behavior =
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
crashpad_info.crashpad_handler_behavior);
options->system_crash_reporter_forwarding =
CrashpadInfoClientOptions::TriStateFromCrashpadInfo(
crashpad_info.system_crash_reporter_forwarding);
}
std::string ModuleSnapshotMac::Name() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return name_;

View File

@ -23,6 +23,8 @@
#include <vector>
#include "base/basictypes.h"
#include "client/crashpad_info.h"
#include "snapshot/mac/crashpad_info_client_options.h"
#include "snapshot/mac/process_reader.h"
#include "snapshot/module_snapshot.h"
#include "util/misc/initialization_state_dcheck.h"
@ -53,6 +55,11 @@ class ModuleSnapshotMac final : public ModuleSnapshot {
bool Initialize(ProcessReader* process_reader,
const ProcessReader::Module& process_reader_module);
//! \brief Returns options from the modules CrashpadInfo structure.
//!
//! \param[out] options Options set in the modules CrashpadInfo structure.
void GetCrashpadOptions(CrashpadInfoClientOptions* options);
// ModuleSnapshot:
std::string Name() const override;

View File

@ -14,6 +14,8 @@
#include "snapshot/mac/process_snapshot_mac.h"
#include "util/misc/tri_state.h"
namespace crashpad {
ProcessSnapshotMac::ProcessSnapshotMac()
@ -79,6 +81,36 @@ bool ProcessSnapshotMac::InitializeException(
return true;
}
void ProcessSnapshotMac::GetCrashpadOptions(
CrashpadInfoClientOptions* options) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
CrashpadInfoClientOptions local_options;
for (internal::ModuleSnapshotMac* module : modules_) {
CrashpadInfoClientOptions module_options;
module->GetCrashpadOptions(&module_options);
if (local_options.crashpad_handler_behavior == TriState::kUnset) {
local_options.crashpad_handler_behavior =
module_options.crashpad_handler_behavior;
}
if (local_options.system_crash_reporter_forwarding == TriState::kUnset) {
local_options.system_crash_reporter_forwarding =
module_options.system_crash_reporter_forwarding;
}
// If non-default values have been found for all options, the loop can end
// early.
if (local_options.crashpad_handler_behavior != TriState::kUnset &&
local_options.system_crash_reporter_forwarding != TriState::kUnset) {
break;
}
}
*options = local_options;
}
pid_t ProcessSnapshotMac::ProcessID() const {
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
return process_reader_.ProcessID();

View File

@ -25,7 +25,9 @@
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "client/crashpad_info.h"
#include "snapshot/exception_snapshot.h"
#include "snapshot/mac/crashpad_info_client_options.h"
#include "snapshot/mac/exception_snapshot_mac.h"
#include "snapshot/mac/module_snapshot_mac.h"
#include "snapshot/mac/process_reader.h"
@ -85,6 +87,13 @@ class ProcessSnapshotMac final : public ProcessSnapshot {
annotations_simple_map_ = annotations_simple_map;
}
//! \brief Returns options from CrashpadInfo structures found in modules in
//! the process.
//!
//! \param[out] options Options set in CrashpadInfo structures in modules in
//! the process.
void GetCrashpadOptions(CrashpadInfoClientOptions* options);
// ProcessSnapshot:
pid_t ProcessID() const override;

View File

@ -27,7 +27,12 @@ PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo)
PROCESS_TYPE_STRUCT_MEMBER(uint32_t, signature)
PROCESS_TYPE_STRUCT_MEMBER(uint32_t, size)
PROCESS_TYPE_STRUCT_MEMBER(uint32_t, version)
PROCESS_TYPE_STRUCT_MEMBER(uint32_t, padding_0)
PROCESS_TYPE_STRUCT_MEMBER(uint8_t, crashpad_handler_behavior) // TriState
// TriState
PROCESS_TYPE_STRUCT_MEMBER(uint8_t, system_crash_reporter_forwarding)
PROCESS_TYPE_STRUCT_MEMBER(uint16_t, padding_0)
// SimpleStringDictionary*
PROCESS_TYPE_STRUCT_MEMBER(Pointer, simple_annotations)

View File

@ -36,6 +36,8 @@
'exception_snapshot.h',
'mac/cpu_context_mac.cc',
'mac/cpu_context_mac.h',
'mac/crashpad_info_client_options.cc',
'mac/crashpad_info_client_options.h',
'mac/exception_snapshot_mac.cc',
'mac/exception_snapshot_mac.h',
'mac/mach_o_image_annotations_reader.cc',
@ -149,6 +151,7 @@
'sources': [
'cpu_context_test.cc',
'mac/cpu_context_mac_test.cc',
'mac/crashpad_info_client_options_test.cc',
'mac/mach_o_image_annotations_reader_test.cc',
'mac/mach_o_image_reader_test.cc',
'mac/mach_o_image_segment_reader_test.cc',
@ -158,6 +161,33 @@
'minidump/process_snapshot_minidump_test.cc',
'win/system_snapshot_win_test.cc',
],
'conditions': [
['OS=="mac"', {
'dependencies': [
'crashpad_snapshot_test_module',
],
}],
],
},
],
'conditions': [
['OS=="mac"', {
'targets': [
{
'target_name': 'crashpad_snapshot_test_module',
'type': 'loadable_module',
'dependencies': [
'../client/client.gyp:crashpad_client',
'../third_party/mini_chromium/mini_chromium.gyp:base',
],
'include_dirs': [
'..',
],
'sources': [
'mac/crashpad_info_client_options_test_module.cc',
],
},
],
}],
],
}

41
util/misc/tri_state.h Normal file
View File

@ -0,0 +1,41 @@
// Copyright 2015 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.
#ifndef CRASHPAD_UTIL_MISC_TRI_STATE_H_
#define CRASHPAD_UTIL_MISC_TRI_STATE_H_
#include <stdint.h>
namespace crashpad {
//! \brief A tri-state value that can be unset, on, or off.
enum class TriState : uint8_t {
//! \brief The value has not explicitly been set.
//!
//! To allow a zero-initialized value to have this behavior, this must have
//! the value `0`.
kUnset = 0,
//! \brief The value has explicitly been set to on, or a behavior has
//! explicitly been enabled.
kEnabled,
//! \brief The value has explicitly been set to off, or a behavior has
//! explicitly been disabled.
kDisabled,
};
} // namespace crashpad
#endif // CRASHPAD_UTIL_MISC_TRI_STATE_H_

View File

@ -93,6 +93,7 @@
'misc/scoped_forbid_return.cc',
'misc/scoped_forbid_return.h',
'misc/symbolic_constants_common.h',
'misc/tri_state.h',
'misc/uuid.cc',
'misc/uuid.h',
'net/http_body.cc',