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.global;
042:
043: import org.netbeans.lib.profiler.global.TransactionalSupport;
044: import org.netbeans.lib.profiler.wireprotocol.InternalStatsResponse;
045:
046: /**
047: * Various data pertinent to the profiling session. Note that this class is used by both client and back end,
048: * although some data at the back end side is contained in the reduced form, to reduce memory usage.
049: *
050: * @author Tomas Hurka
051: * @author Misha Dmitriev
052: * @author Adrian Mos
053: */
054: public class ProfilingSessionStatus {
055: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
056:
057: public static final int N_TIMER_CONSTANTS = 5;
058:
059: //EJB Work: The index pointing to the class for code region instrumentation. It is the first class in the array of classes, as it should be the only class there.
060: public static final int CODE_REGION_CLASS_IDX = 0;
061:
062: //~ Instance fields ----------------------------------------------------------------------------------------------------------
063:
064: // This internal statistics record is filled in at the client side only upon application termination. It exists just to
065: // allow the user to obtain statistics after the target app/VM has already completed execution.
066: public InternalStatsResponse savedInternalStats;
067:
068: // Full (including minor version) target JDK version string - for example 1.5.0_12
069: public String fullTargetJDKVersionString;
070:
071: // Data for both code region and method group instrumentation. In the latter case this data is for the root method.
072: public String instrClassLoaderName;
073:
074: // Command line arguments of the JVM, and the full Java command, that includes main class (or .jar) plus its arguments
075: public String javaCommand;
076:
077: // Command line arguments of the JVM, and the full Java command, that includes main class (or .jar) plus its arguments
078: public String jvmArguments;
079:
080: // Target JDK version string, as returned by Platform.getJDKVersionString()
081: public String targetJDKVersionString;
082:
083: // Target machine OS, as returned by System.getProperty("os.name")
084: public String targetMachineOSName;
085:
086: // Important timer-related characteristics of instrumentation that we inject into the TA.
087: // Each array has 5 elements:
088: // o value when absolute timer is used;
089: // o value when thread-local CPU timer is used;
090: // o value of inner/outer absolute time when both timers are used;
091: // o value of inner/outer thread-local CPU time when both timers are used.
092: // o value when sampled instrumentation is used.
093: // ALL these values (including thread-local CPU ones, see comments in ProfilerCalibrator for explanation) are measured in
094: // absolute timer's counts (which is anything on Windows and nanoseconds on Solaris)
095: public double[] methodEntryExitCallTime = new double[N_TIMER_CONSTANTS]; // Elements #2 and #3 are actually the same
096: public double[] methodEntryExitInnerTime = new double[N_TIMER_CONSTANTS];
097: public double[] methodEntryExitOuterTime = new double[N_TIMER_CONSTANTS];
098: public ProfilingPointServerHandler[] profilingPointHandlers;
099: public int[] profilingPointIDs;
100: public long[] timerCountsInSecond = new long[2];
101: public boolean absoluteTimerOn;
102: public boolean remoteProfiling = false;
103: public boolean runningInAttachedMode; // true if attached to target JVM, false if started it from client
104: public boolean startProfilingPointsActive; // Indicates that inCallGraph should be set by profiling point handler code and NOT rootMethodEntry
105: public volatile boolean targetAppRunning;
106: public boolean threadCPUTimerOn;
107: public int currentInstrType;
108: public int instrEndLine;
109: public int instrScheme;
110: public int instrStartLine;
111:
112: // Server's timestamp of the moment when CPU results dump is requested. Used to display absolute call graph time correctly even
113: // when no absolute timestamps for methods are collected. Note that we could probably do the same thing on a per-thread basis
114: // and thus have thread CPU call graph time always available - but that seems to be too much work for now.
115: public long dumpAbsTimeStamp;
116:
117: // Target machnine maximum heap size returned by Runtime.maxMemory()
118: public long maxHeapSize;
119:
120: // Target machnine statup time returned by Timers.getCurrentTimeInCounts()
121: public long startupTimeInCounts;
122:
123: // Target machnine startup time returned by System.currentTimeMillis()
124: public long startupTimeMillis;
125: private TransactionalSupport transaction = new TransactionalSupport();
126: private int[] allocatedInstancesCount;
127: private int[] classLoaderIds;
128: private String[] classNames;
129: private String[] instrMethodClasses;
130: private boolean[] instrMethodInvoked;
131: private String[] instrMethodNames;
132: private String[] instrMethodSignatures;
133:
134: // Data for the case of object allocations instrumentation
135: private int nInstrClasses;
136:
137: // Data for the case of method group instrumentation
138: private int nInstrMethods;
139:
140: //~ Methods ------------------------------------------------------------------------------------------------------------------
141:
142: public int[] getAllocatedInstancesCount() {
143: beginTrans(false);
144:
145: try {
146: return allocatedInstancesCount;
147: } finally {
148: endTrans();
149: }
150: }
151:
152: public int[] getClassLoaderIds() {
153: beginTrans(false);
154:
155: try {
156: return classLoaderIds;
157: } finally {
158: endTrans();
159: }
160: }
161:
162: public String[] getClassNames() {
163: beginTrans(false);
164:
165: try {
166: return classNames;
167: } finally {
168: endTrans();
169: }
170: }
171:
172: public String[] getInstrMethodClasses() {
173: beginTrans(false);
174:
175: try {
176: return instrMethodClasses;
177: } finally {
178: endTrans();
179: }
180: }
181:
182: public boolean[] getInstrMethodInvoked() {
183: beginTrans(false);
184:
185: try {
186: return instrMethodInvoked;
187: } finally {
188: endTrans();
189: }
190: }
191:
192: public void setInstrMethodNames(String[] value) {
193: beginTrans(true);
194:
195: try {
196: instrMethodNames = value;
197: } finally {
198: endTrans();
199: }
200: }
201:
202: public String[] getInstrMethodNames() {
203: beginTrans(false);
204:
205: try {
206: return instrMethodNames;
207: } finally {
208: endTrans();
209: }
210: }
211:
212: public void setInstrMethodSignatures(String[] value) {
213: beginTrans(true);
214:
215: try {
216: instrMethodSignatures = value;
217: } finally {
218: endTrans();
219: }
220: }
221:
222: public String[] getInstrMethodSignatures() {
223: beginTrans(false);
224:
225: try {
226: return instrMethodSignatures;
227: } finally {
228: endTrans();
229: }
230: }
231:
232: public int getNInstrClasses() {
233: beginTrans(false);
234:
235: try {
236: return nInstrClasses;
237: } finally {
238: endTrans();
239: }
240: }
241:
242: public int getNInstrMethods() {
243: beginTrans(false);
244:
245: try {
246: return nInstrMethods;
247: } finally {
248: endTrans();
249: }
250: }
251:
252: public int getStartingMethodId() {
253: beginTrans(false);
254:
255: try {
256: if (nInstrMethods > 0) {
257: return nInstrMethods;
258: } else {
259: return 1;
260: }
261: } finally {
262: endTrans();
263: }
264: }
265:
266: public void setTimerTypes(boolean absolute, boolean threadCPU) {
267: absoluteTimerOn = absolute;
268: threadCPUTimerOn = threadCPU;
269: }
270:
271: public void beginTrans(boolean mutable) {
272: transaction.beginTrans(mutable);
273: }
274:
275: public boolean collectingTwoTimeStamps() {
276: return (absoluteTimerOn && threadCPUTimerOn);
277: }
278:
279: public void endTrans() {
280: transaction.endTrans();
281: }
282:
283: // ----------------------------------- Recursive method instrumentation management ------------------------------------------
284: public void resetInstrClassAndMethodInfo() {
285: beginTrans(true);
286:
287: try {
288: nInstrMethods = 0;
289: instrMethodClasses = instrMethodNames = instrMethodSignatures = null;
290: instrMethodInvoked = null;
291: nInstrClasses = 0;
292: allocatedInstancesCount = null;
293: classNames = new String[0];
294: } finally {
295: endTrans();
296: }
297: }
298:
299: // --------------------- Object allocations/liveness accounting management ------------------------------
300:
301: /**
302: * Takes the delta in the number of profiled classes and their names, and updates internal data structures.
303: * Should be used only at client side.
304: */
305: public void updateAllocatedInstancesCountInfoInClient(
306: String addedClassName) {
307: beginTrans(true);
308:
309: try {
310: if ((nInstrClasses == 0)
311: || (nInstrClasses == classNames.length)) {
312: boolean firstTime = (nInstrClasses == 0);
313: int newSize = firstTime ? 50
314: : ((nInstrClasses * 3) / 2);
315: int[] newAllocInstCount = new int[newSize];
316: String[] newClassNames = new String[newSize];
317:
318: if (!firstTime) {
319: System.arraycopy(allocatedInstancesCount, 0,
320: newAllocInstCount, 0, nInstrClasses);
321: System.arraycopy(classNames, 0, newClassNames, 0,
322: nInstrClasses);
323: }
324:
325: allocatedInstancesCount = newAllocInstCount;
326: classNames = newClassNames;
327: }
328:
329: classNames[nInstrClasses++] = addedClassName;
330: } finally {
331: endTrans();
332: }
333: }
334:
335: /** Same as above, but takes the total number of classes, and should be used only at server side. */
336: public void updateAllocatedInstancesCountInfoInServer(
337: int nTotalClasses) {
338: beginTrans(true);
339:
340: try {
341: boolean firstTime = (nInstrClasses == 0);
342: int oldSize = firstTime ? 0
343: : allocatedInstancesCount.length;
344: int newLen = nTotalClasses;
345:
346: if (oldSize < newLen) {
347: int newSize = newLen * 2;
348: int[] newAllocInstCount = new int[newSize];
349:
350: if (!firstTime) {
351: System.arraycopy(allocatedInstancesCount, 0,
352: newAllocInstCount, 0, nInstrClasses);
353: }
354:
355: allocatedInstancesCount = newAllocInstCount;
356: }
357:
358: nInstrClasses = nTotalClasses;
359: } finally {
360: endTrans();
361: }
362: }
363:
364: /**
365: * This method updates information about instrumented methods (class, name, signature, class loader for class etc.)
366: * for a group of methods. Can be used at both client and server side.
367: */
368: public void updateInstrMethodsInfo(int nClasses, int nMethods,
369: String[] classes, int[] loaderIds, int[] nMethodsInClass,
370: String[] methodNames, String[] methodSignatures,
371: boolean[] isMethodLeaf) {
372: if (nClasses == 0) {
373: return;
374: }
375:
376: beginTrans(true);
377:
378: try {
379: boolean firstTime = (nInstrMethods == 0);
380: int oldSize = firstTime ? 0
381: : ((classes != null) ? instrMethodNames.length
382: : instrMethodInvoked.length);
383: int newLen = nInstrMethods + nMethods;
384: int emptyCell = (firstTime ? 1 : 0);
385: int nAddedMethods = nMethods + emptyCell;
386:
387: if (oldSize < newLen) { // Grow arrays
388:
389: int newSize = newLen * 2;
390:
391: if (classes != null) { // Client side execution
392:
393: String[] newClasses = new String[newSize];
394: int[] newLoaderIds = new int[newSize];
395: String[] newMethods = new String[newSize];
396: String[] newSignatures = new String[newSize];
397:
398: if (!firstTime) {
399: System.arraycopy(instrMethodClasses, 0,
400: newClasses, 0, nInstrMethods);
401: System.arraycopy(classLoaderIds, 0,
402: newLoaderIds, 0, nInstrMethods);
403: System.arraycopy(instrMethodNames, 0,
404: newMethods, 0, nInstrMethods);
405: System.arraycopy(instrMethodSignatures, 0,
406: newSignatures, 0, nInstrMethods);
407: }
408:
409: instrMethodClasses = newClasses;
410: classLoaderIds = newLoaderIds;
411: instrMethodNames = newMethods;
412: instrMethodSignatures = newSignatures;
413: } else { // Server side execution
414:
415: boolean[] newMethodInvoked = new boolean[newSize];
416:
417: if (!firstTime) {
418: System.arraycopy(instrMethodInvoked, 0,
419: newMethodInvoked, 0, nInstrMethods);
420: }
421:
422: instrMethodInvoked = newMethodInvoked;
423: }
424: }
425:
426: if (classes != null) { // Client side execution
427:
428: if (firstTime) {
429: instrMethodClasses[0] = "Thread"; // NOI18N
430: classLoaderIds[0] = 0;
431: instrMethodNames[0] = ""; // NOI18N
432: instrMethodSignatures[0] = ""; // NOI18N
433: }
434:
435: int idx = nInstrMethods + emptyCell;
436:
437: for (int i = 0; i < nClasses; i++) {
438: for (int j = 0; j < nMethodsInClass[i]; j++) {
439: instrMethodClasses[idx] = classes[i];
440: classLoaderIds[idx] = loaderIds[i];
441: idx++;
442: }
443: }
444:
445: System.arraycopy(methodNames, 0, instrMethodNames,
446: nInstrMethods + emptyCell, nMethods);
447: System.arraycopy(methodSignatures, 0,
448: instrMethodSignatures, nInstrMethods
449: + emptyCell, nMethods);
450: } else { // Server side execution
451:
452: if (isMethodLeaf != null) {
453: System.arraycopy(isMethodLeaf, 0,
454: instrMethodInvoked, nInstrMethods
455: + emptyCell, nMethods);
456: }
457:
458: if (instrScheme == 2) {
459: for (int i = nInstrMethods; i < (nInstrMethods + nAddedMethods); i++) {
460: instrMethodInvoked[i] = true;
461: }
462: }
463: }
464:
465: nInstrMethods += nAddedMethods;
466: } finally {
467: endTrans();
468: }
469:
470: //System.err.println("*** Profiler Engine: nMethods = " + nMethods + ", nInstrMethods = " + nInstrMethods + ", instrMethodInvoked.len = " + instrMethodInvoked.length);
471: }
472:
473: /**
474: * This method adds information about a single instrumented method. Intended to be used on client side only.
475: */
476: public void updateInstrMethodsInfo(String className, int loaderId,
477: String methodName, String methodSignature) {
478: //System.err.println("*** Profiler Engine: updateInstr() called for " + className + "." + methodName + ", nis = " + nInstrMethods);
479: beginTrans(true);
480:
481: try {
482: boolean firstTime = (nInstrMethods == 0);
483: int oldSize = firstTime ? 0 : instrMethodNames.length;
484: int newLen = nInstrMethods + 1;
485: int emptyCell = (firstTime ? 1 : 0);
486: int nAddedMethods = 1 + emptyCell;
487:
488: if (oldSize < newLen) { // Grow arrays
489:
490: int newSize = newLen * 2;
491:
492: String[] newClasses = new String[newSize];
493: int[] newLoaderIds = new int[newSize];
494: String[] newMethods = new String[newSize];
495: String[] newSignatures = new String[newSize];
496:
497: if (!firstTime) {
498: System.arraycopy(instrMethodClasses, 0, newClasses,
499: 0, nInstrMethods);
500: System.arraycopy(classLoaderIds, 0, newLoaderIds,
501: 0, nInstrMethods);
502: System.arraycopy(instrMethodNames, 0, newMethods,
503: 0, nInstrMethods);
504: System.arraycopy(instrMethodSignatures, 0,
505: newSignatures, 0, nInstrMethods);
506: }
507:
508: instrMethodClasses = newClasses;
509: classLoaderIds = newLoaderIds;
510: instrMethodNames = newMethods;
511: instrMethodSignatures = newSignatures;
512: }
513:
514: if (firstTime) {
515: instrMethodClasses[0] = "Thread"; // NOI18N
516: classLoaderIds[0] = 0;
517: instrMethodNames[0] = ""; // NOI18N
518: instrMethodSignatures[0] = ""; // NOI18N
519: }
520:
521: int idx = nInstrMethods + emptyCell;
522: instrMethodClasses[idx] = className;
523: classLoaderIds[idx] = loaderId;
524: instrMethodNames[idx] = methodName;
525: instrMethodSignatures[idx] = methodSignature;
526:
527: nInstrMethods += nAddedMethods;
528: } finally {
529: endTrans();
530: }
531:
532: //System.err.println("*** Profiler Engine: nInstrMethods = " + nInstrMethods + ", instrMethodNames.len = " + instrMethodNames.length);
533: }
534: }
|