rapx/analysis/core/api_dependency/
mod.rs

1mod fuzzable;
2/// NOTE: This analysis module is currently under development and is highly unstable.
3/// The #[allow(unused)] attribute is applied to suppress excessive lint warnings.
4/// Once the analysis stabilizes, this marker should be removed.
5
6#[allow(unused)]
7pub mod graph;
8mod mono;
9mod utils;
10#[allow(unused)]
11mod visit;
12
13use crate::analysis::Analysis;
14pub use graph::ApiDependencyGraph;
15pub use graph::{DepEdge, DepNode};
16use rustc_hir::def_id::LOCAL_CRATE;
17use rustc_middle::ty::TyCtxt;
18use serde::Serialize;
19use std::path::PathBuf;
20pub use utils::{is_def_id_public, is_fuzzable_ty};
21pub use visit::Config as VisitConfig;
22
23#[derive(Debug, Clone, Serialize)]
24pub struct StatsWithCoverage {
25    pub num_apis: usize,
26    pub num_generic_apis: usize,
27    pub num_covered_apis: usize,
28    pub num_covered_generic_apis: usize,
29}
30
31#[derive(Debug, Clone, Eq, PartialEq, PartialOrd)]
32pub struct Config {
33    pub resolve_generic: bool,
34    pub visit_config: visit::Config,
35    pub max_generic_search_iteration: usize,
36    pub dump: Option<PathBuf>,
37}
38
39impl Default for Config {
40    fn default() -> Self {
41        Config {
42            resolve_generic: true,
43            visit_config: VisitConfig::default(),
44            max_generic_search_iteration: 10,
45            dump: None,
46        }
47    }
48}
49
50pub trait ApiDependencyAnalysis<'tcx> {
51    fn get_api_dependency_graph(&self) -> ApiDependencyGraph<'tcx>;
52}
53
54pub struct ApiDependencyAnalyzer<'tcx> {
55    tcx: TyCtxt<'tcx>,
56    config: Config,
57    api_graph: ApiDependencyGraph<'tcx>,
58}
59
60impl<'tcx> ApiDependencyAnalyzer<'tcx> {
61    pub fn new(tcx: TyCtxt<'tcx>, config: Config) -> ApiDependencyAnalyzer<'tcx> {
62        ApiDependencyAnalyzer {
63            tcx,
64            config,
65            api_graph: ApiDependencyGraph::new(tcx),
66        }
67    }
68}
69
70impl<'tcx> Analysis for ApiDependencyAnalyzer<'tcx> {
71    fn name(&self) -> &'static str {
72        "Default API dependency graph analysis algorithm."
73    }
74
75    fn run(&mut self) {
76        let local_crate_name = self.tcx.crate_name(LOCAL_CRATE);
77        let local_crate_type = self.tcx.crate_types()[0];
78        rap_info!(
79            "Build API dependency graph on {} ({}), config = {:?}",
80            local_crate_name.as_str(),
81            local_crate_type,
82            self.config,
83        );
84
85        let api_graph = &mut self.api_graph;
86        api_graph.build(&self.config);
87
88        let stats = api_graph.statistics();
89        stats.info();
90        let mut num_covered_apis = 0;
91        let mut num_covered_generic_apis = 0;
92        let mut num_total = 0;
93
94        api_graph.traverse_covered_api_with(
95            &mut |did| {
96                num_covered_apis += 1;
97                if utils::fn_requires_monomorphization(did, self.tcx) {
98                    num_covered_generic_apis += 1;
99                }
100            },
101            &mut |_| {
102                num_total += 1;
103            },
104        );
105
106        rap_info!("uncovered APIs: {:?}", api_graph.uncovered_api());
107
108        rap_info!(
109            "Cov API/Cov GAPI/#API/#GAPI: {}({:.2})/{}({:.2})/{}/{}",
110            num_covered_apis,
111            num_covered_apis as f64 / stats.num_api as f64,
112            num_covered_generic_apis,
113            num_covered_generic_apis as f64 / stats.num_generic_api as f64,
114            stats.num_api,
115            stats.num_generic_api
116        );
117
118        let stats_with_coverage = StatsWithCoverage {
119            num_apis: stats.num_api,
120            num_generic_apis: stats.num_generic_api,
121            num_covered_apis,
122            num_covered_generic_apis,
123        };
124
125        // dump adg stats
126        let stats_file = std::fs::File::create("adg_stats.json").unwrap();
127        serde_json::to_writer(stats_file, &stats_with_coverage)
128            .expect("failed to dump stats to JSON");
129
130        // dump API graph, determine the format base on extension name
131        if let Some(dump_path) = &self.config.dump {
132            self.api_graph
133                .dump_to_file(dump_path)
134                .inspect_err(|err| {
135                    rap_error!("{:?}", err);
136                })
137                .expect("failed to dump API graph");
138        }
139    }
140
141    fn reset(&mut self) {
142        todo!();
143    }
144}
145
146impl<'tcx> ApiDependencyAnalysis<'tcx> for ApiDependencyAnalyzer<'tcx> {
147    fn get_api_dependency_graph(&self) -> ApiDependencyGraph<'tcx> {
148        self.api_graph.clone()
149    }
150}