ckb_debugger/
machine_analyzer.rs

1use crate::machine_assign::MachineAssign;
2use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider};
3use ckb_vm::cost_model::estimate_cycles;
4use ckb_vm::decoder::{Decoder, build_decoder};
5use ckb_vm::instructions::instruction_length;
6use ckb_vm::machine::VERSION0;
7use ckb_vm::registers::{A0, SP};
8use ckb_vm::{Bytes, CoreMachine, Error, ISA_MOP, Machine, Register, SupportMachine};
9use std::borrow::Cow;
10use std::cell::RefCell;
11use std::collections::HashMap;
12use std::rc::Rc;
13
14type Addr2LineEndianReader = addr2line::gimli::EndianReader<addr2line::gimli::RunTimeEndian, Rc<[u8]>>;
15type Addr2LineContext = addr2line::Context<Addr2LineEndianReader>;
16type Addr2LineFrameIter<'a> = addr2line::FrameIter<'a, Addr2LineEndianReader>;
17
18fn sprint_fun(frame_iter: &mut Addr2LineFrameIter) -> String {
19    let mut s = String::from("??");
20    loop {
21        if let Some(data) = frame_iter.next().unwrap() {
22            if let Some(function) = data.function {
23                s = String::from(addr2line::demangle_auto(Cow::from(function.raw_name().unwrap()), function.language));
24                continue;
25            }
26            continue;
27        }
28        break;
29    }
30    s
31}
32
33fn goblin_fun(elf: &goblin::elf::Elf) -> HashMap<u64, String> {
34    let mut map = HashMap::new();
35    for sym in &elf.syms {
36        if !sym.is_function() {
37            continue;
38        }
39        if let Some(Ok(r)) = elf.strtab.get(sym.st_name) {
40            map.insert(sym.st_value, r.to_string());
41        }
42    }
43    map
44}
45
46fn goblin_get_sym(elf: &goblin::elf::Elf, sym: &str) -> u64 {
47    for e in &elf.syms {
48        if let Some(Ok(r)) = elf.strtab.get(e.st_name) {
49            if r == sym {
50                return e.st_value;
51            }
52        }
53    }
54    return 0;
55}
56
57struct TrieNode {
58    addr: u64,
59    link: u64,
60    pc: u64,
61    parent: Option<Rc<RefCell<TrieNode>>>,
62    childs: Vec<Rc<RefCell<TrieNode>>>,
63    cycles: u64,
64    regs: [[u64; 32]; 2],
65}
66
67impl TrieNode {
68    fn root() -> Self {
69        Self { addr: 0, link: 0, pc: 0, parent: None, childs: vec![], cycles: 0, regs: [[0; 32]; 2] }
70    }
71}
72
73#[derive(Clone, Debug)]
74pub struct Tags {
75    addr: u64,
76    file: String,
77    line: u32,
78    func: String,
79}
80
81impl Tags {
82    fn new(addr: u64) -> Self {
83        Tags { addr, file: String::from("??"), line: 0xffffffff, func: String::from("??") }
84    }
85
86    pub fn func(&self) -> String {
87        if self.func != "??" { self.func.clone() } else { format!("func_0x{:x}", self.addr) }
88    }
89
90    pub fn simple(&self) -> String {
91        format!("{}:{}", self.file, self.func())
92    }
93
94    pub fn detail(&self) -> String {
95        if self.line == 0xffffffff {
96            format!("{}:??:{}", self.file, self.func)
97        } else {
98            format!("{}:{}:{}", self.file, self.line, self.func)
99        }
100    }
101}
102
103pub struct MachineProfile {
104    addrctx: Addr2LineContext,
105    trie_root: Rc<RefCell<TrieNode>>,
106    trie_node: Rc<RefCell<TrieNode>>,
107    cache_tag: HashMap<u64, Tags>,
108    cache_fun: HashMap<u64, String>,
109}
110
111impl MachineProfile {
112    pub fn new(program: &Bytes) -> Result<Self, Box<dyn std::error::Error>> {
113        let object = addr2line::object::File::parse(program.as_ref())?;
114        let ctx = addr2line::Context::new(&object)?;
115        let trie_root = Rc::new(RefCell::new(TrieNode::root()));
116        let elf = goblin::elf::Elf::parse(&program)?;
117        trie_root.borrow_mut().addr = elf.entry;
118        Ok(Self {
119            addrctx: ctx,
120            trie_root: trie_root.clone(),
121            trie_node: trie_root,
122            cache_tag: HashMap::new(),
123            cache_fun: goblin_fun(&elf),
124        })
125    }
126
127    pub fn reset(&mut self, program: &Bytes) -> Result<(), Box<dyn std::error::Error>> {
128        let object = addr2line::object::File::parse(program.as_ref())?;
129        let ctx = addr2line::Context::new(&object)?;
130        let trie_root = Rc::new(RefCell::new(TrieNode::root()));
131        let elf = goblin::elf::Elf::parse(&program)?;
132        trie_root.borrow_mut().addr = elf.entry;
133        self.addrctx = ctx;
134        self.trie_root = trie_root.clone();
135        self.trie_node = trie_root;
136        self.cache_tag = HashMap::new();
137        self.cache_fun = goblin_fun(&elf);
138        Ok(())
139    }
140
141    pub fn get_tag(&mut self, addr: u64) -> Tags {
142        if let Some(data) = self.cache_tag.get(&addr) {
143            return data.clone();
144        }
145        let mut tag = Tags::new(addr);
146        let loc = self.addrctx.find_location(addr).unwrap();
147        if let Some(loc) = loc {
148            tag.file = loc.file.as_ref().unwrap().to_string();
149            if let Some(line) = loc.line {
150                tag.line = line;
151            }
152        }
153        let mut frame_iter = self.addrctx.find_frames(addr).unwrap();
154        tag.func = sprint_fun(&mut frame_iter);
155        self.cache_tag.insert(addr, tag.clone());
156        tag
157    }
158
159    fn display_flamegraph_rec(&mut self, prefix: &str, node: Rc<RefCell<TrieNode>>, writer: &mut impl std::io::Write) {
160        let prefix_name = format!("{}{}", prefix, self.get_tag(node.borrow().addr).simple());
161        writer.write_all(format!("{} {}\n", prefix_name, node.borrow().cycles).as_bytes()).unwrap();
162        for e in &node.borrow().childs {
163            self.display_flamegraph_rec(format!("{}; ", prefix_name).as_str(), e.clone(), writer);
164        }
165        writer.flush().unwrap();
166    }
167
168    pub fn display_flamegraph(&mut self, writer: &mut impl std::io::Write) {
169        self.display_flamegraph_rec("", self.trie_root.clone(), writer);
170    }
171
172    pub fn display_stacktrace(&mut self, prefix: &str, writer: &mut impl std::io::Write) {
173        let mut frame = self.trie_node.clone();
174        let mut stack = vec![self.get_tag(frame.borrow().pc).detail()];
175        loop {
176            stack.push(self.get_tag(frame.borrow().link).detail());
177            let parent = frame.borrow().parent.clone();
178            if let Some(p) = parent {
179                frame = p.clone();
180            } else {
181                break;
182            }
183        }
184        stack.reverse();
185        for i in &stack {
186            writer.write_all(format!("{}{}\n", prefix, i).as_bytes()).unwrap();
187        }
188        writer.flush().unwrap();
189    }
190
191    pub fn step<DL>(&mut self, decoder: &mut Decoder, machine: &mut MachineAssign<DL>) -> Result<(), Error>
192    where
193        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
194    {
195        let pc = machine.pc().to_u64();
196        let inst = decoder.decode(machine.memory_mut(), pc)?;
197        let opcode = ckb_vm::instructions::extract_opcode(inst);
198        let cycles = estimate_cycles(inst);
199        self.trie_node.borrow_mut().cycles += cycles;
200        self.trie_node.borrow_mut().pc = pc;
201
202        let call = |s: &mut Self, addr: u64, link: u64| {
203            let mut regs = [[0; 32]; 2];
204            for i in 0..32 {
205                regs[0][i] = machine.registers()[i].to_u64();
206            }
207            let chd = Rc::new(RefCell::new(TrieNode {
208                addr: addr,
209                link: link,
210                pc: pc,
211                parent: Some(s.trie_node.clone()),
212                childs: vec![],
213                cycles: 0,
214                regs: regs,
215            }));
216            s.trie_node.borrow_mut().childs.push(chd.clone());
217            s.trie_node = chd;
218        };
219
220        let jump = |s: &mut Self, addr: u64| {
221            let mut f = s.trie_node.clone();
222            loop {
223                if f.borrow().link == addr {
224                    for i in 0..32 {
225                        s.trie_node.borrow_mut().regs[1][i] = machine.registers()[i].to_u64();
226                    }
227                    if let Some(p) = f.borrow().parent.clone() {
228                        s.trie_node = p.clone();
229                    } else {
230                        unimplemented!();
231                    }
232                    break;
233                }
234                let p = f.borrow().parent.clone();
235                if let Some(p) = p {
236                    f = p.clone();
237                } else {
238                    break;
239                }
240            }
241        };
242
243        if opcode == ckb_vm::instructions::insts::OP_JAL {
244            let inst_length = instruction_length(inst) as u64;
245            let inst = ckb_vm::instructions::Utype(inst);
246            let addr = pc.wrapping_add(inst.immediate_s() as u64) & 0xfffffffffffffffe;
247            let link = pc + inst_length;
248            if self.cache_fun.contains_key(&addr) {
249                call(self, addr, link);
250                return Ok(());
251            }
252            jump(self, addr);
253            return Ok(());
254        };
255        if opcode == ckb_vm::instructions::insts::OP_JALR_VERSION0 {
256            let inst_length = instruction_length(inst) as u64;
257            let inst = ckb_vm::instructions::Itype(inst);
258            let base = machine.registers()[inst.rs1()].to_u64();
259            let addr = base.wrapping_add(inst.immediate_s() as u64) & 0xfffffffffffffffe;
260            let link = pc + inst_length;
261            if self.cache_fun.contains_key(&addr) {
262                call(self, addr, link);
263                return Ok(());
264            }
265            jump(self, addr);
266            return Ok(());
267        };
268        if opcode == ckb_vm::instructions::insts::OP_JALR_VERSION1 {
269            let inst_length = instruction_length(inst) as u64;
270            let inst = ckb_vm::instructions::Itype(inst);
271            let base = machine.registers()[inst.rs1()].to_u64();
272            let addr = base.wrapping_add(inst.immediate_s() as u64) & 0xfffffffffffffffe;
273            let link = pc + inst_length;
274            if self.cache_fun.contains_key(&addr) {
275                call(self, addr, link);
276                return Ok(());
277            }
278            jump(self, addr);
279            return Ok(());
280        };
281        if opcode == ckb_vm::instructions::insts::OP_FAR_JUMP_ABS {
282            let inst_length = instruction_length(inst) as u64;
283            let inst = ckb_vm::instructions::Utype(inst);
284            let addr = (inst.immediate_s() as u64) & 0xfffffffffffffffe;
285            let link = pc + inst_length;
286            if self.cache_fun.contains_key(&addr) {
287                call(self, addr, link);
288                return Ok(());
289            }
290            jump(self, addr);
291            return Ok(());
292        }
293        if opcode == ckb_vm::instructions::insts::OP_FAR_JUMP_REL {
294            let inst_length = instruction_length(inst) as u64;
295            let inst = ckb_vm::instructions::Utype(inst);
296            let addr = pc.wrapping_add(inst.immediate_s() as u64) & 0xfffffffffffffffe;
297            let link = pc + inst_length;
298            if self.cache_fun.contains_key(&addr) {
299                call(self, addr, link);
300                return Ok(());
301            }
302            jump(self, addr);
303            return Ok(());
304        }
305        return Ok(());
306    }
307}
308
309pub struct MachineOverlap {
310    sbrk_addr: u64,
311    sbrk_heap: u64,
312}
313
314impl MachineOverlap {
315    pub fn new(program: &Bytes) -> Result<Self, Box<dyn std::error::Error>> {
316        let elf = goblin::elf::Elf::parse(&program)?;
317        Ok(Self { sbrk_addr: goblin_get_sym(&elf, "_sbrk"), sbrk_heap: goblin_get_sym(&elf, "_end") })
318    }
319
320    pub fn step<DL>(
321        &mut self,
322        decoder: &mut Decoder,
323        machine: &mut MachineAssign<DL>,
324        profile: &MachineProfile,
325    ) -> Result<(), Error>
326    where
327        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
328    {
329        let pc = machine.pc().to_u64();
330        let sp = machine.registers()[SP].to_u64();
331        if sp < self.sbrk_heap {
332            return Err(Error::External(format!("Heap and stack overlapping sp={} heap={}", sp, self.sbrk_heap)));
333        }
334        let inst = decoder.decode(machine.memory_mut(), pc)?;
335        let opcode = ckb_vm::instructions::extract_opcode(inst);
336        let addr = match opcode {
337            ckb_vm::instructions::insts::OP_JAL => {
338                let inst = ckb_vm::instructions::Utype(inst);
339                let addr = pc.wrapping_add(inst.immediate_s() as u64) & 0xfffffffffffffffe;
340                addr
341            }
342            ckb_vm::instructions::insts::OP_JALR_VERSION0 => {
343                let inst = ckb_vm::instructions::Itype(inst);
344                let base = machine.registers()[inst.rs1()].to_u64();
345                let addr = base.wrapping_add(inst.immediate_s() as u64) & 0xfffffffffffffffe;
346                addr
347            }
348            ckb_vm::instructions::insts::OP_JALR_VERSION1 => {
349                let inst = ckb_vm::instructions::Itype(inst);
350                let base = machine.registers()[inst.rs1()].to_u64();
351                let addr = base.wrapping_add(inst.immediate_s() as u64) & 0xfffffffffffffffe;
352                addr
353            }
354            ckb_vm::instructions::insts::OP_FAR_JUMP_ABS => {
355                let inst = ckb_vm::instructions::Utype(inst);
356                let addr = (inst.immediate_s() as u64) & 0xfffffffffffffffe;
357                addr
358            }
359            ckb_vm::instructions::insts::OP_FAR_JUMP_REL => {
360                let inst = ckb_vm::instructions::Utype(inst);
361                let addr = pc.wrapping_add(inst.immediate_s() as u64) & 0xfffffffffffffffe;
362                addr
363            }
364            _ => return Ok(()),
365        };
366
367        let mut f = profile.trie_node.clone();
368        loop {
369            if f.borrow().link == addr {
370                if profile.trie_node.borrow().addr == self.sbrk_addr {
371                    // https://github.com/nervosnetwork/riscv-newlib/blob/newlib-4.1.0-fork/libgloss/riscv/sys_sbrk.c#L49
372                    // Note incr could be negative.
373                    self.sbrk_heap = profile.trie_node.borrow().regs[0][A0].wrapping_add(machine.registers()[A0]);
374                }
375                break;
376            }
377            let p = f.borrow().parent.clone();
378            if let Some(p) = p {
379                f = p.clone();
380            } else {
381                break;
382            }
383        }
384
385        return Ok(());
386    }
387}
388
389pub struct MachineStepLog {}
390
391impl MachineStepLog {
392    pub fn new() -> Self {
393        Self {}
394    }
395
396    pub fn step<DL>(&mut self, machine: &mut MachineAssign<DL>) -> Result<(), Error>
397    where
398        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
399    {
400        println!("{}", machine);
401        Ok(())
402    }
403}
404
405pub struct MachineAnalyzer<DL>
406where
407    DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
408{
409    pub enable_overlap: u8,
410    pub enable_profile: u8,
411    pub enable_steplog: u8,
412    pub machine: MachineAssign<DL>,
413    pub profile: MachineProfile,
414    pub overlap: MachineOverlap,
415    pub steplog: MachineStepLog,
416}
417
418impl<DL> CoreMachine for MachineAnalyzer<DL>
419where
420    DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
421{
422    type REG = u64;
423    type MEM = <MachineAssign<DL> as CoreMachine>::MEM;
424
425    fn pc(&self) -> &Self::REG {
426        &self.machine.pc()
427    }
428
429    fn update_pc(&mut self, pc: Self::REG) {
430        self.machine.update_pc(pc)
431    }
432
433    fn commit_pc(&mut self) {
434        self.machine.commit_pc()
435    }
436
437    fn memory(&self) -> &Self::MEM {
438        self.machine.memory()
439    }
440
441    fn memory_mut(&mut self) -> &mut Self::MEM {
442        self.machine.memory_mut()
443    }
444
445    fn registers(&self) -> &[Self::REG] {
446        self.machine.registers()
447    }
448
449    fn set_register(&mut self, idx: usize, value: Self::REG) {
450        self.machine.set_register(idx, value)
451    }
452
453    fn isa(&self) -> u8 {
454        self.machine.isa()
455    }
456
457    fn version(&self) -> u32 {
458        self.machine.version()
459    }
460}
461
462impl<DL> Machine for MachineAnalyzer<DL>
463where
464    DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
465{
466    fn ecall(&mut self) -> Result<(), Error> {
467        self.machine.ecall()
468    }
469
470    fn ebreak(&mut self) -> Result<(), Error> {
471        self.machine.ebreak()
472    }
473}
474
475impl<DL> std::fmt::Display for MachineAnalyzer<DL>
476where
477    DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
478{
479    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
480        self.machine.fmt(f)
481    }
482}
483
484impl<DL> MachineAnalyzer<DL>
485where
486    DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
487{
488    pub fn new(
489        machine: MachineAssign<DL>,
490        profile: MachineProfile,
491        overlap: MachineOverlap,
492        steplog: MachineStepLog,
493    ) -> Self {
494        Self { enable_overlap: 0, enable_profile: 1, enable_steplog: 0, machine, profile, overlap, steplog }
495    }
496
497    pub fn run(&mut self) -> Result<i8, Error> {
498        if self.isa() & ISA_MOP != 0 && self.version() == VERSION0 {
499            return Err(Error::InvalidVersion);
500        }
501        let mut decoder = build_decoder::<u64>(self.isa(), self.version());
502        self.machine.set_running(true);
503        while self.machine.running() {
504            if self.machine.reset_signal() {
505                decoder.reset_instructions_cache();
506                self.profile = MachineProfile::new(&self.machine.code()).unwrap();
507            }
508            if self.enable_profile > 0 && self.enable_overlap > 0 {
509                self.overlap.step(&mut decoder, &mut self.machine, &self.profile)?;
510            }
511            if self.enable_profile > 0 {
512                self.profile.step(&mut decoder, &mut self.machine)?;
513            }
514            if self.enable_steplog > 0 {
515                self.steplog.step(&mut self.machine)?;
516            }
517            self.machine.step(&mut decoder)?;
518        }
519        Ok(self.machine.exit_code())
520    }
521}