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.instrumentation;
042:
043: import org.netbeans.lib.profiler.ProfilerEngineSettings;
044: import org.netbeans.lib.profiler.classfile.DynamicClassInfo;
045: import org.netbeans.lib.profiler.classfile.PlaceholderClassInfo;
046: import org.netbeans.lib.profiler.client.RuntimeProfilingPoint;
047: import org.netbeans.lib.profiler.global.InstrumentationFilter;
048: import org.netbeans.lib.profiler.global.ProfilingSessionStatus;
049: import java.util.ArrayList;
050: import java.util.Enumeration;
051: import java.util.Hashtable;
052:
053: /**
054: * Base class for two "recursive" method scanners, implementing the "eager" and "lazy" transitive call subgraph revelation and
055: * instrumentation schemes. This class contains functionality used by both scaners.
056: *
057: * @author Tomas Hurka
058: * @author Misha Dmitriev
059: * @author Adrian Mos
060: */
061: public abstract class RecursiveMethodInstrumentor extends ClassManager {
062: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
063:
064: protected static class ReachableMethodPlaceholder extends
065: PlaceholderClassInfo {
066: //~ Instance fields ------------------------------------------------------------------------------------------------------
067:
068: protected ArrayList methodNamesAndSigs;
069:
070: //~ Constructors ---------------------------------------------------------------------------------------------------------
071:
072: ReachableMethodPlaceholder(String className, int classLoaderId) {
073: super (className, classLoaderId);
074: methodNamesAndSigs = new ArrayList();
075: }
076:
077: //~ Methods --------------------------------------------------------------------------------------------------------------
078:
079: public void registerReachableMethod(String methodName,
080: String methodSig) {
081: int nameIdx = methodNamesAndSigs.indexOf(methodName);
082:
083: if (nameIdx != -1) {
084: if (methodNamesAndSigs.get(nameIdx + 1).equals(
085: methodSig)) {
086: return;
087: }
088: }
089:
090: methodNamesAndSigs.add(methodName);
091: methodNamesAndSigs.add(methodSig);
092: }
093: }
094:
095: //~ Instance fields ----------------------------------------------------------------------------------------------------------
096:
097: protected Hashtable instrClasses = new Hashtable();
098: protected InstrumentationFilter instrFilter;
099: protected byte[] codeBytes;
100: protected boolean dontInstrumentEmptyMethods;
101: protected boolean dontScanGetterSetterMethods;
102: protected boolean instrumentClinit;
103: protected boolean instrumentSpawnedThreads;
104:
105: // This flag shows whether we have already instrumented the java.lang.reflect.Method.invoke() method to intercept all invocations.
106: // The current policy is to instrument it eagerly, in the very first method instrumentation packet. However, the actual
107: // interception can be turned on and off at run time on demand.
108: protected boolean reflectInvokeInstrumented = false;
109: protected int markerInjectionType; // Bytecode injections that are set dependent on the above
110: protected int nInstrClasses;
111: protected int nInstrMethods;
112: protected int normalInjectionType; // Bytecode injections that are set dependent on the above
113: protected int offset;
114: protected int rootInjectionType; // Bytecode injections that are set dependent on the above
115: RootMethods rootMethods;
116:
117: // remembered here because of profiling points
118: private ProfilerEngineSettings engineSettings;
119:
120: //~ Constructors -------------------------------------------------------------------------------------------------------------
121:
122: protected RecursiveMethodInstrumentor(
123: ProfilingSessionStatus status,
124: ProfilerEngineSettings settings) {
125: super (status);
126:
127: switch (status.currentInstrType) {
128: case INSTR_RECURSIVE_FULL:
129: normalInjectionType = INJ_RECURSIVE_NORMAL_METHOD;
130: rootInjectionType = INJ_RECURSIVE_ROOT_METHOD;
131: markerInjectionType = INJ_RECURSIVE_MARKER_METHOD;
132:
133: break;
134: case INSTR_RECURSIVE_SAMPLED:
135: normalInjectionType = INJ_RECURSIVE_SAMPLED_NORMAL_METHOD;
136: rootInjectionType = INJ_RECURSIVE_SAMPLED_ROOT_METHOD;
137: markerInjectionType = INJ_RECURSIVE_SAMPLED_MARKER_METHOD;
138:
139: break;
140: }
141:
142: reflectInvokeInstrumented = false;
143:
144: dontScanGetterSetterMethods = !settings
145: .getInstrumentGetterSetterMethods();
146: dontInstrumentEmptyMethods = !settings
147: .getInstrumentEmptyMethods();
148: instrumentSpawnedThreads = settings
149: .getInstrumentSpawnedThreads();
150: instrFilter = settings.getInstrumentationFilter();
151: engineSettings = settings;
152: }
153:
154: //~ Methods ------------------------------------------------------------------------------------------------------------------
155:
156: /**
157: * This method is called when some class containing an instrumentation root method is loaded by the VM (either has just
158: * been loaded, or the JFluid server, upon the user's command to initiate instrumentation, has detected that it has been
159: * loaded some time in the past). The JFluid server then sends a RootClassLoadedCommand to the tool. This command contains
160: * the information on all classes currently loaded by the VM - see the details in this method's header.
161: * This method should initialize instrumentation-related data structures, register given classes as loaded, and return
162: * the initial set of methods to instrument in the format given by createInstrumentedMethodPack().
163: */
164: abstract Object[] getInitialMethodsToInstrument(
165: String[] loadedClasses, int[] loadedClassLoaderIds,
166: byte[][] cachedClassFileBytes, RootMethods rootMethods);
167:
168: public abstract Object[] getMethodsToInstrumentUponClassLoad(
169: String className, int classLoaderId,
170: boolean threadInCallGraph);
171:
172: /** Methods below return method bytecodes to instrument upon specific events reported by the JFluid server. */
173: public abstract Object[] getMethodsToInstrumentUponMethodInvocation(
174: String className, int classLoaderId, String methodName,
175: String methodSignature);
176:
177: public abstract Object[] getMethodsToInstrumentUponReflectInvoke(
178: String className, int classLoaderId, String methodName,
179: String methodSignature);
180:
181: /** Called every time before a new round of instrumentation, caused by class load, method invoke, etc. */
182: protected void initInstrMethodData() {
183: instrClasses.clear();
184: nInstrClasses = nInstrMethods = 0;
185: instrumentClinit = false;
186: }
187:
188: protected static boolean rootClassNameIsReal(String rootClassName) {
189: return !rootClassName.equals(NO_CLASS_NAME);
190: }
191:
192: protected void addToSubclassList(DynamicClassInfo clazz,
193: DynamicClassInfo addedClassInfo) {
194: String super ClassName = clazz.getSuperclassName();
195: int loaderId = clazz.getLoaderId();
196: DynamicClassInfo super Class = javaClassForName(super ClassName,
197: loaderId);
198: clazz.setSuperClass(super Class);
199:
200: if ((super Class != null) && !clazz.isInterface()) {
201: super Class.addSubclass(addedClassInfo);
202:
203: if (addedClassInfo != null) {
204: findAndMarkOverridingMethodsReachable(super Class,
205: addedClassInfo);
206: }
207:
208: if (super ClassName != "java/lang/Object") {
209: addToSubclassList(super Class, addedClassInfo); // NOI18N
210: }
211: }
212:
213: String[] interfaces = clazz.getInterfaceNames();
214:
215: if (interfaces != null) {
216: for (int i = 0; i < interfaces.length; i++) {
217: DynamicClassInfo super Interface = javaClassForName(
218: interfaces[i], loaderId);
219: clazz.setSuperInterface(super Interface, i);
220:
221: if (super Interface != null) {
222: super Interface.addSubclass(addedClassInfo);
223:
224: if (addedClassInfo != null) {
225: findAndMarkOverridingMethodsReachable(
226: super Interface, addedClassInfo);
227: }
228:
229: addToSubclassList(super Interface, addedClassInfo);
230: }
231: }
232: }
233: }
234:
235: protected abstract void findAndMarkOverridingMethodsReachable(
236: DynamicClassInfo super Class, DynamicClassInfo subClass);
237:
238: protected abstract void processInvoke(DynamicClassInfo clazz,
239: boolean virtualCall, int index);
240:
241: protected final int at(int index) {
242: return codeBytes[offset + index] & 0xFF;
243: }
244:
245: /**
246: * Given the table at the specified index, return the specified entry
247: */
248: protected final long intAt(int tbl, int entry) {
249: int base = tbl + (entry << 2);
250:
251: return (codeBytes[base] << 24)
252: | ((codeBytes[base + 1] & 0xFF) << 16)
253: | ((codeBytes[base + 2] & 0xFF) << 8)
254: | (codeBytes[base + 3] & 0xFF);
255: }
256:
257: protected void scanMethod(DynamicClassInfo clazz, int index) {
258: byte[] bytecode = clazz.getMethodBytecode(index);
259: scanBytecode(clazz, bytecode);
260: }
261:
262: protected final int shortAt(int index) {
263: int base = offset + index;
264:
265: return ((codeBytes[base] & 0xFF) << 8)
266: | (codeBytes[base + 1] & 0xFF);
267: }
268:
269: /**
270: * This method is used either to normally process the bytecodes of a method, in which case clazz != null and the return
271: * result is ignored. If clazz == null, then returns false upon encountering the first invoke bytecode, and true if there
272: * are no invokes, i.e. it's a leaf method.
273: */
274: protected boolean scanBytecode(DynamicClassInfo clazz, byte[] code) {
275: codeBytes = code;
276:
277: for (offset = 0; offset < codeBytes.length;) {
278: int opcode = at(0);
279:
280: if (opcode == opc_wide) {
281: opcode = at(1);
282:
283: if (((opcode >= opc_iload) && (opcode <= opc_aload))
284: || ((opcode >= opc_istore) && (opcode <= opc_astore))
285: || (opcode == opc_ret)) {
286: offset += 4;
287: } else if (opcode == opc_iinc) {
288: offset += 6;
289: } else {
290: offset++;
291: }
292: } else {
293: switch (opcode) {
294: case opc_tableswitch: {
295: int tbl = (offset + 1 + 3) & (~3); // four byte boundry
296: long default_skip = intAt(tbl, 0);
297: long low = intAt(tbl, 1);
298: long high = intAt(tbl, 2);
299: tbl += (3 << 2); // three int header
300: offset = tbl + (int) ((high - low + 1) << 2);
301:
302: break;
303: }
304: case opc_lookupswitch: {
305: int tbl = (offset + 1 + 3) & (~3); // four byte boundry
306: long default_skip = intAt(tbl, 0);
307: int npairs = (int) intAt(tbl, 1);
308: int nints = npairs * 2;
309: tbl += (2 << 2); // two int header
310: offset = tbl + (nints << 2);
311:
312: break;
313: }
314: case opc_invokevirtual:
315: case opc_invokespecial:
316: case opc_invokestatic: {
317: if (clazz == null) {
318: return false; // Using scanBytecode() as a leaf-method checker
319: }
320:
321: int index = shortAt(1);
322: processInvoke(clazz, (opcode == opc_invokevirtual),
323: index);
324: offset += 3;
325:
326: break;
327: }
328: case opc_invokeinterface: {
329: if (clazz == null) {
330: return false; // Using scanBytecode() as a leaf-method checker
331: }
332:
333: int index = shortAt(1);
334: processInvoke(clazz, true, index);
335: offset += 5;
336:
337: break;
338: }
339: default:
340: offset += opc_length[opcode];
341:
342: break;
343: }
344: }
345: }
346:
347: return true;
348: }
349:
350: protected abstract boolean tryInstrumentSpawnedThreads(
351: DynamicClassInfo clazz);
352:
353: protected static boolean isEmptyMethod(byte[] code) {
354: return (code.length == 1); // Can't be anything but "return"
355: }
356:
357: protected static boolean isGetterSetterMethod(byte[] code) {
358: // Getter (accessor) method:
359: // 0 aload_0; 1 getfield x ; 4 (i..a)return. Parameter size = 1
360: // Setter method:
361: // 0 aload_0; 1 (i..a)load_1; 2 putfield x; 5 return. Parameter size = 2
362: if (code.length == 5) {
363: if (((code[0] & 0xFF) == opc_aload_0)
364: && ((code[1] & 0xFF) == opc_getfield)
365: && (((code[4] & 0xFF) >= opc_ireturn) && ((code[4] & 0xFF) <= opc_areturn))) {
366: return true;
367: }
368: } else if (code.length == 6) {
369: if (((code[0] & 0xFF) == opc_aload_0)
370: && (((code[1] & 0xFF) >= opc_iload_1) && ((code[1] & 0xFF) <= opc_aload_1))
371: && ((code[2] & 0xFF) == opc_putfield)
372: && ((code[5] & 0xFF) == opc_return)) {
373: return true;
374: }
375: }
376:
377: return false;
378: }
379:
380: protected boolean isLeafMethod(byte[] code) {
381: return scanBytecode(null, code);
382: }
383:
384: /** Create a multi-class packet of instrumented methods or classes */
385: protected Object[] createInstrumentedMethodPack() {
386: if (nInstrMethods == 0) {
387: return null;
388: }
389:
390: return createInstrumentedMethodPack15();
391: }
392:
393: /** Create a single-class packet of instrumented methods or classes in response to a class load event. */
394: protected Object[] createInstrumentedMethodPack(
395: DynamicClassInfo clazz) {
396: if (nInstrMethods == 0) {
397: return null;
398: }
399:
400: if (clazz.isInterface()) {
401: return null;
402: }
403:
404: return createInstrumentedMethodPack15(clazz);
405: }
406:
407: protected void markAllMethodsMarker(DynamicClassInfo clazz) {
408: clazz.setAllMethodsMarkers();
409: }
410:
411: protected void markAllMethodsRoot(DynamicClassInfo clazz) {
412: clazz.setAllMethodsRoots();
413: }
414:
415: protected void markClassAndMethodForInstrumentation(
416: DynamicClassInfo clazz, int methodIdx) {
417: if ((status.getStartingMethodId() + nInstrMethods) < 65535) {
418: String classNameAndLoader = clazz.getNameAndLoader();
419:
420: if (!instrClasses.containsKey(classNameAndLoader)) {
421: instrClasses.put(classNameAndLoader, clazz);
422: nInstrClasses++;
423: }
424:
425: nInstrMethods++;
426: } else { // Can't instrument more than 64K methods - mark this method as already instrumented
427: clazz.setMethodInstrumented(methodIdx);
428: }
429: }
430:
431: protected boolean markMethod(DynamicClassInfo clazz, int rootMethod) {
432: String rootMethodName = rootMethods.methodNames[rootMethod];
433: String rootMethodSignature = rootMethods.methodSignatures[rootMethod];
434: boolean isMarkerMethod = rootMethods.markerMethods[rootMethod];
435: int rootMethodIdx = clazz.getMethodIndex(rootMethodName,
436: rootMethodSignature);
437:
438: if (rootMethodIdx == -1) {
439: return false;
440: }
441:
442: if (isMarkerMethod) {
443: clazz.setMethodMarker(rootMethodIdx);
444: } else {
445: clazz.setMethodRoot(rootMethodIdx);
446: }
447:
448: return true;
449: }
450:
451: protected boolean markMethodMarker(DynamicClassInfo clazz,
452: String rootMethodName, String rootMethodSignature) {
453: int rootMethodIdx = clazz.getMethodIndex(rootMethodName,
454: rootMethodSignature);
455:
456: if (rootMethodIdx == -1) {
457: return false;
458: }
459:
460: clazz.setMethodMarker(rootMethodIdx);
461:
462: return true;
463: }
464:
465: protected boolean markMethodRoot(DynamicClassInfo clazz,
466: String rootMethodName, String rootMethodSignature) {
467: int rootMethodIdx = clazz.getMethodIndex(rootMethodName,
468: rootMethodSignature);
469:
470: if (rootMethodIdx == -1) {
471: return false;
472: }
473:
474: clazz.setMethodRoot(rootMethodIdx);
475:
476: return true;
477: }
478:
479: protected boolean matchesWildcard(String wildcard,
480: String loadedClassName) {
481: // System.err.println("Matches wildcard: "+loadedClassName+", wild: "+wildcard + " : " + (loadedClassName.startsWith(wildcard) && (loadedClassName.indexOf('/', wildcard.length()) == -1)));
482: return loadedClassName.startsWith(wildcard)
483: && (loadedClassName.indexOf('/', wildcard.length()) == -1); // NOI18N
484: }
485:
486: //---------------------------- Private implementation of instrumentation data packing ---------------------------
487:
488: /** Create a multi-class packet of instrumented 1.5-style data */
489: private Object[] createInstrumentedMethodPack15() {
490: int reflectMethodClassIdx = -1;
491:
492: if (!reflectInvokeInstrumented) {
493: // Check if java.lang.reflect.Method is already among classes to instrument
494: int idx = 0;
495:
496: for (Enumeration e = instrClasses.elements(); e
497: .hasMoreElements(); idx++) {
498: DynamicClassInfo clazz = (DynamicClassInfo) e
499: .nextElement();
500:
501: if (clazz.getName() == JAVA_LANG_REFLECT_METHOD_SLASHED_CLASS_NAME) {
502: reflectMethodClassIdx = idx;
503:
504: break;
505: }
506: }
507:
508: if (reflectMethodClassIdx == -1) {
509: nInstrClasses++;
510: }
511: }
512:
513: String[] instrMethodClasses = new String[nInstrClasses];
514: int[] instrClassLoaderIds = new int[nInstrClasses];
515: boolean[] instrMethodLeaf = new boolean[nInstrMethods];
516: byte[][] replacementClassFileBytes = new byte[nInstrClasses][];
517: int methodId = status.getStartingMethodId();
518: int classIdx = 0;
519: int methodIdx = 0;
520:
521: for (Enumeration e = instrClasses.elements(); e
522: .hasMoreElements();) {
523: DynamicClassInfo clazz = (DynamicClassInfo) e.nextElement();
524: int nMethods = clazz.getMethodNames().length;
525: instrMethodClasses[classIdx] = clazz.getName().replace('/',
526: '.').intern(); // NOI18N
527:
528: boolean hasRootMethods = clazz
529: .hasUninstrumentedRootMethods();
530: boolean hasMarkerMethods = clazz
531: .hasUninstrumentedMarkerMethods();
532: DynamicConstantPoolExtension.getCPFragment(clazz,
533: normalInjectionType);
534:
535: if (hasRootMethods) {
536: DynamicConstantPoolExtension.getCPFragment(clazz,
537: rootInjectionType);
538: }
539:
540: if (hasMarkerMethods) {
541: DynamicConstantPoolExtension.getCPFragment(clazz,
542: markerInjectionType);
543: }
544:
545: int imInClass = 0;
546: byte[][] replacementMethodInfos = new byte[nMethods][];
547: RuntimeProfilingPoint[] pointsForClass = getRuntimeProfilingPoints(
548: engineSettings.getRuntimeProfilingPoints(), clazz);
549:
550: //System.err.println("CLazz: "+clazz.getName());
551: for (int i = 0; i < nMethods; i++) {
552: // FIXME: issue 68840: An overriden method overriding with subclass of return type is instrumented twice
553: // http://profiler.netbeans.org/issues/show_bug.cgi?id=68840
554: // a method whose return type is not exact match as the method which it implements/overrides would be listed
555: // and processed twice, leading to double instrumentation
556:
557: //System.err.println("Method: "+clazz.getMethodName(i)+" " + clazz.getMethodSignature(i));
558: RuntimeProfilingPoint[] points = getRuntimeProfilingPoints(
559: pointsForClass, i);
560:
561: if (!clazz.isMethodInstrumented(i)) {
562: if (clazz.isMethodReachable(i)
563: && !clazz.isMethodUnscannable(i)) {
564: clazz.setMethodInstrumented(i);
565: instrMethodLeaf[methodIdx] = clazz
566: .isMethodLeaf(i);
567: //System.err.println(">>>1 For method " + clazz.getName() + "." + clazz.getMethodName(i) + clazz.getMethodSignature(i) + " gonna use methodId = " + methodId);
568: replacementMethodInfos[i] = InstrumentationFactory
569: .instrumentMethod(clazz, i,
570: normalInjectionType,
571: rootInjectionType,
572: markerInjectionType,
573: methodId++, points);
574: clazz.saveMethodInfo(i,
575: replacementMethodInfos[i]);
576:
577: status.updateInstrMethodsInfo(
578: instrMethodClasses[classIdx], clazz
579: .getLoaderId(), clazz
580: .getMethodNames()[i], clazz
581: .getMethodSignatures()[i]);
582: imInClass++;
583: methodIdx++;
584: } else if (points.length > 0) {
585: replacementMethodInfos[i] = InstrumentationFactory
586: .instrumentAsProiflePointHitMethod(
587: clazz, i, normalInjectionType,
588: points);
589: clazz.saveMethodInfo(i,
590: replacementMethodInfos[i]);
591: }
592: } else {
593: replacementMethodInfos[i] = clazz.getMethodInfo(i); // Will return the previously instrumented methodInfo
594: imInClass++;
595: }
596: }
597:
598: instrumentServletDoMethods(clazz, replacementMethodInfos);
599:
600: if (imInClass > 0) {
601: if (hasRootMethods) {
602: clazz.setHasUninstrumentedRootMethods(false);
603: }
604:
605: if (hasMarkerMethods) {
606: clazz.setHasUninstrumentedMarkerMethods(false);
607: }
608:
609: DynamicConstantPoolExtension wholeECP = DynamicConstantPoolExtension
610: .getAllAddedCPFragments(clazz);
611: int nAddedCPEntries = wholeECP.getNEntries();
612: byte[] addedCPContents = wholeECP.getContents();
613: replacementClassFileBytes[classIdx] = ClassRewriter
614: .rewriteClassFile(clazz,
615: replacementMethodInfos,
616: nAddedCPEntries, addedCPContents);
617: }
618:
619: instrClassLoaderIds[classIdx] = clazz.getLoaderId();
620: classIdx++;
621: }
622:
623: if (!reflectInvokeInstrumented) { // Special instrumentation of java.lang.reflect.Method.invoke()
624:
625: DynamicClassInfo clazz = javaClassForName(
626: JAVA_LANG_REFLECT_METHOD_DOTTED_CLASS_NAME, 0);
627: int nMethods = clazz.getMethodNames().length;
628: byte[][] replacementMethodInfos = new byte[nMethods][];
629:
630: if (reflectMethodClassIdx == -1) {
631: instrMethodClasses[classIdx] = JAVA_LANG_REFLECT_METHOD_DOTTED_CLASS_NAME;
632: instrClassLoaderIds[classIdx] = 0;
633: } else {
634: classIdx = reflectMethodClassIdx;
635:
636: for (int i = 0; i < nMethods; i++) {
637: replacementMethodInfos[i] = clazz.getMethodInfo(i);
638: }
639: }
640:
641: int idx = clazz.getMethodIndex(INVOKE_METHOD_NAME,
642: INVOKE_METHOD_SIGNATURE);
643: DynamicConstantPoolExtension.getCPFragment(clazz,
644: INJ_REFLECT_METHOD_INVOKE);
645:
646: replacementMethodInfos[idx] = InstrumentationFactory
647: .instrumentAsReflectInvokeMethod(clazz, idx);
648:
649: DynamicConstantPoolExtension wholeECP = DynamicConstantPoolExtension
650: .getAllAddedCPFragments(clazz);
651: int nAddedCPEntries = wholeECP.getNEntries();
652: byte[] addedCPContents = wholeECP.getContents();
653: replacementClassFileBytes[classIdx] = ClassRewriter
654: .rewriteClassFile(clazz, replacementMethodInfos,
655: nAddedCPEntries, addedCPContents);
656:
657: clazz.saveMethodInfo(idx, replacementMethodInfos[idx]);
658: reflectInvokeInstrumented = true;
659: }
660:
661: return new Object[] { instrMethodClasses, instrClassLoaderIds,
662: instrMethodLeaf, replacementClassFileBytes };
663: }
664:
665: /** Create a single-class packet of instrumented 1.5-style data */
666: private Object[] createInstrumentedMethodPack15(
667: DynamicClassInfo clazz) {
668: String[] methodNames = clazz.getMethodNames();
669:
670: String[] instrMethodClasses = new String[1];
671: int[] instrClassLoaderIds = new int[] { clazz.getLoaderId() };
672: String className = clazz.getName().replace('/', '.').intern(); // NOI18N
673: instrMethodClasses[0] = className;
674:
675: boolean[] instrMethodLeaf = new boolean[nInstrMethods];
676: int methodId = status.getStartingMethodId();
677: int methodIdx = 0;
678:
679: boolean hasRootMethods = clazz.hasUninstrumentedRootMethods();
680: boolean hasMarkerMethods = clazz
681: .hasUninstrumentedMarkerMethods();
682: boolean isReflectMethodClass = clazz.getName() == JAVA_LANG_REFLECT_METHOD_SLASHED_CLASS_NAME;
683: DynamicConstantPoolExtension.getCPFragment(clazz,
684: normalInjectionType);
685:
686: if (hasRootMethods) {
687: DynamicConstantPoolExtension.getCPFragment(clazz,
688: rootInjectionType);
689: }
690:
691: if (hasMarkerMethods) {
692: DynamicConstantPoolExtension.getCPFragment(clazz,
693: markerInjectionType);
694: }
695:
696: byte[][] replacementMethodInfos = new byte[methodNames.length][];
697: RuntimeProfilingPoint[] pointsForClass = getRuntimeProfilingPoints(
698: engineSettings.getRuntimeProfilingPoints(), clazz);
699:
700: for (int i = 0; i < methodNames.length; i++) {
701: RuntimeProfilingPoint[] points = getRuntimeProfilingPoints(
702: pointsForClass, i);
703:
704: if (!clazz.isMethodInstrumented(i)) {
705: if ((clazz.isMethodReachable(i) && !clazz
706: .isMethodUnscannable(i))
707: || (instrumentClinit && (methodNames[i] == "<clinit>"))) { // NOI18N
708: clazz.setMethodInstrumented(i);
709: instrMethodLeaf[methodIdx++] = clazz
710: .isMethodLeaf(i);
711: //System.err.println("!!!>>>2 For method " + clazz.getName() + "." + clazz.getMethodName(i) + clazz.getMethodSignature(i) + " gonna use methodId = " + methodId);
712: replacementMethodInfos[i] = InstrumentationFactory
713: .instrumentMethod(clazz, i,
714: normalInjectionType,
715: rootInjectionType,
716: markerInjectionType, methodId++,
717: points);
718: clazz.saveMethodInfo(i, replacementMethodInfos[i]);
719: status.updateInstrMethodsInfo(
720: instrMethodClasses[0], clazz.getLoaderId(),
721: clazz.getMethodNames()[i], clazz
722: .getMethodSignatures()[i]);
723: } else if (points.length > 0) {
724: replacementMethodInfos[i] = InstrumentationFactory
725: .instrumentAsProiflePointHitMethod(clazz,
726: i, normalInjectionType, points);
727: clazz.saveMethodInfo(i, replacementMethodInfos[i]);
728: }
729: } else {
730: replacementMethodInfos[i] = clazz.getMethodInfo(i); // Will return the previously instrumented methodInfo
731: }
732: }
733:
734: instrumentServletDoMethods(clazz, replacementMethodInfos);
735:
736: if (hasRootMethods) {
737: clazz.setHasUninstrumentedRootMethods(false);
738: }
739:
740: if (hasMarkerMethods) {
741: clazz.setHasUninstrumentedMarkerMethods(false);
742: }
743:
744: DynamicConstantPoolExtension wholeECP = DynamicConstantPoolExtension
745: .getAllAddedCPFragments(clazz);
746: int nAddedCPEntries = wholeECP.getNEntries();
747: byte[] addedCPContents = wholeECP.getContents();
748: byte[] replacementClassFile = ClassRewriter.rewriteClassFile(
749: clazz, replacementMethodInfos, nAddedCPEntries,
750: addedCPContents);
751:
752: return new Object[] { instrMethodClasses, instrClassLoaderIds,
753: instrMethodLeaf, new byte[][] { replacementClassFile } };
754: }
755:
756: private void instrumentServletDoMethods(DynamicClassInfo clazz,
757: byte[][] replacementMethodInfos) {
758: if (!Boolean
759: .getBoolean("org.netbeans.lib.profiler.servletTracking")) { // NOI18N
760:
761: return;
762: }
763:
764: if (clazz.isServletDoMethodScanned()) {
765: return;
766: }
767:
768: clazz.setServletDoMethodScanned();
769:
770: if (!clazz.isSubclassOf(HandleServletDoMethodCallInjector
771: .getClassName())) {
772: return;
773: }
774:
775: DynamicConstantPoolExtension.getCPFragment(clazz,
776: INJ_SERVLET_DO_METHOD);
777:
778: String[] methods = HandleServletDoMethodCallInjector
779: .getMethodNames();
780: String[] sigs = HandleServletDoMethodCallInjector
781: .getMethodSignatures();
782:
783: for (int i = 0; i < methods.length; i++) {
784: int midx = clazz.getMethodIndex(methods[i], sigs[i]);
785:
786: if (midx != -1) {
787: replacementMethodInfos[midx] = InstrumentationFactory
788: .instrumentAsServletDoMethod(clazz, midx);
789: clazz
790: .saveMethodInfo(midx,
791: replacementMethodInfos[midx]);
792: }
793: }
794: }
795: }
|