GP-1364: Fixed parsing issues for several breakpoint types. Added CATCHPOINT

This commit is contained in:
Dan 2021-10-06 11:30:26 -04:00
parent 1a15d7893a
commit 5e324104a5
5 changed files with 116 additions and 36 deletions

View file

@ -24,10 +24,12 @@ import agent.gdb.manager.parsing.GdbParsingUtils;
/** /**
* Information about a GDB breakpoint * Information about a GDB breakpoint
* *
* The contains the semantic processing for GDB breakpoint information. Mostly, it just stores the * <p>
* This contains the semantic processing for GDB breakpoint information. Mostly, it just stores the
* information, but it also enumerates the locations of a breakpoint and generates the "effective" * information, but it also enumerates the locations of a breakpoint and generates the "effective"
* breakpoints. * breakpoints.
* *
* <p>
* Note this is not a handle to the breakpoint. Rather, this is the captured information from some * Note this is not a handle to the breakpoint. Rather, this is the captured information from some
* event or request. If other commands have been executed since this information was gathered, the * event or request. If other commands have been executed since this information was gathered, the
* information may be stale. * information may be stale.
@ -48,8 +50,10 @@ public class GdbBreakpointInfo {
/** /**
* Process a parsed GDB breakpoint information block * Process a parsed GDB breakpoint information block
* *
* <p>
* The passed info should be the field list containing "{@code bkpt={...}}." * The passed info should be the field list containing "{@code bkpt={...}}."
* *
* <p>
* It may also contain {@code wpt}, {@code hw-awpt}, or {@code hw-rwpt}, in which case the * It may also contain {@code wpt}, {@code hw-awpt}, or {@code hw-rwpt}, in which case the
* "parsed info" is synthesized to match what would be given by {@code -break-list} for that * "parsed info" is synthesized to match what would be given by {@code -break-list} for that
* watchpoint. * watchpoint.
@ -65,15 +69,18 @@ public class GdbBreakpointInfo {
} }
GdbMiFieldList wpt = info.getFieldList("wpt"); GdbMiFieldList wpt = info.getFieldList("wpt");
if (wpt != null) { if (wpt != null) {
return parseWpt(wpt, GdbBreakpointType.HW_WATCHPOINT, curIid); return parseWpt(wpt, GdbBreakpointType.HW_WATCHPOINT,
GdbBreakpointType.HW_WATCHPOINT.getName(), curIid);
} }
GdbMiFieldList hwAWpt = info.getFieldList("hw-awpt"); GdbMiFieldList hwAWpt = info.getFieldList("hw-awpt");
if (hwAWpt != null) { if (hwAWpt != null) {
return parseWpt(hwAWpt, GdbBreakpointType.ACCESS_WATCHPOINT, curIid); return parseWpt(hwAWpt, GdbBreakpointType.ACCESS_WATCHPOINT,
GdbBreakpointType.ACCESS_WATCHPOINT.getName(), curIid);
} }
GdbMiFieldList hwRWpt = info.getFieldList("hw-rwpt"); GdbMiFieldList hwRWpt = info.getFieldList("hw-rwpt");
if (hwRWpt != null) { if (hwRWpt != null) {
return parseWpt(hwRWpt, GdbBreakpointType.READ_WATCHPOINT, curIid); return parseWpt(hwRWpt, GdbBreakpointType.READ_WATCHPOINT,
GdbBreakpointType.READ_WATCHPOINT.getName(), curIid);
} }
throw new AssertionError("No breakpoint or watchpoint in info: " + info); throw new AssertionError("No breakpoint or watchpoint in info: " + info);
} }
@ -88,21 +95,27 @@ public class GdbBreakpointInfo {
*/ */
public static GdbBreakpointInfo parseBkpt(GdbMiFieldList bkpt, public static GdbBreakpointInfo parseBkpt(GdbMiFieldList bkpt,
List<GdbBreakpointLocation> allLocs, Integer curIid) { List<GdbBreakpointLocation> allLocs, Integer curIid) {
// TODO: A more polymorphic approach to info types?
long number = Long.parseLong(bkpt.getString("number")); long number = Long.parseLong(bkpt.getString("number"));
GdbBreakpointType type = GdbBreakpointType.fromStr(bkpt.getString("type")); String typeName = bkpt.getString("type");
GdbBreakpointType type = GdbBreakpointType.fromStr(typeName);
GdbBreakpointDisp disp = GdbBreakpointDisp.fromStr(bkpt.getString("disp")); GdbBreakpointDisp disp = GdbBreakpointDisp.fromStr(bkpt.getString("disp"));
boolean enabled = "y".equals(bkpt.getString("enabled")); boolean enabled = "y".equals(bkpt.getString("enabled"));
String addr = bkpt.getString("addr"); String addr = bkpt.getString("addr");
String at = bkpt.getString("at"); String what = bkpt.getString("at");
if (at == null) { if (what == null) {
at = bkpt.getString("what"); what = bkpt.getString("what");
} }
String catchType = bkpt.getString("catch-type");
String origLoc = bkpt.getString("original-location"); String origLoc = bkpt.getString("original-location");
String pending = bkpt.getString("pending"); String pending = bkpt.getString("pending");
int times = Integer.parseInt(bkpt.getString("times")); int times = Integer.parseInt(bkpt.getString("times"));
List<GdbBreakpointLocation> locations = new ArrayList<>(); List<GdbBreakpointLocation> locations = new ArrayList<>();
if ("<MULTIPLE>".equals(addr)) { if (type == GdbBreakpointType.CATCHPOINT) {
// no locations
}
else if ("<MULTIPLE>".equals(addr)) {
allLocs.stream().filter(l -> l.getNumber() == number).forEachOrdered(locations::add); allLocs.stream().filter(l -> l.getNumber() == number).forEachOrdered(locations::add);
} }
else { else {
@ -112,8 +125,8 @@ public class GdbBreakpointInfo {
} }
locations.add(new GdbBreakpointLocation(number, 1, true, addr, iids)); locations.add(new GdbBreakpointLocation(number, 1, true, addr, iids));
} }
return new GdbBreakpointInfo(number, type, disp, addr, at, origLoc, pending, enabled, times, return new GdbBreakpointInfo(number, type, typeName, disp, addr, what, catchType, origLoc,
locations); pending, enabled, times, locations);
} }
/** /**
@ -125,7 +138,7 @@ public class GdbBreakpointInfo {
* @return the info * @return the info
*/ */
public static GdbBreakpointInfo parseWpt(GdbMiFieldList wpt, GdbBreakpointType type, public static GdbBreakpointInfo parseWpt(GdbMiFieldList wpt, GdbBreakpointType type,
int curIid) { String typeName, int curIid) {
int number = Integer.parseInt(wpt.getString("number")); int number = Integer.parseInt(wpt.getString("number"));
String origLoc = wpt.getString("exp"); String origLoc = wpt.getString("exp");
@ -136,8 +149,8 @@ public class GdbBreakpointInfo {
else { else {
locs = List.of(); locs = List.of();
} }
return new GdbBreakpointInfo(number, type, GdbBreakpointDisp.KEEP, null, origLoc, origLoc, return new GdbBreakpointInfo(number, type, typeName, GdbBreakpointDisp.KEEP, null, null,
null, true, 0, locs); origLoc, origLoc, null, true, 0, locs);
} }
/** /**
@ -165,9 +178,11 @@ public class GdbBreakpointInfo {
private final long number; private final long number;
private final GdbBreakpointType type; private final GdbBreakpointType type;
private final String typeName;
private final GdbBreakpointDisp disp; private final GdbBreakpointDisp disp;
private final String addr; private final String addr;
private final String at; private final String what;
private final String catchType;
private final String originalLocation; private final String originalLocation;
private final String pending; private final String pending;
private final boolean enabled; private final boolean enabled;
@ -181,19 +196,22 @@ public class GdbBreakpointInfo {
* @param type the type of breakpoint * @param type the type of breakpoint
* @param disp the breakpoint disposition * @param disp the breakpoint disposition
* @param addr the location of the breakpoint * @param addr the location of the breakpoint
* @param what the "what" of the breakpoint
* @param pending if pending, the location that is not yet resolved * @param pending if pending, the location that is not yet resolved
* @param enabled true if the breakpoint is enabled, false otherwise * @param enabled true if the breakpoint is enabled, false otherwise
* @param times the number of times the breakpoint has been hit * @param times the number of times the breakpoint has been hit
* @param locations the resolved address(es) of this breakpoint * @param locations the resolved address(es) of this breakpoint
*/ */
GdbBreakpointInfo(long number, GdbBreakpointType type, GdbBreakpointDisp disp, String addr, GdbBreakpointInfo(long number, GdbBreakpointType type, String typeName, GdbBreakpointDisp disp,
String at, String origLoc, String pending, boolean enabled, int times, String addr, String what, String catchType, String origLoc, String pending,
List<GdbBreakpointLocation> locations) { boolean enabled, int times, List<GdbBreakpointLocation> locations) {
this.number = number; this.number = number;
this.type = type; this.type = type;
this.typeName = typeName;
this.disp = disp; this.disp = disp;
this.addr = addr; this.addr = addr;
this.at = at; this.what = what;
this.catchType = catchType;
this.originalLocation = origLoc; this.originalLocation = origLoc;
this.pending = pending; this.pending = pending;
this.enabled = enabled; this.enabled = enabled;
@ -268,6 +286,19 @@ public class GdbBreakpointInfo {
return type; return type;
} }
/**
* Get the type of breakpoint as named by GDB
*
* <p>
* In case of {@link GdbBreakpointType#OTHER}, this at least reports the string GDB uses to name
* the type.
*
* @return the type name
*/
public String getTypeName() {
return typeName;
}
/** /**
* Get the breakpoint disposition, i.e., what happens to the breakpoint once it has been hit * Get the breakpoint disposition, i.e., what happens to the breakpoint once it has been hit
* *
@ -292,7 +323,16 @@ public class GdbBreakpointInfo {
* @return the location * @return the location
*/ */
public String getWhat() { public String getWhat() {
return at; return what;
}
/**
* For a catchpoint, get the event being caught
*
* @return the catch-type
*/
public String getCatchType() {
return catchType;
} }
/** /**
@ -357,7 +397,7 @@ public class GdbBreakpointInfo {
if (isEnabled() == enabled) { if (isEnabled() == enabled) {
return this; return this;
} }
return new GdbBreakpointInfo(number, type, disp, addr, at, originalLocation, pending, return new GdbBreakpointInfo(number, type, typeName, disp, addr, what, catchType,
enabled, times, locations); originalLocation, pending, enabled, times, locations);
} }
} }

View file

@ -47,6 +47,10 @@ public enum GdbBreakpointType {
* An access (r/w) watchpoint, usually set via {@code awatch} * An access (r/w) watchpoint, usually set via {@code awatch}
*/ */
ACCESS_WATCHPOINT("acc watchpoint", true), ACCESS_WATCHPOINT("acc watchpoint", true),
/**
* A catchpoint to catch (break on) events, usual set via {@code catch}
*/
CATCHPOINT("catchpoint", false),
/** /**
* Some type not known to the manager * Some type not known to the manager
*/ */

View file

@ -649,6 +649,7 @@ public class GdbManagerImpl implements GdbManager {
cliThread.setName("GDB Read CLI"); cliThread.setName("GDB Read CLI");
// Looks terrible, but we're already in this world // Looks terrible, but we're already in this world
cliThread.writer.print("set confirm off" + newLine); cliThread.writer.print("set confirm off" + newLine);
cliThread.writer.print("set pagination off" + newLine);
cliThread.writer cliThread.writer
.print("new-ui mi2 " + mi2Pty.getChild().nullSession() + newLine); .print("new-ui mi2 " + mi2Pty.getChild().nullSession() + newLine);
cliThread.writer.flush(); cliThread.writer.flush();
@ -715,9 +716,11 @@ public class GdbManagerImpl implements GdbManager {
*/ */
protected CompletableFuture<Void> rc() { protected CompletableFuture<Void> rc() {
if (cliThread != null) { if (cliThread != null) {
// NB. confirm and pagination are already disabled here
return AsyncUtils.NIL; return AsyncUtils.NIL;
} }
else { else {
// NB. Don't disable pagination here. MI2 is not paginated.
return console("set confirm off", CompletesWithRunning.CANNOT); return console("set confirm off", CompletesWithRunning.CANNOT);
} }
} }

View file

@ -90,11 +90,7 @@ public class GdbModelTargetBreakpointLocation
*/ */
protected CompletableFuture<Void> initWpt() { protected CompletableFuture<Void> initWpt() {
assert loc.getAddr() == null; assert loc.getAddr() == null;
String what = parent.info.getWhat(); String exp = parent.info.getWhat();
if (!what.startsWith(GdbBreakpointLocation.WATCHPOINT_LOCATION_PREFIX)) {
throw new AssertionError("non-location location");
}
String exp = what.substring(GdbBreakpointLocation.WATCHPOINT_LOCATION_PREFIX.length());
int iid = Unique.assertOne(loc.getInferiorIds()); int iid = Unique.assertOne(loc.getInferiorIds());
GdbModelTargetInferior inf = impl.session.inferiors.getTargetInferior(iid); GdbModelTargetInferior inf = impl.session.inferiors.getTargetInferior(iid);
String addrSizeExp = String.format("{(long long)&(%s), (long long)sizeof(%s)}", exp, exp); String addrSizeExp = String.format("{(long long)&(%s), (long long)sizeof(%s)}", exp, exp);

View file

@ -19,8 +19,7 @@ import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import agent.gdb.manager.breakpoint.GdbBreakpointInfo; import agent.gdb.manager.breakpoint.*;
import agent.gdb.manager.breakpoint.GdbBreakpointLocation;
import ghidra.async.AsyncUtils; import ghidra.async.AsyncUtils;
import ghidra.dbg.agent.DefaultTargetObject; import ghidra.dbg.agent.DefaultTargetObject;
import ghidra.dbg.target.TargetBreakpointSpec; import ghidra.dbg.target.TargetBreakpointSpec;
@ -172,9 +171,11 @@ public class GdbModelTargetBreakpointSpec extends
protected void updateAttributesFromInfo(String reason) { protected void updateAttributesFromInfo(String reason) {
changeAttributes(List.of(), Map.of( changeAttributes(List.of(), Map.of(
ENABLED_ATTRIBUTE_NAME, enabled = info.isEnabled(), ENABLED_ATTRIBUTE_NAME, enabled = info.isEnabled(),
EXPRESSION_ATTRIBUTE_NAME, expression = info.getOriginalLocation(), EXPRESSION_ATTRIBUTE_NAME,
expression = info.getType() == GdbBreakpointType.CATCHPOINT ? info.getCatchType()
: info.getOriginalLocation(),
KINDS_ATTRIBUTE_NAME, kinds = computeKinds(info), KINDS_ATTRIBUTE_NAME, kinds = computeKinds(info),
DISPLAY_ATTRIBUTE_NAME, updateDisplay()), DISPLAY_ATTRIBUTE_NAME, display = computeDisplay()),
reason); reason);
} }
@ -186,6 +187,7 @@ public class GdbModelTargetBreakpointSpec extends
this.info = newInfo; this.info = newInfo;
List<GdbModelTargetBreakpointLocation> effectives = newInfo.getLocations() List<GdbModelTargetBreakpointLocation> effectives = newInfo.getLocations()
.stream() .stream()
.filter(l -> !"<PENDING>".equals(l.getAddr()))
.map(this::getTargetBreakpointLocation) .map(this::getTargetBreakpointLocation)
.collect(Collectors.toList()); .collect(Collectors.toList());
breaksBySub.keySet() breaksBySub.keySet()
@ -237,11 +239,46 @@ public class GdbModelTargetBreakpointSpec extends
i -> new GdbModelTargetBreakpointLocation(this, loc)); i -> new GdbModelTargetBreakpointLocation(this, loc));
} }
protected String updateDisplay() { protected String addressFromInfo() {
display = String.format("%d breakpoint %s %s %s %s", if (info.getAddress() != null) {
info.getNumber(), info.getDisp(), info.isEnabled() ? "y" : "n", info.getAddress(), return info.getAddress();
info.getWhat()); }
return display; List<GdbBreakpointLocation> locs = info.getLocations();
if (locs.isEmpty()) {
return "<PENDING>";
}
if (locs.size() == 1) {
String addr = locs.get(0).getAddr();
if (addr == null) {
return "<PENDING>";
}
return addr;
}
return "<MULTIPLE>";
}
protected String computeDisplay() {
Object enb = info.isEnabled() ? "y" : "n";
String addr = addressFromInfo();
String what = info.getWhat() == null ? "" : info.getWhat();
switch (info.getType()) {
case ACCESS_WATCHPOINT:
case HW_WATCHPOINT:
case READ_WATCHPOINT:
case BREAKPOINT:
case HW_BREAKPOINT:
case OTHER:
return String.format("%d %s %s %s %s %s", info.getNumber(), info.getTypeName(),
info.getDisp(), enb, addr, what).trim();
case CATCHPOINT:
return String.format("%d %s %s %s %s", info.getNumber(), info.getTypeName(),
info.getDisp(), enb, what).trim();
case DPRINTF:
// TODO: script?
return String.format("%d %s %s %s %s %s", info.getNumber(), info.getTypeName(),
info.getDisp(), enb, addr, what).trim();
}
throw new AssertionError();
} }
@Override @Override