()
| 62 | } |
| 63 | |
| 64 | fn main() -> Result<()> { |
| 65 | let args = ProfilerHtml::parse(); |
| 66 | let profile = std::fs::read(&args.profile) |
| 67 | .with_context(|| format!("failed to read {:?}", args.profile))?; |
| 68 | |
| 69 | // All known functions and the total of all samples taken. |
| 70 | let mut functions = BTreeMap::new(); |
| 71 | let mut total = 0; |
| 72 | |
| 73 | let mut found_samples = false; |
| 74 | for event in decode(&profile) { |
| 75 | match event? { |
| 76 | Event::Function(addr, name, body) => { |
| 77 | let prev = functions.insert( |
| 78 | addr, |
| 79 | Function { |
| 80 | addr, |
| 81 | name, |
| 82 | body, |
| 83 | hits: 0, |
| 84 | instructions: BTreeMap::new(), |
| 85 | }, |
| 86 | ); |
| 87 | assert!(prev.is_none()); |
| 88 | } |
| 89 | Event::Samples(samples) => { |
| 90 | found_samples = true; |
| 91 | for sample in samples { |
| 92 | let addr = sample.0; |
| 93 | let (_, function) = functions.range_mut(..=addr).next_back().unwrap(); |
| 94 | assert!(addr >= function.addr); |
| 95 | assert!(addr < function.addr + (function.body.len() as u64)); |
| 96 | |
| 97 | total += 1; |
| 98 | function.hits += 1; |
| 99 | *function |
| 100 | .instructions |
| 101 | .entry(u32::try_from(addr - function.addr).unwrap()) |
| 102 | .or_insert(0) += 1; |
| 103 | } |
| 104 | } |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | if functions.is_empty() { |
| 109 | bail!("no functions found in profile"); |
| 110 | } |
| 111 | if !found_samples { |
| 112 | bail!("no samples found in profile"); |
| 113 | } |
| 114 | |
| 115 | let mut funcs = functions |
| 116 | .into_iter() |
| 117 | .map(|(_, func)| func) |
| 118 | .collect::<Vec<_>>(); |
| 119 | funcs.sort_by_key(|f| f.hits); |
| 120 | |
| 121 | let mut term = StandardStream::stdout(ColorChoice::Auto); |
nothing calls this directly
no test coverage detected