Skip to content

Commit 8869130

Browse files
Merge pull request RustPython#1126 from RustPython/coolreader18/flamescope-profiling
Add an speedscope output format for profiling
2 parents 55fbe7f + 3beaf5f commit 8869130

File tree

8 files changed

+136
-77
lines changed

8 files changed

+136
-77
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,7 @@ __pycache__
1010
wasm-pack.log
1111
.idea/
1212
tests/snippets/resources
13+
1314
flame-graph.html
15+
flame.txt
16+
flamescope.json

Cargo.lock

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ path = "./benchmarks/bench.rs"
1616

1717
[features]
1818
default = []
19-
flame-it = ["rustpython-vm/flame-it", "flame"]
19+
flame-it = ["rustpython-vm/flame-it", "flame", "flamescope"]
2020

2121
[dependencies]
2222
log="0.4.1"
@@ -29,6 +29,7 @@ rustyline = "4.1.0"
2929
xdg = "2.2.0"
3030

3131
flame = { version = "0.2", optional = true }
32+
flamescope = { version = "0.1", optional = true }
3233

3334
[dev-dependencies.cpython]
3435
version = "0.2"

DEVELOPMENT.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# RustPython Development Guide and Tips
2+
3+
## Code organization
4+
5+
- `parser/src`: python lexing, parsing and ast
6+
- `vm/src`: python virtual machine
7+
- `builtins.rs`: Builtin functions
8+
- `compile.rs`: the python compiler from ast to bytecode
9+
- `obj`: python builtin types
10+
- `src`: using the other subcrates to bring rustpython to life.
11+
- `docs`: documentation (work in progress)
12+
- `py_code_object`: CPython bytecode to rustpython bytecode converter (work in
13+
progress)
14+
- `wasm`: Binary crate and resources for WebAssembly build
15+
- `tests`: integration test snippets
16+
17+
## Code style
18+
19+
The code style used is the default
20+
[rustfmt](https://github.com/rust-lang/rustfmt) codestyle. Please format your
21+
code accordingly. We also use [clippy](https://github.com/rust-lang/rust-clippy)
22+
to detect rust code issues.
23+
24+
## Testing
25+
26+
To test rustpython, there is a collection of python snippets located in the
27+
`tests/snippets` directory. To run those tests do the following:
28+
29+
```shell
30+
$ cd tests
31+
$ pipenv install
32+
$ pipenv run pytest -v
33+
```
34+
35+
There also are some unit tests, you can run those with cargo:
36+
37+
```shell
38+
$ cargo test --all
39+
```
40+
41+
## Profiling
42+
43+
To profile rustpython, simply build in release mode with the `flame-it` feature.
44+
This will generate a file `flamescope.json`, which you can then view at
45+
https://speedscope.app.
46+
47+
```sh
48+
$ cargo run --release --features flame-it script.py
49+
$ cat flamescope.json
50+
{<json>}
51+
```
52+
53+
You can also pass the `--output-file` option to choose which file to output to
54+
(or stdout if you specify `-`), and the `--output-format` option to choose if
55+
you want to output in the speedscope json format (default), text, or a raw html
56+
viewer (currently broken).

README.md

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -59,19 +59,6 @@ Documentation HTML files can then be found in the `target/doc` directory.
5959

6060
If you wish to update the online documentation, push directly to the `release` branch (or ask a maintainer to do so). This will trigger a Travis build that updates the documentation and WebAssembly demo page.
6161

62-
# Code organization
63-
64-
- `parser/src`: python lexing, parsing and ast
65-
- `vm/src`: python virtual machine
66-
- `builtins.rs`: Builtin functions
67-
- `compile.rs`: the python compiler from ast to bytecode
68-
- `obj`: python builtin types
69-
- `src`: using the other subcrates to bring rustpython to life.
70-
- `docs`: documentation (work in progress)
71-
- `py_code_object`: CPython bytecode to rustpython bytecode converter (work in progress)
72-
- `wasm`: Binary crate and resources for WebAssembly build
73-
- `tests`: integration test snippets
74-
7562
# Contributing
7663

7764
Contributions are more than welcome, and in many cases we are happy to guide contributors through PRs or on gitter.
@@ -88,23 +75,6 @@ You can also simply run
8875
`./whats_left.sh` to assist in finding any
8976
unimplemented method.
9077

91-
# Testing
92-
93-
To test rustpython, there is a collection of python snippets located in the
94-
`tests/snippets` directory. To run those tests do the following:
95-
96-
```shell
97-
$ cd tests
98-
$ pipenv install
99-
$ pipenv run pytest -v
100-
```
101-
102-
There also are some unit tests, you can run those with cargo:
103-
104-
```shell
105-
$ cargo test --all
106-
```
107-
10878
# Using a standard library
10979

11080
As of now the standard library is under construction. You can
@@ -126,11 +96,6 @@ the [ouroboros library](https://github.com/pybee/ouroboros).
12696

12797
[See this doc](wasm/README.md)
12898

129-
# Code style
130-
131-
The code style used is the default [rustfmt](https://github.com/rust-lang/rustfmt) codestyle. Please format your code accordingly.
132-
We also use [clippy](https://github.com/rust-lang/rust-clippy) to detect rust code issues.
133-
13499
# Community
135100

136101
Chat with us on [gitter][gitter].

src/main.rs

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,8 @@ use std::path::PathBuf;
2222
use std::process;
2323

2424
fn main() {
25-
if let Err(err) = run() {
26-
error!("Error: {}", err);
27-
process::exit(1);
28-
}
29-
}
30-
31-
fn run() -> Result<(), Box<dyn std::error::Error>> {
25+
#[cfg(feature = "flame-it")]
26+
let main_guard = flame::start_guard("RustPython main");
3227
env_logger::init();
3328
let app = App::new("RustPython")
3429
.version(crate_version!())
@@ -55,15 +50,19 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
5550
)
5651
.arg(Arg::from_usage("[pyargs] 'args for python'").multiple(true));
5752
#[cfg(feature = "flame-it")]
58-
let app = app.arg(
59-
Arg::with_name("profile_output")
60-
.long("profile-output")
61-
.takes_value(true)
62-
.help(
63-
"the file to output the profile graph to. present due to being \
64-
built with feature 'flame-it'",
65-
),
66-
);
53+
let app = app
54+
.arg(
55+
Arg::with_name("profile_output")
56+
.long("profile-output")
57+
.takes_value(true)
58+
.help("the file to output the profiling information to"),
59+
)
60+
.arg(
61+
Arg::with_name("profile_format")
62+
.long("profile-format")
63+
.takes_value(true)
64+
.help("the profile format to output the profiling information in"),
65+
);
6766
let matches = app.get_matches();
6867

6968
// Construct vm:
@@ -90,16 +89,53 @@ fn run() -> Result<(), Box<dyn std::error::Error>> {
9089

9190
#[cfg(feature = "flame-it")]
9291
{
93-
use std::fs::File;
92+
main_guard.end();
93+
if let Err(e) = write_profile(matches) {
94+
error!("Error writing profile information: {}", e);
95+
process::exit(1);
96+
}
97+
}
98+
}
9499

95-
let profile_output = matches
96-
.value_of_os("profile_output")
97-
.unwrap_or_else(|| "flame-graph.html".as_ref());
98-
if profile_output == "-" {
99-
flame::dump_stdout();
100-
} else {
101-
flame::dump_html(&mut File::create(profile_output)?)?;
100+
#[cfg(feature = "flame-it")]
101+
fn write_profile(matches: clap::ArgMatches) -> Result<(), Box<dyn std::error::Error>> {
102+
use std::fs::File;
103+
104+
enum ProfileFormat {
105+
Html,
106+
Text,
107+
Speedscope,
108+
}
109+
110+
let profile_output = matches.value_of_os("profile_output");
111+
112+
let profile_format = match matches.value_of("profile_format") {
113+
Some("html") => ProfileFormat::Html,
114+
Some("text") => ProfileFormat::Text,
115+
None if profile_output == Some("-".as_ref()) => ProfileFormat::Text,
116+
Some("speedscope") | None => ProfileFormat::Speedscope,
117+
Some(other) => {
118+
error!("Unknown profile format {}", other);
119+
process::exit(1);
102120
}
121+
};
122+
123+
let profile_output = profile_output.unwrap_or_else(|| match profile_format {
124+
ProfileFormat::Html => "flame-graph.html".as_ref(),
125+
ProfileFormat::Text => "flame.txt".as_ref(),
126+
ProfileFormat::Speedscope => "flamescope.json".as_ref(),
127+
});
128+
129+
let profile_output: Box<dyn std::io::Write> = if profile_output == "-" {
130+
Box::new(std::io::stdout())
131+
} else {
132+
Box::new(File::create(profile_output)?)
133+
};
134+
135+
match profile_format {
136+
ProfileFormat::Html => flame::dump_html(profile_output)?,
137+
ProfileFormat::Text => flame::dump_text_to_writer(profile_output)?,
138+
ProfileFormat::Speedscope => flamescope::dump(profile_output)?,
103139
}
104140

105141
Ok(())

vm/src/frame.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -271,11 +271,6 @@ impl Frame {
271271
// #[cfg_attr(feature = "flame-it", flame("Frame"))]
272272
pub fn run(&self, vm: &VirtualMachine) -> Result<ExecutionResult, PyObjectRef> {
273273
flame_guard!(format!("Frame::run({})", self.code.obj_name));
274-
// flame doesn't include notes in the html graph :(
275-
flame_note!(
276-
flame::StrCow::from("CodeObj name"),
277-
self.code.obj_name.clone().into()
278-
);
279274

280275
let filename = &self.code.source_path.to_string();
281276

@@ -342,10 +337,11 @@ impl Frame {
342337

343338
/// Execute a single instruction.
344339
#[allow(clippy::cognitive_complexity)]
345-
#[cfg_attr(feature = "flame-it", flame("Frame"))]
346340
fn execute_instruction(&self, vm: &VirtualMachine) -> FrameResult {
347341
let instruction = self.fetch_instruction();
348342

343+
flame_guard!(format!("Frame::execute_instruction({:?})", instruction));
344+
349345
#[cfg(feature = "vm-tracing-logging")]
350346
{
351347
trace!("=======");

vm/src/macros.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -273,14 +273,3 @@ macro_rules! flame_guard {
273273
let _guard = ::flame::start_guard($name);
274274
};
275275
}
276-
277-
macro_rules! flame_note {
278-
($name:expr, $desc:expr$(,)?) => {
279-
#[cfg(feature = "flame-it")]
280-
::flame::note($name, ::std::option::Option::Some($desc));
281-
};
282-
($name:expr$(,)?) => {
283-
#[cfg(feature = "flame-it")]
284-
::flame::note($name, ::std::option::Option::None);
285-
};
286-
}

0 commit comments

Comments
 (0)