詳解C語言動態(tài)跟蹤工具ProbeVue如何調試Java應用程序
在51CTO的Unix操作系統(tǒng)開發(fā)中我們介紹過AIX6 的安全新特性。 AIX 6.1 中引入的 ProbeVue 是一個動態(tài)跟蹤工具。它最初的設計目的是動態(tài)地跟蹤 C 應用程序和系統(tǒng)中的系統(tǒng)調用。隨著時間的推移,它現(xiàn)在支持調試Java應用程序,還支持獲取實時轉儲和基本系統(tǒng)調用。本文討論 ProbeVue 對 Java 的支持。
ProbeVue 具有以下特性:
◆跟蹤鉤子不需要作為源代碼的組成部分預先編譯。
◆ProbeVue 適用于 32/64 位內核和應用程序,不需要做任何修改。
◆在通過 ProbeVue 放置跟蹤鉤子之前,它們并不存在。
◆可以立即查看跟蹤活動捕捉的跟蹤數(shù)據(jù),可以作為終端輸出顯示它們,或者保存到文件中供以后查看。
◆跟蹤鉤子可以應用于任何函數(shù)的入口或出口(當前對于系統(tǒng)調用只支持出口探測點)。
◆當探測類型為入口時,可以探測傳遞給函數(shù)的參數(shù),這要求在 Vue 腳本的開頭或通過頭文件定義函數(shù)原型。
◆通過在出口點應用跟蹤鉤子并指定函數(shù)原型,可以探測函數(shù)的退出/返回值。
◆可以使用 ProbeVue 進行性能分析和問題調試。
使用 ProbeVue 的前提條件
◆AIX V6.10 和更高版本
◆文件集:不需要特殊的文件集,基本操作系統(tǒng)附帶所需的文件集。
◆在嘗試探測之前需要啟用 ProbeVue 特性,可以使用 SMIT 啟用它。
Vue 腳本語法
◆探測 Java 函數(shù)
- @@uftjava:PID:*:"fully qualified function name":entry
◆探測 Java 庫例程:與 Java 函數(shù)相同。
命令語法
單獨啟動 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));
- }
- }
- }
執(zhí)行基本探測的 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
訪問參數(shù)
除了提供放置探測的功能之外,ProbeVue 還允許收集傳遞給函數(shù)的參數(shù)值。對于訪問參數(shù),不需要為 ProbeVue 指定函數(shù)原型。
注意,Java 應用程序代碼與前一個示例相同,也是 myjava.java。
訪問參數(shù)的示例 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
注意以下幾點:
◆對于靜態(tài)函數(shù),可以使用 __arg1 訪問函數(shù)的第一個實際參數(shù)。
◆對于非靜態(tài)函數(shù),可以使用 __arg2 訪問函數(shù)的第一個實際參數(shù),因為作為 __arg1 隱式地傳遞 this 指針。
其他要點
◆ProbeVue 腳本可以使用完全限定名(例如 java.lang.Math.PI)讀取 Java 類中的靜態(tài)變量和常量。
◆沒有替代 @@uftjava 的語法。
◆不需要指定函數(shù)原型,也不需要頭文件。
◆不需要通過使用 copy_userdata 訪問基本數(shù)據(jù)類型。
◆Java 的布爾數(shù)據(jù)類型映射到 ProbeVue 的整數(shù)數(shù)據(jù)類型,1 值代表 true,0 值代表 false。
◆Java 的字符串數(shù)據(jù)類型映射到 ProbeVue 的字符串數(shù)據(jù)類型。
◆__pname 提供進程名稱 "java" 而不是應用程序名,比如 myjavaapp。
◆所有其他函數(shù)的使用方法相同。
◆可以用 -agentlib:probevuejava 標志啟動 Java 應用程序,但是以后再啟動 ProbeVue 會話。
◆可以探測靜態(tài)和非靜態(tài)函數(shù)。
◆如果在 ProbeVue 命令行上用 -X 選項啟動 JVM,那么只能探測 Java 類中的 main 函數(shù)。這迫使 JVM 等到 ProbeVue 啟動之后才啟動 Java 應用程序。
可能實現(xiàn)的場景
◆統(tǒng)計調用某一函數(shù)的次數(shù)。
◆跟蹤調用各個函數(shù)的次序。
◆檢查參數(shù)值是否正確。
限制
◆只支持 JVM V1.5 和更高版本。
◆目前不支持訪問數(shù)組、實例變量和對象引用。
◆不支持探測重載和多態(tài)的函數(shù)。
◆目前不支持 get_function Vue 函數(shù)。
◆不能探測 Java 函數(shù)的出口點,因此不能獲取函數(shù)的返回值。
【編輯推薦】