Skip to content

Commit 16396be

Browse files
committed
Support arbitrary sized watchpoints
In LLDB command line you can specify any variable and it will attempt to create a series of watchpoints, regardless of the size of the requested variable or range. We do the same: if a non-native size is requested, then create N watchpoints of word-size contiguously from the base address. If any one of them fails, delete all of them.
1 parent bafd86a commit 16396be

File tree

1 file changed

+70
-58
lines changed

1 file changed

+70
-58
lines changed

adapter/codelldb/src/debug_session.rs

Lines changed: 70 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1033,27 +1033,19 @@ impl DebugSession {
10331033
if let Some(child) = child {
10341034
let addr = child.load_address();
10351035
if addr != lldb::INVALID_ADDRESS {
1036-
let size = child.byte_size();
1037-
if self.is_valid_watchpoint_size(size) {
1038-
let data_id = format!("{}/{}", addr, size);
1039-
let desc = child.name().unwrap_or("");
1040-
Ok(DataBreakpointInfoResponseBody {
1041-
data_id: Some(data_id),
1042-
access_types: Some(vec![
1043-
DataBreakpointAccessType::Read,
1044-
DataBreakpointAccessType::Write,
1045-
DataBreakpointAccessType::ReadWrite,
1046-
]),
1047-
description: format!("{} bytes at {:X} ({})", size, addr, desc),
1048-
..Default::default()
1049-
})
1050-
} else {
1051-
Ok(DataBreakpointInfoResponseBody {
1052-
data_id: None,
1053-
description: "Invalid watchpoint size.".into(),
1054-
..Default::default()
1055-
})
1056-
}
1036+
let size = args.bytes.unwrap_or( child.byte_size() as i64 ) as usize;
1037+
let data_id = format!("{}/{}", addr, size);
1038+
let desc = child.name().unwrap_or("");
1039+
Ok(DataBreakpointInfoResponseBody {
1040+
data_id: Some(data_id),
1041+
access_types: Some(vec![
1042+
DataBreakpointAccessType::Read,
1043+
DataBreakpointAccessType::Write,
1044+
DataBreakpointAccessType::ReadWrite,
1045+
]),
1046+
description: format!("{} bytes at {:X} ({})", size, addr, desc),
1047+
..Default::default()
1048+
})
10571049
} else {
10581050
Ok(DataBreakpointInfoResponseBody {
10591051
data_id: None,
@@ -1099,12 +1091,6 @@ impl DebugSession {
10991091
description: format!("Invalid address {}", addr),
11001092
..Default::default()
11011093
})
1102-
} else if self.is_valid_watchpoint_size(size) {
1103-
Ok(DataBreakpointInfoResponseBody {
1104-
data_id: None,
1105-
description: format!("Invalid size {} for watchpoint", size),
1106-
..Default::default()
1107-
})
11081094
} else {
11091095
Ok(DataBreakpointInfoResponseBody {
11101096
data_id: Some(format!("{}/{}", addr, size)),
@@ -1124,26 +1110,18 @@ impl DebugSession {
11241110
let addr = result.load_address();
11251111
if addr != lldb::INVALID_ADDRESS {
11261112
let size = args.bytes.unwrap_or(result.byte_size() as i64) as usize;
1127-
if self.is_valid_watchpoint_size(size) {
1128-
let data_id = format!("{}/{}", addr, size);
1129-
let desc = result.name().unwrap_or(expr);
1130-
Ok(DataBreakpointInfoResponseBody {
1131-
data_id: Some(data_id),
1132-
access_types: Some(vec![
1133-
DataBreakpointAccessType::Read,
1134-
DataBreakpointAccessType::Write,
1135-
DataBreakpointAccessType::ReadWrite,
1136-
]),
1137-
description: format!("{} bytes at {:X} ({})", size, addr, desc),
1138-
..Default::default()
1139-
})
1140-
} else {
1141-
Ok(DataBreakpointInfoResponseBody {
1142-
data_id: None,
1143-
description: format!("Expression '{}' results in invalid watchpoint size: {}.", expr, size),
1144-
..Default::default()
1145-
})
1146-
}
1113+
let data_id = format!("{}/{}", addr, size);
1114+
let desc = result.name().unwrap_or(expr);
1115+
Ok(DataBreakpointInfoResponseBody {
1116+
data_id: Some(data_id),
1117+
access_types: Some(vec![
1118+
DataBreakpointAccessType::Read,
1119+
DataBreakpointAccessType::Write,
1120+
DataBreakpointAccessType::ReadWrite,
1121+
]),
1122+
description: format!("{} bytes at {:X} ({})", size, addr, desc),
1123+
..Default::default()
1124+
})
11471125
} else {
11481126
Ok(DataBreakpointInfoResponseBody {
11491127
data_id: None,
@@ -1192,18 +1170,52 @@ impl DebugSession {
11921170
(true, true) => "read and write",
11931171
_ => unreachable!(),
11941172
};
1195-
let res = match self.target.watch_address(addr, size, read, write) {
1196-
Ok(_wp) => Breakpoint {
1197-
verified: true,
1198-
message: Some(format!("Break on {}", when)),
1199-
..Default::default()
1200-
},
1201-
Err(err) => Breakpoint {
1202-
verified: false,
1203-
message: Some(err.to_string()),
1204-
..Default::default()
1205-
},
1173+
1174+
// In LLDB, if you ask for a watchpoint on a variable (watch
1175+
// set variable foo), and foo's size > the hardware watchpoint size
1176+
// (e.g. 8 bytes), it actually creates N watchpoints, each of size 8
1177+
// bytes, to cover the entire size of 'foo'. We don't implement that
1178+
// here, rather requiring the user to manually add watchpoints to
1179+
// each word. So we do the same.
1180+
let (required_watchpoints, wp_size) = if self.is_valid_watchpoint_size(size) {
1181+
(1, size)
1182+
} else {
1183+
((size + self.target.address_byte_size() - 1) / self.target.address_byte_size(),
1184+
self.target.address_byte_size())
1185+
};
1186+
1187+
let mut res = Breakpoint {
1188+
verified: true,
1189+
message: Some(format!("{} watchpoints on {} to {} bytes at {}", required_watchpoints, when, size, addr)),
1190+
..Default::default()
12061191
};
1192+
1193+
let mut wps = vec![];
1194+
for i in 0..required_watchpoints {
1195+
let offset = (self.target.address_byte_size() * i as usize) as u64;
1196+
match self.target.watch_address(addr + offset, wp_size, read, write) {
1197+
Ok(wp) => wps.push(wp),
1198+
Err(err) => {
1199+
res = Breakpoint {
1200+
verified: false,
1201+
message: Some(err.to_string()),
1202+
..Default::default()
1203+
};
1204+
break;
1205+
}
1206+
};
1207+
}
1208+
1209+
// Undo on partial failure
1210+
// If we need to create N watchpoints, then we should do so
1211+
// atomically, i.e. if any of them fail, we should remove the ones
1212+
// that succeeded
1213+
if !res.verified {
1214+
for wp in wps {
1215+
self.target.delete_watchpoint(wp.id());
1216+
}
1217+
}
1218+
12071219
watchpoints.push(res);
12081220
}
12091221
Ok(SetDataBreakpointsResponseBody {

0 commit comments

Comments
 (0)