diff --git a/minidump/minidump.gyp b/minidump/minidump.gyp index 9caef8de..6763183c 100644 --- a/minidump/minidump.gyp +++ b/minidump/minidump.gyp @@ -34,6 +34,8 @@ 'minidump_context_writer.h', 'minidump_crashpad_info_writer.cc', 'minidump_crashpad_info_writer.h', + 'minidump_crashpad_module_writer.cc', + 'minidump_crashpad_module_writer.h', 'minidump_exception_writer.cc', 'minidump_exception_writer.h', 'minidump_extensions.cc', @@ -77,6 +79,7 @@ 'sources': [ 'minidump_context_writer_test.cc', 'minidump_crashpad_info_writer_test.cc', + 'minidump_crashpad_module_writer_test.cc', 'minidump_exception_writer_test.cc', 'minidump_file_writer_test.cc', 'minidump_memory_writer_test.cc', diff --git a/minidump/minidump_crashpad_info_writer.cc b/minidump/minidump_crashpad_info_writer.cc index 05dcafdc..4d910b44 100644 --- a/minidump/minidump_crashpad_info_writer.cc +++ b/minidump/minidump_crashpad_info_writer.cc @@ -15,24 +15,24 @@ #include "minidump/minidump_crashpad_info_writer.h" #include "base/logging.h" +#include "minidump/minidump_crashpad_module_writer.h" #include "util/file/file_writer.h" namespace crashpad { MinidumpCrashpadInfoWriter::MinidumpCrashpadInfoWriter() - : MinidumpStreamWriter(), crashpad_info_(), simple_annotations_() { - crashpad_info_.size = sizeof(crashpad_info_); - crashpad_info_.version = 1; + : MinidumpStreamWriter(), crashpad_info_(), module_list_(nullptr) { + crashpad_info_.version = MinidumpCrashpadInfo::kVersion; } MinidumpCrashpadInfoWriter::~MinidumpCrashpadInfoWriter() { } -void MinidumpCrashpadInfoWriter::SetSimpleAnnotations( - MinidumpSimpleStringDictionaryWriter* simple_annotations) { +void MinidumpCrashpadInfoWriter::SetModuleList( + MinidumpModuleCrashpadInfoListWriter* module_list) { DCHECK_EQ(state(), kStateMutable); - simple_annotations_ = simple_annotations; + module_list_ = module_list; } bool MinidumpCrashpadInfoWriter::Freeze() { @@ -42,9 +42,8 @@ bool MinidumpCrashpadInfoWriter::Freeze() { return false; } - if (simple_annotations_) { - simple_annotations_->RegisterLocationDescriptor( - &crashpad_info_.simple_annotations); + if (module_list_) { + module_list_->RegisterLocationDescriptor(&crashpad_info_.module_list); } return true; @@ -61,8 +60,8 @@ MinidumpCrashpadInfoWriter::Children() { DCHECK_GE(state(), kStateFrozen); std::vector children; - if (simple_annotations_) { - children.push_back(simple_annotations_); + if (module_list_) { + children.push_back(module_list_); } return children; diff --git a/minidump/minidump_crashpad_info_writer.h b/minidump/minidump_crashpad_info_writer.h index 96c49810..d459d824 100644 --- a/minidump/minidump_crashpad_info_writer.h +++ b/minidump/minidump_crashpad_info_writer.h @@ -15,36 +15,31 @@ #ifndef CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_ #define CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_ -#include -#include - -#include #include #include "base/basictypes.h" #include "minidump/minidump_extensions.h" -#include "minidump/minidump_simple_string_dictionary_writer.h" #include "minidump/minidump_stream_writer.h" -#include "minidump/minidump_writable.h" namespace crashpad { +class MinidumpModuleCrashpadInfoListWriter; + //! \brief The writer for a MinidumpCrashpadInfo stream in a minidump file. class MinidumpCrashpadInfoWriter final : public internal::MinidumpStreamWriter { public: MinidumpCrashpadInfoWriter(); ~MinidumpCrashpadInfoWriter(); - //! \brief Arranges for MinidumpCrashpadInfo::simple_annotations to point to - //! the MinidumpSimpleStringDictionaryWriter object to be written by \a - //! simple_annotations. + //! \brief Arranges for MinidumpCrashpadInfo::module_list to point to the + //! MinidumpModuleCrashpadInfoList object to be written by \a + //! module_list. //! - //! \a simple_annotations will become a child of this object in the overall - //! tree of internal::MinidumpWritable objects. + //! \a module_list will become a child of this object in the overall tree of + //! internal::MinidumpWritable objects. //! //! \note Valid in #kStateMutable. - void SetSimpleAnnotations( - MinidumpSimpleStringDictionaryWriter* simple_annotations); + void SetModuleList(MinidumpModuleCrashpadInfoListWriter* module_list); protected: // MinidumpWritable: @@ -58,7 +53,7 @@ class MinidumpCrashpadInfoWriter final : public internal::MinidumpStreamWriter { private: MinidumpCrashpadInfo crashpad_info_; - MinidumpSimpleStringDictionaryWriter* simple_annotations_; // weak + MinidumpModuleCrashpadInfoListWriter* module_list_; // weak DISALLOW_COPY_AND_ASSIGN(MinidumpCrashpadInfoWriter); }; diff --git a/minidump/minidump_crashpad_info_writer_test.cc b/minidump/minidump_crashpad_info_writer_test.cc index b59301be..e61f3bb2 100644 --- a/minidump/minidump_crashpad_info_writer_test.cc +++ b/minidump/minidump_crashpad_info_writer_test.cc @@ -17,11 +17,10 @@ #include #include "gtest/gtest.h" +#include "minidump/minidump_crashpad_module_writer.h" #include "minidump/minidump_extensions.h" #include "minidump/minidump_file_writer.h" -#include "minidump/minidump_simple_string_dictionary_writer.h" #include "minidump/test/minidump_file_writer_test_util.h" -#include "minidump/test/minidump_string_writer_test_util.h" #include "minidump/test/minidump_writable_test_util.h" #include "util/file/string_file_writer.h" @@ -29,26 +28,9 @@ namespace crashpad { namespace test { namespace { -void GetCrashpadInfoStream( - const std::string& file_contents, - const MinidumpCrashpadInfo** crashpad_info, - const MinidumpSimpleStringDictionary** simple_annotations) { - const size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); - const size_t kCrashpadInfoStreamOffset = - kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); - size_t end = kCrashpadInfoStreamOffset + sizeof(MinidumpCrashpadInfo); - const size_t kSimpleAnnotationsOffset = simple_annotations ? end : 0; - if (simple_annotations) { - end += sizeof(MinidumpSimpleStringDictionary); - } - const size_t kFileSize = end; - - if (!simple_annotations) { - ASSERT_EQ(kFileSize, file_contents.size()); - } else { - EXPECT_GE(file_contents.size(), kFileSize); - } - +void GetCrashpadInfoStream(const std::string& file_contents, + const MinidumpCrashpadInfo** crashpad_info, + const MinidumpModuleCrashpadInfoList** module_list) { const MINIDUMP_DIRECTORY* directory; const MINIDUMP_HEADER* header = MinidumpHeaderAtStart(file_contents, &directory); @@ -56,23 +38,14 @@ void GetCrashpadInfoStream( ASSERT_TRUE(directory); ASSERT_EQ(kMinidumpStreamTypeCrashpadInfo, directory[0].StreamType); - EXPECT_EQ(kCrashpadInfoStreamOffset, directory[0].Location.Rva); *crashpad_info = MinidumpWritableAtLocationDescriptor( file_contents, directory[0].Location); ASSERT_TRUE(*crashpad_info); - if (simple_annotations) { - EXPECT_EQ(kSimpleAnnotationsOffset, - (*crashpad_info)->simple_annotations.Rva); - *simple_annotations = - MinidumpWritableAtLocationDescriptor( - file_contents, (*crashpad_info)->simple_annotations); - ASSERT_TRUE(*simple_annotations); - } else { - ASSERT_EQ(0u, (*crashpad_info)->simple_annotations.DataSize); - ASSERT_EQ(0u, (*crashpad_info)->simple_annotations.Rva); - } + *module_list = + MinidumpWritableAtLocationDescriptor( + file_contents, (*crashpad_info)->module_list); } TEST(MinidumpCrashpadInfoWriter, Empty) { @@ -85,66 +58,51 @@ TEST(MinidumpCrashpadInfoWriter, Empty) { ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer)); const MinidumpCrashpadInfo* crashpad_info; + const MinidumpModuleCrashpadInfoList* module_list; - ASSERT_NO_FATAL_FAILURE( - GetCrashpadInfoStream(file_writer.string(), &crashpad_info, nullptr)); + ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream( + file_writer.string(), &crashpad_info, &module_list)); - EXPECT_EQ(sizeof(*crashpad_info), crashpad_info->size); - EXPECT_EQ(1u, crashpad_info->version); - EXPECT_EQ(0u, crashpad_info->simple_annotations.DataSize); - EXPECT_EQ(0u, crashpad_info->simple_annotations.Rva); + EXPECT_EQ(MinidumpCrashpadInfo::kVersion, crashpad_info->version); + EXPECT_FALSE(module_list); } -TEST(MinidumpCrashpadInfoWriter, SimpleAnnotations) { +TEST(MinidumpCrashpadInfoWriter, CrashpadModuleList) { + const uint32_t kMinidumpModuleListIndex = 3; + MinidumpFileWriter minidump_file_writer; MinidumpCrashpadInfoWriter crashpad_info_writer; minidump_file_writer.AddStream(&crashpad_info_writer); - MinidumpSimpleStringDictionaryWriter simple_annotations_writer; - - // Set a key and value before adding the simple_annotations_writer to - // crashpad_info_writer, and another one after. - const char kKey0[] = "k0"; - const char kValue0[] = "v"; - const char kKey1[] = "KEY1"; - const char kValue1[] = ""; - MinidumpSimpleStringDictionaryEntryWriter entry_0; - entry_0.SetKeyValue(kKey0, kValue0); - simple_annotations_writer.AddEntry(&entry_0); - - crashpad_info_writer.SetSimpleAnnotations(&simple_annotations_writer); - - MinidumpSimpleStringDictionaryEntryWriter entry_1; - entry_1.SetKeyValue(kKey1, kValue1); - simple_annotations_writer.AddEntry(&entry_1); + MinidumpModuleCrashpadInfoListWriter module_list_writer; + MinidumpModuleCrashpadInfoWriter module_writer; + module_writer.SetMinidumpModuleListIndex(kMinidumpModuleListIndex); + module_list_writer.AddModule(&module_writer); + crashpad_info_writer.SetModuleList(&module_list_writer); StringFileWriter file_writer; ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer)); const MinidumpCrashpadInfo* crashpad_info; - const MinidumpSimpleStringDictionary* simple_annotations; + const MinidumpModuleCrashpadInfoList* module_list; ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream( - file_writer.string(), &crashpad_info, &simple_annotations)); + file_writer.string(), &crashpad_info, &module_list)); - EXPECT_EQ(sizeof(*crashpad_info), crashpad_info->size); - EXPECT_EQ(1u, crashpad_info->version); + EXPECT_EQ(MinidumpCrashpadInfo::kVersion, crashpad_info->version); + ASSERT_TRUE(module_list); + ASSERT_EQ(1u, module_list->count); - ASSERT_EQ(2u, simple_annotations->count); + const MinidumpModuleCrashpadInfo* module = + MinidumpWritableAtLocationDescriptor( + file_writer.string(), module_list->modules[0]); + ASSERT_TRUE(module); - EXPECT_EQ(kKey1, - MinidumpUTF8StringAtRVAAsString( - file_writer.string(), simple_annotations->entries[0].key)); - EXPECT_EQ(kValue1, - MinidumpUTF8StringAtRVAAsString( - file_writer.string(), simple_annotations->entries[0].value)); - EXPECT_EQ(kKey0, - MinidumpUTF8StringAtRVAAsString( - file_writer.string(), simple_annotations->entries[1].key)); - EXPECT_EQ(kValue0, - MinidumpUTF8StringAtRVAAsString( - file_writer.string(), simple_annotations->entries[1].value)); + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module->version); + EXPECT_EQ(kMinidumpModuleListIndex, module->minidump_module_list_index); + EXPECT_EQ(0u, module->simple_annotations.DataSize); + EXPECT_EQ(0u, module->simple_annotations.Rva); } } // namespace diff --git a/minidump/minidump_crashpad_module_writer.cc b/minidump/minidump_crashpad_module_writer.cc new file mode 100644 index 00000000..5e89e8de --- /dev/null +++ b/minidump/minidump_crashpad_module_writer.cc @@ -0,0 +1,160 @@ +// 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_crashpad_module_writer.h" + +#include + +#include "base/logging.h" +#include "minidump/minidump_simple_string_dictionary_writer.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpModuleCrashpadInfoWriter::MinidumpModuleCrashpadInfoWriter() + : MinidumpWritable(), module_(), simple_annotations_() { + module_.version = MinidumpModuleCrashpadInfo::kVersion; +} + +MinidumpModuleCrashpadInfoWriter::~MinidumpModuleCrashpadInfoWriter() { +} + +void MinidumpModuleCrashpadInfoWriter::SetSimpleAnnotations( + MinidumpSimpleStringDictionaryWriter* simple_annotations) { + DCHECK_EQ(state(), kStateMutable); + + simple_annotations_ = simple_annotations; +} + +bool MinidumpModuleCrashpadInfoWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + if (simple_annotations_) { + simple_annotations_->RegisterLocationDescriptor( + &module_.simple_annotations); + } + + return true; +} + +size_t MinidumpModuleCrashpadInfoWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(module_); +} + +std::vector +MinidumpModuleCrashpadInfoWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children; + if (simple_annotations_) { + children.push_back(simple_annotations_); + } + + return children; +} + +bool MinidumpModuleCrashpadInfoWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&module_, sizeof(module_)); +} + +MinidumpModuleCrashpadInfoListWriter::MinidumpModuleCrashpadInfoListWriter() + : MinidumpWritable(), + module_list_base_(), + modules_(), + module_location_descriptors_() { +} + +MinidumpModuleCrashpadInfoListWriter::~MinidumpModuleCrashpadInfoListWriter() { +} + +void MinidumpModuleCrashpadInfoListWriter::AddModule( + MinidumpModuleCrashpadInfoWriter* module) { + DCHECK_EQ(state(), kStateMutable); + + modules_.push_back(module); +} + +bool MinidumpModuleCrashpadInfoListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + DCHECK(module_location_descriptors_.empty()); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + size_t module_count = modules_.size(); + if (!AssignIfInRange(&module_list_base_.count, module_count)) { + LOG(ERROR) << "module_count " << module_count << " out of range"; + return false; + } + + module_location_descriptors_.resize(module_count); + for (size_t index = 0; index < module_count; ++index) { + modules_[index]->RegisterLocationDescriptor( + &module_location_descriptors_[index]); + } + + return true; +} + +size_t MinidumpModuleCrashpadInfoListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(module_list_base_) + + modules_.size() * sizeof(MINIDUMP_LOCATION_DESCRIPTOR); +} + +std::vector +MinidumpModuleCrashpadInfoListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children; + for (MinidumpModuleCrashpadInfoWriter* module : modules_) { + children.push_back(module); + } + + return children; +} + +bool MinidumpModuleCrashpadInfoListWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + DCHECK_EQ(modules_.size(), module_location_descriptors_.size()); + + WritableIoVec iov; + iov.iov_base = &module_list_base_; + iov.iov_len = sizeof(module_list_base_); + std::vector iovecs(1, iov); + + if (!module_location_descriptors_.empty()) { + iov.iov_base = &module_location_descriptors_[0]; + iov.iov_len = module_location_descriptors_.size() * + sizeof(MINIDUMP_LOCATION_DESCRIPTOR); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +} // namespace crashpad diff --git a/minidump/minidump_crashpad_module_writer.h b/minidump/minidump_crashpad_module_writer.h new file mode 100644 index 00000000..4541111f --- /dev/null +++ b/minidump/minidump_crashpad_module_writer.h @@ -0,0 +1,102 @@ +// 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_CRASHPAD_MODULE_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_MODULE_WRITER_H_ + +#include + +#include + +#include "base/basictypes.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +class MinidumpSimpleStringDictionaryWriter; + +//! \brief The writer for a MinidumpModuleCrashpadInfo object in a minidump +//! file. +class MinidumpModuleCrashpadInfoWriter final + : public internal::MinidumpWritable { + public: + MinidumpModuleCrashpadInfoWriter(); + ~MinidumpModuleCrashpadInfoWriter(); + + //! \brief Sets MinidumpModuleCrashpadInfo::minidump_module_list_index. + void SetMinidumpModuleListIndex(uint32_t minidump_module_list_index) { + module_.minidump_module_list_index = minidump_module_list_index; + } + + //! \brief Arranges for MinidumpModuleCrashpadInfo::simple_annotations to + //! point to the MinidumpSimpleStringDictionaryWriter object to be written + //! by \a simple_annotations. + //! + //! \a simple_annotations will become a child of this object in the overall + //! tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetSimpleAnnotations( + MinidumpSimpleStringDictionaryWriter* simple_annotations); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MinidumpModuleCrashpadInfo module_; + MinidumpSimpleStringDictionaryWriter* simple_annotations_; // weak + + DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCrashpadInfoWriter); +}; + +//! \brief The writer for a MinidumpModuleCrashpadInfoList object in a minidump +//! file, containing a list of MinidumpModuleCrashpadInfo objects. +class MinidumpModuleCrashpadInfoListWriter final + : public internal::MinidumpWritable { + public: + MinidumpModuleCrashpadInfoListWriter(); + ~MinidumpModuleCrashpadInfoListWriter(); + + //! \brief Adds a MinidumpModuleCrashpadInfo to the + //! MinidumpModuleCrashpadInfoList. + //! + //! \a module will become a child of this object in the overall tree of + //! internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void AddModule(MinidumpModuleCrashpadInfoWriter* module); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MinidumpModuleCrashpadInfoList module_list_base_; + std::vector modules_; // weak + std::vector module_location_descriptors_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpModuleCrashpadInfoListWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_MODULE_WRITER_H_ diff --git a/minidump/minidump_crashpad_module_writer_test.cc b/minidump/minidump_crashpad_module_writer_test.cc new file mode 100644 index 00000000..3eadcbcf --- /dev/null +++ b/minidump/minidump_crashpad_module_writer_test.cc @@ -0,0 +1,266 @@ +// 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_crashpad_module_writer.h" + +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_simple_string_dictionary_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file_writer.h" + +namespace crashpad { +namespace test { +namespace { + +const MinidumpModuleCrashpadInfoList* MinidumpModuleCrashpadInfoListAtStart( + const std::string& file_contents, + size_t count) { + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; + location_descriptor.DataSize = sizeof(MinidumpModuleCrashpadInfoList) + + count * sizeof(MINIDUMP_LOCATION_DESCRIPTOR); + location_descriptor.Rva = 0; + return MinidumpWritableAtLocationDescriptor( + file_contents, location_descriptor); +} + +TEST(MinidumpModuleCrashpadInfoWriter, EmptyList) { + StringFileWriter file_writer; + + MinidumpModuleCrashpadInfoListWriter module_list_writer; + + EXPECT_TRUE(module_list_writer.WriteEverything(&file_writer)); + ASSERT_EQ(sizeof(MinidumpModuleCrashpadInfoList), + file_writer.string().size()); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(file_writer.string(), 0); + ASSERT_TRUE(module_list); + + EXPECT_EQ(0u, module_list->count); +} + +TEST(MinidumpModuleCrashpadInfoWriter, EmptyModule) { + StringFileWriter file_writer; + + MinidumpModuleCrashpadInfoListWriter module_list_writer; + MinidumpModuleCrashpadInfoWriter module_writer; + module_list_writer.AddModule(&module_writer); + + EXPECT_TRUE(module_list_writer.WriteEverything(&file_writer)); + ASSERT_EQ(sizeof(MinidumpModuleCrashpadInfoList) + + sizeof(MINIDUMP_LOCATION_DESCRIPTOR) + + sizeof(MinidumpModuleCrashpadInfo), + file_writer.string().size()); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(file_writer.string(), 1); + ASSERT_TRUE(module_list); + + ASSERT_EQ(1u, module_list->count); + + const MinidumpModuleCrashpadInfo* module = + MinidumpWritableAtLocationDescriptor( + file_writer.string(), module_list->modules[0]); + ASSERT_TRUE(module); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module->version); + EXPECT_EQ(0u, module->minidump_module_list_index); + EXPECT_EQ(0u, module->simple_annotations.DataSize); + EXPECT_EQ(0u, module->simple_annotations.Rva); +} + +TEST(MinidumpModuleCrashpadInfoWriter, FullModule) { + const uint32_t kMinidumpModuleListIndex = 1; + const char kKey[] = "key"; + const char kValue[] = "value"; + + StringFileWriter file_writer; + + MinidumpModuleCrashpadInfoListWriter module_list_writer; + + MinidumpModuleCrashpadInfoWriter module_writer; + module_writer.SetMinidumpModuleListIndex(kMinidumpModuleListIndex); + MinidumpSimpleStringDictionaryWriter simple_string_dictionary_writer; + MinidumpSimpleStringDictionaryEntryWriter + simple_string_dictionary_entry_writer; + simple_string_dictionary_entry_writer.SetKeyValue(kKey, kValue); + simple_string_dictionary_writer.AddEntry( + &simple_string_dictionary_entry_writer); + module_writer.SetSimpleAnnotations(&simple_string_dictionary_writer); + module_list_writer.AddModule(&module_writer); + + EXPECT_TRUE(module_list_writer.WriteEverything(&file_writer)); + ASSERT_EQ(sizeof(MinidumpModuleCrashpadInfoList) + + sizeof(MINIDUMP_LOCATION_DESCRIPTOR) + + sizeof(MinidumpModuleCrashpadInfo) + + sizeof(MinidumpSimpleStringDictionary) + + sizeof(MinidumpSimpleStringDictionaryEntry) + + sizeof(MinidumpUTF8String) + arraysize(kKey) + + sizeof(MinidumpUTF8String) + arraysize(kValue), + file_writer.string().size()); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(file_writer.string(), 1); + ASSERT_TRUE(module_list); + + ASSERT_EQ(1u, module_list->count); + + const MinidumpModuleCrashpadInfo* module = + MinidumpWritableAtLocationDescriptor( + file_writer.string(), module_list->modules[0]); + ASSERT_TRUE(module); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module->version); + EXPECT_EQ(kMinidumpModuleListIndex, module->minidump_module_list_index); + EXPECT_NE(0u, module->simple_annotations.DataSize); + EXPECT_NE(0u, module->simple_annotations.Rva); + + const MinidumpSimpleStringDictionary* simple_annotations = + MinidumpWritableAtLocationDescriptor( + file_writer.string(), module->simple_annotations); + ASSERT_TRUE(simple_annotations); + + ASSERT_EQ(1u, simple_annotations->count); + EXPECT_EQ(kKey, + MinidumpUTF8StringAtRVAAsString( + file_writer.string(), simple_annotations->entries[0].key)); + EXPECT_EQ(kValue, + MinidumpUTF8StringAtRVAAsString( + file_writer.string(), simple_annotations->entries[0].value)); +} + +TEST(MinidumpModuleCrashpadInfoWriter, ThreeModules) { + const uint32_t kMinidumpModuleListIndex0 = 0; + const char kKey0[] = "key"; + const char kValue0[] = "value"; + const uint32_t kMinidumpModuleListIndex1 = 2; + const uint32_t kMinidumpModuleListIndex2 = 5; + const char kKey2A[] = "K"; + const char kValue2A[] = "VVV"; + const char kKey2B[] = "river"; + const char kValue2B[] = "hudson"; + + StringFileWriter file_writer; + + MinidumpModuleCrashpadInfoListWriter module_list_writer; + + MinidumpModuleCrashpadInfoWriter module_writer_0; + module_writer_0.SetMinidumpModuleListIndex(kMinidumpModuleListIndex0); + MinidumpSimpleStringDictionaryWriter simple_string_dictionary_writer_0; + MinidumpSimpleStringDictionaryEntryWriter + simple_string_dictionary_entry_writer_0; + simple_string_dictionary_entry_writer_0.SetKeyValue(kKey0, kValue0); + simple_string_dictionary_writer_0.AddEntry( + &simple_string_dictionary_entry_writer_0); + module_writer_0.SetSimpleAnnotations(&simple_string_dictionary_writer_0); + module_list_writer.AddModule(&module_writer_0); + + MinidumpModuleCrashpadInfoWriter module_writer_1; + module_writer_1.SetMinidumpModuleListIndex(kMinidumpModuleListIndex1); + module_list_writer.AddModule(&module_writer_1); + + MinidumpModuleCrashpadInfoWriter module_writer_2; + module_writer_2.SetMinidumpModuleListIndex(kMinidumpModuleListIndex2); + MinidumpSimpleStringDictionaryWriter simple_string_dictionary_writer_2; + MinidumpSimpleStringDictionaryEntryWriter + simple_string_dictionary_entry_writer_2a; + simple_string_dictionary_entry_writer_2a.SetKeyValue(kKey2A, kValue2A); + simple_string_dictionary_writer_2.AddEntry( + &simple_string_dictionary_entry_writer_2a); + MinidumpSimpleStringDictionaryEntryWriter + simple_string_dictionary_entry_writer_2b; + simple_string_dictionary_entry_writer_2b.SetKeyValue(kKey2B, kValue2B); + simple_string_dictionary_writer_2.AddEntry( + &simple_string_dictionary_entry_writer_2b); + module_writer_2.SetSimpleAnnotations(&simple_string_dictionary_writer_2); + module_list_writer.AddModule(&module_writer_2); + + EXPECT_TRUE(module_list_writer.WriteEverything(&file_writer)); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(file_writer.string(), 3); + ASSERT_TRUE(module_list); + + ASSERT_EQ(3u, module_list->count); + + const MinidumpModuleCrashpadInfo* module_0 = + MinidumpWritableAtLocationDescriptor( + file_writer.string(), module_list->modules[0]); + ASSERT_TRUE(module_0); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_0->version); + EXPECT_EQ(kMinidumpModuleListIndex0, module_0->minidump_module_list_index); + + const MinidumpSimpleStringDictionary* simple_annotations_0 = + MinidumpWritableAtLocationDescriptor( + file_writer.string(), module_0->simple_annotations); + ASSERT_TRUE(simple_annotations_0); + + ASSERT_EQ(1u, simple_annotations_0->count); + EXPECT_EQ(kKey0, + MinidumpUTF8StringAtRVAAsString( + file_writer.string(), simple_annotations_0->entries[0].key)); + EXPECT_EQ(kValue0, + MinidumpUTF8StringAtRVAAsString( + file_writer.string(), simple_annotations_0->entries[0].value)); + + const MinidumpModuleCrashpadInfo* module_1 = + MinidumpWritableAtLocationDescriptor( + file_writer.string(), module_list->modules[1]); + ASSERT_TRUE(module_1); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_1->version); + EXPECT_EQ(kMinidumpModuleListIndex1, module_1->minidump_module_list_index); + + const MinidumpSimpleStringDictionary* simple_annotations_1 = + MinidumpWritableAtLocationDescriptor( + file_writer.string(), module_1->simple_annotations); + EXPECT_FALSE(simple_annotations_1); + + const MinidumpModuleCrashpadInfo* module_2 = + MinidumpWritableAtLocationDescriptor( + file_writer.string(), module_list->modules[2]); + ASSERT_TRUE(module_2); + + EXPECT_EQ(MinidumpModuleCrashpadInfo::kVersion, module_2->version); + EXPECT_EQ(kMinidumpModuleListIndex2, module_2->minidump_module_list_index); + + const MinidumpSimpleStringDictionary* simple_annotations_2 = + MinidumpWritableAtLocationDescriptor( + file_writer.string(), module_2->simple_annotations); + ASSERT_TRUE(simple_annotations_2); + + ASSERT_EQ(2u, simple_annotations_2->count); + EXPECT_EQ(kKey2A, + MinidumpUTF8StringAtRVAAsString( + file_writer.string(), simple_annotations_2->entries[0].key)); + EXPECT_EQ(kValue2A, + MinidumpUTF8StringAtRVAAsString( + file_writer.string(), simple_annotations_2->entries[0].value)); + EXPECT_EQ(kKey2B, + MinidumpUTF8StringAtRVAAsString( + file_writer.string(), simple_annotations_2->entries[1].key)); + EXPECT_EQ(kValue2B, + MinidumpUTF8StringAtRVAAsString( + file_writer.string(), simple_annotations_2->entries[1].value)); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/minidump/minidump_extensions.cc b/minidump/minidump_extensions.cc index 39b210dc..da67d0c5 100644 --- a/minidump/minidump_extensions.cc +++ b/minidump/minidump_extensions.cc @@ -18,5 +18,7 @@ namespace crashpad { const uint32_t MinidumpModuleCodeViewRecordPDB20::kSignature; const uint32_t MinidumpModuleCodeViewRecordPDB70::kSignature; +const uint32_t MinidumpModuleCrashpadInfo::kVersion; +const uint32_t MinidumpCrashpadInfo::kVersion; } // namespace crashpad diff --git a/minidump/minidump_extensions.h b/minidump/minidump_extensions.h index e7618e3e..7386a76f 100644 --- a/minidump/minidump_extensions.h +++ b/minidump/minidump_extensions.h @@ -316,31 +316,108 @@ struct __attribute__((packed, aligned(4))) MinidumpSimpleStringDictionary { MinidumpSimpleStringDictionaryEntry entries[0]; }; -//! \brief Additional Crashpad-specific information carried within a minidump -//! file. -struct __attribute__((packed, aligned(4))) MinidumpCrashpadInfo { - //! \brief The size of the entire structure, in bytes. +//! \brief Additional Crashpad-specific information about a module carried +//! within a minidump file. +//! +//! This structure augments the information provided by MINIDUMP_MODULE. The +//! minidump file must contain a module list stream +//! (::kMinidumpStreamTypeModuleList) in order for this structure to appear. +//! +//! This structure is versioned. When changing this structure, leave the +//! existing structure intact so that earlier parsers will be able to understand +//! the fields they are aware of, and make additions at the end of the +//! structure. Revise #kVersion and document each field’s validity based on +//! #version, so that newer parsers will be able to determine whether the added +//! fields are valid or not. +//! +//! \sa MinidumpModuleCrashpadInfoList +struct __attribute__((packed, aligned(4))) MinidumpModuleCrashpadInfo { + //! \brief The structure’s currently-defined version number. //! //! \sa version - uint32_t size; + static const uint32_t kVersion = 1; - //! \brief The structure’s version number. This can be used to determine which - //! other fields in the structure are valid. + //! \brief The structure’s version number. //! - //! \sa size + //! Readers can use this field to determine which other fields in the + //! structure are valid. Upon encountering a value greater than #kVersion, a + //! reader should assume that the structure’s layout is compatible with the + //! structure defined as having value #kVersion. + //! + //! Writers may produce values less than #kVersion in this field if there is + //! no need for any fields present in later versions. uint32_t version; - //! \brief A MinidumpSimpleStringDictionary pointing to strings interpreted as - //! key-value pairs. The process that crashed controlled the data that - //! appears here. + //! \brief A link to a MINIDUMP_MODULE structure in the module list stream. //! - //! If MINIDUMP_LOCATION_DESCRIPTOR::DataSize is `0`, no key-value pairs are - //! present, and MINIDUMP_LOCATION_DESCRIPTOR::Rva should not be consulted. + //! This field is an index into MINIDUMP_MODULE_LIST::Modules. This field’s + //! value must be in the range of MINIDUMP_MODULE_LIST::NumberOfEntries. + //! + //! This field is present when #version is at least `1`. + uint32_t minidump_module_list_index; + + //! \brief A MinidumpSimpleStringDictionary pointing to strings interpreted as + //! key-value pairs. The module controls the data that appears here. //! //! This field is present when #version is at least `1`. MINIDUMP_LOCATION_DESCRIPTOR simple_annotations; }; +//! \brief Additional Crashpad-specific information about modules carried within +//! a minidump file. +//! +//! This structure augments the information provided by +//! MINIDUMP_MODULE_LIST. The minidump file must contain a module list stream +//! (::kMinidumpStreamTypeModuleList) in order for this structure to appear. +struct __attribute__((packed, aligned(4))) MinidumpModuleCrashpadInfoList { + //! \brief The number of modules present in the #modules array. + //! + //! This may be less than the value of MINIDUMP_MODULE_LIST::NumberOfModules + //! because not every MINIDUMP_MODULE structure carried within the minidump + //! file will necessarily have Crashpad-specific information provided by a + //! MinidumpModuleCrashpadInfo structure. + uint32_t count; + + //! \brief Pointers to MinidumpModuleCrashpadInfo structures. + //! + //! These are referenced indirectly through MINIDUMP_LOCATION_DESCRIPTOR + //! pointers to allow for future growth of the MinidumpModuleCrashpadInfo + //! structure. + MINIDUMP_LOCATION_DESCRIPTOR modules[0]; +}; + +//! \brief Additional Crashpad-specific information carried within a minidump +//! file. +//! +//! This structure is versioned. When changing this structure, leave the +//! existing structure intact so that earlier parsers will be able to understand +//! the fields they are aware of, and make additions at the end of the +//! structure. Revise #kVersion and document each field’s validity based on +//! #version, so that newer parsers will be able to determine whether the added +//! fields are valid or not. +struct __attribute__((packed, aligned(4))) MinidumpCrashpadInfo { + //! \brief The structure’s currently-defined version number. + //! + //! \sa version + static const uint32_t kVersion = 1; + + //! \brief The structure’s version number. + //! + //! Readers can use this field to determine which other fields in the + //! structure are valid. Upon encountering a value greater than #kVersion, a + //! reader should assume that the structure’s layout is compatible with the + //! structure defined as having value #kVersion. + //! + //! Writers may produce values less than #kVersion in this field if there is + //! no need for any fields present in later versions. + uint32_t version; + + //! \brief A pointer to a MinidumpModuleCrashpadInfoList structure. + //! + //! This field is present when #version is at least `1`. + MINIDUMP_LOCATION_DESCRIPTOR module_list; +}; + } // namespace crashpad #endif // CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_ diff --git a/minidump/test/minidump_writable_test_util.cc b/minidump/test/minidump_writable_test_util.cc index 3e98a033..ccee7ed1 100644 --- a/minidump/test/minidump_writable_test_util.cc +++ b/minidump/test/minidump_writable_test_util.cc @@ -160,6 +160,14 @@ struct MinidumpThreadListTraits { } }; +struct MinidumpModuleCrashpadInfoListTraits { + typedef MinidumpModuleCrashpadInfoList ListType; + static constexpr size_t kElementSize = sizeof(MINIDUMP_LOCATION_DESCRIPTOR); + static size_t ElementCount(const ListType* list) { + return list->count; + } +}; + struct MinidumpSimpleStringDictionaryListTraits { typedef MinidumpSimpleStringDictionary ListType; static constexpr size_t kElementSize = @@ -216,6 +224,15 @@ const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor< file_contents, location); } +template <> +const MinidumpModuleCrashpadInfoList* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor( + file_contents, location); +} + template <> const MinidumpSimpleStringDictionary* MinidumpWritableAtLocationDescriptor( diff --git a/minidump/test/minidump_writable_test_util.h b/minidump/test/minidump_writable_test_util.h index 8b5ddba6..43291d69 100644 --- a/minidump/test/minidump_writable_test_util.h +++ b/minidump/test/minidump_writable_test_util.h @@ -102,6 +102,7 @@ MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_DIRECTORY); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_LIST); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MODULE_LIST); MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_THREAD_LIST); +MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCrashpadInfoList); MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpSimpleStringDictionary); // These types have final fields carrying variable-sized data (typically string @@ -216,6 +217,12 @@ const MINIDUMP_THREAD_LIST* MinidumpWritableAtLocationDescriptor< MINIDUMP_THREAD_LIST>(const std::string& file_contents, const MINIDUMP_LOCATION_DESCRIPTOR& location); +template <> +const MinidumpModuleCrashpadInfoList* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + template <> const MinidumpModuleCodeViewRecordPDB20* MinidumpWritableAtLocationDescriptor(