Add metrics for unevaluated constants, failed mir bodies, and failed data layouts

This commit is contained in:
hkalbasi 2023-05-14 17:12:28 +03:30
parent cbd14e9840
commit 51e8b8ff14
2 changed files with 86 additions and 19 deletions

View File

@ -2,7 +2,6 @@
//! errors. //! errors.
use std::{ use std::{
collections::HashMap,
env, env,
time::{SystemTime, UNIX_EPOCH}, time::{SystemTime, UNIX_EPOCH},
}; };
@ -16,7 +15,7 @@ use hir_def::{
hir::{ExprId, PatId}, hir::{ExprId, PatId},
FunctionId, FunctionId,
}; };
use hir_ty::{Interner, TyExt, TypeFlags}; use hir_ty::{Interner, Substitution, TyExt, TypeFlags};
use ide::{Analysis, AnalysisHost, LineCol, RootDatabase}; use ide::{Analysis, AnalysisHost, LineCol, RootDatabase};
use ide_db::base_db::{ use ide_db::base_db::{
salsa::{self, debug::DebugQueryTable, ParallelDatabase}, salsa::{self, debug::DebugQueryTable, ParallelDatabase},
@ -122,14 +121,19 @@ impl flags::AnalysisStats {
eprint!(" crates: {num_crates}"); eprint!(" crates: {num_crates}");
let mut num_decls = 0; let mut num_decls = 0;
let mut funcs = Vec::new(); let mut funcs = Vec::new();
let mut adts = Vec::new();
let mut consts = Vec::new();
while let Some(module) = visit_queue.pop() { while let Some(module) = visit_queue.pop() {
if visited_modules.insert(module) { if visited_modules.insert(module) {
visit_queue.extend(module.children(db)); visit_queue.extend(module.children(db));
for decl in module.declarations(db) { for decl in module.declarations(db) {
num_decls += 1; num_decls += 1;
if let ModuleDef::Function(f) = decl { match decl {
funcs.push(f); ModuleDef::Function(f) => funcs.push(f),
ModuleDef::Adt(a) => adts.push(a),
ModuleDef::Const(c) => consts.push(c),
_ => (),
} }
} }
@ -154,10 +158,13 @@ impl flags::AnalysisStats {
self.run_inference(&host, db, &vfs, &funcs, verbosity); self.run_inference(&host, db, &vfs, &funcs, verbosity);
} }
if self.mir_stats { if !self.skip_mir_stats {
self.lower_mir(db, &funcs); self.run_mir_lowering(db, &funcs, verbosity);
} }
self.run_data_layout(db, &adts, verbosity);
self.run_const_eval(db, &consts, verbosity);
let total_span = analysis_sw.elapsed(); let total_span = analysis_sw.elapsed();
eprintln!("{:<20} {total_span}", "Total:"); eprintln!("{:<20} {total_span}", "Total:");
report_metric("total time", total_span.time.as_millis() as u64, "ms"); report_metric("total time", total_span.time.as_millis() as u64, "ms");
@ -193,22 +200,82 @@ impl flags::AnalysisStats {
Ok(()) Ok(())
} }
fn lower_mir(&self, db: &RootDatabase, funcs: &[Function]) { fn run_data_layout(&self, db: &RootDatabase, adts: &[hir::Adt], verbosity: Verbosity) {
let all = funcs.len(); let mut all = 0;
let mut fail = 0; let mut fail = 0;
let mut h: HashMap<String, usize> = HashMap::new(); for &a in adts {
for f in funcs { if db.generic_params(a.into()).iter().next().is_some() {
let f = FunctionId::from(*f); // Data types with generics don't have layout.
let Err(e) = db.mir_body(f.into()) else { continue;
}
all += 1;
let Err(e) = db.layout_of_adt(hir_def::AdtId::from(a).into(), Substitution::empty(Interner)) else {
continue; continue;
}; };
let es = format!("{:?}", e); if verbosity.is_spammy() {
*h.entry(es).or_default() += 1; let full_name = a
.module(db)
.path_to_root(db)
.into_iter()
.rev()
.filter_map(|it| it.name(db))
.chain(Some(a.name(db)))
.join("::");
println!("Data layout for {full_name} failed due {e:?}");
}
fail += 1;
}
eprintln!("Failed data layouts: {fail} ({}%)", fail * 100 / all);
report_metric("failed data layouts", fail, "#");
}
fn run_const_eval(&self, db: &RootDatabase, consts: &[hir::Const], verbosity: Verbosity) {
let mut all = 0;
let mut fail = 0;
for &c in consts {
all += 1;
let Err(e) = c.render_eval(db) else {
continue;
};
if verbosity.is_spammy() {
let full_name = c
.module(db)
.path_to_root(db)
.into_iter()
.rev()
.filter_map(|it| it.name(db))
.chain(c.name(db))
.join("::");
println!("Const eval for {full_name} failed due {e:?}");
}
fail += 1;
}
eprintln!("Failed const evals: {fail} ({}%)", fail * 100 / all);
report_metric("failed const evals", fail, "#");
}
fn run_mir_lowering(&self, db: &RootDatabase, funcs: &[Function], verbosity: Verbosity) {
let all = funcs.len() as u64;
let mut fail = 0;
for f in funcs {
let Err(e) = db.mir_body(FunctionId::from(*f).into()) else {
continue;
};
if verbosity.is_spammy() {
let full_name = f
.module(db)
.path_to_root(db)
.into_iter()
.rev()
.filter_map(|it| it.name(db))
.chain(Some(f.name(db)))
.join("::");
println!("Mir body for {full_name} failed due {e:?}");
}
fail += 1; fail += 1;
} }
let h = h.into_iter().sorted_by_key(|x| x.1).collect::<Vec<_>>();
eprintln!("Mir failed reasons: {:#?}", h);
eprintln!("Mir failed bodies: {fail} ({}%)", fail * 100 / all); eprintln!("Mir failed bodies: {fail} ({}%)", fail * 100 / all);
report_metric("mir failed bodies", fail, "#");
} }
fn run_inference( fn run_inference(

View File

@ -66,8 +66,8 @@ xflags::xflags! {
optional --memory-usage optional --memory-usage
/// Print the total length of all source and macro files (whitespace is not counted). /// Print the total length of all source and macro files (whitespace is not counted).
optional --source-stats optional --source-stats
/// Print the number of bodies that fail to lower to mir, in addition to failed reasons. /// Only type check, skip lowering to mir
optional --mir-stats optional --skip-mir-stats
/// Only analyze items matching this path. /// Only analyze items matching this path.
optional -o, --only path: String optional -o, --only path: String
@ -171,7 +171,7 @@ pub struct AnalysisStats {
pub parallel: bool, pub parallel: bool,
pub memory_usage: bool, pub memory_usage: bool,
pub source_stats: bool, pub source_stats: bool,
pub mir_stats: bool, pub skip_mir_stats: bool,
pub only: Option<String>, pub only: Option<String>,
pub with_deps: bool, pub with_deps: bool,
pub no_sysroot: bool, pub no_sysroot: bool,