Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provided context for exception's thread for mac #39

Merged
merged 1 commit into from
Nov 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion src/client/mac/handler/exception_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -343,8 +343,13 @@ bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child,
bool result = generator.Write(dump_filename.c_str());

if (callback) {
#ifdef SENTRY_BACKEND_BREAKPAD
return callback(dump_path.c_str(), dump_id.c_str(),
callback_context, nullptr, result);
#else
return callback(dump_path.c_str(), dump_id.c_str(),
callback_context, result);
#endif
}
return result;
}
Expand Down Expand Up @@ -393,13 +398,19 @@ bool ExceptionHandler::WriteMinidumpWithException(
} else {
string minidump_id;

std::remove_pointer<mcontext_t>::type mctx = {};
breakpad_ucontext_t uctx = {};
uctx.uc_mcsize = sizeof(mctx);
uctx.uc_mcontext = &mctx;

// Putting the MinidumpGenerator in its own context will ensure that the
// destructor is executed, closing the newly created minidump file.
if (!dump_path_.empty()) {
MinidumpGenerator md(mach_task_self(),
report_current_thread ? MACH_PORT_NULL :
mach_thread_self());
md.SetTaskContext(task_context);

if (exception_type && exception_code) {
// If this is a real exception, give the filter (if any) a chance to
// decide if this should be sent.
Expand All @@ -410,6 +421,13 @@ bool ExceptionHandler::WriteMinidumpWithException(
exception_subcode, thread_name);
}

// fill context
if (!task_context)
{
md.GetThreadContext(thread_name, &uctx);
task_context = &uctx;
}

result = md.Write(next_minidump_path_c_);
}

Expand All @@ -418,8 +436,12 @@ bool ExceptionHandler::WriteMinidumpWithException(
// If the user callback returned true and we're handling an exception
// (rather than just writing out the file), then we should exit without
// forwarding the exception to the next handler.
if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_,
#ifdef SENTRY_BACKEND_BREAKPAD
if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_, task_context,
result)) {
#else
if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_, result)) {
#endif
if (exit_after_write)
_exit(exception_type);
}
Expand Down
20 changes: 18 additions & 2 deletions src/client/mac/handler/exception_handler.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,34 @@ class ExceptionHandler {
// will immediately report the exception as unhandled without writing a
// minidump, allowing another handler the opportunity to handle it.
typedef bool (*FilterCallback)(void* context);

#ifdef SENTRY_BACKEND_BREAKPAD
// A callback function to run after the minidump has been written.
// |minidump_id| is a unique id for the dump, so the minidump
// file is <dump_dir>/<minidump_id>.dmp.
// |context| is the value passed into the constructor.
// |user_context| is the context of exception's thread
// |succeeded| indicates whether a minidump file was successfully written.
// Return true if the exception was fully handled and breakpad should exit.
// Return false to allow any other exception handlers to process the
// exception.
typedef bool (*MinidumpCallback)(const char* dump_dir,
const char* minidump_id,
void* context, bool succeeded);
void* context,
breakpad_ucontext_t *user_context, bool succeeded);
saf-e marked this conversation as resolved.
Show resolved Hide resolved
#else
// A callback function to run after the minidump has been written.
// |minidump_id| is a unique id for the dump, so the minidump
// file is <dump_dir>/<minidump_id>.dmp.
// |context| is the value passed into the constructor.
// |succeeded| indicates whether a minidump file was successfully written.
// Return true if the exception was fully handled and breakpad should exit.
// Return false to allow any other exception handlers to process the
// exception.
typedef bool (*MinidumpCallback)(const char* dump_dir,
const char* minidump_id,
void* context,
bool succeeded);
#endif

// A callback function which will be called directly if an exception occurs.
// This bypasses the minidump file writing and simply gives the client
Expand Down
76 changes: 69 additions & 7 deletions src/client/mac/handler/minidump_generator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,58 @@ void MinidumpGenerator::SetTaskContext(breakpad_ucontext_t* task_context) {
task_context_ = task_context;
}

bool MinidumpGenerator::GetThreadContext(mach_port_t thread_id, breakpad_ucontext_t* context)
{
breakpad_thread_state_data_t state;
mach_msg_type_number_t state_count
= static_cast<mach_msg_type_number_t>(sizeof(state));

if (!GetThreadState(thread_id, state, &state_count))
return false;

#ifdef HAS_X86_SUPPORT
MDRawContextAMD64 amd64_ctx;
GetContextX86_64(state, &amd64_ctx);

#define CopyReg(reg) context->uc_mcontext->__ss.__##reg = amd64_ctx.reg

CopyReg(rax);
CopyReg(rbx);
CopyReg(rcx);
CopyReg(rdx);
CopyReg(rdi);
CopyReg(rsi);
CopyReg(rbp);
CopyReg(rsp);
CopyReg(r8);
CopyReg(r9);
CopyReg(r10);
CopyReg(r11);
CopyReg(r12);
CopyReg(r13);
CopyReg(r14);
CopyReg(r15);
CopyReg(rip);
CopyReg(cs);
CopyReg(fs);
CopyReg(gs);
context->uc_mcontext->__ss.__rflags = amd64_ctx.eflags;
#undef CopyReg
#endif
#ifdef HAS_ARM64_SUPPORT
MDRawContextARM64_Old arm64_ctx;
GetContextARM64(state, &arm64_ctx);

std::copy_n(std::begin(arm64_ctx.iregs), std::size(context->uc_mcontext->__ss.__x), std::begin(context->uc_mcontext->__ss.__x));
context->uc_mcontext->__ss.__fp = arm64_ctx.iregs[MD_CONTEXT_ARM64_REG_FP];
context->uc_mcontext->__ss.__lr = arm64_ctx.iregs[MD_CONTEXT_ARM64_REG_LR];
context->uc_mcontext->__ss.__sp = arm64_ctx.iregs[MD_CONTEXT_ARM64_REG_SP];
context->uc_mcontext->__ss.__pc = arm64_ctx.iregs[MD_CONTEXT_ARM64_REG_PC];
context->uc_mcontext->__ss.__cpsr = arm64_ctx.cpsr;
#endif
return true;
}

string MinidumpGenerator::UniqueNameInDirectory(const string& dir,
string* unique_name) {
CFUUIDRef uuid = CFUUIDCreate(NULL);
Expand Down Expand Up @@ -559,6 +611,15 @@ MinidumpGenerator::WriteContextARM64(breakpad_thread_state_data_t state,

*register_location = context.location();
MDRawContextARM64_Old* context_ptr = context.get();
GetContextARM64(state, context_ptr);
return true;
}

void MinidumpGenerator::GetContextARM64(breakpad_thread_state_data_t state,
MDRawContextARM64_Old* context_ptr) {
arm_thread_state64_t* machine_state =
reinterpret_cast<arm_thread_state64_t*>(state);

context_ptr->context_flags = MD_CONTEXT_ARM64_FULL_OLD;

#define AddGPR(a) \
Expand Down Expand Up @@ -600,8 +661,6 @@ MinidumpGenerator::WriteContextARM64(breakpad_thread_state_data_t state,
AddGPR(27);
AddGPR(28);
#undef AddGPR

return true;
}
#endif

Expand Down Expand Up @@ -851,14 +910,19 @@ bool MinidumpGenerator::WriteContextX86_64(
breakpad_thread_state_data_t state,
MDLocationDescriptor* register_location) {
TypedMDRVA<MDRawContextAMD64> context(&writer_);
x86_thread_state64_t* machine_state =
reinterpret_cast<x86_thread_state64_t*>(state);

if (!context.Allocate())
return false;

*register_location = context.location();
MDRawContextAMD64* context_ptr = context.get();
GetContextX86_64(state, context_ptr);
return true;
}

void MinidumpGenerator::GetContextX86_64(breakpad_thread_state_data_t state,
MDRawContextAMD64* context_ptr) {
x86_thread_state64_t* machine_state =
reinterpret_cast<x86_thread_state64_t*>(state);

#define AddReg(a) context_ptr->a = static_cast<__typeof__(context_ptr->a)>( \
REGISTER_FROM_THREADSTATE(machine_state, a))
Expand Down Expand Up @@ -890,8 +954,6 @@ bool MinidumpGenerator::WriteContextX86_64(
AddReg(fs);
AddReg(gs);
#undef AddReg

return true;
}
#endif

Expand Down
7 changes: 7 additions & 0 deletions src/client/mac/handler/minidump_generator.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ class MinidumpGenerator {
// the MinidumpGenerator class.
static void GatherSystemInformation();

// Get context of |thread_id| thread
bool GetThreadContext(mach_port_t thread_id, breakpad_ucontext_t* context);

protected:
// Overridable Stream writers
virtual bool WriteExceptionStream(MDRawDirectory* exception_stream);
Expand Down Expand Up @@ -175,6 +178,8 @@ class MinidumpGenerator {
#ifdef HAS_ARM64_SUPPORT
bool WriteStackARM64(breakpad_thread_state_data_t state,
MDMemoryDescriptor* stack_location);
void GetContextARM64(breakpad_thread_state_data_t state,
MDRawContextARM64_Old* context_ptr);
bool WriteContextARM64(breakpad_thread_state_data_t state,
MDLocationDescriptor* register_location);
uint64_t CurrentPCForStackARM64(breakpad_thread_state_data_t state);
Expand All @@ -199,6 +204,8 @@ class MinidumpGenerator {
uint64_t CurrentPCForStackX86(breakpad_thread_state_data_t state);
bool WriteStackX86_64(breakpad_thread_state_data_t state,
MDMemoryDescriptor* stack_location);
void GetContextX86_64(breakpad_thread_state_data_t state,
MDRawContextAMD64* context_ptr);
bool WriteContextX86_64(breakpad_thread_state_data_t state,
MDLocationDescriptor* register_location);
uint64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state);
Expand Down
7 changes: 7 additions & 0 deletions src/client/mac/handler/ucontext_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,14 @@
#ifndef CLIENT_MAC_HANDLER_UCONTEXT_COMPAT_H_
#define CLIENT_MAC_HANDLER_UCONTEXT_COMPAT_H_

#include <Availability.h>
#include <sys/ucontext.h>

// do not have sense at least for mac 10.15
#if defined(MAC_OS_X_VERSION_10_15)
typedef ucontext_t breakpad_ucontext_t;
#define breakpad_uc_mcontext uc_mcontext
#else
// The purpose of this file is to work around the fact that ucontext_t's
// uc_mcontext member is an mcontext_t rather than an mcontext64_t on ARM64.
#if defined(__aarch64__)
Expand All @@ -42,5 +48,6 @@ typedef ucontext64_t breakpad_ucontext_t;
typedef ucontext_t breakpad_ucontext_t;
#define breakpad_uc_mcontext uc_mcontext
#endif // defined(__aarch64__)
#endif

#endif // CLIENT_MAC_HANDLER_UCONTEXT_COMPAT_H_