/** @author i.zaytsev */ public class PoolTransformer implements ClassFileTransformer { private static final Log log = LogFactory.getLog(PoolTransformer.class); private static final boolean ITF_FALSE = false; private static final boolean ITF_TRUE = true; private Map<String, ThreadPoolCfg> pools = new ConcurrentHashMap<String, ThreadPoolCfg>(); public PoolTransformer(Collection<ThreadPoolCfg> pools) { for (ThreadPoolCfg p : pools) { register(p); } } public PoolTransformer(ThreadPoolCfg p) { register(p); } public boolean register(ThreadPoolCfg p) { return this.pools.put(p.className, p) == null; } @Override public byte[] transform( ClassLoader cl, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { byte[] bytes = null; ThreadPoolCfg md = pools.get(className); if (md != null) { if (className.equals(md.className)) { bytes = transformThreadPool(md, classfileBuffer); log.info("Transformed [" + className + "]"); } } return bytes; } private byte[] transformThreadPool(ThreadPoolCfg md, byte[] classfileBuffer) { ClassWriter w = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES); ClassVisitor a = new PoolClassAdapter(w, md); ClassReader r = new ClassReader(classfileBuffer); r.accept(a, ClassReader.EXPAND_FRAMES); return w.toByteArray(); } /** * Transforms ThreadPoolExecutor adding field {@link Stat} and changing constructor and method * 'execute'. Class visibility not private for jrockit runtime. */ protected static class PoolClassAdapter extends ClassVisitor { private ThreadPoolCfg md; private static final String CONSTRUCTOR = "<init>"; public PoolClassAdapter(ClassVisitor cv, ThreadPoolCfg md) { super(Opcodes.ASM4, cv); this.md = md; } @Override public MethodVisitor visitMethod( int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); String className = md.className; if (CONSTRUCTOR.equals(name)) { mv = new InitAdviceAdapter(mv, access, name, desc, className, md.histogram); } else if (md.execName.equals(name) && md.execSign.equals(desc)) { mv = new ExecAdviceAdapter(mv, access, name, desc, className); } return mv; } @Override public void visitEnd() { Util.addField(cv, Const.PSTAT_FNAME, Stat.class, false); super.visitEnd(); } } /** Add ThreadPoolExecutor constructor with registration invocation */ protected static class InitAdviceAdapter extends AdviceAdapter { private String className; private boolean histogram; public InitAdviceAdapter( MethodVisitor mv, int access, String name, String desc, String className, boolean histogram) { super(Opcodes.ASM4, mv, access, name, desc); this.className = className; this.histogram = histogram; } @Override protected void onMethodExit(int opcode) { if (opcode == Opcodes.RETURN) { // StatRegistry reg = StatRegistry.instance; // int index = reg.registerThreadPool(this, false); // this.threadCatPStat = reg.getStatHolder(index); mv.visitFieldInsn(GETSTATIC, Const.REG_T, "instance", Const.REG_D); int local7 = newLocal(Type.getObjectType(Const.REG_T)); mv.visitVarInsn(ASTORE, local7); mv.visitVarInsn(ALOAD, local7); mv.visitVarInsn(ALOAD, 0); mv.visitInsn(histogram ? ICONST_1 : ICONST_0); // Parameters: opcode, owner, name, desc, itf mv.visitMethodInsn( INVOKEVIRTUAL, Const.REG_T, "registerThreadPool", "(Ljava/lang/Object;Z)I", ITF_FALSE); int local8 = newLocal(Type.INT_TYPE); mv.visitVarInsn(ISTORE, local8); mv.visitVarInsn(ALOAD, 0); mv.visitVarInsn(ALOAD, local7); mv.visitVarInsn(ILOAD, local8); mv.visitMethodInsn( INVOKEVIRTUAL, Const.REG_T, "getStatHolder", "(I)".concat(Const.STAT_D), ITF_FALSE); mv.visitFieldInsn(PUTFIELD, className, Const.PSTAT_FNAME, Const.STAT_D); } } } /** Modifies method 'execute' of ThreadPoolExecutor */ protected static class ExecAdviceAdapter extends AdviceAdapter { private String className; public ExecAdviceAdapter( MethodVisitor mv, int access, String name, String desc, String className) { super(Opcodes.ASM4, mv, access, name, desc); this.className = className; } @Override protected void onMethodEnter() { super.onMethodEnter(); // if(runnable instance of Queuable) { mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(INSTANCEOF, Const.Q_T); Label L0 = new Label(); mv.visitJumpInsn(IFEQ, L0); // queuable.setThreadCatExec(System.nanoTime()); mv.visitVarInsn(ALOAD, 1); mv.visitTypeInsn(CHECKCAST, Const.Q_T); mv.visitVarInsn(ASTORE, 2); mv.visitVarInsn(ALOAD, 2); mv.visitMethodInsn(INVOKESTATIC, Const.SYSTEM_T, "nanoTime", "()J", ITF_FALSE); mv.visitMethodInsn(INVOKEINTERFACE, Const.Q_T, Const.EXEC_SETTER, "(J)V", ITF_TRUE); // queuable.setThreadCatStat(this.threadCatStat); mv.visitVarInsn(ALOAD, 2); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn(GETFIELD, className, Const.PSTAT_FNAME, Const.STAT_D); mv.visitMethodInsn( INVOKEINTERFACE, Const.Q_T, Const.PSTAT_SETTER, "(".concat(Const.STAT_D).concat(")V"), ITF_TRUE); // } mv.visitLabel(L0); } } }
/** * Client-side serializer. Encode commands, decode statistics data. * * @author i.zaytsev */ public class CSerializer { private static final Log log = LogFactory.getLog(CSerializer.class); private CharBuffer charBuffer = CharBuffer.allocate(10 * 1024); private JsonWriteHelper jsonWriter = new JsonWriteHelper(charBuffer); private JsonReadHelper jsonReader = new JsonReadHelper(charBuffer); public CharBuffer encodeListCmd(Mode mode) { charBuffer.clear(); jsonWriter .startObject() .writeString(FIELD.type.name(), TYPE.listCmd.name()) .next() .writeString(FIELD.data.name(), mode.name()) .endObject(); charBuffer.flip(); return charBuffer; } public CharBuffer encodeDescCmd(String template) { charBuffer.clear(); jsonWriter .startObject() .writeString(FIELD.type.name(), TYPE.descCmd.name()) .next() .writeString(FIELD.data.name(), template) .endObject(); charBuffer.flip(); return charBuffer; } public CharBuffer encodeRegCmd(Mode mode, String[] names) { charBuffer.clear(); jsonWriter .startObject() .writeString(FIELD.type.name(), TYPE.regCmd.name()) .next() .writeFieldName(FIELD.data.name()) .startObject() .writeString(FIELD.mode.name(), mode.name()) .next() .writeFieldName(FIELD.names.name()) .startArray(); int i = 0; for (String name : names) { if (i > 0) jsonWriter.next(); jsonWriter.writeString(name); i++; } jsonWriter.endArray().endObject().endObject(); charBuffer.flip(); return charBuffer; } public CharBuffer encodeUnregCmd(Mode mode, String[] names) { charBuffer.clear(); jsonWriter .startObject() .writeString(FIELD.type.name(), TYPE.unregCmd.name()) .next() .writeFieldName(FIELD.data.name()) .startObject() .writeString(FIELD.mode.name(), mode.name()) .next() .writeFieldName(FIELD.names.name()) .startArray(); int i = 0; for (String name : names) { if (i > 0) jsonWriter.next(); jsonWriter.writeString(name); i++; } jsonWriter.endArray().endObject().endObject(); charBuffer.flip(); return charBuffer; } public CharBuffer encodeStatCmd() { charBuffer.clear(); jsonWriter.startObject().writeString(FIELD.type.name(), TYPE.statCmd.name()).endObject(); charBuffer.flip(); return charBuffer; } public CharBuffer encodeHistCmd(Boolean activate, String[] names) { charBuffer.clear(); jsonWriter.startObject(); if (activate != null) { jsonWriter .writeString(FIELD.type.name(), TYPE.histCmd.name()) .next() .writeFieldName(FIELD.data.name()) .startObject() .writeBoolean(FIELD.activate.name(), activate) .next() .writeFieldName(FIELD.names.name()) .startArray(); if (names != null) { int i = 0; for (String name : names) { if (i > 0) jsonWriter.next(); jsonWriter.writeString(name); i++; } } jsonWriter.endArray().endObject(); } else { jsonWriter .writeString(FIELD.type.name(), TYPE.statCmd.name()) .next() .writeFieldName(FIELD.data.name()) .startObject() .writeBoolean(FIELD.hist.name(), true) .endObject(); } jsonWriter.endObject(); charBuffer.flip(); return charBuffer; } public CharBuffer encodeResetCmd(int[] ids) { charBuffer.clear(); jsonWriter .startObject() .writeString(FIELD.type.name(), TYPE.resetCmd.name()) .next() .writeFieldName(FIELD.data.name()) .startArray(); int i = 0; for (int id : ids) { if (i > 0) jsonWriter.next(); jsonWriter.writeDecimal(id, 0); } jsonWriter.endArray().endObject(); charBuffer.flip(); return charBuffer; } /*------------------------------------------------------------------------*/ /* */ public ServiceBox decodeBox(String s) { ServiceBox box = null; charBuffer.clear(); charBuffer.put(s); charBuffer.flip(); // String typeString = s.replaceAll("\\\":.*$", "").replaceAll("^.*\\\"", ""); int typeLength = jsonReader.stringLength(FIELD.type.name()); if (typeLength < 1) { log.err("Data type undefined " + s); return null; } String typeString = charBuffer.subSequence(0, typeLength).toString(); TYPE type = TYPE.valueOf(typeString); int statusCode = 0; if (type == TYPE.status) { statusCode = (int) jsonReader.readDecimal(FIELD.data.name()).getUnscaled(); } else { jsonReader.findField(FIELD.data.name()); } ServiceBox.Type boxType; switch (type) { case pList: case mList: case rList: box = createStatBox(ServiceBox.Type.REG); break; case descData: box = createStringBox(ServiceBox.Type.DESC); break; case status: box = createStringBox(statusCode); break; case statData: box = createSnapshotBox(ServiceBox.Type.STAT); break; case cfgData: box = createStringBox(ServiceBox.Type.CFG); break; } return box; } private ServiceBox<String> createStringBox(ServiceBox.Type type) { List<String> list = new ArrayList<String>(); while (jsonReader.next()) { int len = jsonReader.stringLength(); list.add(charBuffer.subSequence(0, len).toString()); charBuffer.position(charBuffer.position() + len); } return new ServiceBox<String>(type, list); } private ServiceBox<String> createStringBox(int statusCode) { List<String> list = new ArrayList<String>(); list.add(statusCode == 0 ? "Ok" : "Error " + statusCode); return new ServiceBox<String>(ServiceBox.Type.STATUS, list); } private ServiceBox<Snapshot> createSnapshotBox(ServiceBox.Type type) { List<Snapshot> snapshots = new ArrayList<Snapshot>(); while (jsonReader.next()) { Snapshot sn = decodeSnapshot(); snapshots.add(sn); } return new ServiceBox<Snapshot>(type, snapshots); } private Snapshot decodeSnapshot() { long execCount = jsonReader.readDecimal(FIELD.count.name()).getUnscaled(); long firstTime = jsonReader.readDecimal(FIELD.first.name()).getUnscaled(); long lastTime = jsonReader.readDecimal(FIELD.last.name()).getUnscaled(); jsonReader.findField(FIELD.exec.name()); TimeHolder[] timeHolders = decodeTimeHolders(); return new SnapshotImpl(firstTime, lastTime, execCount, timeHolders); } private TimeHolder[] decodeTimeHolders() { ArrayList<TimeHolder> list = new ArrayList<TimeHolder>(); while (jsonReader.next()) { long min = jsonReader.readDecimal(FIELD.min.name()).getUnscaled(); long max = jsonReader.readDecimal(FIELD.max.name()).getUnscaled(); long accum = jsonReader.readDecimal(FIELD.accum.name()).getUnscaled(); int[] hist = decodeHistogram(); TimeHolder holder = new TimeHolder(min, max, accum, hist); list.add(holder); } TimeHolder[] holders = list.toArray(new TimeHolder[] {}); return holders; } private int[] decodeHistogram() { jsonReader.findField(FIELD.hist.name()); ArrayList<Integer> list = new ArrayList<Integer>(); while (jsonReader.next()) { list.add((int) jsonReader.readDecimal().getUnscaled()); } int[] hist = new int[list.size()]; for (int i = 0; i < list.size(); i++) { hist[i] = list.get(i); } return hist; } private ServiceBox<StatDto> createStatBox(ServiceBox.Type type) { List<StatDto> sts = new ArrayList<StatDto>(); for (int i = 0; jsonReader.next(); i++) { StatDto s = decodeStatistics(i); sts.add(s); } return new ServiceBox<StatDto>(type, sts); } private StatDto decodeStatistics(int ind) { JsonHelper.Decimal decimal = jsonReader.readDecimal(FIELD.id.name()); int id = decimal == null ? ind : (int) decimal.getUnscaled(); int len = jsonReader.stringLength(FIELD.name.name()); String name = charBuffer.subSequence(0, len).toString(); Boolean b = jsonReader.readBoolean(FIELD.hist.name()); boolean histogram = b != null && b; return new StatDto(id, name, histogram); } /** For client-side only. */ public static class StatDto { public final int id; public final String name; public final boolean hist; public StatDto(int id, String name, boolean hist) { this.id = id; this.name = name; this.hist = hist; } @Override public boolean equals(Object obj) { if (StatDto.class.isInstance(obj)) { StatDto other = (StatDto) obj; return id == other.id && name.equals(other.name) && hist == other.hist; } return false; } @Override public int hashCode() { return (id + name + hist).hashCode(); } } }