Skip to content

Commit b4f1b9b

Browse files
committed
Fix edge cases for breakpoints, add continue
1 parent bc20da4 commit b4f1b9b

File tree

5 files changed

+128
-32
lines changed

5 files changed

+128
-32
lines changed

samples/hello_world/src/main.rs

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ struct HelloMsg {
66
msg: String,
77
}
88

9+
impl HelloMsg {
10+
fn say(&self) {
11+
println!("{}", self.msg);
12+
}
13+
}
14+
915
fn main() {
1016
let msg = HelloMsg {
1117
msg: "Hello world!".into(),
1218
};
1319
thread::sleep(Duration::from_secs(1));
14-
println!("{}", msg.msg);
20+
msg.say();
1521
}

src/breakpoint.rs

+33-6
Original file line numberDiff line numberDiff line change
@@ -40,24 +40,51 @@ impl LineSpec {
4040
}
4141
}
4242

43+
// TODO: This doesn't include all the potential outputs of gdb.
4344
#[derive(Debug, Clone, Eq, PartialEq)]
4445
pub struct Breakpoint {
4546
pub number: u32,
46-
pub addr: u64,
47-
pub file: Utf8PathBuf,
48-
pub line: u32,
47+
pub addr: BreakpointAddr,
48+
pub file: Option<Utf8PathBuf>,
49+
pub line: Option<u32>,
4950
pub thread_groups: Vec<String>,
5051
pub times: u32,
5152
}
5253

54+
#[derive(Debug, Clone, Eq, PartialEq)]
55+
pub enum BreakpointAddr {
56+
Addr(u64),
57+
Pending,
58+
Multiple,
59+
Unknown,
60+
}
61+
5362
impl Breakpoint {
5463
pub fn from_raw(mut raw: raw::Dict) -> Result<Self, Error> {
5564
let number = raw.remove_expect("number")?.expect_number()?;
56-
let addr = raw.remove_expect("addr")?.expect_hex()?;
57-
let file = raw.remove_expect("fullname")?.expect_path()?;
58-
let line = raw.remove_expect("line")?.expect_number()?;
5965
let times = raw.remove_expect("times")?.expect_number()?;
6066

67+
let line = raw
68+
.remove("line")
69+
.map(raw::Value::expect_number)
70+
.transpose()?;
71+
72+
let file = raw
73+
.remove("fullname")
74+
.map(raw::Value::expect_path)
75+
.transpose()?;
76+
77+
let addr = if let Some(addr) = raw.as_map_mut().remove("addr") {
78+
let addr = addr.expect_string()?;
79+
match addr.as_str() {
80+
"<PENDING>" => BreakpointAddr::Pending,
81+
"<MULTIPLE>" => BreakpointAddr::Multiple,
82+
addr => BreakpointAddr::Addr(raw::parse_hex(addr)?),
83+
}
84+
} else {
85+
BreakpointAddr::Unknown
86+
};
87+
6188
let thread_groups = raw
6289
.remove_expect("thread-groups")?
6390
.expect_list()?

src/lib.rs

+50-10
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,10 @@ pub enum Error {
4949
}
5050

5151
#[derive(Debug, Clone, thiserror::Error, displaydoc::Display, Eq, PartialEq)]
52-
/// Received error {code} from gdb: {msg}
52+
/// Received error from gdb. Code: {code:?}, msg: {msg:?}
5353
pub struct GdbError {
54-
code: String,
55-
msg: String,
54+
code: Option<String>,
55+
msg: Option<String>,
5656
}
5757

5858
#[derive(Debug, Clone, thiserror::Error, Eq, PartialEq)]
@@ -104,13 +104,27 @@ impl Gdb {
104104
Ok(Self { inner, timeout })
105105
}
106106

107-
pub async fn run(&self) -> Result<(), Error> {
107+
pub async fn exec_run(&self) -> Result<(), Error> {
108108
self.execute_raw("-exec-run")
109109
.await?
110110
.expect_result()?
111111
.expect_msg_is("running")
112112
}
113113

114+
pub async fn exec_continue(&self) -> Result<(), Error> {
115+
self.execute_raw("-exec-continue")
116+
.await?
117+
.expect_result()?
118+
.expect_msg_is("running")
119+
}
120+
121+
pub async fn exec_continue_reverse(&self) -> Result<(), Error> {
122+
self.execute_raw("-exec-continue --reverse")
123+
.await?
124+
.expect_result()?
125+
.expect_msg_is("running")
126+
}
127+
114128
pub async fn break_insert(&self, at: LineSpec) -> Result<Breakpoint, Error> {
115129
let raw = self
116130
.execute_raw(format!("-break-insert {}", at.serialize()))
@@ -190,6 +204,7 @@ impl fmt::Debug for Gdb {
190204
}
191205
}
192206

207+
// TODO: Move to inner
193208
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
194209
pub struct Token(pub u32);
195210

@@ -209,6 +224,7 @@ mod tests {
209224

210225
use super::*;
211226
use insta::assert_debug_snapshot;
227+
use pretty_assertions::assert_eq;
212228
use test_common::{build_hello_world, init, Result, TIMEOUT};
213229

214230
fn fixture() -> eyre::Result<Gdb> {
@@ -225,8 +241,12 @@ mod tests {
225241
.break_insert(LineSpec::line("samples/hello_world/src/main.rs", 13))
226242
.await?;
227243
assert_eq!(1, bp.number);
228-
assert!(bp.file.ends_with("samples/hello_world/src/main.rs"));
229-
assert_eq!(13, bp.line);
244+
assert!(bp
245+
.file
246+
.as_ref()
247+
.unwrap()
248+
.ends_with("samples/hello_world/src/main.rs"));
249+
assert_eq!(17, bp.line.unwrap());
230250
assert_eq!(0, bp.times);
231251

232252
subject.break_disable(iter::once(&bp)).await?;
@@ -236,9 +256,29 @@ mod tests {
236256
}
237257

238258
#[tokio::test]
239-
async fn test_run() -> Result {
259+
async fn test_exec_continue() -> Result {
260+
let subject = fixture()?;
261+
subject.break_insert(LineSpec::function("main")).await?;
262+
subject.exec_run().await?;
263+
subject.exec_continue().await?;
264+
Ok(())
265+
}
266+
267+
#[tokio::test]
268+
async fn test_exec_continue_not_running() -> Result {
269+
let subject = fixture()?;
270+
let error = match subject.exec_continue().await {
271+
Err(Error::Gdb(error)) => error,
272+
got => panic!("Expected Error::Gdb, got {:?}", got),
273+
};
274+
assert_eq!(error.msg.unwrap(), "The program is not being run.");
275+
Ok(())
276+
}
277+
278+
#[tokio::test]
279+
async fn test_exec_run() -> Result {
240280
let subject = fixture()?;
241-
subject.run().await?;
281+
subject.exec_run().await?;
242282
Ok(())
243283
}
244284

@@ -275,8 +315,8 @@ mod tests {
275315

276316
assert_eq!(
277317
Error::Gdb(GdbError {
278-
code: "undefined-command".into(),
279-
msg: "Undefined MI command: invalid-command".into(),
318+
code: Some("undefined-command".into()),
319+
msg: Some("Undefined MI command: invalid-command".into()),
280320
}),
281321
err
282322
);

src/raw.rs

+31-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::collections::HashMap;
22

33
use camino::Utf8PathBuf;
4-
use tracing::error;
4+
use tracing::{error, warn};
55

66
use crate::{parser, Error, GdbError, ParseHexError};
77

@@ -89,8 +89,14 @@ impl Response {
8989
return Err(Error::ExpectedPayload);
9090
};
9191

92-
let code = payload.remove_expect("code")?.expect_string()?;
93-
let msg = payload.remove_expect("msg")?.expect_string()?;
92+
let code = payload
93+
.remove("code")
94+
.map(Value::expect_string)
95+
.transpose()?;
96+
let msg = payload
97+
.remove("msg")
98+
.map(Value::expect_string)
99+
.transpose()?;
94100

95101
Ok(GdbError { code, msg })
96102
}
@@ -126,10 +132,18 @@ impl Dict {
126132
&mut self.0
127133
}
128134

129-
pub fn remove_expect(&mut self, key: &str) -> std::result::Result<Value, Error> {
130-
self.0
131-
.remove(key)
132-
.map_or(Err(Error::ExpectedDifferentPayload), Ok)
135+
pub fn remove_expect(&mut self, key: &str) -> Result<Value, Error> {
136+
self.0.remove(key).map_or_else(
137+
|| {
138+
warn!("Expected key {} to be present in {:?}", key, self);
139+
Err(Error::ExpectedDifferentPayload)
140+
},
141+
Ok,
142+
)
143+
}
144+
145+
pub fn remove(&mut self, key: &str) -> Option<Value> {
146+
self.0.remove(key)
133147
}
134148
}
135149

@@ -202,12 +216,15 @@ impl Value {
202216
}
203217

204218
pub fn expect_hex(self) -> Result<u64, Error> {
205-
let hex = self.expect_string()?;
206-
if let Some(hex) = hex.strip_prefix("0x") {
207-
let num = u64::from_str_radix(hex, 16)?;
208-
Ok(num)
209-
} else {
210-
Err(ParseHexError::InvalidPrefix.into())
211-
}
219+
parse_hex(&self.expect_string()?)
220+
}
221+
}
222+
223+
pub fn parse_hex(s: &str) -> Result<u64, Error> {
224+
if let Some(hex) = s.strip_prefix("0x") {
225+
let num = u64::from_str_radix(hex, 16)?;
226+
Ok(num)
227+
} else {
228+
Err(ParseHexError::InvalidPrefix.into())
212229
}
213230
}

src/snapshots/gdbmi__tests__symbol_info_function.snap

+7-1
Original file line numberDiff line numberDiff line change
@@ -2418,7 +2418,13 @@ expression: symbols
24182418
],
24192419
"src/main.rs": [
24202420
Symbol {
2421-
line: 9,
2421+
line: 10,
2422+
name: "hello_world::HelloMsg::say",
2423+
symbol_type: "fn (*mut hello_world::HelloMsg)",
2424+
description: "static fn hello_world::HelloMsg::say(*mut hello_world::HelloMsg);",
2425+
},
2426+
Symbol {
2427+
line: 15,
24222428
name: "hello_world::main",
24232429
symbol_type: "fn ()",
24242430
description: "static fn hello_world::main();",

0 commit comments

Comments
 (0)