From 84b44610cfd483fed7bfd3a5627461025be3aeb2 Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Wed, 29 Oct 2014 11:38:49 -0400 Subject: [PATCH] minidump: Add InitializeFromSnapshot() for MinidumpModuleWriter and MinidumpModuleListWriter. TEST=minidump_test R=rsesek@chromium.org Review URL: https://codereview.chromium.org/682263002 --- minidump/minidump_module_writer.cc | 77 +++++++++++++ minidump/minidump_module_writer.h | 32 ++++++ minidump/minidump_module_writer_test.cc | 144 +++++++++++++++++++++++- 3 files changed, 252 insertions(+), 1 deletion(-) diff --git a/minidump/minidump_module_writer.cc b/minidump/minidump_module_writer.cc index 0543e3a6..433121ba 100644 --- a/minidump/minidump_module_writer.cc +++ b/minidump/minidump_module_writer.cc @@ -16,10 +16,14 @@ #include +#include + #include "base/logging.h" #include "minidump/minidump_string_writer.h" #include "minidump/minidump_writer_util.h" +#include "snapshot/module_snapshot.h" #include "util/file/file_writer.h" +#include "util/numeric/in_range_cast.h" #include "util/numeric/safe_assignment.h" namespace crashpad { @@ -96,6 +100,25 @@ MinidumpModuleCodeViewRecordPDB70Writer:: ~MinidumpModuleCodeViewRecordPDB70Writer() { } +void MinidumpModuleCodeViewRecordPDB70Writer::InitializeFromSnapshot( + const ModuleSnapshot* module_snapshot) { + DCHECK_EQ(state(), kStateMutable); + + std::string name = module_snapshot->Name(); + std::string leaf_name; + size_t last_slash = name.find_last_of('/'); + if (last_slash != std::string::npos) { + leaf_name = name.substr(last_slash + 1); + } else { + leaf_name = name; + } + SetPDBName(leaf_name); + + UUID uuid; + module_snapshot->UUID(&uuid); + SetUUIDAndAge(uuid, 0); +} + MinidumpModuleMiscDebugRecordWriter::MinidumpModuleMiscDebugRecordWriter() : internal::MinidumpWritable(), image_debug_misc_(), @@ -186,6 +209,48 @@ MinidumpModuleWriter::MinidumpModuleWriter() MinidumpModuleWriter::~MinidumpModuleWriter() { } +void MinidumpModuleWriter::InitializeFromSnapshot( + const ModuleSnapshot* module_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!name_); + DCHECK(!codeview_record_); + DCHECK(!misc_debug_record_); + + SetName(module_snapshot->Name()); + + SetImageBaseAddress(module_snapshot->Address()); + SetImageSize(InRangeCast(module_snapshot->Size(), + std::numeric_limits::max())); + SetTimestamp(module_snapshot->Timestamp()); + + uint16_t v[4]; + module_snapshot->FileVersion(&v[0], &v[1], &v[2], &v[3]); + SetFileVersion(v[0], v[1], v[2], v[3]); + + module_snapshot->SourceVersion(&v[0], &v[1], &v[2], &v[3]); + SetProductVersion(v[0], v[1], v[2], v[3]); + + uint32_t file_type; + switch (module_snapshot->GetModuleType()) { + case ModuleSnapshot::kModuleTypeExecutable: + file_type = VFT_APP; + break; + case ModuleSnapshot::kModuleTypeSharedLibrary: + case ModuleSnapshot::kModuleTypeLoadableModule: + file_type = VFT_DLL; + break; + default: + file_type = VFT_UNKNOWN; + break; + } + SetFileTypeAndSubtype(file_type, VFT2_UNKNOWN); + + auto codeview_record = + make_scoped_ptr(new MinidumpModuleCodeViewRecordPDB70Writer()); + codeview_record->InitializeFromSnapshot(module_snapshot); + SetCodeViewRecord(codeview_record.Pass()); +} + const MINIDUMP_MODULE* MinidumpModuleWriter::MinidumpModule() const { DCHECK_EQ(state(), kStateWritable); @@ -316,6 +381,18 @@ MinidumpModuleListWriter::MinidumpModuleListWriter() MinidumpModuleListWriter::~MinidumpModuleListWriter() { } +void MinidumpModuleListWriter::InitializeFromSnapshot( + const std::vector& module_snapshots) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(modules_.empty()); + + for (const ModuleSnapshot* module_snapshot : module_snapshots) { + auto module = make_scoped_ptr(new MinidumpModuleWriter()); + module->InitializeFromSnapshot(module_snapshot); + AddModule(module.Pass()); + } +} + void MinidumpModuleListWriter::AddModule( scoped_ptr module) { DCHECK_EQ(state(), kStateMutable); diff --git a/minidump/minidump_module_writer.h b/minidump/minidump_module_writer.h index 4ec6ed46..9fb10c2c 100644 --- a/minidump/minidump_module_writer.h +++ b/minidump/minidump_module_writer.h @@ -32,6 +32,8 @@ namespace crashpad { +class ModuleSnapshot; + namespace internal { class MinidumpUTF16StringWriter; } // namespace internal @@ -117,6 +119,16 @@ class MinidumpModuleCodeViewRecordPDB70Writer final ~MinidumpModuleCodeViewRecordPDB70Writer() override; + //! \brief Initializes the MinidumpModuleCodeViewRecordPDB70 based on \a + //! module_snapshot. + //! + //! \param[in] module_snapshot The module 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 ModuleSnapshot* module_snapshot); + //! \brief Sets MinidumpModuleCodeViewRecordPDB70::uuid and //! MinidumpModuleCodeViewRecordPDB70::age. void SetUUIDAndAge(const UUID& uuid, uint32_t age) { @@ -176,6 +188,15 @@ class MinidumpModuleWriter final : public internal::MinidumpWritable { MinidumpModuleWriter(); ~MinidumpModuleWriter() override; + //! \brief Initializes the MINIDUMP_MODULE based on \a module_snapshot. + //! + //! \param[in] module_snapshot The module 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 ModuleSnapshot* module_snapshot); + //! \brief Returns a MINIDUMP_MODULE referencing this object’s data. //! //! This method is expected to be called by a MinidumpModuleListWriter in @@ -294,6 +315,17 @@ class MinidumpModuleListWriter final : public internal::MinidumpStreamWriter { MinidumpModuleListWriter(); ~MinidumpModuleListWriter() override; + //! \brief Adds an initialized MINIDUMP_MODULE for each module in \a + //! module_snapshots to the MINIDUMP_MODULE_LIST. + //! + //! \param[in] module_snapshots The module snapshots 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 std::vector& module_snapshots); + //! \brief Adds a MinidumpModuleWriter to the MINIDUMP_MODULE_LIST. //! //! This object takes ownership of \a module and becomes its parent in the diff --git a/minidump/minidump_module_writer_test.cc b/minidump/minidump_module_writer_test.cc index ed5f10c8..ae93ad22 100644 --- a/minidump/minidump_module_writer_test.cc +++ b/minidump/minidump_module_writer_test.cc @@ -19,6 +19,7 @@ #include #include +#include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "gtest/gtest.h" #include "minidump/minidump_extensions.h" @@ -26,8 +27,10 @@ #include "minidump/test/minidump_file_writer_test_util.h" #include "minidump/test/minidump_string_writer_test_util.h" #include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_module_snapshot.h" #include "util/file/string_file_writer.h" #include "util/misc/uuid.h" +#include "util/stdlib/pointer_container.h" namespace crashpad { namespace test { @@ -606,7 +609,146 @@ TEST(MinidumpModuleWriter, ThreeModules) { } } -TEST(MinidumpSystemInfoWriterDeathTest, NoModuleName) { +void InitializeTestModuleSnapshotFromMinidumpModule( + TestModuleSnapshot* module_snapshot, + const MINIDUMP_MODULE& minidump_module, + const std::string& name, + const crashpad::UUID& uuid) { + module_snapshot->SetName(name); + + module_snapshot->SetAddressAndSize(minidump_module.BaseOfImage, + minidump_module.SizeOfImage); + module_snapshot->SetTimestamp(minidump_module.TimeDateStamp); + module_snapshot->SetFileVersion( + minidump_module.VersionInfo.dwFileVersionMS >> 16, + minidump_module.VersionInfo.dwFileVersionMS & 0xffff, + minidump_module.VersionInfo.dwFileVersionLS >> 16, + minidump_module.VersionInfo.dwFileVersionLS & 0xffff); + module_snapshot->SetSourceVersion( + minidump_module.VersionInfo.dwProductVersionMS >> 16, + minidump_module.VersionInfo.dwProductVersionMS & 0xffff, + minidump_module.VersionInfo.dwProductVersionLS >> 16, + minidump_module.VersionInfo.dwProductVersionLS & 0xffff); + + ModuleSnapshot::ModuleType module_type; + switch (minidump_module.VersionInfo.dwFileType) { + case VFT_APP: + module_type = ModuleSnapshot::kModuleTypeExecutable; + break; + case VFT_DLL: + module_type = ModuleSnapshot::kModuleTypeSharedLibrary; + break; + default: + module_type = ModuleSnapshot::kModuleTypeUnknown; + break; + } + module_snapshot->SetModuleType(module_type); + + module_snapshot->SetUUID(uuid); +} + +TEST(MinidumpModuleWriter, InitializeFromSnapshot) { + MINIDUMP_MODULE expect_modules[3] = {}; + const char* module_paths[3] = {}; + const char* module_names[3] = {}; + UUID uuids[3] = {}; + + static_assert(arraysize(expect_modules) == arraysize(module_paths), + "array sizes must be equal"); + static_assert(arraysize(expect_modules) == arraysize(module_names), + "array sizes must be equal"); + static_assert(arraysize(expect_modules) == arraysize(uuids), + "array sizes must be equal"); + + expect_modules[0].BaseOfImage = 0x100101000; + expect_modules[0].SizeOfImage = 0xf000; + expect_modules[0].TimeDateStamp = 0x01234567; + expect_modules[0].VersionInfo.dwFileVersionMS = 0x00010002; + expect_modules[0].VersionInfo.dwFileVersionLS = 0x00030004; + expect_modules[0].VersionInfo.dwProductVersionMS = 0x00050006; + expect_modules[0].VersionInfo.dwProductVersionLS = 0x00070008; + expect_modules[0].VersionInfo.dwFileType = VFT_APP; + module_paths[0] = "/usr/bin/true"; + module_names[0] = "true"; + const uint8_t kUUIDBytes0[16] = + {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + uuids[0].InitializeFromBytes(kUUIDBytes0); + + expect_modules[1].BaseOfImage = 0x200202000; + expect_modules[1].SizeOfImage = 0x1e1000; + expect_modules[1].TimeDateStamp = 0x89abcdef; + expect_modules[1].VersionInfo.dwFileVersionMS = 0x0009000a; + expect_modules[1].VersionInfo.dwFileVersionLS = 0x000b000c; + expect_modules[1].VersionInfo.dwProductVersionMS = 0x000d000e; + expect_modules[1].VersionInfo.dwProductVersionLS = 0x000f0000; + expect_modules[1].VersionInfo.dwFileType = VFT_DLL; + module_paths[1] = "/usr/lib/libSystem.B.dylib"; + module_names[1] = "libSystem.B.dylib"; + const uint8_t kUUIDBytes1[16] = + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + uuids[1].InitializeFromBytes(kUUIDBytes1); + + expect_modules[2].BaseOfImage = 0x300303000; + expect_modules[2].SizeOfImage = 0x2d000; + expect_modules[2].TimeDateStamp = 0x76543210; + expect_modules[2].VersionInfo.dwFileVersionMS = 0x11112222; + expect_modules[2].VersionInfo.dwFileVersionLS = 0x33334444; + expect_modules[2].VersionInfo.dwProductVersionMS = 0x9999aaaa; + expect_modules[2].VersionInfo.dwProductVersionLS = 0xbbbbcccc; + expect_modules[2].VersionInfo.dwFileType = VFT_UNKNOWN; + module_paths[2] = "/usr/lib/dyld"; + module_names[2] = "dyld"; + const uint8_t kUUIDBytes2[16] = + {0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0}; + uuids[2].InitializeFromBytes(kUUIDBytes2); + + PointerVector module_snapshots_owner; + std::vector module_snapshots; + for (size_t index = 0; index < arraysize(expect_modules); ++index) { + TestModuleSnapshot* module_snapshot = new TestModuleSnapshot(); + module_snapshots_owner.push_back(module_snapshot); + InitializeTestModuleSnapshotFromMinidumpModule(module_snapshot, + expect_modules[index], + module_paths[index], + uuids[index]); + module_snapshots.push_back(module_snapshot); + } + + auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter()); + module_list_writer->InitializeFromSnapshot(module_snapshots); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(module_list_writer.Pass()); + + StringFileWriter file_writer; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer)); + + const MINIDUMP_MODULE_LIST* module_list; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(file_writer.string(), &module_list)); + + ASSERT_EQ(3u, module_list->NumberOfModules); + + for (size_t index = 0; index < module_list->NumberOfModules; ++index) { + SCOPED_TRACE(base::StringPrintf("index %zu", index)); + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expect_modules[index], + &module_list->Modules[index], + file_writer.string(), + module_paths[index], + module_names[index], + &uuids[index], + 0, + 0, + nullptr, + 0, + false)); + } +} + +TEST(MinidumpModuleWriterDeathTest, NoModuleName) { MinidumpFileWriter minidump_file_writer; auto module_list_writer = make_scoped_ptr(new MinidumpModuleListWriter()); auto module_writer = make_scoped_ptr(new MinidumpModuleWriter());