详解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 和更高版本。
◆目前不支持访问数组、实例变量和对象引用。
◆不支持探测重载和多态的函数。