001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.server;
042:
043: /**
044: * An instance of this class is allocated for each profiled thread, to hold thread-local information, such as whether
045: * the thread is currently in the profiled call subgraph, the simulated call stack, the thread-local rough generated
046: * data buffer (for CPU profiling), etc. Static methods to create and lookup instances of ThreadInfo given a Thread
047: * object are also provided in this class.
048: *
049: * @author Tomas Hurka
050: * @author Misha Dmitriev
051: */
052: public class ThreadInfo {
053: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
054:
055: static final int MAX_EVENT_ENTRIES_IN_LOCAL_BUFFER = 500; // Thread-local buffer, in events
056: static final int MAX_EVENT_SIZE = 1 + 2 + 7 + 7; // In bytes; comprises event type, method id, and two long timestamps
057: static int evBufSize; // Size and threshold, same for each thread-local buffer
058: static int evBufPosThreshold; // Size and threshold, same for each thread-local buffer
059:
060: static {
061: setDefaultEvBufParams();
062: }
063:
064: static Thread[] profilerServerThreads;
065: static int nProfilerServerThreads;
066: static int nProfiledAppThreads;
067: static ThreadInfo dummyThreadInfo = new ThreadInfo(null); // Used just to avoid null checks in some situations
068: private static boolean profilingSuspended = false;
069:
070: // ThreadInfo hash table
071: private static ThreadInfo[] threadInfos = new ThreadInfo[1]; // To avoid null checks - important!
072: private static int nThreads;
073: private static ThreadInfo lastThreadInfo = dummyThreadInfo;
074:
075: //~ Instance fields ----------------------------------------------------------------------------------------------------------
076:
077: Thread thread; // Thread object for this ThreadInfo
078: byte[] evBuf; // Thread-local event (rough profiling data) buffer. Currently used in CPU profiling only.
079: boolean inCallGraph; // Indicates whether the thread is currently in the profiled subgraph
080: boolean sampleDue; // In sampled instrumentation mode, indicates that next sampling should be done
081: int evBufDumpLastPos; // Used to avoid synchronization in writeEvent() and yet to allow for asynchronous event buffer dumps.
082: int evBufPos; // Current position in the local event buffer
083: int inProfilingRuntimeMethod; // Indicates whether currently some profiling runtime method is executed on behalf of this thread
084: int rootMethodStackDepth; // logical stack depth of the root method which is inside of marker method
085: int stackDepth; // Current logical (i.e. relative to the root method frame) stack depth
086: int threadId; // Integer ID
087: long absEntryTime; // Used to support thread suspension and code fragment profiling
088: long lastWaitStartTime; // Used in Code Region profiling for tracking wait times
089: long threadEntryTime; // Used to support thread suspension and code fragment profiling
090: private boolean initialized; // To signal that this thread is not initialized or was reset, so this threadInfo is unusable
091:
092: //~ Constructors -------------------------------------------------------------------------------------------------------------
093:
094: private ThreadInfo(Thread thread) {
095: this .thread = thread;
096: inProfilingRuntimeMethod = 1; // To make possible trace method calls while ThreadInfo is initialized return immediately
097: threadId = nThreads & 0xFFFF;
098: }
099:
100: //~ Methods ------------------------------------------------------------------------------------------------------------------
101:
102: public final boolean isInCallGraph() {
103: return inCallGraph;
104: }
105:
106: public final Thread getThread() {
107: return thread;
108: }
109:
110: public final int getThreadId() {
111: return threadId;
112: }
113:
114: public static boolean isCurrentThreadProfilerServerThread() {
115: return isProfilerServerThread(Thread.currentThread());
116: }
117:
118: public static boolean isProfilerServerThread(Thread thread) {
119: if (profilerServerThreads == null) {
120: return false;
121: }
122:
123: for (int i = 0; i < nProfilerServerThreads; i++) {
124: if (profilerServerThreads[i] == thread) {
125: return true;
126: }
127: }
128:
129: return false;
130: }
131:
132: public static synchronized void addProfilerServerThread(
133: Thread thread) {
134: profilerServerThreads[nProfilerServerThreads++] = thread;
135: }
136:
137: //-----------------------------------------------------------------------------------------------
138: // Accounting for profiler's own threads
139: //-----------------------------------------------------------------------------------------------
140:
141: // We use a simple array of Threads, not a Vector (as once before) here, since Vector's "contains()" method's
142: // performance is really worse than that of simple compare in isProfilerThread() below. It's likely related to the
143: // fact that contains() calls one or two other methods and uses "equals()" for compares, rather than simple "==".
144: // Otherwise these methods don't have to be very sophisticated, since there are not going to be more than 3-6 such
145: // threads. Also, during CPU profiling each of them is called only once, when a ThreadInfo is created for the
146: // corresponding thread. A field is marked accordingly in ThreadInfo, and then checked in each methodEntry/Exit().
147: public static synchronized void clearProfilerServerThreads() {
148: if (profilerServerThreads == null) {
149: profilerServerThreads = new Thread[10];
150: } else {
151: for (int i = 0; i < nProfilerServerThreads; i++) {
152: profilerServerThreads[i] = null;
153: }
154:
155: nProfilerServerThreads = 0;
156: }
157: }
158:
159: public static boolean profilingSuspended() {
160: return profilingSuspended;
161: }
162:
163: public static synchronized void removeProfilerServerThread(
164: Thread thread) {
165: if (profilerServerThreads == null) {
166: return;
167: }
168:
169: for (int i = 0; i < nProfilerServerThreads; i++) {
170: if (profilerServerThreads[i] == thread) {
171: if (i == (nProfilerServerThreads - 1)) {
172: profilerServerThreads[i] = null;
173: } else {
174: System.arraycopy(profilerServerThreads, i + 1,
175: profilerServerThreads, i,
176: nProfilerServerThreads - i - 1);
177: }
178:
179: nProfilerServerThreads--;
180:
181: return;
182: }
183: }
184: }
185:
186: public static void resumeProfiling() {
187: profilingSuspended = false;
188: }
189:
190: public static void suspendProfiling() {
191: profilingSuspended = true;
192: }
193:
194: static byte[] getCurrentLivenessStatus() {
195: ThreadInfo[] tis = threadInfos;
196: int resLen = nThreads;
197: byte[] res = new byte[resLen];
198:
199: for (int i = 0; i < tis.length; i++) {
200: ThreadInfo ti = tis[i];
201:
202: if ((ti != null) && (ti.threadId < resLen)
203: && (ti.thread != null)) {
204: // We don't care if a new thread was created, but we don't list it - at the tool side the currently observed
205: // number of threads is also <= nThreads
206: res[ti.threadId] = ti.thread.isAlive() ? (byte) 1 : 0;
207: }
208: }
209:
210: return res;
211: }
212:
213: static void setDefaultEvBufParams() {
214: evBufSize = MAX_EVENT_ENTRIES_IN_LOCAL_BUFFER * MAX_EVENT_SIZE;
215: evBufPosThreshold = evBufSize - (4 * MAX_EVENT_SIZE) - 1;
216: threadInfos = new ThreadInfo[1]; // To avoid null checks
217: }
218:
219: final boolean isInitialized() {
220: return initialized;
221: }
222:
223: final void initialize() {
224: initialize(false);
225: }
226:
227: final void initialize(boolean trackResultsAvailability) {
228: inProfilingRuntimeMethod++;
229:
230: if (!isProfilerServerThread(thread)) {
231: if (trackResultsAvailability && (nProfiledAppThreads == 0)) {
232: ProfilerServer.notifyClientOnResultsAvailability();
233: }
234:
235: nProfiledAppThreads++;
236: }
237:
238: resetInternalState();
239: initialized = true;
240: inProfilingRuntimeMethod--;
241: }
242:
243: final void useEventBuffer() {
244: evBuf = new byte[evBufSize];
245: }
246:
247: static int getNProfiledAppThreads() {
248: return nProfiledAppThreads;
249: }
250:
251: static void setSampleDueForAllThreads() {
252: ThreadInfo[] tis = threadInfos;
253:
254: for (int i = 0; i < tis.length; i++) {
255: ThreadInfo ti = tis[i];
256:
257: if (ti != null) { // We don't care if a new thread was created, but we don't list it
258: ti.sampleDue = true;
259: }
260: }
261: }
262:
263: static ThreadInfo getThreadInfo() {
264: Thread thread = Thread.currentThread();
265: ThreadInfo ti = lastThreadInfo;
266:
267: if (ti.thread == thread) {
268: return ti;
269: }
270:
271: return getThreadInfo(thread);
272: }
273:
274: static ThreadInfo getThreadInfo(Thread thread) {
275: ThreadInfo ti = getThreadInfoOrNull(thread);
276:
277: if (ti == null) {
278: ti = newThreadInfo(thread);
279: }
280:
281: return ti;
282: }
283:
284: static ThreadInfo[] getThreadInfos() {
285: return threadInfos;
286: }
287:
288: static void changeAllThreadsInProfRuntimeMethodStatus(int val) {
289: synchronized (threadInfos) {
290: for (int i = 0; i < threadInfos.length; i++) {
291: ThreadInfo ti = threadInfos[i];
292:
293: if (!ti.inCallGraph) {
294: continue;
295: }
296:
297: ti.inProfilingRuntimeMethod += val;
298: }
299: }
300: }
301:
302: static void resetThreadInfoTable() {
303: ThreadInfo[] oldTIs = threadInfos;
304:
305: synchronized (threadInfos) {
306: nProfiledAppThreads = 0;
307: lastThreadInfo = dummyThreadInfo; // To avoid null checks
308:
309: for (int i = 0; i < oldTIs.length; i++) {
310: ThreadInfo ti = oldTIs[i];
311:
312: if ((ti == null) || (ti.thread == null)) {
313: oldTIs[i] = null;
314:
315: continue;
316: }
317:
318: ti.initialized = false;
319: ti.resetInternalState();
320: }
321: }
322: }
323:
324: //-----------------------------------------------------------------------------------------------
325: // Special methods for non-standard usage of ThreadInfo
326: //-----------------------------------------------------------------------------------------------
327:
328: // This method is used only by ProfilerCalibrator
329: void setEvBuf(byte[] buf) {
330: evBuf = buf;
331: evBufSize = buf.length;
332: evBufPosThreshold = buf.length - (2 * MAX_EVENT_SIZE) - 1;
333: }
334:
335: static void releaseDeadThreads() {
336: ThreadInfo[] tis = threadInfos;
337:
338: for (int i = 0; i < tis.length; i++) {
339: ThreadInfo ti = tis[i];
340:
341: if (ti != null) {
342: Thread t = ti.thread;
343:
344: if ((t != null) && !t.isAlive()) {
345: ti.thread = null;
346: }
347: }
348: }
349: }
350:
351: private static int getThreadHashCode(Thread t) {
352: return System.identityHashCode(t) & 0x7fffffff;
353: }
354:
355: private static ThreadInfo getThreadInfoOrNull(Thread thread) {
356: ThreadInfo[] tis = threadInfos;
357: int capacity = tis.length;
358: int pos = getThreadHashCode(thread) % capacity;
359: ThreadInfo ti;
360:
361: while ((ti = tis[pos]) != null) {
362: if (ti.thread == thread) {
363: return ti;
364: } else {
365: pos = (pos + 1) % capacity;
366: }
367: }
368:
369: return null;
370: }
371:
372: private static void addThreadInfo(final ThreadInfo res,
373: final Thread thread) {
374: if (nThreads > ((threadInfos.length * 3) / 4)) {
375: rehash();
376: }
377:
378: int capacity = threadInfos.length;
379: int pos = getThreadHashCode(thread) % capacity;
380:
381: while (threadInfos[pos] != null) {
382: pos = (pos + 1) % capacity;
383: }
384:
385: threadInfos[pos] = res;
386: }
387:
388: private static ThreadInfo newThreadInfo(Thread thread) {
389: synchronized (threadInfos) {
390: ThreadInfo ti = getThreadInfoOrNull(thread);
391:
392: if (ti != null) {
393: return ti;
394: }
395:
396: ThreadInfo res = new ThreadInfo(thread);
397:
398: nThreads++;
399: addThreadInfo(res, thread);
400: res.inProfilingRuntimeMethod = 0;
401:
402: return res;
403: }
404: }
405:
406: private static void rehash() {
407: int capacity = (threadInfos.length * 2) + 1;
408: ThreadInfo[] newTIs = new ThreadInfo[capacity];
409:
410: for (int i = 0; i < threadInfos.length; i++) {
411: ThreadInfo ti = threadInfos[i];
412:
413: if ((ti == null) || (ti.thread == null)) {
414: continue;
415: }
416:
417: int pos = getThreadHashCode(ti.thread) % capacity;
418:
419: while (newTIs[pos] != null) {
420: pos = (pos + 1) % capacity;
421: }
422:
423: newTIs[pos] = ti;
424: }
425:
426: threadInfos = newTIs;
427: }
428:
429: private void resetInternalState() {
430: evBufPos = evBufDumpLastPos = 0;
431: absEntryTime = lastWaitStartTime = threadEntryTime = 0;
432: rootMethodStackDepth = stackDepth = 0;
433: inCallGraph = sampleDue = false;
434: evBuf = null;
435: }
436: }
|