PageRenderTime 44ms CodeModel.GetById 5ms app.highlight 34ms RepoModel.GetById 1ms app.codeStats 1ms

/thirdparty/breakpad/processor/basic_source_line_resolver_unittest.cc

http://github.com/tomahawk-player/tomahawk
C++ | 411 lines | 327 code | 43 blank | 41 comment | 13 complexity | 8c81d5b1d964554994bc7b5875f1bbf9 MD5 | raw file
  1// Copyright (c) 2010 Google Inc.
  2// All rights reserved.
  3//
  4// Redistribution and use in source and binary forms, with or without
  5// modification, are permitted provided that the following conditions are
  6// met:
  7//
  8//     * Redistributions of source code must retain the above copyright
  9// notice, this list of conditions and the following disclaimer.
 10//     * Redistributions in binary form must reproduce the above
 11// copyright notice, this list of conditions and the following disclaimer
 12// in the documentation and/or other materials provided with the
 13// distribution.
 14//     * Neither the name of Google Inc. nor the names of its
 15// contributors may be used to endorse or promote products derived from
 16// this software without specific prior written permission.
 17//
 18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 29
 30#include <stdio.h>
 31
 32#include <string>
 33
 34#include "breakpad_googletest_includes.h"
 35#include "google_breakpad/processor/basic_source_line_resolver.h"
 36#include "google_breakpad/processor/code_module.h"
 37#include "google_breakpad/processor/stack_frame.h"
 38#include "google_breakpad/processor/memory_region.h"
 39#include "processor/linked_ptr.h"
 40#include "processor/logging.h"
 41#include "processor/scoped_ptr.h"
 42#include "processor/windows_frame_info.h"
 43#include "processor/cfi_frame_info.h"
 44
 45namespace {
 46
 47using std::string;
 48using google_breakpad::BasicSourceLineResolver;
 49using google_breakpad::CFIFrameInfo;
 50using google_breakpad::CodeModule;
 51using google_breakpad::MemoryRegion;
 52using google_breakpad::StackFrame;
 53using google_breakpad::WindowsFrameInfo;
 54using google_breakpad::linked_ptr;
 55using google_breakpad::scoped_ptr;
 56
 57class TestCodeModule : public CodeModule {
 58 public:
 59  TestCodeModule(string code_file) : code_file_(code_file) {}
 60  virtual ~TestCodeModule() {}
 61
 62  virtual u_int64_t base_address() const { return 0; }
 63  virtual u_int64_t size() const { return 0xb000; }
 64  virtual string code_file() const { return code_file_; }
 65  virtual string code_identifier() const { return ""; }
 66  virtual string debug_file() const { return ""; }
 67  virtual string debug_identifier() const { return ""; }
 68  virtual string version() const { return ""; }
 69  virtual const CodeModule* Copy() const {
 70    return new TestCodeModule(code_file_);
 71  }
 72
 73 private:
 74  string code_file_;
 75};
 76
 77// A mock memory region object, for use by the STACK CFI tests.
 78class MockMemoryRegion: public MemoryRegion {
 79  u_int64_t GetBase() const { return 0x10000; }
 80  u_int32_t GetSize() const { return 0x01000; }
 81  bool GetMemoryAtAddress(u_int64_t address, u_int8_t *value) const {
 82    *value = address & 0xff;
 83    return true;
 84  }
 85  bool GetMemoryAtAddress(u_int64_t address, u_int16_t *value) const {
 86    *value = address & 0xffff;
 87    return true;
 88  }
 89  bool GetMemoryAtAddress(u_int64_t address, u_int32_t *value) const {
 90    switch (address) {
 91      case 0x10008: *value = 0x98ecadc3; break; // saved %ebx
 92      case 0x1000c: *value = 0x878f7524; break; // saved %esi
 93      case 0x10010: *value = 0x6312f9a5; break; // saved %edi
 94      case 0x10014: *value = 0x10038;    break; // caller's %ebp
 95      case 0x10018: *value = 0xf6438648; break; // return address
 96      default: *value = 0xdeadbeef;      break; // junk
 97    }
 98    return true;
 99  }
100  bool GetMemoryAtAddress(u_int64_t address, u_int64_t *value) const {
101    *value = address;
102    return true;
103  }
104};
105
106// Verify that, for every association in ACTUAL, EXPECTED has the same
107// association. (That is, ACTUAL's associations should be a subset of
108// EXPECTED's.) Also verify that ACTUAL has associations for ".ra" and
109// ".cfa".
110static bool VerifyRegisters(
111    const char *file, int line,
112    const CFIFrameInfo::RegisterValueMap<u_int32_t> &expected,
113    const CFIFrameInfo::RegisterValueMap<u_int32_t> &actual) {
114  CFIFrameInfo::RegisterValueMap<u_int32_t>::const_iterator a;
115  a = actual.find(".cfa");
116  if (a == actual.end())
117    return false;
118  a = actual.find(".ra");
119  if (a == actual.end())
120    return false;
121  for (a = actual.begin(); a != actual.end(); a++) {
122    CFIFrameInfo::RegisterValueMap<u_int32_t>::const_iterator e =
123      expected.find(a->first);
124    if (e == expected.end()) {
125      fprintf(stderr, "%s:%d: unexpected register '%s' recovered, value 0x%x\n",
126              file, line, a->first.c_str(), a->second);
127      return false;
128    }
129    if (e->second != a->second) {
130      fprintf(stderr,
131              "%s:%d: register '%s' recovered value was 0x%x, expected 0x%x\n",
132              file, line, a->first.c_str(), a->second, e->second);
133      return false;
134    }
135    // Don't complain if this doesn't recover all registers. Although
136    // the DWARF spec says that unmentioned registers are undefined,
137    // GCC uses omission to mean that they are unchanged.
138  }
139  return true;
140}
141
142
143static bool VerifyEmpty(const StackFrame &frame) {
144  if (frame.function_name.empty() &&
145      frame.source_file_name.empty() &&
146      frame.source_line == 0)
147    return true;
148  return false;
149}
150
151static void ClearSourceLineInfo(StackFrame *frame) {
152  frame->function_name.clear();
153  frame->module = NULL;
154  frame->source_file_name.clear();
155  frame->source_line = 0;
156}
157
158class TestBasicSourceLineResolver : public ::testing::Test {
159public:
160  void SetUp() {
161    testdata_dir = string(getenv("srcdir") ? getenv("srcdir") : ".") +
162                         "/src/processor/testdata";
163  }
164
165  BasicSourceLineResolver resolver;
166  string testdata_dir;
167};
168
169TEST_F(TestBasicSourceLineResolver, TestLoadAndResolve)
170{
171  TestCodeModule module1("module1");
172  ASSERT_TRUE(resolver.LoadModule(&module1, testdata_dir + "/module1.out"));
173  ASSERT_TRUE(resolver.HasModule(&module1));
174  TestCodeModule module2("module2");
175  ASSERT_TRUE(resolver.LoadModule(&module2, testdata_dir + "/module2.out"));
176  ASSERT_TRUE(resolver.HasModule(&module2));
177
178
179  StackFrame frame;
180  scoped_ptr<WindowsFrameInfo> windows_frame_info;
181  scoped_ptr<CFIFrameInfo> cfi_frame_info;
182  frame.instruction = 0x1000;
183  frame.module = NULL;
184  resolver.FillSourceLineInfo(&frame);
185  ASSERT_FALSE(frame.module);
186  ASSERT_TRUE(frame.function_name.empty());
187  ASSERT_EQ(frame.function_base, 0);
188  ASSERT_TRUE(frame.source_file_name.empty());
189  ASSERT_EQ(frame.source_line, 0);
190  ASSERT_EQ(frame.source_line_base, 0);
191
192  frame.module = &module1;
193  resolver.FillSourceLineInfo(&frame);
194  ASSERT_EQ(frame.function_name, "Function1_1");
195  ASSERT_TRUE(frame.module);
196  ASSERT_EQ(frame.module->code_file(), "module1");
197  ASSERT_EQ(frame.function_base, 0x1000);
198  ASSERT_EQ(frame.source_file_name, "file1_1.cc");
199  ASSERT_EQ(frame.source_line, 44);
200  ASSERT_EQ(frame.source_line_base, 0x1000);
201  windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
202  ASSERT_TRUE(windows_frame_info.get());
203  ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
204  ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
205  ASSERT_EQ(windows_frame_info->program_string,
206            "$eip 4 + ^ = $esp $ebp 8 + = $ebp $ebp ^ =");
207
208  ClearSourceLineInfo(&frame);
209  frame.instruction = 0x800;
210  frame.module = &module1;
211  resolver.FillSourceLineInfo(&frame);
212  ASSERT_TRUE(VerifyEmpty(frame));
213  windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
214  ASSERT_FALSE(windows_frame_info.get());
215
216  frame.instruction = 0x1280;
217  resolver.FillSourceLineInfo(&frame);
218  ASSERT_EQ(frame.function_name, "Function1_3");
219  ASSERT_TRUE(frame.source_file_name.empty());
220  ASSERT_EQ(frame.source_line, 0);
221  windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
222  ASSERT_TRUE(windows_frame_info.get());
223  ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_UNKNOWN);
224  ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
225  ASSERT_TRUE(windows_frame_info->program_string.empty());
226
227  frame.instruction = 0x1380;
228  resolver.FillSourceLineInfo(&frame);
229  ASSERT_EQ(frame.function_name, "Function1_4");
230  ASSERT_TRUE(frame.source_file_name.empty());
231  ASSERT_EQ(frame.source_line, 0);
232  windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
233  ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
234  ASSERT_TRUE(windows_frame_info.get());
235  ASSERT_FALSE(windows_frame_info->allocates_base_pointer);
236  ASSERT_FALSE(windows_frame_info->program_string.empty());
237
238  frame.instruction = 0x2000;
239  windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
240  ASSERT_FALSE(windows_frame_info.get());
241
242  // module1 has STACK CFI records covering 3d40..3def;
243  // module2 has STACK CFI records covering 3df0..3e9f;
244  // check that FindCFIFrameInfo doesn't claim to find any outside those ranges.
245  frame.instruction = 0x3d3f;
246  frame.module = &module1;
247  cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
248  ASSERT_FALSE(cfi_frame_info.get());
249
250  frame.instruction = 0x3e9f;
251  frame.module = &module1;
252  cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
253  ASSERT_FALSE(cfi_frame_info.get());
254
255  CFIFrameInfo::RegisterValueMap<u_int32_t> current_registers;
256  CFIFrameInfo::RegisterValueMap<u_int32_t> caller_registers;
257  CFIFrameInfo::RegisterValueMap<u_int32_t> expected_caller_registers;
258  MockMemoryRegion memory;
259
260  // Regardless of which instruction evaluation takes place at, it
261  // should produce the same values for the caller's registers.
262  expected_caller_registers[".cfa"] = 0x1001c;
263  expected_caller_registers[".ra"]  = 0xf6438648;
264  expected_caller_registers["$ebp"] = 0x10038;
265  expected_caller_registers["$ebx"] = 0x98ecadc3;
266  expected_caller_registers["$esi"] = 0x878f7524;
267  expected_caller_registers["$edi"] = 0x6312f9a5;
268
269  frame.instruction = 0x3d40;
270  frame.module = &module1;
271  current_registers.clear();
272  current_registers["$esp"] = 0x10018;
273  current_registers["$ebp"] = 0x10038;
274  current_registers["$ebx"] = 0x98ecadc3;
275  current_registers["$esi"] = 0x878f7524;
276  current_registers["$edi"] = 0x6312f9a5;
277  cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
278  ASSERT_TRUE(cfi_frame_info.get());
279  ASSERT_TRUE(cfi_frame_info.get()
280              ->FindCallerRegs<u_int32_t>(current_registers, memory,
281                                          &caller_registers));
282  ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
283                              expected_caller_registers, caller_registers));
284
285  frame.instruction = 0x3d41;
286  current_registers["$esp"] = 0x10014;
287  cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
288  ASSERT_TRUE(cfi_frame_info.get());
289  ASSERT_TRUE(cfi_frame_info.get()
290              ->FindCallerRegs<u_int32_t>(current_registers, memory,
291                                          &caller_registers));
292  ASSERT_TRUE(VerifyRegisters(__FILE__, __LINE__,
293                              expected_caller_registers, caller_registers));
294
295  frame.instruction = 0x3d43;
296  current_registers["$ebp"] = 0x10014;
297  cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
298  ASSERT_TRUE(cfi_frame_info.get());
299  ASSERT_TRUE(cfi_frame_info.get()
300              ->FindCallerRegs<u_int32_t>(current_registers, memory,
301                                          &caller_registers));
302  VerifyRegisters(__FILE__, __LINE__,
303                  expected_caller_registers, caller_registers);
304
305  frame.instruction = 0x3d54;
306  current_registers["$ebx"] = 0x6864f054U;
307  cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
308  ASSERT_TRUE(cfi_frame_info.get());
309  ASSERT_TRUE(cfi_frame_info.get()
310              ->FindCallerRegs<u_int32_t>(current_registers, memory,
311                                          &caller_registers));
312  VerifyRegisters(__FILE__, __LINE__,
313                  expected_caller_registers, caller_registers);
314
315  frame.instruction = 0x3d5a;
316  current_registers["$esi"] = 0x6285f79aU;
317  cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
318  ASSERT_TRUE(cfi_frame_info.get());
319  ASSERT_TRUE(cfi_frame_info.get()
320              ->FindCallerRegs<u_int32_t>(current_registers, memory,
321                                          &caller_registers));
322  VerifyRegisters(__FILE__, __LINE__,
323                  expected_caller_registers, caller_registers);
324
325  frame.instruction = 0x3d84;
326  current_registers["$edi"] = 0x64061449U;
327  cfi_frame_info.reset(resolver.FindCFIFrameInfo(&frame));
328  ASSERT_TRUE(cfi_frame_info.get());
329  ASSERT_TRUE(cfi_frame_info.get()
330              ->FindCallerRegs<u_int32_t>(current_registers, memory,
331                                          &caller_registers));
332  VerifyRegisters(__FILE__, __LINE__,
333                  expected_caller_registers, caller_registers);
334
335  frame.instruction = 0x2900;
336  frame.module = &module1;
337  resolver.FillSourceLineInfo(&frame);
338  ASSERT_EQ(frame.function_name, string("PublicSymbol"));
339
340  frame.instruction = 0x4000;
341  frame.module = &module1;
342  resolver.FillSourceLineInfo(&frame);
343  ASSERT_EQ(frame.function_name, string("LargeFunction"));
344
345  frame.instruction = 0x2181;
346  frame.module = &module2;
347  resolver.FillSourceLineInfo(&frame);
348  ASSERT_EQ(frame.function_name, "Function2_2");
349  ASSERT_EQ(frame.function_base, 0x2170);
350  ASSERT_TRUE(frame.module);
351  ASSERT_EQ(frame.module->code_file(), "module2");
352  ASSERT_EQ(frame.source_file_name, "file2_2.cc");
353  ASSERT_EQ(frame.source_line, 21);
354  ASSERT_EQ(frame.source_line_base, 0x2180);
355  windows_frame_info.reset(resolver.FindWindowsFrameInfo(&frame));
356  ASSERT_TRUE(windows_frame_info.get());
357  ASSERT_EQ(windows_frame_info->type_, WindowsFrameInfo::STACK_INFO_FRAME_DATA);
358  ASSERT_EQ(windows_frame_info->prolog_size, 1);
359
360  frame.instruction = 0x216f;
361  resolver.FillSourceLineInfo(&frame);
362  ASSERT_EQ(frame.function_name, "Public2_1");
363
364  ClearSourceLineInfo(&frame);
365  frame.instruction = 0x219f;
366  frame.module = &module2;
367  resolver.FillSourceLineInfo(&frame);
368  ASSERT_TRUE(frame.function_name.empty());
369
370  frame.instruction = 0x21a0;
371  frame.module = &module2;
372  resolver.FillSourceLineInfo(&frame);
373  ASSERT_EQ(frame.function_name, "Public2_2");
374}
375
376TEST_F(TestBasicSourceLineResolver, TestInvalidLoads)
377{
378  TestCodeModule module3("module3");
379  ASSERT_FALSE(resolver.LoadModule(&module3,
380                                   testdata_dir + "/module3_bad.out"));
381  ASSERT_FALSE(resolver.HasModule(&module3));
382  TestCodeModule module4("module4");
383  ASSERT_FALSE(resolver.LoadModule(&module4,
384                                   testdata_dir + "/module4_bad.out"));
385  ASSERT_FALSE(resolver.HasModule(&module4));
386  TestCodeModule module5("module5");
387  ASSERT_FALSE(resolver.LoadModule(&module5,
388                                   testdata_dir + "/invalid-filename"));
389  ASSERT_FALSE(resolver.HasModule(&module5));
390  TestCodeModule invalidmodule("invalid-module");
391  ASSERT_FALSE(resolver.HasModule(&invalidmodule));
392}
393
394TEST_F(TestBasicSourceLineResolver, TestUnload)
395{
396  TestCodeModule module1("module1");
397  ASSERT_FALSE(resolver.HasModule(&module1));
398  ASSERT_TRUE(resolver.LoadModule(&module1, testdata_dir + "/module1.out"));
399  ASSERT_TRUE(resolver.HasModule(&module1));
400  resolver.UnloadModule(&module1);
401  ASSERT_FALSE(resolver.HasModule(&module1));
402  ASSERT_TRUE(resolver.LoadModule(&module1, testdata_dir + "/module1.out"));
403  ASSERT_TRUE(resolver.HasModule(&module1));
404}
405
406}  // namespace
407
408int main(int argc, char *argv[]) {
409  ::testing::InitGoogleTest(&argc, argv);
410  return RUN_ALL_TESTS();
411}