Skip to content

Commit

Permalink
[Profiler] fud2 support and processing instrumentation-produced cells (
Browse files Browse the repository at this point in the history
…#2314)

*Note: This PR is still a draft because I have some questions about the
changes I made here that I wanted to ask about. Once those concerns are
resolved I will turn this into a full PR.*
## This PR contains:
- `fud2/scripts/profiler.rhai`: fud2 support for the profiler. I added a
new state `flamegraph` (the `svg` file containing the flame graph) and a
new operation called `profiler`, which takes a Calyx file and produces
the profiled flame graph.
- Updates to existing profiling scripts to read instrumented cell
signals and allow toggling between profiling optimized and non-optimized
Calyx programs.
- Updated fud2 tests to reflect new state and operation.

## Usage

First, clone https://github.com/brendangregg/FlameGraph and edit the
`fud2` configuration file to specify the location of `flamegraph.pl`:
ex)
```
[flamegraph]
script = "/home/ayaka/projects/FlameGraph/flamegraph.pl"
```

To obtain a flame graph from a Calyx program, specify the output as a
`svg` file.
ex) 
```
fud2 tests/correctness/while.futil -o while.svg -s sim.data=tests/correctness/while.futil
```
will produce the below flame graph.

![image](https://github.com/user-attachments/assets/b257ecb6-9f45-49b7-9555-f68e3212403e)

To obtain a flame graph for the non-compiler-optimized version of the
Calyx program, add `-s passes=no-opt`:
ex)
```
fud2 tests/correctness/while.futil -o flame.svg -s sim.data=tests/correctness/while.futil.data -s passes=no-opt
```
  • Loading branch information
ayakayorihiro authored Oct 29, 2024
1 parent b6ef6bc commit b6fd513
Show file tree
Hide file tree
Showing 9 changed files with 203 additions and 174 deletions.
6 changes: 5 additions & 1 deletion calyx-opt/src/passes/profiler_instrumentation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,22 @@ impl Visitor for ProfilerInstrumentation {
.iter()
.map(|group| group.borrow().name())
.collect::<Vec<_>>();
let comp_name = comp.name;
// for each group, construct a instrumentation cell and instrumentation assignment
let mut asgn_and_cell = Vec::with_capacity(group_names.len());
{
let mut builder = ir::Builder::new(comp, sigs);
let one = builder.add_constant(1, 1);
for group_name in group_names.into_iter() {
let name = format!("{}_probe", group_name);
// store group and component name (differentiate between groups of the same name under different components)
let name = format!("{}__{}_probe", group_name, comp_name);
let inst_cell = builder.add_primitive(name, "std_wire", &[1]);
let asgn: [ir::Assignment<ir::Nothing>; 1] = build_assignments!(
builder;
inst_cell["in"] = ? one["out"];
);
// the probes should be @control because they should have value 0 whenever the corresponding group is not active.
inst_cell.borrow_mut().add_attribute(BoolAttr::Control, 1);
inst_cell.borrow_mut().add_attribute(BoolAttr::Protected, 1);
asgn_and_cell.push((asgn[0].clone(), inst_cell));
}
Expand Down
86 changes: 86 additions & 0 deletions fud2/scripts/profiler.rhai
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import "calyx" as c;
import "verilator" as v;
import "rtl_sim" as sim;

export const instrumented_verilog = state("verilog-instrumented", ["sv"]);
export const instrumented_sim = state("sim-instrumented", ["exe"]);
export const instrumented_vcd = state("vcd-instrumented", ["vcd"]);
export const flamegraph = state("flamegraph", ["svg"]);

fn profiling_setup(e) {
e.var_("cells", "cells.json");
e.var_("tdcc-json", "fsm.json"); // might not be necessary if we get rid of fsms?

// series of passes after instrumentation?
e.config_var_or("passes", "profiler.compilation-passes", "all"); // set passes="no-opt" to run without optimizations

// rules for preprocessing

e.config_var_or("component_cells", "component_cells", "$calyx-base/target/debug/component_cells");
e.rule("component-cells", "$component_cells -l $calyx-base $in > $out");

// rules for postprocessing

// script to process vcd and attribute active cycles to every group/cell
e.var_("parse-vcd-script", "$calyx-base/tools/profiler/parse-vcd.py");
e.rule("parse-vcd", "python3 $parse-vcd-script $in $tdcc-json $cells summary.csv $out");

// script to produce trace and visuals
e.var_("create-visuals-script", "$calyx-base/tools/profiler/create-visuals.py");
e.rule("create-visuals", "python3 $create-visuals-script $in $cells timeline.json fsm-timeline.json $out fsm-flame.folded frequency.folded components.folded fsm-components.folded");

e.config_var("flamegraph-script", "flamegraph.script");
e.rule("produce-flame-graph", "$flamegraph-script $in > $out");

// Standalone Verilog testbench. copied from testbench
e.rsrc("tb.sv");

}

fn calyx_to_flamegraph(e, input, output) {
// instrument calyx and produce verilog
let instrumented_verilog = "instrumented.sv";
e.build_cmd(["$cells"], "component-cells", [input], []);
e.build_cmd([instrumented_verilog], "calyx", [input], []);
e.arg("backend", "verilog");
e.arg("args", " -p static-inline -p compile-static -p compile-repeat -p par-to-seq -p compile-invoke -p profiler-instrumentation -p $passes -x tdcc:dump-fsm-json=fsm.json");

let instrumented_sim = "instrumented.exe";
// verilog --> sim; adapted from verilator::verilator_build()
let verilator_out_dir = "verilator-out";
let sim_bin = `${verilator_out_dir}/Vtoplevel`;
e.build_cmd(
[sim_bin],
"verilator-compile-standalone-tb",
[instrumented_verilog],
["tb.sv"],
);
e.arg("out-dir", verilator_out_dir);
e.build("cp", sim_bin, instrumented_sim);

let instrumented_vcd = "instrumented.vcd";
// sim --> vcd; adapted from rtl_sim
e.build_cmd(
["sim.log", instrumented_vcd],
"sim-run",
[instrumented_sim, "$datadir"],
[],
);
e.arg("bin", instrumented_sim);
e.arg("args", `+NOTRACE=0 +OUT=${instrumented_vcd}`);

// vcd --> flamegraph
let elems_profiled_json = "elems-profiled.json";
let flamegraph_folded = "flamegraph.folded";
e.build_cmd([elems_profiled_json], "parse-vcd", [instrumented_vcd], []);
e.build_cmd([flamegraph_folded], "create-visuals", [elems_profiled_json], ["$cells"]);
e.build_cmd([output], "produce-flame-graph", [flamegraph_folded], []);
}

op(
"profiler",
[c::calyx_setup, profiling_setup, v::verilator_setup, sim::sim_setup],
c::calyx_state,
flamegraph,
|e, input, output| calyx_to_flamegraph(e, input, output)
);
6 changes: 6 additions & 0 deletions fud2/tests/snapshots/tests__list_ops.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
source: fud2/tests/tests.rs
snapshot_kind: text
---
[
(
Expand Down Expand Up @@ -97,6 +98,11 @@ source: fud2/tests/tests.rs
"calyx",
"primitive-uses-json",
),
(
"profiler",
"calyx",
"flamegraph",
),
(
"simulate",
"sim",
Expand Down
5 changes: 5 additions & 0 deletions fud2/tests/snapshots/tests__list_states.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
source: fud2/tests/tests.rs
snapshot_kind: text
---
[
"calyx",
Expand All @@ -10,11 +11,15 @@ source: fud2/tests/tests.rs
"dat",
"firrtl",
"firrtl-with-primitives",
"flamegraph",
"mrxl",
"primitive-uses-json",
"sim",
"sim-instrumented",
"vcd",
"vcd-instrumented",
"verilog",
"verilog-instrumented",
"verilog-noverify",
"verilog-refmem",
"verilog-refmem-noverify",
Expand Down
70 changes: 70 additions & 0 deletions fud2/tests/snapshots/tests__test@plan_profiler.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
source: fud2/tests/tests.rs
description: "emit plan: profiler"
snapshot_kind: text
---
build-tool = fud2
rule get-rsrc
command = $build-tool get-rsrc $out

calyx-base = /test/calyx
calyx-exe = $calyx-base/target/debug/calyx
args =
rule calyx
command = $calyx-exe -l $calyx-base -b $backend $args $in > $out
rule calyx-pass
command = $calyx-exe -l $calyx-base -p $pass $args $in > $out
flags = -p none
rule calyx-with-flags
command = $calyx-exe -l $calyx-base $flags $args $in > $out

cells = cells.json
tdcc-json = fsm.json
passes = all
component_cells = $calyx-base/target/debug/component_cells
rule component-cells
command = $component_cells -l $calyx-base $in > $out
parse-vcd-script = $calyx-base/tools/profiler/parse-vcd.py
rule parse-vcd
command = python3 $parse-vcd-script $in $tdcc-json $cells summary.csv $out
create-visuals-script = $calyx-base/tools/profiler/create-visuals.py
rule create-visuals
command = python3 $create-visuals-script $in $cells timeline.json fsm-timeline.json $out fsm-flame.folded frequency.folded components.folded fsm-components.folded

verilator = verilator
cycle-limit = 500000000
rule verilator-compile-standalone-tb
command = $verilator $in tb.sv --trace --binary --top-module toplevel -fno-inline -Mdir $out-dir
rule verilator-compile-custom-tb
command = $verilator $in tb.sv memories.sv --trace --binary --top-module toplevel -fno-inline -Mdir $out-dir
rule cp
command = cp $in $out

python = python3
build json-dat.py: get-rsrc
rule hex-data
command = $python json-dat.py --from-json $in $out
rule json-data
command = $python json-dat.py --to-json $out $in
sim_data = /test/data.json
datadir = sim_data
build $datadir: hex-data $sim_data | json-dat.py
rule sim-run
command = ./$bin +DATA=$datadir +CYCLE_LIMIT=$cycle-limit $args > $out
cycle-limit = 500000000

build $cells: component-cells /input.ext
build instrumented.sv: calyx /input.ext
backend = verilog
args = -p static-inline -p compile-static -p compile-repeat -p par-to-seq -p compile-invoke -p profiler-instrumentation -p $passes -x tdcc:dump-fsm-json=fsm.json
build verilator-out/Vtoplevel: verilator-compile-standalone-tb instrumented.sv | tb.sv
out-dir = verilator-out
build instrumented.exe: cp verilator-out/Vtoplevel
build sim.log instrumented.vcd: sim-run instrumented.exe $datadir
bin = instrumented.exe
args = +NOTRACE=0 +OUT=instrumented.vcd
build elems-profiled.json: parse-vcd instrumented.vcd
build flamegraph.folded: create-visuals elems-profiled.json | $cells
build /output.ext: produce-flame-graph flamegraph.folded

default /output.ext
5 changes: 2 additions & 3 deletions tools/component_cells/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ use serde::Serialize;
use std::path::{Path, PathBuf};
use std::{collections::HashSet, io};

/// Tool to obtain list of names and original component names for all non-primitive cells in each component.

#[derive(FromArgs)]
/// Path for library and path for file to read from
struct Args {
Expand All @@ -26,9 +28,6 @@ fn read_path(path: &str) -> Result<PathBuf, String> {
Ok(Path::new(path).into())
}

#[derive(Default)]
pub struct ComponentCellsBackend;

fn main() -> CalyxResult<()> {
let p: Args = argh::from_env();

Expand Down
9 changes: 7 additions & 2 deletions tools/profiler/create-visuals.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,13 @@ def get_active_group(self):
elif len(self.active_groups) == 1:
return self.active_groups[0]
else:
raise Exception(f'Component {self.component} is parallel! Active groups: {self.active_groups}')
# concatenate all parallel active groups
sorted_active_groups = list(sorted(self.active_groups))
acc = sorted_active_groups[0]
for group in sorted_active_groups[1:]:
suffix = group.split(".")[-1]
acc += "/" + suffix
return acc

"""
Returns the identifier of this stack: either the full name of the active group, or the full name of the cell if no groups are active.
Expand Down Expand Up @@ -266,7 +272,6 @@ def create_timeline_stacks(trace, main_component):
cell_to_stackframe_info["TOP.toplevel"] = (2, 1)

for i in trace:
print(trace[i])
active_this_cycle = set()
# Start from the bottom up. Parent is the previous stack!
parent = "MAIN"
Expand Down
Loading

0 comments on commit b6fd513

Please sign in to comment.