/thirdparty/breakpad/client/mac/tests/minidump_generator_test.cc

http://github.com/tomahawk-player/tomahawk · C++ · 319 lines · 211 code · 49 blank · 59 comment · 7 complexity · fe8a020879168a2103877315244f8bac 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. // minidump_generator_test.cc: Unit tests for google_breakpad::MinidumpGenerator
  30. #include <AvailabilityMacros.h>
  31. #ifndef MAC_OS_X_VERSION_10_6
  32. #define MAC_OS_X_VERSION_10_6 1060
  33. #endif
  34. #include <sys/stat.h>
  35. #include <unistd.h>
  36. #include <string>
  37. #include <vector>
  38. #include "breakpad_googletest_includes.h"
  39. #include "client/mac/handler/minidump_generator.h"
  40. #include "client/mac/tests/spawn_child_process.h"
  41. #include "common/mac/MachIPC.h"
  42. #include "common/tests/auto_tempdir.h"
  43. #include "google_breakpad/processor/minidump.h"
  44. namespace google_breakpad {
  45. // This acts as the log sink for INFO logging from the processor
  46. // logging code. The logging output confuses XCode and makes it think
  47. // there are unit test failures. testlogging.h handles the overriding.
  48. std::ostringstream info_log;
  49. }
  50. namespace {
  51. using std::string;
  52. using std::vector;
  53. using google_breakpad::AutoTempDir;
  54. using google_breakpad::MinidumpGenerator;
  55. using google_breakpad::MachPortSender;
  56. using google_breakpad::MachReceiveMessage;
  57. using google_breakpad::MachSendMessage;
  58. using google_breakpad::Minidump;
  59. using google_breakpad::MinidumpContext;
  60. using google_breakpad::MinidumpException;
  61. using google_breakpad::MinidumpModule;
  62. using google_breakpad::MinidumpModuleList;
  63. using google_breakpad::MinidumpSystemInfo;
  64. using google_breakpad::MinidumpThread;
  65. using google_breakpad::MinidumpThreadList;
  66. using google_breakpad::ReceivePort;
  67. using testing::Test;
  68. using namespace google_breakpad_test;
  69. class MinidumpGeneratorTest : public Test {
  70. public:
  71. AutoTempDir tempDir;
  72. };
  73. static void *Junk(void* data) {
  74. bool* wait = reinterpret_cast<bool*>(data);
  75. while (!*wait) {
  76. usleep(10000);
  77. }
  78. return NULL;
  79. }
  80. TEST_F(MinidumpGeneratorTest, InProcess) {
  81. MinidumpGenerator generator;
  82. string dump_filename =
  83. MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
  84. // Run an extra thread since MinidumpGenerator assumes there
  85. // are 2 or more threads.
  86. pthread_t junk_thread;
  87. bool quit = false;
  88. ASSERT_EQ(0, pthread_create(&junk_thread, NULL, Junk, &quit));
  89. ASSERT_TRUE(generator.Write(dump_filename.c_str()));
  90. // Ensure that minidump file exists and is > 0 bytes.
  91. struct stat st;
  92. ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
  93. ASSERT_LT(0, st.st_size);
  94. // join the background thread
  95. quit = true;
  96. pthread_join(junk_thread, NULL);
  97. // Read the minidump, sanity check some data.
  98. Minidump minidump(dump_filename.c_str());
  99. ASSERT_TRUE(minidump.Read());
  100. MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
  101. ASSERT_TRUE(system_info);
  102. const MDRawSystemInfo* raw_info = system_info->system_info();
  103. ASSERT_TRUE(raw_info);
  104. EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
  105. MinidumpThreadList* thread_list = minidump.GetThreadList();
  106. ASSERT_TRUE(thread_list);
  107. ASSERT_EQ((unsigned int)1, thread_list->thread_count());
  108. MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
  109. ASSERT_TRUE(main_thread);
  110. MinidumpContext* context = main_thread->GetContext();
  111. ASSERT_TRUE(context);
  112. EXPECT_EQ(kNativeContext, context->GetContextCPU());
  113. MinidumpModuleList* module_list = minidump.GetModuleList();
  114. ASSERT_TRUE(module_list);
  115. const MinidumpModule* main_module = module_list->GetMainModule();
  116. ASSERT_TRUE(main_module);
  117. EXPECT_EQ(GetExecutablePath(), main_module->code_file());
  118. }
  119. TEST_F(MinidumpGeneratorTest, OutOfProcess) {
  120. const int kTimeoutMs = 2000;
  121. // Create a mach port to receive the child task on.
  122. char machPortName[128];
  123. sprintf(machPortName, "MinidumpGeneratorTest.OutOfProcess.%d", getpid());
  124. ReceivePort parent_recv_port(machPortName);
  125. // Give the child process a pipe to block on.
  126. int fds[2];
  127. ASSERT_EQ(0, pipe(fds));
  128. // Fork off a child process to dump.
  129. pid_t pid = fork();
  130. if (pid == 0) {
  131. // In the child process
  132. close(fds[1]);
  133. // Send parent process the task port.
  134. MachSendMessage child_message(0);
  135. child_message.AddDescriptor(mach_task_self());
  136. MachPortSender child_sender(machPortName);
  137. if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) {
  138. fprintf(stderr, "Error sending message from child process!\n");
  139. exit(1);
  140. }
  141. // Wait for the parent process.
  142. uint8_t data;
  143. read(fds[0], &data, 1);
  144. exit(0);
  145. }
  146. // In the parent process.
  147. ASSERT_NE(-1, pid);
  148. close(fds[0]);
  149. // Read the child's task port.
  150. MachReceiveMessage child_message;
  151. ASSERT_EQ(KERN_SUCCESS,
  152. parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
  153. mach_port_t child_task = child_message.GetTranslatedPort(0);
  154. ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
  155. // Write a minidump of the child process.
  156. MinidumpGenerator generator(child_task, MACH_PORT_NULL);
  157. string dump_filename =
  158. MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
  159. ASSERT_TRUE(generator.Write(dump_filename.c_str()));
  160. // Ensure that minidump file exists and is > 0 bytes.
  161. struct stat st;
  162. ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
  163. ASSERT_LT(0, st.st_size);
  164. // Unblock child process
  165. uint8_t data = 1;
  166. (void)write(fds[1], &data, 1);
  167. // Child process should have exited with a zero status.
  168. int ret;
  169. ASSERT_EQ(pid, waitpid(pid, &ret, 0));
  170. EXPECT_NE(0, WIFEXITED(ret));
  171. EXPECT_EQ(0, WEXITSTATUS(ret));
  172. // Read the minidump, sanity check some data.
  173. Minidump minidump(dump_filename.c_str());
  174. ASSERT_TRUE(minidump.Read());
  175. MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
  176. ASSERT_TRUE(system_info);
  177. const MDRawSystemInfo* raw_info = system_info->system_info();
  178. ASSERT_TRUE(raw_info);
  179. EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture);
  180. MinidumpThreadList* thread_list = minidump.GetThreadList();
  181. ASSERT_TRUE(thread_list);
  182. ASSERT_EQ((unsigned int)1, thread_list->thread_count());
  183. MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
  184. ASSERT_TRUE(main_thread);
  185. MinidumpContext* context = main_thread->GetContext();
  186. ASSERT_TRUE(context);
  187. EXPECT_EQ(kNativeContext, context->GetContextCPU());
  188. MinidumpModuleList* module_list = minidump.GetModuleList();
  189. ASSERT_TRUE(module_list);
  190. const MinidumpModule* main_module = module_list->GetMainModule();
  191. ASSERT_TRUE(main_module);
  192. EXPECT_EQ(GetExecutablePath(), main_module->code_file());
  193. }
  194. // This test fails on 10.5, but I don't have easy access to a 10.5 machine,
  195. // so it's simpler to just limit it to 10.6 for now.
  196. #if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \
  197. (defined(__x86_64__) || defined(__i386__))
  198. TEST_F(MinidumpGeneratorTest, CrossArchitectureDump) {
  199. const int kTimeoutMs = 5000;
  200. // Create a mach port to receive the child task on.
  201. char machPortName[128];
  202. sprintf(machPortName,
  203. "MinidumpGeneratorTest.CrossArchitectureDump.%d", getpid());
  204. ReceivePort parent_recv_port(machPortName);
  205. // Spawn a child process to dump.
  206. string helper_path = GetHelperPath();
  207. const char* argv[] = {
  208. helper_path.c_str(),
  209. machPortName,
  210. NULL
  211. };
  212. pid_t pid = spawn_child_process(argv);
  213. ASSERT_NE(-1, pid);
  214. // Read the child's task port.
  215. MachReceiveMessage child_message;
  216. ASSERT_EQ(KERN_SUCCESS,
  217. parent_recv_port.WaitForMessage(&child_message, kTimeoutMs));
  218. mach_port_t child_task = child_message.GetTranslatedPort(0);
  219. ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task);
  220. // Write a minidump of the child process.
  221. MinidumpGenerator generator(child_task, MACH_PORT_NULL);
  222. string dump_filename =
  223. MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL);
  224. ASSERT_TRUE(generator.Write(dump_filename.c_str()));
  225. // Ensure that minidump file exists and is > 0 bytes.
  226. struct stat st;
  227. ASSERT_EQ(0, stat(dump_filename.c_str(), &st));
  228. ASSERT_LT(0, st.st_size);
  229. // Kill child process.
  230. kill(pid, SIGKILL);
  231. int ret;
  232. ASSERT_EQ(pid, waitpid(pid, &ret, 0));
  233. const MDCPUArchitecture kExpectedArchitecture =
  234. #if defined(__x86_64__)
  235. MD_CPU_ARCHITECTURE_X86
  236. #elif defined(__i386__)
  237. MD_CPU_ARCHITECTURE_AMD64
  238. #endif
  239. ;
  240. const u_int32_t kExpectedContext =
  241. #if defined(__i386__)
  242. MD_CONTEXT_AMD64
  243. #elif defined(__x86_64__)
  244. MD_CONTEXT_X86
  245. #endif
  246. ;
  247. // Read the minidump, sanity check some data.
  248. Minidump minidump(dump_filename.c_str());
  249. ASSERT_TRUE(minidump.Read());
  250. MinidumpSystemInfo* system_info = minidump.GetSystemInfo();
  251. ASSERT_TRUE(system_info);
  252. const MDRawSystemInfo* raw_info = system_info->system_info();
  253. ASSERT_TRUE(raw_info);
  254. EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture);
  255. MinidumpThreadList* thread_list = minidump.GetThreadList();
  256. ASSERT_TRUE(thread_list);
  257. ASSERT_EQ((unsigned int)1, thread_list->thread_count());
  258. MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0);
  259. ASSERT_TRUE(main_thread);
  260. MinidumpContext* context = main_thread->GetContext();
  261. ASSERT_TRUE(context);
  262. EXPECT_EQ(kExpectedContext, context->GetContextCPU());
  263. MinidumpModuleList* module_list = minidump.GetModuleList();
  264. ASSERT_TRUE(module_list);
  265. const MinidumpModule* main_module = module_list->GetMainModule();
  266. ASSERT_TRUE(main_module);
  267. EXPECT_EQ(helper_path, main_module->code_file());
  268. }
  269. #endif // 10.6 && (x86-64 || i386)
  270. }