diff --git a/minidump/minidump.gyp b/minidump/minidump.gyp index 2fabeaad..8ea64eff 100644 --- a/minidump/minidump.gyp +++ b/minidump/minidump.gyp @@ -32,6 +32,8 @@ 'minidump_context.h', 'minidump_context_writer.cc', 'minidump_context_writer.h', + 'minidump_crashpad_info_writer.cc', + 'minidump_crashpad_info_writer.h', 'minidump_exception_writer.cc', 'minidump_exception_writer.h', 'minidump_extensions.cc', @@ -76,6 +78,7 @@ 'minidump_context_test_util.cc', 'minidump_context_test_util.h', 'minidump_context_writer_test.cc', + 'minidump_crashpad_info_writer_test.cc', 'minidump_exception_writer_test.cc', 'minidump_file_writer_test.cc', 'minidump_file_writer_test_util.cc', @@ -87,6 +90,8 @@ 'minidump_module_writer_test.cc', 'minidump_simple_string_dictionary_writer_test.cc', 'minidump_string_writer_test.cc', + 'minidump_string_writer_test_util.cc', + 'minidump_string_writer_test_util.h', 'minidump_system_info_writer_test.cc', 'minidump_thread_writer_test.cc', 'minidump_writable_test.cc', diff --git a/minidump/minidump_crashpad_info_writer.cc b/minidump/minidump_crashpad_info_writer.cc new file mode 100644 index 00000000..2ad7291f --- /dev/null +++ b/minidump/minidump_crashpad_info_writer.cc @@ -0,0 +1,80 @@ +// 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_info_writer.h" + +#include "base/logging.h" + +namespace crashpad { + +MinidumpCrashpadInfoWriter::MinidumpCrashpadInfoWriter() + : MinidumpStreamWriter(), crashpad_info_(), simple_annotations_() { + crashpad_info_.size = sizeof(crashpad_info_); + crashpad_info_.version = 1; +} + +MinidumpCrashpadInfoWriter::~MinidumpCrashpadInfoWriter() { +} + +void MinidumpCrashpadInfoWriter::SetSimpleAnnotations( + MinidumpSimpleStringDictionaryWriter* simple_annotations) { + DCHECK_EQ(state(), kStateMutable); + + simple_annotations_ = simple_annotations; +} + +bool MinidumpCrashpadInfoWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + if (simple_annotations_) { + simple_annotations_->RegisterLocationDescriptor( + &crashpad_info_.simple_annotations); + } + + return true; +} + +size_t MinidumpCrashpadInfoWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(crashpad_info_); +} + +std::vector +MinidumpCrashpadInfoWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children; + if (simple_annotations_) { + children.push_back(simple_annotations_); + } + + return children; +} + +bool MinidumpCrashpadInfoWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&crashpad_info_, sizeof(crashpad_info_)); +} + +MinidumpStreamType MinidumpCrashpadInfoWriter::StreamType() const { + return kMinidumpStreamTypeCrashpadInfo; +} + +} // namespace crashpad diff --git a/minidump/minidump_crashpad_info_writer.h b/minidump/minidump_crashpad_info_writer.h new file mode 100644 index 00000000..f36c91f5 --- /dev/null +++ b/minidump/minidump_crashpad_info_writer.h @@ -0,0 +1,69 @@ +// 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_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" +#include "util/file/file_writer.h" + +namespace crashpad { + +//! \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. + //! + //! \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; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + MinidumpCrashpadInfo crashpad_info_; + MinidumpSimpleStringDictionaryWriter* simple_annotations_; // weak + + DISALLOW_COPY_AND_ASSIGN(MinidumpCrashpadInfoWriter); +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_ diff --git a/minidump/minidump_crashpad_info_writer_test.cc b/minidump/minidump_crashpad_info_writer_test.cc new file mode 100644 index 00000000..f0b4e24d --- /dev/null +++ b/minidump/minidump_crashpad_info_writer_test.cc @@ -0,0 +1,155 @@ +// 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_info_writer.h" + +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/minidump_file_writer_test_util.h" +#include "minidump/minidump_simple_string_dictionary_writer.h" +#include "minidump/minidump_string_writer_test_util.h" +#include "util/file/string_file_writer.h" + +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); + } + + const MINIDUMP_HEADER* header = + reinterpret_cast(&file_contents[0]); + + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + + const MINIDUMP_DIRECTORY* directory = + reinterpret_cast( + &file_contents[kDirectoryOffset]); + + ASSERT_EQ(kMinidumpStreamTypeCrashpadInfo, directory->StreamType); + ASSERT_EQ(sizeof(MinidumpCrashpadInfo), directory->Location.DataSize); + ASSERT_EQ(kCrashpadInfoStreamOffset, directory->Location.Rva); + + *crashpad_info = reinterpret_cast( + &file_contents[kCrashpadInfoStreamOffset]); + + if (simple_annotations) { + ASSERT_GE((*crashpad_info)->simple_annotations.DataSize, + sizeof(MinidumpSimpleStringDictionary)); + ASSERT_EQ(kSimpleAnnotationsOffset, + (*crashpad_info)->simple_annotations.Rva); + *simple_annotations = + reinterpret_cast( + &file_contents[kSimpleAnnotationsOffset]); + } else { + ASSERT_EQ(0u, (*crashpad_info)->simple_annotations.DataSize); + ASSERT_EQ(0u, (*crashpad_info)->simple_annotations.Rva); + } +} + +TEST(MinidumpCrashpadInfoWriter, Empty) { + MinidumpFileWriter minidump_file_writer; + MinidumpCrashpadInfoWriter crashpad_info_writer; + + minidump_file_writer.AddStream(&crashpad_info_writer); + + StringFileWriter file_writer; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer)); + + const MinidumpCrashpadInfo* crashpad_info; + + ASSERT_NO_FATAL_FAILURE( + GetCrashpadInfoStream(file_writer.string(), &crashpad_info, nullptr)); + + 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); +} + +TEST(MinidumpCrashpadInfoWriter, SimpleAnnotations) { + 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); + + StringFileWriter file_writer; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&file_writer)); + + const MinidumpCrashpadInfo* crashpad_info; + const MinidumpSimpleStringDictionary* simple_annotations; + + ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream( + file_writer.string(), &crashpad_info, &simple_annotations)); + + EXPECT_EQ(sizeof(*crashpad_info), crashpad_info->size); + EXPECT_EQ(1u, crashpad_info->version); + + ASSERT_EQ(2u, simple_annotations->count); + + EXPECT_EQ( + kKey1, + MinidumpUTF8StringAtRVA(file_writer, simple_annotations->entries[0].key)); + EXPECT_EQ(kValue1, + MinidumpUTF8StringAtRVA(file_writer, + simple_annotations->entries[0].value)); + EXPECT_EQ( + kKey0, + MinidumpUTF8StringAtRVA(file_writer, simple_annotations->entries[1].key)); + EXPECT_EQ(kValue0, + MinidumpUTF8StringAtRVA(file_writer, + simple_annotations->entries[1].value)); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/minidump/minidump_simple_string_dictionary_writer_test.cc b/minidump/minidump_simple_string_dictionary_writer_test.cc index 16de38ea..a63cfb33 100644 --- a/minidump/minidump_simple_string_dictionary_writer_test.cc +++ b/minidump/minidump_simple_string_dictionary_writer_test.cc @@ -18,6 +18,7 @@ #include "gtest/gtest.h" #include "minidump/minidump_extensions.h" +#include "minidump/minidump_string_writer_test_util.h" #include "util/file/string_file_writer.h" namespace crashpad { @@ -44,38 +45,6 @@ TEST(MinidumpSimpleStringDictionaryWriter, EmptySimpleStringDictionary) { EXPECT_EQ(0u, dictionary->count); } -std::string MinidumpUTF8StringAtRVA(const StringFileWriter& file_writer, - RVA rva) { - const std::string& contents = file_writer.string(); - if (rva == 0) { - return std::string(); - } - - if (rva + sizeof(MinidumpUTF8String) > contents.size()) { - ADD_FAILURE() - << "rva " << rva << " too large for contents " << contents.size(); - return std::string(); - } - - const MinidumpUTF8String* minidump_string = - reinterpret_cast(&contents[rva]); - - // Verify that the file has enough data for the string’s stated length plus - // its required NUL terminator. - if (rva + sizeof(MinidumpUTF8String) + minidump_string->Length + 1 > - contents.size()) { - ADD_FAILURE() - << "rva " << rva << ", length " << minidump_string->Length - << " too large for contents " << contents.size(); - return std::string(); - } - - std::string minidump_string_data( - reinterpret_cast(&minidump_string->Buffer[0]), - minidump_string->Length); - return minidump_string_data; -} - TEST(MinidumpSimpleStringDictionaryWriter, EmptyKeyValue) { StringFileWriter file_writer; diff --git a/minidump/minidump_string_writer_test_util.cc b/minidump/minidump_string_writer_test_util.cc new file mode 100644 index 00000000..7914c68a --- /dev/null +++ b/minidump/minidump_string_writer_test_util.cc @@ -0,0 +1,56 @@ +// 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_string_writer_test_util.h" + +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" + +namespace crashpad { +namespace test { + +std::string MinidumpUTF8StringAtRVA(const StringFileWriter& file_writer, + RVA rva) { + const std::string& contents = file_writer.string(); + if (rva == 0) { + return std::string(); + } + + if (rva + sizeof(MinidumpUTF8String) > contents.size()) { + ADD_FAILURE() + << "rva " << rva << " too large for contents " << contents.size(); + return std::string(); + } + + const MinidumpUTF8String* minidump_string = + reinterpret_cast(&contents[rva]); + + // Verify that the file has enough data for the string’s stated length plus + // its required NUL terminator. + if (rva + sizeof(MinidumpUTF8String) + minidump_string->Length + 1 > + contents.size()) { + ADD_FAILURE() + << "rva " << rva << ", length " << minidump_string->Length + << " too large for contents " << contents.size(); + return std::string(); + } + + std::string minidump_string_data( + reinterpret_cast(&minidump_string->Buffer[0]), + minidump_string->Length); + return minidump_string_data; +} + +} // namespace test +} // namespace crashpad diff --git a/minidump/minidump_string_writer_test_util.h b/minidump/minidump_string_writer_test_util.h new file mode 100644 index 00000000..db0febcf --- /dev/null +++ b/minidump/minidump_string_writer_test_util.h @@ -0,0 +1,42 @@ +// 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 + +#include + +#include "util/file/string_file_writer.h" + +namespace crashpad { +namespace test { + +//! \brief Returns the contents of a MinidumpUTF8String. +//! +//! If \a rva points outside of the range of \a file_writer, or if any of the +//! string data would lie outside of the range of \a file_writer, this function +//! will fail. +//! +//! \param[in] file_writer A StringFileWriter into which MinidumpWritable +//! objects have been written. +//! \param[in] rva An offset in \a file_writer at which to find the desired +//! string. +//! +//! \return On success, the string read from \a file_writer at offset \a rva. On +//! failure, returns an empty string, with a nonfatal assertion logged to +//! gtest. +std::string MinidumpUTF8StringAtRVA(const StringFileWriter& file_writer, + RVA rva); + +} // namespace test +} // namespace crashpad