@@ -5,35 +5,44 @@ use acvm::pwg::{
55use acvm:: BlackBoxFunctionSolver ;
66use acvm:: { acir:: circuit:: Circuit , acir:: native_types:: WitnessMap } ;
77
8- use nargo:: errors:: ExecutionError ;
8+ use nargo:: artifacts:: debug:: DebugArtifact ;
9+ use nargo:: errors:: { ExecutionError , Location } ;
910use nargo:: ops:: ForeignCallExecutor ;
1011use nargo:: NargoError ;
1112
13+ use std:: collections:: { hash_set:: Iter , HashSet } ;
14+
1215#[ derive( Debug ) ]
1316pub ( super ) enum DebugCommandResult {
1417 Done ,
1518 Ok ,
19+ BreakpointReached ( OpcodeLocation ) ,
1620 Error ( NargoError ) ,
1721}
1822
1923pub ( super ) struct DebugContext < ' a , B : BlackBoxFunctionSolver > {
2024 acvm : ACVM < ' a , B > ,
2125 brillig_solver : Option < BrilligSolver < ' a , B > > ,
2226 foreign_call_executor : ForeignCallExecutor ,
27+ debug_artifact : & ' a DebugArtifact ,
2328 show_output : bool ,
29+ breakpoints : HashSet < OpcodeLocation > ,
2430}
2531
2632impl < ' a , B : BlackBoxFunctionSolver > DebugContext < ' a , B > {
2733 pub ( super ) fn new (
2834 blackbox_solver : & ' a B ,
2935 circuit : & ' a Circuit ,
36+ debug_artifact : & ' a DebugArtifact ,
3037 initial_witness : WitnessMap ,
3138 ) -> Self {
3239 Self {
3340 acvm : ACVM :: new ( blackbox_solver, & circuit. opcodes , initial_witness) ,
3441 brillig_solver : None ,
3542 foreign_call_executor : ForeignCallExecutor :: default ( ) ,
43+ debug_artifact,
3644 show_output : true ,
45+ breakpoints : HashSet :: new ( ) ,
3746 }
3847 }
3948
@@ -55,25 +64,41 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> {
5564 }
5665 }
5766
67+ // Returns the callstack in source code locations for the currently
68+ // executing opcode. This can be None if the execution finished (and
69+ // get_current_opcode_location() returns None) or if the opcode is not
70+ // mapped to a specific source location in the debug artifact (which can
71+ // happen for certain opcodes inserted synthetically by the compiler)
72+ pub ( super ) fn get_current_source_location ( & self ) -> Option < Vec < Location > > {
73+ self . get_current_opcode_location ( )
74+ . as_ref ( )
75+ . and_then ( |location| self . debug_artifact . debug_symbols [ 0 ] . opcode_location ( location) )
76+ }
77+
5878 fn step_brillig_opcode ( & mut self ) -> DebugCommandResult {
5979 let Some ( mut solver) = self . brillig_solver . take ( ) else {
6080 unreachable ! ( "Missing Brillig solver" ) ;
6181 } ;
6282 match solver. step ( ) {
63- Ok ( status) => match status {
64- BrilligSolverStatus :: InProgress => {
65- self . brillig_solver = Some ( solver) ;
83+ Ok ( BrilligSolverStatus :: InProgress ) => {
84+ self . brillig_solver = Some ( solver) ;
85+ if self . breakpoint_reached ( ) {
86+ DebugCommandResult :: BreakpointReached (
87+ self . get_current_opcode_location ( )
88+ . expect ( "Breakpoint reached but we have no location" ) ,
89+ )
90+ } else {
6691 DebugCommandResult :: Ok
6792 }
68- BrilligSolverStatus :: Finished => {
69- let status = self . acvm . finish_brillig_with_solver ( solver ) ;
70- self . handle_acvm_status ( status )
71- }
72- BrilligSolverStatus :: ForeignCallWait ( foreign_call ) => {
73- self . brillig_solver = Some ( solver ) ;
74- self . handle_foreign_call ( foreign_call )
75- }
76- } ,
93+ }
94+ Ok ( BrilligSolverStatus :: Finished ) => {
95+ let status = self . acvm . finish_brillig_with_solver ( solver ) ;
96+ self . handle_acvm_status ( status )
97+ }
98+ Ok ( BrilligSolverStatus :: ForeignCallWait ( foreign_call ) ) => {
99+ self . brillig_solver = Some ( solver ) ;
100+ self . handle_foreign_call ( foreign_call )
101+ }
77102 Err ( err) => DebugCommandResult :: Error ( NargoError :: ExecutionError (
78103 ExecutionError :: SolvingError ( err) ,
79104 ) ) ,
@@ -95,32 +120,41 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> {
95120
96121 fn handle_acvm_status ( & mut self , status : ACVMStatus ) -> DebugCommandResult {
97122 if let ACVMStatus :: RequiresForeignCall ( foreign_call) = status {
98- self . handle_foreign_call ( foreign_call)
99- } else {
100- match status {
101- ACVMStatus :: Solved => DebugCommandResult :: Done ,
102- ACVMStatus :: InProgress => DebugCommandResult :: Ok ,
103- ACVMStatus :: Failure ( error) => DebugCommandResult :: Error (
104- NargoError :: ExecutionError ( ExecutionError :: SolvingError ( error) ) ,
105- ) ,
106- ACVMStatus :: RequiresForeignCall ( _) => {
107- unreachable ! ( "Unexpected pending foreign call resolution" ) ;
123+ return self . handle_foreign_call ( foreign_call) ;
124+ }
125+
126+ match status {
127+ ACVMStatus :: Solved => DebugCommandResult :: Done ,
128+ ACVMStatus :: InProgress => {
129+ if self . breakpoint_reached ( ) {
130+ DebugCommandResult :: BreakpointReached (
131+ self . get_current_opcode_location ( )
132+ . expect ( "Breakpoint reached but we have no location" ) ,
133+ )
134+ } else {
135+ DebugCommandResult :: Ok
108136 }
109137 }
138+ ACVMStatus :: Failure ( error) => DebugCommandResult :: Error ( NargoError :: ExecutionError (
139+ ExecutionError :: SolvingError ( error) ,
140+ ) ) ,
141+ ACVMStatus :: RequiresForeignCall ( _) => {
142+ unreachable ! ( "Unexpected pending foreign call resolution" ) ;
143+ }
110144 }
111145 }
112146
113147 pub ( super ) fn step_into_opcode ( & mut self ) -> DebugCommandResult {
114148 if self . brillig_solver . is_some ( ) {
115- self . step_brillig_opcode ( )
116- } else {
117- match self . acvm . step_into_brillig_opcode ( ) {
118- StepResult :: IntoBrillig ( solver) => {
119- self . brillig_solver = Some ( solver) ;
120- self . step_brillig_opcode ( )
121- }
122- StepResult :: Status ( status) => self . handle_acvm_status ( status) ,
149+ return self . step_brillig_opcode ( ) ;
150+ }
151+
152+ match self . acvm . step_into_brillig_opcode ( ) {
153+ StepResult :: IntoBrillig ( solver) => {
154+ self . brillig_solver = Some ( solver) ;
155+ self . step_brillig_opcode ( )
123156 }
157+ StepResult :: Status ( status) => self . handle_acvm_status ( status) ,
124158 }
125159 }
126160
@@ -133,6 +167,20 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> {
133167 self . handle_acvm_status ( status)
134168 }
135169
170+ pub ( super ) fn next ( & mut self ) -> DebugCommandResult {
171+ let start_location = self . get_current_source_location ( ) ;
172+ loop {
173+ let result = self . step_into_opcode ( ) ;
174+ if !matches ! ( result, DebugCommandResult :: Ok ) {
175+ return result;
176+ }
177+ let new_location = self . get_current_source_location ( ) ;
178+ if new_location. is_some ( ) && new_location != start_location {
179+ return DebugCommandResult :: Ok ;
180+ }
181+ }
182+ }
183+
136184 pub ( super ) fn cont ( & mut self ) -> DebugCommandResult {
137185 loop {
138186 let result = self . step_into_opcode ( ) ;
@@ -142,6 +190,48 @@ impl<'a, B: BlackBoxFunctionSolver> DebugContext<'a, B> {
142190 }
143191 }
144192
193+ fn breakpoint_reached ( & self ) -> bool {
194+ if let Some ( location) = self . get_current_opcode_location ( ) {
195+ self . breakpoints . contains ( & location)
196+ } else {
197+ false
198+ }
199+ }
200+
201+ pub ( super ) fn is_valid_opcode_location ( & self , location : & OpcodeLocation ) -> bool {
202+ let opcodes = self . get_opcodes ( ) ;
203+ match * location {
204+ OpcodeLocation :: Acir ( acir_index) => acir_index < opcodes. len ( ) ,
205+ OpcodeLocation :: Brillig { acir_index, brillig_index } => {
206+ acir_index < opcodes. len ( )
207+ && matches ! ( opcodes[ acir_index] , Opcode :: Brillig ( ..) )
208+ && {
209+ if let Opcode :: Brillig ( ref brillig) = opcodes[ acir_index] {
210+ brillig_index < brillig. bytecode . len ( )
211+ } else {
212+ false
213+ }
214+ }
215+ }
216+ }
217+ }
218+
219+ pub ( super ) fn is_breakpoint_set ( & self , location : & OpcodeLocation ) -> bool {
220+ self . breakpoints . contains ( location)
221+ }
222+
223+ pub ( super ) fn add_breakpoint ( & mut self , location : OpcodeLocation ) -> bool {
224+ self . breakpoints . insert ( location)
225+ }
226+
227+ pub ( super ) fn delete_breakpoint ( & mut self , location : & OpcodeLocation ) -> bool {
228+ self . breakpoints . remove ( location)
229+ }
230+
231+ pub ( super ) fn iterate_breakpoints ( & self ) -> Iter < ' _ , OpcodeLocation > {
232+ self . breakpoints . iter ( )
233+ }
234+
145235 pub ( super ) fn is_solved ( & self ) -> bool {
146236 matches ! ( self . acvm. get_status( ) , ACVMStatus :: Solved )
147237 }
0 commit comments