Skip to content

Commit 53ce362

Browse files
Merge pull request RustPython#1120 from RustPython/coolreader18/flame-profiling
Add flamegraph profiling using flame and flamer
2 parents 86decbb + f585f7e commit 53ce362

File tree

11 files changed

+143
-10
lines changed

11 files changed

+143
-10
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ __pycache__
99
.vscode
1010
wasm-pack.log
1111
.idea/
12-
tests/snippets/resources
12+
tests/snippets/resources
13+
flame-graph.html

Cargo.lock

Lines changed: 44 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: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ members = [".", "derive", "vm", "wasm/lib", "parser", "compiler", "bytecode"]
1414
name = "bench"
1515
path = "./benchmarks/bench.rs"
1616

17+
[features]
18+
default = []
19+
flame-it = ["rustpython-vm/flame-it", "flame"]
1720

1821
[dependencies]
1922
log="0.4.1"
@@ -25,5 +28,7 @@ rustpython-vm = {path = "vm", version = "0.1.0"}
2528
rustyline = "4.1.0"
2629
xdg = "2.2.0"
2730

31+
flame = { version = "0.2", optional = true }
32+
2833
[dev-dependencies.cpython]
2934
version = "0.2"

src/main.rs

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,18 @@ use rustpython_vm::{
1919

2020
use rustyline::{error::ReadlineError, Editor};
2121
use std::path::PathBuf;
22+
use std::process;
2223

2324
fn main() {
25+
if let Err(err) = run() {
26+
error!("Error: {}", err);
27+
process::exit(1);
28+
}
29+
}
30+
31+
fn run() -> Result<(), Box<std::error::Error>> {
2432
env_logger::init();
25-
let matches = App::new("RustPython")
33+
let app = App::new("RustPython")
2634
.version(crate_version!())
2735
.author(crate_authors!())
2836
.about("Rust implementation of the Python language")
@@ -45,8 +53,18 @@ fn main() {
4553
.takes_value(true)
4654
.help("run library module as script"),
4755
)
48-
.arg(Arg::from_usage("[pyargs] 'args for python'").multiple(true))
49-
.get_matches();
56+
.arg(Arg::from_usage("[pyargs] 'args for python'").multiple(true));
57+
#[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+
);
67+
let matches = app.get_matches();
5068

5169
// Construct vm:
5270
let vm = VirtualMachine::new();
@@ -69,6 +87,22 @@ fn main() {
6987

7088
// See if any exception leaked out:
7189
handle_exception(&vm, result);
90+
91+
#[cfg(feature = "flame-it")]
92+
{
93+
use std::fs::File;
94+
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)?)?;
102+
}
103+
}
104+
105+
Ok(())
72106
}
73107

74108
fn _run_string(vm: &VirtualMachine, source: &str, source_path: String) -> PyResult {
@@ -84,7 +118,7 @@ fn _run_string(vm: &VirtualMachine, source: &str, source_path: String) -> PyResu
84118
fn handle_exception(vm: &VirtualMachine, result: PyResult) {
85119
if let Err(err) = result {
86120
print_exception(vm, &err);
87-
std::process::exit(1);
121+
process::exit(1);
88122
}
89123
}
90124

@@ -116,14 +150,14 @@ fn run_script(vm: &VirtualMachine, script_file: &str) -> PyResult {
116150
"can't find '__main__' module in '{}'",
117151
file_path.to_str().unwrap()
118152
);
119-
std::process::exit(1);
153+
process::exit(1);
120154
}
121155
} else {
122156
error!(
123157
"can't open file '{}': No such file or directory",
124158
file_path.to_str().unwrap()
125159
);
126-
std::process::exit(1);
160+
process::exit(1);
127161
};
128162

129163
let dir = file_path.parent().unwrap().to_str().unwrap().to_string();
@@ -138,7 +172,7 @@ fn run_script(vm: &VirtualMachine, script_file: &str) -> PyResult {
138172
file_path.to_str().unwrap(),
139173
err.kind()
140174
);
141-
std::process::exit(1);
175+
process::exit(1);
142176
}
143177
}
144178
}

vm/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ include = ["src/**/*.rs", "Cargo.toml", "build.rs", "Lib/**/*.py"]
1111
[features]
1212
default = ["rustpython-parser", "rustpython-compiler"]
1313
vm-tracing-logging = []
14+
flame-it = ["flame", "flamer"]
1415

1516
[dependencies]
1617
# Crypto:
@@ -57,5 +58,8 @@ maplit = "1.0"
5758
proc-macro-hack = "0.5"
5859
bitflags = "1.1"
5960

61+
flame = { version = "0.2", optional = true }
62+
flamer = { version = "0.3", optional = true }
63+
6064
[target.'cfg(all(unix, not(target_os = "android")))'.dependencies]
6165
pwd = "1"

vm/src/frame.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,15 @@ impl Frame {
268268
}
269269
}
270270

271+
// #[cfg_attr(feature = "flame-it", flame("Frame"))]
271272
pub fn run(&self, vm: &VirtualMachine) -> Result<ExecutionResult, PyObjectRef> {
273+
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+
);
279+
272280
let filename = &self.code.source_path.to_string();
273281

274282
// This is the name of the object being run:
@@ -334,6 +342,7 @@ impl Frame {
334342

335343
/// Execute a single instruction.
336344
#[allow(clippy::cognitive_complexity)]
345+
#[cfg_attr(feature = "flame-it", flame("Frame"))]
337346
fn execute_instruction(&self, vm: &VirtualMachine) -> FrameResult {
338347
let instruction = self.fetch_instruction();
339348

@@ -891,6 +900,7 @@ impl Frame {
891900
}
892901
}
893902

903+
#[cfg_attr(feature = "flame-it", flame("Frame"))]
894904
fn get_elements(
895905
&self,
896906
vm: &VirtualMachine,
@@ -912,6 +922,7 @@ impl Frame {
912922
}
913923
}
914924

925+
#[cfg_attr(feature = "flame-it", flame("Frame"))]
915926
fn import(
916927
&self,
917928
vm: &VirtualMachine,
@@ -938,6 +949,7 @@ impl Frame {
938949
Ok(None)
939950
}
940951

952+
#[cfg_attr(feature = "flame-it", flame("Frame"))]
941953
fn import_star(&self, vm: &VirtualMachine, module: &str, level: usize) -> FrameResult {
942954
let module = vm.import(module, &vm.ctx.new_tuple(vec![]), level)?;
943955

@@ -951,6 +963,7 @@ impl Frame {
951963
}
952964

953965
// Unwind all blocks:
966+
#[cfg_attr(feature = "flame-it", flame("Frame"))]
954967
fn unwind_blocks(&self, vm: &VirtualMachine) -> Option<PyObjectRef> {
955968
while let Some(block) = self.pop_block() {
956969
match block.typ {
@@ -978,6 +991,7 @@ impl Frame {
978991
None
979992
}
980993

994+
#[cfg_attr(feature = "flame-it", flame("Frame"))]
981995
fn unwind_loop(&self, vm: &VirtualMachine) -> Block {
982996
loop {
983997
let block = self.current_block().expect("not in a loop");
@@ -1003,6 +1017,7 @@ impl Frame {
10031017
}
10041018
}
10051019

1020+
#[cfg_attr(feature = "flame-it", flame("Frame"))]
10061021
fn unwind_exception(&self, vm: &VirtualMachine, exc: PyObjectRef) -> Option<PyObjectRef> {
10071022
// unwind block stack on exception and find any handlers:
10081023
while let Some(block) = self.pop_block() {
@@ -1105,6 +1120,7 @@ impl Frame {
11051120
}
11061121
}
11071122

1123+
#[cfg_attr(feature = "flame-it", flame("Frame"))]
11081124
fn load_name(
11091125
&self,
11101126
vm: &VirtualMachine,
@@ -1150,6 +1166,7 @@ impl Frame {
11501166
*self.lasti.borrow_mut() = target_pc;
11511167
}
11521168

1169+
#[cfg_attr(feature = "flame-it", flame("Frame"))]
11531170
fn execute_binop(
11541171
&self,
11551172
vm: &VirtualMachine,
@@ -1194,6 +1211,7 @@ impl Frame {
11941211
Ok(None)
11951212
}
11961213

1214+
#[cfg_attr(feature = "flame-it", flame("Frame"))]
11971215
fn execute_unop(&self, vm: &VirtualMachine, op: &bytecode::UnaryOperator) -> FrameResult {
11981216
let a = self.pop_value();
11991217
let value = match *op {
@@ -1234,6 +1252,7 @@ impl Frame {
12341252
Ok(result)
12351253
}
12361254

1255+
#[cfg_attr(feature = "flame-it", flame("Frame"))]
12371256
fn execute_compare(
12381257
&self,
12391258
vm: &VirtualMachine,
@@ -1326,6 +1345,7 @@ impl Frame {
13261345
stack[stack.len() - depth - 1].clone()
13271346
}
13281347

1348+
#[cfg_attr(feature = "flame-it", flame("Frame"))]
13291349
fn get_exception(&self, vm: &VirtualMachine, none_allowed: bool) -> PyResult {
13301350
let exception = self.pop_value();
13311351
if none_allowed && vm.get_none().is(&exception)

vm/src/import.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,15 @@ use crate::vm::VirtualMachine;
1111
use rustpython_compiler::compile;
1212

1313
pub fn init_importlib(vm: &VirtualMachine, external: bool) -> PyResult {
14+
flame_guard!("init importlib");
1415
let importlib = import_frozen(vm, "_frozen_importlib")?;
1516
let impmod = import_builtin(vm, "_imp")?;
1617
let install = vm.get_attribute(importlib.clone(), "_install")?;
1718
vm.invoke(install, vec![vm.sys_module.clone(), impmod])?;
1819
vm.import_func
1920
.replace(vm.get_attribute(importlib.clone(), "__import__")?);
2021
if external && cfg!(feature = "rustpython-compiler") {
22+
flame_guard!("install_external");
2123
let install_external =
2224
vm.get_attribute(importlib.clone(), "_install_external_importers")?;
2325
vm.invoke(install_external, vec![])?;

vm/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
clippy::implicit_hasher
1313
)]
1414

15+
#[cfg(feature = "flame-it")]
16+
#[macro_use]
17+
extern crate flamer;
18+
1519
#[macro_use]
1620
extern crate bitflags;
1721
#[macro_use]
@@ -30,8 +34,7 @@ extern crate self as rustpython_vm;
3034

3135
pub use rustpython_derive::*;
3236

33-
use proc_macro_hack::proc_macro_hack;
34-
#[proc_macro_hack]
37+
#[proc_macro_hack::proc_macro_hack]
3538
pub use rustpython_derive::py_compile_bytecode;
3639

3740
//extern crate eval; use eval::eval::*;

0 commit comments

Comments
 (0)