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