| // 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 <string> |
| #include <vector> |
| |
| #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<MinidumpWritable*> Children() override { |
| EXPECT_GE(state(), kStateFrozen); |
| if (!children_.empty()) { |
| std::vector<MinidumpWritable*> 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<BaseTestMinidumpWritable*> 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 <typename RVAType> |
| 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 <typename RVAType> |
| RVAType TRVAAtIndex(const std::string& string, size_t index) { |
| return *reinterpret_cast<const RVAType*>(&string[index * sizeof(RVAType)]); |
| } |
| |
| template <typename T> |
| struct TestNames {}; |
| |
| template <> |
| struct TestNames<RVA> { |
| static constexpr char kName[] = "RVA"; |
| }; |
| |
| template <> |
| struct TestNames<RVA64> { |
| static constexpr char kName[] = "RVA64"; |
| }; |
| |
| class TestTypeNames { |
| public: |
| template <typename T> |
| static std::string GetName(int) { |
| return std::string(TestNames<T>::kName); |
| } |
| }; |
| |
| template <typename RVAType> |
| class MinidumpWritable : public ::testing::Test {}; |
| |
| using RVATypes = ::testing::Types<RVA, RVA64>; |
| TYPED_TEST_SUITE(MinidumpWritable, RVATypes, TestTypeNames); |
| |
| TYPED_TEST(MinidumpWritable, RVA) { |
| StringFile string_file; |
| using TestRVAMinidumpWritable = TTestRVAMinidumpWritable<TypeParam>; |
| const auto RVAAtIndex = TRVAAtIndex<TypeParam>; |
| 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 <typename MinidumpLocationDescriptorType> |
| 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<WritableIoVec> 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 <typename MinidumpLocationDescriptorType> |
| struct TLocationDescriptorAndData { |
| MinidumpLocationDescriptorType location_descriptor; |
| const char* string; |
| }; |
| |
| template <typename MinidumpLocationDescriptorType> |
| TLocationDescriptorAndData<MinidumpLocationDescriptorType> TLDDAtIndex( |
| const std::string& str, |
| size_t index) { |
| const MinidumpLocationDescriptorType* location_descriptor = |
| reinterpret_cast<const MinidumpLocationDescriptorType*>(&str[index]); |
| |
| const char* string = reinterpret_cast<const char*>( |
| &str[index] + |
| offsetof(TLocationDescriptorAndData<MinidumpLocationDescriptorType>, |
| string)); |
| |
| return TLocationDescriptorAndData<MinidumpLocationDescriptorType>{ |
| *location_descriptor, |
| string, |
| }; |
| } |
| |
| template <typename MinidumpLocationDescriptorType> |
| class MinidumpWritableLocationDescriptor : public ::testing::Test {}; |
| |
| template <> |
| struct TestNames<MINIDUMP_LOCATION_DESCRIPTOR> { |
| static constexpr char kName[] = "MINIDUMP_LOCATION_DESCRIPTOR"; |
| }; |
| |
| template <> |
| struct TestNames<MINIDUMP_LOCATION_DESCRIPTOR64> { |
| static constexpr char kName[] = "MINIDUMP_LOCATION_DESCRIPTOR64"; |
| }; |
| |
| using MinidumpLocationDescriptorTypes = |
| ::testing::Types<MINIDUMP_LOCATION_DESCRIPTOR, |
| MINIDUMP_LOCATION_DESCRIPTOR64>; |
| TYPED_TEST_SUITE(MinidumpWritableLocationDescriptor, |
| MinidumpLocationDescriptorTypes, |
| TestTypeNames); |
| |
| TYPED_TEST(MinidumpWritableLocationDescriptor, LocationDescriptor) { |
| StringFile string_file; |
| |
| using LocationDescriptorAndData = TLocationDescriptorAndData<TypeParam>; |
| const auto LDDAtIndex = TLDDAtIndex<TypeParam>; |
| using TestLocationDescriptorMinidumpWritable = |
| TTestLocationDescriptorMinidumpWritable<TypeParam>; |
| 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 |