use std::collections::BTreeMap;
use crate::{
container::Container,
demangle::{modpath::PathPrefix, symbol},
inits, stacktrace,
};
#[derive(Debug, Clone)]
pub struct ModuleMap {
pub modules: BTreeMap<String, ModuleInfo>,
}
#[derive(Debug, Clone)]
pub struct ModuleInfo {
pub init_path: Option<String>,
pub prefix: Option<PathPrefix>,
pub init_addr: Option<u64>,
pub dat_init_addr: Option<u64>,
pub symbols: Vec<ModuleSymbol>,
pub file_paths: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct ModuleSymbol {
pub name: String,
pub address: u64,
pub size: u64,
pub item_id: u64,
}
impl ModuleInfo {
pub fn symbol_count(&self) -> usize {
self.symbols.len()
}
}
pub fn build(container: &Container<'_>) -> ModuleMap {
let mut modules: BTreeMap<String, ModuleInfo> = BTreeMap::new();
let inits = inits::scan(container);
for f in &inits {
let key = module_key_from_path(&f.module_path.path);
let entry = modules.entry(key).or_insert_with(default_info);
if entry.init_path.is_none() {
entry.init_path = Some(f.module_path.path.clone());
}
if entry.prefix.is_none() {
entry.prefix = f.module_path.prefix;
}
match f.kind {
inits::InitKind::Init => entry.init_addr = Some(f.address),
inits::InitKind::DatInit => entry.dat_init_addr = Some(f.address),
inits::InitKind::HcrInit => {}
}
}
for sym in container.symbols() {
if let Some(d) = symbol::parse(sym.name.as_ref()) {
if let Some(item_id) = d.item_id {
let key = d.module.to_owned();
let entry = modules.entry(key).or_insert_with(default_info);
entry.symbols.push(ModuleSymbol {
name: d.identifier.into_owned(),
address: sym.vm_addr,
size: sym.size,
item_id,
});
}
}
}
let harvest = stacktrace::harvest(container);
for fp in &harvest.file_paths {
let basename = fp.path.rsplit('/').next().unwrap_or(&fp.path);
let stem = basename.strip_suffix(".nim").unwrap_or(basename);
let matched_key = if modules.contains_key(stem) {
Some(stem.to_owned())
} else {
modules
.iter()
.find(|(_, info)| {
info.init_path
.as_ref()
.is_some_and(|p| p.ends_with(basename))
})
.map(|(k, _)| k.clone())
};
if let Some(key) = matched_key {
if let Some(info) = modules.get_mut(&key) {
if !info.file_paths.contains(&fp.path) {
info.file_paths.push(fp.path.clone());
}
}
} else {
let entry = modules.entry(stem.to_owned()).or_insert_with(default_info);
if !entry.file_paths.contains(&fp.path) {
entry.file_paths.push(fp.path.clone());
}
}
}
ModuleMap { modules }
}
fn default_info() -> ModuleInfo {
ModuleInfo {
init_path: None,
prefix: None,
init_addr: None,
dat_init_addr: None,
symbols: Vec::new(),
file_paths: Vec::new(),
}
}
fn module_key_from_path(path: &str) -> String {
path.strip_suffix(".nim").unwrap_or(path).to_owned()
}