PageRenderTime 55ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/toolkit/crashreporter/google-breakpad/src/client/mac/crash_generation/Inspector.mm

https://github.com/rillian/firefox
Objective C++ | 362 lines | 213 code | 64 blank | 85 comment | 41 complexity | 5b06a96b80b279665e4cd4abfc15f787 MD5 | raw file
  1. // Copyright (c) 2007, 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. //
  30. // Utility that can inspect another process and write a crash dump
  31. #include <cstdio>
  32. #include <iostream>
  33. #include <servers/bootstrap.h>
  34. #include <stdio.h>
  35. #include <string.h>
  36. #include <string>
  37. #import "client/mac/crash_generation/Inspector.h"
  38. #import "client/mac/Framework/Breakpad.h"
  39. #import "client/mac/handler/minidump_generator.h"
  40. #import "common/mac/MachIPC.h"
  41. #include "common/mac/bootstrap_compat.h"
  42. #include "common/mac/launch_reporter.h"
  43. #import "GTMDefines.h"
  44. #import <Foundation/Foundation.h>
  45. namespace google_breakpad {
  46. //=============================================================================
  47. void Inspector::Inspect(const char *receive_port_name) {
  48. kern_return_t result = ResetBootstrapPort();
  49. if (result != KERN_SUCCESS) {
  50. return;
  51. }
  52. result = ServiceCheckIn(receive_port_name);
  53. if (result == KERN_SUCCESS) {
  54. result = ReadMessages();
  55. if (result == KERN_SUCCESS) {
  56. // Inspect the task and write a minidump file.
  57. bool wrote_minidump = InspectTask();
  58. // Send acknowledgement to the crashed process that the inspection
  59. // has finished. It will then be able to cleanly exit.
  60. // The return value is ignored because failure isn't fatal. If the process
  61. // didn't get the message there's nothing we can do, and we still want to
  62. // send the report.
  63. SendAcknowledgement();
  64. if (wrote_minidump) {
  65. // Ask the user if he wants to upload the crash report to a server,
  66. // and do so if he agrees.
  67. LaunchReporter(
  68. config_params_.GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION),
  69. config_file_.GetFilePath());
  70. } else {
  71. fprintf(stderr, "Inspection of crashed process failed\n");
  72. }
  73. // Now that we're done reading messages, cleanup the service, but only
  74. // if there was an actual exception
  75. // Otherwise, it means the dump was generated on demand and the process
  76. // lives on, and we might be needed again in the future.
  77. if (exception_code_) {
  78. ServiceCheckOut(receive_port_name);
  79. }
  80. } else {
  81. PRINT_MACH_RESULT(result, "Inspector: WaitForMessage()");
  82. }
  83. }
  84. }
  85. //=============================================================================
  86. kern_return_t Inspector::ResetBootstrapPort() {
  87. // A reasonable default, in case anything fails.
  88. bootstrap_subset_port_ = bootstrap_port;
  89. mach_port_t self_task = mach_task_self();
  90. kern_return_t kr = task_get_bootstrap_port(self_task,
  91. &bootstrap_subset_port_);
  92. if (kr != KERN_SUCCESS) {
  93. NSLog(@"ResetBootstrapPort: task_get_bootstrap_port failed: %s (%d)",
  94. mach_error_string(kr), kr);
  95. return kr;
  96. }
  97. mach_port_t bootstrap_parent_port;
  98. kr = bootstrap_look_up(bootstrap_subset_port_,
  99. const_cast<char*>(BREAKPAD_BOOTSTRAP_PARENT_PORT),
  100. &bootstrap_parent_port);
  101. if (kr != BOOTSTRAP_SUCCESS) {
  102. NSLog(@"ResetBootstrapPort: bootstrap_look_up failed: %s (%d)",
  103. #if defined(MAC_OS_X_VERSION_10_5) && \
  104. MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5
  105. bootstrap_strerror(kr),
  106. #else
  107. mach_error_string(kr),
  108. #endif
  109. kr);
  110. return kr;
  111. }
  112. kr = task_set_bootstrap_port(self_task, bootstrap_parent_port);
  113. if (kr != KERN_SUCCESS) {
  114. NSLog(@"ResetBootstrapPort: task_set_bootstrap_port failed: %s (%d)",
  115. mach_error_string(kr), kr);
  116. return kr;
  117. }
  118. // Some things access the bootstrap port through this global variable
  119. // instead of calling task_get_bootstrap_port.
  120. bootstrap_port = bootstrap_parent_port;
  121. return KERN_SUCCESS;
  122. }
  123. //=============================================================================
  124. kern_return_t Inspector::ServiceCheckIn(const char *receive_port_name) {
  125. // We need to get the mach port representing this service, so we can
  126. // get information from the crashed process.
  127. kern_return_t kr = bootstrap_check_in(bootstrap_subset_port_,
  128. (char*)receive_port_name,
  129. &service_rcv_port_);
  130. if (kr != KERN_SUCCESS) {
  131. #if VERBOSE
  132. PRINT_MACH_RESULT(kr, "Inspector: bootstrap_check_in()");
  133. #endif
  134. }
  135. return kr;
  136. }
  137. //=============================================================================
  138. kern_return_t Inspector::ServiceCheckOut(const char *receive_port_name) {
  139. // We're done receiving mach messages from the crashed process,
  140. // so clean up a bit.
  141. kern_return_t kr;
  142. // DO NOT use mach_port_deallocate() here -- it will fail and the
  143. // following bootstrap_register() will also fail leaving our service
  144. // name hanging around forever (until reboot)
  145. kr = mach_port_destroy(mach_task_self(), service_rcv_port_);
  146. if (kr != KERN_SUCCESS) {
  147. PRINT_MACH_RESULT(kr,
  148. "Inspector: UNREGISTERING: service_rcv_port mach_port_deallocate()");
  149. return kr;
  150. }
  151. // Unregister the service associated with the receive port.
  152. kr = breakpad::BootstrapRegister(bootstrap_subset_port_,
  153. (char*)receive_port_name,
  154. MACH_PORT_NULL);
  155. if (kr != KERN_SUCCESS) {
  156. PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()");
  157. }
  158. return kr;
  159. }
  160. //=============================================================================
  161. kern_return_t Inspector::ReadMessages() {
  162. // Wait for an initial message from the crashed process containing basic
  163. // information about the crash.
  164. ReceivePort receive_port(service_rcv_port_);
  165. MachReceiveMessage message;
  166. kern_return_t result = receive_port.WaitForMessage(&message, 1000);
  167. if (result == KERN_SUCCESS) {
  168. InspectorInfo &info = (InspectorInfo &)*message.GetData();
  169. exception_type_ = info.exception_type;
  170. exception_code_ = info.exception_code;
  171. exception_subcode_ = info.exception_subcode;
  172. #if VERBOSE
  173. printf("message ID = %d\n", message.GetMessageID());
  174. #endif
  175. remote_task_ = message.GetTranslatedPort(0);
  176. crashing_thread_ = message.GetTranslatedPort(1);
  177. handler_thread_ = message.GetTranslatedPort(2);
  178. ack_port_ = message.GetTranslatedPort(3);
  179. #if VERBOSE
  180. printf("exception_type = %d\n", exception_type_);
  181. printf("exception_code = %d\n", exception_code_);
  182. printf("exception_subcode = %d\n", exception_subcode_);
  183. printf("remote_task = %d\n", remote_task_);
  184. printf("crashing_thread = %d\n", crashing_thread_);
  185. printf("handler_thread = %d\n", handler_thread_);
  186. printf("ack_port_ = %d\n", ack_port_);
  187. printf("parameter count = %d\n", info.parameter_count);
  188. #endif
  189. // In certain situations where multiple crash requests come
  190. // through quickly, we can end up with the mach IPC messages not
  191. // coming through correctly. Since we don't know what parameters
  192. // we've missed, we can't do much besides abort the crash dump
  193. // situation in this case.
  194. unsigned int parameters_read = 0;
  195. // The initial message contains the number of key value pairs that
  196. // we are expected to read.
  197. // Read each key/value pair, one mach message per key/value pair.
  198. for (unsigned int i = 0; i < info.parameter_count; ++i) {
  199. MachReceiveMessage parameter_message;
  200. result = receive_port.WaitForMessage(&parameter_message, 1000);
  201. if(result == KERN_SUCCESS) {
  202. KeyValueMessageData &key_value_data =
  203. (KeyValueMessageData&)*parameter_message.GetData();
  204. // If we get a blank key, make sure we don't increment the
  205. // parameter count; in some cases (notably on-demand generation
  206. // many times in a short period of time) caused the Mach IPC
  207. // messages to not come through correctly.
  208. if (strlen(key_value_data.key) == 0) {
  209. continue;
  210. }
  211. parameters_read++;
  212. config_params_.SetKeyValue(key_value_data.key, key_value_data.value);
  213. } else {
  214. PRINT_MACH_RESULT(result, "Inspector: key/value message");
  215. break;
  216. }
  217. }
  218. if (parameters_read != info.parameter_count) {
  219. return KERN_FAILURE;
  220. }
  221. }
  222. return result;
  223. }
  224. //=============================================================================
  225. bool Inspector::InspectTask() {
  226. // keep the task quiet while we're looking at it
  227. task_suspend(remote_task_);
  228. NSString *minidumpDir;
  229. const char *minidumpDirectory =
  230. config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY);
  231. // If the client app has not specified a minidump directory,
  232. // use a default of Library/<kDefaultLibrarySubdirectory>/<Product Name>
  233. if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) {
  234. NSArray *libraryDirectories =
  235. NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
  236. NSUserDomainMask,
  237. YES);
  238. NSString *applicationSupportDirectory =
  239. [libraryDirectories objectAtIndex:0];
  240. NSString *library_subdirectory = [NSString
  241. stringWithUTF8String:kDefaultLibrarySubdirectory];
  242. NSString *breakpad_product = [NSString
  243. stringWithUTF8String:config_params_.GetValueForKey(BREAKPAD_PRODUCT)];
  244. NSArray *path_components = [NSArray
  245. arrayWithObjects:applicationSupportDirectory,
  246. library_subdirectory,
  247. breakpad_product,
  248. nil];
  249. minidumpDir = [NSString pathWithComponents:path_components];
  250. } else {
  251. minidumpDir = [[NSString stringWithUTF8String:minidumpDirectory]
  252. stringByExpandingTildeInPath];
  253. }
  254. MinidumpLocation minidumpLocation(minidumpDir);
  255. // Obscure bug alert:
  256. // Don't use [NSString stringWithFormat] to build up the path here since it
  257. // assumes system encoding and in RTL locales will prepend an LTR override
  258. // character for paths beginning with '/' which fileSystemRepresentation does
  259. // not remove. Filed as rdar://6889706 .
  260. NSString *path_ns = [NSString
  261. stringWithUTF8String:minidumpLocation.GetPath()];
  262. NSString *pathid_ns = [NSString
  263. stringWithUTF8String:minidumpLocation.GetID()];
  264. NSString *minidumpPath = [path_ns stringByAppendingPathComponent:pathid_ns];
  265. minidumpPath = [minidumpPath
  266. stringByAppendingPathExtension:@"dmp"];
  267. config_file_.WriteFile( 0,
  268. &config_params_,
  269. minidumpLocation.GetPath(),
  270. minidumpLocation.GetID());
  271. MinidumpGenerator generator(remote_task_, handler_thread_);
  272. if (exception_type_ && exception_code_) {
  273. generator.SetExceptionInformation(exception_type_,
  274. exception_code_,
  275. exception_subcode_,
  276. crashing_thread_);
  277. }
  278. bool result = generator.Write([minidumpPath fileSystemRepresentation]);
  279. // let the task continue
  280. task_resume(remote_task_);
  281. return result;
  282. }
  283. //=============================================================================
  284. // The crashed task needs to be told that the inspection has finished.
  285. // It will wait on a mach port (with timeout) until we send acknowledgement.
  286. kern_return_t Inspector::SendAcknowledgement() {
  287. if (ack_port_ != MACH_PORT_DEAD) {
  288. MachPortSender sender(ack_port_);
  289. MachSendMessage ack_message(kMsgType_InspectorAcknowledgement);
  290. kern_return_t result = sender.SendMessage(ack_message, 2000);
  291. #if VERBOSE
  292. PRINT_MACH_RESULT(result, "Inspector: sent acknowledgement");
  293. #endif
  294. return result;
  295. }
  296. return KERN_INVALID_NAME;
  297. }
  298. } // namespace google_breakpad