Я довольно новичок в управлении ASM и байт-кодами. Моя задача довольно проста: я рассказываю своему агенту, какой класс и метод посещать, и измеряет время выполнения этого метода. Измерение выполняется с помощью класса секундомера библиотеки Guava. ASM в основном окружает тело метода, запуская Секундомер в начале и останавливая его в конце тела метода и распечатывая время выполнения. Это работало над некоторыми методами, но большинство из них не удалось.Обработка байт-кода ASM: скорость выполнения метода измерения
Это часть, где ASM посещает начало целевого метода.
package com.agent.agentclasses;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.LocalVariablesSorter;
import org.objectweb.asm.Type;
public class ModifierMethodWriter extends LocalVariablesSorter /*MethodVisitor*/ {
private int time;
protected ModifierMethodWriter(int api, int access, String desc, MethodVisitor mv) {
super(api, access, desc, mv);
}
@Override
public void visitCode() {
System.out.println("I am @ModifierMethodWriter!");
/*
* Guava - beginning of the method !
*/
time = newLocal(Type.getObjectType("stopwatch"));
super.visitMethodInsn(Opcodes.INVOKESTATIC, "com/google/common/base/Stopwatch", "createStarted", "()Lcom/google/common/base/Stopwatch;", false);
super.visitVarInsn(Opcodes.ASTORE, time);
super.visitCode();
}
Добавление кода в конец!
package com.agent.agentclasses;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.commons.AdviceAdapter;
public class AddCodeBeforeReturn extends AdviceAdapter {
public AddCodeBeforeReturn(int api, MethodVisitor mv,
int acc, String name, String desc){
super(api, mv, acc, name, desc);
}
@Override
protected void onMethodExit(int opcode) {
/*
* Guava - Before return !
*/
super.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
super.visitIntInsn(Opcodes.ALOAD, 2);
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "com/google/common/base/Stopwatch", "stop", "()Lcom/google/common/base/Stopwatch;", false);
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "com/google/common/base/Stopwatch", "toString", "()Ljava/lang/String;", false);
super.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
}
}
Это тестовый класс, где я хочу, чтобы проверить мой агент: Это HSQL дб класс и я хочу, чтобы измерить время выполнения SelectAll() метод.
package com.agent.database;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import com.agent.testclasses.*;
public class Database {
private Connection connection = null;
private ResultSet resultSet = null;
private Statement statement = null;
private String createtablestr = " CREATE TABLE IF NOT EXISTS Students(Id int,Name varchar(255));";
private static int id = 1;
public String connect() {
System.out.println("I am in connect!");
try{
Class.forName("org.hsqldb.jdbcDriver");
connection = DriverManager.getConnection("jdbc:hsqldb:file:C:/hsqldb/studentdb", "sa", "");
if (connection == null)
{
return "Error: Connection failed.";
}
statement = connection.createStatement();
statement.executeUpdate(createtablestr);
}catch(Throwable ex){
ex.printStackTrace();
}
return "Connection succesful!";
}
public String insert(Student student){
connect();
try {
String insertStr = "INSERT INTO Students VALUES ('"+ id +"','"+ student.getName() + "')";
statement.executeUpdate(insertStr);
id++;
}catch(Throwable ex){ex.printStackTrace(); return "Error: Data was not written!";}
return "Data written succesfully!";
}
public String selectAll() {
connect();
String outputHTML="";
try{
resultSet = statement.executeQuery("SELECT * FROM Students");
while (resultSet.next())
{
outputHTML+= "<tr>" + "<td>" + resultSet.getString("Id") + "</td>" + "<td>" + resultSet.getString("Name") + "</td>" + "</tr>";
}
resultSet.close();
connection.commit();
connection.close();
}
catch (Throwable e)
{
e.printStackTrace();
}
return outputHTML;
}
}
И, наконец, исключение, Jvm thorws:
Exception in thread "main" java.lang.VerifyError: Stack map does not match the one at exception handler 85
Exception Details:
Location:
com/agent/database/Database.connect()Ljava/lang/String; @85: astore_2
Reason:
Type top (current frame, locals[1]) is not assignable to 'stopwatch' (stack map, locals[1])
Current Frame:
bci: @12
flags: { }
locals: { 'com/agent/database/Database', top, 'com/google/common/base/Stopwatch' }
stack: { 'java/lang/Throwable' }
Stackmap Frame:
bci: @85
flags: { }
locals: { 'com/agent/database/Database', 'stopwatch' }
stack: { 'java/lang/Throwable' }
Bytecode:
0x0000000: b800 2a4d b200 3212 34b6 003a 123c b800
0x0000010: 4257 2a12 4412 4612 48b8 004e b500 182a
0x0000020: b400 18c7 0014 1250 b200 3219 02b6 0053
0x0000030: b600 56b6 003a b02a 2ab4 0018 b900 5e01
0x0000040: 00b5 001c 2ab4 001c 2ab4 0020 b900 6402
0x0000050: 0057 a700 084d 2cb6 0067 1269 b200 3219
0x0000060: 02b6 0053 b600 56b6 003a b0
Exception Handler Table:
bci [12, 38] => handler: 85
bci [55, 82] => handler: 85
Stackmap Table:
append_frame(@55,Object[#88])
same_locals_1_stack_item_frame(@85,Object[#44])
same_frame(@90)
at com.agent.testers.tester1.main(tester1.java:15)
Мне кажется, что после ASM создает новую запись в таблице локальной переменной для Секундомера, JVM пытается переписать его с местным переменные метода, который я пытаюсь настроить (в этом случае selectAll()). Поэтому:
Тип верхней (текущий кадр, местные жители [1]) не может быть назначен на 'Секундомер' (стек карты, местные жители [1])
Я не уверен, если это актуальная проблема или нет, но мне нужно ее решить! В конце концов, мой агент должен быть подключен любым способом. Вся ваша помощь будет оценена! Спасибо.
Hello Eugene! Прежде всего, поблагодарите свой ответ! Во-вторых, я немного смущен, потому что не знаю, – pvammus