-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement crash-tracking handler (#282)
- Loading branch information
Showing
20 changed files
with
1,838 additions
and
275 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
cmake_minimum_required(VERSION 3.19) | ||
project(datadog_profiling_crashtracking_reciever LANGUAGES C CXX) | ||
|
||
find_package(Datadog REQUIRED) | ||
|
||
add_executable(libdatadog-crashtracking-receiver libdatadog-crashtracking-receiver.c) | ||
target_link_libraries(libdatadog-crashtracking-receiver PRIVATE Datadog::Profiling) |
18 changes: 18 additions & 0 deletions
18
profiling-crashtracking-receiver/libdatadog-crashtracking-receiver.c
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2024-Present Datadog, Inc. | ||
|
||
#include <datadog/common.h> | ||
#include <datadog/profiling.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
|
||
int main(void) { | ||
ddog_prof_Profile_Result new_result = ddog_prof_crashtracker_receiver_entry_point(); | ||
if (new_result.tag != DDOG_PROF_PROFILE_NEW_RESULT_OK) { | ||
ddog_CharSlice message = ddog_Error_message(&new_result.err); | ||
fprintf(stderr, "%*s", (int)message.len, message.ptr); | ||
ddog_Error_drop(&new_result.err); | ||
exit(EXIT_FAILURE); | ||
} | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
// Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. | ||
// This product includes software developed at Datadog (https://www.datadoghq.com/). Copyright 2021-Present Datadog, Inc. | ||
#![cfg(unix)] | ||
|
||
use crate::exporter::{self, Endpoint}; | ||
use crate::profiles::ProfileResult; | ||
use datadog_profiling::crashtracker; | ||
use ddcommon::tag::Tag; | ||
use ddcommon_ffi::slice::{AsBytes, CharSlice}; | ||
use ddcommon_ffi::Error; | ||
use std::ops::Not; | ||
|
||
pub use datadog_profiling::crashtracker::{CrashtrackerResolveFrames, ProfilingOpTypes}; | ||
|
||
#[repr(C)] | ||
pub struct CrashtrackerConfiguration<'a> { | ||
pub create_alt_stack: bool, | ||
/// The endpoint to send the crash repor to (can be a file://) | ||
pub endpoint: Endpoint<'a>, | ||
/// Optional filename to forward stderr to (useful for logging/debugging) | ||
pub optional_stderr_filename: CharSlice<'a>, | ||
/// Optional filename to forward stdout to (useful for logging/debugging) | ||
pub optional_stdout_filename: CharSlice<'a>, | ||
pub path_to_receiver_binary: CharSlice<'a>, | ||
/// Whether/when we should attempt to resolve frames | ||
pub resolve_frames: CrashtrackerResolveFrames, | ||
} | ||
|
||
impl<'a> TryFrom<CrashtrackerConfiguration<'a>> for crashtracker::CrashtrackerConfiguration { | ||
type Error = anyhow::Error; | ||
fn try_from(value: CrashtrackerConfiguration<'a>) -> anyhow::Result<Self> { | ||
fn option_from_char_slice(s: CharSlice) -> anyhow::Result<Option<String>> { | ||
let s = unsafe { s.try_to_utf8()?.to_string() }; | ||
Ok(s.is_empty().not().then_some(s)) | ||
} | ||
|
||
let create_alt_stack = value.create_alt_stack; | ||
let endpoint = unsafe { Some(exporter::try_to_endpoint(value.endpoint)?) }; | ||
let path_to_receiver_binary = | ||
unsafe { value.path_to_receiver_binary.try_to_utf8()?.to_string() }; | ||
let resolve_frames = value.resolve_frames; | ||
let stderr_filename = option_from_char_slice(value.optional_stderr_filename)?; | ||
let stdout_filename = option_from_char_slice(value.optional_stdout_filename)?; | ||
|
||
crashtracker::CrashtrackerConfiguration::new( | ||
create_alt_stack, | ||
endpoint, | ||
path_to_receiver_binary, | ||
resolve_frames, | ||
stderr_filename, | ||
stdout_filename, | ||
) | ||
} | ||
} | ||
|
||
#[repr(C)] | ||
pub struct CrashtrackerMetadata<'a> { | ||
pub profiling_library_name: CharSlice<'a>, | ||
pub profiling_library_version: CharSlice<'a>, | ||
pub family: CharSlice<'a>, | ||
/// Should include "service", "environment", etc | ||
pub tags: Option<&'a ddcommon_ffi::Vec<Tag>>, | ||
} | ||
|
||
impl<'a> TryFrom<CrashtrackerMetadata<'a>> for crashtracker::CrashtrackerMetadata { | ||
type Error = anyhow::Error; | ||
fn try_from(value: CrashtrackerMetadata<'a>) -> anyhow::Result<Self> { | ||
let profiling_library_name = | ||
unsafe { value.profiling_library_name.try_to_utf8()?.to_string() }; | ||
let profiling_library_version = | ||
unsafe { value.profiling_library_version.try_to_utf8()?.to_string() }; | ||
let family = unsafe { value.family.try_to_utf8()?.to_string() }; | ||
let tags = value | ||
.tags | ||
.map(|tags| tags.iter().cloned().collect()) | ||
.unwrap_or_default(); | ||
Ok(crashtracker::CrashtrackerMetadata::new( | ||
profiling_library_name, | ||
profiling_library_version, | ||
family, | ||
tags, | ||
)) | ||
} | ||
} | ||
|
||
#[no_mangle] | ||
#[must_use] | ||
pub unsafe extern "C" fn ddog_prof_crashtracker_begin_profiling_op( | ||
op: ProfilingOpTypes, | ||
) -> ProfileResult { | ||
match crashtracker::begin_profiling_op(op) { | ||
Ok(_) => ProfileResult::Ok(true), | ||
Err(err) => ProfileResult::Err(Error::from( | ||
err.context("ddog_prof_crashtracker_init failed"), | ||
)), | ||
} | ||
} | ||
|
||
#[no_mangle] | ||
#[must_use] | ||
pub unsafe extern "C" fn ddog_prof_crashtracker_end_profiling_op( | ||
op: ProfilingOpTypes, | ||
) -> ProfileResult { | ||
match crashtracker::end_profiling_op(op) { | ||
Ok(_) => ProfileResult::Ok(true), | ||
Err(err) => ProfileResult::Err(Error::from( | ||
err.context("ddog_prof_crashtracker_init failed"), | ||
)), | ||
} | ||
} | ||
|
||
#[no_mangle] | ||
#[must_use] | ||
pub unsafe extern "C" fn ddog_prof_crashtracker_shutdown() -> ProfileResult { | ||
match crashtracker::shutdown_crash_handler() { | ||
Ok(_) => ProfileResult::Ok(true), | ||
Err(err) => ProfileResult::Err(Error::from( | ||
err.context("ddog_prof_crashtracker_init failed"), | ||
)), | ||
} | ||
} | ||
|
||
#[no_mangle] | ||
#[must_use] | ||
pub unsafe extern "C" fn ddog_prof_crashtracker_update_on_fork( | ||
config: CrashtrackerConfiguration, | ||
metadata: CrashtrackerMetadata, | ||
) -> ProfileResult { | ||
match ddog_prof_crashtracker_update_on_fork_impl(config, metadata) { | ||
Ok(_) => ProfileResult::Ok(true), | ||
Err(err) => ProfileResult::Err(Error::from( | ||
err.context("ddog_prof_crashtracker_init failed"), | ||
)), | ||
} | ||
} | ||
|
||
unsafe fn ddog_prof_crashtracker_update_on_fork_impl( | ||
config: CrashtrackerConfiguration, | ||
metadata: CrashtrackerMetadata, | ||
) -> anyhow::Result<()> { | ||
let config = config.try_into()?; | ||
let metadata = metadata.try_into()?; | ||
crashtracker::on_fork(config, metadata) | ||
} | ||
|
||
#[no_mangle] | ||
#[must_use] | ||
pub unsafe extern "C" fn ddog_prof_crashtracker_receiver_entry_point() -> ProfileResult { | ||
match crashtracker::receiver_entry_point() { | ||
Ok(_) => ProfileResult::Ok(true), | ||
Err(err) => ProfileResult::Err(Error::from( | ||
err.context("ddog_prof_crashtracker_receiver_entry_point failed"), | ||
)), | ||
} | ||
} | ||
|
||
#[no_mangle] | ||
#[must_use] | ||
pub unsafe extern "C" fn ddog_prof_crashtracker_init( | ||
config: CrashtrackerConfiguration, | ||
metadata: CrashtrackerMetadata, | ||
) -> ProfileResult { | ||
match ddog_prof_crashtracker_init_impl(config, metadata) { | ||
Ok(_) => ProfileResult::Ok(true), | ||
Err(err) => ProfileResult::Err(Error::from( | ||
err.context("ddog_prof_crashtracker_init failed"), | ||
)), | ||
} | ||
} | ||
|
||
unsafe fn ddog_prof_crashtracker_init_impl( | ||
config: CrashtrackerConfiguration, | ||
metadata: CrashtrackerMetadata, | ||
) -> anyhow::Result<()> { | ||
let config = config.try_into()?; | ||
let metadata = metadata.try_into()?; | ||
crashtracker::init(config, metadata) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
# Libdatadog Crashtracker | ||
This module implements a crash tracker that enables libdatadog using profilers to send crash-reports to the datadog backend. | ||
|
||
It has three related parts: | ||
1. A `CrashInfo` struct which stores information about a crash in a structured format, and has built-in functions to upload itself to the datadog backend. | ||
This struct can be generated by the crash-handlers described below (e.g. for python or Ruby), or by language runtime tools from a crash dump (e.g. .Net). | ||
|
||
2. A UNIX crash-handler, which registers signal handlers to detect crashes. | ||
The legal operations within a crash-handler are limited, so the handler does as much out of process as possible. | ||
When a crash occurs, this handler collects relevant information, and puts it on a pipe to the receiver, which processes it out of process. | ||
|
||
3. A binary application `libdatadog-crashtracking-receiver`, which receives the crash-report over a pipe and formats it for transmission to the backend. | ||
|
||
## How to use the crashhandler | ||
|
||
1. Initilize it using `ddog_prof_crashtracker_init` | ||
2. After a fork, reset the crashtracker in the child using `ddog_prof_crashtracker_update_on_fork`. | ||
This can be done in an `pthread_atfork` handler. | ||
2. [Optional]. The crash-tracker can be shutdown, and the previous crash handler restored, using `ddog_prof_crashtracker_shutdown`. | ||
Currently, there is a state machine that stops you from then restarting the crash-tracker. | ||
Fixing this is a todo | ||
|
||
## Todos | ||
|
||
- [ ] Support windows | ||
- [ ] API for generating crash report externally. | ||
- [ ] Instrumentation Telemetry support | ||
- [ ] Enable multiple shutdown/restart cycles for crashtracker | ||
- [ ] Enable dynamic update of metadata/configuration | ||
|
||
|
||
|
||
## How to configure crashtracking | ||
|
||
# |
Oops, something went wrong.