Skip to content

Commit d4ae36c

Browse files
authored
Merge pull request #449 from Microsoft/ByteCodeInstrumentationFix
Merging Byte code instrumentation fix
2 parents 0e2fe37 + 75596d8 commit d4ae36c

File tree

21 files changed

+332
-107
lines changed

21 files changed

+332
-107
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# CHANGELOG
22

33
## Version 1.0.11
4+
- Updating various dependencies to latest version
5+
- Introducing public class CustomClassWriter in Agent to enable finding common super classes used for Agent instrumentation without loading it
6+
- Introducing Parametrized constructor in WebRequestTrackingFilter to ensure name binding
7+
- Fixed Issue #428 (Agent cannot capture oracle dependencies)
8+
- Fixed Issue #418 (Java.lang.verify error caused while capturing sql dependencies in JDK 1.8)
9+
- Fixed Issue #286, #277 (Issues in capturing preparedStatement calls)
10+
- Fixed Issue #365 (Relocate all web jars)
11+
- Fixed Issue #276 (Instrumentation issues with HTTP Client)
412
- Introducing public method 'getIncludedTypes' and 'getExcludedTypes' in TelemetryProcessorXmlElement.
513
- Introducing class 'com.microsoft.applicationinsights.internal.config.ParamIncludedTypeXmlElement'.
614
- Introducing class 'com.microsoft.applicationinsights.internal.config.ParamExcludedTypeXmlElement'

agent/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ jar {
4545
}
4646

4747
dependencies {
48-
compile group: 'org.ow2.asm', name: 'asm-commons', version: '5.0.3'
49-
compile group: 'org.ow2.asm', name: 'asm-all', version: '5.0.3'
48+
compile group: 'org.ow2.asm', name: 'asm-commons', version: '5.2'
49+
compile group: 'org.ow2.asm', name: 'asm-all', version: '5.2'
5050
testCompile group: 'commons-io', name: 'commons-io', version: '2.4'
5151
testCompile group: 'junit', name: 'junit', version: '4.11'
5252
testCompile group: 'org.mockito', name: 'mockito-all', version: '1.8.0'

agent/src/main/java/com/microsoft/applicationinsights/agent/internal/agent/AdvancedAdviceAdapter.java

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,16 @@ public void visitCode() {
7474

7575
@Override
7676
public void visitMaxs(int maxStack, int maxLocals) {
77-
visitTryCatchBlock(startTryFinallyBlock, endTryFinallyBlock, endTryFinallyBlock, null);
78-
mark(endTryFinallyBlock);
77+
//Do not visit try catch if constructor injection
78+
if (!methodName.endsWith("<init>")) {
79+
visitTryCatchBlock(startTryFinallyBlock, endTryFinallyBlock, endTryFinallyBlock, null);
80+
mark(endTryFinallyBlock);
7981

80-
byteCodeForMethodExit(Opcodes.ATHROW);
82+
byteCodeForMethodExit(Opcodes.ATHROW);
83+
84+
mv.visitInsn(Opcodes.ATHROW);
85+
}
8186

82-
mv.visitInsn(Opcodes.ATHROW);
8387
mv.visitMaxs(maxStack, maxLocals);
8488
}
8589

agent/src/main/java/com/microsoft/applicationinsights/agent/internal/agent/AgentImplementation.java

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,28 +21,25 @@
2121

2222
package com.microsoft.applicationinsights.agent.internal.agent;
2323

24+
import com.microsoft.applicationinsights.agent.internal.common.StringUtils;
25+
import com.microsoft.applicationinsights.agent.internal.config.AgentConfiguration;
26+
import com.microsoft.applicationinsights.agent.internal.config.AgentConfigurationBuilderFactory;
27+
import com.microsoft.applicationinsights.agent.internal.config.DataOfConfigurationForException;
28+
import com.microsoft.applicationinsights.agent.internal.coresync.impl.ImplementationsCoordinator;
29+
import com.microsoft.applicationinsights.agent.internal.logger.InternalAgentLogger;
30+
2431
import java.io.File;
2532
import java.io.IOException;
2633
import java.io.UnsupportedEncodingException;
2734
import java.lang.instrument.Instrumentation;
28-
import java.lang.reflect.Constructor;
2935
import java.lang.reflect.InvocationTargetException;
30-
import java.lang.reflect.Method;
3136
import java.net.URL;
3237
import java.net.URLClassLoader;
3338
import java.net.URLDecoder;
3439
import java.util.Enumeration;
3540
import java.util.jar.JarEntry;
3641
import java.util.jar.JarFile;
3742

38-
import com.microsoft.applicationinsights.agent.internal.agent.jmx.JmxConnectorLoader;
39-
import com.microsoft.applicationinsights.agent.internal.common.StringUtils;
40-
import com.microsoft.applicationinsights.agent.internal.config.AgentConfiguration;
41-
import com.microsoft.applicationinsights.agent.internal.config.AgentConfigurationBuilderFactory;
42-
import com.microsoft.applicationinsights.agent.internal.config.DataOfConfigurationForException;
43-
import com.microsoft.applicationinsights.agent.internal.coresync.impl.ImplementationsCoordinator;
44-
import com.microsoft.applicationinsights.agent.internal.logger.InternalAgentLogger;
45-
4643
/**
4744
* Created by gupele on 5/6/2015.
4845
*/
@@ -116,11 +113,13 @@ private static void appendJarsToBootstrapClassLoader(Instrumentation inst) throw
116113
for (File file : agentFolder.listFiles()) {
117114
if (file.getName().indexOf(AGENT_JAR_PREFIX) != -1) {
118115
agentJarName = file.getName();
116+
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.INFO,"Agent jar name is " + agentJarName);
119117
break;
120118
}
121119
}
122120

123121
if (agentJarName == null) {
122+
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR,"Agent Jar Name is null....Throwing runtime exception");
124123
throw new RuntimeException("Could not find agent jar");
125124
}
126125

@@ -136,24 +135,27 @@ private static void appendJarsToBootstrapClassLoader(Instrumentation inst) throw
136135
}
137136

138137
public static String getAgentJarLocation() throws UnsupportedEncodingException {
138+
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
139139
try {
140-
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
141140
if ((systemClassLoader instanceof URLClassLoader)) {
142141
for (URL url : ((URLClassLoader)systemClassLoader).getURLs()) {
143142
String urlPath = url.getPath();
143+
144144
if (urlPath.indexOf(AGENT_JAR_PREFIX) != -1) {
145+
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.INFO,"Agent jar found at " + urlPath);
145146
int index = urlPath.lastIndexOf('/');
146147
urlPath = urlPath.substring(0, index + 1);
147148
return urlPath;
148149
}
150+
149151
}
150152
}
151153
} catch (Throwable throwable) {
152154
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, "Error while trying to fetch Jar Location, Exception: " + throwable.getMessage());
153155
}
154156

155-
String path = AgentImplementation.class.getProtectionDomain().getCodeSource().getLocation().getPath();
156-
return URLDecoder.decode(path, "UTF-8");
157+
String stringPath = AgentImplementation.class.getProtectionDomain().getCodeSource().getLocation().getPath();
158+
return URLDecoder.decode(stringPath, "UTF-8");
157159
}
158160

159161
private static void SetNonWebAppModeIfAskedByConf(String sdkPath) throws Throwable {

agent/src/main/java/com/microsoft/applicationinsights/agent/internal/agent/ByteCodeTransformer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,5 +25,5 @@
2525
* Created by gupele on 8/3/2015.
2626
*/
2727
public interface ByteCodeTransformer {
28-
byte[] transform(byte[] originalBuffer, String className);
28+
byte[] transform(byte[] originalBuffer, String className, ClassLoader loader);
2929
}

agent/src/main/java/com/microsoft/applicationinsights/agent/internal/agent/CodeInjector.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public byte[] transform(
7878
DefaultByteCodeTransformer byteCodeTransformer = classNamesProvider.getAndRemove(className);
7979
if (byteCodeTransformer != null) {
8080
try {
81-
return byteCodeTransformer.transform(originalBuffer, className);
81+
return byteCodeTransformer.transform(originalBuffer, className, loader);
8282
} catch (Throwable throwable) {
8383
throwable.printStackTrace();
8484
InternalAgentLogger.INSTANCE.logAlways(InternalAgentLogger.LoggingLevel.ERROR, "Failed to instrument '%s', exception: '%s': ", className, throwable.getMessage());
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
package com.microsoft.applicationinsights.agent.internal.agent;
2+
3+
import org.objectweb.asm.ClassReader;
4+
import org.objectweb.asm.Opcodes;
5+
6+
import java.io.IOException;
7+
import java.io.InputStream;
8+
9+
/**
10+
* This class overwrites default class writer of ASM to use the ClassLoader
11+
* provided by DefaultByteCode transformer (This loader essentially has all
12+
* the required classes already loaded)
13+
*/
14+
public class CustomClassWriter extends org.objectweb.asm.ClassWriter {
15+
16+
private ClassLoader classLoader;
17+
18+
public CustomClassWriter(int writerFlag, ClassLoader loader) {
19+
super(writerFlag);
20+
this.classLoader = loader;
21+
}
22+
23+
/**
24+
* This method returns common super class for both the classes without actually loading
25+
* the class using forName(). If no super class is present it returns java/lang/Object class.
26+
* @param type1
27+
* @param type2
28+
* @return The String for the common super class of both the classes
29+
*/
30+
@Override
31+
protected String getCommonSuperClass(final String type1, final String type2) {
32+
try {
33+
ClassReader info1 = typeInfo(type1);
34+
ClassReader info2 = typeInfo(type2);
35+
if ((info1.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
36+
if (typeImplements(type2, info2, type1)) {
37+
return type1;
38+
}
39+
if ((info2.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
40+
if (typeImplements(type1, info1, type2)) {
41+
return type2;
42+
}
43+
}
44+
return "java/lang/Object";
45+
}
46+
if ((info2.getAccess() & Opcodes.ACC_INTERFACE) != 0) {
47+
if (typeImplements(type1, info1, type2)) {
48+
return type2;
49+
} else {
50+
return "java/lang/Object";
51+
}
52+
}
53+
StringBuilder b1 = typeAncestors(type1, info1);
54+
StringBuilder b2 = typeAncestors(type2, info2);
55+
String result = "java/lang/Object";
56+
int end1 = b1.length();
57+
int end2 = b2.length();
58+
while (true) {
59+
int start1 = b1.lastIndexOf(";", end1 - 1);
60+
int start2 = b2.lastIndexOf(";", end2 - 1);
61+
if (start1 != -1 && start2 != -1
62+
&& end1 - start1 == end2 - start2) {
63+
String p1 = b1.substring(start1 + 1, end1);
64+
String p2 = b2.substring(start2 + 1, end2);
65+
if (p1.equals(p2)) {
66+
result = p1;
67+
end1 = start1;
68+
end2 = start2;
69+
} else {
70+
return result;
71+
}
72+
} else {
73+
return result;
74+
}
75+
}
76+
} catch (IOException e) {
77+
//This throwing will not cause any harm because nonetheless code will fail
78+
//if this method fails
79+
throw new RuntimeException(e.toString());
80+
}
81+
}
82+
83+
/**
84+
* Determines parent class
85+
* @param type
86+
* @param info
87+
* @return
88+
* @throws IOException
89+
*/
90+
private StringBuilder typeAncestors(String type, ClassReader info) throws IOException {
91+
StringBuilder b = new StringBuilder();
92+
while (!"java/lang/Object".equals(type)) {
93+
b.append(';').append(type);
94+
type = info.getSuperName();
95+
info = typeInfo(type);
96+
}
97+
return b;
98+
}
99+
100+
101+
/**
102+
* Determines common interface implementation
103+
* @param type
104+
* @param info
105+
* @param itf
106+
* @return
107+
* @throws IOException
108+
*/
109+
private boolean typeImplements(String type, ClassReader info, String itf) throws IOException {
110+
while (!"java/lang/Object".equals(type)) {
111+
String[] itfs = info.getInterfaces();
112+
for (String itf2 : itfs) {
113+
if (itf2.equals(itf)) {
114+
return true;
115+
}
116+
}
117+
for (String itf1 : itfs) {
118+
if (typeImplements(itf1, typeInfo(itf1), itf)) {
119+
return true;
120+
}
121+
}
122+
type = info.getSuperName();
123+
info = typeInfo(type);
124+
}
125+
return false;
126+
}
127+
128+
/**
129+
* Generates ASM Classwriter from the Input Stream for detailed information
130+
* @param type
131+
* @return
132+
* @throws IOException
133+
*/
134+
private ClassReader typeInfo(final String type) throws IOException {
135+
InputStream is = classLoader.getResourceAsStream(type + ".class");
136+
return new ClassReader(is);
137+
}
138+
}
139+
140+

agent/src/main/java/com/microsoft/applicationinsights/agent/internal/agent/DefaultByteCodeTransformer.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,11 @@
2121

2222
package com.microsoft.applicationinsights.agent.internal.agent;
2323

24-
import java.io.PrintWriter;
25-
import java.io.StringWriter;
26-
2724
import org.objectweb.asm.ClassReader;
2825
import org.objectweb.asm.ClassVisitor;
2926
import org.objectweb.asm.ClassWriter;
3027
//import org.objectweb.asm.util.CheckClassAdapter;
3128

32-
import com.microsoft.applicationinsights.agent.internal.logger.InternalAgentLogger;
3329

3430
/**
3531
* The class coordinates the byte code transformation
@@ -54,13 +50,13 @@ final class DefaultByteCodeTransformer implements ByteCodeTransformer {
5450
* @return A new buffer containing the class with the changes or the original one if no change was done.
5551
*/
5652
@Override
57-
public byte[] transform(byte[] originalBuffer, String className) {
53+
public byte[] transform(byte[] originalBuffer, String className, ClassLoader loader) {
5854
if (classInstrumentationData == null) {
5955
return originalBuffer;
6056
}
6157

6258
ClassReader cr = new ClassReader(originalBuffer);
63-
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
59+
ClassWriter cw = new CustomClassWriter(ClassWriter.COMPUTE_FRAMES, loader);
6460
ClassVisitor dcv = classInstrumentationData.getDefaultClassInstrumentor(cw);
6561
cr.accept(dcv, ClassReader.SKIP_FRAMES);
6662

agent/src/main/java/com/microsoft/applicationinsights/agent/internal/agent/DefaultClassVisitor.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.objectweb.asm.ClassWriter;
2626
import org.objectweb.asm.MethodVisitor;
2727
import org.objectweb.asm.Opcodes;
28+
import org.objectweb.asm.commons.JSRInlinerAdapter;
2829

2930
/**
3031
* The class is responsible for identifying public methods on non-interface classes.
@@ -38,7 +39,6 @@ public class DefaultClassVisitor extends ClassVisitor {
3839

3940
public DefaultClassVisitor(ClassInstrumentationData instrumentationData, ClassWriter classWriter) {
4041
super(Opcodes.ASM5, classWriter);
41-
4242
this.instrumentationData = instrumentationData;
4343
}
4444

@@ -51,8 +51,8 @@ public void visit(int version, int access, String name, String signature, String
5151
@Override
5252
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
5353
MethodVisitor originalMV = super.visitMethod(access, name, desc, signature, exceptions);
54-
55-
if (isInterface || originalMV == null || ByteCodeUtils.isStaticInitializer(name) || ByteCodeUtils.isPrivate(access)) {
54+
originalMV = new JSRInlinerAdapter(originalMV, access, name, desc, signature, exceptions);
55+
if (isInterface || ByteCodeUtils.isStaticInitializer(name) || ByteCodeUtils.isPrivate(access)) {
5656
return originalMV;
5757
}
5858

agent/src/main/java/com/microsoft/applicationinsights/agent/internal/agent/sql/PreparedStatementByteCodeTransformer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public final class PreparedStatementByteCodeTransformer implements ByteCodeTrans
3737
}
3838

3939
@Override
40-
public byte[] transform(byte[] originalBuffer, String className) {
40+
public byte[] transform(byte[] originalBuffer, String className, ClassLoader loader) {
4141
if (classInstrumentationData == null) {
4242
return originalBuffer;
4343
}

0 commit comments

Comments
 (0)