@@ -12,7 +12,10 @@ use crate::{
1212 get_all_clients_contexts_before, get_all_clients_contexts_current,
1313 get_all_clients_instructions,
1414 } ,
15- ipc:: { cstructs, qemu:: Client } ,
15+ ipc:: {
16+ cstructs:: { self , TBInfo , TBInsnInfo } ,
17+ qemu:: Client ,
18+ } ,
1619 trace:: { TraceEntryData , TraceStore , connect, get_client_trace, store_trace, trace_collect} ,
1720} ;
1821
@@ -90,6 +93,10 @@ impl Broker {
9093 Ok ( ( ) )
9194 }
9295
96+ pub fn clients ( & self ) -> & Vec < Client > {
97+ & self . clients
98+ }
99+
93100 fn run_lockstep ( & mut self , config : & Config ) -> Result < Report > {
94101 // NOTE: maybe move "spawning" the clients into this method
95102 for ( idx, client) in self . clients . iter_mut ( ) . enumerate ( ) {
@@ -114,7 +121,7 @@ impl Broker {
114121 . clients
115122 . iter_mut ( )
116123 . map ( |client| {
117- let res = client. shm . read_buffer ( ) . map ( |i| i. as_insn ( ) ) ;
124+ let res = client. shm . read_buffer ( ) . map ( |opt| opt . map ( | i| i. as_insn ( ) ) ) ;
118125 client. run_count += 1 ;
119126 res
120127 } )
@@ -123,72 +130,144 @@ impl Broker {
123130 let c1insn = reads[ 0 ] ;
124131 let c2insn = reads[ 1 ] ;
125132
126- let diffs = diff_cpus (
127- & c1insn. cpus ,
128- c1insn. init_mask ,
129- & c2insn. cpus ,
130- c2insn. init_mask ,
131- config,
132- ) ;
133-
134- self . trace_clients ( config) ?;
135-
136- if !diffs. is_empty ( ) {
137- let ctx = self . build_diff_context ( config) ?;
138- return Ok ( Report :: failed ( diffs, ctx) ) ;
139- }
140-
141- for client in & mut self . clients {
142- client. shm . end_read_buffer ( ) ;
143- }
133+ match ( c1insn, c2insn) {
134+ // successfully read both clients -> compare
135+ ( Some ( c1insn) , Some ( c2insn) ) => {
136+ let diffs = diff_cpus (
137+ & c1insn. cpus ,
138+ c1insn. init_mask ,
139+ & c2insn. cpus ,
140+ c2insn. init_mask ,
141+ config,
142+ ) ;
143+
144+ self . trace_clients ( config) ?;
145+
146+ if !diffs. is_empty ( ) {
147+ let ctx = self . build_diff_context ( config) ?;
148+ return Ok ( Report :: failed ( diffs, ctx) ) ;
149+ }
150+
151+ for client in & mut self . clients {
152+ client. shm . end_read_buffer ( ) ;
153+ }
154+
155+ if !config. testing . protocol . execute_all_remaining_instructions {
156+ stop_after -= 1 ;
157+ if stop_after == 0 {
158+ break ;
159+ }
160+ }
161+ }
144162
145- if !config. testing . protocol . execute_all_remaining_instructions {
146- stop_after -= 1 ;
147- if stop_after == 0 {
148- break ;
163+ // both clients finished at the same time => stop cosimulation
164+ ( None , None ) => break ,
165+
166+ // one client finished while the other still writes to the buffer, error state!
167+ ( Some ( c1insn) , None ) => {
168+ let diff = DiffEntry :: new (
169+ "invalid-execution" ,
170+ vec ! [ Broker :: format_insn_for_diff( & c1insn. insn_info) ] ,
171+ Broker :: format_invalid_execution_client_msg (
172+ & self . clients [ 0 ] ,
173+ & self . clients [ 1 ] ,
174+ ) ,
175+ ) ;
176+ let ctx = self . build_diff_context ( config) ?;
177+ return Ok ( Report :: failed ( vec ! [ diff] , ctx) ) ;
178+ }
179+ ( None , Some ( c2insn) ) => {
180+ let diff = DiffEntry :: new (
181+ "invalid-execution" ,
182+ vec ! [ Broker :: format_insn_for_diff( & c2insn. insn_info) ] ,
183+ Broker :: format_invalid_execution_client_msg (
184+ & self . clients [ 1 ] ,
185+ & self . clients [ 0 ] ,
186+ ) ,
187+ ) ;
188+ let ctx = self . build_diff_context ( config) ?;
189+ return Ok ( Report :: failed ( vec ! [ diff] , ctx) ) ;
149190 }
150191 }
151192 } ,
152193 crate :: config:: ProtocolLayer :: TB | crate :: config:: ProtocolLayer :: TBStrict => loop {
153194 let reads = self
154195 . clients
155196 . iter_mut ( )
156- . map ( |client| client. shm . read_buffer ( ) . map ( |i| i. as_tb ( ) ) )
197+ . map ( |client| {
198+ let res = client. shm . read_buffer ( ) . map ( |opt| opt. map ( |i| i. as_tb ( ) ) ) ;
199+ client. run_count += 1 ;
200+ res
201+ } )
157202 . collect :: < Result < Vec < _ > > > ( ) ?;
158203
159204 let c1insn = reads[ 0 ] ;
160205 let c2insn = reads[ 1 ] ;
161206
162- if let TBSyncResult :: Diverged ( diff_entry) =
163- Self :: check_if_clients_are_synchronized ( & [ c1insn, c2insn] )
164- {
165- debug ! ( "client diverged during tb synchronization" ) ;
166- return Ok ( Report :: failed ( vec ! [ diff_entry] , vec ! [ ] ) ) ;
167- }
168-
169- let diffs = diff_cpus (
170- & c1insn. cpus ,
171- c1insn. init_mask ,
172- & c2insn. cpus ,
173- c2insn. init_mask ,
174- config,
175- ) ;
176-
177- self . trace_clients ( config) ?;
178-
179- if !diffs. is_empty ( ) {
180- let ctx = self . build_diff_context ( config) ?;
181- return Ok ( Report :: failed ( diffs, ctx) ) ;
182- }
183-
184- for client in & mut self . clients {
185- client. shm . end_read_buffer ( ) ;
186- }
207+ match ( c1insn, c2insn) {
208+ // successfully read both clients -> compare
209+ ( Some ( c1insn) , Some ( c2insn) ) => {
210+ if let TBSyncResult :: Diverged ( diff_entry) =
211+ Self :: check_if_clients_are_synchronized ( & [ c1insn, c2insn] )
212+ {
213+ debug ! ( "client diverged during tb synchronization" ) ;
214+ return Ok ( Report :: failed ( vec ! [ diff_entry] , vec ! [ ] ) ) ;
215+ }
216+
217+ let diffs = diff_cpus (
218+ & c1insn. cpus ,
219+ c1insn. init_mask ,
220+ & c2insn. cpus ,
221+ c2insn. init_mask ,
222+ config,
223+ ) ;
224+
225+ self . trace_clients ( config) ?;
226+
227+ if !diffs. is_empty ( ) {
228+ let ctx = self . build_diff_context ( config) ?;
229+ return Ok ( Report :: failed ( diffs, ctx) ) ;
230+ }
231+
232+ for client in & mut self . clients {
233+ client. shm . end_read_buffer ( ) ;
234+ }
235+
236+ if !config. testing . protocol . execute_all_remaining_instructions {
237+ stop_after -= 1 ;
238+ if stop_after == 0 {
239+ break ;
240+ }
241+ }
242+ }
187243
188- if !config. testing . protocol . execute_all_remaining_instructions {
189- stop_after -= 1 ;
190- if stop_after == 0 {
191- break ;
244+ // both clients finished at the same time => stop cosimulation
245+ ( None , None ) => break ,
246+
247+ // one client finished while the other still writes to the buffer, error state!
248+ ( Some ( c1tb) , None ) => {
249+ let diff = DiffEntry :: new (
250+ "invalid-execution" ,
251+ vec ! [ Broker :: format_tb_for_diff( & c1tb. tb_info) ] ,
252+ Broker :: format_invalid_execution_client_msg (
253+ & self . clients [ 0 ] ,
254+ & self . clients [ 1 ] ,
255+ ) ,
256+ ) ;
257+ let ctx = self . build_diff_context ( config) ?;
258+ return Ok ( Report :: failed ( vec ! [ diff] , ctx) ) ;
259+ }
260+ ( None , Some ( c2tb) ) => {
261+ let diff = DiffEntry :: new (
262+ "invalid-execution" ,
263+ vec ! [ Broker :: format_tb_for_diff( & c2tb. tb_info) ] ,
264+ Broker :: format_invalid_execution_client_msg (
265+ & self . clients [ 1 ] ,
266+ & self . clients [ 0 ] ,
267+ ) ,
268+ ) ;
269+ let ctx = self . build_diff_context ( config) ?;
270+ return Ok ( Report :: failed ( vec ! [ diff] , ctx) ) ;
192271 }
193272 }
194273 } ,
@@ -197,6 +276,45 @@ impl Broker {
197276 Ok ( Report :: passed ( ) )
198277 }
199278
279+ fn format_insn_for_diff ( insn : & TBInsnInfo ) -> String {
280+ format ! (
281+ "pc={}, size={}, symbol={}, hwaddr={}, disas={}, data={}" ,
282+ insn. pc,
283+ insn. size,
284+ insn. symbol. as_str( ) ,
285+ insn. hwaddr. as_str( ) ,
286+ insn. disas. as_str( ) ,
287+ insn. data. buffer_slice_fmt( ) ,
288+ )
289+ }
290+
291+ fn format_tb_for_diff ( tb : & TBInfo ) -> String {
292+ let insns = tb
293+ . insns_info_slice ( )
294+ . iter ( )
295+ . map ( Broker :: format_insn_for_diff)
296+ . collect :: < Vec < _ > > ( ) ;
297+
298+ format ! ( "pc={}, insns={:#?}" , tb. pc, insns)
299+ }
300+
301+ fn format_invalid_execution_client_msg (
302+ executing_client : & Client ,
303+ halted_client : & Client ,
304+ ) -> String {
305+ format ! (
306+ "client \" {}\" executed another instruction while client \" {}\" has already finished" ,
307+ executing_client
308+ . name
309+ . as_deref( )
310+ . unwrap_or( & executing_client. id. to_string( ) ) ,
311+ halted_client
312+ . name
313+ . as_deref( )
314+ . unwrap_or( & halted_client. id. to_string( ) )
315+ )
316+ }
317+
200318 fn check_clients_are_initially_synchronized ( & self , config : & Config ) -> Result < ( ) > {
201319 let start_pcs = self
202320 . clients
0 commit comments