/thirdparty/breakpad/client/linux/minidump_writer/minidump_writer_unittest.cc

http://github.com/tomahawk-player/tomahawk · C++ · 390 lines · 268 code · 51 blank · 71 comment · 17 complexity · 9fff6321e2071169d5f183d46529e400 MD5 · raw file

  1. // Copyright (c) 2011 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. #include <fcntl.h>
  30. #include <sys/poll.h>
  31. #include <sys/stat.h>
  32. #include <sys/syscall.h>
  33. #include <sys/types.h>
  34. #include <unistd.h>
  35. #include <string>
  36. #include "breakpad_googletest_includes.h"
  37. #include "client/linux/handler/exception_handler.h"
  38. #include "client/linux/minidump_writer/linux_dumper.h"
  39. #include "client/linux/minidump_writer/minidump_writer.h"
  40. #include "common/linux/eintr_wrapper.h"
  41. #include "common/linux/file_id.h"
  42. #include "common/linux/safe_readlink.h"
  43. #include "common/tests/auto_tempdir.h"
  44. #include "google_breakpad/processor/minidump.h"
  45. using namespace google_breakpad;
  46. // Length of a formatted GUID string =
  47. // sizeof(MDGUID) * 2 + 4 (for dashes) + 1 (null terminator)
  48. const int kGUIDStringSize = 37;
  49. namespace {
  50. typedef testing::Test MinidumpWriterTest;
  51. }
  52. TEST(MinidumpWriterTest, Setup) {
  53. int fds[2];
  54. ASSERT_NE(-1, pipe(fds));
  55. const pid_t child = fork();
  56. if (child == 0) {
  57. close(fds[1]);
  58. char b;
  59. HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
  60. close(fds[0]);
  61. syscall(__NR_exit);
  62. }
  63. close(fds[0]);
  64. ExceptionHandler::CrashContext context;
  65. memset(&context, 0, sizeof(context));
  66. AutoTempDir temp_dir;
  67. std::string templ = temp_dir.path() + "/minidump-writer-unittest";
  68. // Set a non-zero tid to avoid tripping asserts.
  69. context.tid = 1;
  70. ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context)));
  71. struct stat st;
  72. ASSERT_EQ(stat(templ.c_str(), &st), 0);
  73. ASSERT_GT(st.st_size, 0u);
  74. close(fds[1]);
  75. }
  76. // Test that mapping info can be specified when writing a minidump,
  77. // and that it ends up in the module list of the minidump.
  78. TEST(MinidumpWriterTest, MappingInfo) {
  79. int fds[2];
  80. ASSERT_NE(-1, pipe(fds));
  81. // These are defined here so the parent can use them to check the
  82. // data from the minidump afterwards.
  83. const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
  84. const char* kMemoryName = "a fake module";
  85. const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
  86. 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
  87. 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
  88. };
  89. char module_identifier_buffer[kGUIDStringSize];
  90. FileID::ConvertIdentifierToString(kModuleGUID,
  91. module_identifier_buffer,
  92. sizeof(module_identifier_buffer));
  93. string module_identifier(module_identifier_buffer);
  94. // Strip out dashes
  95. size_t pos;
  96. while ((pos = module_identifier.find('-')) != string::npos) {
  97. module_identifier.erase(pos, 1);
  98. }
  99. // And append a zero, because module IDs include an "age" field
  100. // which is always zero on Linux.
  101. module_identifier += "0";
  102. // Get some memory.
  103. char* memory =
  104. reinterpret_cast<char*>(mmap(NULL,
  105. kMemorySize,
  106. PROT_READ | PROT_WRITE,
  107. MAP_PRIVATE | MAP_ANON,
  108. -1,
  109. 0));
  110. const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
  111. ASSERT_TRUE(memory);
  112. const pid_t child = fork();
  113. if (child == 0) {
  114. close(fds[1]);
  115. char b;
  116. HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
  117. close(fds[0]);
  118. syscall(__NR_exit);
  119. }
  120. close(fds[0]);
  121. ExceptionHandler::CrashContext context;
  122. memset(&context, 0, sizeof(context));
  123. context.tid = 1;
  124. AutoTempDir temp_dir;
  125. std::string templ = temp_dir.path() + "/minidump-writer-unittest";
  126. // Add information about the mapped memory.
  127. MappingInfo info;
  128. info.start_addr = kMemoryAddress;
  129. info.size = kMemorySize;
  130. info.offset = 0;
  131. strcpy(info.name, kMemoryName);
  132. MappingList mappings;
  133. MappingEntry mapping;
  134. mapping.first = info;
  135. memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
  136. mappings.push_back(mapping);
  137. ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context),
  138. mappings));
  139. // Read the minidump. Load the module list, and ensure that
  140. // the mmap'ed |memory| is listed with the given module name
  141. // and debug ID.
  142. Minidump minidump(templ.c_str());
  143. ASSERT_TRUE(minidump.Read());
  144. MinidumpModuleList* module_list = minidump.GetModuleList();
  145. ASSERT_TRUE(module_list);
  146. const MinidumpModule* module =
  147. module_list->GetModuleForAddress(kMemoryAddress);
  148. ASSERT_TRUE(module);
  149. EXPECT_EQ(kMemoryAddress, module->base_address());
  150. EXPECT_EQ(kMemorySize, module->size());
  151. EXPECT_EQ(kMemoryName, module->code_file());
  152. EXPECT_EQ(module_identifier, module->debug_identifier());
  153. close(fds[1]);
  154. }
  155. // Test that mapping info can be specified, and that it overrides
  156. // existing mappings that are wholly contained within the specified
  157. // range.
  158. TEST(MinidumpWriterTest, MappingInfoContained) {
  159. int fds[2];
  160. ASSERT_NE(-1, pipe(fds));
  161. // These are defined here so the parent can use them to check the
  162. // data from the minidump afterwards.
  163. const u_int32_t kMemorySize = sysconf(_SC_PAGESIZE);
  164. const char* kMemoryName = "a fake module";
  165. const u_int8_t kModuleGUID[sizeof(MDGUID)] = {
  166. 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
  167. 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF
  168. };
  169. char module_identifier_buffer[kGUIDStringSize];
  170. FileID::ConvertIdentifierToString(kModuleGUID,
  171. module_identifier_buffer,
  172. sizeof(module_identifier_buffer));
  173. string module_identifier(module_identifier_buffer);
  174. // Strip out dashes
  175. size_t pos;
  176. while ((pos = module_identifier.find('-')) != string::npos) {
  177. module_identifier.erase(pos, 1);
  178. }
  179. // And append a zero, because module IDs include an "age" field
  180. // which is always zero on Linux.
  181. module_identifier += "0";
  182. // mmap a file
  183. AutoTempDir temp_dir;
  184. std::string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp";
  185. int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0);
  186. ASSERT_NE(-1, fd);
  187. unlink(tempfile.c_str());
  188. // fill with zeros
  189. char buffer[kMemorySize];
  190. memset(buffer, 0, kMemorySize);
  191. ASSERT_EQ(kMemorySize, write(fd, buffer, kMemorySize));
  192. lseek(fd, 0, SEEK_SET);
  193. char* memory =
  194. reinterpret_cast<char*>(mmap(NULL,
  195. kMemorySize,
  196. PROT_READ | PROT_WRITE,
  197. MAP_PRIVATE,
  198. fd,
  199. 0));
  200. const uintptr_t kMemoryAddress = reinterpret_cast<uintptr_t>(memory);
  201. ASSERT_TRUE(memory);
  202. close(fd);
  203. const pid_t child = fork();
  204. if (child == 0) {
  205. close(fds[1]);
  206. char b;
  207. HANDLE_EINTR(read(fds[0], &b, sizeof(b)));
  208. close(fds[0]);
  209. syscall(__NR_exit);
  210. }
  211. close(fds[0]);
  212. ExceptionHandler::CrashContext context;
  213. memset(&context, 0, sizeof(context));
  214. context.tid = 1;
  215. std::string dumpfile = temp_dir.path() + "/minidump-writer-unittest";
  216. // Add information about the mapped memory. Report it as being larger than
  217. // it actually is.
  218. MappingInfo info;
  219. info.start_addr = kMemoryAddress - kMemorySize;
  220. info.size = kMemorySize * 3;
  221. info.offset = 0;
  222. strcpy(info.name, kMemoryName);
  223. MappingList mappings;
  224. MappingEntry mapping;
  225. mapping.first = info;
  226. memcpy(mapping.second, kModuleGUID, sizeof(MDGUID));
  227. mappings.push_back(mapping);
  228. ASSERT_TRUE(
  229. WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context),
  230. mappings));
  231. // Read the minidump. Load the module list, and ensure that
  232. // the mmap'ed |memory| is listed with the given module name
  233. // and debug ID.
  234. Minidump minidump(dumpfile.c_str());
  235. ASSERT_TRUE(minidump.Read());
  236. MinidumpModuleList* module_list = minidump.GetModuleList();
  237. ASSERT_TRUE(module_list);
  238. const MinidumpModule* module =
  239. module_list->GetModuleForAddress(kMemoryAddress);
  240. ASSERT_TRUE(module);
  241. EXPECT_EQ(info.start_addr, module->base_address());
  242. EXPECT_EQ(info.size, module->size());
  243. EXPECT_EQ(kMemoryName, module->code_file());
  244. EXPECT_EQ(module_identifier, module->debug_identifier());
  245. close(fds[1]);
  246. }
  247. TEST(MinidumpWriterTest, DeletedBinary) {
  248. static const int kNumberOfThreadsInHelperProgram = 1;
  249. char kNumberOfThreadsArgument[2];
  250. sprintf(kNumberOfThreadsArgument, "%d", kNumberOfThreadsInHelperProgram);
  251. // Locate helper binary next to the current binary.
  252. char self_path[PATH_MAX];
  253. if (!SafeReadLink("/proc/self/exe", self_path)) {
  254. FAIL() << "readlink failed";
  255. exit(1);
  256. }
  257. string helper_path(self_path);
  258. size_t pos = helper_path.rfind('/');
  259. if (pos == string::npos) {
  260. FAIL() << "no trailing slash in path: " << helper_path;
  261. exit(1);
  262. }
  263. helper_path.erase(pos + 1);
  264. helper_path += "linux_dumper_unittest_helper";
  265. // Copy binary to a temp file.
  266. AutoTempDir temp_dir;
  267. std::string binpath = temp_dir.path() + "/linux-dumper-unittest-helper";
  268. char cmdline[2 * PATH_MAX];
  269. sprintf(cmdline, "/bin/cp \"%s\" \"%s\"", helper_path.c_str(),
  270. binpath.c_str());
  271. ASSERT_EQ(0, system(cmdline));
  272. ASSERT_EQ(0, chmod(binpath.c_str(), 0755));
  273. int fds[2];
  274. ASSERT_NE(-1, pipe(fds));
  275. pid_t child_pid = fork();
  276. if (child_pid == 0) {
  277. // In child process.
  278. close(fds[0]);
  279. // Pass the pipe fd and the number of threads as arguments.
  280. char pipe_fd_string[8];
  281. sprintf(pipe_fd_string, "%d", fds[1]);
  282. execl(binpath.c_str(),
  283. binpath.c_str(),
  284. pipe_fd_string,
  285. kNumberOfThreadsArgument,
  286. NULL);
  287. }
  288. close(fds[1]);
  289. // Wait for the child process to signal that it's ready.
  290. struct pollfd pfd;
  291. memset(&pfd, 0, sizeof(pfd));
  292. pfd.fd = fds[0];
  293. pfd.events = POLLIN | POLLERR;
  294. const int r = HANDLE_EINTR(poll(&pfd, 1, 1000));
  295. ASSERT_EQ(1, r);
  296. ASSERT_TRUE(pfd.revents & POLLIN);
  297. uint8_t junk;
  298. const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk)));
  299. ASSERT_EQ(sizeof(junk), nr);
  300. close(fds[0]);
  301. // Child is ready now.
  302. // Unlink the test binary.
  303. unlink(binpath.c_str());
  304. ExceptionHandler::CrashContext context;
  305. memset(&context, 0, sizeof(context));
  306. std::string templ = temp_dir.path() + "/minidump-writer-unittest";
  307. // Set a non-zero tid to avoid tripping asserts.
  308. context.tid = 1;
  309. ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context,
  310. sizeof(context)));
  311. kill(child_pid, SIGKILL);
  312. struct stat st;
  313. ASSERT_EQ(stat(templ.c_str(), &st), 0);
  314. ASSERT_GT(st.st_size, 0u);
  315. Minidump minidump(templ.c_str());
  316. ASSERT_TRUE(minidump.Read());
  317. // Check that the main module filename is correct.
  318. MinidumpModuleList* module_list = minidump.GetModuleList();
  319. ASSERT_TRUE(module_list);
  320. const MinidumpModule* module = module_list->GetMainModule();
  321. EXPECT_STREQ(binpath.c_str(), module->code_file().c_str());
  322. // Check that the file ID is correct.
  323. FileID fileid(helper_path.c_str());
  324. uint8_t identifier[sizeof(MDGUID)];
  325. EXPECT_TRUE(fileid.ElfFileIdentifier(identifier));
  326. char identifier_string[kGUIDStringSize];
  327. FileID::ConvertIdentifierToString(identifier,
  328. identifier_string,
  329. kGUIDStringSize);
  330. string module_identifier(identifier_string);
  331. // Strip out dashes
  332. while ((pos = module_identifier.find('-')) != string::npos) {
  333. module_identifier.erase(pos, 1);
  334. }
  335. // And append a zero, because module IDs include an "age" field
  336. // which is always zero on Linux.
  337. module_identifier += "0";
  338. EXPECT_EQ(module_identifier, module->debug_identifier());
  339. }