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