diff --git a/src/bin/flamegraph.rs b/src/bin/flamegraph.rs index bac79d3b..0abb227e 100644 --- a/src/bin/flamegraph.rs +++ b/src/bin/flamegraph.rs @@ -82,7 +82,7 @@ struct Opt { short = "c", long = "colors", default_value = defaults::COLORS, - possible_values = &["aqua","blue","green","hot","io","java","js","mem","orange","perl","purple","red","wakeup","yellow"], + possible_values = &["annotated","aqua","blue","green","hot","io","java","js","mem","orange","perl","purple","red","wakeup","yellow","grey","gray"], value_name = "STRING" )] colors: Palette, diff --git a/src/flamegraph/color/mod.rs b/src/flamegraph/color/mod.rs index 85bdc36b..bec1f08c 100644 --- a/src/flamegraph/color/mod.rs +++ b/src/flamegraph/color/mod.rs @@ -66,21 +66,21 @@ impl Default for BackgroundColor { /// A flame graph color palette. /// -/// Defaults to [`BasicPalette::Hot`]. +/// Defaults to [`MultiPalette::Annotated`]. #[derive(Clone, Copy, Debug, PartialEq)] pub enum Palette { /// A plain color palette in which the color is not chosen based on function semantics. /// /// See [`BasicPalette`] for details. Basic(BasicPalette), - /// A semantic color palette in which different hues are used to signifiy semantic aspects of + /// A semantic color palette in which different hues are used to signify semantic aspects of /// different function names (kernel functions, JIT functions, etc.). Multi(MultiPalette), } impl Default for Palette { fn default() -> Self { - Palette::Basic(BasicPalette::Hot) + Palette::Multi(MultiPalette::Annotated) } } @@ -112,12 +112,16 @@ pub enum BasicPalette { Purple, /// A palette in which colors are chosen from a orange spectrum. Orange, + /// A palette in which colors are chosen from a gray spectrum. + Gray, } /// A semantic color palette in which different hues are used to signifiy semantic aspects of /// different function names (kernel functions, JIT functions, etc.). #[derive(Clone, Copy, Debug, PartialEq)] pub enum MultiPalette { + /// Use function annotations with no specific language heuristics to color frames. + Annotated, /// Use Java semantics to color frames. Java, /// Use JavaScript semantics to color frames. @@ -193,6 +197,7 @@ impl FromStr for Palette { "mem" => Ok(Palette::Basic(BasicPalette::Mem)), "io" => Ok(Palette::Basic(BasicPalette::Io)), "wakeup" => Ok(Palette::Multi(MultiPalette::Wakeup)), + "annotated" => Ok(Palette::Multi(MultiPalette::Annotated)), "java" => Ok(Palette::Multi(MultiPalette::Java)), "js" => Ok(Palette::Multi(MultiPalette::Js)), "perl" => Ok(Palette::Multi(MultiPalette::Perl)), @@ -203,6 +208,7 @@ impl FromStr for Palette { "yellow" => Ok(Palette::Basic(BasicPalette::Yellow)), "purple" => Ok(Palette::Basic(BasicPalette::Purple)), "orange" => Ok(Palette::Basic(BasicPalette::Orange)), + "gray" | "grey" => Ok(Palette::Basic(BasicPalette::Gray)), unknown => Err(format!("unknown color palette: {}", unknown)), } } @@ -304,6 +310,7 @@ macro_rules! color { fn rgb_components_for_palette(palette: Palette, name: &str, v1: f32, v2: f32, v3: f32) -> Color { let basic_palette = match palette { Palette::Basic(basic) => basic, + Palette::Multi(MultiPalette::Annotated) => palettes::annotated::resolve(name), Palette::Multi(MultiPalette::Java) => palettes::java::resolve(name), Palette::Multi(MultiPalette::Perl) => palettes::perl::resolve(name), Palette::Multi(MultiPalette::Js) => palettes::js::resolve(name), @@ -325,6 +332,11 @@ fn rgb_components_for_palette(palette: Palette, name: &str, v1: f32, v2: f32, v3 } BasicPalette::Aqua => color!(t!(50, 60_f32, v1), t!(165, 55_f32, v1), t!(165, 55_f32, v1)), BasicPalette::Orange => color!(t!(190, 65_f32, v1), t!(90, 65_f32, v1), t!(0, 0_f32, v1)), + BasicPalette::Gray => color!( + t!(190, 40_f32, v1), + t!(190, 40_f32, v1), + t!(190, 40_f32, v1) + ), } } diff --git a/src/flamegraph/color/palettes.rs b/src/flamegraph/color/palettes.rs index 46181476..beb07778 100644 --- a/src/flamegraph/color/palettes.rs +++ b/src/flamegraph/color/palettes.rs @@ -1,4 +1,44 @@ +enum Annotation { + Kernel, + Inline, + Jit, +} + +fn resolve_annotation(name: &str) -> Option { + if name.ends_with(']') { + if let Some(ai) = name.rfind("_[") { + if name[ai..].len() == 4 { + match &name[ai + 2..ai + 3] { + "k" => return Some(Annotation::Kernel), + "i" => return Some(Annotation::Inline), + "j" => return Some(Annotation::Jit), + _ => {} + } + } + } + } + + None +} + +pub(super) mod annotated { + use super::Annotation; + use crate::flamegraph::color::BasicPalette; + + /// Handle annotations (_[j], _[i], ...; which are + /// accurate) in a generic way. + pub fn resolve(name: &str) -> BasicPalette { + match super::resolve_annotation(name) { + Some(Annotation::Kernel) => BasicPalette::Gray, + Some(Annotation::Inline) => BasicPalette::Aqua, + Some(Annotation::Jit) => BasicPalette::Green, + None => BasicPalette::Hot, + } + } +} + pub(super) mod java { + use super::Annotation; use crate::flamegraph::color::BasicPalette; /// Handle both annotations (_[j], _[i], ...; which are @@ -6,20 +46,12 @@ pub(super) mod java { /// best as possible. Without annotations, we get a little hacky /// and match on java|org|com, etc. pub fn resolve(name: &str) -> BasicPalette { - if name.ends_with(']') { - if let Some(ai) = name.rfind("_[") { - if name[ai..].len() == 4 { - match &name[ai + 2..ai + 3] { - // kernel annotation - "k" => return BasicPalette::Orange, - // inline annotation - "i" => return BasicPalette::Aqua, - // jit annotation - "j" => return BasicPalette::Green, - _ => {} - } - } - } + if let Some(annotation) = super::resolve_annotation(name) { + return match annotation { + Annotation::Kernel => BasicPalette::Orange, + Annotation::Inline => BasicPalette::Aqua, + Annotation::Jit => BasicPalette::Green, + }; } let java_prefix = if name.starts_with('L') { @@ -51,7 +83,7 @@ pub(super) mod perl { use crate::flamegraph::color::BasicPalette; pub fn resolve(name: &str) -> BasicPalette { - if name.ends_with("_[k]") { + if let Some(super::Annotation::Kernel) = super::resolve_annotation(name) { BasicPalette::Orange } else if name.contains("Perl") || name.contains(".pl") { BasicPalette::Green @@ -67,11 +99,13 @@ pub(super) mod js { use crate::flamegraph::color::BasicPalette; pub fn resolve(name: &str) -> BasicPalette { + let annotation = super::resolve_annotation(name); + if !name.is_empty() && name.trim().is_empty() { return BasicPalette::Green; - } else if name.ends_with("_[k]") { + } else if let Some(super::Annotation::Kernel) = annotation { return BasicPalette::Orange; - } else if name.ends_with("_[j]") { + } else if let Some(super::Annotation::Jit) = annotation { if name.contains('/') { return BasicPalette::Green; } else { diff --git a/src/flamegraph/mod.rs b/src/flamegraph/mod.rs index 97299974..f5f336ef 100644 --- a/src/flamegraph/mod.rs +++ b/src/flamegraph/mod.rs @@ -75,7 +75,7 @@ pub mod defaults { } define! { - COLORS: &str = "hot", + COLORS: &str = "annotated", SEARCH_COLOR: &str = "#e600e6", TITLE: &str = "Flame Graph", CHART_TITLE: &str = "Flame Chart",