/*
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.btrace.runtime;
import com.sun.btrace.annotations.Kind;
import com.sun.btrace.annotations.Sampled;
import com.sun.btrace.annotations.Where;
import com.sun.btrace.org.objectweb.asm.AnnotationVisitor;
import com.sun.btrace.org.objectweb.asm.Opcodes;
import static com.sun.btrace.runtime.Constants.*;
import com.sun.btrace.org.objectweb.asm.tree.MethodNode;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
/**
*
* @author Jaroslav Bachorik
*/
public class BTraceMethodNode extends MethodNode {
public static final Comparator<MethodNode> COMPARATOR = new Comparator<MethodNode>() {
@Override
public int compare(MethodNode o1, MethodNode o2) {
return (o1.name + "#" + o1.desc).compareTo(o2.name + "#" + o2.desc);
}
};
private OnMethod om;
private OnProbe op;
private Location loc;
private boolean sampled;
private final BTraceProbe cn;
private boolean isBTraceHandler;
private final CallGraph graph;
private final String methodId;
BTraceMethodNode(MethodNode from, BTraceProbe cn) {
super(Opcodes.ASM5, from.access, from.name, from.desc, from.signature, ((List<String>)from.exceptions).toArray(new String[0]));
this.cn = cn;
this.graph = cn.getGraph();
this.methodId = CallGraph.methodId(name, desc);
}
@Override
public void visitEnd() {
if (om != null) {
verifySpecialParameters(om);
cn.addOnMethod(om);
}
if (op != null) {
cn.addOnProbe(op);
}
if (isBTraceHandler) {
graph.addStarting(new CallGraph.Node(methodId));
}
super.visitEnd();
}
@Override
public AnnotationVisitor visitAnnotation(String type, boolean visible) {
AnnotationVisitor av = super.visitAnnotation(type, visible);
if (type.startsWith("Lcom/sun/btrace/annotations/")) {
isBTraceHandler = true;
}
if (type.equals(ONMETHOD_DESC)) {
om = new OnMethod(this);
om.setTargetName(name);
om.setTargetDescriptor(desc);
return new AnnotationVisitor(Opcodes.ASM5, av) {
@Override
public void visit(String name, Object value) {
super.visit(name, value);
switch (name) {
case "clazz":
om.setClazz((String)value);
break;
case "method":
om.setMethod((String)value);
break;
case "type":
om.setType((String)value);
break;
default:
System.err.println("btrace WARNING: Unsupported @OnMethod attribute: " + name);
}
}
@Override
public AnnotationVisitor visitAnnotation(String name, String desc) {
AnnotationVisitor av1 = super.visitAnnotation(name, desc);
if (desc.equals(LOCATION_DESC)) {
loc = new Location();
return new AnnotationVisitor(Opcodes.ASM5, av1) {
@Override
public void visitEnum(String name, String desc, String value) {
super.visitEnum(name, desc, value);
if (desc.equals(WHERE_DESC)) {
loc.setWhere(Enum.valueOf(Where.class, value));
} else if (desc.equals(KIND_DESC)) {
loc.setValue(Enum.valueOf(Kind.class, value));
}
}
@Override
public void visit(String name, Object value) {
super.visit(name, value);
switch (name) {
case "clazz":
loc.setClazz((String)value);
break;
case "method":
loc.setMethod((String)value);
break;
case "type":
loc.setType((String)value);
break;
case "field":
loc.setField((String)value);
break;
case "line":
loc.setLine(((Number)value).intValue());
break;
}
}
@Override
public void visitEnd() {
if (loc != null) {
om.setLocation(loc);
}
super.visitEnd();
}
};
} else if (desc.equals(LEVEL_DESC)) {
return new AnnotationVisitor(Opcodes.ASM5, av1) {
@Override
public void visit(String name, Object value) {
super.visit(name, value);
if ("value".equals(name)) {
om.setLevel(Level.fromString((String)value));
}
}
};
}
return av1;
}
};
} else if (type.equals(ONPROBE_DESC)) {
op = new OnProbe(this);
op.setTargetName(name);
op.setTargetDescriptor(desc);
return new AnnotationVisitor(Opcodes.ASM5, av) {
@Override
public void visit(String name, Object value) {
super.visit(name, value);
switch (name) {
case "namespace":
op.setNamespace((String)value);
break;
case "name":
op.setName((String)value);
break;
}
}
};
} else if (type.equals(SAMPLED_DESC)) {
if (om != null) {
om.setSamplerKind(Sampled.Sampler.Adaptive);
return new AnnotationVisitor(Opcodes.ASM5, av) {
private boolean meanSet = false;
@Override
public void visit(String name, Object value) {
super.visit(name, value);
if (name.equals("mean")) {
om.setSamplerMean((Integer)value);
meanSet = true;
}
}
@Override
public void visitEnum(String name, String desc, String value) {
super.visitEnum(name, desc, value);
if (name.equals("kind") && desc.equals(SAMPLER_DESC)) {
om.setSamplerKind(Sampled.Sampler.valueOf(value));
}
}
@Override
public void visitEnd() {
if (!meanSet) {
if (om.getSamplerKind() == Sampled.Sampler.Adaptive) {
om.setSamplerMean(500);
} else if (om.getSamplerKind() == Sampled.Sampler.Const) {
om.setSamplerMean(Sampled.MEAN_DEFAULT);
}
}
if (om.getSamplerKind() == Sampled.Sampler.Adaptive) {
// the time frame for adaptive sampling
// should be at least 180ns -
// (80ns timestamps + 15ns stub) * 2 safety margin
if (om.getSamplerMean() < 180) {
System.err.println("Setting the adaptive sampler time windows to the default of 180ns");
om.setSamplerMean(180);
}
}
super.visitEnd();
}
};
}
sampled = true;
}
return av;
}
@Override
public AnnotationVisitor visitParameterAnnotation(int parameter, final String desc, boolean visible) {
AnnotationVisitor av = super.visitParameterAnnotation(parameter, desc, visible);
if (om != null) {
av = setSpecialParameters(om, desc, parameter, av);
} else if (op != null) {
av = setSpecialParameters(op, desc, parameter, av);
}
return av;
}
@Override
public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
owner = cn.translateOwner(owner);
if (opcode == Opcodes.INVOKESTATIC) {
graph.addEdge(methodId, CallGraph.methodId(name, desc));
}
super.visitMethodInsn(opcode, owner, name, desc, itf);
}
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
super.visitFieldInsn(opcode, cn.translateOwner(owner), name, desc);
}
public boolean isBcpRequired() {
return isBTraceHandler && om == null && op == null;
}
public boolean isBTraceHandler() {
return isBTraceHandler;
}
public OnMethod getOnMethod() {
return om;
}
public boolean isSampled() {
return sampled;
}
public Set<BTraceMethodNode> getCallees() {
return cn.callees(name, desc);
}
public Set<BTraceMethodNode> getCallers() {
return cn.callers(name, desc);
}
boolean isFieldInjected(String name) {
return cn.isFieldInjected(name);
}
OnProbe getOnProbe() {
return op;
}
private AnnotationVisitor setSpecialParameters(final SpecialParameterHolder ph, final String desc, int parameter, AnnotationVisitor av) {
// for OnProbe the 'loc' variable will be null; we will need to verfiy the placement later on
if (desc.equals(SELF_DESC)) {
ph.setSelfParameter(parameter);
} else if (desc.equals(BTRACE_PROBECLASSNAME_DESC)) {
ph.setClassNameParameter(parameter);
} else if (desc.equals(BTRACE_PROBEMETHODNAME_DESC)) {
ph.setMethodParameter(parameter);
av = new AnnotationVisitor(Opcodes.ASM5, av) {
@Override
public void visit(String name, Object val) {
if (name.equals("fqn")) {
ph.setMethodFqn((Boolean)val);
}
super.visit(name, val);
}
};
} else if (desc.equals(RETURN_DESC)) {
ph.setReturnParameter(parameter);
} else if (desc.equals(TARGETMETHOD_DESC)) {
ph.setTargetMethodOrFieldParameter(parameter);
av = new AnnotationVisitor(Opcodes.ASM5, av) {
@Override
public void visit(String name, Object val) {
if (name.equals("fqn")) {
ph.setTargetMethodOrFieldFqn((Boolean)val);
}
super.visit(name, val);
}
};
} else if (desc.equals(TARGETINSTANCE_DESC)) {
ph.setTargetInstanceParameter(parameter);
} else if (desc.equals(DURATION_DESC)) {
ph.setDurationParameter(parameter);
}
return av;
}
private void verifySpecialParameters(OnMethod om) {
Location loc = om.getLocation();
if (om.getReturnParameter() != -1) {
if (!(loc.getValue() == Kind.RETURN ||
(loc.getValue() == Kind.CALL && loc.getWhere() == Where.AFTER) ||
(loc.getValue() == Kind.ARRAY_GET && loc.getWhere() == Where.AFTER) ||
(loc.getValue() == Kind.FIELD_GET && loc.getWhere() == Where.AFTER) ||
(loc.getValue() == Kind.NEW && loc.getWhere() == Where.AFTER) ||
(loc.getValue() == Kind.NEWARRAY && loc.getWhere() == Where.AFTER))) {
Verifier.reportError("return.desc.invalid", om.getTargetName() + om.getTargetDescriptor() + "(" + om.getReturnParameter() + ")");
}
}
if (om.getTargetMethodOrFieldParameter() != -1) {
if (!(loc.getValue() == Kind.CALL ||
loc.getValue() == Kind.FIELD_GET ||
loc.getValue() == Kind.FIELD_SET ||
loc.getValue() == Kind.ARRAY_GET ||
loc.getValue() == Kind.ARRAY_SET)) {
Verifier.reportError("target-method.desc.invalid", om.getTargetName() + om.getTargetDescriptor() + "(" + om.getTargetMethodOrFieldParameter() + ")");
}
}
if (om.getTargetInstanceParameter() != -1) {
if (!(loc.getValue() == Kind.CALL ||
loc.getValue() == Kind.FIELD_GET ||
loc.getValue() == Kind.FIELD_SET ||
loc.getValue() == Kind.ARRAY_GET ||
loc.getValue() == Kind.ARRAY_SET ||
loc.getValue() == Kind.INSTANCEOF ||
loc.getValue() == Kind.CHECKCAST ||
loc.getValue() == Kind.SYNC_ENTRY ||
loc.getValue() == Kind.SYNC_EXIT)) {
Verifier.reportError("target-instance.desc.invalid", om.getTargetName() + om.getTargetDescriptor() + "(" + om.getTargetInstanceParameter() + ")");
}
}
if (om.getDurationParameter() != -1) {
if (!((loc.getValue() == Kind.RETURN ||
loc.getValue() == Kind.ERROR) ||
(loc.getValue() == Kind.CALL &&
loc.getWhere() == Where.AFTER))) {
Verifier.reportError("duration.desc.invalid", om.getTargetName() + om.getTargetDescriptor() + "(" + om.getDurationParameter() + ")");
}
}
}
@Override
public String toString() {
return "BTraceMethodNode{name = " + name + ", desc=" + desc + '}';
}
}