| // -*- mode: c++ -*- |
| |
| // Copyright (c) 2010 Google Inc. All Rights Reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| // Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> |
| |
| // dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher. |
| |
| #include <stdint.h> |
| |
| #include <string> |
| #include <utility> |
| |
| #include "breakpad_googletest_includes.h" |
| |
| #include "common/dwarf/dwarf2diehandler.h" |
| #include "common/using_std_string.h" |
| |
| using std::make_pair; |
| |
| using ::testing::_; |
| using ::testing::ContainerEq; |
| using ::testing::ElementsAreArray; |
| using ::testing::Eq; |
| using ::testing::InSequence; |
| using ::testing::Return; |
| using ::testing::Sequence; |
| using ::testing::StrEq; |
| |
| using dwarf2reader::DIEDispatcher; |
| using dwarf2reader::DIEHandler; |
| using dwarf2reader::DwarfAttribute; |
| using dwarf2reader::DwarfForm; |
| using dwarf2reader::DwarfTag; |
| using dwarf2reader::RootDIEHandler; |
| |
| class MockDIEHandler: public DIEHandler { |
| public: |
| MOCK_METHOD3(ProcessAttributeUnsigned, |
| void(DwarfAttribute, DwarfForm, uint64)); |
| MOCK_METHOD3(ProcessAttributeSigned, |
| void(DwarfAttribute, DwarfForm, int64)); |
| MOCK_METHOD3(ProcessAttributeReference, |
| void(DwarfAttribute, DwarfForm, uint64)); |
| MOCK_METHOD4(ProcessAttributeBuffer, |
| void(DwarfAttribute, DwarfForm, const uint8_t *, uint64)); |
| MOCK_METHOD3(ProcessAttributeString, |
| void(DwarfAttribute, DwarfForm, const string &)); |
| MOCK_METHOD3(ProcessAttributeSignature, |
| void(DwarfAttribute, DwarfForm, uint64)); |
| MOCK_METHOD0(EndAttributes, bool()); |
| MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag)); |
| MOCK_METHOD0(Finish, void()); |
| }; |
| |
| class MockRootDIEHandler: public RootDIEHandler { |
| public: |
| MOCK_METHOD3(ProcessAttributeUnsigned, |
| void(DwarfAttribute, DwarfForm, uint64)); |
| MOCK_METHOD3(ProcessAttributeSigned, |
| void(DwarfAttribute, DwarfForm, int64)); |
| MOCK_METHOD3(ProcessAttributeReference, |
| void(DwarfAttribute, DwarfForm, uint64)); |
| MOCK_METHOD4(ProcessAttributeBuffer, |
| void(DwarfAttribute, DwarfForm, const uint8_t *, uint64)); |
| MOCK_METHOD3(ProcessAttributeString, |
| void(DwarfAttribute, DwarfForm, const string &)); |
| MOCK_METHOD3(ProcessAttributeSignature, |
| void(DwarfAttribute, DwarfForm, uint64)); |
| MOCK_METHOD0(EndAttributes, bool()); |
| MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64, DwarfTag)); |
| MOCK_METHOD0(Finish, void()); |
| MOCK_METHOD5(StartCompilationUnit, bool(uint64, uint8, uint8, uint64, uint8)); |
| MOCK_METHOD2(StartRootDIE, bool(uint64, DwarfTag)); |
| }; |
| |
| // If the handler elects to skip the compilation unit, the dispatcher |
| // should tell the reader so. |
| TEST(Dwarf2DIEHandler, SkipCompilationUnit) { |
| Sequence s; |
| MockRootDIEHandler mock_root_handler; |
| DIEDispatcher die_dispatcher(&mock_root_handler); |
| |
| EXPECT_CALL(mock_root_handler, |
| StartCompilationUnit(0x8d42aed77cfccf3eLL, |
| 0x89, 0xdc, |
| 0x2ecb4dc778a80f21LL, |
| 0x66)) |
| .InSequence(s) |
| .WillOnce(Return(false)); |
| |
| EXPECT_FALSE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL, |
| 0x89, 0xdc, |
| 0x2ecb4dc778a80f21LL, |
| 0x66)); |
| } |
| |
| // If the handler elects to skip the root DIE, the dispatcher should |
| // tell the reader so. |
| TEST(Dwarf2DIEHandler, SkipRootDIE) { |
| Sequence s; |
| MockRootDIEHandler mock_root_handler; |
| DIEDispatcher die_dispatcher(&mock_root_handler); |
| |
| EXPECT_CALL(mock_root_handler, |
| StartCompilationUnit(0xde8994029fc8b999LL, 0xf4, 0x02, |
| 0xb00febffa76e2b2bLL, 0x5c)) |
| .InSequence(s) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(mock_root_handler, |
| StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6)) |
| .InSequence(s) |
| .WillOnce(Return(false)); |
| |
| EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0xde8994029fc8b999LL, |
| 0xf4, 0x02, |
| 0xb00febffa76e2b2bLL, 0x5c)); |
| EXPECT_FALSE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL, |
| (DwarfTag) 0xb4f98da6)); |
| die_dispatcher.EndDIE(0x7d08242b4b510cf2LL); |
| } |
| |
| // If the handler elects to skip the root DIE's children, the |
| // dispatcher should tell the reader so --- and avoid deleting the |
| // root handler. |
| TEST(Dwarf2DIEHandler, SkipRootDIEChildren) { |
| MockRootDIEHandler mock_root_handler; |
| DIEDispatcher die_dispatcher(&mock_root_handler); |
| |
| { |
| InSequence s; |
| |
| EXPECT_CALL(mock_root_handler, |
| StartCompilationUnit(0x15d6897480cc65a7LL, 0x26, 0xa0, |
| 0x09f8bf0767f91675LL, 0xdb)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(mock_root_handler, |
| StartRootDIE(0x7d08242b4b510cf2LL, (DwarfTag) 0xb4f98da6)) |
| .WillOnce(Return(true)); |
| // Please don't tell me about my children. |
| EXPECT_CALL(mock_root_handler, EndAttributes()) |
| .WillOnce(Return(false)); |
| EXPECT_CALL(mock_root_handler, Finish()) |
| .WillOnce(Return()); |
| } |
| |
| EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x15d6897480cc65a7LL, |
| 0x26, 0xa0, |
| 0x09f8bf0767f91675LL, 0xdb)); |
| EXPECT_TRUE(die_dispatcher.StartDIE(0x7d08242b4b510cf2LL, |
| (DwarfTag) 0xb4f98da6)); |
| EXPECT_FALSE(die_dispatcher.StartDIE(0x435150ceedccda18LL, |
| (DwarfTag) 0xc3a17bba)); |
| die_dispatcher.EndDIE(0x435150ceedccda18LL); |
| die_dispatcher.EndDIE(0x7d08242b4b510cf2LL); |
| } |
| |
| // The dispatcher should pass attribute values through to the die |
| // handler accurately. |
| TEST(Dwarf2DIEHandler, PassAttributeValues) { |
| MockRootDIEHandler mock_root_handler; |
| DIEDispatcher die_dispatcher(&mock_root_handler); |
| |
| const uint8_t buffer[10] = { |
| 0x24, 0x24, 0x35, 0x9a, 0xca, 0xcf, 0xa8, 0x84, 0xa7, 0x18 |
| }; |
| string str = "\xc8\x26\x2e\x0d\xa4\x9c\x37\xd6\xfb\x1d"; |
| |
| // Set expectations. |
| { |
| InSequence s; |
| |
| // We'll like the compilation unit header. |
| EXPECT_CALL(mock_root_handler, |
| StartCompilationUnit(0x8d42aed77cfccf3eLL, 0x89, 0xdc, |
| 0x2ecb4dc778a80f21LL, 0x66)) |
| .WillOnce(Return(true)); |
| |
| // We'll like the root DIE. |
| EXPECT_CALL(mock_root_handler, |
| StartRootDIE(0xe2222da01e29f2a9LL, (DwarfTag) 0x9829445c)) |
| .WillOnce(Return(true)); |
| |
| // Expect some attribute values. |
| EXPECT_CALL(mock_root_handler, |
| ProcessAttributeUnsigned((DwarfAttribute) 0x1cc0bfed, |
| (DwarfForm) 0x424f1468, |
| 0xa592571997facda1ULL)) |
| .WillOnce(Return()); |
| EXPECT_CALL(mock_root_handler, |
| ProcessAttributeSigned((DwarfAttribute) 0x43694dc9, |
| (DwarfForm) 0xf6f78901L, |
| 0x92602a4e3bf1f446LL)) |
| .WillOnce(Return()); |
| EXPECT_CALL(mock_root_handler, |
| ProcessAttributeReference((DwarfAttribute) 0x4033e8cL, |
| (DwarfForm) 0xf66fbe0bL, |
| 0x50fddef44734fdecULL)) |
| .WillOnce(Return()); |
| EXPECT_CALL(mock_root_handler, |
| ProcessAttributeBuffer((DwarfAttribute) 0x25d7e0af, |
| (DwarfForm) 0xe99a539a, |
| buffer, sizeof(buffer))) |
| .WillOnce(Return()); |
| EXPECT_CALL(mock_root_handler, |
| ProcessAttributeString((DwarfAttribute) 0x310ed065, |
| (DwarfForm) 0x15762fec, |
| StrEq(str))) |
| .WillOnce(Return()); |
| EXPECT_CALL(mock_root_handler, |
| ProcessAttributeSignature((DwarfAttribute) 0x58790d72, |
| (DwarfForm) 0x4159f138, |
| 0x94682463613e6a5fULL)) |
| .WillOnce(Return()); |
| EXPECT_CALL(mock_root_handler, EndAttributes()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(mock_root_handler, FindChildHandler(_, _)) |
| .Times(0); |
| EXPECT_CALL(mock_root_handler, Finish()) |
| .WillOnce(Return()); |
| } |
| |
| // Drive the dispatcher. |
| |
| // Report the CU header. |
| EXPECT_TRUE(die_dispatcher.StartCompilationUnit(0x8d42aed77cfccf3eLL, |
| 0x89, 0xdc, |
| 0x2ecb4dc778a80f21LL, |
| 0x66)); |
| // Report the root DIE. |
| EXPECT_TRUE(die_dispatcher.StartDIE(0xe2222da01e29f2a9LL, |
| (DwarfTag) 0x9829445c)); |
| |
| // Report some attribute values. |
| die_dispatcher.ProcessAttributeUnsigned(0xe2222da01e29f2a9LL, |
| (DwarfAttribute) 0x1cc0bfed, |
| (DwarfForm) 0x424f1468, |
| 0xa592571997facda1ULL); |
| die_dispatcher.ProcessAttributeSigned(0xe2222da01e29f2a9LL, |
| (DwarfAttribute) 0x43694dc9, |
| (DwarfForm) 0xf6f78901, |
| 0x92602a4e3bf1f446LL); |
| die_dispatcher.ProcessAttributeReference(0xe2222da01e29f2a9LL, |
| (DwarfAttribute) 0x4033e8c, |
| (DwarfForm) 0xf66fbe0b, |
| 0x50fddef44734fdecULL); |
| die_dispatcher.ProcessAttributeBuffer(0xe2222da01e29f2a9LL, |
| (DwarfAttribute) 0x25d7e0af, |
| (DwarfForm) 0xe99a539a, |
| buffer, sizeof(buffer)); |
| die_dispatcher.ProcessAttributeString(0xe2222da01e29f2a9LL, |
| (DwarfAttribute) 0x310ed065, |
| (DwarfForm) 0x15762fec, |
| str); |
| die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL, |
| (DwarfAttribute) 0x58790d72, |
| (DwarfForm) 0x4159f138, |
| 0x94682463613e6a5fULL); |
| |
| // Finish the root DIE (and thus the CU). |
| die_dispatcher.EndDIE(0xe2222da01e29f2a9LL); |
| } |
| |
| TEST(Dwarf2DIEHandler, FindAndSkipChildren) { |
| MockRootDIEHandler mock_root_handler; |
| MockDIEHandler *mock_child1_handler = new(MockDIEHandler); |
| MockDIEHandler *mock_child3_handler = new(MockDIEHandler); |
| DIEDispatcher die_dispatcher(&mock_root_handler); |
| |
| { |
| InSequence s; |
| |
| // We'll like the compilation unit header. |
| EXPECT_CALL(mock_root_handler, |
| StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21, |
| 0x47dd3c764275a216LL, 0xa5)) |
| .WillOnce(Return(true)); |
| |
| // Root DIE. |
| { |
| EXPECT_CALL(mock_root_handler, |
| StartRootDIE(0x15f0e06bdfe3c372LL, (DwarfTag) 0xf5d60c59)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(mock_root_handler, |
| ProcessAttributeSigned((DwarfAttribute) 0xf779a642, |
| (DwarfForm) 0x2cb63027, |
| 0x18e744661769d08fLL)) |
| .WillOnce(Return()); |
| EXPECT_CALL(mock_root_handler, EndAttributes()) |
| .WillOnce(Return(true)); |
| |
| // First child DIE. |
| EXPECT_CALL(mock_root_handler, |
| FindChildHandler(0x149f644f8116fe8cLL, |
| (DwarfTag) 0xac2cbd8c)) |
| .WillOnce(Return(mock_child1_handler)); |
| { |
| EXPECT_CALL(*mock_child1_handler, |
| ProcessAttributeSigned((DwarfAttribute) 0xa6fd6f65, |
| (DwarfForm) 0xe4f64c41, |
| 0x1b04e5444a55fe67LL)) |
| .WillOnce(Return()); |
| EXPECT_CALL(*mock_child1_handler, EndAttributes()) |
| .WillOnce(Return(false)); |
| // Skip first grandchild DIE and first great-grandchild DIE. |
| EXPECT_CALL(*mock_child1_handler, Finish()) |
| .WillOnce(Return()); |
| } |
| |
| // Second child DIE. Root handler will decline to return a handler |
| // for this child. |
| EXPECT_CALL(mock_root_handler, |
| FindChildHandler(0x97412be24875de9dLL, |
| (DwarfTag) 0x505a068b)) |
| .WillOnce(Return((DIEHandler *) NULL)); |
| |
| // Third child DIE. |
| EXPECT_CALL(mock_root_handler, |
| FindChildHandler(0x753c964c8ab538aeLL, |
| (DwarfTag) 0x8c22970e)) |
| .WillOnce(Return(mock_child3_handler)); |
| { |
| EXPECT_CALL(*mock_child3_handler, |
| ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb, |
| (DwarfForm) 0x610b7ae1, |
| 0x3ea5c609d7d7560fLL)) |
| .WillOnce(Return()); |
| EXPECT_CALL(*mock_child3_handler, EndAttributes()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(*mock_child3_handler, Finish()) |
| .WillOnce(Return()); |
| } |
| |
| EXPECT_CALL(mock_root_handler, Finish()) |
| .WillOnce(Return()); |
| } |
| } |
| |
| |
| // Drive the dispatcher. |
| |
| // Report the CU header. |
| EXPECT_TRUE(die_dispatcher |
| .StartCompilationUnit(0x9ec1e6d05e434a0eLL, 0xeb, 0x21, |
| 0x47dd3c764275a216LL, 0xa5)); |
| // Report the root DIE. |
| { |
| EXPECT_TRUE(die_dispatcher.StartDIE(0x15f0e06bdfe3c372LL, |
| (DwarfTag) 0xf5d60c59)); |
| die_dispatcher.ProcessAttributeSigned(0x15f0e06bdfe3c372LL, |
| (DwarfAttribute) 0xf779a642, |
| (DwarfForm) 0x2cb63027, |
| 0x18e744661769d08fLL); |
| |
| // First child DIE. |
| { |
| EXPECT_TRUE(die_dispatcher.StartDIE(0x149f644f8116fe8cLL, |
| (DwarfTag) 0xac2cbd8c)); |
| die_dispatcher.ProcessAttributeSigned(0x149f644f8116fe8cLL, |
| (DwarfAttribute) 0xa6fd6f65, |
| (DwarfForm) 0xe4f64c41, |
| 0x1b04e5444a55fe67LL); |
| |
| // First grandchild DIE. Will be skipped. |
| { |
| EXPECT_FALSE(die_dispatcher.StartDIE(0xd68de1ee0bd29419LL, |
| (DwarfTag) 0x22f05a15)); |
| // First great-grandchild DIE. Will be skipped without being |
| // mentioned to any handler. |
| { |
| EXPECT_FALSE(die_dispatcher |
| .StartDIE(0xb3076285d25cac25LL, |
| (DwarfTag) 0xcff4061b)); |
| die_dispatcher.EndDIE(0xb3076285d25cac25LL); |
| } |
| die_dispatcher.EndDIE(0xd68de1ee0bd29419LL); |
| } |
| die_dispatcher.EndDIE(0x149f644f8116fe8cLL); |
| } |
| |
| // Second child DIE. Root handler will decline to find a handler for it. |
| { |
| EXPECT_FALSE(die_dispatcher.StartDIE(0x97412be24875de9dLL, |
| (DwarfTag) 0x505a068b)); |
| die_dispatcher.EndDIE(0x97412be24875de9dLL); |
| } |
| |
| // Third child DIE. |
| { |
| EXPECT_TRUE(die_dispatcher.StartDIE(0x753c964c8ab538aeLL, |
| (DwarfTag) 0x8c22970e)); |
| die_dispatcher.ProcessAttributeSigned(0x753c964c8ab538aeLL, |
| (DwarfAttribute) 0x4e2b7cfb, |
| (DwarfForm) 0x610b7ae1, |
| 0x3ea5c609d7d7560fLL); |
| die_dispatcher.EndDIE(0x753c964c8ab538aeLL); |
| } |
| |
| // Finish the root DIE (and thus the CU). |
| die_dispatcher.EndDIE(0x15f0e06bdfe3c372LL); |
| } |
| } |
| |
| // The DIEDispatcher destructor is supposed to delete all handlers on |
| // the stack, except for the root. |
| TEST(Dwarf2DIEHandler, FreeHandlersOnStack) { |
| MockRootDIEHandler mock_root_handler; |
| MockDIEHandler *mock_child_handler = new(MockDIEHandler); |
| MockDIEHandler *mock_grandchild_handler = new(MockDIEHandler); |
| |
| { |
| InSequence s; |
| |
| // We'll like the compilation unit header. |
| EXPECT_CALL(mock_root_handler, |
| StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89, |
| 0x76d392ff393ddda2LL, 0xbf)) |
| .WillOnce(Return(true)); |
| |
| // Root DIE. |
| { |
| EXPECT_CALL(mock_root_handler, |
| StartRootDIE(0xbf13b761691ddc91LL, (DwarfTag) 0x98980361)) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(mock_root_handler, EndAttributes()) |
| .WillOnce(Return(true)); |
| |
| // Child DIE. |
| EXPECT_CALL(mock_root_handler, |
| FindChildHandler(0x058f09240c5fc8c9LL, |
| (DwarfTag) 0x898bf0d0)) |
| .WillOnce(Return(mock_child_handler)); |
| { |
| EXPECT_CALL(*mock_child_handler, EndAttributes()) |
| .WillOnce(Return(true)); |
| |
| // Grandchild DIE. |
| EXPECT_CALL(*mock_child_handler, |
| FindChildHandler(0x32dc00c9945dc0c8LL, |
| (DwarfTag) 0x2802d007)) |
| .WillOnce(Return(mock_grandchild_handler)); |
| { |
| EXPECT_CALL(*mock_grandchild_handler, |
| ProcessAttributeSigned((DwarfAttribute) 0x4e2b7cfb, |
| (DwarfForm) 0x610b7ae1, |
| 0x3ea5c609d7d7560fLL)) |
| .WillOnce(Return()); |
| |
| // At this point, we abandon the traversal, so none of the |
| // usual stuff should get called. |
| EXPECT_CALL(*mock_grandchild_handler, EndAttributes()) |
| .Times(0); |
| EXPECT_CALL(*mock_grandchild_handler, Finish()) |
| .Times(0); |
| } |
| |
| EXPECT_CALL(*mock_child_handler, Finish()) |
| .Times(0); |
| } |
| |
| EXPECT_CALL(mock_root_handler, Finish()) |
| .Times(0); |
| } |
| } |
| |
| // The dispatcher. |
| DIEDispatcher die_dispatcher(&mock_root_handler); |
| |
| // Report the CU header. |
| EXPECT_TRUE(die_dispatcher |
| .StartCompilationUnit(0x87b41ba8381cd71cLL, 0xff, 0x89, |
| 0x76d392ff393ddda2LL, 0xbf)); |
| // Report the root DIE. |
| { |
| EXPECT_TRUE(die_dispatcher.StartDIE(0xbf13b761691ddc91LL, |
| (DwarfTag) 0x98980361)); |
| |
| // Child DIE. |
| { |
| EXPECT_TRUE(die_dispatcher.StartDIE(0x058f09240c5fc8c9LL, |
| (DwarfTag) 0x898bf0d0)); |
| |
| // Grandchild DIE. |
| { |
| EXPECT_TRUE(die_dispatcher.StartDIE(0x32dc00c9945dc0c8LL, |
| (DwarfTag) 0x2802d007)); |
| die_dispatcher.ProcessAttributeSigned(0x32dc00c9945dc0c8LL, |
| (DwarfAttribute) 0x4e2b7cfb, |
| (DwarfForm) 0x610b7ae1, |
| 0x3ea5c609d7d7560fLL); |
| |
| // Stop the traversal abruptly, so that there will still be |
| // handlers on the stack when the dispatcher is destructed. |
| |
| // No EndDIE call... |
| } |
| // No EndDIE call... |
| } |
| // No EndDIE call... |
| } |
| } |