-
Notifications
You must be signed in to change notification settings - Fork 3
feat: add initial support for pollcatch #18
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,8 @@ use std::time::Duration; | |
| use aws_config::BehaviorVersion; | ||
| use clap::Parser; | ||
|
|
||
| mod slow; | ||
|
|
||
| pub fn set_up_tracing() { | ||
| use tracing_subscriber::{prelude::*, EnvFilter}; | ||
|
|
||
|
|
@@ -34,8 +36,21 @@ struct Args { | |
| bucket: String, | ||
| } | ||
|
|
||
| #[tokio::main] | ||
| async fn main() -> Result<(), anyhow::Error> { | ||
| #[allow(unexpected_cfgs)] | ||
| pub fn main() -> anyhow::Result<()> { | ||
| let mut rt: tokio::runtime::Builder = tokio::runtime::Builder::new_multi_thread(); | ||
| rt.enable_all(); | ||
|
Comment on lines
+40
to
+42
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you file an issue for turning an example into an integration test that we can run to validate future changes?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. opened #19 |
||
|
|
||
| #[cfg(tokio_unstable)] | ||
| { | ||
| rt.on_before_task_poll(|_| async_profiler_agent::pollcatch::before_poll_hook()) | ||
| .on_after_task_poll(|_| async_profiler_agent::pollcatch::after_poll_hook()); | ||
| } | ||
| let rt = rt.build().unwrap(); | ||
| rt.block_on(main_internal()) | ||
| } | ||
|
|
||
| async fn main_internal() -> Result<(), anyhow::Error> { | ||
| set_up_tracing(); | ||
| tracing::info!("main started"); | ||
|
|
||
|
|
@@ -58,16 +73,6 @@ async fn main() -> Result<(), anyhow::Error> { | |
| profiler.spawn()?; | ||
| tracing::info!("profiler started"); | ||
|
|
||
| let sleep_secs = 6; | ||
| let sleep_duration = Duration::from_secs(sleep_secs); | ||
| let mut random_string: String = String::with_capacity(1); | ||
| loop { | ||
| random_string.push('a'); | ||
|
|
||
| tracing::info!("inside loop: going to sleep {sleep_secs} seconds"); | ||
| tokio::time::sleep(sleep_duration).await; | ||
| tracing::info!("application woke up"); | ||
|
|
||
| random_string.pop(); | ||
| } | ||
| slow::run().await; | ||
| Ok(()) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| #[inline(never)] | ||
| #[allow(deprecated)] | ||
| fn accidentally_slow() { | ||
| std::thread::sleep_ms(10); | ||
| std::hint::black_box(0); | ||
| } | ||
|
|
||
| #[inline(never)] | ||
| fn accidentally_slow_2() { | ||
| accidentally_slow(); | ||
| std::hint::black_box(0); | ||
| } | ||
|
|
||
| #[inline(never)] | ||
| fn short_sleep() { | ||
| std::thread::sleep(std::time::Duration::from_micros(100)); | ||
| std::hint::black_box(0); | ||
| } | ||
|
|
||
| #[inline(never)] | ||
| fn short_sleep_2() { | ||
| short_sleep(); | ||
| std::hint::black_box(0); | ||
| } | ||
|
|
||
| pub async fn run() { | ||
| let mut ts: Vec<tokio::task::JoinHandle<()>> = vec![]; | ||
| for _ in 0..16 { | ||
| ts.push(tokio::task::spawn(async move { | ||
| loop { | ||
| tokio::task::yield_now().await; | ||
| // make sure most time is spent in `short_sleep_2`, | ||
| // but `accidentally_slow_2` will cause long polls. | ||
| if rand::random::<f64>() < 0.001 { | ||
| accidentally_slow_2(); | ||
| } else { | ||
| short_sleep_2(); | ||
| } | ||
| } | ||
| })); | ||
| } | ||
| for t in ts { | ||
| t.await.ok(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,6 +4,7 @@ | |
| use std::{ | ||
| ffi::{c_char, CStr, CString}, | ||
| path::Path, | ||
| ptr::{self, addr_of}, | ||
| sync::Arc, | ||
| }; | ||
|
|
||
|
|
@@ -32,12 +33,65 @@ impl AsProfBuilder { | |
| } | ||
| } | ||
|
|
||
| #[derive(Copy, Clone, Debug)] | ||
| pub struct UserJfrKey { | ||
| key: i32, | ||
| } | ||
|
|
||
| pub struct AsProf {} | ||
|
|
||
| impl AsProf { | ||
| pub fn builder() -> AsProfBuilder { | ||
| AsProfBuilder::default() | ||
| } | ||
|
|
||
| /// Return the async-profiler's sample counter | ||
| pub fn get_sample_counter() -> Option<u64> { | ||
| unsafe { | ||
| let prof = raw::async_profiler().ok()?; | ||
| let thread_local_data = prof.asprof_get_thread_local_data.as_ref()?(); | ||
| if thread_local_data.is_null() { | ||
| None | ||
| } else { | ||
| Some(ptr::read_volatile(addr_of!( | ||
| (*thread_local_data).sample_counter | ||
| ))) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// Create a user JFR key from a given key | ||
| pub fn create_user_jfr_key(key: &CStr) -> Result<UserJfrKey, AsProfError> { | ||
| unsafe { | ||
| let prof = raw::async_profiler()?; | ||
| let asprof_register_jfr_event = | ||
| prof.asprof_register_jfr_event.as_ref().ok_or_else(|| { | ||
| AsProfError::AsyncProfilerError( | ||
| "async-profiler does not support user JFR events".into(), | ||
| ) | ||
| })?; | ||
| let res = asprof_register_jfr_event(key.as_ptr()); | ||
| if res < 0 { | ||
| Err(AsProfError::AsyncProfilerError( | ||
| "unable to register JFR event".into(), | ||
| )) | ||
| } else { | ||
| Ok(UserJfrKey { key: res }) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| pub fn emit_user_jfr(key: UserJfrKey, jfr: &[u8]) -> Result<(), AsProfError> { | ||
| unsafe { | ||
| let prof = raw::async_profiler()?; | ||
| let asprof_emit_jfr_event = prof.asprof_emit_jfr_event.as_ref().ok_or_else(|| { | ||
| AsProfError::AsyncProfilerError( | ||
| "async-profiler does not support user JFR events".into(), | ||
| ) | ||
| })?; | ||
| Self::asprof_error(asprof_emit_jfr_event(key.key, jfr.as_ptr(), jfr.len())) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl super::profiler::ProfilerEngine for AsProf { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this was copied from the previous agent. I can rename. |
||
|
|
@@ -69,6 +123,25 @@ impl super::profiler::ProfilerEngine for AsProf { | |
| } | ||
|
|
||
| impl AsProf { | ||
| /// convert an asprof_error_t to a Result | ||
| /// | ||
| /// SAFETY: response must be a valid asprof_error_t | ||
| unsafe fn asprof_error(response: raw::asprof_error_t) -> Result<(), AsProfError> { | ||
| if !response.is_null() { | ||
| let response = (raw::async_profiler()?.asprof_error_str)(response); | ||
| if response.is_null() { | ||
| return Ok(()); | ||
| } | ||
| let response = unsafe { CStr::from_ptr(response) }; | ||
| let response_str = response.to_string_lossy(); | ||
| tracing::error!("received error from async-profiler: {}", response_str); | ||
| Err(AsProfError::AsyncProfilerError(response_str.to_string())) | ||
| // TODO: stop the background thread in case there is an error | ||
| } else { | ||
| Ok(()) | ||
| } | ||
| } | ||
|
|
||
| fn asprof_execute(args: &str) -> Result<(), AsProfError> { | ||
| unsafe extern "C" fn callback(buf: *const c_char, size: usize) { | ||
| unsafe { | ||
|
|
@@ -85,17 +158,11 @@ impl AsProf { | |
| } | ||
|
|
||
| let args_compatible = CString::new(args).unwrap(); | ||
| let response = unsafe { | ||
| (raw::async_profiler()?.asprof_execute)(args_compatible.as_ptr(), Some(callback)) | ||
| }; | ||
| if !response.is_null() { | ||
| let response = unsafe { CStr::from_ptr(response) }; | ||
| let response_str = response.to_string_lossy(); | ||
| tracing::error!("received error from async-profiler: {}", response_str); | ||
| Err(AsProfError::AsyncProfilerError(response_str.to_string())) | ||
| // TODO: stop the background thread in case there is an error | ||
| } else { | ||
| Ok(()) | ||
| unsafe { | ||
| Self::asprof_error((raw::async_profiler()?.asprof_execute)( | ||
| args_compatible.as_ptr(), | ||
| Some(callback), | ||
| )) | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,5 +3,6 @@ | |
|
|
||
| mod asprof; | ||
| pub mod metadata; | ||
| pub mod pollcatch; | ||
| pub mod profiler; | ||
| pub mod reporter; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this has to be set in the builder right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yea