Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: a93eb722bcbff15f380888a05b9fb953f1384dcd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/*******************************************************************************
 * Copyright (c) 2006, 2009 Wind River Systems and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *******************************************************************************/
package org.eclipse.cdt.dsf.concurrent;

import java.util.HashSet;
import java.util.Set;

import org.eclipse.cdt.dsf.internal.DsfPlugin;
import org.eclipse.cdt.dsf.internal.LoggingUtils;
import org.eclipse.core.runtime.Platform;

/**
 * Instrumented base class for
 * <ul>
 * <li>Runnable/Callable objects that are to be submitted to a DsfExecutor
 * <li>objects that have a primary execution method (resembling
 * <code>Runnable.run</code>) and that tend to be exercised from a DSF Executor
 * and/or that submit work to a DSF executor.
 * </ul>
 * 
 * <p>
 * Derivative classes benefit from additional fields that can be of help when
 * debugging a DSF session. Derivatives that implement Runnable/Callable and are
 * fed to DSF executors additionally benefit from tracing (when turned on by the
 * user). A trace message is generated when the Runnable/Callable is submitted
 * to the DsfExecutor.
 * 
 * <p>
 * Note that DSF executors need not be fed instances of this type. It is
 * perfectly fine for clients to call the DSF executor with a plain vanilla
 * Runnable/Callable, but such objects will obviously not benefit from the
 * instrumentation.
 * 
 * <p>
 * When this base class is used to instrument a Runnable/Callable that is
 * destined for a DSF executor, no additional work is imposed on the derived
 * class. In all other cases, the subclass is responsible for calling
 * {@link #setSubmitted()} from its primary execution method (e.g.,
 * {@link RequestMonitor#done()}
 * 
 * All fields and methods in this class are for tracing and debugging purposes
 * only.
 * 
 * @since 1.0
 */
@ThreadSafe
public class DsfExecutable {
    /** 
     * Flag indicating that tracing of the DSF executor is enabled.  It enables
     * storing of the "creator" information as well as tracing of disposed
     * 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.
     */
    static boolean ASSERTIONS_ENABLED = false;

    static {
        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$          
    }

	/**
	 * Stack trace indicating where this object was created.
	 */
    final StackTraceWrapper fCreatedAt;

	/**
	 * If this object was created by a runnable/callable that was submitted to a
	 * DsfExecutor, this field holds a reference to its tracing wrapper.
	 * Otherwise, this field is null.
	 */
    final DefaultDsfExecutor.TracingWrapper fCreatedBy;

	/**
	 * If this object is a Runnable/Callable, this flag indicates whether this
	 * object was ever submitted to a DsfExecutor for execution. If this is not
	 * a Runnable/Callable, then this indicates whether the primary execution
	 * method of the object was ever invoked. The subclass is required to
	 * explicitly call this method at the start of that primary method.
	 */
    private volatile boolean fSubmitted = false;
    
    @SuppressWarnings("unchecked")
    public DsfExecutable() {
        // Use assertion flag (-ea) to jre to avoid affecting performance when not debugging.
        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) {
                fCreatedBy = executor.fCurrentlyExecuting;
            } else {
                fCreatedBy = null;
            }
            
            // Get the stack trace and find the first method that is not a 
            // constructor of this object. 
            StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
            Class thisClass = getClass();
            Set<String> classNamesSet = new HashSet<String>();
            while(thisClass != null) {
                classNamesSet.add(thisClass.getName());
                thisClass = thisClass.getSuperclass();
            }
            int i;
            for (i = 3; i < stackTrace.length; i++) {
                if ( !classNamesSet.contains(stackTrace[i].getClassName()) ) break;
            }
            fCreatedAt = new StackTraceWrapper(new StackTraceElement[stackTrace.length - i]); 
            System.arraycopy(stackTrace, i, fCreatedAt.fStackTraceElements, 0, fCreatedAt.fStackTraceElements.length);
        } else {
            fCreatedAt = null;
            fCreatedBy = null;
        }
    }

	/**
	 * Was this object submitted for execution?
	 * 
	 * See {@link #setSubmitted()}
	 */
    public boolean getSubmitted() {
        return fSubmitted;
    }

	/**
	 * Mark that this object was submitted for execution.
	 * 
	 * <p>
	 * More specifically, if this object is a runnable/callable, this method is
	 * called right before the execute/call method is invoked by a DSF executor.
	 * If the object is not a runnable/callable, then this method is called
	 * right before a DsfExecutor invokes a callable/runnable that will invoke
	 * the target method of this object.
	 */
    public void setSubmitted() {
        fSubmitted = true;
    }

	/**
	 * Returns whether this object is always expected to be executed.
	 * We output a trace message if we are garbage collected without having been
	 * executed...that is unless this method returns false. 
	 * 
	 * Subclasses should override this method and return false if instances of
	 * it aren't meant to always be executed, thus avoiding unnecessary trace
	 * output.
	 * 
	 * @return true if this object should always be executed
	 */
    protected boolean isExecutionRequired() {
        return true;
    }

	/**
	 * Checks to see if the object was executed before being garbage collected.
	 * If not, and it's expected to have been, then output a trace message to
	 * that effect.
	 * 
	 * @see java.lang.Object#finalize()
	 */
    @Override
    protected void finalize() {
        if (DEBUG_EXECUTOR && !fSubmitted && isExecutionRequired()) {
            StringBuilder traceBuilder = new StringBuilder();

            // Record the time
            traceBuilder.append(DsfPlugin.getDebugTime());
            traceBuilder.append(' ');
            
            final String refstr = LoggingUtils.toString(this, false);
            traceBuilder.append("DSF executable was never executed: " + refstr); //$NON-NLS-1$
            final String tostr = LoggingUtils.trimTrailingNewlines(this.toString());
            if (!tostr.equals(refstr)) {
            	traceBuilder.append(" ["); //$NON-NLS-1$
            	traceBuilder.append(tostr);
            	traceBuilder.append(']');
            }
            traceBuilder.append("\nCreated at:\n"); //$NON-NLS-1$
            traceBuilder.append(fCreatedAt);
            
            DsfPlugin.debug(traceBuilder.toString());
        }
    }
}

Back to the top