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.classfile;
042:
043: import java.io.IOException;
044: import java.util.ArrayList;
045:
046: /**
047: * A representation of a binary Java class that contains information for the class file itself, plus various status
048: * bits used for proper instrumentation state accounting in JFluid.
049: *
050: * @author Tomas Hurka
051: * @author Misha Dmitirev
052: */
053: public class DynamicClassInfo extends ClassInfo {
054: //~ Instance fields ----------------------------------------------------------------------------------------------------------
055:
056: private ArrayList subclasses; // Subclasses as DynamicClassInfos
057: private DynamicClassInfo super Class; // Superclass as a DynamicClassInfo (just name is inconvenient when multiple classloaders are used)
058: private String classFileLocation; // Directory or .jar file where the .class file is located.
059: private int[] baseCPoolCount;
060: private char[] instrMethodIds; // Ids assigned to instrumented methods, 0 for uninstrumented methods
061: private DynamicClassInfo[] interfacesDCI; // Ditto for superinterfaces
062:
063: // Data used by our call graph revelation mechanism, to mark classes/methods according to their reachability,
064: // scannability, etc. properties
065: private char[] methodScanStatus;
066:
067: // On JDK 1.5, we save methodinfos for all instrumented methods (until they are deinstrumented), so when methods from
068: // same class are instrumented one-by-one and redefineClasses is used for each of them, we don't have to regenerate the
069: // code for each previously instrumented method over and over again.
070: private byte[][] modifiedAndSavedMethodInfos;
071: private boolean allMethodsMarkers = false;
072: private boolean allMethodsRoots = false;
073: private boolean hasUninstrumentedMarkerMethods;
074: private boolean hasUninstrumentedRootMethods;
075: private boolean isLoaded;
076:
077: // true if class was scanned for for HttpServlet.do*() methods
078: private boolean servletDoMethodScanned;
079:
080: /** Data for supporting both 1.4.2-style constant pool incremental extending and 1.5-style redefinition as a whole */
081: private int currentCPoolCount; // The current number of entries in the cpool of this class (increased due to instrumentation)
082: // When we add entries to cpool for a particular injection type, its size before entries are added (base count) is stored
083: // in this array's element corresponding to this injection type number (e.g. INJ_RECURSIVE_NORMAL_METHOD or INJ_CODE_REGION).
084: private int nInstrumentedMethods;
085:
086: //~ Constructors -------------------------------------------------------------------------------------------------------------
087:
088: public DynamicClassInfo(String className, int loaderId,
089: String classFileLocation) throws IOException,
090: ClassFormatError {
091: super (className, loaderId);
092: this .classFileLocation = classFileLocation;
093:
094: byte[] classFileBytes = getClassFileBytes();
095:
096: try {
097: (new ClassFileParser())
098: .parseClassFile(classFileBytes, this );
099:
100: if (!className.equals(name)) {
101: throw new ClassFormatError(
102: "Mismatch between name in .class file and location for "
103: + className // NOI18N
104: + "\nYour class path setting may be incorrect."); // NOI18N
105: }
106: } catch (ClassFileParser.ClassFileReadException ex) {
107: throw new ClassFormatError(ex.getMessage());
108: }
109:
110: methodScanStatus = new char[methodNames.length];
111: instrMethodIds = new char[methodNames.length];
112: currentCPoolCount = origCPoolCount;
113: baseCPoolCount = new int[INJ_MAXNUMBER];
114:
115: for (int i = 0; i < INJ_MAXNUMBER; i++) {
116: baseCPoolCount[i] = -1;
117: }
118: }
119:
120: //~ Methods ------------------------------------------------------------------------------------------------------------------
121:
122: public void setAllMethodsMarkers() {
123: allMethodsMarkers = true;
124: hasUninstrumentedMarkerMethods = true;
125: }
126:
127: public boolean getAllMethodsMarkers() {
128: return allMethodsMarkers;
129: }
130:
131: public void setAllMethodsRoots() {
132: allMethodsRoots = true;
133: hasUninstrumentedRootMethods = true;
134: }
135:
136: public boolean getAllMethodsRoots() {
137: return allMethodsRoots;
138: }
139:
140: public void setBaseCPoolCount(int injType, int v) {
141: baseCPoolCount[injType] = v;
142: }
143:
144: public int getBaseCPoolCount(int injType) {
145: return baseCPoolCount[injType];
146: }
147:
148: public int getBaseCPoolCountLen() {
149: return baseCPoolCount.length;
150: }
151:
152: public byte[] getClassFileBytes() throws IOException {
153: return ClassFileCache.getDefault().getClassFile(name,
154: classFileLocation);
155: }
156:
157: public String getClassFileLocation() {
158: return classFileLocation;
159: } // TODO CHECK: unused method
160:
161: public void setCurrentCPoolCount(int v) {
162: currentCPoolCount = v;
163: }
164:
165: public int getCurrentCPoolCount() {
166: return currentCPoolCount;
167: }
168:
169: public int getExceptionTableStartOffsetInMethodInfo(int idx) {
170: if ((modifiedAndSavedMethodInfos != null)
171: && (modifiedAndSavedMethodInfos[idx] != null)) {
172: int bcLen = getBCLenForModifiedAndSavedMethodInfo(idx);
173:
174: return methodBytecodesOffsets[idx] + bcLen;
175: } else {
176: return super .getExceptionTableStartOffsetInMethodInfo(idx);
177: }
178: }
179:
180: public void setHasUninstrumentedMarkerMethods(boolean v) {
181: hasUninstrumentedMarkerMethods = v;
182: }
183:
184: public void setHasUninstrumentedRootMethods(boolean v) {
185: hasUninstrumentedRootMethods = v;
186: }
187:
188: public void setInstrMethodId(int i, int id) {
189: instrMethodIds[i] = (char) id;
190: }
191:
192: public char getInstrMethodId(int i) {
193: return instrMethodIds[i];
194: } // TODO CHECK: unused method
195:
196: public void setLoaded(boolean loaded) {
197: isLoaded = loaded;
198: }
199:
200: public boolean isLoaded() {
201: return isLoaded;
202: }
203:
204: public byte[] getMethodBytecode(int idx) {
205: if ((modifiedAndSavedMethodInfos != null)
206: && (modifiedAndSavedMethodInfos[idx] != null)) {
207: byte[] methodInfo = modifiedAndSavedMethodInfos[idx];
208: int bcLen = getBCLenForModifiedAndSavedMethodInfo(idx);
209: byte[] ret = new byte[bcLen];
210: System.arraycopy(methodInfo, methodBytecodesOffsets[idx],
211: ret, 0, bcLen);
212:
213: return ret;
214: } else {
215: return super .getMethodBytecode(idx);
216: }
217: }
218:
219: public int getMethodBytecodesLength(int idx) {
220: if ((modifiedAndSavedMethodInfos != null)
221: && (modifiedAndSavedMethodInfos[idx] != null)) {
222: return getBCLenForModifiedAndSavedMethodInfo(idx);
223: } else {
224: return super .getMethodBytecodesLength(idx);
225: }
226: }
227:
228: public byte[] getMethodInfo(int idx) {
229: if ((modifiedAndSavedMethodInfos != null)
230: && (modifiedAndSavedMethodInfos[idx] != null)) {
231: return modifiedAndSavedMethodInfos[idx];
232: } else {
233: return super .getMethodInfo(idx);
234: }
235: }
236:
237: public int getMethodInfoLength(int idx) {
238: if ((modifiedAndSavedMethodInfos != null)
239: && (modifiedAndSavedMethodInfos[idx] != null)) {
240: return modifiedAndSavedMethodInfos[idx].length;
241: } else {
242: return super .getMethodInfoLength(idx);
243: }
244: }
245:
246: public void setMethodInstrumented(int i) {
247: methodScanStatus[i] |= 8;
248: nInstrumentedMethods++;
249: }
250:
251: public boolean isMethodInstrumented(int i) {
252: return (methodScanStatus[i] & 8) != 0;
253: }
254:
255: public void setMethodLeaf(int i) {
256: methodScanStatus[i] |= 16;
257: }
258:
259: public boolean isMethodLeaf(int i) {
260: return (methodScanStatus[i] & 16) != 0;
261: }
262:
263: public void setMethodMarker(int i) {
264: methodScanStatus[i] |= 256;
265: hasUninstrumentedMarkerMethods = true;
266: }
267:
268: public boolean isMethodMarker(int i) {
269: return allMethodsMarkers || ((methodScanStatus[i] & 256) != 0);
270: }
271:
272: public void setMethodReachable(int i) {
273: methodScanStatus[i] |= 1;
274: }
275:
276: public boolean isMethodReachable(int i) {
277: return (methodScanStatus[i] & 1) != 0;
278: }
279:
280: public void setMethodRoot(int i) {
281: methodScanStatus[i] |= 64;
282: hasUninstrumentedRootMethods = true;
283: }
284:
285: public boolean isMethodRoot(int i) {
286: return allMethodsRoots || ((methodScanStatus[i] & 64) != 0);
287: }
288:
289: public void setMethodScanned(int i) {
290: methodScanStatus[i] |= 4; /* hasUninstrumentedScannedMethods = true; */
291: }
292:
293: public boolean isMethodScanned(int i) {
294: return (methodScanStatus[i] & 4) != 0;
295: }
296:
297: public void setMethodSpecial(int i) {
298: methodScanStatus[i] |= 128;
299: }
300:
301: public boolean isMethodSpecial(int i) {
302: return (methodScanStatus[i] & 128) != 0;
303: }
304:
305: public void setMethodUnscannable(int i) {
306: methodScanStatus[i] |= 2;
307: }
308:
309: public boolean isMethodUnscannable(int i) {
310: return (methodScanStatus[i] & 2) != 0;
311: }
312:
313: public void setMethodVirtual(int i) {
314: methodScanStatus[i] |= 32;
315: }
316:
317: public boolean isMethodVirtual(int i) {
318: return (methodScanStatus[i] & 32) != 0;
319: }
320:
321: public byte[] getOrigMethodInfo(int idx) {
322: return super .getMethodInfo(idx);
323: }
324:
325: public int getOrigMethodInfoLength(int idx) {
326: return super .getMethodInfoLength(idx);
327: }
328:
329: public void setServletDoMethodScanned() {
330: servletDoMethodScanned = true;
331: }
332:
333: public boolean isServletDoMethodScanned() {
334: return servletDoMethodScanned;
335: }
336:
337: public boolean isSubclassOf(String super Class) {
338: if (getName() == super Class) {
339: return true;
340: }
341:
342: DynamicClassInfo sc = getSuperClass();
343:
344: if ((sc == null) || (sc == this )) {
345: return false;
346: }
347:
348: return sc.isSubclassOf(super Class);
349: }
350:
351: public ArrayList getSubclasses() {
352: return subclasses;
353: }
354:
355: public void setSuperClass(DynamicClassInfo sc) {
356: super Class = sc;
357: }
358:
359: public DynamicClassInfo getSuperClass() {
360: return super Class;
361: }
362:
363: public void setSuperInterface(DynamicClassInfo si, int idx) {
364: if (interfacesDCI == null) {
365: interfacesDCI = new DynamicClassInfo[interfaces.length];
366: }
367:
368: interfacesDCI[idx] = si;
369: }
370:
371: public DynamicClassInfo[] getSuperInterfaces() {
372: return interfacesDCI;
373: }
374:
375: public void addSubclass(DynamicClassInfo subclass) {
376: if (subclasses == null) {
377: if (name == "java/lang/Object") {
378: subclasses = new ArrayList(500); // NOI18N
379: } else {
380: subclasses = new ArrayList();
381: }
382: }
383:
384: subclasses.add(subclass);
385: }
386:
387: public boolean hasInstrumentedMethods() {
388: return (nInstrumentedMethods > 0);
389: }
390:
391: public boolean hasUninstrumentedMarkerMethods() {
392: return hasUninstrumentedMarkerMethods;
393: }
394:
395: /*
396: public boolean hasUninstrumentedScannedMethods() { return hasUninstrumentedScannedMethods; } // TODO CHECK: unused method
397: public void setHasUninstrumentedScannedMethods(boolean v) { hasUninstrumentedScannedMethods = v; } // TODO CHECK: unused method
398: */
399: public boolean hasUninstrumentedRootMethods() {
400: return hasUninstrumentedRootMethods;
401: }
402:
403: /**
404: * Note that this method uses the name of the interface in question intentionally - its (few) callers
405: * benefit from providing the name rather than a DynamicClassInfo.
406: */
407: public boolean implements Interface(String intfName) {
408: int loaderId = getLoaderId();
409: String[] intfs = getInterfaceNames();
410:
411: if (intfs != null) {
412: for (int i = 0; i < intfs.length; i++) {
413: if (intfName == intfs[i]) {
414: return true;
415: }
416: }
417:
418: DynamicClassInfo[] intfsDCI = getSuperInterfaces();
419:
420: if (intfsDCI != null) {
421: for (int i = 0; i < intfsDCI.length; i++) {
422: DynamicClassInfo intfClazz = intfsDCI[i];
423:
424: if ((intfClazz != null)
425: && intfClazz.implements Interface(intfName)) {
426: return true;
427: }
428: }
429: }
430: }
431:
432: DynamicClassInfo super Class = getSuperClass();
433:
434: if ((super Class == null)
435: || (super Class.getName() == "java/lang/Object")) {
436: return false; // NOI18N
437: }
438:
439: return super Class.implements Interface(intfName);
440: }
441:
442: public void saveMethodInfo(int idx, byte[] methodInfo) {
443: if (modifiedAndSavedMethodInfos == null) {
444: modifiedAndSavedMethodInfos = new byte[methodNames.length][];
445: }
446:
447: modifiedAndSavedMethodInfos[idx] = methodInfo;
448: }
449:
450: public void unsetMethodInstrumented(int i) {
451: methodScanStatus[i] &= (~8);
452: nInstrumentedMethods--;
453: }
454:
455: public void unsetMethodSpecial(int i) {
456: methodScanStatus[i] &= (~128);
457: }
458:
459: private int getBCLenForModifiedAndSavedMethodInfo(int idx) {
460: byte[] methodInfo = modifiedAndSavedMethodInfos[idx];
461: int bcLenPos = methodBytecodesOffsets[idx] - 4;
462:
463: return (((methodInfo[bcLenPos++] & 255) << 24)
464: + ((methodInfo[bcLenPos++] & 255) << 16)
465: + ((methodInfo[bcLenPos++] & 255) << 8) + (methodInfo[bcLenPos++] & 255));
466: }
467: }
|