1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#[macro_use]
pub(crate) mod common;

/// Stack collapsing for the output of [`dtrace`](https://www.joyent.com/dtrace).
///
/// See the [crate-level documentation] for details.
///
///   [crate-level documentation]: ../../index.html
pub mod dtrace;

/// Attempts to use whichever Collapse implementation is appropriate for a given input
pub mod guess;

/// 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;

/// Internal string match helper functions for perf
pub(crate) mod matcher;

/// Stack collapsing for the output of [`sample`](https://gist.github.com/loderunner/36724cc9ee8db66db305#profiling-with-sample) on macOS.
///
/// See the [crate-level documentation] for details.
///
///   [crate-level documentation]: ../../index.html
pub mod sample;

/// Stack collapsing for the output of [`VTune`](https://software.intel.com/en-us/vtune-amplifier-help-command-line-interface).
///
/// See the [crate-level documentation] for details.
///
///   [crate-level documentation]: ../../index.html
pub mod vtune;

/// Collapse direct recursive backtraces.
///
/// Post-process a stack list and merge direct recursive calls.
///
/// For example, collapses
/// ```text
/// main;recursive;recursive;recursive;helper 1
/// ```
/// into
/// ```text
/// main;recursive;helper 1
/// ```
///
/// See the [crate-level documentation] for details.
///
///   [crate-level documentation]: ../../index.html
pub mod recursive;

/// Stack collapsing for the output of the [Visual Studio built-in profiler](https://docs.microsoft.com/en-us/visualstudio/profiling/profiling-feature-tour?view=vs-2019).
///
/// See the [crate-level documentation] for details.
///
///   [crate-level documentation]: ../../index.html
pub mod vsprof;

/// Stack collapsing for the output of the [GHC's built-in profiler](https://downloads.haskell.org/ghc/latest/docs/users_guide/profiling.html).
///
/// See the [crate-level documentation] for details.
///
///   [crate-level documentation]: ../../index.html
pub mod ghcprof;

use is_terminal::IsTerminal;

// DEFAULT_NTHREADS is public because we use it in the help text of the binaries,
// but it doesn't need to be exposed to library users, hence #[doc(hidden)].
#[doc(hidden)]
pub use self::common::DEFAULT_NTHREADS;

use std::fs::File;
use std::io;
use std::path::Path;

use self::common::{CollapsePrivate, CAPACITY_READER};

/// 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_lines`].
///
/// See also the [crate-level documentation] for details.
///
///   [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<R, W>(&mut self, reader: R, writer: W) -> io::Result<()>
    where
        R: io::BufRead,
        W: io::Write;

    /// Collapses the contents of the provided file (or of STDIN if `infile` is `None`) and
    /// writes folded stack lines to provided `writer`.
    fn collapse_file<P, W>(&mut self, infile: Option<P>, writer: W) -> io::Result<()>
    where
        P: AsRef<Path>,
        W: io::Write,
    {
        match infile {
            Some(ref path) => {
                let file = File::open(path)?;
                let reader = io::BufReader::with_capacity(CAPACITY_READER, file);
                self.collapse(reader, writer)
            }
            None => {
                let stdin = io::stdin();
                let stdin_guard = stdin.lock();
                let reader = io::BufReader::with_capacity(CAPACITY_READER, stdin_guard);
                self.collapse(reader, writer)
            }
        }
    }

    /// Collapses the contents of the provided file (or of STDIN if `infile` is `None`) and
    /// writes folded stack lines to STDOUT.
    fn collapse_file_to_stdout<P>(&mut self, infile: Option<P>) -> io::Result<()>
    where
        P: AsRef<Path>,
    {
        if std::io::stdout().is_terminal() {
            self.collapse_file(infile, io::stdout().lock())
        } else {
            self.collapse_file(infile, io::BufWriter::new(io::stdout().lock()))
        }
    }

    /// Returns whether this implementation is appropriate for the given input.
    ///
    /// - `None` means "not sure -- need more input"
    /// - `Some(true)` means "yes, this implementation should work with this string"
    /// - `Some(false)` means "no, this implementation definitely won't work"
    #[allow(clippy::wrong_self_convention)]
    fn is_applicable(&mut self, input: &str) -> Option<bool>;
}

impl<T> Collapse for T
where
    T: CollapsePrivate,
{
    fn collapse<R, W>(&mut self, reader: R, writer: W) -> io::Result<()>
    where
        R: io::BufRead,
        W: io::Write,
    {
        <Self as CollapsePrivate>::collapse(self, reader, writer)
    }

    fn is_applicable(&mut self, input: &str) -> Option<bool> {
        <Self as CollapsePrivate>::is_applicable(self, input)
    }
}