一种实现用户态调试器调试单个函数的方法及系统的制作方法

文档序号:6470443阅读:206来源:国知局
专利名称:一种实现用户态调试器调试单个函数的方法及系统的制作方法
技术领域
本发明涉及调试器的实现技术,尤其涉及一种实现用户态调试器调 试单个函数的方法及系统。
背景技术
调试器是用来帮助开发人员定位程序故障的一种工具软件。目前的
调试器总体分为两类内核级调试器和用户态调试器。内核级调试器能 够跟踪和调试运行在内核态的任务;用户态调试器只能对运行在用户态 的任务进行调试。调试器提供了设置断点,查看变量、寄存器、显示堆 栈和求表达式值等基本调试功能,以及控制程序运行的单步跳过,单步 进入,继续运行等功能。这些功能对于程序员跟踪和发现故障起着重要 作用。
在程序设计语言中,函数作为逻辑上相对独立的代码片断,能够完 成函数设计者所指定的功能。函数功能的正确性对程序整体行为的正确 性起着决定作用,因此,如何针对单个函数进行调试并验证其正确性, 成为调试的一种需要。
以WindRiver公司的Vxworks(—种嵌入式实时操作系统)为例,它 提供的shell调试器,就具备调试单个函数的功能。通过在shell中执行 命令,创建新的任务,该任务执行命令指定的函数,并能够对其进行调 试。Vxworks的shell调试器属于内核级调试器,被运行起来的函数是在 内核态运行的。其功能的实现依赖于内核提供的创建任务的接口函数, 通过传入要运行的函数的地址以及函数参数来完成。由此可知,在内核 态下,只需要调用内核任务创建函数,并传入要创建函数的入口地址, 即可实现调试单个函数的功能。然而,对于用户态调试器,在用户态下没有一个统一的接口实现在 其它进程中创建新线程。具体的说,用户态的程序通常被称为一个进程,
一个进程可以拥有多个线程,线程之间共享同一个进程的资源;而进程 之间的地址空间却是相互独立的,每个进程都有自己的虚拟地址空间, 一个进程不能直接读写其它进程的虚拟地址空间的内容;同时,操作系 统没有提供在其它进程中创建线程运行单个函数的接口。因此,用户态 调试器作为用户态下运行的一个普通进程,要实现在被调试任务中创建 线程运行单个函数,无法直接利用操作系统的接口完成。在目前的用户
态调试器中还没有创建线程运行单个函数功能的实现。

发明内容
本发明所要解决的技术问题是提供一种实现用户态调试器调试单 个函凄t的方法,以及实J见该方法的系统。
为解决上述技术问题,本发明是通过以下技术方案实现的
一种实现用户态调试器调试单个函数的方法,所述用户态调试器在 收到调试所述单个函数的命令后,将该函数作为当前的待调试函数,并 作以下处理
A 、对当前的#皮调试程序以及被调试程序加载的动态库的符号信息 进行分析,根据分析结果建立哈希表;同时,从所述被调试程序的任务 中选择一个任务作为待处理任务;
B、 在所述哈希表中查找线程创建库函数的入口地址,当找到后, 停止所述待处理任务的运行;
C、 以所述线程创建库函数及所述待调试函数为依据,修改所述待 处理任务的各寄存器和堆栈中的值,以改变所述待处理任务的行为,使 其在之后运行时创建线程来执行所述待调试函数,修改成功后运行所述 待处理任务。
其中,所述步骤B中,在停止所述待处理任务的运行之后,还包括将当前所述待处理任务的全部寄存器和堆栈中的值进行保存;
步骤C之后还包括才艮据所述待处理任务返回的信号确认所述待调
试函数的运行是否结束,若已结束,则将所述待处理任务的全部寄存器
和堆栈中的值恢复为所述步骤B中在停止所述待处理任务的运行之后 所保存的值。
其中,所述步骤B中还包括若在所述^^希表中未查找到线程创建 库函数的入口地址,则将线程库加载到所述被调试程序中,加载成功后, 将所述线程库的符号信息添加入所述哈希表中,再从该哈希表中查找出 所述线程创建库函数的入口地址。
其中,步骤B中,所述线程库加载到所述被调试程序中的方法为
① 在标准C函lt库中查找动态库加载函lt的入口地址;
② 将所述待处理任务的程序计数器的值设置为动态库加载函数的 入口地址;
③ 从所述待处理任务的堆栈指针寄存器中读取当前栈顶的地址;以 此地址为起始地址,在所述待处理任务的堆栈上构建所述动态库加载函 数的调用栈帧,该调用栈帧中包括动态库路径、模式、动态库路径地址 及返回地址参数;其中,所述动态库路径为线程库的路径,所述4莫式为 所述线程库的加载模式,所述动态库路径地址为所述线程库的路径地
址;
④ 设置待处理任务的堆栈指针寄存器的值为新的栈顶地址。
其中,所述步骤c中,所述待处理任务的各寄存器和堆栈中的值的 ^奮改方法为
el、设置所述待处理任务的程序计数器的值为线程创建库函数的入 口地址;
e2、从所述待处理任务的堆栈指针寄存器中读取当前栈顶的地址; e3、以所述当前栈顶的地址为起始地址,在所述待处理任务的堆栈中,构建所述待调试函数的函数调用栈帧;
e4 、设置所述堆栈指针寄存器中的指针指向新的栈顶地址。
其中,所述步骤e3中,所述待调试函数的函数调用栈帧的构建过 程包括
ml、在所述堆栈上分配存放线程内部标识的局部变量的空间,写入 所述线程内部标识的局部变量;
m2、在堆栈上分配存放待调试函数的参数的空间,写入所述待调试 函数的各项参数;
m3、在所述堆栈上分配空间,依次写入待调试函数的入口地址、线 程属性、线程内部标识局部变量的地址。
一种实现用户态调试器调试单个函数的系统,包括符号表操作模 块、寄存器操作模块、内存操作模块,还包括运行控制模块、运行环 境构建模块;
所述符号表操作模块,用于对被调试程序以及被调试程序所加载的 动态库的符号信息进行分析,根据分析结果建立哈希表;
所述寄存器操作模块,用于对被调试程序的任务的各寄存器的值进 行读写操作;
所述内存操作模块,用于对被调试程序的任务的堆栈中的值进行读 耳又和修改的操作;
所述运行控制模块,用于接收调试单个函数的命令,将所述单个函 数作为待调试函数,进行以下操作从当前被调试程序的任务中选取一 个任务作为待处理任务,调用符号表操作模块建立哈希表并从中查找线 程创建库函数的入口地址,然后停止所述待处理任务的运行;控制运行 环境构建模块以所述线程创建库函数及待调试函数为依据来改变所述 待处理任务的行为,使该任务在之后运行时创建线程来执行所述待调试 函数,并在修改完成之后启动所述待处理任务的运行;所述运行环境构建模块,用于以所述线程创建库函数及待调试函数 为依据,通过调用寄存器操作模块和内存操作模块修改所述待处理任务 的各寄存器和堆栈的值来改变所述待处理任务的行为,为所述待调试函 数创建运行环境。
上述系统中,还包括动态库加载模块,用于向被调试程序中加载指
定的动态库;
所述运行控制模块,还用于在从所述哈希表中未查找到线程创建库 函数的入口地址时,通过调用动态库加载模块向所述被调试程序中加载 线程库,并调用符号表操作^f莫块将所述线程库的符号信息添加入所述口合 希表中,然后从该哈希表中重新查找所述线程创建库函数。
上述系统中,还包括信号处理模块和运行环境恢复模块; 所述信号处理模块,用于等待接收所述待处理任务在运行过程中返
回的信号,根据信号的类型判断所述待调试函数的运行是否结束,若已
结束,则通知运行环境恢复模块;
所述运行环境恢复模块,用于调用寄存器操作模块,保存所述待处
理任务的各寄存器和堆栈在被所述运行环境构建模块修改之前的值,并
在收到信号处理模块的通知后,将所述待处理任务的各寄存器和堆栈的
值恢复为之前保存的值。
本发明具有以下有益效果
采用本发明,能够在各种CPU上实现创建线程运行单个函数的功 能,克服了目前的用户态调试器中缺少对运行单个函数进行调试的支 持。


图l是本发明的系统结构示意图; 图2是寄存器操作模块的结构示意图;图3是内存操作模块的结构示意图; 图4是符号表操作模块的结构示意图; 图5是信号处理模块的结构示意图; 图6是本发明的用户态调试器调试单个函数的方法流程图; 图7是本发明中动态加载库到被调试程序中的方法流程图; 图8是本发明动态加载线程库过程中的堆栈布局示意图; 图9是本发明创建线程运行单个函数过程中的堆栈布局示意图; 图IO是本发明中信号处理循环流程的示意图。
具体实施例方式
本发明的核心思想为通过调试器来修改被调试程序的执行环境, 改变被调试任务的行为,使其创建线程来执行用户指定的待调试函数, 待待调试函数运行完成后,再恢复被调试程序原来的执行环境。
下面结合附图及具体实施例对本发明作进一步详细的描述
请参阅图1 ,本发明所述的实现用户态调试器运行单个函数的系统 包括以下八个模块寄存器操作模块、内存操作模块、符号表操作模块、 信号处理模块、动态库加载模块、运行环境构建模块、运行环境恢复模 块、运行控制模块。其中寄存器操作模块,内存操作模块、符号表操作 模块为调试器通用功能模块,其它为本发明所涉及的创建线程运行单个 函数功能的模块。
寄存器操作模块,用于对被调试程序的寄存器的值进行读写操作。 如图2所示,包括寄存器映射緩存、读、写寄存器接口以及寄存器操作 接口。其中寄存器映射緩存用于保存当前任务的所有寄存器的值,通过 读寄存器接口将当前任务的所有寄存器的值读取到寄存器映射緩存中; 通过写寄存器接口 ,将寄存器映射緩存的内容写入到任务的寄存器中。 寄存器操作接口对外提供了读写寄存器的统一接口 。内存操作模块,用于对被调试程序的虚拟地址空间进行读取和修改 的操作。如图3所示,通过系统接口,实现将内存緩冲区的内容写到指 定的地址或者将指定的地址的内容读出到内存緩冲区中。
符号表操作模块,如图4所示,用于分析被调试程序的符号信息以 及被调试程序加载的动态库的符号信息,建立hash表(哈希表),提供 通过符号名查找符号地址和通过符号地址查找符号名的功能。
信号处理模块,用于根据被调试程序向调试器发送的信号,感知新 线程创建等异步事件。如图5所示,调试器首先注册需要监听的信号集 合,然后监听异步信号事件。对于收到的异步信号,分析该信号的类型, 并处理该信号,或者通知用户。
动态库加载模块,用于控制被调试程序加载指定的动态库。该模块 的处理流程如图7所示。
运行环境构建模块,用于为运行单个函数做好运行前的环境构建工作。
运行环境恢复模块,用于恢复被调试程序在创建线程运行单个函数 前的所有状态。该模块主要通过调用寄存器操作模块,恢复所有寄存器 和堆栈的内容。
运行控制模块,用于对创建线程运行单个函数流程的总体控制和错 误处理等。
上述系统中的工作原理为
运行控制模块在接收到调试单个函数的命令后,将所述单个函数作 为待调试函数,进行以下操作从当前被调试程序的任务中选取一个任 务作为待处理任务;调用符号表操作模块建立哈希表并从中查找线程创 建库函数的入口地址,若未查找到,则调用动态库加载模块,向所述被 调试程序中加载线程库,并调用符号表操作模块将所述线程库的符号信 息添加入所述哈希表中,然后从该哈希表中重新查找所述线程创建库函
12数;找到后,停止待处理任务的运行,控制运行环境构建模块以所述线
程创建库函数及待调试函数为依据来改变待处理任务的行为,使该任务 在之后运行时创建线程来执行所述待调试函凄t,并在々务改完成之后启动 所述待处理任务的运行。在运行过程中,信号处理模块等待接收待处理 任务在运行过程中返回的信号,根据信号的类型判断所述待调试函数的 运行是否结束,若已结束,则通知运行环境恢复模块,将待处理任务的 各寄存器和堆栈的值恢复为之前的初始状态值。
请参阅图6,该图所示为本发明的用户态调试器调试单个函数的方 法,具体包括以下步骤
601、 对被调试程序以及被调试程序加载的动态库的符号信息进行 分析,根据分析结果建立哈希表通过读取和分析被调试程序的可执行 文件,获取被调试程序符号的名称、虚拟地址和类型信息(符号类型包 括全局函数,全局变量,静态函数,静态变量等),把所有符号信息以 符号名为关键字,建立hash表;同理,对于该被调试程序加载的所有动 态库,也按照上述方法将其符号信息加入到hash表中。同时,从被调试 程序的多个任务之中选择一个任务作为待处理任务。
602、 在hash表中查找pthread—create函数(线程创建库函数)的入 口地址。该地址是线程库中的符号地址,如果在hash表中没有查找到, 则执行步骤603,否则执行步骤604。
603、 调用动态库加载模块,将libpthread.so.O线程库加载到被调试 程序中;加载成功后,再次调用符号表操作模块,将libpthread.so.O线 程库的符号信息添加到hash表中,然后从该表中查找出pthread—create 的入口i也址。
请参阅图7,上述线程库的加载方法为
701、在libc库(标准C函数库)中查找dl—open函数(动态库加 载函^t)的入口地址。702、 将待处理任务的pc寄存器(程序计数器)的值设置为dl—open 的入口i也址。
703、 在待处理任务的堆栈上构造d1—叩en函数的调用栈帧,新构造 的堆栈的布局如图8所示,该布局适用于x86体系结构,对其它架构 CPU可参照其ABI接口中的定义。在新构造的栈帧中,首先是一个存 放线程库全路径的緩冲区;其次是参数mode,表明打开线程库的模式, 设置为RTLD—NOW,表示立即加载;然后是线程库路径的地址,即指 向最开始分配的存放库路径的緩冲区地址(dl—open函数的第一个参数 是库路径的地址,第二个参数是mode,本实施例中是将这两个参数凡 方向顺序写入堆栈的);最后是返回地址,本实施例中将此返回地址设 置为一个异常地址,用于信号处理模块判断函数调用的结束。
704、 设置待处理任务的sp寄存器(堆栈指针寄存器)的指针指向 新的一戋顶。
705、 在构造好dl一open函数的调用栈后,继续待处理任务的运行, 待处理任务就会从dl_open函数的入口地址处开始执行,并加载线程库。
706、 从eax寄存器中读取d1—open函数的返回值,如果加载成功, 则返回值为线程库的一个句柄;如果加载失败,则返回值为-l。
604、 停止待处理任务的运行;并调用运行环境恢复模块,将待处 理任务的所有寄存器和堆栈中的当前值保存起来。
605、 调用运行环境构建模块,构建待调试函数的运行环境
i )设置待处理任务的pc寄存器的值为pthread—create的入口地址。
ii )根据不同CPU的ABI (应用程序二进制接口 )中对函数调用 的定义不同,构建待调试函数的函数调用栈帧,将待调试函数的各项参 数放置到堆栈中的正确位置,并将函数调用后的返回地址设置为一个异 常地址;构建方法为先在所述堆栈上分配存放线程内部标识的局部变量的空间,写入所述线程内部标识的局部变量;再在堆栈上分配存力文待调 试函数的参数的空间,写入所述待调试函数的各项参数;最后在所述堆 栈上分配空间,依次写入待调试函数的入口地址、线程属性、线程内部 标识局部变量的地址。新构建的堆栈布局如图9所示。
iii)设置待处理任务的sp寄存器中的指针指向新的栈顶。
606、 运行待处理任务。
607、 信号处理模块等待接收待处理任务发送给调试器的异步信号, 并根据所接收的异步信号的类型作不同的处理,如图10所示。如果收 到新线程创建的信号(在linux2.6内核下,可以通过对SIGTRAP信号 进行移位操作,并取得其reason字段进行判断),将新创建的线程加入 到被调试的任务链表中管理起来,然后继续等待待处理任务的信号;如 果收到异常信号,则说明该任务调用pthread—create函数创建线程结束, 执行步骤608;如果收到其他信号,则处理该信号后继续等待待处理任 务的信号。
608、 调用运行环境恢复模块,恢复之前保存的所有寄存器和堆栈 的值,回到待处理任务的最初状态。
以上实施例仅用以说明本发明的技术方案而非限制,仅仅参照较佳 实施例对本发明进行了详细说明。本领域的普通技术人员应当理解,可 以对本发明的技术方案进行修改或者等同替换,而不脱离本发明技术方 案的精神和范围,均应涵盖在本发明的权利要求范围当中。
权利要求
1、一种实现用户态调试器调试单个函数的方法,其特征在于,所述用户态调试器在收到调试所述单个函数的命令后,将该函数作为当前的待调试函数,并作以下处理A、对当前的被调试程序以及被调试程序加载的动态库的符号信息进行分析,根据分析结果建立哈希表;同时,从所述被调试程序的任务中选择一个任务作为待处理任务;B、在所述哈希表中查找线程创建库函数的入口地址,当找到后,停止所述待处理任务的运行;C、以所述线程创建库函数及所述待调试函数为依据,修改所述待处理任务的各寄存器和堆栈中的值,以改变所述待处理任务的行为,使其在之后运行时创建线程来执行所述待调试函数,修改成功后运行所述待处理任务。
2、 如权利要求1所述的实现用户态调试器调试单个函数的方法, 其特征在于,所述步骤B中,在停止所述待处理任务的运行之后, 还包括将当前所述待处理任务的全部寄存器和堆栈中的值进行保 存;步骤C之后还包括根据所述待处理任务返回的信号确认所述 待调试函数的运行是否结束,若已结束,则将所述待处理任务的全部 寄存器和堆栈中的值恢复为所述步骤B中在停止所述待处理任务的 运行之后所保存的值。
3、 如权利要求1或2所述的实现用户态调试器调试单个函数的 方法,其特征在于,所述步骤B中还包括若在所述哈希表中未查 找到线程创建库函数的入口地址,则将线程库加载到所述被调试程序 中,加载成功后,将所述线程库的符号信息添加入所述哈希表中,再 从该哈希表中查找出所述线程创建库函数的入口地址。
4、 如权利要求3所述的实现用户态调试器调试单个函数的方法, 其特征在于,步骤B中,所述线程库加载到所述被调试程序中的方 法为① 在标准C函数库中查找动态库加载函数的入口地址;② 将所述待处理任务的程序计数器的值设置为动态库加载函数 的入口地址;③ 从所述待处理任务的堆栈指针寄存器中读取当前栈顶的地址; 以此地址为起始地址,在所述待处理任务的堆栈上构建所述动态库加 载函数的调用栈帧,该调用栈帧中包括动态库路径、模式、动态库路 径地址及返回地址参数;其中,所述动态库路径为线程库的路径,所 述模式为所述线程库的加载模式,所述动态库路径地址为所述线程库 的絲4圣地址; 设置待处理任务的堆栈指针寄存器的值为新的栈顶地址。
5、 如权利要求1或2所述的实现用户态调试器调试单个函数的 方法,其特征在于,所述步骤C中,所述待处理任务的各寄存器和 堆栈中的值的修改方法为el、设置所述待处理任务的程序计数器的值为线程创建库函数的 入口地址;e2、从所述待处理任务的堆栈指针寄存器中读取当前栈顶的地址;e3、以所述当前栈顶的地址为起始地址,在所述待处理任务的堆 栈中,构建所述待调试函数的函数调用栈帧;e4 、设置所述堆栈指针寄存器中的指针指向新的栈顶地址。
6、 如权利要求5所述的实现用户态调试器调试单个函数的方法, 其特征在于,所述步骤e3中,所述待调试函数的函数调用栈帧的构 建过程包括ml 、在所述堆栈上分配存放线程内部标识的局部变量的空间, 写入所述线程内部标识的局部变量;m2、在堆栈上分配存放待调试函数的参数的空间,写入所述待 调试函数的各项参数;m3、在所述堆栈上分配空间,依次写入待调试函数的入口地址、 线程属性、线程内部标识局部变量的地址。
7、 一种实现用户态调试器调试单个函数的系统,包括符号表操 作模块、寄存器操作模块、内存操作模块,其特征在于,还包括运 行控制模块、运行环境构建模块;所述符号表操作模块,用于对被调试程序以及被调试程序所加载 的动态库的符号信息进行分析,根据分析结果建立哈希表;所述寄存器操作模块,用于对被调试程序的任务的各寄存器的值进行读写操作;所述内存搡作模块,用于对被调试程序的任务的堆栈中的值进行读取和修改的操作;所述运行控制模块,用于接收调试单个函数的命令,将所述单个 函数作为待调试函数,进行以下操作从当前被调试程序的任务中选 取一个任务作为待处理任务,调用符号表操作模块建立哈希表并从中 查找线程创建库函数的入口地址,然后停止所述待处理任务的运行; 控制运行环境构建模块以所述线程创建库函数及待调试函数为依据 来改变所述待处理任务的行为,使该任务在之后运行时创建线程来执 行所述待调试函数,并在修改完成之后启动所述待处理任务的运行;所述运行环境构建模块,用于以所述线程创建库函数及待调试函 数为依据,通过调用寄存器操作模块和内存操作模块修改所述待处理调试函数创建运行环境。
8、 如权利要求7所述的实现用户态调试器调试单个函数的系统, 其特征在于,还包括动态库加载模块,用于向被调试程序中加载指定 的动态库;所述运行控制模块,还用于在从所述哈希表中未查找到线程创建 库函数的入口地址时,通过调用动态库加载^t块向所述纟皮调试程序中 加载线程库,并调用符号表操作模块将所述线程库的符号信息添加入 所述哈希表中,然后从该哈希表中重新查找所述线程创建库函数。
9、 如权利要求7或8所述的实现用户态调试器调试单个函数的 系统,其特征在于,还包括信号处理模块和运行环境恢复模块;所述信号处理模块,用于等待接收所述待处理任务在运行过程中 返回的信号,根据信号的类型判断所述待调试函数的运行是否结束, 若已结束,则通知运行环境恢复模块;所述运行环境恢复模块,用于调用寄存器操作模块,保存所述待 处理任务的各寄存器和堆栈在被所述运行环境构建模块修改之前的 值,并在收到信号处理模块的通知后,将所述待处理任务的各寄存器 和堆栈的值恢复为之前保存的值。
全文摘要
本发明公开了一种实现用户态调试器调试单个函数的方法及系统,系统包括符号表操作模块、寄存器操作模块、内存操作模块、运行控制模块、运行环境构建模块;方法为A.符号表操作模块分析当前的被调试程序及其动态库的符号信息,建立哈希表;运行控制模块从被调试程序的任务中选择一个作为待处理任务;B.运行控制模块从哈希表查找线程创建库函数,找到后停止运行待处理任务;C.运行环境构建模块依据线程创建库函数及待调试函数,调用寄存器和内存操作模块来修改待处理任务的各寄存器和堆栈中的值,以改变待处理任务的行为,使其在之后运行时创建线程来执行待调试函数,之后运行待处理任务。采用本发明,实现了在各种CPU上通过创建线程来运行单个函数的功能。
文档编号G06F11/36GK101446918SQ20081021839
公开日2009年6月3日 申请日期2008年12月10日 优先权日2008年12月10日
发明者红 向 申请人:中兴通讯股份有限公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1