Ticket #12905: patch-bug-report-generate-report.patch
File patch-bug-report-generate-report.patch, 20.0 KB (added by , 9 years ago) |
---|
-
new file src/org/openstreetmap/josm/tools/StreamUtils.java
diff --git a/src/org/openstreetmap/josm/tools/StreamUtils.java b/src/org/openstreetmap/josm/tools/StreamUtils.java new file mode 100644 index 0000000..ead9c1c
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.tools; 3 4 import java.util.Iterator; 5 import java.util.Spliterator; 6 import java.util.Spliterators; 7 import java.util.stream.Stream; 8 import java.util.stream.StreamSupport; 9 10 /** 11 * Utility methods for streams. 12 * @author Michael Zangl 13 * @since xxx 14 */ 15 public final class StreamUtils { 16 17 /** 18 * Untility class 19 */ 20 private StreamUtils() {} 21 22 /** 23 * Convert an iterator to a stream. 24 * @param <T> The element type to iterate over 25 * @param iterator The iterator 26 * @return The stream of for that iterator. 27 */ 28 public static <T> Stream<T> toStream(Iterator<? extends T> iterator) { 29 Spliterator<T> spliterator = Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED); 30 return StreamSupport.stream(spliterator, false); 31 } 32 } -
src/org/openstreetmap/josm/tools/bugreport/BugReport.java
diff --git a/src/org/openstreetmap/josm/tools/bugreport/BugReport.java b/src/org/openstreetmap/josm/tools/bugreport/BugReport.java index 16758bf..a2f2287 100644
a b 1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.tools.bugreport; 3 3 4 import java.io.PrintWriter; 5 import java.io.StringWriter; 6 import java.util.concurrent.CopyOnWriteArrayList; 7 8 import org.openstreetmap.josm.actions.ShowStatusReportAction; 9 4 10 /** 5 11 * This class contains utility methods to create and handle a bug report. 6 12 * <p> … … package org.openstreetmap.josm.tools.bugreport; 19 25 * String tag = "..."; 20 26 * try { 21 27 * ... your code ... 22 * } catch ( Throwablet) {28 * } catch (RuntimeException t) { 23 29 * throw BugReport.intercept(t).put("id", id).put("tag", tag); 24 30 * } 25 31 * </pre> … … package org.openstreetmap.josm.tools.bugreport; 31 37 * @since 10285 32 38 */ 33 39 public class BugReport { 40 private boolean includeStatusReport = true; 41 private boolean includeData = true; 42 private boolean includeAllStackTraces; 43 private ReportedException exception; 44 private final CopyOnWriteArrayList<BugReportListener> listeners = new CopyOnWriteArrayList<>(); 45 34 46 /** 35 47 * Create a new bug report 36 48 * @param e The {@link ReportedException} to use. No more data should be added after creating the report. 37 49 */ 38 50 public BugReport(ReportedException e) { 39 // TODO: Use this class to create the bug report. 51 this.exception = e; 52 includeAllStackTraces = e.mayHaveConcurrentSource(); 53 } 54 55 /** 56 * Get if this report should include a system status report 57 * @return <code>true</code> to include it. 58 * @since xxx 59 */ 60 public boolean getIncludeStatusReport() { 61 return includeStatusReport; 62 } 63 64 /** 65 * Set if this report should include a system status report 66 * @param includeStatusReport if the status report should be included 67 * @since xxx 68 */ 69 public void setIncludeStatusReport(boolean includeStatusReport) { 70 this.includeStatusReport = includeStatusReport; 71 fireChange(); 72 } 73 74 /** 75 * Get if this report should include the data that was traced. 76 * @return <code>true</code> to include it. 77 * @since xxx 78 */ 79 public boolean getIncludeData() { 80 return includeData; 81 } 82 83 /** 84 * Set if this report should include the data that was traced. 85 * @param includeData if data should be included 86 * @since xxx 87 */ 88 public void setIncludeData(boolean includeData) { 89 this.includeData = includeData; 90 fireChange(); 91 } 92 93 /** 94 * Get if this report should include the stack traces for all other threads. 95 * @return <code>true</code> to include it. 96 * @since xxx 97 */ 98 public boolean getIncludeAllStackTraces() { 99 return includeAllStackTraces; 100 } 101 102 /** 103 * Sets if this report should include the stack traces for all other threads. 104 * @param includeAllStackTraces if all stack traces should be included 105 * @since xxx 106 */ 107 public void setIncludeAllStackTraces(boolean includeAllStackTraces) { 108 this.includeAllStackTraces = includeAllStackTraces; 109 fireChange(); 110 } 111 112 /** 113 * Gets the full string that should be send as error report. 114 * @return The string. 115 * @since xxx 116 */ 117 public String getReportText() { 118 StringWriter stringWriter = new StringWriter(); 119 PrintWriter out = new PrintWriter(stringWriter); 120 if (getIncludeStatusReport()) { 121 out.println(ShowStatusReportAction.getReportHeader()); 122 } 123 if (getIncludeData()) { 124 exception.printReportDataTo(out); 125 } 126 exception.printReportStackTo(out); 127 if (getIncludeAllStackTraces()) { 128 exception.printReportThreadsTo(out); 129 } 130 return stringWriter.toString().replaceAll("\r", ""); 131 } 132 133 /** 134 * Add a new change listener. 135 * @param listener The listener 136 * @since xxx 137 */ 138 public void addChangeListener(BugReportListener listener) { 139 listeners.add(listener); 140 } 141 142 /** 143 * Remove a change listener. 144 * @param listener The listener 145 * @since xxx 146 */ 147 public void removeChangeListener(BugReportListener listener) { 148 listeners.remove(listener); 149 } 150 151 private void fireChange() { 152 listeners.stream().forEach(l -> l.bugReportChanged(this)); 40 153 } 41 154 42 155 /** … … public class BugReport { 74 187 } 75 188 return "?"; 76 189 } 190 191 /** 192 * A listener that listens to changes to this report. 193 * @author Michael Zangl 194 * @since xxx 195 */ 196 @FunctionalInterface 197 public interface BugReportListener { 198 /** 199 * Called whenever this bug report was changed, e.g. the data to be included in it. 200 * @param report The report that was changed. 201 */ 202 void bugReportChanged(BugReport report); 203 } 77 204 } -
src/org/openstreetmap/josm/tools/bugreport/BugReportExceptionHandler.java
diff --git a/src/org/openstreetmap/josm/tools/bugreport/BugReportExceptionHandler.java b/src/org/openstreetmap/josm/tools/bugreport/BugReportExceptionHandler.java index 054eca0..c31c15c 100644
a b public final class BugReportExceptionHandler implements Thread.UncaughtException 102 102 String.valueOf(josmVersion), String.valueOf(latestVersion)); 103 103 } 104 104 } catch (IOException | NumberFormatException ex) { 105 Main.warn("Unable to detect latest version of JOSM: "+ex.getMessage() );105 Main.warn("Unable to detect latest version of JOSM: "+ex.getMessage(), ex); 106 106 } 107 107 } 108 108 // Build panel … … public final class BugReportExceptionHandler implements Thread.UncaughtException 136 136 try { 137 137 Main.platform.openUrl(Main.getJOSMWebsite()); 138 138 } catch (IOException ex) { 139 Main.warn("Unable to access JOSM website: "+ex.getMessage() );139 Main.warn("Unable to access JOSM website: "+ex.getMessage(), ex); 140 140 } 141 141 } else { 142 142 // "Report bug" … … public final class BugReportExceptionHandler implements Thread.UncaughtException 196 196 } 197 197 198 198 static JPanel buildPanel(final Throwable e) { 199 StringWriter stack = new StringWriter(); 200 PrintWriter writer = new PrintWriter(stack); 199 DebugTextDisplay textarea; 201 200 if (e instanceof ReportedException) { 202 201 // Temporary! 203 ((ReportedException) e).printReportDataTo(writer); 204 ((ReportedException) e).printReportStackTo(writer); 202 textarea = new DebugTextDisplay(new BugReport((ReportedException) e)); 205 203 } else { 204 StringWriter stack = new StringWriter(); 205 PrintWriter writer = new PrintWriter(stack); 206 206 e.printStackTrace(writer); 207 String text = ShowStatusReportAction.getReportHeader() + stack.getBuffer().toString(); 208 textarea = new DebugTextDisplay(text); 207 209 } 208 210 209 String text = ShowStatusReportAction.getReportHeader() + stack.getBuffer().toString();210 text = text.replaceAll("\r", "");211 212 211 JPanel p = new JPanel(new GridBagLayout()); 213 212 p.add(new JMultilineLabel( 214 213 tr("You have encountered an error in JOSM. Before you file a bug report " + … … public final class BugReportExceptionHandler implements Thread.UncaughtException 219 218 tr("You should also update your plugins. If neither of those help please " + 220 219 "file a bug report in our bugtracker using this link:")), 221 220 GBC.eol().fill(GridBagConstraints.HORIZONTAL)); 222 p.add(new JButton(new ReportBugAction(text )), GBC.eop().insets(8, 0, 0, 0));221 p.add(new JButton(new ReportBugAction(textarea.getCodeText())), GBC.eop().insets(8, 0, 0, 0)); 223 222 p.add(new JMultilineLabel( 224 223 tr("There the error information provided below should already be " + 225 224 "filled in for you. Please include information on how to reproduce " + … … public final class BugReportExceptionHandler implements Thread.UncaughtException 230 229 "below at this URL:")), GBC.eol().fill(GridBagConstraints.HORIZONTAL)); 231 230 p.add(new UrlLabel(Main.getJOSMWebsite()+"/newticket", 2), GBC.eop().insets(8, 0, 0, 0)); 232 231 233 // Wiki formatting for manual copy-paste234 DebugTextDisplay textarea = new DebugTextDisplay(text);235 236 232 if (textarea.copyToClippboard()) { 237 233 p.add(new JLabel(tr("(The text has already been copied to your clipboard.)")), 238 234 GBC.eop().fill(GridBagConstraints.HORIZONTAL)); -
new file src/org/openstreetmap/josm/tools/bugreport/BugReportSettingsPanel.java
diff --git a/src/org/openstreetmap/josm/tools/bugreport/BugReportSettingsPanel.java b/src/org/openstreetmap/josm/tools/bugreport/BugReportSettingsPanel.java new file mode 100644 index 0000000..4881906
- + 1 // License: GPL. For details, see LICENSE file. 2 package org.openstreetmap.josm.tools.bugreport; 3 4 import static org.openstreetmap.josm.tools.I18n.tr; 5 6 import javax.swing.BoxLayout; 7 import javax.swing.JCheckBox; 8 import javax.swing.JPanel; 9 10 /** 11 * This panel displays the settings that can be changed before submitting a bug report to the web page. 12 * @author Michael Zangl 13 * @since xxx 14 */ 15 public class BugReportSettingsPanel extends JPanel { 16 /** 17 * Creates the new settings panel. 18 * @param report The report this panel should influence. 19 */ 20 public BugReportSettingsPanel(BugReport report) { 21 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 22 23 JCheckBox statusReport = new JCheckBox(tr("Include the system status report.")); 24 statusReport.setSelected(report.getIncludeStatusReport()); 25 statusReport.addChangeListener(e -> report.setIncludeStatusReport(statusReport.isSelected())); 26 add(statusReport); 27 28 JCheckBox data = new JCheckBox(tr("Include information about the data that was worked on.")); 29 data.setSelected(report.getIncludeData()); 30 data.addChangeListener(e -> report.setIncludeData(data.isSelected())); 31 add(data); 32 33 JCheckBox allStackTraces = new JCheckBox(tr("Include all stack traces.")); 34 allStackTraces.setSelected(report.getIncludeAllStackTraces()); 35 allStackTraces.addChangeListener(e -> report.setIncludeAllStackTraces(allStackTraces.isSelected())); 36 add(allStackTraces); 37 } 38 } -
src/org/openstreetmap/josm/tools/bugreport/DebugTextDisplay.java
diff --git a/src/org/openstreetmap/josm/tools/bugreport/DebugTextDisplay.java b/src/org/openstreetmap/josm/tools/bugreport/DebugTextDisplay.java index a0e2841..dffe029 100644
a b import org.openstreetmap.josm.tools.Utils; 14 14 * @since 10055 15 15 */ 16 16 public class DebugTextDisplay extends JScrollPane { 17 private static final String CODE_PATTERN = "{{{%n%s%n}}}"; 17 18 private String text; 19 private JosmTextArea textArea; 18 20 19 21 /** 20 * Creates a new text are with the fixed text21 * @ param textToDisplay The text to display.22 * Creates a new text area. 23 * @since xxx 22 24 */ 23 public DebugTextDisplay(String textToDisplay) { 24 text = "{{{\n" + Utils.strip(textToDisplay) + "\n}}}"; 25 JosmTextArea textArea = new JosmTextArea(text); 25 private DebugTextDisplay() { 26 textArea = new JosmTextArea(); 26 27 textArea.setCaretPosition(0); 27 28 textArea.setEditable(false); 28 29 setViewportView(textArea); … … public class DebugTextDisplay extends JScrollPane { 30 31 } 31 32 32 33 /** 33 * Copies the debug text to the clippboard. 34 * Creates a new text area with an inital text to display 35 * @param textToDisplay The text to display. 36 */ 37 public DebugTextDisplay(String textToDisplay) { 38 this(); 39 setCodeText(textToDisplay); 40 } 41 42 /** 43 * Creates a new text area that displays the bug report data 44 * @param report The bug report data to display. 45 */ 46 public DebugTextDisplay(BugReport report) { 47 this(); 48 setCodeText(report.getReportText()); 49 report.addChangeListener(e -> setCodeText(report.getReportText())); 50 } 51 52 /** 53 * Sets the text that should be displayed in this view. 54 * @param textToDisplay The text 55 * @since xxx 56 */ 57 private void setCodeText(String textToDisplay) { 58 text = Utils.strip(textToDisplay).replaceAll("\r", ""); 59 textArea.setText(String.format(CODE_PATTERN, text)); 60 } 61 62 /** 63 * Copies the debug text to the clippboard. This includes the code tags for trac. 34 64 * @return <code>true</code> if copy was successful 65 * @since 10055 35 66 */ 36 67 public boolean copyToClippboard() { 37 return Utils.copyToClipboard(text); 68 return Utils.copyToClipboard(String.format(CODE_PATTERN, text)); 69 } 70 71 /** 72 * Gets the text this are displays, without the code tag. 73 * @return The stripped text set by {@link #setCodeText(String)} 74 * @since xxx 75 */ 76 public String getCodeText() { 77 return text; 38 78 } 39 79 } -
src/org/openstreetmap/josm/tools/bugreport/ReportedException.java
diff --git a/src/org/openstreetmap/josm/tools/bugreport/ReportedException.java b/src/org/openstreetmap/josm/tools/bugreport/ReportedException.java index 41bf1ba..054e666 100644
a b 2 2 package org.openstreetmap.josm.tools.bugreport; 3 3 4 4 import java.io.PrintWriter; 5 import java.io.Serializable; 6 import java.lang.reflect.InvocationTargetException; 5 7 import java.util.ArrayList; 6 8 import java.util.Arrays; 7 9 import java.util.Collection; 8 10 import java.util.Collections; 11 import java.util.ConcurrentModificationException; 9 12 import java.util.IdentityHashMap; 13 import java.util.Iterator; 10 14 import java.util.LinkedList; 11 15 import java.util.Map; 12 16 import java.util.Map.Entry; 17 import java.util.NoSuchElementException; 13 18 import java.util.Set; 14 19 15 20 import org.openstreetmap.josm.Main; 21 import org.openstreetmap.josm.tools.StreamUtils; 16 22 17 23 /** 18 24 * This is a special exception that cannot be directly thrown. … … import org.openstreetmap.josm.Main; 24 30 * @since 10285 25 31 */ 26 32 public class ReportedException extends RuntimeException { 27 private static final int MAX_COLLECTION_ENTRIES = 30;28 33 /** 29 * 34 * How many entries of a collection to include in the bug report. 30 35 */ 36 private static final int MAX_COLLECTION_ENTRIES = 30; 37 31 38 private static final long serialVersionUID = 737333873766201033L; 39 32 40 /** 33 41 * We capture all stack traces on exception creation. This allows us to trace synchonization problems better. We cannot be really sure what 34 42 * happened but we at least see which threads 35 43 */ 36 44 private final transient Map<Thread, StackTraceElement[]> allStackTraces; 37 private final transientLinkedList<Section> sections = new LinkedList<>();45 private final LinkedList<Section> sections = new LinkedList<>(); 38 46 private final transient Thread caughtOnThread; 39 47 private final Throwable exception; 40 48 private String methodWarningFrom; … … public class ReportedException extends RuntimeException { 143 151 return false; 144 152 } 145 153 146 Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>()); 147 return hasSameStackTrace(dejaVu, this.exception, e.exception); 154 return hasSameStackTrace(new CauseTraceIterator(), e.exception); 148 155 } 149 156 150 private static boolean hasSameStackTrace( Set<Throwable> dejaVu, Throwable e1, Throwable e2) {151 if ( dejaVu.contains(e1)) {152 // cycle. If it was the same until here, we assume both have that cycle.157 private static boolean hasSameStackTrace(CauseTraceIterator causeTraceIterator, Throwable e2) { 158 if (!causeTraceIterator.hasNext()) { 159 // all done. 153 160 return true; 154 161 } 155 dejaVu.add(e1); 156 162 Throwable e1 = causeTraceIterator.next(); 157 163 StackTraceElement[] t1 = e1.getStackTrace(); 158 164 StackTraceElement[] t2 = e2.getStackTrace(); 159 165 … … public class ReportedException extends RuntimeException { 166 172 if ((c1 == null) != (c2 == null)) { 167 173 return false; 168 174 } else if (c1 != null) { 169 return hasSameStackTrace( dejaVu, c1, c2);175 return hasSameStackTrace(causeTraceIterator, c2); 170 176 } else { 171 177 return true; 172 178 } … … public class ReportedException extends RuntimeException { 227 233 .toString(); 228 234 } 229 235 230 private static class SectionEntry { 236 237 /** 238 * Check if this exception may be caused by a threading issue. 239 * @return <code>true</code> if it is. 240 * @since xxx 241 */ 242 public boolean mayHaveConcurrentSource() { 243 return StreamUtils.toStream(new CauseTraceIterator()) 244 .anyMatch(t -> t instanceof ConcurrentModificationException || t instanceof InvocationTargetException); 245 } 246 247 /** 248 * Iterates over the causes for this exception. Ignores cycles and aborts iteration then. 249 * @author Michal Zangl 250 * @since xxx 251 */ 252 private final class CauseTraceIterator implements Iterator<Throwable> { 253 private Throwable current = exception; 254 private final Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>()); 255 256 @Override 257 public boolean hasNext() { 258 return current != null; 259 } 260 261 @Override 262 public Throwable next() { 263 if (!hasNext()) { 264 throw new NoSuchElementException(); 265 } 266 Throwable toReturn = current; 267 advance(); 268 return toReturn; 269 } 270 271 private void advance() { 272 dejaVu.add(current); 273 current = current.getCause(); 274 if (current != null && dejaVu.contains(current)) { 275 current = null; 276 } 277 } 278 } 279 280 private static class SectionEntry implements Serializable { 281 282 private static final long serialVersionUID = 1L; 283 231 284 private final String key; 232 285 private final String value; 233 286 … … public class ReportedException extends RuntimeException { 248 301 } 249 302 } 250 303 251 private static class Section { 304 private static class Section implements Serializable { 305 306 private static final long serialVersionUID = 1L; 252 307 253 308 private String sectionName; 254 309 private ArrayList<SectionEntry> entries = new ArrayList<>();