mirror of
https://github.com/chromium/crashpad.git
synced 2024-12-26 23:01:05 +08:00
Add crashpad_info, MachOImageAnnotationsReader, and its test.
TEST=snapshot_test MachOImageAnnotationsReader.* R=rsesek@chromium.org Review URL: https://codereview.chromium.org/651283003
This commit is contained in:
parent
bcae4d94d5
commit
b43f510a52
@ -26,6 +26,8 @@
|
||||
'sources': [
|
||||
'capture_context_mac.h',
|
||||
'capture_context_mac.S',
|
||||
'crashpad_info.cc',
|
||||
'crashpad_info.h',
|
||||
'simple_string_dictionary.cc',
|
||||
'simple_string_dictionary.h',
|
||||
'simulate_crash.h',
|
||||
|
76
client/crashpad_info.cc
Normal file
76
client/crashpad_info.cc
Normal file
@ -0,0 +1,76 @@
|
||||
// Copyright 2014 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"
|
||||
|
||||
#include <mach-o/loader.h>
|
||||
|
||||
#if CXX_LIBRARY_VERSION >= 2011
|
||||
#include <type_traits>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
static const uint32_t kCrashpadInfoVersion = 1;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
#if CXX_LIBRARY_VERSION >= 2011
|
||||
// In C++11, check that CrashpadInfo has standard layout, which is what is
|
||||
// actually important.
|
||||
static_assert(std::is_standard_layout<CrashpadInfo>::value,
|
||||
"CrashpadInfo must be standard layout");
|
||||
#else
|
||||
// In C++98 (ISO 14882), section 9.5.1 says that a union cannot have a member
|
||||
// with a non-trivial ctor, copy ctor, dtor, or assignment operator. Use this
|
||||
// property to ensure that CrashpadInfo remains POD. This doesn’t work for C++11
|
||||
// because the requirements for unions have been relaxed.
|
||||
union Compile_Assert {
|
||||
CrashpadInfo Compile_Assert__CrashpadInfo_must_be_pod;
|
||||
};
|
||||
#endif
|
||||
|
||||
// Put the structure in __DATA,__crashpad_info where it can be easily found
|
||||
// without having to consult the symbol table. The “used” attribute prevents it
|
||||
// from being dead-stripped. This isn’t placed in an unnamed namespace:
|
||||
// hopefully, this will catch attempts to place multiple copies of this
|
||||
// structure into the same module. If that’s attempted, and the name of the
|
||||
// symbol is the same in each translation unit, it will result in a linker
|
||||
// error, which is better than having multiple structures show up.
|
||||
//
|
||||
// This may result in a static module initializer in debug-mode builds, but
|
||||
// because it’s POD, no code should need to run to initialize this under
|
||||
// release-mode optimization.
|
||||
__attribute__((section(SEG_DATA ",__crashpad_info"),
|
||||
used,
|
||||
visibility("hidden"))) CrashpadInfo g_crashpad_info;
|
||||
|
||||
// static
|
||||
CrashpadInfo* CrashpadInfo::GetCrashpadInfo() {
|
||||
return &g_crashpad_info;
|
||||
}
|
||||
|
||||
CrashpadInfo::CrashpadInfo()
|
||||
: signature_(kSignature),
|
||||
size_(sizeof(*this)),
|
||||
version_(kCrashpadInfoVersion),
|
||||
padding_0_(0),
|
||||
simple_annotations_(nullptr) {
|
||||
}
|
||||
|
||||
const uint32_t CrashpadInfo::kSignature;
|
||||
|
||||
} // namespace crashpad
|
66
client/crashpad_info.h
Normal file
66
client/crashpad_info.h
Normal file
@ -0,0 +1,66 @@
|
||||
// Copyright 2014 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_CLIENT_CRASHPAD_INFO_H_
|
||||
#define CRASHPAD_CLIENT_CRASHPAD_INFO_H_
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "client/simple_string_dictionary.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief A structure that can be used by a Crashpad-enabled program to
|
||||
//! provide information to the Crashpad crash handler.
|
||||
//!
|
||||
//! It is possible for one CrashpadInfo structure to appear in each loaded code
|
||||
//! module in a process, but from the perspective of the user of the client
|
||||
//! interface, there is only one global CrashpadInfo structure, located in the
|
||||
//! module that contains the client interface code.
|
||||
struct CrashpadInfo {
|
||||
public:
|
||||
//! \brief Returns the global CrashpadInfo structure.
|
||||
static CrashpadInfo* GetCrashpadInfo();
|
||||
|
||||
CrashpadInfo();
|
||||
|
||||
void set_simple_annotations(SimpleStringDictionary* simple_annotations) {
|
||||
simple_annotations_ = simple_annotations;
|
||||
}
|
||||
|
||||
static const uint32_t kSignature = 'CPad';
|
||||
|
||||
private:
|
||||
// The compiler won’t necessarily see anyone using these fields, but it
|
||||
// shouldn’t warn about that. These fields aren’t intended for use by the
|
||||
// process they’re found in, they’re supposed to be read by the crash
|
||||
// reporting process.
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wunused-private-field"
|
||||
// Fields present in version 1:
|
||||
uint32_t signature_; // kSignature
|
||||
uint32_t size_; // The size of the entire CrashpadInfo structure.
|
||||
uint32_t version_; // kVersion
|
||||
uint32_t padding_0_;
|
||||
SimpleStringDictionary* simple_annotations_; // weak
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashpadInfo);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_CLIENT_CRASHPAD_INFO_H_
|
198
snapshot/mac/mach_o_image_annotations_reader.cc
Normal file
198
snapshot/mac/mach_o_image_annotations_reader.cc
Normal file
@ -0,0 +1,198 @@
|
||||
// Copyright 2014 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/mach_o_image_annotations_reader.h"
|
||||
|
||||
#include <mach-o/loader.h>
|
||||
#include <mach/mach.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "client/crashpad_info.h"
|
||||
#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"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
MachOImageAnnotationsReader::MachOImageAnnotationsReader(
|
||||
ProcessReader* process_reader,
|
||||
const MachOImageReader* image_reader,
|
||||
const std::string& name)
|
||||
: name_(name),
|
||||
process_reader_(process_reader),
|
||||
image_reader_(image_reader) {
|
||||
}
|
||||
|
||||
std::vector<std::string> MachOImageAnnotationsReader::Vector() const {
|
||||
std::vector<std::string> vector_annotations;
|
||||
|
||||
ReadCrashReporterClientAnnotations(&vector_annotations);
|
||||
ReadDyldErrorStringAnnotation(&vector_annotations);
|
||||
|
||||
return vector_annotations;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> MachOImageAnnotationsReader::SimpleMap()
|
||||
const {
|
||||
std::map<std::string, std::string> simple_map_annotations;
|
||||
|
||||
ReadCrashpadSimpleAnnotations(&simple_map_annotations);
|
||||
|
||||
return simple_map_annotations;
|
||||
}
|
||||
|
||||
void MachOImageAnnotationsReader::ReadCrashReporterClientAnnotations(
|
||||
std::vector<std::string>* vector_annotations) const {
|
||||
mach_vm_address_t crash_info_address;
|
||||
const process_types::section* crash_info_section =
|
||||
image_reader_->GetSectionByName(
|
||||
SEG_DATA, "__crash_info", &crash_info_address);
|
||||
if (!crash_info_section) {
|
||||
return;
|
||||
}
|
||||
|
||||
process_types::crashreporter_annotations_t crash_info;
|
||||
if (crash_info_section->size < crash_info.ExpectedSize(process_reader_)) {
|
||||
LOG(WARNING) << "small crash info section size " << crash_info_section->size
|
||||
<< " in " << name_;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!crash_info.Read(process_reader_, crash_info_address)) {
|
||||
LOG(WARNING) << "could not read crash info from " << name_;
|
||||
return;
|
||||
}
|
||||
|
||||
if (crash_info.version != 4) {
|
||||
LOG(WARNING) << "unexpected crash info version " << crash_info.version
|
||||
<< " in " << name_;
|
||||
return;
|
||||
}
|
||||
|
||||
// This number was totally made up out of nowhere, but it seems prudent to
|
||||
// enforce some limit.
|
||||
const size_t kMaxMessageSize = 1024;
|
||||
if (crash_info.message) {
|
||||
std::string message;
|
||||
if (process_reader_->Memory()->
|
||||
ReadCStringSizeLimited(
|
||||
crash_info.message, kMaxMessageSize, &message)) {
|
||||
vector_annotations->push_back(message);
|
||||
} else {
|
||||
LOG(WARNING) << "could not read crash message in " << name_;
|
||||
}
|
||||
}
|
||||
|
||||
if (crash_info.message2) {
|
||||
std::string message;
|
||||
if (process_reader_->Memory()->
|
||||
ReadCStringSizeLimited(
|
||||
crash_info.message2, kMaxMessageSize, &message)) {
|
||||
vector_annotations->push_back(message);
|
||||
} else {
|
||||
LOG(WARNING) << "could not read crash message 2 in " << name_;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MachOImageAnnotationsReader::ReadDyldErrorStringAnnotation(
|
||||
std::vector<std::string>* vector_annotations) const {
|
||||
// dyld stores its error string at the external symbol for |const char
|
||||
// error_string[1024]|. See 10.9.5 dyld-239.4/src/dyld.cpp error_string.
|
||||
if (image_reader_->FileType() != MH_DYLINKER) {
|
||||
return;
|
||||
}
|
||||
|
||||
mach_vm_address_t error_string_address;
|
||||
if (!image_reader_->LookUpExternalDefinedSymbol("_error_string",
|
||||
&error_string_address)) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string message;
|
||||
// 1024 here is distinct from kMaxMessageSize above, because it refers to a
|
||||
// precisely-sized buffer inside dyld.
|
||||
if (process_reader_->Memory()->
|
||||
ReadCStringSizeLimited(error_string_address, 1024, &message)) {
|
||||
if (!message.empty()) {
|
||||
vector_annotations->push_back(message);
|
||||
}
|
||||
} else {
|
||||
LOG(WARNING) << "could not read dylinker error string from " << name_;
|
||||
}
|
||||
}
|
||||
|
||||
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_;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!crashpad_info.simple_annotations) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<SimpleStringDictionary::Entry>
|
||||
simple_annotations(SimpleStringDictionary::num_entries);
|
||||
if (!process_reader_->Memory()
|
||||
->Read(crashpad_info.simple_annotations,
|
||||
simple_annotations.size() * sizeof(simple_annotations[0]),
|
||||
&simple_annotations[0])) {
|
||||
LOG(WARNING) << "could not read simple annotations from " << name_;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& entry : simple_annotations) {
|
||||
size_t key_length = strnlen(entry.key, sizeof(entry.key));
|
||||
if (key_length) {
|
||||
std::string key(entry.key, key_length);
|
||||
std::string value(entry.value, strnlen(entry.value, sizeof(entry.value)));
|
||||
if (!simple_map_annotations->count(key)) {
|
||||
simple_map_annotations->insert(
|
||||
std::pair<std::string, std::string>(key, value));
|
||||
} else {
|
||||
LOG(INFO) << "duplicate simple annotation " << key << " in " << name_;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
92
snapshot/mac/mach_o_image_annotations_reader.h
Normal file
92
snapshot/mac/mach_o_image_annotations_reader.h
Normal file
@ -0,0 +1,92 @@
|
||||
// Copyright 2014 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_MACH_O_IMAGE_ANNOTATIONS_READER_H_
|
||||
#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
class MachOImageReader;
|
||||
class ProcessReader;
|
||||
|
||||
//! \brief A reader for annotations stored in a Mach-O image mapped into another
|
||||
//! process.
|
||||
//!
|
||||
//! These annotations are stored for the benefit of crash reporters, and provide
|
||||
//! information though to be potentially useful for crash analysis. This class
|
||||
//! can decode annotations stored in these formats:
|
||||
//! - CrashpadInfo. This format is used by Crashpad clients. The “simple
|
||||
//! annotations” are recovered from any module with a compatible data
|
||||
//! section, and are included in the annotations returned by SimpleMap().
|
||||
//! - `CrashReporterClient.h`’s `crashreporter_annotations_t`. This format is
|
||||
//! used by Apple code. The `message` and `message2` fields can be recovered
|
||||
//! from any module with a compatible data section, and are included in the
|
||||
//! annotations returned by Vector().
|
||||
//! - `dyld`’s `error_string`. This format is used exclusively by dyld,
|
||||
//! typically for fatal errors. This string can be recovered from any
|
||||
//! `MH_DYLINKER`-type module with this symbol, and is included in the
|
||||
//! annotations returned by Vector().
|
||||
class MachOImageAnnotationsReader {
|
||||
public:
|
||||
//! \brief Constructs an object.
|
||||
//!
|
||||
//! \param[in] process_reader The reader for the remote process.
|
||||
//! \param[in] image_reader The MachOImageReader for the Mach-O image file
|
||||
//! contained within the remote process.
|
||||
//! \param[in] name The module’s name, a string to be used in logged messages.
|
||||
//! This string is for diagnostic purposes only, and may be empty.
|
||||
MachOImageAnnotationsReader(ProcessReader* process_reader,
|
||||
const MachOImageReader* image_reader,
|
||||
const std::string& name);
|
||||
|
||||
~MachOImageAnnotationsReader() {}
|
||||
|
||||
//! \brief Returns the module’s annotations that are organized as a vector of
|
||||
//! strings.
|
||||
std::vector<std::string> Vector() const;
|
||||
|
||||
//! \brief Returns the module’s annotations that are organized as key-value
|
||||
//! pairs, where all keys and values are strings.
|
||||
std::map<std::string, std::string> SimpleMap() const;
|
||||
|
||||
private:
|
||||
// Reades crashreporter_annotations_t::message and
|
||||
// crashreporter_annotations_t::message2 on behalf of Vector().
|
||||
void ReadCrashReporterClientAnnotations(
|
||||
std::vector<std::string>* vector_annotations) const;
|
||||
|
||||
// Reads dyld_error_string on behalf of Vector().
|
||||
void ReadDyldErrorStringAnnotation(
|
||||
std::vector<std::string>* vector_annotations) const;
|
||||
|
||||
// Reads CrashpadInfo::simple_annotations_ on behalf of SimpleMap().
|
||||
void ReadCrashpadSimpleAnnotations(
|
||||
std::map<std::string, std::string>* simple_map_annotations) const;
|
||||
|
||||
std::string name_;
|
||||
ProcessReader* process_reader_; // weak
|
||||
const MachOImageReader* image_reader_; // weak
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MachOImageAnnotationsReader);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_
|
334
snapshot/mac/mach_o_image_annotations_reader_test.cc
Normal file
334
snapshot/mac/mach_o_image_annotations_reader_test.cc
Normal file
@ -0,0 +1,334 @@
|
||||
// Copyright 2014 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/mach_o_image_annotations_reader.h"
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <mach/mach.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "client/crashpad_info.h"
|
||||
#include "client/simple_string_dictionary.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "snapshot/mac/process_reader.h"
|
||||
#include "util/file/fd_io.h"
|
||||
#include "util/mac/mac_util.h"
|
||||
#include "util/mach/exc_server_variants.h"
|
||||
#include "util/mach/exception_ports.h"
|
||||
#include "util/mach/mach_message_server.h"
|
||||
#include "util/test/errors.h"
|
||||
#include "util/test/mac/mach_errors.h"
|
||||
#include "util/test/mac/mach_multiprocess.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
class TestMachOImageAnnotationsReader final : public MachMultiprocess,
|
||||
public UniversalMachExcServer {
|
||||
public:
|
||||
enum TestType {
|
||||
// Don’t crash, just test the CrashpadInfo interface.
|
||||
kDontCrash = 0,
|
||||
|
||||
// The child process should crash by calling abort(). The parent verifies
|
||||
// that the system libraries set the expected annotations.
|
||||
kCrashAbort,
|
||||
|
||||
// The child process should crash by setting DYLD_INSERT_LIBRARIES to
|
||||
// contain a nonexistent library. The parent verifies that dyld sets the
|
||||
// expected annotations.
|
||||
kCrashDyld,
|
||||
};
|
||||
|
||||
explicit TestMachOImageAnnotationsReader(TestType test_type)
|
||||
: MachMultiprocess(),
|
||||
UniversalMachExcServer(),
|
||||
test_type_(test_type) {
|
||||
}
|
||||
|
||||
~TestMachOImageAnnotationsReader() {}
|
||||
|
||||
// UniversalMachExcServer:
|
||||
kern_return_t CatchMachException(exception_behavior_t behavior,
|
||||
exception_handler_t exception_port,
|
||||
thread_t thread,
|
||||
task_t task,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_type_t* code,
|
||||
mach_msg_type_number_t code_count,
|
||||
thread_state_flavor_t* flavor,
|
||||
const natural_t* old_state,
|
||||
mach_msg_type_number_t old_state_count,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t* new_state_count,
|
||||
bool* destroy_complex_request) override {
|
||||
*destroy_complex_request = true;
|
||||
|
||||
EXPECT_EQ(ChildTask(), task);
|
||||
|
||||
ProcessReader process_reader;
|
||||
bool rv = process_reader.Initialize(task);
|
||||
if (!rv) {
|
||||
ADD_FAILURE();
|
||||
} else {
|
||||
const std::vector<ProcessReader::Module>& modules =
|
||||
process_reader.Modules();
|
||||
std::vector<std::string> all_annotations_vector;
|
||||
for (const ProcessReader::Module& module : modules) {
|
||||
MachOImageAnnotationsReader module_annotations_reader(
|
||||
&process_reader, module.reader, module.name);
|
||||
std::vector<std::string> module_annotations_vector =
|
||||
module_annotations_reader.Vector();
|
||||
all_annotations_vector.insert(all_annotations_vector.end(),
|
||||
module_annotations_vector.begin(),
|
||||
module_annotations_vector.end());
|
||||
}
|
||||
|
||||
// Mac OS X 10.6 doesn’t have support for CrashReporter annotations
|
||||
// (CrashReporterClient.h), so don’t look for any special annotations in
|
||||
// that version.
|
||||
int mac_os_x_minor_version = MacOSXMinorVersion();
|
||||
if (mac_os_x_minor_version > 7) {
|
||||
EXPECT_GE(all_annotations_vector.size(), 1u);
|
||||
|
||||
const char* expected_annotation = nullptr;
|
||||
switch (test_type_) {
|
||||
case kCrashAbort:
|
||||
// The child process calls abort(), so the expected annotation
|
||||
// reflects this, with a string set by 10.7.5
|
||||
// Libc-763.13/stdlib/abort-fbsd.c abort(). This string is still
|
||||
// present in 10.9.5 Libc-997.90.3/stdlib/FreeBSD/abort.c abort(),
|
||||
// but because abort() tests to see if a message is already set and
|
||||
// something else in Libc will have set a message, this string is
|
||||
// not the expectation on 10.9 or higher. Instead, after fork(), the
|
||||
// child process has a message indicating that a fork() without
|
||||
// exec() occurred. See 10.9.5 Libc-997.90.3/sys/_libc_fork_child.c
|
||||
// _libc_fork_child().
|
||||
expected_annotation =
|
||||
mac_os_x_minor_version <= 8
|
||||
? "abort() called"
|
||||
: "crashed on child side of fork pre-exec";
|
||||
break;
|
||||
|
||||
case kCrashDyld:
|
||||
// This is independent of dyld’s error_string, which is tested
|
||||
// below.
|
||||
expected_annotation = "dyld: launch, loading dependent libraries";
|
||||
break;
|
||||
|
||||
default:
|
||||
ADD_FAILURE();
|
||||
break;
|
||||
}
|
||||
|
||||
size_t expected_annotation_length = strlen(expected_annotation);
|
||||
bool found = false;
|
||||
for (const std::string& annotation : all_annotations_vector) {
|
||||
// Look for the expectation as a leading susbtring, because the actual
|
||||
// string that dyld uses will have the contents of the
|
||||
// DYLD_INSERT_LIBRARIES environment variable appended to it on Mac
|
||||
// OS X 10.10.
|
||||
if (annotation.substr(0, expected_annotation_length) ==
|
||||
expected_annotation) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(found);
|
||||
}
|
||||
|
||||
// dyld exposes its error_string at least as far back as Mac OS X 10.4.
|
||||
if (test_type_ == kCrashDyld) {
|
||||
const char kExpectedAnnotation[] = "could not load inserted library";
|
||||
size_t expected_annotation_length = strlen(kExpectedAnnotation);
|
||||
bool found = false;
|
||||
for (const std::string& annotation : all_annotations_vector) {
|
||||
// Look for the expectation as a leading substring, because the actual
|
||||
// string will contain the library’s pathname and, on Mac OS X 10.9
|
||||
// and later, a reason.
|
||||
if (annotation.substr(0, expected_annotation_length) ==
|
||||
kExpectedAnnotation) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT_TRUE(found);
|
||||
}
|
||||
}
|
||||
|
||||
return ExcServerSuccessfulReturnValue(behavior, false);
|
||||
}
|
||||
|
||||
private:
|
||||
// MachMultiprocess:
|
||||
|
||||
void MachMultiprocessParent() override {
|
||||
ProcessReader process_reader;
|
||||
ASSERT_TRUE(process_reader.Initialize(ChildTask()));
|
||||
|
||||
// Wait for the child process to indicate that it’s done setting up its
|
||||
// annotations via the CrashpadInfo interface.
|
||||
char c;
|
||||
CheckedReadFD(ReadPipeFD(), &c, sizeof(c));
|
||||
|
||||
// Verify the “simple map” annotations set via the CrashpadInfo interface.
|
||||
const std::vector<ProcessReader::Module>& modules =
|
||||
process_reader.Modules();
|
||||
std::map<std::string, std::string> all_annotations_simple_map;
|
||||
for (const ProcessReader::Module& module : modules) {
|
||||
MachOImageAnnotationsReader module_annotations_reader(
|
||||
&process_reader, module.reader, module.name);
|
||||
std::map<std::string, std::string> module_annotations_simple_map =
|
||||
module_annotations_reader.SimpleMap();
|
||||
all_annotations_simple_map.insert(module_annotations_simple_map.begin(),
|
||||
module_annotations_simple_map.end());
|
||||
}
|
||||
|
||||
EXPECT_GE(all_annotations_simple_map.size(), 5u);
|
||||
EXPECT_EQ("crash", all_annotations_simple_map["#TEST# pad"]);
|
||||
EXPECT_EQ("value", all_annotations_simple_map["#TEST# key"]);
|
||||
EXPECT_EQ("y", all_annotations_simple_map["#TEST# x"]);
|
||||
EXPECT_EQ("shorter", all_annotations_simple_map["#TEST# longer"]);
|
||||
EXPECT_EQ("", all_annotations_simple_map["#TEST# empty_value"]);
|
||||
|
||||
// Tell the child process that it’s permitted to crash.
|
||||
CheckedWriteFD(WritePipeFD(), &c, sizeof(c));
|
||||
|
||||
if (test_type_ != kDontCrash) {
|
||||
// Handle the child’s crash. Further validation will be done in
|
||||
// CatchMachException().
|
||||
mach_msg_return_t mr =
|
||||
MachMessageServer::Run(this,
|
||||
LocalPort(),
|
||||
MACH_MSG_OPTION_NONE,
|
||||
MachMessageServer::kOneShot,
|
||||
MachMessageServer::kBlocking,
|
||||
MACH_MSG_TIMEOUT_NONE);
|
||||
EXPECT_EQ(MACH_MSG_SUCCESS, mr)
|
||||
<< MachErrorMessage(mr, "MachMessageServer::Run");
|
||||
|
||||
switch (test_type_) {
|
||||
case kCrashAbort:
|
||||
SetExpectedChildTermination(kTerminationSignal, SIGABRT);
|
||||
break;
|
||||
|
||||
case kCrashDyld:
|
||||
// dyld fatal errors result in the execution of an int3 instruction on
|
||||
// x86 and a trap instruction on ARM, both of which raise SIGTRAP.
|
||||
// 10.9.5 dyld-239.4/src/dyldStartup.s _dyld_fatal_error.
|
||||
SetExpectedChildTermination(kTerminationSignal, SIGTRAP);
|
||||
break;
|
||||
|
||||
default:
|
||||
FAIL();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MachMultiprocessChild() override {
|
||||
CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo();
|
||||
|
||||
// This is “leaked” to crashpad_info.
|
||||
SimpleStringDictionary* simple_annotations = new SimpleStringDictionary();
|
||||
simple_annotations->SetKeyValue("#TEST# pad", "break");
|
||||
simple_annotations->SetKeyValue("#TEST# key", "value");
|
||||
simple_annotations->SetKeyValue("#TEST# pad", "crash");
|
||||
simple_annotations->SetKeyValue("#TEST# x", "y");
|
||||
simple_annotations->SetKeyValue("#TEST# longer", "shorter");
|
||||
simple_annotations->SetKeyValue("#TEST# empty_value", "");
|
||||
|
||||
crashpad_info->set_simple_annotations(simple_annotations);
|
||||
|
||||
// Tell the parent that the environment has been set up.
|
||||
char c = '\0';
|
||||
CheckedWriteFD(WritePipeFD(), &c, sizeof(c));
|
||||
|
||||
// Wait for the parent to indicate that it’s safe to crash.
|
||||
CheckedReadFD(ReadPipeFD(), &c, sizeof(c));
|
||||
|
||||
// Direct an exception message to the exception server running in the
|
||||
// parent.
|
||||
ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask,
|
||||
mach_task_self());
|
||||
ASSERT_TRUE(exception_ports.SetExceptionPort(
|
||||
EXC_MASK_CRASH, RemotePort(), EXCEPTION_DEFAULT, THREAD_STATE_NONE));
|
||||
|
||||
switch (test_type_) {
|
||||
case kDontCrash:
|
||||
break;
|
||||
|
||||
case kCrashAbort:
|
||||
abort();
|
||||
break;
|
||||
|
||||
case kCrashDyld: {
|
||||
// Set DYLD_INSERT_LIBRARIES to contain a library that does not exist.
|
||||
// Unable to load it, dyld will abort with a fatal error.
|
||||
ASSERT_EQ(
|
||||
0,
|
||||
setenv(
|
||||
"DYLD_INSERT_LIBRARIES", "/var/empty/NoDirectory/NoLibrary", 1))
|
||||
<< ErrnoMessage("setenv");
|
||||
|
||||
// The actual executable doesn’t matter very much, because dyld won’t
|
||||
// ever launch it. It just needs to be an executable that uses dyld as
|
||||
// its LC_LOAD_DYLINKER (all normal executables do). /usr/bin/true is on
|
||||
// every system, so use it.
|
||||
ASSERT_EQ(0, execl("/usr/bin/true", "true", nullptr))
|
||||
<< ErrnoMessage("execl");
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
TestType test_type_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(TestMachOImageAnnotationsReader);
|
||||
};
|
||||
|
||||
TEST(MachOImageAnnotationsReader, DontCrash) {
|
||||
TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
|
||||
TestMachOImageAnnotationsReader::kDontCrash);
|
||||
test_mach_o_image_annotations_reader.Run();
|
||||
}
|
||||
|
||||
TEST(MachOImageAnnotationsReader, CrashAbort) {
|
||||
TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
|
||||
TestMachOImageAnnotationsReader::kCrashAbort);
|
||||
test_mach_o_image_annotations_reader.Run();
|
||||
}
|
||||
|
||||
TEST(MachOImageAnnotationsReader, CrashDyld) {
|
||||
TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader(
|
||||
TestMachOImageAnnotationsReader::kCrashDyld);
|
||||
test_mach_o_image_annotations_reader.Run();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -39,6 +39,8 @@ class ProcessReader;
|
||||
//! This class is capable of reading both 32-bit (`mach_header`/`MH_MAGIC`) and
|
||||
//! 64-bit (`mach_header_64`/`MH_MAGIC_64`) images based on the bitness of the
|
||||
//! remote process.
|
||||
//!
|
||||
//! \sa MachOImageAnnotationsReader
|
||||
class MachOImageReader {
|
||||
public:
|
||||
MachOImageReader();
|
||||
|
@ -400,13 +400,14 @@ void ProcessReader::InitializeModules() {
|
||||
//
|
||||
// TODO(mark): It may be possible to recover from these situations by looking
|
||||
// through memory mappings for Mach-O images.
|
||||
//
|
||||
// Continue along when this situation is detected, because even without any
|
||||
// images in infoArray, dyldImageLoadAddress may be set, and it may be
|
||||
// possible to recover some information from dyld.
|
||||
if (all_image_infos.infoArrayCount == 0) {
|
||||
LOG(WARNING) << "all_image_infos.infoArrayCount is zero";
|
||||
return;
|
||||
}
|
||||
if (!all_image_infos.infoArray) {
|
||||
} else if (!all_image_infos.infoArray) {
|
||||
LOG(WARNING) << "all_image_infos.infoArray is nullptr";
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<process_types::dyld_image_info> image_info_vector(
|
||||
|
@ -19,6 +19,7 @@
|
||||
// snapshot/mac/process_types.cc to produce process type struct definitions and
|
||||
// accessors.
|
||||
|
||||
#include "snapshot/mac/process_types/crashpad_info.proctype"
|
||||
#include "snapshot/mac/process_types/crashreporterclient.proctype"
|
||||
#include "snapshot/mac/process_types/dyld_images.proctype"
|
||||
#include "snapshot/mac/process_types/loader.proctype"
|
||||
|
34
snapshot/mac/process_types/crashpad_info.proctype
Normal file
34
snapshot/mac/process_types/crashpad_info.proctype
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// The file corresponds to Crashpad’s client/crashpad_info.h.
|
||||
//
|
||||
// This file is intended to be included multiple times in the same translation
|
||||
// unit, so #include guards are intentionally absent.
|
||||
//
|
||||
// This file is included by snapshot/mac/process_types.h and
|
||||
// snapshot/mac/process_types.cc to produce process type struct definitions and
|
||||
// accessors.
|
||||
|
||||
// Client Mach-O images will contain a __DATA,__crashpad_info section formatted
|
||||
// according to this structure.
|
||||
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)
|
||||
|
||||
// SimpleStringDictionary*
|
||||
PROCESS_TYPE_STRUCT_MEMBER(Pointer, simple_annotations)
|
||||
PROCESS_TYPE_STRUCT_END(CrashpadInfo)
|
@ -18,6 +18,7 @@
|
||||
'target_name': 'snapshot',
|
||||
'type': 'static_library',
|
||||
'dependencies': [
|
||||
'../client/client.gyp:client',
|
||||
'../compat/compat.gyp:compat',
|
||||
'../third_party/mini_chromium/mini_chromium/base/base.gyp:base',
|
||||
'../util/util.gyp:util',
|
||||
@ -34,6 +35,8 @@
|
||||
'mac/cpu_context_mac.h',
|
||||
'mac/exception_snapshot_mac.cc',
|
||||
'mac/exception_snapshot_mac.h',
|
||||
'mac/mach_o_image_annotations_reader.cc',
|
||||
'mac/mach_o_image_annotations_reader.h',
|
||||
'mac/mach_o_image_reader.cc',
|
||||
'mac/mach_o_image_reader.h',
|
||||
'mac/mach_o_image_segment_reader.cc',
|
||||
@ -47,6 +50,7 @@
|
||||
'mac/process_types.cc',
|
||||
'mac/process_types.h',
|
||||
'mac/process_types/all.proctype',
|
||||
'mac/process_types/crashpad_info.proctype',
|
||||
'mac/process_types/crashreporterclient.proctype',
|
||||
'mac/process_types/custom.cc',
|
||||
'mac/process_types/dyld_images.proctype',
|
||||
@ -71,6 +75,7 @@
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'snapshot',
|
||||
'../client/client.gyp:client',
|
||||
'../compat/compat.gyp:compat',
|
||||
'../third_party/gtest/gtest.gyp:gtest',
|
||||
'../third_party/gtest/gtest.gyp:gtest_main',
|
||||
@ -83,6 +88,7 @@
|
||||
],
|
||||
'sources': [
|
||||
'mac/cpu_context_mac_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',
|
||||
'mac/process_reader_test.cc',
|
||||
|
Loading…
x
Reference in New Issue
Block a user