Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tracing #244

Merged
merged 51 commits into from
Oct 31, 2022
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
ab96784
Refactor tracing
MarcelGarus Oct 4, 2022
2bf4012
Display stack traces
MarcelGarus Oct 4, 2022
c7499ba
Make cloning to other heap cheaper
MarcelGarus Oct 4, 2022
475ec6d
Improve stack trace formatting
MarcelGarus Oct 4, 2022
9a25969
Trace needs
MarcelGarus Oct 4, 2022
8087055
Format stack traces spanning multiple fibers
MarcelGarus Oct 5, 2022
add41fc
Move tracer into its own component
MarcelGarus Oct 5, 2022
316fe76
Revert " Move tracer into its own component"
MarcelGarus Oct 5, 2022
61ab412
Add responsibility to VM
MarcelGarus Oct 7, 2022
a2553d8
Fix fault attribution
MarcelGarus Oct 7, 2022
80fde35
Adapt fuzzing to new tracing
MarcelGarus Oct 10, 2022
9de29ee
Update todos
MarcelGarus Oct 13, 2022
8bf3321
Print to stdout using channels
MarcelGarus Oct 13, 2022
1885315
Merge branch 'channels' into tracing
MarcelGarus Oct 13, 2022
7ddf44c
Fix ids
MarcelGarus Oct 13, 2022
6c07c6f
Update fuzzing to responsibilites
MarcelGarus Oct 17, 2022
80f7b7c
Fix responsibilities of curly closures
MarcelGarus Oct 17, 2022
42673b9
Fix language server
MarcelGarus Oct 17, 2022
1b25bbf
Fix some lints
MarcelGarus Oct 17, 2022
f2c848a
Remove logging all stack traces
MarcelGarus Oct 17, 2022
1b51a94
Remove let_else feature flag
MarcelGarus Oct 17, 2022
734154a
Merge branch 'channels' into tracing
MarcelGarus Oct 17, 2022
5686ac0
Fix Clippy lints
MarcelGarus Oct 17, 2022
bd04d47
Merge branch 'channels' into tracing
MarcelGarus Oct 23, 2022
8f2dfff
Merge branch 'main' into tracing
MarcelGarus Oct 29, 2022
925e679
Merge branch 'main' into tracing
MarcelGarus Oct 29, 2022
11669c1
Implement some suggestions
MarcelGarus Oct 29, 2022
1d83364
Merge branch 'tracing' of https://github.com/candy-lang/candy into tr…
MarcelGarus Oct 29, 2022
7ef6394
Apply suggestions from code review
MarcelGarus Oct 29, 2022
46f01ba
Update compiler/src/vm/fiber.rs
MarcelGarus Oct 29, 2022
16d9537
Update compiler/src/vm/fiber.rs
MarcelGarus Oct 29, 2022
a102000
Restructure tracing
MarcelGarus Oct 29, 2022
182e550
Merge branch 'tracing' of https://github.com/candy-lang/candy into tr…
MarcelGarus Oct 29, 2022
4544a72
Fix indentation
MarcelGarus Oct 29, 2022
919608d
Pad stack trace strings to width
MarcelGarus Oct 29, 2022
dfaaa03
Make UseProvider non-mut
MarcelGarus Oct 29, 2022
3295ce8
Add assert in complete_try
MarcelGarus Oct 29, 2022
9e61013
Restructure tracing (again)
MarcelGarus Oct 30, 2022
36bce9c
Improve tracing of finishing fibers
MarcelGarus Oct 30, 2022
6699e3b
Remove unused events
MarcelGarus Oct 30, 2022
ce36027
Fix clippy lints
MarcelGarus Oct 30, 2022
5f4b696
Update compiler/src/vm/tracer/mod.rs
MarcelGarus Oct 30, 2022
8a9b492
Update compiler/src/vm/tracer/mod.rs
MarcelGarus Oct 30, 2022
930b4d0
Update compiler/src/vm/tracer/full.rs
MarcelGarus Oct 30, 2022
939a5e8
Add Cargo workspace
MarcelGarus Oct 31, 2022
8584929
Move tracer methods
MarcelGarus Oct 31, 2022
2724256
Move tracer methods
MarcelGarus Oct 31, 2022
4644a95
Refactor tracer
MarcelGarus Oct 31, 2022
5ba00af
Format main
MarcelGarus Oct 31, 2022
35b5c3b
Update .gitignores and lock files
MarcelGarus Oct 31, 2022
09c9be4
Remove commented out code
MarcelGarus Oct 31, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,19 +137,21 @@ We already have a language server that provides some tooling.
## Short-term TODOs

- fix fault attribution
- rearchitect tracing
- new name?
- add caching while compile-time evaluating code
- tags?
- tags
- pattern matching
- pipe operator
- add CI
- add tests
- add a more lightweight tracer that only tracks stack traces
- text interpolation
- eliminate common subtrees
- inline functions
- optimize: eliminate common subtrees
- optimize: inline functions
- minimize inputs found through fuzzing
- fuzz parser
- remove builtinPrint
- tail call optimization
- optimize: tail call optimization
- parse function declaration with doc comment but no code
- tracing visualization
- distinguish packages from normal modules
Expand Down
10 changes: 10 additions & 0 deletions compiler/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions compiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ notify = "4.0.17"
num-bigint = { version = "0.4.3", features = ["rand"] }
num-integer = { version = "0.1.45", features = ["i128"] }
num-traits = { version = "0.2.15", features = ["i128"] }
pad = "0.1.6"
proptest = "1.0.0"
rand = "0.8.5"
regex = "1.5.5"
Expand Down
12 changes: 12 additions & 0 deletions compiler/src/compiler/hir_to_lir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ impl LoweringContext {
.collect_vec(),
lambda.parameters.len(),
instructions,
!lambda.fuzzable,
);
if lambda.fuzzable {
self.emit_register_fuzzable_closure(id.clone());
Expand All @@ -96,9 +97,11 @@ impl LoweringContext {
}

self.emit_push_from_stack(function.clone());
self.emit_start_responsibility(id.clone());
self.emit_trace_call_starts(id.clone(), arguments.len());
self.emit_call(id.clone(), arguments.len());
self.emit_trace_call_ends();
self.emit_end_responsibility();
}
Expression::Builtin(builtin) => {
self.emit_create_builtin(id.clone(), *builtin);
Expand Down Expand Up @@ -147,11 +150,14 @@ impl LoweringContext {
captured: Vec<StackOffset>,
num_args: usize,
instructions: Vec<Instruction>,
is_curly: bool,
) {
self.emit(Instruction::CreateClosure {
id: id.clone(),
captured,
num_args,
body: instructions,
is_curly,
});
self.stack.push(id);
}
Expand Down Expand Up @@ -184,6 +190,12 @@ impl LoweringContext {
self.emit(Instruction::UseModule { current_module });
self.stack.push(id); // exported definitions
}
fn emit_start_responsibility(&mut self, responsible: hir::Id) {
self.emit(Instruction::StartResponsibility(responsible));
}
fn emit_end_responsibility(&mut self) {
self.emit(Instruction::EndResponsibility);
}
fn emit_needs(&mut self, id: hir::Id) {
self.stack.pop(); // reason
self.stack.pop(); // condition
Expand Down
25 changes: 22 additions & 3 deletions compiler/src/compiler/lir.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::error::CompilerError;
use super::{error::CompilerError, hir::Id};
use crate::{builtin_functions::BuiltinFunction, hir, module::Module};
use itertools::Itertools;
use num_bigint::BigUint;
Expand Down Expand Up @@ -33,9 +33,11 @@ pub enum Instruction {
///
/// a -> a, pointer to closure
CreateClosure {
id: hir::Id,
captured: Vec<StackOffset>,
num_args: usize,
body: Vec<Instruction>,
is_curly: bool,
},

/// Pushes a builtin function.
Expand Down Expand Up @@ -89,6 +91,12 @@ pub enum Instruction {
current_module: Module,
},

/// Contrary to other languages, in Candy it's always clear who's fault it
/// is when a program panics. Each fiber maintains a responsibility stack
/// which notes which call-site is responsible for needs to be fulfilled.
StartResponsibility(Id),
EndResponsibility,

/// Pops a boolean condition and a reason. If the condition is true, it
/// just pushes Nothing. If the condition is false, it panics with the
/// reason.
Expand Down Expand Up @@ -130,13 +138,15 @@ impl Display for Instruction {
write!(f, "createStruct {num_entries}")
}
Instruction::CreateClosure {
id,
captured,
num_args,
body: instructions,
is_curly,
} => {
write!(
f,
"createClosure with {num_args} {} capturing {}",
"createClosure {id} with {num_args} {} capturing {} {}",
if *num_args == 1 {
"argument"
} else {
Expand All @@ -146,7 +156,12 @@ impl Display for Instruction {
"nothing".to_string()
} else {
captured.iter().join(", ")
}
},
if *is_curly {
"(is curly)"
} else {
"(is not curly)"
},
)?;
for instruction in instructions {
let indented = format!("{instruction}")
Expand All @@ -171,6 +186,10 @@ impl Display for Instruction {
Instruction::UseModule { current_module } => {
write!(f, "useModule (currently in {})", current_module)
}
Instruction::StartResponsibility(responsible) => {
write!(f, "responsibility of {responsible} starts")
}
Instruction::EndResponsibility => write!(f, "responsibility ends"),
Instruction::Needs => write!(f, "needs"),
Instruction::RegisterFuzzableClosure(hir_id) => {
write!(f, "registerFuzzableClosure {hir_id}")
Expand Down
51 changes: 26 additions & 25 deletions compiler/src/fuzzer/fuzzer.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
use super::{generator::generate_n_values, utils::did_need_in_closure_cause_panic};
use super::generator::generate_n_values;
use crate::{
compiler::hir,
database::Database,
vm::{
self,
context::{ExecutionController, UseProvider},
tracer::Tracer,
Closure, Heap, Pointer, TearDownResult, Vm,
tracer::{full::FullTracer, Tracer},
Closure, Heap, Packet, Pointer, Vm,
},
};
use itertools::Itertools;
use std::mem;

pub struct Fuzzer {
Expand All @@ -24,15 +24,15 @@ pub enum Status {
// input that triggers the loop.
StillFuzzing {
vm: Vm,
arguments: Vec<Pointer>,
arguments: Vec<Packet>,
tracer: FullTracer,
},
// TODO: In the future, also add a state for trying to simplify the
// arguments.
PanickedForArguments {
heap: Heap,
arguments: Vec<Pointer>,
arguments: Vec<Packet>,
reason: String,
tracer: Tracer,
tracer: FullTracer,
},
}

Expand All @@ -45,12 +45,20 @@ impl Status {

let mut vm_heap = Heap::default();
let closure = closure_heap.clone_single_to_other_heap(&mut vm_heap, closure);
let arguments = generate_n_values(&mut vm_heap, num_args);
let arguments = generate_n_values(num_args);
let argument_addresses = arguments
.iter()
.map(|arg| arg.clone_to_other_heap(&mut vm_heap))
.collect_vec();

let mut vm = Vm::new();
vm.set_up_for_running_closure(vm_heap, closure, &arguments);
vm.set_up_for_running_closure(vm_heap, closure, &argument_addresses);

Status::StillFuzzing { vm, arguments }
Status::StillFuzzing {
vm,
arguments,
tracer: FullTracer::default(),
}
}
}
impl Fuzzer {
Expand All @@ -74,46 +82,41 @@ impl Fuzzer {

pub fn run<U: UseProvider, E: ExecutionController>(
&mut self,
db: &Database,
use_provider: &mut U,
execution_controller: &mut E,
) {
let mut status = mem::replace(&mut self.status, None).unwrap();
while matches!(status, Status::StillFuzzing { .. })
&& execution_controller.should_continue_running()
{
status = self.map_status(status, db, use_provider, execution_controller);
status = self.map_status(status, use_provider, execution_controller);
}
self.status = Some(status);
}
fn map_status<U: UseProvider, E: ExecutionController>(
&self,
status: Status,
db: &Database,
use_provider: &mut U,
use_provider: &U,
execution_controller: &mut E,
) -> Status {
match status {
Status::StillFuzzing { mut vm, arguments } => match vm.status() {
Status::StillFuzzing { mut vm, arguments, mut tracer } => match vm.status() {
vm::Status::CanRun => {
vm.run(use_provider, execution_controller);
Status::StillFuzzing { vm, arguments }
vm.run(use_provider, execution_controller, &mut tracer.for_vm());
Status::StillFuzzing { vm, arguments, tracer }
}
vm::Status::WaitingForOperations => panic!("Fuzzing should not have to wait on channel operations because arguments were not channels."),
// The VM finished running without panicking.
vm::Status::Done => Status::new_fuzzing_attempt(&self.closure_heap, self.closure),
vm::Status::Panicked { reason } => {
vm::Status::Panicked { reason, responsible } => {
// If a `needs` directly inside the tested closure was not
// satisfied, then the panic is not closure's fault, but our
// fault.
let TearDownResult { heap, tracer, .. } = vm.tear_down();
let is_our_fault =
did_need_in_closure_cause_panic(db, &self.closure_id, &tracer);
let is_our_fault = responsible.is_none();
if is_our_fault {
Status::new_fuzzing_attempt(&self.closure_heap, self.closure)
} else {
Status::PanickedForArguments {
heap,
arguments,
reason,
tracer,
Expand All @@ -124,12 +127,10 @@ impl Fuzzer {
// We already found some arguments that caused the closure to panic,
// so there's nothing more to do.
Status::PanickedForArguments {
heap,
arguments,
reason,
tracer,
} => Status::PanickedForArguments {
heap,
arguments,
reason,
tracer,
Expand Down
15 changes: 10 additions & 5 deletions compiler/src/fuzzer/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,26 @@ use std::collections::HashMap;

use crate::{
builtin_functions,
vm::{Heap, Pointer},
vm::{Heap, Packet, Pointer},
};
use num_bigint::RandBigInt;
use rand::{prelude::ThreadRng, Rng};

pub fn generate_n_values(heap: &mut Heap, n: usize) -> Vec<Pointer> {
pub fn generate_n_values(n: usize) -> Vec<Packet> {
let mut values = vec![];
for _ in 0..n {
values.push(generate_value(heap));
values.push(generate_value());
}
values
}

fn generate_value(heap: &mut Heap) -> Pointer {
generate_value_with_complexity(heap, &mut rand::thread_rng(), 100.0)
fn generate_value() -> Packet {
let mut heap = Heap::default();
let value = generate_value_with_complexity(&mut heap, &mut rand::thread_rng(), 100.0);
Packet {
heap,
address: value,
}
}
fn generate_value_with_complexity(
heap: &mut Heap,
Expand Down
Loading