mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-04-23 22:52:11 +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;
|
package org.eclipse.cdt.dsf.ui.concurrent;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
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;
|
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -24,10 +30,23 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
|
||||||
*/
|
*/
|
||||||
public class ViewerDataRequestMonitor<V> extends DataRequestMonitor<V> {
|
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;
|
private final IViewerUpdate fUpdate;
|
||||||
|
|
||||||
|
|
||||||
public ViewerDataRequestMonitor(Executor executor, IViewerUpdate update) {
|
public ViewerDataRequestMonitor(Executor executor, IViewerUpdate update) {
|
||||||
super(executor, null);
|
super(executor, null);
|
||||||
fUpdate = update;
|
fUpdate = update;
|
||||||
|
|
||||||
|
if (DEBUG_MONITORS) {
|
||||||
|
createMonitorBacktrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -51,4 +70,115 @@ public class ViewerDataRequestMonitor<V> extends DataRequestMonitor<V> {
|
||||||
fUpdate.setStatus(getStatus());
|
fUpdate.setStatus(getStatus());
|
||||||
fUpdate.done();
|
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,6 +1,7 @@
|
||||||
org.eclipse.cdt.dsf/debug = false
|
org.eclipse.cdt.dsf/debug = false
|
||||||
org.eclipse.cdt.dsf/debug/executor = false
|
org.eclipse.cdt.dsf/debug/executor = false
|
||||||
org.eclipse.cdt.dsf/debug/executorName =
|
org.eclipse.cdt.dsf/debug/executorName =
|
||||||
|
org.eclipse.cdt.dsf/debug/monitors = false
|
||||||
org.eclipse.cdt.dsf/debugCache = false
|
org.eclipse.cdt.dsf/debugCache = false
|
||||||
org.eclipse.cdt.dsf/debug/session = false
|
org.eclipse.cdt.dsf/debug/session = false
|
||||||
org.eclipse.cdt.dsf/debug/session/listeners = false
|
org.eclipse.cdt.dsf/debug/session/listeners = false
|
||||||
|
|
|
@ -60,6 +60,20 @@ public class DsfExecutable {
|
||||||
*/
|
*/
|
||||||
static boolean DEBUG_EXECUTOR = false;
|
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
|
* Flag indicating that assertions are enabled. It enables storing of the
|
||||||
* "creator" executable for debugging purposes.
|
* "creator" executable for debugging purposes.
|
||||||
|
@ -70,6 +84,9 @@ public class DsfExecutable {
|
||||||
assert (ASSERTIONS_ENABLED = true) == true;
|
assert (ASSERTIONS_ENABLED = true) == true;
|
||||||
DEBUG_EXECUTOR = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$
|
DEBUG_EXECUTOR = DsfPlugin.DEBUG && "true".equals( //$NON-NLS-1$
|
||||||
Platform.getDebugOption("org.eclipse.cdt.dsf/debug/executor")); //$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")
|
@SuppressWarnings("unchecked")
|
||||||
public DsfExecutable() {
|
public DsfExecutable() {
|
||||||
// Use assertion flag (-ea) to jre to avoid affecting performance when not debugging.
|
// 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.
|
// Find the runnable/callable that is currently running.
|
||||||
DefaultDsfExecutor executor = DefaultDsfExecutor.fThreadToExecutorMap.get(Thread.currentThread());
|
DefaultDsfExecutor executor = DefaultDsfExecutor.fThreadToExecutorMap.get(Thread.currentThread());
|
||||||
if (executor != null) {
|
if (executor != null) {
|
||||||
|
|
|
@ -110,6 +110,18 @@ public class RequestMonitor extends DsfExecutable {
|
||||||
private boolean fCanceled = false;
|
private boolean fCanceled = false;
|
||||||
private boolean fDone = 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.
|
* 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);
|
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