crashpad/minidump/minidump_module_writer_test.cc
2014-08-13 15:26:21 -07:00

674 lines
25 KiB
C++

// 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