blob: 2246ca2d5757491cc9e8f9ebbb7d8fe78751e2a3 [file] [log] [blame]
use std::collections::HashMap;
use std::sync::LazyLock;
use anyhow::{Context, ensure};
use regex::Regex;
use crate::llvm_utils::{truncated_md5, unescape_llvm_string_contents};
use crate::parser::Parser;
#[derive(Debug, Default)]
pub(crate) struct FilenameTables {
map: HashMap<u64, Vec<String>>,
}
impl FilenameTables {
pub(crate) fn lookup(&self, filenames_hash: u64, global_file_id: usize) -> Option<&str> {
let table = self.map.get(&filenames_hash)?;
let filename = table.get(global_file_id)?;
Some(filename)
}
}
struct CovmapLineData {
payload: Vec<u8>,
}
pub(crate) fn make_filename_tables(llvm_ir: &str) -> anyhow::Result<FilenameTables> {
let mut map = HashMap::default();
for line in llvm_ir.lines().filter(|line| is_covmap_line(line)) {
let CovmapLineData { payload } = parse_covmap_line(line)?;
let mut parser = Parser::new(&payload);
let n_filenames = parser.read_uleb128_usize()?;
let uncompressed_bytes = parser.read_chunk_to_uncompressed_bytes()?;
parser.ensure_empty()?;
let mut filenames_table = vec![];
let mut parser = Parser::new(&uncompressed_bytes);
for _ in 0..n_filenames {
let len = parser.read_uleb128_usize()?;
let bytes = parser.read_n_bytes(len)?;
let filename = str::from_utf8(bytes)?;
filenames_table.push(filename.to_owned());
}
let filenames_hash = truncated_md5(&payload);
map.insert(filenames_hash, filenames_table);
}
Ok(FilenameTables { map })
}
fn is_covmap_line(line: &str) -> bool {
line.starts_with("@__llvm_coverage_mapping ")
}
fn parse_covmap_line(line: &str) -> anyhow::Result<CovmapLineData> {
ensure!(is_covmap_line(line));
const RE_STRING: &str = r#"(?x)^
@__llvm_coverage_mapping \ =
.*
\[ [0-9]+ \ x \ i8 \] \ c"(?<payload>[^"]*)"
.*$
"#;
static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(RE_STRING).unwrap());
let captures =
RE.captures(line).with_context(|| format!("couldn't parse covmap line: {line:?}"))?;
let payload = unescape_llvm_string_contents(&captures["payload"]);
Ok(CovmapLineData { payload })
}