flame: add support for filtering and collapsing flamegraphs (#756)

* add support for filtering and collapsing flamegraphs

* apply changes from code review

* fix compile error

* Apply suggestions from code review

Co-authored-by: Eliza Weisman <eliza@buoyant.io>

* fix fmt

Co-authored-by: Eliza Weisman <eliza@buoyant.io>
This commit is contained in:
Jane Lusby 2020-06-22 16:32:44 -07:00 committed by GitHub
parent b36d59ddbe
commit 2027f727af
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -193,9 +193,28 @@ thread_local! {
#[derive(Debug)]
pub struct FlameLayer<S, W> {
out: Arc<Mutex<W>>,
config: Config,
_inner: PhantomData<S>,
}
#[derive(Debug)]
struct Config {
/// Don't include samples where no spans are open
empty_samples: bool,
/// Don't include thread_id
threads_collapsed: bool,
}
impl Default for Config {
fn default() -> Self {
Self {
empty_samples: true,
threads_collapsed: false,
}
}
}
/// An RAII guard for managing flushing a global writer that is
/// otherwise inaccessible.
///
@ -224,6 +243,7 @@ where
let _unused = *START;
Self {
out: Arc::new(Mutex::new(writer)),
config: Default::default(),
_inner: PhantomData,
}
}
@ -235,6 +255,39 @@ where
out: self.out.clone(),
}
}
/// Configures whether or not periods of time where no spans are entered
/// should be included in the output.
///
/// Defaults to `true`.
///
/// Setting this feature to false can help with situations where no span is
/// active for large periods of time. This can include time spent idling, or
/// doing uninteresting work that isn't being measured.
/// When a large number of empty samples are recorded, the flamegraph
/// may be harder to interpret and navigate, since the recorded spans will
/// take up a correspondingly smaller percentage of the graph. In some
/// cases, a large number of empty samples may even hide spans which
/// would otherwise appear in the flamegraph.
pub fn with_empty_samples(mut self, enabled: bool) -> Self {
self.config.empty_samples = enabled;
self
}
/// Configures whether or not spans from different threads should be
/// collapsed into one pool of events.
///
/// Defaults to `false`.
///
/// Setting this feature to true can help with applications that distribute
/// work evenly across many threads, such as thread pools. In such
/// cases it can be difficult to get an overview of where the application
/// as a whole spent most of its time, because work done in the same
/// span may be split up across many threads.
pub fn with_threads_collapsed(mut self, enabled: bool) -> Self {
self.config.threads_collapsed = enabled;
self
}
}
impl<W> FlushGuard<W>
@ -301,11 +354,20 @@ where
let samples = self.time_since_last_event();
let first = ctx.span(id).expect("expected: span id exists in registry");
if !self.config.empty_samples && first.from_root().count() == 0 {
return;
}
let parents = first.from_root();
let mut stack = String::new();
if !self.config.threads_collapsed {
THREAD_NAME.with(|name| stack += name.as_str());
} else {
stack += "all-threads";
}
for parent in parents {
stack += "; ";
@ -342,7 +404,11 @@ where
let parents = first.from_root();
let mut stack = String::new();
if !self.config.threads_collapsed {
THREAD_NAME.with(|name| stack += name.as_str());
} else {
stack += "all-threads";
}
stack += "; ";
for parent in parents {