GP-5271: Fix break/watchpoints with lldb. Many framework changes.

This commit is contained in:
Dan 2025-01-17 07:58:01 -05:00
parent 3bfcb8695f
commit 2a41c8fe6b
16 changed files with 335 additions and 260 deletions

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -272,6 +272,13 @@ public class DBTraceBreakpoint
}
}
@Override
public boolean isAlive(long snap) {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {
return lifespan.contains(snap);
}
}
@Override
public long getPlacedSnap() {
try (LockHold hold = LockHold.lock(space.lock.readLock())) {

View file

@ -196,6 +196,13 @@ public class DBTraceObjectBreakpointLocation
}
}
@Override
public boolean isAlive(long snap) {
try (LockHold hold = object.getTrace().lockRead()) {
return object.isAlive(snap);
}
}
@Override
public Lifespan computeSpan() {
Lifespan span = TraceObjectBreakpointLocation.super.computeSpan();

View file

@ -106,6 +106,13 @@ public class DBTraceObjectBreakpointSpec
return computeSpan();
}
@Override
public boolean isAlive(long snap) {
try (LockHold hold = object.getTrace().lockRead()) {
return object.isAlive(snap);
}
}
@Override
public long getPlacedSnap() {
return computeMinSnap();

View file

@ -163,23 +163,36 @@ public class DBTraceObject extends DBAnnotatedObject implements TraceObject {
}
}
protected LifeSet ensureCachedLife() {
if (cachedLife != null) {
return cachedLife;
}
MutableLifeSet result = new DefaultLifeSet();
getCanonicalParents(Lifespan.ALL).forEach(v -> result.add(v.getLifespan()));
cachedLife = result;
return result;
}
@Override
public LifeSet getLife() {
try (LockHold hold = manager.trace.lockRead()) {
if (cachedLife != null) {
synchronized (cachedLife) {
return DefaultLifeSet.copyOf(cachedLife);
}
}
MutableLifeSet result = new DefaultLifeSet();
getCanonicalParents(Lifespan.ALL).forEach(v -> result.add(v.getLifespan()));
cachedLife = result;
LifeSet result = ensureCachedLife();
synchronized (result) {
return DefaultLifeSet.copyOf(result);
}
}
}
@Override
public boolean isAlive(long snap) {
try (LockHold hold = manager.trace.lockRead()) {
LifeSet result = ensureCachedLife();
synchronized (result) {
return result.contains(snap);
}
}
}
protected DBTraceObject doCreateCanonicalParentObject() {
return manager.doCreateObject(path.parent());
}

View file

@ -4,9 +4,9 @@
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@ -23,6 +23,7 @@ import ghidra.pcode.exec.SleighUtils;
import ghidra.program.model.address.Address;
import ghidra.program.model.address.AddressRange;
import ghidra.trace.model.*;
import ghidra.trace.model.target.TraceObjectValue;
import ghidra.trace.model.thread.TraceThread;
import ghidra.util.exception.DuplicateNameException;
@ -105,9 +106,21 @@ public interface TraceBreakpoint extends TraceUniqueObject {
* Get the lifespan of this breakpoint
*
* @return the lifespan
* @deprecated Either this method no longer makes sense, or we need to wrap a
* {@link TraceObjectValue} instead. Even then, the attribute values can vary over
* the lifespan.
*/
@Deprecated(since = "11.3", forRemoval = true)
Lifespan getLifespan();
/**
* Check if the breakpoint is present at the given snap
*
* @param snap the snap
* @return true if alive, false if not
*/
boolean isAlive(long snap);
/**
* Get the placed snap of this breakpoint
*

View file

@ -180,6 +180,17 @@ public interface TraceObject extends TraceUniqueObject {
*/
LifeSet getLife();
/**
* Check if the object is alive at the given snap
*
* <p>
* This is preferable to {@link #getLife()}, when we only need to check one snap
*
* @param snap the snap
* @return true if alive, false if not
*/
boolean isAlive(long snap);
/**
* Inserts this object at its canonical path for the given lifespan
*

View file

@ -497,18 +497,23 @@ public interface TraceObjectSchema {
public void nextLevel() {
Set<T> nextLevel = new HashSet<>();
for (T ent : allOnLevel) {
if (!descend(ent)) {
continue;
if (descendAttributes(ent)) {
expandAttributes(nextLevel, ent);
expandDefaultAttribute(nextLevel, ent);
}
if (descendElements(ent)) {
expandElements(nextLevel, ent);
expandDefaultElement(nextLevel, ent);
}
expandAttributes(nextLevel, ent);
expandDefaultAttribute(nextLevel, ent);
expandElements(nextLevel, ent);
expandDefaultElement(nextLevel, ent);
}
allOnLevel = nextLevel;
}
public boolean descend(T ent) {
public boolean descendAttributes(T ent) {
return true;
}
public boolean descendElements(T ent) {
return true;
}
@ -549,10 +554,15 @@ public interface TraceObjectSchema {
}
@Override
public boolean descend(SearchEntry ent) {
public boolean descendAttributes(SearchEntry ent) {
return ent.schema.getInterfaces().contains(TraceObjectAggregate.class);
}
@Override
public boolean descendElements(SearchEntry ent) {
return ent.schema.isCanonicalContainer();
}
@Override
public void expandAttribute(Set<SearchEntry> nextLevel, SearchEntry ent,
TraceObjectSchema schema, KeyPath path) {

View file

@ -16,12 +16,15 @@
package ghidra.dbg.target.schema;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import java.io.IOException;
import org.jdom.JDOMException;
import org.junit.Test;
import ghidra.trace.model.breakpoint.TraceObjectBreakpointLocation;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.target.schema.*;
import ghidra.trace.model.target.schema.DefaultTraceObjectSchema.DefaultAttributeSchema;
import ghidra.trace.model.target.schema.TraceObjectSchema.*;
@ -75,4 +78,35 @@ public class XmlTargetObjectSchemaTest {
SchemaContext result = XmlSchemaContext.deserialize(SCHEMA_XML);
assertEquals(CTX, result);
}
@Test
public void testSearchWithMultipleImpls() throws Exception {
SchemaContext ctx = XmlSchemaContext.deserialize("""
<context>
<schema name="root">
<interface name="Aggregate" />
<attribute name="Watches" schema="WatchContainer" />
<attribute name="Breaks" schema="BreakContainer" />
</schema>
<schema name="WatchContainer" canonical="yes">
<element schema="Watch" />
</schema>
<schema name="Watch">
<interface name="BreakpointSpec" />
<interface name="BreakpointLocation" />
</schema>
<schema name="BreakContainer" canonical="yes">
<element schema="Break" />
</schema>
<schema name="Break">
<interface name="BreakpointSpec" />
<interface name="BreakpointLocation" />
</schema>
</context>
""");
KeyPath found = ctx.getSchema(new SchemaName("root"))
.searchForSuitable(TraceObjectBreakpointLocation.class, KeyPath.ROOT);
assertNotNull(found);
}
}