Skip to main content

ckb_vm_syscall_tracer/
lib.rs

1pub mod generated {
2    pub mod traces {
3        use once_cell::sync::Lazy;
4        use prost_reflect::DescriptorPool;
5
6        pub static DESCRIPTOR_POOL: Lazy<DescriptorPool> = Lazy::new(|| {
7            DescriptorPool::decode(include_bytes!(concat!(env!("OUT_DIR"), "/file_descriptor_set.bin")).as_ref())
8                .unwrap()
9        });
10
11        include!(concat!(env!("OUT_DIR"), "/generated.traces.rs"));
12    }
13}
14pub mod readonly_machines;
15
16use crate::{
17    generated::traces,
18    readonly_machines::{ReadonlyMachine, ReadonlySnapshotMachine},
19};
20use ckb_chain_spec::consensus::ConsensusBuilder;
21use ckb_mock_tx_types::{MockTransaction, Resource};
22use ckb_script::{
23    ROOT_VM_ID, Scheduler, generate_ckb_syscalls,
24    types::{DebugPrinter, ScriptGroup, SgData, TerminatedResult, VmContext, VmId, VmState},
25};
26use ckb_script::{
27    TransactionScriptsVerifier, TxVerifyEnv,
28    types::{DataPieceId, Machine},
29};
30use ckb_std::ckb_constants::Source;
31use ckb_traits::{CellDataProvider, ExtensionProvider, HeaderProvider};
32use ckb_types::{
33    core::{EpochNumberWithFraction, HeaderView, cell::resolve_transaction, hardfork},
34    packed::Byte32,
35    prelude::*,
36};
37use ckb_vm::{
38    CoreMachine, DefaultMachineRunner, Error, FlattenedArgsReader, Memory, Register, SupportMachine, Syscalls,
39    registers::{A0, A1, A2, A3, A4, A5, A7},
40    snapshot2::DataSource,
41};
42use ckb_vm_fuzzing_utils::SyscallCode;
43use clap::{Args, ValueEnum};
44use prost::Message;
45use serde::{Deserialize, Serialize};
46use std::collections::{HashMap, HashSet};
47use std::sync::{Arc, Mutex};
48
49#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
50pub enum CollectorKind {
51    /// Syscall based collector, data from each syscall are collected for replays.
52    Syscall,
53
54    /// Tx based collector, certain data are collected from the tx as a whole
55    TxParts,
56
57    /// VM creation collector, sub VM creation events are collected.
58    VmCreate,
59}
60
61impl CollectorKind {
62    pub fn message_name(&self) -> &'static str {
63        match self {
64            CollectorKind::Syscall => "generated.traces.Syscalls",
65            CollectorKind::TxParts => "generated.traces.Parts",
66            CollectorKind::VmCreate => "generated.traces.VmCreations",
67        }
68    }
69}
70
71impl TryFrom<&[u8]> for traces::Parts {
72    type Error = Error;
73
74    fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
75        Self::decode(v).map_err(|e| Error::External(format!("prost decoding error: {}", e)))
76    }
77}
78
79impl From<traces::Parts> for Vec<u8> {
80    fn from(value: traces::Parts) -> Self {
81        value.encode_to_vec()
82    }
83}
84
85impl TryFrom<&[u8]> for traces::Syscalls {
86    type Error = Error;
87
88    fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
89        Self::decode(v).map_err(|e| Error::External(format!("prost decoding error: {}", e)))
90    }
91}
92
93impl From<traces::Syscalls> for Vec<u8> {
94    fn from(value: traces::Syscalls) -> Self {
95        value.encode_to_vec()
96    }
97}
98
99impl TryFrom<&[u8]> for traces::VmCreations {
100    type Error = Error;
101
102    fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
103        Self::decode(v).map_err(|e| Error::External(format!("prost decoding error: {}", e)))
104    }
105}
106
107impl From<traces::VmCreations> for Vec<u8> {
108    fn from(value: traces::VmCreations) -> Self {
109        value.encode_to_vec()
110    }
111}
112
113#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, Args)]
114pub struct BinaryLocator {
115    /// Index of requested cell or witness
116    #[arg(long)]
117    pub index: u64,
118
119    /// Source of requested cell or witness
120    #[arg(long)]
121    pub source: u64,
122
123    /// Starting offset of binary in cell or witnes
124    #[arg(long)]
125    pub offset: u32,
126
127    /// Length of binary
128    #[arg(long)]
129    pub length: u32,
130
131    /// True to load from a cell, false to load from a witness
132    #[arg(long, value_parser = parse_from_cell, default_value_t = true)]
133    pub from_cell: bool,
134}
135
136fn parse_from_cell(s: &str) -> Result<bool, String> {
137    if s == "t" || s == "true" { Ok(true) } else { Ok(false) }
138}
139
140#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
141pub struct CollectorKey {
142    pub vm_id: VmId,
143    pub generation_id: u64,
144}
145
146#[derive(Clone, Debug, PartialEq, Eq)]
147pub struct CollectorResult<T> {
148    pub exit_code: i8,
149    pub cycles: u64,
150    pub traces: HashMap<CollectorKey, T>,
151}
152
153pub trait Collector: Clone + Default {
154    type Trace;
155
156    fn syscall_generator<DL, M>(
157        vm_id: &VmId,
158        sg_data: &SgData<DL>,
159        vm_context: &VmContext<DL>,
160        data: &Self,
161    ) -> Vec<Box<dyn Syscalls<M>>>
162    where
163        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
164        M: SupportMachine + 'static;
165
166    fn preprocess<DL, V, M>(
167        &self,
168        _verifier: &TransactionScriptsVerifier<DL, V, M>,
169        _script_group: &ScriptGroup,
170        _scheduler: &mut Scheduler<DL, V, M>,
171    ) -> Result<(), Error>
172    where
173        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
174        V: Clone,
175        M: DefaultMachineRunner,
176    {
177        Ok(())
178    }
179
180    fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
181    where
182        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
183        V: Clone,
184        M: DefaultMachineRunner;
185
186    fn seal(self) -> HashMap<CollectorKey, Self::Trace>;
187
188    fn build_verifier(
189        &self,
190        mock_tx: &MockTransaction,
191    ) -> Result<TransactionScriptsVerifier<Resource, Self, Machine>, Error> {
192        let resource = Resource::from_mock_tx(mock_tx).map_err(Error::External)?;
193        let resolved_transaction =
194            resolve_transaction(mock_tx.core_transaction(), &mut HashSet::new(), &resource, &resource)
195                .map_err(|e| Error::External(format!("resolving transaction error: {}", e)))?;
196
197        let hardforks = hardfork::HardForks {
198            ckb2021: hardfork::CKB2021::new_mirana().as_builder().rfc_0032(20).build().unwrap(),
199            ckb2023: hardfork::CKB2023::new_mirana().as_builder().rfc_0049(30).build().unwrap(),
200        };
201        let consensus = Arc::new(ConsensusBuilder::default().hardfork_switch(hardforks).build());
202        let epoch = EpochNumberWithFraction::new(35, 0, 1);
203        let header_view = HeaderView::new_advanced_builder().epoch(epoch.pack()).build();
204        let tx_env = Arc::new(TxVerifyEnv::new_commit(&header_view));
205        Ok(TransactionScriptsVerifier::new_with_generator(
206            Arc::new(resolved_transaction),
207            resource,
208            consensus,
209            tx_env,
210            Self::syscall_generator,
211            self.clone(),
212        ))
213    }
214
215    fn collect<DL, V, M>(
216        self,
217        verifier: &TransactionScriptsVerifier<DL, V, M>,
218        script_group: &ScriptGroup,
219    ) -> Result<CollectorResult<Self::Trace>, Error>
220    where
221        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
222        V: Clone,
223        M: DefaultMachineRunner,
224    {
225        let mut scheduler = verifier
226            .create_scheduler(script_group)
227            .map_err(|e| Error::External(format!("scheduler creation error: {}", e)))?;
228        self.preprocess(verifier, script_group, &mut scheduler)?;
229
230        let (exit_code, cycles) = loop {
231            let iteration_result = scheduler.iterate()?;
232            if let Some(TerminatedResult { exit_code, consumed_cycles: cycles }) = iteration_result.terminated_status {
233                break (exit_code, cycles);
234            }
235            self.postprocess(&mut scheduler)?;
236        };
237
238        Ok(CollectorResult { exit_code, cycles, traces: self.seal() })
239    }
240}
241
242#[derive(Default, Clone)]
243pub struct TxPartsBasedCollector {
244    syscall_collector: SyscallBasedCollector,
245    data: Arc<Mutex<HashMap<CollectorKey, traces::Parts>>>,
246}
247
248impl TxPartsBasedCollector {
249    fn fetch<V, F: Fn(&traces::Parts) -> V>(&self, vm_id: VmId, f: F) -> V {
250        let key = self.syscall_collector.key(vm_id);
251        let mut m = self.data.lock().expect("lock");
252
253        let parts = m.entry(key).or_default();
254        f(parts)
255    }
256
257    fn modify<F: Fn(&mut traces::Parts)>(&mut self, vm_id: VmId, f: F) {
258        let key = self.syscall_collector.key(vm_id);
259        let mut m = self.data.lock().expect("lock");
260
261        let parts = m.entry(key).or_default();
262        f(parts);
263    }
264}
265
266impl Collector for TxPartsBasedCollector {
267    type Trace = traces::Parts;
268
269    fn syscall_generator<DL, M>(
270        vm_id: &VmId,
271        sg_data: &SgData<DL>,
272        vm_context: &VmContext<DL>,
273        data: &Self,
274    ) -> Vec<Box<dyn Syscalls<M>>>
275    where
276        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
277        M: SupportMachine + 'static,
278    {
279        vec![Box::new(TxPartsBasedCollectorVMSyscalls {
280            vm_id: *vm_id,
281            sg_data: sg_data.clone(),
282            data: data.clone(),
283            inner_collector_syscalls: SyscallBasedCollector::syscall_generator(
284                vm_id,
285                sg_data,
286                vm_context,
287                &data.syscall_collector,
288            ),
289            ckb_syscalls: generate_ckb_syscalls(vm_id, sg_data, vm_context, &debug_printer()),
290        })]
291    }
292
293    fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
294    where
295        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
296        V: Clone,
297        M: DefaultMachineRunner,
298    {
299        // TxPartsBasedCollector requires no postprocess, we only need to invoke postprocess
300        // for SyscallBasedCollector
301        self.syscall_collector.postprocess(scheduler)
302    }
303
304    fn seal(self) -> HashMap<CollectorKey, Self::Trace> {
305        let mut m = self.data.lock().expect("lock").clone();
306        for (key, syscalls) in self.syscall_collector.seal() {
307            m.entry(key).or_default().other_syscalls = Some(syscalls);
308        }
309        m
310    }
311}
312
313struct TxPartsBasedCollectorVMSyscalls<DL, M> {
314    vm_id: VmId,
315    sg_data: SgData<DL>,
316    data: TxPartsBasedCollector,
317    inner_collector_syscalls: Vec<Box<dyn Syscalls<M>>>,
318    ckb_syscalls: Vec<Box<dyn Syscalls<M>>>,
319}
320
321impl<DL: CellDataProvider + Send + Sync, M: SupportMachine> Syscalls<M> for TxPartsBasedCollectorVMSyscalls<DL, M> {
322    fn initialize(&mut self, machine: &mut M) -> Result<(), Error> {
323        for syscall in &mut self.inner_collector_syscalls {
324            syscall.initialize(machine)?;
325        }
326        for syscall in &mut self.ckb_syscalls {
327            syscall.initialize(machine)?;
328        }
329        Ok(())
330    }
331
332    fn ecall(&mut self, machine: &mut M) -> Result<bool, Error> {
333        // Detect and keep certain tx parts, for those syscalls,
334        // we can skip the IOData in SyscallBasedCollector
335        let mut skip_syscall_based_collector = false;
336        if let Ok(code) = machine.registers()[A7].to_u64().try_into() {
337            match code {
338                SyscallCode::LoadTxHash => {
339                    let tx_hash = self.sg_data.rtx.transaction.hash();
340                    self.data.modify(self.vm_id, |parts| {
341                        parts.tx_hash = tx_hash.as_slice().to_vec();
342                    });
343                    skip_syscall_based_collector = true;
344                }
345                SyscallCode::LoadCell => {
346                    let index = machine.registers()[A3].to_u64();
347                    let source = machine.registers()[A4].to_u64();
348
349                    if let Some(actual_index) = locate_input(index, source, &self.sg_data.sg_info.script_group) {
350                        let fill_length = std::cmp::min(actual_index + 1, self.sg_data.rtx.resolved_inputs.len());
351                        let already_filled_length = self.data.fetch(self.vm_id, |parts| parts.input_cells.len());
352
353                        if already_filled_length < fill_length {
354                            let inputs: Vec<Vec<u8>> = self
355                                .sg_data
356                                .rtx
357                                .resolved_inputs
358                                .iter()
359                                .skip(already_filled_length)
360                                .take(fill_length - already_filled_length)
361                                .map(|meta| meta.cell_output.as_slice().to_vec())
362                                .collect();
363
364                            self.data.modify(self.vm_id, |parts| parts.input_cells.extend_from_slice(&inputs));
365                        }
366                        skip_syscall_based_collector = true;
367                    }
368                }
369                SyscallCode::LoadCellData => {
370                    let index = machine.registers()[A3].to_u64();
371                    let source = machine.registers()[A4].to_u64();
372
373                    if let Some(actual_index) = locate_input(index, source, &self.sg_data.sg_info.script_group) {
374                        let fill_length = std::cmp::min(actual_index + 1, self.sg_data.rtx.resolved_inputs.len());
375                        let already_filled_length = self.data.fetch(self.vm_id, |parts| parts.input_cell_data.len());
376
377                        if already_filled_length < fill_length {
378                            let input_data: Vec<Vec<u8>> = self
379                                .sg_data
380                                .rtx
381                                .resolved_inputs
382                                .iter()
383                                .skip(already_filled_length)
384                                .take(fill_length - already_filled_length)
385                                .map(|meta| {
386                                    self.sg_data.data_loader().load_cell_data(meta).expect("load data").to_vec()
387                                })
388                                .collect();
389
390                            self.data.modify(self.vm_id, |parts| parts.input_cell_data.extend_from_slice(&input_data));
391                        }
392                        skip_syscall_based_collector = true;
393                    }
394                }
395                SyscallCode::LoadWitness => {
396                    let index = machine.registers()[A3].to_u64();
397                    let source = machine.registers()[A4].to_u64();
398
399                    if let Some(actual_index) = locate_witness(index, source, &self.sg_data.sg_info.script_group) {
400                        let fill_length =
401                            std::cmp::min(actual_index + 1, self.sg_data.rtx.transaction.witnesses().len());
402                        let already_filled_length = self.data.fetch(self.vm_id, |parts| parts.witnesses.len());
403
404                        if already_filled_length < fill_length {
405                            let witnesses: Vec<Vec<u8>> = self
406                                .sg_data
407                                .rtx
408                                .transaction
409                                .witnesses()
410                                .into_iter()
411                                .skip(already_filled_length)
412                                .take(fill_length - already_filled_length)
413                                .map(|witness| witness.raw_data().to_vec())
414                                .collect();
415
416                            self.data.modify(self.vm_id, |parts| parts.witnesses.extend_from_slice(&witnesses));
417                        }
418                        skip_syscall_based_collector = true;
419                    }
420                }
421                _ => (),
422            }
423        }
424
425        if skip_syscall_based_collector {
426            delegate_to_syscalls(machine, &mut self.ckb_syscalls)
427        } else {
428            delegate_to_syscalls(machine, &mut self.inner_collector_syscalls)
429        }
430    }
431}
432
433#[derive(Default, Clone)]
434pub struct SyscallBasedCollector {
435    partial_contents: Arc<Mutex<HashMap<CollectorKey, PartialSyscallContent>>>,
436    data: Arc<Mutex<HashMap<CollectorKey, traces::Syscalls>>>,
437    generation_tracker: GenerationTracker,
438}
439
440impl SyscallBasedCollector {
441    // fn fulfill(&self, key: CollectorKey, syscall: traces::Syscall) {
442    //     self.partial_contents.lock().expect("lock").remove(&key);
443    //     self.data.lock().expect("lock").entry(key.clone()).or_default().syscalls.push(syscall);
444    // }
445
446    fn insert_syscall(&self, key: CollectorKey, syscall: traces::Syscall) {
447        self.data.lock().expect("lock").entry(key).or_default().syscalls.push(syscall);
448    }
449
450    fn set_args(&self, key: CollectorKey, args: Vec<Vec<u8>>) {
451        self.data.lock().expect("lock").entry(key).or_default().args = args;
452    }
453
454    pub fn key(&self, vm_id: VmId) -> CollectorKey {
455        self.generation_tracker.key(vm_id)
456    }
457}
458
459impl Collector for SyscallBasedCollector {
460    type Trace = traces::Syscalls;
461
462    fn syscall_generator<DL, M>(
463        vm_id: &VmId,
464        sg_data: &SgData<DL>,
465        vm_context: &VmContext<DL>,
466        data: &Self,
467    ) -> Vec<Box<dyn Syscalls<M>>>
468    where
469        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
470        M: SupportMachine + 'static,
471    {
472        // Generation tracker only tracks, never processes syscalls. So
473        // it's safe to concatenate both vectors.
474        let mut syscalls = GenerationTracker::syscall_generator(vm_id, sg_data, vm_context, &data.generation_tracker);
475        for syscall in generate_ckb_syscalls(vm_id, sg_data, vm_context, &debug_printer()) {
476            syscalls.push(syscall);
477        }
478
479        vec![Box::new(SyscallBasedCollectorVMSyscalls { vm_id: *vm_id, data: data.clone(), syscalls })]
480    }
481
482    fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
483    where
484        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
485        V: Clone,
486        M: DefaultMachineRunner,
487    {
488        self.generation_tracker.postprocess(scheduler)?;
489
490        let mut fulfills = HashSet::new();
491        for (key, partial_content) in self.partial_contents.lock().expect("lock").iter() {
492            // For runnable VMs, apply the partial content for syscall traces
493            if scheduler.state(&key.vm_id) == Some(VmState::Runnable) {
494                let syscall = scheduler.peek(
495                    &key.vm_id,
496                    |machine| apply_partial_content(partial_content, &mut ReadonlyMachine::new(machine.inner_mut())),
497                    |snapshot, sg_data| {
498                        apply_partial_content(
499                            partial_content,
500                            &mut ReadonlySnapshotMachine::<
501                                _,
502                                _,
503                                <<M as DefaultMachineRunner>::Inner as CoreMachine>::REG,
504                            >::new(snapshot, sg_data),
505                        )
506                    },
507                )?;
508
509                // For successful exec and spawn, extracts argv
510                match (partial_content, &syscall) {
511                    (
512                        PartialSyscallContent::Spawn { args },
513                        traces::Syscall { value: Some(traces::syscall::Value::SuccessOutputData(process_id)) },
514                    ) => {
515                        let key = self.key(*process_id);
516                        self.set_args(key, args.clone());
517                    }
518                    (
519                        PartialSyscallContent::Exec { args },
520                        traces::Syscall { value: Some(traces::syscall::Value::Terminated(_)) },
521                    ) => {
522                        // When exec succeeds, a new generation is created, a new key is thus required.
523                        let key = self.key(key.vm_id);
524                        self.set_args(key, args.clone());
525                    }
526                    _ => (),
527                }
528
529                self.insert_syscall(key.clone(), syscall);
530                fulfills.insert(key.clone());
531            }
532        }
533
534        self.partial_contents.lock().expect("lock").retain(|test_key, _| !fulfills.contains(test_key));
535        Ok(())
536    }
537
538    fn seal(self) -> HashMap<CollectorKey, Self::Trace> {
539        self.data.lock().expect("lock").clone()
540    }
541}
542
543struct SyscallBasedCollectorVMSyscalls<M> {
544    vm_id: VmId,
545    data: SyscallBasedCollector,
546    syscalls: Vec<Box<dyn Syscalls<M>>>,
547}
548
549impl<M: SupportMachine> Syscalls<M> for SyscallBasedCollectorVMSyscalls<M> {
550    fn initialize(&mut self, machine: &mut M) -> Result<(), Error> {
551        for syscall in &mut self.syscalls {
552            syscall.initialize(machine)?;
553        }
554        Ok(())
555    }
556
557    fn ecall(&mut self, machine: &mut M) -> Result<bool, Error> {
558        let key = self.data.key(self.vm_id);
559        assert!(!self.data.partial_contents.lock().expect("lock").contains_key(&key));
560
561        let partial_content = build_partial_content(machine)?;
562
563        let result = delegate_to_syscalls(machine, &mut self.syscalls);
564        if let Some(partial_content) = partial_content {
565            match result {
566                Ok(true) => {
567                    // Syscall is completed, we can apply partial content now
568                    let data = apply_partial_content(&partial_content, &mut ReadonlyMachine::new(machine))?;
569                    self.data.insert_syscall(key, data);
570                }
571                Err(Error::Yield) => {
572                    // Wait till the VM becomes runnable again to apply partial content
573                    self.data.partial_contents.lock().expect("lock").insert(key, partial_content);
574                }
575                _ => {
576                    // The syscall is not handled, or unrecoverable errors happen,
577                    // we don't do anything here.
578                }
579            }
580        }
581
582        result
583    }
584}
585
586enum PartialSyscallContent {
587    ReturnWithCode,
588    IoData { data_addr: u64, input_length: u64 },
589    IoDataAsCode { data_addr: u64, input_length: u64 }, // Actual binary code.
590    Exec { args: Vec<Vec<u8>> },
591    Spawn { args: Vec<Vec<u8>> },
592    Wait,
593    Pipe { fds_addr: u64 },
594    Write,
595    Read,
596    InheritedFd { buffer_addr: u64 },
597}
598
599fn build_partial_content<M: SupportMachine>(machine: &mut M) -> Result<Option<PartialSyscallContent>, Error> {
600    Ok(if let Ok(code) = machine.registers()[A7].to_u64().try_into() {
601        Some(match code {
602            SyscallCode::LoadTransaction
603            | SyscallCode::LoadScript
604            | SyscallCode::LoadTxHash
605            | SyscallCode::LoadScriptHash
606            | SyscallCode::LoadCell
607            | SyscallCode::LoadHeader
608            | SyscallCode::LoadInput
609            | SyscallCode::LoadWitness
610            | SyscallCode::LoadCellByField
611            | SyscallCode::LoadHeaderByField
612            | SyscallCode::LoadInputByField
613            | SyscallCode::LoadCellData
614            | SyscallCode::LoadBlockExtension => {
615                let data_addr = machine.registers()[A0].to_u64();
616                let length_addr = machine.registers()[A1].to_u64();
617                let input_length = machine.memory_mut().load64(&M::REG::from_u64(length_addr))?.to_u64();
618
619                PartialSyscallContent::IoData { data_addr, input_length }
620            }
621            SyscallCode::LoadCellDataAsCode => {
622                let data_addr = machine.registers()[A0].to_u64();
623                let input_length = machine.registers()[A3].to_u64();
624                PartialSyscallContent::IoDataAsCode { data_addr, input_length }
625            }
626            SyscallCode::VmVersion => PartialSyscallContent::ReturnWithCode,
627            SyscallCode::CurrentCycles => PartialSyscallContent::ReturnWithCode,
628            SyscallCode::Exec => {
629                let argc = machine.registers()[A4].to_u64();
630                let argv = machine.registers()[A5].to_u64();
631                let args = extract_args(machine, argc, argv)?;
632                PartialSyscallContent::Exec { args }
633            }
634            SyscallCode::Spawn => {
635                let (argc, argv) = extract_spawn_argc_argv(machine)?;
636                let args = extract_args(machine, argc, argv)?;
637                PartialSyscallContent::Spawn { args }
638            }
639            SyscallCode::Wait => PartialSyscallContent::Wait,
640            SyscallCode::ProcessId => PartialSyscallContent::ReturnWithCode,
641            SyscallCode::Pipe => {
642                let fds_addr = machine.registers()[A0].to_u64();
643                PartialSyscallContent::Pipe { fds_addr }
644            }
645            SyscallCode::Write => PartialSyscallContent::Write,
646            SyscallCode::Read => PartialSyscallContent::Read,
647            SyscallCode::InheritedFd => {
648                let buffer_addr = machine.registers()[A0].to_u64();
649                PartialSyscallContent::InheritedFd { buffer_addr }
650            }
651            SyscallCode::Close => PartialSyscallContent::ReturnWithCode,
652            SyscallCode::Debug => return Ok(None),
653        })
654    } else {
655        None
656    })
657}
658
659// When this is invoked, the passed machine must be in runnable state.
660fn apply_partial_content<M: SupportMachine>(
661    partial_content: &PartialSyscallContent,
662    machine: &mut M,
663) -> Result<traces::Syscall, Error> {
664    Ok(match partial_content {
665        PartialSyscallContent::ReturnWithCode => {
666            let return_code = machine.registers()[A0].to_i64();
667            return return_syscall(return_code);
668        }
669        PartialSyscallContent::IoData { data_addr, input_length } => {
670            let return_code = machine.registers()[A0].to_i64();
671            if return_code != 0 {
672                return return_syscall(return_code);
673            }
674            let length_addr = machine.registers()[A1].clone();
675            let output_length = machine.memory_mut().load64(&length_addr)?.to_u64();
676
677            let actual_data_length = std::cmp::min(*input_length, output_length);
678            let data = machine.memory_mut().load_bytes(*data_addr, actual_data_length)?;
679
680            traces::Syscall {
681                value: Some(traces::syscall::Value::IoData(traces::IoData {
682                    available_data: data.as_ref().to_vec(),
683                    additional_length: output_length - data.len() as u64,
684                })),
685            }
686        }
687        PartialSyscallContent::IoDataAsCode { data_addr, input_length } => {
688            let return_code = machine.registers()[A0].to_i64();
689            if return_code != 0 {
690                return return_syscall(return_code);
691            }
692            let data = machine.memory_mut().load_bytes(*data_addr, *input_length)?;
693            traces::Syscall {
694                value: Some(traces::syscall::Value::IoData(traces::IoData {
695                    available_data: data.as_ref().to_vec(),
696                    additional_length: 0,
697                })),
698            }
699        }
700        PartialSyscallContent::Exec { .. } => {
701            let return_code = machine.registers()[A0].to_i64();
702            if return_code != 0 {
703                return return_syscall(return_code);
704            }
705            traces::Syscall { value: Some(traces::syscall::Value::Terminated(traces::Terminated {})) }
706        }
707        PartialSyscallContent::Spawn { .. } => {
708            let return_code = machine.registers()[A0].to_i64();
709            if return_code != 0 {
710                return return_syscall(return_code);
711            }
712            let process_id = extract_spawned_process_id(machine)?;
713            traces::Syscall { value: Some(traces::syscall::Value::SuccessOutputData(process_id)) }
714        }
715        PartialSyscallContent::Wait => {
716            let return_code = machine.registers()[A0].to_i64();
717            if return_code != 0 {
718                return return_syscall(return_code);
719            }
720            let exit_code_addr = machine.registers()[A1].clone();
721            let exit_code = machine.memory_mut().load8(&exit_code_addr)?.to_i64() as u64;
722            traces::Syscall { value: Some(traces::syscall::Value::SuccessOutputData(exit_code)) }
723        }
724        PartialSyscallContent::Pipe { fds_addr } => {
725            let return_code = machine.registers()[A0].to_i64();
726            if return_code != 0 {
727                return return_syscall(return_code);
728            }
729            let fd1 = machine.memory_mut().load64(&M::REG::from_u64(*fds_addr))?.to_u64();
730            let fd2 = machine.memory_mut().load64(&M::REG::from_u64(*fds_addr + 8))?.to_u64();
731            traces::Syscall { value: Some(traces::syscall::Value::Fds(traces::Fds { fds: vec![fd1, fd2] })) }
732        }
733        PartialSyscallContent::Write => {
734            let return_code = machine.registers()[A0].to_i64();
735            if return_code != 0 {
736                return return_syscall(return_code);
737            }
738            let length_addr = machine.registers()[A2].clone();
739            let length = machine.memory_mut().load64(&length_addr)?.to_u64();
740            traces::Syscall { value: Some(traces::syscall::Value::SuccessOutputData(length)) }
741        }
742        PartialSyscallContent::Read => {
743            let return_code = machine.registers()[A0].to_i64();
744            if return_code != 0 {
745                return return_syscall(return_code);
746            }
747            let data_addr = machine.registers()[A1].to_u64();
748            let length_addr = machine.registers()[A2].clone();
749            let length = machine.memory_mut().load64(&length_addr)?.to_u64();
750            let data = machine.memory_mut().load_bytes(data_addr, length)?;
751
752            traces::Syscall {
753                value: Some(traces::syscall::Value::IoData(traces::IoData {
754                    available_data: data.as_ref().to_vec(),
755                    additional_length: 0,
756                })),
757            }
758        }
759        PartialSyscallContent::InheritedFd { buffer_addr } => {
760            let count_addr = machine.registers()[A1].clone();
761            let count = machine.memory_mut().load64(&count_addr)?.to_u64();
762
763            let addr = *buffer_addr;
764            let mut fds = Vec::with_capacity(count as usize);
765            for i in 0..count {
766                fds.push(machine.memory_mut().load64(&M::REG::from_u64(addr + i * 8))?.to_u64());
767            }
768            traces::Syscall { value: Some(traces::syscall::Value::Fds(traces::Fds { fds })) }
769        }
770    })
771}
772
773fn return_syscall(code: i64) -> Result<traces::Syscall, Error> {
774    Ok(traces::Syscall { value: Some(traces::syscall::Value::ReturnWithCode(code)) })
775}
776
777#[derive(Default, Clone)]
778pub struct GenerationTracker {
779    pending: Arc<Mutex<HashSet<VmId>>>,
780    generations: Arc<Mutex<HashMap<VmId, u64>>>,
781}
782
783impl GenerationTracker {
784    fn mark_pending(&self, vm_id: VmId) {
785        let mut s = self.pending.lock().expect("lock");
786
787        s.insert(vm_id);
788    }
789
790    fn increase_generation(&self, vm_id: VmId) {
791        let mut m = self.generations.lock().expect("lock");
792
793        *m.entry(vm_id).or_default() += 1;
794    }
795
796    pub fn generation(&self, vm_id: VmId) -> u64 {
797        let mut m = self.generations.lock().expect("lock");
798
799        *m.entry(vm_id).or_default()
800    }
801
802    pub fn key(&self, vm_id: VmId) -> CollectorKey {
803        CollectorKey { vm_id, generation_id: self.generation(vm_id) }
804    }
805}
806
807impl Collector for GenerationTracker {
808    type Trace = ();
809
810    fn syscall_generator<DL, M>(
811        vm_id: &VmId,
812        _sg_data: &SgData<DL>,
813        _vm_context: &VmContext<DL>,
814        data: &Self,
815    ) -> Vec<Box<dyn Syscalls<M>>>
816    where
817        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
818        M: SupportMachine + 'static,
819    {
820        vec![Box::new(GenerationTrackerSyscalls { vm_id: *vm_id, data: data.clone() })]
821    }
822
823    fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
824    where
825        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
826        V: Clone,
827        M: DefaultMachineRunner,
828    {
829        for vm_id in self.pending.lock().expect("lock").drain() {
830            assert_eq!(scheduler.state(&vm_id), Some(VmState::Runnable));
831            let terminated = scheduler.peek(
832                &vm_id,
833                |machine| {
834                    let machine = ReadonlyMachine::new(machine.inner_mut());
835                    Ok(machine.registers()[A0].to_u64() == 0)
836                },
837                |snapshot, sg_data| {
838                    let machine =
839                        ReadonlySnapshotMachine::<_, _, <<M as DefaultMachineRunner>::Inner as CoreMachine>::REG>::new(
840                            snapshot, sg_data,
841                        );
842                    Ok(machine.registers()[A0].to_u64() == 0)
843                },
844            )?;
845            if terminated {
846                self.increase_generation(vm_id);
847            }
848        }
849        Ok(())
850    }
851
852    fn seal(self) -> HashMap<CollectorKey, ()> {
853        HashMap::default()
854    }
855}
856
857struct GenerationTrackerSyscalls {
858    vm_id: VmId,
859    data: GenerationTracker,
860}
861
862impl<M: SupportMachine> Syscalls<M> for GenerationTrackerSyscalls {
863    fn initialize(&mut self, _machine: &mut M) -> Result<(), Error> {
864        Ok(())
865    }
866
867    fn ecall(&mut self, machine: &mut M) -> Result<bool, Error> {
868        if let Ok(code) = SyscallCode::try_from(machine.registers()[A7].to_u64()) {
869            if code == SyscallCode::Exec {
870                self.data.mark_pending(self.vm_id);
871            }
872        }
873        Ok(false)
874    }
875}
876
877#[derive(Default, Clone)]
878pub struct BinaryLocatorCollector<C: Collector> {
879    partial_locators: Arc<Mutex<HashMap<CollectorKey, PartialLocator>>>,
880    data: Arc<Mutex<HashMap<CollectorKey, BinaryLocator>>>,
881    generation_tracker: GenerationTracker,
882    collector: C,
883}
884
885impl<C: Collector + Send + 'static> Collector for BinaryLocatorCollector<C> {
886    type Trace = (BinaryLocator, C::Trace);
887
888    fn syscall_generator<DL, M>(
889        vm_id: &VmId,
890        sg_data: &SgData<DL>,
891        vm_context: &VmContext<DL>,
892        data: &Self,
893    ) -> Vec<Box<dyn Syscalls<M>>>
894    where
895        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
896        M: SupportMachine + 'static,
897    {
898        // Generation tracker only tracks, never processes syscalls. So
899        // it's safe to concatenate both vectors.
900        let mut syscalls = GenerationTracker::syscall_generator(vm_id, sg_data, vm_context, &data.generation_tracker);
901        for syscall in C::syscall_generator(vm_id, sg_data, vm_context, &data.collector) {
902            syscalls.push(syscall);
903        }
904
905        vec![Box::new(BinaryLocatorCollectorSyscalls { vm_id: *vm_id, data: data.clone(), syscalls })]
906    }
907
908    fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
909    where
910        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
911        V: Clone,
912        M: DefaultMachineRunner,
913    {
914        self.generation_tracker.postprocess(scheduler)?;
915        self.collector.postprocess(scheduler)?;
916
917        let sg_data = scheduler.sg_data().clone();
918
919        for (key, partial_locator) in self.partial_locators.lock().expect("lock").drain() {
920            assert_eq!(scheduler.state(&key.vm_id), Some(VmState::Runnable));
921
922            let new_key = self.generation_tracker.key(key.vm_id);
923            let locator = scheduler.peek(
924                &key.vm_id,
925                |machine| {
926                    apply_partial_locator(
927                        &key,
928                        &new_key,
929                        &partial_locator,
930                        &mut ReadonlyMachine::new(machine.inner_mut()),
931                        &sg_data,
932                    )
933                },
934                |snapshot, sg_data| {
935                    apply_partial_locator(
936                        &key,
937                        &new_key,
938                        &partial_locator,
939                        &mut ReadonlySnapshotMachine::<
940                            _,
941                            _,
942                            <<M as DefaultMachineRunner>::Inner as CoreMachine>::REG,
943                        >::new(snapshot, sg_data),
944                        sg_data,
945                    )
946                },
947            )?;
948
949            if let Some((vm_id, locator)) = locator {
950                let key = self.generation_tracker.key(vm_id);
951                self.data.lock().expect("lock").insert(key, locator);
952            }
953        }
954        Ok(())
955    }
956
957    fn seal(self) -> HashMap<CollectorKey, Self::Trace> {
958        let mut self_data = self.data.lock().expect("lock").clone();
959
960        let mut result = HashMap::default();
961        for (key, collector_trace) in self.collector.seal().drain() {
962            if let Some(self_trace) = self_data.remove(&key) {
963                result.insert(key, (self_trace, collector_trace));
964            }
965        }
966        result
967    }
968
969    fn preprocess<DL, V, M>(
970        &self,
971        verifier: &TransactionScriptsVerifier<DL, V, M>,
972        script_group: &ScriptGroup,
973        scheduler: &mut Scheduler<DL, V, M>,
974    ) -> Result<(), Error>
975    where
976        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
977        V: Clone,
978        M: DefaultMachineRunner,
979    {
980        let root_locator = BinaryLocator {
981            index: scheduler
982                .sg_data()
983                .tx_info
984                .extract_referenced_dep_index(&script_group.script)
985                .map_err(|e| Error::External(format!("extract dep index: {}", e)))? as u64,
986            source: Source::CellDep as u64,
987            offset: 0,
988            length: verifier
989                .extract_script(&script_group.script)
990                .map_err(|e| Error::External(format!("extract script error: {}", e)))?
991                .len() as u32,
992            from_cell: true,
993        };
994        self.data.lock().expect("lock").insert(CollectorKey { vm_id: ROOT_VM_ID, generation_id: 0 }, root_locator);
995
996        Ok(())
997    }
998}
999
1000struct BinaryLocatorCollectorSyscalls<C: Collector, M> {
1001    vm_id: VmId,
1002    data: BinaryLocatorCollector<C>,
1003    syscalls: Vec<Box<dyn Syscalls<M>>>,
1004}
1005
1006impl<C: Collector + Send, M: SupportMachine> Syscalls<M> for BinaryLocatorCollectorSyscalls<C, M> {
1007    fn initialize(&mut self, machine: &mut M) -> Result<(), Error> {
1008        for syscall in &mut self.syscalls {
1009            syscall.initialize(machine)?;
1010        }
1011        Ok(())
1012    }
1013
1014    fn ecall(&mut self, machine: &mut M) -> Result<bool, Error> {
1015        let key = self.data.generation_tracker.key(self.vm_id);
1016        assert!(!self.data.partial_locators.lock().expect("lock").contains_key(&key));
1017
1018        if let Ok(code) = SyscallCode::try_from(machine.registers()[A7].to_u64()) {
1019            if code == SyscallCode::Exec || code == SyscallCode::Spawn {
1020                // Tracer requires spawn syscalls, when spawn is enabled,
1021                // exec will use V2 implementation, which uses a yield
1022                // in syscalls. So we don't have to check the result of
1023                // delegate_to_syscalls
1024                if let Some(partial_locator) = build_partial_locator(machine) {
1025                    self.data.partial_locators.lock().expect("lock").insert(key, partial_locator);
1026                }
1027            }
1028        }
1029
1030        delegate_to_syscalls(machine, &mut self.syscalls)
1031    }
1032}
1033
1034enum PartialLocator {
1035    Exec(BinaryLocator),
1036    Spawn(BinaryLocator),
1037}
1038
1039enum PartialVmCreate {
1040    Exec(CollectorKey),
1041    Spawn(CollectorKey),
1042}
1043
1044#[derive(Default, Clone)]
1045pub struct VmCreateCollector {
1046    pending: Arc<Mutex<Option<PartialVmCreate>>>,
1047    data: Arc<Mutex<HashMap<CollectorKey, Vec<CollectorKey>>>>,
1048    generation_tracker: GenerationTracker,
1049}
1050
1051impl Collector for VmCreateCollector {
1052    type Trace = traces::VmCreations;
1053
1054    fn syscall_generator<DL, M>(
1055        vm_id: &VmId,
1056        sg_data: &SgData<DL>,
1057        vm_context: &VmContext<DL>,
1058        data: &Self,
1059    ) -> Vec<Box<dyn Syscalls<M>>>
1060    where
1061        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
1062        M: SupportMachine + 'static,
1063    {
1064        // Generation tracker only tracks, never processes syscalls. So
1065        // it's safe to concatenate both vectors.
1066        let mut syscalls = GenerationTracker::syscall_generator(vm_id, sg_data, vm_context, &data.generation_tracker);
1067        for syscall in generate_ckb_syscalls(vm_id, sg_data, vm_context, &debug_printer()) {
1068            syscalls.push(syscall);
1069        }
1070        vec![Box::new(VmCreateCollectorVMSyscalls { vm_id: *vm_id, data: data.clone(), syscalls })]
1071    }
1072
1073    fn preprocess<DL, V, M>(
1074        &self,
1075        _verifier: &TransactionScriptsVerifier<DL, V, M>,
1076        _script_group: &ScriptGroup,
1077        _scheduler: &mut Scheduler<DL, V, M>,
1078    ) -> Result<(), Error>
1079    where
1080        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
1081        V: Clone,
1082        M: DefaultMachineRunner,
1083    {
1084        let key = self.generation_tracker.key(ROOT_VM_ID);
1085        self.data.lock().unwrap().insert(key, vec![]);
1086        Ok(())
1087    }
1088
1089    fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
1090    where
1091        DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
1092        V: Clone,
1093        M: DefaultMachineRunner,
1094    {
1095        self.generation_tracker.postprocess(scheduler)?;
1096
1097        if let Some(partial_vm_create) = self.pending.lock().unwrap().take() {
1098            match partial_vm_create {
1099                PartialVmCreate::Exec(key) => {
1100                    let child_key = self.generation_tracker.key(key.vm_id);
1101                    self.data.lock().unwrap().entry(key).or_default().push(child_key.clone());
1102                    self.data.lock().unwrap().insert(child_key, vec![]);
1103                }
1104                PartialVmCreate::Spawn(key) => {
1105                    assert_eq!(scheduler.state(&key.vm_id), Some(VmState::Runnable));
1106                    let child_id = scheduler.peek(
1107                        &key.vm_id,
1108                        |machine| {
1109                            let mut machine = ReadonlyMachine::new(machine.inner_mut());
1110                            extract_spawned_process_id(&mut machine)
1111                        },
1112                        |snapshot, sg_data| {
1113                            let mut machine = ReadonlySnapshotMachine::<
1114                                _,
1115                                _,
1116                                <<M as DefaultMachineRunner>::Inner as CoreMachine>::REG,
1117                            >::new(snapshot, sg_data);
1118                            extract_spawned_process_id(&mut machine)
1119                        },
1120                    )?;
1121                    let child_key = self.generation_tracker.key(child_id);
1122                    self.data.lock().unwrap().entry(key).or_default().push(child_key.clone());
1123                    self.data.lock().unwrap().insert(child_key, vec![]);
1124                }
1125            }
1126        }
1127        Ok(())
1128    }
1129
1130    fn seal(self) -> HashMap<CollectorKey, Self::Trace> {
1131        self.data
1132            .lock()
1133            .expect("lock")
1134            .iter()
1135            .map(|(k, v)| {
1136                (
1137                    k.clone(),
1138                    traces::VmCreations {
1139                        vm_creations: v
1140                            .iter()
1141                            .map(|ck| traces::VmCreation { vm_id: ck.vm_id, generation_id: ck.generation_id })
1142                            .collect(),
1143                    },
1144                )
1145            })
1146            .collect()
1147    }
1148}
1149
1150struct VmCreateCollectorVMSyscalls<M> {
1151    vm_id: VmId,
1152    data: VmCreateCollector,
1153    syscalls: Vec<Box<dyn Syscalls<M>>>,
1154}
1155
1156impl<M: SupportMachine> Syscalls<M> for VmCreateCollectorVMSyscalls<M> {
1157    fn initialize(&mut self, machine: &mut M) -> Result<(), Error> {
1158        for syscall in &mut self.syscalls {
1159            syscall.initialize(machine)?;
1160        }
1161        Ok(())
1162    }
1163
1164    fn ecall(&mut self, machine: &mut M) -> Result<bool, Error> {
1165        if let Ok(code) = SyscallCode::try_from(machine.registers()[A7].to_u64()) {
1166            let key = self.data.generation_tracker.key(self.vm_id);
1167            match code {
1168                SyscallCode::Exec => {
1169                    let _ = self.data.pending.lock().unwrap().insert(PartialVmCreate::Exec(key));
1170                }
1171                SyscallCode::Spawn => {
1172                    let _ = self.data.pending.lock().unwrap().insert(PartialVmCreate::Spawn(key));
1173                }
1174                _ => {}
1175            }
1176        }
1177        delegate_to_syscalls(machine, &mut self.syscalls)
1178    }
1179}
1180
1181macro_rules! define_combine_collector {
1182    ($name:ident, $trace_name:ident, $($field:ident: $type:ident),*) => {
1183        #[derive(Clone)]
1184        pub struct $trace_name<$($type: Collector),*> {
1185            $(pub $field: Option<$type::Trace>),*
1186        }
1187
1188        impl<$($type: Collector),*> Default for $trace_name<$($type),*> {
1189            fn default() -> Self {
1190                Self { $($field: None),* }
1191            }
1192        }
1193
1194        #[derive(Default, Clone)]
1195        pub struct $name<$($type: Collector),*> {
1196            $(pub $field: $type),*
1197        }
1198
1199        impl<$($type: Collector),*> $name<$($type),*> {
1200            pub fn new($($field: $type),*) -> Self {
1201                Self {
1202                    $($field),*
1203                }
1204            }
1205        }
1206
1207        impl<$($type: Collector),*> Collector for $name<$($type),*>
1208        {
1209            type Trace = $trace_name<$($type),*>;
1210
1211            fn syscall_generator<DL, M>(
1212                vm_id: &VmId,
1213                sg_data: &SgData<DL>,
1214                vm_context: &VmContext<DL>,
1215                data: &Self,
1216            ) -> Vec<Box<dyn Syscalls<M>>>
1217            where
1218                DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone + 'static,
1219                M: SupportMachine + 'static,
1220            {
1221                let mut r = vec![];
1222                $(r.extend($type::syscall_generator(vm_id, sg_data, vm_context, &data.$field));)*
1223                r
1224            }
1225
1226            fn preprocess<DL, V, M>(
1227                &self,
1228                verifier: &TransactionScriptsVerifier<DL, V, M>,
1229                script_group: &ScriptGroup,
1230                scheduler: &mut Scheduler<DL, V, M>,
1231            ) -> Result<(), Error>
1232            where
1233                DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
1234                V: Clone,
1235                M: DefaultMachineRunner,
1236            {
1237                $(self.$field.preprocess(verifier, script_group, scheduler)?;)*
1238                Ok(())
1239            }
1240
1241            fn postprocess<DL, V, M>(&self, scheduler: &mut Scheduler<DL, V, M>) -> Result<(), Error>
1242            where
1243                DL: CellDataProvider + HeaderProvider + ExtensionProvider + Send + Sync + Clone,
1244                V: Clone,
1245                M: DefaultMachineRunner,
1246            {
1247                $(self.$field.postprocess(scheduler)?;)*
1248                Ok(())
1249            }
1250
1251            fn seal(self) -> HashMap<CollectorKey, Self::Trace> {
1252                let mut r: HashMap<CollectorKey, Self::Trace> = HashMap::new();
1253                $(
1254                    for (k, v) in self.$field.seal() {
1255                        let d = r.entry(k).or_insert_with($trace_name::default);
1256                        d.$field = Some(v);
1257                    }
1258                )*
1259                r
1260            }
1261        }
1262    };
1263}
1264
1265define_combine_collector!(CombineCollector2, CombineCollector2Trace, c1: C, c2: D);
1266define_combine_collector!(CombineCollector3, CombineCollector3Trace, c1: C, c2: D, c3: E);
1267define_combine_collector!(CombineCollector4, CombineCollector4Trace, c1: C, c2: D, c3: E, c4: F);
1268define_combine_collector!(CombineCollector5, CombineCollector5Trace, c1: C, c2: D, c3: E, c4: F, c5: G);
1269define_combine_collector!(CombineCollector6, CombineCollector6Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H);
1270define_combine_collector!(CombineCollector7, CombineCollector7Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I);
1271define_combine_collector!(CombineCollector8, CombineCollector8Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J);
1272define_combine_collector!(CombineCollector9, CombineCollector9Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K);
1273define_combine_collector!(CombineCollector10, CombineCollector10Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L);
1274define_combine_collector!(CombineCollector11, CombineCollector11Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L, c11: N);
1275define_combine_collector!(CombineCollector12, CombineCollector12Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L, c11: N, c12: O);
1276define_combine_collector!(CombineCollector13, CombineCollector13Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L, c11: N, c12: O, c13: P);
1277define_combine_collector!(CombineCollector14, CombineCollector14Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L, c11: N, c12: O, c13: P, c14: Q);
1278define_combine_collector!(CombineCollector15, CombineCollector15Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L, c11: N, c12: O, c13: P, c14: Q, c15: R);
1279define_combine_collector!(CombineCollector16, CombineCollector16Trace, c1: C, c2: D, c3: E, c4: F, c5: G, c6: H, c7: I, c8: J, c9: K, c10: L, c11: N, c12: O, c13: P, c14: Q, c15: R, c16: S);
1280
1281fn build_partial_locator<M: SupportMachine>(machine: &mut M) -> Option<PartialLocator> {
1282    let regs = machine.registers();
1283    let index = regs[A0].to_u64();
1284    let source = regs[A1].to_u64();
1285    let bounds = regs[A3].to_u64();
1286    let offset = (bounds >> 32) as u32;
1287    let length = bounds as u32;
1288    let from_cell = regs[A2].to_u64() == 0;
1289    let locator = BinaryLocator { index, source, offset, length, from_cell };
1290
1291    if let Ok(code) = machine.registers()[A7].to_u64().try_into() {
1292        match code {
1293            SyscallCode::Exec => Some(PartialLocator::Exec(locator)),
1294            SyscallCode::Spawn => Some(PartialLocator::Spawn(locator)),
1295            _ => None,
1296        }
1297    } else {
1298        None
1299    }
1300}
1301
1302fn apply_partial_locator<M: SupportMachine, DL: CellDataProvider>(
1303    old_key: &CollectorKey,
1304    new_key: &CollectorKey,
1305    partial_locator: &PartialLocator,
1306    machine: &mut M,
1307    sg_data: &SgData<DL>,
1308) -> Result<Option<(VmId, BinaryLocator)>, Error> {
1309    assert_eq!(old_key.vm_id, new_key.vm_id);
1310
1311    match partial_locator {
1312        PartialLocator::Exec(locator) => {
1313            if old_key != new_key {
1314                Ok(Some((new_key.vm_id, normalize_locator(locator, sg_data)?)))
1315            } else {
1316                Ok(None)
1317            }
1318        }
1319        PartialLocator::Spawn(locator) => {
1320            let process_id = extract_spawned_process_id(machine)?;
1321            assert_ne!(process_id, new_key.vm_id);
1322
1323            Ok(Some((process_id, normalize_locator(locator, sg_data)?)))
1324        }
1325    }
1326}
1327
1328// Normalize a BinaryLocator so length does not contain 0. While CKB does not
1329// need this, it aids debugging purposes.
1330fn normalize_locator<DL: CellDataProvider>(
1331    locator: &BinaryLocator,
1332    sg_data: &SgData<DL>,
1333) -> Result<BinaryLocator, Error> {
1334    if locator.length > 0 {
1335        return Ok(locator.clone());
1336    }
1337
1338    let data_piece_id = DataPieceId::try_from((locator.source, locator.index, if locator.from_cell { 0 } else { 1 }))
1339        .map_err(|e| Error::External(format!("Converting data piece error: {}", e)))?;
1340
1341    let (_, length) = sg_data
1342        .load_data(&data_piece_id, locator.offset as u64, 0)
1343        .ok_or_else(|| Error::External(format!("Locator {:?} is invalid!", locator)))?;
1344
1345    let mut locator = locator.clone();
1346    locator.length = length as u32;
1347
1348    Ok(locator)
1349}
1350
1351fn delegate_to_syscalls<M: SupportMachine>(
1352    machine: &mut M,
1353    syscalls: &mut [Box<dyn Syscalls<M>>],
1354) -> Result<bool, Error> {
1355    for syscall in syscalls {
1356        let processed = syscall.ecall(machine)?;
1357        if processed {
1358            return Ok(true);
1359        }
1360    }
1361    Ok(false)
1362}
1363
1364fn debug_printer() -> DebugPrinter {
1365    Arc::new(|_hash: &Byte32, message: &str| {
1366        let message = message.trim_end_matches('\n');
1367        if !message.is_empty() {
1368            println!("Script log: {}", message);
1369        }
1370    })
1371}
1372
1373fn locate_input(index: u64, source: u64, script_group: &ScriptGroup) -> Option<usize> {
1374    if source == Source::Input as u64 {
1375        return Some(index as usize);
1376    } else if source == Source::GroupInput as u64 {
1377        return script_group.input_indices.get(index as usize).copied();
1378    }
1379    None
1380}
1381
1382fn locate_witness(index: u64, source: u64, script_group: &ScriptGroup) -> Option<usize> {
1383    if source == Source::Input as u64 || source == Source::Output as u64 {
1384        return Some(index as usize);
1385    } else if source == Source::GroupInput as u64 {
1386        return script_group.input_indices.get(index as usize).copied();
1387    } else if source == Source::GroupOutput as u64 {
1388        return script_group.output_indices.get(index as usize).copied();
1389    }
1390    None
1391}
1392
1393fn extract_spawned_process_id<M: SupportMachine>(machine: &mut M) -> Result<u64, Error> {
1394    let spgs_addr = machine.registers()[A4].clone();
1395    let process_id_addr_addr = spgs_addr.overflowing_add(&M::REG::from_u64(16));
1396    let process_id_addr = machine.memory_mut().load64(&process_id_addr_addr)?;
1397    let process_id = machine.memory_mut().load64(&process_id_addr)?.to_u64();
1398
1399    Ok(process_id)
1400}
1401
1402fn extract_spawn_argc_argv<M: SupportMachine>(machine: &mut M) -> Result<(u64, u64), Error> {
1403    let spgs_addr = machine.registers()[A4].clone();
1404    let argc_addr = spgs_addr.clone();
1405    let argc = machine.memory_mut().load64(&argc_addr)?.to_u64();
1406    let argv_addr = spgs_addr.overflowing_add(&M::REG::from_u64(8));
1407    let argv = machine.memory_mut().load64(&argv_addr)?.to_u64();
1408    Ok((argc, argv))
1409}
1410
1411fn extract_args<M: SupportMachine>(machine: &mut M, argc: u64, argv: u64) -> Result<Vec<Vec<u8>>, Error> {
1412    let reader = FlattenedArgsReader::new(machine.memory_mut(), M::REG::from_u64(argc), M::REG::from_u64(argv));
1413    let mut result = Vec::with_capacity(reader.len());
1414    for item in reader {
1415        result.push(item?.to_vec());
1416    }
1417    Ok(result)
1418}