mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-19 16:40:38 -09:00
GP-6891 Improved Debugger Trace import/export support and added packed DBTrace GZT file type.
This commit is contained in:
@@ -37,7 +37,6 @@ import ghidra.program.model.data.DataTypeManager;
|
||||
import ghidra.program.model.data.FileDataTypeManager;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class IsfServer extends Thread {
|
||||
@@ -102,13 +101,13 @@ public class IsfServer extends Thread {
|
||||
try {
|
||||
DataTypeManager dtm;
|
||||
if (ns.endsWith(".gdt")) {
|
||||
dtm = openAsArchive(ns);
|
||||
dtm = openAsDataTypeArchive(ns);
|
||||
}
|
||||
else if (ns.endsWith(".gzf")) {
|
||||
dtm = openAsDatabase(ns);
|
||||
dtm = openAsProgramDatabase(ns);
|
||||
}
|
||||
else {
|
||||
dtm = openAsDomainFile(ns);
|
||||
dtm = openAsProgramFile(ns);
|
||||
}
|
||||
managers.put(ns, dtm);
|
||||
return dtm;
|
||||
@@ -120,43 +119,43 @@ public class IsfServer extends Thread {
|
||||
}
|
||||
}
|
||||
|
||||
private DataTypeManager openAsDomainFile(String ns) throws Exception {
|
||||
private DataTypeManager openAsProgramFile(String ns) throws Exception {
|
||||
ProjectData projectData = project.getProjectData();
|
||||
DomainFile df = projectData.getFile(ns);
|
||||
if (!Program.class.isAssignableFrom(df.getDomainObjectClass())) {
|
||||
throw new IOException("File does not correspond to Program content: " + ns);
|
||||
}
|
||||
|
||||
// FIXME: Need to track and release Program instance after DTM use is complete (GP-6895)
|
||||
Program program = (Program) df.getDomainObject(this, false, false, TaskMonitor.DUMMY);
|
||||
return program.getDataTypeManager();
|
||||
}
|
||||
|
||||
private DataTypeManager openAsArchive(String ns) throws Exception {
|
||||
private DataTypeManager openAsDataTypeArchive(String ns) throws Exception {
|
||||
File gdt = new File(ns);
|
||||
return FileDataTypeManager.openFileArchive(gdt, false);
|
||||
}
|
||||
|
||||
private DataTypeManager openAsDatabase(String ns) throws Exception {
|
||||
private DataTypeManager openAsProgramDatabase(String ns) throws Exception {
|
||||
File gzf = new File(ns);
|
||||
TaskMonitor dummy = TaskMonitor.DUMMY;
|
||||
PackedDatabase db = PackedDatabase.getPackedDatabase(gzf, dummy);
|
||||
|
||||
DBHandle dbh = db.openForUpdate(dummy);
|
||||
ProgramDB p = null;
|
||||
|
||||
Program p;
|
||||
boolean success = false;
|
||||
try {
|
||||
p = new ProgramDB(dbh, OpenMode.UPDATE, dummy, this);
|
||||
}
|
||||
catch (VersionException e) {
|
||||
if (!e.isUpgradable()) {
|
||||
throw new RuntimeException(p + " uses an older version and is not upgradable.");
|
||||
}
|
||||
p = new ProgramDB(dbh, OpenMode.UPGRADE, dummy, this);
|
||||
success = true;
|
||||
}
|
||||
finally {
|
||||
dbh.close();
|
||||
}
|
||||
|
||||
dbh = db.openForUpdate(dummy);
|
||||
p = new ProgramDB(dbh, OpenMode.UPGRADE, dummy, this);
|
||||
|
||||
if (!p.isChanged()) {
|
||||
throw new RuntimeException(p + " uses an older version and was not upgraded.");
|
||||
if (!success) {
|
||||
dbh.close();
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Need to track and release Program instance after DTM use is complete (GP-6895)
|
||||
return p.getListing().getDataTypeManager();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,27 +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.export;
|
||||
|
||||
import ghidra.app.util.exporter.AsciiExporter;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.trace.model.Trace;
|
||||
|
||||
public class TraceViewAsciiExporter extends AsciiExporter {
|
||||
@Override
|
||||
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
|
||||
return Trace.class.isAssignableFrom(domainObjectClass);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +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.export;
|
||||
|
||||
import ghidra.app.util.exporter.BinaryExporter;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.trace.model.Trace;
|
||||
|
||||
public class TraceViewBinaryExporter extends BinaryExporter {
|
||||
@Override
|
||||
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
|
||||
return Trace.class.isAssignableFrom(domainObjectClass);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +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.export;
|
||||
|
||||
import ghidra.app.util.exporter.HtmlExporter;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.trace.model.Trace;
|
||||
|
||||
public class TraceViewHtmlExporter extends HtmlExporter {
|
||||
@Override
|
||||
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
|
||||
return Trace.class.isAssignableFrom(domainObjectClass);
|
||||
}
|
||||
}
|
||||
@@ -1,27 +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.export;
|
||||
|
||||
import ghidra.app.util.exporter.IntelHexExporter;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.trace.model.Trace;
|
||||
|
||||
public class TraceViewIntelHexExporter extends IntelHexExporter {
|
||||
@Override
|
||||
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
|
||||
return Trace.class.isAssignableFrom(domainObjectClass);
|
||||
}
|
||||
}
|
||||
@@ -1,56 +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.export;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import ghidra.app.util.*;
|
||||
import ghidra.app.util.exporter.XmlExporter;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.trace.model.Trace;
|
||||
|
||||
// TODO: perhaps getApplicableExporters should use domainObject's class, not file's object class.
|
||||
// TODO: Where un-supported, be less abrasive, e.g., present empty managers.
|
||||
public class TraceViewXmlExporter extends XmlExporter {
|
||||
private final Map<String, Object> hideOpts = Map.of(
|
||||
"Properties", false,
|
||||
"Relocation Table", false,
|
||||
"External Libraries", false);
|
||||
|
||||
@Override
|
||||
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
|
||||
return Trace.class.isAssignableFrom(domainObjectClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Option> getOptions(DomainObjectService domainObjectService) {
|
||||
List<Option> options = super.getOptions(domainObjectService);
|
||||
return options.stream()
|
||||
.filter(o -> !hideOpts.keySet().contains(o.getName()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOptions(List<Option> options) throws OptionException {
|
||||
List<Option> opts = new ArrayList<>(options);
|
||||
options.stream().filter(o -> !hideOpts.keySet().contains(o.getName())).forEach(opts::add);
|
||||
for (Map.Entry<String, Object> ent : hideOpts.entrySet()) {
|
||||
opts.add(new Option(ent.getKey(), ent.getValue()));
|
||||
}
|
||||
super.setOptions(opts);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
/* ###
|
||||
* 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.export;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.OptionDialog;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||
import ghidra.app.context.ProgramActionContext;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.plugin.core.debug.DebuggerPluginPackage;
|
||||
import ghidra.app.plugin.core.debug.utils.GztExporter;
|
||||
import ghidra.app.plugin.core.help.AboutDomainObjectUtils;
|
||||
import ghidra.app.services.CodeViewerService;
|
||||
import ghidra.app.services.DebuggerTraceManagerService;
|
||||
import ghidra.app.util.importer.MessageLog;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.framework.preferences.Preferences;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.program.util.ProgramSelection;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.program.TraceProgramView;
|
||||
import ghidra.util.Swing;
|
||||
import ghidra.util.filechooser.ExtensionFileFilter;
|
||||
import ghidra.util.filechooser.GhidraFileFilter;
|
||||
import ghidra.util.task.TaskLauncher;
|
||||
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
category = PluginCategoryNames.DEBUGGER,
|
||||
packageName = DebuggerPluginPackage.NAME,
|
||||
shortDescription = "Export Debugger Trace",
|
||||
description = "This plugin exports a Debugger Trace to an external file.",
|
||||
servicesRequired = {
|
||||
DebuggerTraceManagerService.class
|
||||
}
|
||||
)
|
||||
//@formatter:on
|
||||
public class TraceExportPlugin extends Plugin {
|
||||
|
||||
private DebuggerTraceManagerService traceMgrSvc;
|
||||
|
||||
public TraceExportPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
createToolAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
traceMgrSvc = tool.getService(DebuggerTraceManagerService.class);
|
||||
}
|
||||
|
||||
private void createToolAction() {
|
||||
|
||||
new ActionBuilder("Export Trace", getName())
|
||||
.description("Export Debbuger Trace as compressed GZT file.")
|
||||
// .helpLocation(new HelpLocation("ExporterPlugin", "Export"))
|
||||
.menuPath(ToolConstants.MENU_FILE, "Export Trace...")
|
||||
.menuGroup("DomainObjectSaveExport")
|
||||
.enabledWhen(c -> getTrace(c) != null)
|
||||
.onAction(c -> exportTrace(c))
|
||||
.buildAndInstall(tool);
|
||||
}
|
||||
|
||||
private Trace getTrace(ActionContext ctx) {
|
||||
if (ctx instanceof ProgramActionContext programCtx) {
|
||||
Program p = programCtx.getProgram();
|
||||
if (p instanceof TraceProgramView traceProgrmView) {
|
||||
return traceProgrmView.getTrace();
|
||||
}
|
||||
}
|
||||
return traceMgrSvc.getCurrentTrace();
|
||||
}
|
||||
|
||||
private void exportTrace(ActionContext ctx) {
|
||||
|
||||
Trace trace = getTrace(ctx);
|
||||
if (trace == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
File file = chooseDestinationFile(ctx);
|
||||
if (file == null) {
|
||||
return; // file chooser cancelled
|
||||
}
|
||||
|
||||
File gztFile = file;
|
||||
GztExporter exporter = new GztExporter();
|
||||
|
||||
TaskLauncher.launchModal("Export Trace", m -> {
|
||||
exporter.export(gztFile, trace, null, m);
|
||||
});
|
||||
|
||||
displaySummaryResults(trace, gztFile, exporter.getMessageLog());
|
||||
}
|
||||
|
||||
private File chooseDestinationFile(ActionContext ctx) {
|
||||
GhidraFileChooser chooser = new GhidraFileChooser(ctx.getSourceComponent());
|
||||
chooser.setCurrentDirectory(getLastExportDirectory());
|
||||
chooser.setTitle("Select Trace Output File");
|
||||
chooser.setApproveButtonText("Export Trace");
|
||||
chooser.setApproveButtonToolTipText("Export Debugger Trace");
|
||||
chooser.setFileSelectionMode(GhidraFileChooserMode.FILES_ONLY);
|
||||
chooser.setSelectedFileFilter(GhidraFileFilter.ALL);
|
||||
chooser.setFileFilter(
|
||||
new ExtensionFileFilter(GztExporter.EXTENSION, GztExporter.NAME));
|
||||
|
||||
File file;
|
||||
while (true) {
|
||||
file = chooser.getSelectedFile();
|
||||
if (file == null) {
|
||||
break;
|
||||
}
|
||||
setLastExportDirectory(file);
|
||||
if (!file.getName().endsWith(GztExporter.SUFFIX)) {
|
||||
file = new File(file.getParent(), file.getName() + GztExporter.SUFFIX);
|
||||
}
|
||||
if (!file.exists()) {
|
||||
break; // continue with file return
|
||||
}
|
||||
if (!file.isFile()) {
|
||||
chooser.setStatusText("Invalid File Selection");
|
||||
continue;
|
||||
}
|
||||
int rc = OptionDialog.showYesNoCancelDialog(chooser.getComponent(),
|
||||
"Overwrite Confirmation",
|
||||
"Overwrite Trace export file?\n" + file);
|
||||
if (rc == OptionDialog.YES_OPTION) {
|
||||
break; // continue with file return
|
||||
}
|
||||
file = null; // don't overwrite
|
||||
if (rc != OptionDialog.NO_OPTION) {
|
||||
break; // continue with null return / export cancelled
|
||||
}
|
||||
}
|
||||
chooser.dispose();
|
||||
return file;
|
||||
}
|
||||
|
||||
private File getLastExportDirectory() {
|
||||
String lastDirStr = Preferences.getProperty(Preferences.LAST_EXPORT_DIRECTORY,
|
||||
System.getProperty("user.home"), true);
|
||||
return new File(lastDirStr);
|
||||
}
|
||||
|
||||
private void setLastExportDirectory(File file) {
|
||||
Preferences.setProperty(Preferences.LAST_EXPORT_DIRECTORY, file.getParent());
|
||||
Preferences.store();
|
||||
}
|
||||
|
||||
protected ProgramSelection getSelection() {
|
||||
CodeViewerService service = tool.getService(CodeViewerService.class);
|
||||
if (service != null) {
|
||||
return service.getCurrentSelection();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void displaySummaryResults(Trace trace, File outputFile, MessageLog log) {
|
||||
|
||||
StringBuffer resultsBuffer = new StringBuffer();
|
||||
|
||||
resultsBuffer.append("Destination file: " + outputFile.getAbsolutePath() + "\n\n");
|
||||
resultsBuffer.append("Destination file Size: " + outputFile.length() + "\n");
|
||||
resultsBuffer.append("Format: " + GztExporter.NAME + "\n\n");
|
||||
resultsBuffer.append(log.toString());
|
||||
|
||||
Map<String, String> metadata = trace.getMetadata();
|
||||
|
||||
Swing.runLater(() -> {
|
||||
AboutDomainObjectUtils.displayInformation(tool, trace.getDomainFile(), metadata,
|
||||
"Trace Export Results Summary", resultsBuffer.toString(), null);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -555,7 +555,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
|
||||
protected void contextChanged() {
|
||||
Trace trace = current.getTrace();
|
||||
String itemName = trace == null ? "..." : trace.getName();
|
||||
String itemName = trace == null ? "..." : trace.getDomainFile().getName();
|
||||
actionCloseTrace.getMenuBarData().setMenuItemName(CloseTraceAction.NAME_PREFIX + itemName);
|
||||
actionSaveTrace.getMenuBarData().setMenuItemName(SaveTraceAction.NAME_PREFIX + itemName);
|
||||
tool.contextChanged(null);
|
||||
@@ -939,7 +939,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
|
||||
@Override
|
||||
public void run(TaskMonitor monitor) throws CancelledException {
|
||||
String filename = trace.getName();
|
||||
String filename = trace.getDomainFile().getName();
|
||||
try (DomainObjectLockHold hold = maybeLock(trace, force)) {
|
||||
for (int i = 1;; i++) {
|
||||
try {
|
||||
@@ -947,7 +947,7 @@ public class DebuggerTraceManagerServicePlugin extends Plugin
|
||||
break;
|
||||
}
|
||||
catch (DuplicateFileException e) {
|
||||
filename = trace.getName() + "." + i;
|
||||
filename = trace.getDomainFile().getName() + "." + i;
|
||||
}
|
||||
}
|
||||
trace.save("Initial save", monitor);
|
||||
|
||||
@@ -0,0 +1,122 @@
|
||||
/* ###
|
||||
* 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.utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import ghidra.app.util.DomainObjectService;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.exporter.Exporter;
|
||||
import ghidra.app.util.exporter.ExporterException;
|
||||
import ghidra.framework.model.DomainFile;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.program.model.address.AddressSetView;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
|
||||
public class GztExporter extends Exporter {
|
||||
|
||||
public static final String EXTENSION = "gzt";
|
||||
public static final String SUFFIX = "." + EXTENSION;
|
||||
|
||||
public static final String NAME = "Ghidra Trace Zip File";
|
||||
|
||||
public GztExporter() {
|
||||
super(NAME, EXTENSION, new HelpLocation("ExporterPlugin", "gzt"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canExportDomainFile(DomainFile domainFile) {
|
||||
// Avoid exporting link-files or non-Trace files
|
||||
return !domainFile.isLink() && canExportDomainObject(domainFile.getDomainObjectClass());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canExportDomainObject(Class<? extends DomainObject> domainObjectClass) {
|
||||
return DBTrace.class.isAssignableFrom(domainObjectClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return (obj instanceof GztExporter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean export(File file, DomainObject domainObj, AddressSetView addrSet,
|
||||
TaskMonitor monitor) {
|
||||
if (!canExportDomainObject(domainObj.getClass())) {
|
||||
throw new UnsupportedOperationException("only DBTrace objects are supported");
|
||||
}
|
||||
try {
|
||||
file.delete();
|
||||
domainObj.saveToPackedFile(file, monitor);
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
log.appendMsg("Content does not support packed file export!");
|
||||
log.appendException(e);
|
||||
return false;
|
||||
}
|
||||
catch (CancelledException ce) {
|
||||
return false;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.appendMsg("Unexpected exception exporting file: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean export(File file, DomainFile domainFile, TaskMonitor monitor)
|
||||
throws ExporterException, IOException {
|
||||
if (!canExportDomainFile(domainFile)) {
|
||||
throw new UnsupportedOperationException("only DBTrace files are supported");
|
||||
}
|
||||
try {
|
||||
domainFile.packFile(file, monitor);
|
||||
}
|
||||
catch (CancelledException e) {
|
||||
return false;
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.appendMsg("Unexpected exception exporting file: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Option> getOptions(DomainObjectService domainObjectService) {
|
||||
return EMPTY_OPTIONS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOptions(List<Option> options) {
|
||||
// no options for this exporter
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns false. GZT export only supports entire database.
|
||||
*/
|
||||
@Override
|
||||
public boolean supportsAddressRestrictedExport() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/* ###
|
||||
* 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.utils;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import db.DBHandle;
|
||||
import ghidra.app.util.Option;
|
||||
import ghidra.app.util.bin.ByteProvider;
|
||||
import ghidra.app.util.opinion.*;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.framework.data.OpenMode;
|
||||
import ghidra.framework.model.DomainObject;
|
||||
import ghidra.framework.store.db.PackedDatabase;
|
||||
import ghidra.framework.store.local.ItemSerializer;
|
||||
import ghidra.program.model.lang.LanguageNotFoundException;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import ghidra.trace.database.DBTrace;
|
||||
import ghidra.trace.database.DBTraceContentHandler;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.exception.VersionException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import utilities.util.FileUtilities;
|
||||
|
||||
/**
|
||||
* Loads a packed Ghidra Trace file.
|
||||
*/
|
||||
public class GztLoader implements Loader {
|
||||
|
||||
public final static String GZT_NAME = "GZT Input Format";
|
||||
|
||||
@Override
|
||||
public LoaderTier getTier() {
|
||||
return LoaderTier.SPECIALIZED_TARGET_LOADER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTierPriority() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String validateOptions(ByteProvider provider, LoadSpec loadSpec, List<Option> options,
|
||||
Program program) {
|
||||
if (options != null && options.size() > 0) {
|
||||
return "GztLoader takes no options";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Option> getDefaultOptions(ByteProvider provider, LoadSpec loadSpec,
|
||||
DomainObject domainObject, boolean loadIntoProgram, boolean mirrorFsLayout) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LoadResults<? extends DomainObject> load(ImporterSettings settings)
|
||||
throws IOException, CancelledException, VersionException {
|
||||
|
||||
Trace trace = loadPackedTraceDatabase(settings.provider(), settings.importName(),
|
||||
settings.consumer(), settings.monitor());
|
||||
return new LoadResults<>(new Loaded<>(trace, settings));
|
||||
}
|
||||
|
||||
private Trace loadPackedTraceDatabase(ByteProvider provider, String traceName,
|
||||
Object consumer, TaskMonitor monitor)
|
||||
throws IOException, CancelledException, VersionException, LanguageNotFoundException {
|
||||
Trace trace;
|
||||
File file = provider.getFile();
|
||||
File tmpFile = null;
|
||||
if (file == null) {
|
||||
file = tmpFile = createTmpFile(provider, monitor);
|
||||
}
|
||||
|
||||
try {
|
||||
PackedDatabase packedDatabase = PackedDatabase.getPackedDatabase(file, true, monitor);
|
||||
boolean success = false;
|
||||
DBHandle dbh = null;
|
||||
try {
|
||||
if (!DBTraceContentHandler.TRACE_CONTENT_TYPE
|
||||
.equals(packedDatabase.getContentType())) {
|
||||
throw new IOException("File imported is not a Trace: " + traceName);
|
||||
}
|
||||
|
||||
monitor.setMessage("Restoring " + provider.getName());
|
||||
|
||||
dbh = packedDatabase.open(monitor);
|
||||
|
||||
trace = new DBTrace(dbh, OpenMode.UPGRADE, monitor, consumer);
|
||||
success = true;
|
||||
}
|
||||
finally {
|
||||
if (!success) {
|
||||
if (dbh != null) {
|
||||
dbh.close(); // also disposes packed database object
|
||||
}
|
||||
else {
|
||||
packedDatabase.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
return trace;
|
||||
}
|
||||
finally {
|
||||
if (tmpFile != null) {
|
||||
tmpFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadInto(Program program, ImporterSettings settings)
|
||||
throws IOException, LoadException, CancelledException {
|
||||
throw new LoadException("Cannot add GZT to program");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<LoadSpec> findSupportedLoadSpecs(ByteProvider provider) throws IOException {
|
||||
List<LoadSpec> loadSpecs = new ArrayList<>();
|
||||
if (isGztFile(provider)) {
|
||||
loadSpecs.add(new LoadSpec(this, 0, false));
|
||||
}
|
||||
return loadSpecs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPreferredFileName(ByteProvider provider) {
|
||||
return FilenameUtils.removeExtension(provider.getName());
|
||||
}
|
||||
|
||||
private static File createTmpFile(ByteProvider provider, TaskMonitor monitor)
|
||||
throws IOException {
|
||||
File tmpFile = Application.createTempFile("ghidra_gzt_loader", null);
|
||||
try (InputStream is = provider.getInputStream(0);
|
||||
FileOutputStream fos = new FileOutputStream(tmpFile)) {
|
||||
FileUtilities.copyStreamToStream(is, fos, monitor);
|
||||
}
|
||||
return tmpFile;
|
||||
}
|
||||
|
||||
private static boolean isGztFile(ByteProvider provider) {
|
||||
if (!provider.getName().toLowerCase().endsWith(".gzt")) {
|
||||
return false;
|
||||
}
|
||||
boolean isGZT = false;
|
||||
try (InputStream inputStream = provider.getInputStream(0)) {
|
||||
isGZT = ItemSerializer.isPackedFile(inputStream);
|
||||
}
|
||||
catch (IOException e) {
|
||||
// ignore
|
||||
}
|
||||
return isGZT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return GZT_NAME;
|
||||
}
|
||||
}
|
||||
@@ -814,7 +814,7 @@ public abstract class AbstractGhidraHeadedDebuggerTest
|
||||
|
||||
protected File pack(DomainObject object) throws Exception {
|
||||
File tempDir = Files.createTempDirectory("ghidra-" + name.getMethodName()).toFile();
|
||||
File pack = new File(tempDir, "obj" + System.identityHashCode(object) + ".gzf");
|
||||
File pack = new File(tempDir, "obj" + System.identityHashCode(object) + ".gzt");
|
||||
object.saveToPackedFile(pack, monitor);
|
||||
return pack;
|
||||
}
|
||||
|
||||
@@ -236,9 +236,9 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a .gzf compressed trace
|
||||
* Open a .gzt packed trace
|
||||
*
|
||||
* @param file the .gzf file containing the trace
|
||||
* @param file the .gzt file containing the trace
|
||||
* @throws CancelledException never, since the monitor cannot be cancelled
|
||||
* @throws VersionException if the trace's version is not as expected
|
||||
* @throws LanguageNotFoundException if the trace's language cannot be found
|
||||
@@ -936,7 +936,7 @@ public class ToyDBTraceBuilder implements AutoCloseable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the trace to a temporary .gzf file
|
||||
* Save the trace to a temporary packed .gzt file
|
||||
*
|
||||
* @return the new file
|
||||
* @throws IOException if the trace could not be saved
|
||||
|
||||
Reference in New Issue
Block a user