diff --git a/minidump/minidump_exception_writer.cc b/minidump/minidump_exception_writer.cc index b88f46ae..87382b22 100644 --- a/minidump/minidump_exception_writer.cc +++ b/minidump/minidump_exception_writer.cc @@ -32,12 +32,12 @@ MinidumpExceptionWriter::~MinidumpExceptionWriter() { void MinidumpExceptionWriter::InitializeFromSnapshot( const ExceptionSnapshot* exception_snapshot, - const MinidumpThreadIDMap* thread_id_map) { + const MinidumpThreadIDMap& thread_id_map) { DCHECK_EQ(state(), kStateMutable); DCHECK(!context_); - auto thread_id_it = thread_id_map->find(exception_snapshot->ThreadID()); - DCHECK(thread_id_it != thread_id_map->end()); + auto thread_id_it = thread_id_map.find(exception_snapshot->ThreadID()); + DCHECK(thread_id_it != thread_id_map.end()); SetThreadID(thread_id_it->second); SetExceptionCode(exception_snapshot->Exception()); diff --git a/minidump/minidump_exception_writer.h b/minidump/minidump_exception_writer.h index 1578ad58..a094a471 100644 --- a/minidump/minidump_exception_writer.h +++ b/minidump/minidump_exception_writer.h @@ -49,7 +49,7 @@ class MinidumpExceptionWriter final : public internal::MinidumpStreamWriter { //! this method, and it is not normally necessary to call any mutator //! methods after this method. void InitializeFromSnapshot(const ExceptionSnapshot* exception_snapshot, - const MinidumpThreadIDMap* thread_id_map); + const MinidumpThreadIDMap& thread_id_map); //! \brief Arranges for MINIDUMP_EXCEPTION_STREAM::ThreadContext to point to //! the CPU context to be written by \a context. diff --git a/minidump/minidump_exception_writer_test.cc b/minidump/minidump_exception_writer_test.cc index fa2f14d2..8c708c85 100644 --- a/minidump/minidump_exception_writer_test.cc +++ b/minidump/minidump_exception_writer_test.cc @@ -232,7 +232,7 @@ TEST(MinidumpExceptionWriter, InitializeFromSnapshot) { thread_id_map[kThreadID] = expect_exception.ThreadId; auto exception_writer = make_scoped_ptr(new MinidumpExceptionWriter()); - exception_writer->InitializeFromSnapshot(&exception_snapshot, &thread_id_map); + exception_writer->InitializeFromSnapshot(&exception_snapshot, thread_id_map); MinidumpFileWriter minidump_file_writer; minidump_file_writer.AddStream(exception_writer.Pass()); diff --git a/minidump/minidump_file_writer.cc b/minidump/minidump_file_writer.cc index a3a169dd..ef45a660 100644 --- a/minidump/minidump_file_writer.cc +++ b/minidump/minidump_file_writer.cc @@ -15,7 +15,16 @@ #include "minidump/minidump_file_writer.h" #include "base/logging.h" +#include "minidump/minidump_crashpad_info_writer.h" +#include "minidump/minidump_exception_writer.h" +#include "minidump/minidump_memory_writer.h" +#include "minidump/minidump_misc_info_writer.h" +#include "minidump/minidump_module_writer.h" +#include "minidump/minidump_system_info_writer.h" +#include "minidump/minidump_thread_id_map.h" +#include "minidump/minidump_thread_writer.h" #include "minidump/minidump_writer_util.h" +#include "snapshot/process_snapshot.h" #include "util/file/file_writer.h" #include "util/numeric/safe_assignment.h" @@ -36,6 +45,63 @@ MinidumpFileWriter::MinidumpFileWriter() MinidumpFileWriter::~MinidumpFileWriter() { } +void MinidumpFileWriter::InitializeFromSnapshot( + const ProcessSnapshot* process_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(header_.Signature, 0u); + DCHECK_EQ(header_.TimeDateStamp, 0u); + DCHECK_EQ(header_.Flags, MiniDumpNormal); + DCHECK(streams_.empty()); + + // This time is truncated to an integer number of seconds, not rounded, for + // compatibility with the truncation of process_snapshot->ProcessStartTime() + // done by MinidumpMiscInfoWriter::InitializeFromSnapshot(). Handling both + // timestamps in the same way allows the highest-fidelity computation of + // process uptime as the difference between the two values. + timeval snapshot_time; + process_snapshot->SnapshotTime(&snapshot_time); + SetTimestamp(snapshot_time.tv_sec); + + const SystemSnapshot* system_snapshot = process_snapshot->System(); + auto system_info = make_scoped_ptr(new MinidumpSystemInfoWriter()); + system_info->InitializeFromSnapshot(system_snapshot); + AddStream(system_info.Pass()); + + auto misc_info = make_scoped_ptr(new MinidumpMiscInfoWriter()); + misc_info->InitializeFromSnapshot(process_snapshot); + AddStream(misc_info.Pass()); + + auto memory_list = make_scoped_ptr(new MinidumpMemoryListWriter()); + auto thread_list = make_scoped_ptr(new MinidumpThreadListWriter()); + thread_list->SetMemoryListWriter(memory_list.get()); + MinidumpThreadIDMap thread_id_map; + thread_list->InitializeFromSnapshot(process_snapshot->Threads(), + &thread_id_map); + AddStream(thread_list.Pass()); + + const ExceptionSnapshot* exception_snapshot = process_snapshot->Exception(); + if (exception_snapshot) { + auto exception = make_scoped_ptr(new MinidumpExceptionWriter()); + exception->InitializeFromSnapshot(exception_snapshot, thread_id_map); + AddStream(exception.Pass()); + } + + auto module_list = make_scoped_ptr(new MinidumpModuleListWriter()); + module_list->InitializeFromSnapshot(process_snapshot->Modules()); + AddStream(module_list.Pass()); + + auto crashpad_info = make_scoped_ptr(new MinidumpCrashpadInfoWriter()); + crashpad_info->InitializeFromSnapshot(process_snapshot); + + // Since the MinidumpCrashpadInfo stream is an extension, it’s safe to not add + // it to the minidump file if it wouldn’t carry any useful information. + if (crashpad_info->IsUseful()) { + AddStream(crashpad_info.Pass()); + } + + AddStream(memory_list.Pass()); +} + void MinidumpFileWriter::SetTimestamp(time_t timestamp) { DCHECK_EQ(state(), kStateMutable); diff --git a/minidump/minidump_file_writer.h b/minidump/minidump_file_writer.h index 22b65fc4..ccaaa886 100644 --- a/minidump/minidump_file_writer.h +++ b/minidump/minidump_file_writer.h @@ -30,6 +30,8 @@ namespace crashpad { +class ProcessSnapshot; + //! \brief The root-level object in a minidump file. //! //! This object writes a MINIDUMP_HEADER and list of MINIDUMP_DIRECTORY entries @@ -39,6 +41,34 @@ class MinidumpFileWriter final : public internal::MinidumpWritable { MinidumpFileWriter(); ~MinidumpFileWriter() override; + //! \brief Initializes the MinidumpFileWriter and populates it with + //! appropriate child streams based on \a process_snapshot. + //! + //! This method will add additional streams to the minidump file as children + //! of the MinidumpFileWriter object and as pointees of the top-level + //! MINIDUMP_DIRECTORY. To do so, it will obtain other snapshot information + //! from \a process_snapshot, such as a SystemSnapshot, lists of + //! ThreadSnapshot and ModuleSnapshot objects, and, if available, an + //! ExceptionSnapshot. + //! + //! The streams are added in the order that they are expected to be most + //! useful to minidump readers, to improve data locality and minimize seeking. + //! The streams are added in this order: + //! - kMinidumpStreamTypeSystemInfo + //! - kMinidumpStreamTypeMiscInfo + //! - kMinidumpStreamTypeThreadList + //! - kMinidumpStreamTypeException (if present) + //! - kMinidumpStreamTypeModuleList + //! - kMinidumpStreamTypeCrashpadInfo (if present) + //! - kMinidumpStreamTypeMemoryList + //! + //! \param[in] process_snapshot The process snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ProcessSnapshot* process_snapshot); + //! \brief Sets MINIDUMP_HEADER::Timestamp. //! //! \note Valid in #kStateMutable. diff --git a/minidump/minidump_file_writer_test.cc b/minidump/minidump_file_writer_test.cc index c4a9064a..c35f6af2 100644 --- a/minidump/minidump_file_writer_test.cc +++ b/minidump/minidump_file_writer_test.cc @@ -24,6 +24,12 @@ #include "minidump/minidump_writable.h" #include "minidump/test/minidump_file_writer_test_util.h" #include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_cpu_context.h" +#include "snapshot/test/test_exception_snapshot.h" +#include "snapshot/test/test_module_snapshot.h" +#include "snapshot/test/test_process_snapshot.h" +#include "snapshot/test/test_system_snapshot.h" +#include "snapshot/test/test_thread_snapshot.h" #include "util/file/file_writer.h" #include "util/file/string_file_writer.h" @@ -232,6 +238,185 @@ TEST(MinidumpFileWriter, ZeroLengthStream) { EXPECT_EQ(kStreamOffset, directory[0].Location.Rva); } +TEST(MinidumpFileWriter, InitializeFromSnapshot_Basic) { + const uint32_t kSnapshotTime = 0x4976043c; + const timeval kSnapshotTimeval = { implicit_cast(kSnapshotTime), 0 }; + + TestProcessSnapshot process_snapshot; + process_snapshot.SetSnapshotTime(kSnapshotTimeval); + + auto system_snapshot = make_scoped_ptr(new TestSystemSnapshot()); + system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64); + system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX); + process_snapshot.SetSystem(system_snapshot.Pass()); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.InitializeFromSnapshot(&process_snapshot); + + StringFileWriter file_writer; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer)); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_writer.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 5, kSnapshotTime)); + ASSERT_TRUE(directory); + + EXPECT_EQ(kMinidumpStreamTypeSystemInfo, directory[0].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[0].Location)); + + EXPECT_EQ(kMinidumpStreamTypeMiscInfo, directory[1].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[1].Location)); + + EXPECT_EQ(kMinidumpStreamTypeThreadList, directory[2].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[2].Location)); + + EXPECT_EQ(kMinidumpStreamTypeModuleList, directory[3].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[3].Location)); + + EXPECT_EQ(kMinidumpStreamTypeMemoryList, directory[4].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[4].Location)); +} + +TEST(MinidumpFileWriter, InitializeFromSnapshot_Exception) { + // In a 32-bit environment, this will give a “timestamp out of range” warning, + // but the test should complete without failure. + const uint32_t kSnapshotTime = 0xfd469ab8; + const timeval kSnapshotTimeval = { implicit_cast(kSnapshotTime), 0 }; + + TestProcessSnapshot process_snapshot; + process_snapshot.SetSnapshotTime(kSnapshotTimeval); + + auto system_snapshot = make_scoped_ptr(new TestSystemSnapshot()); + system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64); + system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX); + process_snapshot.SetSystem(system_snapshot.Pass()); + + auto thread_snapshot = make_scoped_ptr(new TestThreadSnapshot()); + InitializeCPUContextX86_64(thread_snapshot->MutableContext(), 5); + process_snapshot.AddThread(thread_snapshot.Pass()); + + auto exception_snapshot = make_scoped_ptr(new TestExceptionSnapshot()); + InitializeCPUContextX86_64(exception_snapshot->MutableContext(), 11); + process_snapshot.SetException(exception_snapshot.Pass()); + + // The module does not have anything that needs to be represented in a + // MinidumpModuleCrashpadInfo structure, so no such structure is expected to + // be present, which will in turn suppress the addition of a + // MinidumpCrashpadInfo stream. + auto module_snapshot = make_scoped_ptr(new TestModuleSnapshot()); + process_snapshot.AddModule(module_snapshot.Pass()); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.InitializeFromSnapshot(&process_snapshot); + + StringFileWriter file_writer; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer)); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_writer.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 6, kSnapshotTime)); + ASSERT_TRUE(directory); + + EXPECT_EQ(kMinidumpStreamTypeSystemInfo, directory[0].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[0].Location)); + + EXPECT_EQ(kMinidumpStreamTypeMiscInfo, directory[1].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[1].Location)); + + EXPECT_EQ(kMinidumpStreamTypeThreadList, directory[2].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[2].Location)); + + EXPECT_EQ(kMinidumpStreamTypeException, directory[3].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[3].Location)); + + EXPECT_EQ(kMinidumpStreamTypeModuleList, directory[4].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[4].Location)); + + EXPECT_EQ(kMinidumpStreamTypeMemoryList, directory[5].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[5].Location)); +} + +TEST(MinidumpFileWriter, InitializeFromSnapshot_CrashpadInfo) { + const uint32_t kSnapshotTime = 0x15393bd3; + const timeval kSnapshotTimeval = { implicit_cast(kSnapshotTime), 0 }; + + TestProcessSnapshot process_snapshot; + process_snapshot.SetSnapshotTime(kSnapshotTimeval); + + auto system_snapshot = make_scoped_ptr(new TestSystemSnapshot()); + system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64); + system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX); + process_snapshot.SetSystem(system_snapshot.Pass()); + + auto thread_snapshot = make_scoped_ptr(new TestThreadSnapshot()); + InitializeCPUContextX86_64(thread_snapshot->MutableContext(), 5); + process_snapshot.AddThread(thread_snapshot.Pass()); + + auto exception_snapshot = make_scoped_ptr(new TestExceptionSnapshot()); + InitializeCPUContextX86_64(exception_snapshot->MutableContext(), 11); + process_snapshot.SetException(exception_snapshot.Pass()); + + // The module needs an annotation for the MinidumpCrashpadInfo stream to be + // considered useful and be included. + auto module_snapshot = make_scoped_ptr(new TestModuleSnapshot()); + std::vector annotations_list(1, std::string("annotation")); + module_snapshot->SetAnnotationsVector(annotations_list); + process_snapshot.AddModule(module_snapshot.Pass()); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.InitializeFromSnapshot(&process_snapshot); + + StringFileWriter file_writer; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer)); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_writer.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 7, kSnapshotTime)); + ASSERT_TRUE(directory); + + EXPECT_EQ(kMinidumpStreamTypeSystemInfo, directory[0].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[0].Location)); + + EXPECT_EQ(kMinidumpStreamTypeMiscInfo, directory[1].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[1].Location)); + + EXPECT_EQ(kMinidumpStreamTypeThreadList, directory[2].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[2].Location)); + + EXPECT_EQ(kMinidumpStreamTypeException, directory[3].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[3].Location)); + + EXPECT_EQ(kMinidumpStreamTypeModuleList, directory[4].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[4].Location)); + + EXPECT_EQ(kMinidumpStreamTypeCrashpadInfo, directory[5].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[5].Location)); + + EXPECT_EQ(kMinidumpStreamTypeMemoryList, directory[6].StreamType); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + file_writer.string(), directory[6].Location)); +} + TEST(MinidumpFileWriterDeathTest, SameStreamType) { MinidumpFileWriter minidump_file;