mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GP-878: ObjectsProvider fixes. dbgeng termination/exit fixes.
This commit is contained in:
parent
75f950880e
commit
93b2f5cc64
16 changed files with 211 additions and 61 deletions
|
@ -103,7 +103,11 @@ public class DebugSystemObjectsImpl1 implements DebugSystemObjectsInternal {
|
|||
@Override
|
||||
public int getNumberThreads() {
|
||||
ULONGByReference pulNumber = new ULONGByReference();
|
||||
COMUtils.checkRC(jnaSysobj.GetNumberThreads(pulNumber));
|
||||
HRESULT hr = jnaSysobj.GetNumberThreads(pulNumber);
|
||||
if (hr.equals(COMUtilsExtra.E_UNEXPECTED)) {
|
||||
return 0;
|
||||
}
|
||||
COMUtils.checkRC(hr);
|
||||
return pulNumber.getValue().intValue();
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,8 @@ public class DbgDebugEventCallbacksAdapter extends DebugEventCallbacksAdapter {
|
|||
DebugStatus status = DebugStatus.fromArgument(argument);
|
||||
Msg.info(this, "***ExecutionStatus: " + status);
|
||||
if (status.equals(DebugStatus.NO_DEBUGGEE)) {
|
||||
event.setState(DbgState.SESSION_EXIT);
|
||||
long processCount = manager.getProcessCount();
|
||||
event.setState(processCount > 0 ? DbgState.SESSION_EXIT : DbgState.EXIT);
|
||||
}
|
||||
return checkInterrupt(manager.processEvent(event));
|
||||
}
|
||||
|
@ -131,10 +132,18 @@ public class DbgDebugEventCallbacksAdapter extends DebugEventCallbacksAdapter {
|
|||
return checkInterrupt(DebugStatus.NO_CHANGE);
|
||||
}
|
||||
|
||||
//@Override
|
||||
//public DebugStatus changeDebuggeeState(BitmaskSet<ChangeDebuggeeState> flags, long argument) {
|
||||
// System.err.println("CHANGE_DEBUGGEE_STATE: " + flags + ":" + argument);
|
||||
// return DebugStatus.NO_CHANGE;
|
||||
//}
|
||||
/*
|
||||
@Override
|
||||
public DebugStatus changeDebuggeeState(BitmaskSet<ChangeDebuggeeState> flags, long argument) {
|
||||
System.err.println("CHANGE_DEBUGGEE_STATE: " + flags + ":" + argument);
|
||||
return DebugStatus.NO_CHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DebugStatus sessionStatus(SessionStatus status) {
|
||||
System.err.println("SESSION_STATUS: " + status);
|
||||
return DebugStatus.NO_CHANGE;
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
|
|
@ -110,6 +110,7 @@ public class DbgManagerImpl implements DbgManager {
|
|||
private volatile boolean waiting = false;
|
||||
private boolean kernelMode = false;
|
||||
private CompletableFuture<String> continuation;
|
||||
private long processCount = 0;
|
||||
|
||||
/**
|
||||
* Instantiate a new manager
|
||||
|
@ -998,6 +999,12 @@ public class DbgManagerImpl implements DbgManager {
|
|||
waiting = true;
|
||||
|
||||
Long info = evt.getInfo();
|
||||
if (info.intValue() >= 0) {
|
||||
processCount++;
|
||||
}
|
||||
else {
|
||||
processCount--;
|
||||
}
|
||||
DebugProcessId id = new DebugProcessId(info.intValue());
|
||||
|
||||
String key = Integer.toHexString(id.id);
|
||||
|
@ -1523,4 +1530,9 @@ public class DbgManagerImpl implements DbgManager {
|
|||
public void setContinuation(CompletableFuture<String> continuation) {
|
||||
this.continuation = continuation;
|
||||
}
|
||||
|
||||
public long getProcessCount() {
|
||||
return processCount;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,11 +27,26 @@ import ghidra.dbg.target.*;
|
|||
import ghidra.dbg.target.schema.*;
|
||||
import ghidra.dbg.util.PathUtils;
|
||||
|
||||
@TargetObjectSchemaInfo(name = "Debugger", elements = {
|
||||
@TargetElementType(type = Void.class) }, attributes = {
|
||||
@TargetAttributeType(name = "Available", type = DbgModelTargetAvailableContainerImpl.class, required = true, fixed = true),
|
||||
@TargetAttributeType(name = "Connectors", type = DbgModelTargetConnectorContainerImpl.class, required = true, fixed = true),
|
||||
@TargetAttributeType(name = "Sessions", type = DbgModelTargetSessionContainerImpl.class, required = true, fixed = true),
|
||||
@TargetObjectSchemaInfo(
|
||||
name = "Debugger",
|
||||
elements = {
|
||||
@TargetElementType(type = Void.class) },
|
||||
attributes = {
|
||||
@TargetAttributeType(
|
||||
name = "Available",
|
||||
type = DbgModelTargetAvailableContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(
|
||||
name = "Connectors",
|
||||
type = DbgModelTargetConnectorContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(
|
||||
name = "Sessions",
|
||||
type = DbgModelTargetSessionContainerImpl.class,
|
||||
required = true,
|
||||
fixed = true),
|
||||
@TargetAttributeType(type = Void.class) })
|
||||
public class DbgModelTargetRootImpl extends DbgModelDefaultTargetModelRoot
|
||||
implements DbgModelTargetRoot {
|
||||
|
@ -120,9 +135,11 @@ public class DbgModelTargetRootImpl extends DbgModelDefaultTargetModelRoot
|
|||
DbgReason reason) {
|
||||
DbgModelTargetThread targetThread =
|
||||
(DbgModelTargetThread) getModel().getModelObject(thread);
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
TargetEventScope.EVENT_OBJECT_ATTRIBUTE_NAME, targetThread //
|
||||
), reason.desc());
|
||||
if (targetThread != null) {
|
||||
changeAttributes(List.of(), List.of(), Map.of( //
|
||||
TargetEventScope.EVENT_OBJECT_ATTRIBUTE_NAME, targetThread //
|
||||
), reason.desc());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -68,4 +68,14 @@ public abstract class AbstractModelForDbgengInterpreterTest
|
|||
public DebuggerTestSpecimen getLaunchSpecimen() {
|
||||
return WindowsSpecimen.PRINT;
|
||||
}
|
||||
|
||||
/*
|
||||
@Override
|
||||
@Ignore
|
||||
@Test(expected = DebuggerModelTerminatingException.class)
|
||||
public void testExecuteQuit() throws Throwable {
|
||||
// Different behavior for dbg clients vice gdb
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ import org.junit.Ignore;
|
|||
import org.junit.Test;
|
||||
|
||||
import agent.dbgeng.model.AbstractModelForDbgengInterpreterTest;
|
||||
import ghidra.dbg.error.DebuggerModelTerminatingException;
|
||||
|
||||
public class GadpModelForDbgengInterpreterTest extends AbstractModelForDbgengInterpreterTest {
|
||||
|
||||
|
@ -37,11 +36,4 @@ public class GadpModelForDbgengInterpreterTest extends AbstractModelForDbgengInt
|
|||
super.testAttachViaInterpreterShowsInProcessContainer();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Ignore
|
||||
@Test(expected = DebuggerModelTerminatingException.class)
|
||||
public void testExecuteQuit() throws Throwable {
|
||||
// Hangs after DebuggerModelTerminatingException
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import org.junit.Ignore;
|
|||
import org.junit.Test;
|
||||
|
||||
import agent.dbgeng.model.AbstractModelForDbgengInterpreterTest;
|
||||
import ghidra.dbg.error.DebuggerModelTerminatingException;
|
||||
|
||||
public class InVmModelForDbgengInterpreterTest extends AbstractModelForDbgengInterpreterTest {
|
||||
@Override
|
||||
|
@ -32,4 +33,12 @@ public class InVmModelForDbgengInterpreterTest extends AbstractModelForDbgengInt
|
|||
public void testAttachViaInterpreterShowsInProcessContainer() throws Throwable {
|
||||
super.testAttachViaInterpreterShowsInProcessContainer();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Ignore
|
||||
@Test(expected = DebuggerModelTerminatingException.class)
|
||||
public void testExecuteQuit() throws Throwable {
|
||||
// Different behavior for dbg clients vice gdb
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package agent.dbgmodel.model.impl;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -112,9 +113,11 @@ public class DbgModel2TargetObjectImpl extends DefaultTargetObject<TargetObject,
|
|||
@Override
|
||||
public CompletableFuture<Void> requestElements(boolean refresh) {
|
||||
List<TargetObject> nlist = new ArrayList<>();
|
||||
List<String> rlist = new ArrayList<>();
|
||||
return requestNativeElements().thenCompose(list -> {
|
||||
synchronized (elements) {
|
||||
for (TargetObject element : elements.values()) {
|
||||
for (Entry<String, TargetObject> entry : elements.entrySet()) {
|
||||
TargetObject element = entry.getValue();
|
||||
if (!list.contains(element)) {
|
||||
if (element instanceof DbgStateListener) {
|
||||
getManager().removeStateListener((DbgStateListener) element);
|
||||
|
@ -122,6 +125,7 @@ public class DbgModel2TargetObjectImpl extends DefaultTargetObject<TargetObject,
|
|||
if (element instanceof DbgEventsListener) {
|
||||
getManager().removeEventsListener((DbgEventsListener) element);
|
||||
}
|
||||
rlist.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
nlist.addAll(list);
|
||||
|
@ -129,18 +133,20 @@ public class DbgModel2TargetObjectImpl extends DefaultTargetObject<TargetObject,
|
|||
//return processModelObjectElements(nlist);
|
||||
}
|
||||
}).thenAccept(__ -> {
|
||||
changeElements(List.of(), nlist, Map.of(), "Refreshed");
|
||||
changeElements(rlist, nlist, Map.of(), "Refreshed");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompletableFuture<Void> requestAttributes(boolean refresh) {
|
||||
Map<String, Object> nmap = new HashMap<>();
|
||||
List<String> rlist = new ArrayList<>();
|
||||
return requestNativeAttributes().thenCompose(map -> {
|
||||
synchronized (attributes) {
|
||||
if (map != null) {
|
||||
Collection<?> values = map.values();
|
||||
for (Object attribute : attributes.values()) {
|
||||
for (Entry<String, Object> entry : attributes.entrySet()) {
|
||||
Object attribute = entry.getValue();
|
||||
if (!values.contains(attribute)) {
|
||||
if (attribute instanceof DbgStateListener) {
|
||||
getManager().removeStateListener((DbgStateListener) attribute);
|
||||
|
@ -148,6 +154,7 @@ public class DbgModel2TargetObjectImpl extends DefaultTargetObject<TargetObject,
|
|||
if (attribute instanceof DbgEventsListener) {
|
||||
getManager().removeEventsListener((DbgEventsListener) attribute);
|
||||
}
|
||||
rlist.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
nmap.putAll(map);
|
||||
|
|
|
@ -332,10 +332,10 @@ public class DbgModel2TargetRootImpl extends DbgModel2DefaultTargetModelRoot
|
|||
return;
|
||||
}
|
||||
DbgModelTargetProcess process = (DbgModelTargetProcess) object.getProxy();
|
||||
process.setExecutionState(TargetExecutionState.INACTIVE, "Detached");
|
||||
DbgProcess proc = process.getProcess();
|
||||
getListeners().fire.event(getProxy(), null, TargetEventType.PROCESS_EXITED,
|
||||
"Process " + proc.getId() + " exited code=" + proc.getExitCode(), List.of(process));
|
||||
if (!process.getExecutionState().equals(TargetExecutionState.TERMINATED)) {
|
||||
process.setExecutionState(TargetExecutionState.INACTIVE, "Detached");
|
||||
}
|
||||
process.getParent().resync();
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -540,8 +540,7 @@ public class DbgModel2TargetRootImpl extends DbgModel2DefaultTargetModelRoot
|
|||
}
|
||||
return TargetEventType.STOPPED;
|
||||
case SESSION_EXIT:
|
||||
getModel().close();
|
||||
break;
|
||||
return TargetEventType.PROCESS_EXITED;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -143,6 +143,7 @@ src/main/resources/images/launch.png||GHIDRA||||END|
|
|||
src/main/resources/images/modules.png||GHIDRA||||END|
|
||||
src/main/resources/images/object-populated.png||GHIDRA||||END|
|
||||
src/main/resources/images/object-running.png||GHIDRA||||END|
|
||||
src/main/resources/images/object-terminated.png||GHIDRA||||END|
|
||||
src/main/resources/images/object-unpopulated.png||GHIDRA||||END|
|
||||
src/main/resources/images/process.png||GHIDRA||||END|
|
||||
src/main/resources/images/record.png||GHIDRA||||END|
|
||||
|
@ -182,6 +183,7 @@ src/main/svg/disconnect.svg||GHIDRA||||END|
|
|||
src/main/svg/kill.svg||GHIDRA||||END|
|
||||
src/main/svg/launch.svg||GHIDRA||||END|
|
||||
src/main/svg/memory.svg||GHIDRA||||END|
|
||||
src/main/svg/object-terminated.svg||GHIDRA||||END|
|
||||
src/main/svg/process.svg||GHIDRA||||END|
|
||||
src/main/svg/recording.svg||GHIDRA||||END|
|
||||
src/main/svg/register-marker.svg||GHIDRA||||END|
|
||||
|
|
|
@ -101,6 +101,8 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
|||
public static final String OPTION_NAME_SUBSCRIBED_FOREGROUND_COLOR = "Object Colors.Subscribed";
|
||||
public static final String OPTION_NAME_INVISIBLE_FOREGROUND_COLOR =
|
||||
"Object Colors.Invisible (when toggled on)";
|
||||
public static final String OPTION_NAME_INVALIDATED_FOREGROUND_COLOR =
|
||||
"Object Colors.Invalidated";
|
||||
public static final String OPTION_NAME_ERROR_FOREGROUND_COLOR = "Object Colors.Errors";
|
||||
public static final String OPTION_NAME_INTRINSIC_FOREGROUND_COLOR = "Object Colors.Intrinsics";
|
||||
public static final String OPTION_NAME_TARGET_FOREGROUND_COLOR = "Object Colors.Targets";
|
||||
|
@ -127,6 +129,12 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
|||
help = @HelpInfo(anchor = "colors") //
|
||||
)
|
||||
Color invisibleForegroundColor = Color.LIGHT_GRAY;
|
||||
@AutoOptionDefined( //
|
||||
name = OPTION_NAME_INVALIDATED_FOREGROUND_COLOR, //
|
||||
description = "The foreground color for items no longer valid", //
|
||||
help = @HelpInfo(anchor = "colors") //
|
||||
)
|
||||
Color invalidatedForegroundColor = Color.LIGHT_GRAY;
|
||||
@AutoOptionDefined( //
|
||||
name = OPTION_NAME_MODIFIED_FOREGROUND_COLOR, //
|
||||
description = "The foreground color for modified items in the objects tree", //
|
||||
|
@ -388,6 +396,14 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
|||
}
|
||||
}
|
||||
|
||||
@AutoOptionConsumed(name = OPTION_NAME_INVALIDATED_FOREGROUND_COLOR)
|
||||
private void setInvalidatedForegroundColor(Color color) {
|
||||
invalidatedForegroundColor = color;
|
||||
if (pane != null) {
|
||||
pane.getComponent().repaint();
|
||||
}
|
||||
}
|
||||
|
||||
@AutoOptionConsumed(name = OPTION_NAME_LINK_FOREGROUND_COLOR)
|
||||
private void setLinkForegroundColor(Color color) {
|
||||
linkForegroundColor = color;
|
||||
|
@ -734,16 +750,13 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
|||
boolean usingAttributes) {
|
||||
String xkey = usingAttributes ? key : "[" + key + "]";
|
||||
if (val instanceof TargetObject) {
|
||||
TargetObject ref = (TargetObject) val;
|
||||
List<String> path = ref.getPath();
|
||||
TargetObject to = (TargetObject) val;
|
||||
List<String> path = to.getPath();
|
||||
boolean isLink = PathUtils.isLink(parent.getPath(), xkey, path);
|
||||
boolean isMethod = false;
|
||||
if (ref instanceof TargetObject) {
|
||||
TargetObject to = ref;
|
||||
isMethod = to instanceof TargetMethod;
|
||||
}
|
||||
isMethod = to instanceof TargetMethod;
|
||||
if (!(val instanceof DummyTargetObject) && !isMethod) {
|
||||
return new ObjectContainer(ref, isLink ? xkey : null);
|
||||
return new ObjectContainer(to, isLink ? xkey : null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -1786,6 +1799,8 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
|||
return intrinsicForegroundColor;
|
||||
case OPTION_NAME_INVISIBLE_FOREGROUND_COLOR:
|
||||
return invisibleForegroundColor;
|
||||
case OPTION_NAME_INVALIDATED_FOREGROUND_COLOR:
|
||||
return invalidatedForegroundColor;
|
||||
case OPTION_NAME_MODIFIED_FOREGROUND_COLOR:
|
||||
return modifiedForegroundColor;
|
||||
case OPTION_NAME_SUBSCRIBED_FOREGROUND_COLOR:
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package ghidra.app.plugin.core.debug.gui.objects;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import org.jdom.Element;
|
||||
|
@ -64,6 +65,14 @@ public class ObjectContainer implements Comparable<ObjectContainer> {
|
|||
return nc;
|
||||
}
|
||||
|
||||
public void updateUsing(ObjectContainer container) {
|
||||
attributeMap.clear();
|
||||
attributeMap.putAll(container.getAttributeMap());
|
||||
elementMap.clear();
|
||||
elementMap.putAll(container.getElementMap());
|
||||
targetObject = container.targetObject;
|
||||
}
|
||||
|
||||
public boolean hasElements() {
|
||||
return !elementMap.isEmpty();
|
||||
}
|
||||
|
@ -168,32 +177,40 @@ public class ObjectContainer implements Comparable<ObjectContainer> {
|
|||
public void augmentElements(Collection<String> elementsRemoved,
|
||||
Map<String, ? extends TargetObject> elementsAdded) {
|
||||
Set<ObjectContainer> result = new TreeSet<ObjectContainer>();
|
||||
Map<String, Object> newAdds = new HashMap<>();
|
||||
for (Entry<String, ? extends TargetObject> entry : elementsAdded.entrySet()) {
|
||||
newAdds.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
boolean structureChanged = false;
|
||||
synchronized (elementMap) {
|
||||
for (ObjectContainer child : currentChildren) {
|
||||
String name = child.getName();
|
||||
if (name.startsWith("[")) {
|
||||
name = name.substring(1, name.length() - 1);
|
||||
String key = child.getName();
|
||||
if (key.startsWith("[")) {
|
||||
key = key.substring(1, key.length() - 1);
|
||||
}
|
||||
if (elementsRemoved.contains(name) && !elementsAdded.containsKey(name)) {
|
||||
elementMap.remove(name);
|
||||
if (elementsRemoved.contains(key) && !elementsAdded.containsKey(key)) {
|
||||
elementMap.remove(key);
|
||||
structureChanged = true;
|
||||
continue;
|
||||
}
|
||||
if (elementsAdded.containsKey(key)) {
|
||||
Object val = elementsAdded.get(key);
|
||||
ObjectContainer newChild =
|
||||
DebuggerObjectsProvider.buildContainerFromObject(targetObject, key, val,
|
||||
true);
|
||||
child.updateUsing(newChild);
|
||||
newAdds.remove(key);
|
||||
provider.signalDataChanged(child);
|
||||
}
|
||||
result.add(child);
|
||||
}
|
||||
for (String key : elementsAdded.keySet()) {
|
||||
TargetObject val = elementsAdded.get(key);
|
||||
ObjectContainer child =
|
||||
DebuggerObjectsProvider.buildContainerFromObject(targetObject, key, val, false);
|
||||
if (!elementMap.containsKey(key)) {
|
||||
structureChanged = true;
|
||||
}
|
||||
else {
|
||||
provider.signalDataChanged(child);
|
||||
}
|
||||
elementMap.put(key, val);
|
||||
result.add(child);
|
||||
structureChanged = true;
|
||||
}
|
||||
}
|
||||
currentChildren = result;
|
||||
|
@ -207,30 +224,38 @@ public class ObjectContainer implements Comparable<ObjectContainer> {
|
|||
public void augmentAttributes(Collection<String> attributesRemoved,
|
||||
Map<String, ?> attributesAdded) {
|
||||
Set<ObjectContainer> result = new TreeSet<ObjectContainer>();
|
||||
Map<String, Object> newAdds = new HashMap<>();
|
||||
for (Entry<String, ?> entry : attributesAdded.entrySet()) {
|
||||
newAdds.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
boolean structureChanged = false;
|
||||
synchronized (attributeMap) {
|
||||
for (ObjectContainer child : currentChildren) {
|
||||
String name = child.getName();
|
||||
if (attributesRemoved.contains(name) && !attributesAdded.containsKey(name)) {
|
||||
attributeMap.remove(name);
|
||||
String key = child.getName();
|
||||
if (attributesRemoved.contains(key) && !attributesAdded.containsKey(key)) {
|
||||
attributeMap.remove(key);
|
||||
structureChanged = true;
|
||||
continue;
|
||||
}
|
||||
if (attributesAdded.containsKey(key)) {
|
||||
Object val = attributesAdded.get(key);
|
||||
ObjectContainer newChild =
|
||||
DebuggerObjectsProvider.buildContainerFromObject(targetObject, key, val,
|
||||
true);
|
||||
child.updateUsing(newChild);
|
||||
newAdds.remove(key);
|
||||
provider.signalDataChanged(child);
|
||||
}
|
||||
result.add(child);
|
||||
}
|
||||
for (String key : attributesAdded.keySet()) {
|
||||
Object val = attributesAdded.get(key);
|
||||
for (String key : newAdds.keySet()) {
|
||||
Object val = newAdds.get(key);
|
||||
ObjectContainer child =
|
||||
DebuggerObjectsProvider.buildContainerFromObject(targetObject, key, val, true);
|
||||
if (child != null) {
|
||||
if (!attributeMap.containsKey(key)) {
|
||||
structureChanged = true;
|
||||
}
|
||||
else {
|
||||
provider.signalDataChanged(child);
|
||||
}
|
||||
attributeMap.put(key, val);
|
||||
result.add(child);
|
||||
structureChanged = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@ public class ObjectNode extends GTreeSlowLoadingNode { //extends GTreeNode
|
|||
ResourceManager.loadImage("images/object-populated.png");
|
||||
static final ImageIcon ICON_EMPTY = ResourceManager.loadImage("images/object-unpopulated.png");
|
||||
static final ImageIcon ICON_RUNNING = ResourceManager.loadImage("images/object-running.png");
|
||||
static final ImageIcon ICON_TERMINATED =
|
||||
ResourceManager.loadImage("images/object-terminated.png");
|
||||
static final ImageIcon ICON_EVENT = ResourceManager.loadImage("images/register-marker.png");
|
||||
|
||||
private ObjectContainer container;
|
||||
|
@ -142,6 +144,9 @@ public class ObjectNode extends GTreeSlowLoadingNode { //extends GTreeNode
|
|||
if (stateful.getExecutionState().equals(TargetExecutionState.RUNNING)) {
|
||||
return ICON_RUNNING;
|
||||
}
|
||||
if (stateful.getExecutionState().equals(TargetExecutionState.TERMINATED)) {
|
||||
return ICON_TERMINATED;
|
||||
}
|
||||
}
|
||||
/*
|
||||
Map<String, Object> attributeMap = container.getAttributeMap();
|
||||
|
|
|
@ -24,6 +24,8 @@ import javax.swing.tree.TreePath;
|
|||
import docking.widgets.tree.support.GTreeRenderer;
|
||||
import ghidra.app.plugin.core.debug.gui.objects.DebuggerObjectsProvider;
|
||||
import ghidra.app.plugin.core.debug.gui.objects.ObjectContainer;
|
||||
import ghidra.dbg.target.TargetExecutionStateful;
|
||||
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
|
||||
import ghidra.dbg.target.TargetObject;
|
||||
|
||||
// TODO: In the new scheme, I'm not sure this is applicable anymore.
|
||||
|
@ -66,6 +68,14 @@ class ObjectTreeCellRenderer extends GTreeRenderer {
|
|||
component.setForeground(provider
|
||||
.getColor(DebuggerObjectsProvider.OPTION_NAME_INVISIBLE_FOREGROUND_COLOR));
|
||||
}
|
||||
if (container.getTargetObject() instanceof TargetExecutionStateful) {
|
||||
TargetExecutionStateful stateful = (TargetExecutionStateful) targetObject;
|
||||
if (stateful.getExecutionState().equals(TargetExecutionState.TERMINATED)) {
|
||||
component.setForeground(provider
|
||||
.getColor(
|
||||
DebuggerObjectsProvider.OPTION_NAME_INVALIDATED_FOREGROUND_COLOR));
|
||||
}
|
||||
}
|
||||
if (container.isLink()) {
|
||||
component.setForeground(
|
||||
provider.getColor(DebuggerObjectsProvider.OPTION_NAME_LINK_FOREGROUND_COLOR));
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 353 B |
34
Ghidra/Debug/Debugger/src/main/svg/object-terminated.svg
Normal file
34
Ghidra/Debug/Debugger/src/main/svg/object-terminated.svg
Normal file
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="16px"
|
||||
height="16px"
|
||||
viewBox="0 0 16 16"
|
||||
version="1.1"
|
||||
id="SVGRoot">
|
||||
<defs
|
||||
id="defs5287" />
|
||||
<metadata
|
||||
id="metadata5290">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
id="layer1">
|
||||
<path
|
||||
style="opacity:1;fill:#7f7f7f;fill-opacity:1;stroke:none;stroke-width:1.14285719;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 8 4 A 4 4 0 0 0 4 8 A 4 4 0 0 0 8 12 A 4 4 0 0 0 12 8 A 4 4 0 0 0 8 4 z M 8 6 A 2 2 0 0 1 10 8 A 2 2 0 0 1 8 10 A 2 2 0 0 1 6 8 A 2 2 0 0 1 8 6 z "
|
||||
id="path821" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
Loading…
Add table
Add a link
Reference in a new issue