Linux命令拾遗之动态追踪工具(实例详解)

本篇文章给大家带来了linux命令动态追踪工具的相关知识,其中主要介绍工作中常用的动态追踪工具strace、arthas、bpftrace等。希望对大家有帮助。

Linux命令拾遗之动态追踪工具(实例详解)

线程与内存剖析,只能观测到进程的整体情况,有些时候我们需要观测到某一方法级别,比如调用方法test()时,传入的参数是什么,返回值是多少,花费了多少时间?这种情况下,我们就需要使用一些动态追踪工具了,如strace、arthas、bpftrace、systemtap等。

strace与ltrace

strace是Linux中用来观测系统调用的工具,学过操作系统原理都知道,操作系统向应用程序暴露了一批系统调用接口,应用程序只能通过这些系统调用接口来访问操作系统,比如申请内存、文件或网络io操作等。

用法如下:

# -T 打印系统调用花费的时间# -tt 打印系统调用的时间点# -s 输出的最大长度,默认32,对于调用参数较长的场景,建议加大# -f 是否追踪fork出来子进程的系统调用,由于服务端服务普通使用线程池,建议加上# -p 指定追踪的进程pid# -o 指定追踪日志输出到哪个文件,不指定则直接输出到终端$ strace -T -tt -f -s 10000 -p 87 -o strace.log

实例:抓取实际发送的SQL

有些时候,我们会发现代码中完全没问题的SQL,却查不到数据,这极有可能是由于项目中一些底层框架改写了SQL,导致真实发送的SQL与代码中的SQL不一样。

遇到这种情况,先别急着扒底层框架代码,那样会比较花时间,毕竟程序员的时间很宝贵,不然要加更多班的,怎么快速确认是不是这个原因呢?

有两种方法,第一种是使用wireshark抓包,第二种就是本篇介绍的strace了,由于程序必然要通过网络io相关的系统调用,将SQL命令发送到数据库,因此我们只需要用strace追踪所有系统调用,然后grep出发送SQL的系统调用即可,如下:

$ strace -T -tt -f -s 10000 -p 87 |& tee strace.log

45.png

从图中可以清晰看到,mysql的jdbc驱动是通过sendto系统调用来发送SQL,通过recvfrom来获取返回结果,可以发现,由于SQL是字符串,strace自动帮我们识别出来了,而返回结果因为是二进制的,就不容易识别了,需要非常熟悉mysql协议才行。

另外,从上面其实也很容易看出SQL执行耗时,计算相同线程号的sendto与recvfrom之间的时间差即可。

ltrace

由于大多数进程基本都会使用基础c库,而不是系统调用,如Linux上的glibc,Windows上的msvc,所以还有一个工具ltrace,可以用来追踪库调用,如下:

$ ltrace -T -tt -f -s 10000 -p 87 -o ltrace.log

基本用法和strace一样,一般来说,使用strace就够了。

arthas

arthas是java下的一款动态追踪工具,可以观测到java方法的调用参数、返回值等,除此之外,还提供了很多实用功能,如反编译、线程剖析、堆内存转储、火焰图等。

下载与使用

# 下载arthas$ wget https://arthas.aliyun.com/download/3.4.6?mirror=aliyun -O arthas-packaging-3.4.6-bin.zip# 解压$ unzip arthas-packaging-3.4.6-bin.zip -d arthas && cd arthas/# 进入arthas命令交互界面$ java -jar arthas-boot.jar `pgrep -n java`[INFO] arthas-boot version: 3.4.6[INFO] arthas home: /home/work/arthas[INFO] Try to attach process 3368243[INFO] Attach process 3368243 success.[INFO] arthas-client connect 127.0.0.1 3658  ,---.  ,------. ,--------.,--.  ,--.  ,---.   ,---. /  O   |  .--. ''--.  .--'|  '--'  | /  O   '   .-'|  .-.  ||  '--'.'   |  |   |  .--.  ||  .-.  |`.  `-.|  | |  ||  |      |  |   |  |  |  ||  | |  |.-'    |`--' `--'`--' '--'   `--'   `--'  `--'`--' `--'`-----'wiki      https://arthas.aliyun.com/doctutorials https://arthas.aliyun.com/doc/arthas-tutorials.htmlversion   3.4.6pid       3368243time      2021-11-13 13:35:49# help可查看arthas提供了哪些命令[arthas@3368243]$ help# help watch可查看watch命令具体用法[arthas@3368243]$ help watch

watch、trace与stack

在arthas中,使用watch、trace、stack命令可以观测方法调用情况,如下:

# watch观测执行的查询SQL,-x 3指定对象展开层级[arthas@3368243]$ watch org.apache.ibatis.executor.statement.PreparedStatementHandler parameterize '{target.boundSql.sql,target.boundSql.parameterObject}' -x 3method=org.apache.ibatis.executor.statement.PreparedStatementHandler.parameterize location=AtExitts=2021-11-13 14:50:34; [cost=0.071342ms] result=@ArrayList[    @String[select id,log_info,create_time,update_time,add_time from app_log where id=?],    @ParamMap[        @String[id]:@Long[41115],        @String[param1]:@Long[41115],    ],]# watch观测耗时超过200ms的SQL[arthas@3368243]$ watch com.mysql.jdbc.PreparedStatement execute '{target.toString()}' 'target.originalSql.contains("select") && #cost > 200' -x 2Press Q or Ctrl+C to abort.Affect(class count: 3 , method count: 1) cost in 123 ms, listenerId: 25method=com.mysql.jdbc.PreparedStatement.execute location=AtExitts=2021-11-13 14:58:42; [cost=1001.558851ms] result=@ArrayList[    @String[com.mysql.jdbc.PreparedStatement@6283cfe6: select count(*) from app_log],]# trace追踪方法耗时,层层追踪,就可找到耗时根因,--skipJDKMethod false显示jdk方法耗时,默认不显示[arthas@3368243]$ trace com.mysql.jdbc.PreparedStatement execute 'target.originalSql.contains("select") && #cost > 200'  --skipJDKMethod falsePress Q or Ctrl+C to abort.Affect(class count: 3 , method count: 1) cost in 191 ms, listenerId: 26---ts=2021-11-13 15:00:40;thread_name=http-nio-8080-exec-47;id=76;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@5a2d131d    ---[1001.465544ms] com.mysql.jdbc.PreparedStatement:execute()        +---[0.022119ms] com.mysql.jdbc.PreparedStatement:checkClosed() #1274        +---[0.016294ms] com.mysql.jdbc.MySQLConnection:getConnectionMutex() #57        +---[0.017862ms] com.mysql.jdbc.PreparedStatement:checkReadOnlySafeStatement() #1278        +---[0.008996ms] com.mysql.jdbc.PreparedStatement:createStreamingResultSet() #1294        +---[0.010783ms] com.mysql.jdbc.PreparedStatement:clearWarnings() #1296        +---[0.017843ms] com.mysql.jdbc.PreparedStatement:fillSendPacket() #1316        +---[0.008543ms] com.mysql.jdbc.MySQLConnection:getCatalog() #1320        +---[0.009293ms] java.lang.String:equals() #57        +---[0.008824ms] com.mysql.jdbc.MySQLConnection:getCacheResultSetMetadata() #1328        +---[0.009892ms] com.mysql.jdbc.MySQLConnection:useMaxRows() #1354        +---[1001.055229ms] com.mysql.jdbc.PreparedStatement:executeInternal() #1379        +---[0.02076ms] com.mysql.jdbc.ResultSetInternalMethods:reallyResult() #1388        +---[0.011517ms] com.mysql.jdbc.MySQLConnection:getCacheResultSetMetadata() #57        +---[0.00842ms] com.mysql.jdbc.ResultSetInternalMethods:getUpdateID() #1404        ---[0.008112ms] com.mysql.jdbc.ResultSetInternalMethods:reallyResult() #1409# stack追踪方法调用栈,找到耗时SQL来源[arthas@3368243]$ stack com.mysql.jdbc.PreparedStatement execute 'target.originalSql.contains("select") && #cost > 200'Press Q or Ctrl+C to abort.Affect(class count: 3 , method count: 1) cost in 138 ms, listenerId: 27ts=2021-11-13 15:01:55;thread_name=http-nio-8080-exec-5;id=2d;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@5a2d131d    @com.mysql.jdbc.PreparedStatement.execute()        at com.alibaba.druid.pool.DruidPooledPreparedStatement.execute(DruidPooledPreparedStatement.java:493)        at org.apache.ibatis.executor.statement.PreparedStatementHandler.query(PreparedStatementHandler.java:63)        at org.apache.ibatis.executor.statement.RoutingStatementHandler.query(RoutingStatementHandler.java:79)        at org.apache.ibatis.executor.SimpleExecutor.doQuery(SimpleExecutor.java:63)        at org.apache.ibatis.executor.BaseExecutor.queryFromDatabase(BaseExecutor.java:326)        at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:156)        at org.apache.ibatis.executor.BaseExecutor.query(BaseExecutor.java:136)        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:148)        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:141)        at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:77)        at sun.reflect.GeneratedMethodAccessor75.invoke(null:-1)        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)        at java.lang.reflect.Method.invoke(Method.java:498)        at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:433)        at com.sun.proxy.$Proxy113.selectOne(null:-1)        at org.mybatis.spring.SqlSessionTemplate.selectOne(SqlSessionTemplate.java:166)        at org.apache.ibatis.binding.MapperMethod.execute(MapperMethod.java:83)        at org.apache.ibatis.binding.MapperProxy.invoke(MapperProxy.java:59)        at com.sun.proxy.$Proxy119.selectCost(null:-1)        at com.demo.example.web.controller.TestController.select(TestController.java:57)

可以看到watch、trace、stack命令中都可以指定条件表达式,只要满足ognl表达式语法即可,ognl完整语法很复杂,如下是一些经常使用的:

46.png

ognl

通过ognl命令,可直接查看静态变量的值,如下:

# 调用System.getProperty静态函数,查看jvm默认字符编码[arthas@3368243]$ ognl '@System@getProperty("file.encoding")'@String[UTF-8]# 找到springboot类加载器[arthas@3368243]$ classloader -t+-BootstrapClassLoader+-sun.misc.Launcher$ExtClassLoader@492691d7  +-sun.misc.Launcher$AppClassLoader@764c12b6    +-org.springframework.boot.loader.LaunchedURLClassLoader@4361bd48# 获取springboot中所有的beanName,-c指定springboot的classloader的hash值# 一般Spring项目,都会定义一个SpringUtil的,用于获取bean容器ApplicationContext[arthas@3368243]$ ognl -c 4361bd48 '#context=@com.demo.example.web.SpringUtil@applicationContext, #context.beanFactory.beanDefinitionNames'@String[][    @String[org.springframework.context.annotation.internalConfigurationAnnotationProcessor],    @String[org.springframework.context.annotation.internalAutowiredAnnotationProcessor],    @String[org.springframework.context.annotation.internalCommonAnnotationProcessor],    @String[testController],    @String[apiController],    @String[loginService],    ...]# 获取springboot配置,如server.port是配置http服务端口的[arthas@3368243]$ ognl -c 4361bd48 '#context=@com.demo.example.web.SpringUtil@applicationContext, #context.getEnvironment().getProperty("server.port")'@String[8080]# 查看server.port定义在哪个配置文件中# 可以很容易看到,server.port定义在application-web.yml[arthas@3368243]$ ognl -c 4361bd48 '#context=@com.demo.example.web.SpringUtil@applicationContext, #context.environment.propertySources.propertySourceList.{? containsProperty("server.port")}'@ArrayList[    @ConfigurationPropertySourcesPropertySource[ConfigurationPropertySourcesPropertySource {name='configurationProperties'}],    @OriginTrackedMapPropertySource[OriginTrackedMapPropertySource {name='applicationConfig: [classpath:/application-web.yml]'}],]# 调用springboot中bean的方法,获取返回值[arthas@3368243]$ ognl -c 4361bd48 '#context=@com.demo.example.web.SpringUtil@applicationContext, #context.getBean("demoMapper").queryOne(12)' -x 2@ArrayList[    @HashMap[        @String[update_time]:@Timestamp[2021-11-09 18:38:13,000],        @String[create_time]:@Timestamp[2021-04-17 15:52:55,000],        @String[log_info]:@String[TbTRNsh2SixuFrkYLTeb25a6zklEZj0uWANKRMe],        @String[id]:@Long[12],        @String[add_time]:@Integer[61],    ],]# 查看springboot自带tomcat的线程池的情况[arthas@3368243]$ ognl -c 4361bd48 '#context=@com.demo.example.web.SpringUtil@applicationContext, #context.webServer.tomcat.server.services[0].connectors[0].protocolHandler.endpoint.executor'@ThreadPoolExecutor[    sm=@StringManager[org.apache.tomcat.util.res.StringManager@16886f49],    submittedCount=@AtomicInteger[1],    threadRenewalDelay=@Long[1000],    workQueue=@TaskQueue[isEmpty=true;size=0],    mainLock=@ReentrantLock[java.util.concurrent.locks.ReentrantLock@69e9cf90[Unlocked]],    workers=@HashSet[isEmpty=false;size=10],    largestPoolSize=@Integer[49],    completedTaskCount=@Long[10176],    threadFactory=@TaskThreadFactory[org.apache.tomcat.util.threads.TaskThreadFactory@63c03c4f],    handler=@RejectHandler[org.apache.tomcat.util.threads.ThreadPoolExecutor$RejectHandler@3667e559],    keepAliveTime=@Long[60000000000],    allowCoreThreadTimeOut=@Boolean[false],    corePoolSize=@Integer[10],    maximumPoolSize=@Integer[8000],]

其它命令

arthas还提供了jvm大盘、线程剖析、堆转储、反编译、火焰图等功能,如下:

# 显示耗cpu较多的前4个线程[arthas@3368243]$ thread -n 4"C2 CompilerThread0" [Internal] cpuUsage=8.13% deltaTime=16ms time=46159ms"C2 CompilerThread1" [Internal] cpuUsage=4.2% deltaTime=8ms time=47311ms"C1 CompilerThread2" [Internal] cpuUsage=3.06% deltaTime=6ms time=17402ms"http-nio-8080-exec-40" Id=111 cpuUsage=1.29% deltaTime=2ms time=624ms RUNNABLE (in native)    at java.net.SocketInputStream.socketRead0(Native Method)    ...    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4113)    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2570)    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2731)    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2818)    ...    at com.demo.example.web.controller.TestController.select(TestController.java:57)# 堆转储[arthas@3368243]$ heapdumpDumping heap to /tmp/heapdump2021-11-13-15-117226383240040009563.hprof ...Heap dump file created# cpu火焰图,容器环境下profiler start可能用不了,可用profiler start -e itimer替代[arthas@3368243]$ profiler startStarted [cpu] profiling[arthas@3368243]$ profiler stopOKprofiler output file: /home/work/app/arthas-output/20211113-151208.svg# dashboard就类似Linux下的top一样,可看jvm线程、堆内存的整体情况[arthas@3368243]$ dashboard# jvm就类似Linux下的ps一样,可以看jvm进程的一些基本信息,如:jvm参数、类加载、线程数、打开文件描述符数等[arthas@3368243]$ jvm# 反编译[arthas@3368243]$ jad com.demo.example.web.controller.TestController

可见,arthas已经不是一个单纯的动态追踪工具了,它把jvm下常用的诊断功能几乎全囊括了。

bpftrace

arthas只能追踪java程序,对于原生程序(如MySQL)就无能为力了,好在Linux生态提供了大量的机制以及配套工具,可用于追踪原生程序的调用,如perf、bpftrace、systemtap等,由于bpftrace使用难度较小,本篇主要介绍它的用法。

bpftrace是基于ebpf技术实现的动态追踪工具,它对ebpf技术进行封装,实现了一种脚本语言,就像上面介绍的arthas基于ognl一样,它实现的脚本语言类似于awk,封装了常见语句块,并提供内置变量与内置函数,如下:

$ sudo bpftrace -e 'BEGIN { printf("Hello, World!n"); } 'Attaching 1 probe...Hello, World!

实例:在调用端追踪慢SQL

前面我们用strace追踪过mysql的jdbc驱动,它使用sendto与recvfrom系统调用来与mysql服务器通信,因此,我们在sendto调用时,计下时间点,然后在recvfrom结束时,计算时间之差,就可以得到相应SQL的耗时了,如下:

先找到sendto与recvfrom系统调用在bpftrace中的追踪点,如下:

# 查找sendto|recvfrom系统调用的追踪点,可以看到sys_enter_开头的追踪点应该是进入时触发,sys_exit_开头的退出时触发$ sudo bpftrace -l '*tracepoint:syscalls*' |grep -E 'sendto|recvfrom'tracepoint:syscalls:sys_enter_sendto  tracepoint:syscalls:sys_exit_sendto   tracepoint:syscalls:sys_enter_recvfrom  tracepoint:syscalls:sys_exit_recvfrom # 查看系统调用参数,方便我们编写脚本$ sudo bpftrace -lv tracepoint:syscalls:sys_enter_sendtotracepoint:syscalls:sys_enter_sendto    int __syscall_nr;    int fd;    void * buff;    size_t len;    unsigned int flags;    struct sockaddr * addr;    int addr_len;

编写追踪脚本trace_slowsql_from_syscall.bt,脚本代码如下:

#!/usr/local/bin/bpftraceBEGIN {    printf("Tracing jdbc SQL slower than %d ms by sendto/recvfrom syscalln", $1);    printf("%-10s %-6s %6s %sn", "TIME(ms)", "PID", "MS", "QUERY");}tracepoint:syscalls:sys_enter_sendto /comm == "java"/ {    // mysql协议中,包开始的第5字节指示命令类型,3代表SQL查询    $com = *(((uint8 *) args->buff)+4);    if($com == (uint8)3){        @query[tid]=str(((uint8 *) args->buff)+5, (args->len)-5);        @start[tid]=nsecs;    }}tracepoint:syscalls:sys_exit_recvfrom /comm == "java" && @start[tid]/ {    $dur = (nsecs - @start[tid]) / 1000000;    if ($dur > $1) {        printf("%-10u %-6d %6d %sn", elapsed / 1000000, pid, $dur, @query[tid]);    }    delete(@query[tid]);    delete(@start[tid]);}

其中,comm表示进程名称,tid表示线程号,@query[tid]与@start[tid]类似map,以tid为key的话,这个变量就像一个线程本地变量了。

调用上面的脚本,可以看到各SQL执行耗时,如下:

$ sudo BPFTRACE_STRLEN=80 bpftrace trace_slowsql_from_syscall.btAttaching 3 probes...Tracing jdbc SQL slower than 0 ms by sendto/recvfrom syscallTIME(ms)   PID        MS QUERY6398       3368243    125 select sleep(0.1)16427      3368243     22 select id from app_log al order by id desc limit 116431      3368243     20 select id,log_info,create_time,update_time,add_time from app_log where id=1169217492      3368243     21 select id,log_info,create_time,update_time,add_time from app_log where id=29214

实例:在服务端追踪慢SQL

从调用端来追踪SQL耗时,会包含网络往返时间,为了得到更精确的SQL耗时,我们可以写一个追踪服务端mysql的脚本,来观测SQL耗时,如下:

确定mysqld服务进程的可执行文件与入口函数

$ which mysqld/usr/local/mysql/bin/mysqld# objdump可导出可执行文件的动态符号表,做几张mysqld的火焰图就可发现,dispatch_command是SQL处理的入口函数# 另外,由于mysql是c++写的,方法名是编译器改写过的,这也是为啥下面脚本中要用*dispatch_command*模糊匹配$ objdump -tT /usr/local/mysql/bin/mysqld | grep dispatch_command00000000014efdf3 g     F .text  000000000000252e              _Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command00000000014efdf3 g    DF .text  000000000000252e  Base        _Z16dispatch_commandP3THDPK8COM_DATA19enum_server_command

使用uprobe追踪dispatch_command的调用,如下:

#!/usr/bin/bpftraceBEGIN{    printf("Tracing mysqld SQL slower than %d ms. Ctrl-C to end.n", $1);    printf("%-10s %-6s %6s %sn", "TIME(ms)", "PID", "MS", "QUERY");}uprobe:/usr/local/mysql/bin/mysqld:*dispatch_command*{    if (arg2 == (uint8)3) {        @query[tid] = str(*arg1);        @start[tid] = nsecs;    }}uretprobe:/usr/local/mysql/bin/mysqld:*dispatch_command* /@start[tid]/{    $dur = (nsecs - @start[tid]) / 1000000;    if ($dur > $1) {        printf("%-10u %-6d %6d %sn", elapsed / 1000000, pid, $dur, @query[tid]);    }    delete(@query[tid]);    delete(@start[tid]);}

追踪脚本整体上与之前系统调用版本的类似,不过追踪点不一样而已。

实例:找出扫描大量行的SQL

众所周知,SQL执行时需要扫描数据,并且扫描的数据越多,SQL性能就会越差。

但对于一些中间情况,SQL扫描行数不多也不少,如2w条。且这2w条数据都在缓存中的话,SQL执行时间不会很长,导致没有记录在慢查询日志中,但如果这样的SQL并发量大起来的话,会非常耗费CPU。

对于mysql的话,扫描行的函数是row_search_mvcc(如果你经常抓取mysql栈的话,很容易发现这个函数),每扫一行调用一次,如果在追踪脚本中追踪此函数,记录下调用次数,就可以观测SQL的扫描行数了,如下:

#!/usr/bin/bpftraceBEGIN{    printf("Tracing mysqld SQL scan row than %d. Ctrl-C to end.n", $1);    printf("%-10s %-6s %6s %10s %sn", "TIME(ms)", "PID", "MS", "SCAN_NUM", "QUERY");}uprobe:/usr/local/mysql/bin/mysqld:*dispatch_command*{    $COM_QUERY = (uint8)3;    if (arg2 == $COM_QUERY) {        @query[tid] = str(*arg1);        @start[tid] = nsecs;    }}uprobe:/usr/local/mysql/bin/mysqld:*row_search_mvcc*{    @scan_num[tid]++;}uretprobe:/usr/local/mysql/bin/mysqld:*dispatch_command* /@start[tid]/{    $dur = (nsecs - @start[tid]) / 1000000;    if (@scan_num[tid] > $1) {        printf("%-10u %-6d %6d %10d %sn", elapsed / 1000000, pid, $dur, @scan_num[tid], @query[tid]);    }    delete(@query[tid]);    delete(@start[tid]);    delete(@scan_num[tid]);}

脚本运行效果如下:

$ sudo BPFTRACE_STRLEN=80 bpftrace trace_mysql_scan.bt 200Attaching 4 probes...Tracing mysqld SQL scan row than 200. Ctrl-C to end.TIME(ms)   PID        MS   SCAN_NUM QUERY150614     1892        4        300 select * from app_log limit 300                                    # 全表扫描,慢!17489      1892      424      43717 select count(*) from app_log                                                                         # 大范围索引扫描,慢!193013     1892      253      20000 select count(*) from app_log where id < 20000                                                        # 深分页,会查询前20300条,取最后300条,慢!213395     1892      209      20300 select * from app_log limit 20000,300                                                                # 索引效果不佳,虽然只会查到一条数据,但扫描数据量不会少,慢!430374     1892      186      15000 select * from app_log where id < 20000 and seq = 15000 limit 1

如上所示,app_log是我建的一张测试表,共43716条数据,其中id字段是自增主键,seq值与id值一样,但没有索引。

可以看到上面的几个场景,不管什么场景,只要扫描行数变大,耗时就会变长,但也都没有超过500毫秒的,原因是这个表很小,数据可以全部缓存在内存中。

可见,像bpftrace这样的动态追踪工具是非常强大的,而且比arthas更加灵活,arthas只能追踪单个函数,而bpftrace可以跨函数追踪。

总结

已经介绍了不少诊断工具了,这里简单概括一下它们的应用场景:

软件资源观测,如ps、lsof、netstat,用来检查进程基本情况,如内存占用、打开哪些文件、创建了哪些连接等。

硬件资源观测,如top、iostat、sar等,类似Windows上任务管理器一样,让你对硬件资源占用以及在进程上情况有个大概了解,最多观测到线程级别,这些工具一般只能指示进一步调查的方向,很少能直接确认原因。

线程与内存剖析,如jstack、jmap等,比较容易分析线程卡住或内存oom问题,而oncpu火焰图容易找出热代码路径,分析高cpu瓶颈,offcpu火焰图则容易找出经常阻塞的代码路径,分析高耗时问题。

动态追踪工具,如arthas、bpftrace等,能追踪到方法调用级,常用于问题需要深入调查的场景,如问题不在代码实现上,而在调用数据上,就像上面row_search_mvcc函数一样,抓火焰图会发现它出现频繁,但由于它是核心函数,这个频繁可能是正常的,也可能是不正常的,需要将调用次数分散到SQL上才能确认。

相关推荐:《Linux视频教程》

以上就是Linux命令拾遗之动态追踪工具(实例详解)的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/152165.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2025年12月3日 16:20:39
directx怎么安装 directx缺失修复指南
下一篇 2025年12月3日 16:22:41

相关推荐

  • 即梦AI运镜控制怎么控制_即梦AI视频镜头移动技巧详解

    掌握即梦AI运镜需四步:一、用“镜头缓慢推进”等预设提示词生成标准运动;二、通过动效画板框选主体并绘制运动路径;三、设置首尾帧引导转场,实现穿越或循环效果;四、结合“希区柯克式变焦”“时间冻结环绕”等高级技巧增强视觉表现。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 Dee…

    2026年9月21日
    000
  • Linux如何检查系统中缺失的依赖库

    使用ldd和readelf检查依赖,通过包管理器安装缺失库。ldd显示not found时,用apt-file或yum provides查找并安装对应软件包,必要时添加库路径至/etc/ld.so.conf并运行ldconfig更新缓存。 在Linux系统中,程序运行时依赖各种共享库(.so文件),…

    2026年9月21日
    000
  • avg计算平均值在mysql中如何使用

    AVG()是MySQL中计算列平均值的聚合函数,忽略NULL值。基本语法为SELECT AVG(列名) FROM 表名;可结合WHERE筛选条件,如SELECT AVG(score) FROM students WHERE subject = ‘math’ AND score…

    2026年9月21日
    000
  • 三星电视携手京东开启艺术视听盛典以科技美学重塑家居生活新模式

    三星电视携手京东开启艺术视听盛典以科技美学重塑家居生活新模式三星电视携手京东开启艺术视听盛典以科技美学重塑家居生活新模式三星电视携手京东开启艺术视听盛典以科技美学重塑家居生活新模式三星电视携手京东开启艺术视听盛典以科技美学重塑家居生活新模式

    随着消费理念升级与需求日益多样化,电视已不再仅仅是观看节目和影音娱乐的工具,而是逐渐演变为承载家居美学、传递情感温度、连接智慧生活的艺术载体。在这一变革浪潮中,三星率先引领艺术电视领域的创新风向,theframe画壁艺术电视与theserif画境艺术电视成功打破科技与艺术之间的界限,将电视升华为可观…

    2026年9月21日 用户投稿
    100
  • 分布式锁(Redis)解决数据竞争

    使用redis实现分布式锁来解决数据竞争可以通过setnx和expire命令。1)使用setnx尝试获取锁,并通过expire设置锁的过期时间防止死锁。2)释放锁时使用watch命令确保锁未被其他客户端获取。需要注意redis的单点故障、高并发性能瓶颈和锁的过期时间设置。 在处理高并发的应用场景中,…

    2026年9月21日
    000
  • 如何在Weka中处理向量属性:ARFF格式的限制与解决方案

    本文探讨了weka中arff格式对直接向量属性表示的限制,并提供了两种主要解决方案。对于时间序列数据,建议利用weka的内置时间序列分析功能。对于非时间序列数据,核心在于通过特征工程(如使用addexpression、multifilter等)将向量拆解并转换为可被weka有效处理的独立特征,以揭示…

    2026年9月21日
    000
  • 哪些Docker扩展能让你在VSCode内轻松管理容器?

    Docker官方扩展是VSCode中管理容器的核心工具,提供容器、镜像、卷、网络的可视化操作,结合Remote-Containers可实现容器内开发,辅以YAML、GitLens等扩展提升效率,需确保本地Docker daemon运行。 在 VSCode 中管理 Docker 容器,最核心的扩展是 …

    2026年9月21日
    000
  • PostgreSQL地理位置数据按距离排序的最佳实践:数据库层优化策略

    在处理大量地理位置数据并按距离排序时,将排序逻辑下推至数据库层(如postgresql)是更优的选择。这种方法能有效减少应用层的数据传输和内存消耗,充分利用数据库的计算能力,从而提升整体性能和资源利用率,而非在spring boot应用服务层进行排序。 1. 地理位置排序的需求与挑战 在现代Web应…

    2026年9月21日
    100
  • REDMI K90标准版外观预热:两款低饱和配色

    今日,redmi k90标准版正式开启预热,并首次公开完整外观设计。 官方称其为REDMI K系列历史上最为精致的标准版机型。从目前已公布的信息来看,新机将推出两种低饱和度配色:通透紫与纯净白,机身采用一体化包裹式金属中框,搭配同色系对称天线带设计,正面配备一块6.59英寸显示屏,凭借高达94%的屏…

    2026年9月21日
    100
  • Flyway配置中安全使用环境变量的实践指南

    flyway配置中直接暴露数据库连接参数存在安全隐患。本文详细阐述了如何通过命令行参数和api调用两种主要方式,将环境变量安全地集成到flyway配置流程中。通过外部化管理敏感信息,可以有效提升数据库迁移配置的安全性、灵活性和可维护性,避免将凭证硬编码到配置文件中。 在数据库迁移实践中,将敏感的数据…

    2026年9月21日
    100
  • 如何为VSCode设置最小化到系统托盘?

    VSCode不支持内置最小化到系统托盘功能,可通过第三方工具实现:Windows推荐使用RBTray或AutoHotkey脚本,Linux可借助AppIndicator扩展,macOS则依赖Dock最小化及辅助工具视觉隐藏。 VSCode 本身不提供内置的“最小化到系统托盘”功能,但可以通过一些方法…

    2026年9月21日
    000
  • Image Thresholding

    大家好,又见面了,我是你们的朋友全栈君。 Simple Thresholding The function cv.threshold is used to apply the thresholding. The first argument is the source image, which sh…

    2026年9月21日
    000
  • 如何用SumoPaint的AI裁剪图片?快速完成智能图片裁剪教程

    如何用SumoPaint的AI裁剪图片?快速完成智能图片裁剪教程如何用SumoPaint的AI裁剪图片?快速完成智能图片裁剪教程如何用SumoPaint的AI裁剪图片?快速完成智能图片裁剪教程如何用SumoPaint的AI裁剪图片?快速完成智能图片裁剪教程

    答案:SumoPaint虽无AI裁剪功能,但可通过魔棒、套索工具精确选区,结合图层蒙版与羽化、反选等操作实现智能裁剪效果,最后按需导出PNG或JPG高质量文件。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ 在SumoPaint中,虽然它不…

    2026年9月21日 用户投稿
    100
  • MySQL缓存机制对性能提升的作用_MySQL缓存配置及调优方案

    MySQL缓存机制对性能提升的作用_MySQL缓存配置及调优方案MySQL缓存机制对性能提升的作用_MySQL缓存配置及调优方案MySQL缓存机制对性能提升的作用_MySQL缓存配置及调优方案MySQL缓存机制对性能提升的作用_MySQL缓存配置及调优方案

    mysql的缓存机制主要包括innodb缓冲池、查询缓存和操作系统文件系统缓存等,其中innodb缓冲池是性能优化的核心。1. innodb缓冲池缓存表数据和索引页,减少磁盘i/o,提升读写效率;2. 查询缓存因失效频繁及锁竞争问题,在高并发场景下易成瓶颈,已在mysql 8.0中移除;3. 操作系…

    2026年9月21日 用户投稿
    100
  • VSCode中竖线怎么设置_VSCode编辑区竖线(标尺)显示与配置教程

    在VSCode中启用垂直标尺需修改settings.json文件中的editor.rulers属性,如设置{ “editor.rulers”: [80, 120] }可在第80和120列显示竖线,提升代码对齐与可读性;虽原生不支持自定义颜色样式,但可通过安装Guides或In…

    2026年9月21日
    100
  • PHP 数组值比较与嵌套数组过滤教程

    本教程详细讲解如何在 PHP 中比较一个简单数组与一个复杂嵌套数组,并根据特定条件(如文件名匹配)过滤嵌套数组中的所有相关子数组。我们将通过识别非匹配项的索引,然后从所有子数组中移除这些项并重新索引,实现精确的数据筛选。 问题背景 在 php 开发中,我们经常会遇到需要处理结构复杂的数组数据。例如,…

    2026年9月21日
    100
  • Linux之包管理工具(RPM和YUM)

    包管理工具1. rpm包1.1 rpm指令1.1.1 查询指令使用rpm查询已安装的rpm列表:rpm -qa | grep xx 检查是否已安装firefox:rpm -qa | grep firefox 如果显示i686或i386,表示32位系统,noarch表示通用rpm -qa:列出所有已安…

    2026年9月21日
    000
  • Chrome浏览器怎么开启数据同步功能_Chrome浏览器跨设备数据同步设置教程

    首先登录Google账户启用Chrome同步功能,确保书签、历史记录、密码等数据跨设备一致;接着在设置中自定义同步内容类型以满足隐私需求;然后通过Google账户密钥或自定义密码加密同步数据,提升安全性;最后在新设备登录同一账户,自动接收已同步的浏览数据,实现无缝体验。 如果您希望在不同设备间无缝使…

    2026年9月21日
    000
  • 如何使用XGBoost训练AI大模型?优化机器学习模型的步骤

    XGBoost并非用于训练GPT类大模型,而是擅长处理结构化数据的高效梯度提升算法,其优势在于速度快、准确性高、支持并行计算、内置正则化与缺失值处理,适用于表格数据建模;通过分阶段超参数调优(如学习率、树深度、采样策略)、结合贝叶斯优化与交叉验证,并配合特征工程、数据预处理和集成学习等关键步骤,可显…

    2026年9月21日
    000
  • MySQL全文搜索如何与外部引擎结合_提升搜索体验?

    MySQL全文搜索如何与外部引擎结合_提升搜索体验?MySQL全文搜索如何与外部引擎结合_提升搜索体验?MySQL全文搜索如何与外部引擎结合_提升搜索体验?MySQL全文搜索如何与外部引擎结合_提升搜索体验?

    mysql 的全文搜索在中文分词和复杂查询上存在局限,常结合外部引擎提升性能。1. 使用 elasticsearch,通过 logstash 或 canal 同步数据,安装中文分词插件并利用布尔查询等优化搜索。2. 利用 sphinx,从 mysql 直接构建索引,通过 sql-like 接口和中文…

    2026年9月21日 用户投稿
    100

发表回复

登录后才能评论
关注微信