mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
Merge remote-tracking branch 'origin/GP-870_d-millar_TimeOverview_RB250303--SQUASHED'
This commit is contained in:
commit
8f30c4da14
12 changed files with 1593 additions and 0 deletions
|
@ -106,6 +106,10 @@
|
|||
sortgroup="u"
|
||||
target="help/topics/DebuggerMemviewPlugin/DebuggerMemviewPlugin.html" />
|
||||
|
||||
<tocdef id="DebuggerTimeOverviewPlugin" text="Time Overview Sidebar"
|
||||
sortgroup="m"
|
||||
target="help/topics/DebuggerTimeOverviewPlugin/DebuggerTimeOverviewPlugin.html" />
|
||||
|
||||
<tocdef id="DebuggerPcodeStepperPlugin" text="P-code Stepper"
|
||||
sortgroup="v"
|
||||
target="help/topics/DebuggerPcodeStepperPlugin/DebuggerPcodeStepperPlugin.html" />
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
<!DOCTYPE doctype PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN">
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<META name="generator" content=
|
||||
"HTML Tidy for Java (vers. 2009-12-01), see jtidy.sourceforge.net">
|
||||
|
||||
<TITLE>Debugger: Time Overview</TITLE>
|
||||
<META http-equiv="Content-Type" content="text/html; charset=windows-1252">
|
||||
<LINK rel="stylesheet" type="text/css" href="help/shared/DefaultStyle.css">
|
||||
</HEAD>
|
||||
|
||||
<BODY lang="EN-US">
|
||||
<H1><A name="plugin"></A>Debugger: Time Overview</H1>
|
||||
|
||||
<P>Patterned on the Program Overview, this plugin provides a pair of sidebars for the
|
||||
Dynamic Listing that indicate the history of the current trace, similar to those in the
|
||||
<A href="help/topics/DebuggerTimePlugin/DebuggerTimePlugin.html">Time</A>
|
||||
and <A href="help/topics/DebuggerMemviewPlugin/DebuggerMemviewPlugin.html">Memview</A> plugins.
|
||||
The Trace Overview bar gives a compressed view of various events
|
||||
(thread creation/destruction, module loads/unloads, et cetera). Events are added in time
|
||||
snap order <em>without</em> spaces between consecutive events, as in the Memview display. The Trace
|
||||
Selection sidebar allows the user to click & drag over a section of either sidebar and zooms
|
||||
in on that time span. In the zoomed version, events are in snap order <em>with</em> intervening space
|
||||
to indicate the actual time delays (although these are dictated, in part, by the numbering
|
||||
schema for events).
|
||||
</P>
|
||||
|
||||
|
||||
<H2>Navigation</H2>
|
||||
|
||||
<H3><A name="zoom"></A><IMG alt="" src="icon.widget.imagepanel.zoom.in">Zoom</H3>
|
||||
|
||||
<P>Clicking and dragging over a region of the Trace Overview causes that
|
||||
span to be displayed uncompressed in the Trace Selection. The same action can also
|
||||
be applied to the Trace Selection itself, resulting in a more detailed zoom.</P>
|
||||
|
||||
<H3><A name="move"></A><IMG alt="" src="icon.widget.imagepanel.zoom.in">Move</H3>
|
||||
|
||||
<P>Shift click & drag on the Trace Selection allows the user to move forward and
|
||||
backward in the view without rescaling.
|
||||
</P>
|
||||
</BODY>
|
||||
</HTML>
|
|
@ -0,0 +1,251 @@
|
|||
/* ###
|
||||
* 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.timeoverview;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.*;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import ghidra.app.nav.Navigatable;
|
||||
import ghidra.app.util.viewer.listingpanel.OverviewProvider;
|
||||
import ghidra.app.util.viewer.util.AddressIndexMap;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.util.task.SwingUpdateManager;
|
||||
|
||||
/**
|
||||
* Overview bar component. Uses color to indicate various snap-based properties for a program.
|
||||
* Uses an {@link TimeOverviewColorService} to get the appropriate color for a snaps.
|
||||
*/
|
||||
public class TimeOverviewColorComponent extends JPanel implements OverviewProvider {
|
||||
private static final Color DEFAULT_COLOR = Color.GRAY;
|
||||
protected TimeOverviewColorService service;
|
||||
private Color[] colorsByPixel = new Color[0];
|
||||
private final SwingUpdateManager refreshUpdater =
|
||||
new SwingUpdateManager(100, 15000, () -> doRefresh());
|
||||
|
||||
private PluginTool tool;
|
||||
private List<DockingActionIf> actions;
|
||||
private TimeOverviewColorPlugin plugin;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param tool the PluginTool
|
||||
* @param overviewColorService the {@link TimeOverviewColorService} that provides colors for
|
||||
* various snaps.
|
||||
*/
|
||||
public TimeOverviewColorComponent(PluginTool tool,
|
||||
TimeOverviewColorService overviewColorService) {
|
||||
this.tool = tool;
|
||||
this.service = overviewColorService;
|
||||
overviewColorService.setOverviewComponent(this);
|
||||
addMouseListener(new MouseAdapter() {
|
||||
private int pressedY;
|
||||
private boolean enableDrag = false;
|
||||
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getButton() == MouseEvent.BUTTON1) {
|
||||
Long snap = service.getSnap(e.getY());
|
||||
gotoSnap(snap);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
enableDrag = true;
|
||||
pressedY = e.getY();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseReleased(MouseEvent e) {
|
||||
if (enableDrag) {
|
||||
Long start = service.getSnap(pressedY);
|
||||
Long stop = service.getSnap(e.getY());
|
||||
if (start == null || stop == null) {
|
||||
return;
|
||||
}
|
||||
Lifespan span;
|
||||
if ((e.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0) {
|
||||
Lifespan prev = getLifespan();
|
||||
if (prev != null) {
|
||||
int shift = stop.intValue() - start.intValue();
|
||||
span = Lifespan.span(prev.lmin() - shift, prev.lmax() - shift);
|
||||
}
|
||||
else {
|
||||
span = prev;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (start > stop) {
|
||||
Long tmp = stop;
|
||||
stop = start;
|
||||
start = tmp;
|
||||
}
|
||||
span = Lifespan.span(start, stop);
|
||||
}
|
||||
plugin.setLifespan(span);
|
||||
enableDrag = false;
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
ToolTipManager.sharedInstance().registerComponent(this);
|
||||
actions = service.getActions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs actions for this component
|
||||
*/
|
||||
public void installActions() {
|
||||
if (actions == null) {
|
||||
return;
|
||||
}
|
||||
for (DockingActionIf action : actions) {
|
||||
tool.addAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes previous installed actions for this component.
|
||||
*/
|
||||
public void uninstallActions() {
|
||||
if (actions == null) {
|
||||
return;
|
||||
}
|
||||
for (DockingActionIf action : actions) {
|
||||
tool.removeAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return new Dimension(16, 1);
|
||||
}
|
||||
|
||||
protected void gotoSnap(Long snap) {
|
||||
plugin.gotoSnap(snap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText(MouseEvent e) {
|
||||
Long snap = service.getSnap(e.getY());
|
||||
return service.getToolTipText(snap);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
int width = getWidth();
|
||||
int pixelCount = getOverviewPixelCount();
|
||||
|
||||
g.setColor(getBackground());
|
||||
g.fillRect(0, 0, width - 1, getHeight() - 1);
|
||||
|
||||
if (service.getTrace() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < pixelCount; i++) {
|
||||
Color color = getColor(i);
|
||||
g.setColor(color);
|
||||
g.fillRect(1, i, width - 3, 1);
|
||||
}
|
||||
if (colorsByPixel.length != pixelCount) {
|
||||
colorsByPixel = new Color[pixelCount];
|
||||
refreshUpdater.updateLater();
|
||||
}
|
||||
}
|
||||
|
||||
private Color getColor(int index) {
|
||||
if (colorsByPixel != null && index < colorsByPixel.length) {
|
||||
return colorsByPixel[index];
|
||||
}
|
||||
return DEFAULT_COLOR;
|
||||
}
|
||||
|
||||
public int getOverviewPixelCount() {
|
||||
return Math.max(getHeight(), 0);
|
||||
}
|
||||
|
||||
private void doRefresh() {
|
||||
for (int i = 0; i < colorsByPixel.length; i++) {
|
||||
if (colorsByPixel[i] == null) {
|
||||
Long snap = service.getSnap(i);
|
||||
colorsByPixel[i] = service.getColor(snap);
|
||||
}
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setLifeSet(TreeSet<Long> set) {
|
||||
service.setIndices(set);
|
||||
colorsByPixel = new Color[getOverviewPixelCount()];
|
||||
refreshUpdater.updateLater();
|
||||
}
|
||||
|
||||
/**
|
||||
* Causes this component to completely compute the colors used to paint the overview bar.
|
||||
*/
|
||||
public void refreshAll() {
|
||||
colorsByPixel = new Color[getOverviewPixelCount()];
|
||||
refreshUpdater.updateLater();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the PluginTool
|
||||
*
|
||||
* @return the PluginTool
|
||||
*/
|
||||
public PluginTool getTool() {
|
||||
return tool;
|
||||
}
|
||||
|
||||
public void setPlugin(TimeOverviewColorPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
service.setPlugin(plugin);
|
||||
}
|
||||
|
||||
public Lifespan getLifespan() {
|
||||
return service.getBounds();
|
||||
}
|
||||
|
||||
public void setLifespan(Lifespan bounds) {
|
||||
service.setBounds(bounds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setProgram(Program program, AddressIndexMap map) {
|
||||
// Ignored
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavigatable(Navigatable navigatable) {
|
||||
// Ignored
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,374 @@
|
|||
/* ###
|
||||
* 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.timeoverview;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import org.apache.commons.lang3.tuple.ImmutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.*;
|
||||
import docking.menu.MultiActionDockingAction;
|
||||
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.*;
|
||||
import ghidra.app.plugin.core.debug.gui.timeoverview.timetype.TimeType;
|
||||
import ghidra.app.services.DebuggerListingService;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
import ghidra.framework.options.SaveState;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.annotation.AutoServiceConsumed;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.bookmark.TraceBookmark;
|
||||
import ghidra.trace.model.bookmark.TraceBookmarkManager;
|
||||
import ghidra.trace.model.breakpoint.*;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.model.modules.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.thread.*;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import resources.ResourceManager;
|
||||
|
||||
/**
|
||||
* Plugin to manage {@link TimeOverviewColorService}s. It creates actions for each service and
|
||||
* installs and removes {@link TimeOverviewColorComponent} as indicated by the action.
|
||||
*/
|
||||
|
||||
@PluginInfo(
|
||||
status = PluginStatus.UNSTABLE,
|
||||
packageName = DebuggerPluginPackage.NAME,
|
||||
category = PluginCategoryNames.DEBUGGER,
|
||||
shortDescription = "Time Overview Color Manager",
|
||||
description = "Provides various color mappings for the trace snap space.",
|
||||
eventsConsumed = {
|
||||
TraceOpenedPluginEvent.class, //
|
||||
TraceClosedPluginEvent.class, //
|
||||
TraceActivatedPluginEvent.class, //
|
||||
}, //
|
||||
servicesRequired = { //
|
||||
DebuggerTraceManagerService.class, //
|
||||
} //
|
||||
)
|
||||
|
||||
public class TimeOverviewColorPlugin extends AbstractDebuggerPlugin {
|
||||
|
||||
@AutoServiceConsumed
|
||||
private DebuggerTraceManagerService traceManager;
|
||||
@AutoServiceConsumed
|
||||
private DebuggerListingService listingService;
|
||||
@SuppressWarnings("unused")
|
||||
private final AutoService.Wiring autoServiceWiring;
|
||||
|
||||
|
||||
public static final String HELP_TOPIC = "OverviewPlugin";
|
||||
private static final String ACTIVE_SERVICES = "ActiveServices";
|
||||
private List<TimeOverviewColorService> allServices;
|
||||
private Map<TimeOverviewColorService, TimeOverviewColorComponent> activeServices =
|
||||
new LinkedHashMap<>(); // maintain the left to right order of the active overview bars.
|
||||
private Map<TimeOverviewColorService, OverviewToggleAction> actionMap = new HashMap<>();
|
||||
private MultiActionDockingAction multiAction;
|
||||
|
||||
private Trace currentTrace;
|
||||
private final TimeOverviewEventListener eventListener = new TimeOverviewEventListener(this);
|
||||
private Map<Trace, TreeSet<Long>> sets = new WeakHashMap<>();
|
||||
private Map<Long, Set<Pair<TimeType, String>>> types = new HashMap<>();
|
||||
private long LMAX = Lifespan.ALL.lmax();
|
||||
|
||||
public TimeOverviewColorPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
autoServiceWiring = AutoService.wireServicesConsumed(tool, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
super.init();
|
||||
allServices = ClassSearcher.getInstances(TimeOverviewColorService.class);
|
||||
createActions();
|
||||
for (TimeOverviewColorService service : allServices) {
|
||||
service.initialize(tool);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readConfigState(SaveState saveState) {
|
||||
String[] activeServiceNames = saveState.getStrings(ACTIVE_SERVICES, new String[0]);
|
||||
for (String serviceName : activeServiceNames) {
|
||||
TimeOverviewColorService service = getService(serviceName);
|
||||
if (service == null) {
|
||||
Msg.warn(this, "Can't restore TimeOverviewColorService: " + serviceName);
|
||||
continue;
|
||||
}
|
||||
OverviewToggleAction action = actionMap.get(service);
|
||||
action.setSelected(true);
|
||||
// do this later so that they show up to the left of the standard marker service overview.
|
||||
SwingUtilities.invokeLater(() -> installOverview(service));
|
||||
}
|
||||
}
|
||||
|
||||
private TimeOverviewColorService getService(String serviceName) {
|
||||
for (TimeOverviewColorService service : allServices) {
|
||||
if (service.getName().equals(serviceName)) {
|
||||
return service;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cleanup() {
|
||||
|
||||
List<TimeOverviewColorService> services = new ArrayList<>(activeServices.keySet());
|
||||
for (TimeOverviewColorService service : services) {
|
||||
uninstallOverview(service);
|
||||
}
|
||||
listingService.removeLocalAction(multiAction);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeConfigState(SaveState saveState) {
|
||||
saveState.putStrings(ACTIVE_SERVICES, getActiveServiceNames());
|
||||
}
|
||||
|
||||
private String[] getActiveServiceNames() {
|
||||
List<String> names =
|
||||
activeServices.keySet().stream().map(s -> s.getName()).collect(Collectors.toList());
|
||||
|
||||
return names.toArray(new String[names.size()]);
|
||||
}
|
||||
|
||||
private void createActions() {
|
||||
for (TimeOverviewColorService overviewColorService : allServices) {
|
||||
actionMap.put(overviewColorService,
|
||||
new OverviewToggleAction(getName(), overviewColorService));
|
||||
}
|
||||
multiAction = new MultiActionDockingAction("TimeOverview", getName());
|
||||
//multiAction.setPerformActionOnButtonClick(false);
|
||||
multiAction.setActions(new ArrayList<DockingActionIf>(actionMap.values()));
|
||||
multiAction.setToolBarData(
|
||||
new ToolBarData(ResourceManager.loadImage("images/x-office-document-template.png")));
|
||||
listingService.addLocalAction(multiAction);
|
||||
multiAction.setDescription("Toggles trace overview margin displays.");
|
||||
multiAction.setHelpLocation(
|
||||
new HelpLocation(TimeOverviewColorPlugin.HELP_TOPIC,
|
||||
TimeOverviewColorPlugin.HELP_TOPIC));
|
||||
|
||||
}
|
||||
|
||||
private class OverviewToggleAction extends ToggleDockingAction {
|
||||
|
||||
private TimeOverviewColorService service;
|
||||
|
||||
public OverviewToggleAction(String owner, TimeOverviewColorService service) {
|
||||
super(service.getName(), owner);
|
||||
this.service = service;
|
||||
setMenuBarData(new MenuData(new String[] { "Show " + service.getName() }));
|
||||
setHelpLocation(service.getHelpLocation());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
if (isSelected()) {
|
||||
installOverview(service);
|
||||
}
|
||||
else {
|
||||
uninstallOverview(service);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Installs the given {@link TimeOverviewColorService} into the Listing margin bars. This is
|
||||
* public only for testing and screenshot purposes.
|
||||
*
|
||||
* @param overviewColorService the service to display colors in the Listing's margin bars.
|
||||
*/
|
||||
public void installOverview(TimeOverviewColorService overviewColorService) {
|
||||
overviewColorService.setTrace(currentTrace);
|
||||
TimeOverviewColorComponent overview =
|
||||
new TimeOverviewColorComponent(tool, overviewColorService);
|
||||
activeServices.put(overviewColorService, overview);
|
||||
listingService.addOverviewProvider(overview);
|
||||
overview.installActions();
|
||||
overview.setPlugin(this);
|
||||
}
|
||||
|
||||
private void uninstallOverview(TimeOverviewColorService overviewColorService) {
|
||||
TimeOverviewColorComponent overviewComponent = activeServices.get(overviewColorService);
|
||||
overviewComponent.uninstallActions();
|
||||
listingService.removeOverviewProvider(overviewComponent);
|
||||
activeServices.remove(overviewColorService);
|
||||
overviewColorService.setTrace(null);
|
||||
}
|
||||
|
||||
protected void traceActivated(Trace trace) {
|
||||
if (trace != null && trace != currentTrace) {
|
||||
if (currentTrace != null) {
|
||||
currentTrace.removeListener(eventListener);
|
||||
}
|
||||
currentTrace = trace;
|
||||
currentTrace.addListener(eventListener);
|
||||
for (TimeOverviewColorService service : activeServices.keySet()) {
|
||||
service.setTrace(trace);
|
||||
}
|
||||
updateMap();
|
||||
}
|
||||
}
|
||||
|
||||
protected void traceDeactivated(Trace trace) {
|
||||
if (trace == currentTrace) {
|
||||
currentTrace.removeListener(eventListener);
|
||||
currentTrace = null;
|
||||
for (TimeOverviewColorService service : activeServices.keySet()) {
|
||||
service.setTrace(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void processEvent(PluginEvent event) {
|
||||
super.processEvent(event);
|
||||
if (event instanceof TraceActivatedPluginEvent ev) {
|
||||
DebuggerCoordinates coordinates = ev.getActiveCoordinates();
|
||||
traceActivated(coordinates.getTrace());
|
||||
eventListener.coordinatesActivated(coordinates);
|
||||
}
|
||||
else if (event instanceof TraceClosedPluginEvent ev) {
|
||||
Trace trace = ev.getTrace();
|
||||
if (trace == currentTrace) {
|
||||
sets.remove(trace);
|
||||
traceDeactivated(trace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateMap() {
|
||||
TreeSet<Long> set = new TreeSet<>();
|
||||
Trace trace = traceManager.getCurrentTrace();
|
||||
TraceThreadManager threadManager = trace.getThreadManager();
|
||||
for (TraceThread thread : threadManager.getAllThreads()) {
|
||||
if (thread instanceof TraceObjectThread objThread) {
|
||||
addObject(set, objThread.getObject());
|
||||
}
|
||||
}
|
||||
TraceModuleManager moduleManager = trace.getModuleManager();
|
||||
for (TraceModule module : moduleManager.getAllModules()) {
|
||||
if (module instanceof TraceObjectModule objModule) {
|
||||
addObject(set, objModule.getObject());
|
||||
}
|
||||
}
|
||||
TraceMemoryManager memoryManager = trace.getMemoryManager();
|
||||
for (TraceMemoryRegion region : memoryManager.getAllRegions()) {
|
||||
if (region instanceof TraceObjectMemoryRegion objRegion) {
|
||||
addObject(set, objRegion.getObject());
|
||||
}
|
||||
}
|
||||
TraceBreakpointManager breakpointManager = trace.getBreakpointManager();
|
||||
for (TraceBreakpoint bpt : breakpointManager.getAllBreakpoints()) {
|
||||
if (bpt instanceof TraceObjectBreakpointLocation objBreakpoint) {
|
||||
addObject(set, objBreakpoint.getObject());
|
||||
}
|
||||
}
|
||||
TraceBookmarkManager bookmarkManager = trace.getBookmarkManager();
|
||||
for (TraceBookmark mark : bookmarkManager.getAllBookmarks()) {
|
||||
Lifespan span = mark.getLifespan();
|
||||
set.add(span.min());
|
||||
if (span.lmax() == LMAX) {
|
||||
set.add(span.max());
|
||||
}
|
||||
}
|
||||
for (TimeOverviewColorComponent provider : activeServices.values()) {
|
||||
provider.setLifeSet(set);
|
||||
}
|
||||
}
|
||||
|
||||
private void addObject(TreeSet<Long> set, TraceObject obj) {
|
||||
for (Lifespan span : obj.getLife().spans()) {
|
||||
set.add(span.min());
|
||||
if (span.lmax() == LMAX) {
|
||||
set.add(span.max());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void updateMap(long offset, TimeType type, String desc, boolean override) {
|
||||
TreeSet<Long> set = getLifeSet();
|
||||
if (!override && set.contains(offset)) {
|
||||
return;
|
||||
}
|
||||
if (offset == LMAX) {
|
||||
return;
|
||||
}
|
||||
set.add(offset);
|
||||
setLifespanType(offset, type, desc);
|
||||
for (TimeOverviewColorComponent provider : activeServices.values()) {
|
||||
provider.setLifeSet(set);
|
||||
}
|
||||
}
|
||||
|
||||
TreeSet<Long> getLifeSet() {
|
||||
TreeSet<Long> set = sets.get(currentTrace);
|
||||
if (set == null) {
|
||||
set = new TreeSet<>();
|
||||
sets.put(currentTrace, set);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the {@link TimeType} for the given offset
|
||||
*
|
||||
* @param offset the offset for which to get an LifespanType.
|
||||
* @return the {@link TimeType} for the given offset.
|
||||
*/
|
||||
public Set<Pair<TimeType, String>> getTypes(Long offset) {
|
||||
Set<Pair<TimeType, String>> set = types.get(offset);
|
||||
if (set == null) {
|
||||
set = new HashSet<>();
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
void setLifespanType(Long offset, TimeType type, String desc) {
|
||||
Set<Pair<TimeType, String>> set = getTypes(offset);
|
||||
set.add(new ImmutablePair<TimeType, String>(type, desc));
|
||||
types.put(offset, set);
|
||||
}
|
||||
|
||||
public void gotoSnap(Long offset) {
|
||||
if (offset == null) {
|
||||
offset = 0L;
|
||||
}
|
||||
traceManager.activateSnap(offset);
|
||||
}
|
||||
|
||||
public void setLifespan(Lifespan span) {
|
||||
for (TimeOverviewColorComponent provider : activeServices.values()) {
|
||||
provider.setLifespan(span);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
/* ###
|
||||
* 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.timeoverview;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.*;
|
||||
|
||||
import docking.action.DockingActionIf;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
|
||||
/**
|
||||
* Interface for services that know how to associate colors with any snap in a program. Instances
|
||||
* of these services are discovered and presented as options on the Listing's right margin area.
|
||||
*/
|
||||
public interface TimeOverviewColorService extends ExtensionPoint {
|
||||
|
||||
/**
|
||||
* Returns the name of this color service.
|
||||
*
|
||||
* @return the name of this color service.
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Returns the color that this service associates with the given snap.
|
||||
*
|
||||
* @param snap the snap to convert to a color.
|
||||
* @return the color that this service associates with the given snap.
|
||||
*/
|
||||
public Color getColor(Long snap);
|
||||
|
||||
/**
|
||||
* Sets the trace that this service will provide snap colors for.
|
||||
*
|
||||
* @param trace the program that this service will provide snap colors for.
|
||||
*/
|
||||
public void setTrace(Trace trace);
|
||||
|
||||
/**
|
||||
* Sets the component that will be displaying the colors for this
|
||||
* service.
|
||||
*
|
||||
* @param component the {@link TimeOverviewColorComponent} that will be displaying the colors
|
||||
* for this service.
|
||||
*/
|
||||
public void setOverviewComponent(TimeOverviewColorComponent component);
|
||||
|
||||
/**
|
||||
* Returns the tool tip that the {@link TimeOverviewColorComponent} should display when the
|
||||
* mouse is hovering on the pixel that maps to the given snap.
|
||||
*
|
||||
* @param snap the snap for which to get a tooltip.
|
||||
* @return the tooltip text for the given snap.
|
||||
*/
|
||||
public String getToolTipText(Long snap);
|
||||
|
||||
/**
|
||||
* Returns a list of popup actions to be shown when the user right-clicks on the
|
||||
* {@link TimeOverviewColorComponent} associated with this service.
|
||||
*
|
||||
* @return the list of popup actions.
|
||||
*/
|
||||
public List<DockingActionIf> getActions();
|
||||
|
||||
/**
|
||||
* Returns the {@link HelpLocation} for this service
|
||||
*
|
||||
* @return the {@link HelpLocation} for this service
|
||||
*/
|
||||
public HelpLocation getHelpLocation();
|
||||
|
||||
/**
|
||||
* Initialize the service which typically is used to read options for the service.
|
||||
*
|
||||
* @param tool the {@link PluginTool} using this service.
|
||||
*/
|
||||
public void initialize(PluginTool tool);
|
||||
|
||||
/**
|
||||
* Returns the current trace used by the service.
|
||||
*
|
||||
* @return the current trace used by the service.
|
||||
*/
|
||||
public Trace getTrace();
|
||||
|
||||
/**
|
||||
* Set the plugin
|
||||
*
|
||||
* @param plugin overview plugin
|
||||
*/
|
||||
public void setPlugin(TimeOverviewColorPlugin plugin);
|
||||
|
||||
/**
|
||||
* Get the snap for a given pixel's time coordinate
|
||||
*
|
||||
* @param pixel location in the display
|
||||
* @return snap
|
||||
*/
|
||||
public Long getSnap(int pixel);
|
||||
|
||||
/**
|
||||
* Set the indices for mapping pixels->indices->snaps (and vice-versa)
|
||||
*
|
||||
* @param set tree-set of snaps
|
||||
*/
|
||||
public void setIndices(TreeSet<Long> set);
|
||||
|
||||
/**
|
||||
* Get the display bounds
|
||||
*
|
||||
* @return bounds time-range to display
|
||||
*/
|
||||
public Lifespan getBounds();
|
||||
|
||||
/**
|
||||
* Set the display bounds
|
||||
*
|
||||
* @param bounds time-range to display
|
||||
*/
|
||||
public void setBounds(Lifespan bounds);
|
||||
|
||||
}
|
|
@ -0,0 +1,329 @@
|
|||
/* ###
|
||||
* 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.timeoverview;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import ghidra.app.plugin.core.debug.gui.timeoverview.timetype.TimeType;
|
||||
import ghidra.debug.api.tracemgr.DebuggerCoordinates;
|
||||
import ghidra.framework.model.DomainObjectChangeRecord;
|
||||
import ghidra.framework.model.DomainObjectEvent;
|
||||
import ghidra.trace.model.*;
|
||||
import ghidra.trace.model.bookmark.TraceBookmark;
|
||||
import ghidra.trace.model.breakpoint.*;
|
||||
import ghidra.trace.model.memory.*;
|
||||
import ghidra.trace.model.modules.*;
|
||||
import ghidra.trace.model.target.TraceObject;
|
||||
import ghidra.trace.model.thread.*;
|
||||
import ghidra.trace.util.TraceEvents;
|
||||
import ghidra.util.Swing;
|
||||
|
||||
public class TimeOverviewEventListener extends TraceDomainObjectListener {
|
||||
|
||||
private TimeOverviewColorPlugin p;
|
||||
private DebuggerCoordinates current = DebuggerCoordinates.NOWHERE;
|
||||
|
||||
public TimeOverviewEventListener(TimeOverviewColorPlugin plugin) {
|
||||
|
||||
this.p = plugin;
|
||||
|
||||
listenForUntyped(DomainObjectEvent.RESTORED, this::objectRestored);
|
||||
|
||||
listenFor(TraceEvents.THREAD_ADDED, this::threadAdded);
|
||||
listenFor(TraceEvents.THREAD_CHANGED, this::threadChanged);
|
||||
listenFor(TraceEvents.THREAD_LIFESPAN_CHANGED, this::threadChanged);
|
||||
listenFor(TraceEvents.THREAD_DELETED, this::threadDeleted);
|
||||
|
||||
listenFor(TraceEvents.MODULE_ADDED, this::moduleAdded);
|
||||
listenFor(TraceEvents.MODULE_CHANGED, this::moduleChanged);
|
||||
listenFor(TraceEvents.MODULE_LIFESPAN_CHANGED, this::moduleChanged);
|
||||
listenFor(TraceEvents.MODULE_DELETED, this::moduleDeleted);
|
||||
|
||||
listenFor(TraceEvents.REGION_ADDED, this::regionAdded);
|
||||
listenFor(TraceEvents.REGION_CHANGED, this::regionChanged);
|
||||
listenFor(TraceEvents.REGION_LIFESPAN_CHANGED, this::regionChanged);
|
||||
listenFor(TraceEvents.REGION_DELETED, this::regionDeleted);
|
||||
|
||||
listenFor(TraceEvents.BREAKPOINT_ADDED, this::bptAdded);
|
||||
listenFor(TraceEvents.BREAKPOINT_CHANGED, this::bptChanged);
|
||||
listenFor(TraceEvents.BREAKPOINT_LIFESPAN_CHANGED, this::bptChanged);
|
||||
listenFor(TraceEvents.BREAKPOINT_DELETED, this::bptDeleted);
|
||||
|
||||
listenFor(TraceEvents.BOOKMARK_ADDED, this::bookmarkAdded);
|
||||
listenFor(TraceEvents.BOOKMARK_CHANGED, this::bookmarkChanged);
|
||||
listenFor(TraceEvents.BOOKMARK_LIFESPAN_CHANGED, this::bookmarkChanged);
|
||||
listenFor(TraceEvents.BOOKMARK_DELETED, this::bookmarkDeleted);
|
||||
|
||||
}
|
||||
|
||||
public void coordinatesActivated(DebuggerCoordinates coordinates) {
|
||||
//DebuggerCoordinates adjusted = adjustCoordinates(coordinates);
|
||||
setCoordinates(coordinates);
|
||||
Trace trace = coordinates.getTrace();
|
||||
if (trace != null) {
|
||||
Swing.runLater(() -> processTrace(trace));
|
||||
}
|
||||
}
|
||||
|
||||
protected void addListener() {
|
||||
Trace trace = current.getTrace();
|
||||
if (trace != null) {
|
||||
trace.addListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected void removeListener() {
|
||||
Trace trace = current.getTrace();
|
||||
if (trace != null) {
|
||||
trace.removeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void setCoordinates(DebuggerCoordinates coordinates) {
|
||||
boolean doListeners = !Objects.equals(current.getTrace(), coordinates.getTrace());
|
||||
if (doListeners) {
|
||||
removeListener();
|
||||
}
|
||||
current = coordinates;
|
||||
if (doListeners) {
|
||||
addListener();
|
||||
}
|
||||
}
|
||||
|
||||
private void processTrace(Trace trace) {
|
||||
//updateList.clear();
|
||||
TraceThreadManager threadManager = trace.getThreadManager();
|
||||
for (TraceThread thread : threadManager.getAllThreads()) {
|
||||
threadChanged(thread);
|
||||
}
|
||||
TraceModuleManager moduleManager = trace.getModuleManager();
|
||||
for (TraceModule module : moduleManager.getAllModules()) {
|
||||
moduleChanged(module);
|
||||
}
|
||||
TraceMemoryManager memoryManager = trace.getMemoryManager();
|
||||
for (TraceMemoryRegion region : memoryManager.getAllRegions()) {
|
||||
regionChanged(region);
|
||||
}
|
||||
TraceBreakpointManager breakpointManager = trace.getBreakpointManager();
|
||||
for (TraceBreakpoint bpt : breakpointManager.getAllBreakpoints()) {
|
||||
bptChanged(bpt);
|
||||
}
|
||||
}
|
||||
|
||||
private void threadAdded(TraceThread thread) {
|
||||
if (!(thread instanceof TraceObjectThread objThread)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TraceObject obj = objThread.getObject();
|
||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectBreakpointLocation.KEY_RANGE, true)
|
||||
.forEach(v -> {
|
||||
long snap = v.getMinSnap();
|
||||
p.updateMap(snap, TimeType.BPT_ADDED, thread.getName(snap), true);
|
||||
});
|
||||
}
|
||||
|
||||
private void threadChanged(TraceThread thread) {
|
||||
if (!(thread instanceof TraceObjectThread objThread)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TraceObject obj = objThread.getObject();
|
||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectThread.KEY_TID, true)
|
||||
.forEach(v -> {
|
||||
long snapMin = v.getMinSnap();
|
||||
long snapMax = v.getMaxSnap();
|
||||
if (snapMin == snapMax) {
|
||||
p.updateMap(snapMin, TimeType.THREAD_CHANGED, thread.getName(snapMin), true);
|
||||
}
|
||||
else {
|
||||
p.updateMap(snapMin, TimeType.THREAD_ADDED, thread.getName(snapMin), true);
|
||||
p.updateMap(snapMax, TimeType.THREAD_REMOVED, thread.getName(snapMax), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void threadDeleted(TraceThread thread) {
|
||||
if (!(thread instanceof TraceObjectThread objThread)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TraceObject obj = objThread.getObject();
|
||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectBreakpointLocation.KEY_RANGE, true)
|
||||
.forEach(v -> {
|
||||
long snap = v.getMaxSnap();
|
||||
p.updateMap(snap, TimeType.THREAD_REMOVED, thread.getName(snap), true);
|
||||
});
|
||||
}
|
||||
|
||||
private void moduleAdded(TraceModule module) {
|
||||
if (!(module instanceof TraceObjectModule objMod)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TraceObject obj = objMod.getObject();
|
||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectBreakpointLocation.KEY_RANGE, true)
|
||||
.forEach(v -> {
|
||||
long snap = v.getMinSnap();
|
||||
p.updateMap(snap, TimeType.MODULE_ADDED, module.getName(snap), true);
|
||||
});
|
||||
}
|
||||
|
||||
private void moduleChanged(TraceModule module) {
|
||||
if (!(module instanceof TraceObjectModule objMod)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TraceObject obj = objMod.getObject();
|
||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectBreakpointLocation.KEY_RANGE, true)
|
||||
.forEach(v -> {
|
||||
long snapMin = v.getMinSnap();
|
||||
long snapMax = v.getMaxSnap();
|
||||
if (snapMin == snapMax) {
|
||||
p.updateMap(snapMin, TimeType.MODULE_CHANGED, module.getName(snapMin), true);
|
||||
}
|
||||
else {
|
||||
p.updateMap(snapMin, TimeType.MODULE_ADDED, module.getName(snapMin), true);
|
||||
p.updateMap(snapMax, TimeType.MODULE_REMOVED, module.getName(snapMax), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void moduleDeleted(TraceModule module) {
|
||||
if (!(module instanceof TraceObjectModule objMod)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TraceObject obj = objMod.getObject();
|
||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectBreakpointLocation.KEY_RANGE, true)
|
||||
.forEach(v -> {
|
||||
long snap = v.getMaxSnap();
|
||||
p.updateMap(snap, TimeType.MODULE_REMOVED, module.getName(snap), true);
|
||||
});
|
||||
}
|
||||
|
||||
private void regionAdded(TraceMemoryRegion region) {
|
||||
if (!(region instanceof TraceObjectMemoryRegion objReg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TraceObject obj = objReg.getObject();
|
||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectBreakpointLocation.KEY_RANGE, true)
|
||||
.forEach(v -> {
|
||||
long snap = v.getMinSnap();
|
||||
p.updateMap(snap, TimeType.REGION_ADDED, region.getName(snap), true);
|
||||
});
|
||||
}
|
||||
|
||||
private void regionChanged(TraceMemoryRegion region) {
|
||||
if (!(region instanceof TraceObjectMemoryRegion objReg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TraceObject obj = objReg.getObject();
|
||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectBreakpointLocation.KEY_RANGE, true)
|
||||
.forEach(v -> {
|
||||
long snapMin = v.getMinSnap();
|
||||
long snapMax = v.getMaxSnap();
|
||||
if (snapMin == snapMax) {
|
||||
p.updateMap(snapMin, TimeType.REGION_CHANGED, region.getName(snapMin), true);
|
||||
}
|
||||
else {
|
||||
p.updateMap(snapMin, TimeType.REGION_ADDED, region.getName(snapMin), true);
|
||||
p.updateMap(snapMax, TimeType.REGION_REMOVED, region.getName(snapMax), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void regionDeleted(TraceMemoryRegion region) {
|
||||
if (!(region instanceof TraceObjectMemoryRegion objReg)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TraceObject obj = objReg.getObject();
|
||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectBreakpointLocation.KEY_RANGE, true)
|
||||
.forEach(v -> {
|
||||
long snap = v.getMaxSnap();
|
||||
p.updateMap(snap, TimeType.REGION_REMOVED, region.getName(snap), true);
|
||||
});
|
||||
}
|
||||
|
||||
private void bptAdded(TraceBreakpoint bpt) {
|
||||
if (!(bpt instanceof TraceObjectBreakpointLocation objBpt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TraceObject obj = objBpt.getObject();
|
||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectBreakpointLocation.KEY_RANGE, true)
|
||||
.forEach(v -> {
|
||||
long snap = v.getMinSnap();
|
||||
p.updateMap(snap, TimeType.BPT_ADDED, bpt.getName(snap), true);
|
||||
});
|
||||
}
|
||||
|
||||
private void bptChanged(TraceBreakpoint bpt) {
|
||||
if (!(bpt instanceof TraceObjectBreakpointLocation objBpt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TraceObject obj = objBpt.getObject();
|
||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectBreakpointLocation.KEY_RANGE, true)
|
||||
.forEach(v -> {
|
||||
long snapMin = v.getMinSnap();
|
||||
long snapMax = v.getMaxSnap();
|
||||
if (snapMin == snapMax) {
|
||||
p.updateMap(snapMin, TimeType.BPT_CHANGED, bpt.getName(snapMin), true);
|
||||
}
|
||||
else {
|
||||
p.updateMap(snapMin, TimeType.BPT_ADDED, bpt.getName(snapMin), true);
|
||||
p.updateMap(snapMax, TimeType.BPT_REMOVED, bpt.getName(snapMax), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void bptDeleted(TraceBreakpoint bpt) {
|
||||
if (!(bpt instanceof TraceObjectBreakpointLocation objBpt)) {
|
||||
return;
|
||||
}
|
||||
|
||||
TraceObject obj = objBpt.getObject();
|
||||
obj.getOrderedValues(Lifespan.ALL, TraceObjectBreakpointLocation.KEY_RANGE, true)
|
||||
.forEach(v -> {
|
||||
long snap = v.getMaxSnap();
|
||||
p.updateMap(snap, TimeType.BPT_REMOVED, bpt.getName(snap), true);
|
||||
});
|
||||
}
|
||||
|
||||
private void bookmarkAdded(TraceBookmark bookmark) {
|
||||
long snap = bookmark.getLifespan().lmin();
|
||||
p.updateMap(snap, TimeType.BOOKMARK_ADDED, bookmark.getComment(), true);
|
||||
}
|
||||
|
||||
private void bookmarkChanged(TraceBookmark bookmark) {
|
||||
long snapMin = bookmark.getLifespan().lmin();
|
||||
p.updateMap(snapMin, TimeType.BOOKMARK_CHANGED, bookmark.getComment(), false);
|
||||
}
|
||||
|
||||
private void bookmarkDeleted(TraceBookmark bookmark) {
|
||||
long snap = bookmark.getLifespan().lmax();
|
||||
p.updateMap(snap, TimeType.BOOKMARK_REMOVED, bookmark.getComment(), true);
|
||||
}
|
||||
|
||||
private void objectRestored(DomainObjectChangeRecord domainobjectchangerecord1) {
|
||||
p.updateMap();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/* ###
|
||||
* 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.timeoverview.timetype;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import ghidra.trace.model.Lifespan;
|
||||
|
||||
public class TimeSelectionOverviewColorService
|
||||
extends TimeTypeOverviewColorService {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Trace Selection";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setIndices(TreeSet<Long> set) {
|
||||
snapToIndex = new HashMap<>();
|
||||
indexToSnap = new HashMap<>();
|
||||
if (bounds != null) {
|
||||
set.add(bounds.min());
|
||||
int splits = overviewComponent.getOverviewPixelCount();
|
||||
float span = (float)(bounds.lmax() - bounds.lmin())/splits;
|
||||
for (int i = 0; i < splits; i++) {
|
||||
long snap = (long)(bounds.lmin() + i*span);
|
||||
snapToIndex.put(snap, i);
|
||||
indexToSnap.put(i, snap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lifespan getBounds() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(Lifespan bounds) {
|
||||
this.bounds = bounds;
|
||||
TreeSet<Long> minset = new TreeSet<>();
|
||||
minset.add(bounds.min());
|
||||
overviewComponent.setLifeSet(minset);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/* ###
|
||||
* 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.timeoverview.timetype;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
import generic.theme.GColor;
|
||||
|
||||
/**
|
||||
* An enum for the different types that are represented by unique colors by the
|
||||
* {@link TimeTypeOverviewColorService}
|
||||
*/
|
||||
public enum TimeType {
|
||||
THREAD_ADDED("+T", new GColor("color.debugger.plugin.timeoverview.box.type.thread.added")),
|
||||
THREAD_REMOVED("-T", new GColor("color.debugger.plugin.timeoverview.box.type.thread.removed")),
|
||||
THREAD_CHANGED("*T", new GColor("color.debugger.plugin.timeoverview.box.type.thread.changed")),
|
||||
MODULE_ADDED("+M", new GColor("color.debugger.plugin.timeoverview.box.type.module.added")),
|
||||
MODULE_REMOVED("-M", new GColor("color.debugger.plugin.timeoverview.box.type.module.removed")),
|
||||
MODULE_CHANGED("*M", new GColor("color.debugger.plugin.timeoverview.box.type.module.changed")),
|
||||
REGION_ADDED("+R", new GColor("color.debugger.plugin.timeoverview.box.type.region.added")),
|
||||
REGION_REMOVED("-R", new GColor("color.debugger.plugin.timeoverview.box.type.region.removed")),
|
||||
REGION_CHANGED("*R", new GColor("color.debugger.plugin.timeoverview.box.type.region.changed")),
|
||||
BPT_ADDED("+B", new GColor("color.debugger.plugin.timeoverview.box.type.breakpoint.added")),
|
||||
BPT_REMOVED("-B", new GColor("color.debugger.plugin.timeoverview.box.type.breakpoint.removed")),
|
||||
BPT_CHANGED("*B", new GColor("color.debugger.plugin.timeoverview.box.type.breakpoint.changed")),
|
||||
BPT_HIT(">B", new GColor("color.debugger.plugin.timeoverview.box.type.breakpoint.hit")),
|
||||
BOOKMARK_ADDED("+MK", new GColor("color.debugger.plugin.timeoverview.box.type.bookmark.added")),
|
||||
BOOKMARK_REMOVED("-MK", new GColor("color.debugger.plugin.timeoverview.box.type.bookmark.removed")),
|
||||
BOOKMARK_CHANGED("*MK", new GColor("color.debugger.plugin.timeoverview.box.type.bookmark.changed")),
|
||||
UNDEFINED("", new GColor("color.debugger.plugin.timeoverview.box.type.undefined"));
|
||||
|
||||
final private String description;
|
||||
final private Color color;
|
||||
|
||||
TimeType(String description, Color color) {
|
||||
this.description = description;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a description of this enum value.
|
||||
*
|
||||
* @return a description of this enum value.
|
||||
*/
|
||||
public String getDescription() {
|
||||
return description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a color of this enum value.
|
||||
*
|
||||
* @return a color of this enum value.
|
||||
*/
|
||||
public Color getDefaultColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
/* ###
|
||||
* 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.timeoverview.timetype;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.math.BigInteger;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.action.DockingActionIf;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import generic.theme.GThemeDefaults.Colors;
|
||||
import ghidra.app.plugin.core.debug.gui.timeoverview.*;
|
||||
import ghidra.app.plugin.core.overview.OverviewColorLegendDialog;
|
||||
import ghidra.app.plugin.core.overview.OverviewColorPlugin;
|
||||
import ghidra.framework.options.ToolOptions;
|
||||
import ghidra.framework.plugintool.PluginTool;
|
||||
import ghidra.trace.model.Lifespan;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.util.*;
|
||||
|
||||
public class TimeTypeOverviewColorService implements TimeOverviewColorService {
|
||||
private static final String OPTIONS_NAME = "Time Overview";
|
||||
private static final Color DEFAULT_UNDEFINED_COLOR = Color.LIGHT_GRAY;
|
||||
private static final Color DEFAULT_UNINITIALIZED_COLOR = Color.GRAY;
|
||||
|
||||
Map<TimeType, Color> colorMap = new HashMap<>();
|
||||
Color undefinedColor = DEFAULT_UNDEFINED_COLOR;
|
||||
Color uninitializedColor = DEFAULT_UNINITIALIZED_COLOR;
|
||||
|
||||
private Trace trace;
|
||||
protected TimeOverviewColorComponent overviewComponent;
|
||||
private PluginTool tool;
|
||||
private DialogComponentProvider legendDialog;
|
||||
private TimeTypeOverviewLegendPanel legendPanel;
|
||||
private TimeOverviewColorPlugin plugin;
|
||||
|
||||
protected Map<Integer,Long> indexToSnap = new HashMap<>();
|
||||
protected Map<Long,Integer> snapToIndex = new HashMap<>();
|
||||
protected Lifespan bounds;
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Trace Overview";
|
||||
}
|
||||
|
||||
@Override
|
||||
public HelpLocation getHelpLocation() {
|
||||
return new HelpLocation("DebuggerTimeOverviewPlugin", "plugin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color getColor(Long snap) {
|
||||
Set<Pair<TimeType, String>> types = plugin.getTypes(snap);
|
||||
Color c = Colors.BACKGROUND;
|
||||
for (Pair<TimeType, String> pair : types) {
|
||||
c = ColorUtils.addColors(c, pair.getLeft().getDefaultColor());
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getToolTipText(Long snap) {
|
||||
// TODO: Right now, there's an inconsistency in how time is rendered
|
||||
// the Time Table uses decimal; the Model Tree, Memview, and Overview
|
||||
// use hex
|
||||
if (snap == null) {
|
||||
return "";
|
||||
}
|
||||
Set<Pair<TimeType, String>> types = plugin.getTypes(snap);
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append("<b>");
|
||||
buffer.append(HTMLUtilities.escapeHTML(getName()));
|
||||
buffer.append(" (");
|
||||
buffer.append(Long.toHexString(snap));
|
||||
buffer.append(")");
|
||||
buffer.append("</b>\n");
|
||||
for (Pair<TimeType, String> pair : types) {
|
||||
TimeType tt = pair.getLeft();
|
||||
String key = pair.getRight();
|
||||
buffer.append(tt.getDescription() + " : " + key + "\n");
|
||||
}
|
||||
return HTMLUtilities.toWrappedHTML(buffer.toString(), 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<DockingActionIf> getActions() {
|
||||
List<DockingActionIf> actions = new ArrayList<>();
|
||||
actions.add(new ActionBuilder("Show Legend", getName())
|
||||
.popupMenuPath("Show Legend")
|
||||
.description("Show types and associated colors")
|
||||
.helpLocation(getHelpLocation())
|
||||
.enabledWhen(c -> c.getContextObject() == overviewComponent)
|
||||
.onAction(c -> tool.showDialog(getLegendDialog()))
|
||||
.build());
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTrace(Trace trace) {
|
||||
this.trace = trace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(PluginTool pluginTool) {
|
||||
this.tool = pluginTool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOverviewComponent(TimeOverviewColorComponent component) {
|
||||
this.overviewComponent = component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the color associated with the given {@link TimeType}
|
||||
*
|
||||
* @param timeType the span type for which to get a color.
|
||||
* @return the color associated with the given {@link TimeType}
|
||||
*/
|
||||
public Color getColor(TimeType timeType) {
|
||||
Color color = colorMap.get(timeType);
|
||||
if (color == null) {
|
||||
colorMap.put(timeType, timeType.getDefaultColor());
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color to be associated with a given {@link TimeType}
|
||||
*
|
||||
* @param type the LifespanType for which to assign the color.
|
||||
* @param newColor the new color for the given {@link TimeType}
|
||||
*/
|
||||
public void setColor(TimeType type, Color newColor) {
|
||||
ToolOptions options = tool.getOptions(OPTIONS_NAME);
|
||||
options.setColor(type.getDescription(), newColor);
|
||||
}
|
||||
|
||||
private DialogComponentProvider getLegendDialog() {
|
||||
if (legendDialog == null) {
|
||||
legendPanel = new TimeTypeOverviewLegendPanel(this);
|
||||
|
||||
legendDialog =
|
||||
new OverviewColorLegendDialog("Overview Legend", legendPanel, getHelpLocation());
|
||||
}
|
||||
return legendDialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Trace getTrace() {
|
||||
return trace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPlugin(TimeOverviewColorPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long getSnap(int pixelIndex) {
|
||||
BigInteger bigHeight = BigInteger.valueOf(overviewComponent.getOverviewPixelCount());
|
||||
BigInteger bigPixelIndex = BigInteger.valueOf(pixelIndex);
|
||||
|
||||
BigInteger span = BigInteger.valueOf(indexToSnap.size());
|
||||
BigInteger offset = span.multiply(bigPixelIndex).divide(bigHeight);
|
||||
return indexToSnap.get(offset.intValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setIndices(TreeSet<Long> set) {
|
||||
snapToIndex = new HashMap<>();
|
||||
indexToSnap = new HashMap<>();
|
||||
int index = 0;
|
||||
Iterator<Long> iterator = set.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
Long snap = iterator.next();
|
||||
snapToIndex.put(snap, index);
|
||||
indexToSnap.put(index, snap);
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Lifespan getBounds() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBounds(Lifespan bounds) {
|
||||
this.bounds = bounds;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
/* ###
|
||||
* 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.timeoverview.timetype;
|
||||
|
||||
import java.awt.*;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.widgets.label.GLabel;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
||||
/**
|
||||
* A component for displaying the color legend for the {@link TimeTypeOverviewColorService}
|
||||
*/
|
||||
public class TimeTypeOverviewLegendPanel extends JPanel {
|
||||
private static Dimension COLOR_SIZE = new Dimension(15, 15);
|
||||
private TimeTypeOverviewColorService colorService;
|
||||
|
||||
public TimeTypeOverviewLegendPanel(TimeTypeOverviewColorService colorService) {
|
||||
this.colorService = colorService;
|
||||
setLayout(new PairLayout(4, 10));
|
||||
setBorder(BorderFactory.createEmptyBorder(4, 20, 4, 30));
|
||||
buildLegend();
|
||||
}
|
||||
|
||||
/**
|
||||
* Kick to repaint when the colors have changed.
|
||||
*/
|
||||
public void updateColors() {
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void buildLegend() {
|
||||
removeAll();
|
||||
TimeType[] values = TimeType.values();
|
||||
for (TimeType timeType : values) {
|
||||
JPanel panel = new ColorPanel(timeType);
|
||||
add(panel);
|
||||
add(new GLabel(timeType.getDescription()));
|
||||
}
|
||||
}
|
||||
|
||||
private class ColorPanel extends JPanel {
|
||||
private TimeType type;
|
||||
|
||||
ColorPanel(TimeType type) {
|
||||
this.type = type;
|
||||
setPreferredSize(COLOR_SIZE);
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mousePressed(MouseEvent e) {
|
||||
Color newColor =
|
||||
JColorChooser.showDialog(ColorPanel.this, "Select Color", getBackground());
|
||||
colorService.setColor(type, newColor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
setBackground(colorService.getColor(type));
|
||||
super.paintComponent(g);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue