isla_lib/
executor.rs

1// BSD 2-Clause License
2//
3// Copyright (c) 2019, 2020 Alasdair Armstrong
4// Copyright (c) 2020 Brian Campbell
5// Copyright (c) 2020 Dhruv Makwana
6//
7// All rights reserved.
8//
9// Redistribution and use in source and binary forms, with or without
10// modification, are permitted provided that the following conditions are
11// met:
12//
13// 1. Redistributions of source code must retain the above copyright
14// notice, this list of conditions and the following disclaimer.
15//
16// 2. Redistributions in binary form must reproduce the above copyright
17// notice, this list of conditions and the following disclaimer in the
18// documentation and/or other materials provided with the distribution.
19//
20// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31
32//! This module implements the core of the symbolic execution engine.
33
34use crossbeam::deque::{Injector, Steal, Stealer, Worker};
35use crossbeam::queue::SegQueue;
36use crossbeam::thread;
37use std::collections::{HashMap, HashSet};
38use std::mem;
39use std::sync::atomic::{AtomicBool, Ordering};
40use std::sync::mpsc;
41use std::sync::mpsc::{Receiver, Sender};
42use std::sync::{Arc, RwLock};
43use std::thread::sleep;
44use std::time::{Duration, Instant};
45
46use crate::bitvector::BV;
47use crate::error::ExecError;
48use crate::ir::*;
49use crate::log;
50use crate::memory::Memory;
51use crate::primop;
52use crate::probe;
53use crate::smt::*;
54use crate::zencode;
55
56/// Create a Symbolic value of a specified type. Can return a concrete value if the type only
57/// permits a single value, such as for the unit type or the zero-length bitvector type (which is
58/// ideal because SMT solvers don't allow zero-length bitvectors). Compound types like structs will
59/// be a concrete structure with symbolic values for each field. Returns the `NoSymbolicType` error
60/// if the type cannot be represented in the SMT solver.
61pub fn symbolic<B: BV>(
62    ty: &Ty<Name>,
63    shared_state: &SharedState<B>,
64    solver: &mut Solver<B>,
65) -> Result<Val<B>, ExecError> {
66    let smt_ty = match ty {
67        Ty::Unit => return Ok(Val::Unit),
68        Ty::Bits(0) => return Ok(Val::Bits(B::zeros(0))),
69
70        Ty::I64 => smtlib::Ty::BitVec(64),
71        Ty::I128 => smtlib::Ty::BitVec(128),
72        Ty::Bits(sz) => smtlib::Ty::BitVec(*sz),
73        Ty::Bool => smtlib::Ty::Bool,
74        Ty::Bit => smtlib::Ty::BitVec(1),
75
76        Ty::Struct(name) => {
77            if let Some(field_types) = shared_state.structs.get(name) {
78                let field_values = field_types
79                    .iter()
80                    .map(|(f, ty)| match symbolic(ty, shared_state, solver) {
81                        Ok(value) => Ok((*f, value)),
82                        Err(error) => Err(error),
83                    })
84                    .collect::<Result<_, _>>()?;
85                return Ok(Val::Struct(field_values));
86            } else {
87                let name = zencode::decode(shared_state.symtab.to_str(*name));
88                return Err(ExecError::Unreachable(format!("Struct {} does not appear to exist!", name)));
89            }
90        }
91
92        Ty::Enum(name) => {
93            let enum_size = shared_state.enums.get(name).unwrap().len();
94            let enum_id = solver.get_enum(enum_size);
95            return solver.declare_const(smtlib::Ty::Enum(enum_id)).into();
96        }
97
98        Ty::FixedVector(sz, ty) => {
99            let values = (0..*sz).map(|_| symbolic(ty, shared_state, solver)).collect::<Result<_, _>>()?;
100            return Ok(Val::Vector(values));
101        }
102
103        // Some things we just can't represent symbolically, but we can continue in the hope that
104        // they never actually get used.
105        _ => return Ok(Val::Poison),
106    };
107
108    solver.declare_const(smt_ty).into()
109}
110
111#[derive(Clone)]
112struct LocalState<'ir, B> {
113    vars: Bindings<'ir, B>,
114    regs: Bindings<'ir, B>,
115    lets: Bindings<'ir, B>,
116}
117
118/// Gets a value from a variable `Bindings` map. Note that this function is set up to handle the
119/// following case:
120///
121/// ```Sail
122/// var x;
123/// x = 3;
124/// ```
125///
126/// When we declare a variable it has the value `UVal::Uninit(ty)` where `ty` is its type. When
127/// that variable is first accessed it'll be initialized to a symbolic value in the SMT solver if it
128/// is still uninitialized. This means that in the above code, because `x` is immediately assigned
129/// the value 3, no interaction with the SMT solver will occur.
130fn get_and_initialize<'ir, B: BV>(
131    v: Name,
132    vars: &mut Bindings<'ir, B>,
133    shared_state: &SharedState<'ir, B>,
134    solver: &mut Solver<B>,
135) -> Result<Option<Val<B>>, ExecError> {
136    Ok(match vars.get(&v) {
137        Some(UVal::Uninit(ty)) => {
138            let sym = symbolic(ty, shared_state, solver)?;
139            vars.insert(v, UVal::Init(sym.clone()));
140            Some(sym)
141        }
142        Some(UVal::Init(value)) => Some(value.clone()),
143        None => None,
144    })
145}
146
147fn get_id_and_initialize<'ir, B: BV>(
148    id: Name,
149    local_state: &mut LocalState<'ir, B>,
150    shared_state: &SharedState<'ir, B>,
151    solver: &mut Solver<B>,
152    accessor: &mut Vec<Accessor>,
153) -> Result<Val<B>, ExecError> {
154    Ok(match get_and_initialize(id, &mut local_state.vars, shared_state, solver)? {
155        Some(value) => value,
156        None => match get_and_initialize(id, &mut local_state.regs, shared_state, solver)? {
157            Some(value) => {
158                let symbol = zencode::decode(shared_state.symtab.to_str(id));
159                // HACK: Don't store the entire TLB in the trace
160                if symbol != "_TLB" {
161                    // log!(log::VERBOSE, &format!("Reading register: {} {:?}", symbol, value));
162                    solver.add_event(Event::ReadReg(id, accessor.to_vec(), value.clone()));
163                }
164                value
165            }
166            None => match get_and_initialize(id, &mut local_state.lets, shared_state, solver)? {
167                Some(value) => value,
168                None => match shared_state.enum_members.get(&id) {
169                    Some((member, enum_size)) => {
170                        let enum_id = solver.get_enum(*enum_size);
171                        Val::Enum(EnumMember { enum_id, member: *member })
172                    }
173                    None => panic!("Symbol {} ({:?}) not found", zencode::decode(shared_state.symtab.to_str(id)), id),
174                },
175            },
176        },
177    })
178}
179
180fn get_loc_and_initialize<'ir, B: BV>(
181    loc: &Loc<Name>,
182    local_state: &mut LocalState<'ir, B>,
183    shared_state: &SharedState<'ir, B>,
184    solver: &mut Solver<B>,
185    accessor: &mut Vec<Accessor>,
186) -> Result<Val<B>, ExecError> {
187    Ok(match loc {
188        Loc::Id(id) => get_id_and_initialize(*id, local_state, shared_state, solver, accessor)?,
189        Loc::Field(loc, field) => {
190            accessor.push(Accessor::Field(*field));
191            if let Val::Struct(members) = get_loc_and_initialize(loc, local_state, shared_state, solver, accessor)? {
192                match members.get(field) {
193                    Some(field_value) => field_value.clone(),
194                    None => panic!("No field {:?}", shared_state.symtab.to_str(*field)),
195                }
196            } else {
197                panic!("Struct expression did not evaluate to a struct")
198            }
199        }
200        _ => panic!("Cannot get_loc_and_initialize"),
201    })
202}
203
204fn eval_exp_with_accessor<'ir, B: BV>(
205    exp: &Exp<Name>,
206    local_state: &mut LocalState<'ir, B>,
207    shared_state: &SharedState<'ir, B>,
208    solver: &mut Solver<B>,
209    accessor: &mut Vec<Accessor>,
210) -> Result<Val<B>, ExecError> {
211    use Exp::*;
212    Ok(match exp {
213        Id(id) => get_id_and_initialize(*id, local_state, shared_state, solver, accessor)?,
214
215        I64(i) => Val::I64(*i),
216        I128(i) => Val::I128(*i),
217        Unit => Val::Unit,
218        Bool(b) => Val::Bool(*b),
219        // The parser only returns 64-bit or less bitvectors
220        Bits(bv) => Val::Bits(B::new(bv.lower_u64(), bv.len())),
221        String(s) => Val::String(s.clone()),
222
223        Undefined(ty) => symbolic(ty, shared_state, solver)?,
224
225        Call(op, args) => {
226            let args: Vec<Val<B>> =
227                args.iter().map(|arg| eval_exp(arg, local_state, shared_state, solver)).collect::<Result<_, _>>()?;
228            match op {
229                Op::Lt => primop::op_lt(args[0].clone(), args[1].clone(), solver)?,
230                Op::Gt => primop::op_gt(args[0].clone(), args[1].clone(), solver)?,
231                Op::Lteq => primop::op_lteq(args[0].clone(), args[1].clone(), solver)?,
232                Op::Gteq => primop::op_gteq(args[0].clone(), args[1].clone(), solver)?,
233                Op::Eq => primop::op_eq(args[0].clone(), args[1].clone(), solver)?,
234                Op::Neq => primop::op_neq(args[0].clone(), args[1].clone(), solver)?,
235                Op::Add => primop::op_add(args[0].clone(), args[1].clone(), solver)?,
236                Op::Sub => primop::op_sub(args[0].clone(), args[1].clone(), solver)?,
237                Op::Bvnot => primop::not_bits(args[0].clone(), solver)?,
238                Op::Bvor => primop::or_bits(args[0].clone(), args[1].clone(), solver)?,
239                Op::Bvxor => primop::xor_bits(args[0].clone(), args[1].clone(), solver)?,
240                Op::Bvand => primop::and_bits(args[0].clone(), args[1].clone(), solver)?,
241                Op::Bvadd => primop::add_bits(args[0].clone(), args[1].clone(), solver)?,
242                Op::Bvsub => primop::sub_bits(args[0].clone(), args[1].clone(), solver)?,
243                Op::Bvaccess => primop::vector_access(args[0].clone(), args[1].clone(), solver)?,
244                Op::Concat => primop::append(args[0].clone(), args[1].clone(), solver)?,
245                Op::Not => primop::not_bool(args[0].clone(), solver)?,
246                Op::And => primop::and_bool(args[0].clone(), args[1].clone(), solver)?,
247                Op::Or => primop::or_bool(args[0].clone(), args[1].clone(), solver)?,
248                Op::Slice(len) => primop::op_slice(args[0].clone(), args[1].clone(), *len, solver)?,
249                Op::SetSlice => primop::op_set_slice(args[0].clone(), args[1].clone(), args[2].clone(), solver)?,
250                Op::Unsigned(_) => primop::op_unsigned(args[0].clone(), solver)?,
251                Op::Signed(_) => primop::op_signed(args[0].clone(), solver)?,
252                Op::Head => primop::op_head(args[0].clone(), solver)?,
253                Op::Tail => primop::op_tail(args[0].clone(), solver)?,
254                Op::ZeroExtend(len) => primop::op_zero_extend(args[0].clone(), *len, solver)?,
255            }
256        }
257
258        Kind(ctor_a, exp) => {
259            let v = eval_exp(exp, local_state, shared_state, solver)?;
260            match v {
261                Val::Ctor(ctor_b, _) => Val::Bool(*ctor_a != ctor_b),
262                _ => return Err(ExecError::Type(format!("Kind check on non-constructor {:?}", &v))),
263            }
264        }
265
266        Unwrap(ctor_a, exp) => {
267            let v = eval_exp(exp, local_state, shared_state, solver)?;
268            match v {
269                Val::Ctor(ctor_b, v) => {
270                    if *ctor_a == ctor_b {
271                        *v
272                    } else {
273                        return Err(ExecError::Type(format!("Constructors did not match in unwrap {:?}", &v)));
274                    }
275                }
276                _ => return Err(ExecError::Type(format!("Tried to unwrap non-constructor {:?}", &v))),
277            }
278        }
279
280        Field(exp, field) => {
281            accessor.push(Accessor::Field(*field));
282            if let Val::Struct(struct_value) = eval_exp_with_accessor(exp, local_state, shared_state, solver, accessor)?
283            {
284                match struct_value.get(field) {
285                    Some(field_value) => field_value.clone(),
286                    None => panic!("No field {:?}", shared_state.symtab.to_str(*field)),
287                }
288            } else {
289                panic!("Struct expression did not evaluate to a struct")
290            }
291        }
292
293        Ref(reg) => Val::Ref(*reg),
294
295        _ => panic!("Could not evaluate expression {:?}", exp),
296    })
297}
298
299fn eval_exp<'ir, B: BV>(
300    exp: &Exp<Name>,
301    local_state: &mut LocalState<'ir, B>,
302    shared_state: &SharedState<'ir, B>,
303    solver: &mut Solver<B>,
304) -> Result<Val<B>, ExecError> {
305    eval_exp_with_accessor(exp, local_state, shared_state, solver, &mut Vec::new())
306}
307
308fn assign_with_accessor<'ir, B: BV>(
309    loc: &Loc<Name>,
310    v: Val<B>,
311    local_state: &mut LocalState<'ir, B>,
312    shared_state: &SharedState<'ir, B>,
313    solver: &mut Solver<B>,
314    accessor: &mut Vec<Accessor>,
315) -> Result<(), ExecError> {
316    match loc {
317        Loc::Id(id) => {
318            if local_state.vars.contains_key(id) || *id == RETURN {
319                local_state.vars.insert(*id, UVal::Init(v));
320            } else if local_state.lets.contains_key(id) {
321                local_state.lets.insert(*id, UVal::Init(v));
322            } else {
323                let symbol = zencode::decode(shared_state.symtab.to_str(*id));
324                // HACK: Don't store the entire TLB in the trace
325                if symbol != "_TLB" {
326                    solver.add_event(Event::WriteReg(*id, accessor.to_vec(), v.clone()))
327                }
328                local_state.regs.insert(*id, UVal::Init(v));
329            }
330        }
331
332        Loc::Field(loc, field) => {
333            let mut accessor = Vec::new();
334            accessor.push(Accessor::Field(*field));
335            if let Val::Struct(field_values) =
336                get_loc_and_initialize(loc, local_state, shared_state, solver, &mut accessor)?
337            {
338                // As a sanity test, check that the field exists.
339                match field_values.get(field) {
340                    Some(_) => {
341                        let mut field_values = field_values.clone();
342                        field_values.insert(*field, v);
343                        assign_with_accessor(
344                            loc,
345                            Val::Struct(field_values),
346                            local_state,
347                            shared_state,
348                            solver,
349                            &mut accessor,
350                        )?;
351                    }
352                    None => panic!("Invalid field assignment"),
353                }
354            } else {
355                panic!(
356                    "Cannot assign struct to non-struct {:?}.{:?} ({:?})",
357                    loc,
358                    field,
359                    get_loc_and_initialize(loc, local_state, shared_state, solver, &mut accessor)
360                )
361            }
362        }
363
364        Loc::Addr(loc) => {
365            if let Val::Ref(reg) = get_loc_and_initialize(loc, local_state, shared_state, solver, accessor)? {
366                assign_with_accessor(&Loc::Id(reg), v, local_state, shared_state, solver, accessor)?
367            } else {
368                panic!("Cannot get address of non-reference {:?}", loc)
369            }
370        }
371    };
372    Ok(())
373}
374
375fn assign<'ir, B: BV>(
376    tid: usize,
377    loc: &Loc<Name>,
378    v: Val<B>,
379    local_state: &mut LocalState<'ir, B>,
380    shared_state: &SharedState<'ir, B>,
381    solver: &mut Solver<B>,
382) -> Result<(), ExecError> {
383    let id = loc.id();
384    if shared_state.probes.contains(&id) {
385        let mut symbol = String::from(shared_state.symtab.to_str(id));
386        if symbol.starts_with('z') {
387            symbol = zencode::decode(&symbol);
388        }
389        log_from!(tid, log::PROBE, &format!("Assigning {}[{:?}] <- {:?}", symbol, id, v))
390    }
391
392    assign_with_accessor(loc, v, local_state, shared_state, solver, &mut Vec::new())
393}
394
395/// The callstack is implemented as a closure that restores the
396/// caller's stack frame. It additionally takes the shared state as
397/// input also to avoid ownership issues when creating the closure.
398type Stack<'ir, B> = Option<
399    Arc<
400        dyn 'ir
401            + Send
402            + Sync
403            + Fn(Val<B>, &mut LocalFrame<'ir, B>, &SharedState<'ir, B>, &mut Solver<B>) -> Result<(), ExecError>,
404    >,
405>;
406
407pub type Backtrace = Vec<(Name, usize)>;
408
409/// A `Frame` is an immutable snapshot of the program state while it
410/// is being symbolically executed.
411#[derive(Clone)]
412pub struct Frame<'ir, B> {
413    function_name: Name,
414    pc: usize,
415    forks: u32,
416    backjumps: u32,
417    local_state: Arc<LocalState<'ir, B>>,
418    memory: Arc<Memory<B>>,
419    instrs: &'ir [Instr<Name, B>],
420    stack_vars: Arc<Vec<Bindings<'ir, B>>>,
421    stack_call: Stack<'ir, B>,
422    backtrace: Arc<Backtrace>,
423}
424
425/// A `LocalFrame` is a mutable frame which is used by a currently
426/// executing thread. It is turned into an immutable `Frame` when the
427/// control flow forks on a choice, which can be shared by threads.
428pub struct LocalFrame<'ir, B> {
429    function_name: Name,
430    pc: usize,
431    forks: u32,
432    backjumps: u32,
433    local_state: LocalState<'ir, B>,
434    memory: Memory<B>,
435    instrs: &'ir [Instr<Name, B>],
436    stack_vars: Vec<Bindings<'ir, B>>,
437    stack_call: Stack<'ir, B>,
438    backtrace: Backtrace,
439}
440
441pub fn unfreeze_frame<'ir, B: BV>(frame: &Frame<'ir, B>) -> LocalFrame<'ir, B> {
442    LocalFrame {
443        function_name: frame.function_name,
444        pc: frame.pc,
445        forks: frame.forks,
446        backjumps: frame.backjumps,
447        local_state: (*frame.local_state).clone(),
448        memory: (*frame.memory).clone(),
449        instrs: frame.instrs,
450        stack_vars: (*frame.stack_vars).clone(),
451        stack_call: frame.stack_call.clone(),
452        backtrace: (*frame.backtrace).clone(),
453    }
454}
455
456pub fn freeze_frame<'ir, B: BV>(frame: &LocalFrame<'ir, B>) -> Frame<'ir, B> {
457    Frame {
458        function_name: frame.function_name,
459        pc: frame.pc,
460        forks: frame.forks,
461        backjumps: frame.backjumps,
462        local_state: Arc::new(frame.local_state.clone()),
463        memory: Arc::new(frame.memory.clone()),
464        instrs: frame.instrs,
465        stack_vars: Arc::new(frame.stack_vars.clone()),
466        stack_call: frame.stack_call.clone(),
467        backtrace: Arc::new(frame.backtrace.clone()),
468    }
469}
470
471impl<'ir, B: BV> LocalFrame<'ir, B> {
472    pub fn vars_mut(&mut self) -> &mut Bindings<'ir, B> {
473        &mut self.local_state.vars
474    }
475
476    pub fn vars(&self) -> &Bindings<'ir, B> {
477        &self.local_state.vars
478    }
479
480    pub fn regs_mut(&mut self) -> &mut Bindings<'ir, B> {
481        &mut self.local_state.regs
482    }
483
484    pub fn regs(&self) -> &Bindings<'ir, B> {
485        &self.local_state.regs
486    }
487
488    pub fn add_regs(&mut self, regs: &Bindings<'ir, B>) -> &mut Self {
489        for (k, v) in regs.iter() {
490            self.local_state.regs.insert(*k, v.clone());
491        }
492        self
493    }
494
495    pub fn lets_mut(&mut self) -> &mut Bindings<'ir, B> {
496        &mut self.local_state.lets
497    }
498
499    pub fn lets(&self) -> &Bindings<'ir, B> {
500        &self.local_state.lets
501    }
502
503    pub fn add_lets(&mut self, lets: &Bindings<'ir, B>) -> &mut Self {
504        for (k, v) in lets.iter() {
505            self.local_state.lets.insert(*k, v.clone());
506        }
507        self
508    }
509
510    pub fn get_exception(&self) -> Option<(&Val<B>, &str)> {
511        if let Some(UVal::Init(Val::Bool(true))) = self.lets().get(&HAVE_EXCEPTION) {
512            if let Some(UVal::Init(val)) = self.lets().get(&CURRENT_EXCEPTION) {
513                let loc = match self.lets().get(&THROW_LOCATION) {
514                    Some(UVal::Init(Val::String(s))) => s,
515                    Some(UVal::Init(_)) => "location has wrong type",
516                    _ => "missing location",
517                };
518                Some((val, loc))
519            } else {
520                None
521            }
522        } else {
523            None
524        }
525    }
526
527    pub fn memory(&self) -> &Memory<B> {
528        &self.memory
529    }
530
531    pub fn memory_mut(&mut self) -> &mut Memory<B> {
532        &mut self.memory
533    }
534
535    pub fn set_memory(&mut self, memory: Memory<B>) -> &mut Self {
536        self.memory = memory;
537        self
538    }
539
540    pub fn new(
541        name: Name,
542        args: &[(Name, &'ir Ty<Name>)],
543        vals: Option<&[Val<B>]>,
544        instrs: &'ir [Instr<Name, B>],
545    ) -> Self {
546        let mut vars = HashMap::new();
547        match vals {
548            Some(vals) => {
549                for ((id, _), val) in args.iter().zip(vals) {
550                    vars.insert(*id, UVal::Init(val.clone()));
551                }
552            }
553            None => {
554                for (id, ty) in args {
555                    vars.insert(*id, UVal::Uninit(ty));
556                }
557            }
558        }
559
560        let mut lets = HashMap::new();
561        lets.insert(HAVE_EXCEPTION, UVal::Init(Val::Bool(false)));
562        lets.insert(CURRENT_EXCEPTION, UVal::Uninit(&Ty::Union(SAIL_EXCEPTION)));
563        lets.insert(THROW_LOCATION, UVal::Uninit(&Ty::String));
564        lets.insert(NULL, UVal::Init(Val::List(Vec::new())));
565
566        let regs = HashMap::new();
567
568        LocalFrame {
569            function_name: name,
570            pc: 0,
571            forks: 0,
572            backjumps: 0,
573            local_state: LocalState { vars, regs, lets },
574            memory: Memory::new(),
575            instrs,
576            stack_vars: Vec::new(),
577            stack_call: None,
578            backtrace: Vec::new(),
579        }
580    }
581
582    pub fn new_call(
583        &self,
584        name: Name,
585        args: &[(Name, &'ir Ty<Name>)],
586        vals: Option<&[Val<B>]>,
587        instrs: &'ir [Instr<Name, B>],
588    ) -> Self {
589        let mut new_frame = LocalFrame::new(name, args, vals, instrs);
590        new_frame.forks = self.forks;
591        new_frame.local_state.regs = self.local_state.regs.clone();
592        new_frame.local_state.lets = self.local_state.lets.clone();
593        new_frame.memory = self.memory.clone();
594        new_frame
595    }
596
597    pub fn task_with_checkpoint<'task>(
598        &self,
599        task_id: usize,
600        state: &'task TaskState<B>,
601        checkpoint: Checkpoint<B>,
602    ) -> Task<'ir, 'task, B> {
603        Task { id: task_id, frame: freeze_frame(&self), checkpoint, fork_cond: None, state, stop_functions: None }
604    }
605
606    pub fn task<'task>(&self, task_id: usize, state: &'task TaskState<B>) -> Task<'ir, 'task, B> {
607        self.task_with_checkpoint(task_id, state, Checkpoint::new())
608    }
609}
610
611fn push_call_stack<'ir, B: BV>(frame: &mut LocalFrame<'ir, B>) {
612    let mut vars = Box::new(HashMap::new());
613    mem::swap(&mut *vars, frame.vars_mut());
614    frame.stack_vars.push(*vars)
615}
616
617fn pop_call_stack<'ir, B: BV>(frame: &mut LocalFrame<'ir, B>) {
618    if let Some(mut vars) = frame.stack_vars.pop() {
619        mem::swap(&mut vars, frame.vars_mut())
620    }
621}
622
623#[derive(Copy, Clone, Debug)]
624struct Timeout {
625    start_time: Instant,
626    duration: Option<Duration>,
627}
628
629impl Timeout {
630    fn unlimited() -> Self {
631        Timeout { start_time: Instant::now(), duration: None }
632    }
633
634    fn timed_out(&self) -> bool {
635        self.duration.is_some() && self.start_time.elapsed() > self.duration.unwrap()
636    }
637}
638
639fn run<'ir, 'task, B: BV>(
640    tid: usize,
641    task_id: usize,
642    timeout: Timeout,
643    stop_functions: Option<&'task HashSet<Name>>,
644    queue: &Worker<Task<'ir, 'task, B>>,
645    frame: &Frame<'ir, B>,
646    task_state: &'task TaskState<B>,
647    shared_state: &SharedState<'ir, B>,
648    solver: &mut Solver<B>,
649) -> Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)> {
650    let mut frame = unfreeze_frame(frame);
651    match run_loop(tid, task_id, timeout, stop_functions, queue, &mut frame, task_state, shared_state, solver) {
652        Ok(v) => Ok((v, frame)),
653        Err(err) => {
654            frame.backtrace.push((frame.function_name, frame.pc));
655            Err((err, frame.backtrace))
656        }
657    }
658}
659
660fn run_loop<'ir, 'task, B: BV>(
661    tid: usize,
662    task_id: usize,
663    timeout: Timeout,
664    stop_functions: Option<&'task HashSet<Name>>,
665    queue: &Worker<Task<'ir, 'task, B>>,
666    frame: &mut LocalFrame<'ir, B>,
667    task_state: &'task TaskState<B>,
668    shared_state: &SharedState<'ir, B>,
669    solver: &mut Solver<B>,
670) -> Result<Val<B>, ExecError> {
671    loop {
672        if frame.pc >= frame.instrs.len() {
673            // Currently this happens when evaluating letbindings.
674            log_from!(tid, log::VERBOSE, "Fell from end of instruction list");
675            return Ok(Val::Unit);
676        }
677
678        if timeout.timed_out() {
679            return Err(ExecError::Timeout);
680        }
681
682        match &frame.instrs[frame.pc] {
683            Instr::Decl(v, ty) => {
684                frame.vars_mut().insert(*v, UVal::Uninit(ty));
685                frame.pc += 1;
686            }
687
688            Instr::Init(var, _, exp) => {
689                let value = eval_exp(exp, &mut frame.local_state, shared_state, solver)?;
690                frame.vars_mut().insert(*var, UVal::Init(value));
691                frame.pc += 1;
692            }
693
694            Instr::Jump(exp, target, loc) => {
695                let value = eval_exp(exp, &mut frame.local_state, shared_state, solver)?;
696                match value {
697                    Val::Symbolic(v) => {
698                        use smtlib::Def::*;
699                        use smtlib::Exp::*;
700
701                        let test_true = Var(v);
702                        let test_false = Not(Box::new(Var(v)));
703                        let can_be_true = solver.check_sat_with(&test_true).is_sat()?;
704                        let can_be_false = solver.check_sat_with(&test_false).is_sat()?;
705
706                        if can_be_true && can_be_false {
707                            if_logging!(log::FORK, {
708                                log_from!(tid, log::FORK, loc);
709                                probe::taint_info(log::FORK, v, Some(shared_state), solver)
710                            });
711
712                            // Track which asserts are assocated with each fork in the trace, so we
713                            // can turn a set of traces into a tree later
714                            solver.add_event(Event::Fork(frame.forks, v, loc.clone()));
715                            frame.forks += 1;
716
717                            let point = checkpoint(solver);
718                            let frozen = Frame { pc: frame.pc + 1, ..freeze_frame(&frame) };
719                            queue.push(Task {
720                                id: task_id,
721                                frame: frozen,
722                                checkpoint: point,
723                                fork_cond: Some(Assert(test_false)),
724                                state: task_state,
725                                stop_functions,
726                            });
727                            solver.add(Assert(test_true));
728                            frame.pc = *target
729                        } else if can_be_true {
730                            solver.add(Assert(test_true));
731                            frame.pc = *target
732                        } else if can_be_false {
733                            solver.add(Assert(test_false));
734                            frame.pc += 1
735                        } else {
736                            return Err(ExecError::Dead);
737                        }
738                    }
739                    Val::Bool(jump) => {
740                        if jump {
741                            frame.pc = *target
742                        } else {
743                            frame.pc += 1
744                        }
745                    }
746                    _ => {
747                        return Err(ExecError::Type(format!("Jump on non boolean {:?}", &value)));
748                    }
749                }
750            }
751
752            Instr::Goto(target) => frame.pc = *target,
753
754            Instr::Copy(loc, exp) => {
755                let value = eval_exp(exp, &mut frame.local_state, shared_state, solver)?;
756                assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?;
757                frame.pc += 1;
758            }
759
760            Instr::PrimopUnary(loc, f, arg) => {
761                let arg = eval_exp(arg, &mut frame.local_state, shared_state, solver)?;
762                let value = f(arg, solver)?;
763                assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?;
764                frame.pc += 1;
765            }
766
767            Instr::PrimopBinary(loc, f, arg1, arg2) => {
768                let arg1 = eval_exp(arg1, &mut frame.local_state, shared_state, solver)?;
769                let arg2 = eval_exp(arg2, &mut frame.local_state, shared_state, solver)?;
770                let value = f(arg1, arg2, solver)?;
771                assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?;
772                frame.pc += 1;
773            }
774
775            Instr::PrimopVariadic(loc, f, args) => {
776                let args = args
777                    .iter()
778                    .map(|arg| eval_exp(arg, &mut frame.local_state, shared_state, solver))
779                    .collect::<Result<_, _>>()?;
780                let value = f(args, solver, frame)?;
781                assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?;
782                frame.pc += 1;
783            }
784
785            Instr::Call(loc, _, f, args) => {
786                if let Some(s) = stop_functions {
787                    if s.contains(f) {
788                        let symbol = zencode::decode(shared_state.symtab.to_str(*f));
789                        return Err(ExecError::Stopped(symbol));
790                    }
791                }
792
793                match shared_state.functions.get(&f) {
794                    None => {
795                        if *f == INTERNAL_VECTOR_INIT && args.len() == 1 {
796                            let arg = eval_exp(&args[0], &mut frame.local_state, shared_state, solver)?;
797                            match loc {
798                                Loc::Id(v) => match (arg, frame.vars().get(v)) {
799                                    (Val::I64(len), Some(UVal::Uninit(Ty::Vector(_)))) => assign(
800                                        tid,
801                                        loc,
802                                        Val::Vector(vec![Val::Poison; len as usize]),
803                                        &mut frame.local_state,
804                                        shared_state,
805                                        solver,
806                                    )?,
807                                    _ => return Err(ExecError::Type(format!("internal_vector_init {:?}", &loc))),
808                                },
809                                _ => return Err(ExecError::Type(format!("internal_vector_init {:?}", &loc))),
810                            };
811                            frame.pc += 1
812                        } else if *f == INTERNAL_VECTOR_UPDATE && args.len() == 3 {
813                            let args = args
814                                .iter()
815                                .map(|arg| eval_exp(arg, &mut frame.local_state, shared_state, solver))
816                                .collect::<Result<Vec<Val<B>>, _>>()?;
817                            let vector = primop::vector_update(args, solver, frame)?;
818                            assign(tid, loc, vector, &mut frame.local_state, shared_state, solver)?;
819                            frame.pc += 1
820                        } else if *f == SAIL_EXIT {
821                            return Err(ExecError::Exit);
822                        } else if *f == RESET_REGISTERS {
823                            for (loc, reset) in &shared_state.reset_registers {
824                                if !task_state.reset_registers.contains_key(loc) {
825                                    let value = reset(&frame.memory, solver)?;
826                                    assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?
827                                }
828                            }
829                            for (loc, reset) in &task_state.reset_registers {
830                                let value = reset(&frame.memory, solver)?;
831                                assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?
832                            }
833                            frame.pc += 1
834                        } else if *f == REG_DEREF && args.len() == 1 {
835                            if let Val::Ref(reg) = eval_exp(&args[0], &mut frame.local_state, shared_state, solver)? {
836                                match get_and_initialize(reg, frame.regs_mut(), shared_state, solver)? {
837                                    Some(value) => {
838                                        solver.add_event(Event::ReadReg(reg, Vec::new(), value.clone()));
839                                        assign(tid, loc, value, &mut frame.local_state, shared_state, solver)?
840                                    }
841                                    None => return Err(ExecError::Type(format!("reg_deref {:?}", &reg))),
842                                }
843                            } else {
844                                return Err(ExecError::Type(format!("reg_deref (not a register) {:?}", &f)));
845                            };
846                            frame.pc += 1
847                        } else if shared_state.union_ctors.contains(f) {
848                            assert!(args.len() == 1);
849                            let arg = eval_exp(&args[0], &mut frame.local_state, shared_state, solver)?;
850                            assign(
851                                tid,
852                                loc,
853                                Val::Ctor(*f, Box::new(arg)),
854                                &mut frame.local_state,
855                                shared_state,
856                                solver,
857                            )?;
858                            frame.pc += 1
859                        } else {
860                            let symbol = zencode::decode(shared_state.symtab.to_str(*f));
861                            panic!("Attempted to call non-existent function {} ({:?})", symbol, *f)
862                        }
863                    }
864
865                    Some((params, _, instrs)) => {
866                        let mut args = args
867                            .iter()
868                            .map(|arg| eval_exp(arg, &mut frame.local_state, shared_state, solver))
869                            .collect::<Result<Vec<Val<B>>, _>>()?;
870
871                        if shared_state.probes.contains(f) {
872                            log_from!(tid, log::PROBE, probe::call_info(*f, &args, &shared_state.symtab));
873                            probe::args_info(tid, &args, shared_state, solver)
874                        }
875
876                        let caller_pc = frame.pc;
877                        let caller_instrs = frame.instrs;
878                        let caller_stack_call = frame.stack_call.clone();
879                        push_call_stack(frame);
880                        frame.backtrace.push((frame.function_name, caller_pc));
881                        frame.function_name = *f;
882
883                        // Set up a closure to restore our state when
884                        // the function we call returns
885                        frame.stack_call = Some(Arc::new(move |ret, frame, shared_state, solver| {
886                            pop_call_stack(frame);
887                            // could avoid putting caller_pc into the stack?
888                            if let Some((name, _)) = frame.backtrace.pop() {
889                                frame.function_name = name;
890                            }
891                            frame.pc = caller_pc + 1;
892                            frame.instrs = caller_instrs;
893                            frame.stack_call = caller_stack_call.clone();
894                            assign(tid, &loc.clone(), ret, &mut frame.local_state, shared_state, solver)
895                        }));
896
897                        for (i, arg) in args.drain(..).enumerate() {
898                            frame.vars_mut().insert(params[i].0, UVal::Init(arg));
899                        }
900                        frame.pc = 0;
901                        frame.instrs = instrs;
902                    }
903                }
904            }
905
906            Instr::End => match frame.vars().get(&RETURN) {
907                None => panic!("Reached end without assigning to return"),
908                Some(value) => {
909                    let value = match value {
910                        UVal::Uninit(ty) => symbolic(ty, shared_state, solver)?,
911                        UVal::Init(value) => value.clone(),
912                    };
913                    if shared_state.probes.contains(&frame.function_name) {
914                        let symbol = zencode::decode(shared_state.symtab.to_str(frame.function_name));
915                        log_from!(
916                            tid,
917                            log::PROBE,
918                            &format!("Returning {} = {}", symbol, value.to_string(&shared_state.symtab))
919                        );
920                        probe::args_info(tid, std::slice::from_ref(&value), shared_state, solver)
921                    }
922                    let caller = match &frame.stack_call {
923                        None => return Ok(value),
924                        Some(caller) => Arc::clone(caller),
925                    };
926                    (*caller)(value, frame, shared_state, solver)?
927                }
928            },
929
930            // The idea beind the Monomorphize operation is it takes a
931            // bitvector identifier, and if that identifer has a
932            // symbolic value, then it uses the SMT solver to find all
933            // the possible values for that bitvector and case splits
934            // (i.e. forks) on them. This allows us to guarantee that
935            // certain bitvectors are non-symbolic, at the cost of
936            // increasing the number of paths.
937            Instr::Monomorphize(id) => {
938                let val = get_id_and_initialize(*id, &mut frame.local_state, shared_state, solver, &mut Vec::new())?;
939                if let Val::Symbolic(v) = val {
940                    use smtlib::bits64;
941                    use smtlib::Def::*;
942                    use smtlib::Exp::*;
943                    use smtlib::Ty::*;
944
945                    let point = checkpoint(solver);
946
947                    let len = solver.length(v).ok_or_else(|| ExecError::Type(format!("_monomorphize {:?}", &v)))?;
948
949                    // For the variable v to appear in the model, there must be some assertion that references it
950                    let sym = solver.declare_const(BitVec(len));
951                    solver.assert_eq(Var(v), Var(sym));
952
953                    if solver.check_sat().is_unsat()? {
954                        return Err(ExecError::Dead);
955                    }
956
957                    let (result, size) = {
958                        let mut model = Model::new(solver);
959                        log_from!(tid, log::FORK, format!("Model: {:?}", model));
960                        match model.get_var(v) {
961                            Ok(Some(Bits64(bv))) => (bv.lower_u64(), bv.len()),
962                            // __monomorphize should have a 'n <= 64 constraint in Sail
963                            Ok(Some(other)) => return Err(ExecError::Type(format!("__monomorphize {:?}", &other))),
964                            Ok(None) => return Err(ExecError::Z3Error(format!("No value for variable v{}", v))),
965                            Err(error) => return Err(error),
966                        }
967                    };
968
969                    let loc = format!("Fork @ monomorphizing v{}", v);
970                    log_from!(tid, log::FORK, loc);
971                    solver.add_event(Event::Fork(frame.forks, v, loc.clone()));
972                    frame.forks += 1;
973
974                    queue.push(Task {
975                        id: task_id,
976                        frame: freeze_frame(&frame),
977                        checkpoint: point,
978                        fork_cond: Some(Assert(Neq(Box::new(Var(v)), Box::new(bits64(result, size))))),
979                        state: task_state,
980                        stop_functions,
981                    });
982
983                    solver.assert_eq(Var(v), bits64(result, size));
984
985                    assign(
986                        tid,
987                        &Loc::Id(*id),
988                        Val::Bits(B::new(result, size)),
989                        &mut frame.local_state,
990                        shared_state,
991                        solver,
992                    )?;
993                }
994                frame.pc += 1
995            }
996
997            // Arbitrary means return any value. It is used in the
998            // Sail->C compilation for exceptional control flow paths
999            // to avoid compiler warnings (which would also be UB in
1000            // C++ compilers). The value should never be used, so we
1001            // return Val::Poison here.
1002            Instr::Arbitrary => {
1003                if shared_state.probes.contains(&frame.function_name) {
1004                    let symbol = zencode::decode(shared_state.symtab.to_str(frame.function_name));
1005                    log_from!(
1006                        tid,
1007                        log::PROBE,
1008                        &format!("Returning via arbitrary {}[{:?}] = poison", symbol, frame.function_name)
1009                    );
1010                }
1011                let caller = match &frame.stack_call {
1012                    None => return Ok(Val::Poison),
1013                    Some(caller) => Arc::clone(caller),
1014                };
1015                (*caller)(Val::Poison, frame, shared_state, solver)?
1016            }
1017
1018            Instr::Failure => return Err(ExecError::MatchFailure),
1019        }
1020    }
1021}
1022
1023/// A collector is run on the result of each path found via symbolic execution through the code. It
1024/// takes the result of the execution, which is either a combination of the return value and local
1025/// state at the end of the execution or an error, as well as the shared state and the SMT solver
1026/// state associated with that execution. It build a final result for all the executions by
1027/// collecting the results into a type R.
1028pub type Collector<'ir, B, R> = dyn 'ir
1029    + Sync
1030    + Fn(usize, usize, Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)>, &SharedState<'ir, B>, Solver<B>, &R);
1031
1032pub struct TaskState<B> {
1033    reset_registers: HashMap<Loc<Name>, Reset<B>>,
1034}
1035
1036impl<B> TaskState<B> {
1037    pub fn new() -> Self {
1038        TaskState { reset_registers: HashMap::new() }
1039    }
1040
1041    pub fn with_reset_registers(reset_registers: HashMap<Loc<Name>, Reset<B>>) -> Self {
1042        TaskState { reset_registers }
1043    }
1044}
1045
1046impl<B> Default for TaskState<B> {
1047    fn default() -> Self {
1048        Self::new()
1049    }
1050}
1051
1052/// A `Task` is a suspended point in the symbolic execution of a
1053/// program. It consists of a frame, which is a snapshot of the
1054/// program variables, a checkpoint which allows us to reconstruct the
1055/// SMT solver state, and finally an option SMTLIB definiton which is
1056/// added to the solver state when the task is resumed.
1057pub struct Task<'ir, 'task, B> {
1058    id: usize,
1059    frame: Frame<'ir, B>,
1060    checkpoint: Checkpoint<B>,
1061    fork_cond: Option<smtlib::Def>,
1062    state: &'task TaskState<B>,
1063    stop_functions: Option<&'task HashSet<Name>>,
1064}
1065
1066impl<'ir, 'task, B> Task<'ir, 'task, B> {
1067    pub fn set_stop_functions(&mut self, new_fns: &'task HashSet<Name>) {
1068        self.stop_functions = Some(new_fns);
1069    }
1070}
1071
1072/// Start symbolically executing a Task using just the current thread, collecting the results using
1073/// the given collector.
1074pub fn start_single<'ir, 'task, B: BV, R>(
1075    task: Task<'ir, 'task, B>,
1076    shared_state: &SharedState<'ir, B>,
1077    collected: &R,
1078    collector: &Collector<'ir, B, R>,
1079) {
1080    let queue = Worker::new_lifo();
1081    queue.push(task);
1082    while let Some(task) = queue.pop() {
1083        let mut cfg = Config::new();
1084        cfg.set_param_value("model", "true");
1085        let ctx = Context::new(cfg);
1086        let mut solver = Solver::from_checkpoint(&ctx, task.checkpoint);
1087        if let Some(def) = task.fork_cond {
1088            solver.add(def)
1089        };
1090        let result = run(
1091            0,
1092            task.id,
1093            Timeout::unlimited(),
1094            task.stop_functions,
1095            &queue,
1096            &task.frame,
1097            &task.state,
1098            shared_state,
1099            &mut solver,
1100        );
1101        collector(0, task.id, result, shared_state, solver, collected)
1102    }
1103}
1104
1105fn find_task<T>(local: &Worker<T>, global: &Injector<T>, stealers: &RwLock<Vec<Stealer<T>>>) -> Option<T> {
1106    let stealers = stealers.read().unwrap();
1107    local.pop().or_else(|| {
1108        std::iter::repeat_with(|| {
1109            let stolen: Steal<T> = stealers.iter().map(|s| s.steal()).collect();
1110            stolen.or_else(|| global.steal_batch_and_pop(local))
1111        })
1112        .find(|s| !s.is_retry())
1113        .and_then(|s| s.success())
1114    })
1115}
1116
1117fn do_work<'ir, 'task, B: BV, R>(
1118    tid: usize,
1119    timeout: Timeout,
1120    queue: &Worker<Task<'ir, 'task, B>>,
1121    task: Task<'ir, 'task, B>,
1122    shared_state: &SharedState<'ir, B>,
1123    collected: &R,
1124    collector: &Collector<'ir, B, R>,
1125) {
1126    let cfg = Config::new();
1127    let ctx = Context::new(cfg);
1128    let mut solver = Solver::from_checkpoint(&ctx, task.checkpoint);
1129    if let Some(def) = task.fork_cond {
1130        solver.add(def)
1131    };
1132    let result =
1133        run(tid, task.id, timeout, task.stop_functions, queue, &task.frame, &task.state, shared_state, &mut solver);
1134    collector(tid, task.id, result, shared_state, solver, collected)
1135}
1136
1137enum Response {
1138    Poke,
1139    Kill,
1140}
1141
1142#[derive(Clone)]
1143enum Activity {
1144    Idle(usize, Sender<Response>),
1145    Busy(usize),
1146}
1147
1148/// Start symbolically executing a Task across `num_threads` new threads, collecting the results
1149/// using the given collector.
1150pub fn start_multi<'ir, 'task, B: BV, R>(
1151    num_threads: usize,
1152    timeout: Option<u64>,
1153    tasks: Vec<Task<'ir, 'task, B>>,
1154    shared_state: &SharedState<'ir, B>,
1155    collected: Arc<R>,
1156    collector: &Collector<'ir, B, R>,
1157) where
1158    R: Send + Sync,
1159{
1160    let timeout = Timeout { start_time: Instant::now(), duration: timeout.map(Duration::from_secs) };
1161
1162    let (tx, rx): (Sender<Activity>, Receiver<Activity>) = mpsc::channel();
1163    let global: Arc<Injector<Task<B>>> = Arc::new(Injector::<Task<B>>::new());
1164    let stealers: Arc<RwLock<Vec<Stealer<Task<B>>>>> = Arc::new(RwLock::new(Vec::new()));
1165
1166    for task in tasks {
1167        global.push(task);
1168    }
1169
1170    thread::scope(|scope| {
1171        for tid in 0..num_threads {
1172            // When a worker is idle, it reports that to the main orchestrating thread, which can
1173            // then 'poke' it to wake it up via a channel, which will cause the worker to try to
1174            // steal some work, or the main thread can kill the worker.
1175            let (poke_tx, poke_rx): (Sender<Response>, Receiver<Response>) = mpsc::channel();
1176            let thread_tx = tx.clone();
1177            let global = global.clone();
1178            let stealers = stealers.clone();
1179            let collected = collected.clone();
1180
1181            scope.spawn(move |_| {
1182                let q = Worker::new_lifo();
1183                {
1184                    let mut stealers = stealers.write().unwrap();
1185                    stealers.push(q.stealer());
1186                }
1187                loop {
1188                    if let Some(task) = find_task(&q, &global, &stealers) {
1189                        thread_tx.send(Activity::Busy(tid)).unwrap();
1190                        do_work(tid, timeout, &q, task, &shared_state, collected.as_ref(), collector);
1191                        while let Some(task) = find_task(&q, &global, &stealers) {
1192                            do_work(tid, timeout, &q, task, &shared_state, collected.as_ref(), collector)
1193                        }
1194                    };
1195                    thread_tx.send(Activity::Idle(tid, poke_tx.clone())).unwrap();
1196                    match poke_rx.recv().unwrap() {
1197                        Response::Poke => (),
1198                        Response::Kill => break,
1199                    }
1200                }
1201            });
1202        }
1203
1204        // Figuring out when to exit is a little complex. We start with only a few threads able to
1205        // work because we haven't actually explored any of the state space, so all the other
1206        // workers start idle and repeatedly try to steal work. There may be points when workers
1207        // have no work, but we want them to become active again if more work becomes available. We
1208        // therefore want to exit only when 1) all threads are idle, 2) we've told all the threads
1209        // to steal some work, and 3) all the threads fail to do so and remain idle.
1210        let mut current_activity = vec![0; num_threads];
1211        let mut last_messages = vec![Activity::Busy(0); num_threads];
1212        loop {
1213            loop {
1214                match rx.try_recv() {
1215                    Ok(Activity::Busy(tid)) => {
1216                        last_messages[tid] = Activity::Busy(tid);
1217                        current_activity[tid] = 0;
1218                    }
1219                    Ok(Activity::Idle(tid, poke)) => {
1220                        last_messages[tid] = Activity::Idle(tid, poke);
1221                        current_activity[tid] += 1;
1222                    }
1223                    Err(_) => break,
1224                }
1225            }
1226            let mut quiescent = true;
1227            for idleness in &current_activity {
1228                if *idleness < 2 {
1229                    quiescent = false
1230                }
1231            }
1232            if quiescent {
1233                for message in &last_messages {
1234                    match message {
1235                        Activity::Idle(_tid, poke) => poke.send(Response::Kill).unwrap(),
1236                        Activity::Busy(tid) => panic!("Found busy thread {} when quiescent", tid),
1237                    }
1238                }
1239                break;
1240            }
1241            for message in &last_messages {
1242                match message {
1243                    Activity::Idle(tid, poke) => {
1244                        poke.send(Response::Poke).unwrap();
1245                        current_activity[*tid] = 1;
1246                    }
1247                    Activity::Busy(_) => (),
1248                }
1249            }
1250            sleep(Duration::from_millis(1))
1251        }
1252    })
1253    .unwrap();
1254}
1255
1256/// This `Collector` is used for boolean Sail functions. It returns
1257/// true via an AtomicBool if all reachable paths through the program
1258/// are unsatisfiable, which implies that the function always returns
1259/// true.
1260pub fn all_unsat_collector<'ir, B: BV>(
1261    tid: usize,
1262    _: usize,
1263    result: Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)>,
1264    shared_state: &SharedState<'ir, B>,
1265    mut solver: Solver<B>,
1266    collected: &AtomicBool,
1267) {
1268    match result {
1269        Ok(value) => match value {
1270            (Val::Symbolic(v), _) => {
1271                use smtlib::Def::*;
1272                use smtlib::Exp::*;
1273                solver.add(Assert(Not(Box::new(Var(v)))));
1274                if solver.check_sat() != SmtResult::Unsat {
1275                    log_from!(tid, log::VERBOSE, "Got sat");
1276                    collected.store(false, Ordering::Release)
1277                } else {
1278                    log_from!(tid, log::VERBOSE, "Got unsat")
1279                }
1280            }
1281            (Val::Bool(true), _) => log_from!(tid, log::VERBOSE, "Got true"),
1282            (Val::Bool(false), _) => {
1283                log_from!(tid, log::VERBOSE, "Got false");
1284                collected.store(false, Ordering::Release)
1285            }
1286            (value, _) => log_from!(tid, log::VERBOSE, &format!("Got value {:?}", value)),
1287        },
1288        Err((err, backtrace)) => match err {
1289            ExecError::Dead => log_from!(tid, log::VERBOSE, "Dead"),
1290            _ => {
1291                if_logging!(log::VERBOSE, {
1292                    log_from!(tid, log::VERBOSE, &format!("Got error, {:?}", err));
1293                    for (f, pc) in backtrace.iter().rev() {
1294                        log_from!(tid, log::VERBOSE, format!("  {} @ {}", shared_state.symtab.to_str(*f), pc));
1295                    }
1296                });
1297                collected.store(false, Ordering::Release)
1298            }
1299        },
1300    }
1301}
1302
1303pub type TraceQueue<B> = SegQueue<Result<(usize, Vec<Event<B>>), String>>;
1304
1305pub type TraceResultQueue<B> = SegQueue<Result<(usize, bool, Vec<Event<B>>), String>>;
1306
1307pub type TraceValueQueue<B> = SegQueue<Result<(usize, Val<B>, Vec<Event<B>>), String>>;
1308
1309pub fn trace_collector<'ir, B: BV>(
1310    _: usize,
1311    task_id: usize,
1312    result: Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)>,
1313    _: &SharedState<'ir, B>,
1314    mut solver: Solver<B>,
1315    collected: &TraceQueue<B>,
1316) {
1317    match result {
1318        Ok(_) | Err((ExecError::Exit, _)) => {
1319            let mut events = solver.trace().to_vec();
1320            collected.push(Ok((task_id, events.drain(..).cloned().collect())))
1321        }
1322        Err((ExecError::Dead, _)) => (),
1323        Err((err, _)) => {
1324            if solver.check_sat() == SmtResult::Sat {
1325                let model = Model::new(&solver);
1326                collected.push(Err(format!("Error {:?}\n{:?}", err, model)))
1327            } else {
1328                collected.push(Err(format!("Error {:?}\nno model", err)))
1329            }
1330        }
1331    }
1332}
1333
1334pub fn trace_value_collector<'ir, B: BV>(
1335    _: usize,
1336    task_id: usize,
1337    result: Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)>,
1338    _: &SharedState<'ir, B>,
1339    mut solver: Solver<B>,
1340    collected: &TraceValueQueue<B>,
1341) {
1342    match result {
1343        Ok((val, _)) => {
1344            let mut events = solver.trace().to_vec();
1345            collected.push(Ok((task_id, val, events.drain(..).cloned().collect())))
1346        }
1347        Err((ExecError::Dead, _)) => (),
1348        Err((err, _)) => {
1349            if solver.check_sat() == SmtResult::Sat {
1350                let model = Model::new(&solver);
1351                collected.push(Err(format!("Error {:?}\n{:?}", err, model)))
1352            } else {
1353                collected.push(Err(format!("Error {:?}\nno model", err)))
1354            }
1355        }
1356    }
1357}
1358
1359pub fn trace_result_collector<'ir, B: BV>(
1360    _: usize,
1361    task_id: usize,
1362    result: Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)>,
1363    _: &SharedState<'ir, B>,
1364    solver: Solver<B>,
1365    collected: &TraceResultQueue<B>,
1366) {
1367    match result {
1368        Ok((Val::Bool(result), _)) => {
1369            let mut events = solver.trace().to_vec();
1370            collected.push(Ok((task_id, result, events.drain(..).cloned().collect())))
1371        }
1372        Ok((val, _)) => collected.push(Err(format!("Unexpected footprint return value: {:?}", val))),
1373        Err((ExecError::Dead, _)) => (),
1374        Err((err, _)) => collected.push(Err(format!("Error {:?}", err))),
1375    }
1376}
1377
1378pub fn footprint_collector<'ir, B: BV>(
1379    _: usize,
1380    task_id: usize,
1381    result: Result<(Val<B>, LocalFrame<'ir, B>), (ExecError, Backtrace)>,
1382    _: &SharedState<'ir, B>,
1383    solver: Solver<B>,
1384    collected: &TraceQueue<B>,
1385) {
1386    match result {
1387        // Footprint function returns true on traces we need to consider as part of the footprint
1388        Ok((Val::Bool(true), _)) => {
1389            let mut events = solver.trace().to_vec();
1390            collected.push(Ok((task_id, events.drain(..).cloned().collect())))
1391        }
1392        // If it returns false or unit, we ignore that trace
1393        Ok((Val::Bool(false), _)) => (),
1394        // Anything else is an error!
1395        Ok((val, _)) => collected.push(Err(format!("Unexpected footprint return value: {:?}", val))),
1396
1397        Err((ExecError::Dead, _)) => (),
1398        Err((err, _)) => collected.push(Err(format!("Error {:?}", err))),
1399    }
1400}