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: import org.netbeans.lib.profiler.server.system.Timers;
044:
045: /**
046: * This class contains the actual methods for full instrumentation recursive CPU profiling, calls to which are injected
047: * into the target application (TA) bytecodes when they are instrumented.
048: *
049: * @author Tomas Hurka
050: * @author Misha Dmitriev
051: */
052: public class ProfilerRuntimeCPUFullInstr extends ProfilerRuntimeCPU {
053: //~ Methods ------------------------------------------------------------------------------------------------------------------
054:
055: public static void enableProfiling(boolean v) {
056: if (v) {
057: createNewDataStructures();
058: ProfilerRuntimeCPU.enableProfiling(v);
059: } else {
060: ProfilerRuntimeCPU.enableProfiling(v);
061: clearDataStructures();
062: }
063: }
064:
065: // ---------------------------------- Profile Data Acquisition --------------------------------------
066: /** Called upon entry into a special root method used for */
067: public static void markerMethodEntry(char methodId) {
068: if (recursiveInstrumentationDisabled) {
069: return; // See the comment at the recursiveInstrumentationDisabled variable declaration
070: }
071:
072: ThreadInfo ti = ThreadInfo.getThreadInfo();
073:
074: if (ti.inProfilingRuntimeMethod > 0) {
075: return;
076: }
077:
078: //if (instrMethodClasses != null && methodId < instrMethodClasses.length) System.out.println("++++++Marker methodEntry for " + instrMethodClasses[methodId] + "." + instrMethodNames[methodId] + ", thread = " + Thread.currentThread());
079: //else System.out.println("++++++Marker methodEntry for methodId = " + (int)methodId + ", thread = " + Thread.currentThread());
080: if (!ti.isInitialized()) {
081: if ((nProfiledThreadsAllowed > 0)
082: && !ThreadInfo
083: .isCurrentThreadProfilerServerThread()) {
084: ti.initialize();
085: ti.useEventBuffer();
086:
087: synchronized (eventBuffer) { // Make this happen atomically wrt. other operations on eventBuffer, such as reset collectors
088: nProfiledThreadsAllowed--;
089:
090: ti.inProfilingRuntimeMethod++;
091: ti.inCallGraph = true;
092: writeThreadCreationEvent(ti);
093: }
094: } else {
095: return;
096: }
097: } else {
098: ti.inProfilingRuntimeMethod++;
099: ti.inCallGraph = true;
100: }
101:
102: // This is to bypass what seems to be a compiler bug (at least C1 on Windows): when methodId > 64K/2 is passed here
103: // using our instrumentation's sipush command at the call site, it's treated here as a signed integer. Thus without
104: // the below fix we can get e.g. an ArrayIndexOutOfBoundsException(-32768) when methodId == 32768 (***)
105: methodId = (char) ((int) methodId);
106:
107: // DO NOT perform instrumentation of its immediate callees
108: if (!instrMethodInvoked[methodId]) {
109: instrMethodInvoked[methodId] = true;
110: }
111:
112: ti.stackDepth++; //= 1; // This is the logical stack depth
113: writeTimeStampedEvent(MARKER_ENTRY, ti, methodId);
114: ti.inProfilingRuntimeMethod--;
115: }
116:
117: /** Called upon exit from the marker method. */
118: public static void markerMethodExit(char methodId) {
119: if (recursiveInstrumentationDisabled) {
120: return;
121: }
122:
123: ThreadInfo ti = ThreadInfo.getThreadInfo();
124:
125: if (ti.isInitialized() && ti.inCallGraph) { // ti == null may happen if instrumentation has been removed or data collectors reset
126:
127: if (ti.inProfilingRuntimeMethod > 0) {
128: return;
129: }
130:
131: if (ti.rootMethodStackDepth > 0) {
132: methodExit(methodId);
133: } else {
134: ti.inProfilingRuntimeMethod++;
135:
136: //System.out.println("------markerMethodExit for " + instrMethodClasses[methodId] + "." + instrMethodNames[methodId] + ", depth = " + ti.stackDepth + ", id = " + (int) methodId);
137: ti.stackDepth--;
138:
139: if (ti.stackDepth < 1) {
140: ti.inCallGraph = false; // We are exiting the marker method of our call subgraph
141: }
142:
143: writeTimeStampedEvent(MARKER_EXIT, ti, methodId);
144: ti.inProfilingRuntimeMethod--;
145: }
146: }
147: }
148:
149: /** Called upon entry into a non-root target application method */
150: public static void methodEntry(char methodId) {
151: if (recursiveInstrumentationDisabled) {
152: return; // See the comment at the recursiveInstrumentationDisabled variable declaration
153: }
154:
155: ThreadInfo ti = ThreadInfo.getThreadInfo();
156:
157: if (ti.isInitialized() && ti.inCallGraph
158: && (ti.rootMethodStackDepth > 0)) {
159: if (ti.inProfilingRuntimeMethod > 0) {
160: return;
161: }
162:
163: ti.inProfilingRuntimeMethod++;
164: //System.out.println("++++++methodEntry, depth = " + ti.stackDepth + ", id = " + (int) methodId);
165:
166: // See comment marked with (***)
167: methodId = (char) ((int) methodId);
168:
169: // Now check if it's the first invocation of this method, and if so, perform instrumentation of nearest callees
170: if (!instrMethodInvoked[methodId]) {
171: long absTimeStamp = Timers.getCurrentTimeInCounts();
172: long threadTimeStamp = Timers.getThreadCPUTimeInNanos();
173: externalActionsHandler
174: .handleFirstTimeMethodInvoke(methodId);
175: instrMethodInvoked[methodId] = true; // Mark this method as invoked
176: writeAdjustTimeEvent(ti, absTimeStamp, threadTimeStamp);
177: }
178:
179: ti.stackDepth++;
180: writeTimeStampedEvent(METHOD_ENTRY, ti, methodId);
181:
182: ti.inProfilingRuntimeMethod--;
183: }
184: }
185:
186: /** Called upon exit from the method. */
187: public static void methodExit(char methodId) {
188: if (recursiveInstrumentationDisabled) {
189: return; // See the comment at the recursiveInstrumentationDisabled variable declaration
190: }
191:
192: ThreadInfo ti = ThreadInfo.getThreadInfo();
193:
194: if (ti.isInitialized() && ti.inCallGraph
195: && (ti.rootMethodStackDepth > 0)) { // ti == null may happen if instrumentation has been removed or data collectors reset
196:
197: if (ti.inProfilingRuntimeMethod > 0) {
198: return;
199: }
200:
201: ti.inProfilingRuntimeMethod++;
202:
203: //System.out.println("------methodExit, depth = " + ti.stackDepth + ", id = " + (int) methodId);
204: if (ti.rootMethodStackDepth == ti.stackDepth) {
205: ti.rootMethodStackDepth = 0;
206: }
207:
208: ti.stackDepth--;
209:
210: if (ti.stackDepth < 1) {
211: ti.inCallGraph = false; // We are exiting the root method of our call subgraph
212: writeTimeStampedEvent(ROOT_EXIT, ti, methodId);
213: } else {
214: writeTimeStampedEvent(METHOD_EXIT, ti, methodId);
215: }
216:
217: ti.inProfilingRuntimeMethod--;
218: }
219: }
220:
221: public static void resumeActiveTimers() {
222: writeTimeStampedEvent(THREADS_RESUMED, null, (char) 0); // FIXME: see above
223: changeAllThreadsInProfRuntimeMethodStatus(-1); // See the comment in suspendActiveTimers()
224: recursiveInstrumentationDisabled = false;
225: }
226:
227: /** Called upon entry into a root target application method */
228: public static void rootMethodEntry(char methodId) {
229: if (recursiveInstrumentationDisabled) {
230: return; // See the comment at the recursiveInstrumentationDisabled variable declaration
231: }
232:
233: ThreadInfo ti = ThreadInfo.getThreadInfo();
234:
235: if (ti.inProfilingRuntimeMethod > 0) {
236: return;
237: }
238:
239: ProfilerServer.notifyClientOnResultsAvailability();
240:
241: if (ti.isInitialized() && !ti.inCallGraph
242: && (ti.stackDepth > 0)) {
243: ti.inCallGraph = true;
244: methodEntry(methodId);
245: ti.inCallGraph = false;
246:
247: return;
248: }
249:
250: if (ti.isInitialized() && ti.inCallGraph
251: && (ti.rootMethodStackDepth > 0)) {
252: methodEntry(methodId);
253: } else { // Entered the root method from outside this call subgraph
254: //if (instrMethodClasses != null && methodId < instrMethodClasses.length) System.out.println("++++++Root methodEntry for " + instrMethodClasses[methodId] + "." + instrMethodNames[methodId] + ", thread = " + Thread.currentThread());
255: //else System.out.println("++++++Root methodEntry for methodId = " + (int)methodId + ", thread = " + Thread.currentThread());
256:
257: if (!ti.isInitialized()) {
258: if ((nProfiledThreadsAllowed > 0)
259: && !ThreadInfo
260: .isCurrentThreadProfilerServerThread()) {
261: ti.initialize();
262: ti.useEventBuffer();
263:
264: synchronized (eventBuffer) { // Make this happen atomically wrt. other operations on eventBuffer, such as reset collectors
265: nProfiledThreadsAllowed--;
266: ti.inProfilingRuntimeMethod++;
267:
268: if (!ProfilerServer
269: .startProfilingPointsActive()) {
270: ti.inCallGraph = true;
271: }
272:
273: writeThreadCreationEvent(ti);
274: }
275: } else {
276: return;
277: }
278: } else {
279: if (ti.inProfilingRuntimeMethod > 0) {
280: return;
281: }
282:
283: if (ti.stackDepth > 0) {
284: ti.rootMethodStackDepth = ti.stackDepth + 1;
285: methodEntry(methodId);
286:
287: return;
288: } else {
289: ti.inProfilingRuntimeMethod++;
290:
291: if (!ProfilerServer.startProfilingPointsActive()) {
292: ti.inCallGraph = true;
293: }
294: }
295: }
296:
297: // This is to bypass what seems to be a compiler bug (at least C1 on Windows): when methodId > 64K/2 is passed here
298: // using our instrumentation's sipush command at the call site, it's treated here as a signed integer. Thus without
299: // the below fix we can get e.g. an ArrayIndexOutOfBoundsException(-32768) when methodId == 32768 (***)
300: methodId = (char) ((int) methodId);
301:
302: // Check if it's the first invocation of this method, and if so, perform instrumentation of its immediate callees
303: if (!instrMethodInvoked[methodId]) {
304: externalActionsHandler
305: .handleFirstTimeMethodInvoke(methodId);
306: instrMethodInvoked[methodId] = true;
307: }
308:
309: ti.stackDepth++; //= 1; // This is the logical stack depth
310: writeTimeStampedEvent(ROOT_ENTRY, ti, methodId);
311:
312: ti.rootMethodStackDepth = ti.stackDepth;
313: ti.inProfilingRuntimeMethod--;
314: }
315: }
316:
317: public static void suspendActiveTimers() {
318: changeAllThreadsInProfRuntimeMethodStatus(+1); // In case any instrumented method is called while we perform profiler work on behalf of this thread.
319: recursiveInstrumentationDisabled = true;
320: writeTimeStampedEvent(THREADS_SUSPENDED, null, (char) 0); // FIXME: need a special event writing method or something
321: }
322:
323: protected static void clearDataStructures() {
324: ProfilerRuntimeCPU.clearDataStructures();
325: }
326: }
|