@@ -2,6 +2,7 @@ use std::fs::File;
2
2
use std:: io:: { BufRead , BufReader , Read , Write } ;
3
3
use std:: net:: { SocketAddr , TcpStream } ;
4
4
use std:: process:: { Command , Stdio } ;
5
+ use std:: str:: FromStr ;
5
6
use std:: sync:: { Arc , Mutex } ;
6
7
use std:: thread;
7
8
use std:: time:: Duration ;
@@ -29,8 +30,9 @@ use Constraint::{Fill, Length, Min};
29
30
mod mi;
30
31
use mi:: {
31
32
data_disassemble, data_read_sp_bytes, join_registers, parse_asm_insns_values,
32
- parse_key_value_pairs, parse_register_names_values, parse_register_values, read_pc_value, Asm ,
33
- MIResponse , Register ,
33
+ parse_key_value_pairs, parse_memory_mappings, parse_register_names_values,
34
+ parse_register_values, read_pc_value, Asm , MIResponse , MemoryMapping , Register ,
35
+ MEMORY_MAP_START_STR ,
34
36
} ;
35
37
36
38
enum InputMode {
@@ -108,6 +110,7 @@ struct App {
108
110
input : Input ,
109
111
input_mode : InputMode ,
110
112
messages : LimitedBuffer < String > ,
113
+ memory_map : Arc < Mutex < Option < Vec < MemoryMapping > > > > ,
111
114
current_pc : Arc < Mutex < u64 > > , // TODO: replace with AtomicU64?
112
115
output_scroll : usize ,
113
116
output : Arc < Mutex < Vec < String > > > ,
@@ -166,6 +169,7 @@ impl App {
166
169
messages : LimitedBuffer :: new ( 10 ) ,
167
170
current_pc : Arc :: new ( Mutex :: new ( 0 ) ) ,
168
171
output_scroll : 0 ,
172
+ memory_map : Arc :: new ( Mutex :: new ( None ) ) ,
169
173
output : Arc :: new ( Mutex :: new ( Vec :: new ( ) ) ) ,
170
174
stream_output_prompt : Arc :: new ( Mutex :: new ( String :: new ( ) ) ) ,
171
175
register_changed : Arc :: new ( Mutex :: new ( vec ! [ ] ) ) ,
@@ -213,6 +217,7 @@ fn main() -> Result<(), Box<dyn Error>> {
213
217
let register_changed_arc = Arc :: clone ( & app. register_changed ) ;
214
218
let register_names_arc = Arc :: clone ( & app. register_names ) ;
215
219
let registers_arc = Arc :: clone ( & app. registers ) ;
220
+ let memory_map_arc = Arc :: clone ( & app. memory_map ) ;
216
221
let stack_arc = Arc :: clone ( & app. stack ) ;
217
222
let asm_arc = Arc :: clone ( & app. asm ) ;
218
223
@@ -229,6 +234,7 @@ fn main() -> Result<(), Box<dyn Error>> {
229
234
gdb_stdin_arc,
230
235
output_arc,
231
236
stream_output_prompt_arc,
237
+ memory_map_arc,
232
238
)
233
239
} ) ;
234
240
@@ -258,7 +264,9 @@ fn gdb_interact(
258
264
gdb_stdin_arc : Arc < Mutex < dyn Write + Send > > ,
259
265
output_arc : Arc < Mutex < Vec < String > > > ,
260
266
stream_output_prompt_arc : Arc < Mutex < String > > ,
267
+ memory_map_arc : Arc < Mutex < Option < Vec < MemoryMapping > > > > ,
261
268
) {
269
+ let mut current_map = ( false , String :: new ( ) ) ;
262
270
let mut next_write = vec ! [ String :: new( ) ] ;
263
271
for line in gdb_stdout. lines ( ) {
264
272
if let Ok ( line) = line {
@@ -278,7 +286,11 @@ fn gdb_interact(
278
286
next_write. push ( "-data-list-register-names" . to_string ( ) ) ;
279
287
// When a breakpoint is hit, query for register values
280
288
next_write. push ( "-data-list-register-values x" . to_string ( ) ) ;
289
+ // get a list of changed registers
281
290
next_write. push ( "-data-list-changed-registers" . to_string ( ) ) ;
291
+ // get the memory mapping
292
+ next_write
293
+ . push ( r#"-interpreter-exec console "info proc mappings""# . to_string ( ) ) ;
282
294
}
283
295
}
284
296
MIResponse :: ExecResult ( status, kv) => {
@@ -299,6 +311,16 @@ fn gdb_interact(
299
311
let mut regs = registers_arc. lock ( ) . unwrap ( ) ;
300
312
regs. clear ( ) ;
301
313
}
314
+ if status == "done" {
315
+ // Check if we were looking for a mapping
316
+ // TODO: This should be an enum or something?
317
+ if current_map. 0 {
318
+ let m = parse_memory_mappings ( & current_map. 1 ) ;
319
+ let mut memory_map = memory_map_arc. lock ( ) . unwrap ( ) ;
320
+ * memory_map = Some ( m) ;
321
+ current_map = ( false , String :: new ( ) ) ;
322
+ }
323
+ }
302
324
303
325
if let Some ( value) = kv. get ( "value" ) {
304
326
// This works b/c we only use this for PC, but will most likely
@@ -375,8 +397,23 @@ fn gdb_interact(
375
397
}
376
398
}
377
399
MIResponse :: StreamOutput ( t, s) => {
378
- // let split: Vec<String> = s.to_string().split('\n').collect();
379
- let mut split: Vec < String > =
400
+ // when we find the start of a memory map, we sent this
401
+ // and it's quite noisy to the regular output so don't
402
+ // include
403
+ if s. starts_with ( "process" ) || s. starts_with ( "Mapped address spaces:" ) {
404
+ // HACK: completely skip the following, as they are a side
405
+ // effect of not having a GDB MI way of getting a memory map
406
+ continue ;
407
+ }
408
+ if s. trim_end ( ) == MEMORY_MAP_START_STR {
409
+ current_map. 0 = true ;
410
+ }
411
+ if current_map. 0 {
412
+ current_map. 1 . push_str ( s) ;
413
+ continue ;
414
+ }
415
+
416
+ let split: Vec < String > =
380
417
s. split ( '\n' ) . map ( String :: from) . map ( |a| a. trim_end ( ) . to_string ( ) ) . collect ( ) ;
381
418
for s in split {
382
419
if !s. is_empty ( ) {
@@ -401,16 +438,20 @@ fn gdb_interact(
401
438
}
402
439
if !next_write. is_empty ( ) {
403
440
for w in & next_write {
404
- let mut stdin = gdb_stdin_arc. lock ( ) . unwrap ( ) ;
405
- debug ! ( "writing {}" , w) ;
406
- writeln ! ( stdin, "{}" , w) . expect ( "Failed to send command" ) ;
441
+ write_mi ( & gdb_stdin_arc, w) ;
407
442
}
408
443
next_write. clear ( ) ;
409
444
}
410
445
}
411
446
}
412
447
}
413
448
449
+ fn write_mi ( gdb_stdin_arc : & Arc < Mutex < dyn Write + Send > > , w : & String ) {
450
+ let mut stdin = gdb_stdin_arc. lock ( ) . unwrap ( ) ;
451
+ debug ! ( "writing {}" , w) ;
452
+ writeln ! ( stdin, "{}" , w) . expect ( "Failed to send command" ) ;
453
+ }
454
+
414
455
fn run_app < B : Backend > ( terminal : & mut Terminal < B > , app : & mut App ) -> io:: Result < ( ) > {
415
456
loop {
416
457
terminal. draw ( |f| ui ( f, app) ) ?;
@@ -841,16 +882,49 @@ fn draw_registers(app: &App, f: &mut Frame, register: Rect) {
841
882
if let Ok ( regs) = app. registers . lock ( ) {
842
883
for ( i, ( name, register) ) in regs. iter ( ) . enumerate ( ) {
843
884
if let Some ( reg) = register {
844
- if reg. value == Some ( "<unavailable>" . to_string ( ) ) {
845
- continue ;
846
- }
847
- let changed = reg_changed_lock. contains ( & ( i as u8 ) ) ;
848
- let mut addr = Cell :: from ( name. to_string ( ) ) . style ( Style :: new ( ) . fg ( PURPLE ) ) ;
849
- let val = Cell :: from ( reg. value . clone ( ) . unwrap ( ) ) ;
850
- if changed {
851
- addr = addr. style ( Style :: new ( ) . fg ( RED ) ) ;
885
+ if let Some ( reg_value) = & reg. value {
886
+ if reg_value == & "<unavailable>" . to_string ( ) {
887
+ continue ;
888
+ }
889
+ let val = u64:: from_str_radix ( & reg_value[ 2 ..] , 16 ) . unwrap ( ) ;
890
+ let changed = reg_changed_lock. contains ( & ( i as u8 ) ) ;
891
+ let mut addr = Cell :: from ( name. to_string ( ) ) . style ( Style :: new ( ) . fg ( PURPLE ) ) ;
892
+
893
+ let mut is_stack = false ;
894
+ let mut is_heap = false ;
895
+ if val != 0 {
896
+ // look through, add see if the value is part of the stack
897
+ let memory_map = app. memory_map . lock ( ) . unwrap ( ) ;
898
+ if memory_map. is_some ( ) {
899
+ for r in memory_map. as_ref ( ) . unwrap ( ) {
900
+ if r. is_stack ( ) {
901
+ if r. contains ( val) {
902
+ is_stack = true ;
903
+ break ;
904
+ }
905
+ }
906
+ if r. is_heap ( ) {
907
+ if r. contains ( val) {
908
+ is_heap = true ;
909
+ break ;
910
+ }
911
+ }
912
+ }
913
+ }
914
+ }
915
+
916
+ let mut val = Cell :: from ( reg. value . clone ( ) . unwrap ( ) ) ;
917
+ if is_stack {
918
+ val = val. style ( Style :: new ( ) . fg ( BLUE ) )
919
+ }
920
+ if is_heap {
921
+ val = val. style ( Style :: new ( ) . fg ( PURPLE ) )
922
+ }
923
+ if changed {
924
+ addr = addr. style ( Style :: new ( ) . fg ( RED ) ) ;
925
+ }
926
+ rows. push ( Row :: new ( vec ! [ addr, val] ) ) ;
852
927
}
853
- rows. push ( Row :: new ( vec ! [ addr, val] ) ) ;
854
928
}
855
929
}
856
930
}
0 commit comments