Switch-Router

跟我一起学习drgn(1)---使用初体验

Published at 2023-12-06 | Last Update 2023-12-06

drgn (读作”dragon”, 官网主页 github地址) 是一款由 Meta 公司开发的可编程调试器. 它可用于调试用户态程序, 也可以调试内核.

说到调试器, 绕不开的就是 GDB, 那么 drgn 与 GDB 有何不同呢? GDB 调试相信大家已经很熟悉了, 因此, 这里我们用一个 drgn 的例子来看下:

先编写一个简单的C语言程序global_inc.c, 该程序每隔1s将变量值累加.

#include <stdio.h>
#include <unistd.h>

int global_var = 0;

int main() {
    while (1) {
        global_var++;
        printf("Global variable: %d\n", global_var);
        sleep(1);
    }
    return 0;
}

编译该程序, 注意加上-g选项, 然后运行

$ gcc global_inc.c -g -o global
$ ./global

新开一个终端, 使用 drgn 交互模式调试该程序 (-p 指定运行中的程序的 pid), 并在交互式控制台中多次查看变量的值

这种使用方式是不是与 GDB 有点像?

不过, 用户态呈迅速这并不是 drgn 的主要用武之地. 内核部分才是. 再看下面这个例子, 我们启动 drgn, 但不指定 pid

drgn 直接窥测了程序在内核部分的结构!

由此, 我们可以总结 GDB 与 drgn 的不同之处。

  • GDB 更侧重于用户空间, 优秀的断点机制可以很方便地进行单步调试或堆栈分析
  • drgn 更侧重于内核空间, 它可以窥测内核数据结构.

drgn 的调试目标是内存映像的内容, 比如内核的 /proc/kcore, crash dump 文件, 又或者 /proc//mem

安装与启动

drgn 有多种安装方式, 具体可参考 官方文档

比如我是使用 pip 进行安装:

sudo pip3 install drgn

drgn 的正常运行还需要安装 debugging symbol, 不同发行版获取 debugging symbol 的方法参考 Getting Debugging Symbols

如果是自己编译内核, 那么可以在编译时打开CONFIG_DEBUG_INFO选项.

交互模式 vs 脚本模式

drgn 支持交互模式和脚本模式.

交互模式通过运行 drgn 启动, 根据参数不同,drgn 调试不同的内存目标, 默认的sudo drgn 调试正在运行的内核, sudo drgn -p $PID调试指定的program, drgn -c $PATH调试指定的 core dump 文件(可以是内核的 vmcore 或者用户态的 coredump 文件)

脚本模式是指编写一个 python 文件, 让 drgn 去执行, 下面的例子摘自官网

方式一: 将脚本文件作为参数运行 drgn

$ cat script.py
import sys
from drgn.helpers.linux import find_task

pid = int(sys.argv[1])
uid = find_task(pid).cred.uid.val.value_()
print(f'PID {pid} is being run by UID {uid}')
$ sudo drgn script.py 601
PID 601 is being run by UID 1000

方式二: 指定 drgn 作为解释器, 直接运行脚本

$ cat script2.py
#!/usr/bin/env drgn

mounts = prog['init_task'].nsproxy.mnt_ns.mounts.value_()
print(f'You have {mounts} filesystems mounted')
$ sudo ./script2.py
You have 36 filesystems mounted