Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions rapx/src/analysis/senryx/visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ use crate::{
dominated_graph::FunctionSummary,
symbolic_analysis::{AnaOperand, SymbolicDef, ValueDomain},
},
utils::{draw_dot::render_dot_string, fn_info::*, show_mir::display_mir},
utils::{
draw_dot::{DotGraph, render_dot_string},
fn_info::*,
show_mir::display_mir,
},
},
rap_debug, rap_warn,
};
Expand Down Expand Up @@ -1715,6 +1719,7 @@ impl<'tcx> BodyVisitor<'tcx> {

let name = format!("{}_path_{}", base_name, path_suffix);
let dot_string = self.chains.to_dot_graph();
render_dot_string(name, dot_string);
let dot_graph = DotGraph::new(name, dot_string);
render_dot_string(&dot_graph);
}
}
44 changes: 0 additions & 44 deletions rapx/src/analysis/upg/draw_dot.rs

This file was deleted.

33 changes: 28 additions & 5 deletions rapx/src/analysis/upg/mod.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
/*
* This module generates the unsafety propagation graph for each Rust module in the target crate.
*/
pub mod draw_dot;
pub mod fn_collector;
pub mod hir_visitor;
pub mod std_upg;
pub mod upg_graph;
pub mod upg_unit;

use crate::{
analysis::utils::{draw_dot::render_dot_graphs, fn_info::*},
utils::source::{get_fn_name_byid, get_module_name},
analysis::utils::{
draw_dot::{DotGraph, render_dot_graphs, render_dot_graphs_html},
fn_info::*,
},
utils::{
log::{span_to_filename, span_to_line_number},
source::{get_fn_name_byid, get_module_name, get_span},
},
};
use fn_collector::FnCollector;
use hir_visitor::ContainsUnsafe;
Expand Down Expand Up @@ -222,9 +227,27 @@ impl<'tcx> UPGAnalysis<'tcx> {
let mut final_dots = Vec::new();
for (mod_name, data) in modules_data {
let dot = data.upg_unit_string(&mod_name);
final_dots.push((mod_name, dot));
let url_map = data
.nodes_def_ids()
.iter()
.filter_map(|def_id| {
get_span(self.tcx, *def_id).map(|span| {
let label = format!("{:?}", *def_id);
let mut filename = span_to_filename(span);
filename = filename
.strip_prefix("src/")
.unwrap_or(&filename)
.to_string();
let line_number = span_to_line_number(span);
let url = format!("{}.html#{}", filename, line_number);
(label, url)
})
})
.collect();
final_dots.push(DotGraph::new_with_url_map(mod_name, dot, url_map));
}
rap_info!("{:?}", final_dots); // Output required for tests; do not change.
render_dot_graphs(final_dots);
render_dot_graphs(&final_dots);
render_dot_graphs_html(&final_dots);
}
}
13 changes: 7 additions & 6 deletions rapx/src/analysis/upg/std_upg.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
use super::{UPGAnalysis, upg_graph::UPGraph};
use crate::analysis::{
upg::draw_dot::render_dot_graphs,
utils::{fn_info::*, show_mir::display_mir},
use crate::analysis::utils::{
draw_dot::{DotGraph, render_dot_graphs},
fn_info::*,
show_mir::display_mir,
};
use rustc_hir::{Safety, def::DefKind, def_id::DefId};
use rustc_middle::{
Expand All @@ -28,13 +29,13 @@ impl<'tcx> UPGAnalysis<'tcx> {
}

pub fn render_dot(&mut self) {
let mut dot_strs = Vec::new();
let mut dots = Vec::new();
for upg in &self.upgs {
let dot_str = UPGraph::generate_dot_from_upg_unit(upg);
let caller_name = get_cleaned_def_path_name(self.tcx, upg.caller.def_id);
dot_strs.push((caller_name, dot_str));
dots.push(DotGraph::new(caller_name, dot_str));
}
render_dot_graphs(dot_strs);
render_dot_graphs(&dots);
}

pub fn get_chains(&mut self) {
Expand Down
4 changes: 4 additions & 0 deletions rapx/src/analysis/upg/upg_graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -266,4 +266,8 @@ impl UPGraph {
// println!("{}", dot_str);
dot_str
}

pub fn nodes_def_ids(&self) -> Vec<DefId> {
self.nodes.keys().map(|def_id| *def_id).collect()
}
}
63 changes: 63 additions & 0 deletions rapx/src/analysis/utils/assets/index.html.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
<!DOCTYPE html>
<html>

<head>
<style>
body {
margin: 20px;
font-family: Arial, sans-serif;
}

#graph {
border: 1px solid #ddd;
padding: 10px;
}
</style>
</head>

<body>
<h3 id="graph_title"></h3>
<div id="graph"></div>
<script type="module">
import { instance } from 'https://cdn.jsdelivr.net/npm/@viz-js/viz@3/+esm'; //Viz3库

// Template placeholders
let title = `{{TITLE}}`;
let dot = `{{DOT}}`;
let base_url = `{{BASE_URL}}`;
let url_map = `{{URL_MAP}}`;
url_map = JSON.parse(url_map);

async function renderGraph() {
// Render the title
document.getElementById("graph_title").innerText = title;

// Render the graph
try {
const viz = await instance();
const svg = viz.renderSVGElement(dot);
document.getElementById("graph").appendChild(svg);
} catch (error) {
console.error("Render Error:", error);
document.getElementById("graph").innerHTML =
`<div style="color:red;">Render Failed: ${error.message}</div>`;
}

// Add click event to each node
const nodes = document.querySelectorAll('g.node');
nodes.forEach(node => {
const label = node.querySelector('text').textContent;
if (url_map[label]) {
node.style.cursor = 'pointer';
node.addEventListener('click', () => {
let url = new URL(url_map[label], base_url).href;
window.open(url, '_blank');
});
}
});
}

renderGraph();
</script>
</body>
</html>
77 changes: 68 additions & 9 deletions rapx/src/analysis/utils/draw_dot.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,106 @@
use std::collections::HashMap;
use std::fs::{File, remove_file};
use std::io::Write;
use std::process::Command;

const HTML_TEMPLATE: &str = include_str!("assets/index.html.template");

#[derive(Debug)]
pub struct DotGraph {
pub name: String,
pub content: String,
pub url_map: HashMap<String, String>, // from node label to URL path
}

impl DotGraph {
pub fn new(name: String, content: String) -> Self {
Self {
name,
content,
url_map: HashMap::new(),
}
}

pub fn new_with_url_map(
name: String,
content: String,
url_map: HashMap<String, String>,
) -> Self {
Self {
name,
content,
url_map,
}
}
}

// please ensure 'graphviz' has been installed
pub fn render_dot_graphs(dot_graphs: Vec<(String, String)>) {
pub fn render_dot_graphs(dot_graphs: &Vec<DotGraph>) {
Command::new("mkdir")
.args(["UPG"])
.output()
.expect("Failed to create directory");

for (_index, dot) in dot_graphs.into_iter().enumerate() {
let file_name = format!("{}.dot", dot.0);
for dot_graph in dot_graphs.iter() {
let file_name = format!("{}.dot", dot_graph.name);
let mut file = File::create(&file_name).expect("Unable to create file");
file.write_all(dot.1.as_bytes())
file.write_all(dot_graph.content.as_bytes())
.expect("Unable to write data");

Command::new("dot")
.args(["-Tpng", &file_name, "-o", &format!("UPG/{}.png", dot.0)])
.args([
"-Tpng",
&file_name,
"-o",
&format!("UPG/{}.png", dot_graph.name),
])
.output()
.expect("Failed to execute Graphviz dot command");

remove_file(&file_name).expect("Failed to delete .dot file");
}
}

pub fn render_dot_string(name: String, dot_graph: String) {
pub fn render_dot_graphs_html(dot_graphs: &Vec<DotGraph>) {
Command::new("mkdir")
.args(["UPG"])
.output()
.expect("Failed to create directory");

for dot_graph in dot_graphs.iter() {
let title = &dot_graph.name;
let dot = &dot_graph.content;
let url_map = serde_json::to_string_pretty(&dot_graph.url_map).unwrap();
let html = HTML_TEMPLATE
.replace("{{TITLE}}", title)
.replace("{{DOT}}", dot)
.replace("{{URL_MAP}}", &url_map);

let file_name = format!("UPG/{}.html", dot_graph.name);
let mut file = File::create(&file_name).expect("Unable to create file");
file.write_all(html.as_bytes())
.expect("Unable to write data");
}
}

pub fn render_dot_string(dot_graph: &DotGraph) {
Command::new("mkdir")
.args(["MIR_dot_graph"])
.output()
.expect("Failed to create directory");

let file_name = format!("{}.dot", name);
let file_name = format!("{}.dot", dot_graph.name);
rap_debug!("render graph {:?}", file_name);
let mut file = File::create(&file_name).expect("Unable to create file");
file.write_all(dot_graph.as_bytes())
file.write_all(dot_graph.content.as_bytes())
.expect("Unable to write data");

Command::new("dot")
.args([
"-Tpng",
&file_name,
"-o",
&format!("MIR_dot_graph/{}.png", name),
&format!("MIR_dot_graph/{}.png", dot_graph.name),
])
.output()
.expect("Failed to execute Graphviz dot command");
Expand Down
13 changes: 8 additions & 5 deletions rapx/src/analysis/utils/fn_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ use super::draw_dot::render_dot_string;
use crate::analysis::{
core::dataflow::{DataFlowAnalysis, default::DataFlowAnalyzer},
senryx::{
contracts::{property, property::PropertyContract},
contracts::property::{self, PropertyContract},
matcher::parse_unsafe_api,
},
utils::draw_dot::DotGraph,
};
use crate::def_id::*;
use crate::{rap_debug, rap_warn};
Expand Down Expand Up @@ -143,19 +144,20 @@ pub fn get_cleaned_def_path_name_ori(tcx: TyCtxt, def_id: DefId) -> String {

pub fn get_sp_tags_json() -> serde_json::Value {
let json_data: serde_json::Value =
serde_json::from_str(include_str!("data/std_sps.json")).expect("Unable to parse JSON");
serde_json::from_str(include_str!("assets/std_sps.json")).expect("Unable to parse JSON");
json_data
}

pub fn get_std_api_signature_json() -> serde_json::Value {
let json_data: serde_json::Value =
serde_json::from_str(include_str!("data/std_sig.json")).expect("Unable to parse JSON");
serde_json::from_str(include_str!("assets/std_sig.json")).expect("Unable to parse JSON");
json_data
}

pub fn get_sp_tags_and_args_json() -> serde_json::Value {
let json_data: serde_json::Value =
serde_json::from_str(include_str!("data/std_sps_args.json")).expect("Unable to parse JSON");
serde_json::from_str(include_str!("assets/std_sps_args.json"))
.expect("Unable to parse JSON");
json_data
}

Expand Down Expand Up @@ -1471,7 +1473,8 @@ pub fn generate_mir_cfg_dot<'tcx>(
}
dot_content.push_str("}\n");
let name = get_cleaned_def_path_name(tcx, def_id);
render_dot_string(name, dot_content);
let dot_graph = DotGraph::new(name, dot_content);
render_dot_string(&dot_graph);
rap_debug!("render dot for {:?}", def_id);
Ok(())
}
Expand Down
Loading