【Linux】BCC 工具使用

【Linux】BCC 工具使用

本實驗參照該實驗手冊:linux-tracing-workshop

1. 緩慢的文件I/O

嘗試跟蹤由於I/O操作緩慢而表現出延遲的應用程序

編譯並運行應用程序: 首先運行以下命令來編譯logger應用程序,讓其在後臺執行

[root@rumia linux-tracing-workshop-master]$ gcc -g -fno-omit-frame-pointer -O0 -pthread logger.c -o logger
[root@rumia linux-tracing-workshop-master]$./logger

收集I/O延遲信息:/bcc/tools目錄下執行.能夠快速看到logger應用程序執行的一些較大的I/O 比其他較小的I/O花費的時間更長

[root@rumia tools]$ ./biosnoop.py
TIME(s)     COMM           PID    DISK    T SECTOR     BYTES  LAT(ms)
0.000000    logger         28988  sda     W 16668104   262144    2.75
0.000024    logger         28988  sda     W 16668616   262144    2.75
0.000047    logger         28988  sda     W 16669128   262144    2.77
0.000282    logger         28988  sda     W 16669640   262144    2.99
0.001292    logger         28988  sda     W 10504856   4096      0.75
0.011483    logger         28988  sda     W 17394904   4096      0.94
0.012350    logger         28988  sda     W 10504864   4096      0.73

觀察文件I/O操作緩慢: 查看它正在寫哪些文件,哪些操作所需的時間比其他時間長

[root@rumia tools]$ ./fileslower.py 1
Tracing sync read/writes slower than 1 ms
TIME(s)  COMM           TID    D BYTES   LAT(ms) FILENAME
0.023    logger         28988  W 1024       1.94 log.data
0.041    logger         28989  W 1048576    6.15 flush.data
0.044    logger         28988  W 1024       1.61 log.data
0.066    logger         28988  W 1024       1.95 log.data
0.088    logger         28988  W 1024       1.82 log.data

2. 追蹤泄漏

請確保內核版本高於4.6

編譯並運行應用程序: 使用javac編譯完成後, 運行java XXX.class

// 內存泄漏場景
static Map map = new HashMap();

public static void main(String[] args) {
    Scanner in = new Scanner(System.in);
    for (int i = 0; i < 100000000; i++) {
        byte[] b = new byte[10*10];
        map.put(i,b);
    }
    System.out.println(map);
}

尋找泄漏源: memleak附加到內存分配和釋放功能,並收集所有未釋放的分配的數據。具體來說,對於用戶模式C ++應用程序,memleak可以將其附加到libc 的mallocfree函數。對於內核內存泄漏,memleak可以附加到kmallockfree函數

$ ./memleak.py -p 18052

默認情況下,memleak將打印按剩餘分配排序的前10個堆棧-也就是說,由於該工具已附加到您的進程中,已執行但未釋放的分配。嘗試自己分析這些調用堆棧

[00:49:23] Top 10 stacks with outstanding allocations:
	16 bytes in 1 allocations from stack
		os::malloc(unsigned long, MemoryType, NativeCallStack const&)+0xad [libjvm.so]
		CHeapObj<(MemoryType)6>::operator new(unsigned long, NativeCallStack const&)+0x19 [libjvm.so]
		CHeapObj<(MemoryType)6>::operator new(unsigned long)+0x75 [libjvm.so]
		add_derived_oop(oopDesc**, oopDesc**)+0x33 [libjvm.so]
		OopMapSet::oops_do(frame const*, RegisterMap const*, OopClosure*)+0x187 [libjvm.so]
		frame::oops_do_internal(OopClosure*, CLDClosure*, CodeBlobClosure*, RegisterMap*, bool)+0x99 [libjvm.so]
		JavaThread::oops_do(OopClosure*, CLDClosure*, CodeBlobClosure*)+0x185 [libjvm.so]
		ThreadRootsMarkingTask::do_it(GCTaskManager*, unsigned int)+0x10a [libjvm.so]
		GCTaskThread::run()+0x172 [libjvm.so]
		java_start(Thread*)+0xf2 [libjvm.so]
		start_thread+0xc5 [libpthread-2.17.so]

3. 啓用和跟蹤JVM USDT探針

OpenJDK還配備了許多USDT探針。它們中的大多數都是開箱即用的,有些必須啓用特殊-XX:+ExtendedDTraceProbes標誌,因爲它們會導致性能下降。要探索其中的一些探針,請導航至$JAVA_HOME並查看tapset目錄

這些.stp文件包含一堆探針的描述和聲明,包括其參數。例如,嘗試查找gc_collect_tenured_begingc_collect_tenured_end探針描述。

$ cd /etc/alternatives/java_sdk
$ ls tapset/
hotspot-1.8.0.242.b08-0.el7_7.x86_64.stp     hotspot_jni-1.8.0.242.b08-0.el7_7.x86_64.stp
hotspot_gc-1.8.0.242.b08-0.el7_7.x86_64.stp  jstack-1.8.0.242.b08-0.el7_7.x86_64.stp

現在,讓我們來看一個更實際的例子。現在,您將通過使用class_loaded探針來跟蹤正在運行的Java應用程序,並查看正在加載哪些類。首先,通過運行以下命令: 看起來有四個參數,並且正在加載的類名作爲第一個參數給出

$ grep -A 10 'probe.*class_loaded' tapset/*.stp
tapset/hotspot-1.8.0.242.b08-0.el7_7.x86_64.stp:probe hotspot.class_loaded =
tapset/hotspot-1.8.0.242.b08-0.el7_7.x86_64.stp-  process("/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64/jre/lib/amd64/server/libjvm.so").mark("class__loaded")
tapset/hotspot-1.8.0.242.b08-0.el7_7.x86_64.stp-{
tapset/hotspot-1.8.0.242.b08-0.el7_7.x86_64.stp-  name = "class_loaded";
tapset/hotspot-1.8.0.242.b08-0.el7_7.x86_64.stp-  class = user_string_n($arg1, $arg2);
tapset/hotspot-1.8.0.242.b08-0.el7_7.x86_64.stp-  classloader_id = $arg3;
tapset/hotspot-1.8.0.242.b08-0.el7_7.x86_64.stp-  is_shared = $arg4;
tapset/hotspot-1.8.0.242.b08-0.el7_7.x86_64.stp-  probestr = sprintf("%s(class='%s',classloader_id=0x%x,is_shared=%d)",
tapset/hotspot-1.8.0.242.b08-0.el7_7.x86_64.stp-                     name, class, classloader_id, is_shared);
tapset/hotspot-1.8.0.242.b08-0.el7_7.x86_64.stp-}
tapset/hotspot-1.8.0.242.b08-0.el7_7.x86_64.stp-

此時,我們可以運行一個Java應用程序

$ java slowy/App

現在,使用tplist以下命令發現可用的跟蹤點:

$ jps
19322 App
19391 Jps
$ ./tplist.py -p 19322 '*class*loaded'
/proc/19322/root/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64/jre/lib/amd64/server/libjvm.so hotspot:class__unloaded
/proc/19322/root/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.242.b08-0.el7_7.x86_64/jre/lib/amd64/server/libjvm.so hotspot:class__loaded

最後,我們可以使用跟蹤有趣的跟蹤點trace,此時,每當Java應用程序加載類時,您都應該獲得一條跟蹤消息

cd /bcc/tools
$./trace.py 'u:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.77-1.b03.fc22.x86_64/jre/lib/amd64/server/libjvm.so:class__loaded "%s", arg1'

PID     TID     COMM            FUNC             -
28625   28626   java            class__loaded    java/util/Formatter
28625   28626   java            class__loaded    java/util/regex/Pattern
28625   28626   java            class__loaded    java/util/regex/Pattern$Node
[...]
28625   28626   java            class__loaded    java/util/regex/Pattern$Start
28625   28626   java            class__loaded    java/util/regex/Pattern$TreeInfo
28625   28626   java            class__loaded    java/util/Locale$Category
28625   28626   java            class__loaded    java/util/Locale$1
[...]
28625   28626   java            class__loaded    java/util/Formatter$Conversion
28625   28626   java            class__loaded    java/lang/Shutdown
28625   28626   java            class__loaded    java/lang/Shutdown$Lock
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章