/**
* Copyright (C) Zhang,Yuexiang (xfeep)
*
*/
package nginx.clojure.wave;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import nginx.clojure.asm.ClassReader;
import nginx.clojure.asm.tree.analysis.Analyzer;
import nginx.clojure.wave.MethodDatabase.ClassEntry;
import nginx.clojure.wave.MethodDatabase.FuzzyLazyClassEntry;
import nginx.clojure.wave.MethodDatabase.LazyClassEntry;
public class MethodDatabaseUtil {
public final static Pattern FUZZY_CLASS_PATTERN = Pattern.compile("(\\d+)");
public MethodDatabaseUtil() {
}
//eg. load(db, "nginx/clojure/wave/coroutine-method-db.txt")
public static void load(MethodDatabase db, String resource) throws IOException {
InputStream in = null;
try {
in = Thread.currentThread().getContextClassLoader().getResourceAsStream(resource);
if (in == null) {
throw new IOException("can not load resource [" + resource + "] from classpath");
}
BufferedReader r = new BufferedReader(new InputStreamReader(in, MethodDatabase.UTF_8));
db.getUserDefinedWaveConfigFiles().add(resource);
String l = null;
ClassEntry ce = null;
LazyClassEntry lce = null;
FuzzyLazyClassEntry flce = null;
int lc = 0;
String clz = null;
while ((l = r.readLine()) != null) {
lc++;
l = l.trim();
if (l.startsWith("class:")) {
lce = null;
flce = null;
clz = l.substring("class:".length());
ce = db.getClasses().get(clz);
if (ce == null) {
ce = buildClassEntryFamily(db, clz);
if (ce == null) {
db.warn("file %s line %d : not found class: %s", resource , lc, clz);
ce = null;
continue;
}
}
}else if (l.startsWith("lazyclass:")) {
clz = l.substring("lazyclass:".length());
ce = null;
flce = null;
lce = db.getLazyClasses().get(clz);
if (lce == null) {
db.getLazyClasses().put(clz, lce = new LazyClassEntry(resource));
}
}else if (l.startsWith("fuzzyclass:")) {
clz = l.substring("fuzzyclass:".length());
ce = null;
lce = null;
flce = new FuzzyLazyClassEntry(Pattern.compile(clz), resource);
db.getFuzzlyLazyClasses().add(flce);
}else if (l.startsWith("retransform:")) {
db.getRetransformedClasses().add(l.substring("retransform:".length()).trim());
ce = null;
lce = null;
flce = null;
}else if (l.startsWith("filter:")) {
db.getFilters().add(l.substring("filter:".length()).trim());
ce = null;
lce = null;
flce = null;
}else if (l.length() == 0 || (ce == null && lce == null && flce == null) || l.charAt(0) == '#'){
continue;
}else {
String[] ma = l.split(":");
String m = ma[0];
Integer st = MethodDatabase.SUSPEND_NORMAL;
if (ma.length > 1) {
if (MethodDatabase.SUSPEND_NORMAL_STR.equals(ma[1])) {
st = MethodDatabase.SUSPEND_NORMAL;
}else if (MethodDatabase.SUSPEND_NONE_STR.equals(ma[1])) {
st = MethodDatabase.SUSPEND_NONE;
}else if (MethodDatabase.SUSPEND_JUST_MARK_STR.equals(ma[1])) {
st = MethodDatabase.SUSPEND_JUST_MARK;
}else if (MethodDatabase.SUSPEND_BLOCKING_STR.equals(ma[1])) {
st = MethodDatabase.SUSPEND_BLOCKING;
}else if (MethodDatabase.SUSPEND_FAMILY_STR.equals(ma[1])) {
st = MethodDatabase.SUSPEND_FAMILY;
}else if (MethodDatabase.SUSPEND_SKIP_STR.equals(ma[1])) {
st = MethodDatabase.SUSPEND_SKIP;
}else {
db.warn("file %s line %d : unknown suspend type: %s , we just set to 'normal'", resource , lc, ma[1]);
st = MethodDatabase.SUSPEND_NORMAL;
}
}
if (lce != null) {
Map<String, Integer> methods = lce.getMethods();
Integer ost = methods.get(m);
if (ost == null || st.intValue() > ost.intValue()) {
methods.put(m, st);
}else {
st = ost;
}
if (db.meetTraceTargetClassMethod(clz, m)) {
db.info("meet traced method %s.%s, suspend type = %s", clz, m, MethodDatabase.SUSPEND_TYPE_STRS[st]);
}
continue;
}else if (flce != null) {
Map<String, Integer> methods = flce.getMethods();
Integer ost = methods.get(m);
if (ost == null || st.intValue() > ost.intValue()) {
methods.put(m, st);
}else {
st = ost;
}
if (db.meetTraceTargetClassMethod(clz, m)) {
db.info("meet traced method %s.%s, suspend type = %s", clz, m, MethodDatabase.SUSPEND_TYPE_STRS[st]);
}
continue;
}
if (m.charAt(0) == '/') { // regex pattern
Pattern p = Pattern.compile(m.substring(1));
boolean matched = false;
for (Entry<String, Integer> me : ce.getMethods().entrySet()) {
if (p.matcher(me.getKey()).find()) {
me.setValue(st);
matched = true;
}
}
if (!matched) {
db.warn("file %s line %d : none of methods matched regex: %s ,ignored", resource , lc, m);
}
}else if (ce.getMethods().get(m) == null) {
db.warn("file %s line %d : unknown method: %s ,ignored", resource , lc, m);
continue;
}else {
Integer ost = ce.getMethods().get(m);
if (ost == null || st.intValue() > ost.intValue()) {
ce.set(m, st);
}else {
st = ost;
}
if (db.meetTraceTargetClassMethod(clz, m)) {
db.info("meet traced method %s.%s, suspend type = %s", clz, m, MethodDatabase.SUSPEND_TYPE_STRS[st]);
}
}
}
}
if (db.isDebugEnabled()) {
MethodDatabase.getLog().debug("total filters:" + db.getFilters());
}
}finally {
if (in != null) {
in.close();
}
}
}
public static String toFuzzyString(Pattern p, String s, String rep) {
Matcher m = p.matcher(s);
int start = 0;
StringBuilder rt = new StringBuilder();
while (m.find()) {
rt.append(Matcher.quoteReplacement(s.substring(start, m.start())));
rt.append(rep);
start = m.end();
}
if (rt.length() == 0){
return null;
}
if (start < s.length()) {
rt.append(s.substring(start));
}
return rt.toString();
}
public static ClassEntry buildClassEntryFamily(MethodDatabase db, ClassReader r) {
ClassEntry ce = db.getClasses().get(r.getClassName());
if (ce == null) {
CheckInstrumentationVisitor civ = db.checkClass(r);
return buildClassEntryFamily(db, civ);
}
return ce;
}
public static Analyzer buildAnalyzer(MethodDatabase db) {
return new TypeAnalyzer(new TypeInterpreter(db));
// SimpleVerifier sv = new TypeInterpreter(db);//new SimpleVerifier();
// sv.setClassLoader(Thread.currentThread().getContextClassLoader());
// return new TypeAnalyzer(sv);
}
public static void mergeMethodsSuspendTypeFromSuper(ClassEntry ce, ClassEntry sce) {
if (ce == null || sce == null) {
return;
}
Map<String, Integer> sms = sce.getMethods();
for (Entry<String, Integer> me : ce.getMethods().entrySet()) {
Integer st = me.getValue();
if (st == MethodDatabase.SUSPEND_NONE) {
Integer sst = sms.get(me.getKey());
if (sst != null && sst != MethodDatabase.SUSPEND_NONE) {
me.setValue(sst);
}
}
}
}
public static ClassEntry buildClassEntryFamily(MethodDatabase db, CheckInstrumentationVisitor civ) {
ClassEntry ce = civ.getClassEntry();
String clz = civ.getName();
LazyClassEntry lce = db.getLazyClasses().get(clz);
if (lce != null) {
db.debug("rebuild wave info for lazy class %s", clz);
for (Entry<String, Integer> lme : lce.getMethods().entrySet()) {
String m = lme.getKey();
Integer st = lme.getValue();
if (m.charAt(0) == '/') { // regex pattern
Pattern p = Pattern.compile(m.substring(1));
boolean matched = false;
for (Entry<String, Integer> me : ce.getMethods().entrySet()) {
if (p.matcher(me.getKey()).find()) {
me.setValue(st);
matched = true;
}
}
if (!matched) {
db.warn("file %s lazy class %s: none of methods matched regex: %s ,ignored", lce.getResource() , clz, m);
}
}else if (ce.getMethods().get(m) == null) {
db.warn("file %s lazy class %s: : unknown method: %s ,ignored", lce.getResource() , clz, m);
continue;
}else {
Integer ost = ce.getMethods().get(m);
if (ost == null || st.intValue() > ost.intValue()) {
ce.set(m, st);
}
}
}
}
if (FUZZY_CLASS_PATTERN.matcher(clz).find()) {
for (FuzzyLazyClassEntry flce : db.getFuzzlyLazyClasses()) {
if (flce.getPattern().matcher(clz).find()) {
db.debug("rebuild wave info for fuzzylazy class %s, pattern %s", clz, flce.getPattern().toString());
for (Entry<String, Integer> lme : flce.getMethods().entrySet()) {
String m = lme.getKey();
Integer st = lme.getValue();
if (m.charAt(0) == '/') { // regex pattern
Pattern p = Pattern.compile(m.substring(1));
boolean matched = false;
for (Entry<String, Integer> me : ce.getMethods().entrySet()) {
if (p.matcher(me.getKey()).find()) {
me.setValue(st);
matched = true;
}
}
if (!matched) {
db.warn("file %s fuzzylazy class %s: none of methods matched regex: %s ,ignored", flce.getResource() , clz, m);
}
}else if (ce.getMethods().get(m) == null) {
db.warn("file %s fuzzylazy class %s: : unknown method: %s ,ignored", flce.getResource() , clz, m);
continue;
}else {
Integer ost = ce.getMethods().get(m);
if (ost == null || st.intValue() > ost.intValue()) {
ce.set(m, st);
}
}
}
}
}
}
String superName = ce.getSuperName();
db.recordSuspendableMethods(clz, ce);
if (superName != null) {
ClassEntry sce = db.getClasses().get(superName);
if (sce == null) {
CheckInstrumentationVisitor sciv = db.checkClass(superName);
if (sciv == null) {
db.error("super class %s can not visited", superName);
}else {
sce = buildClassEntryFamily(db, sciv);
db.recordSuspendableMethods(superName, sce);
}
}
mergeMethodsSuspendTypeFromSuper(ce, sce);
}
String[] interfaces = ce.getInterfaces();
if (interfaces != null) {
for (String itf : interfaces) {
ClassEntry sce = db.getClasses().get(itf);
if (sce == null) {
CheckInstrumentationVisitor sciv = db.checkClass(itf);
if (sciv == null) {
return null;
}
sce = buildClassEntryFamily(db, sciv);
db.recordSuspendableMethods(itf, sce);
}
mergeMethodsSuspendTypeFromSuper(ce, sce);
}
}
return ce;
}
public static ClassEntry buildClassEntryFamily(MethodDatabase db, String name) {
ClassEntry ce = db.getClasses().get(name);
if (ce == null) {
CheckInstrumentationVisitor civ = db.checkClass(name);
if (civ == null) {
return null;
}
return buildClassEntryFamily(db, civ);
}
return ce;
}
}