package com.google.dart.tools.ui.watchdog;
import com.google.dart.tools.core.DartCore;
import com.google.dart.tools.ui.instrumentation.UIInstrumentation;
import com.google.dart.tools.ui.instrumentation.UIInstrumentationBuilder;
import org.eclipse.swt.widgets.Display;
/**
* Watchdog timer thread which periodically checks if the Workbench main event loop is currently
* processing an event and how long it has been processing. If the event and duration meets our
* critera, we begin sampling it more often to capture stack traces of what it's doing.
* <p>
* To keep the total number of collected traces with in a maximum limit, we shrink the traces size
* to half by dropping traces at odd positions and double the sampling interval when we reach the
* max trace count.
*/
public class WatchdogThread extends Thread {
/** Indication of when to stop the thread from executing */
private volatile boolean stop = false;
public WatchdogThread() {
super("WatchdogThread");
setDaemon(true);
setPriority(NORM_PRIORITY + 1);
}
/**
* Main run loop for this thread which periodically checks with the Workbench to see if an event
* is current being processed and captures details about it if it meets our criteria.
*/
@Override
public void run() {
DartCore.logInformation("WatchdogThread running");
// Capture traces if an event takes longer than X milliseconds
int monitoringEventTimeMillis = 500;
int idleSampleRateMillis = monitoringEventTimeMillis / 2;
// Sample interval to capture the traces of an unresponsive event
int activeSampleRateMillis = 100;
// Maximum number of traces to keep
int maxTraceCount = 8;
Thread mainEventThread = Display.getDefault().getThread();
Event event = null;
Event blockedJobsEvent = null;
while (!stop) {
// Log all occurrences of the Blocked Jobs dialog.
BlockedTaskInfo blockedTaskInfo = MonitoringUtil.getBlockedTaskInfo();
long blockedJobstStartTimeMilliseconds = (blockedTaskInfo == null) ? 0
: blockedTaskInfo.getStartTimeInMilliseconds();
if (blockedJobsEvent != null
&& blockedJobstStartTimeMilliseconds != blockedJobsEvent.getStartTimeMillis()) {
eventCompleted(blockedJobsEvent, blockedJobstStartTimeMilliseconds);
blockedJobsEvent = null;
}
if (blockedJobstStartTimeMilliseconds > 0 && blockedJobsEvent == null
&& blockedTaskInfo != null) {
// TODO(thirumala): Report the thread that is blocking the UI thread.
blockedJobsEvent = new Event(
blockedJobstStartTimeMilliseconds,
blockedTaskInfo.getTaskName(),
blockedTaskInfo.getReason());
// Avoid logging multiple messages for this pause.
event = null;
}
// Log when the UI thread is frozen for more than WARN_TIME_MILLIS
long nextEventStartTimeMilliseconds = MonitoringUtil.getCurrentUIEventStartTimeInMilliseconds();
if (event != null && nextEventStartTimeMilliseconds != event.getStartTimeMillis()) {
eventCompleted(event, nextEventStartTimeMilliseconds);
event = null;
}
try {
if (nextEventStartTimeMilliseconds > 0) { // If an event is being handled, start sampling it
// How long has it been running?
long duration = System.currentTimeMillis() - nextEventStartTimeMilliseconds;
if (duration > monitoringEventTimeMillis) {
if (event == null) {
event = new Event(nextEventStartTimeMilliseconds);
}
if (event.getTracesCount() >= maxTraceCount) {
// Shrink traces size to half and double the sampling interval to keep the total
// number of traces with in the max trace count limit.
event.shrinkTraces();
activeSampleRateMillis *= 2;
}
event.sample(mainEventThread);
}
Thread.sleep(activeSampleRateMillis);
} else {
Thread.sleep(idleSampleRateMillis);
}
} catch (InterruptedException e) {
DartCore.logInformation("Thread sleep interrupted", e);
}
}
}
public void shutdown() {
stop = true;
}
/**
* Log an event that has completed.
*/
private void eventCompleted(Event event, long nextEventStartTime) {
if (event != null && !event.isEventExcluded()) {
// Set the stop time to either the next event's start time or the current time.
long stopTimeMillis = nextEventStartTime > 0 ? nextEventStartTime
: System.currentTimeMillis();
event.setStopTimeMillis(stopTimeMillis);
UIInstrumentationBuilder instrumentation = UIInstrumentation.builder(this.getClass());
try {
DartCore.logInformation(event.toLogMessage(instrumentation));
} catch (RuntimeException e) {
instrumentation.record(e);
DartCore.logInformation("Failed to log Watchdog Event", e);
} finally {
instrumentation.log();
}
}
}
}