searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

使用gdb脚本dump进程的数据结构

2023-09-21 02:51:08
64
0
背景
  工作中使用gdb调试c/c++程序时,经常会遇到从运行的进程中dump数据结构的场景。对于简单或连续存储的数据我们可以直接使用p或x命令,而对于较为复杂的数据结构就使用p或x命令就力不从心了,为此本文以一个链表为例展示如何使用python脚本来dump所有元素。
 
介绍
   gdb 中的 Python 脚本是使用 gdb 的 Python 命令来执行的。Python 命令会启动一个 Python 解释器,并将 gdb 的上下文传递给解释器。gdb 中的 Python 脚本可以访问 gdb 的所有功能。例如,可以使用 Python 脚本来设置断点、查看变量值、跟踪程序执行流程等,较单纯的输入命令更加灵活。
 
示例代码
 
// main.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

struct list_head {
	struct list_head *next, *prev;
};

static inline void INIT_LIST_HEAD(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

static inline void __list_add(struct list_head *recent,
			      struct list_head *prev,
			      struct list_head *next)
{
	next->prev = recent;
	recent->next = next;
	recent->prev = prev;
	prev->next = recent;
}

static inline void list_add_tail(struct list_head *recent, struct list_head *head)
{
	__list_add(recent, head->prev, head);
}

struct node {
    uint32_t value;
    struct list_head list;
};

int main()
{
    struct list_head head;

    INIT_LIST_HEAD(&head);
    for (int i = 0; i < 100; i++) {
        struct node *n = malloc(sizeof(struct node));
        if (!n)
            return -1;
        n->value = i; 
        list_add_tail(&n->list, &head);
    }

    printf("done\n"); // break point
	// Ignore memory free

    return 0;
}
 
gdb python脚本
# list_dump.py
import gdb

def offset_of(typeobj, field):
    element = gdb.Value(0).cast(typeobj)
    return int(str(element[field].address).split()[0], 16)

def container_of(ptr, typeobj, member):
    return (ptr.cast(gdb.lookup_type('long')) -
            offset_of(typeobj, member)).cast(typeobj)

class ListDump(gdb.Command):
    def __init__(self):
        super(ListDump, self).__init__(
            'list_dump', gdb.COMMAND_USER
        )
    
    def complete(self, text, word):
        return gdb.COMPLETE_SYMBOL

    def _list_to_str(self, head_ptr):
        result = []
        pos_ptr = head_ptr['next']
        while pos_ptr != head_ptr:
            node_ptr = container_of(pos_ptr, gdb.lookup_type('struct node').pointer(), 'list')
            result.append(str(node_ptr['value']))
            pos_ptr = pos_ptr['next']
        result.append('\n')
        return ' '.join(result)

    def invoke(self, args, from_tty):
        argv = gdb.string_to_argv(args)
        if len(argv) != 1:
            gdb.write("ERROR: %s\n" % 'Expected fmt "list_dump ptr"', gdb.STDERR)
            return
        head_ptr = gdb.parse_and_eval(args)
        if str(head_ptr.type) != 'struct list_head *':
            gdb.write("ERROR: %s\n" % 'Expected pointer argument of type (list_head *)', gdb.STDERR)
            return
        print(self._list_to_str(head_ptr))

ListDump()
 
调试
$ gcc main.c -o main -g -O0
$ gdb ./main

(gdb) source list_dump.py
(gdb) b main.c:48
Breakpoint 1 at 0x1290: file main.c, line 48.

(gdb) run
Starting program: /home/st/clang/mxml/tmp/main

Breakpoint 1, main () at main.c:48
48          printf("done\n"); // break point

(gdb) list_dump &head
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

总结

  • 自动化重复性的调试任务。例如,可以使用 Python 脚本来自动设置断点、查看变量值、跟踪程序执行流程等。
  • 分析程序的运行状态。例如,可以使用 Python 脚本来计算程序的运行时间、内存使用情况等。
  • 创建自定义的调试工具。例如,可以使用 Python 脚本来创建一个可以跟踪程序执行流程的图形工具。

gdb 中的 Python 脚本是调试 C 和 C++ 程序的强大工具。通过使用 Python 脚本,可以提高调试效率和准确性。

 

0条评论
0 / 1000
s****n
1文章数
0粉丝数
s****n
1 文章 | 0 粉丝
s****n
1文章数
0粉丝数
s****n
1 文章 | 0 粉丝
原创

使用gdb脚本dump进程的数据结构

2023-09-21 02:51:08
64
0
背景
  工作中使用gdb调试c/c++程序时,经常会遇到从运行的进程中dump数据结构的场景。对于简单或连续存储的数据我们可以直接使用p或x命令,而对于较为复杂的数据结构就使用p或x命令就力不从心了,为此本文以一个链表为例展示如何使用python脚本来dump所有元素。
 
介绍
   gdb 中的 Python 脚本是使用 gdb 的 Python 命令来执行的。Python 命令会启动一个 Python 解释器,并将 gdb 的上下文传递给解释器。gdb 中的 Python 脚本可以访问 gdb 的所有功能。例如,可以使用 Python 脚本来设置断点、查看变量值、跟踪程序执行流程等,较单纯的输入命令更加灵活。
 
示例代码
 
// main.c
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

struct list_head {
	struct list_head *next, *prev;
};

static inline void INIT_LIST_HEAD(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

static inline void __list_add(struct list_head *recent,
			      struct list_head *prev,
			      struct list_head *next)
{
	next->prev = recent;
	recent->next = next;
	recent->prev = prev;
	prev->next = recent;
}

static inline void list_add_tail(struct list_head *recent, struct list_head *head)
{
	__list_add(recent, head->prev, head);
}

struct node {
    uint32_t value;
    struct list_head list;
};

int main()
{
    struct list_head head;

    INIT_LIST_HEAD(&head);
    for (int i = 0; i < 100; i++) {
        struct node *n = malloc(sizeof(struct node));
        if (!n)
            return -1;
        n->value = i; 
        list_add_tail(&n->list, &head);
    }

    printf("done\n"); // break point
	// Ignore memory free

    return 0;
}
 
gdb python脚本
# list_dump.py
import gdb

def offset_of(typeobj, field):
    element = gdb.Value(0).cast(typeobj)
    return int(str(element[field].address).split()[0], 16)

def container_of(ptr, typeobj, member):
    return (ptr.cast(gdb.lookup_type('long')) -
            offset_of(typeobj, member)).cast(typeobj)

class ListDump(gdb.Command):
    def __init__(self):
        super(ListDump, self).__init__(
            'list_dump', gdb.COMMAND_USER
        )
    
    def complete(self, text, word):
        return gdb.COMPLETE_SYMBOL

    def _list_to_str(self, head_ptr):
        result = []
        pos_ptr = head_ptr['next']
        while pos_ptr != head_ptr:
            node_ptr = container_of(pos_ptr, gdb.lookup_type('struct node').pointer(), 'list')
            result.append(str(node_ptr['value']))
            pos_ptr = pos_ptr['next']
        result.append('\n')
        return ' '.join(result)

    def invoke(self, args, from_tty):
        argv = gdb.string_to_argv(args)
        if len(argv) != 1:
            gdb.write("ERROR: %s\n" % 'Expected fmt "list_dump ptr"', gdb.STDERR)
            return
        head_ptr = gdb.parse_and_eval(args)
        if str(head_ptr.type) != 'struct list_head *':
            gdb.write("ERROR: %s\n" % 'Expected pointer argument of type (list_head *)', gdb.STDERR)
            return
        print(self._list_to_str(head_ptr))

ListDump()
 
调试
$ gcc main.c -o main -g -O0
$ gdb ./main

(gdb) source list_dump.py
(gdb) b main.c:48
Breakpoint 1 at 0x1290: file main.c, line 48.

(gdb) run
Starting program: /home/st/clang/mxml/tmp/main

Breakpoint 1, main () at main.c:48
48          printf("done\n"); // break point

(gdb) list_dump &head
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99

总结

  • 自动化重复性的调试任务。例如,可以使用 Python 脚本来自动设置断点、查看变量值、跟踪程序执行流程等。
  • 分析程序的运行状态。例如,可以使用 Python 脚本来计算程序的运行时间、内存使用情况等。
  • 创建自定义的调试工具。例如,可以使用 Python 脚本来创建一个可以跟踪程序执行流程的图形工具。

gdb 中的 Python 脚本是调试 C 和 C++ 程序的强大工具。通过使用 Python 脚本,可以提高调试效率和准确性。

 

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0