1
1
use std:: { collections:: HashMap , fmt, process:: Stdio , time:: Duration } ;
2
2
3
+ use breakpoint:: { Breakpoint , LineSpec } ;
3
4
use camino:: { Utf8Path , Utf8PathBuf } ;
4
5
use rand:: Rng ;
5
6
use tokio:: { io, process} ;
6
7
use tracing:: { debug, error} ;
7
8
9
+ pub mod breakpoint;
8
10
mod inner;
9
11
pub mod parser;
10
12
pub mod raw;
@@ -19,7 +21,7 @@ use crate::symbol::Symbol;
19
21
mod test_common;
20
22
21
23
#[ derive( Debug , Clone , thiserror:: Error , Eq , PartialEq ) ]
22
- pub enum ResponseError {
24
+ pub enum Error {
23
25
#[ error( transparent) ]
24
26
Gdb ( #[ from] GdbError ) ,
25
27
@@ -33,9 +35,12 @@ pub enum ResponseError {
33
35
#[ error( "Expected response to have a payload" ) ]
34
36
ExpectedPayload ,
35
37
36
- #[ error( "Failed to parse payload value as u32, got: {0} " ) ]
38
+ #[ error( "Failed to parse payload value as u32" ) ]
37
39
ParseU32 ( #[ from] std:: num:: ParseIntError ) ,
38
40
41
+ #[ error( "Failed to parse payload value as hex" ) ]
42
+ ParseHex ( #[ from] ParseHexError ) ,
43
+
39
44
#[ error( "Expected response to have message {expected}, got {actual}" ) ]
40
45
UnexpectedResponseMessage { expected : String , actual : String } ,
41
46
@@ -50,6 +55,14 @@ pub struct GdbError {
50
55
msg : String ,
51
56
}
52
57
58
+ #[ derive( Debug , Clone , thiserror:: Error , Eq , PartialEq ) ]
59
+ pub enum ParseHexError {
60
+ #[ error( "Expected to start with 0x" ) ]
61
+ InvalidPrefix ,
62
+ #[ error( transparent) ]
63
+ ParseInt ( #[ from] std:: num:: ParseIntError ) ,
64
+ }
65
+
53
66
pub struct Gdb {
54
67
inner : Inner ,
55
68
timeout : Duration ,
@@ -91,16 +104,56 @@ impl Gdb {
91
104
Ok ( Self { inner, timeout } )
92
105
}
93
106
94
- pub async fn run ( & self ) -> Result < ( ) , ResponseError > {
107
+ pub async fn run ( & self ) -> Result < ( ) , Error > {
95
108
self . execute_raw ( "-exec-run" )
96
109
. await ?
97
110
. expect_result ( ) ?
98
111
. expect_msg_is ( "running" )
99
112
}
100
113
101
- pub async fn symbol_info_functions (
102
- & self ,
103
- ) -> Result < HashMap < Utf8PathBuf , Vec < Symbol > > , ResponseError > {
114
+ pub async fn break_insert ( & self , at : LineSpec ) -> Result < Breakpoint , Error > {
115
+ let raw = self
116
+ . execute_raw ( format ! ( "-break-insert {}" , at. serialize( ) ) )
117
+ . await ?
118
+ . expect_result ( ) ?
119
+ . expect_payload ( ) ?
120
+ . remove_expect ( "bkpt" ) ?
121
+ . expect_dict ( ) ?;
122
+
123
+ Breakpoint :: from_raw ( raw)
124
+ }
125
+
126
+ pub async fn break_disable < ' a , I > ( & self , breakpoints : I ) -> Result < ( ) , Error >
127
+ where
128
+ I : IntoIterator < Item = & ' a Breakpoint > ,
129
+ {
130
+ let mut raw = String :: new ( ) ;
131
+ for bp in breakpoints {
132
+ raw. push_str ( & format ! ( "{} " , bp. number) ) ;
133
+ }
134
+
135
+ self . execute_raw ( format ! ( "-break-disable {}" , raw) )
136
+ . await ?
137
+ . expect_result ( ) ?
138
+ . expect_msg_is ( "done" )
139
+ }
140
+
141
+ pub async fn break_delete < ' a , I > ( & self , breakpoints : I ) -> Result < ( ) , Error >
142
+ where
143
+ I : IntoIterator < Item = & ' a Breakpoint > ,
144
+ {
145
+ let mut raw = String :: new ( ) ;
146
+ for bp in breakpoints {
147
+ raw. push_str ( & format ! ( "{} " , bp. number) ) ;
148
+ }
149
+
150
+ self . execute_raw ( format ! ( "-break-delete {}" , raw) )
151
+ . await ?
152
+ . expect_result ( ) ?
153
+ . expect_msg_is ( "done" )
154
+ }
155
+
156
+ pub async fn symbol_info_functions ( & self ) -> Result < HashMap < Utf8PathBuf , Vec < Symbol > > , Error > {
104
157
let payload = self
105
158
. execute_raw ( "-symbol-info-functions" )
106
159
. await ?
@@ -112,17 +165,14 @@ impl Gdb {
112
165
/// Execute a command for a response.
113
166
///
114
167
/// Your command will be prefixed with a token and suffixed with a newline.
115
- pub async fn execute_raw (
116
- & self ,
117
- msg : impl Into < String > ,
118
- ) -> Result < raw:: Response , ResponseError > {
168
+ pub async fn execute_raw ( & self , msg : impl Into < String > ) -> Result < raw:: Response , Error > {
119
169
self . inner . execute ( msg. into ( ) , self . timeout ) . await
120
170
}
121
171
122
172
/// Waits until gdb is responsive to commands.
123
173
///
124
174
/// You do not need to call this before sending commands yourself.
125
- pub async fn await_ready ( & self ) -> Result < ( ) , ResponseError > {
175
+ pub async fn await_ready ( & self ) -> Result < ( ) , Error > {
126
176
self . execute_raw ( "-list-target-features" ) . await ?;
127
177
Ok ( ( ) )
128
178
}
@@ -155,7 +205,7 @@ impl Token {
155
205
156
206
#[ cfg( test) ]
157
207
mod tests {
158
- use std:: collections:: BTreeMap ;
208
+ use std:: { collections:: BTreeMap , iter } ;
159
209
160
210
use super :: * ;
161
211
use insta:: assert_debug_snapshot;
@@ -167,6 +217,24 @@ mod tests {
167
217
Ok ( Gdb :: spawn ( bin, TIMEOUT ) ?)
168
218
}
169
219
220
+ #[ tokio:: test]
221
+ async fn test_break ( ) -> Result {
222
+ let subject = fixture ( ) ?;
223
+
224
+ let bp = subject
225
+ . break_insert ( LineSpec :: line ( "samples/hello_world/src/main.rs" , 13 ) )
226
+ . await ?;
227
+ assert_eq ! ( 1 , bp. number) ;
228
+ assert ! ( bp. file. ends_with( "samples/hello_world/src/main.rs" ) ) ;
229
+ assert_eq ! ( 13 , bp. line) ;
230
+ assert_eq ! ( 0 , bp. times) ;
231
+
232
+ subject. break_disable ( iter:: once ( & bp) ) . await ?;
233
+ subject. break_delete ( iter:: once ( & bp) ) . await ?;
234
+
235
+ Ok ( ( ) )
236
+ }
237
+
170
238
#[ tokio:: test]
171
239
async fn test_run ( ) -> Result {
172
240
let subject = fixture ( ) ?;
@@ -206,7 +274,7 @@ mod tests {
206
274
let err = subject. execute_raw ( "-invalid-command" ) . await . unwrap_err ( ) ;
207
275
208
276
assert_eq ! (
209
- ResponseError :: Gdb ( GdbError {
277
+ Error :: Gdb ( GdbError {
210
278
code: "undefined-command" . into( ) ,
211
279
msg: "Undefined MI command: invalid-command" . into( ) ,
212
280
} ) ,
0 commit comments