Skip to content
Draft
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
20 changes: 20 additions & 0 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -1636,6 +1636,26 @@ SENTRY_EXPERIMENTAL_API void sentry_options_set_before_screenshot(
sentry_options_t *opts, sentry_before_screenshot_function_t func,
void *user_data);

/**
* Enables capturing a short retroactive video clip on crash. Currently only
* supported on Xbox via the OS-managed game recording ring. The clip is
* attached to the crash envelope as `replay-clip.mp4`.
*
* Set the duration via `sentry_options_set_replay_clip_duration_ms` (default
* 5000 ms). Disabled by default. Must be set before `sentry_init`.
*/
SENTRY_EXPERIMENTAL_API void sentry_options_set_attach_replay_clip(
sentry_options_t *opts, int val);

/**
* Sets the requested duration of the retroactive replay clip in milliseconds.
*
* The resulting clip can be shorter than the requested duration if it hasn't
* accumulated enough buffered frames yet. Defaults to 5000 ms.
*/
SENTRY_EXPERIMENTAL_API void sentry_options_set_replay_clip_duration_ms(
sentry_options_t *opts, uint32_t duration_ms);

/**
* Sets the path to the crashpad handler if the crashpad backend is used.
*
Expand Down
9 changes: 9 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ sentry_target_sources_cwd(sentry
sentry_scope.c
sentry_scope.h
sentry_screenshot.h
sentry_replay_clip.h
sentry_session.c
sentry_session.h
sentry_slice.c
Expand All @@ -65,6 +66,7 @@ sentry_target_sources_cwd(sentry
sentry_tracing.h
path/sentry_path.c
screenshot/sentry_screenshot.c
replay_clip/sentry_replay_clip.c
transports/sentry_disk_transport.c
transports/sentry_disk_transport.h
transports/sentry_function_transport.c
Expand Down Expand Up @@ -280,3 +282,10 @@ elseif(NOT WIN32)
screenshot/sentry_screenshot_none.c
)
endif()

# replay clip
if(NOT XBOX)
sentry_target_sources_cwd(sentry
replay_clip/sentry_replay_clip_none.c
)
endif()
2 changes: 2 additions & 0 deletions src/backends/native/sentry_crash_context.h
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ typedef struct {
int crash_reporting_mode; // sentry_crash_reporting_mode_t
bool debug_enabled; // Debug logging enabled in parent process
bool attach_screenshot; // Screenshot attachment enabled in parent process
bool attach_replay_clip; // Replay clip attachment enabled in parent process
uint32_t replay_clip_duration_ms; // Requested replay clip duration in ms
bool cache_keep;
bool require_user_consent;
bool enable_large_attachments;
Expand Down
42 changes: 42 additions & 0 deletions src/backends/native/sentry_crash_daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "sentry_options.h"
#include "sentry_path.h"
#include "sentry_process.h"
#include "sentry_replay_clip.h"
#include "sentry_screenshot.h"
#include "sentry_string.h"
#include "sentry_symbolizer.h"
Expand Down Expand Up @@ -2504,6 +2505,17 @@ write_envelope_with_native_stacktrace(const sentry_options_t *options,
}
}

// Add replay clip attachment if captured by the daemon
if (ctx->attach_replay_clip && run_folder) {
sentry_path_t *clip_path
= sentry__path_join_str(run_folder, "replay-clip.mp4");
if (clip_path) {
write_attachment_to_envelope(
fd, clip_path->path, "replay-clip.mp4", "video/mp4");
sentry__path_free(clip_path);
}
}

#if defined(SENTRY_PLATFORM_UNIX)
close(fd);
#elif defined(SENTRY_PLATFORM_WINDOWS)
Expand Down Expand Up @@ -2740,6 +2752,17 @@ write_envelope_with_minidump(const sentry_options_t *options,
}
}

// Add replay clip attachment if captured by the daemon
if (ctx->attach_replay_clip && run_folder) {
sentry_path_t *clip_path
= sentry__path_join_str(run_folder, "replay-clip.mp4");
if (clip_path) {
write_attachment_to_envelope(
fd, clip_path->path, "replay-clip.mp4", "video/mp4");
sentry__path_free(clip_path);
}
}

#if defined(SENTRY_PLATFORM_UNIX)
close(fd);
#elif defined(SENTRY_PLATFORM_WINDOWS)
Expand Down Expand Up @@ -2922,6 +2945,23 @@ sentry__process_crash(const sentry_options_t *options, sentry_crash_ipc_t *ipc)
sentry__path_free(screenshot_path);
}
}

// Capture replay clip if enabled. Like screenshot, this runs out-of-process
// because the underlying OS APIs are not signal-safe.
if (ctx->attach_replay_clip && run_folder) {
SENTRY_DEBUG("Capturing replay clip");
sentry_path_t *clip_path
= sentry__path_join_str(run_folder, "replay-clip.mp4");
if (clip_path) {
if (sentry__replay_clip_capture(clip_path,
ctx->replay_clip_duration_ms, (uint32_t)ctx->crashed_pid)) {
SENTRY_DEBUG("Replay clip captured successfully");
} else {
SENTRY_DEBUG("Replay clip capture failed");
}
sentry__path_free(clip_path);
}
}
#endif

// On Linux, capture modules and threads from /proc for native mode
Expand Down Expand Up @@ -3289,6 +3329,8 @@ sentry__crash_daemon_main(pid_t app_pid, uint64_t app_tid, HANDLE event_handle,
// Use debug logging and screenshot settings from parent process
sentry_options_set_debug(options, ipc->shmem->debug_enabled);
options->attach_screenshot = ipc->shmem->attach_screenshot;
options->attach_replay_clip = ipc->shmem->attach_replay_clip;
options->replay_clip_duration_ms = ipc->shmem->replay_clip_duration_ms;
options->cache_keep = ipc->shmem->cache_keep;
options->enable_large_attachments = ipc->shmem->enable_large_attachments;
options->http_retry = false;
Expand Down
13 changes: 13 additions & 0 deletions src/backends/sentry_backend_breakpad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ extern "C" {
# include "sentry_os.h"
#endif
#include "sentry_path.h"
#include "sentry_replay_clip.h"
#include "sentry_screenshot.h"
#include "sentry_string.h"
#include "sentry_sync.h"
Expand Down Expand Up @@ -219,6 +220,18 @@ breakpad_backend_callback(const google_breakpad::MinidumpDescriptor &descriptor,
sentry__attachment_free(screenshot);
}

if (options->attach_replay_clip) {
sentry_attachment_t *clip = sentry__attachment_from_path(
sentry__replay_clip_get_path(options));
if (clip
&& sentry__replay_clip_capture(
clip->path, options->replay_clip_duration_ms, 0)) {
sentry__envelope_add_attachment(envelope, clip);
} else {
sentry__attachment_free(clip);
}
}

if (!sentry__launch_external_crash_reporter(options, envelope)) {
// capture the envelope with the disk transport
sentry_transport_t *disk_transport
Expand Down
13 changes: 13 additions & 0 deletions src/backends/sentry_backend_inproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
# include "sentry_os.h"
# include <signal.h>
#endif
#include "sentry_replay_clip.h"
#include "sentry_scope.h"
#include "sentry_screenshot.h"
#include "sentry_sync.h"
Expand Down Expand Up @@ -1202,6 +1203,18 @@ process_ucontext_deferred(const sentry_ucontext_t *uctx,
sentry__attachment_free(screenshot);
}

if (options->attach_replay_clip) {
sentry_attachment_t *clip = sentry__attachment_from_path(
sentry__replay_clip_get_path(options));
if (clip
&& sentry__replay_clip_capture(
clip->path, options->replay_clip_duration_ms, 0)) {
sentry__envelope_add_attachment(envelope, clip);
} else {
sentry__attachment_free(clip);
}
}

if (!sentry__launch_external_crash_reporter(options, envelope)) {
// capture the envelope with the disk transport
sentry_transport_t *disk_transport
Expand Down
2 changes: 2 additions & 0 deletions src/backends/sentry_backend_native.c
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ native_backend_startup(
// Pass debug logging setting to daemon
ctx->debug_enabled = options->debug;
ctx->attach_screenshot = options->attach_screenshot;
ctx->attach_replay_clip = options->attach_replay_clip;
ctx->replay_clip_duration_ms = options->replay_clip_duration_ms;
ctx->cache_keep = options->cache_keep;
ctx->require_user_consent = options->require_user_consent;
ctx->enable_large_attachments = options->enable_large_attachments;
Expand Down
7 changes: 7 additions & 0 deletions src/replay_clip/sentry_replay_clip.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "sentry_replay_clip.h"

sentry_path_t *
sentry__replay_clip_get_path(const sentry_options_t *options)
{
return sentry__path_join_str(options->run->run_path, "replay-clip.mp4");
}
10 changes: 10 additions & 0 deletions src/replay_clip/sentry_replay_clip_none.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#include "sentry_replay_clip.h"

#include "sentry_core.h"

bool
sentry__replay_clip_capture(const sentry_path_t *UNUSED(path),
uint32_t UNUSED(duration_ms), uint32_t UNUSED(pid))
{
return false;
}
15 changes: 15 additions & 0 deletions src/sentry_options.c
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ sentry_options_new(void)
opts->auto_session_tracking = true;
opts->system_crash_reporter_enabled = false;
opts->attach_screenshot = false;
opts->attach_replay_clip = false;
opts->replay_clip_duration_ms = 5000;
opts->crashpad_wait_for_upload = false;
// On macOS, breakpad suspends all other threads of the process before
// writing the minidump and invokes our backend callback from inside
Expand Down Expand Up @@ -651,6 +653,19 @@ sentry_options_set_before_screenshot(sentry_options_t *opts,
opts->before_screenshot_data = user_data;
}

void
sentry_options_set_attach_replay_clip(sentry_options_t *opts, int val)
{
opts->attach_replay_clip = !!val;
}

void
sentry_options_set_replay_clip_duration_ms(
sentry_options_t *opts, uint32_t duration_ms)
{
opts->replay_clip_duration_ms = duration_ms;
}

void
sentry_options_set_handler_path(sentry_options_t *opts, const char *path)
{
Expand Down
2 changes: 2 additions & 0 deletions src/sentry_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ struct sentry_options_s {
bool attach_screenshot;
sentry_before_screenshot_function_t before_screenshot_func;
void *before_screenshot_data;
bool attach_replay_clip;
uint32_t replay_clip_duration_ms;
bool crashpad_wait_for_upload;
bool enable_logging_when_crashed;
bool propagate_traceparent;
Expand Down
27 changes: 27 additions & 0 deletions src/sentry_replay_clip.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#ifndef SENTRY_REPLAY_CLIP_H_INCLUDED
#define SENTRY_REPLAY_CLIP_H_INCLUDED

#include "sentry_boot.h"

#include "sentry_options.h"
#include "sentry_path.h"

/**
* Captures a short retroactive video clip and saves it to the specified path.
*
* @param path The path where the clip should be saved (typically MP4).
* @param duration_ms The requested duration in milliseconds.
* @param pid The process ID whose output should be captured (0 = current
* process).
*
* Returns true if the clip was successfully captured and saved.
*/
bool sentry__replay_clip_capture(
const sentry_path_t *path, uint32_t duration_ms, uint32_t pid);

/**
* Returns the path where a replay clip should be saved.
*/
sentry_path_t *sentry__replay_clip_get_path(const sentry_options_t *options);

#endif
Loading