Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "runtime_tracing"
version = "0.6.0"
version = "0.7.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
Expand Down
45 changes: 35 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,40 +56,65 @@ mod tests {
}
assert!(matches!(should_be_call, TraceLowLevelEvent::Call(CallRecord { .. })));

let int_value = ValueRecord::Int {
let int_value_1 = ValueRecord::Int {
i: 1,
type_id: tracer.ensure_type_id(TypeKind::Int, "Int"),
};
let int_value_2 = ValueRecord::Int {
i: 2,
type_id: tracer.ensure_type_id(TypeKind::Int, "Int"),
};
tracer.register_variable_with_full_value("test_variable", int_value.clone());
let int_value_3 = ValueRecord::Int {
i: 3,
type_id: tracer.ensure_type_id(TypeKind::Int, "Int"),
};

tracer.register_variable_with_full_value("test_variable", int_value_1.clone());

let not_supported_value = ValueRecord::Error {
msg: "not supported".to_string(),
type_id: NONE_TYPE_ID,
};
tracer.register_variable_with_full_value("test_variable2", not_supported_value);

tracer.register_cell_value(ValueId(0), int_value);
tracer.register_cell_value(Place(0), int_value_1.clone());
let type_id = tracer.ensure_type_id(TypeKind::Seq, "Vector<Int>");
tracer.register_compound_value(
ValueId(1),
Place(1),
ValueRecord::Sequence {
elements: vec![ValueRecord::Cell { value_id: ValueId(0) }], // #0
elements: vec![ValueRecord::Cell { place: Place(0) }], // #0
type_id,
},
);
tracer.register_variable("test_variable3", ValueId(1));
tracer.assign_cell(ValueId(1), int_value_2.clone());
tracer.register_cell_value(ValueId(2), int_value_2.clone());
tracer.assign_compound_item(ValueId(0), 0, ValueId(2));
tracer.register_variable("test_variable3", Place(1));
tracer.assign_cell(Place(1), int_value_2.clone());
tracer.register_cell_value(Place(2), int_value_2.clone());
tracer.assign_compound_item(Place(0), 0, Place(2));

tracer.register_return(NONE_VALUE);
tracer.drop_variable("test_variable3");

assert_eq!(tracer.events.len(), 34);
// example of the history events
tracer.bind_variable("variable1", Place(1));
tracer.bind_variable("variable2", Place(2));
tracer.bind_variable("variable3", Place(3));

tracer.register_variable_with_full_value("variable1", int_value_1.clone());
tracer.register_variable_with_full_value("variable2", int_value_2.clone());
tracer.register_variable_with_full_value("variable3", int_value_3.clone());

// tracer.assign_simple("variable1", "variable2", PassBy::Value);
// tracer.assign_compound("variable1", &["variable2", "variable3"], PassBy::Value);

// more future-proof hopefully, if we add other kinds of RValue
let rvalue_1 = tracer.simple_rvalue("variable2");
tracer.assign("variable1", rvalue_1, PassBy::Value);
let rvalue_2 = tracer.compound_rvalue(&["variable2".to_string(), "variable3".to_string()]);
tracer.assign("variable1", rvalue_2, PassBy::Value);

tracer.drop_variables(&["variable1".to_string(), "variable2".to_string(), "variable3".to_string()]);

assert_eq!(tracer.events.len(), 46);
// visible with
// cargo tets -- --nocapture
// println!("{:#?}", tracer.events);
Expand Down
75 changes: 59 additions & 16 deletions src/tracer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ use std::fs;
use std::path::{Path, PathBuf};

use crate::types::{
AssignCellRecord, AssignCompoundItemRecord, CallRecord, CellValueRecord, CompoundValueRecord, EventLogKind, FullValueRecord, FunctionId,
FunctionRecord, Line, PathId, RecordEvent, ReturnRecord, StepRecord, TraceLowLevelEvent, TraceMetadata, TypeId, TypeKind, TypeRecord,
TypeSpecificInfo, ValueId, ValueRecord, VariableCellRecord, VariableId,
AssignCellRecord, AssignCompoundItemRecord, AssignmentRecord, CallRecord, CellValueRecord, CompoundValueRecord, EventLogKind, FullValueRecord,
FunctionId, FunctionRecord, Line, PassBy, PathId, Place, RecordEvent, ReturnRecord, StepRecord, TraceLowLevelEvent, TraceMetadata, TypeId,
TypeKind, TypeRecord, TypeSpecificInfo, ValueRecord, VariableCellRecord, VariableId,
};
use crate::RValue;

pub struct Tracer {
// trace metadata:
Expand Down Expand Up @@ -119,7 +120,7 @@ impl Tracer {

pub fn register_step(&mut self, path: &Path, line: Line) {
let path_id = self.ensure_path_id(path);
self.events.push(TraceLowLevelEvent::Step(StepRecord { path_id, line}));
self.events.push(TraceLowLevelEvent::Step(StepRecord { path_id, line }));
}

pub fn register_call(&mut self, function_id: FunctionId, args: Vec<FullValueRecord>) {
Expand Down Expand Up @@ -182,37 +183,79 @@ impl Tracer {
self.events.push(TraceLowLevelEvent::Value(FullValueRecord { variable_id, value }));
}

pub fn register_compound_value(&mut self, value_id: ValueId, value: ValueRecord) {
self.events
.push(TraceLowLevelEvent::CompoundValue(CompoundValueRecord { value_id, value }));
pub fn register_compound_value(&mut self, place: Place, value: ValueRecord) {
self.events.push(TraceLowLevelEvent::CompoundValue(CompoundValueRecord { place, value }));
}

pub fn register_cell_value(&mut self, value_id: ValueId, value: ValueRecord) {
self.events.push(TraceLowLevelEvent::CellValue(CellValueRecord { value_id, value }));
pub fn register_cell_value(&mut self, place: Place, value: ValueRecord) {
self.events.push(TraceLowLevelEvent::CellValue(CellValueRecord { place, value }));
}

pub fn assign_compound_item(&mut self, value_id: ValueId, index: usize, item_value_id: ValueId) {
pub fn assign_compound_item(&mut self, place: Place, index: usize, item_place: Place) {
self.events.push(TraceLowLevelEvent::AssignCompoundItem(AssignCompoundItemRecord {
value_id,
place,
index,
item_value_id,
item_place,
}));
}
pub fn assign_cell(&mut self, value_id: ValueId, new_value: ValueRecord) {
self.events.push(TraceLowLevelEvent::AssignCell(AssignCellRecord { value_id, new_value }));
pub fn assign_cell(&mut self, place: Place, new_value: ValueRecord) {
self.events.push(TraceLowLevelEvent::AssignCell(AssignCellRecord { place, new_value }));
}

pub fn register_variable(&mut self, variable_name: &str, value_id: ValueId) {
pub fn register_variable(&mut self, variable_name: &str, place: Place) {
let variable_id = self.ensure_variable_id(variable_name);
self.events
.push(TraceLowLevelEvent::VariableCell(VariableCellRecord { variable_id, value_id }));
.push(TraceLowLevelEvent::VariableCell(VariableCellRecord { variable_id, place }));
}

pub fn drop_variable(&mut self, variable_name: &str) {
let variable_id = self.ensure_variable_id(variable_name);
self.events.push(TraceLowLevelEvent::DropVariable(variable_id));
}

// history event helpers
pub fn assign(&mut self, variable_name: &str, rvalue: RValue, pass_by: PassBy) {
let variable_id = self.ensure_variable_id(variable_name);
self.events.push(TraceLowLevelEvent::Assignment(AssignmentRecord {
to: variable_id,
from: rvalue,
pass_by,
}));
}

pub fn bind_variable(&mut self, variable_name: &str, place: Place) {
let variable_id = self.ensure_variable_id(variable_name);
self.events.push(TraceLowLevelEvent::BindVariable(crate::BindVariableRecord {
variable_id,
place,
}));
}

pub fn drop_variables(&mut self, variable_names: &[String]) {
let variable_ids : Vec<VariableId> = variable_names
.to_vec()
.iter()
.map(| variable_name | self.ensure_variable_id(variable_name))
.collect();
self.events.push(TraceLowLevelEvent::DropVariables(
variable_ids
))
}

pub fn simple_rvalue(&mut self, variable_name: &str) -> RValue {
let variable_id = self.ensure_variable_id(variable_name);
RValue::Simple(variable_id)
}

pub fn compound_rvalue(&mut self, variable_dependencies: &[String]) -> RValue {
let variable_ids : Vec<VariableId> = variable_dependencies
.to_vec()
.iter()
.map(| variable_dependency | self.ensure_variable_id(variable_dependency))
.collect();
RValue::Compound(variable_ids)
}

pub fn drop_last_step(&mut self) {
self.events.push(TraceLowLevelEvent::DropLastStep);
}
Expand Down
75 changes: 62 additions & 13 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,15 @@ pub enum TraceLowLevelEvent {
Event(RecordEvent),
Asm(Vec<String>),

// events useful for history
BindVariable(BindVariableRecord), // bind a variable to a certain place
Assignment(AssignmentRecord), // assigning or passing by params
DropVariables(Vec<VariableId>), // dropping variables e.g. in the end of scope/heap lifetime

// experimental modification value tracking events
// probably will be reworked or replaced by the newer
// history events with some additions
// for now here for backward compatibility/experiments
CompoundValue(CompoundValueRecord),
CellValue(CellValueRecord),
AssignCompoundItem(AssignCompoundItemRecord),
Expand All @@ -39,42 +47,83 @@ pub enum TraceLowLevelEvent {
DropLastStep,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BindVariableRecord {
pub variable_id: VariableId,
pub place: Place,
}

#[derive(Default, Debug, Clone, Serialize, Deserialize)]
pub enum PassBy {
#[default]
Value,
Reference,
// TODO: languages with more special ways of passing
}

// used for all kinds of by value/by ref assignment/passing
// * assignments
// * arg(parameter) passing
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssignmentRecord {
pub to: VariableId,
pub pass_by: PassBy,
pub from: RValue,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "kind")]
pub enum RValue {
Simple(VariableId),
// eventually in future:
// discuss more: Const(String, ValueRecord),
Compound(Vec<VariableId>),
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CompoundValueRecord {
pub value_id: ValueId,
pub place: Place,
pub value: ValueRecord,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CellValueRecord {
pub value_id: ValueId,
pub place: Place,
pub value: ValueRecord,
}

#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct AssignCompoundItemRecord {
pub value_id: ValueId,
pub place: Place,
pub index: usize,
pub item_value_id: ValueId,
pub item_place: Place,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AssignCellRecord {
pub value_id: ValueId,
pub place: Place,
pub new_value: ValueRecord,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VariableCellRecord {
pub variable_id: VariableId,
pub value_id: ValueId,
}

// for now can be both just an index and
// a 64-bit pointer; think if we need
// something more general?
pub place: Place,
}

// opaque(?) id:
// can be anything, depending on the lang
// and its implementation
// usually we expects it's
// * some kind of pointer/address
// * some kind of internal index(interpreter or stack)
// * some other kind of id which somehow
// uniquely represents the "place" of this variable
// it's useful to let us track things on the more direct value
// level/things like aliasing/mutable variables in different frames
// history of mutations to a value etc
#[derive(Hash, Debug, Default, Copy, Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq)]
pub struct ValueId(pub usize);
pub struct Place(pub i64);

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FullValueRecord {
Expand Down Expand Up @@ -306,7 +355,7 @@ pub enum ValueRecord {
type_id: TypeId,
},
Cell {
value_id: ValueId,
place: Place,
},
}

Expand Down