Bonsoir, je recherche un moyen de timer toutes les méthodes d'un programme sans pour autant les modifier à la main une à une.
J'ai essayé en utilisant BCEL (en adaptant un peu un code chinois), cela fonctionne bien sauf sur les méthodes contenant des "try - catch".
Si vous connaissez un moyen pour régler ce problème (ou un autre système), je vous en serais éterneeeeeeellement reconnaissant
Voilà le code de l'agent que j'utilise actuellement. Fonctionne bien sauf si une classe possède un code du genre
try
{
// instruction non vide
}
catch (Exception e) {}
--------------------------------------------------------------------
import
java.io.ByteArrayInputStream;
import
java.io.ByteArrayOutputStream;
import
java.io.InputStream;
import
java.lang.instrument.ClassFileTransformer;
import
java.lang.instrument.IllegalClassFormatException;
import
java.lang.instrument.Instrumentation;
import
java.security.ProtectionDomain;
import
org.apache.bcel.Constants;
import
org.apache.bcel.classfile.ClassParser;
import
org.apache.bcel.classfile.JavaClass;
import
org.apache.bcel.classfile.Method;
import
org.apache.bcel.generic.ClassGen;
import
org.apache.bcel.generic.ConstantPoolGen;
import
org.apache.bcel.generic.InstructionConstants;
import
org.apache.bcel.generic.InstructionFactory;
import
org.apache.bcel.generic.InstructionList;
import
org.apache.bcel.generic.MethodGen;
import
org.apache.bcel.generic.ObjectType;
import
org.apache.bcel.generic.PUSH;
import
org.apache.bcel.generic.Type;
/**
*
Class
réécrivant
*/public
class Timer
implements ClassFileTransformer {
private
static
final String
EXT_NAME =
"_copie";
public
byte [] transform(ClassLoader loader, String className, Class cBR, ProtectionDomain pD,
byte [] classfileBuffer)
throws IllegalClassFormatException {
try {
// Charge la classe couranteString classFileName = className +
".class";InputStream is = Timer.
class .getClassLoader().getResourceAsStream(classFileName);JavaClass jclass =
new ClassParser(is, classFileName).parse();
// Initialise le ClassGenClassGen cgen =
new ClassGen(jclass);
// Ajoute le timer pour chaque méthode de la classeMethod[] methods = jclass.getMethods();
for (
int index = 0; index < methods.
length; index++) {
try {
// Pas de timer sur constructeur (... pour le moment !)
if (!methods[index].getName().equals(
" "))addTimer (cgen, methods[index]);
}
catch (Exception e) {e.printStackTrace();}}
// Renvoie le code la classe ByteArrayOutputStream bos =
new ByteArrayOutputStream();cgen.getJavaClass().dump(bos);
return bos.toByteArray();
}
catch (Exception e) {
e.printStackTrace();
}
return
null ;}
private
static
void addTimer(ClassGen cgen, Method method) {
// ----------------- DES INITIALISATIONS...
InstructionFactory ifact
new InstructionFactory(cgen);InstructionList ilist
new InstructionList();ConstantPoolGen pgen = cgen.getConstantPool();
String cname = cgen.getClassName();
MethodGen wrapgen =
new MethodGen(method, cname, pgen);wrapgen.setInstructionList(ilist);
// ----------------- RENOMME LA METHODE ORIGINALE
// Méthode à modifierMethodGen methgen =
new MethodGen(method, cname, pgen);
// Suppression de la méthodecgen.removeMethod(method);
String iname = methgen.getName() +
EXT_NAME ;methgen.setName(iname);
// Ajout de la méhode, renomméecgen.addMethod(methgen.getMethod());
// ----------------- PREPARE LA NOUVELLE METHODE
// Type de retour de la méthodeType result = methgen.getReturnType();
// Paramètres de la méthodeType[] parameters = methgen.getArgumentTypes();
int stackIndex = methgen.isStatic() ? 0 : 1;
for (
int i = 0; i < parameters.
length; i++)stackIndex += parameters[i].getSize();
// Enregistre le temps au début de la méthodeilist.append(ifact.createInvoke(
"java.lang.System",
"currentTimeMillis", Type.
LONG , Type.
NO_ARGS , Constants.
INVOKESTATIC ));ilist.append(InstructionFactory.
createStore (Type.
LONG , stackIndex));
// Méthode static ou non TODO Voir pour constructeurs ici
int offset = 0;
short invoke = Constants.
INVOKESTATIC ;
if (!methgen.isStatic()) {
ilist.append(InstructionFactory.
createLoad (Type.
OBJECT , 0));offset = 1;
invoke = Constants.
INVOKEVIRTUAL ;}
// Les paramètres
for (
int i = 0; i < parameters.
length; i++) {
Type type = parameters[i];
ilist.append(InstructionFactory. createLoad (type, offset));
offset + = type.getSize();
}
// Signature de la méthodeilist.append(ifact.createInvoke(cname, iname, result, parameters, invoke));
// ???
if (result ! = Type.
VOID )ilist.append(InstructionFactory.
createStore (result, stackIndex+2));
// ----------------- AFFICHAGE DU TEMPS ECOULE
ilist.append(ifact.createFieldAccess(
"java.lang.System",
"out",
new ObjectType(
"java.io.PrintStream"),Constants.
GETSTATIC ));ilist.append(InstructionConstants.
DUP );ilist.append(InstructionConstants.
DUP );String text = cgen.getClassName() +
"\t"+ methgen.getName() +
"\t";ilist.append(
new PUSH(pgen, text));
ilist.append(ifact.createInvoke(
"java.io.PrintStream",
"print", Type.
VOID ,
new Type[] { Type.
STRING },Constants.
INVOKEVIRTUAL ));ilist.append(ifact.createInvoke(
"java.lang.System",
"currentTimeMillis", Type.
LONG , Type.
NO_ARGS , Constants.
INVOKESTATIC ));ilist.append(InstructionFactory.
createLoad (Type.
LONG , stackIndex));ilist.append(InstructionConstants.
LSUB );
ilist.append(ifact.createInvoke(
"java.io.PrintStream",
"print", Type.
VOID ,
new Type[] { Type.
LONG },Constants.
INVOKEVIRTUAL ));ilist.append(
new PUSH(pgen,
" ms."));ilist.append(ifact.createInvoke(
"java.io.PrintStream",
"println", Type.
VOID ,
new Type[] { Type.
STRING },Constants.
INVOKEVIRTUAL ));
// ----------------- RENVOI LE RETOUR DE LA METHODE ORIGINALE
if (result ! = Type.
VOID ) ilist.append(InstructionFactory.
createLoad (result, stackIndex+2));
ilist.append(InstructionFactory.
createReturn (result));
// ----------------- POUR FINIR...wrapgen.stripAttributes(
true );wrapgen.setMaxStack();
wrapgen.setMaxLocals();
cgen.addMethod(wrapgen.getMethod());
ilist.dispose();
}
/**
*
Correspond
au
"Main"
de
l'agent.
Utiliser
l'appel
:
*
java
-javaagent:test.jar
package.MainClass
*
@param
options
*
@param
ins
*/
public
static
void premain(String options, Instrumentation ins) {
ins.addTransformer(
new Timer());}
}
Afficher la suite