// Copyright 2014 The Crashpad Authors // // 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_writable.h" #include #include #include "gtest/gtest.h" #include "util/file/string_file.h" namespace crashpad { namespace test { namespace { class BaseTestMinidumpWritable : public crashpad::internal::MinidumpWritable { public: BaseTestMinidumpWritable() : MinidumpWritable(), children_(), expected_offset_(-1), alignment_(0), phase_(kPhaseEarly), has_alignment_(false), has_phase_(false), verified_(false) {} BaseTestMinidumpWritable(const BaseTestMinidumpWritable&) = delete; BaseTestMinidumpWritable& operator=(const BaseTestMinidumpWritable&) = delete; ~BaseTestMinidumpWritable() { EXPECT_TRUE(verified_); } void SetAlignment(size_t alignment) { alignment_ = alignment; has_alignment_ = true; } void AddChild(BaseTestMinidumpWritable* child) { children_.push_back(child); } void SetPhaseLate() { phase_ = kPhaseLate; has_phase_ = true; } void Verify() { verified_ = true; EXPECT_EQ(state(), kStateWritten); for (BaseTestMinidumpWritable* child : children_) { child->Verify(); } } protected: bool Freeze() override { EXPECT_EQ(state(), kStateMutable); bool rv = MinidumpWritable::Freeze(); EXPECT_TRUE(rv); EXPECT_EQ(state(), kStateFrozen); return rv; } size_t Alignment() override { EXPECT_GE(state(), kStateFrozen); return has_alignment_ ? alignment_ : MinidumpWritable::Alignment(); } std::vector Children() override { EXPECT_GE(state(), kStateFrozen); if (!children_.empty()) { std::vector children; for (BaseTestMinidumpWritable* child : children_) { children.push_back(child); } return children; } return MinidumpWritable::Children(); } Phase WritePhase() override { return has_phase_ ? phase_ : MinidumpWritable::Phase(); } bool WillWriteAtOffsetImpl(FileOffset offset) override { EXPECT_EQ(kStateFrozen, state()); expected_offset_ = offset; bool rv = MinidumpWritable::WillWriteAtOffsetImpl(offset); EXPECT_TRUE(rv); return rv; } bool WriteObject(FileWriterInterface* file_writer) override { EXPECT_EQ(kStateWritable, state()); EXPECT_EQ(file_writer->Seek(0, SEEK_CUR), expected_offset_); // Subclasses must override this. return false; } private: std::vector children_; FileOffset expected_offset_; size_t alignment_; Phase phase_; bool has_alignment_; bool has_phase_; bool verified_; }; class TestStringMinidumpWritable final : public BaseTestMinidumpWritable { public: TestStringMinidumpWritable() : BaseTestMinidumpWritable(), data_() {} TestStringMinidumpWritable(const TestStringMinidumpWritable&) = delete; TestStringMinidumpWritable& operator=(const TestStringMinidumpWritable&) = delete; ~TestStringMinidumpWritable() {} void SetData(const std::string& string) { data_ = string; } protected: size_t SizeOfObject() override { EXPECT_GE(state(), kStateFrozen); return data_.size(); } bool WriteObject(FileWriterInterface* file_writer) override { BaseTestMinidumpWritable::WriteObject(file_writer); bool rv = file_writer->Write(&data_[0], data_.size()); EXPECT_TRUE(rv); return rv; } private: std::string data_; }; TEST(MinidumpWritable, MinidumpWritable) { StringFile string_file; { SCOPED_TRACE("empty"); string_file.Reset(); TestStringMinidumpWritable string_writable; EXPECT_TRUE(string_writable.WriteEverything(&string_file)); EXPECT_TRUE(string_file.string().empty()); string_writable.Verify(); } { SCOPED_TRACE("childless"); string_file.Reset(); TestStringMinidumpWritable string_writable; string_writable.SetData("a"); EXPECT_TRUE(string_writable.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 1u); EXPECT_EQ(string_file.string(), "a"); string_writable.Verify(); } { SCOPED_TRACE("parent-child"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("b"); TestStringMinidumpWritable child; child.SetData("c"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 5u); EXPECT_EQ(string_file.string(), std::string("b\0\0\0c", 5)); parent.Verify(); } { SCOPED_TRACE("base alignment 2"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("de"); TestStringMinidumpWritable child; child.SetData("f"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 5u); EXPECT_EQ(string_file.string(), std::string("de\0\0f", 5)); parent.Verify(); } { SCOPED_TRACE("base alignment 3"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("ghi"); TestStringMinidumpWritable child; child.SetData("j"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 5u); EXPECT_EQ(string_file.string(), std::string("ghi\0j", 5)); parent.Verify(); } { SCOPED_TRACE("base alignment 4"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("klmn"); TestStringMinidumpWritable child; child.SetData("o"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 5u); EXPECT_EQ(string_file.string(), "klmno"); parent.Verify(); } { SCOPED_TRACE("base alignment 5"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("pqrst"); TestStringMinidumpWritable child; child.SetData("u"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 9u); EXPECT_EQ(string_file.string(), std::string("pqrst\0\0\0u", 9)); parent.Verify(); } { SCOPED_TRACE("two children"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("parent"); TestStringMinidumpWritable child_0; child_0.SetData("child_0"); parent.AddChild(&child_0); TestStringMinidumpWritable child_1; child_1.SetData("child_1"); parent.AddChild(&child_1); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 23u); EXPECT_EQ(string_file.string(), std::string("parent\0\0child_0\0child_1", 23)); parent.Verify(); } { SCOPED_TRACE("grandchild"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("parent"); TestStringMinidumpWritable child; child.SetData("child"); parent.AddChild(&child); TestStringMinidumpWritable grandchild; grandchild.SetData("grandchild"); child.AddChild(&grandchild); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 26u); EXPECT_EQ(string_file.string(), std::string("parent\0\0child\0\0\0grandchild", 26)); parent.Verify(); } { SCOPED_TRACE("grandchild with empty parent"); string_file.Reset(); TestStringMinidumpWritable parent; TestStringMinidumpWritable child; child.SetData("child"); parent.AddChild(&child); TestStringMinidumpWritable grandchild; grandchild.SetData("grandchild"); child.AddChild(&grandchild); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 18u); EXPECT_EQ(string_file.string(), std::string("child\0\0\0grandchild", 18)); parent.Verify(); } { SCOPED_TRACE("grandchild with empty child"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("parent"); TestStringMinidumpWritable child; parent.AddChild(&child); TestStringMinidumpWritable grandchild; grandchild.SetData("grandchild"); child.AddChild(&grandchild); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 18u); EXPECT_EQ(string_file.string(), std::string("parent\0\0grandchild", 18)); parent.Verify(); } { SCOPED_TRACE("grandchild with empty grandchild"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("parent"); TestStringMinidumpWritable child; child.SetData("child"); parent.AddChild(&child); TestStringMinidumpWritable grandchild; child.AddChild(&grandchild); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 13u); EXPECT_EQ(string_file.string(), std::string("parent\0\0child", 13)); parent.Verify(); } { SCOPED_TRACE("grandchild with late-phase grandchild"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("parent"); TestStringMinidumpWritable child; child.SetData("child"); parent.AddChild(&child); TestStringMinidumpWritable grandchild; grandchild.SetData("grandchild"); grandchild.SetPhaseLate(); child.AddChild(&grandchild); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 26u); EXPECT_EQ(string_file.string(), std::string("parent\0\0child\0\0\0grandchild", 26)); parent.Verify(); } { SCOPED_TRACE("grandchild with late-phase child"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("parent"); TestStringMinidumpWritable child; child.SetData("child"); child.SetPhaseLate(); parent.AddChild(&child); TestStringMinidumpWritable grandchild; grandchild.SetData("grandchild"); child.AddChild(&grandchild); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 25u); EXPECT_EQ(string_file.string(), std::string("parent\0\0grandchild\0\0child", 25)); parent.Verify(); } { SCOPED_TRACE("family tree"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("P.."); TestStringMinidumpWritable child_0; child_0.SetData("C0."); parent.AddChild(&child_0); TestStringMinidumpWritable child_1; child_1.SetData("C1."); parent.AddChild(&child_1); TestStringMinidumpWritable grandchild_00; grandchild_00.SetData("G00"); child_0.AddChild(&grandchild_00); TestStringMinidumpWritable grandchild_01; grandchild_01.SetData("G01"); child_0.AddChild(&grandchild_01); TestStringMinidumpWritable grandchild_10; grandchild_10.SetData("G10"); child_1.AddChild(&grandchild_10); TestStringMinidumpWritable grandchild_11; grandchild_11.SetData("G11"); child_1.AddChild(&grandchild_11); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 27u); EXPECT_EQ(string_file.string(), std::string("P..\0C0.\0G00\0G01\0C1.\0G10\0G11", 27)); parent.Verify(); } { SCOPED_TRACE("family tree with C0 late"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("P.."); TestStringMinidumpWritable child_0; child_0.SetData("C0."); child_0.SetPhaseLate(); parent.AddChild(&child_0); TestStringMinidumpWritable child_1; child_1.SetData("C1."); parent.AddChild(&child_1); TestStringMinidumpWritable grandchild_00; grandchild_00.SetData("G00"); child_0.AddChild(&grandchild_00); TestStringMinidumpWritable grandchild_01; grandchild_01.SetData("G01"); child_0.AddChild(&grandchild_01); TestStringMinidumpWritable grandchild_10; grandchild_10.SetData("G10"); child_1.AddChild(&grandchild_10); TestStringMinidumpWritable grandchild_11; grandchild_11.SetData("G11"); child_1.AddChild(&grandchild_11); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 27u); EXPECT_EQ(string_file.string(), std::string("P..\0G00\0G01\0C1.\0G10\0G11\0C0.", 27)); parent.Verify(); } { SCOPED_TRACE("family tree with G0 late"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("P.."); TestStringMinidumpWritable child_0; child_0.SetData("C0."); parent.AddChild(&child_0); TestStringMinidumpWritable child_1; child_1.SetData("C1."); parent.AddChild(&child_1); TestStringMinidumpWritable grandchild_00; grandchild_00.SetData("G00"); grandchild_00.SetPhaseLate(); child_0.AddChild(&grandchild_00); TestStringMinidumpWritable grandchild_01; grandchild_01.SetData("G01"); grandchild_01.SetPhaseLate(); child_0.AddChild(&grandchild_01); TestStringMinidumpWritable grandchild_10; grandchild_10.SetData("G10"); child_1.AddChild(&grandchild_10); TestStringMinidumpWritable grandchild_11; grandchild_11.SetData("G11"); child_1.AddChild(&grandchild_11); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 27u); EXPECT_EQ(string_file.string(), std::string("P..\0C0.\0C1.\0G10\0G11\0G00\0G01", 27)); parent.Verify(); } { SCOPED_TRACE("align 1"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("p"); TestStringMinidumpWritable child; child.SetData("c"); child.SetAlignment(1); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 2u); EXPECT_EQ(string_file.string(), "pc"); parent.Verify(); } { SCOPED_TRACE("align 2"); string_file.Reset(); TestStringMinidumpWritable parent; parent.SetData("p"); TestStringMinidumpWritable child; child.SetData("c"); child.SetAlignment(2); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&string_file)); EXPECT_EQ(string_file.string().size(), 3u); EXPECT_EQ(string_file.string(), std::string("p\0c", 3)); parent.Verify(); } } template class TTestRVAMinidumpWritable final : public BaseTestMinidumpWritable { public: TTestRVAMinidumpWritable() : BaseTestMinidumpWritable(), rva_() {} TTestRVAMinidumpWritable(const TTestRVAMinidumpWritable&) = delete; TTestRVAMinidumpWritable& operator=(const TTestRVAMinidumpWritable&) = delete; ~TTestRVAMinidumpWritable() {} void SetRVA(MinidumpWritable* other) { other->RegisterRVA(&rva_); } protected: size_t SizeOfObject() override { EXPECT_GE(state(), kStateFrozen); return sizeof(rva_); } bool WriteObject(FileWriterInterface* file_writer) override { BaseTestMinidumpWritable::WriteObject(file_writer); EXPECT_TRUE(file_writer->Write(&rva_, sizeof(rva_))); return true; } private: RVAType rva_; }; template RVAType TRVAAtIndex(const std::string& string, size_t index) { return *reinterpret_cast(&string[index * sizeof(RVAType)]); } template struct TestNames {}; template <> struct TestNames { static constexpr char kName[] = "RVA"; }; template <> struct TestNames { static constexpr char kName[] = "RVA64"; }; class TestTypeNames { public: template static std::string GetName(int) { return std::string(TestNames::kName); } }; template class MinidumpWritable : public ::testing::Test {}; using RVATypes = ::testing::Types; TYPED_TEST_SUITE(MinidumpWritable, RVATypes, TestTypeNames); TYPED_TEST(MinidumpWritable, RVA) { StringFile string_file; using TestRVAMinidumpWritable = TTestRVAMinidumpWritable; const auto RVAAtIndex = TRVAAtIndex; constexpr size_t kRVASize = sizeof(TypeParam); { SCOPED_TRACE("unset"); string_file.Reset(); TestRVAMinidumpWritable rva_writable; EXPECT_TRUE(rva_writable.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), kRVASize); EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * kRVASize); rva_writable.Verify(); } { SCOPED_TRACE("self"); string_file.Reset(); TestRVAMinidumpWritable rva_writable; rva_writable.SetRVA(&rva_writable); EXPECT_TRUE(rva_writable.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), kRVASize); EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * kRVASize); rva_writable.Verify(); } { SCOPED_TRACE("parent-child self"); string_file.Reset(); TestRVAMinidumpWritable parent; parent.SetRVA(&parent); TestRVAMinidumpWritable child; child.SetRVA(&child); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), 2 * kRVASize); EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * kRVASize); EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 1 * kRVASize); parent.Verify(); } { SCOPED_TRACE("parent-child only"); string_file.Reset(); TestRVAMinidumpWritable parent; TestRVAMinidumpWritable child; parent.SetRVA(&child); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), 2 * kRVASize); EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * kRVASize); EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * kRVASize); parent.Verify(); } { SCOPED_TRACE("parent-child circular"); string_file.Reset(); TestRVAMinidumpWritable parent; TestRVAMinidumpWritable child; parent.SetRVA(&child); child.SetRVA(&parent); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), 2 * kRVASize); EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * kRVASize); EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * kRVASize); parent.Verify(); } { SCOPED_TRACE("grandchildren"); string_file.Reset(); TestRVAMinidumpWritable parent; TestRVAMinidumpWritable child; parent.SetRVA(&child); parent.AddChild(&child); TestRVAMinidumpWritable grandchild_0; grandchild_0.SetRVA(&child); child.AddChild(&grandchild_0); TestRVAMinidumpWritable grandchild_1; grandchild_1.SetRVA(&child); child.AddChild(&grandchild_1); TestRVAMinidumpWritable grandchild_2; grandchild_2.SetRVA(&child); child.AddChild(&grandchild_2); EXPECT_TRUE(parent.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), 5 * kRVASize); EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * kRVASize); EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * kRVASize); EXPECT_EQ(RVAAtIndex(string_file.string(), 2), 1 * kRVASize); EXPECT_EQ(RVAAtIndex(string_file.string(), 3), 1 * kRVASize); EXPECT_EQ(RVAAtIndex(string_file.string(), 4), 1 * kRVASize); parent.Verify(); } } template class TTestLocationDescriptorMinidumpWritable final : public BaseTestMinidumpWritable { public: TTestLocationDescriptorMinidumpWritable() : BaseTestMinidumpWritable(), location_descriptor_(), string_() {} TTestLocationDescriptorMinidumpWritable( const TTestLocationDescriptorMinidumpWritable&) = delete; TTestLocationDescriptorMinidumpWritable& operator=( const TTestLocationDescriptorMinidumpWritable&) = delete; ~TTestLocationDescriptorMinidumpWritable() {} void SetLocationDescriptor(MinidumpWritable* other) { other->RegisterLocationDescriptor(&location_descriptor_); } void SetString(const std::string& string) { string_ = string; } protected: size_t SizeOfObject() override { EXPECT_GE(state(), kStateFrozen); // NUL-terminate. return sizeof(location_descriptor_) + string_.size() + 1; } bool WriteObject(FileWriterInterface* file_writer) override { BaseTestMinidumpWritable::WriteObject(file_writer); WritableIoVec iov; iov.iov_base = &location_descriptor_; iov.iov_len = sizeof(location_descriptor_); std::vector iovecs(1, iov); // NUL-terminate. iov.iov_base = &string_[0]; iov.iov_len = string_.size() + 1; iovecs.push_back(iov); EXPECT_TRUE(file_writer->WriteIoVec(&iovecs)); return true; } private: MinidumpLocationDescriptorType location_descriptor_; std::string string_; }; template struct TLocationDescriptorAndData { MinidumpLocationDescriptorType location_descriptor; const char* string; }; template TLocationDescriptorAndData TLDDAtIndex( const std::string& str, size_t index) { const MinidumpLocationDescriptorType* location_descriptor = reinterpret_cast(&str[index]); const char* string = reinterpret_cast( &str[index] + offsetof(TLocationDescriptorAndData, string)); return TLocationDescriptorAndData{ *location_descriptor, string, }; } template class MinidumpWritableLocationDescriptor : public ::testing::Test {}; template <> struct TestNames { static constexpr char kName[] = "MINIDUMP_LOCATION_DESCRIPTOR"; }; template <> struct TestNames { static constexpr char kName[] = "MINIDUMP_LOCATION_DESCRIPTOR64"; }; using MinidumpLocationDescriptorTypes = ::testing::Types; TYPED_TEST_SUITE(MinidumpWritableLocationDescriptor, MinidumpLocationDescriptorTypes, TestTypeNames); TYPED_TEST(MinidumpWritableLocationDescriptor, LocationDescriptor) { StringFile string_file; using LocationDescriptorAndData = TLocationDescriptorAndData; const auto LDDAtIndex = TLDDAtIndex; using TestLocationDescriptorMinidumpWritable = TTestLocationDescriptorMinidumpWritable; constexpr size_t kMinidumpLocationDescriptorSize = sizeof(TypeParam); { SCOPED_TRACE("unset"); string_file.Reset(); TestLocationDescriptorMinidumpWritable location_descriptor_writable; EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), kMinidumpLocationDescriptorSize + 1); LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0); EXPECT_EQ(ldd.location_descriptor.DataSize, 0u); EXPECT_EQ(ldd.location_descriptor.Rva, 0u); location_descriptor_writable.Verify(); } { SCOPED_TRACE("self"); string_file.Reset(); TestLocationDescriptorMinidumpWritable location_descriptor_writable; location_descriptor_writable.SetLocationDescriptor( &location_descriptor_writable); EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), kMinidumpLocationDescriptorSize + 1); LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0); EXPECT_EQ(ldd.location_descriptor.DataSize, kMinidumpLocationDescriptorSize + 1); EXPECT_EQ(ldd.location_descriptor.Rva, 0u); location_descriptor_writable.Verify(); } { SCOPED_TRACE("self with data"); string_file.Reset(); TestLocationDescriptorMinidumpWritable location_descriptor_writable; location_descriptor_writable.SetLocationDescriptor( &location_descriptor_writable); location_descriptor_writable.SetString("zz"); EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), kMinidumpLocationDescriptorSize + 3); LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0); EXPECT_EQ(ldd.location_descriptor.DataSize, kMinidumpLocationDescriptorSize + 3); EXPECT_EQ(ldd.location_descriptor.Rva, 0u); EXPECT_STREQ("zz", ldd.string); location_descriptor_writable.Verify(); } { SCOPED_TRACE("parent-child self"); string_file.Reset(); TestLocationDescriptorMinidumpWritable parent; parent.SetLocationDescriptor(&parent); parent.SetString("yy"); TestLocationDescriptorMinidumpWritable child; child.SetLocationDescriptor(&child); child.SetString("x"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), kMinidumpLocationDescriptorSize * 2 + 6); LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0); EXPECT_EQ(ldd.location_descriptor.DataSize, kMinidumpLocationDescriptorSize + 3); EXPECT_EQ(ldd.location_descriptor.Rva, 0u); EXPECT_STREQ("yy", ldd.string); ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize + 4); EXPECT_EQ(ldd.location_descriptor.DataSize, kMinidumpLocationDescriptorSize + 2); EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 4); EXPECT_STREQ("x", ldd.string); parent.Verify(); } { SCOPED_TRACE("parent-child only"); string_file.Reset(); TestLocationDescriptorMinidumpWritable parent; TestLocationDescriptorMinidumpWritable child; parent.SetLocationDescriptor(&child); parent.SetString("www"); child.SetString("vv"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), kMinidumpLocationDescriptorSize * 2 + 7); LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0); EXPECT_EQ(ldd.location_descriptor.DataSize, kMinidumpLocationDescriptorSize + 3); EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 4); EXPECT_STREQ("www", ldd.string); ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize + 4); EXPECT_EQ(ldd.location_descriptor.DataSize, 0u); EXPECT_EQ(ldd.location_descriptor.Rva, 0u); EXPECT_STREQ("vv", ldd.string); parent.Verify(); } { SCOPED_TRACE("parent-child circular"); string_file.Reset(); TestLocationDescriptorMinidumpWritable parent; TestLocationDescriptorMinidumpWritable child; parent.SetLocationDescriptor(&child); parent.SetString("uuuu"); child.SetLocationDescriptor(&parent); child.SetString("tttt"); parent.AddChild(&child); EXPECT_TRUE(parent.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), kMinidumpLocationDescriptorSize * 2 + 13); LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0); EXPECT_EQ(ldd.location_descriptor.DataSize, kMinidumpLocationDescriptorSize + 5); EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 8); EXPECT_STREQ("uuuu", ldd.string); ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize + 8); EXPECT_EQ(ldd.location_descriptor.DataSize, kMinidumpLocationDescriptorSize + 5); EXPECT_EQ(ldd.location_descriptor.Rva, 0u); EXPECT_STREQ("tttt", ldd.string); parent.Verify(); } { SCOPED_TRACE("grandchildren"); string_file.Reset(); TestLocationDescriptorMinidumpWritable parent; TestLocationDescriptorMinidumpWritable child; parent.SetLocationDescriptor(&child); parent.SetString("s"); parent.AddChild(&child); child.SetString("r"); TestLocationDescriptorMinidumpWritable grandchild_0; grandchild_0.SetLocationDescriptor(&child); grandchild_0.SetString("q"); child.AddChild(&grandchild_0); TestLocationDescriptorMinidumpWritable grandchild_1; grandchild_1.SetLocationDescriptor(&child); grandchild_1.SetString("p"); child.AddChild(&grandchild_1); TestLocationDescriptorMinidumpWritable grandchild_2; grandchild_2.SetLocationDescriptor(&child); grandchild_2.SetString("o"); child.AddChild(&grandchild_2); EXPECT_TRUE(parent.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), kMinidumpLocationDescriptorSize * 5 + 18); LocationDescriptorAndData ldd = LDDAtIndex(string_file.string(), 0); EXPECT_EQ(ldd.location_descriptor.DataSize, kMinidumpLocationDescriptorSize + 2); EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 4); EXPECT_STREQ("s", ldd.string); ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize + 4); EXPECT_EQ(ldd.location_descriptor.DataSize, 0u); EXPECT_EQ(ldd.location_descriptor.Rva, 0u); EXPECT_STREQ("r", ldd.string); ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize * 2 + 8); EXPECT_EQ(ldd.location_descriptor.DataSize, kMinidumpLocationDescriptorSize + 2); EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 4); EXPECT_STREQ("q", ldd.string); ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize * 3 + 12); EXPECT_EQ(ldd.location_descriptor.DataSize, kMinidumpLocationDescriptorSize + 2); EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 4); EXPECT_STREQ("p", ldd.string); ldd = LDDAtIndex(string_file.string(), kMinidumpLocationDescriptorSize * 4 + 16); EXPECT_EQ(ldd.location_descriptor.DataSize, kMinidumpLocationDescriptorSize + 2); EXPECT_EQ(ldd.location_descriptor.Rva, kMinidumpLocationDescriptorSize + 4); EXPECT_STREQ("o", ldd.string); parent.Verify(); } } } // namespace } // namespace test } // namespace crashpad