Skip to content

Commit

Permalink
Adds urls to graph.visualize generated dot file. (FuelLabs#4310)
Browse files Browse the repository at this point in the history
## Description

graph.visualize now can include urls that are clickable in generated svg
files.
To set this up we can call forc with the argument
`--dca-graph-url-format`.
For vscode we would use: `--dca-graph-url-format
'vscode://file/{path}:{line}:{col}'`

This commit also changes forc `--dca-graph` to receive a path.
We still can output to std by using `--dca-graph ''`.

Also adds edge labels.
Also changes entry node shape to be different, a double octagon instead
of an ellipse.

## Checklist

- [ ] I have linked to any relevant issues.
- [ ] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [ ] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.
  • Loading branch information
esdrubal authored Apr 5, 2023
1 parent 77f575a commit 08e1c2f
Show file tree
Hide file tree
Showing 12 changed files with 132 additions and 39 deletions.
9 changes: 6 additions & 3 deletions forc-pkg/src/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,8 @@ pub struct DependencyDetails {
#[serde(rename_all = "kebab-case")]
pub struct BuildProfile {
pub print_ast: bool,
pub print_dca_graph: bool,
pub print_dca_graph: Option<String>,
pub print_dca_graph_url_format: Option<String>,
pub print_ir: bool,
pub print_finalized_asm: bool,
pub print_intermediate_asm: bool,
Expand Down Expand Up @@ -598,7 +599,8 @@ impl BuildProfile {
pub fn debug() -> Self {
Self {
print_ast: false,
print_dca_graph: false,
print_dca_graph: None,
print_dca_graph_url_format: None,
print_ir: false,
print_finalized_asm: false,
print_intermediate_asm: false,
Expand All @@ -613,7 +615,8 @@ impl BuildProfile {
pub fn release() -> Self {
Self {
print_ast: false,
print_dca_graph: false,
print_dca_graph: None,
print_dca_graph_url_format: None,
print_ir: false,
print_finalized_asm: false,
print_intermediate_asm: false,
Expand Down
19 changes: 15 additions & 4 deletions forc-pkg/src/pkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,13 @@ pub struct PkgOpts {
pub struct PrintOpts {
/// Print the generated Sway AST (Abstract Syntax Tree).
pub ast: bool,
/// Print the computed Sway DCA (Dead Code Analysis) graph.
pub dca_graph: bool,
/// Print the computed Sway DCA (Dead Code Analysis) graph to the specified path.
/// If not specified prints to stdout.
pub dca_graph: Option<String>,
/// Specifies the url format to be used in the generated dot file.
/// Variables {path}, {line} {col} can be used in the provided format.
/// An example for vscode would be: "vscode://file/{path}:{line}:{col}"
pub dca_graph_url_format: Option<String>,
/// Print the finalized ASM.
///
/// This is the state of the ASM with registers allocated and optimisations applied.
Expand Down Expand Up @@ -1534,7 +1539,8 @@ pub fn sway_build_config(
manifest_dir.to_path_buf(),
build_target,
)
.print_dca_graph(build_profile.print_dca_graph)
.print_dca_graph(build_profile.print_dca_graph.clone())
.print_dca_graph_url_format(build_profile.print_dca_graph_url_format.clone())
.print_finalized_asm(build_profile.print_finalized_asm)
.print_intermediate_asm(build_profile.print_intermediate_asm)
.print_ir(build_profile.print_ir)
Expand Down Expand Up @@ -2016,7 +2022,12 @@ fn build_profile_from_opts(
Default::default()
});
profile.print_ast |= print.ast;
profile.print_dca_graph |= print.dca_graph;
if profile.print_dca_graph.is_none() {
profile.print_dca_graph = print.dca_graph.clone();
}
if profile.print_dca_graph_url_format.is_none() {
profile.print_dca_graph_url_format = print.dca_graph_url_format.clone();
}
profile.print_ir |= print.ir;
profile.print_finalized_asm |= print.finalized_asm;
profile.print_intermediate_asm |= print.intermediate_asm;
Expand Down
3 changes: 2 additions & 1 deletion forc-plugins/forc-client/src/op/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,7 +236,8 @@ fn build_opts_from_cmd(cmd: &cmd::Deploy) -> pkg::BuildOpts {
},
print: pkg::PrintOpts {
ast: cmd.print.ast,
dca_graph: cmd.print.dca_graph,
dca_graph: cmd.print.dca_graph.clone(),
dca_graph_url_format: cmd.print.dca_graph_url_format.clone(),
finalized_asm: cmd.print.finalized_asm,
intermediate_asm: cmd.print.intermediate_asm,
ir: cmd.print.ir,
Expand Down
3 changes: 2 additions & 1 deletion forc-plugins/forc-client/src/op/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,8 @@ fn build_opts_from_cmd(cmd: &cmd::Run) -> pkg::BuildOpts {
},
print: pkg::PrintOpts {
ast: cmd.print.ast,
dca_graph: cmd.print.dca_graph,
dca_graph: cmd.print.dca_graph.clone(),
dca_graph_url_format: cmd.print.dca_graph_url_format.clone(),
finalized_asm: cmd.print.finalized_asm,
intermediate_asm: cmd.print.intermediate_asm,
ir: cmd.print.ir,
Expand Down
1 change: 1 addition & 0 deletions forc/src/cli/commands/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ fn opts_from_cmd(cmd: Command) -> forc_test::Opts {
print: pkg::PrintOpts {
ast: cmd.build.print.ast,
dca_graph: cmd.build.print.dca_graph,
dca_graph_url_format: cmd.build.print.dca_graph_url_format,
finalized_asm: cmd.build.print.finalized_asm,
intermediate_asm: cmd.build.print.intermediate_asm,
ir: cmd.build.print.ir,
Expand Down
10 changes: 9 additions & 1 deletion forc/src/cli/shared.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,16 @@ pub struct Print {
#[clap(long)]
pub ast: bool,
/// Print the computed Sway DCA graph.
/// DCA graph is printed to the specified path.
/// If specified '' graph is printed to stdout.
#[clap(long)]
pub dca_graph: bool,
pub dca_graph: Option<String>,
/// Specifies the url format to be used in the generated dot file.
/// Variables {path}, {line} {col} can be used in the provided format.
/// An example for vscode would be:
/// "vscode://file/{path}:{line}:{col}"
#[clap(long, verbatim_doc_comment)]
pub dca_graph_url_format: Option<String>,
/// Print the finalized ASM.
///
/// This is the state of the ASM with registers allocated and optimisations applied.
Expand Down
1 change: 1 addition & 0 deletions forc/src/ops/forc_build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ fn opts_from_cmd(cmd: BuildCommand) -> pkg::BuildOpts {
print: pkg::PrintOpts {
ast: cmd.build.print.ast,
dca_graph: cmd.build.print.dca_graph,
dca_graph_url_format: cmd.build.print.dca_graph_url_format,
finalized_asm: cmd.build.print.finalized_asm,
intermediate_asm: cmd.build.print.intermediate_asm,
ir: cmd.build.print.ir,
Expand Down
3 changes: 2 additions & 1 deletion forc/src/ops/forc_contract_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ fn build_opts_from_cmd(cmd: &ContractIdCommand) -> pkg::BuildOpts {
},
print: pkg::PrintOpts {
ast: cmd.print.ast,
dca_graph: cmd.print.dca_graph,
dca_graph: cmd.print.dca_graph.clone(),
dca_graph_url_format: cmd.print.dca_graph_url_format.clone(),
finalized_asm: cmd.print.finalized_asm,
intermediate_asm: cmd.print.intermediate_asm,
ir: cmd.print.ir,
Expand Down
1 change: 1 addition & 0 deletions forc/src/ops/forc_predicate_root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ fn build_opts_from_cmd(cmd: PredicateRootCommand) -> pkg::BuildOpts {
print: pkg::PrintOpts {
ast: cmd.print.ast,
dca_graph: cmd.print.dca_graph,
dca_graph_url_format: cmd.print.dca_graph_url_format.clone(),
finalized_asm: cmd.print.finalized_asm,
intermediate_asm: cmd.print.intermediate_asm,
ir: cmd.print.ir,
Expand Down
15 changes: 12 additions & 3 deletions sway-core/src/build_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ pub struct BuildConfig {
// The canonical file path to the root module.
// E.g. `/home/user/project/src/main.sw`.
pub(crate) canonical_root_module: Arc<PathBuf>,
pub(crate) print_dca_graph: bool,
pub(crate) print_dca_graph: Option<String>,
pub(crate) print_dca_graph_url_format: Option<String>,
pub(crate) print_intermediate_asm: bool,
pub(crate) print_finalized_asm: bool,
pub(crate) print_ir: bool,
Expand Down Expand Up @@ -81,21 +82,29 @@ impl BuildConfig {
Self {
build_target,
canonical_root_module: Arc::new(canonical_root_module),
print_dca_graph: false,
print_dca_graph: None,
print_dca_graph_url_format: None,
print_intermediate_asm: false,
print_finalized_asm: false,
print_ir: false,
include_tests: false,
}
}

pub fn print_dca_graph(self, a: bool) -> Self {
pub fn print_dca_graph(self, a: Option<String>) -> Self {
Self {
print_dca_graph: a,
..self
}
}

pub fn print_dca_graph_url_format(self, a: Option<String>) -> Self {
Self {
print_dca_graph_url_format: a,
..self
}
}

pub fn print_intermediate_asm(self, a: bool) -> Self {
Self {
print_intermediate_asm: a,
Expand Down
82 changes: 69 additions & 13 deletions sway-core/src/control_flow_analysis/flow_graph/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! This is the flow graph, a graph which contains edges that represent possible steps of program
//! execution.
use std::collections::HashMap;
use std::{collections::HashMap, fs};

use crate::{
decl_engine::*,
Expand All @@ -10,7 +10,7 @@ use crate::{
transform, Engines, Ident,
};

use sway_types::{span::Span, BaseIdent, IdentUnique};
use sway_types::{span::Span, BaseIdent, IdentUnique, Spanned};

use petgraph::{graph::EdgeIndex, prelude::NodeIndex};

Expand Down Expand Up @@ -200,17 +200,60 @@ impl<'cfg> ControlFlowGraph<'cfg> {
}

/// Prints out GraphViz DOT format for this graph.
pub(crate) fn visualize(&self, engines: Engines<'_>) {
use petgraph::dot::{Config, Dot};
let string_graph = self.graph.filter_map(
|_idx, node| Some(format!("{:?}", engines.help_out(node))),
|_idx, edge| Some(edge.0.clone()),
);

tracing::info!(
"{:?}",
Dot::with_config(&string_graph, &[Config::EdgeNoLabel])
);
pub(crate) fn visualize(
&self,
engines: Engines<'_>,
print_graph: Option<String>,
print_graph_url_format: Option<String>,
) {
if let Some(graph_path) = print_graph {
use petgraph::dot::{Config, Dot};
let string_graph = self.graph.filter_map(
|_idx, node| Some(format!("{:?}", engines.help_out(node))),
|_idx, edge| Some(edge.0.clone()),
);

let output = format!(
"{:?}",
Dot::with_attr_getters(
&string_graph,
&[Config::NodeNoLabel, Config::EdgeNoLabel],
&|_, er| format!("label = {:?}", er.weight()),
&|_, nr| {
let node = &self.graph[nr.0];
let mut shape = "";
if self.entry_points.contains(&nr.0) {
shape = "shape=doubleoctagon";
}
let mut url = "".to_string();
if let Some(url_format) = print_graph_url_format.clone() {
if let Some(span) = node.span() {
if let Some(path) = span.path_str() {
let (line, col) = span.start_pos().line_col();
let url_format = url_format
.replace("{path}", path.to_string().as_str())
.replace("{line}", line.to_string().as_str())
.replace("{col}", col.to_string().as_str());
url = format!("URL = {url_format:?}");
}
}
}
format!("{shape} label = {:?} {url}", nr.1)
},
)
);

if graph_path.is_empty() {
tracing::info!("{output}");
} else {
let result = fs::write(graph_path.clone(), output);
if let Some(error) = result.err() {
tracing::error!(
"There was an issue while outputing DCA grap to path {graph_path:?}\n{error}"
);
}
}
}
}
}

Expand Down Expand Up @@ -243,4 +286,17 @@ impl<'cfg> ControlFlowGraphNode<'cfg> {
parent_node: None,
}
}

fn span(&self) -> Option<Span> {
match self {
ControlFlowGraphNode::OrganizationalDominator(_) => None,
ControlFlowGraphNode::ProgramNode { node, .. } => Some(node.span.clone()),
ControlFlowGraphNode::EnumVariant { variant_name, .. } => Some(variant_name.span()),
ControlFlowGraphNode::MethodDeclaration { span, .. } => Some(span.clone()),
ControlFlowGraphNode::StructField {
struct_field_name, ..
} => Some(struct_field_name.span()),
ControlFlowGraphNode::StorageField { field_name } => Some(field_name.span()),
}
}
}
24 changes: 12 additions & 12 deletions sway-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,15 +372,16 @@ pub fn parsed_to_ast(
_ => None,
}));

let (print_graph, print_graph_url_format) = match build_config {
Some(cfg) => (
cfg.print_dca_graph.clone(),
cfg.print_dca_graph_url_format.clone(),
),
None => (None, None),
};
// Perform control flow analysis and extend with any errors.
let cfa_res = perform_control_flow_analysis(
engines,
&typed_program,
match build_config {
Some(cfg) => cfg.print_dca_graph,
None => false,
},
);
let cfa_res =
perform_control_flow_analysis(engines, &typed_program, print_graph, print_graph_url_format);
errors.extend(cfa_res.errors);
warnings.extend(cfa_res.warnings);

Expand Down Expand Up @@ -676,7 +677,8 @@ pub fn asm_to_bytecode(
fn perform_control_flow_analysis(
engines: Engines<'_>,
program: &ty::TyProgram,
print_graph: bool,
print_graph: Option<String>,
print_graph_url_format: Option<String>,
) -> CompileResult<()> {
let dca_res = dead_code_analysis(engines, program);
let rpa_errors = return_path_analysis(engines, program);
Expand All @@ -686,9 +688,7 @@ fn perform_control_flow_analysis(
err(vec![], rpa_errors)
};
if let Some(graph) = dca_res.clone().value {
if print_graph {
graph.visualize(engines);
}
graph.visualize(engines, print_graph, print_graph_url_format);
}
dca_res.flat_map(|_| rpa_res)
}
Expand Down

0 comments on commit 08e1c2f

Please sign in to comment.