mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
ios: Migrate ios/snapshot to writing intermediate dumps.
This migrates all the logic that used to live in ios/snapshots that gathers all the various information during an exception. Everything in InProcessIntermediateDumpHandler is considered `RUNS-DURING-CRASH`. Change-Id: Icc47c9de0f66be2b14a46a13d1038176082a3218 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2920547 Commit-Queue: Justin Cohen <justincohen@chromium.org> Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
parent
e47918b80a
commit
40cd1b72cf
@ -35,6 +35,8 @@ crashpad_static_library("client") {
|
|||||||
"crashpad_client_ios.cc",
|
"crashpad_client_ios.cc",
|
||||||
"ios_handler/exception_processor.h",
|
"ios_handler/exception_processor.h",
|
||||||
"ios_handler/exception_processor.mm",
|
"ios_handler/exception_processor.mm",
|
||||||
|
"ios_handler/in_process_intermediate_dump_handler.cc",
|
||||||
|
"ios_handler/in_process_intermediate_dump_handler.h",
|
||||||
"simulate_crash_ios.h",
|
"simulate_crash_ios.h",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -165,13 +167,7 @@ source_set("client_test") {
|
|||||||
sources += [
|
sources += [
|
||||||
"crashpad_client_ios_test.mm",
|
"crashpad_client_ios_test.mm",
|
||||||
"ios_handler/exception_processor_test.mm",
|
"ios_handler/exception_processor_test.mm",
|
||||||
]
|
"ios_handler/in_process_intermediate_dump_handler_test.cc",
|
||||||
sources -= [
|
|
||||||
"annotation_list_test.cc",
|
|
||||||
"annotation_test.cc",
|
|
||||||
"crash_report_database_test.cc",
|
|
||||||
"prune_crash_reports_test.cc",
|
|
||||||
"settings_test.cc",
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,12 +177,12 @@ source_set("client_test") {
|
|||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
":client",
|
":client",
|
||||||
|
"$mini_chromium_source_parent:base",
|
||||||
"../compat",
|
"../compat",
|
||||||
"../snapshot",
|
"../snapshot",
|
||||||
"../test",
|
"../test",
|
||||||
"../third_party/googletest:googlemock",
|
"../third_party/googletest:googlemock",
|
||||||
"../third_party/googletest:googletest",
|
"../third_party/googletest:googletest",
|
||||||
"$mini_chromium_source_parent:base",
|
|
||||||
"../util",
|
"../util",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -29,7 +29,11 @@
|
|||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
#if defined(OS_IOS)
|
||||||
|
namespace internal {
|
||||||
|
class InProcessIntermediateDumpHandler;
|
||||||
|
} // namespace internal
|
||||||
|
#endif
|
||||||
class AnnotationList;
|
class AnnotationList;
|
||||||
|
|
||||||
//! \brief Base class for an annotation, which records a name-value pair of
|
//! \brief Base class for an annotation, which records a name-value pair of
|
||||||
@ -167,6 +171,9 @@ class Annotation {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class AnnotationList;
|
friend class AnnotationList;
|
||||||
|
#if defined(OS_IOS)
|
||||||
|
friend class internal::InProcessIntermediateDumpHandler;
|
||||||
|
#endif
|
||||||
|
|
||||||
std::atomic<Annotation*>& link_node() { return link_node_; }
|
std::atomic<Annotation*>& link_node() { return link_node_; }
|
||||||
|
|
||||||
|
@ -16,9 +16,15 @@
|
|||||||
#define CRASHPAD_CLIENT_ANNOTATION_LIST_H_
|
#define CRASHPAD_CLIENT_ANNOTATION_LIST_H_
|
||||||
|
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
#include "client/annotation.h"
|
#include "client/annotation.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
#if defined(OS_IOS)
|
||||||
|
namespace internal {
|
||||||
|
class InProcessIntermediateDumpHandler;
|
||||||
|
} // namespace internal
|
||||||
|
#endif
|
||||||
|
|
||||||
//! \brief A list that contains all the currently set annotations.
|
//! \brief A list that contains all the currently set annotations.
|
||||||
//!
|
//!
|
||||||
@ -77,6 +83,17 @@ class AnnotationList {
|
|||||||
//! \brief Returns an iterator past the last element of the annotation list.
|
//! \brief Returns an iterator past the last element of the annotation list.
|
||||||
Iterator end();
|
Iterator end();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
#if defined(OS_IOS)
|
||||||
|
friend class internal::InProcessIntermediateDumpHandler;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
//! \brief Returns a pointer to the tail node.
|
||||||
|
const Annotation* tail_pointer() const { return tail_pointer_; }
|
||||||
|
|
||||||
|
//! \brief Returns a pointer to the head element.
|
||||||
|
const Annotation* head() const { return &head_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// To make it easier for the handler to locate the dummy tail node, store the
|
// To make it easier for the handler to locate the dummy tail node, store the
|
||||||
// pointer. Placed first for packing.
|
// pointer. Placed first for packing.
|
||||||
|
@ -32,6 +32,10 @@ namespace crashpad {
|
|||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
#if defined(OS_IOS)
|
||||||
|
class InProcessIntermediateDumpHandler;
|
||||||
|
#endif
|
||||||
|
|
||||||
//! \brief A linked list of blocks representing custom streams in the minidump,
|
//! \brief A linked list of blocks representing custom streams in the minidump,
|
||||||
//! with addresses (and size) stored as uint64_t to simplify reading from
|
//! with addresses (and size) stored as uint64_t to simplify reading from
|
||||||
//! the handler process.
|
//! the handler process.
|
||||||
@ -223,6 +227,15 @@ struct CrashpadInfo {
|
|||||||
kSignature = 'CPad',
|
kSignature = 'CPad',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
#if defined(OS_IOS)
|
||||||
|
friend class internal::InProcessIntermediateDumpHandler;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uint32_t signature() const { return signature_; }
|
||||||
|
uint32_t version() const { return version_; }
|
||||||
|
uint32_t size() const { return size_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// The compiler won’t necessarily see anyone using these fields, but it
|
// 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
|
// shouldn’t warn about that. These fields aren’t intended for use by the
|
||||||
|
1258
client/ios_handler/in_process_intermediate_dump_handler.cc
Normal file
1258
client/ios_handler/in_process_intermediate_dump_handler.cc
Normal file
File diff suppressed because it is too large
Load Diff
146
client/ios_handler/in_process_intermediate_dump_handler.h
Normal file
146
client/ios_handler/in_process_intermediate_dump_handler.h
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
// Copyright 2021 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_IOS_HANDLER_IN_PROCESS_INTERMEDIATE_DUMP_HANDLER_H_
|
||||||
|
#define CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_INTERMEDIATE_DUMP_HANDLER_H_
|
||||||
|
|
||||||
|
#include <mach-o/loader.h>
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include "client/crashpad_info.h"
|
||||||
|
#include "util/ios/ios_intermediate_dump_writer.h"
|
||||||
|
#include "util/ios/ios_system_data_collector.h"
|
||||||
|
#include "util/mach/mach_extensions.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace internal {
|
||||||
|
|
||||||
|
//! \brief Dump all in-process data to iOS intermediate dump.
|
||||||
|
//! Note: All methods are `RUNS-DURING-CRASH`.
|
||||||
|
class InProcessIntermediateDumpHandler final {
|
||||||
|
public:
|
||||||
|
//! \brief Set kVersion to 1.
|
||||||
|
//!
|
||||||
|
//! \param[in] writer The dump writer
|
||||||
|
static void WriteHeader(IOSIntermediateDumpWriter* writer);
|
||||||
|
|
||||||
|
//! \brief Write ProcessSnapshot data to the intermediate dump.
|
||||||
|
//!
|
||||||
|
//! \param[in] writer The dump writer
|
||||||
|
static void WriteProcessInfo(IOSIntermediateDumpWriter* writer);
|
||||||
|
|
||||||
|
//! \brief Write SystemSnapshot data to the intermediate dump.
|
||||||
|
//!
|
||||||
|
//! \param[in] writer The dump writer
|
||||||
|
static void WriteSystemInfo(IOSIntermediateDumpWriter* writer,
|
||||||
|
const IOSSystemDataCollector& system_data);
|
||||||
|
|
||||||
|
//! \brief Write ThreadSnapshot data to the intermediate dump.
|
||||||
|
//!
|
||||||
|
//! For uncaught NSExceptions, \a frames and \a num_frames will be added to
|
||||||
|
//! the intermediate dump for the exception thread. Otherwise, or for the
|
||||||
|
//! remaining threads, use `thread_get_state`.
|
||||||
|
//!
|
||||||
|
//! \param[in] writer The dump writer
|
||||||
|
//! \param[in] frames An array of callstack return addresses.
|
||||||
|
//! \param[in] num_frames The number of callstack return address in \a frames.
|
||||||
|
static void WriteThreadInfo(IOSIntermediateDumpWriter* writer,
|
||||||
|
const uint64_t* frames,
|
||||||
|
const size_t num_frames);
|
||||||
|
|
||||||
|
//! \brief Write ModuleSnapshot data to the intermediate dump.
|
||||||
|
//!
|
||||||
|
//! This includes both modules and annotations.
|
||||||
|
//!
|
||||||
|
//! \param[in] writer The dump writer
|
||||||
|
static void WriteModuleInfo(IOSIntermediateDumpWriter* writer);
|
||||||
|
|
||||||
|
//! \brief Write an ExceptionSnapshot from a signal to the intermediate dump.
|
||||||
|
//!
|
||||||
|
//! Only one of the WriteExceptionFromSignal, WriteExceptionFromMachException
|
||||||
|
//! and WriteExceptionFromNSException should be called per intermediate dump.
|
||||||
|
//!
|
||||||
|
//! \param[in] writer The dump writer
|
||||||
|
//! \param[in] system_data An object containing various system data points.
|
||||||
|
//! \param[in] siginfo A pointer to a `siginfo_t` object received by a signal
|
||||||
|
//! handler.
|
||||||
|
//! \param[in] context A pointer to a `ucontext_t` object received by a
|
||||||
|
//! signal.
|
||||||
|
static void WriteExceptionFromSignal(
|
||||||
|
IOSIntermediateDumpWriter* writer,
|
||||||
|
const IOSSystemDataCollector& system_data,
|
||||||
|
siginfo_t* siginfo,
|
||||||
|
ucontext_t* context);
|
||||||
|
|
||||||
|
//! \brief Write an ExceptionSnapshot from a mach exception to the
|
||||||
|
//! intermediate dump.
|
||||||
|
//!
|
||||||
|
//! Only one of the WriteExceptionFromSignal, WriteExceptionFromMachException
|
||||||
|
//! and WriteExceptionFromNSException should be called per intermediate dump.
|
||||||
|
//!
|
||||||
|
//! \param[in] writer The dump writer
|
||||||
|
//! \param[in] system_data An object containing various system data points.
|
||||||
|
//! \param[in] behavior
|
||||||
|
//! \param[in] thread
|
||||||
|
//! \param[in] exception
|
||||||
|
//! \param[in] code
|
||||||
|
//! \param[in] code_count
|
||||||
|
//! \param[in] flavor
|
||||||
|
//! \param[in] old_state
|
||||||
|
//! \param[in] old_state_count
|
||||||
|
static void WriteExceptionFromMachException(
|
||||||
|
IOSIntermediateDumpWriter* writer,
|
||||||
|
exception_behavior_t behavior,
|
||||||
|
thread_t thread,
|
||||||
|
exception_type_t exception,
|
||||||
|
const mach_exception_data_type_t* code,
|
||||||
|
mach_msg_type_number_t code_count,
|
||||||
|
thread_state_flavor_t flavor,
|
||||||
|
ConstThreadState old_state,
|
||||||
|
mach_msg_type_number_t old_state_count);
|
||||||
|
|
||||||
|
//! \brief Write an ExceptionSnapshot from an NSException to the
|
||||||
|
//! intermediate dump.
|
||||||
|
//!
|
||||||
|
//! Only one of the WriteExceptionFromSignal, WriteExceptionFromMachException
|
||||||
|
//! and WriteExceptionFromNSException should be called per intermediate dump.
|
||||||
|
//!
|
||||||
|
//! \param[in] writer The dump writer
|
||||||
|
static void WriteExceptionFromNSException(IOSIntermediateDumpWriter* writer);
|
||||||
|
|
||||||
|
private:
|
||||||
|
//! \brief Parse and extract module and annotation information from header.
|
||||||
|
static void WriteModuleInfoAtAddress(IOSIntermediateDumpWriter* writer,
|
||||||
|
uint64_t address,
|
||||||
|
bool is_dyld);
|
||||||
|
|
||||||
|
//! \brief Extract and write Apple crashreporter_annotations_t data and
|
||||||
|
//! Crashpad annotations.
|
||||||
|
static void WriteDataSegmentAnnotations(IOSIntermediateDumpWriter* writer,
|
||||||
|
const segment_command_64* segment_ptr,
|
||||||
|
vm_size_t slide);
|
||||||
|
|
||||||
|
//! \brief Write Crashpad annotations list.
|
||||||
|
static void WriteCrashpadAnnotationsList(IOSIntermediateDumpWriter* writer,
|
||||||
|
CrashpadInfo* crashpad_info);
|
||||||
|
|
||||||
|
DISALLOW_IMPLICIT_CONSTRUCTORS(InProcessIntermediateDumpHandler);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace internal
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_INTERMEDIATE_DUMP_HANDLER_H_
|
242
client/ios_handler/in_process_intermediate_dump_handler_test.cc
Normal file
242
client/ios_handler/in_process_intermediate_dump_handler_test.cc
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
// Copyright 2021 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/ios_handler/in_process_intermediate_dump_handler.h"
|
||||||
|
|
||||||
|
#include <sys/utsname.h>
|
||||||
|
|
||||||
|
#include "base/cxx17_backports.h"
|
||||||
|
#include "base/files/file_path.h"
|
||||||
|
#include "build/build_config.h"
|
||||||
|
#include "client/annotation.h"
|
||||||
|
#include "client/annotation_list.h"
|
||||||
|
#include "client/crashpad_info.h"
|
||||||
|
#include "client/simple_string_dictionary.h"
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "snapshot/ios/process_snapshot_ios_intermediate_dump.h"
|
||||||
|
#include "test/scoped_temp_dir.h"
|
||||||
|
#include "test/test_paths.h"
|
||||||
|
#include "util/file/filesystem.h"
|
||||||
|
#include "util/misc/capture_context.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
namespace test {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
using internal::InProcessIntermediateDumpHandler;
|
||||||
|
|
||||||
|
class InProcessIntermediateDumpHandlerTest : public testing::Test {
|
||||||
|
protected:
|
||||||
|
// testing::Test:
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
path_ = temp_dir_.path().Append("dump_file");
|
||||||
|
writer_ = std::make_unique<internal::IOSIntermediateDumpWriter>();
|
||||||
|
EXPECT_TRUE(writer_->Open(path_));
|
||||||
|
ASSERT_TRUE(IsRegularFile(path_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
writer_.reset();
|
||||||
|
EXPECT_FALSE(IsRegularFile(path_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteReport() {
|
||||||
|
internal::IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer_.get());
|
||||||
|
InProcessIntermediateDumpHandler::WriteHeader(writer_.get());
|
||||||
|
InProcessIntermediateDumpHandler::WriteProcessInfo(writer_.get());
|
||||||
|
InProcessIntermediateDumpHandler::WriteSystemInfo(writer_.get(),
|
||||||
|
system_data_);
|
||||||
|
InProcessIntermediateDumpHandler::WriteThreadInfo(writer_.get(), 0, 0);
|
||||||
|
InProcessIntermediateDumpHandler::WriteModuleInfo(writer_.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteMachException() {
|
||||||
|
crashpad::NativeCPUContext cpu_context;
|
||||||
|
crashpad::CaptureContext(&cpu_context);
|
||||||
|
const mach_exception_data_type_t code[2] = {};
|
||||||
|
static constexpr int kSimulatedException = -1;
|
||||||
|
InProcessIntermediateDumpHandler::WriteExceptionFromMachException(
|
||||||
|
writer_.get(),
|
||||||
|
MACH_EXCEPTION_CODES,
|
||||||
|
mach_thread_self(),
|
||||||
|
kSimulatedException,
|
||||||
|
code,
|
||||||
|
base::size(code),
|
||||||
|
MACHINE_THREAD_STATE,
|
||||||
|
reinterpret_cast<ConstThreadState>(&cpu_context),
|
||||||
|
MACHINE_THREAD_STATE_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& path() const { return path_; }
|
||||||
|
auto writer() const { return writer_.get(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<internal::IOSIntermediateDumpWriter> writer_;
|
||||||
|
internal::IOSSystemDataCollector system_data_;
|
||||||
|
ScopedTempDir temp_dir_;
|
||||||
|
base::FilePath path_;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(InProcessIntermediateDumpHandlerTest, TestSystem) {
|
||||||
|
WriteReport();
|
||||||
|
internal::ProcessSnapshotIOSIntermediateDump process_snapshot;
|
||||||
|
ASSERT_TRUE(process_snapshot.Initialize(path(), {}));
|
||||||
|
|
||||||
|
// Snpahot
|
||||||
|
const SystemSnapshot* system = process_snapshot.System();
|
||||||
|
ASSERT_NE(system, nullptr);
|
||||||
|
#if defined(ARCH_CPU_X86_64)
|
||||||
|
EXPECT_EQ(system->GetCPUArchitecture(), kCPUArchitectureX86_64);
|
||||||
|
EXPECT_STREQ(system->CPUVendor().c_str(), "GenuineIntel");
|
||||||
|
#elif defined(ARCH_CPU_ARM64)
|
||||||
|
EXPECT_EQ(system->GetCPUArchitecture(), kCPUArchitectureARM64);
|
||||||
|
utsname uts;
|
||||||
|
ASSERT_EQ(uname(&uts), 0);
|
||||||
|
EXPECT_STREQ(system->MachineDescription().c_str(), uts.machine);
|
||||||
|
#else
|
||||||
|
#error Port to your CPU architecture
|
||||||
|
#endif
|
||||||
|
EXPECT_EQ(system->GetOperatingSystem(), SystemSnapshot::kOperatingSystemIOS);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InProcessIntermediateDumpHandlerTest, TestAnnotations) {
|
||||||
|
// This is “leaked” to crashpad_info.
|
||||||
|
crashpad::SimpleStringDictionary* simple_annotations =
|
||||||
|
new crashpad::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::CrashpadInfo* crashpad_info =
|
||||||
|
crashpad::CrashpadInfo::GetCrashpadInfo();
|
||||||
|
|
||||||
|
crashpad_info->set_simple_annotations(simple_annotations);
|
||||||
|
|
||||||
|
crashpad::AnnotationList::Register(); // This is “leaked” to crashpad_info.
|
||||||
|
|
||||||
|
static crashpad::StringAnnotation<32> test_annotation_one{"#TEST# one"};
|
||||||
|
static crashpad::StringAnnotation<32> test_annotation_two{"#TEST# two"};
|
||||||
|
static crashpad::StringAnnotation<32> test_annotation_three{
|
||||||
|
"#TEST# same-name"};
|
||||||
|
static crashpad::StringAnnotation<32> test_annotation_four{
|
||||||
|
"#TEST# same-name"};
|
||||||
|
|
||||||
|
test_annotation_one.Set("moocow");
|
||||||
|
test_annotation_two.Set("this will be cleared");
|
||||||
|
test_annotation_three.Set("same-name 3");
|
||||||
|
test_annotation_four.Set("same-name 4");
|
||||||
|
test_annotation_two.Clear();
|
||||||
|
|
||||||
|
WriteReport();
|
||||||
|
internal::ProcessSnapshotIOSIntermediateDump process_snapshot;
|
||||||
|
ASSERT_TRUE(process_snapshot.Initialize(path(), {{"after_dump", "post"}}));
|
||||||
|
|
||||||
|
auto process_map = process_snapshot.AnnotationsSimpleMap();
|
||||||
|
EXPECT_EQ(process_map.size(), 1u);
|
||||||
|
EXPECT_EQ(process_map["after_dump"], "post");
|
||||||
|
|
||||||
|
std::map<std::string, std::string> all_annotations_simple_map;
|
||||||
|
std::vector<AnnotationSnapshot> all_annotations;
|
||||||
|
for (const auto* module : process_snapshot.Modules()) {
|
||||||
|
std::map<std::string, std::string> module_annotations_simple_map =
|
||||||
|
module->AnnotationsSimpleMap();
|
||||||
|
all_annotations_simple_map.insert(module_annotations_simple_map.begin(),
|
||||||
|
module_annotations_simple_map.end());
|
||||||
|
|
||||||
|
std::vector<AnnotationSnapshot> annotations = module->AnnotationObjects();
|
||||||
|
all_annotations.insert(
|
||||||
|
all_annotations.end(), annotations.begin(), annotations.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(all_annotations_simple_map.size(), 5u);
|
||||||
|
EXPECT_EQ(all_annotations_simple_map["#TEST# pad"], "crash");
|
||||||
|
EXPECT_EQ(all_annotations_simple_map["#TEST# key"], "value");
|
||||||
|
EXPECT_EQ(all_annotations_simple_map["#TEST# x"], "y");
|
||||||
|
EXPECT_EQ(all_annotations_simple_map["#TEST# longer"], "shorter");
|
||||||
|
EXPECT_EQ(all_annotations_simple_map["#TEST# empty_value"], "");
|
||||||
|
|
||||||
|
bool saw_same_name_3 = false, saw_same_name_4 = false;
|
||||||
|
for (const auto& annotation : all_annotations) {
|
||||||
|
EXPECT_EQ(annotation.type,
|
||||||
|
static_cast<uint16_t>(Annotation::Type::kString));
|
||||||
|
std::string value(reinterpret_cast<const char*>(annotation.value.data()),
|
||||||
|
annotation.value.size());
|
||||||
|
if (annotation.name == "#TEST# one") {
|
||||||
|
EXPECT_EQ(value, "moocow");
|
||||||
|
} else if (annotation.name == "#TEST# same-name") {
|
||||||
|
if (value == "same-name 3") {
|
||||||
|
EXPECT_FALSE(saw_same_name_3);
|
||||||
|
saw_same_name_3 = true;
|
||||||
|
} else if (value == "same-name 4") {
|
||||||
|
EXPECT_FALSE(saw_same_name_4);
|
||||||
|
saw_same_name_4 = true;
|
||||||
|
} else {
|
||||||
|
ADD_FAILURE() << "unexpected annotation value " << value;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ADD_FAILURE() << "unexpected annotation " << annotation.name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InProcessIntermediateDumpHandlerTest, TestThreads) {
|
||||||
|
WriteReport();
|
||||||
|
internal::ProcessSnapshotIOSIntermediateDump process_snapshot;
|
||||||
|
ASSERT_TRUE(process_snapshot.Initialize(path(), {}));
|
||||||
|
|
||||||
|
const auto& threads = process_snapshot.Threads();
|
||||||
|
ASSERT_GT(threads.size(), 0u);
|
||||||
|
|
||||||
|
thread_identifier_info identifier_info;
|
||||||
|
mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT;
|
||||||
|
ASSERT_EQ(thread_info(mach_thread_self(),
|
||||||
|
THREAD_IDENTIFIER_INFO,
|
||||||
|
reinterpret_cast<thread_info_t>(&identifier_info),
|
||||||
|
&count),
|
||||||
|
0);
|
||||||
|
EXPECT_EQ(threads[0]->ThreadID(), identifier_info.thread_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InProcessIntermediateDumpHandlerTest, TestProcess) {
|
||||||
|
WriteReport();
|
||||||
|
internal::ProcessSnapshotIOSIntermediateDump process_snapshot;
|
||||||
|
ASSERT_TRUE(process_snapshot.Initialize(path(), {}));
|
||||||
|
EXPECT_EQ(process_snapshot.ProcessID(), getpid());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InProcessIntermediateDumpHandlerTest, TestMachException) {
|
||||||
|
WriteReport();
|
||||||
|
internal::ProcessSnapshotIOSIntermediateDump process_snapshot;
|
||||||
|
ASSERT_TRUE(process_snapshot.Initialize(path(), {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InProcessIntermediateDumpHandlerTest, TestSignalException) {
|
||||||
|
WriteReport();
|
||||||
|
internal::ProcessSnapshotIOSIntermediateDump process_snapshot;
|
||||||
|
ASSERT_TRUE(process_snapshot.Initialize(path(), {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(InProcessIntermediateDumpHandlerTest, TestNSException) {
|
||||||
|
WriteReport();
|
||||||
|
internal::ProcessSnapshotIOSIntermediateDump process_snapshot;
|
||||||
|
ASSERT_TRUE(process_snapshot.Initialize(path(), {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
} // namespace test
|
||||||
|
} // namespace crashpad
|
@ -156,20 +156,18 @@ bool ExceptionSnapshotIOSIntermediateDump::InitializeFromMachException(
|
|||||||
exception_ = exception;
|
exception_ = exception;
|
||||||
}
|
}
|
||||||
|
|
||||||
mach_msg_type_number_t code_count;
|
|
||||||
GetDataValueFromMap(exception_data, Key::kCodeCount, &code_count);
|
|
||||||
|
|
||||||
const IOSIntermediateDumpData* code_dump =
|
const IOSIntermediateDumpData* code_dump =
|
||||||
GetDataFromMap(exception_data, Key::kCode);
|
GetDataFromMap(exception_data, Key::kCodes);
|
||||||
if (code_dump) {
|
if (code_dump) {
|
||||||
const std::vector<uint8_t>& bytes = code_dump->bytes();
|
const std::vector<uint8_t>& bytes = code_dump->bytes();
|
||||||
const mach_exception_data_type_t* code =
|
const mach_exception_data_type_t* code =
|
||||||
reinterpret_cast<const mach_exception_data_type_t*>(bytes.data());
|
reinterpret_cast<const mach_exception_data_type_t*>(bytes.data());
|
||||||
if (!code ||
|
if (bytes.size() == 0 || !code) {
|
||||||
bytes.size() != sizeof(mach_exception_data_type_t) * code_count) {
|
|
||||||
LOG(ERROR) << "Invalid mach exception code.";
|
LOG(ERROR) << "Invalid mach exception code.";
|
||||||
} else {
|
} else {
|
||||||
// TODO: rationalize with the macOS implementation.
|
// TODO: rationalize with the macOS implementation.
|
||||||
|
mach_msg_type_number_t code_count =
|
||||||
|
bytes.size() / sizeof(mach_exception_data_type_t);
|
||||||
for (mach_msg_type_number_t code_index = 0; code_index < code_count;
|
for (mach_msg_type_number_t code_index = 0; code_index < code_count;
|
||||||
++code_index) {
|
++code_index) {
|
||||||
codes_.push_back(code[code_index]);
|
codes_.push_back(code[code_index]);
|
||||||
@ -288,22 +286,24 @@ void ExceptionSnapshotIOSIntermediateDump::LoadContextFromThread(
|
|||||||
float_state_type float_state;
|
float_state_type float_state;
|
||||||
debug_state_type debug_state;
|
debug_state_type debug_state;
|
||||||
|
|
||||||
mach_msg_type_number_t state_count = 0;
|
|
||||||
thread_state_flavor_t flavor = THREAD_STATE_NONE;
|
thread_state_flavor_t flavor = THREAD_STATE_NONE;
|
||||||
if (GetDataValueFromMap(exception_data, Key::kStateCount, &state_count) &&
|
if (GetDataValueFromMap(exception_data, Key::kFlavor, &flavor) &&
|
||||||
GetDataValueFromMap(exception_data, Key::kFlavor, &flavor) &&
|
|
||||||
GetDataValueFromMap(other_thread, Key::kThreadState, &thread_state) &&
|
GetDataValueFromMap(other_thread, Key::kThreadState, &thread_state) &&
|
||||||
GetDataValueFromMap(other_thread, Key::kFloatState, &float_state) &&
|
GetDataValueFromMap(other_thread, Key::kFloatState, &float_state) &&
|
||||||
GetDataValueFromMap(other_thread, Key::kDebugState, &debug_state)) {
|
GetDataValueFromMap(other_thread, Key::kDebugState, &debug_state)) {
|
||||||
size_t expected_length = ThreadStateLengthForFlavor(flavor);
|
|
||||||
const IOSIntermediateDumpData* state_dump =
|
const IOSIntermediateDumpData* state_dump =
|
||||||
GetDataFromMap(exception_data, Key::kState);
|
GetDataFromMap(exception_data, Key::kState);
|
||||||
if (state_dump) {
|
if (state_dump) {
|
||||||
const std::vector<uint8_t>& bytes = state_dump->bytes();
|
const std::vector<uint8_t>& bytes = state_dump->bytes();
|
||||||
size_t actual_length = bytes.size();
|
size_t actual_length = bytes.size();
|
||||||
|
size_t expected_length = ThreadStateLengthForFlavor(flavor);
|
||||||
|
// TODO(justincohen): Consider zero-ing out bytes if actual_length is
|
||||||
|
// shorter than expected_length, and tolerating actual_length longer than
|
||||||
|
// expected_length.
|
||||||
if (expected_length == actual_length) {
|
if (expected_length == actual_length) {
|
||||||
const ConstThreadState state =
|
const ConstThreadState state =
|
||||||
reinterpret_cast<const ConstThreadState>(bytes.data());
|
reinterpret_cast<const ConstThreadState>(bytes.data());
|
||||||
|
mach_msg_type_number_t state_count = bytes.size() / sizeof(uint32_t);
|
||||||
#if defined(ARCH_CPU_X86_64)
|
#if defined(ARCH_CPU_X86_64)
|
||||||
InitializeCPUContextX86_64(&context_x86_64_,
|
InitializeCPUContextX86_64(&context_x86_64_,
|
||||||
flavor,
|
flavor,
|
||||||
|
@ -281,7 +281,6 @@ class ProcessSnapshotIOSIntermediateDumpTest : public testing::Test {
|
|||||||
|
|
||||||
#if defined(ARCH_CPU_X86_64)
|
#if defined(ARCH_CPU_X86_64)
|
||||||
thread_state_flavor_t flavor = x86_THREAD_STATE;
|
thread_state_flavor_t flavor = x86_THREAD_STATE;
|
||||||
mach_msg_type_number_t state_count = x86_THREAD_STATE_COUNT;
|
|
||||||
x86_thread_state_t state = {};
|
x86_thread_state_t state = {};
|
||||||
state.tsh.flavor = x86_THREAD_STATE64;
|
state.tsh.flavor = x86_THREAD_STATE64;
|
||||||
state.tsh.count = x86_THREAD_STATE64_COUNT;
|
state.tsh.count = x86_THREAD_STATE64_COUNT;
|
||||||
@ -289,7 +288,6 @@ class ProcessSnapshotIOSIntermediateDumpTest : public testing::Test {
|
|||||||
size_t state_length = sizeof(x86_thread_state_t);
|
size_t state_length = sizeof(x86_thread_state_t);
|
||||||
#elif defined(ARCH_CPU_ARM64)
|
#elif defined(ARCH_CPU_ARM64)
|
||||||
thread_state_flavor_t flavor = ARM_UNIFIED_THREAD_STATE;
|
thread_state_flavor_t flavor = ARM_UNIFIED_THREAD_STATE;
|
||||||
mach_msg_type_number_t state_count = ARM_UNIFIED_THREAD_STATE_COUNT;
|
|
||||||
arm_unified_thread_state_t state = {};
|
arm_unified_thread_state_t state = {};
|
||||||
state.ash.flavor = ARM_THREAD_STATE64;
|
state.ash.flavor = ARM_THREAD_STATE64;
|
||||||
state.ash.count = ARM_THREAD_STATE64_COUNT;
|
state.ash.count = ARM_THREAD_STATE64_COUNT;
|
||||||
@ -297,12 +295,10 @@ class ProcessSnapshotIOSIntermediateDumpTest : public testing::Test {
|
|||||||
size_t state_length = sizeof(arm_unified_thread_state_t);
|
size_t state_length = sizeof(arm_unified_thread_state_t);
|
||||||
#endif
|
#endif
|
||||||
EXPECT_TRUE(writer->AddProperty(Key::kException, &exception));
|
EXPECT_TRUE(writer->AddProperty(Key::kException, &exception));
|
||||||
EXPECT_TRUE(writer->AddProperty(Key::kCode, code, code_count));
|
EXPECT_TRUE(writer->AddProperty(Key::kCodes, code, code_count));
|
||||||
EXPECT_TRUE(writer->AddProperty(Key::kCodeCount, &code_count));
|
|
||||||
EXPECT_TRUE(writer->AddProperty(Key::kFlavor, &flavor));
|
EXPECT_TRUE(writer->AddProperty(Key::kFlavor, &flavor));
|
||||||
EXPECT_TRUE(writer->AddPropertyBytes(
|
EXPECT_TRUE(writer->AddPropertyBytes(
|
||||||
Key::kState, reinterpret_cast<const void*>(&state), state_length));
|
Key::kState, reinterpret_cast<const void*>(&state), state_length));
|
||||||
EXPECT_TRUE(writer->AddProperty(Key::kStateCount, &state_count));
|
|
||||||
uint64_t thread_id = 1;
|
uint64_t thread_id = 1;
|
||||||
EXPECT_TRUE(writer->AddProperty(Key::kThreadID, &thread_id));
|
EXPECT_TRUE(writer->AddProperty(Key::kThreadID, &thread_id));
|
||||||
}
|
}
|
||||||
|
@ -701,7 +701,7 @@ void ProcessReaderMac::LocateRedZone(mach_vm_address_t* const start_address,
|
|||||||
#if defined(ARCH_CPU_X86_FAMILY)
|
#if defined(ARCH_CPU_X86_FAMILY)
|
||||||
if (Is64Bit()) {
|
if (Is64Bit()) {
|
||||||
// x86_64 has a red zone. See AMD64 ABI 0.99.8,
|
// x86_64 has a red zone. See AMD64 ABI 0.99.8,
|
||||||
// https://raw.githubusercontent.com/wiki/hjl-tools/x86-psABI/x86-64-psABI-r252.pdf#page=19,
|
// https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/uploads/01de35b2c8adc7545de52604cc45d942/x86-64-psABI-2021-05-20.pdf#page=23.
|
||||||
// section 3.2.2, “The Stack Frame”.
|
// section 3.2.2, “The Stack Frame”.
|
||||||
constexpr mach_vm_size_t kRedZoneSize = 128;
|
constexpr mach_vm_size_t kRedZoneSize = 128;
|
||||||
mach_vm_address_t red_zone_base =
|
mach_vm_address_t red_zone_base =
|
||||||
|
@ -23,7 +23,8 @@
|
|||||||
//
|
//
|
||||||
// This file is included by snapshot/mac/process_types.h and
|
// This file is included by snapshot/mac/process_types.h and
|
||||||
// snapshot/mac/process_types.cc to produce process type struct definitions and
|
// snapshot/mac/process_types.cc to produce process type struct definitions and
|
||||||
// accessors.
|
// accessors. This file is also used by the iOS in process handler to read both
|
||||||
|
// messages in client/ios_handler/in_process_intermediate_dump_handler.cc.
|
||||||
|
|
||||||
// Client Mach-O images will contain a __DATA,__crash_info section formatted
|
// Client Mach-O images will contain a __DATA,__crash_info section formatted
|
||||||
// according to this structure.
|
// according to this structure.
|
||||||
|
@ -26,12 +26,10 @@ namespace internal {
|
|||||||
TD(kInvalid, 0) \
|
TD(kInvalid, 0) \
|
||||||
TD(kVersion, 1) \
|
TD(kVersion, 1) \
|
||||||
TD(kMachException, 1000) \
|
TD(kMachException, 1000) \
|
||||||
TD(kCode, 1001) \
|
TD(kCodes, 1001) \
|
||||||
TD(kCodeCount, 1002) \
|
TD(kException, 1002) \
|
||||||
TD(kException, 1003) \
|
TD(kFlavor, 1003) \
|
||||||
TD(kFlavor, 1004) \
|
TD(kState, 1004) \
|
||||||
TD(kState, 1005) \
|
|
||||||
TD(kStateCount, 1006) \
|
|
||||||
TD(kSignalException, 2000) \
|
TD(kSignalException, 2000) \
|
||||||
TD(kSignalNumber, 2001) \
|
TD(kSignalNumber, 2001) \
|
||||||
TD(kSignalCode, 2002) \
|
TD(kSignalCode, 2002) \
|
||||||
|
@ -29,16 +29,16 @@ class IOSSystemDataCollector {
|
|||||||
~IOSSystemDataCollector();
|
~IOSSystemDataCollector();
|
||||||
|
|
||||||
void OSVersion(int* major, int* minor, int* bugfix) const;
|
void OSVersion(int* major, int* minor, int* bugfix) const;
|
||||||
std::string MachineDescription() const { return machine_description_; }
|
const std::string& MachineDescription() const { return machine_description_; }
|
||||||
int ProcessorCount() const { return processor_count_; }
|
int ProcessorCount() const { return processor_count_; }
|
||||||
std::string Build() const { return build_; }
|
const std::string& Build() const { return build_; }
|
||||||
std::string CPUVendor() const { return cpu_vendor_; }
|
const std::string& CPUVendor() const { return cpu_vendor_; }
|
||||||
bool HasDaylightSavingTime() const { return has_next_daylight_saving_time_; }
|
bool HasDaylightSavingTime() const { return has_next_daylight_saving_time_; }
|
||||||
bool IsDaylightSavingTime() const { return is_daylight_saving_time_; }
|
bool IsDaylightSavingTime() const { return is_daylight_saving_time_; }
|
||||||
int StandardOffsetSeconds() const { return standard_offset_seconds_; }
|
int StandardOffsetSeconds() const { return standard_offset_seconds_; }
|
||||||
int DaylightOffsetSeconds() const { return daylight_offset_seconds_; }
|
int DaylightOffsetSeconds() const { return daylight_offset_seconds_; }
|
||||||
std::string StandardName() const { return standard_name_; }
|
const std::string& StandardName() const { return standard_name_; }
|
||||||
std::string DaylightName() const { return daylight_name_; }
|
const std::string& DaylightName() const { return daylight_name_; }
|
||||||
|
|
||||||
// Currently unused by minidump.
|
// Currently unused by minidump.
|
||||||
int Orientation() const { return orientation_; }
|
int Orientation() const { return orientation_; }
|
||||||
|
@ -53,7 +53,7 @@ bool ScopedVMReadInternal::Read(const void* data, const size_t data_length) {
|
|||||||
data_ = vm_read_data_ + (data_address - page_region_address);
|
data_ = vm_read_data_ + (data_address - page_region_address);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
CRASHPAD_RAW_LOG_ERROR(kr, "vm_read");
|
// It's expected that this will sometimes fail. Don't log here.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,13 +70,22 @@ class ScopedVMRead {
|
|||||||
//! \param[in] data Memory to be read by vm_read.
|
//! \param[in] data Memory to be read by vm_read.
|
||||||
//! \param[in] count Length of \a data.
|
//! \param[in] count Length of \a data.
|
||||||
//!
|
//!
|
||||||
//! \return `true` if all the data was read. Logs an error and returns false
|
//! \return `true` if all \a data was read. Returns false on failure.
|
||||||
//! on failure
|
|
||||||
bool Read(const void* data, size_t count = 1) {
|
bool Read(const void* data, size_t count = 1) {
|
||||||
size_t data_length = count * sizeof(T);
|
size_t data_length = count * sizeof(T);
|
||||||
return internal_.Read(data, data_length);
|
return internal_.Read(data, data_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//! \brief Releases any previously read data and vm_reads address.
|
||||||
|
//!
|
||||||
|
//! \param[in] address Address of memory to be read by vm_read.
|
||||||
|
//! \param[in] count Length of \a data.
|
||||||
|
//!
|
||||||
|
//! \return `true` if all of \a address was read. Returns false on failure.
|
||||||
|
bool Read(vm_address_t address, size_t count = 1) {
|
||||||
|
return Read(reinterpret_cast<T*>(address), count);
|
||||||
|
}
|
||||||
|
|
||||||
//! \brief Returns the pointer to memory safe to read during the in-process
|
//! \brief Returns the pointer to memory safe to read during the in-process
|
||||||
//! crash handler.
|
//! crash handler.
|
||||||
T* operator->() const { return get(); }
|
T* operator->() const { return get(); }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user