Merge remote-tracking branch 'origin/GP-6990_ghidra_red_BreakpointTimelineDocs--SQUASHED'

This commit is contained in:
Ryan Kurtz
2026-07-01 13:47:25 -04:00
11 changed files with 496 additions and 46 deletions

View File

@@ -9,6 +9,10 @@ README.md||GHIDRA||||END|
data/ExtensionPoint.manifest||GHIDRA||||END|
data/debugger.theme.properties||GHIDRA||||END|
src/main/help/help/TOC_Source.xml||GHIDRA||||END|
src/main/help/help/topics/BreakpointTimelinePlugin/BreakpointTimelinePlugin.html||GHIDRA||||END|
src/main/help/help/topics/BreakpointTimelinePlugin/images/BreakpointTimelinePlugin.png||GHIDRA||||END|
src/main/help/help/topics/BreakpointTimelinePlugin/images/BreakpointTimelinePluginNoGrid.png||GHIDRA||||END|
src/main/help/help/topics/BreakpointTimelinePlugin/images/BreakpointTimelinePluginSingleColumn.png||GHIDRA||||END|
src/main/help/help/topics/Debugger/Debugger.html||GHIDRA||||END|
src/main/help/help/topics/Debugger/GettingStarted.html||GHIDRA||||END|
src/main/help/help/topics/Debugger/Troubleshooting.html||GHIDRA||||END|

View File

@@ -72,6 +72,10 @@
text="In the Listings"
sortgroup="a"
target="help/topics/DebuggerBreakpointMarkerPlugin/DebuggerBreakpointMarkerPlugin.html" />
<tocdef id="BreakpointTimelinePlugin" text="Breakpoint Timeline"
sortgroup="b"
target="help/topics/BreakpointTimelinePlugin/BreakpointTimelinePlugin.html" />
</tocdef>
<tocdef id="DebuggerRegionsPlugin" text="Memory Regions"

View File

@@ -0,0 +1,216 @@
<!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: Breakpoint Timeline</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: Breakpoint Timeline</H1>
<DIV class="image">
<IMG alt="" src="images/BreakpointTimelinePlugin.png">
</DIV>
<P>The Breakpoint Timeline window provides a visual overview of all snapshots in the current
trace, highlighting snapshots where breakpoints were hit. Each cell in the grid represents one
or more consecutive snapshots. Cells are ordered left to right, top to bottom starting from the
lowest snapshot to the highest. Cells are colored according to the type of breakpoint hit
recorded in those snapshots. Clicking a cell navigates to the earliest hit or snapshot in the
cell. A click and drag across cells opens another Timeline window zoomed on the selected
snapshot range.</P>
<P>Breakpoint hits are detected by examining the trace's recorded data. For execution
breakpoints (hardware or software), the plugin searches for stack frames whose program counter
falls within the breakpoint's address range. For memory read and write breakpoints, it searches
for trace memory references to the breakpoint's address range with the corresponding reference
type. The plugin listens for changes to breakpoints and snapshots and updates the display
automatically. <B>NOTE:</B> R/W breakpoints are only supported for connectors and/or importers
that record memory references to the trace. At this time, that includes only the TENET and
TENET++ loaders.</P>
<P>The timeline panel fills the window with a grid of cells. Each cell covers a contiguous
range of snapshots. The number of snapshots per cell is determined automatically so that all
snapshots from 0 to the maximum snapshot (or from the start to the stop snapshot of a zoomed
window) fit within the visible area.</P>
<H3>Cell Colors</H3>
<P>Each cell is filled with a color indicating the breakpoint activity in its snapshot
range:</P>
<UL>
<LI><B>Empty (Background color)</B> - No breakpoint hit was recorded in this range.</LI>
<LI><B>Instruction hit (Red by default)</B> - An execution breakpoint was hit in this
range.</LI>
<LI><B>Memory read (Blue by default)</B> - A memory read breakpoint was hit in this
range.</LI>
<LI><B>Memory write (Yellow by default)</B> - A memory write breakpoint was hit in this
range.</LI>
<LI><B>Multiple hits (Colors of hits are blended)</B> - Multiple breakpoint types were hit in
this range.</LI>
</UL>
<P>In addition, three overlay colors indicate interactive state:</P>
<UL>
<LI><B>Current snapshot (Green by default)</B> - The cell containing the active snapshot is
highlighted.</LI>
<LI><B>Hover (Green by default)</B> - The cell under the mouse cursor is highlighted.</LI>
<LI><B>Selection (Green by default)</B> - Cells covered by an active drag selection are
highlighted.</LI>
</UL>
<H3>Tooltips</H3>
<P>Hovering over a cell shows a tooltip with:</P>
<UL>
<LI>The snapshot range covered by the cell (start and end snapshot numbers).</LI>
<LI>The snapshot the cell will navigate to when clicked.</LI>
<LI>The breakpoint type of the first event in the cell (if any).</LI>
<LI>The name of the breakpoint that was hit (if any).</LI>
</UL>
<H2>Navigation and Interaction</H2>
<H3>Click to Navigate</H3>
<P>Clicking a cell activates the snapshot associated with the first breakpoint hit event in
that cell. If the cell contains no breakpoint hit, the first snapshot in the cell's range is
activated.</P>
<H3>Click and Drag to Zoom</H3>
<P>Clicking and dragging across a range of cells opens a new zoom window focused on the
snapshot range covered by the drag selection. The zoom window is a separate <B>Breakpoint
Timeline</B> provider showing only the selected sub-range. Zoom windows are trace specific and
are hidden when a different trace is activated and restored when the original trace is
reactivated.</P>
<H3>Mouse Wheel</H3>
<P>Scrolling the mouse wheel over the timeline panel activates the next (wheel down) or
previous (wheel up) snapshot.</P>
<H2>Toolbar Actions</H2>
<H3><A name="toggle_grid_column"></A> <IMG alt="" src="icon.debugger.breakpoint.timeline.grid">
<IMG alt="" src="icon.debugger.breakpoint.timeline.single_column">Toggle between Grid and
Single Column</H3>
<DIV class="image">
<IMG alt="" src="images/BreakpointTimelinePluginSingleColumn.png">
</DIV>
<P>Switches the layout between a 2D grid and a single vertical column. In column mode, each
cell occupies the full width of the panel and cells are stacked vertically from top (earliest
snapshot) to bottom (latest snapshot).</P>
<H3><A name="toggle_grid_outline"></A> <IMG alt="" src=
"icon.debugger.breakpoint.timeline.outline"> <IMG alt="" src=
"icon.debugger.breakpoint.timeline.no_outline">Toggle Grid Outline</H3>
<DIV class="image">
<IMG alt="" src="images/BreakpointTimelinePluginNoGrid.png">
</DIV>
<P>Shows or hides the border drawn around each cell. When the grid outline is enabled, cell
boundaries are drawn in the disabled foreground color.</P>
<H3><A name="zoom_in"></A> <IMG alt="" src="icon.debugger.breakpoint.timeline.zoom_in">Zoom In
(Increase Cell Size)</H3>
<P>Increases the default cell size by one pixel in each dimension. The grid is recalculated
after each step.</P>
<H3><A name="zoom_out"></A> <IMG alt="" src="icon.debugger.breakpoint.timeline.zoom_out">Zoom
Out (Decrease Cell Size)</H3>
<P>Decreases the default cell size by one pixel in each dimension, down to a minimum of one
pixel.</P>
<H3><A name="smallest_cell"></A> <IMG alt="" src=
"icon.debugger.breakpoint.timeline.zoom_out_max">Set Default Cell Size to the Smallest</H3>
<P>Sets the default cell size to the minimum (one pixel).</P>
<H3><A name="close_zoom_windows"></A> <IMG alt="" src=
"icon.debugger.breakpoint.timeline.close_all_zoom_windows">Close All Zoom Windows</H3>
<P>Closes all zoom sub-windows that were created for the current trace. This action has no
effect on the main Breakpoint Timeline window itself.</P>
<H2>Context Menu Actions</H2>
<P>The Breakpoint Timeline plugin adds context menu actions to the following views: the <A
href="help/topics/CodeBrowserPlugin/CodeBrowser.htm">Static Listing</A>, the <A href=
"help/topics/DebuggerListingPlugin/DebuggerListingPlugin.html">Dynamic Listing</A>, the <A
href="help/topics/DebuggerRegistersPlugin/DebuggerRegistersPlugin.html">Registers</A> window,
and the <A href="help/topics/DebuggerMemoryBytesPlugin/DebuggerMemoryBytesPlugin.html">Byte
Viewer</A>. These actions search the current trace for breakpoint hits at the address or
address range selected in the active view and navigate to or list the matching snapshots.</P>
<P>The address range used for the search is determined from the current selection in the active
view. If there is no selection, the single address at the cursor is used.</P>
<P>Actions are organized into two top-level menu groups:</P>
<H3><A name="goto_actions"></A>Go to...</H3>
<P>These actions navigate to a single snapshot. They appear as submenus under <B>Go to...</B>,
grouped by search direction:</P>
<UL>
<LI><B>First...</B> - Navigate to the earliest snapshot in the entire trace where the
selected address was hit.</LI>
<LI><B>Previous...</B> - Navigate to the latest snapshot <EM>before</EM> the current snapshot
where the selected address was hit.</LI>
<LI><B>Next...</B> - Navigate to the earliest snapshot <EM>after</EM> the current snapshot
where the selected address was hit.</LI>
<LI><B>Final...</B> - Navigate to the latest snapshot in the entire trace where the selected
address was hit.</LI>
</UL>
<P>Each submenu contains one entry per breakpoint type:</P>
<UL>
<LI><B>Execution</B> - Search for execution breakpoint hits at the selected address
range.</LI>
<LI><B>Memory Read</B> - Search for memory read breakpoint hits at the selected address
range.</LI>
<LI><B>Memory Write</B> - Search for memory write breakpoint hits at the selected address
range.</LI>
<LI><B>Memory Access</B> - Search for any memory access (read or write) breakpoint hits at
the selected address range.</LI>
</UL>
<H3><A name="show_all_actions"></A>Show All...</H3>
<P>These actions open a table window listing every snapshot in the trace where the selected
address was hit, for the chosen breakpoint type. The table has two columns: <B>Snap</B> (the
snapshot number) and <B>Address</B> (the PC when the hit occurred). Double-clicking a row in
the table navigates to that snapshot. The same four breakpoint type entries (Execution, Memory
Read, Memory Write, Memory Access) are used for this action.</P>
</BODY>
</HTML>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -191,7 +191,7 @@
debugger. This failure can be overcome by enabling <A href=
"help/topics/DebuggerRegionsPlugin/DebuggerRegionsPlugin.html">Force Full View</A>.</P>
<P>Some examples (in <b>Hex</b> mode):</P>
<P>Some examples (in <B>Hex</B> mode):</P>
<UL>
<LI><CODE>00401234</CODE> &mdash; A constant address</LI>

View File

@@ -31,14 +31,14 @@ import ghidra.app.plugin.core.debug.gui.listing.DebuggerListingActionContext;
import ghidra.app.plugin.core.debug.gui.register.DebuggerRegisterActionContext;
import ghidra.app.services.DebuggerStaticMappingService;
import ghidra.app.services.DebuggerTraceManagerService;
import ghidra.debug.api.modules.MappedAddressRange;
import ghidra.framework.plugintool.PluginTool;
import ghidra.program.model.address.*;
import ghidra.program.util.ProgramLocation;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.Trace;
import ghidra.trace.model.*;
import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.symbol.TraceReference;
import ghidra.trace.model.target.TraceObjectValue;
import ghidra.util.HelpLocation;
import ghidra.util.Msg;
public class BreakpointTimelineActions {
@@ -145,11 +145,13 @@ public class BreakpointTimelineActions {
new String[] { "Go to...", searchType.menuGroup, breakType.menuName },
null,
BreakpointTimelineActions.this.name));
setHelpLocation(new HelpLocation("BreakpointTimelinePlugin", "goto_actions"));
}
else {
setPopupMenuData(
new MenuData(new String[] { searchType.menuGroup, breakType.menuName },
null, BreakpointTimelineActions.this.name));
setHelpLocation(new HelpLocation("BreakpointTimelinePlugin", "show_all_actions"));
}
}
@@ -282,52 +284,39 @@ public class BreakpointTimelineActions {
}
private AddressSet getAddressRangeFromCodeViewerActionContext(CodeViewerActionContext c) {
final Trace trace =
tool.getService(DebuggerTraceManagerService.class).getCurrentTrace();
final DebuggerStaticMappingService staticMappingService =
tool.getService(DebuggerStaticMappingService.class);
final AddressSet dynamicSet = new AddressSet();
String noMapping = "No mapping";
String noMappingFormat = "No mapping for %s @ 0x%x";
List<AddressRange> addressRanges = new ArrayList<>();
if (c.getSelection().isEmpty()) {
final ProgramLocation dynamicLocation =
staticMappingService.getDynamicLocationFromStatic(trace.getProgramView(),
new ProgramLocation(c.getProgram(), c.getAddress()));
if (dynamicLocation == null) {
Msg.showInfo(null, null, noMapping,
noMappingFormat.formatted(c.getProgram(), c.getAddress().getOffset()));
}
else {
dynamicSet.add(dynamicLocation.getAddress());
}
addressRanges.add(new AddressRangeImpl(c.getAddress(), c.getAddress()));
}
else {
for (final AddressRange range : c.getSelection().getAddressRanges()) {
final ProgramLocation startDynamicLocation =
staticMappingService.getDynamicLocationFromStatic(
trace.getProgramView(),
new ProgramLocation(c.getProgram(), range.getMinAddress()));
if (startDynamicLocation == null) {
Msg.showInfo(null, null, noMapping,
noMappingFormat.formatted(c.getProgram(),
range.getMinAddress().getOffset()));
c.getSelection().getAddressRanges().forEachRemaining(addressRanges::add);
}
for (final AddressRange range : addressRanges) {
Map<TraceSpan, Collection<MappedAddressRange>> openMappedViews =
staticMappingService.getOpenMappedViews(c.getProgram(),
new AddressSet(range));
for (Map.Entry<TraceSpan, Collection<MappedAddressRange>> entry :
openMappedViews.entrySet()) {
if (entry.getKey().getTrace() != currentTrace) {
continue;
}
final ProgramLocation endDynamicLocation =
staticMappingService.getDynamicLocationFromStatic(
trace.getProgramView(),
new ProgramLocation(c.getProgram(), range.getMaxAddress()));
if (endDynamicLocation == null) {
Msg.showInfo(null, null, noMapping,
noMappingFormat.formatted(c.getProgram(),
range.getMaxAddress().getOffset()));
continue;
for (MappedAddressRange addressRange : entry.getValue()) {
dynamicSet.add(addressRange.getDestinationAddressRange().getMinAddress(),
addressRange.getDestinationAddressRange().getMaxAddress());
}
dynamicSet.add(startDynamicLocation.getAddress(),
endDynamicLocation.getAddress());
}
}
if (dynamicSet.isEmpty()) {
Msg.showInfo(null, null, "No mapping",
"No mapping for %s @ 0x%x".formatted(c.getProgram(),
c.getAddress().getOffset()));
}
return dynamicSet;
}
@@ -380,6 +369,7 @@ public class BreakpointTimelineActions {
private final PluginTool tool;
String name = this.getClass().getSimpleName();
List<TimelineAction> actions;
private Trace currentTrace;
BreakpointTimelineActions(PluginTool tool) {
this.tool = tool;
@@ -397,4 +387,8 @@ public class BreakpointTimelineActions {
tool.removeAction(action);
}
}
public void setTrace(Trace t) {
currentTrace = t;
}
}

View File

@@ -93,6 +93,7 @@ public class BreakpointTimelinePlugin extends AbstractDebuggerPlugin {
if (currentTrace == t) {
currentTrace = null;
provider.setTrace(null);
actions.setTrace(null);
}
removeZoomProviders(t);
}
@@ -110,6 +111,7 @@ public class BreakpointTimelinePlugin extends AbstractDebuggerPlugin {
else {
refreshAllProviders(null);
}
actions.setTrace(t);
}
default -> {
}

View File

@@ -15,11 +15,10 @@
*/
package ghidra.app.plugin.core.debug.gui.breakpoint.timeline;
import java.awt.BorderLayout;
import javax.swing.*;
import java.awt.*;
import java.util.*;
import javax.swing.JComponent;
import javax.swing.JPanel;
import java.util.List;
import docking.ActionContext;
import docking.ComponentProvider;
@@ -37,6 +36,7 @@ import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.symbol.TraceReference;
import ghidra.trace.model.target.TraceObjectValue;
import ghidra.trace.util.TraceEvents;
import ghidra.util.HelpLocation;
public class BreakpointTimelineProvider extends ComponentProvider {
record BreakpointHitEvent(long snap, TraceBreakpointKind breakType, String breakpointName) {}
@@ -78,6 +78,7 @@ public class BreakpointTimelineProvider extends ComponentProvider {
super("Close all zoom windows", provider.getOwner());
setEnabled(true);
setToolBarData(new ToolBarData(ICON, "1"));
setHelpLocation(new HelpLocation("BreakpointTimelinePlugin", "close_zoom_windows"));
}
@Override
@@ -93,6 +94,7 @@ public class BreakpointTimelineProvider extends ComponentProvider {
super("Set default cell size to the smallest", provider.getOwner());
setEnabled(true);
setToolBarData(new ToolBarData(ICON, "zoom"));
setHelpLocation(new HelpLocation("BreakpointTimelinePlugin", "smallest_cell"));
}
@Override
@@ -111,6 +113,7 @@ public class BreakpointTimelineProvider extends ComponentProvider {
super("Toggle Grid Outline", provider.getOwner());
setEnabled(true);
setToolBarData(new ToolBarData(OUTLINE_ICON, "2"));
setHelpLocation(new HelpLocation("BreakpointTimelinePlugin", "toggle_grid_outline"));
}
@Override
@@ -119,7 +122,6 @@ public class BreakpointTimelineProvider extends ComponentProvider {
getToolBarData().setIcon(grid ? OUTLINE_ICON : NO_OUTLINE_ICON);
breakpointTimelinePanel.toggleGridOutline();
breakpointTimelinePlugin.refreshAllProviders(BreakpointTimelineProvider.this);
}
}
@@ -133,6 +135,7 @@ public class BreakpointTimelineProvider extends ComponentProvider {
super("Toggle between grid and single column", provider.getOwner());
setEnabled(true);
setToolBarData(new ToolBarData(GRID_ICON, "2"));
setHelpLocation(new HelpLocation("BreakpointTimelinePlugin", "toggle_grid_column"));
}
@Override
@@ -151,6 +154,7 @@ public class BreakpointTimelineProvider extends ComponentProvider {
super("Increase cell size", provider.getOwner());
setEnabled(true);
setToolBarData(new ToolBarData(ICON, "zoom"));
setHelpLocation(new HelpLocation("BreakpointTimelinePlugin", "zoom_in"));
}
@Override
@@ -166,6 +170,7 @@ public class BreakpointTimelineProvider extends ComponentProvider {
super("Decrease cell size", provider.getOwner());
setEnabled(true);
setToolBarData(new ToolBarData(ICON, "zoom"));
setHelpLocation(new HelpLocation("BreakpointTimelinePlugin", "zoom_out"));
}
@Override
@@ -174,6 +179,9 @@ public class BreakpointTimelineProvider extends ComponentProvider {
}
}
ToggleGridOrColumnAction toggleGridOrColumnAction;
ToggleGridAction toggleGridAction;
@Internal
public static Iterator<? extends TraceObjectValue> getTraceObjectValuesWithPCsIntersectingRange(
Trace trace, AddressRange range) {
@@ -208,6 +216,7 @@ public class BreakpointTimelineProvider extends ComponentProvider {
boolean makeTransient) {
super(breakpointTimelinePlugin.getTool(), "Breakpoint Timeline",
breakpointTimelinePlugin.getName());
setHelpLocation(new HelpLocation("BreakpointTimelinePlugin", "plugin"));
this.breakpointTimelinePlugin = breakpointTimelinePlugin;
wrapperPanel = new JPanel(new BorderLayout());
breakpointTimelinePanel = new BreakpointTimelinePanel(this);
@@ -234,8 +243,10 @@ public class BreakpointTimelineProvider extends ComponentProvider {
}
private void createActions() {
dockingTool.addLocalAction(this, new ToggleGridOrColumnAction(this));
dockingTool.addLocalAction(this, new ToggleGridAction(this));
toggleGridOrColumnAction = new ToggleGridOrColumnAction(this);
dockingTool.addLocalAction(this, toggleGridOrColumnAction);
toggleGridAction = new ToggleGridAction(this);
dockingTool.addLocalAction(this, toggleGridAction);
dockingTool.addLocalAction(this, new ZoomInAction(this));
dockingTool.addLocalAction(this, new ZoomOutAction(this));
dockingTool.addLocalAction(this, new CloseAllZoomWindowsAction(this));
@@ -301,7 +312,7 @@ public class BreakpointTimelineProvider extends ComponentProvider {
breakpointTimelinePanel.refresh();
}
private void refreshBreakpointHits() {
/*testing*/ void refreshBreakpointHits() {
breakpointHits.clear();
if (currentTrace == null) {

View File

@@ -0,0 +1,219 @@
/* ###
* 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.breakpoint.timeline;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;
import db.Transaction;
import generic.test.category.NightlyCategory;
import ghidra.app.plugin.core.debug.service.emulation.ProgramEmulationUtils;
import ghidra.app.plugin.core.debug.service.modules.DebuggerStaticMappingServicePlugin;
import ghidra.app.plugin.core.debug.service.tracemgr.DebuggerTraceManagerServicePlugin;
import ghidra.app.plugin.core.progmgr.ProgramManagerPlugin;
import ghidra.async.AsyncTestUtils;
import ghidra.framework.model.DomainFolder;
import ghidra.framework.model.DomainObject;
import ghidra.program.model.address.Address;
import ghidra.program.model.listing.Program;
import ghidra.program.model.symbol.RefType;
import ghidra.program.model.symbol.SourceType;
import ghidra.test.ToyProgramBuilder;
import ghidra.trace.database.ToyDBTraceBuilder;
import ghidra.trace.database.breakpoint.DBTraceBreakpointManager;
import ghidra.trace.model.Lifespan;
import ghidra.trace.model.breakpoint.TraceBreakpointKind;
import ghidra.trace.model.stack.TraceStack;
import ghidra.trace.model.stack.TraceStackFrame;
import ghidra.trace.model.target.TraceObject;
import ghidra.trace.model.target.path.KeyPath;
import ghidra.trace.model.thread.TraceThread;
import ghidra.trace.model.time.TraceSnapshot;
import ghidra.util.InvalidNameException;
import ghidra.util.exception.CancelledException;
import ghidra.util.task.ConsoleTaskMonitor;
import ghidra.util.task.TaskMonitor;
import help.screenshot.GhidraScreenShotGenerator;
import org.junit.*;
import org.junit.experimental.categories.Category;
@Category(NightlyCategory.class) // this may actually be an @PortSensitive test
public class BreakpointTimelinePluginScreenShots extends GhidraScreenShotGenerator
implements AsyncTestUtils {
private static final TaskMonitor MONITOR = new ConsoleTaskMonitor();
ToyDBTraceBuilder tb;
Program progHw;
Program progLibc;
private ProgramManagerPlugin programManager;
private DebuggerTraceManagerServicePlugin traceManager;
private BreakpointTimelineProvider provider;
private DebuggerStaticMappingServicePlugin mappingService;
@Before
public void setUpMine() throws Exception {
programManager = addPlugin(tool, ProgramManagerPlugin.class);
traceManager = addPlugin(tool, DebuggerTraceManagerServicePlugin.class);
mappingService = addPlugin(tool, DebuggerStaticMappingServicePlugin.class);
addPlugin(tool, BreakpointTimelinePlugin.class);
tb = new ToyDBTraceBuilder("toy", ToyProgramBuilder._X64);
provider = waitForComponentProvider(BreakpointTimelineProvider.class);
populateTraceAndPrograms();
}
private void populateTraceAndPrograms() throws Exception {
ToyProgramBuilder helloworld_builder = new ToyProgramBuilder("helloworld", false, this);
helloworld_builder.createMemory(".text", "0x00400000", 0x2000);
helloworld_builder.createLabel("0x00401234", "main");
helloworld_builder.createFunction("0x00401234");
progHw = helloworld_builder.getProgram();
ToyProgramBuilder libc_builder = new ToyProgramBuilder("libc", false, this);
libc_builder.createMemory(".text", "0x7fff0000", 0x2000);
libc_builder.createLabel("0x7fff0110", "puts");
libc_builder.createFunction("0x7fff0110");
progLibc = libc_builder.getProgram();
intoProject(progHw);
intoProject(progLibc);
intoProject(tb.trace);
programManager.openProgram(progLibc);
programManager.openProgram(progHw);
traceManager.openTrace(tb.trace);
mappingService.changesSettled().get(1, TimeUnit.SECONDS);
try (Transaction tx = tb.startTransaction()) {
tb.trace.getObjectManager().createRootObject(ProgramEmulationUtils.EMU_SESSION_SCHEMA);
tb.trace.getModuleManager()
.addLoadedModule("Modules[helloword]", "helloworld",
tb.range(0x00400000, 0x00402000), 0);
tb.trace.getModuleManager()
.addLoadedModule("Modules[libc]", "libc", tb.range(0x7fff0000, 0x7fff2000), 0);
mappingService.addIdentityMapping(tb.trace, progHw, Lifespan.nowOn(0), true);
mappingService.addIdentityMapping(tb.trace, progLibc, Lifespan.nowOn(0), true);
TraceThread thread = tb.getOrAddThread("Threads[1]", 0);
tb.trace.getObjectManager()
.createObject(KeyPath.parse("Threads[1].Registers"))
.insert(Lifespan.nowOn(0), TraceObject.ConflictResolution.DENY);
thread.setName(0, "1 main");
List<Address> pcs =
Arrays.asList(tb.addr(0x00401234), tb.addr(0x7fff0110), tb.addr(0x7fff0113),
tb.addr(0x7fff0115));
List<Address> memRefs =
Arrays.asList(tb.addr(0x11111), tb.addr(0x22222), tb.addr(0x33333), null, null,
null, null);
List<RefType> refTypes = List.of(RefType.READ, RefType.WRITE);
Random rand = new Random(1337);
for (int i = 0; i < 200; i++) {
addSnap(thread, pcs.get(rand.nextInt(pcs.size())), i,
memRefs.get(rand.nextInt(memRefs.size())),
refTypes.get(rand.nextInt(refTypes.size())));
}
DBTraceBreakpointManager bm = tb.trace.getBreakpointManager();
bm.placeBreakpoint("Breakpoints[1]", 0, tb.addr(0x00401234), List.of(),
TraceBreakpointKind.CommonSet.SWX.kinds(), true, "");
bm.placeBreakpoint("Breakpoints[2]", 0, tb.addr(0x11111), List.of(),
TraceBreakpointKind.CommonSet.READ.kinds(), true, "");
bm.placeBreakpoint("Breakpoints[3]", 0, tb.addr(0x22222), List.of(),
TraceBreakpointKind.CommonSet.WRITE.kinds(), true, "");
}
mappingService.changesSettled().get(1, TimeUnit.SECONDS);
traceManager.activateTrace(tb.trace);
traceManager.activateSnap(0);
}
protected void intoProject(DomainObject obj) {
waitForDomainObject(obj);
DomainFolder rootFolder = tool.getProject().getProjectData().getRootFolder();
waitForCondition(() -> {
try {
rootFolder.createFile(obj.getName(), obj, MONITOR);
return true;
}
catch (InvalidNameException | CancelledException e) {
throw new AssertionError(e);
}
catch (IOException e) {
// Usually "object is busy". Try again.
return false;
}
});
}
private void addSnap(TraceThread thread, Address address, int num, Address memRef,
RefType refType) {
TraceSnapshot snap = tb.trace.getTimeManager().createSnapshot(Integer.toString(num));
snap.setEventThread(thread);
TraceStack latestStack = tb.trace.getStackManager().getStack(thread, snap.getKey(), true);
TraceStackFrame frame = latestStack.getFrame(0, 0, true);
frame.setProgramCounter(Lifespan.nowOn(snap.getKey()), address);
if (memRef != null) {
tb.trace.getReferenceManager()
.addMemoryReference(Lifespan.at(snap.getKey()), address, memRef, refType,
SourceType.ANALYSIS, 0);
}
}
public static void waitForDomainObject(DomainObject object) {
object.flushEvents();
waitForSwing();
}
@After
public void tearDownMine() {
tb.close();
if (progHw != null) {
progHw.release(this);
progHw = null;
}
if (progLibc != null) {
progLibc.release(this);
progLibc = null;
}
}
@Test
public void testCaptureBreakpointTimelinePluginSingleColumn() {
performAction(provider.toggleGridOrColumnAction, true);
captureIsolatedProvider(provider, 300, 300);
}
@Test
public void testCaptureBreakpointTimelinePlugin() {
captureIsolatedProvider(provider, 300, 300);
}
@Test
public void testCaptureBreakpointTimelinePluginNoGrid() {
performAction(provider.toggleGridAction, true);
captureIsolatedProvider(provider, 300, 300);
}
}