mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 19:42:36 +02:00
GP-3844: Replacing the 'Show VM Memory' dialog with an upgraded 'Runtime Information' dialog
This commit is contained in:
parent
f739df7c21
commit
8c89a8bb3c
19 changed files with 949 additions and 594 deletions
|
@ -38,7 +38,7 @@ import ghidra.app.plugin.core.equate.EquatePlugin;
|
|||
import ghidra.app.plugin.core.function.FunctionPlugin;
|
||||
import ghidra.app.plugin.core.label.LabelMgrPlugin;
|
||||
import ghidra.app.plugin.core.symtable.SymbolTablePlugin;
|
||||
import ghidra.app.plugin.debug.MemoryUsagePlugin;
|
||||
import ghidra.app.plugin.runtimeinfo.RuntimeInfoPlugin;
|
||||
import ghidra.framework.plugintool.util.PluginException;
|
||||
import ghidra.program.model.address.AddressOverflowException;
|
||||
import ghidra.program.model.data.Undefined4DataType;
|
||||
|
@ -109,7 +109,7 @@ public class DebuggerManualTest extends AbstractGhidraHeadedDebuggerTest {
|
|||
addPlugin(tool, LabelMgrPlugin.class);
|
||||
//addPlugin(tool, LocationReferencesPlugin.class);
|
||||
//addPlugin(tool, MarkerManagerPlugin.class);
|
||||
addPlugin(tool, MemoryUsagePlugin.class);
|
||||
addPlugin(tool, RuntimeInfoPlugin.class);
|
||||
//addPlugin(tool, MemSearchPlugin.class);
|
||||
//addPlugin(tool, MnemonicSearchPlugin.class);
|
||||
//addPlugin(tool, NextPrevAddressPlugin.class);
|
||||
|
|
|
@ -268,7 +268,6 @@ src/main/help/help/topics/FrontEndPlugin/Project_Info.htm||GHIDRA||||END|
|
|||
src/main/help/help/topics/FrontEndPlugin/Re-opening_a_Project.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/Restore_Project.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/Saving_a_Ghidra_Project.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/ShowMemoryUsage.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/ArchiveFileExists.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/ArchiveProject.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/ChangeAccessList.png||GHIDRA||||END|
|
||||
|
@ -285,7 +284,6 @@ src/main/help/help/topics/FrontEndPlugin/images/DeleteProject.png||GHIDRA||||END
|
|||
src/main/help/help/topics/FrontEndPlugin/images/EditPluginPath.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/EditProjectAccessList.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/LinkOtherProject.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/MemoryUsage.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/NonSharedProjectInfo.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/OpenProject.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/FrontEndPlugin/images/PrivateFileIcon.png||GHIDRA||||END|
|
||||
|
@ -500,6 +498,8 @@ src/main/help/help/topics/Repository/images/SymbolRemoveVsChangeConflict.png||GH
|
|||
src/main/help/help/topics/Repository/images/SymbolRenameWithScopeConflict.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/topics/Repository/images/UserDefinedUseForAllConflict.png||GHIDRA||reviewed||END|
|
||||
src/main/help/help/topics/ResourceActionsPlugin/ResourceActions.html||GHIDRA||||END|
|
||||
src/main/help/help/topics/RuntimeInfoPlugin/InstalledProcessors.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/RuntimeInfoPlugin/RuntimeInfo.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/ScalarSearchPlugin/The_Scalar_Table.htm||GHIDRA||||END|
|
||||
src/main/help/help/topics/ScalarSearchPlugin/images/ScalarWindow.png||GHIDRA||||END|
|
||||
src/main/help/help/topics/ScalarSearchPlugin/images/SearchAllScalarsDialog.png||GHIDRA||||END|
|
||||
|
|
|
@ -62,25 +62,6 @@
|
|||
</LI>
|
||||
</UL>
|
||||
|
||||
<H2 align="center"><A name="ProcessorList"/>Installed Processors</H2>
|
||||
|
||||
<P>The <I>Installed Processors</I> dialog displays all processors supported by the various
|
||||
language modules currently installed. This list is quite brief and does not convey the
|
||||
assorted variants which may be implemented for each processor.</P>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P><IMG border="0" src="help/shared/tip.png"> This option is also available from the
|
||||
<I><A href="help/topics/FrontEndPlugin/Ghidra_Front_end.htm">Ghidra Project
|
||||
Window</A> </I></P>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H3><B>To view the Installed Processors list</B></H3>
|
||||
|
||||
<UL type="disc">
|
||||
<LI>From the menu-bar in tool, click <B>Help <IMG src="help/shared/arrow.gif" border="0"
|
||||
alt="">Installed Processors</B></LI>
|
||||
</UL>
|
||||
|
||||
<H2 align="center"><A name="User_Agreement"/>User Agreement</H2>
|
||||
|
||||
<P>The <I>User Agreement</I> dialog displays the conditions that you must agree to in
|
||||
|
|
|
@ -30,16 +30,6 @@
|
|||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2>Help Menu Actions</H2>
|
||||
<BLOCKQUOTE>
|
||||
<H3><A name="Display_Supported_File_Systems_and_Loaders"></A>Display Supported File Systems
|
||||
and Loaders</H3>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
<P>Lists the currently supported file systems and loaders.</P>
|
||||
</BLOCKQUOTE>
|
||||
</BLOCKQUOTE>
|
||||
|
||||
<H2>Right-click Context Menu Actions</H2>
|
||||
|
||||
<BLOCKQUOTE>
|
||||
|
|
|
@ -1,40 +0,0 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>Memory Usage</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>
|
||||
<H1 align="center"><A name="ShowMemoryUsage"></A>Memory Usage</H1>
|
||||
|
||||
<P>The Memory usage feature pops up a dialog showing continuous snapshots of the Ghidra memory
|
||||
usage within the the Java Virtual Machine. It is primarily a diagnostic tool for Ghidra
|
||||
developers.nbsp;</P>
|
||||
|
||||
<P>To display the memory usage dialog:</P>
|
||||
|
||||
<OL>
|
||||
<LI>From the Project Window, Select <B>Help<IMG border="0" src="help/shared/arrow.gif">Show VM Memory</B> from the
|
||||
menu.</LI>
|
||||
|
||||
</OL>
|
||||
|
||||
<TABLE border="0" width="100%">
|
||||
<TR>
|
||||
<TD width="100%" align="center"><IMG border="0" src="images/MemoryUsage.png"></TD>
|
||||
</TR>
|
||||
</TABLE>
|
||||
|
||||
<P>The four values displayed are as follows:</P>
|
||||
<OL>
|
||||
<LI> Max Memory - the maximum memory that the Java VM is allowed to use.</LI>
|
||||
<LI> Total Memory - the amount of memory that the Java VM is currently using.</LI>
|
||||
<LI> Free Memory - the amount of the "Total Memory" that is currently unused by the Ghidra application.</LI>
|
||||
<LI> Used Memory - the amount of the "Total Memory" that is currently used by the Ghidra application.</LI>
|
||||
</OL>
|
||||
<P> </P>
|
||||
</BODY>
|
||||
</HTML>
|
Binary file not shown.
Before Width: | Height: | Size: 7.4 KiB |
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>Runtime Information</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>
|
||||
|
||||
<A name="InstalledProcessors"></A>
|
||||
<H1 align="center">Installed Processors</H1>
|
||||
|
||||
<P>The Installed Processors feature pops up a dialog showing a list of processors that the
|
||||
active Ghidra application supports.</P>
|
||||
|
||||
<P>To display the Installed Processors dialog:</P>
|
||||
|
||||
<UL>
|
||||
<LI>From the Project Window, Select <B>Help<IMG border="0" src="help/shared/arrow.gif">
|
||||
Installed Processors</B> from the menu.</LI>
|
||||
</UL>
|
||||
|
||||
<P> </P>
|
||||
</BODY>
|
||||
</HTML>
|
|
@ -0,0 +1,64 @@
|
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<HTML>
|
||||
<HEAD>
|
||||
<TITLE>Runtime Information</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>
|
||||
|
||||
<A name="RuntimeInfo"></A>
|
||||
<H1 align="center">Runtime Information</H1>
|
||||
|
||||
<P>The Runtime Information feature pops up a dialog showing information about the internal
|
||||
settings of the active Ghidra installation. It is primarily a diagnostic tool to aid in
|
||||
debugging.</P>
|
||||
|
||||
<P>To display the Runtime Information dialog:</P>
|
||||
|
||||
<UL>
|
||||
<LI>From the Project Window, Select <B>Help<IMG border="0" src="help/shared/arrow.gif">
|
||||
Runtime Information</B> from the menu.</LI>
|
||||
</UL>
|
||||
|
||||
<P>Runtime Information categories are organized in tabs. The following categories are provided:
|
||||
<UL>
|
||||
<LI>
|
||||
<B>Version</B> - Ghidra, Operating System, and Java version information. Clicking the
|
||||
<B><I>Copy</I></B> button will copy this information to the clipboard for easy transfer into a bug report.
|
||||
</LI>
|
||||
<LI>
|
||||
<B>Memory</B> - JVM memory usage. Clicking the <B><I>Collect Garbage</I></B> button will
|
||||
suggest to the JVM that garbage collection should run.
|
||||
</LI>
|
||||
<LI>
|
||||
<B>Application Layout</B> - Ghidra application layout information, including directory
|
||||
locations.
|
||||
</LI>
|
||||
<LI>
|
||||
<B>Properties</B> - Defined JVM system properties for the active Ghidra application.
|
||||
</LI>
|
||||
<LI>
|
||||
<B>Environment</B> - Defined environment variables for the active Ghidra application.
|
||||
</LI>
|
||||
<LI>
|
||||
<B>Modules</B> - A list of discovered Ghidra Modules.
|
||||
</LI>
|
||||
<LI>
|
||||
<B>Extension Points</B> - A list of discovered Ghidra Extension Points.
|
||||
</LI>
|
||||
<LI>
|
||||
<B>Classpath</B> - The ordered classpath for the active Ghidra application.
|
||||
</LI>
|
||||
<LI>
|
||||
<B>Extensions Classpath</B> - The ordered extensions classpath, if the
|
||||
<B><I>ghidra.extensions.classpath.restricted</I></B> property is set (see
|
||||
<I>support/launch.properties</I>).
|
||||
</LI>
|
||||
</UL>
|
||||
|
||||
<P> </P>
|
||||
</BODY>
|
||||
</HTML>
|
|
@ -1,292 +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.help;
|
||||
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.Transferable;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.ReusableDialogComponentProvider;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import docking.dnd.GClipboard;
|
||||
import docking.dnd.StringTransferable;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.table.AbstractSortedTableModel;
|
||||
import docking.widgets.table.GTable;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.util.HelpTopics;
|
||||
import ghidra.framework.main.ApplicationLevelPlugin;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.program.model.lang.*;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
import ghidra.util.HelpLocation;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = CorePluginPackage.NAME,
|
||||
category = PluginCategoryNames.COMMON,
|
||||
shortDescription = "Displays list of installed processor modules",
|
||||
description = "This plugin provides a Help action that displays a list of installed processor modules"
|
||||
)
|
||||
//@formatter:on
|
||||
public class ProcessorListPlugin extends Plugin implements ApplicationLevelPlugin {
|
||||
|
||||
private DockingAction processorListAction;
|
||||
|
||||
private ProcessorListDialogProvider dialogProvider;
|
||||
|
||||
public ProcessorListPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void init() {
|
||||
setupActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
tool.removeAction(processorListAction);
|
||||
processorListAction.dispose();
|
||||
|
||||
if (dialogProvider != null) {
|
||||
dialogProvider.dispose();
|
||||
}
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
private void setupActions() {
|
||||
|
||||
processorListAction = new DockingAction("Installed Processors", this.getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
showProcessorList();
|
||||
}
|
||||
};
|
||||
|
||||
processorListAction.setEnabled(true);
|
||||
|
||||
processorListAction.setMenuBarData(new MenuData(
|
||||
new String[] { ToolConstants.MENU_HELP, processorListAction.getName() }, null, "AAAZ"));
|
||||
|
||||
processorListAction.setHelpLocation(new HelpLocation(HelpTopics.ABOUT, "ProcessorList"));
|
||||
processorListAction.setDescription(getPluginDescription().getDescription());
|
||||
tool.addAction(processorListAction);
|
||||
}
|
||||
|
||||
private void dialogClosed() {
|
||||
dialogProvider = null;
|
||||
}
|
||||
|
||||
private void showProcessorList() {
|
||||
if (dialogProvider == null) {
|
||||
dialogProvider = new ProcessorListDialogProvider();
|
||||
}
|
||||
tool.showDialog(dialogProvider);
|
||||
}
|
||||
|
||||
private void copy(boolean asHtml) {
|
||||
Clipboard systemClipboard = GClipboard.getSystemClipboard();
|
||||
Transferable transferable = new StringTransferable(getProcessorList(asHtml));
|
||||
systemClipboard.setContents(transferable, null);
|
||||
}
|
||||
|
||||
private Set<Processor> getProcessors() {
|
||||
TreeSet<Processor> processors = new TreeSet<>();
|
||||
LanguageService languageService = DefaultLanguageService.getLanguageService();
|
||||
for (LanguageDescription languageDescription : languageService.getLanguageDescriptions(
|
||||
true)) {
|
||||
processors.add(languageDescription.getProcessor());
|
||||
}
|
||||
return processors;
|
||||
}
|
||||
|
||||
private String getProcessorList(boolean asHtml) {
|
||||
StringBuilder strBuilder = new StringBuilder();
|
||||
if (asHtml) {
|
||||
strBuilder.append("<HTML><BODY>\n");
|
||||
strBuilder.append("<table width=\"100%\" cellpadding=\"0\" cellspacing=\"0\">\n<tr>");
|
||||
}
|
||||
|
||||
Set<Processor> processors = getProcessors();
|
||||
int itemsPerColum = (processors.size() + 2) / 3;
|
||||
int colCnt = 0;
|
||||
|
||||
for (Processor processor : processors) {
|
||||
if (asHtml) {
|
||||
if ((colCnt % itemsPerColum) == 0) {
|
||||
if (colCnt != 0) {
|
||||
strBuilder.append("</ul>\n</td>");
|
||||
}
|
||||
strBuilder.append("<td width=\"33%\">\n<ul>");
|
||||
}
|
||||
strBuilder.append("<li>");
|
||||
}
|
||||
++colCnt;
|
||||
strBuilder.append(processor.toString());
|
||||
if (asHtml) {
|
||||
strBuilder.append("</li>");
|
||||
}
|
||||
strBuilder.append("\n");
|
||||
}
|
||||
if (asHtml) {
|
||||
strBuilder.append("</ul>\n</td></tr>\n</table>");
|
||||
strBuilder.append("</BODY></HTML>");
|
||||
}
|
||||
return strBuilder.toString();
|
||||
}
|
||||
|
||||
private class ProcessorListDialogProvider extends ReusableDialogComponentProvider {
|
||||
|
||||
ProcessorListDialogProvider() {
|
||||
super("Installed Processor Modules", false, false, true, false);
|
||||
ProcessorListTableProvider tableProvider =
|
||||
new ProcessorListTableProvider(tool, getName());
|
||||
setRememberLocation(true);
|
||||
addWorkPanel(tableProvider.getComponent());
|
||||
|
||||
setHelpLocation(new HelpLocation(HelpTopics.ABOUT, "ProcessorList"));
|
||||
|
||||
if (SystemUtilities.isInDevelopmentMode()) {
|
||||
|
||||
JButton copyButton = new JButton("Copy");
|
||||
copyButton.addActionListener(e -> copy(false));
|
||||
addButton(copyButton);
|
||||
|
||||
JButton copyHtmlButton = new JButton("Copy as HTML");
|
||||
copyHtmlButton.addActionListener(e -> copy(true));
|
||||
addButton(copyHtmlButton);
|
||||
}
|
||||
|
||||
JButton closeButton = new JButton("Close");
|
||||
closeButton.addActionListener(e -> close());
|
||||
addButton(closeButton);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dialogClosed() {
|
||||
super.dialogClosed();
|
||||
ProcessorListPlugin.this.dialogClosed();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ProcessorListTableProvider extends ComponentProviderAdapter {
|
||||
GTable table;
|
||||
private ProcessorListTableModel processorTableModel;
|
||||
private JScrollPane scrollPane;
|
||||
|
||||
public ProcessorListTableProvider(PluginTool tool, String owner) {
|
||||
super(tool, "Processor Table", owner);
|
||||
buildTable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public JComponent getComponent() {
|
||||
return scrollPane;
|
||||
}
|
||||
|
||||
private void buildTable() {
|
||||
|
||||
TreeSet<Processor> processors = new TreeSet<>();
|
||||
LanguageService languageService = DefaultLanguageService.getLanguageService();
|
||||
for (LanguageDescription languageDescription : languageService.getLanguageDescriptions(
|
||||
true)) {
|
||||
processors.add(languageDescription.getProcessor());
|
||||
}
|
||||
|
||||
processorTableModel = new ProcessorListTableModel(new ArrayList<>(processors));
|
||||
|
||||
table = new GTable(processorTableModel);
|
||||
scrollPane = new JScrollPane(table);
|
||||
table.getSelectionManager().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class ProcessorListTableModel extends AbstractSortedTableModel<Processor> {
|
||||
|
||||
private static final int PROCESSOR_COL = 0;
|
||||
|
||||
private List<Processor> processors;
|
||||
|
||||
public ProcessorListTableModel(List<Processor> processors) {
|
||||
this.processors = processors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getColumnValueForRow(Processor p, int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case PROCESSOR_COL:
|
||||
return p.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Processors";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Processor> getModelData() {
|
||||
return processors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSortable(int columnIndex) {
|
||||
return false; // maybe later when we add more columns
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRowCount() {
|
||||
return processors.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColumnName(int column) {
|
||||
switch (column) {
|
||||
case PROCESSOR_COL:
|
||||
return "Processor";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<?> getColumnClass(int columnIndex) {
|
||||
switch (columnIndex) {
|
||||
case PROCESSOR_COL:
|
||||
return String.class;
|
||||
}
|
||||
return Object.class;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
/* ###
|
||||
* 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.runtimeinfo;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Dimension;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.ReusableDialogComponentProvider;
|
||||
import ghidra.program.model.lang.LanguageDescription;
|
||||
import ghidra.program.model.lang.Processor;
|
||||
import ghidra.program.util.DefaultLanguageService;
|
||||
|
||||
/**
|
||||
* A dialog that shows the supported platforms (processors, loaders, file systems, etc)
|
||||
*/
|
||||
class InstalledProcessorsProvider extends ReusableDialogComponentProvider {
|
||||
|
||||
private RuntimeInfoPlugin plugin;
|
||||
private JTabbedPane tabbedPane;
|
||||
|
||||
/**
|
||||
* Creates a new {@link InstalledProcessorsProvider}
|
||||
*
|
||||
* @param plugin The associated {@link RuntimeInfoPlugin}
|
||||
*/
|
||||
InstalledProcessorsProvider(RuntimeInfoPlugin plugin) {
|
||||
super("Installed Processors", false, false, true, false);
|
||||
this.plugin = plugin;
|
||||
|
||||
setHelpLocation(plugin.getInstalledProcessorsHelpLocation());
|
||||
addWorkPanel(createWorkPanel());
|
||||
}
|
||||
|
||||
private JComponent createWorkPanel() {
|
||||
tabbedPane = new JTabbedPane();
|
||||
|
||||
addProcessors();
|
||||
|
||||
JPanel mainPanel = new JPanel(new BorderLayout()) {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return new Dimension(700, 400);
|
||||
}
|
||||
};
|
||||
mainPanel.add(tabbedPane, BorderLayout.CENTER);
|
||||
return mainPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a "processors" panel to the tabbed pane.
|
||||
* <p>
|
||||
* The goal of this panel is to display every {@link Processor} that Ghidra discovered and
|
||||
* loaded.
|
||||
*/
|
||||
private void addProcessors() {
|
||||
Map<String, Integer> map = new HashMap<>();
|
||||
for (LanguageDescription desc : DefaultLanguageService.getLanguageService()
|
||||
.getLanguageDescriptions(true)) {
|
||||
String processor = desc.getProcessor().toString();
|
||||
int count = map.getOrDefault(processor, 0);
|
||||
map.put(processor, count + 1);
|
||||
}
|
||||
String name = "Processors";
|
||||
tabbedPane.add(
|
||||
new MapTablePanel<String, Integer>(name, map, "Name", "Variants", 300, false, plugin),
|
||||
name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/* ###
|
||||
* 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.runtimeinfo;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.JPanel;
|
||||
|
||||
import docking.widgets.table.*;
|
||||
import ghidra.docking.settings.Settings;
|
||||
import ghidra.framework.plugintool.Plugin;
|
||||
import ghidra.framework.plugintool.ServiceProvider;
|
||||
import ghidra.util.Disposable;
|
||||
|
||||
/**
|
||||
* A {@link JPanel} that displays a 2-column table created from a {@link Map}
|
||||
*
|
||||
* @param <K> The {@link Map} key type
|
||||
* @param <V> The {@link Map} value type
|
||||
*/
|
||||
class MapTablePanel<K, V> extends JPanel implements Disposable {
|
||||
|
||||
private String name;
|
||||
private Map<K, V> map;
|
||||
private String keyColumnName;
|
||||
private String valueColumnName;
|
||||
private int keyColumnWidth;
|
||||
private boolean showValueColumn;
|
||||
private Plugin plugin;
|
||||
private GFilterTable<Map.Entry<K, V>> table;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MapTablePanel}
|
||||
*
|
||||
* @param name The name of the panel
|
||||
* @param map The {@link Map}
|
||||
* @param keyColumnName The name of the key column
|
||||
* @param valueName The name of the value column
|
||||
* @param keyColumnWidth The width of the key column, in pixels
|
||||
* @param showValueColumn True if the value column should be visible; false if it should be
|
||||
* hidden
|
||||
* @param plugin The {@link Plugin} associated with this {@link MapTablePanel}
|
||||
*/
|
||||
MapTablePanel(String name, Map<K, V> map, String keyColumnName, String valueName,
|
||||
int keyColumnWidth, boolean showValueColumn, Plugin plugin) {
|
||||
this.name = name;
|
||||
this.map = map;
|
||||
this.keyColumnName = keyColumnName;
|
||||
this.valueColumnName = valueName;
|
||||
this.keyColumnWidth = keyColumnWidth;
|
||||
this.showValueColumn = showValueColumn;
|
||||
this.plugin = plugin;
|
||||
this.table = new GFilterTable<>(new MapModel());
|
||||
|
||||
setLayout(new BorderLayout());
|
||||
add(table, BorderLayout.CENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
table.dispose();
|
||||
}
|
||||
|
||||
private class MapModel
|
||||
extends GDynamicColumnTableModel<Map.Entry<K, V>, List<Map.Entry<K, V>>> {
|
||||
|
||||
private List<Map.Entry<K, V>> entries;
|
||||
|
||||
public MapModel() {
|
||||
super(plugin.getTool());
|
||||
entries = new ArrayList<>(map.entrySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map.Entry<K, V>> getModelData() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TableColumnDescriptor<Map.Entry<K, V>> createTableColumnDescriptor() {
|
||||
TableColumnDescriptor<Map.Entry<K, V>> columnDescriptor = new TableColumnDescriptor<>();
|
||||
columnDescriptor.addVisibleColumn(new KeyColumn());
|
||||
if (showValueColumn) {
|
||||
columnDescriptor.addVisibleColumn(new ValueColumn());
|
||||
}
|
||||
else {
|
||||
columnDescriptor.addHiddenColumn(new ValueColumn());
|
||||
}
|
||||
return columnDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Map.Entry<K, V>> getDataSource() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
private class KeyColumn
|
||||
extends AbstractDynamicTableColumn<Map.Entry<K, V>, K, List<Map.Entry<K, V>>> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return keyColumnName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public K getValue(Map.Entry<K, V> entry, Settings settings, List<Map.Entry<K, V>> data,
|
||||
ServiceProvider services) throws IllegalArgumentException {
|
||||
return entry.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getColumnPreferredWidth() {
|
||||
return keyColumnWidth;
|
||||
}
|
||||
}
|
||||
|
||||
private class ValueColumn
|
||||
extends AbstractDynamicTableColumn<Map.Entry<K, V>, V, List<Map.Entry<K, V>>> {
|
||||
|
||||
@Override
|
||||
public String getColumnName() {
|
||||
return valueColumnName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getValue(Map.Entry<K, V> entry, Settings settings, List<Map.Entry<K, V>> data,
|
||||
ServiceProvider services) throws IllegalArgumentException {
|
||||
return entry.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
/* ###
|
||||
* 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.runtimeinfo;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GLabel;
|
||||
import ghidra.util.Disposable;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
||||
/**
|
||||
* A {@link JPanel} that displays live memory usage and provides a button to initiate garbage
|
||||
* collection on-demand
|
||||
*/
|
||||
class MemoryUsagePanel extends JPanel implements Disposable {
|
||||
|
||||
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat();
|
||||
|
||||
private Timer timer;
|
||||
|
||||
/**
|
||||
* Creates a new {@link MemoryUsagePanel}
|
||||
*/
|
||||
MemoryUsagePanel() {
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
// Center panel
|
||||
JPanel centerPanel = new JPanel(new PairLayout());
|
||||
centerPanel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
JLabel maxMem = new GDLabel("00000000000", SwingConstants.RIGHT);
|
||||
JLabel totalMem = new GDLabel("00000000000", SwingConstants.RIGHT);
|
||||
JLabel freeMem = new GDLabel("00000000000", SwingConstants.RIGHT);
|
||||
JLabel usedMem = new GDLabel("00000000000", SwingConstants.RIGHT);
|
||||
centerPanel.add(new GLabel("Max Memory:"));
|
||||
centerPanel.add(maxMem);
|
||||
centerPanel.add(new GLabel("Total Memory:"));
|
||||
centerPanel.add(totalMem);
|
||||
centerPanel.add(new GLabel("Free Memory:"));
|
||||
centerPanel.add(freeMem);
|
||||
centerPanel.add(new GLabel("Used Memory:"));
|
||||
centerPanel.add(usedMem);
|
||||
add(centerPanel, BorderLayout.CENTER);
|
||||
|
||||
// Bottom panel
|
||||
JPanel bottomPanel = new JPanel();
|
||||
JButton gcButton = new JButton("Collect Garbage");
|
||||
gcButton.addActionListener(e -> Runtime.getRuntime().gc());
|
||||
bottomPanel.add(gcButton);
|
||||
add(bottomPanel, BorderLayout.SOUTH);
|
||||
|
||||
// Garbage collection refresh timer
|
||||
timer = new Timer(2000, e -> {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
maxMem.setText(formatMemoryValue(runtime.maxMemory()));
|
||||
totalMem.setText(formatMemoryValue(runtime.totalMemory()));
|
||||
freeMem.setText(formatMemoryValue(runtime.freeMemory()));
|
||||
usedMem.setText(formatMemoryValue(runtime.totalMemory() - runtime.freeMemory()));
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
timer.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called when this {@link MemoryUsagePanel} is shown
|
||||
*/
|
||||
void shown() {
|
||||
timer.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Should be called when this {@link MemoryUsagePanel} is hidden
|
||||
*/
|
||||
void hidden() {
|
||||
timer.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the given raw memory value (in bytes) to a more human-readable string
|
||||
*
|
||||
* @param value A memory value in bytes
|
||||
* @return A more human-readable memory value representation
|
||||
*/
|
||||
private String formatMemoryValue(long value) {
|
||||
return DECIMAL_FORMAT.format(value >>> 20) + "MB";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
/* ###
|
||||
* 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.runtimeinfo;
|
||||
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.framework.main.ApplicationLevelOnlyPlugin;
|
||||
import ghidra.framework.main.UtilityPluginPackage;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = UtilityPluginPackage.NAME,
|
||||
category = PluginCategoryNames.SUPPORT,
|
||||
shortDescription = "Runtime Information",
|
||||
description = "Plugin for displaying runtime information"
|
||||
)
|
||||
//@formatter:on
|
||||
public class RuntimeInfoPlugin extends Plugin implements ApplicationLevelOnlyPlugin {
|
||||
|
||||
private InstalledProcessorsProvider installedProcessorsProvider;
|
||||
private RuntimeInfoProvider runtimeInfoProvider;
|
||||
|
||||
/**
|
||||
* Creates a new {@link RuntimeInfoPlugin}
|
||||
*
|
||||
* @param tool The tool
|
||||
*/
|
||||
public RuntimeInfoPlugin(PluginTool tool) {
|
||||
super(tool);
|
||||
|
||||
String supportedActionName = "Installed Processors";
|
||||
new ActionBuilder(supportedActionName, getName())
|
||||
.onAction(context -> showInstalledProcessors())
|
||||
.enabled(true)
|
||||
.menuPath("Help", supportedActionName)
|
||||
.menuGroup("YYY") // trying to put this just above the last menu entry
|
||||
.helpLocation(getInstalledProcessorsHelpLocation())
|
||||
.buildAndInstall(tool);
|
||||
|
||||
String runtimeInfoActionName = "Runtime Information";
|
||||
new ActionBuilder(runtimeInfoActionName, getName())
|
||||
.onAction(context -> showRuntimeInfo())
|
||||
.enabled(true)
|
||||
.menuPath("Help", runtimeInfoActionName)
|
||||
.menuGroup("YYY")
|
||||
.helpLocation(getRuntimeInfoHelpLocation())
|
||||
.buildAndInstall(tool);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispose() {
|
||||
super.dispose();
|
||||
if (installedProcessorsProvider != null) {
|
||||
installedProcessorsProvider.dispose();
|
||||
installedProcessorsProvider = null;
|
||||
}
|
||||
if (runtimeInfoProvider != null) {
|
||||
runtimeInfoProvider.dispose();
|
||||
runtimeInfoProvider = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this plugin's installed processors {@link HelpLocation}
|
||||
*
|
||||
* @return This plugin's installed processors {@link HelpLocation}
|
||||
*/
|
||||
protected HelpLocation getInstalledProcessorsHelpLocation() {
|
||||
return new HelpLocation("RuntimeInfoPlugin", "InstalledProcessors");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this plugin's runtime info {@link HelpLocation}
|
||||
*
|
||||
* @return This plugin's runtime info {@link HelpLocation}
|
||||
*/
|
||||
protected HelpLocation getRuntimeInfoHelpLocation() {
|
||||
return new HelpLocation("RuntimeInfoPlugin", "RuntimeInfo");
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the {@link InstalledProcessorsProvider}
|
||||
*/
|
||||
private void showInstalledProcessors() {
|
||||
if (installedProcessorsProvider == null) {
|
||||
installedProcessorsProvider = new InstalledProcessorsProvider(this);
|
||||
}
|
||||
|
||||
tool.showDialog(installedProcessorsProvider);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the {@link RuntimeInfoProvider}
|
||||
*/
|
||||
private void showRuntimeInfo() {
|
||||
if (runtimeInfoProvider == null) {
|
||||
runtimeInfoProvider = new RuntimeInfoProvider(this);
|
||||
}
|
||||
|
||||
tool.showDialog(runtimeInfoProvider);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,242 @@
|
|||
/* ###
|
||||
* 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.runtimeinfo;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.ReusableDialogComponentProvider;
|
||||
import generic.jar.ResourceFile;
|
||||
import ghidra.GhidraClassLoader;
|
||||
import ghidra.framework.Application;
|
||||
import ghidra.util.Disposable;
|
||||
import ghidra.util.classfinder.ClassSearcher;
|
||||
import ghidra.util.classfinder.ExtensionPoint;
|
||||
|
||||
/**
|
||||
* A dialog that shows useful runtime information
|
||||
*/
|
||||
class RuntimeInfoProvider extends ReusableDialogComponentProvider {
|
||||
|
||||
private RuntimeInfoPlugin plugin;
|
||||
private JTabbedPane tabbedPane;
|
||||
private MemoryUsagePanel memoryUsagePanel;
|
||||
|
||||
/**
|
||||
* Creates a new {@link RuntimeInfoProvider}
|
||||
*
|
||||
* @param plugin The associated {@link RuntimeInfoPlugin}
|
||||
*/
|
||||
RuntimeInfoProvider(RuntimeInfoPlugin plugin) {
|
||||
super("Runtime Information", false, false, true, false);
|
||||
this.plugin = plugin;
|
||||
|
||||
|
||||
setHelpLocation(plugin.getRuntimeInfoHelpLocation());
|
||||
addWorkPanel(createWorkPanel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
super.dispose();
|
||||
for (Component c : tabbedPane.getComponents()) {
|
||||
if (c instanceof Disposable d) {
|
||||
d.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dialogShown() {
|
||||
memoryUsagePanel.shown();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dialogClosed() {
|
||||
memoryUsagePanel.hidden();
|
||||
}
|
||||
|
||||
|
||||
private JComponent createWorkPanel() {
|
||||
tabbedPane = new JTabbedPane();
|
||||
|
||||
addVersionInfoPanel();
|
||||
addMemory();
|
||||
addApplicationLayout();
|
||||
addProperties();
|
||||
addEnvironment();
|
||||
addModules();
|
||||
addExtensionPoints();
|
||||
addClasspath();
|
||||
addExtensionsClasspath();
|
||||
|
||||
JPanel mainPanel = new JPanel(new BorderLayout()) {
|
||||
@Override
|
||||
public Dimension getPreferredSize() {
|
||||
return new Dimension(700, 400);
|
||||
}
|
||||
};
|
||||
mainPanel.add(tabbedPane, BorderLayout.CENTER);
|
||||
return mainPanel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a "version" panel to the tabbed pane.
|
||||
* <p>
|
||||
* The goal of this panel is to display version information that would be useful to include in
|
||||
* a bug report, and provide a button that copies this information to the system clipboard.
|
||||
*/
|
||||
private void addVersionInfoPanel() {
|
||||
tabbedPane.add(new VersionInfoPanel(), "Version");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a "memory" panel to the tabbed pane.
|
||||
* <p>
|
||||
* The goal of this panel is to display live memory usage, and provide a button to initiate
|
||||
* garbage collection on-demand.
|
||||
*/
|
||||
private void addMemory() {
|
||||
memoryUsagePanel = new MemoryUsagePanel();
|
||||
tabbedPane.add(memoryUsagePanel, "Memory");
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an "application layout" panel to the tabbed pane.
|
||||
* <p>
|
||||
* The goal of this panel is to display information information about the application such as
|
||||
* what directories it is using on disk, what its PID is, etc.
|
||||
*/
|
||||
private void addApplicationLayout() {
|
||||
Map<String, String> map = new HashMap<>();
|
||||
map.put("PID", ProcessHandle.current().pid() + "");
|
||||
map.put("Installation Directory", Application.getInstallationDirectory().getAbsolutePath());
|
||||
map.put("Settings Directory", Application.getUserSettingsDirectory().getPath());
|
||||
map.put("Cache Directory", Application.getUserCacheDirectory().getPath());
|
||||
map.put("Temp Directory", Application.getUserTempDirectory().getPath());
|
||||
String name = "Application Layout";
|
||||
tabbedPane.add(
|
||||
new MapTablePanel<String, String>(name, map, "Name", "Path", 200, true, plugin), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a "properties" panel to the tabbed pane.
|
||||
* <p>
|
||||
* The goal of this panel is to display every defined system property in a table.
|
||||
*/
|
||||
private void addProperties() {
|
||||
Properties properties = System.getProperties();
|
||||
Map<String, String> map = new HashMap<>();
|
||||
for (Object key : properties.keySet()) {
|
||||
map.put(key.toString(), properties.getOrDefault(key, "").toString());
|
||||
}
|
||||
String name = "Properties";
|
||||
tabbedPane.add(
|
||||
new MapTablePanel<String, String>(name, map, "Name", "Value", 400, true, plugin), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an "environment" panel to the tabbed pane.
|
||||
* <p>
|
||||
* The goal of this panel is to display every defined environment variable in a table.
|
||||
*/
|
||||
private void addEnvironment() {
|
||||
Map<String, String> map = System.getenv();
|
||||
String name = "Environment";
|
||||
tabbedPane.add(
|
||||
new MapTablePanel<String, String>(name, map, "Name", "Value", 400, true, plugin), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a "modules" panel to the tabbed pane.
|
||||
* <p>
|
||||
* The goal of this panel is to display every module that Ghidra discovered and loaded.
|
||||
*/
|
||||
private void addModules() {
|
||||
Map<String, ResourceFile> map = Application.getApplicationLayout()
|
||||
.getModules()
|
||||
.entrySet()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().getModuleRoot()));
|
||||
String name = "Modules";
|
||||
tabbedPane.add(
|
||||
new MapTablePanel<String, ResourceFile>(name, map, "Name", "Path", 400, true, plugin),
|
||||
name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an "extension points" panel to the tabbed pane.
|
||||
* <p>
|
||||
* The goal of this panel is to display every {@link ExtensionPoint} that Ghidra discovered and
|
||||
* loaded.
|
||||
*/
|
||||
private void addExtensionPoints() {
|
||||
Map<String, String> map = ClassSearcher.getClasses(ExtensionPoint.class)
|
||||
.stream()
|
||||
.collect(Collectors.toMap(e -> e.getName(),
|
||||
e -> ClassSearcher.getExtensionPointName(e.getName())));
|
||||
String name = "Extension Points";
|
||||
tabbedPane.add(new MapTablePanel<String, String>(name, map, "Name", "Extension Point", 400,
|
||||
true, plugin), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a "classpath" panel to the tabbed pane.
|
||||
* <p>
|
||||
* The goal of this panel is to display Ghidra's current classpath.
|
||||
*/
|
||||
private void addClasspath() {
|
||||
Map<Integer, String> map = getClasspathMap(GhidraClassLoader.CP);
|
||||
String name = "Classpath";
|
||||
tabbedPane.add(
|
||||
new MapTablePanel<Integer, String>(name, map, "Index", "Path", 40, true, plugin), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an "extensions classpath" panel to the tabbed pane.
|
||||
* <p>
|
||||
* The goal of this panel is to display Ghidra's current extension classpath.
|
||||
*/
|
||||
private void addExtensionsClasspath() {
|
||||
Map<Integer, String> map = getClasspathMap(GhidraClassLoader.CP_EXT);
|
||||
String name = "Extensions Classpath";
|
||||
tabbedPane.add(
|
||||
new MapTablePanel<Integer, String>(name, map, "Index", "Path", 40, true, plugin), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link Map} of classpath entries, where the key is a 0-based integer index of each
|
||||
* classpath entry
|
||||
*
|
||||
* @param propertyName The classpath system property name
|
||||
* @return A {@link Map} of classpath entries, where the key is a 0-based integer index of each
|
||||
* classpath entry
|
||||
*/
|
||||
private Map<Integer, String> getClasspathMap(String propertyName) {
|
||||
Map<Integer, String> map = new HashMap<>();
|
||||
StringTokenizer st =
|
||||
new StringTokenizer(System.getProperty(propertyName, ""), File.pathSeparator);
|
||||
int i = 0;
|
||||
while (st.hasMoreTokens()) {
|
||||
map.put(i++, st.nextToken());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,129 @@
|
|||
/* ###
|
||||
* 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.runtimeinfo;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.datatransfer.Clipboard;
|
||||
import java.awt.datatransfer.StringSelection;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.dnd.GClipboard;
|
||||
import ghidra.framework.*;
|
||||
import ghidra.util.SystemUtilities;
|
||||
|
||||
/**
|
||||
* A {@link JPanel} that displays version information that would be useful to include in a bug
|
||||
* report, and provide a button that copies this information to the system clipboard
|
||||
*/
|
||||
class VersionInfoPanel extends JPanel {
|
||||
|
||||
/**
|
||||
* Creates a new {@link VersionInfoPanel}
|
||||
*/
|
||||
VersionInfoPanel() {
|
||||
setLayout(new BorderLayout());
|
||||
|
||||
JTextArea textArea = new JTextArea(gatherVersionInfo());
|
||||
add(textArea, BorderLayout.CENTER);
|
||||
|
||||
JPanel bottomPanel = new JPanel();
|
||||
JButton copyButton = new JButton("Copy");
|
||||
copyButton.addActionListener(e -> {
|
||||
Clipboard clipboard = GClipboard.getSystemClipboard();
|
||||
clipboard.setContents(new StringSelection(textArea.getText()), null);
|
||||
});
|
||||
bottomPanel.add(copyButton);
|
||||
add(bottomPanel, BorderLayout.SOUTH);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Gathers version information
|
||||
*
|
||||
* @return The version information text
|
||||
*/
|
||||
private String gatherVersionInfo() {
|
||||
final String def = "???";
|
||||
List<String> lines = new ArrayList<>();
|
||||
|
||||
addApplicationInfo(lines, def);
|
||||
addOperatingSystemInfo(lines, def);
|
||||
addJavaInfo(lines, def);
|
||||
|
||||
return String.join("\n", lines);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Ghidra application information to the version information
|
||||
*
|
||||
* @param lines A {@link List} of lines to add to
|
||||
* @param def A default value to use if a piece of information cannot be found
|
||||
*/
|
||||
private void addApplicationInfo(List<String> lines, String def) {
|
||||
ApplicationProperties props = Application.getApplicationLayout().getApplicationProperties();
|
||||
lines.add("Ghidra Version: " + props.getApplicationVersion());
|
||||
lines.add("Ghidra Release: " + props.getApplicationReleaseName());
|
||||
lines.add("Ghidra Build Date: " + props.getApplicationBuildDate());
|
||||
lines.add("Ghidra Revision: " +
|
||||
props.getProperty(ApplicationProperties.REVISION_PROPERTY_PREFIX + "ghidra", def));
|
||||
lines.add("Ghidra Development Mode: " + SystemUtilities.isInDevelopmentMode());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds operating system information to the version information
|
||||
*
|
||||
* @param lines A {@link List} of lines to add to
|
||||
* @param def A default value to use if a piece of information cannot be found
|
||||
*/
|
||||
private void addOperatingSystemInfo(List<String> lines, String def) {
|
||||
lines.add("OS Name: " + System.getProperty("os.name", def));
|
||||
lines.add("OS Arch: " + System.getProperty("os.arch", def));
|
||||
lines.add("OS Version: " + System.getProperty("os.version", def));
|
||||
if (OperatingSystem.CURRENT_OPERATING_SYSTEM.equals(OperatingSystem.LINUX)) {
|
||||
String prettyName = def;
|
||||
File osReleaseFile = new File("/etc/os-release");
|
||||
if (!osReleaseFile.isFile()) {
|
||||
osReleaseFile = new File("/usr/lib/os-release");
|
||||
}
|
||||
try (BufferedReader reader = new BufferedReader(new FileReader(osReleaseFile))) {
|
||||
Properties props = new Properties();
|
||||
props.load(reader);
|
||||
prettyName = props.getProperty("PRETTY_NAME", def);
|
||||
if (prettyName.startsWith("\"") && prettyName.endsWith("\"")) {
|
||||
prettyName = prettyName.substring(1, prettyName.length() - 1);
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
// That's ok, pretty name is optional
|
||||
}
|
||||
lines.add("OS Pretty Name: " + prettyName);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds Java/JVM information to the version information
|
||||
*
|
||||
* @param lines A {@link List} of lines to add to
|
||||
* @param def A default value to use if a piece of information cannot be found
|
||||
*/
|
||||
private void addJavaInfo(List<String> lines, String def) {
|
||||
lines.add("Java Vendor: " + System.getProperty("java.vendor", def));
|
||||
lines.add("Java Version: " + System.getProperty("java.version", def));
|
||||
}
|
||||
}
|
|
@ -27,14 +27,12 @@ import javax.swing.KeyStroke;
|
|||
import docking.action.DockingAction;
|
||||
import docking.action.builder.ActionBuilder;
|
||||
import docking.tool.ToolConstants;
|
||||
import docking.widgets.dialogs.MultiLineMessageDialog;
|
||||
import docking.widgets.filechooser.GhidraFileChooser;
|
||||
import docking.widgets.filechooser.GhidraFileChooserMode;
|
||||
import ghidra.app.CorePluginPackage;
|
||||
import ghidra.app.events.ProgramActivatedPluginEvent;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.app.services.*;
|
||||
import ghidra.app.util.opinion.LoaderService;
|
||||
import ghidra.formats.gfilesystem.*;
|
||||
import ghidra.framework.main.ApplicationLevelPlugin;
|
||||
import ghidra.framework.main.FrontEndService;
|
||||
|
@ -108,14 +106,6 @@ public class FileSystemBrowserPlugin extends Plugin
|
|||
.keyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_I, InputEvent.CTRL_DOWN_MASK))
|
||||
.onAction(ac -> doOpenFileSystem())
|
||||
.buildAndInstall(tool);
|
||||
showFileSystemImplsAction =
|
||||
new ActionBuilder("Display Supported File Systems and Loaders", this.getName())
|
||||
.description("Display Supported File Systems and Loaders")
|
||||
.enabledWhen(ac -> true)
|
||||
.menuPath(ToolConstants.MENU_HELP, "List File Systems")
|
||||
.menuGroup("AAAZ") // this "AAAZ" is from ProcessorListPlugin
|
||||
.onAction(ac -> showSupportedFileSystems())
|
||||
.buildAndInstall(tool);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -326,29 +316,4 @@ public class FileSystemBrowserPlugin extends Plugin
|
|||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows a list of supported file system types and loaders.
|
||||
*/
|
||||
private void showSupportedFileSystems() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
sb.append(
|
||||
"<html><table><tr><td>Supported File Systems</td><td>Supported Loaders</td></tr>\n");
|
||||
sb.append("<tr valign='top'><td><ul>");
|
||||
for (String fileSystemName : fsService().getAllFilesystemNames()) {
|
||||
sb.append("<li>" + fileSystemName + "\n");
|
||||
}
|
||||
|
||||
sb.append("</ul></td><td><ul>");
|
||||
for (String loaderName : LoaderService.getAllLoaderNames()) {
|
||||
sb.append("<li>" + loaderName + "\n");
|
||||
}
|
||||
sb.append("</ul></td></tr></table>");
|
||||
|
||||
MultiLineMessageDialog.showModalMessageDialog(getTool().getActiveWindow(),
|
||||
"Supported File Systems and Loaders", "", sb.toString(),
|
||||
MultiLineMessageDialog.INFORMATION_MESSAGE);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.lang.reflect.Constructor;
|
|||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
@ -278,6 +279,26 @@ public class ClassSearcher {
|
|||
log.info(finishedMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the given class's extension point name
|
||||
*
|
||||
* @param className The name of the potential extension point class
|
||||
* @return The given class's extension point name, or null if it is not an extension point
|
||||
*/
|
||||
public static String getExtensionPointName(String className) {
|
||||
if (className.indexOf("Test$") > 0 || className.endsWith("Test")) {
|
||||
return null;
|
||||
}
|
||||
int packageIndex = className.lastIndexOf('.');
|
||||
int innerClassIndex = className.lastIndexOf('$');
|
||||
int maximumIndex = StrictMath.max(packageIndex, innerClassIndex);
|
||||
if (maximumIndex > 0) {
|
||||
className = className.substring(maximumIndex + 1);
|
||||
}
|
||||
Matcher m = extensionPointSuffixPattern.matcher(className);
|
||||
return m.find() && m.groupCount() == 1 ? m.group(1) : null;
|
||||
}
|
||||
|
||||
private static List<String> gatherSearchPaths() {
|
||||
|
||||
//
|
||||
|
@ -394,16 +415,7 @@ public class ClassSearcher {
|
|||
}
|
||||
|
||||
static boolean isExtensionPointName(String name) {
|
||||
if (name.indexOf("Test$") > 0 || name.endsWith("Test")) {
|
||||
return false;
|
||||
}
|
||||
int packageIndex = name.lastIndexOf('.');
|
||||
int innerClassIndex = name.lastIndexOf('$');
|
||||
int maximumIndex = StrictMath.max(packageIndex, innerClassIndex);
|
||||
if (maximumIndex > 0) {
|
||||
name = name.substring(maximumIndex + 1);
|
||||
}
|
||||
return extensionPointSuffixPattern.matcher(name).matches();
|
||||
return getExtensionPointName(name) != null;
|
||||
}
|
||||
|
||||
private static void fireClassListChanged() {
|
||||
|
|
|
@ -1,88 +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.debug;
|
||||
|
||||
import docking.ActionContext;
|
||||
import docking.DialogComponentProvider;
|
||||
import docking.action.DockingAction;
|
||||
import docking.action.MenuData;
|
||||
import ghidra.app.plugin.PluginCategoryNames;
|
||||
import ghidra.framework.main.ApplicationLevelOnlyPlugin;
|
||||
import ghidra.framework.main.UtilityPluginPackage;
|
||||
import ghidra.framework.plugintool.*;
|
||||
import ghidra.framework.plugintool.util.PluginStatus;
|
||||
import ghidra.util.HelpLocation;
|
||||
|
||||
//@formatter:off
|
||||
@PluginInfo(
|
||||
status = PluginStatus.RELEASED,
|
||||
packageName = UtilityPluginPackage.NAME,
|
||||
category = PluginCategoryNames.SUPPORT,
|
||||
shortDescription = "VM Memory Display",
|
||||
description = "Plugin for displaying the VM memory information."
|
||||
)
|
||||
//@formatter:on
|
||||
public class MemoryUsagePlugin extends Plugin implements ApplicationLevelOnlyPlugin {
|
||||
private DialogComponentProvider dialog;
|
||||
|
||||
public MemoryUsagePlugin(PluginTool tool) {
|
||||
|
||||
super(tool);
|
||||
|
||||
setupActions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dispose() {
|
||||
super.dispose();
|
||||
|
||||
if (dialog != null) {
|
||||
dialog.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
private void setupActions() {
|
||||
DockingAction action;
|
||||
|
||||
// add menu action for Hello->Program
|
||||
action = new DockingAction("Show VM memory", getName()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionContext context) {
|
||||
showMemory();
|
||||
}
|
||||
};
|
||||
|
||||
action.setEnabled(true);
|
||||
action.setHelpLocation(new HelpLocation("FrontEndPlugin", "ShowMemoryUsage"));
|
||||
String group = "YYY"; // trying to put this just above the last menu entry
|
||||
action.setMenuBarData(new MenuData(new String[] { "Help", "Show VM Memory" }, group));
|
||||
tool.addAction(action);
|
||||
|
||||
}
|
||||
|
||||
void clearDialog() {
|
||||
dialog = null;
|
||||
}
|
||||
|
||||
public void showMemory() {
|
||||
if (dialog == null) {
|
||||
dialog = new ShowMemoryDialog(this);
|
||||
}
|
||||
else {
|
||||
dialog.toFront();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,96 +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.debug;
|
||||
|
||||
import java.text.DecimalFormat;
|
||||
|
||||
import javax.swing.*;
|
||||
|
||||
import docking.ReusableDialogComponentProvider;
|
||||
import docking.widgets.label.GDLabel;
|
||||
import docking.widgets.label.GLabel;
|
||||
import ghidra.util.layout.PairLayout;
|
||||
|
||||
class ShowMemoryDialog extends ReusableDialogComponentProvider {
|
||||
private MemoryUsagePlugin plugin;
|
||||
private JLabel maxMem;
|
||||
private JLabel totalMem;
|
||||
private JLabel freeMem;
|
||||
private JLabel usedMem;
|
||||
private Timer timer;
|
||||
|
||||
ShowMemoryDialog(MemoryUsagePlugin plugin) {
|
||||
super("VM Memory Usage", false, false, true, false);
|
||||
this.plugin = plugin;
|
||||
addOKButton();
|
||||
setOkButtonText("GC");
|
||||
addWorkPanel(createWorkPanel());
|
||||
plugin.getTool().showDialog(this);
|
||||
final DecimalFormat df = new DecimalFormat();
|
||||
timer = new Timer(2000, e -> {
|
||||
Runtime runtime = Runtime.getRuntime();
|
||||
maxMem.setText(df.format(runtime.maxMemory() / 1000) + "K");
|
||||
totalMem.setText(df.format(runtime.totalMemory() / 1000) + "K");
|
||||
freeMem.setText(df.format(runtime.freeMemory() / 1000) + "K");
|
||||
usedMem.setText(
|
||||
df.format((runtime.totalMemory() - runtime.freeMemory()) / 1000) + "K");
|
||||
});
|
||||
timer.start();
|
||||
}
|
||||
|
||||
boolean isInitialized() {
|
||||
String text = maxMem.getText();
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
char c = text.charAt(i);
|
||||
if ('0' != c) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void cancelCallback() {
|
||||
timer.stop();
|
||||
plugin.clearDialog();
|
||||
super.cancelCallback();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void okCallback() {
|
||||
Runtime.getRuntime().gc();
|
||||
}
|
||||
|
||||
private JComponent createWorkPanel() {
|
||||
JPanel panel = new JPanel(new PairLayout());
|
||||
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||
maxMem = new GDLabel("00000000000", SwingConstants.RIGHT);
|
||||
totalMem = new GDLabel("00000000000", SwingConstants.RIGHT);
|
||||
freeMem = new GDLabel("00000000000", SwingConstants.RIGHT);
|
||||
usedMem = new GDLabel("00000000000", SwingConstants.RIGHT);
|
||||
|
||||
panel.add(new GLabel("Max Memory:"));
|
||||
panel.add(maxMem);
|
||||
panel.add(new GLabel("Total Memory:"));
|
||||
panel.add(totalMem);
|
||||
panel.add(new GLabel("Free Memory:"));
|
||||
panel.add(freeMem);
|
||||
panel.add(new GLabel("Used Memory:"));
|
||||
panel.add(usedMem);
|
||||
|
||||
return panel;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue