/thirdparty/breakpad/client/windows/crash_generation/minidump_generator.cc

http://github.com/tomahawk-player/tomahawk · C++ · 309 lines · 218 code · 44 blank · 47 comment · 40 complexity · 7cb4cf9b8631197da001b5d2cc71aa4d MD5 · raw file

  1. // Copyright (c) 2008, 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 "client/windows/crash_generation/minidump_generator.h"
  30. #include <cassert>
  31. #include "client/windows/common/auto_critical_section.h"
  32. #include "common/windows/guid_string.h"
  33. using std::wstring;
  34. namespace google_breakpad {
  35. MinidumpGenerator::MinidumpGenerator(const wstring& dump_path)
  36. : dbghelp_module_(NULL),
  37. rpcrt4_module_(NULL),
  38. dump_path_(dump_path),
  39. write_dump_(NULL),
  40. create_uuid_(NULL) {
  41. InitializeCriticalSection(&module_load_sync_);
  42. InitializeCriticalSection(&get_proc_address_sync_);
  43. }
  44. MinidumpGenerator::~MinidumpGenerator() {
  45. if (dbghelp_module_) {
  46. FreeLibrary(dbghelp_module_);
  47. }
  48. if (rpcrt4_module_) {
  49. FreeLibrary(rpcrt4_module_);
  50. }
  51. DeleteCriticalSection(&get_proc_address_sync_);
  52. DeleteCriticalSection(&module_load_sync_);
  53. }
  54. bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
  55. DWORD process_id,
  56. DWORD thread_id,
  57. DWORD requesting_thread_id,
  58. EXCEPTION_POINTERS* exception_pointers,
  59. MDRawAssertionInfo* assert_info,
  60. MINIDUMP_TYPE dump_type,
  61. bool is_client_pointers,
  62. wstring* dump_path) {
  63. // Just call the full WriteMinidump with NULL as the full_dump_path.
  64. return this->WriteMinidump(process_handle, process_id, thread_id,
  65. requesting_thread_id, exception_pointers,
  66. assert_info, dump_type, is_client_pointers,
  67. dump_path, NULL);
  68. }
  69. bool MinidumpGenerator::WriteMinidump(HANDLE process_handle,
  70. DWORD process_id,
  71. DWORD thread_id,
  72. DWORD requesting_thread_id,
  73. EXCEPTION_POINTERS* exception_pointers,
  74. MDRawAssertionInfo* assert_info,
  75. MINIDUMP_TYPE dump_type,
  76. bool is_client_pointers,
  77. wstring* dump_path,
  78. wstring* full_dump_path) {
  79. MiniDumpWriteDumpType write_dump = GetWriteDump();
  80. if (!write_dump) {
  81. return false;
  82. }
  83. wstring dump_file_path;
  84. if (!GenerateDumpFilePath(&dump_file_path)) {
  85. return false;
  86. }
  87. // If the client requests a full memory dump, we will write a normal mini
  88. // dump and a full memory dump. Both dump files use the same uuid as file
  89. // name prefix.
  90. bool full_memory_dump = (dump_type & MiniDumpWithFullMemory) != 0;
  91. wstring full_dump_file_path;
  92. if (full_memory_dump) {
  93. full_dump_file_path.assign(dump_file_path);
  94. full_dump_file_path.resize(full_dump_file_path.size() - 4); // strip .dmp
  95. full_dump_file_path.append(TEXT("-full.dmp"));
  96. }
  97. HANDLE dump_file = CreateFile(dump_file_path.c_str(),
  98. GENERIC_WRITE,
  99. 0,
  100. NULL,
  101. CREATE_NEW,
  102. FILE_ATTRIBUTE_NORMAL,
  103. NULL);
  104. if (dump_file == INVALID_HANDLE_VALUE) {
  105. return false;
  106. }
  107. HANDLE full_dump_file = INVALID_HANDLE_VALUE;
  108. if (full_memory_dump) {
  109. full_dump_file = CreateFile(full_dump_file_path.c_str(),
  110. GENERIC_WRITE,
  111. 0,
  112. NULL,
  113. CREATE_NEW,
  114. FILE_ATTRIBUTE_NORMAL,
  115. NULL);
  116. if (full_dump_file == INVALID_HANDLE_VALUE) {
  117. CloseHandle(dump_file);
  118. return false;
  119. }
  120. }
  121. MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = NULL;
  122. MINIDUMP_EXCEPTION_INFORMATION dump_exception_info;
  123. // Setup the exception information object only if it's a dump
  124. // due to an exception.
  125. if (exception_pointers) {
  126. dump_exception_pointers = &dump_exception_info;
  127. dump_exception_info.ThreadId = thread_id;
  128. dump_exception_info.ExceptionPointers = exception_pointers;
  129. dump_exception_info.ClientPointers = is_client_pointers;
  130. }
  131. // Add an MDRawBreakpadInfo stream to the minidump, to provide additional
  132. // information about the exception handler to the Breakpad processor.
  133. // The information will help the processor determine which threads are
  134. // relevant. The Breakpad processor does not require this information but
  135. // can function better with Breakpad-generated dumps when it is present.
  136. // The native debugger is not harmed by the presence of this information.
  137. MDRawBreakpadInfo breakpad_info = {0};
  138. if (!is_client_pointers) {
  139. // Set the dump thread id and requesting thread id only in case of
  140. // in-process dump generation.
  141. breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID |
  142. MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID;
  143. breakpad_info.dump_thread_id = thread_id;
  144. breakpad_info.requesting_thread_id = requesting_thread_id;
  145. }
  146. // Leave room in user_stream_array for a possible assertion info stream.
  147. MINIDUMP_USER_STREAM user_stream_array[2];
  148. user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM;
  149. user_stream_array[0].BufferSize = sizeof(breakpad_info);
  150. user_stream_array[0].Buffer = &breakpad_info;
  151. MINIDUMP_USER_STREAM_INFORMATION user_streams;
  152. user_streams.UserStreamCount = 1;
  153. user_streams.UserStreamArray = user_stream_array;
  154. MDRawAssertionInfo* actual_assert_info = assert_info;
  155. MDRawAssertionInfo client_assert_info = {0};
  156. if (assert_info) {
  157. // If the assertion info object lives in the client process,
  158. // read the memory of the client process.
  159. if (is_client_pointers) {
  160. SIZE_T bytes_read = 0;
  161. if (!ReadProcessMemory(process_handle,
  162. assert_info,
  163. &client_assert_info,
  164. sizeof(client_assert_info),
  165. &bytes_read)) {
  166. CloseHandle(dump_file);
  167. if (full_dump_file != INVALID_HANDLE_VALUE)
  168. CloseHandle(full_dump_file);
  169. return false;
  170. }
  171. if (bytes_read != sizeof(client_assert_info)) {
  172. CloseHandle(dump_file);
  173. if (full_dump_file != INVALID_HANDLE_VALUE)
  174. CloseHandle(full_dump_file);
  175. return false;
  176. }
  177. actual_assert_info = &client_assert_info;
  178. }
  179. user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM;
  180. user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo);
  181. user_stream_array[1].Buffer = actual_assert_info;
  182. ++user_streams.UserStreamCount;
  183. }
  184. bool result_minidump = write_dump(
  185. process_handle,
  186. process_id,
  187. dump_file,
  188. static_cast<MINIDUMP_TYPE>((dump_type & (~MiniDumpWithFullMemory))
  189. | MiniDumpNormal),
  190. exception_pointers ? &dump_exception_info : NULL,
  191. &user_streams,
  192. NULL) != FALSE;
  193. bool result_full_memory = true;
  194. if (full_memory_dump) {
  195. result_full_memory = write_dump(
  196. process_handle,
  197. process_id,
  198. full_dump_file,
  199. static_cast<MINIDUMP_TYPE>(dump_type & (~MiniDumpNormal)),
  200. exception_pointers ? &dump_exception_info : NULL,
  201. &user_streams,
  202. NULL) != FALSE;
  203. }
  204. bool result = result_minidump && result_full_memory;
  205. CloseHandle(dump_file);
  206. if (full_dump_file != INVALID_HANDLE_VALUE)
  207. CloseHandle(full_dump_file);
  208. // Store the path of the dump file in the out parameter if dump generation
  209. // succeeded.
  210. if (result && dump_path) {
  211. *dump_path = dump_file_path;
  212. }
  213. if (result && full_memory_dump && full_dump_path) {
  214. *full_dump_path = full_dump_file_path;
  215. }
  216. return result;
  217. }
  218. HMODULE MinidumpGenerator::GetDbghelpModule() {
  219. AutoCriticalSection lock(&module_load_sync_);
  220. if (!dbghelp_module_) {
  221. dbghelp_module_ = LoadLibrary(TEXT("dbghelp.dll"));
  222. }
  223. return dbghelp_module_;
  224. }
  225. MinidumpGenerator::MiniDumpWriteDumpType MinidumpGenerator::GetWriteDump() {
  226. AutoCriticalSection lock(&get_proc_address_sync_);
  227. if (!write_dump_) {
  228. HMODULE module = GetDbghelpModule();
  229. if (module) {
  230. FARPROC proc = GetProcAddress(module, "MiniDumpWriteDump");
  231. write_dump_ = reinterpret_cast<MiniDumpWriteDumpType>(proc);
  232. }
  233. }
  234. return write_dump_;
  235. }
  236. HMODULE MinidumpGenerator::GetRpcrt4Module() {
  237. AutoCriticalSection lock(&module_load_sync_);
  238. if (!rpcrt4_module_) {
  239. rpcrt4_module_ = LoadLibrary(TEXT("rpcrt4.dll"));
  240. }
  241. return rpcrt4_module_;
  242. }
  243. MinidumpGenerator::UuidCreateType MinidumpGenerator::GetCreateUuid() {
  244. AutoCriticalSection lock(&module_load_sync_);
  245. if (!create_uuid_) {
  246. HMODULE module = GetRpcrt4Module();
  247. if (module) {
  248. FARPROC proc = GetProcAddress(module, "UuidCreate");
  249. create_uuid_ = reinterpret_cast<UuidCreateType>(proc);
  250. }
  251. }
  252. return create_uuid_;
  253. }
  254. bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) {
  255. UUID id = {0};
  256. UuidCreateType create_uuid = GetCreateUuid();
  257. if (!create_uuid) {
  258. return false;
  259. }
  260. create_uuid(&id);
  261. wstring id_str = GUIDString::GUIDToWString(&id);
  262. *file_path = dump_path_ + TEXT("\\") + id_str + TEXT(".dmp");
  263. return true;
  264. }
  265. } // namespace google_breakpad