diff --git a/src/bin/collapse-perf.rs b/src/bin/collapse-perf.rs index c5b9cb31..4899302a 100644 --- a/src/bin/collapse-perf.rs +++ b/src/bin/collapse-perf.rs @@ -2,8 +2,8 @@ use std::io; use std::path::PathBuf; use structopt::StructOpt; -use inferno::collapse::perf::{Options, Perf}; -use inferno::collapse::Frontend; +use inferno::collapse::perf::{Folder, Options}; +use inferno::collapse::Collapse; #[derive(Debug, StructOpt)] #[structopt( @@ -77,5 +77,5 @@ impl Opt { fn main() -> io::Result<()> { let (infile, options) = Opt::from_args().into_parts(); - Perf::from_options(options).collapse_file(infile.as_ref()) + Folder::from(options).collapse_file(infile.as_ref()) } diff --git a/src/collapse/mod.rs b/src/collapse/mod.rs index 7fe10cf8..22368586 100644 --- a/src/collapse/mod.rs +++ b/src/collapse/mod.rs @@ -1,8 +1,8 @@ -//! Tools for collapsing the output of various profilers (e.g. [perf]) into the -//! format expected by flamegraph. -//! -//! [perf]: https://en.wikipedia.org/wiki/Perf_(Linux) - +/// Stack collapsing for the output of [`perf script`](https://linux.die.net/man/1/perf-script). +/// +/// See the [crate-level documentation] for details. +/// +/// [crate-level documentation]: ../../index.html pub mod perf; use std::fs::File; @@ -11,32 +11,27 @@ use std::path::Path; const READER_CAPACITY: usize = 128 * 1024; -/// This trait represents the ability to collapse the output of various profilers, such as -/// [perf], into the format expected by flamegraph. +/// The abstract behavior of stack collapsing. +/// +/// Implementors of this trait are providing a way to take the stack traces produced by a +/// particular profiler's output (like `perf script`) and produce lines in the folded stack format +/// expected by [`crate::flamegraph::from_sorted_lines`]. +/// +/// See also the [crate-level documentation] for details. /// -/// [perf]: https://en.wikipedia.org/wiki/Perf_(Linux) -pub trait Frontend { - /// Collapses the contents of the provided `reader` into the format expected by flamegraph. - /// Writes the output to the provided `writer`. - /// - /// # Errors - /// - /// Return an [`io::Error`] if unsuccessful. - /// - /// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html +/// [crate-level documentation]: ../index.html +// https://github.com/rust-lang/rust/issues/45040 +// #[doc(spotlight)] +pub trait Collapse { + /// Collapses the contents of the provided `reader` and writes folded stack lines to the + /// provided `writer`. fn collapse(&mut self, reader: R, writer: W) -> io::Result<()> where R: io::BufRead, W: io::Write; - /// Collapses the contents of a file (or of STDIN if `infile` is `None`) into the - /// format expected by flamegraph. Writes the output to STDOUT. - /// - /// # Errors - /// - /// Return an [`io::Error`] if unsuccessful. - /// - /// [`io::Error`]: https://doc.rust-lang.org/std/io/struct.Error.html + /// Collapses the contents of a file (or of STDIN if `infile` is `None`) and writes folded + /// stack lines to `STDOUT`. fn collapse_file

(&mut self, infile: Option

) -> io::Result<()> where P: AsRef, diff --git a/src/collapse/perf.rs b/src/collapse/perf.rs index 31d87709..3289e83a 100644 --- a/src/collapse/perf.rs +++ b/src/collapse/perf.rs @@ -1,3 +1,4 @@ +use super::Collapse; use smallvec::SmallVec; use std::borrow::Cow; use std::collections::{HashMap, VecDeque}; @@ -5,36 +6,53 @@ use std::fs::File; use std::io; use std::io::prelude::*; -use super::Frontend; - const TIDY_GENERIC: bool = true; const TIDY_JAVA: bool = true; -/// Settings one may pass during the construction of a `Perf` (via its `from_options` method). +/// Settings that change how frames are named from the incoming stack traces. +/// +/// All options default to off. #[derive(Clone, Debug, Default)] pub struct Options { - /// include PID with process names [1] + /// Include PID in the root frame. + /// + /// If disabled, the root frame is given the name of the profiled process. pub include_pid: bool, - /// include TID and PID with process names [1] + /// Include TID and PID in the root frame. + /// + /// Implies `include_pid`. pub include_tid: bool, - /// include raw addresses where symbols can't be found + /// Include raw addresses (e.g., `0xbfff0836`) where symbols can't be found. pub include_addrs: bool, - /// annotate jit functions with a _[j] + /// Annotate JIT functions with a `_[j]` suffix. pub annotate_jit: bool, - /// annotate kernel functions with a _[k] + /// Annotate kernel functions with a `_[k]` suffix. pub annotate_kernel: bool, - /// un-inline using addr2line + /// Resolve function names (including inlined ones) using `addr2line`. + /// + /// When this option is disabled, only the function name observed by `perf` is included, which + /// may not include inlined functions in the call stack. + /// + /// When this option is enabled, the function names observed by `perf` are ignored, and each + /// observed function address is instead resolved using + /// [`addr2line`](https://docs.rs/addr2line/). This is slower, but will use binary debug info + /// to resolve even inline call-chains. pub show_inline: bool, - /// adds source context to inline + /// If enabled, each frame name is also annoted with the file name and line number of the + /// sampled source line. + /// + /// Implies `show_inline`. pub show_context: bool, - /// event type filter, defaults to first encountered event + /// Only consider samples of the given event type (see `perf list`). + /// + /// If this option is set to `None`, it will be set to the first encountered event type. pub event_filter: Option, } @@ -51,9 +69,12 @@ impl Default for EventFilterState { } } -/// The perf implementation of `Frontend`. +/// A stack collapser for the output of `perf script`. +/// +/// To construct one, either use `perf::Folder::default()` or create an [`Options`] and use +/// `perf::Folder::from(options)`. #[derive(Default)] -pub struct Perf { +pub struct Folder { /// All lines until the next empty line are stack lines. in_event: bool, @@ -79,7 +100,7 @@ pub struct Perf { opt: Options, } -impl Frontend for Perf { +impl Collapse for Folder { fn collapse(&mut self, mut reader: R, writer: W) -> io::Result<()> where R: BufRead, @@ -108,9 +129,10 @@ impl Frontend for Perf { } } -impl Perf { - /// Constructs a `Perf` with the provided `Options`. - pub fn from_options(opt: Options) -> Self { +impl From for Folder { + fn from(mut opt: Options) -> Self { + opt.include_pid = opt.include_pid || opt.include_tid; + opt.show_inline = opt.show_inline || opt.show_context; Self { in_event: false, skip_stack: false, @@ -122,7 +144,9 @@ impl Perf { opt, } } +} +impl Folder { fn on_line(&mut self, line: &str) { if !self.in_event { self.on_event_line(line) diff --git a/src/flamegraph/attrs.rs b/src/flamegraph/attrs.rs index 81b88349..8520e5a8 100644 --- a/src/flamegraph/attrs.rs +++ b/src/flamegraph/attrs.rs @@ -18,17 +18,19 @@ macro_rules! unwrap_or_continue { pub struct FuncFrameAttrsMap(HashMap); impl FuncFrameAttrsMap { - /// Parse a FuncFrameAttrsMap from a file. - /// Each line should be a function name followed by a tab, - /// then a sequence of tab separated name=value pairs. + /// Parse frame attributes from a file. + /// + /// Each line should consist of a function name, a tab (`\t`), and then a sequence of + /// tab-separated `name=value` pairs. pub fn from_file(path: &PathBuf) -> io::Result { let file = BufReader::new(File::open(path)?); FuncFrameAttrsMap::from_reader(file) } - /// Parse a FuncFrameAttrsMap from a reader. - /// Each line should be a function name followed by a tab, - /// then a sequence of tab separated name=value pairs. + /// Parse frame attributes from a `BufRead`. + /// + /// Each line should consist of a function name, a tab (`\t`), and then a sequence of + /// tab-separated `name=value` pairs. pub fn from_reader(mut reader: R) -> io::Result { let mut funcattr_map = FuncFrameAttrsMap::default(); let mut line = String::new(); @@ -77,56 +79,56 @@ impl FuncFrameAttrsMap { } /// Return FrameAttrs for the given function name if it exists - pub fn frameattrs_for_func(&self, func: &str) -> Option<&FrameAttrs> { + pub(super) fn frameattrs_for_func(&self, func: &str) -> Option<&FrameAttrs> { self.0.get(func) } } /// Attributes to set on the SVG elements of a frame #[derive(PartialEq, Eq, Debug, Default)] -pub struct FrameAttrs { +pub(super) struct FrameAttrs { /// The text to include in the `title` element. /// If set to None, the title is dynamically generated based on the function name. - pub title: Option, + pub(super) title: Option, - pub g: GElementAttrs, - pub a: AElementAttrs, + pub(super) g: GElementAttrs, + pub(super) a: AElementAttrs, } /// Attributes to set on the SVG `g` element. /// Any of them set to `None` will get the default value. #[derive(PartialEq, Eq, Debug, Default)] -pub struct GElementAttrs { +pub(super) struct GElementAttrs { /// Defaults to "func_g" - pub class: Option, + pub(super) class: Option, /// Will not be included if None - pub style: Option, + pub(super) style: Option, /// Defaults to "s(this)" - pub onmouseover: Option, + pub(super) onmouseover: Option, /// Defaults to "c()" - pub onmouseout: Option, + pub(super) onmouseout: Option, /// Defaults to "zoom(this)" - pub onclick: Option, + pub(super) onclick: Option, /// Extra attributes to include - pub extra: Vec<(String, String)>, + pub(super) extra: Vec<(String, String)>, } /// Attributes to set on the SVG `a` element #[derive(PartialEq, Eq, Debug, Default)] -pub struct AElementAttrs { +pub(super) struct AElementAttrs { /// If set to None the `a` tag will not be added - pub href: Option, + pub(super) href: Option, /// Defaults to "_top" - pub target: Option, + pub(super) target: Option, /// Extra attributes to include - pub extra: Vec<(String, String)>, + pub(super) extra: Vec<(String, String)>, } fn parse_extra_attrs(attrs: &mut Vec<(String, String)>, s: &str) { diff --git a/src/flamegraph/color/mod.rs b/src/flamegraph/color/mod.rs index 7d8fe915..a6e8e0dc 100644 --- a/src/flamegraph/color/mod.rs +++ b/src/flamegraph/color/mod.rs @@ -16,49 +16,103 @@ const BLUE_GRADIENT: (&str, &str) = ("#eeeeee", "#e0e0ff"); const GREEN_GRADIENT: (&str, &str) = ("#eef2ee", "#e0ffe0"); const GRAY_GRADIENT: (&str, &str) = ("#f8f8f8", "#e8e8e8"); +/// A flame graph background color. +/// +/// The default background color usually depends on the color scheme: +/// +/// - [`BasicPalette::Mem`] defaults to [`BackgroundColor::Green`]. +/// - [`BasicPalette::Io`] and [`MultiPalette::Wakeup`] default to [`BackgroundColor::Blue`]. +/// - [`BasicPalette::Hot`] defaults to [`BackgroundColor::Yellow`]. +/// - All other [`BasicPalette`] variants default to [`BackgroundColor::Grey`]. +/// - All other [`MultiPalette`] variants default to [`BackgroundColor::Yellow`]. +/// +/// `BackgroundColor::default()` is `Yellow`. #[derive(Clone, Copy, Debug)] pub enum BackgroundColor { + /// A yellow gradient from `#EEEEEE` to `#EEEEB0`. Yellow, + /// A blue gradient from `#EEEEEE` to `#E0E0FF`. Blue, + /// A green gradient from `#EEF2EE` to `#E0FFE0`. Green, + /// A grey gradient from `#F8F8F8` to `#E8E8E8`. Grey, + /// A flag background color with the given RGB components. + /// + /// Expressed in string form as `#RRGGBB` where each component is written in hexadecimal. Flat(u8, u8, u8), } +impl Default for BackgroundColor { + fn default() -> Self { + BackgroundColor::Yellow + } +} + +/// A flame graph color palette. +/// +/// Defaults to [`BasicPalette::Hot`]. #[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 + /// different function names (kernel functions, JIT functions, etc.). Multi(MultiPalette), } +impl Default for Palette { + fn default() -> Self { + Palette::Basic(BasicPalette::Hot) + } +} + +/// A plain color palette in which the color is not chosen based on function semantics. +/// +/// Exactly how the color is chosen depends on a number of other configuration options like +/// [`super::Options.consistent_palette`] and [`super::Options.hash`]. In the absence of options +/// like that, these palettes all choose colors randomly from the indicated spectrum, and does not +/// consider the name of the frame's function when doing so. #[derive(Clone, Copy, Debug, PartialEq)] pub enum BasicPalette { + /// A palette in which colors are chosen from a red-yellow spectrum. Hot, + /// A palette in which colors are chosen from a green-blue spectrum. Mem, + /// A palette in which colors are chosen from a wide blue spectrum. Io, + /// A palette in which colors are chosen from a red spectrum. Red, + /// A palette in which colors are chosen from a green spectrum. Green, + /// A palette in which colors are chosen from a blue spectrum. Blue, + /// A palette in which colors are chosen from an aqua-tinted spectrum. Aqua, + /// A palette in which colors are chosen from a yellow spectrum. Yellow, + /// A palette in which colors are chosen from a purple spectrum. Purple, + /// A palette in which colors are chosen from a orange spectrum. Orange, } +/// 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 Java semantics to color frames. Java, + /// Use JavaScript semantics to color frames. Js, + /// Use Perl semantics to color frames. Perl, + /// Equivalent to [`BasicPalette::Aqua`] with [`BackgroundColor::Blue`]. Wakeup, } -impl Default for BackgroundColor { - fn default() -> Self { - BackgroundColor::Yellow - } -} - impl FromStr for BackgroundColor { type Err = String; @@ -95,12 +149,6 @@ fn parse_flat_bgcolor(s: &str) -> Option<(u8, u8, u8)> { } } -impl Default for Palette { - fn default() -> Self { - Palette::Basic(BasicPalette::Hot) - } -} - impl FromStr for Palette { type Err = String; @@ -334,7 +382,7 @@ mod tests { macro_rules! test_hash { ($name:expr, $expected:expr) => { - assert_eq!(namehash($name.bytes()), $expected) + assert!((dbg!(namehash($name.bytes())) - $expected).abs() < std::f32::EPSILON); }; } @@ -342,18 +390,18 @@ mod tests { fn namehash_test() { test_hash!( "org/mozilla/javascript/NativeFunction:.initScriptFunction_[j]", - 0.77964604 + 0.779_646_04 ); test_hash!( "]j[_noitcnuFtpircStini.:noitcnuFevitaN/tpircsavaj/allizom/gro", - 0.64415313 + 0.644_153_1 ); - test_hash!("genunix`kmem_cache_free", 0.46692634); - test_hash!("eerf_ehcac_memk`xinuneg", 0.84041037); - test_hash!("unix`0xfffffffffb8001d6", 0.41813117); - test_hash!("6d1008bfffffffffx0`xinu", 0.84041037); - test_hash!("un`0xfffffffffb8001d6", 0.41813117); - test_hash!("``0xfffffffffb8001d6", 0.41813117); + test_hash!("genunix`kmem_cache_free", 0.466_926_34); + test_hash!("eerf_ehcac_memk`xinuneg", 0.840_410_3); + test_hash!("unix`0xfffffffffb8001d6", 0.418_131_17); + test_hash!("6d1008bfffffffffx0`xinu", 0.840_410_3); + test_hash!("un`0xfffffffffb8001d6", 0.418_131_17); + test_hash!("``0xfffffffffb8001d6", 0.418_131_17); test_hash!("", 1.0); } } diff --git a/src/flamegraph/color/palette_map.rs b/src/flamegraph/color/palette_map.rs index 7852b369..0ad4dd69 100644 --- a/src/flamegraph/color/palette_map.rs +++ b/src/flamegraph/color/palette_map.rs @@ -9,7 +9,7 @@ use std::io::Write; use std::path::Path; use std::str::FromStr; -pub struct PaletteMap<'a>(HashMap, (u8, u8, u8)>); +pub(crate) struct PaletteMap<'a>(HashMap, (u8, u8, u8)>); impl<'a> PaletteMap<'a> { pub fn load(file: &str) -> quick_xml::Result { diff --git a/src/flamegraph/mod.rs b/src/flamegraph/mod.rs index 88dd2c9a..9376aaf9 100644 --- a/src/flamegraph/mod.rs +++ b/src/flamegraph/mod.rs @@ -1,24 +1,20 @@ -//! Flamegraph plotting tools. - mod attrs; +mod color; +mod merge; +mod svg; pub use attrs::FuncFrameAttrsMap; - -use std::io; -use std::io::prelude::*; +pub use color::BackgroundColor; +pub use color::Palette; use num_format::Locale; use quick_xml::{ events::{BytesEnd, BytesStart, BytesText, Event}, Writer, }; +use std::io; +use std::io::prelude::*; use str_stack::StrStack; - -mod color; -mod merge; -mod svg; -pub use color::BackgroundColor; -pub use color::Palette; use svg::StyleOptions; const IMAGEWIDTH: usize = 1200; // max width, pixels @@ -32,15 +28,53 @@ const XPAD: usize = 10; // pad lefm and right const FRAMEPAD: usize = 1; // vertical padding for frames const PALETTE_FILE: &str = "palette.map"; +/// Configure the flame graph. #[derive(Debug)] pub struct Options { + /// The color palette to use when plotting. pub colors: color::Palette, + + /// The background color for the plot. + /// + /// If `None`, the background color will be selected based on the value of `colors`. pub bgcolors: Option, + + /// Choose names based on the hashes of function names. + /// + /// This will cause similar functions to be colored similarly. pub hash: bool, + + /// Store the choice of color for each function so that later invocations use the same colors. + /// + /// With this option enabled, a file called `palette.map` will be created the first time a + /// flame graph is generated, and the color chosen for each function will be written into it. + /// On subsequent invocations, functions that already have a color registered in that file will + /// be given the stored color rather than be assigned a new one. New functions will have their + /// colors persisted for future runs. + /// + /// This feature was first implemented [by Shawn + /// Sterling](https://github.com/brendangregg/FlameGraph/pull/25). pub consistent_palette: bool, + + /// Assign extra attributes to particular functions. + /// + /// In particular, if a function appears in the given map, it will have extra attributes set in + /// the resulting SVG based on its value in the map. pub func_frameattrs: FuncFrameAttrsMap, + + /// Whether to plot a plot that grows top-to-bottom or bottom-up (the default). pub direction: Direction, + + /// The title for the flame chart. + /// + /// Defaults to "Flame Graph". pub title: String, + + /// By default, if [differential] samples are included in the provided stacks, the resulting + /// flame graph will compute and show differentials as `sample#2 - sample#1`. If this option is + /// set, the differential is instead computed using `sample#1 - sample#2`. + /// + /// [differential]: http://www.brendangregg.com/blog/2014-11-09/differential-flame-graphs.html pub negate_differentials: bool, } @@ -59,12 +93,26 @@ impl Default for Options { } } +/// The direction the plot should grow. #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum Direction { + /// Stacks grow from the bottom to the top. + /// + /// The `(all)` meta frame will be at the bottom. Straight, + + /// Stacks grow from the top to the bottom. + /// + /// The `(all)` meta frame will be at the top. Inverted, } +impl Default for Direction { + fn default() -> Self { + Direction::Straight + } +} + macro_rules! args { ($($key:expr => $value:expr),*) => {{ [$(($key, $value),)*].into_iter().map(|(k, v): &(&str, &str)| (*k, *v)) @@ -158,6 +206,21 @@ impl Rectangle { } } +/// Produce a flame graph from a sorted iterator over folded stack lines. +/// +/// This function expects each folded stack to contain the following whitespace-separated fields: +/// +/// - A semicolon-separated list of frame names (e.g., `main;foo;bar;baz`). +/// - A sample count for the given stack. +/// - An optional second sample count. +/// +/// If two sample counts are provided, a [differential flame graph] is produced. In this mode, the +/// flame graph uses the difference between the two sample counts to show how the sample counts for +/// each stack has changed between the first and second profiling. +/// +/// The resulting flame graph will be written out to `writer` in SVG format. +/// +/// [differential flame graph]: http://www.brendangregg.com/blog/2014-11-09/differential-flame-graphs.html #[allow(clippy::cyclomatic_complexity)] pub fn from_sorted_lines<'a, I, W>(opt: Options, lines: I, writer: W) -> quick_xml::Result<()> where @@ -435,6 +498,11 @@ where Ok(()) } +/// Produce a flame graph from a reader that contains a sorted sequence of folded stack lines. +/// +/// See [`from_sorted_lines`] for the expected format of each line. +/// +/// The resulting flame graph will be written out to `writer` in SVG format. pub fn from_reader(opt: Options, mut reader: R, writer: W) -> quick_xml::Result<()> where R: Read, @@ -448,6 +516,12 @@ where from_sorted_lines(opt, input.lines(), writer) } +/// Produce a flame graph from a set of readers that contain folded stack lines. +/// +/// This function sorts all the read lines before processing them. +/// See [`from_sorted_lines`] for the expected format of each line. +/// +/// The resulting flame graph will be written out to `writer` in SVG format. pub fn from_readers(opt: Options, readers: R, writer: W) -> quick_xml::Result<()> where R: IntoIterator, @@ -505,9 +579,3 @@ fn filled_rectangle( } svg.write_event(&cache_rect) } - -impl Default for Direction { - fn default() -> Self { - Direction::Straight - } -} diff --git a/src/lib.rs b/src/lib.rs index 163234b9..a07aa5d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,92 @@ +//! Inferno is a set of tools that let you to produce [flame graphs] from performance profiles of +//! your application. It's a port of parts Brendan Gregg's original [flamegraph toolkit] that aims +//! to improve the performance of the original flamegraph tools and provide programmatic access to +//! them to facilitate integration with _other_ tools (like [not-perf]). +//! +//! Inferno, like the original flame graph toolkit, consists of two "stages": stack collapsing and +//! plotting. In the original Perl implementations, these were represented by the `stackcollapse-*` +//! binaries and `flamegraph.pl` respectively. In Inferno, collapsing is available through the +//! [`collapse`] module and the `inferno-collapse-*` binaries, and plotting can be found in the +//! [`flamegraph`] module and the `inferno-flamegraph` binary. +//! +//! # Command-line use +//! +//! ## Collapsing stacks +//! +//! Most sampling profilers (as opposed to [tracing profilers]) work by repeatedly recording the +//! state of the [call stack]. The stack can be sampled based on a fixed sampling interval, based +//! on [hardware or software events], or some combination of the two. In the end, you get a series +//! of [stack traces], each of which represents a snapshot of where the program was at different +//! points in time. +//! +//! Given enough of these snapshots, you can get a pretty good idea of where your program is +//! spending its time by looking at which functions appear in many of the traces. To ease this +//! analysis, we want to "collapse" the stack traces so if a particular trace occurs more than +//! once, we instead just keep it _once_ along with a count of how many times we've seen it. This +//! is what the various collapsing tools do! You'll sometimes see the resulting tuples of stack + +//! count called a "folded stack trace". +//! +//! Since profiling tools produce stack traces in a myriad of different formats, and the flame +//! graph plotter expects input in a particular folded stack trace format, each profiler needs a +//! separate collapse implementation. While the original Perl implementation supports _lots_ of +//! profilers, Inferno currently only supports one: the widely used [`perf`] tool (specifically the +//! output from `perf script`). Support for xdebug is [hopefully coming soon], and [`bpftrace`] +//! should get [native support] before too long. +//! +//! To profile a Rust application with perf for example, you would first set +//! +//! ```toml +//! [profile.release] +//! debug = true +//! ``` +//! +//! in your `Cargo.toml`, and then run: +//! +//! ```console +//! $ cargo b --release --bin mybin +//! $ perf record --call-graph dwarf target/release/mybin +//! $ perf script | inferno-collapse-perf > stacks.folded +//! ``` +//! +//! For more advanced uses, see Brendan Gregg's excellent [perf examples] page. +//! +//! ## Producing a flame graph +//! +//! Once you have a folded stack file, you're ready to produce the flame graph SVG image. To do so, +//! simply provide the folded stack file to `inferno-flamegraph`, and it will print the resulting +//! SVG. Following on from the example above: +//! +//! ```console +//! $ cat stacks.folded | inferno-flamegraph > profile.svg +//! ``` +//! +//! And then open `profile.svg` in your viewer of choice. +//! +//! # Programmatic access +//! +//! TODO: [https://github.com/jonhoo/inferno/issues/30](https://github.com/jonhoo/inferno/issues/30) +//! +//! # Development +//! +//! This crate was initially developed through [a series of live coding sessions]. If you want to +//! contribute to the code, that may be a good way to learn why it's all designed the way it is! +//! +//! [flame graphs]: http://www.brendangregg.com/flamegraphs.html +//! [flamegraph toolkit]: https://github.com/brendangregg/FlameGraph +//! [not-perf]: https://github.com/nokia/not-perf +//! [tracing profilers]: https://danluu.com/perf-tracing/ +//! [call stack]: https://en.wikipedia.org/wiki/Call_stack +//! [hardware or software events]: https://perf.wiki.kernel.org/index.php/Tutorial#Events +//! [stack traces]: https://en.wikipedia.org/wiki/Stack_trace +//! [`perf`]: https://perf.wiki.kernel.org/index.php/Main_Page +//! [hopefully coming soon]: https://twitter.com/DanielLockyer/status/1094605231155900416 +//! [native support]: https://github.com/jonhoo/inferno/issues/51#issuecomment-466732304 +//! [`bpftrace`]: https://github.com/iovisor/bpftrace +//! [perf examples]: http://www.brendangregg.com/perf.html +//! [a series of live coding sessions]: https://www.youtube.com/watch?v=jTpK-bNZiA4&list=PLqbS7AVVErFimAvMW-kIJUwxpPvcPBCsz + +#![deny(missing_docs)] + #[cfg(test)] #[macro_use] extern crate pretty_assertions; @@ -5,5 +94,16 @@ extern crate pretty_assertions; #[macro_use] extern crate log; +/// Stack collapsing for various input formats. +/// +/// See the [crate-level documentation] for details. +/// +/// [crate-level documentation]: ../index.html pub mod collapse; + +/// Tools for producing flame graphs from folded stack traces. +/// +/// See the [crate-level documentation] for details. +/// +/// [crate-level documentation]: ../index.html pub mod flamegraph; diff --git a/tests/collapse-perf.rs b/tests/collapse-perf.rs index 3aa9706d..3a661ba7 100644 --- a/tests/collapse-perf.rs +++ b/tests/collapse-perf.rs @@ -3,8 +3,8 @@ extern crate pretty_assertions; extern crate inferno; -use inferno::collapse::perf::{Options, Perf}; -use inferno::collapse::Frontend; +use inferno::collapse::perf::{Folder, Options}; +use inferno::collapse::Collapse; use std::fs::{self, File}; use std::io::{self, BufRead, BufReader, Cursor}; use std::path::Path; @@ -159,7 +159,7 @@ fn test_collapse_perf(test_file: &str, expected_file: &str, options: Options) -> let r = BufReader::new(File::open(test_file)?); let expected_len = fs::metadata(expected_file)?.len() as usize; let mut result = Cursor::new(Vec::with_capacity(expected_len)); - let mut perf = Perf::from_options(options); + let mut perf = Folder::from(options); perf.collapse(r, &mut result)?; let mut expected = BufReader::new(File::open(expected_file)?);