PageRenderTime 47ms CodeModel.GetById 12ms app.highlight 30ms RepoModel.GetById 1ms app.codeStats 1ms

/thirdparty/breakpad/processor/minidump_processor_unittest.cc

http://github.com/tomahawk-player/tomahawk
C++ | 380 lines | 287 code | 51 blank | 42 comment | 14 complexity | 4a40f345587807a1a2bff3d1da4f42f4 MD5 | raw file
  1// Copyright (c) 2006, 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// Unit test for MinidumpProcessor.  Uses a pre-generated minidump and
 31// corresponding symbol file, and checks the stack frames for correctness.
 32
 33#include <stdlib.h>
 34
 35#include <string>
 36#include <iostream>
 37#include <fstream>
 38#include <map>
 39#include <utility>
 40
 41#include "breakpad_googletest_includes.h"
 42#include "google_breakpad/processor/basic_source_line_resolver.h"
 43#include "google_breakpad/processor/call_stack.h"
 44#include "google_breakpad/processor/code_module.h"
 45#include "google_breakpad/processor/code_modules.h"
 46#include "google_breakpad/processor/minidump.h"
 47#include "google_breakpad/processor/minidump_processor.h"
 48#include "google_breakpad/processor/process_state.h"
 49#include "google_breakpad/processor/stack_frame.h"
 50#include "google_breakpad/processor/symbol_supplier.h"
 51#include "processor/logging.h"
 52#include "processor/scoped_ptr.h"
 53
 54using std::map;
 55
 56namespace google_breakpad {
 57class MockMinidump : public Minidump {
 58 public:
 59  MockMinidump() : Minidump("") {
 60  }
 61
 62  MOCK_METHOD0(Read, bool());
 63  MOCK_CONST_METHOD0(path, string());
 64  MOCK_CONST_METHOD0(header, const MDRawHeader*());
 65  MOCK_METHOD0(GetThreadList, MinidumpThreadList*());
 66};
 67}
 68
 69namespace {
 70
 71using google_breakpad::BasicSourceLineResolver;
 72using google_breakpad::CallStack;
 73using google_breakpad::CodeModule;
 74using google_breakpad::MinidumpProcessor;
 75using google_breakpad::MinidumpThreadList;
 76using google_breakpad::MinidumpThread;
 77using google_breakpad::MockMinidump;
 78using google_breakpad::ProcessState;
 79using google_breakpad::scoped_ptr;
 80using google_breakpad::SymbolSupplier;
 81using google_breakpad::SystemInfo;
 82using std::string;
 83using ::testing::_;
 84using ::testing::Mock;
 85using ::testing::Ne;
 86using ::testing::Property;
 87using ::testing::Return;
 88
 89static const char *kSystemInfoOS = "Windows NT";
 90static const char *kSystemInfoOSShort = "windows";
 91static const char *kSystemInfoOSVersion = "5.1.2600 Service Pack 2";
 92static const char *kSystemInfoCPU = "x86";
 93static const char *kSystemInfoCPUInfo =
 94    "GenuineIntel family 6 model 13 stepping 8";
 95
 96#define ASSERT_TRUE_ABORT(cond) \
 97  if (!(cond)) {                                                        \
 98    fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \
 99    abort(); \
100  }
101
102#define ASSERT_EQ_ABORT(e1, e2) ASSERT_TRUE_ABORT((e1) == (e2))
103
104class TestSymbolSupplier : public SymbolSupplier {
105 public:
106  TestSymbolSupplier() : interrupt_(false) {}
107
108  virtual SymbolResult GetSymbolFile(const CodeModule *module,
109                                     const SystemInfo *system_info,
110                                     string *symbol_file);
111
112  virtual SymbolResult GetSymbolFile(const CodeModule *module,
113                                     const SystemInfo *system_info,
114                                     string *symbol_file,
115                                     string *symbol_data);
116
117  virtual SymbolResult GetCStringSymbolData(const CodeModule *module,
118                                            const SystemInfo *system_info,
119                                            string *symbol_file,
120                                            char **symbol_data);
121
122  virtual void FreeSymbolData(const CodeModule *module);
123
124  // When set to true, causes the SymbolSupplier to return INTERRUPT
125  void set_interrupt(bool interrupt) { interrupt_ = interrupt; }
126
127 private:
128  bool interrupt_;
129  map<string, char *> memory_buffers_;
130};
131
132SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile(
133    const CodeModule *module,
134    const SystemInfo *system_info,
135    string *symbol_file) {
136  ASSERT_TRUE_ABORT(module);
137  ASSERT_TRUE_ABORT(system_info);
138  ASSERT_EQ_ABORT(system_info->cpu, kSystemInfoCPU);
139  ASSERT_EQ_ABORT(system_info->cpu_info, kSystemInfoCPUInfo);
140  ASSERT_EQ_ABORT(system_info->os, kSystemInfoOS);
141  ASSERT_EQ_ABORT(system_info->os_short, kSystemInfoOSShort);
142  ASSERT_EQ_ABORT(system_info->os_version, kSystemInfoOSVersion);
143
144  if (interrupt_) {
145    return INTERRUPT;
146  }
147
148  if (module && module->code_file() == "c:\\test_app.exe") {
149      *symbol_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
150                     "/src/processor/testdata/symbols/test_app.pdb/" +
151                     module->debug_identifier() +
152                     "/test_app.sym";
153    return FOUND;
154  }
155
156  return NOT_FOUND;
157}
158
159SymbolSupplier::SymbolResult TestSymbolSupplier::GetSymbolFile(
160    const CodeModule *module,
161    const SystemInfo *system_info,
162    string *symbol_file,
163    string *symbol_data) {
164  SymbolSupplier::SymbolResult s = GetSymbolFile(module, system_info,
165                                                 symbol_file);
166  if (s == FOUND) {
167    std::ifstream in(symbol_file->c_str());
168    std::getline(in, *symbol_data, std::string::traits_type::to_char_type(
169                     std::string::traits_type::eof()));
170    in.close();
171  }
172
173  return s;
174}
175
176SymbolSupplier::SymbolResult TestSymbolSupplier::GetCStringSymbolData(
177    const CodeModule *module,
178    const SystemInfo *system_info,
179    string *symbol_file,
180    char **symbol_data) {
181  string symbol_data_string;
182  SymbolSupplier::SymbolResult s = GetSymbolFile(module,
183                                                 system_info,
184                                                 symbol_file,
185                                                 &symbol_data_string);
186  if (s == FOUND) {
187    unsigned int size = symbol_data_string.size() + 1;
188    *symbol_data = new char[size];
189    if (*symbol_data == NULL) {
190      BPLOG(ERROR) << "Memory allocation failed for module: "
191                   << module->code_file() << " size: " << size;
192      return INTERRUPT;
193    }
194    strcpy(*symbol_data, symbol_data_string.c_str());
195    memory_buffers_.insert(make_pair(module->code_file(), *symbol_data));
196  }
197
198  return s;
199}
200
201void TestSymbolSupplier::FreeSymbolData(const CodeModule *module) {
202  map<string, char *>::iterator it = memory_buffers_.find(module->code_file());
203  if (it != memory_buffers_.end()) {
204    delete [] it->second;
205    memory_buffers_.erase(it);
206  }
207}
208
209// A mock symbol supplier that always returns NOT_FOUND; one current
210// use for testing the processor's caching of symbol lookups.
211class MockSymbolSupplier : public SymbolSupplier {
212 public:
213  MockSymbolSupplier() { }
214  MOCK_METHOD3(GetSymbolFile, SymbolResult(const CodeModule*,
215                                           const SystemInfo*,
216                                           string*));
217  MOCK_METHOD4(GetSymbolFile, SymbolResult(const CodeModule*,
218                                           const SystemInfo*,
219                                           string*,
220                                           string*));
221  MOCK_METHOD4(GetCStringSymbolData, SymbolResult(const CodeModule*,
222                                                  const SystemInfo*,
223                                                  string*,
224                                                  char**));
225  MOCK_METHOD1(FreeSymbolData, void(const CodeModule*));
226};
227
228class MinidumpProcessorTest : public ::testing::Test {
229};
230
231TEST_F(MinidumpProcessorTest, TestCorruptMinidumps) {
232  MockMinidump dump;
233  TestSymbolSupplier supplier;
234  BasicSourceLineResolver resolver;
235  MinidumpProcessor processor(&supplier, &resolver);
236  ProcessState state;
237
238  EXPECT_EQ(processor.Process("nonexistent minidump", &state),
239            google_breakpad::PROCESS_ERROR_MINIDUMP_NOT_FOUND);
240
241  EXPECT_CALL(dump, path()).WillRepeatedly(Return("mock minidump"));
242  EXPECT_CALL(dump, Read()).WillRepeatedly(Return(true));
243
244  MDRawHeader fakeHeader;
245  fakeHeader.time_date_stamp = 0;
246  EXPECT_CALL(dump, header()).WillOnce(Return((MDRawHeader*)NULL)).
247      WillRepeatedly(Return(&fakeHeader));
248  EXPECT_EQ(processor.Process(&dump, &state),
249            google_breakpad::PROCESS_ERROR_NO_MINIDUMP_HEADER);
250
251  EXPECT_CALL(dump, GetThreadList()).
252      WillOnce(Return((MinidumpThreadList*)NULL));
253  EXPECT_EQ(processor.Process(&dump, &state),
254            google_breakpad::PROCESS_ERROR_NO_THREAD_LIST);
255}
256
257// This test case verifies that the symbol supplier is only consulted
258// once per minidump per module.
259TEST_F(MinidumpProcessorTest, TestSymbolSupplierLookupCounts) {
260  MockSymbolSupplier supplier;
261  BasicSourceLineResolver resolver;
262  MinidumpProcessor processor(&supplier, &resolver);
263
264  string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
265                         "/src/processor/testdata/minidump2.dmp";
266  ProcessState state;
267  EXPECT_CALL(supplier, GetCStringSymbolData(
268      Property(&google_breakpad::CodeModule::code_file,
269               "c:\\test_app.exe"),
270      _, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND));
271  EXPECT_CALL(supplier, GetCStringSymbolData(
272      Property(&google_breakpad::CodeModule::code_file,
273               Ne("c:\\test_app.exe")),
274      _, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND));
275  ASSERT_EQ(processor.Process(minidump_file, &state),
276            google_breakpad::PROCESS_OK);
277
278  ASSERT_TRUE(Mock::VerifyAndClearExpectations(&supplier));
279
280  // We need to verify that across minidumps, the processor will refetch
281  // symbol files, even with the same symbol supplier.
282  EXPECT_CALL(supplier, GetCStringSymbolData(
283      Property(&google_breakpad::CodeModule::code_file,
284               "c:\\test_app.exe"),
285      _, _, _)).WillOnce(Return(SymbolSupplier::NOT_FOUND));
286  EXPECT_CALL(supplier, GetCStringSymbolData(
287      Property(&google_breakpad::CodeModule::code_file,
288               Ne("c:\\test_app.exe")),
289      _, _, _)).WillRepeatedly(Return(SymbolSupplier::NOT_FOUND));
290  ASSERT_EQ(processor.Process(minidump_file, &state),
291            google_breakpad::PROCESS_OK);
292}
293
294TEST_F(MinidumpProcessorTest, TestBasicProcessing) {
295  TestSymbolSupplier supplier;
296  BasicSourceLineResolver resolver;
297  MinidumpProcessor processor(&supplier, &resolver);
298
299  string minidump_file = string(getenv("srcdir") ? getenv("srcdir") : ".") +
300                         "/src/processor/testdata/minidump2.dmp";
301
302  ProcessState state;
303  ASSERT_EQ(processor.Process(minidump_file, &state),
304            google_breakpad::PROCESS_OK);
305  ASSERT_EQ(state.system_info()->os, kSystemInfoOS);
306  ASSERT_EQ(state.system_info()->os_short, kSystemInfoOSShort);
307  ASSERT_EQ(state.system_info()->os_version, kSystemInfoOSVersion);
308  ASSERT_EQ(state.system_info()->cpu, kSystemInfoCPU);
309  ASSERT_EQ(state.system_info()->cpu_info, kSystemInfoCPUInfo);
310  ASSERT_TRUE(state.crashed());
311  ASSERT_EQ(state.crash_reason(), "EXCEPTION_ACCESS_VIOLATION_WRITE");
312  ASSERT_EQ(state.crash_address(), 0x45U);
313  ASSERT_EQ(state.threads()->size(), size_t(1));
314  ASSERT_EQ(state.requesting_thread(), 0);
315
316  CallStack *stack = state.threads()->at(0);
317  ASSERT_TRUE(stack);
318  ASSERT_EQ(stack->frames()->size(), 4U);
319
320  ASSERT_TRUE(stack->frames()->at(0)->module);
321  ASSERT_EQ(stack->frames()->at(0)->module->base_address(), 0x400000U);
322  ASSERT_EQ(stack->frames()->at(0)->module->code_file(), "c:\\test_app.exe");
323  ASSERT_EQ(stack->frames()->at(0)->function_name,
324            "`anonymous namespace'::CrashFunction");
325  ASSERT_EQ(stack->frames()->at(0)->source_file_name, "c:\\test_app.cc");
326  ASSERT_EQ(stack->frames()->at(0)->source_line, 58);
327
328  ASSERT_TRUE(stack->frames()->at(1)->module);
329  ASSERT_EQ(stack->frames()->at(1)->module->base_address(), 0x400000U);
330  ASSERT_EQ(stack->frames()->at(1)->module->code_file(), "c:\\test_app.exe");
331  ASSERT_EQ(stack->frames()->at(1)->function_name, "main");
332  ASSERT_EQ(stack->frames()->at(1)->source_file_name, "c:\\test_app.cc");
333  ASSERT_EQ(stack->frames()->at(1)->source_line, 65);
334
335  // This comes from the CRT
336  ASSERT_TRUE(stack->frames()->at(2)->module);
337  ASSERT_EQ(stack->frames()->at(2)->module->base_address(), 0x400000U);
338  ASSERT_EQ(stack->frames()->at(2)->module->code_file(), "c:\\test_app.exe");
339  ASSERT_EQ(stack->frames()->at(2)->function_name, "__tmainCRTStartup");
340  ASSERT_EQ(stack->frames()->at(2)->source_file_name,
341            "f:\\sp\\vctools\\crt_bld\\self_x86\\crt\\src\\crt0.c");
342  ASSERT_EQ(stack->frames()->at(2)->source_line, 327);
343
344  // No debug info available for kernel32.dll
345  ASSERT_TRUE(stack->frames()->at(3)->module);
346  ASSERT_EQ(stack->frames()->at(3)->module->base_address(), 0x7c800000U);
347  ASSERT_EQ(stack->frames()->at(3)->module->code_file(),
348            "C:\\WINDOWS\\system32\\kernel32.dll");
349  ASSERT_TRUE(stack->frames()->at(3)->function_name.empty());
350  ASSERT_TRUE(stack->frames()->at(3)->source_file_name.empty());
351  ASSERT_EQ(stack->frames()->at(3)->source_line, 0);
352
353  ASSERT_EQ(state.modules()->module_count(), 13U);
354  ASSERT_TRUE(state.modules()->GetMainModule());
355  ASSERT_EQ(state.modules()->GetMainModule()->code_file(), "c:\\test_app.exe");
356  ASSERT_FALSE(state.modules()->GetModuleForAddress(0));
357  ASSERT_EQ(state.modules()->GetMainModule(),
358            state.modules()->GetModuleForAddress(0x400000));
359  ASSERT_EQ(state.modules()->GetModuleForAddress(0x7c801234)->debug_file(),
360            "kernel32.pdb");
361  ASSERT_EQ(state.modules()->GetModuleForAddress(0x77d43210)->version(),
362            "5.1.2600.2622");
363
364  // Test that disabled exploitability engine defaults to
365  // EXPLOITABILITY_NOT_ANALYZED.
366  ASSERT_EQ(google_breakpad::EXPLOITABILITY_NOT_ANALYZED,
367            state.exploitability());
368
369  // Test that the symbol supplier can interrupt processing
370  state.Clear();
371  supplier.set_interrupt(true);
372  ASSERT_EQ(processor.Process(minidump_file, &state),
373            google_breakpad::PROCESS_SYMBOL_SUPPLIER_INTERRUPTED);
374}
375}  // namespace
376
377int main(int argc, char *argv[]) {
378  ::testing::InitGoogleTest(&argc, argv);
379  return RUN_ALL_TESTS();
380}