Skip to main content

noirc_errors/
debug_info.rs

1use acvm::acir::circuit::AcirOpcodeLocation;
2use acvm::acir::circuit::BrilligOpcodeLocation;
3use acvm::acir::circuit::OpcodeLocation;
4use acvm::acir::circuit::brillig::BrilligFunctionId;
5use acvm::compiler::AcirTransformationMap;
6
7use base64::Engine;
8use flate2::Compression;
9use flate2::read::DeflateDecoder;
10use flate2::write::DeflateEncoder;
11use serde::Deserializer;
12use serde::Serializer;
13//use serde_with::DisplayFromStr;
14//use serde_with::serde_as;
15use std::collections::BTreeMap;
16use std::io::Read;
17use std::io::Write;
18use std::mem;
19
20use crate::Location;
21use crate::call_stack::CallStackId;
22use crate::call_stack::LocationTree;
23use noirc_printable_type::PrintableType;
24use serde::{
25    Deserialize, Serialize, de::Error as DeserializationError, ser::Error as SerializationError,
26};
27
28#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Deserialize, Serialize)]
29pub struct DebugVarId(pub u32);
30
31#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Deserialize, Serialize)]
32pub struct DebugFnId(pub u32);
33
34#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd, Ord, Deserialize, Serialize)]
35pub struct DebugTypeId(pub u32);
36
37#[derive(Debug, Clone, Hash, Deserialize, Serialize)]
38pub struct DebugVariable {
39    pub name: String,
40    pub debug_type_id: DebugTypeId,
41}
42
43#[derive(Debug, Clone, Hash, Deserialize, Serialize)]
44pub struct DebugFunction {
45    pub name: String,
46    pub arg_names: Vec<String>,
47}
48
49pub type DebugVariables = BTreeMap<DebugVarId, DebugVariable>;
50pub type DebugFunctions = BTreeMap<DebugFnId, DebugFunction>;
51pub type DebugTypes = BTreeMap<DebugTypeId, PrintableType>;
52
53#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Deserialize, Serialize)]
54pub struct ProcedureDebugId(pub u32);
55
56#[derive(Default, Debug, Clone, Deserialize, Serialize)]
57pub struct ProgramDebugInfo {
58    pub debug_infos: Vec<DebugInfo>,
59}
60
61impl ProgramDebugInfo {
62    pub fn serialize_compressed_base64_json<S>(
63        debug_info: &ProgramDebugInfo,
64        s: S,
65    ) -> Result<S::Ok, S::Error>
66    where
67        S: Serializer,
68    {
69        let json_str = serde_json::to_string(debug_info).map_err(S::Error::custom)?;
70
71        let mut encoder = DeflateEncoder::new(Vec::new(), Compression::default());
72        encoder.write_all(json_str.as_bytes()).map_err(S::Error::custom)?;
73        let compressed_data = encoder.finish().map_err(S::Error::custom)?;
74
75        let encoded_b64 = base64::prelude::BASE64_STANDARD.encode(compressed_data);
76        s.serialize_str(&encoded_b64)
77    }
78
79    pub fn deserialize_compressed_base64_json<'de, D>(
80        deserializer: D,
81    ) -> Result<ProgramDebugInfo, D::Error>
82    where
83        D: Deserializer<'de>,
84    {
85        let encoded_b64: String = Deserialize::deserialize(deserializer)?;
86
87        let compressed_data =
88            base64::prelude::BASE64_STANDARD.decode(encoded_b64).map_err(D::Error::custom)?;
89
90        let mut decoder = DeflateDecoder::new(&compressed_data[..]);
91        let mut decompressed_data = Vec::new();
92        decoder.read_to_end(&mut decompressed_data).map_err(D::Error::custom)?;
93
94        let json_str = String::from_utf8(decompressed_data).map_err(D::Error::custom)?;
95        serde_json::from_str(&json_str).map_err(D::Error::custom)
96    }
97}
98
99#[derive(Default, Debug, Clone, Deserialize, Serialize, Hash)]
100pub struct DebugInfo {
101    pub brillig_locations:
102        BTreeMap<BrilligFunctionId, BTreeMap<BrilligOpcodeLocation, CallStackId>>,
103    pub location_tree: LocationTree,
104    /// Map opcode index of an ACIR circuit into the source code location
105    pub acir_locations: BTreeMap<AcirOpcodeLocation, CallStackId>,
106    pub variables: DebugVariables,
107    pub functions: DebugFunctions,
108    pub types: DebugTypes,
109    /// This a map per brillig function representing the range of opcodes where a procedure is activated.
110    pub brillig_procedure_locs:
111        BTreeMap<BrilligFunctionId, BTreeMap<ProcedureDebugId, (usize, usize)>>,
112}
113
114impl DebugInfo {
115    pub fn new(
116        brillig_locations: BTreeMap<
117            BrilligFunctionId,
118            BTreeMap<BrilligOpcodeLocation, CallStackId>,
119        >,
120        location_map: BTreeMap<AcirOpcodeLocation, CallStackId>,
121        location_tree: LocationTree,
122        variables: DebugVariables,
123        functions: DebugFunctions,
124        types: DebugTypes,
125        brillig_procedure_locs: BTreeMap<
126            BrilligFunctionId,
127            BTreeMap<ProcedureDebugId, (usize, usize)>,
128        >,
129    ) -> Self {
130        Self {
131            brillig_locations,
132            acir_locations: location_map,
133            location_tree,
134            variables,
135            functions,
136            types,
137            brillig_procedure_locs,
138        }
139    }
140
141    /// Updates the locations map when the [`Circuit`][acvm::acir::circuit::Circuit] is modified.
142    ///
143    /// The [`OpcodeLocation`]s are generated with the ACIR, but passing the ACIR through a transformation step
144    /// renders the old `OpcodeLocation`s invalid. The AcirTransformationMap is able to map the old `OpcodeLocation` to the new ones.
145    /// Note: One old `OpcodeLocation` might have transformed into more than one new `OpcodeLocation`.
146    #[tracing::instrument(level = "trace", skip(self, update_map))]
147    pub fn update_acir(&mut self, update_map: AcirTransformationMap) {
148        let old_locations = mem::take(&mut self.acir_locations);
149
150        for (old_opcode_location, source_locations) in old_locations {
151            update_map.new_acir_locations(old_opcode_location).for_each(|new_opcode_location| {
152                self.acir_locations.insert(new_opcode_location, source_locations);
153            });
154        }
155    }
156
157    pub fn acir_opcode_location(&self, loc: &AcirOpcodeLocation) -> Option<Vec<Location>> {
158        self.acir_locations
159            .get(loc)
160            .map(|call_stack_id| self.location_tree.get_call_stack(*call_stack_id))
161    }
162
163    pub fn opcode_location(&self, loc: &OpcodeLocation) -> Option<Vec<Location>> {
164        match loc {
165            OpcodeLocation::Brillig { .. } => None, //TODO: need brillig function id in order to look into brillig_locations
166            OpcodeLocation::Acir(loc) => self.acir_opcode_location(&AcirOpcodeLocation::new(*loc)),
167        }
168    }
169}