Add ProcessSnapshotMinidump::CustomMinidumpStreams()
Add a method to ProcessSnapshotMinidump to expose a similar interface
to ModuleSnapshot::CustomMinidumpStreams(). It's implemented on the
process snapshot here because there is no way to map custom minidump
streams back to a specific module. This allows implementing tests that
inspect custom user streams in minidumps.
Bug: 896019
Change-Id: I1673c342753e13d64bddcc0083ca29fa356deac7
Reviewed-on: https://chromium-review.googlesource.com/c/1271405
Commit-Queue: Vlad Tsyrklevich <vtsyrklevich@chromium.org>
Reviewed-by: Mark Mentovai <mark@chromium.org>
diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h
index f3701dc..bdaaa48 100644
--- a/minidump/minidump_extensions.h
+++ b/minidump/minidump_extensions.h
@@ -92,10 +92,18 @@
//! \sa MemoryInfoListStream
kMinidumpStreamTypeMemoryInfoList = MemoryInfoListStream,
+ //! \brief The last reserved minidump stream.
+ //!
+ //! \sa MemoryInfoListStream
+ kMinidumpStreamTypeLastReservedStream = LastReservedStream,
+
// 0x4350 = "CP"
//! \brief The stream type for MinidumpCrashpadInfo.
kMinidumpStreamTypeCrashpadInfo = 0x43500001,
+
+ //! \brief The last reserved crashpad stream.
+ kMinidumpStreamTypeCrashpadLastReservedStream = 0x4350ffff,
};
//! \brief A variable-length UTF-8-encoded string carried within a minidump
diff --git a/snapshot/BUILD.gn b/snapshot/BUILD.gn
index 3e75817..cbc853a 100644
--- a/snapshot/BUILD.gn
+++ b/snapshot/BUILD.gn
@@ -36,16 +36,17 @@
"memory_snapshot.cc",
"memory_snapshot.h",
"memory_snapshot_generic.h",
+ "minidump/memory_snapshot_minidump.cc",
+ "minidump/memory_snapshot_minidump.h",
"minidump/minidump_annotation_reader.cc",
"minidump/minidump_annotation_reader.h",
"minidump/minidump_simple_string_dictionary_reader.cc",
"minidump/minidump_simple_string_dictionary_reader.h",
+ "minidump/minidump_stream.h",
"minidump/minidump_string_list_reader.cc",
"minidump/minidump_string_list_reader.h",
"minidump/minidump_string_reader.cc",
"minidump/minidump_string_reader.h",
- "minidump/memory_snapshot_minidump.cc",
- "minidump/memory_snapshot_minidump.h",
"minidump/module_snapshot_minidump.cc",
"minidump/module_snapshot_minidump.h",
"minidump/process_snapshot_minidump.cc",
diff --git a/snapshot/minidump/minidump_stream.h b/snapshot/minidump/minidump_stream.h
new file mode 100644
index 0000000..1c75cb6
--- /dev/null
+++ b/snapshot/minidump/minidump_stream.h
@@ -0,0 +1,42 @@
+// Copyright 2018 The Crashpad Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STREAM_H_
+#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STREAM_H_
+
+#include <stdint.h>
+
+#include <vector>
+
+namespace crashpad {
+
+//! \brief Stores a minidump stream along with its stream ID.
+class MinidumpStream {
+ public:
+ MinidumpStream(uint32_t stream_type, std::vector<uint8_t> data)
+ : stream_type_(stream_type), data_(data){};
+
+ uint32_t stream_type() const { return stream_type_; }
+ const std::vector<uint8_t>& data() const { return data_; }
+
+ private:
+ uint32_t stream_type_;
+ std::vector<uint8_t> data_;
+
+ DISALLOW_COPY_AND_ASSIGN(MinidumpStream);
+};
+
+} // namespace crashpad
+
+#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STREAM_H_
diff --git a/snapshot/minidump/process_snapshot_minidump.cc b/snapshot/minidump/process_snapshot_minidump.cc
index 5cc5ad8..620f5de 100644
--- a/snapshot/minidump/process_snapshot_minidump.cc
+++ b/snapshot/minidump/process_snapshot_minidump.cc
@@ -16,6 +16,7 @@
#include <utility>
+#include "minidump/minidump_extensions.h"
#include "snapshot/memory_map_region_snapshot.h"
#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h"
#include "util/file/file_io.h"
@@ -45,7 +46,11 @@
stream_directory_(),
stream_map_(),
modules_(),
+ threads_(),
unloaded_modules_(),
+ mem_regions_(),
+ mem_regions_exposed_(),
+ custom_streams_(),
crashpad_info_(),
system_snapshot_(),
arch_(CPUArchitecture::kCPUArchitectureUnknown),
@@ -109,7 +114,8 @@
!InitializeModules() ||
!InitializeSystemSnapshot() ||
!InitializeMemoryInfo() ||
- !InitializeThreads()) {
+ !InitializeThreads() ||
+ !InitializeCustomMinidumpStreams()) {
return false;
}
@@ -234,6 +240,18 @@
return nullptr;
}
+std::vector<const MinidumpStream*>
+ProcessSnapshotMinidump::CustomMinidumpStreams() const {
+ INITIALIZATION_STATE_DCHECK_VALID(initialized_);
+
+ std::vector<const MinidumpStream*> result;
+ result.reserve(custom_streams_.size());
+ for (const auto& custom_stream : custom_streams_) {
+ result.push_back(custom_stream.get());
+ }
+ return result;
+}
+
bool ProcessSnapshotMinidump::InitializeCrashpadInfo() {
const auto& stream_it = stream_map_.find(kMinidumpStreamTypeCrashpadInfo);
if (stream_it == stream_map_.end()) {
@@ -529,4 +547,33 @@
return true;
}
+bool ProcessSnapshotMinidump::InitializeCustomMinidumpStreams() {
+ for (size_t i = 0; i < stream_directory_.size(); i++) {
+ const auto& stream = stream_directory_[i];
+
+ // Filter out reserved minidump and crashpad streams.
+ const uint32_t stream_type = stream.StreamType;
+ if (stream_type <=
+ MinidumpStreamType::kMinidumpStreamTypeLastReservedStream ||
+ (stream_type >= MinidumpStreamType::kMinidumpStreamTypeCrashpadInfo &&
+ stream_type <= MinidumpStreamType::
+ kMinidumpStreamTypeCrashpadLastReservedStream)) {
+ continue;
+ }
+
+ std::vector<uint8_t> data(stream.Location.DataSize);
+ if (!file_reader_->SeekSet(stream.Location.Rva) ||
+ !file_reader_->ReadExactly(data.data(), data.size())) {
+ LOG(ERROR) << "Failed to read stream with ID 0x" << std::hex
+ << stream_type << std::dec << " at index " << i;
+ return false;
+ }
+
+ custom_streams_.push_back(
+ std::make_unique<MinidumpStream>(stream_type, std::move(data)));
+ }
+
+ return true;
+}
+
} // namespace crashpad
diff --git a/snapshot/minidump/process_snapshot_minidump.h b/snapshot/minidump/process_snapshot_minidump.h
index 2a868a1..8653d33 100644
--- a/snapshot/minidump/process_snapshot_minidump.h
+++ b/snapshot/minidump/process_snapshot_minidump.h
@@ -29,6 +29,7 @@
#include "minidump/minidump_extensions.h"
#include "snapshot/exception_snapshot.h"
#include "snapshot/memory_snapshot.h"
+#include "snapshot/minidump/minidump_stream.h"
#include "snapshot/minidump/module_snapshot_minidump.h"
#include "snapshot/minidump/system_snapshot_minidump.h"
#include "snapshot/minidump/thread_snapshot_minidump.h"
@@ -83,6 +84,16 @@
std::vector<const MemorySnapshot*> ExtraMemory() const override;
const ProcessMemory* Memory() const override;
+ //! \brief Returns a list of custom minidump streams. This routine is the
+ //! equivalent of ModuleSnapshot::CustomMinidumpStreams(), except that in
+ //! a minidump it is impossible to associate a custom stream to a specific
+ //! module.
+ //!
+ //! \return The caller does not take ownership of the returned objects, they
+ //! are scoped to the lifetime of the ProcessSnapshotMinidump object that
+ //! they were obtained from.
+ std::vector<const MinidumpStream*> CustomMinidumpStreams() const;
+
private:
// Initializes data carried in a MinidumpCrashpadInfo stream on behalf of
// Initialize().
@@ -115,6 +126,9 @@
// Initialize().
bool InitializeMiscInfo();
+ // Initializes custom minidump streams.
+ bool InitializeCustomMinidumpStreams();
+
MINIDUMP_HEADER header_;
std::vector<MINIDUMP_DIRECTORY> stream_directory_;
std::map<MinidumpStreamType, const MINIDUMP_LOCATION_DESCRIPTOR*> stream_map_;
@@ -124,6 +138,7 @@
std::vector<std::unique_ptr<internal::MemoryMapRegionSnapshotMinidump>>
mem_regions_;
std::vector<const MemoryMapRegionSnapshot*> mem_regions_exposed_;
+ std::vector<std::unique_ptr<MinidumpStream>> custom_streams_;
MinidumpCrashpadInfo crashpad_info_;
internal::SystemSnapshotMinidump system_snapshot_;
CPUArchitecture arch_;
diff --git a/snapshot/minidump/process_snapshot_minidump_test.cc b/snapshot/minidump/process_snapshot_minidump_test.cc
index 4d74454..52b0992 100644
--- a/snapshot/minidump/process_snapshot_minidump_test.cc
+++ b/snapshot/minidump/process_snapshot_minidump_test.cc
@@ -1109,6 +1109,68 @@
EXPECT_EQ(delegate.result, minidump_stack);
}
+TEST(ProcessSnapshotMinidump, CustomMinidumpStreams) {
+ StringFile string_file;
+
+ MINIDUMP_HEADER header = {};
+ ASSERT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ static const char kStreamReservedData[] = "A string";
+ static const char kStreamUnreservedData[] = "Another string";
+ // In the minidump reserved range
+ constexpr MinidumpStreamType kStreamTypeReserved1 =
+ (MinidumpStreamType)0x1111;
+ // In the crashpad reserved range
+ constexpr MinidumpStreamType kStreamTypeReserved2 =
+ (MinidumpStreamType)0x43501111;
+ constexpr MinidumpStreamType kStreamTypeUnreserved =
+ (MinidumpStreamType)0xffffffff;
+
+ MINIDUMP_DIRECTORY misc_directory = {};
+ RVA reserved1_offset = static_cast<RVA>(string_file.SeekGet());
+ ASSERT_TRUE(
+ string_file.Write(kStreamReservedData, sizeof(kStreamReservedData)));
+ RVA reserved2_offset = static_cast<RVA>(string_file.SeekGet());
+ ASSERT_TRUE(
+ string_file.Write(kStreamReservedData, sizeof(kStreamReservedData)));
+ RVA unreserved_offset = static_cast<RVA>(string_file.SeekGet());
+ ASSERT_TRUE(
+ string_file.Write(kStreamUnreservedData, sizeof(kStreamUnreservedData)));
+
+ header.StreamDirectoryRva = static_cast<RVA>(string_file.SeekGet());
+ misc_directory.StreamType = kStreamTypeReserved1;
+ misc_directory.Location.DataSize = sizeof(kStreamReservedData);
+ misc_directory.Location.Rva = reserved1_offset;
+ ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory)));
+
+ misc_directory.StreamType = kStreamTypeReserved2;
+ misc_directory.Location.DataSize = sizeof(kStreamReservedData);
+ misc_directory.Location.Rva = reserved2_offset;
+ ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory)));
+
+ misc_directory.StreamType = kStreamTypeUnreserved;
+ misc_directory.Location.DataSize = sizeof(kStreamUnreservedData);
+ misc_directory.Location.Rva = unreserved_offset;
+ ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory)));
+
+ header.Signature = MINIDUMP_SIGNATURE;
+ header.Version = MINIDUMP_VERSION;
+ header.NumberOfStreams = 3;
+ ASSERT_TRUE(string_file.SeekSet(0));
+ ASSERT_TRUE(string_file.Write(&header, sizeof(header)));
+
+ ProcessSnapshotMinidump process_snapshot;
+ ASSERT_TRUE(process_snapshot.Initialize(&string_file));
+
+ auto custom_streams = process_snapshot.CustomMinidumpStreams();
+ ASSERT_EQ(custom_streams.size(), 1U);
+ EXPECT_EQ(custom_streams[0]->stream_type(), (uint32_t)kStreamTypeUnreserved);
+
+ auto stream_data = custom_streams[0]->data();
+ EXPECT_EQ(stream_data.size(), sizeof(kStreamUnreservedData));
+ EXPECT_STREQ((char*)&stream_data.front(), kStreamUnreservedData);
+}
+
} // namespace
} // namespace test
} // namespace crashpad
diff --git a/snapshot/snapshot.gyp b/snapshot/snapshot.gyp
index dd60b0c..ac7de29 100644
--- a/snapshot/snapshot.gyp
+++ b/snapshot/snapshot.gyp
@@ -111,6 +111,7 @@
'minidump/minidump_annotation_reader.h',
'minidump/minidump_simple_string_dictionary_reader.cc',
'minidump/minidump_simple_string_dictionary_reader.h',
+ 'minidump/minidump_stream.h',
'minidump/minidump_string_list_reader.cc',
'minidump/minidump_string_list_reader.h',
'minidump/minidump_string_reader.cc',