Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dialog for showing exception details and creating an GitHub issue #1399

Merged
merged 5 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion jadx-core/src/main/java/jadx/core/Jadx.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,14 @@ public static List<IDexTreeVisitor> getPassesList(JadxArgs args) {
return passes;
}

public static final String VERSION_DEV = "dev";

private static String version;

public static String getVersion() {
if (version != null) {
return version;
}
try {
ClassLoader classLoader = Jadx.class.getClassLoader();
if (classLoader != null) {
Expand All @@ -188,6 +195,7 @@ public static String getVersion() {
Manifest manifest = new Manifest(is);
String ver = manifest.getMainAttributes().getValue("jadx-version");
if (ver != null) {
version = ver;
return ver;
}
}
Expand All @@ -196,6 +204,6 @@ public static String getVersion() {
} catch (Exception e) {
LOG.error("Can't get manifest file", e);
}
return "dev";
return VERSION_DEV;
}
}
3 changes: 2 additions & 1 deletion jadx-gui/src/main/java/jadx/gui/JadxGUI.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import jadx.cli.LogHelper;
import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.JadxSettingsAdapter;
import jadx.gui.ui.ExceptionDialog;
import jadx.gui.ui.MainWindow;
import jadx.gui.utils.LafManager;
import jadx.gui.utils.NLS;
Expand All @@ -29,7 +30,7 @@ public static void main(String[] args) {
printSystemInfo();
LafManager.init(settings);
NLS.setLocale(settings.getLangLocale());

ExceptionDialog.registerUncaughtExceptionHandler();
SwingUtilities.invokeLater(new MainWindow(settings)::init);
} catch (Exception e) {
LOG.error("Error: {}", e.getMessage(), e);
Expand Down
2 changes: 1 addition & 1 deletion jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public void save() {
buildGson(basePath).toJson(data, writer);
saved = true;
} catch (Exception e) {
LOG.error("Error saving project", e);
throw new RuntimeException("Error saving project", e);
}
}
}
Expand Down
184 changes: 184 additions & 0 deletions jadx-gui/src/main/java/jadx/gui/ui/ExceptionDialog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package jadx.gui.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
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;
import java.lang.management.ManagementFactory;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.KeyStroke;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jadx.api.JadxDecompiler;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.gui.settings.JadxSettings;
import jadx.gui.settings.JadxSettingsAdapter;
import jadx.gui.utils.LafManager;
import jadx.gui.utils.Link;

public class ExceptionDialog extends JDialog {

private static final Logger LOG = LoggerFactory.getLogger(ExceptionDialog.class);

private static final String FMT_DETAIL_LENGTH = "-13";

public static void registerUncaughtExceptionHandler() {
Thread.setDefaultUncaughtExceptionHandler((thread, ex) -> showExceptionDialog(thread, ex));
}

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");
this.getContentPane().setLayout(new BorderLayout());
JPanel titlePanel = new JPanel(new GridBagLayout());
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.CENTER;
c.gridx = 0;
c.weightx = 1.0;
c.insets = new Insets(2, 5, 5, 5);
JLabel titleLabel = new JLabel("<html><h1>An error occurred</h1><p>Jadx encountered an unexpected error.</p></html>");

Map<String, String> details = new LinkedHashMap<>();
details.put("Jadx version", JadxDecompiler.getVersion());
details.put("Java version", System.getProperty("java.version", "?"));
details.put("Java VM", String.format("%s %s", System.getProperty("java.vm.vendor", "?"),
System.getProperty("java.vm.name", "?")));
details.put("Platform", String.format("%s (%s %s)", System.getProperty("os.name", "?"),
System.getProperty("os.version", "?"), System.getProperty("os.arch", "?")));
Runtime runtime = Runtime.getRuntime();
details.put("Max heap size", String.format("%d MB", runtime.maxMemory() / (1024 * 1024)));

try {
// TODO: Use ProcessHandle.current().info().commandLine() once min Java is 9+
List<String> args = ManagementFactory.getRuntimeMXBean().getInputArguments();
details.put("Program args", args.stream().collect(Collectors.joining(" ")));
} catch (Throwable t) {
LOG.error("failed to get program arguments", t);
}

StringWriter stackTraceWriter = new StringWriter(1024);
ex.printStackTrace(new PrintWriter(stackTraceWriter));
final String stackTrace = stackTraceWriter.toString();

String issueTitle;
try {
issueTitle = URLEncoder.encode(ex.toString(), StandardCharsets.UTF_8.toString());
} catch (Exception e) {
LOG.error("URL encoding of title failed", e);
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";

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 issueBody;
try {
issueBody = URLEncoder.encode(body, StandardCharsets.UTF_8.toString());
} catch (Exception e) {
LOG.error("URL encoding of body failed", e);
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);
JTextArea messageArea = new JTextArea();
messageArea.setEditable(false);
messageArea.setFont(new Font(Font.MONOSPACED, Font.PLAIN, 12));
messageArea.setForeground(Color.BLACK);
messageArea.setBackground(Color.WHITE);

StringBuilder detailsTextBuilder = new StringBuilder();
details.forEach((key, value) -> detailsTextBuilder.append(String.format("%" + FMT_DETAIL_LENGTH + "s: %s\n", key, value)));

messageArea.setText(detailsTextBuilder.toString() + "\n" + stackTrace);

JPanel buttonPanel = new JPanel();
JButton exitButton = new JButton("Terminate Jadx");
exitButton.addActionListener((event) -> System.exit(1));
buttonPanel.add(exitButton);
JButton closeButton = new JButton("Go back to Jadx");
closeButton.addActionListener((event) -> setVisible(false));
buttonPanel.add(closeButton);
JScrollPane messageAreaScroller = new JScrollPane(messageArea);
messageAreaScroller.setMinimumSize(new Dimension(600, 400));
messageAreaScroller.setPreferredSize(new Dimension(600, 400));

this.add(titlePanel, BorderLayout.NORTH);
this.add(messageAreaScroller, BorderLayout.CENTER);
this.add(buttonPanel, BorderLayout.SOUTH);
this.pack();

javax.swing.SwingUtilities.invokeLater(() -> messageAreaScroller.getVerticalScrollBar().setValue(0));

final Toolkit toolkit = Toolkit.getDefaultToolkit();
final Dimension screenSize = toolkit.getScreenSize();
final int x = (screenSize.width - getWidth()) / 2;
final int y = (screenSize.height - getHeight()) / 2;
setLocation(x, y);

getRootPane().registerKeyboardAction((event) -> setVisible(false),
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0),
JComponent.WHEN_IN_FOCUSED_WINDOW);

this.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
this.setVisible(true);
}

public static void throwTestException() {
try {
throw new RuntimeException("Inner exception message");
} catch (Exception e) {
throw new JadxRuntimeException("Outer exception message", e);
}
}

public static void showTestExceptionDialog() {
try {
throwTestException();
} catch (Exception e) {
showExceptionDialog(Thread.currentThread(), e);
}
}

public static void main(String[] args) {
JadxSettings settings = JadxSettingsAdapter.load();
LafManager.init(settings);
showTestExceptionDialog();
}
}
10 changes: 10 additions & 0 deletions jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
import jadx.api.JadxArgs;
import jadx.api.JavaNode;
import jadx.api.ResourceFile;
import jadx.core.Jadx;
import jadx.core.utils.StringUtils;
import jadx.core.utils.Utils;
import jadx.core.utils.files.FileUtils;
Expand Down Expand Up @@ -824,6 +825,7 @@ public void syncWithEditor() {
}

private void initMenuAndToolbar() {
final boolean devVersion = (Jadx.VERSION_DEV.equals(Jadx.getVersion()));
Action openAction = new AbstractAction(NLS.str("file.open_action"), ICON_OPEN) {
@Override
public void actionPerformed(ActionEvent e) {
Expand Down Expand Up @@ -1085,6 +1087,14 @@ public void actionPerformed(ActionEvent e) {
JMenu help = new JMenu(NLS.str("menu.help"));
help.setMnemonic(KeyEvent.VK_H);
help.add(logAction);
if (devVersion) {
help.add(new AbstractAction("Show sample error report") {
@Override
public void actionPerformed(ActionEvent e) {
ExceptionDialog.throwTestException();
}
});
}
help.add(aboutAction);

JMenuBar menuBar = new JMenuBar();
Expand Down