[alibaba/arthas]添加look命令,用来观测方法内的本地变量表,可以通过行号或者特殊Code(jad -L 来输出)来指定位置

2024-07-17 877 views
7
为什么要做这个呢?
  • 协助快速定位排查数据问题,打日志看的话有时候会出现需要多次重复打的情况
  • 有看到isuues里边也有一些小伙伴提到这个
为什么要定义新的命令?
  • 跟观测非常契合的命令是watch,但是watch的对象是method,而我们观测local variables的时候,用来作为定位点是比较合适的(因为同一个变量会被多次重复赋值),这样会导致watchlistenerkey跟根据参数而有所差异,在处理注册监听时会比较麻烦
  • 独立一个命令也能降低对旧命令的影响
实现思路:
  • 定义新的Advice变量 varMap,类型是HashMap,key是变量名,value是变量值
  • 插桩的位置是命令参数传入的 行号 或 特殊Code 之前,并使用-1来表示方法退出前的插桩点
  • 监听器的注册等也作了相应的区分处理
已经做的基本测试:
  • 基础功能实现,能观测到定义的local variables,并能在condition-express中使用
  • 重复插桩处理,增加了对应的LocationFilter,避免重复插桩
  • 多监听的兼容支持
  • tracewatch命令的同时使用的兼容处理
其它:

回答

9

特殊Code的解释如下:

kotlin源码:

/*56*/    fun index(): JSONObject {
/*57*/        listOf(1,2,3,4,5,6).map {
/*58*/            listOf("a","b","c","d","e","f").map {
/*59*/                listOf(true,false,true,false,false).map {
/*60*/                    println(it)
/*61*/                    if (true) return JSONObject()
/*62*/                }
/*63*/            }
/*64*/        }
/*65*/        return ResponseBuilder().ok().data("Hello World!").build()
/*66*/    }

jad出来的样子:

       public final JSONObject index() {
           void var3_3;
           void $receiver$iv$iv;
           Iterable $receiver$iv;
/*57*/     Iterable iterable = $receiver$iv = (Iterable)CollectionsKt.listOf(1, 2, 3, 4, 5, 6);
           Collection destination$iv$iv = new ArrayList(CollectionsKt.collectionSizeOrDefault($receiver$iv, 10));
/*83*/     for (Object item$iv$iv : $receiver$iv$iv) {
               void $receiver$iv$iv2;
               Iterable $receiver$iv2;
               int n = ((Number)item$iv$iv).intValue();
               Collection collection = destination$iv$iv;
               boolean bl = false;
/*58*/         Iterable iterable2 = $receiver$iv2 = (Iterable)CollectionsKt.listOf("a", "b", "c", "d", "e", "f");
               Collection destination$iv$iv2 = new ArrayList(CollectionsKt.collectionSizeOrDefault($receiver$iv2, 10));
/*86*/         for (Object item$iv$iv2 : $receiver$iv$iv2) {
                   void $receiver$iv$iv3;
                   Iterable $receiver$iv3;
                   String string = (String)item$iv$iv2;
                   Collection collection2 = destination$iv$iv2;
                   boolean bl2 = false;
/*59*/             Iterable iterable3 = $receiver$iv3 = (Iterable)CollectionsKt.listOf(true, false, true, false, false);
                   Collection destination$iv$iv3 = new ArrayList(CollectionsKt.collectionSizeOrDefault($receiver$iv3, 10));
                   Iterator iterator2 = $receiver$iv$iv3.iterator();
                   if (iterator2.hasNext()) {
                       void it;
                       void it2;
                       void it3;
                       Object item$iv$iv3 = iterator2.next();
                       boolean bl3 = (Boolean)item$iv$iv3;
                       Collection collection3 = destination$iv$iv3;
                       boolean bl4 = false;
/*60*/                 System.out.println((boolean)it3);
                       JSONObject jSONObject = new JSONObject();
                       return jSONObject;
                   }
/*91*/             List list = (List)destination$iv$iv3;
                   collection2.add(list);
               }
/*92*/         List list = (List)destination$iv$iv2;
               collection.add(list);
           }
/*93*/     List cfr_ignored_0 = (List)var3_3;
           return new ResponseBuilder().ok().data((Object)"Hello World!").build();
       }

问题: 反编译后,它的行号分布是乱序的,另外实践中还发现有重复行号的问题,而且反编译后与源代码大相径庭,也生成了很多的额外的零时变量,也有很多行是没有行号的,所以使用行号定位的话,是不够完善的,有些点无法进行定位插入监听!

另外的想法: 如何能监听到所有本地变量的变化过程呢? -> 变量在何时会被改变呢? -> 赋值、作为方法参数被调用 基于此,通过筛选方法中的InsnNode,只保留 VarInsnNode 和 MethodInsnNode 作为备选插入点,然后生成类似如下的标记行并以此为变量监测的插入点. 但是感觉这样使用起来很麻烦,需要结合源码,对比行号,调用等才能找到对应的点,大佬觉得呢 @hengyunabc 目前格式如: 行号 + 标识 + 方法调用/变量赋值

/*57*/   d6e3-1   invoke-method: java.lang.Integer#valueOf:(I)Ljava/lang/Integer;
/*57*/   d6e3-2   invoke-method: java.lang.Integer#valueOf:(I)Ljava/lang/Integer;
/*57*/   d6e3-3   invoke-method: java.lang.Integer#valueOf:(I)Ljava/lang/Integer;
/*57*/   d6e3-4   invoke-method: java.lang.Integer#valueOf:(I)Ljava/lang/Integer;
/*57*/   d6e3-5   invoke-method: java.lang.Integer#valueOf:(I)Ljava/lang/Integer;
/*57*/   d6e3-6   invoke-method: java.lang.Integer#valueOf:(I)Ljava/lang/Integer;
/*57*/   395b-1   invoke-method: kotlin.collections.CollectionsKt#listOf:([Ljava/lang/Object;)Ljava/util/List;
/*82*/   0ee8-1   invoke-method: kotlin.collections.CollectionsKt#collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
/*82*/   f0bd-1   invoke-method: java.util.ArrayList#<init>:(I)V
/*83*/   7cda-1   invoke-method: java.lang.Iterable#iterator:()Ljava/util/Iterator;
/*83*/   ad72-1   invoke-method: java.util.Iterator#hasNext:()Z
/*83*/   b105-1   invoke-method: java.util.Iterator#next:()Ljava/lang/Object;
/*84*/   f9e5-1   invoke-method: java.lang.Number#intValue:()I
/*84*/   11a1-1   assign-variable: it
/*84*/   30e3-1   assign-variable: $i$a$-map-MainApplication$index$1
/*58*/   395b-2   invoke-method: kotlin.collections.CollectionsKt#listOf:([Ljava/lang/Object;)Ljava/util/List;
/*85*/   0ee8-2   invoke-method: kotlin.collections.CollectionsKt#collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
/*85*/   f0bd-2   invoke-method: java.util.ArrayList#<init>:(I)V
/*86*/   7cda-2   invoke-method: java.lang.Iterable#iterator:()Ljava/util/Iterator;
/*86*/   ad72-2   invoke-method: java.util.Iterator#hasNext:()Z
/*86*/   b105-2   invoke-method: java.util.Iterator#next:()Ljava/lang/Object;
/*87*/   a430-1   assign-variable: $i$a$-map-MainApplication$index$1$1
/*59*/   38d8-1   invoke-method: java.lang.Boolean#valueOf:(Z)Ljava/lang/Boolean;
/*59*/   38d8-2   invoke-method: java.lang.Boolean#valueOf:(Z)Ljava/lang/Boolean;
/*59*/   38d8-3   invoke-method: java.lang.Boolean#valueOf:(Z)Ljava/lang/Boolean;
/*59*/   38d8-4   invoke-method: java.lang.Boolean#valueOf:(Z)Ljava/lang/Boolean;
/*59*/   38d8-5   invoke-method: java.lang.Boolean#valueOf:(Z)Ljava/lang/Boolean;
/*59*/   395b-3   invoke-method: kotlin.collections.CollectionsKt#listOf:([Ljava/lang/Object;)Ljava/util/List;
/*88*/   0ee8-3   invoke-method: kotlin.collections.CollectionsKt#collectionSizeOrDefault:(Ljava/lang/Iterable;I)I
/*88*/   f0bd-3   invoke-method: java.util.ArrayList#<init>:(I)V
/*89*/   7cda-3   invoke-method: java.lang.Iterable#iterator:()Ljava/util/Iterator;
/*89*/   ad72-3   invoke-method: java.util.Iterator#hasNext:()Z
/*89*/   b105-3   invoke-method: java.util.Iterator#next:()Ljava/lang/Object;
/*90*/   cdc0-1   invoke-method: java.lang.Boolean#booleanValue:()Z
/*90*/   11a1-2   assign-variable: it
/*90*/   9fbd-1   assign-variable: $i$a$-map-MainApplication$index$1$1$1
/*60*/   9a06-1   invoke-method: java.io.PrintStream#println:(Z)V
/*61*/   d797-1   invoke-method: com.alibaba.fastjson.JSONObject#<init>:()V
/*91*/   fd39-1   invoke-method: java.util.Collection#add:(Ljava/lang/Object;)Z
/*92*/   fd39-2   invoke-method: java.util.Collection#add:(Ljava/lang/Object;)Z
/*67*/   962f-1   invoke-method: com.seewo.common.util.ResponseBuilder#<init>:()V
/*67*/   32f7-1   invoke-method: com.seewo.common.util.ResponseBuilder#ok:()Lcom/seewo/study/minder/common/util/ResponseBuilder;
/*67*/   7c58-1   invoke-method: com.seewo.common.util.ResponseBuilder#data:(Ljava/lang/Object;)Lcom/seewo/common/util/ResponseBuilder;
/*67*/   7be4-1   invoke-method: com.seewo.common.util.ResponseBuilder#build:()Lcom/alibaba/fastjson/JSONObject;
/*
0

已自行打包后吃到红利,过来还愿