OLD | NEW |
---|---|
1 // Copyright 2014 The Crashpad Authors. All rights reserved. | 1 // Copyright 2014 The Crashpad Authors. All rights reserved. |
2 // | 2 // |
3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
6 // | 6 // |
7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
8 // | 8 // |
9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
13 // limitations under the License. | 13 // limitations under the License. |
14 | 14 |
15 #include "client/crashpad_client.h" | 15 #include "client/crashpad_client.h" |
16 | 16 |
17 #include <errno.h> | |
17 #include <mach/mach.h> | 18 #include <mach/mach.h> |
19 #include <pthread.h> | |
18 #include <sys/wait.h> | 20 #include <sys/wait.h> |
19 #include <unistd.h> | 21 #include <unistd.h> |
20 | 22 |
21 #include "base/logging.h" | 23 #include "base/logging.h" |
22 #include "base/mac/mach_logging.h" | 24 #include "base/mac/mach_logging.h" |
23 #include "base/posix/eintr_wrapper.h" | 25 #include "base/posix/eintr_wrapper.h" |
24 #include "base/strings/stringprintf.h" | 26 #include "base/strings/stringprintf.h" |
27 #include "util/mac/mac_util.h" | |
25 #include "util/mach/child_port_handshake.h" | 28 #include "util/mach/child_port_handshake.h" |
26 #include "util/mach/exception_ports.h" | 29 #include "util/mach/exception_ports.h" |
27 #include "util/mach/mach_extensions.h" | 30 #include "util/mach/mach_extensions.h" |
31 #include "util/mach/mach_message.h" | |
32 #include "util/mach/notify_server.h" | |
33 #include "util/misc/clock.h" | |
28 #include "util/misc/implicit_cast.h" | 34 #include "util/misc/implicit_cast.h" |
29 #include "util/posix/close_multiple.h" | 35 #include "util/posix/close_multiple.h" |
30 | 36 |
31 namespace crashpad { | 37 namespace crashpad { |
32 | 38 |
33 namespace { | 39 namespace { |
34 | 40 |
35 std::string FormatArgumentString(const std::string& name, | 41 std::string FormatArgumentString(const std::string& name, |
36 const std::string& value) { | 42 const std::string& value) { |
37 return base::StringPrintf("--%s=%s", name.c_str(), value.c_str()); | 43 return base::StringPrintf("--%s=%s", name.c_str(), value.c_str()); |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
72 // so AND them with ExcMaskValid(). EXC_MASK_CRASH is always supported. | 78 // so AND them with ExcMaskValid(). EXC_MASK_CRASH is always supported. |
73 bool SetCrashExceptionPorts(exception_handler_t exception_handler) { | 79 bool SetCrashExceptionPorts(exception_handler_t exception_handler) { |
74 ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL); | 80 ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL); |
75 return exception_ports.SetExceptionPort( | 81 return exception_ports.SetExceptionPort( |
76 (EXC_MASK_CRASH | EXC_MASK_RESOURCE | EXC_MASK_GUARD) & ExcMaskValid(), | 82 (EXC_MASK_CRASH | EXC_MASK_RESOURCE | EXC_MASK_GUARD) & ExcMaskValid(), |
77 exception_handler, | 83 exception_handler, |
78 EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, | 84 EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, |
79 MACHINE_THREAD_STATE); | 85 MACHINE_THREAD_STATE); |
80 } | 86 } |
81 | 87 |
82 //! \brief Starts a Crashpad handler. | 88 class ScopedPthreadAttrDestroy { |
83 class HandlerStarter final { | |
84 public: | 89 public: |
85 //! \brief Starts a Crashpad handler. | 90 explicit ScopedPthreadAttrDestroy(pthread_attr_t* pthread_attr) |
91 : pthread_attr_(pthread_attr) { | |
92 } | |
93 | |
94 ~ScopedPthreadAttrDestroy() { | |
95 errno = pthread_attr_destroy(pthread_attr_); | |
96 PLOG_IF(WARNING, errno != 0) << "pthread_attr_destroy"; | |
97 } | |
98 | |
99 private: | |
100 pthread_attr_t* pthread_attr_; | |
101 | |
102 DISALLOW_COPY_AND_ASSIGN(ScopedPthreadAttrDestroy); | |
103 }; | |
104 | |
105 //! \brief Starts a Crashpad handler, possibly restarting it if it dies. | |
106 class HandlerStarter final : public NotifyServer::DefaultInterface { | |
107 public: | |
108 ~HandlerStarter() {} | |
109 | |
110 //! \brief Starts a Crashpad handler initially, as opposed to starting it for | |
111 //! subsequent restarts. | |
86 //! | 112 //! |
87 //! All parameters are as in CrashpadClient::StartHandler(). | 113 //! All parameters are as in CrashpadClient::StartHandler(). |
88 //! | 114 //! |
89 //! \return On success, a send right to the Crashpad handler that has been | 115 //! \return On success, a send right to the Crashpad handler that has been |
90 //! started. On failure, `MACH_PORT_NULL` with a message logged. | 116 //! started. On failure, `MACH_PORT_NULL` with a message logged. |
91 static base::mac::ScopedMachSendRight Start( | 117 static base::mac::ScopedMachSendRight InitialStart( |
92 const base::FilePath& handler, | 118 const base::FilePath& handler, |
93 const base::FilePath& database, | 119 const base::FilePath& database, |
94 const std::string& url, | 120 const std::string& url, |
95 const std::map<std::string, std::string>& annotations, | 121 const std::map<std::string, std::string>& annotations, |
96 const std::vector<std::string>& arguments) { | 122 const std::vector<std::string>& arguments, |
123 bool restartable) { | |
97 base::mac::ScopedMachReceiveRight receive_right( | 124 base::mac::ScopedMachReceiveRight receive_right( |
98 NewMachPort(MACH_PORT_RIGHT_RECEIVE)); | 125 NewMachPort(MACH_PORT_RIGHT_RECEIVE)); |
99 if (receive_right == kMachPortNull) { | 126 if (receive_right == kMachPortNull) { |
100 return base::mac::ScopedMachSendRight(); | 127 return base::mac::ScopedMachSendRight(); |
101 } | 128 } |
102 | 129 |
103 mach_port_t port; | 130 mach_port_t port; |
104 mach_msg_type_name_t right_type; | 131 mach_msg_type_name_t right_type; |
105 kern_return_t kr = mach_port_extract_right(mach_task_self(), | 132 kern_return_t kr = mach_port_extract_right(mach_task_self(), |
106 receive_right.get(), | 133 receive_right.get(), |
107 MACH_MSG_TYPE_MAKE_SEND, | 134 MACH_MSG_TYPE_MAKE_SEND, |
108 &port, | 135 &port, |
109 &right_type); | 136 &right_type); |
110 if (kr != KERN_SUCCESS) { | 137 if (kr != KERN_SUCCESS) { |
111 MACH_LOG(ERROR, kr) << "mach_port_extract_right"; | 138 MACH_LOG(ERROR, kr) << "mach_port_extract_right"; |
112 return base::mac::ScopedMachSendRight(); | 139 return base::mac::ScopedMachSendRight(); |
113 } | 140 } |
114 base::mac::ScopedMachSendRight send_right(port); | 141 base::mac::ScopedMachSendRight send_right(port); |
115 DCHECK_EQ(port, receive_right.get()); | 142 DCHECK_EQ(port, receive_right.get()); |
116 DCHECK_EQ(right_type, | 143 DCHECK_EQ(right_type, |
117 implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND)); | 144 implicit_cast<mach_msg_type_name_t>(MACH_MSG_TYPE_PORT_SEND)); |
118 | 145 |
146 scoped_ptr<HandlerStarter> handler_starter; | |
Robert Sesek
2015/11/03 15:50:27
handler_restarter ?
| |
147 if (restartable) { | |
148 handler_starter.reset(new HandlerStarter()); | |
149 if (handler_starter->notify_port_ == kMachPortNull) { | |
Robert Sesek
2015/11/03 15:50:27
Leave a comment here indicating that this is an er
| |
150 handler_starter.reset(); | |
151 } | |
152 } | |
153 | |
119 if (!CommonStart(handler, | 154 if (!CommonStart(handler, |
120 database, | 155 database, |
121 url, | 156 url, |
122 annotations, | 157 annotations, |
123 arguments, | 158 arguments, |
124 receive_right.Pass())) { | 159 receive_right.Pass(), |
160 handler_starter.get(), | |
161 false)) { | |
125 return base::mac::ScopedMachSendRight(); | 162 return base::mac::ScopedMachSendRight(); |
126 } | 163 } |
127 | 164 |
165 if (handler_starter && handler_starter->StartRestartThread( | |
166 handler, database, url, annotations, arguments)) { | |
167 // The thread owns the object now. | |
168 ignore_result(handler_starter.release()); | |
169 } | |
170 | |
128 return send_right; | 171 return send_right; |
129 } | 172 } |
130 | 173 |
174 // NotifyServer::DefaultInterface: | |
175 | |
176 kern_return_t DoMachNotifyPortDestroyed(notify_port_t notify, | |
177 mach_port_t rights, | |
178 const mach_msg_trailer_t* trailer, | |
179 bool* destroy_request) override { | |
180 // The receive right corresponding to this process’ crash exception port is | |
181 // now owned by this process. Any crashes that occur before the receive | |
182 // right is moved to a new handler process will cause the process to hang in | |
183 // an unkillable state prior to OS X 10.10. | |
184 | |
185 if (notify != notify_port_) { | |
186 LOG(WARNING) << "notify port mismatch"; | |
187 return KERN_FAILURE; | |
188 } | |
189 | |
190 // If CommonStart() fails, the receive right will die, and this will just | |
191 // be called again for another try. | |
192 CommonStart(handler_, | |
193 database_, | |
194 url_, | |
195 annotations_, | |
196 arguments_, | |
197 base::mac::ScopedMachReceiveRight(rights), | |
198 this, | |
199 true); | |
200 | |
201 return KERN_SUCCESS; | |
202 } | |
203 | |
131 private: | 204 private: |
205 HandlerStarter() | |
206 : NotifyServer::DefaultInterface(), | |
207 handler_(), | |
208 database_(), | |
209 url_(), | |
210 annotations_(), | |
211 arguments_(), | |
212 notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)), | |
213 last_start_time_(0) { | |
214 } | |
215 | |
216 //! \brief Starts a Crashpad handler. | |
217 //! | |
218 //! All parameters are as in CrashpadClient::StartHandler(), with these | |
219 //! additions: | |
220 //! | |
221 //! \param[in] receive_right The receive right to move to the Crashpad | |
222 //! handler. The handler will use this receive right to run its exception | |
223 //! server. | |
224 //! \param[in] handler_starter If CrashpadClient::StartHandler() was invoked | |
225 //! with \a restartable set to `true`, this is the restart state object. | |
226 //! Otherwise, this is `nullptr`. | |
227 //! \param[in] restart If CrashpadClient::StartHandler() was invoked with \a | |
228 //! restartable set to `true` and CommonStart() is being called to restart | |
229 //! a previously-started handler, this is `true`. Otherwise, this is | |
230 //! `false`. | |
231 //! | |
232 //! \return `true` on success, `false` on failure, with a message logged. | |
233 //! Failures include failure to start the handler process and failure to | |
234 //! rendezvous with it via ChildPortHandshake. | |
132 static bool CommonStart(const base::FilePath& handler, | 235 static bool CommonStart(const base::FilePath& handler, |
133 const base::FilePath& database, | 236 const base::FilePath& database, |
134 const std::string& url, | 237 const std::string& url, |
135 const std::map<std::string, std::string>& annotations, | 238 const std::map<std::string, std::string>& annotations, |
136 const std::vector<std::string>& arguments, | 239 const std::vector<std::string>& arguments, |
137 base::mac::ScopedMachReceiveRight receive_right) { | 240 base::mac::ScopedMachReceiveRight receive_right, |
241 HandlerStarter* handler_starter, | |
242 bool restart) { | |
243 if (handler_starter) { | |
244 // The port-destroyed notification must be requested each time. It uses | |
245 // a send-once right, so once the notification is received, it won’t be | |
246 // sent again unless re-requested. | |
247 mach_port_t previous; | |
248 kern_return_t kr = | |
249 mach_port_request_notification(mach_task_self(), | |
250 receive_right.get(), | |
251 MACH_NOTIFY_PORT_DESTROYED, | |
252 0, | |
253 handler_starter->notify_port_.get(), | |
254 MACH_MSG_TYPE_MAKE_SEND_ONCE, | |
255 &previous); | |
256 if (kr != KERN_SUCCESS) { | |
257 MACH_LOG(WARNING, kr) << "mach_port_request_notification"; | |
258 | |
259 // This will cause the restart thread to terminate after this restart | |
260 // attempt. There’s no longer any need for it, because no more | |
261 // port-destroyed notifications can be delivered. | |
262 handler_starter->notify_port_.reset(); | |
263 } else { | |
264 base::mac::ScopedMachSendRight previous_owner(previous); | |
265 if (!restart) { | |
266 DCHECK_EQ(previous, kMachPortNull); | |
267 } | |
268 } | |
269 | |
270 if (handler_starter->last_start_time_) { | |
271 // If the handler was ever started before, don’t restart it too quickly. | |
272 const uint64_t kNanosecondsPerSecond = 1E9; | |
273 const uint64_t kMinimumStartInterval = 1 * kNanosecondsPerSecond; | |
274 | |
275 const uint64_t earliest_next_start_time = | |
276 handler_starter->last_start_time_ + kMinimumStartInterval; | |
277 const uint64_t now_time = ClockMonotonicNanoseconds(); | |
278 if (earliest_next_start_time > now_time) { | |
279 SleepNanoseconds(earliest_next_start_time - now_time); | |
280 } | |
281 } | |
282 | |
283 handler_starter->last_start_time_ = ClockMonotonicNanoseconds(); | |
284 } | |
285 | |
138 // Set up the arguments for execve() first. These aren’t needed until | 286 // Set up the arguments for execve() first. These aren’t needed until |
139 // execve() is called, but it’s dangerous to do this in a child process | 287 // execve() is called, but it’s dangerous to do this in a child process |
140 // after fork(). | 288 // after fork(). |
141 ChildPortHandshake child_port_handshake; | 289 ChildPortHandshake child_port_handshake; |
142 base::ScopedFD server_write_fd = child_port_handshake.ServerWriteFD(); | 290 base::ScopedFD server_write_fd = child_port_handshake.ServerWriteFD(); |
143 | 291 |
144 // Use handler as argv[0], followed by arguments directed by this method’s | 292 // Use handler as argv[0], followed by arguments directed by this method’s |
145 // parameters and a --handshake-fd argument. |arguments| are added first so | 293 // parameters and a --handshake-fd argument. |arguments| are added first so |
146 // that if it erroneously contains an argument such as --url, the actual | 294 // that if it erroneously contains an argument such as --url, the actual |
147 // |url| argument passed to this method will supersede it. In normal | 295 // |url| argument passed to this method will supersede it. In normal |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
188 // process will not result in a zombie process. | 336 // process will not result in a zombie process. |
189 pid_t pid = fork(); | 337 pid_t pid = fork(); |
190 if (pid < 0) { | 338 if (pid < 0) { |
191 PLOG(ERROR) << "fork"; | 339 PLOG(ERROR) << "fork"; |
192 return false; | 340 return false; |
193 } | 341 } |
194 | 342 |
195 if (pid == 0) { | 343 if (pid == 0) { |
196 // Child process. | 344 // Child process. |
197 | 345 |
346 if (restart) { | |
347 // When restarting, reset the system default crash handler first. | |
348 // Otherwise, the crash exception port here will have been inherited | |
349 // from the parent process, which was probably using the exception | |
350 // server now being restarted. The handler can’t monitor itself for its | |
351 // own crashes via this interface. | |
352 CrashpadClient::UseSystemDefaultHandler(); | |
353 } | |
354 | |
198 // Call setsid(), creating a new process group and a new session, both led | 355 // Call setsid(), creating a new process group and a new session, both led |
199 // by this process. The new process group has no controlling terminal. | 356 // by this process. The new process group has no controlling terminal. |
200 // This disconnects it from signals generated by the parent process’ | 357 // This disconnects it from signals generated by the parent process’ |
201 // terminal. | 358 // terminal. |
202 // | 359 // |
203 // setsid() is done in the child instead of the grandchild so that the | 360 // setsid() is done in the child instead of the grandchild so that the |
204 // grandchild will not be a session leader. If it were a session leader, | 361 // grandchild will not be a session leader. If it were a session leader, |
205 // an accidental open() of a terminal device without O_NOCTTY would make | 362 // an accidental open() of a terminal device without O_NOCTTY would make |
206 // that terminal the controlling terminal. | 363 // that terminal the controlling terminal. |
207 // | 364 // |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
262 // Rendezvous with the handler running in the grandchild process. | 419 // Rendezvous with the handler running in the grandchild process. |
263 if (!child_port_handshake.RunClient(receive_right.get(), | 420 if (!child_port_handshake.RunClient(receive_right.get(), |
264 MACH_MSG_TYPE_MOVE_RECEIVE)) { | 421 MACH_MSG_TYPE_MOVE_RECEIVE)) { |
265 return false; | 422 return false; |
266 } | 423 } |
267 | 424 |
268 ignore_result(receive_right.release()); | 425 ignore_result(receive_right.release()); |
269 return true; | 426 return true; |
270 } | 427 } |
271 | 428 |
272 DISALLOW_IMPLICIT_CONSTRUCTORS(HandlerStarter); | 429 bool StartRestartThread(const base::FilePath& handler, |
430 const base::FilePath& database, | |
431 const std::string& url, | |
432 const std::map<std::string, std::string>& annotations, | |
433 const std::vector<std::string>& arguments) { | |
434 handler_ = handler; | |
435 database_ = database; | |
436 url_ = url; | |
437 annotations_ = annotations; | |
438 arguments_ = arguments; | |
439 | |
440 pthread_attr_t pthread_attr; | |
441 errno = pthread_attr_init(&pthread_attr); | |
442 if (errno != 0) { | |
443 PLOG(WARNING) << "pthread_attr_init"; | |
444 return false; | |
445 } | |
446 ScopedPthreadAttrDestroy pthread_attr_owner(&pthread_attr); | |
447 | |
448 errno = pthread_attr_setdetachstate(&pthread_attr, PTHREAD_CREATE_DETACHED); | |
449 if (errno != 0) { | |
450 PLOG(WARNING) << "pthread_attr_setdetachstate"; | |
451 return false; | |
452 } | |
453 | |
454 pthread_t pthread; | |
455 errno = pthread_create(&pthread, &pthread_attr, RestartThreadMain, this); | |
456 if (errno != 0) { | |
457 PLOG(WARNING) << "pthread_create"; | |
458 return false; | |
459 } | |
460 | |
461 return true; | |
462 } | |
463 | |
464 static void* RestartThreadMain(void* argument) { | |
465 HandlerStarter* self = reinterpret_cast<HandlerStarter*>(argument); | |
466 | |
467 NotifyServer notify_server(self); | |
468 mach_msg_return_t mr; | |
469 do { | |
470 mr = MachMessageServer::Run(¬ify_server, | |
471 self->notify_port_.get(), | |
472 0, | |
473 MachMessageServer::kPersistent, | |
474 MachMessageServer::kReceiveLargeError, | |
475 kMachMessageTimeoutWaitIndefinitely); | |
476 MACH_LOG_IF(ERROR, mr != MACH_MSG_SUCCESS, mr) | |
477 << "MachMessageServer::Run"; | |
478 } while (self->notify_port_ != kMachPortNull && mr == MACH_MSG_SUCCESS); | |
479 | |
480 delete self; | |
481 | |
482 return nullptr; | |
483 } | |
484 | |
485 base::FilePath handler_; | |
486 base::FilePath database_; | |
487 std::string url_; | |
488 std::map<std::string, std::string> annotations_; | |
489 std::vector<std::string> arguments_; | |
490 base::mac::ScopedMachReceiveRight notify_port_; | |
491 uint64_t last_start_time_; | |
492 | |
493 DISALLOW_COPY_AND_ASSIGN(HandlerStarter); | |
273 }; | 494 }; |
274 | 495 |
275 } // namespace | 496 } // namespace |
276 | 497 |
277 CrashpadClient::CrashpadClient() | 498 CrashpadClient::CrashpadClient() |
278 : exception_port_() { | 499 : exception_port_() { |
279 } | 500 } |
280 | 501 |
281 CrashpadClient::~CrashpadClient() { | 502 CrashpadClient::~CrashpadClient() { |
282 } | 503 } |
283 | 504 |
284 bool CrashpadClient::StartHandler( | 505 bool CrashpadClient::StartHandler( |
285 const base::FilePath& handler, | 506 const base::FilePath& handler, |
286 const base::FilePath& database, | 507 const base::FilePath& database, |
287 const std::string& url, | 508 const std::string& url, |
288 const std::map<std::string, std::string>& annotations, | 509 const std::map<std::string, std::string>& annotations, |
289 const std::vector<std::string>& arguments) { | 510 const std::vector<std::string>& arguments, |
511 bool restartable) { | |
290 DCHECK(!exception_port_.is_valid()); | 512 DCHECK(!exception_port_.is_valid()); |
291 | 513 |
292 exception_port_ = HandlerStarter::Start( | 514 // The “restartable” behavior can only be selected on OS X 10.10 and later. In |
293 handler, database, url, annotations, arguments); | 515 // previous OS versions, if the initial client were to crash while attempting |
516 // to restart the handler, it would become an unkillable process. | |
517 exception_port_ = HandlerStarter::InitialStart( | |
518 handler, | |
519 database, | |
520 url, | |
521 annotations, | |
522 arguments, | |
523 restartable && MacOSXMinorVersion() >= 10); | |
294 if (!exception_port_.is_valid()) { | 524 if (!exception_port_.is_valid()) { |
295 return false; | 525 return false; |
296 } | 526 } |
297 | 527 |
298 return true; | 528 return true; |
299 } | 529 } |
300 | 530 |
301 bool CrashpadClient::UseHandler() { | 531 bool CrashpadClient::UseHandler() { |
302 DCHECK(exception_port_.is_valid()); | 532 DCHECK(exception_port_.is_valid()); |
303 | 533 |
304 return SetCrashExceptionPorts(exception_port_.get()); | 534 return SetCrashExceptionPorts(exception_port_.get()); |
305 } | 535 } |
306 | 536 |
307 // static | 537 // static |
308 void CrashpadClient::UseSystemDefaultHandler() { | 538 void CrashpadClient::UseSystemDefaultHandler() { |
309 base::mac::ScopedMachSendRight | 539 base::mac::ScopedMachSendRight |
310 system_crash_reporter_handler(SystemCrashReporterHandler()); | 540 system_crash_reporter_handler(SystemCrashReporterHandler()); |
311 | 541 |
312 // Proceed even if SystemCrashReporterHandler() failed, setting MACH_PORT_NULL | 542 // Proceed even if SystemCrashReporterHandler() failed, setting MACH_PORT_NULL |
313 // to clear the current exception ports. | 543 // to clear the current exception ports. |
314 if (!SetCrashExceptionPorts(system_crash_reporter_handler.get())) { | 544 if (!SetCrashExceptionPorts(system_crash_reporter_handler.get())) { |
315 SetCrashExceptionPorts(MACH_PORT_NULL); | 545 SetCrashExceptionPorts(MACH_PORT_NULL); |
316 } | 546 } |
317 } | 547 } |
318 | 548 |
319 } // namespace crashpad | 549 } // namespace crashpad |
OLD | NEW |