mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 14:06:33 +00:00
Add MinidumpModuleWriter, MinidumpModuleListWriter, related classes, and
their test. TEST=minidump_test MinidumpModuleTest R=rsesek@chromium.org Review URL: https://codereview.chromium.org/460933004
This commit is contained in:
parent
3b2c6d32f4
commit
0ba9d2b065
@ -29,6 +29,7 @@
|
||||
'..',
|
||||
],
|
||||
'sources': [
|
||||
'minidump_extensions.cc',
|
||||
'minidump_extensions.h',
|
||||
'minidump_file_writer.cc',
|
||||
'minidump_file_writer.h',
|
||||
@ -36,6 +37,8 @@
|
||||
'minidump_memory_writer.h',
|
||||
'minidump_misc_info_writer.cc',
|
||||
'minidump_misc_info_writer.h',
|
||||
'minidump_module_writer.cc',
|
||||
'minidump_module_writer.h',
|
||||
'minidump_stream_writer.cc',
|
||||
'minidump_stream_writer.h',
|
||||
'minidump_string_writer.cc',
|
||||
@ -64,6 +67,7 @@
|
||||
'minidump_file_writer_test.cc',
|
||||
'minidump_memory_writer_test.cc',
|
||||
'minidump_misc_info_writer_test.cc',
|
||||
'minidump_module_writer_test.cc',
|
||||
'minidump_string_writer_test.cc',
|
||||
'minidump_system_info_writer_test.cc',
|
||||
'minidump_test_util.cc',
|
||||
|
22
minidump/minidump_extensions.cc
Normal file
22
minidump/minidump_extensions.cc
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2014 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_extensions.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
const uint32_t MinidumpModuleCodeViewRecordPDB20::kSignature;
|
||||
const uint32_t MinidumpModuleCodeViewRecordPDB70::kSignature;
|
||||
|
||||
} // namespace crashpad
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "minidump/minidump_extensions.h"
|
||||
#include "minidump/minidump_file_writer.h"
|
||||
#include "minidump/minidump_stream_writer.h"
|
||||
#include "minidump/minidump_test_util.h"
|
||||
@ -248,27 +249,36 @@ TEST(MinidumpMemoryWriter, TwoMemoryRegions) {
|
||||
EXPECT_EQ(2u, memory_list->NumberOfMemoryRanges);
|
||||
|
||||
MINIDUMP_MEMORY_DESCRIPTOR expected;
|
||||
expected.StartOfMemoryRange = kBaseAddress1;
|
||||
expected.Memory.DataSize = kSize1;
|
||||
expected.Memory.Rva =
|
||||
sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||
sizeof(MINIDUMP_MEMORY_LIST) +
|
||||
memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
|
||||
ExpectMemoryDescriptorAndContents(&expected,
|
||||
&memory_list->MemoryRanges[0],
|
||||
file_writer.string(),
|
||||
kValue1,
|
||||
false);
|
||||
|
||||
expected.StartOfMemoryRange = kBaseAddress2;
|
||||
expected.Memory.DataSize = kSize2;
|
||||
expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva +
|
||||
memory_list->MemoryRanges[0].Memory.DataSize;
|
||||
ExpectMemoryDescriptorAndContents(&expected,
|
||||
&memory_list->MemoryRanges[1],
|
||||
file_writer.string(),
|
||||
kValue2,
|
||||
true);
|
||||
{
|
||||
SCOPED_TRACE("region 0");
|
||||
|
||||
expected.StartOfMemoryRange = kBaseAddress1;
|
||||
expected.Memory.DataSize = kSize1;
|
||||
expected.Memory.Rva =
|
||||
sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||
sizeof(MINIDUMP_MEMORY_LIST) +
|
||||
memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
|
||||
ExpectMemoryDescriptorAndContents(&expected,
|
||||
&memory_list->MemoryRanges[0],
|
||||
file_writer.string(),
|
||||
kValue1,
|
||||
false);
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE("region 1");
|
||||
|
||||
expected.StartOfMemoryRange = kBaseAddress2;
|
||||
expected.Memory.DataSize = kSize2;
|
||||
expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva +
|
||||
memory_list->MemoryRanges[0].Memory.DataSize;
|
||||
ExpectMemoryDescriptorAndContents(&expected,
|
||||
&memory_list->MemoryRanges[1],
|
||||
file_writer.string(),
|
||||
kValue2,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
class TestMemoryStream final : public internal::MinidumpStreamWriter {
|
||||
@ -346,27 +356,36 @@ TEST(MinidumpMemoryWriter, ExtraMemory) {
|
||||
EXPECT_EQ(2u, memory_list->NumberOfMemoryRanges);
|
||||
|
||||
MINIDUMP_MEMORY_DESCRIPTOR expected;
|
||||
expected.StartOfMemoryRange = kBaseAddress1;
|
||||
expected.Memory.DataSize = kSize1;
|
||||
expected.Memory.Rva =
|
||||
sizeof(MINIDUMP_HEADER) + 2 * sizeof(MINIDUMP_DIRECTORY) +
|
||||
sizeof(MINIDUMP_MEMORY_LIST) +
|
||||
memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
|
||||
ExpectMemoryDescriptorAndContents(&expected,
|
||||
&memory_list->MemoryRanges[0],
|
||||
file_writer.string(),
|
||||
kValue1,
|
||||
false);
|
||||
|
||||
expected.StartOfMemoryRange = kBaseAddress2;
|
||||
expected.Memory.DataSize = kSize2;
|
||||
expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva +
|
||||
memory_list->MemoryRanges[0].Memory.DataSize;
|
||||
ExpectMemoryDescriptorAndContents(&expected,
|
||||
&memory_list->MemoryRanges[1],
|
||||
file_writer.string(),
|
||||
kValue2,
|
||||
true);
|
||||
{
|
||||
SCOPED_TRACE("region 0");
|
||||
|
||||
expected.StartOfMemoryRange = kBaseAddress1;
|
||||
expected.Memory.DataSize = kSize1;
|
||||
expected.Memory.Rva =
|
||||
sizeof(MINIDUMP_HEADER) + 2 * sizeof(MINIDUMP_DIRECTORY) +
|
||||
sizeof(MINIDUMP_MEMORY_LIST) +
|
||||
memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR);
|
||||
ExpectMemoryDescriptorAndContents(&expected,
|
||||
&memory_list->MemoryRanges[0],
|
||||
file_writer.string(),
|
||||
kValue1,
|
||||
false);
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE("region 1");
|
||||
|
||||
expected.StartOfMemoryRange = kBaseAddress2;
|
||||
expected.Memory.DataSize = kSize2;
|
||||
expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva +
|
||||
memory_list->MemoryRanges[0].Memory.DataSize;
|
||||
ExpectMemoryDescriptorAndContents(&expected,
|
||||
&memory_list->MemoryRanges[1],
|
||||
file_writer.string(),
|
||||
kValue2,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
375
minidump/minidump_module_writer.cc
Normal file
375
minidump/minidump_module_writer.cc
Normal file
@ -0,0 +1,375 @@
|
||||
// Copyright 2014 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_module_writer.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "minidump/minidump_string_writer.h"
|
||||
#include "minidump/minidump_writer_util.h"
|
||||
#include "util/numeric/safe_assignment.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
MinidumpModuleCodeViewRecordWriter::~MinidumpModuleCodeViewRecordWriter() {
|
||||
}
|
||||
|
||||
namespace internal {
|
||||
|
||||
template <typename CodeViewRecordType>
|
||||
MinidumpModuleCodeViewRecordPDBLinkWriter<
|
||||
CodeViewRecordType>::MinidumpModuleCodeViewRecordPDBLinkWriter()
|
||||
: MinidumpModuleCodeViewRecordWriter(), codeview_record_(), pdb_name_() {
|
||||
codeview_record_.signature = CodeViewRecordType::kSignature;
|
||||
}
|
||||
|
||||
template <typename CodeViewRecordType>
|
||||
MinidumpModuleCodeViewRecordPDBLinkWriter<
|
||||
CodeViewRecordType>::~MinidumpModuleCodeViewRecordPDBLinkWriter() {
|
||||
}
|
||||
|
||||
template <typename CodeViewRecordType>
|
||||
size_t
|
||||
MinidumpModuleCodeViewRecordPDBLinkWriter<CodeViewRecordType>::SizeOfObject() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
|
||||
// NUL-terminate.
|
||||
return offsetof(typeof(codeview_record_), pdb_name) +
|
||||
(pdb_name_.size() + 1) * sizeof(pdb_name_[0]);
|
||||
}
|
||||
|
||||
template <typename CodeViewRecordType>
|
||||
bool MinidumpModuleCodeViewRecordPDBLinkWriter<CodeViewRecordType>::WriteObject(
|
||||
FileWriterInterface* file_writer) {
|
||||
DCHECK_EQ(state(), kStateWritable);
|
||||
|
||||
WritableIoVec iov;
|
||||
iov.iov_base = &codeview_record_;
|
||||
iov.iov_len = offsetof(typeof(codeview_record_), pdb_name);
|
||||
std::vector<WritableIoVec> iovecs(1, iov);
|
||||
|
||||
// NUL-terminate.
|
||||
iov.iov_base = &pdb_name_[0];
|
||||
iov.iov_len = (pdb_name_.size() + 1) * sizeof(pdb_name_[0]);
|
||||
iovecs.push_back(iov);
|
||||
|
||||
return file_writer->WriteIoVec(&iovecs);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
|
||||
MinidumpModuleCodeViewRecordPDB20>;
|
||||
|
||||
MinidumpModuleCodeViewRecordPDB20Writer::
|
||||
~MinidumpModuleCodeViewRecordPDB20Writer() {
|
||||
}
|
||||
|
||||
void MinidumpModuleCodeViewRecordPDB20Writer::SetTimestampAndAge(
|
||||
time_t timestamp,
|
||||
uint32_t age) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
internal::MinidumpWriterUtil::AssignTimeT(&codeview_record()->timestamp,
|
||||
timestamp);
|
||||
|
||||
codeview_record()->age = age;
|
||||
}
|
||||
|
||||
template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
|
||||
MinidumpModuleCodeViewRecordPDB70>;
|
||||
|
||||
MinidumpModuleCodeViewRecordPDB70Writer::
|
||||
~MinidumpModuleCodeViewRecordPDB70Writer() {
|
||||
}
|
||||
|
||||
MinidumpModuleMiscDebugRecordWriter::MinidumpModuleMiscDebugRecordWriter()
|
||||
: internal::MinidumpWritable(),
|
||||
image_debug_misc_(),
|
||||
data_(),
|
||||
data_utf16_() {
|
||||
}
|
||||
|
||||
void MinidumpModuleMiscDebugRecordWriter::SetData(const std::string& data,
|
||||
bool utf16) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
if (!utf16) {
|
||||
data_utf16_.clear();
|
||||
image_debug_misc_.Unicode = 0;
|
||||
data_ = data;
|
||||
} else {
|
||||
data_.clear();
|
||||
image_debug_misc_.Unicode = 1;
|
||||
data_utf16_ = internal::MinidumpWriterUtil::ConvertUTF8ToUTF16(data);
|
||||
}
|
||||
}
|
||||
|
||||
bool MinidumpModuleMiscDebugRecordWriter::Freeze() {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
if (!MinidumpWritable::Freeze()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// NUL-terminate.
|
||||
if (!image_debug_misc_.Unicode) {
|
||||
DCHECK(data_utf16_.empty());
|
||||
image_debug_misc_.Length = offsetof(typeof(image_debug_misc_), Data) +
|
||||
(data_.size() + 1) * sizeof(data_[0]);
|
||||
} else {
|
||||
DCHECK(data_.empty());
|
||||
image_debug_misc_.Length =
|
||||
offsetof(typeof(image_debug_misc_), Data) +
|
||||
(data_utf16_.size() + 1) * sizeof(data_utf16_[0]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t MinidumpModuleMiscDebugRecordWriter::SizeOfObject() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
|
||||
return image_debug_misc_.Length;
|
||||
}
|
||||
|
||||
bool MinidumpModuleMiscDebugRecordWriter::WriteObject(
|
||||
FileWriterInterface* file_writer) {
|
||||
DCHECK_EQ(state(), kStateWritable);
|
||||
|
||||
const size_t base_length = offsetof(typeof(image_debug_misc_), Data);
|
||||
|
||||
WritableIoVec iov;
|
||||
iov.iov_base = &image_debug_misc_;
|
||||
iov.iov_len = base_length;
|
||||
std::vector<WritableIoVec> iovecs(1, iov);
|
||||
|
||||
iov.iov_len = image_debug_misc_.Length - base_length;
|
||||
if (!image_debug_misc_.Unicode) {
|
||||
DCHECK(data_utf16_.empty());
|
||||
iov.iov_base = &data_[0];
|
||||
} else {
|
||||
DCHECK(data_.empty());
|
||||
iov.iov_base = &data_utf16_[0];
|
||||
}
|
||||
iovecs.push_back(iov);
|
||||
|
||||
return file_writer->WriteIoVec(&iovecs);
|
||||
}
|
||||
|
||||
MinidumpModuleWriter::MinidumpModuleWriter()
|
||||
: MinidumpWritable(),
|
||||
module_(),
|
||||
name_(),
|
||||
codeview_record_(NULL),
|
||||
misc_debug_record_(NULL) {
|
||||
module_.VersionInfo.dwSignature = VS_FFI_SIGNATURE;
|
||||
module_.VersionInfo.dwStrucVersion = VS_FFI_STRUCVERSION;
|
||||
}
|
||||
|
||||
MinidumpModuleWriter::~MinidumpModuleWriter() {
|
||||
}
|
||||
|
||||
const MINIDUMP_MODULE* MinidumpModuleWriter::MinidumpModule() const {
|
||||
DCHECK_EQ(state(), kStateWritable);
|
||||
|
||||
return &module_;
|
||||
}
|
||||
|
||||
void MinidumpModuleWriter::SetName(const std::string& name) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
if (!name_) {
|
||||
name_.reset(new internal::MinidumpUTF16StringWriter());
|
||||
}
|
||||
name_->SetUTF8(name);
|
||||
}
|
||||
|
||||
void MinidumpModuleWriter::SetCodeViewRecord(
|
||||
MinidumpModuleCodeViewRecordWriter* codeview_record) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
codeview_record_ = codeview_record;
|
||||
}
|
||||
|
||||
void MinidumpModuleWriter::SetMiscDebugRecord(
|
||||
MinidumpModuleMiscDebugRecordWriter* misc_debug_record) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
misc_debug_record_ = misc_debug_record;
|
||||
}
|
||||
|
||||
void MinidumpModuleWriter::SetTimestamp(time_t timestamp) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
internal::MinidumpWriterUtil::AssignTimeT(&module_.TimeDateStamp, timestamp);
|
||||
}
|
||||
|
||||
void MinidumpModuleWriter::SetFileVersion(uint16_t version_0,
|
||||
uint16_t version_1,
|
||||
uint16_t version_2,
|
||||
uint16_t version_3) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
module_.VersionInfo.dwFileVersionMS =
|
||||
(static_cast<uint32_t>(version_0) << 16) | version_1;
|
||||
module_.VersionInfo.dwFileVersionLS =
|
||||
(static_cast<uint32_t>(version_2) << 16) | version_3;
|
||||
}
|
||||
|
||||
void MinidumpModuleWriter::SetProductVersion(uint16_t version_0,
|
||||
uint16_t version_1,
|
||||
uint16_t version_2,
|
||||
uint16_t version_3) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
module_.VersionInfo.dwProductVersionMS =
|
||||
(static_cast<uint32_t>(version_0) << 16) | version_1;
|
||||
module_.VersionInfo.dwProductVersionLS =
|
||||
(static_cast<uint32_t>(version_2) << 16) | version_3;
|
||||
}
|
||||
|
||||
void MinidumpModuleWriter::SetFileFlagsAndMask(uint32_t file_flags,
|
||||
uint32_t file_flags_mask) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
DCHECK_EQ(file_flags & ~file_flags_mask, 0u);
|
||||
|
||||
module_.VersionInfo.dwFileFlags = file_flags;
|
||||
module_.VersionInfo.dwFileFlagsMask = file_flags_mask;
|
||||
}
|
||||
|
||||
bool MinidumpModuleWriter::Freeze() {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
if (!MinidumpWritable::Freeze()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK(name_);
|
||||
|
||||
name_->RegisterRVA(&module_.ModuleNameRva);
|
||||
|
||||
if (codeview_record_) {
|
||||
codeview_record_->RegisterLocationDescriptor(&module_.CvRecord);
|
||||
}
|
||||
|
||||
if (misc_debug_record_) {
|
||||
misc_debug_record_->RegisterLocationDescriptor(&module_.MiscRecord);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t MinidumpModuleWriter::SizeOfObject() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
|
||||
// This object doesn’t directly write anything itself. Its MINIDUMP_MODULE is
|
||||
// written by its parent as part of a MINIDUMP_MODULE_LIST, and its children
|
||||
// are responsible for writing themselves.
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<internal::MinidumpWritable*> MinidumpModuleWriter::Children() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
|
||||
std::vector<MinidumpWritable*> children;
|
||||
if (name_) {
|
||||
children.push_back(name_.get());
|
||||
}
|
||||
if (codeview_record_) {
|
||||
children.push_back(codeview_record_);
|
||||
}
|
||||
if (misc_debug_record_) {
|
||||
children.push_back(misc_debug_record_);
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
bool MinidumpModuleWriter::WriteObject(FileWriterInterface* file_writer) {
|
||||
DCHECK_EQ(state(), kStateWritable);
|
||||
|
||||
// This object doesn’t directly write anything itself. Its MINIDUMP_MODULE is
|
||||
// written by its parent as part of a MINIDUMP_MODULE_LIST, and its children
|
||||
// are responsible for writing themselves.
|
||||
return true;
|
||||
}
|
||||
|
||||
MinidumpModuleListWriter::MinidumpModuleListWriter()
|
||||
: MinidumpStreamWriter(), module_list_base_(), modules_() {
|
||||
}
|
||||
|
||||
MinidumpModuleListWriter::~MinidumpModuleListWriter() {
|
||||
}
|
||||
|
||||
void MinidumpModuleListWriter::AddModule(MinidumpModuleWriter* module) {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
modules_.push_back(module);
|
||||
}
|
||||
|
||||
bool MinidumpModuleListWriter::Freeze() {
|
||||
DCHECK_EQ(state(), kStateMutable);
|
||||
|
||||
if (!MinidumpStreamWriter::Freeze()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t module_count = modules_.size();
|
||||
if (!AssignIfInRange(&module_list_base_.NumberOfModules, module_count)) {
|
||||
LOG(ERROR) << "module_count " << module_count << " out of range";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t MinidumpModuleListWriter::SizeOfObject() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
|
||||
return sizeof(module_list_base_) + modules_.size() * sizeof(MINIDUMP_MODULE);
|
||||
}
|
||||
|
||||
std::vector<internal::MinidumpWritable*> MinidumpModuleListWriter::Children() {
|
||||
DCHECK_GE(state(), kStateFrozen);
|
||||
|
||||
std::vector<MinidumpWritable*> children;
|
||||
for (MinidumpModuleWriter* module : modules_) {
|
||||
children.push_back(module);
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
bool MinidumpModuleListWriter::WriteObject(FileWriterInterface* file_writer) {
|
||||
DCHECK_EQ(state(), kStateWritable);
|
||||
|
||||
WritableIoVec iov;
|
||||
iov.iov_base = &module_list_base_;
|
||||
iov.iov_len = sizeof(module_list_base_);
|
||||
std::vector<WritableIoVec> iovecs(1, iov);
|
||||
|
||||
for (const MinidumpModuleWriter* module : modules_) {
|
||||
iov.iov_len = sizeof(MINIDUMP_MODULE);
|
||||
iov.iov_base = module->MinidumpModule();
|
||||
iovecs.push_back(iov);
|
||||
}
|
||||
|
||||
return file_writer->WriteIoVec(&iovecs);
|
||||
}
|
||||
|
||||
MinidumpStreamType MinidumpModuleListWriter::StreamType() const {
|
||||
return kMinidumpStreamTypeModuleList;
|
||||
}
|
||||
|
||||
} // namespace crashpad
|
324
minidump/minidump_module_writer.h
Normal file
324
minidump/minidump_module_writer.h
Normal file
@ -0,0 +1,324 @@
|
||||
// Copyright 2014 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_MODULE_WRITER_H_
|
||||
#define CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_
|
||||
|
||||
#include <dbghelp.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/strings/string16.h"
|
||||
#include "minidump/minidump_extensions.h"
|
||||
#include "minidump/minidump_stream_writer.h"
|
||||
#include "minidump/minidump_writable.h"
|
||||
#include "util/file/file_writer.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
namespace internal {
|
||||
class MinidumpUTF16StringWriter;
|
||||
} // namespace internal
|
||||
|
||||
//! \brief The base class for writers of CodeView records referenced by
|
||||
//! MINIDUMP_MODULE::CvRecord in minidump files.
|
||||
class MinidumpModuleCodeViewRecordWriter : public internal::MinidumpWritable {
|
||||
public:
|
||||
virtual ~MinidumpModuleCodeViewRecordWriter();
|
||||
|
||||
protected:
|
||||
MinidumpModuleCodeViewRecordWriter() : MinidumpWritable() {}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordWriter);
|
||||
};
|
||||
|
||||
namespace internal {
|
||||
|
||||
//! \brief The base class for writers of CodeView records that serve as links to
|
||||
//! `.pdb` (program database) files.
|
||||
template <typename CodeViewRecordType>
|
||||
class MinidumpModuleCodeViewRecordPDBLinkWriter
|
||||
: public MinidumpModuleCodeViewRecordWriter {
|
||||
public:
|
||||
//! \brief Sets the name of the `.pdb` file being linked to.
|
||||
void SetPDBName(const std::string& pdb_name) { pdb_name_ = pdb_name; }
|
||||
|
||||
protected:
|
||||
MinidumpModuleCodeViewRecordPDBLinkWriter();
|
||||
virtual ~MinidumpModuleCodeViewRecordPDBLinkWriter();
|
||||
|
||||
// MinidumpWritable:
|
||||
virtual size_t SizeOfObject() override;
|
||||
virtual bool WriteObject(FileWriterInterface* file_writer) override;
|
||||
|
||||
//! \brief Returns a pointer to the raw CodeView record’s data.
|
||||
//!
|
||||
//! Subclasses can use this to set fields in their codeview records other than
|
||||
//! the `pdb_name` field.
|
||||
CodeViewRecordType* codeview_record() { return &codeview_record_; }
|
||||
|
||||
private:
|
||||
CodeViewRecordType codeview_record_;
|
||||
std::string pdb_name_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDBLinkWriter);
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
//! \brief The writer for a MinidumpModuleCodeViewRecordPDB20 object in a
|
||||
//! minidump file.
|
||||
//!
|
||||
//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead.
|
||||
class MinidumpModuleCodeViewRecordPDB20Writer final
|
||||
: public internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
|
||||
MinidumpModuleCodeViewRecordPDB20> {
|
||||
public:
|
||||
MinidumpModuleCodeViewRecordPDB20Writer()
|
||||
: internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
|
||||
MinidumpModuleCodeViewRecordPDB20>() {}
|
||||
|
||||
virtual ~MinidumpModuleCodeViewRecordPDB20Writer();
|
||||
|
||||
//! \brief Sets MinidumpModuleCodeViewRecordPDB20::timestamp and
|
||||
//! MinidumpModuleCodeViewRecordPDB20::age.
|
||||
void SetTimestampAndAge(time_t timestamp, uint32_t age);
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDB20Writer);
|
||||
};
|
||||
|
||||
//! \brief The writer for a MinidumpModuleCodeViewRecordPDB70 object in a
|
||||
//! minidump file.
|
||||
class MinidumpModuleCodeViewRecordPDB70Writer final
|
||||
: public internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
|
||||
MinidumpModuleCodeViewRecordPDB70> {
|
||||
public:
|
||||
MinidumpModuleCodeViewRecordPDB70Writer()
|
||||
: internal::MinidumpModuleCodeViewRecordPDBLinkWriter<
|
||||
MinidumpModuleCodeViewRecordPDB70>() {}
|
||||
|
||||
virtual ~MinidumpModuleCodeViewRecordPDB70Writer();
|
||||
|
||||
//! \brief Sets MinidumpModuleCodeViewRecordPDB70::uuid and
|
||||
//! MinidumpModuleCodeViewRecordPDB70::age.
|
||||
void SetUUIDAndAge(const UUID& uuid, uint32_t age) {
|
||||
codeview_record()->uuid = uuid;
|
||||
codeview_record()->age = age;
|
||||
}
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCodeViewRecordPDB70Writer);
|
||||
};
|
||||
|
||||
//! \brief The writer for an IMAGE_DEBUG_MISC object in a minidump file.
|
||||
//!
|
||||
//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead.
|
||||
class MinidumpModuleMiscDebugRecordWriter final
|
||||
: public internal::MinidumpWritable {
|
||||
public:
|
||||
MinidumpModuleMiscDebugRecordWriter();
|
||||
~MinidumpModuleMiscDebugRecordWriter() {}
|
||||
|
||||
//! \brief Sets IMAGE_DEBUG_MISC::DataType.
|
||||
void SetDataType(uint32_t data_type) {
|
||||
image_debug_misc_.DataType = data_type;
|
||||
}
|
||||
|
||||
//! \brief Sets IMAGE_DEBUG_MISC::Data, IMAGE_DEBUG_MISC::Length, and
|
||||
//! IMAGE_DEBUG_MISC::Unicode.
|
||||
//!
|
||||
//! If \a utf16 is `true`, \a data will be treated as UTF-8 data and will be
|
||||
//! converted to UTF-16, and IMAGE_DEBUG_MISC::Unicode will be set to `1`.
|
||||
//! Otherwise, \a data will be used as-is and IMAGE_DEBUG_MISC::Unicode will
|
||||
//! be set to `0`.
|
||||
void SetData(const std::string& data, bool utf16);
|
||||
|
||||
protected:
|
||||
// MinidumpWritable:
|
||||
virtual bool Freeze() override;
|
||||
virtual size_t SizeOfObject() override;
|
||||
virtual bool WriteObject(FileWriterInterface* file_writer) override;
|
||||
|
||||
private:
|
||||
IMAGE_DEBUG_MISC image_debug_misc_;
|
||||
std::string data_;
|
||||
string16 data_utf16_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MinidumpModuleMiscDebugRecordWriter);
|
||||
};
|
||||
|
||||
//! \brief The writer for a MINIDUMP_MODULE object in a minidump file.
|
||||
//!
|
||||
//! Because MINIDUMP_MODULE objects only appear as elements of
|
||||
//! MINIDUMP_MODULE_LIST objects, this class does not write any data on its own.
|
||||
//! It makes its MINIDUMP_MODULE data available to its MinidumpModuleListWriter
|
||||
//! parent, which writes it as part of a MINIDUMP_MODULE_LIST.
|
||||
class MinidumpModuleWriter final : public internal::MinidumpWritable {
|
||||
public:
|
||||
MinidumpModuleWriter();
|
||||
~MinidumpModuleWriter();
|
||||
|
||||
//! \brief Returns a MINIDUMP_MODULE referencing this object’s data.
|
||||
//!
|
||||
//! This method is expected to be called by a MinidumpModuleListWriter in
|
||||
//! order to obtain a MINIDUMP_MODULE to include in its list.
|
||||
//!
|
||||
//! \note Valid in #kStateWritable.
|
||||
const MINIDUMP_MODULE* MinidumpModule() const;
|
||||
|
||||
//! \brief Arranges for MINIDUMP_MODULE::ModuleNameRva to point to a
|
||||
//! MINIDUMP_STRING containing \a name.
|
||||
//!
|
||||
//! A name is required in all MINIDUMP_MODULE objects.
|
||||
//!
|
||||
//! \note Valid in #kStateMutable.
|
||||
void SetName(const std::string& name);
|
||||
|
||||
//! \brief Arranges for MINIDUMP_MODULE::CvRecord to point to a CodeView
|
||||
//! record to be written by \a codeview_record.
|
||||
//!
|
||||
//! \a codeview_record will become a child of this object in the overall tree
|
||||
//! of internal::MinidumpWritable objects.
|
||||
//!
|
||||
//! \note Valid in #kStateMutable.
|
||||
void SetCodeViewRecord(MinidumpModuleCodeViewRecordWriter* codeview_record);
|
||||
|
||||
//! \brief Arranges for MINIDUMP_MODULE::MiscRecord to point to an
|
||||
//! IMAGE_DEBUG_MISC object to be written by \a misc_debug_record.
|
||||
//!
|
||||
//! \a misc_debug_record will become a child of this object in the overall
|
||||
//! tree of internal::MinidumpWritable objects.
|
||||
//!
|
||||
//! \note Valid in #kStateMutable.
|
||||
void SetMiscDebugRecord(
|
||||
MinidumpModuleMiscDebugRecordWriter* misc_debug_record);
|
||||
|
||||
//! \brief Sets IMAGE_DEBUG_MISC::BaseOfImage.
|
||||
void SetImageBaseAddress(uint64_t image_base_address) {
|
||||
module_.BaseOfImage = image_base_address;
|
||||
}
|
||||
|
||||
//! \brief Sets IMAGE_DEBUG_MISC::SizeOfImage.
|
||||
void SetImageSize(uint32_t image_size) { module_.SizeOfImage = image_size; }
|
||||
|
||||
//! \brief Sets IMAGE_DEBUG_MISC::CheckSum.
|
||||
void SetChecksum(uint32_t checksum) { module_.CheckSum = checksum; }
|
||||
|
||||
//! \brief Sets IMAGE_DEBUG_MISC::TimeDateStamp.
|
||||
//!
|
||||
//! \note Valid in #kStateMutable.
|
||||
void SetTimestamp(time_t timestamp);
|
||||
|
||||
//! \brief Sets \ref VS_FIXEDFILEINFO::dwFileVersionMS
|
||||
//! "IMAGE_DEBUG_MISC::VersionInfo::dwFileVersionMS" and \ref
|
||||
//! VS_FIXEDFILEINFO::dwFileVersionLS
|
||||
//! "IMAGE_DEBUG_MISC::VersionInfo::dwFileVersionLS".
|
||||
//!
|
||||
//! \note Valid in #kStateMutable.
|
||||
void SetFileVersion(uint16_t version_0,
|
||||
uint16_t version_1,
|
||||
uint16_t version_2,
|
||||
uint16_t version_3);
|
||||
|
||||
//! \brief Sets \ref VS_FIXEDFILEINFO::dwProductVersionMS
|
||||
//! "IMAGE_DEBUG_MISC::VersionInfo::dwProductVersionMS" and \ref
|
||||
//! VS_FIXEDFILEINFO::dwProductVersionLS
|
||||
//! "IMAGE_DEBUG_MISC::VersionInfo::dwProductVersionLS".
|
||||
//!
|
||||
//! \note Valid in #kStateMutable.
|
||||
void SetProductVersion(uint16_t version_0,
|
||||
uint16_t version_1,
|
||||
uint16_t version_2,
|
||||
uint16_t version_3);
|
||||
|
||||
//! \brief Sets \ref VS_FIXEDFILEINFO::dwFileFlags
|
||||
//! "IMAGE_DEBUG_MISC::VersionInfo::dwFileFlags" and \ref
|
||||
//! VS_FIXEDFILEINFO::dwFileFlagsMask
|
||||
//! "IMAGE_DEBUG_MISC::VersionInfo::dwFileFlagsMask".
|
||||
//!
|
||||
//! \note Valid in #kStateMutable.
|
||||
void SetFileFlagsAndMask(uint32_t file_flags, uint32_t file_flags_mask);
|
||||
|
||||
//! \brief Sets \ref VS_FIXEDFILEINFO::dwFileOS
|
||||
//! "IMAGE_DEBUG_MISC::VersionInfo::dwFileOS".
|
||||
void SetFileOS(uint32_t file_os) { module_.VersionInfo.dwFileOS = file_os; }
|
||||
|
||||
//! \brief Sets \ref VS_FIXEDFILEINFO::dwFileType
|
||||
//! "IMAGE_DEBUG_MISC::VersionInfo::dwFileType" and \ref
|
||||
//! VS_FIXEDFILEINFO::dwFileSubtype
|
||||
//! "IMAGE_DEBUG_MISC::VersionInfo::dwFileSubtype".
|
||||
void SetFileTypeAndSubtype(uint32_t file_type, uint32_t file_subtype) {
|
||||
module_.VersionInfo.dwFileType = file_type;
|
||||
module_.VersionInfo.dwFileSubtype = file_subtype;
|
||||
}
|
||||
|
||||
protected:
|
||||
// MinidumpWritable:
|
||||
virtual bool Freeze() override;
|
||||
virtual size_t SizeOfObject() override;
|
||||
virtual std::vector<MinidumpWritable*> Children() override;
|
||||
virtual bool WriteObject(FileWriterInterface* file_writer) override;
|
||||
|
||||
private:
|
||||
MINIDUMP_MODULE module_;
|
||||
scoped_ptr<internal::MinidumpUTF16StringWriter> name_;
|
||||
MinidumpModuleCodeViewRecordWriter* codeview_record_; // weak
|
||||
MinidumpModuleMiscDebugRecordWriter* misc_debug_record_; // weak
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MinidumpModuleWriter);
|
||||
};
|
||||
|
||||
//! \brief The writer for a MINIDUMP_MODULE_LIST stream in a minidump file,
|
||||
//! containing a list of MINIDUMP_MODULE objects.
|
||||
class MinidumpModuleListWriter final : public internal::MinidumpStreamWriter {
|
||||
public:
|
||||
MinidumpModuleListWriter();
|
||||
~MinidumpModuleListWriter();
|
||||
|
||||
//! \brief Adds a MinidumpModuleWriter to the MINIDUMP_MODULE_LIST.
|
||||
//!
|
||||
//! \a module will become a child of this object in the overall tree of
|
||||
//! internal::MinidumpWritable objects.
|
||||
//!
|
||||
//! \note Valid in #kStateMutable.
|
||||
void AddModule(MinidumpModuleWriter* module);
|
||||
|
||||
protected:
|
||||
// MinidumpWritable:
|
||||
virtual bool Freeze() override;
|
||||
virtual size_t SizeOfObject() override;
|
||||
virtual std::vector<MinidumpWritable*> Children() override;
|
||||
virtual bool WriteObject(FileWriterInterface* file_writer) override;
|
||||
|
||||
// MinidumpStreamWriter:
|
||||
virtual MinidumpStreamType StreamType() const override;
|
||||
|
||||
private:
|
||||
MINIDUMP_MODULE_LIST module_list_base_;
|
||||
std::vector<MinidumpModuleWriter*> modules_; // weak
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MinidumpModuleListWriter);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_
|
673
minidump/minidump_module_writer_test.cc
Normal file
673
minidump/minidump_module_writer_test.cc
Normal file
@ -0,0 +1,673 @@
|
||||
// Copyright 2014 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_module_writer.h"
|
||||
|
||||
#include <dbghelp.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "minidump/minidump_extensions.h"
|
||||
#include "minidump/minidump_file_writer.h"
|
||||
#include "minidump/minidump_test_util.h"
|
||||
#include "util/file/string_file_writer.h"
|
||||
#include "util/misc/uuid.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace crashpad;
|
||||
using namespace crashpad::test;
|
||||
|
||||
void GetModuleListStream(const std::string& file_contents,
|
||||
const MINIDUMP_MODULE_LIST** module_list) {
|
||||
const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER);
|
||||
const size_t kModuleListStreamOffset =
|
||||
kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY);
|
||||
const size_t kModulesOffset =
|
||||
kModuleListStreamOffset + sizeof(MINIDUMP_MODULE_LIST);
|
||||
|
||||
ASSERT_GE(file_contents.size(), kModulesOffset);
|
||||
|
||||
const MINIDUMP_HEADER* header =
|
||||
reinterpret_cast<const MINIDUMP_HEADER*>(&file_contents[0]);
|
||||
|
||||
VerifyMinidumpHeader(header, 1, 0);
|
||||
if (testing::Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const MINIDUMP_DIRECTORY* directory =
|
||||
reinterpret_cast<const MINIDUMP_DIRECTORY*>(
|
||||
&file_contents[kDirectoryOffset]);
|
||||
|
||||
ASSERT_EQ(kMinidumpStreamTypeModuleList, directory->StreamType);
|
||||
ASSERT_GE(directory->Location.DataSize, sizeof(MINIDUMP_MODULE_LIST));
|
||||
ASSERT_EQ(kModuleListStreamOffset, directory->Location.Rva);
|
||||
|
||||
*module_list = reinterpret_cast<const MINIDUMP_MODULE_LIST*>(
|
||||
&file_contents[kModuleListStreamOffset]);
|
||||
|
||||
ASSERT_EQ(sizeof(MINIDUMP_MODULE_LIST) +
|
||||
(*module_list)->NumberOfModules * sizeof(MINIDUMP_MODULE),
|
||||
directory->Location.DataSize);
|
||||
}
|
||||
|
||||
TEST(MinidumpModuleWriter, EmptyModuleList) {
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
MinidumpModuleListWriter module_list_writer;
|
||||
|
||||
minidump_file_writer.AddStream(&module_list_writer);
|
||||
|
||||
StringFileWriter file_writer;
|
||||
ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer));
|
||||
|
||||
ASSERT_EQ(sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||
sizeof(MINIDUMP_MODULE_LIST),
|
||||
file_writer.string().size());
|
||||
|
||||
const MINIDUMP_MODULE_LIST* module_list;
|
||||
GetModuleListStream(file_writer.string(), &module_list);
|
||||
if (Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EXPECT_EQ(0u, module_list->NumberOfModules);
|
||||
}
|
||||
|
||||
// If |expected_pdb_name| is non-NULL, |codeview_record| is used to locate a
|
||||
// CodeView record in |file_contents|, and its fields are compared against the
|
||||
// the |expected_pdb_*| values. If |expected_pdb_uuid| is supplied, the CodeView
|
||||
// record must be a PDB 7.0 link, otherwise, it must be a PDB 2.0 link. If
|
||||
// |expected_pdb_name| is NULL, |codeview_record| must not point to anything.
|
||||
void ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR* codeview_record,
|
||||
const std::string& file_contents,
|
||||
const char* expected_pdb_name,
|
||||
const UUID* expected_pdb_uuid,
|
||||
time_t expected_pdb_timestamp,
|
||||
uint32_t expected_pdb_age) {
|
||||
if (expected_pdb_name) {
|
||||
EXPECT_NE(0u, codeview_record->Rva);
|
||||
ASSERT_LE(codeview_record->Rva + codeview_record->DataSize,
|
||||
file_contents.size());
|
||||
|
||||
std::string observed_pdb_name;
|
||||
if (expected_pdb_uuid) {
|
||||
// The CodeView record should be a PDB 7.0 link.
|
||||
EXPECT_GE(codeview_record->DataSize,
|
||||
sizeof(MinidumpModuleCodeViewRecordPDB70));
|
||||
const MinidumpModuleCodeViewRecordPDB70* codeview_pdb70_record =
|
||||
reinterpret_cast<const MinidumpModuleCodeViewRecordPDB70*>(
|
||||
&file_contents[codeview_record->Rva]);
|
||||
EXPECT_EQ(MinidumpModuleCodeViewRecordPDB70::kSignature,
|
||||
codeview_pdb70_record->signature);
|
||||
EXPECT_EQ(0,
|
||||
memcmp(expected_pdb_uuid,
|
||||
&codeview_pdb70_record->uuid,
|
||||
sizeof(codeview_pdb70_record->uuid)));
|
||||
EXPECT_EQ(expected_pdb_age, codeview_pdb70_record->age);
|
||||
|
||||
observed_pdb_name.assign(
|
||||
reinterpret_cast<const char*>(&codeview_pdb70_record->pdb_name[0]),
|
||||
codeview_record->DataSize -
|
||||
offsetof(MinidumpModuleCodeViewRecordPDB70, pdb_name));
|
||||
} else {
|
||||
// The CodeView record should be a PDB 2.0 link.
|
||||
EXPECT_GE(codeview_record->DataSize,
|
||||
sizeof(MinidumpModuleCodeViewRecordPDB20));
|
||||
const MinidumpModuleCodeViewRecordPDB20* codeview_pdb20_record =
|
||||
reinterpret_cast<const MinidumpModuleCodeViewRecordPDB20*>(
|
||||
&file_contents[codeview_record->Rva]);
|
||||
EXPECT_EQ(MinidumpModuleCodeViewRecordPDB20::kSignature,
|
||||
codeview_pdb20_record->signature);
|
||||
EXPECT_EQ(expected_pdb_timestamp, codeview_pdb20_record->timestamp);
|
||||
EXPECT_EQ(expected_pdb_age, codeview_pdb20_record->age);
|
||||
|
||||
observed_pdb_name.assign(
|
||||
reinterpret_cast<const char*>(&codeview_pdb20_record->pdb_name[0]),
|
||||
codeview_record->DataSize -
|
||||
offsetof(MinidumpModuleCodeViewRecordPDB20, pdb_name));
|
||||
}
|
||||
|
||||
// Check for, and then remove, the NUL terminator.
|
||||
EXPECT_EQ('\0', observed_pdb_name[observed_pdb_name.size() - 1]);
|
||||
observed_pdb_name.resize(observed_pdb_name.size() - 1);
|
||||
|
||||
EXPECT_EQ(expected_pdb_name, observed_pdb_name);
|
||||
} else {
|
||||
// There should be no CodeView record.
|
||||
EXPECT_EQ(0u, codeview_record->DataSize);
|
||||
EXPECT_EQ(0u, codeview_record->Rva);
|
||||
}
|
||||
}
|
||||
|
||||
// If |expected_debug_name| is non-NULL, |misc_record| is used to locate a
|
||||
// miscellanous debugging record in |file_contents|, and its fields are compared
|
||||
// against the the |expected_debug_*| values. If |expected_debug_name| is NULL,
|
||||
// |misc_record| must not point to anything.
|
||||
void ExpectMiscellaneousDebugRecord(
|
||||
const MINIDUMP_LOCATION_DESCRIPTOR* misc_record,
|
||||
const std::string& file_contents,
|
||||
const char* expected_debug_name,
|
||||
uint32_t expected_debug_type,
|
||||
bool expected_debug_utf16) {
|
||||
if (expected_debug_name) {
|
||||
EXPECT_GE(misc_record->DataSize, sizeof(IMAGE_DEBUG_MISC));
|
||||
EXPECT_NE(0u, misc_record->Rva);
|
||||
ASSERT_LE(misc_record->Rva + misc_record->DataSize, file_contents.size());
|
||||
const IMAGE_DEBUG_MISC* misc_debug_record =
|
||||
reinterpret_cast<const IMAGE_DEBUG_MISC*>(
|
||||
&file_contents[misc_record->Rva]);
|
||||
EXPECT_EQ(expected_debug_type, misc_debug_record->DataType);
|
||||
EXPECT_EQ(misc_record->DataSize, misc_debug_record->Length);
|
||||
EXPECT_EQ(expected_debug_utf16, misc_debug_record->Unicode);
|
||||
EXPECT_EQ(0u, misc_debug_record->Reserved[0]);
|
||||
EXPECT_EQ(0u, misc_debug_record->Reserved[1]);
|
||||
EXPECT_EQ(0u, misc_debug_record->Reserved[2]);
|
||||
|
||||
// Check for the NUL terminator.
|
||||
size_t bytes_available =
|
||||
misc_debug_record->Length - offsetof(IMAGE_DEBUG_MISC, Data);
|
||||
EXPECT_EQ('\0', misc_debug_record->Data[bytes_available - 1]);
|
||||
std::string observed_data(
|
||||
reinterpret_cast<const char*>(misc_debug_record->Data));
|
||||
|
||||
size_t bytes_used;
|
||||
if (misc_debug_record->Unicode) {
|
||||
string16 observed_data_utf16(
|
||||
reinterpret_cast<const char16*>(misc_debug_record->Data));
|
||||
bytes_used = (observed_data_utf16.size() + 1) * sizeof(char16);
|
||||
observed_data = base::UTF16ToUTF8(observed_data_utf16);
|
||||
} else {
|
||||
observed_data = reinterpret_cast<const char*>(misc_debug_record->Data);
|
||||
bytes_used = (observed_data.size() + 1) * sizeof(char);
|
||||
}
|
||||
EXPECT_LE(bytes_used, bytes_available);
|
||||
|
||||
// Make sure that any padding bytes after the first NUL are also NUL.
|
||||
for (size_t index = bytes_used; index < bytes_available; ++index) {
|
||||
EXPECT_EQ('\0', misc_debug_record->Data[index]);
|
||||
}
|
||||
|
||||
EXPECT_EQ(expected_debug_name, observed_data);
|
||||
} else {
|
||||
// There should be no miscellaneous debugging record.
|
||||
EXPECT_EQ(0u, misc_record->DataSize);
|
||||
EXPECT_EQ(0u, misc_record->Rva);
|
||||
}
|
||||
}
|
||||
|
||||
// ExpectModule() verifies that |expected| matches |observed|. Fields that are
|
||||
// supposed to contain constant magic numbers are verified against the expected
|
||||
// constants instead of |expected|. Reserved fields are verified to be 0. RVA
|
||||
// and MINIDUMP_LOCATION_DESCRIPTOR fields are not verified against |expected|.
|
||||
// Instead, |ModuleNameRva| is used to locate the module name, which is compared
|
||||
// against |expected_module_name|. ExpectCodeViewRecord() and
|
||||
// ExpectMiscellaneousDebugRecord() are used to verify the |CvRecord| and
|
||||
// |MiscRecord| fields against |expected_pdb_*| and |expected_debug_*|
|
||||
// parameters, respectively.
|
||||
void ExpectModule(const MINIDUMP_MODULE* expected,
|
||||
const MINIDUMP_MODULE* observed,
|
||||
const std::string& file_contents,
|
||||
const std::string& expected_module_name,
|
||||
const char* expected_pdb_name,
|
||||
const UUID* expected_pdb_uuid,
|
||||
time_t expected_pdb_timestamp,
|
||||
uint32_t expected_pdb_age,
|
||||
const char* expected_debug_name,
|
||||
uint32_t expected_debug_type,
|
||||
bool expected_debug_utf16) {
|
||||
EXPECT_EQ(expected->BaseOfImage, observed->BaseOfImage);
|
||||
EXPECT_EQ(expected->SizeOfImage, observed->SizeOfImage);
|
||||
EXPECT_EQ(expected->CheckSum, observed->CheckSum);
|
||||
EXPECT_EQ(expected->TimeDateStamp, observed->TimeDateStamp);
|
||||
EXPECT_EQ(static_cast<uint32_t>(VS_FFI_SIGNATURE),
|
||||
observed->VersionInfo.dwSignature);
|
||||
EXPECT_EQ(static_cast<uint32_t>(VS_FFI_STRUCVERSION),
|
||||
observed->VersionInfo.dwStrucVersion);
|
||||
EXPECT_EQ(expected->VersionInfo.dwFileVersionMS,
|
||||
observed->VersionInfo.dwFileVersionMS);
|
||||
EXPECT_EQ(expected->VersionInfo.dwFileVersionLS,
|
||||
observed->VersionInfo.dwFileVersionLS);
|
||||
EXPECT_EQ(expected->VersionInfo.dwProductVersionMS,
|
||||
observed->VersionInfo.dwProductVersionMS);
|
||||
EXPECT_EQ(expected->VersionInfo.dwProductVersionLS,
|
||||
observed->VersionInfo.dwProductVersionLS);
|
||||
EXPECT_EQ(expected->VersionInfo.dwFileFlagsMask,
|
||||
observed->VersionInfo.dwFileFlagsMask);
|
||||
EXPECT_EQ(expected->VersionInfo.dwFileFlags,
|
||||
observed->VersionInfo.dwFileFlags);
|
||||
EXPECT_EQ(expected->VersionInfo.dwFileOS, observed->VersionInfo.dwFileOS);
|
||||
EXPECT_EQ(expected->VersionInfo.dwFileType, observed->VersionInfo.dwFileType);
|
||||
EXPECT_EQ(expected->VersionInfo.dwFileSubtype,
|
||||
observed->VersionInfo.dwFileSubtype);
|
||||
EXPECT_EQ(expected->VersionInfo.dwFileDateMS,
|
||||
observed->VersionInfo.dwFileDateMS);
|
||||
EXPECT_EQ(expected->VersionInfo.dwFileDateLS,
|
||||
observed->VersionInfo.dwFileDateLS);
|
||||
EXPECT_EQ(0u, observed->Reserved0);
|
||||
EXPECT_EQ(0u, observed->Reserved1);
|
||||
|
||||
EXPECT_NE(0u, observed->ModuleNameRva);
|
||||
ASSERT_LE(observed->ModuleNameRva,
|
||||
file_contents.size() - sizeof(MINIDUMP_STRING));
|
||||
const MINIDUMP_STRING* module_name = reinterpret_cast<const MINIDUMP_STRING*>(
|
||||
&file_contents[observed->ModuleNameRva]);
|
||||
ASSERT_LE(observed->ModuleNameRva + sizeof(MINIDUMP_STRING) +
|
||||
(module_name->Length + 1),
|
||||
file_contents.size());
|
||||
ASSERT_EQ(0u, module_name->Length % 2);
|
||||
string16 observed_module_name_utf16(
|
||||
reinterpret_cast<const char16*>(
|
||||
&file_contents[observed->ModuleNameRva + sizeof(MINIDUMP_STRING)]),
|
||||
module_name->Length / 2);
|
||||
string16 expected_module_name_utf16 = base::UTF8ToUTF16(expected_module_name);
|
||||
EXPECT_EQ(expected_module_name_utf16, observed_module_name_utf16);
|
||||
|
||||
ExpectCodeViewRecord(&observed->CvRecord,
|
||||
file_contents,
|
||||
expected_pdb_name,
|
||||
expected_pdb_uuid,
|
||||
expected_pdb_timestamp,
|
||||
expected_pdb_age);
|
||||
if (testing::Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ExpectMiscellaneousDebugRecord(&observed->MiscRecord,
|
||||
file_contents,
|
||||
expected_debug_name,
|
||||
expected_debug_type,
|
||||
expected_debug_utf16);
|
||||
if (testing::Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MinidumpModuleWriter, EmptyModule) {
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
MinidumpModuleListWriter module_list_writer;
|
||||
|
||||
const char kModuleName[] = "test_executable";
|
||||
|
||||
MinidumpModuleWriter module_writer;
|
||||
module_writer.SetName(kModuleName);
|
||||
|
||||
module_list_writer.AddModule(&module_writer);
|
||||
minidump_file_writer.AddStream(&module_list_writer);
|
||||
|
||||
StringFileWriter file_writer;
|
||||
ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer));
|
||||
|
||||
ASSERT_GT(file_writer.string().size(),
|
||||
sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||
sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
|
||||
|
||||
const MINIDUMP_MODULE_LIST* module_list;
|
||||
GetModuleListStream(file_writer.string(), &module_list);
|
||||
if (Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EXPECT_EQ(1u, module_list->NumberOfModules);
|
||||
|
||||
MINIDUMP_MODULE expected = {};
|
||||
ExpectModule(&expected,
|
||||
&module_list->Modules[0],
|
||||
file_writer.string(),
|
||||
kModuleName,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
false);
|
||||
if (Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MinidumpModuleWriter, OneModule) {
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
MinidumpModuleListWriter module_list_writer;
|
||||
|
||||
const char kModuleName[] = "statically_linked";
|
||||
const uint64_t kModuleBase = 0x10da69000;
|
||||
const uint32_t kModuleSize = 0x1000;
|
||||
const uint32_t kChecksum = 0x76543210;
|
||||
const time_t kTimestamp = 0x386d4380;
|
||||
const uint32_t kFileVersionMS = 0x00010002;
|
||||
const uint32_t kFileVersionLS = 0x00030004;
|
||||
const uint32_t kProductVersionMS = 0x00050006;
|
||||
const uint32_t kProductVersionLS = 0x00070008;
|
||||
const uint32_t kFileFlagsMask = VS_FF_DEBUG | VS_FF_PRERELEASE |
|
||||
VS_FF_PATCHED | VS_FF_PRIVATEBUILD |
|
||||
VS_FF_INFOINFERRED | VS_FF_SPECIALBUILD;
|
||||
const uint32_t kFileFlags = VS_FF_PRIVATEBUILD | VS_FF_SPECIALBUILD;
|
||||
const uint32_t kFileOS = VOS_DOS;
|
||||
const uint32_t kFileType = VFT_DRV;
|
||||
const uint32_t kFileSubtype = VFT2_DRV_KEYBOARD;
|
||||
const char kPDBName[] = "statical.pdb";
|
||||
const uint8_t kPDBUUIDBytes[16] =
|
||||
{0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10,
|
||||
0x08, 0x19, 0x2a, 0x3b, 0x4c, 0x5d, 0x6e, 0x7f};
|
||||
UUID pdb_uuid;
|
||||
pdb_uuid.InitializeFromBytes(kPDBUUIDBytes);
|
||||
const uint32_t kPDBAge = 1;
|
||||
const uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME;
|
||||
const char kDebugName[] = "statical.dbg";
|
||||
const bool kDebugUTF16 = false;
|
||||
|
||||
MinidumpModuleWriter module_writer;
|
||||
module_writer.SetName(kModuleName);
|
||||
module_writer.SetImageBaseAddress(kModuleBase);
|
||||
module_writer.SetImageSize(kModuleSize);
|
||||
module_writer.SetChecksum(kChecksum);
|
||||
module_writer.SetTimestamp(kTimestamp);
|
||||
module_writer.SetFileVersion(kFileVersionMS >> 16,
|
||||
kFileVersionMS & 0xffff,
|
||||
kFileVersionLS >> 16,
|
||||
kFileVersionLS & 0xffff);
|
||||
module_writer.SetProductVersion(kProductVersionMS >> 16,
|
||||
kProductVersionMS & 0xffff,
|
||||
kProductVersionLS >> 16,
|
||||
kProductVersionLS & 0xffff);
|
||||
module_writer.SetFileFlagsAndMask(kFileFlags, kFileFlagsMask);
|
||||
module_writer.SetFileOS(kFileOS);
|
||||
module_writer.SetFileTypeAndSubtype(kFileType, kFileSubtype);
|
||||
|
||||
MinidumpModuleCodeViewRecordPDB70Writer codeview_pdb70_writer;
|
||||
codeview_pdb70_writer.SetPDBName(kPDBName);
|
||||
codeview_pdb70_writer.SetUUIDAndAge(pdb_uuid, kPDBAge);
|
||||
module_writer.SetCodeViewRecord(&codeview_pdb70_writer);
|
||||
|
||||
MinidumpModuleMiscDebugRecordWriter misc_debug_writer;
|
||||
misc_debug_writer.SetDataType(kDebugType);
|
||||
misc_debug_writer.SetData(kDebugName, kDebugUTF16);
|
||||
module_writer.SetMiscDebugRecord(&misc_debug_writer);
|
||||
|
||||
module_list_writer.AddModule(&module_writer);
|
||||
minidump_file_writer.AddStream(&module_list_writer);
|
||||
|
||||
StringFileWriter file_writer;
|
||||
ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer));
|
||||
|
||||
ASSERT_GT(file_writer.string().size(),
|
||||
sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||
sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
|
||||
|
||||
const MINIDUMP_MODULE_LIST* module_list;
|
||||
GetModuleListStream(file_writer.string(), &module_list);
|
||||
if (Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EXPECT_EQ(1u, module_list->NumberOfModules);
|
||||
|
||||
MINIDUMP_MODULE expected = {};
|
||||
expected.BaseOfImage = kModuleBase;
|
||||
expected.SizeOfImage = kModuleSize;
|
||||
expected.CheckSum = kChecksum;
|
||||
expected.TimeDateStamp = kTimestamp;
|
||||
expected.VersionInfo.dwFileVersionMS = kFileVersionMS;
|
||||
expected.VersionInfo.dwFileVersionLS = kFileVersionLS;
|
||||
expected.VersionInfo.dwProductVersionMS = kProductVersionMS;
|
||||
expected.VersionInfo.dwProductVersionLS = kProductVersionLS;
|
||||
expected.VersionInfo.dwFileFlagsMask = kFileFlagsMask;
|
||||
expected.VersionInfo.dwFileFlags = kFileFlags;
|
||||
expected.VersionInfo.dwFileOS = kFileOS;
|
||||
expected.VersionInfo.dwFileType = kFileType;
|
||||
expected.VersionInfo.dwFileSubtype = kFileSubtype;
|
||||
|
||||
ExpectModule(&expected,
|
||||
&module_list->Modules[0],
|
||||
file_writer.string(),
|
||||
kModuleName,
|
||||
kPDBName,
|
||||
&pdb_uuid,
|
||||
0,
|
||||
kPDBAge,
|
||||
kDebugName,
|
||||
kDebugType,
|
||||
kDebugUTF16);
|
||||
if (Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MinidumpModuleWriter, OneModule_CodeViewUsesPDB20_MiscUsesUTF16) {
|
||||
// MinidumpModuleWriter.OneModule tested with a PDB 7.0 link as the CodeView
|
||||
// record and an IMAGE_DEBUG_MISC record in UTF-8. This test exercises the
|
||||
// alternatives, a PDB 2.0 link as the CodeView record and an IMAGE_DEBUG_MISC
|
||||
// record with UTF-16 data.
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
MinidumpModuleListWriter module_list_writer;
|
||||
|
||||
const char kModuleName[] = "dinosaur";
|
||||
const char kPDBName[] = "d1n05.pdb";
|
||||
const time_t kPDBTimestamp = 0x386d4380;
|
||||
const uint32_t kPDBAge = 1;
|
||||
const uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME;
|
||||
const char kDebugName[] = "d1n05.dbg";
|
||||
const bool kDebugUTF16 = true;
|
||||
|
||||
MinidumpModuleWriter module_writer;
|
||||
module_writer.SetName(kModuleName);
|
||||
|
||||
MinidumpModuleCodeViewRecordPDB20Writer codeview_pdb20_writer;
|
||||
codeview_pdb20_writer.SetPDBName(kPDBName);
|
||||
codeview_pdb20_writer.SetTimestampAndAge(kPDBTimestamp, kPDBAge);
|
||||
module_writer.SetCodeViewRecord(&codeview_pdb20_writer);
|
||||
|
||||
MinidumpModuleMiscDebugRecordWriter misc_debug_writer;
|
||||
misc_debug_writer.SetDataType(kDebugType);
|
||||
misc_debug_writer.SetData(kDebugName, kDebugUTF16);
|
||||
module_writer.SetMiscDebugRecord(&misc_debug_writer);
|
||||
|
||||
module_list_writer.AddModule(&module_writer);
|
||||
minidump_file_writer.AddStream(&module_list_writer);
|
||||
|
||||
StringFileWriter file_writer;
|
||||
ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer));
|
||||
|
||||
ASSERT_GT(file_writer.string().size(),
|
||||
sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||
sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
|
||||
|
||||
const MINIDUMP_MODULE_LIST* module_list;
|
||||
GetModuleListStream(file_writer.string(), &module_list);
|
||||
if (Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EXPECT_EQ(1u, module_list->NumberOfModules);
|
||||
|
||||
MINIDUMP_MODULE expected = {};
|
||||
|
||||
ExpectModule(&expected,
|
||||
&module_list->Modules[0],
|
||||
file_writer.string(),
|
||||
kModuleName,
|
||||
kPDBName,
|
||||
NULL,
|
||||
kPDBTimestamp,
|
||||
kPDBAge,
|
||||
kDebugName,
|
||||
kDebugType,
|
||||
kDebugUTF16);
|
||||
if (Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MinidumpModuleWriter, ThreeModules) {
|
||||
// As good exercise, this test uses three modules, one with a PDB 7.0 link as
|
||||
// its CodeView record, one with no CodeView record, and one with a PDB 2.0
|
||||
// link as its CodeView record.
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
MinidumpModuleListWriter module_list_writer;
|
||||
|
||||
const char kModuleName1[] = "main";
|
||||
const uint64_t kModuleBase1 = 0x100101000;
|
||||
const uint32_t kModuleSize1 = 0xf000;
|
||||
const char kPDBName1[] = "main";
|
||||
const uint8_t kPDBUUIDBytes1[16] =
|
||||
{0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11,
|
||||
0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99};
|
||||
UUID pdb_uuid_1;
|
||||
pdb_uuid_1.InitializeFromBytes(kPDBUUIDBytes1);
|
||||
const uint32_t kPDBAge1 = 0;
|
||||
|
||||
const char kModuleName2[] = "ld.so";
|
||||
const uint64_t kModuleBase2 = 0x200202000;
|
||||
const uint32_t kModuleSize2 = 0x1e000;
|
||||
|
||||
const char kModuleName3[] = "libc.so";
|
||||
const uint64_t kModuleBase3 = 0x300303000;
|
||||
const uint32_t kModuleSize3 = 0x2d000;
|
||||
const char kPDBName3[] = "libc.so";
|
||||
const time_t kPDBTimestamp3 = 0x386d4380;
|
||||
const uint32_t kPDBAge3 = 2;
|
||||
|
||||
MinidumpModuleWriter module_writer_1;
|
||||
module_writer_1.SetName(kModuleName1);
|
||||
module_writer_1.SetImageBaseAddress(kModuleBase1);
|
||||
module_writer_1.SetImageSize(kModuleSize1);
|
||||
|
||||
MinidumpModuleCodeViewRecordPDB70Writer codeview_pdb70_writer_1;
|
||||
codeview_pdb70_writer_1.SetPDBName(kPDBName1);
|
||||
codeview_pdb70_writer_1.SetUUIDAndAge(pdb_uuid_1, kPDBAge1);
|
||||
module_writer_1.SetCodeViewRecord(&codeview_pdb70_writer_1);
|
||||
|
||||
module_list_writer.AddModule(&module_writer_1);
|
||||
|
||||
MinidumpModuleWriter module_writer_2;
|
||||
module_writer_2.SetName(kModuleName2);
|
||||
module_writer_2.SetImageBaseAddress(kModuleBase2);
|
||||
module_writer_2.SetImageSize(kModuleSize2);
|
||||
|
||||
module_list_writer.AddModule(&module_writer_2);
|
||||
|
||||
MinidumpModuleWriter module_writer_3;
|
||||
module_writer_3.SetName(kModuleName3);
|
||||
module_writer_3.SetImageBaseAddress(kModuleBase3);
|
||||
module_writer_3.SetImageSize(kModuleSize3);
|
||||
|
||||
MinidumpModuleCodeViewRecordPDB20Writer codeview_pdb70_writer_3;
|
||||
codeview_pdb70_writer_3.SetPDBName(kPDBName3);
|
||||
codeview_pdb70_writer_3.SetTimestampAndAge(kPDBTimestamp3, kPDBAge3);
|
||||
module_writer_3.SetCodeViewRecord(&codeview_pdb70_writer_3);
|
||||
|
||||
module_list_writer.AddModule(&module_writer_3);
|
||||
|
||||
minidump_file_writer.AddStream(&module_list_writer);
|
||||
|
||||
StringFileWriter file_writer;
|
||||
ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer));
|
||||
|
||||
ASSERT_GT(file_writer.string().size(),
|
||||
sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) +
|
||||
sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE));
|
||||
|
||||
const MINIDUMP_MODULE_LIST* module_list;
|
||||
GetModuleListStream(file_writer.string(), &module_list);
|
||||
if (Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
|
||||
EXPECT_EQ(3u, module_list->NumberOfModules);
|
||||
|
||||
MINIDUMP_MODULE expected = {};
|
||||
|
||||
{
|
||||
SCOPED_TRACE("module 0");
|
||||
|
||||
expected.BaseOfImage = kModuleBase1;
|
||||
expected.SizeOfImage = kModuleSize1;
|
||||
|
||||
ExpectModule(&expected,
|
||||
&module_list->Modules[0],
|
||||
file_writer.string(),
|
||||
kModuleName1,
|
||||
kPDBName1,
|
||||
&pdb_uuid_1,
|
||||
0,
|
||||
kPDBAge1,
|
||||
NULL,
|
||||
0,
|
||||
false);
|
||||
if (Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE("module 1");
|
||||
|
||||
expected.BaseOfImage = kModuleBase2;
|
||||
expected.SizeOfImage = kModuleSize2;
|
||||
|
||||
ExpectModule(&expected,
|
||||
&module_list->Modules[1],
|
||||
file_writer.string(),
|
||||
kModuleName2,
|
||||
NULL,
|
||||
NULL,
|
||||
0,
|
||||
0,
|
||||
NULL,
|
||||
0,
|
||||
false);
|
||||
if (Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
SCOPED_TRACE("module 2");
|
||||
|
||||
expected.BaseOfImage = kModuleBase3;
|
||||
expected.SizeOfImage = kModuleSize3;
|
||||
|
||||
ExpectModule(&expected,
|
||||
&module_list->Modules[2],
|
||||
file_writer.string(),
|
||||
kModuleName3,
|
||||
kPDBName3,
|
||||
NULL,
|
||||
kPDBTimestamp3,
|
||||
kPDBAge3,
|
||||
NULL,
|
||||
0,
|
||||
false);
|
||||
if (Test::HasFatalFailure()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST(MinidumpSystemInfoWriterDeathTest, NoModuleName) {
|
||||
MinidumpFileWriter minidump_file_writer;
|
||||
MinidumpModuleListWriter module_list_writer;
|
||||
MinidumpModuleWriter module_writer;
|
||||
module_list_writer.AddModule(&module_writer);
|
||||
minidump_file_writer.AddStream(&module_list_writer);
|
||||
|
||||
StringFileWriter file_writer;
|
||||
ASSERT_DEATH(minidump_file_writer.WriteEverything(&file_writer), "name_");
|
||||
}
|
||||
|
||||
} // namespace
|
Loading…
x
Reference in New Issue
Block a user