首页 > 其他分享 >systemtap从入门到放弃(二)

systemtap从入门到放弃(二)

时间:2023-01-04 10:11:37浏览次数:60  
标签:main systemtap 入门 io int gdb workspace test 放弃

前言

上一篇《systemtap从入门到放弃(一)》我们知道了什么是systemtap,以及如何书写简单的systemtap脚本。本篇承接上文,介绍systemtap的安装和简易场景应用,通过几个小例子掌握systemtap在内核开发调试中的简单使用。

安装systemtap

在linux发行版上安装systemtap,相对而言是一件比较简单得事情,可以选择命令行直接安装,也可以选择源码安装。下面介绍下安装步骤:

1、安装前先确认是否是否配置下面的CONFIG选项,因为systemtap依赖kprobe和relay文件系统:

# 可以通过cat /boot/config-xxx | grep xxx  检查下面的配置项是否配置
CONFIG_DEBUG_INFO
CONFIG_KPROBES
CONFIG_DEBUG_FS
CONFIG_RELAY
# 如果没有,需要配置并重新编译安装内核

2、类似kdump等,调试内核必然需要安装内核debuginfo包,一般安装位置是/usr/lib/debug,检查没有的话可选择用下面的方法安装:

# 查看内核版本
    uname -r 
# centos:
    在http://debuginfo.centos.org/8/x86_64/网址中找到对应版本,下载kernel-debug-debuginfo 以及kernel-debuginfo-common
	
# ubuntu:
	在http://ddebs.ubuntu.com/pool/main/l/linux/ 中下载对应版本(例如linux-image-unsigned-5.4.0-61-generic-dbgsym_5.4.0-61.69_amd64.ddeb)
    dpkg   -i xxx.ddeb 安装

3、命令行安装(ubuntu)

apt-get install systemtap
apt-get install systemtap-sdt-dev

其他安装方式可以参考systemtap官方wiki。systemtap脚本解析命令是"stap",可以用来查看是否安装成功。

stap常用的参数和用法如下:

Usage: stap [options] FILE                    Run script in file.
   or: stap [options] -e SCRIPT               Run given script.
   or: stap [options] -l PROBE                List matching probes.
   or: stap [options] -L PROBE                List matching probes and local variables.
[options]
                      -T TIME    terminate the script after TIME seconds

前两个是基本的执行脚本命令,后面“-l -L”选项可以列出程序中的探测点。下面以一个实际的例子看下"-L "参数的使用。

  • C源码:
main.c:
     1	#include <stdlib.h>
     2	#include <stdio.h> 
     3	extern int sum(int value);
     4	 
     5	struct inout {
     6	    int value;
     7	    int result;
     8	};
     9	
    10	int main(int argc, char * argv[])
    11	{
    12	    struct inout * io = (struct inout * ) malloc(sizeof(struct inout));
    13	    if (NULL == io) {
    14	        printf("Malloc failed.\n");
    15	        return -1;
    16	    }
    17	
    18	    if (argc != 2) {
    19	        printf("Wrong para!\n");
    20	        return -1;
    21	    }
    22	
    23	    io -> value = *argv[1] - '0';
    24	    io -> result = sum(io -> value);
    25	    printf("Your enter: %d, result:%d\n", *argv[1] - '0', io -> result);
    26	    return 0;
    27	}
sum.c:
     1	
     2	int sum(int value) {
     3	    int result = 0;
     4	    int i = 0;
     5	    for (i = 0; i < value; i++)
     6	        result += (i + 1);
     7	    return result;
     8	}

加"-g"选项编译生成test可执行文件,使用stap 查看该程序探测点命令如下:

【2】打印结构体变量

struct value { 
	int member;
	struct in_value in_v;
	struct in_value *in_v2;
}
  • 打印结构体变量:可以直接使用\(value->member,多级就是\)value->in_v->member。注意,无论结构体变量是否是指针,这里都用"->"而不用".";
  • 打印整个结构体:打印整个结构体,只需要在结构体后面加个\(:“\)value\(”,如果打印两层,就加两个:“\)value$$”。

【3】修改函数变量

在指定位置probe后,直接给变量赋予新值即可,只是需要注意的是stap要加-g参数在guru模式下才能修改变量的值。还是使用文章刚开始时的main.c 和sum.c 程序,调试步骤如下:

root@ubuntu2004:# stap -L 'process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:*")'
process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:11") $argc:int $argv:char** $io:struct inout*
process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:12") $argc:int $argv:char** $io:struct inout*
process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:13") $argc:int $argv:char** $io:struct inout*
process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:14") $argc:int $argv:char** $io:struct inout*
process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:15") $argc:int $argv:char** $io:struct inout*
process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:18") $argc:int $argv:char** $io:struct inout*
process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:19") $argc:int $argv:char** $io:struct inout*
process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:20") $argc:int $argv:char** $io:struct inout*
process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:23") $argc:int $argv:char** $io:struct inout*
process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:24") $argc:int $argv:char** $io:struct inout*
process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:25") $argc:int $argv:char** $io:struct inout*
process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:26") $argc:int $argv:char** $io:struct inout*
process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:27") $argc:int $argv:char** $io:struct inout*

我们要修改main函数中io -> value的值,该变量在23行被初始化,我们probe 24行,并修改变量值:

    10	int main(int argc, char * argv[])
    11	{
            ... ...
    23	    io -> value = *argv[1] - '0';
    24	    io -> result = sum(io -> value);
            ... ...
        }

脚本change_value.stp:

#!/usr/bin/stapprobe begin {    printf("=== begin ===\n");}probe process("/workspace/test/gdb/test").statement("main@/workspace/test/gdb/main.c:24") {	printf("input val = %d\n", $io->value);	$io->value = 9;	printf("after val = %d\n", $io->value);}probe end {    printf("=== end ===\n");}

结果:

root@ubuntu2004:# stap change_value.stp -c '../gdb/test 8' -g    	Your enter: 8, result:45	=== begin ===	input val = 8 after val = 9        === end ===

变量被初始化位8,被我们手动改位9,因此累加结果变成了49!

总结

本文通过几个简单的例子介绍了几个简单调试场景下systemtap的时候,更加复杂的调试,比如page fault、tcp等可以参考systemtap的tapset介绍。另外systemtap在性能测试方面也有很多用处,有需要的朋友可以自行去systemtap官网上查看。

标签:main,systemtap,入门,io,int,gdb,workspace,test,放弃
From: https://www.cnblogs.com/linhaostudy/p/17024077.html

相关文章