| 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 }) |
| } |