package com.google.dart.tools.ui.watchdog; import com.google.dart.tools.core.utilities.io.PrintStringWriter; import com.google.dart.tools.ui.instrumentation.UIInstrumentationBuilder; import com.google.dart.tools.ui.watchdog.SwtEventMonitorThread.StackTraceInfo; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * An internal data holder for event data, including the timestamp and sample stack traces. */ class Event { /** Time stamp of when the Event started in milliseconds. */ private long startTimeMillis; /** Duration of the Event, in milliseconds, or 0 if duration is unknown */ private long durationMillis; /** The list of individual traces created when sample is called. */ private List<Trace> traces; /** True if this event shouldn't be reported to the logging server. */ private boolean excludeEvent; /** The name of the user's operation that is blocked */ private final String blockedOperation; /** The reason why the user's operation is blocked */ private final String blockedReason; private List<String> filterOutTraces; private long stopTimeMillis; /** Create a new Event which started at the timestamp in milliseconds */ public Event(long timestamp) { this(timestamp, null, null); } /** Create a new Event which started at timestamp and lasted for duration microseconds. */ public Event(long timestamp, long durationMicros) { this.startTimeMillis = timestamp; this.durationMillis = durationMicros; this.blockedOperation = null; this.blockedReason = null; traces = new ArrayList<Trace>(); } /** Create a new Event which started at the timestamp in milliseconds */ public Event(long timestamp, String operation, String reason) { this.startTimeMillis = timestamp; this.durationMillis = 0; this.blockedOperation = operation == null ? "" : operation; this.blockedReason = reason == null ? "" : reason; traces = new ArrayList<Trace>(); } public void addSamples(StackTraceInfo[] stackTraces, int numStacks, Thread displayThread) { long lastCaptureTime = 0; for (int i = 0; i < numStacks && !excludeEvent; ++i) { StackTraceElement[] stack = stackTraces[i].stacks.get(displayThread); if (stack != null) { long captureTime = stackTraces[i].captureTime.getTime(); long duration = lastCaptureTime != 0 ? 1000 * (captureTime - lastCaptureTime) : 0; lastCaptureTime = captureTime; excludeEvent = shouldExclude4xEvent(stack); traces.add(new Trace(this, captureTime, duration, stack)); } } } public String getBlockedOperation() { return blockedOperation; } public String getBlockedReason() { return blockedReason; } public long getDurationMillis() { return durationMillis; } public long getStartTimeMillis() { return startTimeMillis; } public Trace[] getTraces() { return traces.toArray(new Trace[traces.size()]); } public int getTracesCount() { return traces.size(); } public boolean isEventExcluded() { return excludeEvent; } /** * Records a stack trace with the current event if it matches our criteria, e.g. not a modal * dialog. */ public void sample(long traceTime, StackTraceElement[] trace) { if (trace != null) { excludeEvent = shouldExclude3xEvent(trace); traces.add(new Trace(this, traceTime, durationMillis, trace)); } } /** * Captures a stack trace from the main loop and records it with the current event if it matches * our criteria, e.g. not a modal dialog. */ public void sample(Thread eventThread) { Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces(); StackTraceElement[] trace = allStackTraces.get(eventThread); long traceTime = System.currentTimeMillis(); sample(traceTime, trace); } public void setDurationMillis(long durationMillis) { this.durationMillis = durationMillis; } public void setStartTimeMillis(long startTimeMillis) { this.startTimeMillis = startTimeMillis; } public void setStopTimeMillis(long stopTimeMillis) { this.stopTimeMillis = stopTimeMillis; setDurationMillis(stopTimeMillis - startTimeMillis); } /** * Shrink traces size to half by dropping traces at odd positions. * <p> * Dropping the traces at odd positions [1,3,5,...] and retaining the ones at even positions * [0,2,4,...] to always keep the first trace. */ public void shrinkTraces() { List<Trace> evenTraces = new ArrayList<Trace>(traces.size()); for (int i = 0; i < traces.size(); i += 2) { evenTraces.add(traces.get(i)); } traces = evenTraces; } public String toLogMessage(UIInstrumentationBuilder instrumentation) { @SuppressWarnings("resource") PrintStringWriter msg = new PrintStringWriter(); msg.println("Watchdog Event:"); instrumentation.data("BlockedOperation", blockedOperation); msg.println(blockedOperation != null ? blockedOperation : "unknown"); instrumentation.data("BlockedReason", blockedReason); msg.println(blockedReason != null ? blockedReason : "unknown"); instrumentation.metric("Started", startTimeMillis); msg.println("Started : " + startTimeMillis); instrumentation.metric("Stopped", stopTimeMillis); msg.println("Stopped : " + stopTimeMillis); instrumentation.metric("Duration", durationMillis); msg.println("Duration: " + durationMillis); int traceCount = 0; for (Trace trace : traces) { msg.println("Trace " + ++traceCount); instrumentation.metric("TraceTime[" + traceCount + "]", trace.getTimestampMillis()); msg.println("Time: " + trace.getTimestampMillis()); instrumentation.metric("TraceDuration[" + traceCount + "]", trace.getDurationMicros()); msg.println("Duration: " + trace.getDurationMicros()); @SuppressWarnings("resource") PrintStringWriter writer = new PrintStringWriter(); for (StackTraceElement stackElem : trace.getStackTrace()) { writer.println(stackElem.toString()); msg.println(" " + stackElem); } instrumentation.data("Trace[" + traceCount + "]", writer.toString()); } return msg.toString(); } List<String> getFilters() { if (filterOutTraces == null) { filterOutTraces = new ArrayList<String>(); filterOutTraces.add("org.eclipse.swt.internal.gtk.OS.gtk_dialog_run"); filterOutTraces.add("org.eclipse.e4.ui.workbench.addons.dndaddon.DnDManager.startDrag"); } return filterOutTraces; } /** * Returns true if the stack trace contains a modal dialog invocation (Window.runEventLoop). */ private boolean isModalDialog(StackTraceElement[] stackTrace) { for (StackTraceElement s : stackTrace) { if (s.getClassName().equals("org.eclipse.jface.window.Window") && s.getMethodName().equals("runEventLoop")) { return true; } } return false; } private boolean shouldExclude3xEvent(StackTraceElement[] stackTrace) { if (isModalDialog(stackTrace)) { return true; } if (stackTrace.length > 0) { String fullyQualifiedMethodName = stackTrace[0].getClassName() + "." + stackTrace[0].getMethodName(); // We see events with this at the top of the stack a lot when Eclipse is idle // and the screensaver is on. if (fullyQualifiedMethodName.startsWith("org.eclipse.swt.internal.gtk.OS._g_main_context_")) { return true; } } return false; } private boolean shouldExclude4xEvent(StackTraceElement[] stackTrace) { for (StackTraceElement element : stackTrace) { String fullyQualifiedMethodName = element.getClassName() + "." + element.getMethodName(); for (String filter : getFilters()) { if (fullyQualifiedMethodName.equals(filter)) { return true; } } } return false; } }