#![feature(rustc_private)]
extern crate rustc_driver;
extern crate rustc_hir;
extern crate rustc_interface;
extern crate rustc_middle;
extern crate rustc_session;
extern crate rustc_span;
mod extract;
mod fold;
mod sinks;
mod util;
use std::{path::PathBuf, process::Command};
use panicgraph::{Artifact, BuildConfig, StdMode};
use rustc_driver::{Callbacks, Compilation};
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::ty::TyCtxt;
use crate::extract::Extractor;
const OUT_DIR: &str = "PANICGRAPH_OUT";
const PROFILE: &str = "PANICGRAPH_PROFILE";
const STD_MODE: &str = "PANICGRAPH_STD_MODE";
struct PanicGraph;
impl Callbacks for PanicGraph {
fn after_analysis(
&mut self,
_compiler: &rustc_interface::interface::Compiler,
tcx: TyCtxt<'_>,
) -> Compilation {
if let Err(err) = emit(tcx) {
eprintln!("panicgraph: could not write artifact: {err}");
}
Compilation::Continue
}
}
fn emit(tcx: TyCtxt<'_>) -> std::io::Result<()> {
let Some(dir) = std::env::var_os(OUT_DIR) else {
return Ok(());
};
if std::env::var_os("CARGO_PRIMARY_PACKAGE").is_none() {
return Ok(());
}
let dir = PathBuf::from(dir);
std::fs::create_dir_all(&dir)?;
let krate = tcx.crate_name(LOCAL_CRATE).to_string();
let artifact = Artifact {
krate: krate.clone(),
config: build_config(tcx),
bodies: Extractor::new(tcx).run(),
};
let stamp = tcx.stable_crate_id(LOCAL_CRATE).as_u64();
let path = dir.join(format!("{krate}-{stamp:016x}.json"));
let json = serde_json::to_vec(&artifact).map_err(std::io::Error::other)?;
std::fs::write(path, json)
}
fn build_config(tcx: TyCtxt<'_>) -> BuildConfig {
let debug_assertions = tcx.sess.opts.debug_assertions;
let std_mode = match std::env::var(STD_MODE).as_deref() {
Ok("full") => StdMode::Full,
_ => StdMode::Shipped,
};
BuildConfig {
rustc: tcx.sess.cfg_version.to_owned(),
profile: std::env::var(PROFILE)
.unwrap_or_else(|_| "unknown".to_owned()),
debug_assertions,
overflow_checks: tcx
.sess
.opts
.cg
.overflow_checks
.unwrap_or(debug_assertions),
std_mode,
}
}
fn sysroot() -> Option<String> {
let out = Command::new("rustc").arg("--print=sysroot").output().ok()?;
let text = String::from_utf8(out.stdout).ok()?;
Some(text.trim().to_owned())
}
fn main() -> std::process::ExitCode {
let mut args: Vec<String> = std::env::args().collect();
if args.len() > 1 && args[1].ends_with("rustc") {
args.remove(1);
}
if let Some(root) = sysroot() {
args.push(format!("--sysroot={root}"));
}
args.push("-Zalways-encode-mir".to_owned());
rustc_driver::catch_with_exit_code(|| {
rustc_driver::run_compiler(&args, &mut PanicGraph);
})
}