// 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_writable.h" #include #include #include "base/macros.h" #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() { 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_; DISALLOW_COPY_AND_ASSIGN(BaseTestMinidumpWritable); }; class TestStringMinidumpWritable final : public BaseTestMinidumpWritable { public: TestStringMinidumpWritable() : BaseTestMinidumpWritable(), data_() {} ~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_; DISALLOW_COPY_AND_ASSIGN(TestStringMinidumpWritable); }; 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(); } } class TestRVAMinidumpWritable final : public BaseTestMinidumpWritable { public: TestRVAMinidumpWritable() : BaseTestMinidumpWritable(), rva_() {} ~TestRVAMinidumpWritable() {} 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: RVA rva_; DISALLOW_COPY_AND_ASSIGN(TestRVAMinidumpWritable); }; RVA RVAAtIndex(const std::string& string, size_t index) { return *reinterpret_cast(&string[index * sizeof(RVA)]); } TEST(MinidumpWritable, RVA) { StringFile string_file; { SCOPED_TRACE("unset"); string_file.Reset(); TestRVAMinidumpWritable rva_writable; EXPECT_TRUE(rva_writable.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), sizeof(RVA)); EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * sizeof(RVA)); 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(), sizeof(RVA)); EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * sizeof(RVA)); 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 * sizeof(RVA)); EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * sizeof(RVA)); EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 1 * sizeof(RVA)); 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 * sizeof(RVA)); EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * sizeof(RVA)); EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * sizeof(RVA)); 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 * sizeof(RVA)); EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * sizeof(RVA)); EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * sizeof(RVA)); 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 * sizeof(RVA)); EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * sizeof(RVA)); EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * sizeof(RVA)); EXPECT_EQ(RVAAtIndex(string_file.string(), 2), 1 * sizeof(RVA)); EXPECT_EQ(RVAAtIndex(string_file.string(), 3), 1 * sizeof(RVA)); EXPECT_EQ(RVAAtIndex(string_file.string(), 4), 1 * sizeof(RVA)); parent.Verify(); } } class TestLocationDescriptorMinidumpWritable final : public BaseTestMinidumpWritable { public: TestLocationDescriptorMinidumpWritable() : BaseTestMinidumpWritable(), location_descriptor_(), string_() {} ~TestLocationDescriptorMinidumpWritable() {} 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: MINIDUMP_LOCATION_DESCRIPTOR location_descriptor_; std::string string_; DISALLOW_COPY_AND_ASSIGN(TestLocationDescriptorMinidumpWritable); }; struct LocationDescriptorAndData { MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; char string[1]; }; const LocationDescriptorAndData* LDDAtIndex(const std::string& string, size_t index) { return reinterpret_cast(&string[index]); } TEST(MinidumpWritable, LocationDescriptor) { StringFile string_file; { SCOPED_TRACE("unset"); string_file.Reset(); TestLocationDescriptorMinidumpWritable location_descriptor_writable; EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file)); ASSERT_EQ(string_file.string().size(), 9u); const 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(), 9u); const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); EXPECT_EQ(ldd->location_descriptor.DataSize, 9u); 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(), 11u); const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); EXPECT_EQ(ldd->location_descriptor.DataSize, 11u); 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(), 22u); const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); EXPECT_EQ(ldd->location_descriptor.DataSize, 11u); EXPECT_EQ(ldd->location_descriptor.Rva, 0u); EXPECT_STREQ("yy", ldd->string); ldd = LDDAtIndex(string_file.string(), 12); EXPECT_EQ(ldd->location_descriptor.DataSize, 10u); EXPECT_EQ(ldd->location_descriptor.Rva, 12u); 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(), 23u); const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); EXPECT_EQ(ldd->location_descriptor.DataSize, 11u); EXPECT_EQ(ldd->location_descriptor.Rva, 12u); EXPECT_STREQ("www", ldd->string); ldd = LDDAtIndex(string_file.string(), 12); 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(), 29u); const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); EXPECT_EQ(ldd->location_descriptor.DataSize, 13u); EXPECT_EQ(ldd->location_descriptor.Rva, 16u); EXPECT_STREQ("uuuu", ldd->string); ldd = LDDAtIndex(string_file.string(), 16); EXPECT_EQ(ldd->location_descriptor.DataSize, 13u); 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(), 58u); const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); EXPECT_EQ(ldd->location_descriptor.DataSize, 10u); EXPECT_EQ(ldd->location_descriptor.Rva, 12u); EXPECT_STREQ("s", ldd->string); ldd = LDDAtIndex(string_file.string(), 12); EXPECT_EQ(ldd->location_descriptor.DataSize, 0u); EXPECT_EQ(ldd->location_descriptor.Rva, 0u); EXPECT_STREQ("r", ldd->string); ldd = LDDAtIndex(string_file.string(), 24); EXPECT_EQ(ldd->location_descriptor.DataSize, 10u); EXPECT_EQ(ldd->location_descriptor.Rva, 12u); EXPECT_STREQ("q", ldd->string); ldd = LDDAtIndex(string_file.string(), 36); EXPECT_EQ(ldd->location_descriptor.DataSize, 10u); EXPECT_EQ(ldd->location_descriptor.Rva, 12u); EXPECT_STREQ("p", ldd->string); ldd = LDDAtIndex(string_file.string(), 48); EXPECT_EQ(ldd->location_descriptor.DataSize, 10u); EXPECT_EQ(ldd->location_descriptor.Rva, 12u); EXPECT_STREQ("o", ldd->string); parent.Verify(); } } } // namespace } // namespace test } // namespace crashpad