-
Notifications
You must be signed in to change notification settings - Fork 15
[crashtracking] Allow runtimes to register runtime stack collection callbacks #1252
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
Open
gyuheon0h
wants to merge
10
commits into
main
Choose a base branch
from
gyuheon0h/prof-12432-runtime-stacks
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,247
−2
Open
Changes from 6 commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
7b66803
First pass
gyuheon0h 5fad559
Dont pass in runtime type
gyuheon0h cc03667
Add bin tests
gyuheon0h 57fb3bd
Fix: lint + test
gyuheon0h e99acfe
Writer pointer magic
gyuheon0h 1b5d9eb
Test function sign update
gyuheon0h d353a4e
Schema update
gyuheon0h 66063f3
Remove module and class name field and update bin test
gyuheon0h 538a7f1
Line by line receiver bug fix
gyuheon0h 7a03e21
Test no callback registered
gyuheon0h File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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
131 changes: 131 additions & 0 deletions
131
bin_tests/src/modes/unix/test_010_runtime_callback_frame.rs
This file contains hidden or 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,131 @@ | ||
// Copyright 2024-Present Datadog, Inc. https://www.datadoghq.com/ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// This test validates the runtime stack collection callback mechanism using frame-by-frame mode. | ||
// It registers a test callback that provides mock runtime stack frames, | ||
// then crashes and verifies that the runtime frames appear in the crash report. | ||
// | ||
// This test uses CallbackType::Frame to emit structured runtime stack data. | ||
|
||
use crate::modes::behavior::{file_write_msg, Behavior}; | ||
use datadog_crashtracker::{ | ||
register_runtime_stack_callback, CallbackType, CrashtrackerConfiguration, RuntimeStackFrame, | ||
}; | ||
use std::ffi::c_char; | ||
use std::path::Path; | ||
use std::ptr; | ||
|
||
pub struct Test; | ||
|
||
impl Behavior for Test { | ||
fn setup( | ||
&self, | ||
output_dir: &Path, | ||
_config: &mut CrashtrackerConfiguration, | ||
) -> anyhow::Result<()> { | ||
// Write a marker file to indicate we're testing runtime callback frame mode | ||
let marker_file = output_dir.join("runtime_callback_test"); | ||
file_write_msg(&marker_file, "frame_mode")?; | ||
Ok(()) | ||
} | ||
|
||
fn pre(&self, _output_dir: &Path) -> anyhow::Result<()> { | ||
// Register our test runtime callback before crashtracker initialization | ||
register_runtime_stack_callback(test_runtime_callback_frame, CallbackType::Frame) | ||
.map_err(|e| anyhow::anyhow!("Failed to register runtime callback: {:?}", e))?; | ||
Ok(()) | ||
} | ||
|
||
fn post(&self, _output_dir: &Path) -> anyhow::Result<()> { | ||
// Nothing to do post-initialization for this test | ||
Ok(()) | ||
} | ||
} | ||
|
||
// Signal-safe test callback that emits mock runtime stack frames | ||
unsafe extern "C" fn test_runtime_callback_frame( | ||
emit_frame: unsafe extern "C" fn(*const RuntimeStackFrame), | ||
_emit_stacktrace_string: unsafe extern "C" fn(*const c_char), | ||
) { | ||
// Use static null-terminated strings to avoid allocation in signal context | ||
// In a real runtime, these would come from the runtime's managed string pool | ||
static FUNCTION_NAME_1: &[u8] = b"runtime_function_1\0"; | ||
static FUNCTION_NAME_2: &[u8] = b"runtime_function_2\0"; | ||
static FUNCTION_NAME_3: &[u8] = b"runtime_main\0"; | ||
static FILE_NAME_1: &[u8] = b"script.py\0"; | ||
static FILE_NAME_2: &[u8] = b"module.py\0"; | ||
static FILE_NAME_3: &[u8] = b"main.py\0"; | ||
static CLASS_NAME_1: &[u8] = b"TestClass\0"; | ||
static CLASS_NAME_2: &[u8] = b"MyModule\0"; | ||
static MODULE_NAME_1: &[u8] = b"test_module\0"; | ||
static MODULE_NAME_2: &[u8] = b"my_package.submodule\0"; | ||
static MODULE_NAME_3: &[u8] = b"__main__\0"; | ||
|
||
// Frame 1: runtime_function_1 in script.py | ||
let frame1 = RuntimeStackFrame { | ||
function_name: FUNCTION_NAME_1.as_ptr() as *const c_char, | ||
file_name: FILE_NAME_1.as_ptr() as *const c_char, | ||
line_number: 42, | ||
column_number: 15, | ||
class_name: CLASS_NAME_1.as_ptr() as *const c_char, | ||
module_name: MODULE_NAME_1.as_ptr() as *const c_char, | ||
}; | ||
emit_frame(&frame1); | ||
|
||
// Frame 2: runtime_function_2 in module.py | ||
let frame2 = RuntimeStackFrame { | ||
function_name: FUNCTION_NAME_2.as_ptr() as *const c_char, | ||
file_name: FILE_NAME_2.as_ptr() as *const c_char, | ||
line_number: 100, | ||
column_number: 8, | ||
class_name: CLASS_NAME_2.as_ptr() as *const c_char, | ||
module_name: MODULE_NAME_2.as_ptr() as *const c_char, | ||
}; | ||
emit_frame(&frame2); | ||
|
||
// Frame 3: runtime_main in main.py (no class) | ||
let frame3 = RuntimeStackFrame { | ||
function_name: FUNCTION_NAME_3.as_ptr() as *const c_char, | ||
file_name: FILE_NAME_3.as_ptr() as *const c_char, | ||
line_number: 10, | ||
column_number: 1, | ||
class_name: ptr::null(), // No class for main function | ||
module_name: MODULE_NAME_3.as_ptr() as *const c_char, | ||
}; | ||
emit_frame(&frame3); | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use datadog_crashtracker::{clear_runtime_callback, is_runtime_callback_registered}; | ||
|
||
#[test] | ||
fn test_runtime_callback_frame_registration() { | ||
// Ensure clean state | ||
unsafe { | ||
clear_runtime_callback(); | ||
} | ||
|
||
// Test that no callback is initially registered | ||
assert!(!is_runtime_callback_registered()); | ||
|
||
// Test frame mode registration | ||
let result = | ||
register_runtime_stack_callback(test_runtime_callback_frame, CallbackType::Frame); | ||
assert!(result.is_ok(), "Frame callback registration should succeed"); | ||
assert!( | ||
is_runtime_callback_registered(), | ||
"Callback should be registered" | ||
); | ||
|
||
// Clean up | ||
unsafe { | ||
clear_runtime_callback(); | ||
} | ||
assert!( | ||
!is_runtime_callback_registered(), | ||
"Callback should be cleared" | ||
); | ||
} | ||
} |
102 changes: 102 additions & 0 deletions
102
bin_tests/src/modes/unix/test_011_runtime_callback_string.rs
This file contains hidden or 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,102 @@ | ||
// Copyright 2024-Present Datadog, Inc. https://www.datadoghq.com/ | ||
// SPDX-License-Identifier: Apache-2.0 | ||
// | ||
// This test validates the runtime stack collection callback mechanism using stacktrace string mode. | ||
// It registers a test callback that provides a complete stacktrace string, | ||
// then crashes and verifies that the runtime stacktrace appears in the crash report. | ||
// | ||
// This test uses CallbackType::StacktraceString to emit complete stacktrace text. | ||
// | ||
// NOTE: This test currently has issues with the receiver's line-by-line parsing | ||
// of multiline stacktrace strings. It serves as a demonstration of the string mode | ||
// but may timeout due to receiver implementation details. | ||
|
||
use crate::modes::behavior::{file_write_msg, Behavior}; | ||
use datadog_crashtracker::{ | ||
register_runtime_stack_callback, CallbackType, CrashtrackerConfiguration, | ||
}; | ||
use std::ffi::{c_char, c_void}; | ||
use std::path::Path; | ||
|
||
pub struct Test; | ||
|
||
impl Behavior for Test { | ||
fn setup( | ||
&self, | ||
output_dir: &Path, | ||
_config: &mut CrashtrackerConfiguration, | ||
) -> anyhow::Result<()> { | ||
// Write a marker file to indicate we're testing runtime callback string mode | ||
let marker_file = output_dir.join("runtime_callback_test"); | ||
file_write_msg(&marker_file, "string_mode")?; | ||
Ok(()) | ||
} | ||
|
||
fn pre(&self, _output_dir: &Path) -> anyhow::Result<()> { | ||
// Register our test runtime callback before crashtracker initialization | ||
register_runtime_stack_callback( | ||
test_runtime_callback_string, | ||
CallbackType::StacktraceString, | ||
) | ||
.map_err(|e| anyhow::anyhow!("Failed to register runtime callback: {:?}", e))?; | ||
Ok(()) | ||
} | ||
|
||
fn post(&self, _output_dir: &Path) -> anyhow::Result<()> { | ||
// Nothing to do post-initialization for this test | ||
Ok(()) | ||
} | ||
} | ||
|
||
// Signal-safe test callback that emits a complete stacktrace string | ||
unsafe extern "C" fn test_runtime_callback_string( | ||
_emit_frame: unsafe extern "C" fn(*const datadog_crashtracker::RuntimeStackFrame), | ||
emit_stacktrace_string: unsafe extern "C" fn(*const c_char), | ||
) { | ||
// Use static null-terminated string to avoid allocation in signal context | ||
// IMPORTANT: No embedded newlines - the receiver processes this line by line | ||
static STACKTRACE: &[u8] = b"RuntimeError in script.py:42 runtime_function_1 -> module.py:100 runtime_function_2 -> main.py:10 runtime_main\0"; | ||
|
||
// Emit the complete stacktrace string | ||
emit_stacktrace_string(STACKTRACE.as_ptr() as *const c_char); | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
use datadog_crashtracker::{clear_runtime_callback, is_runtime_callback_registered}; | ||
|
||
#[test] | ||
fn test_runtime_callback_string_registration() { | ||
// Ensure clean state | ||
unsafe { | ||
clear_runtime_callback(); | ||
} | ||
|
||
// Test that no callback is initially registered | ||
assert!(!is_runtime_callback_registered()); | ||
|
||
// Test string mode registration | ||
let result = register_runtime_stack_callback( | ||
test_runtime_callback_string, | ||
CallbackType::StacktraceString, | ||
); | ||
assert!( | ||
result.is_ok(), | ||
"String callback registration should succeed" | ||
); | ||
assert!( | ||
is_runtime_callback_registered(), | ||
"Callback should be registered" | ||
); | ||
|
||
// Clean up | ||
unsafe { | ||
clear_runtime_callback(); | ||
} | ||
assert!( | ||
!is_runtime_callback_registered(), | ||
"Callback should be cleared" | ||
); | ||
} | ||
} |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.