GP-665: Deleting DebuggerTimelineProvider and related.

This commit is contained in:
Dan
2021-02-08 13:50:47 -05:00
parent 3a550a8979
commit 991da5eb17
8 changed files with 0 additions and 1178 deletions

View File

@@ -1,30 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.timeline;
import docking.ActionContext;
public class DebuggerTimelineActionContext extends ActionContext {
private final TimelineRow selection;
public DebuggerTimelineActionContext(TimelineRow selection) {
this.selection = selection;
}
public TimelineRow getSelection() {
return selection;
}
}

View File

@@ -1,161 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.timeline;
import java.awt.*;
import java.util.List;
import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import com.google.common.collect.Range;
import docking.widgets.*;
import docking.widgets.RangeCursorPanel.Direction;
import docking.widgets.table.RowObjectTableModel;
import docking.widgets.timeline.TimelinePanel;
import docking.widgets.timeline.TimelineViewRangeListener;
import ghidra.util.datastruct.ListenerSet;
public class DebuggerTimelinePanel extends JScrollPane {
static class SnapRequestVetoedException extends Exception {
private final long requestedSnap;
private final long newSnap;
public SnapRequestVetoedException(long requestedSnap, long newSnap) {
this.requestedSnap = requestedSnap;
this.newSnap = newSnap;
}
public long getRequestedSnap() {
return requestedSnap;
}
public long getNewSnap() {
return newSnap;
}
}
public interface VetoableSnapRequestListener {
void snapRequested(long snap, EventTrigger trigger) throws SnapRequestVetoedException;
}
protected class ObjectTimelinePanel extends TimelinePanel<TimelineRow, Long> {
private DebuggerTimelinePanel panel;
public ObjectTimelinePanel(DebuggerTimelinePanel panel,
RowObjectTableModel<TimelineRow> model) {
super(model, TimelineRow::getLifespan);
this.panel = panel;
}
@Override
protected void selectionChanged(ListSelectionEvent e) {
super.selectionChanged(e);
List<TimelineRow> items = rows.items();
int min = selectionModel.getMinSelectionIndex();
if (items != null && min >= 0) {
TimelineRow row = items.get(min);
Component component = getComponent(row);
Point loc = component.getLocation();
JViewport vp = panel.getViewport();
vp.setViewPosition(new Point(loc.x, vp.getView().getY()));
}
}
}
protected class ObjectRangeCursorPanel extends RangeCursorPanel {
public ObjectRangeCursorPanel(Direction direction) {
super(direction);
}
@Override
protected double adjustRequestedValue(double requested) {
double rounded = Math.round(requested);
// TODO: Also remove 1-snap view buffer?
// Until I figure out the event processing order, leaving the buffer
// prevents some stepping glitches.
if (range.hasLowerBound() && rounded < range.lowerEndpoint() /*+1*/) {
return range.lowerEndpoint(); // +1
}
if (range.hasUpperBound() && rounded > range.upperEndpoint() /*-1*/) {
return range.upperEndpoint(); // -1
}
return rounded;
}
}
protected final ObjectTimelinePanel timeline;
protected final RangeCursorPanel topCursor = new ObjectRangeCursorPanel(Direction.SOUTH) {
Dimension preferredSize = new Dimension(super.getPreferredSize());
@Override
public Dimension getPreferredSize() {
preferredSize.width = timeline.getPreferredSize().width;
return preferredSize;
}
};
protected final ListenerSet<VetoableSnapRequestListener> listeners =
new ListenerSet<>(VetoableSnapRequestListener.class);
protected final RangeCursorValueListener positionListener = this::cursorPositionChanged;
protected final TimelineViewRangeListener viewRangeListener = this::viewRangeChanged;
protected ListSelectionModel selectionModel;
public DebuggerTimelinePanel(RowObjectTableModel<TimelineRow> model) {
setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
timeline = new ObjectTimelinePanel(this, model);
timeline.setCompressed(false);
setViewportView(timeline);
setColumnHeaderView(topCursor);
topCursor.addValueListener(positionListener);
timeline.addViewRangeListener(viewRangeListener);
}
public void addSnapRequestedListener(VetoableSnapRequestListener listener) {
listeners.add(listener);
}
private void cursorPositionChanged(double position, EventTrigger trigger) {
// try {
// listeners.fire.snapRequested(Math.round(position), trigger);
// }
// catch (SnapRequestVetoedException e) {
// position = e.getNewSnap();
// }
}
private void viewRangeChanged(Range<Double> range) {
topCursor.setRange(range);
}
public void setSelectionModel(ListSelectionModel selectionModel) {
timeline.setSelectionModel(selectionModel);
}
public void setSnap(long snap) {
topCursor.requestValue(snap);
}
public void setMaxSnapAtLeast(Long maxSnapAtLeast) {
if (maxSnapAtLeast != null) {
timeline.setMaxAtLeast(maxSnapAtLeast);
}
}
}

View File

@@ -1,124 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.timeline;
import java.util.LinkedHashMap;
import java.util.Map;
import ghidra.app.events.*;
import ghidra.app.plugin.PluginCategoryNames;
import ghidra.app.plugin.core.debug.AbstractDebuggerPlugin;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.event.TraceActivatedPluginEvent;
import ghidra.app.plugin.core.debug.gui.objects.ObjectUpdatedEvent;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.framework.plugintool.*;
import ghidra.framework.plugintool.util.PluginStatus;
import ghidra.program.model.lang.CompilerSpec;
import ghidra.program.model.listing.Program;
import ghidra.trace.database.DBTrace;
import ghidra.trace.model.Trace;
@PluginInfo( //
shortDescription = "Debugger timeline manager", //
description = "GUI to view object timelines", //
category = PluginCategoryNames.DEBUGGER, //
packageName = DebuggerPluginPackage.NAME, //
status = PluginStatus.UNSTABLE, //
eventsConsumed = { //
ProgramOpenedPluginEvent.class, // TODO: Remove?
ProgramSelectionPluginEvent.class, // TODO: Later or remove
ProgramHighlightPluginEvent.class, // TODO: Later or remove
ProgramActivatedPluginEvent.class, // TODO: Remove? Covered by Location?
ProgramClosedPluginEvent.class, // For marker set cleanup
ProgramLocationPluginEvent.class, // For static listing sync
TraceActivatedPluginEvent.class, //
ObjectUpdatedEvent.class, //
}, //
servicesRequired = { //
DebuggerTraceManagerService.class, //
} //
)
public class DebuggerTimelinePlugin extends AbstractDebuggerPlugin {
protected DebuggerTimelineProvider provider;
private Program currentProgram;
private Map<Program, Trace> traceMap = new LinkedHashMap<>();
public DebuggerTimelinePlugin(PluginTool tool) {
super(tool);
}
@Override
protected void init() {
provider = new DebuggerTimelineProvider(this);
super.init();
}
@Override
protected void dispose() {
tool.removeComponentProvider(provider);
super.dispose();
}
@Override
public void processEvent(PluginEvent event) {
super.processEvent(event);
if (event instanceof ProgramOpenedPluginEvent) {
setProgram(((ProgramOpenedPluginEvent) event).getProgram(), false);
}
if (event instanceof ProgramActivatedPluginEvent) {
setProgram(((ProgramActivatedPluginEvent) event).getActiveProgram(), false);
}
if (event instanceof ProgramClosedPluginEvent) {
setProgram(((ProgramClosedPluginEvent) event).getProgram(), true);
}
if (event instanceof ObjectUpdatedEvent) {
provider.update(((ObjectUpdatedEvent) event).getObject());
}
if (event instanceof TraceActivatedPluginEvent) {
TraceActivatedPluginEvent ev = (TraceActivatedPluginEvent) event;
provider.coordinatesActivated(ev.getActiveCoordinates());
}
}
private void setProgram(Program pgm, boolean remove) {
currentProgram = pgm;
if (pgm == null) {
return;
}
try {
Trace trace = null;
if (traceMap.containsKey(pgm)) {
trace = traceMap.get(pgm);
if (remove) {
traceMap.remove(pgm);
trace = null;
}
}
else {
// TODO: I'm not sure what this is doing, but it seems like a bad idea....
CompilerSpec cspec = pgm.getCompilerSpec();
trace = new DBTrace(pgm.getName(), cspec, this);
}
// TODO: Nothing seems to put into traceMap, so just commenting out
// provider.doSetTrace(trace);
}
catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -1,487 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.timeline;
import static ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import java.awt.BorderLayout;
import java.awt.event.MouseEvent;
import java.util.*;
import javax.swing.*;
import docking.ActionContext;
import docking.WindowPosition;
import docking.action.DockingActionIf;
import docking.action.ToolBarData;
import docking.widgets.EventTrigger;
import docking.widgets.table.*;
import ghidra.app.plugin.core.debug.DebuggerCoordinates;
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
import ghidra.app.plugin.core.debug.gui.DebuggerResources.*;
import ghidra.app.plugin.core.debug.gui.DebuggerSnapActionContext;
import ghidra.app.plugin.core.debug.gui.objects.ObjectContainer;
import ghidra.app.plugin.core.debug.gui.timeline.DebuggerTimelinePanel.VetoableSnapRequestListener;
import ghidra.app.services.*;
import ghidra.framework.model.DomainObject;
import ghidra.framework.plugintool.AutoService;
import ghidra.framework.plugintool.ComponentProviderAdapter;
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
import ghidra.trace.model.Trace;
import ghidra.trace.model.Trace.TraceSnapshotChangeType;
import ghidra.trace.model.Trace.TraceThreadChangeType;
import ghidra.trace.model.TraceDomainObjectListener;
import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.modules.TraceModuleManager;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.thread.TraceThreadManager;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.util.database.UndoableTransaction;
import ghidra.util.table.GhidraTable;
import ghidra.util.table.GhidraTableFilterPanel;
public class DebuggerTimelineProvider extends ComponentProviderAdapter {
protected static boolean sameCoordinates(DebuggerCoordinates a, DebuggerCoordinates b) {
if (!Objects.equals(a.getTrace(), b.getTrace())) {
return false;
}
if (!Objects.equals(a.getSnap(), b.getSnap())) {
return false;
}
return true;
}
private final DebuggerTimelinePlugin plugin;
@AutoServiceConsumed
private DebuggerModelService modelService;
@AutoServiceConsumed
private DebuggerTraceManagerService traceManager;
@SuppressWarnings("unused")
private final AutoService.Wiring autoWiring;
DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
private Trace currentTrace; // Copy for transition
private TraceObject currentObject;
private final ThreadsListener threadsListener = new ThreadsListener();
private final VetoableSnapRequestListener snapListener = this::snapRequested;
protected final EnumeratedColumnTableModel<TimelineRow> timelineTableModel =
new DefaultEnumeratedColumnTableModel<>("Objects", TimelineTableColumns.class);
private JSplitPane mainPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
protected GTable objectTable;
private DebuggerTimelinePanel timelinePanel;
private GhidraTableFilterPanel<TimelineRow> timelineFilterPanel;
private ActionContext myActionContext;
StepTraceBackwardAction actionStepTraceBackward;
StepTraceForwardAction actionStepTraceForward;
SeekTracePresentAction actionSeekTracePresent;
private Map<String, TraceObject> map = new LinkedHashMap<String, TraceObject>();
private Map<String, TimelineRow> rowMap = new LinkedHashMap<String, TimelineRow>();
public DebuggerTimelineProvider(final DebuggerTimelinePlugin plugin) {
super(plugin.getTool(), TITLE_PROVIDER_OBJECTS, plugin.getName());
this.plugin = plugin;
this.autoWiring = AutoService.wireServicesConsumed(plugin, this);
setIcon(ICON_PROVIDER_OBJECTS);
setHelpLocation(HELP_PROVIDER_OBJECTS);
setWindowMenuGroup(DebuggerPluginPackage.NAME);
buildMainPanel();
// TODO: Consider a custom cell renderer in the table instead of a timeline widget?
// TODO: Should I receive clicks on that renderer to seek to a given snap?
setDefaultWindowPosition(WindowPosition.BOTTOM);
myActionContext = new DebuggerSnapActionContext(0);
createActions();
contextChanged();
setVisible(true);
}
private void doSetTrace(Trace trace) {
removeOldListeners();
currentTrace = trace;
addNewListeners();
loadObjects();
}
private void doSetObject(TraceObject object) {
// TODO: Use a map if speed becomes an issue
timelineFilterPanel
.setSelectedItem(timelineTableModel.findFirst(row -> row.getObject() == object));
currentObject = object;
}
private void doSetSnap(long snap) {
timelinePanel.setSnap(snap);
myActionContext = new DebuggerSnapActionContext(snap);
}
public void coordinatesActivated(DebuggerCoordinates coordinates) {
if (sameCoordinates(current, coordinates)) {
current = coordinates;
return;
}
current = coordinates;
doSetTrace(current.getTrace());
doSetSnap(current.getSnap());
contextChanged();
}
public void setObject(TraceObject object) {
if (currentObject == object) {
return;
}
doSetObject(object);
contextChanged();
}
private void removeOldListeners() {
if (currentTrace == null) {
return;
}
currentTrace.removeListener(threadsListener);
}
private void addNewListeners() {
if (currentTrace == null) {
return;
}
currentTrace.addListener(threadsListener);
}
protected void loadObjects() {
map.clear();
rowMap.clear();
timelineTableModel.clear();
Trace curTrace = current.getTrace();
if (curTrace == null) {
return;
}
TraceThreadManager manager = curTrace.getThreadManager();
Collection<? extends TraceThread> allThreads = manager.getAllThreads();
for (TraceThread thread : allThreads) {
TraceObject t = new TraceObject(thread);
timelineTableModel.add(new TimelineRow(modelService, t));
}
TraceModuleManager modmgr = curTrace.getModuleManager();
Collection<? extends TraceModule> allModules = modmgr.getAllModules();
for (TraceModule module : allModules) {
TraceObject t = new TraceObject(module);
timelineTableModel.add(new TimelineRow(modelService, t));
}
timelinePanel.setMaxSnapAtLeast(curTrace.getTimeManager().getMaxSnap());
}
@Override
public void addLocalAction(DockingActionIf action) {
super.addLocalAction(action);
}
@Override
public ActionContext getActionContext(MouseEvent event) {
if (myActionContext == null) {
return super.getActionContext(event);
}
return myActionContext;
}
private void snapRequested(long snap, EventTrigger trigger) {
if (snap < 0) {
snap = 0;
}
Trace curTrace = current.getTrace();
if (curTrace == null) {
snap = 0;
}
else {
Long maxSnap = curTrace.getTimeManager().getMaxSnap();
if (maxSnap == null) {
maxSnap = 0L;
}
if (snap > maxSnap) {
snap = maxSnap;
}
}
if (trigger == EventTrigger.GUI_ACTION) {
traceManager.activateSnap(snap);
}
myActionContext = new DebuggerSnapActionContext(snap);
contextChanged();
}
protected void buildMainPanel() {
mainPanel.setContinuousLayout(true);
JPanel tablePanel = new JPanel(new BorderLayout());
objectTable = new GhidraTable(timelineTableModel);
objectTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
tablePanel.add(new JScrollPane(objectTable));
timelineFilterPanel = new GhidraTableFilterPanel<>(objectTable, timelineTableModel);
tablePanel.add(timelineFilterPanel, BorderLayout.SOUTH);
mainPanel.setLeftComponent(tablePanel);
timelinePanel = new DebuggerTimelinePanel(timelineFilterPanel.getTableFilterModel());
mainPanel.setRightComponent(timelinePanel);
mainPanel.setResizeWeight(0.4);
objectTable.getSelectionModel().addListSelectionListener(evt -> {
TimelineRow row = timelineFilterPanel.getSelectedItem();
myActionContext = new DebuggerTimelineActionContext(row);
contextChanged();
if (row != null && traceManager != null) {
TraceObject object = row.getObject();
if (object.isThread()) {
traceManager.activateThread((TraceThread) object.getObject());
}
}
});
timelinePanel.setSelectionModel(objectTable.getSelectionModel());
timelinePanel.addSnapRequestedListener(snapListener);
}
protected void createActions() {
actionStepTraceBackward = new StepTraceBackwardAction();
actionStepTraceForward = new StepTraceForwardAction();
actionSeekTracePresent = new SeekTracePresentAction();
}
@Override
public JComponent getComponent() {
return mainPanel;
}
public void update(ObjectContainer container) {
String shortName = container.getShortName();
if (shortName == null || !shortName.equals("Sequence")) {
return;
}
TraceObject object = null;
Trace curTrace = current.getTrace();
try (UndoableTransaction tid =
UndoableTransaction.start(curTrace, "Populate Snapshot", true)) {
ObjectContainer ancestor = container;
while (ancestor != null && !ancestor.getPrefixedName().contains("[")) {
ancestor = ancestor.getParent();
}
if (ancestor == null) {
return;
}
String objName = ancestor.getPrefixedName();
TimelineRow row = null;
if (map.containsKey(objName)) {
object = map.get(objName);
row = rowMap.get(objName);
}
else {
object = new TraceObject(curTrace);
object.setName(objName);
map.put(objName, object);
row = new TimelineRow(modelService, object);
rowMap.put(objName, row);
timelineTableModel.add(row);
}
String[] split = container.getName().split(" ");
String searchString = container.getTargetObject().getJoinedPath(".");
long pos = -1;
if (searchString.contains("MinPos")) {
System.err.println("minpos");
pos = Long.parseLong(split[split.length - 1], 16);
object.setCreationSnap(pos);
curTrace.getTimeManager()
.getSnapshot(pos, true)
.setDescription(object + " created");
}
else if (searchString.contains("MaxPos")) {
System.err.println("maxpos");
pos = Long.parseLong(split[split.length - 1], 16);
object.setDestructionSnap(pos);
curTrace.getTimeManager()
.getSnapshot(pos, true)
.setDescription(object + " destroyed");
}
else if (searchString.contains("Position")) {
/*
pos = Long.parseLong(split[split.length - 1], 16);
object.setCreationSnap(pos);
curTrace.getTimeManager()
.getSnapshot(pos, true)
.setDescription(object + " mark set");
*/
}
if (pos >= 0L) {
timelinePanel.setMaxSnapAtLeast(pos);
}
timelineTableModel.notifyUpdated(row);
mainPanel.validate();
mainPanel.repaint();
}
catch (Exception e) {
e.printStackTrace();
}
setObject(object);
}
protected class StepTraceBackwardAction extends AbstractStepTraceBackwardAction {
public static final String GROUP = GROUP_GENERAL;
public StepTraceBackwardAction() {
super(plugin);
setToolBarData(new ToolBarData(ICON, GROUP));
addLocalAction(this);
setEnabled(false);
}
@Override
public void actionPerformed(ActionContext context) {
traceManager.activateSnap(current.getSnap() - 1);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
if (current.getTrace() == null) {
return false;
}
// TODO: Can I track minTick?
if (current.getSnap() <= 0) {
return false;
}
return true;
}
}
protected class StepTraceForwardAction extends AbstractStepTraceForwardAction {
public static final String GROUP = GROUP_GENERAL;
public StepTraceForwardAction() {
super(plugin);
setToolBarData(new ToolBarData(ICON, GROUP));
addLocalAction(this);
setEnabled(false);
}
@Override
public void actionPerformed(ActionContext context) {
traceManager.activateSnap(current.getSnap() + 1);
}
@Override
public boolean isEnabledForContext(ActionContext context) {
Trace curTrace = current.getTrace();
if (curTrace == null) {
return false;
}
Long maxSnap = curTrace.getTimeManager().getMaxSnap();
if (maxSnap == null || current.getSnap() >= maxSnap) {
return false;
}
return true;
}
}
protected class SeekTracePresentAction extends AbstractSeekTracePresentAction {
public static final String GROUP = GROUP_GENERAL;
public SeekTracePresentAction() {
super(plugin);
setToolBarData(new ToolBarData(ICON, GROUP));
addLocalAction(this);
setEnabled(false);
}
@Override
public void actionPerformed(ActionContext context) {
if (current.getTrace() == null) {
return;
}
TraceRecorder recorder = current.getRecorder();
if (recorder == null) {
return;
}
traceManager.activateSnap(recorder.getSnap());
}
@Override
public boolean isEnabled() {
if (current.getTrace() == null) {
return false;
}
TraceRecorder recorder = current.getRecorder();
if (recorder == null) {
return false;
}
if (current.getSnap() == recorder.getSnap()) {
return false;
}
return true;
}
}
private class ThreadsListener extends TraceDomainObjectListener {
public ThreadsListener() {
listenForUntyped(DomainObject.DO_OBJECT_RESTORED, e -> objectRestored());
listenFor(TraceThreadChangeType.ADDED, this::threadAdded);
listenFor(TraceThreadChangeType.CHANGED, this::threadChanged);
listenFor(TraceThreadChangeType.LIFESPAN_CHANGED, this::threadChanged);
listenFor(TraceThreadChangeType.DELETED, this::threadDeleted);
listenFor(TraceSnapshotChangeType.ADDED, this::snapshotAdded);
listenFor(TraceSnapshotChangeType.DELETED, this::snapshotDeleted);
}
private void objectRestored() {
loadObjects();
}
private void threadAdded(TraceThread thread) {
timelineTableModel.add(new TimelineRow(modelService, new TraceObject(thread)));
}
private void threadChanged(TraceThread thread) {
timelineTableModel.notifyUpdatedWith(row -> row.getObject() == thread);
}
private void threadDeleted(TraceThread thread) {
timelineTableModel.deleteWith(row -> row.getObject() == thread);
}
private void snapshotAdded(TraceSnapshot snapshot) {
long maxSnap = current.getTrace().getTimeManager().getMaxSnap();
timelinePanel.setMaxSnapAtLeast(maxSnap);
}
private void snapshotDeleted(TraceSnapshot snapshot) {
timelinePanel.setMaxSnapAtLeast(current.getTrace().getTimeManager().getMaxSnap());
}
}
}

View File

@@ -1,109 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.timeline;
import com.google.common.collect.Range;
import ghidra.app.services.DebuggerModelService;
import ghidra.app.services.TraceRecorder;
import ghidra.dbg.target.TargetExecutionStateful.TargetExecutionState;
import ghidra.trace.model.Trace;
import ghidra.util.database.UndoableTransaction;
public class TimelineRow {
private final DebuggerModelService service;
private final TraceObject object;
public TimelineRow(DebuggerModelService service, TraceObject object) {
this.service = service;
this.object = object;
}
public TraceObject getObject() {
return object;
}
public Trace getTrace() {
return object.getTrace();
}
public void setName(String name) {
try (UndoableTransaction tid =
UndoableTransaction.start(object.getTrace(), "Renamed thread", true)) {
object.setName(name);
}
}
public String getName() {
return object.getName();
}
public long getCreationTick() {
return object.getCreationSnap();
}
public String getDestructionTick() {
long tick = object.getDestructionSnap();
return tick == Long.MAX_VALUE ? "" : Long.toString(tick);
}
public Range<Long> getLifespan() {
return object.getLifespan();
}
public void setComment(String comment) {
try (UndoableTransaction tid =
UndoableTransaction.start(object.getTrace(), "Renamed thread", true)) {
object.setComment(comment);
}
}
public String getComment() {
return object.getComment();
}
public TimelineState getState() {
if (!object.isAlive()) {
return TimelineState.TERMINATED;
}
TraceRecorder recorder = service.getRecorder(object.getTrace());
if (recorder == null) {
return TimelineState.ALIVE;
}
TargetExecutionState targetState = recorder.getTargetThreadState(object);
if (targetState == null) {
return TimelineState.UNKNOWN;
}
switch (targetState) {
case ALIVE:
return TimelineState.ALIVE;
case INACTIVE:
return TimelineState.UNKNOWN;
case RUNNING:
return TimelineState.RUNNING;
case STOPPED:
return TimelineState.STOPPED;
case TERMINATED:
return TimelineState.TERMINATED;
}
throw new AssertionError();
}
@Override
public String toString() {
return getName();
}
}

View File

@@ -1,43 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.timeline;
public enum TimelineState {
/**
* The last recorded state is alive, but the recorder is not tracking the live thread
*
* This state is generally erroneous. If it is seen, the recorder has fallen out of sync with
* the live session and/or the trace.
*/
UNKNOWN,
/**
* The last recorded state is alive, but there is no live session to know STOPPED or RUNNING
*/
// TODO: Should the thread state transitions be recorded?
ALIVE,
/**
* The thread is alive, but suspended
*/
STOPPED,
/**
* The thread is alive and running
*/
RUNNING,
/**
* The thread has been terminated (either as recorded, or as reported by the live session)
*/
TERMINATED;
}

View File

@@ -1,73 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.timeline;
import java.util.function.BiConsumer;
import java.util.function.Function;
import docking.widgets.table.DefaultEnumeratedColumnTableModel.EnumeratedTableColumn;
public enum TimelineTableColumns
implements EnumeratedTableColumn<TimelineTableColumns, TimelineRow> {
NAME("Name", String.class, TimelineRow::getName, TimelineRow::setName),
CREATED("Created", Long.class, TimelineRow::getCreationTick),
DESTROYED("Destroyed", String.class, TimelineRow::getDestructionTick),
STATE("State", TimelineState.class, TimelineRow::getState),
COMMENT("Comment", String.class, TimelineRow::getComment, TimelineRow::setComment);
private final String header;
private final Function<TimelineRow, ?> getter;
private final BiConsumer<TimelineRow, Object> setter;
private final Class<?> cls;
<T> TimelineTableColumns(String header, Class<T> cls, Function<TimelineRow, T> getter) {
this(header, cls, getter, null);
}
@SuppressWarnings("unchecked")
<T> TimelineTableColumns(String header, Class<T> cls, Function<TimelineRow, T> getter,
BiConsumer<TimelineRow, T> setter) {
this.header = header;
this.cls = cls;
this.getter = getter;
this.setter = (BiConsumer<TimelineRow, Object>) setter;
}
@Override
public String getHeader() {
return header;
}
@Override
public Class<?> getValueClass() {
return cls;
}
@Override
public Object getValueOf(TimelineRow row) {
return getter.apply(row);
}
@Override
public boolean isEditable(TimelineRow row) {
return setter != null;
}
@Override
public void setValueOf(TimelineRow row, Object value) {
setter.accept(row, value);
}
}

View File

@@ -1,151 +0,0 @@
/* ###
* IP: GHIDRA
*
* 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.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ghidra.app.plugin.core.debug.gui.timeline;
import com.google.common.collect.Range;
import ghidra.trace.model.Trace;
import ghidra.trace.model.modules.TraceModule;
import ghidra.trace.model.thread.TraceThread;
// TODO: Create an interface for time-bound objects
// Then either:
// 1) The appropriate trace and/or target objects can implement them
// 2) We create the necessary wrappers, one for trace threads, and another for TTD target objects
public class TraceObject implements TraceThread {
private Object object;
private Trace trace;
private String name;
private Long creationTick;
private Long destructionTick;
private Range<Long> lifespan;
private String comment;
private long key = 0L;
private boolean isThread = false;
public TraceObject(Trace currentTrace) {
this.trace = currentTrace;
this.object = null;
creationTick = 0L;
destructionTick = Long.MAX_VALUE;
lifespan = Range.atLeast(creationTick);
}
public TraceObject(TraceThread thread) {
this.trace = thread.getTrace();
this.object = thread;
name = thread.getName();
creationTick = thread.getCreationSnap();
destructionTick = thread.getDestructionSnap();
lifespan = thread.getLifespan();
key = thread.getKey();
comment = thread.getComment();
isThread = true;
}
public TraceObject(TraceModule module) {
this.trace = module.getTrace();
this.object = module;
name = module.getName();
creationTick = module.getLoadedSnap();
destructionTick = module.getUnloadedSnap();
lifespan = module.getLifespan();
comment = module.toString();
}
public Object getObject() {
return object;
}
@Override
public Trace getTrace() {
return trace;
}
@Override
public long getKey() {
return key;
}
@Override
public String getPath() {
return name; // TODO
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public void setCreationSnap(long creationTick) {
this.creationTick = creationTick;
lifespan = Range.closed(creationTick, destructionTick);
}
@Override
public long getCreationSnap() {
return creationTick;
}
@Override
public void setDestructionSnap(long destructionTick) {
this.destructionTick = destructionTick;
lifespan = Range.closed(creationTick, destructionTick);
}
@Override
public long getDestructionSnap() {
return destructionTick;
}
@Override
public void setLifespan(Range<Long> lifespan) {
this.lifespan = lifespan;
}
@Override
public Range<Long> getLifespan() {
return lifespan;
}
@Override
public void setComment(String comment) {
this.comment = comment;
}
@Override
public String getComment() {
return comment;
}
@Override
public void delete() {
// TODO Auto-generated method stub
}
public boolean isThread() {
return isThread;
}
}