详解C语言动态跟踪工具ProbeVue如何调试Java应用程序

ProbeVue 具有以下特性:

◆跟踪钩子不需要作为源代码的组成部分预先编译。

◆ProbeVue 适用于 32/64 位内核和应用程序,不需要做任何修改。

◆在通过 ProbeVue 放置跟踪钩子之前,它们并不存在。

◆可以立即查看跟踪活动捕捉的跟踪数据,可以作为终端输出显示它们,或者保存到文件中供以后查看。

◆跟踪钩子可以应用于任何函数的入口或出口(当前对于系统调用只支持出口探测点)。

◆当探测类型为入口时,可以探测传递给函数的参数,这要求在 Vue 脚本的开头或通过头文件定义函数原型。

◆通过在出口点应用跟踪钩子并指定函数原型,可以探测函数的退出/返回值。

◆可以使用 ProbeVue 进行性能分析和问题调试。

使用 ProbeVue 的前提条件

◆AIX V6.10 和更高版本

◆文件集:不需要特殊的文件集,基本操作系统附带所需的文件集。

◆在尝试探测之前需要启用 ProbeVue 特性,可以使用 SMIT 启用它。

Vue 脚本语法

◆探测 Java 函数

@@uftjava:PID:*:"fully qualified function name":entry  

◆探测 Java 库例程:与 Java 函数相同。

命令语法

单独启动 Java 应用程序和 ProbeVue

◆Java 应用程序

对于 32 位:java -agentlib:probevuejava <additional parameters> myjavaapp  



对于 64 位:java -agentlib:probevuejava64 <additional parameters> myjavaapp  



 

◆ProbeVue

ProbeVue <additional arguments> myscript.e <script arguments> 


 

作为 ProbeVue 的子进程启动 Java 应用程序

◆对于 32 位:probevue -X <path of java> -A "-agentlib:probevuejava <additional parameters> my javaapp" myscript.e

◆对于 64 位:probevue -X <path of java> -A "-agentlib:probevuejava64 <additional parameters> my javaapp" myscript.e

基本探测示例

基本探测示例:myjava.java

import java.lang.reflect.*;  


import java.util.*;  


import java.lang.*;  


 


class myclass1  


{  


        int i;  


        float f;  


        double d;  


        boolean b;  


        String s;  


        public myclass1(int j)  


        {  


                i=j;  


        }  


        public void set_i(int j)  


                {  


                        i=j;  


                }  


        public void set_f(float j)  


                {  


                        f=j;  


                }  


        public void set_d(double j)  


                {  


                        d=j;  


                }  


        public void set_b(boolean j)  


                {  


                        b=j;  


                }  


        public void set_s(String j)  


                {  


                        s=j;  


                }  


        public  void print_i()  


                {  


                        System.out.println("Value of Integer i:"+i);  


                        System.out.println("Value of Float f:"+f);  


                        System.out.println("Value of Double d:"+d);  


                        System.out.println("Value of Boolean b:"+b);  


                        System.out.println("Value of String s:"+s);  


                }  


}  


public class myjava  


{  


        public static void main(String args[]) throws java.lang.InterruptedException  


        {  


                Thread.sleep(60);  


                System.out.println("In main");  


                myclass1 MC1=new myclass1(20);  


                MC1.set_i(10);  


                MC1.set_f((float)10.03);  


                MC1.set_d(10.1123);  


                MC1.set_b(false);  


                MC1.set_s("ProbeVue");  


                MC1.print_i();  


                int [] int1;  


                int1 = new int[10];  


                for(int i=0;i<10;i++)  


                Array.set(int1,i,(int)i);  


                for(int i=0;i<10;i++)  


                {  


                        System.out.println(Array.getInt(int1,i));  


                 }  


 


 


        }  


}  


 

执行基本探测的 Vue 脚本:basic_probing.e

@@BEGIN  


{  


// Declare and Initialize the variable to track the number of calls made   


// to Array.set function  


        int Number_Of_Calls_Of_Array_set;  


        Number_Of_Calls_Of_Array_set=0;  


}  


//Probe String to trace the calls to function myclass1.set_d  


@@uftjava:$__CPID:*:"myclass1.set_d":entry  


{  


//Printing the message for user notification that this function has been called  


//By adding ProbeVue tag to message we can easily filter out the ProbeVue messages only.  


        printf("ProbeVue - Entered  myclass1.set_d function \n");  


}  


@@uftjava:$__CPID:*:"myclass1.set_f":entry  


{  


        printf("ProbeVue - Entered  myclass1.set_f function \n");  


}  


@@uftjava:$__CPID:*:"myclass1.set_i":entry  


{  


        printf("ProbeVue - Entered  myclass1.set_i function \n");  


}  


@@uftjava:$__CPID:*:"myclass1.set_s":entry  


{  


        printf("ProbeVue - Entered  myclass1.set_s function \n");  


}  


@@uftjava:$__CPID:*:"myclass1.set_b":entry  


{  


        printf("ProbeVue - Entered  myclass1.set_b function \n");  


}  


@@uftjava:$__CPID:*:"myjava.main":entry  


{  


        printf("ProbeVue - Entered myjava.main function \n");  


// Printing the Process Id and Parent Process Id  


        printf("       Process Id : %ld\n",__pid);  


        printf("Parent Process Id : %ld\n",__ppid);  


}  


@@uftjava:$__CPID:*:"java.lang.reflect.Array.set":entry  


{  


        printf("ProbeVue - Entered java.lang.reflect.Array.set function \n");  


// Increment the count whenever the function is called  


        Number_Of_Calls_Of_Array_set++;  


}  


@@syscall:$__CPID:exit:entry  


{  


// Exit when the application exits  


        exit();  


}  


@@END  


{  


//This is executed when ProbeVue session exits and prints the following message.  


        printf("Number Of times function - \"java.lang.reflect.Array.set\" called is   


   : %d\n",Number_Of_Calls_Of_Array_set);  


}  


 

输出

# probevue -X `which java` -A "-agentlib:probevuejava myjava" basic_probing.e  


ProbeVue - Entered myjava.main function  


       Process Id : 7209080  


Parent Process Id : 5767168  


In main  


Value of Integer i:10  


ProbeVue - Entered  myclass1.set_i function  


ProbeVue - Entered  myclass1.set_f function  


ProbeVue - Entered  myclass1.set_d function  


ProbeVue - Entered  myclass1.set_b function  


ProbeVue - Entered  myclass1.set_s function  


Value of Float f:10.03  


Value of Double d:10.1123  


Value of Boolean b:false  


Value of String s:ProbeVue  


0  


1  


2  


3  


4  


5  


6  


7  


8  


9  


ProbeVue - Entered java.lang.reflect.Array.set function  


ProbeVue - Entered java.lang.reflect.Array.set function  


ProbeVue - Entered java.lang.reflect.Array.set function  


ProbeVue - Entered java.lang.reflect.Array.set function  


ProbeVue - Entered java.lang.reflect.Array.set function  


ProbeVue - Entered java.lang.reflect.Array.set function  


ProbeVue - Entered java.lang.reflect.Array.set function  


ProbeVue - Entered java.lang.reflect.Array.set function  


ProbeVue - Entered java.lang.reflect.Array.set function  


ProbeVue - Entered java.lang.reflect.Array.set function  


Number Of times function - "java.lang.reflect.Array.set" called is : 10  


 

访问参数

除了提供放置探测的功能之外,ProbeVue 还允许收集传递给函数的参数值。对于访问参数,不需要为 ProbeVue 指定函数原型。

注意,Java 应用程序代码与前一个示例相同,也是 myjava.java。

访问参数的示例 Vue 脚本

# cat accessing_argument.e  


@@uftjava:$__CPID:*:"myclass1.set_d":entry  


{  


//Declaring Vue variable - d of type double  


        double d;  


        d=__arg2;  


        printf("ProbeVue - Entered  myclass1.set_d function with   


   argument :%llf\n",__arg2);  


        printf("ProbeVue Variable d : %llf\n",d);  


// Above is to demonstrate that argument values could be stored in Vue variables and then  


// either operated and printed or printed directly  


}  


@@uftjava:$__CPID:*:"myclass1.set_f":entry  


{  


        printf("ProbeVue - Entered  myclass1.set_f function with argument :%f\n",__arg2);  


}  


@@uftjava:$__CPID:*:"myclass1.set_i":entry  


{  


        printf("ProbeVue - Entered  myclass1.set_i function with argument :%d\n",__arg2);  


}  


@@uftjava:$__CPID:*:"myclass1.set_s":entry  


{  


//Declaring String type Vue variable - s with its size  


        String s[100];  


//String type variable of Java can be directly copied to String type variable of Vue  


        s=__arg2;  


        printf("ProbeVue - Entered  myclass1.set_s function with argument :%s\n",__arg2);  


        printf("ProbeVue Variable s : %s\n",s);  


}  


@@uftjava:$__CPID:*:"myclass1.set_b":entry  


{  


        printf("ProbeVue - Entered  myclass1.set_b function with argument :%d\n",__arg2);  


}  


@@uftjava:$__CPID:*:"myjava.main":entry  


{  


        printf("ProbeVue - Entered Probed Main\n");  


}  


@@uftjava:$__CPID:*:"java.lang.reflect.Array.set":entry  


{  


        printf("ProbeVue - Entered java.lang.reflect.Array.set function with   


   2nd argument as : %d\n",__arg2);  


}  


@@syscall:$__CPID:exit:entry  


{  


        exit();  


}  


 

输出

#probevue -X `which java` -A "-agentlib:probevuejava myjava" accessing_argument.e  


ProbeVue - Entered Probed Main  


In main  


Value of Integer i:10  


ProbeVue - Entered  myclass1.set_i function with argument :10  


ProbeVue - Entered  myclass1.set_f function with argument :10.030000  


ProbeVue - Entered  myclass1.set_d function with argument :10.112300  


ProbeVue Variable d : 10.112300  


ProbeVue - Entered  myclass1.set_b function with argument :0  


ProbeVue - Entered  myclass1.set_s function with argument :ProbeVue  


ProbeVue Variable s : ProbeVue  


Value of Float f:10.03  


Value of Double d:10.1123  


Value of Boolean b:false  


Value of String s:ProbeVue  


0  


1  


2  


3  


4  


5  


6  


7  


8  


9  


ProbeVue - Entered java.lang.reflect.Array.set function with 2nd argument as : 0  


ProbeVue - Entered java.lang.reflect.Array.set function with 2nd argument as : 1  


ProbeVue - Entered java.lang.reflect.Array.set function with 2nd argument as : 2  


ProbeVue - Entered java.lang.reflect.Array.set function with 2nd argument as : 3  


ProbeVue - Entered java.lang.reflect.Array.set function with 2nd argument as : 4  


ProbeVue - Entered java.lang.reflect.Array.set function with 2nd argument as : 5  


ProbeVue - Entered java.lang.reflect.Array.set function with 2nd argument as : 6  


ProbeVue - Entered java.lang.reflect.Array.set function with 2nd argument as : 7  


ProbeVue - Entered java.lang.reflect.Array.set function with 2nd argument as : 8  


ProbeVue - Entered java.lang.reflect.Array.set function with 2nd argument as : 9  


 

注意以下几点:

◆对于静态函数,可以使用 __arg1 访问函数的第一个实际参数。

◆对于非静态函数,可以使用 __arg2 访问函数的第一个实际参数,因为作为 __arg1 隐式地传递 this 指针。

其他要点

◆ProbeVue 脚本可以使用完全限定名(例如 java.lang.Math.PI)读取 Java 类中的静态变量和常量。

◆没有替代 @@uftjava 的语法。

◆不需要指定函数原型,也不需要头文件。

◆不需要通过使用 copy_userdata 访问基本数据类型。

◆Java 的布尔数据类型映射到 ProbeVue 的整数数据类型,1 值代表 true,0 值代表 false。

◆Java 的字符串数据类型映射到 ProbeVue 的字符串数据类型。

◆__pname 提供进程名称 "java" 而不是应用程序名,比如 myjavaapp。

◆所有其他函数的使用方法相同。

◆可以用 -agentlib:probevuejava 标志启动 Java 应用程序,但是以后再启动 ProbeVue 会话。

◆可以探测静态和非静态函数。

◆如果在 ProbeVue 命令行上用 -X 选项启动 JVM,那么只能探测 Java 类中的 main 函数。这迫使 JVM 等到 ProbeVue 启动之后才启动 Java 应用程序。

可能实现的场景

◆统计调用某一函数的次数。

◆跟踪调用各个函数的次序。

◆检查参数值是否正确。

限制

◆只支持 JVM V1.5 和更高版本。

◆目前不支持访问数组、实例变量和对象引用。

◆不支持探测重载和多态的函数。