001: /*
002: Copyright (c) 2005-2006, MentorGen, LLC
003: All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without
006: modification, are permitted provided that the following conditions are met:
007:
008: + Redistributions of source code must retain the above copyright notice,
009: this list of conditions and the following disclaimer.
010: + Redistributions in binary form must reproduce the above copyright notice,
011: this list of conditions and the following disclaimer in the documentation
012: and/or other materials provided with the distribution.
013: + Neither the name of MentorGen LLC nor the names of its contributors may be
014: used to endorse or promote products derived from this software without
015: specific prior written permission.
016:
017: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
018: AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
019: IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
020: ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
021: LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
022: CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
023: SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
024: INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
025: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
026: ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
027: POSSIBILITY OF SUCH DAMAGE.
028: */
029: package com.mentorgen.tools.profile.instrument;
030:
031: import org.objectweb.asm.jip.Label;
032: import org.objectweb.asm.jip.MethodAdapter;
033: import org.objectweb.asm.jip.MethodVisitor;
034: import org.objectweb.asm.jip.Opcodes;
035:
036: import com.mentorgen.tools.profile.Controller;
037:
038: import static org.objectweb.asm.jip.Opcodes.INVOKESTATIC;
039:
040: /**
041: * This class is responsible for instrumenting a method to
042: * call the profiler in order for performance
043: * data to be gathered. The basic idea is that the profiler is called
044: * when a method starts and when it exists which allows the profiler
045: * to gather performance data (note that a method can be exited from
046: * when an exception is thrown as well as when return is called). The
047: * one big caveate is static initializers. They are not called as part
048: * of the flow of the program — they are called by the classloader.
049: * Since they whole premise of the profiler is built on the idea of a
050: * orderly call stack, static initializers are not instrumented.
051: *
052: * @author Andrew Wilcox
053: *
054: */
055: public class PerfMethodAdapter extends MethodAdapter {
056: private String _className, _methodName;
057: private boolean _clinit = false;
058: private boolean _init = false;
059:
060: public PerfMethodAdapter(MethodVisitor visitor, String className,
061: String methodName) {
062: super (visitor);
063: _className = className;
064: _methodName = methodName;
065:
066: // Static initializers are excluded. The reason for this
067: // is the the profiling algorithm we're using mirrors the call stack.
068: // Since static initializers are called by the classloader
069: // and therefore aren't part of the programs flow of control,
070: // static initializers can really mess up the profiler, especially
071: // when they're called before the program's flow of control is started
072: // (for example, the when the class with the main() method has a
073: // static initalizer). So yes, this is a short comming in the
074: // design of the profiler, but we're willing to live with it because
075: // this profiler is lightweight and allows us to use it interactively.
076: //
077: if (methodName.equals("<clinit>")) {
078: _clinit = true;
079: } else if (methodName.startsWith("<init>")) {
080: _init = true;
081: }
082: }
083:
084: public void visitCode() {
085: if (_clinit) {
086: super .visitCode();
087: return;
088: }
089:
090: // Because the alloc method looks at the class + method of the caller
091: // this call needs to come before the call to Profile.start
092: //
093: if (Controller._trackObjectAlloc && _init) {
094: this .visitLdcInsn(_className);
095: this .visitMethodInsn(INVOKESTATIC, Controller._profiler,
096: "alloc", "(Ljava/lang/String;)V");
097: }
098:
099: this .visitLdcInsn(_className);
100: this .visitLdcInsn(_methodName);
101: this .visitMethodInsn(INVOKESTATIC, Controller._profiler,
102: "start", "(Ljava/lang/String;Ljava/lang/String;)V");
103:
104: super .visitCode();
105: }
106:
107: public void visitInsn(int inst) {
108: if (_clinit) {
109: super .visitInsn(inst);
110: return;
111: }
112:
113: switch (inst) {
114: case Opcodes.ARETURN:
115: case Opcodes.DRETURN:
116: case Opcodes.FRETURN:
117: case Opcodes.IRETURN:
118: case Opcodes.LRETURN:
119: case Opcodes.RETURN:
120: case Opcodes.ATHROW:
121:
122: this .visitLdcInsn(_className);
123: this .visitLdcInsn(_methodName);
124:
125: this .visitMethodInsn(INVOKESTATIC, Controller._profiler,
126: "end", "(Ljava/lang/String;Ljava/lang/String;)V");
127: break;
128:
129: default:
130: break;
131: }
132:
133: if (Opcodes.MONITORENTER == inst) {
134: this .visitLdcInsn(_className);
135: this .visitLdcInsn(_methodName);
136:
137: this .visitMethodInsn(INVOKESTATIC, Controller._profiler,
138: "beginWait",
139: "(Ljava/lang/String;Ljava/lang/String;)V");
140:
141: super .visitInsn(inst);
142:
143: this .visitLdcInsn(_className);
144: this .visitLdcInsn(_methodName);
145:
146: this .visitMethodInsn(INVOKESTATIC, Controller._profiler,
147: "endWait",
148: "(Ljava/lang/String;Ljava/lang/String;)V");
149: } else {
150: super .visitInsn(inst);
151: }
152: }
153:
154: @Override
155: public void visitMethodInsn(int opcode, String owner, String name,
156: String desc) {
157: if (isWaitInsn(opcode, owner, name, desc)) {
158:
159: this .visitLdcInsn(_className);
160: this .visitLdcInsn(_methodName);
161:
162: this .visitMethodInsn(INVOKESTATIC, Controller._profiler,
163: "beginWait",
164: "(Ljava/lang/String;Ljava/lang/String;)V");
165:
166: super .visitMethodInsn(opcode, owner, name, desc);
167:
168: this .visitLdcInsn(_className);
169: this .visitLdcInsn(_methodName);
170:
171: this .visitMethodInsn(INVOKESTATIC, Controller._profiler,
172: "endWait",
173: "(Ljava/lang/String;Ljava/lang/String;)V");
174: } else {
175: super .visitMethodInsn(opcode, owner, name, desc);
176: }
177: }
178:
179: //
180: // code to handle unwinding the call stack when an exception is thrown
181: // (many thanks to Fredrik SvarŽn for posting this code in the help forum!)
182: //
183:
184: @Override
185: public void visitTryCatchBlock(Label start, Label end,
186: Label handler, String type) {
187: super .visitTryCatchBlock(start, end, handler, type);
188:
189: // Note: static initializers aren't measured, so make sure that the exception
190: // isn't being caught in one
191: if (type != null && !_clinit) {
192: handler.info = new ExceptionInfo(type);
193: }
194: }
195:
196: @Override
197: public void visitLabel(Label label) {
198: super .visitLabel(label);
199:
200: if (label.info instanceof ExceptionInfo) {
201: this .visitLdcInsn(_className);
202: this .visitLdcInsn(_methodName);
203: this .visitLdcInsn(((ExceptionInfo) label.info).type);
204:
205: this
206: .visitMethodInsn(INVOKESTATIC,
207: Controller._profiler, "unwind",
208: "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
209: }
210: }
211:
212: class ExceptionInfo {
213: String type;
214:
215: ExceptionInfo(String type) {
216: this .type = type;
217: }
218: }
219:
220: //
221: // private methods
222: //
223:
224: private static boolean isWaitInsn(int opcode, String owner,
225: String name, String desc) {
226: boolean isWait = (opcode == Opcodes.INVOKEVIRTUAL
227: && "java/lang/Object".equals(owner)
228: && "wait".equals(name) && ("()V".equals(desc)
229: || "(J)V".equals(desc) || "(JI)V".equals(desc)));
230: if (isWait)
231: return true;
232:
233: isWait = (opcode == Opcodes.INVOKEVIRTUAL
234: && "java/lang/Thread".equals(owner)
235: && "join".equals(name) && ("()V".equals(desc)
236: || "(J)V".equals(desc) || "(JI)V".equals(desc)));
237: if (isWait)
238: return true;
239:
240: isWait = (opcode == Opcodes.INVOKESTATIC
241: && "java/lang/Thread".equals(owner)
242: && "sleep".equals(name) && ("(J)V".equals(desc) || "(JI)V"
243: .equals(desc)));
244: if (isWait)
245: return true;
246:
247: isWait = (opcode == Opcodes.INVOKESTATIC
248: && "java/lang/Thread".equals(owner)
249: && "yield".equals(name) && "()V".equals(desc));
250: if (isWait)
251: return true;
252:
253: return isWait;
254: }
255:
256: }
|