feat(gui): report plugin exceptions to plugin github project

This commit is contained in:
Skylot
2025-12-23 20:39:40 +00:00
parent b3be2794a0
commit a3df83e60f
6 changed files with 109 additions and 25 deletions
@@ -16,7 +16,6 @@ import jadx.gui.logs.LogCollector;
import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.JadxSettingsData;
import jadx.gui.ui.MainWindow;
import jadx.gui.ui.dialog.ExceptionDialog;
import jadx.gui.utils.LafManager;
import jadx.gui.utils.NLS;
@@ -35,7 +34,6 @@ public class JadxGUI {
LogCollector.register();
printSystemInfo();
ExceptionDialog.registerUncaughtExceptionHandler();
NLS.setLocale(settings.getLangLocale());
SwingUtilities.invokeLater(() -> {
LafManager.init(settings);
@@ -0,0 +1,19 @@
package jadx.gui.report;
final class ExceptionData {
private final Throwable exception;
private final String githubProject;
ExceptionData(Throwable exception, String githubProject) {
this.exception = exception;
this.githubProject = githubProject;
}
public Throwable getException() {
return exception;
}
public String getGithubProject() {
return githubProject;
}
}
@@ -1,4 +1,4 @@
package jadx.gui.ui.dialog;
package jadx.gui.report;
import java.awt.BorderLayout;
import java.awt.Color;
@@ -8,7 +8,6 @@ import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.Window;
import java.awt.event.KeyEvent;
import java.io.PrintWriter;
import java.io.StringWriter;
@@ -37,8 +36,10 @@ import jadx.commons.app.JadxSystemInfo;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.JadxSettingsData;
import jadx.gui.ui.MainWindow;
import jadx.gui.utils.LafManager;
import jadx.gui.utils.Link;
import jadx.gui.utils.TextStandardActions;
public class ExceptionDialog extends JDialog {
@@ -46,17 +47,8 @@ public class ExceptionDialog extends JDialog {
private static final String FMT_DETAIL_LENGTH = "-13";
public static void registerUncaughtExceptionHandler() {
Thread.setDefaultUncaughtExceptionHandler(ExceptionDialog::showExceptionDialog);
}
public static void showExceptionDialog(Thread thread, Throwable ex) {
LOG.error("Exception was thrown", ex);
new ExceptionDialog(thread, ex);
}
public ExceptionDialog(Thread thread, Throwable ex) {
super((Window) null, "Jadx Error");
ExceptionDialog(MainWindow mainWindow, ExceptionData data) {
super(mainWindow, "Jadx Error");
this.getContentPane().setLayout(new BorderLayout());
JPanel titlePanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
@@ -84,6 +76,7 @@ public class ExceptionDialog extends JDialog {
LOG.error("failed to get program arguments", t);
}
Throwable ex = data.getException();
StringWriter stackTraceWriter = new StringWriter(1024);
ex.printStackTrace(new PrintWriter(stackTraceWriter));
final String stackTrace = stackTraceWriter.toString();
@@ -96,13 +89,13 @@ public class ExceptionDialog extends JDialog {
issueTitle = ex.getClass().getSimpleName();
}
String message = "Please describe what you did before the error occurred.\n";
message += "**IMPORTANT!** If the error occurs with a specific APK file please attach or provide link to apk file!\n";
String message = "Please describe what you did before the error occurred.\n\n";
message += "**IMPORTANT!** If the error occurs with a specific APK file please attach or provide link to apk file!\n\n";
StringBuilder detailsIssueBuilder = new StringBuilder();
details.forEach((key, value) -> detailsIssueBuilder.append(String.format("* %s: %s\n", key, value)));
String body = String.format("%s %s\n```\n%s\n```", message, detailsIssueBuilder, stackTrace);
String body = String.format("%s%s\n```\n%s\n```", message, detailsIssueBuilder, stackTrace);
String issueBody;
try {
@@ -112,13 +105,19 @@ public class ExceptionDialog extends JDialog {
issueBody = "Please copy the displayed text in the Jadx error dialog and paste it here";
}
String url = String.format("https://github.com/skylot/jadx/issues/new?labels=bug&title=%s&body=%s", issueTitle, issueBody);
Link issueLink = new Link("<html><u><b>Create a new issue at GitHub</b></u></html>", url);
c.gridy = 0;
titlePanel.add(titleLabel, c);
c.gridy = 1;
titlePanel.add(issueLink, c);
String project = data.getGithubProject();
if (!project.isEmpty()) {
String url = String.format("https://github.com/%s/issues/new?labels=bug&title=%s&body=%s",
project, issueTitle, issueBody);
Link issueLink = new Link("<html><u><b>Create a new issue at GitHub</b></u></html>", url);
c.gridy = 1;
titlePanel.add(issueLink, c);
}
JTextArea messageArea = new JTextArea();
TextStandardActions.attach(messageArea);
messageArea.setEditable(false);
messageArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
messageArea.setForeground(Color.BLACK);
@@ -173,7 +172,7 @@ public class ExceptionDialog extends JDialog {
try {
throwTestException();
} catch (Exception e) {
showExceptionDialog(Thread.currentThread(), e);
new ExceptionDialog(null, new ExceptionData(e, JadxExceptionHandler.MAIN_PROJECT_STRING));
}
}
@@ -0,0 +1,64 @@
package jadx.gui.report;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jadx.gui.ui.MainWindow;
import jadx.plugins.tools.JadxPluginsTools;
import jadx.plugins.tools.data.JadxPluginMetadata;
import static jadx.plugins.tools.JadxExternalPluginsLoader.JADX_PLUGIN_CLASSLOADER_PREFIX;
public class JadxExceptionHandler implements Thread.UncaughtExceptionHandler {
private static final Logger LOG = LoggerFactory.getLogger(JadxExceptionHandler.class);
public static final String MAIN_PROJECT_STRING = "skylot/jadx";
public static void register(MainWindow mainWindow) {
Thread.setDefaultUncaughtExceptionHandler(new JadxExceptionHandler(mainWindow));
}
private final MainWindow mainWindow;
private JadxExceptionHandler(MainWindow mainWindow) {
this.mainWindow = mainWindow;
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
LOG.error("Exception was thrown", ex);
new ExceptionDialog(mainWindow, buildExceptionData(ex));
}
private ExceptionData buildExceptionData(Throwable ex) {
for (StackTraceElement stackTraceElement : ex.getStackTrace()) {
String classLoaderName = stackTraceElement.getClassLoaderName();
if (classLoaderName != null && classLoaderName.startsWith(JADX_PLUGIN_CLASSLOADER_PREFIX)) {
// plugin exception
String jarName = classLoaderName.substring(JADX_PLUGIN_CLASSLOADER_PREFIX.length());
String pluginProject = resolvePluginByJarName(jarName);
LOG.debug("Report exception in plugin: {}", pluginProject);
return new ExceptionData(ex, pluginProject);
}
}
return new ExceptionData(ex, MAIN_PROJECT_STRING);
}
private String resolvePluginByJarName(String jarName) {
for (JadxPluginMetadata jadxPluginMetadata : JadxPluginsTools.getInstance().getInstalled()) {
if (jadxPluginMetadata.getJar().equals(jarName)) {
String githubProject = getGithubProject(jadxPluginMetadata.getLocationId());
return githubProject != null ? githubProject : "";
}
}
return "";
}
private static @Nullable String getGithubProject(String locationId) {
if (locationId.startsWith("github:")) {
return locationId.substring("github:".length()).replace(':', '/');
}
return null;
}
}
@@ -118,6 +118,8 @@ import jadx.gui.plugins.context.CommonGuiPluginsContext;
import jadx.gui.plugins.context.TreePopupMenuEntry;
import jadx.gui.plugins.mappings.RenameMappingsGui;
import jadx.gui.plugins.quark.QuarkDialog;
import jadx.gui.report.ExceptionDialog;
import jadx.gui.report.JadxExceptionHandler;
import jadx.gui.settings.JadxProject;
import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.data.SaveOptionEnum;
@@ -138,7 +140,6 @@ import jadx.gui.ui.codearea.theme.EditorThemeManager;
import jadx.gui.ui.dialog.ADBDialog;
import jadx.gui.ui.dialog.AboutDialog;
import jadx.gui.ui.dialog.CharsetDialog;
import jadx.gui.ui.dialog.ExceptionDialog;
import jadx.gui.ui.dialog.GotoAddressDialog;
import jadx.gui.ui.dialog.LogViewerDialog;
import jadx.gui.ui.dialog.SearchDialog;
@@ -266,6 +267,7 @@ public class MainWindow extends JFrame {
this.editorThemeManager = new EditorThemeManager(settings);
JadxEventQueue.register();
JadxExceptionHandler.register(this);
resetCache();
initUI();
this.editorSyncManager = new EditorSyncManager(this, tabbedPane);