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;
13use 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 pub acir_locations: BTreeMap<AcirOpcodeLocation, CallStackId>,
106 pub variables: DebugVariables,
107 pub functions: DebugFunctions,
108 pub types: DebugTypes,
109 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 #[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, OpcodeLocation::Acir(loc) => self.acir_opcode_location(&AcirOpcodeLocation::new(*loc)),
167 }
168 }
169}