diff --git a/compat/non_win/dbghelp.h b/compat/non_win/dbghelp.h index 389544bd..9e73e55b 100644 --- a/compat/non_win/dbghelp.h +++ b/compat/non_win/dbghelp.h @@ -209,7 +209,7 @@ union __attribute__((packed, aligned(4))) CPU_INFORMATION { //! 32-bit x86 processes. struct __attribute__((packed, aligned(4))) { //! \brief Bitfields containing supported CPU capabilities as identified by - //! bits corresponding to `PF_*` values passed to + //! bits corresponding to \ref PF_x "PF_*" values passed to //! `IsProcessorFeaturePresent()`. uint64_t ProcessorFeatures[2]; } OtherCpuInfo; diff --git a/compat/non_win/winnt.h b/compat/non_win/winnt.h index e9ccacaa..d61504c0 100644 --- a/compat/non_win/winnt.h +++ b/compat/non_win/winnt.h @@ -69,8 +69,8 @@ //! \anchor PF_x //! \name PF_* //! -//! \brief CPU feature values for -//! CPU_INFORMATION::OtherCpuInfo::ProcessorFeatures. +//! \brief CPU feature values for \ref CPU_INFORMATION::ProcessorFeatures +//! "CPU_INFORMATION::OtherCpuInfo::ProcessorFeatures". //! //! \{ #define PF_FLOATING_POINT_PRECISION_ERRATA 0 diff --git a/minidump/minidump.gyp b/minidump/minidump.gyp index 9fe0e787..2692aa24 100644 --- a/minidump/minidump.gyp +++ b/minidump/minidump.gyp @@ -36,6 +36,8 @@ 'minidump_stream_writer.h', 'minidump_string_writer.cc', 'minidump_string_writer.h', + 'minidump_system_info_writer.cc', + 'minidump_system_info_writer.h', 'minidump_writable.cc', 'minidump_writable.h', 'minidump_writer_util.cc', @@ -57,6 +59,7 @@ '../third_party/gtest/gtest/src/gtest_main.cc', 'minidump_file_writer_test.cc', 'minidump_string_writer_test.cc', + 'minidump_system_info_writer_test.cc', 'minidump_writable_test.cc', ], }, diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index 26b34519..e7618e3e 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -121,7 +121,13 @@ enum MinidumpCPUArchitecture : uint16_t { //! These systems identify their CPUs as “x86_64”, “amd64”, or “x64”. kMinidumpCPUArchitectureAMD64 = PROCESSOR_ARCHITECTURE_AMD64, + //! \brief A 32-bit x86 process running on IA-64 (Itanium). + //! + //! \note This value is not used in minidump files for 32-bit x86 processes + //! running on a 64-bit-capable x86 CPU and operating system. In that + //! configuration, #kMinidumpCPUArchitectureX86 is used instead. kMinidumpCPUArchitectureX86Win64 = PROCESSOR_ARCHITECTURE_IA32_ON_WIN64, + kMinidumpCPUArchitectureNeutral = PROCESSOR_ARCHITECTURE_NEUTRAL, kMinidumpCPUArchitectureSPARC = 0x8001, diff --git a/minidump/minidump_system_info_writer.cc b/minidump/minidump_system_info_writer.cc new file mode 100644 index 00000000..f59e3d37 --- /dev/null +++ b/minidump/minidump_system_info_writer.cc @@ -0,0 +1,153 @@ +// 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_system_info_writer.h" + +#include + +#include "base/logging.h" +#include "minidump/minidump_string_writer.h" + +namespace crashpad { + +MinidumpSystemInfoWriter::MinidumpSystemInfoWriter() + : MinidumpStreamWriter(), system_info_(), csd_version_() { + system_info_.ProcessorArchitecture = kMinidumpCPUArchitectureUnknown; +} + +MinidumpSystemInfoWriter::~MinidumpSystemInfoWriter() { +} + +void MinidumpSystemInfoWriter::SetCSDVersion(const std::string& csd_version) { + DCHECK_EQ(state(), kStateMutable); + + if (!csd_version_) { + csd_version_.reset(new internal::MinidumpUTF16StringWriter()); + } + + csd_version_->SetUTF8(csd_version); +} + +void MinidumpSystemInfoWriter::SetCPUX86Vendor(uint32_t ebx, + uint32_t edx, + uint32_t ecx) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 || + system_info_.ProcessorArchitecture == + kMinidumpCPUArchitectureX86Win64); + + COMPILE_ASSERT(arraysize(system_info_.Cpu.X86CpuInfo.VendorId) == 3, + vendor_id_must_have_3_elements); + + system_info_.Cpu.X86CpuInfo.VendorId[0] = ebx; + system_info_.Cpu.X86CpuInfo.VendorId[1] = edx; + system_info_.Cpu.X86CpuInfo.VendorId[2] = ecx; +} + +void MinidumpSystemInfoWriter::SetCPUX86VendorString( + const std::string& vendor) { + DCHECK_EQ(state(), kStateMutable); + CHECK_EQ(vendor.size(), sizeof(system_info_.Cpu.X86CpuInfo.VendorId)); + + uint32_t registers[3]; + COMPILE_ASSERT( + sizeof(registers) == sizeof(system_info_.Cpu.X86CpuInfo.VendorId), + vendor_id_sizes_must_be_equal); + + for (size_t index = 0; index < arraysize(registers); ++index) { + memcpy(®isters[index], + &vendor[index * sizeof(*registers)], + sizeof(*registers)); + } + + SetCPUX86Vendor(registers[0], registers[1], registers[2]); +} + +void MinidumpSystemInfoWriter::SetCPUX86VersionAndFeatures(uint32_t version, + uint32_t features) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 || + system_info_.ProcessorArchitecture == + kMinidumpCPUArchitectureX86Win64); + + system_info_.Cpu.X86CpuInfo.VersionInformation = version; + system_info_.Cpu.X86CpuInfo.FeatureInformation = features; +} + +void MinidumpSystemInfoWriter::SetCPUX86AMDExtendedFeatures( + uint32_t extended_features) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 || + system_info_.ProcessorArchitecture == + kMinidumpCPUArchitectureX86Win64); + DCHECK(system_info_.Cpu.X86CpuInfo.VendorId[0] == 'htuA' && + system_info_.Cpu.X86CpuInfo.VendorId[1] == 'itne' && + system_info_.Cpu.X86CpuInfo.VendorId[2] == 'DMAc'); + + system_info_.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = extended_features; +} + +void MinidumpSystemInfoWriter::SetCPUOtherFeatures(uint64_t features_0, + uint64_t features_1) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(system_info_.ProcessorArchitecture != kMinidumpCPUArchitectureX86 && + system_info_.ProcessorArchitecture != + kMinidumpCPUArchitectureX86Win64); + + COMPILE_ASSERT( + arraysize(system_info_.Cpu.OtherCpuInfo.ProcessorFeatures) == 2, + processor_features_must_have_2_elements); + + system_info_.Cpu.OtherCpuInfo.ProcessorFeatures[0] = features_0; + system_info_.Cpu.OtherCpuInfo.ProcessorFeatures[1] = features_1; +} + +bool MinidumpSystemInfoWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK(csd_version_); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + csd_version_->RegisterRVA(&system_info_.CSDVersionRva); + + return true; +} + +size_t MinidumpSystemInfoWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(system_info_); +} + +std::vector MinidumpSystemInfoWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK(csd_version_); + + std::vector children(1, csd_version_.get()); + return children; +} + +bool MinidumpSystemInfoWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&system_info_, sizeof(system_info_)); +} + +MinidumpStreamType MinidumpSystemInfoWriter::StreamType() const { + return kMinidumpStreamTypeSystemInfo; +} + +} // namespace crashpad diff --git a/minidump/minidump_system_info_writer.h b/minidump/minidump_system_info_writer.h new file mode 100644 index 00000000..eeeb7f5f --- /dev/null +++ b/minidump/minidump_system_info_writer.h @@ -0,0 +1,186 @@ +// 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_SYSTEM_INFO_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_ + +#include +#include +#include + +#include +#include + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.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 writer for a MINIDUMP_SYSTEM_INFO stream in a minidump file. +class MinidumpSystemInfoWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpSystemInfoWriter(); + ~MinidumpSystemInfoWriter(); + + //! \brief Sets MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. + void SetCPUArchitecture(MinidumpCPUArchitecture processor_architecture) { + system_info_.ProcessorArchitecture = processor_architecture; + } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::ProcessorLevel and + //! MINIDUMP_SYSTEM_INFO::ProcessorRevision. + void SetCPULevelAndRevision(uint16_t processor_level, + uint16_t processor_revision) { + system_info_.ProcessorLevel = processor_level; + system_info_.ProcessorRevision = processor_revision; + } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::NumberOfProcessors. + void SetCPUCount(uint8_t number_of_processors) { + system_info_.NumberOfProcessors = number_of_processors; + } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::PlatformId. + void SetOS(MinidumpOS platform_id) { system_info_.PlatformId = platform_id; } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::ProductType. + void SetOSType(MinidumpOSType product_type) { + system_info_.ProductType = product_type; + } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::MajorVersion, + //! MINIDUMP_SYSTEM_INFO::MinorVersion, and + //! MINIDUMP_SYSTEM_INFO::BuildNumber. + void SetOSVersion(uint32_t major_version, + uint32_t minor_version, + uint32_t build_number) { + system_info_.MajorVersion = major_version; + system_info_.MinorVersion = minor_version; + system_info_.BuildNumber = build_number; + } + + //! \brief Arranges for MINIDUMP_SYSTEM_INFO::CSDVersionRva to point to a + //! MINIDUMP_STRING containing the supplied string. + //! + //! This method must be called prior to Freeze(). A CSD version is required + //! in all MINIDUMP_SYSTEM_INFO streams. An empty string is an acceptable + //! value. + void SetCSDVersion(const std::string& csd_version); + + //! \brief Sets MINIDUMP_SYSTEM_INFO::SuiteMask. + void SetSuiteMask(uint16_t suite_mask) { + system_info_.SuiteMask = suite_mask; + } + + //! \brief Sets \ref CPU_INFORMATION::VendorId + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VendorId". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to #kMinidumpCPUArchitectureX86 or + //! #kMinidumpCPUArchitectureX86Win64. + //! + //! \param[in] ebx The first 4 bytes of the CPU vendor string, the value + //! reported in `cpuid 0` `ebx`. + //! \param[in] edx The middle 4 bytes of the CPU vendor string, the value + //! reported in `cpuid 0` `edx`. + //! \param[in] ecx The last 4 bytes of the CPU vendor string, the value + //! reported by `cpuid 0` `ecx`. + //! + //! \note Do not call this method if SetCPUArchitecture() has been used to set + //! the CPU architecture to #kMinidumpCPUArchitectureAMD64. + //! + //! \sa SetCPUX86VendorString() + void SetCPUX86Vendor(uint32_t ebx, uint32_t edx, uint32_t ecx); + + //! \brief Sets \ref CPU_INFORMATION::VendorId + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VendorId". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to #kMinidumpCPUArchitectureX86 or + //! #kMinidumpCPUArchitectureX86Win64. + //! + //! \param[in] vendor The entire CPU vendor string, which must be exactly 12 + //! bytes long. + //! + //! \note Do not call this method if SetCPUArchitecture() has been used to set + //! the CPU architecture to #kMinidumpCPUArchitectureAMD64. + //! + //! \sa SetCPUX86Vendor() + void SetCPUX86VendorString(const std::string& vendor); + + //! \brief Sets \ref CPU_INFORMATION::VersionInformation + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VersionInformation" and + //! \ref CPU_INFORMATION::FeatureInformation + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::FeatureInformation". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to #kMinidumpCPUArchitectureX86 or + //! #kMinidumpCPUArchitectureX86Win64. + //! + //! \note Do not call this method if SetCPUArchitecture() has been used to set + //! the CPU architecture to #kMinidumpCPUArchitectureAMD64. + void SetCPUX86VersionAndFeatures(uint32_t version, uint32_t features); + + //! \brief Sets \ref CPU_INFORMATION::AMDExtendedCpuFeatures + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::AMDExtendedCPUFeatures". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to #kMinidumpCPUArchitectureX86 or + //! #kMinidumpCPUArchitectureX86Win64, and if SetCPUX86Vendor() or + //! SetCPUX86VendorString() has been used to set the CPU vendor to + //! “AuthenticAMD”. + //! + //! \note Do not call this method if SetCPUArchitecture() has been used to set + //! the CPU architecture to #kMinidumpCPUArchitectureAMD64. + void SetCPUX86AMDExtendedFeatures(uint32_t extended_features); + + //! \brief Sets \ref CPU_INFORMATION::ProcessorFeatures + //! "MINIDUMP_SYSTEM_INFO::Cpu::OtherCpuInfo::ProcessorFeatures". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to an architecture other than #kMinidumpCPUArchitectureX86 + //! or #kMinidumpCPUArchitectureX86Win64. + //! + //! \note This method may be called if SetCPUArchitecture() has been used to + //! set the CPU architecture to #kMinidumpCPUArchitectureAMD64. + void SetCPUOtherFeatures(uint64_t features_0, uint64_t features_1); + + protected: + // MinidumpWritable: + virtual bool Freeze() override; + virtual size_t SizeOfObject() override; + virtual std::vector Children() override; + virtual bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + virtual MinidumpStreamType StreamType() const override; + + private: + MINIDUMP_SYSTEM_INFO system_info_; + scoped_ptr csd_version_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpSystemInfoWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_ diff --git a/minidump/minidump_system_info_writer_test.cc b/minidump/minidump_system_info_writer_test.cc new file mode 100644 index 00000000..27963a20 --- /dev/null +++ b/minidump/minidump_system_info_writer_test.cc @@ -0,0 +1,286 @@ +// 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_system_info_writer.h" + +#include +#include + +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "util/file/string_file_writer.h" + +namespace { + +using namespace crashpad; + +void GetSystemInfoStream(const std::string& file_contents, + size_t csd_version_length, + const MINIDUMP_SYSTEM_INFO** system_info, + const MINIDUMP_STRING** csd_version) { + // The expected number of bytes for the CSD version’s MINIDUMP_STRING::Buffer. + const size_t kCSDVersionBytes = + csd_version_length * sizeof(MINIDUMP_STRING::Buffer[0]); + const size_t kCSDVersionBytesWithNUL = + kCSDVersionBytes + sizeof(MINIDUMP_STRING::Buffer[0]); + + const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kSystemInfoStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + const size_t kCSDVersionOffset = + kSystemInfoStreamOffset + sizeof(MINIDUMP_SYSTEM_INFO); + const size_t kFileSize = + kCSDVersionOffset + sizeof(MINIDUMP_STRING) + kCSDVersionBytesWithNUL; + + ASSERT_EQ(kFileSize, file_contents.size()); + + const MINIDUMP_HEADER* header = + reinterpret_cast(&file_contents[0]); + + EXPECT_EQ(static_cast(MINIDUMP_SIGNATURE), header->Signature); + EXPECT_EQ(static_cast(MINIDUMP_VERSION), header->Version); + ASSERT_EQ(1u, header->NumberOfStreams); + ASSERT_EQ(kDirectoryOffset, header->StreamDirectoryRva); + EXPECT_EQ(0u, header->CheckSum); + EXPECT_EQ(0u, header->TimeDateStamp); + EXPECT_EQ(MiniDumpNormal, header->Flags); + + const MINIDUMP_DIRECTORY* directory = + reinterpret_cast( + &file_contents[kDirectoryOffset]); + + ASSERT_EQ(kMinidumpStreamTypeSystemInfo, directory->StreamType); + ASSERT_EQ(sizeof(MINIDUMP_SYSTEM_INFO), directory->Location.DataSize); + ASSERT_EQ(kSystemInfoStreamOffset, directory->Location.Rva); + + *system_info = reinterpret_cast( + &file_contents[kSystemInfoStreamOffset]); + + ASSERT_EQ(kCSDVersionOffset, (*system_info)->CSDVersionRva); + + *csd_version = reinterpret_cast( + &file_contents[kCSDVersionOffset]); + + ASSERT_EQ(kCSDVersionBytes, (*csd_version)->Length); +} + +TEST(MinidumpSystemInfoWriter, Empty) { + MinidumpFileWriter minidump_file_writer; + MinidumpSystemInfoWriter system_info_writer; + + system_info_writer.SetCSDVersion(std::string()); + + minidump_file_writer.AddStream(&system_info_writer); + + StringFileWriter file_writer; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer)); + + const MINIDUMP_SYSTEM_INFO* system_info; + const MINIDUMP_STRING* csd_version; + + GetSystemInfoStream(file_writer.string(), 0, &system_info, &csd_version); + + EXPECT_EQ(kMinidumpCPUArchitectureUnknown, + system_info->ProcessorArchitecture); + EXPECT_EQ(0u, system_info->ProcessorLevel); + EXPECT_EQ(0u, system_info->ProcessorRevision); + EXPECT_EQ(0u, system_info->NumberOfProcessors); + EXPECT_EQ(0u, system_info->ProductType); + EXPECT_EQ(0u, system_info->MajorVersion); + EXPECT_EQ(0u, system_info->MinorVersion); + EXPECT_EQ(0u, system_info->BuildNumber); + EXPECT_EQ(0u, system_info->PlatformId); + EXPECT_EQ(0u, system_info->SuiteMask); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VendorId[0]); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VendorId[1]); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VendorId[2]); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VersionInformation); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.FeatureInformation); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.AMDExtendedCpuFeatures); + EXPECT_EQ(0u, system_info->Cpu.OtherCpuInfo.ProcessorFeatures[0]); + EXPECT_EQ(0u, system_info->Cpu.OtherCpuInfo.ProcessorFeatures[1]); + + EXPECT_EQ('\0', csd_version->Buffer[0]); +} + +TEST(MinidumpSystemInfoWriter, X86_Win) { + MinidumpFileWriter minidump_file_writer; + MinidumpSystemInfoWriter system_info_writer; + + const MinidumpCPUArchitecture kCPUArchitecture = kMinidumpCPUArchitectureX86; + const uint16_t kCPULevel = 0x0010; + const uint16_t kCPURevision = 0x0602; + const uint8_t kCPUCount = 1; + const MinidumpOS kOS = kMinidumpOSWin32NT; + const MinidumpOSType kOSType = kMinidumpOSTypeWorkstation; + const uint32_t kOSVersionMajor = 6; + const uint32_t kOSVersionMinor = 1; + const uint32_t kOSVersionBuild = 7601; + const char kCSDVersion[] = "Service Pack 1"; + const uint16_t kSuiteMask = VER_SUITE_SINGLEUSERTS; + const char kCPUVendor[] = "AuthenticAMD"; + const uint32_t kCPUVersion = 0x00100f62; + const uint32_t kCPUFeatures = 0x078bfbff; + const uint32_t kAMDFeatures = 0xefd3fbff; + + uint32_t cpu_vendor_registers[3]; + ASSERT_EQ(sizeof(cpu_vendor_registers), strlen(kCPUVendor)); + memcpy(cpu_vendor_registers, kCPUVendor, sizeof(cpu_vendor_registers)); + + system_info_writer.SetCPUArchitecture(kCPUArchitecture); + system_info_writer.SetCPULevelAndRevision(kCPULevel, kCPURevision); + system_info_writer.SetCPUCount(kCPUCount); + system_info_writer.SetOS(kOS); + system_info_writer.SetOSType(kMinidumpOSTypeWorkstation); + system_info_writer.SetOSVersion( + kOSVersionMajor, kOSVersionMinor, kOSVersionBuild); + system_info_writer.SetCSDVersion(kCSDVersion); + system_info_writer.SetSuiteMask(kSuiteMask); + system_info_writer.SetCPUX86VendorString(kCPUVendor); + system_info_writer.SetCPUX86VersionAndFeatures(kCPUVersion, kCPUFeatures); + system_info_writer.SetCPUX86AMDExtendedFeatures(kAMDFeatures); + + minidump_file_writer.AddStream(&system_info_writer); + + StringFileWriter file_writer; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer)); + + const MINIDUMP_SYSTEM_INFO* system_info; + const MINIDUMP_STRING* csd_version; + + GetSystemInfoStream( + file_writer.string(), strlen(kCSDVersion), &system_info, &csd_version); + + EXPECT_EQ(kCPUArchitecture, system_info->ProcessorArchitecture); + EXPECT_EQ(kCPULevel, system_info->ProcessorLevel); + EXPECT_EQ(kCPURevision, system_info->ProcessorRevision); + EXPECT_EQ(kCPUCount, system_info->NumberOfProcessors); + EXPECT_EQ(kOSType, system_info->ProductType); + EXPECT_EQ(kOSVersionMajor, system_info->MajorVersion); + EXPECT_EQ(kOSVersionMinor, system_info->MinorVersion); + EXPECT_EQ(kOSVersionBuild, system_info->BuildNumber); + EXPECT_EQ(kOS, system_info->PlatformId); + EXPECT_EQ(kSuiteMask, system_info->SuiteMask); + EXPECT_EQ(cpu_vendor_registers[0], system_info->Cpu.X86CpuInfo.VendorId[0]); + EXPECT_EQ(cpu_vendor_registers[1], system_info->Cpu.X86CpuInfo.VendorId[1]); + EXPECT_EQ(cpu_vendor_registers[2], system_info->Cpu.X86CpuInfo.VendorId[2]); + EXPECT_EQ(kCPUVersion, system_info->Cpu.X86CpuInfo.VersionInformation); + EXPECT_EQ(kCPUFeatures, system_info->Cpu.X86CpuInfo.FeatureInformation); + EXPECT_EQ(kAMDFeatures, system_info->Cpu.X86CpuInfo.AMDExtendedCpuFeatures); + + for (size_t index = 0; index < strlen(kCSDVersion); ++index) { + EXPECT_EQ(kCSDVersion[index], csd_version->Buffer[index]) << index; + } +} + +TEST(MinidumpSystemInfoWriter, X86_64_Mac) { + MinidumpFileWriter minidump_file_writer; + MinidumpSystemInfoWriter system_info_writer; + + const MinidumpCPUArchitecture kCPUArchitecture = + kMinidumpCPUArchitectureAMD64; + const uint16_t kCPULevel = 0x0006; + const uint16_t kCPURevision = 0x3a09; + const uint8_t kCPUCount = 8; + const MinidumpOS kOS = kMinidumpOSMacOSX; + const MinidumpOSType kOSType = kMinidumpOSTypeWorkstation; + const uint32_t kOSVersionMajor = 10; + const uint32_t kOSVersionMinor = 9; + const uint32_t kOSVersionBuild = 4; + const char kCSDVersion[] = "13E28"; + const uint64_t kCPUFeatures[2] = {0x10427f4c, 0x00000000}; + + system_info_writer.SetCPUArchitecture(kCPUArchitecture); + system_info_writer.SetCPULevelAndRevision(kCPULevel, kCPURevision); + system_info_writer.SetCPUCount(kCPUCount); + system_info_writer.SetOS(kOS); + system_info_writer.SetOSType(kMinidumpOSTypeWorkstation); + system_info_writer.SetOSVersion( + kOSVersionMajor, kOSVersionMinor, kOSVersionBuild); + system_info_writer.SetCSDVersion(kCSDVersion); + system_info_writer.SetCPUOtherFeatures(kCPUFeatures[0], kCPUFeatures[1]); + + minidump_file_writer.AddStream(&system_info_writer); + + StringFileWriter file_writer; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer)); + + const MINIDUMP_SYSTEM_INFO* system_info; + const MINIDUMP_STRING* csd_version; + + GetSystemInfoStream( + file_writer.string(), strlen(kCSDVersion), &system_info, &csd_version); + + EXPECT_EQ(kCPUArchitecture, system_info->ProcessorArchitecture); + EXPECT_EQ(kCPULevel, system_info->ProcessorLevel); + EXPECT_EQ(kCPURevision, system_info->ProcessorRevision); + EXPECT_EQ(kCPUCount, system_info->NumberOfProcessors); + EXPECT_EQ(kOSType, system_info->ProductType); + EXPECT_EQ(kOSVersionMajor, system_info->MajorVersion); + EXPECT_EQ(kOSVersionMinor, system_info->MinorVersion); + EXPECT_EQ(kOSVersionBuild, system_info->BuildNumber); + EXPECT_EQ(kOS, system_info->PlatformId); + EXPECT_EQ(0u, system_info->SuiteMask); + EXPECT_EQ(kCPUFeatures[0], + system_info->Cpu.OtherCpuInfo.ProcessorFeatures[0]); + EXPECT_EQ(kCPUFeatures[1], + system_info->Cpu.OtherCpuInfo.ProcessorFeatures[1]); +} + +TEST(MinidumpSystemInfoWriter, X86_CPUVendorFromRegisters) { + // MinidumpSystemInfoWriter.X86_Win already tested SetCPUX86VendorString(). + // This test exercises SetCPUX86Vendor() to set the vendor from register + // values. + MinidumpFileWriter minidump_file_writer; + MinidumpSystemInfoWriter system_info_writer; + + const MinidumpCPUArchitecture kCPUArchitecture = kMinidumpCPUArchitectureX86; + const uint32_t kCPUVendor[] = {'uneG', 'Ieni', 'letn'}; + + system_info_writer.SetCPUArchitecture(kCPUArchitecture); + system_info_writer.SetCPUX86Vendor( + kCPUVendor[0], kCPUVendor[1], kCPUVendor[2]); + system_info_writer.SetCSDVersion(std::string()); + + minidump_file_writer.AddStream(&system_info_writer); + + StringFileWriter file_writer; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer)); + + const MINIDUMP_SYSTEM_INFO* system_info; + const MINIDUMP_STRING* csd_version; + + GetSystemInfoStream(file_writer.string(), 0, &system_info, &csd_version); + + EXPECT_EQ(kCPUArchitecture, system_info->ProcessorArchitecture); + EXPECT_EQ(0u, system_info->ProcessorLevel); + EXPECT_EQ(kCPUVendor[0], system_info->Cpu.X86CpuInfo.VendorId[0]); + EXPECT_EQ(kCPUVendor[1], system_info->Cpu.X86CpuInfo.VendorId[1]); + EXPECT_EQ(kCPUVendor[2], system_info->Cpu.X86CpuInfo.VendorId[2]); + EXPECT_EQ(0u, system_info->Cpu.X86CpuInfo.VersionInformation); +} + +TEST(MinidumpSystemInfoWriterDeathTest, NoCSDVersion) { + MinidumpFileWriter minidump_file_writer; + MinidumpSystemInfoWriter system_info_writer; + minidump_file_writer.AddStream(&system_info_writer); + + StringFileWriter file_writer; + ASSERT_DEATH(minidump_file_writer.WriteEverything(&file_writer), + "csd_version_"); +} + +} // namespace