Changeset 10585 in josm for trunk


Ignore:
Timestamp:
2016-07-22T22:21:19+02:00 (4 years ago)
Author:
Don-vip
Message:

see #11390, fix #12905 - Add more information to the bug report (patch by michael2402) - gsoc-core - requires java 8

Location:
trunk/src/org/openstreetmap/josm/tools
Files:
2 added
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/tools/bugreport/BugReport.java

    r10412 r10585  
    11// License: GPL. For details, see LICENSE file.
    22package org.openstreetmap.josm.tools.bugreport;
     3
     4import java.io.PrintWriter;
     5import java.io.StringWriter;
     6import java.util.concurrent.CopyOnWriteArrayList;
     7
     8import org.openstreetmap.josm.actions.ShowStatusReportAction;
    39
    410/**
     
    2026 * try {
    2127 *   ... your code ...
    22  * } catch (Throwable t) {
     28 * } catch (RuntimeException t) {
    2329 *   throw BugReport.intercept(t).put("id", id).put("tag", tag);
    2430 * }
     
    3238 */
    3339public final 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
    3446    /**
    3547     * Create a new bug report
    3648     * @param e The {@link ReportedException} to use. No more data should be added after creating the report.
    3749     */
    38     private BugReport(ReportedException e) {
    39         // TODO: Use this class to create the bug report.
     50    BugReport(ReportedException e) {
     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 10585
     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 10585
     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 10585
     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 10585
     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 10585
     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 10585
     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 10585
     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 10585
     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 10585
     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));
    40153    }
    41154
     
    75188        return "?";
    76189    }
     190
     191    /**
     192     * A listener that listens to changes to this report.
     193     * @author Michael Zangl
     194     * @since 10585
     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    }
    77204}
  • trunk/src/org/openstreetmap/josm/tools/bugreport/BugReportExceptionHandler.java

    r10420 r10585  
    197197
    198198    static JPanel buildPanel(final Throwable e) {
    199         StringWriter stack = new StringWriter();
    200         PrintWriter writer = new PrintWriter(stack);
     199        DebugTextDisplay textarea;
    201200        if (e instanceof ReportedException) {
    202201            // Temporary!
    203             ((ReportedException) e).printReportDataTo(writer);
    204             ((ReportedException) e).printReportStackTo(writer);
     202            textarea = new DebugTextDisplay(new BugReport((ReportedException) e));
    205203        } else {
    206             e.printStackTrace(writer);
    207         }
    208 
    209         String text = ShowStatusReportAction.getReportHeader() + stack.getBuffer().toString();
    210         text = text.replaceAll("\r", "");
     204            StringWriter stack = new StringWriter();
     205            e.printStackTrace(new PrintWriter(stack));
     206            textarea = new DebugTextDisplay(ShowStatusReportAction.getReportHeader() + stack.getBuffer().toString());
     207        }
    211208
    212209        JPanel p = new JPanel(new GridBagLayout());
     
    220217                        "file a bug report in our bugtracker using this link:")),
    221218                        GBC.eol().fill(GridBagConstraints.HORIZONTAL));
    222         p.add(new JButton(new ReportBugAction(text)), GBC.eop().insets(8, 0, 0, 0));
     219        p.add(new JButton(new ReportBugAction(textarea.getCodeText())), GBC.eop().insets(8, 0, 0, 0));
    223220        p.add(new JMultilineLabel(
    224221                tr("There the error information provided below should already be " +
     
    230227                        "below at this URL:")), GBC.eol().fill(GridBagConstraints.HORIZONTAL));
    231228        p.add(new UrlLabel(Main.getJOSMWebsite()+"/newticket", 2), GBC.eop().insets(8, 0, 0, 0));
    232 
    233         // Wiki formatting for manual copy-paste
    234         DebugTextDisplay textarea = new DebugTextDisplay(text);
    235229
    236230        if (textarea.copyToClippboard()) {
  • trunk/src/org/openstreetmap/josm/tools/bugreport/DebugTextDisplay.java

    r10306 r10585  
    1515 */
    1616public class DebugTextDisplay extends JScrollPane {
    17     private final String text;
     17    private static final String CODE_PATTERN = "{{{%n%s%n}}}";
     18    private String text;
     19    private JosmTextArea textArea;
    1820
    1921    /**
    20      * Creates a new text are with the fixed text
    21      * @param textToDisplay The text to display.
     22     * Creates a new text area.
     23     * @since 10585
    2224     */
    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();
    2627        textArea.setCaretPosition(0);
    2728        textArea.setEditable(false);
     
    3132
    3233    /**
    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     * @since 10585
     46     */
     47    public DebugTextDisplay(BugReport report) {
     48        this();
     49        setCodeText(report.getReportText());
     50        report.addChangeListener(e -> setCodeText(report.getReportText()));
     51    }
     52
     53    /**
     54     * Sets the text that should be displayed in this view.
     55     * @param textToDisplay The text
     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.
    3464     * @return <code>true</code> if copy was successful
    3565     */
    3666    public boolean copyToClippboard() {
    37         return Utils.copyToClipboard(text);
     67        return Utils.copyToClipboard(String.format(CODE_PATTERN, text));
     68    }
     69
     70    /**
     71     * Gets the text this are displays, without the code tag.
     72     * @return The stripped text set by {@link #setCodeText(String)}
     73     * @since 10585
     74     */
     75    public String getCodeText() {
     76        return text;
    3877    }
    3978}
  • trunk/src/org/openstreetmap/josm/tools/bugreport/ReportedException.java

    r10306 r10585  
    33
    44import java.io.PrintWriter;
     5import java.io.Serializable;
     6import java.lang.reflect.InvocationTargetException;
    57import java.util.ArrayList;
    68import java.util.Arrays;
    79import java.util.Collection;
    810import java.util.Collections;
     11import java.util.ConcurrentModificationException;
    912import java.util.IdentityHashMap;
     13import java.util.Iterator;
    1014import java.util.LinkedList;
    1115import java.util.Map;
    1216import java.util.Map.Entry;
     17import java.util.NoSuchElementException;
    1318import java.util.Set;
    1419
    1520import org.openstreetmap.josm.Main;
     21import org.openstreetmap.josm.tools.StreamUtils;
    1622
    1723/**
     
    2531 */
    2632public class ReportedException extends RuntimeException {
     33    /**
     34     * How many entries of a collection to include in the bug report.
     35     */
    2736    private static final int MAX_COLLECTION_ENTRIES = 30;
    28     /**
    29      *
    30      */
     37
    3138    private static final long serialVersionUID = 737333873766201033L;
     39
    3240    /**
    3341     * We capture all stack traces on exception creation. This allows us to trace synchonization problems better. We cannot be really sure what
     
    3543     */
    3644    private final transient Map<Thread, StackTraceElement[]> allStackTraces;
    37     private final transient LinkedList<Section> sections = new LinkedList<>();
     45    private final LinkedList<Section> sections = new LinkedList<>();
    3846    private final transient Thread caughtOnThread;
    3947    private final Throwable exception;
     
    144152        }
    145153
    146         Set<Throwable> dejaVu = Collections.newSetFromMap(new IdentityHashMap<Throwable, Boolean>());
    147         return hasSameStackTrace(dejaVu, this.exception, e.exception);
    148     }
    149 
    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.
     154        return hasSameStackTrace(new CauseTraceIterator(), e.exception);
     155    }
     156
     157    private static boolean hasSameStackTrace(CauseTraceIterator causeTraceIterator, Throwable e2) {
     158        if (!causeTraceIterator.hasNext()) {
     159            // all done.
    153160            return true;
    154161        }
    155         dejaVu.add(e1);
    156 
     162        Throwable e1 = causeTraceIterator.next();
    157163        StackTraceElement[] t1 = e1.getStackTrace();
    158164        StackTraceElement[] t2 = e2.getStackTrace();
     
    167173            return false;
    168174        } else if (c1 != null) {
    169             return hasSameStackTrace(dejaVu, c1, c2);
     175            return hasSameStackTrace(causeTraceIterator, c2);
    170176        } else {
    171177            return true;
     
    228234    }
    229235
    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 10585
     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 10585
     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
    231284        private final String key;
    232285        private final String value;
     
    249302    }
    250303
    251     private static class Section {
     304    private static class Section implements Serializable {
     305
     306        private static final long serialVersionUID = 1L;
    252307
    253308        private final String sectionName;
Note: See TracChangeset for help on using the changeset viewer.