本实验参照该实验手册:linux-tracing-workshop
尝试跟踪由于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
请确保内核版本高于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 的malloc
和free
函数。对于内核内存泄漏,memleak
可以附加到kmalloc
和kfree
函数
$ ./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]
OpenJDK还配备了许多USDT探针。它们中的大多数都是开箱即用的,有些必须启用特殊-XX:+ExtendedDTraceProbes
标志,因为它们会导致性能下降。要探索其中的一些探针,请导航至$JAVA_HOME
并查看tapset目录
这些.stp
文件包含一堆探针的描述和声明,包括其参数。例如,尝试查找gc_collect_tenured_begin
和gc_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