Skip to content

Commit 8ffbd55

Browse files
committed
WIP: feat: add CrashpadClient::Add/RemoveAttachment() for macOS
1 parent b160f39 commit 8ffbd55

File tree

9 files changed

+263
-8
lines changed

9 files changed

+263
-8
lines changed

client/crashpad_client.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -855,7 +855,7 @@ class CrashpadClient {
855855
static void SetCrashLoopBefore(uint64_t crash_loop_before_time);
856856
#endif
857857

858-
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || DOXYGEN
858+
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || DOXYGEN
859859
//! \brief Adds a file to the list of files to be attached to the crash
860860
//! report.
861861
//!

client/crashpad_client_mac.cc

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#include "util/mach/mach_extensions.h"
3636
#include "util/mach/mach_message.h"
3737
#include "util/mach/notify_server.h"
38+
#include "util/mach/payload_message.h"
3839
#include "util/misc/clock.h"
3940
#include "util/misc/implicit_cast.h"
4041
#include "util/posix/spawn_subprocess.h"
@@ -520,7 +521,7 @@ bool CrashpadClient::StartHandler(
520521
bool wait_for_upload,
521522
const base::FilePath& crash_reporter,
522523
const base::FilePath& crash_envelope) {
523-
(void) wait_for_upload; // unused in mac (for now)
524+
(void)wait_for_upload; // unused in mac (for now)
524525

525526
// The “restartable” behavior can only be selected on OS X 10.10 and later. In
526527
// previous OS versions, if the initial client were to crash while attempting
@@ -609,4 +610,13 @@ void CrashpadClient::UseSystemDefaultHandler() {
609610
}
610611
}
611612

613+
void CrashpadClient::AddAttachment(const base::FilePath& attachment) {
614+
SendPayloadMessage(exception_port_.get(), kAddAttachment, attachment.value());
615+
}
616+
617+
void CrashpadClient::RemoveAttachment(const base::FilePath& attachment) {
618+
SendPayloadMessage(
619+
exception_port_.get(), kRemoveAttachment, attachment.value());
620+
}
621+
612622
} // namespace crashpad

handler/mac/crash_report_exception_handler.cc

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
#include <utility>
1818
#include <vector>
19+
#include <algorithm>
1920

2021
#include "base/apple/mach_logging.h"
2122
#include "base/apple/scoped_mach_port.h"
@@ -55,10 +56,15 @@ CrashReportExceptionHandler::CrashReportExceptionHandler(
5556
: database_(database),
5657
upload_thread_(upload_thread),
5758
process_annotations_(process_annotations),
58-
attachments_(attachments),
59+
attachments_(),
5960
user_stream_data_sources_(user_stream_data_sources),
6061
crash_reporter_(crash_reporter),
61-
crash_envelope_(crash_envelope) {}
62+
crash_envelope_(crash_envelope) {
63+
if (attachments) {
64+
// Copy the attachments into our owned vector.
65+
attachments_ = *attachments;
66+
}
67+
}
6268

6369
CrashReportExceptionHandler::~CrashReportExceptionHandler() {
6470
}
@@ -182,7 +188,7 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
182188
return KERN_FAILURE;
183189
}
184190

185-
for (const auto& attachment : (*attachments_)) {
191+
for (const auto& attachment : attachments_) {
186192
FileReader file_reader;
187193
if (!file_reader.Open(attachment)) {
188194
LOG(ERROR) << "attachment " << attachment.value().c_str()
@@ -206,7 +212,7 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
206212
if (has_crash_reporter) {
207213
CrashReportDatabase::Envelope envelope(new_report->ReportID());
208214
if (envelope.Initialize(*crash_envelope_)) {
209-
envelope.AddAttachments(*attachments_);
215+
envelope.AddAttachments(attachments_);
210216
if (auto reader = new_report->Reader()) {
211217
envelope.AddMinidump(reader);
212218
}
@@ -306,4 +312,40 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
306312
return KERN_SUCCESS;
307313
}
308314

315+
void CrashReportExceptionHandler::HandlePayloadMessage(
316+
const PayloadMessage& message) {
317+
std::string payload(static_cast<const char*>(message.payload.address),
318+
message.payload.size - 1);
319+
320+
switch (message.type) {
321+
case kAddAttachment:
322+
AddAttachment(base::FilePath(payload));
323+
break;
324+
case kRemoveAttachment:
325+
RemoveAttachment(base::FilePath(payload));
326+
break;
327+
default:
328+
LOG(ERROR) << "unknown attachment message type: " << message.type;
329+
break;
330+
}
331+
}
332+
333+
void CrashReportExceptionHandler::AddAttachment(const base::FilePath& attachment) {
334+
auto it = std::find(attachments_.begin(), attachments_.end(), attachment);
335+
if (it != attachments_.end()) {
336+
LOG(WARNING) << "ignoring duplicate attachment " << attachment;
337+
return;
338+
}
339+
attachments_.push_back(attachment);
340+
}
341+
342+
void CrashReportExceptionHandler::RemoveAttachment(const base::FilePath& attachment) {
343+
auto it = std::find(attachments_.begin(), attachments_.end(), attachment);
344+
if (it == attachments_.end()) {
345+
LOG(WARNING) << "ignoring non-existent attachment " << attachment;
346+
return;
347+
}
348+
attachments_.erase(it);
349+
}
350+
309351
} // namespace crashpad

handler/mac/crash_report_exception_handler.h

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222

2323
#include "client/crash_report_database.h"
2424
#include "handler/crash_report_upload_thread.h"
25+
#include "handler/mac/exception_handler_server.h"
2526
#include "handler/user_stream_data_source.h"
2627
#include "util/mach/exc_server_variants.h"
2728

@@ -30,7 +31,8 @@ namespace crashpad {
3031
//! \brief An exception handler that writes crash reports for exception messages
3132
//! to a CrashReportDatabase.
3233
class CrashReportExceptionHandler final
33-
: public UniversalMachExcServer::Interface {
34+
: public UniversalMachExcServer::Interface,
35+
public PayloadMessageHandler {
3436
public:
3537
//! \brief Creates a new object that will store crash reports in \a database.
3638
//!
@@ -89,11 +91,27 @@ class CrashReportExceptionHandler final
8991
const mach_msg_trailer_t* trailer,
9092
bool* destroy_complex_request) override;
9193

94+
// PayloadMessageHandler:
95+
96+
//! \brief Processes a payload message by adding or removing an attachment
97+
//! to or from the attachments vector.
98+
void HandlePayloadMessage(const PayloadMessage& message) override;
99+
100+
//! \brief Adds an attachment to the attachments list.
101+
//!
102+
//! \param[in] path The path of the attachment to add.
103+
void AddAttachment(const base::FilePath& path);
104+
105+
//! \brief Removes an attachment from the attachments list.
106+
//!
107+
//! \param[in] path The path of the attachment to remove.
108+
void RemoveAttachment(const base::FilePath& path);
109+
92110
private:
93111
CrashReportDatabase* database_; // weak
94112
CrashReportUploadThread* upload_thread_; // weak
95113
const std::map<std::string, std::string>* process_annotations_; // weak
96-
const std::vector<base::FilePath>* attachments_; // weak
114+
std::vector<base::FilePath> attachments_; // owned
97115
const UserStreamDataSources* user_stream_data_sources_; // weak
98116
const base::FilePath* crash_reporter_; // weak
99117
const base::FilePath* crash_envelope_; // weak

handler/mac/exception_handler_server.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "base/apple/mach_logging.h"
2020
#include "base/check.h"
2121
#include "base/logging.h"
22+
#include "handler/mac/crash_report_exception_handler.h"
2223
#include "util/mach/composite_mach_message_server.h"
2324
#include "util/mach/mach_extensions.h"
2425
#include "util/mach/mach_message.h"
@@ -29,6 +30,49 @@ namespace crashpad {
2930

3031
namespace {
3132

33+
// Custom server for handling payload messages
34+
class PayloadMessageServer : public MachMessageServer::Interface {
35+
public:
36+
PayloadMessageServer(PayloadMessageHandler* handler) : handler_(handler) {}
37+
38+
PayloadMessageServer(const PayloadMessageServer&) = delete;
39+
PayloadMessageServer& operator=(const PayloadMessageServer&) = delete;
40+
41+
// MachMessageServer::Interface:
42+
43+
// Processes an incoming message and dispatches it to the appropriate handler.
44+
bool MachMessageServerFunction(const mach_msg_header_t* in_header,
45+
mach_msg_header_t* out_header,
46+
bool* destroy_complex_request) override {
47+
const PayloadMessage* message =
48+
ReceivePayloadMessage(in_header, out_header);
49+
if (message) {
50+
if (handler_) {
51+
handler_->HandlePayloadMessage(*message);
52+
}
53+
*destroy_complex_request = true;
54+
return true;
55+
}
56+
57+
return false;
58+
}
59+
60+
std::set<mach_msg_id_t> MachMessageServerRequestIDs() override {
61+
return {kPayloadMessageID};
62+
}
63+
64+
mach_msg_size_t MachMessageServerRequestSize() override {
65+
return sizeof(PayloadMessage);
66+
}
67+
68+
mach_msg_size_t MachMessageServerReplySize() override {
69+
return sizeof(mig_reply_error_t); // Minimal size for a proper Mach reply message
70+
}
71+
72+
private:
73+
PayloadMessageHandler* handler_; // weak
74+
};
75+
3276
class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
3377
public NotifyServer::DefaultInterface {
3478
public:
@@ -41,6 +85,7 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
4185
NotifyServer::DefaultInterface(),
4286
mach_exc_server_(this),
4387
notify_server_(this),
88+
payload_message_server_(dynamic_cast<CrashReportExceptionHandler*>(exception_interface)),
4489
composite_mach_message_server_(),
4590
exception_interface_(exception_interface),
4691
exception_port_(exception_port),
@@ -49,6 +94,7 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
4994
launchd_(launchd) {
5095
composite_mach_message_server_.AddHandler(&mach_exc_server_);
5196
composite_mach_message_server_.AddHandler(&notify_server_);
97+
composite_mach_message_server_.AddHandler(&payload_message_server_);
5298
}
5399

54100
ExceptionHandlerServerRun(const ExceptionHandlerServerRun&) = delete;
@@ -182,6 +228,7 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
182228
private:
183229
UniversalMachExcServer mach_exc_server_;
184230
NotifyServer notify_server_;
231+
PayloadMessageServer payload_message_server_;
185232
CompositeMachMessageServer composite_mach_message_server_;
186233
UniversalMachExcServer::Interface* exception_interface_; // weak
187234
mach_port_t exception_port_; // weak

handler/mac/exception_handler_server.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,21 @@
1919

2020
#include "base/apple/scoped_mach_port.h"
2121
#include "util/mach/exc_server_variants.h"
22+
#include "util/mach/payload_message.h"
2223

2324
namespace crashpad {
2425

26+
//! \brief Interface for handling payload messages
27+
class PayloadMessageHandler {
28+
public:
29+
virtual ~PayloadMessageHandler() {}
30+
31+
//! \brief Processes a payload message
32+
//!
33+
//! \param[in] message The message to process
34+
virtual void HandlePayloadMessage(const PayloadMessage& message) = 0;
35+
};
36+
2537
//! \brief Runs the main exception-handling server in Crashpad’s handler
2638
//! process.
2739
class ExceptionHandlerServer {

util/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,8 @@ if(APPLE)
193193
mach/exception_types.h
194194
mach/notify_server.cc
195195
mach/notify_server.h
196+
mach/payload_message.cc
197+
mach/payload_message.h
196198
mach/scoped_task_suspend.cc
197199
mach/scoped_task_suspend.h
198200
mach/task_for_pid.cc

util/mach/payload_message.cc

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright 2014 The Crashpad Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (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
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "util/mach/payload_message.h"
16+
17+
#include "base/apple/mach_logging.h"
18+
#include "base/logging.h"
19+
#include "util/mach/mach_message.h"
20+
21+
namespace crashpad {
22+
23+
bool SendPayloadMessage(mach_port_t port,
24+
PayloadMessageType type,
25+
const std::string& payload) {
26+
if (port == MACH_PORT_NULL) {
27+
DLOG(ERROR) << "Cannot send attachment message, no exception port";
28+
return false;
29+
}
30+
31+
PayloadMessage message = {};
32+
message.header.msgh_bits =
33+
MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0) | MACH_MSGH_BITS_COMPLEX;
34+
message.header.msgh_size = sizeof(message);
35+
message.header.msgh_remote_port = port;
36+
message.header.msgh_local_port = MACH_PORT_NULL;
37+
message.header.msgh_id = kPayloadMessageID;
38+
39+
message.body.msgh_descriptor_count = 1;
40+
41+
message.payload.address = const_cast<char*>(payload.c_str());
42+
message.payload.size = payload.size() + 1;
43+
message.payload.deallocate = FALSE;
44+
message.payload.copy = MACH_MSG_VIRTUAL_COPY;
45+
message.payload.type = MACH_MSG_OOL_DESCRIPTOR;
46+
47+
message.ndr = NDR_record;
48+
message.type = type;
49+
50+
kern_return_t kr = mach_msg(&message.header,
51+
MACH_SEND_MSG,
52+
sizeof(message),
53+
0,
54+
MACH_PORT_NULL,
55+
MACH_MSG_TIMEOUT_NONE,
56+
MACH_PORT_NULL);
57+
58+
if (kr != KERN_SUCCESS) {
59+
MACH_LOG(ERROR, kr) << "mach_msg";
60+
return false;
61+
}
62+
63+
return true;
64+
}
65+
66+
const PayloadMessage* ReceivePayloadMessage(const mach_msg_header_t* in_header,
67+
mach_msg_header_t* out_header) {
68+
if (in_header->msgh_id != kPayloadMessageID) {
69+
return nullptr;
70+
}
71+
72+
PrepareMIGReplyFromRequest(in_header, out_header);
73+
SetMIGReplyError(out_header, MIG_NO_REPLY);
74+
return reinterpret_cast<const PayloadMessage*>(in_header);
75+
}
76+
77+
} // namespace crashpad

0 commit comments

Comments
 (0)