use std::sync::atomic::{AtomicBool, AtomicI32, Ordering};
use std::sync::{Mutex, OnceLock};
use crate::ported::zsh_h::OPT_ISSET;
#[derive(Debug, Clone, Default)]
pub struct Pfunc { pub name: String, pub calls: i64, pub time: f64, pub self_time: f64, pub num: i64, }
#[derive(Debug, Clone, Copy)]
pub struct Sfunc { pub p: usize, pub beg: f64, }
#[derive(Debug, Clone, Default)]
pub struct Parc { pub from: usize, pub to: usize, pub calls: i64, pub time: f64, pub self_time: f64, }
pub static CALLS: Mutex<Vec<Pfunc>> = Mutex::new(Vec::new());
pub static NCALLS: AtomicI32 = AtomicI32::new(0);
pub static ARCS: Mutex<Vec<Parc>> = Mutex::new(Vec::new());
pub static NARCS: AtomicI32 = AtomicI32::new(0);
pub static STACK: Mutex<Vec<Sfunc>> = Mutex::new(Vec::new());
pub static ZPROF_MODULE: AtomicBool = AtomicBool::new(false);
pub fn freepfuncs(f: &mut Vec<Pfunc>) { f.clear(); }
pub fn freeparcs(a: &mut Vec<Parc>) { a.clear(); }
pub fn findpfunc(name: &str) -> Option<usize> { let calls = CALLS.lock().unwrap();
calls.iter().position(|f| f.name == name)
}
pub fn findparc(f: usize, t: usize) -> Option<usize> { let arcs = ARCS.lock().unwrap();
arcs.iter().position(|a| a.from == f && a.to == t)
}
pub fn cmpsfuncs(a: &Pfunc, b: &Pfunc) -> std::cmp::Ordering { b.self_time.partial_cmp(&a.self_time).unwrap_or(std::cmp::Ordering::Equal)
}
pub fn cmptfuncs(a: &Pfunc, b: &Pfunc) -> std::cmp::Ordering { b.time.partial_cmp(&a.time).unwrap_or(std::cmp::Ordering::Equal)
}
pub fn cmpparcs(a: &Parc, b: &Parc) -> std::cmp::Ordering { b.time.partial_cmp(&a.time).unwrap_or(std::cmp::Ordering::Equal)
}
pub fn bin_zprof(_nam: &str, _args: &[String], ops: &crate::ported::zsh_h::options, _func: i32) -> i32 {
let opt_c = OPT_ISSET(ops, b'c');
if opt_c {
let mut calls = CALLS.lock().unwrap();
freepfuncs(&mut calls); NCALLS.store(0, Ordering::SeqCst); let mut arcs = ARCS.lock().unwrap();
freeparcs(&mut arcs); NARCS.store(0, Ordering::SeqCst); return 0; }
let calls = CALLS.lock().unwrap();
let arcs = ARCS.lock().unwrap();
let mut fs: Vec<usize> = (0..calls.len()).collect(); let as_arcs: Vec<usize> = (0..arcs.len()).collect(); let mut total: f64 = 0.0; for &i in &fs {
total += calls[i].self_time; }
fs.sort_by(|&a, &b| cmpsfuncs(&calls[a], &calls[b]));
println!("num calls time self name");
println!("-----------------------------------------------------------------------------------");
drop(calls);
{
let mut calls_w = CALLS.lock().unwrap();
for (i, &idx) in fs.iter().enumerate() { calls_w[idx].num = (i + 1) as i64; }
}
let calls = CALLS.lock().unwrap();
for &idx in &fs { let f = &calls[idx];
let avg_t = if f.calls > 0 { f.time / f.calls as f64 } else { 0.0 };
let avg_s = if f.calls > 0 { f.self_time / f.calls as f64 } else { 0.0 };
let pct_t = if total != 0.0 { (f.time / total) * 100.0 } else { 0.0 };
let pct_s = if total != 0.0 { (f.self_time / total) * 100.0 } else { 0.0 };
println!(
"{:2}) {:4} {:8.2} {:8.2} {:6.2}% {:8.2} {:8.2} {:6.2}% {}",
f.num, f.calls, f.time, avg_t, pct_t,
f.self_time, avg_s, pct_s,
f.name
);
}
let mut fs_t: Vec<usize> = fs.clone();
fs_t.sort_by(|&a, &b| cmptfuncs(&calls[a], &calls[b]));
for &fp_idx in &fs_t { println!();
println!("-----------------------------------------------------------------------------------");
println!();
let f = &calls[fp_idx];
for &ap in &as_arcs { let a = &arcs[ap];
if a.to == fp_idx { let avg_t = if a.calls > 0 { a.time / a.calls as f64 } else { 0.0 };
let avg_s = if a.calls > 0 { a.self_time / a.calls as f64 } else { 0.0 };
let pct_t = if total != 0.0 { (a.time / total) * 100.0 } else { 0.0 };
let from_name = &calls[a.from].name;
let from_num = calls[a.from].num;
println!(
" {:4}/{:<4} {:8.2} {:8.2} {:6.2}% {:8.2} {:8.2} {} [{}]",
a.calls, f.calls, a.time, avg_t, pct_t,
a.self_time, avg_s,
from_name, from_num
);
}
}
let avg_t = if f.calls > 0 { f.time / f.calls as f64 } else { 0.0 };
let avg_s = if f.calls > 0 { f.self_time / f.calls as f64 } else { 0.0 };
let pct_t = if total != 0.0 { (f.time / total) * 100.0 } else { 0.0 };
let pct_s = if total != 0.0 { (f.self_time / total) * 100.0 } else { 0.0 };
println!(
"{:2}) {:4} {:8.2} {:8.2} {:6.2}% {:8.2} {:8.2} {:6.2}% {}",
f.num, f.calls, f.time, avg_t, pct_t,
f.self_time, avg_s, pct_s,
f.name
);
for &ap in as_arcs.iter().rev() { let a = &arcs[ap];
if a.from == fp_idx { let avg_t = if a.calls > 0 { a.time / a.calls as f64 } else { 0.0 };
let avg_s = if a.calls > 0 { a.self_time / a.calls as f64 } else { 0.0 };
let pct_t = if total != 0.0 { (a.time / total) * 100.0 } else { 0.0 };
let to_name = &calls[a.to].name;
let to_num = calls[a.to].num;
let to_calls = calls[a.to].calls;
println!(
" {:4}/{:<4} {:8.2} {:8.2} {:6.2}% {:8.2} {:8.2} {} [{}]",
a.calls, to_calls, a.time, avg_t, pct_t,
a.self_time, avg_s,
to_name, to_num
);
}
}
}
0 }
pub fn name_for_anonymous_function(name: &str, filename: &str, lineno: i32) -> String { format!("{} [{}:{}]", name, filename, lineno)
}
pub fn zprof_wrapper(_name: &str) -> i32 { 0 }
use crate::ported::zsh_h::module;
#[allow(unused_variables)]
pub fn setup_(m: *const module) -> i32 { ZPROF_MODULE.store(true, Ordering::SeqCst); 0 }
pub fn features_(m: *const module, features: &mut Vec<String>) -> i32 { *features = featuresarray(m, module_features());
0 }
pub fn enables_(m: *const module, enables: &mut Option<Vec<i32>>) -> i32 { handlefeatures(m, module_features(), enables) }
#[allow(unused_variables)]
pub fn boot_(m: *const module) -> i32 { let mut calls = CALLS.lock().unwrap();
calls.clear(); NCALLS.store(0, Ordering::SeqCst); let mut arcs = ARCS.lock().unwrap();
arcs.clear(); NARCS.store(0, Ordering::SeqCst); STACK.lock().unwrap().clear(); 0 }
pub fn cleanup_(m: *const module) -> i32 { let mut calls = CALLS.lock().unwrap();
freepfuncs(&mut calls); let mut arcs = ARCS.lock().unwrap();
freeparcs(&mut arcs); ZPROF_MODULE.store(false, Ordering::SeqCst);
setfeatureenables(m, module_features(), None) }
#[allow(unused_variables)]
pub fn finish_(m: *const module) -> i32 { 0
}
#[cfg(test)]
mod tests {
use super::*;
static TEST_LOCK: Mutex<()> = Mutex::new(());
fn reset_state() {
let mut c = CALLS.lock().unwrap();
c.clear();
let mut a = ARCS.lock().unwrap();
a.clear();
STACK.lock().unwrap().clear();
NCALLS.store(0, Ordering::SeqCst);
NARCS.store(0, Ordering::SeqCst);
}
#[test]
fn pfunc_default_zeros() {
let p = Pfunc::default();
assert_eq!(p.name, "");
assert_eq!(p.calls, 0);
assert_eq!(p.time, 0.0);
assert_eq!(p.self_time, 0.0);
assert_eq!(p.num, 0);
}
#[test]
fn freepfuncs_clears() {
let mut v = vec![Pfunc { name: "a".into(), ..Default::default() }];
freepfuncs(&mut v);
assert!(v.is_empty());
}
#[test]
fn findpfunc_matches_by_name() {
let _g = TEST_LOCK.lock().unwrap();
reset_state();
CALLS.lock().unwrap().push(Pfunc { name: "alpha".into(), ..Default::default() });
CALLS.lock().unwrap().push(Pfunc { name: "beta".into(), ..Default::default() });
assert_eq!(findpfunc("alpha"), Some(0));
assert_eq!(findpfunc("beta"), Some(1));
assert_eq!(findpfunc("none"), None);
reset_state();
}
#[test]
fn findparc_matches_pair() {
let _g = TEST_LOCK.lock().unwrap();
reset_state();
ARCS.lock().unwrap().push(Parc { from: 0, to: 1, ..Default::default() });
ARCS.lock().unwrap().push(Parc { from: 0, to: 2, ..Default::default() });
assert_eq!(findparc(0, 1), Some(0));
assert_eq!(findparc(0, 2), Some(1));
assert_eq!(findparc(1, 0), None);
reset_state();
}
#[test]
fn cmpsfuncs_descending() {
let a = Pfunc { self_time: 5.0, ..Default::default() };
let b = Pfunc { self_time: 10.0, ..Default::default() };
assert_eq!(cmpsfuncs(&a, &b), std::cmp::Ordering::Greater);
assert_eq!(cmpsfuncs(&b, &a), std::cmp::Ordering::Less);
}
#[test]
fn bin_zprof_clear_resets_tables() {
let _g = TEST_LOCK.lock().unwrap();
reset_state();
CALLS.lock().unwrap().push(Pfunc { name: "x".into(), ..Default::default() });
ARCS.lock().unwrap().push(Parc { from: 0, to: 0, ..Default::default() });
NCALLS.store(1, Ordering::SeqCst);
NARCS.store(1, Ordering::SeqCst);
use crate::ported::zsh_h::{options, MAX_OPS};
let mut ops = options { ind: [0u8; MAX_OPS], args: Vec::new(),
argscount: 0, argsalloc: 0 };
ops.ind[b'c' as usize] = 1;
let r = bin_zprof("zprof", &["-c".to_string()], &ops, 0);
assert_eq!(r, 0);
assert!(CALLS.lock().unwrap().is_empty());
assert!(ARCS.lock().unwrap().is_empty());
assert_eq!(NCALLS.load(Ordering::SeqCst), 0);
assert_eq!(NARCS.load(Ordering::SeqCst), 0);
}
#[test]
fn zprof_wrapper_returns_zero() {
assert_eq!(zprof_wrapper("foo"), 0);
}
#[test]
fn name_for_anonymous_function_format() {
let s = name_for_anonymous_function("anon", "/tmp/foo.zsh", 42);
assert_eq!(s, "anon [/tmp/foo.zsh:42]");
}
}
use crate::ported::zsh_h::features as features_t;
static MODULE_FEATURES: OnceLock<Mutex<features_t>> = OnceLock::new();
fn module_features() -> &'static Mutex<features_t> {
MODULE_FEATURES.get_or_init(|| Mutex::new(features_t {
bn_list: None,
bn_size: 1,
cd_list: None,
cd_size: 0,
mf_list: None,
mf_size: 0,
pd_list: None,
pd_size: 0,
n_abstract: 0,
}))
}
fn featuresarray(_m: *const module, _f: &Mutex<features_t>) -> Vec<String> {
vec!["b:zprof".to_string()]
}
fn handlefeatures(
_m: *const module,
_f: &Mutex<features_t>,
enables: &mut Option<Vec<i32>>,
) -> i32 {
if enables.is_none() {
*enables = Some(vec![1; 1]);
}
0
}
fn setfeatureenables(
_m: *const module,
_f: &Mutex<features_t>,
_e: Option<&[i32]>,
) -> i32 {
0
}