【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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章