diff --git a/client/crashpad_info.cc b/client/crashpad_info.cc index 5913be81..36471a10 100644 --- a/client/crashpad_info.cc +++ b/client/crashpad_info.cc @@ -101,7 +101,8 @@ CrashpadInfo::CrashpadInfo() gather_indirectly_referenced_memory_(TriState::kUnset), padding_0_(0), extra_memory_ranges_(nullptr), - simple_annotations_(nullptr) + simple_annotations_(nullptr), + user_data_minidump_stream_head_(nullptr) #if !defined(NDEBUG) && defined(OS_WIN) , invalid_read_detection_(0xbadc0de) @@ -109,4 +110,17 @@ CrashpadInfo::CrashpadInfo() { } +void CrashpadInfo::AddUserDataMinidumpStream(uint32_t stream_type, + const void* data, + size_t size) { + auto to_be_added = new internal::UserDataMinidumpStreamListEntry(); + to_be_added->next = base::checked_cast( + reinterpret_cast(user_data_minidump_stream_head_)); + to_be_added->stream_type = stream_type; + to_be_added->base_address = + base::checked_cast(reinterpret_cast(data)); + to_be_added->size = base::checked_cast(size); + user_data_minidump_stream_head_ = to_be_added; +} + } // namespace crashpad diff --git a/client/crashpad_info.h b/client/crashpad_info.h index 4dd61abe..a181f997 100644 --- a/client/crashpad_info.h +++ b/client/crashpad_info.h @@ -29,6 +29,29 @@ namespace crashpad { +namespace internal { + +//! \brief A linked list of blocks representing custom streams in the minidump, +//! with addresses (and size) stored as uint64_t to simplify reading from +//! the handler process. +struct UserDataMinidumpStreamListEntry { + //! \brief The address of the next entry in the linked list. + uint64_t next; + + //! \brief The base address of the memory block in the target process' address + //! space that represents the user data stream. + uint64_t base_address; + + //! \brief The size of memory block in the target process' address space that + //! represents the user data stream. + uint64_t size; + + //! \brief The stream type identifier. + uint32_t stream_type; +}; + +} // namespace internal + //! \brief A structure that can be used by a Crashpad-enabled program to //! provide information to the Crashpad crash handler. //! @@ -89,7 +112,7 @@ struct CrashpadInfo { //! that it has been disabled. //! //! The Crashpad handler should not normally be disabled. More commonly, it - //! is appropraite to disable crash report upload by calling + //! is appropriate to disable crash report upload by calling //! Settings::SetUploadsEnabled(). void set_crashpad_handler_behavior(TriState crashpad_handler_behavior) { crashpad_handler_behavior_ = crashpad_handler_behavior; @@ -130,6 +153,27 @@ struct CrashpadInfo { gather_indirectly_referenced_memory_ = gather_indirectly_referenced_memory; } + //! \brief Adds a custom stream to the minidump. + //! + //! The memory block referenced by \a data and \a size will added to the + //! minidump as separate stream with type \stream_type. The memory referred to + //! by \a data and \a size is owned by the caller and must remain valid while + //! it is in effect for the CrashpadInfo object. + //! + //! Note that streams will appear in the minidump in the reverse order to + //! which they are added. + //! + //! TODO(scottmg) This is currently only supported on Windows. + //! + //! \param[in] stream_type The stream type identifier to use. This should be + //! normally be larger than `MINIDUMP_STREAM_TYPE::LastReservedStream` + //! which is `0xffff`. + //! \param[in] data The base pointer of the stream data. + //! \param[in] size The size of the stream data. + void AddUserDataMinidumpStream(uint32_t stream_type, + const void* data, + size_t size); + enum : uint32_t { kSignature = 'CPad', }; @@ -154,6 +198,7 @@ struct CrashpadInfo { uint8_t padding_0_; SimpleAddressRangeBag* extra_memory_ranges_; // weak SimpleStringDictionary* simple_annotations_; // weak + internal::UserDataMinidumpStreamListEntry* user_data_minidump_stream_head_; #if !defined(NDEBUG) && defined(OS_WIN) uint32_t invalid_read_detection_; diff --git a/compat/non_win/dbghelp.h b/compat/non_win/dbghelp.h index 2b7c974c..71ef1f4c 100644 --- a/compat/non_win/dbghelp.h +++ b/compat/non_win/dbghelp.h @@ -176,6 +176,10 @@ enum MINIDUMP_STREAM_TYPE { //! \brief The stream type for MINIDUMP_MEMORY_INFO_LIST. MemoryInfoListStream = 16, + + //! \brief Values greater than this value will not be used by the system + //! and can be used for custom user data streams. + LastReservedStream = 0xffff, }; //! \brief Information about the CPU (or CPUs) that ran the process that the diff --git a/handler/win/crashy_test_program.cc b/handler/win/crashy_test_program.cc index a3331398..a2b74134 100644 --- a/handler/win/crashy_test_program.cc +++ b/handler/win/crashy_test_program.cc @@ -225,6 +225,18 @@ int CrashyMain(int argc, wchar_t* argv[]) { crashpad::CrashpadInfo::GetCrashpadInfo() ->set_gather_indirectly_referenced_memory(TriState::kEnabled); + std::vector data_stream1(128, 'x'); + crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream( + 222222, + reinterpret_cast(data_stream1.data()), + data_stream1.size()); + + std::vector data_stream2(4096, 'z'); + crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream( + 333333, + reinterpret_cast(data_stream2.data()), + data_stream2.size()); + AllocateMemoryOfVariousProtections(); if (InitializeCriticalSectionWithDebugInfoIfPossible( diff --git a/minidump/minidump.gyp b/minidump/minidump.gyp index 3f714668..35528060 100644 --- a/minidump/minidump.gyp +++ b/minidump/minidump.gyp @@ -72,6 +72,8 @@ 'minidump_thread_writer.h', 'minidump_unloaded_module_writer.cc', 'minidump_unloaded_module_writer.h', + 'minidump_user_stream_writer.cc', + 'minidump_user_stream_writer.h', 'minidump_writable.cc', 'minidump_writable.h', 'minidump_writer_util.cc', diff --git a/minidump/minidump_file_writer.cc b/minidump/minidump_file_writer.cc index 0c79c78d..7f8f47d5 100644 --- a/minidump/minidump_file_writer.cc +++ b/minidump/minidump_file_writer.cc @@ -28,8 +28,10 @@ #include "minidump/minidump_thread_id_map.h" #include "minidump/minidump_thread_writer.h" #include "minidump/minidump_unloaded_module_writer.h" +#include "minidump/minidump_user_stream_writer.h" #include "minidump/minidump_writer_util.h" #include "snapshot/exception_snapshot.h" +#include "snapshot/module_snapshot.h" #include "snapshot/process_snapshot.h" #include "util/file/file_writer.h" #include "util/numeric/safe_assignment.h" @@ -96,6 +98,14 @@ void MinidumpFileWriter::InitializeFromSnapshot( module_list->InitializeFromSnapshot(process_snapshot->Modules()); AddStream(std::move(module_list)); + for (const auto& module : process_snapshot->Modules()) { + for (const UserMinidumpStream* stream : module->CustomMinidumpStreams()) { + auto user_stream = make_scoped_ptr(new MinidumpUserStreamWriter()); + user_stream->InitializeFromSnapshot(stream); + AddStream(std::move(user_stream)); + } + } + auto unloaded_modules = process_snapshot->UnloadedModules(); if (!unloaded_modules.empty()) { auto unloaded_module_list = diff --git a/minidump/minidump_test.gyp b/minidump/minidump_test.gyp index 30a179f1..cdae4caa 100644 --- a/minidump/minidump_test.gyp +++ b/minidump/minidump_test.gyp @@ -50,6 +50,7 @@ 'minidump_thread_id_map_test.cc', 'minidump_thread_writer_test.cc', 'minidump_unloaded_module_writer_test.cc', + 'minidump_user_stream_writer_test.cc', 'minidump_writable_test.cc', 'test/minidump_context_test_util.cc', 'test/minidump_context_test_util.h', diff --git a/minidump/minidump_user_stream_writer.cc b/minidump/minidump_user_stream_writer.cc new file mode 100644 index 00000000..7332cd01 --- /dev/null +++ b/minidump/minidump_user_stream_writer.cc @@ -0,0 +1,73 @@ +// Copyright 2016 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 "minidump/minidump_user_stream_writer.h" + +#include "util/file/file_writer.h" + +namespace crashpad { + +MinidumpUserStreamWriter::MinidumpUserStreamWriter() + : stream_type_(0), reader_() { +} + +MinidumpUserStreamWriter::~MinidumpUserStreamWriter() { +} + +void MinidumpUserStreamWriter::InitializeFromSnapshot( + const UserMinidumpStream* stream) { + DCHECK_EQ(state(), kStateMutable); + + stream_type_ = stream->stream_type(); + if (stream->memory()) + stream->memory()->Read(&reader_); +} + +bool MinidumpUserStreamWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + DCHECK_NE(stream_type_, 0u); + return MinidumpStreamWriter::Freeze(); +} + +size_t MinidumpUserStreamWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + return reader_.size(); +} + +std::vector +MinidumpUserStreamWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + return std::vector(); +} + +bool MinidumpUserStreamWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + return file_writer->Write(reader_.data(), reader_.size()); +} + +MinidumpStreamType MinidumpUserStreamWriter::StreamType() const { + return static_cast(stream_type_); +} + +MinidumpUserStreamWriter::MemoryReader::~MemoryReader() {} + +bool MinidumpUserStreamWriter::MemoryReader::MemorySnapshotDelegateRead( + void* data, + size_t size) { + data_.resize(size); + memcpy(&data_[0], data, size); + return true; +} + +} // namespace crashpad diff --git a/minidump/minidump_user_stream_writer.h b/minidump/minidump_user_stream_writer.h new file mode 100644 index 00000000..b31247a3 --- /dev/null +++ b/minidump/minidump_user_stream_writer.h @@ -0,0 +1,77 @@ +// Copyright 2016 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_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_ + +#include +#include +#include + +#include + +#include "base/macros.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" +#include "snapshot/module_snapshot.h" + +namespace crashpad { + +//! \brief The writer for a MINIDUMP_USER_STREAM in a minidump file. +class MinidumpUserStreamWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpUserStreamWriter(); + ~MinidumpUserStreamWriter() override; + + //! \brief Initializes a MINIDUMP_USER_STREAM based on \a stream. + //! + //! \param[in] stream The memory and stream type to use as source data. + //! + //! \note Valid in #kStateMutable. + void InitializeFromSnapshot(const UserMinidumpStream* stream); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + class MemoryReader : public MemorySnapshot::Delegate { + public: + ~MemoryReader() override; + bool MemorySnapshotDelegateRead(void* data, size_t size) override; + + const void* data() const { + return reinterpret_cast(data_.data()); + } + size_t size() const { return data_.size(); } + + private: + std::vector data_; + }; + + uint32_t stream_type_; + MemoryReader reader_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpUserStreamWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_ diff --git a/minidump/minidump_user_stream_writer_test.cc b/minidump/minidump_user_stream_writer_test.cc new file mode 100644 index 00000000..ae58d825 --- /dev/null +++ b/minidump/minidump_user_stream_writer_test.cc @@ -0,0 +1,104 @@ +// Copyright 2016 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 "minidump/minidump_user_stream_writer.h" + +#include +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_memory_snapshot.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +// The user stream is expected to be the only stream. +void GetUserStream(const std::string& file_contents, + MINIDUMP_LOCATION_DESCRIPTOR* user_stream_location, + uint32_t stream_type) { + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kUserStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + const size_t kDirectoryIndex = 0; + + ASSERT_EQ(stream_type, directory[kDirectoryIndex].StreamType); + EXPECT_EQ(kUserStreamOffset, directory[kDirectoryIndex].Location.Rva); + *user_stream_location = directory[kDirectoryIndex].Location; +} + +TEST(MinidumpUserStreamWriter, NoData) { + MinidumpFileWriter minidump_file_writer; + auto user_stream_writer = make_scoped_ptr(new MinidumpUserStreamWriter()); + const uint32_t kTestStreamId = 0x123456; + auto stream = make_scoped_ptr(new UserMinidumpStream(kTestStreamId, nullptr)); + user_stream_writer->InitializeFromSnapshot(stream.get()); + minidump_file_writer.AddStream(std::move(user_stream_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY), + string_file.string().size()); + + MINIDUMP_LOCATION_DESCRIPTOR user_stream_location; + ASSERT_NO_FATAL_FAILURE(GetUserStream( + string_file.string(), &user_stream_location, kTestStreamId)); + EXPECT_EQ(0u, user_stream_location.DataSize); +} + +TEST(MinidumpUserStreamWriter, OneStream) { + MinidumpFileWriter minidump_file_writer; + auto user_stream_writer = make_scoped_ptr(new MinidumpUserStreamWriter()); + const uint32_t kTestStreamId = 0x123456; + + TestMemorySnapshot* test_data = new TestMemorySnapshot(); + test_data->SetAddress(97865); + const size_t kStreamSize = 128; + test_data->SetSize(kStreamSize); + test_data->SetValue('c'); + auto stream = + make_scoped_ptr(new UserMinidumpStream(kTestStreamId, test_data)); + user_stream_writer->InitializeFromSnapshot(stream.get()); + minidump_file_writer.AddStream(std::move(user_stream_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + kStreamSize, + string_file.string().size()); + + MINIDUMP_LOCATION_DESCRIPTOR user_stream_location; + ASSERT_NO_FATAL_FAILURE(GetUserStream( + string_file.string(), &user_stream_location, kTestStreamId)); + EXPECT_EQ(kStreamSize, user_stream_location.DataSize); + const std::string stream_data = string_file.string().substr( + user_stream_location.Rva, user_stream_location.DataSize); + EXPECT_EQ(std::string(kStreamSize, 'c'), stream_data); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/snapshot/mac/module_snapshot_mac.cc b/snapshot/mac/module_snapshot_mac.cc index 4f4df2a5..4eaa12b1 100644 --- a/snapshot/mac/module_snapshot_mac.cc +++ b/snapshot/mac/module_snapshot_mac.cc @@ -187,5 +187,10 @@ std::set> ModuleSnapshotMac::ExtraMemoryRanges() const { return std::set>(); } +std::vector +ModuleSnapshotMac::CustomMinidumpStreams() const { + return std::vector(); +} + } // namespace internal } // namespace crashpad diff --git a/snapshot/mac/module_snapshot_mac.h b/snapshot/mac/module_snapshot_mac.h index 0c6bd69a..23904a5e 100644 --- a/snapshot/mac/module_snapshot_mac.h +++ b/snapshot/mac/module_snapshot_mac.h @@ -80,6 +80,7 @@ class ModuleSnapshotMac final : public ModuleSnapshot { std::vector AnnotationsVector() const override; std::map AnnotationsSimpleMap() const override; std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; private: std::string name_; diff --git a/snapshot/mac/process_types/crashpad_info.proctype b/snapshot/mac/process_types/crashpad_info.proctype index 7d5383a3..90ed0d44 100644 --- a/snapshot/mac/process_types/crashpad_info.proctype +++ b/snapshot/mac/process_types/crashpad_info.proctype @@ -42,4 +42,7 @@ PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo) // SimpleStringDictionary* PROCESS_TYPE_STRUCT_MEMBER(Pointer, simple_annotations) + + // UserDataStreamListEntry* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, user_data_minidump_stream_head) PROCESS_TYPE_STRUCT_END(CrashpadInfo) diff --git a/snapshot/minidump/module_snapshot_minidump.cc b/snapshot/minidump/module_snapshot_minidump.cc index c83de596..f5132270 100644 --- a/snapshot/minidump/module_snapshot_minidump.cc +++ b/snapshot/minidump/module_snapshot_minidump.cc @@ -138,9 +138,17 @@ ModuleSnapshotMinidump::AnnotationsSimpleMap() const { std::set> ModuleSnapshotMinidump::ExtraMemoryRanges() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 return std::set>(); } +std::vector +ModuleSnapshotMinidump::CustomMinidumpStreams() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return std::vector(); +} + bool ModuleSnapshotMinidump::InitializeModuleCrashpadInfo( FileReaderInterface* file_reader, const MINIDUMP_LOCATION_DESCRIPTOR* diff --git a/snapshot/minidump/module_snapshot_minidump.h b/snapshot/minidump/module_snapshot_minidump.h index dc375f03..3d63f2ac 100644 --- a/snapshot/minidump/module_snapshot_minidump.h +++ b/snapshot/minidump/module_snapshot_minidump.h @@ -77,6 +77,7 @@ class ModuleSnapshotMinidump final : public ModuleSnapshot { std::vector AnnotationsVector() const override; std::map AnnotationsSimpleMap() const override; std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; private: // Initializes data carried in a MinidumpModuleCrashpadInfo structure on diff --git a/snapshot/module_snapshot.h b/snapshot/module_snapshot.h index 3a1c3216..3e5d18af 100644 --- a/snapshot/module_snapshot.h +++ b/snapshot/module_snapshot.h @@ -23,11 +23,35 @@ #include #include +#include "base/memory/scoped_ptr.h" #include "util/misc/uuid.h" #include "util/numeric/checked_range.h" +#include "snapshot/memory_snapshot.h" namespace crashpad { +class MemorySnapshot; + +//! \brief Information describing a custom user data stream in a minidump. +class UserMinidumpStream { + public: + //! \brief Constructs a UserMinidumpStream, takes ownership of \a memory. + UserMinidumpStream(uint32_t stream_type, MemorySnapshot* memory) + : memory_(memory), stream_type_(stream_type) {} + + const MemorySnapshot* memory() const { return memory_.get(); } + uint32_t stream_type() const { return stream_type_; } + + private: + //! \brief The memory representing the custom minidump stream. + scoped_ptr memory_; + + //! \brief The stream type that the minidump stream will be tagged with. + uint32_t stream_type_; + + DISALLOW_COPY_AND_ASSIGN(UserMinidumpStream); +}; + //! \brief An abstract interface to a snapshot representing a code module //! (binary image) loaded into a snapshot process. class ModuleSnapshot { @@ -174,6 +198,15 @@ class ModuleSnapshot { //! \brief Returns a set of extra memory ranges specified in the module as //! being desirable to include in the crash dump. virtual std::set> ExtraMemoryRanges() const = 0; + + //! \brief Returns a list of custom minidump stream specified in the module to + //! be included in the crash dump. + //! + //! \return The caller does not take ownership of the returned objects, they + //! are scoped to the lifetime of the ModuleSnapshot object that they were + //! obtained from. + virtual std::vector CustomMinidumpStreams() + const = 0; }; } // namespace crashpad diff --git a/snapshot/test/test_module_snapshot.cc b/snapshot/test/test_module_snapshot.cc index c853a780..08776d65 100644 --- a/snapshot/test/test_module_snapshot.cc +++ b/snapshot/test/test_module_snapshot.cc @@ -98,5 +98,10 @@ std::set> TestModuleSnapshot::ExtraMemoryRanges() const { return extra_memory_ranges_; } +std::vector +TestModuleSnapshot::CustomMinidumpStreams() const { + return custom_minidump_streams_; +} + } // namespace test } // namespace crashpad diff --git a/snapshot/test/test_module_snapshot.h b/snapshot/test/test_module_snapshot.h index bbe6193d..7589d628 100644 --- a/snapshot/test/test_module_snapshot.h +++ b/snapshot/test/test_module_snapshot.h @@ -24,6 +24,7 @@ #include "base/macros.h" #include "snapshot/module_snapshot.h" +#include "util/stdlib/pointer_container.h" namespace crashpad { namespace test { @@ -79,6 +80,9 @@ class TestModuleSnapshot final : public ModuleSnapshot { const std::set>& extra_memory_ranges) { extra_memory_ranges_ = extra_memory_ranges; } + void AddCustomMinidumpStream(const UserMinidumpStream* stream) { + custom_minidump_streams_.push_back(stream); + } // ModuleSnapshot: @@ -100,6 +104,7 @@ class TestModuleSnapshot final : public ModuleSnapshot { std::vector AnnotationsVector() const override; std::map AnnotationsSimpleMap() const override; std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; private: std::string name_; @@ -115,6 +120,7 @@ class TestModuleSnapshot final : public ModuleSnapshot { std::vector annotations_vector_; std::map annotations_simple_map_; std::set> extra_memory_ranges_; + PointerVector custom_minidump_streams_; DISALLOW_COPY_AND_ASSIGN(TestModuleSnapshot); }; diff --git a/snapshot/win/end_to_end_test.py b/snapshot/win/end_to_end_test.py index 404d6d3a..a3506b64 100755 --- a/snapshot/win/end_to_end_test.py +++ b/snapshot/win/end_to_end_test.py @@ -290,6 +290,12 @@ def RunTests(cdb_path, r'\?\?\?\?\?\?\?\? \?\?\?\?\?\?\?\?', 'extra memory removal') + out = CdbRun(cdb_path, dump_path, '.dumpdebug') + out.Check(r'type \?\?\? \(333333\), size 00001000', + 'first user stream') + out.Check(r'type \?\?\? \(222222\), size 00000080', + 'second user stream') + if z7_dump_path: out = CdbRun(cdb_path, z7_dump_path, '.ecxr;lm') out.Check('This dump file has an exception of interest stored in it', diff --git a/snapshot/win/module_snapshot_win.cc b/snapshot/win/module_snapshot_win.cc index b86e2029..54856cc8 100644 --- a/snapshot/win/module_snapshot_win.cc +++ b/snapshot/win/module_snapshot_win.cc @@ -15,7 +15,9 @@ #include "snapshot/win/module_snapshot_win.h" #include "base/strings/utf_string_conversions.h" +#include "client/crashpad_info.h" #include "client/simple_address_range_bag.h" +#include "snapshot/win/memory_snapshot_win.h" #include "snapshot/win/pe_image_annotations_reader.h" #include "snapshot/win/pe_image_reader.h" #include "util/misc/tri_state.h" @@ -196,6 +198,24 @@ std::set> ModuleSnapshotWin::ExtraMemoryRanges() const { return ranges; } +std::vector +ModuleSnapshotWin::CustomMinidumpStreams() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + streams_.clear(); + if (process_reader_->Is64Bit()) { + GetCrashpadUserMinidumpStreams( + &streams_); + } else { + GetCrashpadUserMinidumpStreams( + &streams_); + } + + std::vector result; + for (const auto* stream : streams_) + result.push_back(stream); + return result; +} + template void ModuleSnapshotWin::GetCrashpadOptionsInternal( CrashpadInfoClientOptions* options) { @@ -262,5 +282,32 @@ void ModuleSnapshotWin::GetCrashpadExtraMemoryRanges( } } +template +void ModuleSnapshotWin::GetCrashpadUserMinidumpStreams( + PointerVector* streams) const { + process_types::CrashpadInfo crashpad_info; + if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info)) + return; + + for (uint64_t cur = crashpad_info.user_data_minidump_stream_head; cur;) { + internal::UserDataMinidumpStreamListEntry list_entry; + if (!process_reader_->ReadMemory( + cur, sizeof(list_entry), &list_entry)) { + LOG(WARNING) << "could not read user data stream entry from " + << base::UTF16ToUTF8(name_); + return; + } + + scoped_ptr memory( + new internal::MemorySnapshotWin()); + memory->Initialize( + process_reader_, list_entry.base_address, list_entry.size); + streams->push_back( + new UserMinidumpStream(list_entry.stream_type, memory.release())); + + cur = list_entry.next; + } +} + } // namespace internal } // namespace crashpad diff --git a/snapshot/win/module_snapshot_win.h b/snapshot/win/module_snapshot_win.h index 85aeb123..dd12ae8b 100644 --- a/snapshot/win/module_snapshot_win.h +++ b/snapshot/win/module_snapshot_win.h @@ -29,6 +29,7 @@ #include "snapshot/win/process_reader_win.h" #include "util/misc/initialization_state.h" #include "util/misc/initialization_state_dcheck.h" +#include "util/stdlib/pointer_container.h" #include "util/win/process_info.h" namespace crashpad { @@ -86,6 +87,7 @@ class ModuleSnapshotWin final : public ModuleSnapshot { std::vector AnnotationsVector() const override; std::map AnnotationsSimpleMap() const override; std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; private: template @@ -95,6 +97,10 @@ class ModuleSnapshotWin final : public ModuleSnapshot { void GetCrashpadExtraMemoryRanges( std::set>* ranges) const; + template + void GetCrashpadUserMinidumpStreams( + PointerVector* streams) const; + // Initializes vs_fixed_file_info_ if it has not yet been initialized, and // returns a pointer to it. Returns nullptr on failure, with a message logged // on the first call. @@ -107,6 +113,8 @@ class ModuleSnapshotWin final : public ModuleSnapshot { ProcessReaderWin* process_reader_; // weak time_t timestamp_; uint32_t age_; + // Too const-y: https://crashpad.chromium.org/bug/9. + mutable PointerVector streams_; InitializationStateDcheck initialized_; // VSFixedFileInfo() is logically const, but updates these members on the diff --git a/snapshot/win/pe_image_reader.h b/snapshot/win/pe_image_reader.h index 96529bf8..6e5ef7d7 100644 --- a/snapshot/win/pe_image_reader.h +++ b/snapshot/win/pe_image_reader.h @@ -45,6 +45,7 @@ struct CrashpadInfo { uint8_t padding_0; typename Traits::Pointer extra_address_ranges; typename Traits::Pointer simple_annotations; + typename Traits::Pointer user_data_minidump_stream_head; }; } // namespace process_types