Skip to content

Commit e55d269

Browse files
committed
Add RemoveMonitorVistor
We can use it to give a chance to handle 'Pinned: MONITOR' issue #250 #273.
1 parent 7198d9b commit e55d269

File tree

10 files changed

+271
-67
lines changed

10 files changed

+271
-67
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,15 @@ jobs:
117117
- export PATH=$JAVA_HOME/bin:$PATH
118118
- java -version
119119
- cd /home/who/git/nginx-clojure/
120-
- lein with-profile nativeCoroutine jar
120+
- lein with-profile nativeCoroutine jar && lein with-profile unittest junit compile
121121
- cd /home/who/git/nginx-clojure/test/nginx-working-dir
122122
- ./nginx -c /home/who/git/nginx-clojure/test/nginx-working-dir/conf/nginx-coroutine-jdk19.conf &
123123
- sleep 30
124124
- tail -f logs/error.log &
125125
- curl -v http://localhost:8080/clojure
126126
- killall tail
127127
- cd /home/who/git/nginx-clojure/
128-
- lein with-profile cljremotetest test :all
128+
- lein with-profile jdk19cljremotetest test :all
129129
- cd /home/who/git/nginx-clojure/test/nginx-working-dir
130130
- ./nginx -c /home/who/git/nginx-clojure/test/nginx-working-dir/conf/nginx-coroutine-jdk19.conf -s stop
131131

project.clj

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@
7878
:global-vars {*warn-on-reflection* true
7979
*assert* false}
8080
:java-source-paths ["src/java", "src/nativeCoroutine"]
81+
:test-paths ["src/test/clojure", "src/test/java"]
8182
:dependencies [;only for test / compile usage
8283
[org.clojure/clojure "1.9.0"]
8384
[ring/ring-core "1.7.1"]
@@ -184,5 +185,45 @@
184185
[redis.clients/jedis "3.1.0"]
185186
[org.clojure/tools.trace "0.7.10"]
186187
]
187-
}
188+
}
189+
190+
:jdk19cljremotetest {
191+
:jvm-opts ["--enable-preview"
192+
"--add-opens=java.base/jdk.internal.vm=ALL-UNNAMED"
193+
"--add-opens=java.base/java.lang=ALL-UNNAMED"
194+
"--add-opens=java.base/sun.nio.cs=ALL-UNNAMED"
195+
"--add-opens=java.base/sun.nio.ch=ALL-UNNAMED"]
196+
:java-source-paths ["test/java" "test/clojure"]
197+
:test-paths ["src/test/clojure"]
198+
:source-paths ["test/clojure" "test/java" "test/nginx-working-dir/coroutine-udfs"]
199+
:compile-path "target/testclasses"
200+
:test-selectors {:default (fn [m] (and (:remote m) (not (:async m)) (not (:jdbc m))))
201+
:async :async
202+
:jdbc :jdbc
203+
:no-async (fn [m] (and (:remote m) (not (:async m))))
204+
:access-handler :access-handler
205+
:rewrite-handler :rewrite-handler
206+
:websocket :websocket
207+
:keepalive :keepalive
208+
:all :remote}
209+
:dependencies [
210+
[org.clojure/clojure "1.9.0"]
211+
[ring/ring-core "1.7.1"]
212+
[compojure "1.1.6"]
213+
[clj-http "0.7.8"]
214+
[clj-http-lite "0.3.0"]
215+
[junit/junit "4.13.1"]
216+
[org.clojure/java.jdbc "0.3.3"]
217+
[org.clojure/tools.nrepl "0.2.3"]
218+
;for test file upload with ring-core which need it
219+
[javax.servlet/servlet-api "2.5"]
220+
[org.codehaus.jackson/jackson-mapper-asl "1.9.13"]
221+
[org.clojure/data.json "0.2.5"]
222+
[stylefruits/gniazdo "1.1.2"]
223+
[javax.xml.bind/jaxb-api "2.3.1"]
224+
;[mysql/mysql-connector-java "5.1.30"]
225+
[redis.clients/jedis "3.1.0"]
226+
[org.clojure/tools.trace "0.7.10"]
227+
]
228+
}
188229
})

src/java/nginx/clojure/net/NginxClojureSocketImpl.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,14 @@ protected void connect(String host, int port) throws IOException {
219219
log.debug("socket#%d: yield on connect", as.s);
220220
}
221221
attachCoroutine();
222-
Coroutine.yield();
222+
223+
try {
224+
Coroutine.yield();
225+
} catch (IllegalStateException e) {
226+
as.close(); // high level caller maybe has not initialized socket so it has no chance to close the socket.
227+
throw e;
228+
}
229+
223230

224231
if (status == NginxClojureAsynSocket.NGX_HTTP_CLOJURE_SOCKET_ERR_RESOLVE) {
225232
throw new NoRouteToHostException(as.buildError(status));

src/java/nginx/clojure/wave/JavaAgent.java

Lines changed: 57 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,16 @@ public class JavaAgent {
9999

100100
public static void premain(String agentArguments, Instrumentation instrumentation) {
101101
ClassFileTransformer cft = buildClassFileTransformer(agentArguments);
102-
if (cft != null && !db.isEnableNativeCoroutine()) {
102+
if (cft != null) {
103103
instrumentation.addTransformer(cft, true);
104-
for (String c : db.getRetransformedClasses()) {
105-
try {
106-
instrumentation.retransformClasses(db.getClassLoader().loadClass(c));
107-
} catch (Throwable e) {
108-
db.warn("retransformClasses error:" + c, e);
109-
}
104+
if (!db.isEnableNativeCoroutine()) {
105+
for (String c : db.getRetransformedClasses()) {
106+
try {
107+
instrumentation.retransformClasses(db.getClassLoader().loadClass(c));
108+
} catch (Throwable e) {
109+
db.warn("retransformClasses error:" + c, e);
110+
}
111+
}
110112
}
111113
}
112114
}
@@ -181,12 +183,15 @@ public static ClassFileTransformer buildClassFileTransformer(String agentArgumen
181183
}
182184

183185
//load system configurations for method database
184-
try {
185-
db.info("load system coroutine wave file %s", "nginx/clojure/wave/coroutine-method-db.txt");
186-
MethodDatabaseUtil.load(db, "nginx/clojure/wave/coroutine-method-db.txt");
187-
} catch (IOException e) {
188-
db.error("can not load nginx/clojure/wave/coroutine-method-db.txt", e);
189-
}
186+
if (!db.isEnableNativeCoroutine()) {
187+
try {
188+
db.info("load system coroutine wave file %s", "nginx/clojure/wave/coroutine-method-db.txt");
189+
MethodDatabaseUtil.load(db, "nginx/clojure/wave/coroutine-method-db.txt");
190+
} catch (IOException e) {
191+
db.error("can not load nginx/clojure/wave/coroutine-method-db.txt", e);
192+
}
193+
}
194+
190195
String udfs = System.getProperty(NGINX_CLOJURE_WAVE_UDFS);
191196
if (udfs != null) {
192197
for (String udf : udfs.split(",|;")) {
@@ -215,18 +220,30 @@ public static ClassFileTransformer buildClassFileTransformer(String agentArgumen
215220

216221
}
217222

218-
static byte[] instrumentClass(MethodDatabase db, byte[] data, boolean check) {
219-
ClassReader r = new ClassReader(data);
220-
ClassWriter cw = new DBClassWriter(db, r);
221-
ClassVisitor cv = check ? new CheckClassAdapter(cw) : cw;
222-
ClassEntry ce = MethodDatabaseUtil.buildClassEntryFamily(db, r);
223-
if(db.shouldIgnore(r.getClassName()) || ce == null) {
224-
return null;
225-
}
226-
db.trace("TRANSFORM: %s", r.getClassName());
227-
InstrumentClass ic = new InstrumentClass(r.getClassName(), ce, cv, db, false);
228-
r.accept(ic, ClassReader.SKIP_FRAMES);
229-
return cw.toByteArray();
223+
static byte[] instrumentClass(MethodDatabase db, String className, byte[] data, boolean check) {
224+
225+
if (!db.isEnableNativeCoroutine() || (db.isEnableNativeCoroutine() && db.inClassesOrPackages(className))) {
226+
ClassReader r = new ClassReader(data);
227+
ClassWriter cw = db.isEnableNativeCoroutine() ? new ClassWriter(r, 0) : new DBClassWriter(db, r);
228+
ClassVisitor cv = check ? new CheckClassAdapter(cw) : cw;
229+
ClassEntry ce = MethodDatabaseUtil.buildClassEntryFamily(db, r);
230+
231+
if(db.shouldIgnore(r.getClassName()) || ce == null) {
232+
return null;
233+
}
234+
db.trace("TRANSFORM: %s", r.getClassName());
235+
236+
if (db.isEnableNativeCoroutine()) {
237+
RemoveMonitorVisitor rv = new RemoveMonitorVisitor(Opcodes.ASM9, cv);
238+
r.accept(rv, ClassReader.SKIP_FRAMES);
239+
} else {
240+
InstrumentClass ic = new InstrumentClass(r.getClassName(), ce, cv, db, false);
241+
r.accept(ic, ClassReader.SKIP_FRAMES);
242+
}
243+
244+
return cw.toByteArray();
245+
}
246+
return null;
230247
}
231248

232249
public static void dumpClass(byte[] classfileBuffer, File df, MethodDatabase db) {
@@ -257,22 +274,29 @@ public byte[] transform(ClassLoader loader, String className, Class<?> classBein
257274
if (className.startsWith("java/util/LinkedHashMap")) {
258275
return null;
259276
}
260-
if (db.meetTraceTargetClass(className)) {
277+
278+
boolean meetTraceClass = db.meetTraceTargetClass(className);
279+
280+
if (meetTraceClass) {
261281
db.info("meet traced class %s", className);
262282
}
263283

264284
try {
265-
byte[] bs = instrumentClass(db, classfileBuffer, check);
266-
if (db.isDump() && bs != null && bs.length != classfileBuffer.length) {
267-
File wavedFile = new File(new File(db.getDumpDir() + "/waved"), className + ".class");
268-
wavedFile.getParentFile().mkdirs();
269-
dumpClass(bs, wavedFile, db);
270-
}
271-
if (db.meetTraceTargetClass(className)) {
285+
286+
if (meetTraceClass) {
272287
File orgFile = new File(new File(db.getDumpDir() + "/org"), className + ".class");
273288
orgFile.getParentFile().mkdirs();
274289
dumpClass(classfileBuffer, orgFile, db);
275290
}
291+
292+
byte[] bs = instrumentClass(db, className, classfileBuffer, check);
293+
294+
if (meetTraceClass && bs != null || db.isDump() && bs != null && bs.length != classfileBuffer.length) {
295+
File wavedFile = new File(new File(db.getDumpDir() + "/waved"), className + ".class");
296+
wavedFile.getParentFile().mkdirs();
297+
dumpClass(bs, wavedFile, db);
298+
}
299+
276300
return bs;
277301
} catch(Throwable ex) {
278302
if (db.isDump()){

src/java/nginx/clojure/wave/MethodDatabase.java

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@
3434
import java.io.InputStream;
3535
import java.nio.charset.Charset;
3636
import java.util.ArrayList;
37+
import java.util.HashSet;
3738
import java.util.LinkedHashMap;
3839
import java.util.List;
3940
import java.util.Map;
41+
import java.util.Set;
4042
import java.util.concurrent.ConcurrentHashMap;
4143
import java.util.regex.Pattern;
4244

@@ -102,6 +104,9 @@ public class MethodDatabase implements LoggerService {
102104

103105
private final ConcurrentHashMap<String, LazyClassEntry> lazyClasses;
104106

107+
private final Set<String> packages;
108+
private int packageMinLen = 0;
109+
105110
private final ArrayList<FuzzyLazyClassEntry> fuzzlyLazyClasses;
106111

107112
private final ArrayList<String> retransformedClasses = new ArrayList<String>();
@@ -125,6 +130,7 @@ public class MethodDatabase implements LoggerService {
125130
private Pattern traceClassPattern = null;
126131
private Pattern traceClassMethodPattern = null;
127132

133+
128134
private String dumpDir;
129135

130136
public MethodDatabase(ClassLoader classloader) {
@@ -139,12 +145,20 @@ public MethodDatabase(ClassLoader classloader) {
139145
fuzzlyLazyClasses = new ArrayList<MethodDatabase.FuzzyLazyClassEntry>();
140146
workList = new ArrayList<File>();
141147
filters = new ArrayList<String>();
148+
packages = new HashSet<String>();
142149
getLog();
143150
}
144151

145152
public ArrayList<String> getRetransformedClasses() {
146153
return retransformedClasses;
147154
}
155+
156+
/**
157+
* @return the packages
158+
*/
159+
public Set<String> getPackages() {
160+
return packages;
161+
}
148162

149163
public boolean isAllowMonitors() {
150164
return allowMonitors;
@@ -690,6 +704,26 @@ public boolean shouldIgnore(String className) {
690704
return false;
691705
}
692706

707+
public boolean inClassesOrPackages(String clz) {
708+
boolean inClasses = classes.containsKey(clz) || lazyClasses.containsKey(clz);
709+
if (inClasses) {
710+
return true;
711+
} else {
712+
do {
713+
int p = clz.lastIndexOf('/');
714+
if (p < 0) {
715+
return false;
716+
}
717+
clz = clz.substring(0, p);
718+
if (packages.contains(clz)) {
719+
return true;
720+
}
721+
} while (clz.length() > getPackageMinLen());
722+
723+
return false;
724+
}
725+
}
726+
693727
public boolean meetTraceTargetClass(String clz) {
694728
return traceClassPattern != null && traceClassPattern.matcher(clz).find();
695729
}
@@ -721,7 +755,21 @@ public ArrayList<String> getUserDefinedWaveConfigFiles() {
721755
// private static final ClassEntry CLASS_NOT_FOUND = new ClassEntry("<class not found>", new String[0], false);
722756

723757

724-
public static final class LazyClassEntry {
758+
/**
759+
* @return the packageMinLen
760+
*/
761+
public int getPackageMinLen() {
762+
return packageMinLen;
763+
}
764+
765+
/**
766+
* @param packageMinLen the packageMinLen to set
767+
*/
768+
public void setPackageMinLen(int packageMinLen) {
769+
this.packageMinLen = packageMinLen;
770+
}
771+
772+
public static final class LazyClassEntry {
725773
private final LinkedHashMap<String, Integer> methods = new LinkedHashMap<String, Integer>();
726774
private final String resource;
727775
public LazyClassEntry(String resource) {

0 commit comments

Comments
 (0)