Index: src/client/mac/crash_generation/crash_generation_server.cc |
=================================================================== |
new file mode 100644 |
--- /dev/null |
+++ b/src/client/mac/crash_generation/crash_generation_server.cc |
@@ -0,0 +1,157 @@ |
+// Copyright (c) 2010 Google Inc. |
+// All rights reserved. |
+// |
+// Redistribution and use in source and binary forms, with or without |
+// modification, are permitted provided that the following conditions are |
+// met: |
+// |
+// * Redistributions of source code must retain the above copyright |
+// notice, this list of conditions and the following disclaimer. |
+// * Redistributions in binary form must reproduce the above |
+// copyright notice, this list of conditions and the following disclaimer |
+// in the documentation and/or other materials provided with the |
+// distribution. |
+// * Neither the name of Google Inc. nor the names of its |
+// contributors may be used to endorse or promote products derived from |
+// this software without specific prior written permission. |
+// |
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
+ |
+#include "client/mac/crash_generation/crash_generation_server.h" |
+ |
+#include "client/mac/crash_generation/client_info.h" |
+#include "client/mac/handler/minidump_generator.h" |
+#include "common/mac/scoped_task_suspend-inl.h" |
+ |
+namespace google_breakpad { |
+ |
+CrashGenerationServer::CrashGenerationServer( |
+ const char *mach_port_name, |
+ OnClientDumpRequestCallback dump_callback, |
+ void *dump_context, |
+ OnClientExitingCallback exit_callback, |
+ void *exit_context, |
+ bool generate_dumps, |
+ const std::string &dump_path) |
+ : dump_callback_(dump_callback), |
+ dump_context_(dump_context), |
+ exit_callback_(exit_callback), |
+ exit_context_(exit_context), |
+ generate_dumps_(generate_dumps), |
+ dump_dir_(dump_path.empty() ? "/tmp" : dump_path), |
+ started_(false), |
+ receive_port_(mach_port_name), |
+ mach_port_name_(mach_port_name) { |
+} |
+ |
+CrashGenerationServer::~CrashGenerationServer() { |
+ if (started_) |
+ Stop(); |
+} |
+ |
+bool CrashGenerationServer::Start() { |
+ int thread_create_result = pthread_create(&server_thread_, NULL, |
+ &WaitForMessages, this); |
+ started_ = thread_create_result == 0; |
+ return started_; |
+} |
+ |
+bool CrashGenerationServer::Stop() { |
+ if (!started_) |
+ return false; |
+ |
+ // Send a quit message to the background thread, and then join it. |
+ MachPortSender sender(mach_port_name_.c_str()); |
+ MachSendMessage quit_message(kQuitMessage); |
+ const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000; |
+ kern_return_t result = sender.SendMessage(quit_message, kSendTimeoutMs); |
+ if (result == KERN_SUCCESS) { |
+ int thread_join_result = pthread_join(server_thread_, NULL); |
+ started_ = thread_join_result != 0; |
+ } |
+ |
+ return !started_; |
+} |
+ |
+// static |
+void *CrashGenerationServer::WaitForMessages(void *server) { |
+ CrashGenerationServer *self = |
+ reinterpret_cast<CrashGenerationServer*>(server); |
+ while (self->WaitForOneMessage()) {} |
+ return NULL; |
+} |
+ |
+bool CrashGenerationServer::WaitForOneMessage() { |
+ MachReceiveMessage message; |
+ kern_return_t result = receive_port_.WaitForMessage(&message, |
+ MACH_MSG_TIMEOUT_NONE); |
+ if (result == KERN_SUCCESS) { |
+ switch (message.GetMessageID()) { |
+ case kDumpRequestMessage: { |
+ ClientInfo &info = (ClientInfo &)*message.GetData(); |
+ |
+ mach_port_t remote_task = message.GetTranslatedPort(0); |
+ mach_port_t crashing_thread = message.GetTranslatedPort(1); |
+ mach_port_t handler_thread = message.GetTranslatedPort(2); |
+ mach_port_t ack_port = message.GetTranslatedPort(3); |
+ |
+ bool result; |
+ std::string dump_path; |
+ if (generate_dumps_) { |
+ ScopedTaskSuspend suspend(remote_task); |
+ |
+ MinidumpGenerator generator(remote_task, handler_thread); |
+ dump_path = generator.UniqueNameInDirectory(dump_dir_, NULL); |
+ |
+ if (info.exception_type && info.exception_code) { |
+ generator.SetExceptionInformation(info.exception_type, |
+ info.exception_code, |
+ info.exception_subcode, |
+ crashing_thread); |
+ } |
+ result = generator.Write(dump_path.c_str()); |
+ } else { |
+ result = true; |
+ } |
+ |
+ if (result && dump_callback_) { |
+ dump_callback_(dump_context_, info, dump_path); |
+ } |
+ |
+ // TODO(ted): support a way for the client to send additional data, perhaps |
Mark Mentovai
2010/08/13 20:07:28
80
|
+ // with a callback so users of the server can read the data themselves? |
+ |
+ if (ack_port != MACH_PORT_DEAD && ack_port != MACH_PORT_NULL) { |
+ MachPortSender sender(ack_port); |
+ MachSendMessage ack_message(kAcknowledgementMessage); |
+ const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000; |
+ |
+ sender.SendMessage(ack_message, kSendTimeoutMs); |
+ } |
+ |
+ if (exit_callback_) { |
+ exit_callback_(exit_context_, info); |
+ } |
+ break; |
+ } |
+ case kQuitMessage: |
+ return false; |
+ } |
+ } else { // result != KERN_SUCCESS |
+ mach_error("WaitForMessage", result); |
+ return false; |
+ } |
+ return true; |
+} |
+ |
+} // namespace google_breakpad |