diff options
Diffstat (limited to 'src/processor')
-rw-r--r-- | src/processor/stackwalker_address_list.cc | 92 | ||||
-rw-r--r-- | src/processor/stackwalker_address_list.h | 72 | ||||
-rw-r--r-- | src/processor/stackwalker_address_list_unittest.cc | 195 |
3 files changed, 359 insertions, 0 deletions
diff --git a/src/processor/stackwalker_address_list.cc b/src/processor/stackwalker_address_list.cc new file mode 100644 index 00000000..fdc9b9ae --- /dev/null +++ b/src/processor/stackwalker_address_list.cc @@ -0,0 +1,92 @@ +// Copyright (c) 2013 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. + +// stackwalker_address_list.cc: a pseudo stack walker. +// +// See stackwalker_address_list.h for documentation. +// +// Author: Chris Hamilton <chrisha@chromium.org> + +#include <assert.h> + +#include <vector> + +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame.h" +#include "processor/logging.h" +#include "processor/stackwalker_address_list.h" + +namespace google_breakpad { + +StackwalkerAddressList::StackwalkerAddressList( + const uint64_t* frames, + size_t frame_count, + const CodeModules* modules, + StackFrameSymbolizer* frame_symbolizer) + : Stackwalker(NULL, NULL, modules, frame_symbolizer), + frames_(frames), + frame_count_(frame_count) { + assert(frames); + assert(frame_symbolizer); +} + +StackFrame* StackwalkerAddressList::GetContextFrame() { + if (frame_count_ == 0) + return NULL; + + StackFrame* frame = new StackFrame(); + frame->instruction = frames_[0]; + frame->trust = StackFrame::FRAME_TRUST_CONTEXT; + return frame; +} + +StackFrame* StackwalkerAddressList::GetCallerFrame(const CallStack* stack, + bool stack_scan_allowed) { + if (!stack) { + BPLOG(ERROR) << "Can't get caller frame without stack"; + return NULL; + } + + size_t frame_index = stack->frames()->size(); + + // There are no more frames to fetch. + if (frame_index >= frame_count_) + return NULL; + + // All frames have the highest level of trust because they were + // explicitly provided. + StackFrame* frame = new StackFrame(); + frame->instruction = frames_[frame_index]; + frame->trust = StackFrame::FRAME_TRUST_CONTEXT; + return frame; +} + +} // namespace google_breakpad diff --git a/src/processor/stackwalker_address_list.h b/src/processor/stackwalker_address_list.h new file mode 100644 index 00000000..0f8c989e --- /dev/null +++ b/src/processor/stackwalker_address_list.h @@ -0,0 +1,72 @@ +// Copyright (c) 2013 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. + +// stackwalker_address_list.h: a pseudo stackwalker. +// +// Doesn't actually walk a stack, rather initializes a CallStack given an +// explicit list of already walked return addresses. +// +// Author: Chris Hamilton <chrisha@chromium.org> + +#ifndef PROCESSOR_STACKWALKER_ADDRESS_LIST_H_ +#define PROCESSOR_STACKWALKER_ADDRESS_LIST_H_ + +#include "common/basictypes.h" +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/stackwalker.h" + +namespace google_breakpad { + +class CodeModules; + +class StackwalkerAddressList : public Stackwalker { + public: + // Initializes this stack walker with an explicit set of frame addresses. + // |modules| and |frame_symbolizer| are passed directly through to the base + // Stackwalker constructor. + StackwalkerAddressList(const uint64_t* frames, + size_t frame_count, + const CodeModules* modules, + StackFrameSymbolizer* frame_symbolizer); + + private: + // Implementation of Stackwalker. + virtual StackFrame* GetContextFrame(); + virtual StackFrame* GetCallerFrame(const CallStack* stack, + bool stack_scan_allowed); + + const uint64_t* frames_; + size_t frame_count_; + + DISALLOW_COPY_AND_ASSIGN(StackwalkerAddressList); +}; + +} // namespace google_breakpad + +#endif // PROCESSOR_STACKWALKER_ADDRESS_LIST_H_ diff --git a/src/processor/stackwalker_address_list_unittest.cc b/src/processor/stackwalker_address_list_unittest.cc new file mode 100644 index 00000000..8af63fb1 --- /dev/null +++ b/src/processor/stackwalker_address_list_unittest.cc @@ -0,0 +1,195 @@ +// Copyright (c) 2013, 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. + +// stackwalker_address_list_unittest.cc: Unit tests for the +// StackwalkerAddressList class. +// +// Author: Chris Hamilton <chrisha@chromium.org> + +#include <string> +#include <vector> + +#include "breakpad_googletest_includes.h" +#include "common/using_std_string.h" +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/basic_source_line_resolver.h" +#include "google_breakpad/processor/call_stack.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/source_line_resolver_interface.h" +#include "google_breakpad/processor/stack_frame.h" +#include "processor/stackwalker_unittest_utils.h" +#include "processor/stackwalker_address_list.h" + +using google_breakpad::BasicSourceLineResolver; +using google_breakpad::CallStack; +using google_breakpad::CodeModule; +using google_breakpad::StackFrameSymbolizer; +using google_breakpad::StackFrame; +using google_breakpad::Stackwalker; +using google_breakpad::StackwalkerAddressList; +using std::vector; +using testing::_; +using testing::AnyNumber; +using testing::Return; +using testing::SetArgumentPointee; + +#define arraysize(f) (sizeof(f) / sizeof(*f)) + +// Addresses and sizes of a couple dummy modules. +uint64_t kModule1Base = 0x40000000; +uint64_t kModule1Size = 0x10000; +uint64_t kModule2Base = 0x50000000; +uint64_t kModule2Size = 0x10000; + +// A handful of addresses that lie within the modules above. +const uint64_t kDummyFrames[] = { + 0x50003000, 0x50002000, 0x50001000, 0x40002000, 0x40001000 }; + +class StackwalkerAddressListTest : public testing::Test { + public: + StackwalkerAddressListTest() + : // Give the two modules reasonable standard locations and names + // for tests to play with. + module1(kModule1Base, kModule1Size, "module1", "version1"), + module2(kModule2Base, kModule2Size, "module2", "version2") { + // Create some modules with some stock debugging information. + modules.Add(&module1); + modules.Add(&module2); + + // By default, none of the modules have symbol info; call + // SetModuleSymbols to override this. + EXPECT_CALL(supplier, GetCStringSymbolData(_, _, _, _, _)) + .WillRepeatedly(Return(MockSymbolSupplier::NOT_FOUND)); + + // Avoid GMOCK WARNING "Uninteresting mock function call - returning + // directly" for FreeSymbolData(). + EXPECT_CALL(supplier, FreeSymbolData(_)).Times(AnyNumber()); + } + + // Set the Breakpad symbol information that supplier should return for + // MODULE to INFO. + void SetModuleSymbols(MockCodeModule *module, const string &info) { + size_t buffer_size; + char *buffer = supplier.CopySymbolDataAndOwnTheCopy(info, &buffer_size); + EXPECT_CALL(supplier, GetCStringSymbolData(module, NULL, _, _, _)) + .WillRepeatedly(DoAll(SetArgumentPointee<3>(buffer), + SetArgumentPointee<4>(buffer_size), + Return(MockSymbolSupplier::FOUND))); + } + + void CheckCallStack(const CallStack& call_stack) { + const std::vector<StackFrame*>* frames = call_stack.frames(); + ASSERT_EQ(arraysize(kDummyFrames), frames->size()); + for (size_t i = 0; i < arraysize(kDummyFrames); ++i) + ASSERT_EQ(kDummyFrames[i], frames->at(i)->instruction); + ASSERT_EQ(static_cast<const CodeModule*>(&module2), frames->at(0)->module); + ASSERT_EQ(static_cast<const CodeModule*>(&module2), frames->at(1)->module); + ASSERT_EQ(static_cast<const CodeModule*>(&module2), frames->at(2)->module); + ASSERT_EQ(static_cast<const CodeModule*>(&module1), frames->at(3)->module); + ASSERT_EQ(static_cast<const CodeModule*>(&module1), frames->at(4)->module); + } + + MockCodeModule module1; + MockCodeModule module2; + MockCodeModules modules; + MockSymbolSupplier supplier; + BasicSourceLineResolver resolver; +}; + +TEST_F(StackwalkerAddressListTest, ScanWithoutSymbols) { + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerAddressList walker(kDummyFrames, arraysize(kDummyFrames), + &modules, &frame_symbolizer); + + CallStack call_stack; + vector<const CodeModule*> modules_without_symbols; + vector<const CodeModule*> modules_with_corrupt_symbols; + ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols, + &modules_with_corrupt_symbols)); + + // The stack starts in module2, so we expect that to be the first module + // found without symbols. + ASSERT_EQ(2U, modules_without_symbols.size()); + ASSERT_EQ("module2", modules_without_symbols[0]->debug_file()); + ASSERT_EQ("module1", modules_without_symbols[1]->debug_file()); + ASSERT_EQ(0u, modules_with_corrupt_symbols.size()); + + ASSERT_NO_FATAL_FAILURE(CheckCallStack(call_stack)); +} + +TEST_F(StackwalkerAddressListTest, ScanWithSymbols) { + // File : FILE number(dex) name + // Function: FUNC address(hex) size(hex) parameter_size(hex) name + // Line : address(hex) size(hex) line(dec) filenum(dec) + SetModuleSymbols(&module2, + "FILE 1 module2.cc\n" + "FUNC 3000 100 10 mod2func3\n" + "3000 10 1 1\n" + "FUNC 2000 200 10 mod2func2\n" + "FUNC 1000 300 10 mod2func1\n"); + SetModuleSymbols(&module1, + "FUNC 2000 200 10 mod1func2\n" + "FUNC 1000 300 10 mod1func1\n"); + + StackFrameSymbolizer frame_symbolizer(&supplier, &resolver); + StackwalkerAddressList walker(kDummyFrames, arraysize(kDummyFrames), + &modules, &frame_symbolizer); + + CallStack call_stack; + vector<const CodeModule*> modules_without_symbols; + vector<const CodeModule*> modules_with_corrupt_symbols; + ASSERT_TRUE(walker.Walk(&call_stack, &modules_without_symbols, + &modules_with_corrupt_symbols)); + + ASSERT_EQ(0u, modules_without_symbols.size()); + ASSERT_EQ(0u, modules_with_corrupt_symbols.size()); + + ASSERT_NO_FATAL_FAILURE(CheckCallStack(call_stack)); + + const std::vector<StackFrame*>* frames = call_stack.frames(); + + // We have full file/line information for the first function call. + ASSERT_EQ("mod2func3", frames->at(0)->function_name); + ASSERT_EQ(0x50003000u, frames->at(0)->function_base); + ASSERT_EQ("module2.cc", frames->at(0)->source_file_name); + ASSERT_EQ(1, frames->at(0)->source_line); + ASSERT_EQ(0x50003000u, frames->at(0)->source_line_base); + + ASSERT_EQ("mod2func2", frames->at(1)->function_name); + ASSERT_EQ(0x50002000u, frames->at(1)->function_base); + + ASSERT_EQ("mod2func1", frames->at(2)->function_name); + ASSERT_EQ(0x50001000u, frames->at(2)->function_base); + + ASSERT_EQ("mod1func2", frames->at(3)->function_name); + ASSERT_EQ(0x40002000u, frames->at(3)->function_base); + + ASSERT_EQ("mod1func1", frames->at(4)->function_name); + ASSERT_EQ(0x40001000u, frames->at(4)->function_base); +} |