mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-21 21:52:10 +02:00
[291684] Equip monitors with a monitor traceback (when tracing)
This commit is contained in:
parent
bd20ae7286
commit
4a942b7640
4 changed files with 187 additions and 3 deletions
|
@ -10,9 +10,15 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.ui.concurrent;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.DsfExecutable;
|
||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
|
||||
import org.eclipse.cdt.dsf.internal.DsfPlugin;
|
||||
import org.eclipse.cdt.dsf.ui.viewmodel.VMViewerUpdate;
|
||||
import org.eclipse.core.runtime.Platform;
|
||||
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
|
||||
|
||||
/**
|
||||
|
@ -23,11 +29,24 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
|
|||
* @since 1.0
|
||||
*/
|
||||
public class ViewerDataRequestMonitor<V> extends DataRequestMonitor<V> {
|
||||
|
||||
/**
|
||||
* Same as {@link DsfExecutable#DEBUG_MONITORS}
|
||||
*/
|
||||
static private boolean DEBUG_MONITORS = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$
|
||||
Platform.getDebugOption("org.eclipse.cdt.dsf/debug/monitors")); //$NON-NLS-1$
|
||||
|
||||
|
||||
private final IViewerUpdate fUpdate;
|
||||
|
||||
|
||||
public ViewerDataRequestMonitor(Executor executor, IViewerUpdate update) {
|
||||
super(executor, null);
|
||||
fUpdate = update;
|
||||
|
||||
if (DEBUG_MONITORS) {
|
||||
createMonitorBacktrace();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -51,4 +70,115 @@ public class ViewerDataRequestMonitor<V> extends DataRequestMonitor<V> {
|
|||
fUpdate.setStatus(getStatus());
|
||||
fUpdate.done();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instrument this object with a backtrace of the monitors this instance is
|
||||
* chained to. See {@link DsfExecutable#DEBUG_MONITORS}. The logic here has
|
||||
* to subvert Java access protection by using reflection. That's OK; this is
|
||||
* not production code. This stuff will only ever run when tracing is turned
|
||||
* on.
|
||||
*/
|
||||
private void createMonitorBacktrace() {
|
||||
StringBuilder str = new StringBuilder();
|
||||
|
||||
RequestMonitor nextrm = this;
|
||||
VMViewerUpdate nextupdate = null;
|
||||
String type = null;
|
||||
while (true) {
|
||||
StackTraceElement topFrame = null;
|
||||
if (nextupdate != null) {
|
||||
type = "update "; //$NON-NLS-1$ extra space to match length of 'monitor'
|
||||
topFrame = getCreatedAtTopFrame(nextupdate);
|
||||
nextrm = getMonitor(nextupdate);
|
||||
nextupdate = null;
|
||||
}
|
||||
else if (nextrm != null) {
|
||||
type = "monitor"; //$NON-NLS-1$
|
||||
topFrame = getCreatedAtTopFrame(nextrm);
|
||||
if (nextrm instanceof ViewerDataRequestMonitor<?>) {
|
||||
ViewerDataRequestMonitor<?> vdrm = (ViewerDataRequestMonitor<?>)nextrm;
|
||||
nextupdate = (vdrm.fUpdate instanceof VMViewerUpdate) ? (VMViewerUpdate)vdrm.fUpdate : null;
|
||||
nextrm = null;
|
||||
}
|
||||
else {
|
||||
nextrm = getParentMonitor(nextrm);
|
||||
nextupdate = null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
if (topFrame != null) {
|
||||
str.append("[" + type + "] " + topFrame + "\n"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
|
||||
}
|
||||
else {
|
||||
str.append("<unknown>\n"); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
Field field;
|
||||
try {
|
||||
field = RequestMonitor.class.getDeclaredField("fMonitorBacktrace"); //$NON-NLS-1$
|
||||
field.setAccessible(true);
|
||||
field.set(this, str.toString());
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (SecurityException e) {
|
||||
} catch (NoSuchFieldException e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility used by {@link #createMonitorBacktrace()}. Subverts access
|
||||
* protection.
|
||||
*/
|
||||
private static RequestMonitor getMonitor(VMViewerUpdate update) {
|
||||
try {
|
||||
Field field = VMViewerUpdate.class.getDeclaredField("fRequestMonitor"); //$NON-NLS-1$
|
||||
field.setAccessible(true);
|
||||
return (RequestMonitor) field.get(update);
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (SecurityException e) {
|
||||
} catch (NoSuchFieldException e) {
|
||||
} catch (ClassCastException e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility used by {@link #createMonitorBacktrace()}. Subverts access
|
||||
* protection.
|
||||
*/
|
||||
private static RequestMonitor getParentMonitor(RequestMonitor rm) {
|
||||
try {
|
||||
Field field = RequestMonitor.class.getDeclaredField("fParentRequestMonitor"); //$NON-NLS-1$
|
||||
field.setAccessible(true);
|
||||
return (RequestMonitor) field.get(rm);
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (NoSuchFieldException e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility used by {@link #createMonitorBacktrace()}. Subverts access
|
||||
* protection.
|
||||
*/
|
||||
private static <T extends DsfExecutable> StackTraceElement getCreatedAtTopFrame(T dsfExecutable) {
|
||||
try {
|
||||
Field field_fCreatedAt = DsfExecutable.class.getDeclaredField("fCreatedAt"); //$NON-NLS-1$
|
||||
field_fCreatedAt.setAccessible(true);
|
||||
Object obj_fCreatedAt = field_fCreatedAt.get(dsfExecutable);
|
||||
Class<?> class_StackTraceElement = Class.forName("org.eclipse.cdt.dsf.concurrent.StackTraceWrapper"); //$NON-NLS-1$
|
||||
Field field_fStackTraceElements = class_StackTraceElement.getDeclaredField("fStackTraceElements"); //$NON-NLS-1$
|
||||
field_fStackTraceElements.setAccessible(true);
|
||||
StackTraceElement[] frames = (StackTraceElement[])field_fStackTraceElements.get(obj_fCreatedAt);
|
||||
if (frames != null && frames.length > 0) {
|
||||
return frames[0];
|
||||
}
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (NoSuchFieldException e) {
|
||||
} catch (ClassNotFoundException e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
org.eclipse.cdt.dsf/debug = false
|
||||
org.eclipse.cdt.dsf/debug/executor = false
|
||||
org.eclipse.cdt.dsf/debug/executorName =
|
||||
org.eclipse.cdt.dsf/debug/monitors = false
|
||||
org.eclipse.cdt.dsf/debugCache = false
|
||||
org.eclipse.cdt.dsf/debug/session = false
|
||||
org.eclipse.cdt.dsf/debug/session/listeners = false
|
||||
org.eclipse.cdt.dsf/debug/session/dispatches = false
|
||||
org.eclipse.cdt.dsf/debug/session/modelAdapters = false
|
||||
org.eclipse.cdt.dsf/debug/session/modelAdapters = false
|
||||
|
|
|
@ -59,7 +59,21 @@ public class DsfExecutable {
|
|||
* runnables that have not been submitted to the executor.
|
||||
*/
|
||||
static boolean DEBUG_EXECUTOR = false;
|
||||
|
||||
|
||||
/**
|
||||
* Flag indicating that monitor objects should be instrumented. A monitor is
|
||||
* an object that is usually constructed as an anonymous inner classes and
|
||||
* is used when making an asynchronous call--one that needs to return some
|
||||
* result or at least notify its caller when it has completed. These objects
|
||||
* usually end up getting chained together at runtime, forming what is
|
||||
* effectively a very disjointed code path. When this trace option is
|
||||
* enabled, these objects are given a String field at construction time that
|
||||
* contains the instantiation backtrace. This turns out to be a fairly
|
||||
* dependable alternative to the standard program stack trace, which is of
|
||||
* virtually no help when debugging asynchronous, monitor-assisted code.
|
||||
*/
|
||||
static boolean DEBUG_MONITORS = false;
|
||||
|
||||
/**
|
||||
* Flag indicating that assertions are enabled. It enables storing of the
|
||||
* "creator" executable for debugging purposes.
|
||||
|
@ -70,6 +84,9 @@ public class DsfExecutable {
|
|||
assert (ASSERTIONS_ENABLED = true) == true;
|
||||
DEBUG_EXECUTOR = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$
|
||||
Platform.getDebugOption("org.eclipse.cdt.dsf/debug/executor")); //$NON-NLS-1$
|
||||
|
||||
DEBUG_MONITORS = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$
|
||||
Platform.getDebugOption("org.eclipse.cdt.dsf/debug/monitors")); //$NON-NLS-1$
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -96,7 +113,7 @@ public class DsfExecutable {
|
|||
@SuppressWarnings("unchecked")
|
||||
public DsfExecutable() {
|
||||
// Use assertion flag (-ea) to jre to avoid affecting performance when not debugging.
|
||||
if (ASSERTIONS_ENABLED || DEBUG_EXECUTOR) {
|
||||
if (ASSERTIONS_ENABLED || DEBUG_EXECUTOR || DEBUG_MONITORS) {
|
||||
// Find the runnable/callable that is currently running.
|
||||
DefaultDsfExecutor executor = DefaultDsfExecutor.fThreadToExecutorMap.get(Thread.currentThread());
|
||||
if (executor != null) {
|
||||
|
|
|
@ -110,6 +110,18 @@ public class RequestMonitor extends DsfExecutable {
|
|||
private boolean fCanceled = false;
|
||||
private boolean fDone = false;
|
||||
|
||||
/**
|
||||
* This field is never read by any code; its purpose is strictly to assist
|
||||
* developers debug DPF code. Developer can select this field in the
|
||||
* Variables view and see a monitor backtrace in the details pane. See
|
||||
* {@link DsfExecutable#DEBUG_MONITORS}.
|
||||
*
|
||||
* <p>
|
||||
* This field is set only when tracing is enabled.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private String fMonitorBacktrace;
|
||||
|
||||
/**
|
||||
* Constructor with an optional parent monitor.
|
||||
*
|
||||
|
@ -139,6 +151,10 @@ public class RequestMonitor extends DsfExecutable {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (DEBUG_MONITORS) {
|
||||
createMonitorBacktrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -446,4 +462,24 @@ public class RequestMonitor extends DsfExecutable {
|
|||
DsfPlugin.getDefault().getLog().log(logStatus);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instrument this object with a backtrace of the monitors this instance is
|
||||
* chained to. See {@link DsfExecutable#DEBUG_MONITORS}
|
||||
*/
|
||||
private void createMonitorBacktrace() {
|
||||
StringBuilder str = new StringBuilder();
|
||||
for (RequestMonitor nextrm = this; nextrm != null; nextrm = nextrm.fParentRequestMonitor) {
|
||||
final StackTraceElement[] stackTraceElems = (nextrm.fCreatedAt != null) ? nextrm.fCreatedAt.fStackTraceElements : null;
|
||||
if (stackTraceElems != null && stackTraceElems.length > 0)
|
||||
{
|
||||
str.append(stackTraceElems[0] + "\n"); //$NON-NLS-1$
|
||||
}
|
||||
else {
|
||||
str.append("<unknown>\n"); //$NON-NLS-1$
|
||||
}
|
||||
}
|
||||
|
||||
fMonitorBacktrace = str.toString();
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue