Add MinidumpFileWriter::InitializeFromSnapshot() and its tests.

TEST=minidump_test MinidumpFileWriter.InitializeFromSnapshot*
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/703223003
This commit is contained in:
Mark Mentovai 2014-11-07 14:47:08 -05:00
parent e5048b3a80
commit 360e441c53
6 changed files with 286 additions and 5 deletions

View File

@ -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());

View File

@ -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.

View File

@ -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());

View File

@ -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, its safe to not add
// it to the minidump file if it wouldnt 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);

View File

@ -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.

View File

@ -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<time_t>(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<MINIDUMP_SYSTEM_INFO>(
file_writer.string(), directory[0].Location));
EXPECT_EQ(kMinidumpStreamTypeMiscInfo, directory[1].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MISC_INFO_4>(
file_writer.string(), directory[1].Location));
EXPECT_EQ(kMinidumpStreamTypeThreadList, directory[2].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(
file_writer.string(), directory[2].Location));
EXPECT_EQ(kMinidumpStreamTypeModuleList, directory[3].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(
file_writer.string(), directory[3].Location));
EXPECT_EQ(kMinidumpStreamTypeMemoryList, directory[4].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(
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<time_t>(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<MINIDUMP_SYSTEM_INFO>(
file_writer.string(), directory[0].Location));
EXPECT_EQ(kMinidumpStreamTypeMiscInfo, directory[1].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MISC_INFO_4>(
file_writer.string(), directory[1].Location));
EXPECT_EQ(kMinidumpStreamTypeThreadList, directory[2].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(
file_writer.string(), directory[2].Location));
EXPECT_EQ(kMinidumpStreamTypeException, directory[3].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_EXCEPTION_STREAM>(
file_writer.string(), directory[3].Location));
EXPECT_EQ(kMinidumpStreamTypeModuleList, directory[4].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(
file_writer.string(), directory[4].Location));
EXPECT_EQ(kMinidumpStreamTypeMemoryList, directory[5].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(
file_writer.string(), directory[5].Location));
}
TEST(MinidumpFileWriter, InitializeFromSnapshot_CrashpadInfo) {
const uint32_t kSnapshotTime = 0x15393bd3;
const timeval kSnapshotTimeval = { implicit_cast<time_t>(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<std::string> 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<MINIDUMP_SYSTEM_INFO>(
file_writer.string(), directory[0].Location));
EXPECT_EQ(kMinidumpStreamTypeMiscInfo, directory[1].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MISC_INFO_4>(
file_writer.string(), directory[1].Location));
EXPECT_EQ(kMinidumpStreamTypeThreadList, directory[2].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_THREAD_LIST>(
file_writer.string(), directory[2].Location));
EXPECT_EQ(kMinidumpStreamTypeException, directory[3].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_EXCEPTION_STREAM>(
file_writer.string(), directory[3].Location));
EXPECT_EQ(kMinidumpStreamTypeModuleList, directory[4].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MODULE_LIST>(
file_writer.string(), directory[4].Location));
EXPECT_EQ(kMinidumpStreamTypeCrashpadInfo, directory[5].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MinidumpCrashpadInfo>(
file_writer.string(), directory[5].Location));
EXPECT_EQ(kMinidumpStreamTypeMemoryList, directory[6].StreamType);
EXPECT_TRUE(MinidumpWritableAtLocationDescriptor<MINIDUMP_MEMORY_LIST>(
file_writer.string(), directory[6].Location));
}
TEST(MinidumpFileWriterDeathTest, SameStreamType) {
MinidumpFileWriter minidump_file;