Skip to content

Commit

Permalink
wrap macro
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsn committed Nov 21, 2024
1 parent 175369a commit 8c2b85c
Show file tree
Hide file tree
Showing 2 changed files with 290 additions and 0 deletions.
270 changes: 270 additions & 0 deletions crashtracker-ffi/src/crash_info/stacktrace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,270 @@
// Copyright 2024-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

use std::ptr;

use super::to_inner::ToInner;
use crate::{option_from_char_slice, Result};
use ::function_name::named;
use anyhow::Context;
use ddcommon_ffi::{
slice::{AsBytes, ByteSlice},
CharSlice, Error, Slice,
};

/// Represents a StackFrame. Do not access its member for any reason, only use
/// the C API functions on this struct.
#[repr(C)]
pub struct StackFrame {
// This may be null, but if not it will point to a valid StackFrame.
inner: *mut datadog_crashtracker::rfc5_crash_info::StackFrame,
}

impl ToInner for StackFrame {
type Inner = datadog_crashtracker::rfc5_crash_info::StackFrame;

unsafe fn to_inner_mut(&mut self) -> anyhow::Result<&mut Self::Inner> {
self.inner
.as_mut()
.context("inner pointer was null, indicates use after free")
}
}

impl StackFrame {
pub(super) fn new(crash_info: datadog_crashtracker::rfc5_crash_info::StackFrame) -> Self {
StackFrame {
inner: Box::into_raw(Box::new(crash_info)),
}
}

pub(super) fn take(
&mut self,
) -> Option<Box<datadog_crashtracker::rfc5_crash_info::StackFrame>> {
// Leaving a null will help with double-free issues that can
// arise in C. Of course, it's best to never get there in the
// first place!
let raw = std::mem::replace(&mut self.inner, std::ptr::null_mut());

if raw.is_null() {
None
} else {
Some(unsafe { Box::from_raw(raw) })
}
}
}

impl Drop for StackFrame {
fn drop(&mut self) {
drop(self.take())
}
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// FFI API //
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Returned by [ddog_prof_Profile_new].
#[repr(C)]
pub enum StackFrameNewResult {
Ok(StackFrame),
#[allow(dead_code)]
Err(Error),
}

/// Create a new StackFrame, and returns an opaque reference to it.
/// # Safety
/// No safety issues.
#[no_mangle]
#[must_use]
pub unsafe extern "C" fn ddog_crasht_StackFrame_new() -> StackFrameNewResult {
StackFrameNewResult::Ok(StackFrame::new(
datadog_crashtracker::rfc5_crash_info::StackFrame::default(),
))
}

/// # Safety
/// The `frame` can be null, but if non-null it must point to a Frame
/// made by this module, which has not previously been dropped.
#[no_mangle]
pub unsafe extern "C" fn ddog_crasht_StackFrame_drop(frame: *mut StackFrame) {
// Technically, this function has been designed so if it's double-dropped
// then it's okay, but it's not something that should be relied on.
if !frame.is_null() {
drop((*frame).take())
}
}

/// # Safety
/// The `frame` can be null, but if non-null it must point to a Frame made by this module,
/// which has not previously been dropped.
/// The CharSlice must be valid.
#[no_mangle]
#[named]
pub unsafe extern "C" fn ddog_crasht_StackFrame_with_ip(
mut frame: *mut StackFrame,
ip: CharSlice,
) -> Result {
(|| {
let frame = frame.to_inner_mut()?;
frame.ip = option_from_char_slice(ip)?;
anyhow::Ok(())
})()
.context(concat!(function_name!(), " failed"))
.into()
}

/// # Safety
/// The `frame` can be null, but if non-null it must point to a Frame made by this module,
/// which has not previously been dropped.
/// The CharSlice must be valid.
#[no_mangle]
#[named]
pub unsafe extern "C" fn ddog_crasht_StackFrame_with_module_base_address(
mut frame: *mut StackFrame,
module_base_address: CharSlice,
) -> Result {
(|| {
let frame = frame.to_inner_mut()?;
frame.module_base_address = option_from_char_slice(module_base_address)?;
anyhow::Ok(())
})()
.context(concat!(function_name!(), " failed"))
.into()
}

/// # Safety
/// The `frame` can be null, but if non-null it must point to a Frame made by this module,
/// which has not previously been dropped.
/// The CharSlice must be valid.
#[no_mangle]
#[named]
pub unsafe extern "C" fn ddog_crasht_StackFrame_with_sp(
mut frame: *mut StackFrame,
sp: CharSlice,
) -> Result {
(|| {
let frame = frame.to_inner_mut()?;
frame.sp = option_from_char_slice(sp)?;
anyhow::Ok(())
})()
.context(concat!(function_name!(), " failed"))
.into()
}

/// # Safety
/// The `frame` can be null, but if non-null it must point to a Frame made by this module,
/// which has not previously been dropped.
/// The CharSlice must be valid.
#[no_mangle]
#[named]
pub unsafe extern "C" fn ddog_crasht_StackFrame_with_symbol_address(
mut frame: *mut StackFrame,
symbol_address: CharSlice,
) -> Result {
(|| {
let frame = frame.to_inner_mut()?;
frame.symbol_address = option_from_char_slice(symbol_address)?;
anyhow::Ok(())
})()
.context(concat!(function_name!(), " failed"))
.into()
}

/// # Safety
/// The `frame` can be null, but if non-null it must point to a Frame made by this module,
/// which has not previously been dropped.
/// The CharSlice must be valid.
#[no_mangle]
#[named]
pub unsafe extern "C" fn ddog_crasht_StackFrame_with_build_id(
mut frame: *mut StackFrame,
build_id: CharSlice,
) -> Result {
(|| {
let frame = frame.to_inner_mut()?;
frame.build_id = option_from_char_slice(build_id)?;
anyhow::Ok(())
})()
.context(concat!(function_name!(), " failed"))
.into()
}

/// # Safety
/// The `frame` can be null, but if non-null it must point to a Frame made by this module,
/// which has not previously been dropped.
/// The CharSlice must be valid.
#[no_mangle]
#[named]
pub unsafe extern "C" fn ddog_crasht_StackFrame_with_path(
mut frame: *mut StackFrame,
path: CharSlice,
) -> Result {
(|| {
let frame = frame.to_inner_mut()?;
frame.path = option_from_char_slice(path)?;
anyhow::Ok(())
})()
.context(concat!(function_name!(), " failed"))
.into()
}

/// # Safety
/// The `frame` can be null, but if non-null it must point to a Frame made by this module,
/// which has not previously been dropped.
/// The CharSlice must be valid.
#[no_mangle]
#[named]
pub unsafe extern "C" fn ddog_crasht_StackFrame_with_relative_address(
mut frame: *mut StackFrame,
relative_address: CharSlice,
) -> Result {
(|| {
let frame = frame.to_inner_mut()?;
frame.relative_address = option_from_char_slice(relative_address)?;
anyhow::Ok(())
})()
.context(concat!(function_name!(), " failed"))
.into()
}

/// # Safety
/// The `frame` can be null, but if non-null it must point to a Frame made by this module,
/// which has not previously been dropped.
/// The CharSlice must be valid.
#[no_mangle]
#[named]
pub unsafe extern "C" fn ddog_crasht_StackFrame_with_file(
mut frame: *mut StackFrame,
file: CharSlice,
) -> Result {
(|| {
let frame = frame.to_inner_mut()?;
frame.file = option_from_char_slice(file)?;
anyhow::Ok(())
})()
.context(concat!(function_name!(), " failed"))
.into()
}
macro_rules! wrap {
($body:expr) => {
(|| $body)()
.context(concat!(function_name!(), " failed"))
.into()
};
}

/// # Safety
/// The `frame` can be null, but if non-null it must point to a Frame made by this module,
/// which has not previously been dropped.
/// The CharSlice must be valid.
#[no_mangle]
#[named]
pub unsafe extern "C" fn ddog_crasht_StackFrame_with_function(
mut frame: *mut StackFrame,
function: CharSlice,
) -> Result {
wrap!({
let frame = frame.to_inner_mut()?;
frame.function = option_from_char_slice(function)?;
anyhow::Ok(())
})
}
20 changes: 20 additions & 0 deletions crashtracker-ffi/src/crash_info/to_inner.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright 2024-Present Datadog, Inc. https://www.datadoghq.com/
// SPDX-License-Identifier: Apache-2.0

use anyhow::Context;

pub(crate) trait ToInner {
type Inner;
unsafe fn to_inner_mut(&mut self) -> anyhow::Result<&mut Self::Inner>;
}

impl<T> ToInner for *mut T
where
T: ToInner,
{
type Inner = T::Inner;
unsafe fn to_inner_mut(&mut self) -> anyhow::Result<&mut Self::Inner> {
let inner = self.as_mut().context("Null pointer")?;
inner.to_inner_mut()
}
}

0 comments on commit 8c2b85c

Please sign in to comment.