Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

在线debug时,是否可以展示方法返回值? #167

Open
724027069 opened this issue May 12, 2023 · 3 comments
Open

在线debug时,是否可以展示方法返回值? #167

724027069 opened this issue May 12, 2023 · 3 comments

Comments

@724027069
Copy link

No description provided.

@fuwei199006
Copy link

fuwei199006 commented May 12, 2023 via email

@724027069
Copy link
Author

我想到一种解决方案:在qunar.tc.bistoury.instrument.client.debugger.DebuggerMethodVisitor中新增onMethodExit方法,该方法继承自org.objectweb.asm.commons.AdviceAdapter,可以再目标方法返回值之前获取到返回值,由于每个方法都会插桩,所以我们需要记录断点所在方法名称(qunar.tc.bistoury.instrument.client.debugger.DebuggerMethodVisitor#methodUniqueName),用于后续比较,代码如下:

    @Override
    protected void onMethodExit(int opcode) {
        if (opcode == RETURN) {
            super.visitLdcInsn("void method");
        } else if (opcode == ATHROW) {
            super.visitLdcInsn("Exception happened");
        } else if (opcode == ARETURN) {
            // 复制操作数栈顶的1个数值,并将复制结果压入操作数栈顶,此时操作数栈上有2个连续相同的数值
            // 复制的目的是,多出来的这个数值用来打印到控制台,原来栈顶的数值不受影响
            dup();
        } else if (opcode == LRETURN || opcode == DRETURN) {
            // 因为double和long类型(64bit)占2个slot,所以要复制操作数栈顶的2个数值,并将其压入操作数栈顶
            dup2();
            // 对栈顶的数据按照返回值类型进行包装,并用包装好的值替换原来栈顶的这个数值
            // double类型会用Double.valueOf()进行包装,long类型会用Long.valueOf()进行包装
            box(Type.getReturnType(methodDesc));
        } else {
            dup();
            // 这里排除上面几种返回值类型,这里的opcode应该是 FRETURN 和 IRETURN
            // 对相应类型的数据进行Float.valueOf()或者Integer.valueOf()包装
            box(Type.getReturnType(methodDesc));
        }

        boxingIfShould(methodDesc);

        // 将methodName压入栈顶
        super.visitLdcInsn(methodUniqueName);
        // 所以要调用swap方法,将栈顶最顶端的两个数值互换
        swap();

        // 因为这里打印时,需要参数是Object类型,所以上面的2个box(getReturnType())必须有,目的是将基本数据类型转成包装类
        // 否则打印时,传的是基本数据类型,不是Object一定会报错
        super.visitMethodInsn(INVOKESTATIC, SPY_NAME, BistourySpys1.FILL_RETURN_OBJ,
            "(Ljava/lang/String;Ljava/lang/Object;)V", false);
    }

DefaultSnapshotStore类中:

    @Override
    public void fillReturnObj(String breakpointId, String methodUniqueName, Object returnObj) {
        Snapshot snapshot = snapshotCache.get(breakpointId);
        if (snapshot == null) {
            logger.debug("fill returnObj error, {}, breakpoint not exist now", breakpointId);
            return;
        }
        if (Strings.isNullOrEmpty(snapshot.getMethodUniqueName()) || !snapshot.getMethodUniqueName().equals(methodUniqueName)) {
            return;
        }

        logger.debug("start fill returnObj, {}", breakpointId);
        String stacktraceRecord = DebugJsonWriter.write(returnObj);
        snapshot.setReturnObj(stacktraceRecord);
        logger.debug("end fill returnObj, {}, {}", breakpointId, stacktraceRecord);
    }

@xleiy
Copy link
Contributor

xleiy commented May 26, 2023

bistoury这边是没有支持的,可以看下你这种方案是否可行。现在得到方法的调用方看方法返回值

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants