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

http://github.com/tomahawk-player/tomahawk · C++ · 345 lines · 193 code · 46 blank · 106 comment · 30 complexity · 3cd84c0cf2ce989b4f13f481144d7fbd 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/crash_generation_client.h"
  30. #include <cassert>
  31. #include <utility>
  32. #include "client/windows/common/ipc_protocol.h"
  33. namespace google_breakpad {
  34. const int kPipeBusyWaitTimeoutMs = 2000;
  35. #ifdef _DEBUG
  36. const DWORD kWaitForServerTimeoutMs = INFINITE;
  37. #else
  38. const DWORD kWaitForServerTimeoutMs = 15000;
  39. #endif
  40. const int kPipeConnectMaxAttempts = 2;
  41. const DWORD kPipeDesiredAccess = FILE_READ_DATA |
  42. FILE_WRITE_DATA |
  43. FILE_WRITE_ATTRIBUTES;
  44. const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION |
  45. SECURITY_SQOS_PRESENT;
  46. const DWORD kPipeMode = PIPE_READMODE_MESSAGE;
  47. const size_t kWaitEventCount = 2;
  48. // This function is orphan for production code. It can be used
  49. // for debugging to help repro some scenarios like the client
  50. // is slow in writing to the pipe after connecting, the client
  51. // is slow in reading from the pipe after writing, etc. The parameter
  52. // overlapped below is not used and it is present to match the signature
  53. // of this function to TransactNamedPipe Win32 API. Uncomment if needed
  54. // for debugging.
  55. /**
  56. static bool TransactNamedPipeDebugHelper(HANDLE pipe,
  57. const void* in_buffer,
  58. DWORD in_size,
  59. void* out_buffer,
  60. DWORD out_size,
  61. DWORD* bytes_count,
  62. LPOVERLAPPED) {
  63. // Uncomment the next sleep to create a gap before writing
  64. // to pipe.
  65. // Sleep(5000);
  66. if (!WriteFile(pipe,
  67. in_buffer,
  68. in_size,
  69. bytes_count,
  70. NULL)) {
  71. return false;
  72. }
  73. // Uncomment the next sleep to create a gap between write
  74. // and read.
  75. // Sleep(5000);
  76. return ReadFile(pipe, out_buffer, out_size, bytes_count, NULL) != FALSE;
  77. }
  78. **/
  79. CrashGenerationClient::CrashGenerationClient(
  80. const wchar_t* pipe_name,
  81. MINIDUMP_TYPE dump_type,
  82. const CustomClientInfo* custom_info)
  83. : pipe_name_(pipe_name),
  84. dump_type_(dump_type),
  85. thread_id_(0),
  86. server_process_id_(0),
  87. crash_event_(NULL),
  88. crash_generated_(NULL),
  89. server_alive_(NULL),
  90. exception_pointers_(NULL),
  91. custom_info_() {
  92. memset(&assert_info_, 0, sizeof(assert_info_));
  93. if (custom_info) {
  94. custom_info_ = *custom_info;
  95. }
  96. }
  97. CrashGenerationClient::~CrashGenerationClient() {
  98. if (crash_event_) {
  99. CloseHandle(crash_event_);
  100. }
  101. if (crash_generated_) {
  102. CloseHandle(crash_generated_);
  103. }
  104. if (server_alive_) {
  105. CloseHandle(server_alive_);
  106. }
  107. }
  108. // Performs the registration step with the server process.
  109. // The registration step involves communicating with the server
  110. // via a named pipe. The client sends the following pieces of
  111. // data to the server:
  112. //
  113. // * Message tag indicating the client is requesting registration.
  114. // * Process id of the client process.
  115. // * Address of a DWORD variable in the client address space
  116. // that will contain the thread id of the client thread that
  117. // caused the crash.
  118. // * Address of a EXCEPTION_POINTERS* variable in the client
  119. // address space that will point to an instance of EXCEPTION_POINTERS
  120. // when the crash happens.
  121. // * Address of an instance of MDRawAssertionInfo that will contain
  122. // relevant information in case of non-exception crashes like assertion
  123. // failures and pure calls.
  124. //
  125. // In return the client expects the following information from the server:
  126. //
  127. // * Message tag indicating successful registration.
  128. // * Server process id.
  129. // * Handle to an object that client can signal to request dump
  130. // generation from the server.
  131. // * Handle to an object that client can wait on after requesting
  132. // dump generation for the server to finish dump generation.
  133. // * Handle to a mutex object that client can wait on to make sure
  134. // server is still alive.
  135. //
  136. // If any step of the expected behavior mentioned above fails, the
  137. // registration step is not considered successful and hence out-of-process
  138. // dump generation service is not available.
  139. //
  140. // Returns true if the registration is successful; false otherwise.
  141. bool CrashGenerationClient::Register() {
  142. HANDLE pipe = ConnectToServer();
  143. if (!pipe) {
  144. return false;
  145. }
  146. bool success = RegisterClient(pipe);
  147. CloseHandle(pipe);
  148. return success;
  149. }
  150. bool CrashGenerationClient::RequestUpload(DWORD crash_id) {
  151. HANDLE pipe = ConnectToServer();
  152. if (!pipe) {
  153. return false;
  154. }
  155. CustomClientInfo custom_info = {NULL, 0};
  156. ProtocolMessage msg(MESSAGE_TAG_UPLOAD_REQUEST, crash_id,
  157. static_cast<MINIDUMP_TYPE>(NULL), NULL, NULL, NULL,
  158. custom_info, NULL, NULL, NULL);
  159. DWORD bytes_count = 0;
  160. bool success = WriteFile(pipe, &msg, sizeof(msg), &bytes_count, NULL) != 0;
  161. CloseHandle(pipe);
  162. return success;
  163. }
  164. HANDLE CrashGenerationClient::ConnectToServer() {
  165. HANDLE pipe = ConnectToPipe(pipe_name_.c_str(),
  166. kPipeDesiredAccess,
  167. kPipeFlagsAndAttributes);
  168. if (!pipe) {
  169. return NULL;
  170. }
  171. DWORD mode = kPipeMode;
  172. if (!SetNamedPipeHandleState(pipe, &mode, NULL, NULL)) {
  173. CloseHandle(pipe);
  174. pipe = NULL;
  175. }
  176. return pipe;
  177. }
  178. bool CrashGenerationClient::RegisterClient(HANDLE pipe) {
  179. ProtocolMessage msg(MESSAGE_TAG_REGISTRATION_REQUEST,
  180. GetCurrentProcessId(),
  181. dump_type_,
  182. &thread_id_,
  183. &exception_pointers_,
  184. &assert_info_,
  185. custom_info_,
  186. NULL,
  187. NULL,
  188. NULL);
  189. ProtocolMessage reply;
  190. DWORD bytes_count = 0;
  191. // The call to TransactNamedPipe below can be changed to a call
  192. // to TransactNamedPipeDebugHelper to help repro some scenarios.
  193. // For details see comments for TransactNamedPipeDebugHelper.
  194. if (!TransactNamedPipe(pipe,
  195. &msg,
  196. sizeof(msg),
  197. &reply,
  198. sizeof(ProtocolMessage),
  199. &bytes_count,
  200. NULL)) {
  201. return false;
  202. }
  203. if (!ValidateResponse(reply)) {
  204. return false;
  205. }
  206. ProtocolMessage ack_msg;
  207. ack_msg.tag = MESSAGE_TAG_REGISTRATION_ACK;
  208. if (!WriteFile(pipe, &ack_msg, sizeof(ack_msg), &bytes_count, NULL)) {
  209. return false;
  210. }
  211. crash_event_ = reply.dump_request_handle;
  212. crash_generated_ = reply.dump_generated_handle;
  213. server_alive_ = reply.server_alive_handle;
  214. server_process_id_ = reply.id;
  215. return true;
  216. }
  217. HANDLE CrashGenerationClient::ConnectToPipe(const wchar_t* pipe_name,
  218. DWORD pipe_access,
  219. DWORD flags_attrs) {
  220. for (int i = 0; i < kPipeConnectMaxAttempts; ++i) {
  221. HANDLE pipe = CreateFile(pipe_name,
  222. pipe_access,
  223. 0,
  224. NULL,
  225. OPEN_EXISTING,
  226. flags_attrs,
  227. NULL);
  228. if (pipe != INVALID_HANDLE_VALUE) {
  229. return pipe;
  230. }
  231. // Cannot continue retrying if error is something other than
  232. // ERROR_PIPE_BUSY.
  233. if (GetLastError() != ERROR_PIPE_BUSY) {
  234. break;
  235. }
  236. // Cannot continue retrying if wait on pipe fails.
  237. if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) {
  238. break;
  239. }
  240. }
  241. return NULL;
  242. }
  243. bool CrashGenerationClient::ValidateResponse(
  244. const ProtocolMessage& msg) const {
  245. return (msg.tag == MESSAGE_TAG_REGISTRATION_RESPONSE) &&
  246. (msg.id != 0) &&
  247. (msg.dump_request_handle != NULL) &&
  248. (msg.dump_generated_handle != NULL) &&
  249. (msg.server_alive_handle != NULL);
  250. }
  251. bool CrashGenerationClient::IsRegistered() const {
  252. return crash_event_ != NULL;
  253. }
  254. bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info,
  255. MDRawAssertionInfo* assert_info) {
  256. if (!IsRegistered()) {
  257. return false;
  258. }
  259. exception_pointers_ = ex_info;
  260. thread_id_ = GetCurrentThreadId();
  261. if (assert_info) {
  262. memcpy(&assert_info_, assert_info, sizeof(assert_info_));
  263. } else {
  264. memset(&assert_info_, 0, sizeof(assert_info_));
  265. }
  266. return SignalCrashEventAndWait();
  267. }
  268. bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) {
  269. return RequestDump(ex_info, NULL);
  270. }
  271. bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) {
  272. return RequestDump(NULL, assert_info);
  273. }
  274. bool CrashGenerationClient::SignalCrashEventAndWait() {
  275. assert(crash_event_);
  276. assert(crash_generated_);
  277. assert(server_alive_);
  278. // Reset the dump generated event before signaling the crash
  279. // event so that the server can set the dump generated event
  280. // once it is done generating the event.
  281. if (!ResetEvent(crash_generated_)) {
  282. return false;
  283. }
  284. if (!SetEvent(crash_event_)) {
  285. return false;
  286. }
  287. HANDLE wait_handles[kWaitEventCount] = {crash_generated_, server_alive_};
  288. DWORD result = WaitForMultipleObjects(kWaitEventCount,
  289. wait_handles,
  290. FALSE,
  291. kWaitForServerTimeoutMs);
  292. // Crash dump was successfully generated only if the server
  293. // signaled the crash generated event.
  294. return result == WAIT_OBJECT_0;
  295. }
  296. } // namespace google_breakpad