基于寄存器传输语言确定动态函数调用关系的方法

文档序号:6535687阅读:231来源:国知局
基于寄存器传输语言确定动态函数调用关系的方法【专利摘要】本发明公开一种基于寄存器传输语言确定动态函数调用关系的方法,主要针对静态函数调用关系无法分析函数指针调用和可加载模块函数调用关系的问题而设计。本发明在系统运行过程中检测函数调用执行事件,若检测到调用指令后,解析该指令调用的函数入口地址对应的函数名,统计指令次数,输出函数名、调用发生时间、所属进程号、所属线程号、所属栈顶指针以及模块号,并保存到跟踪数据文件;若检测的指令是返回指令后,输出时间、所属进程号、所属线程号以及所属栈顶指针到跟踪数据文件;对所述跟踪数据文件进行处理获得预定标准的数据格式,将所述跟踪数据文件传输至静态函数调用关系生成装置中,生成描述函数调用关系的动态函数调用图。【专利说明】基于寄存器传输语言确定动态函数调用关系的方法【
技术领域
】[0001]本发明涉及函数调用关系【
技术领域
】,具体涉及一种基于寄存器传输语言确定动态函数调用关系的方法。【
背景技术
】[0002]对源代码的阅读分析可以通过函数调用图来实现,函数调用图可以是宏观的(源码模块间调用关系),也可以是微观的(具体到某一个函数的调入和调出关系)。函数调用图可以展现源代码静态的一种函数调用走向,也可以用来展示系统或程序运行时函数调用状态。为了分析的精确、方便以及高效,研究者开发许多函数调用图的生成工具。[0003]静态函数调用图生成工具是指建立在已知源码的基础上,根据不同的分析需要开发的工具,这些工具中有些作用对象仅为源码如SourceInsight、Understand等,有些作用对象是编译过程生成的中间结果如Codeviz、Egypt、LLVM以及0pen64等。[0004]动态函数调用图生成工具是建立在获取系统或软件在运行过程中发生的函数调用关系并绘制成调用图的工具,为了获取运行时的函数调用情况也开发了一些工具如Systemtap>gprof、KCachegrincUftrace、pproftool等,它们有些是针对于内核有些事针对有软件的,其基于的目的也各不相同,但它们都是基于函数调用关系分析。[0005]Gprof是在编译时让每个函数均调用mcount函数,同时会在内存中保存函数调用图,这样做会降低系统的性能,如果对系统全部函数调用跟踪的话,会造成内存使用不足。Ftrace的原理类似于gprof的原理,只是它不会在内存中保存函数调用图,而且mcount的使用也是动态的,可以根据用户的要求在需要时替换nop为mcount指令。SystemTap是监控和跟踪运行中的Linux内核的操作的动态方法,SystemTap没有使用工具构建一个特殊的内核,而是允许您在运行时动态地安装该工具,它通过一个名为KpiObes的应用编程接口(API)来实现该目的。pproftool主要用于分析cpu运行瓶颈需要对应用软件产生中断后记录相关信息。Gprof、ftrace以及KCachegrind都是基于静态的插桩技术,ftrace其本质也是静态的插桩技术只是实现上有所不同。而本文将要采用的跟踪工具是建立在虚拟机之上的,不会对源代码插入代码,因此不会影响系统执行本身的性能。[0006]KCachegrind无法跟踪到系统调用,而gprof、ftrace等可以跟踪到系统调用但无法跟踪到系统启动过程,这些问题都将在本文的工具中得到解决。SimSight基于Simics的动态跟踪工具,与本文设计理念最为接近,但SimSight无法给出静态与动态相结合的函数调用图。【
发明内容】[0007]针对上述问题,本发明提供一种能够清楚描述指针函数以及可加载模块的调用关系的基于寄存器传输语言确定动态函数调用关系的方法。[0008]为达到上述目的,本发明基于寄存器传输语言确定动态函数调用关系的方法,所述方法包括:[0009]在系统运行过程中检测函数调用执行事件,[0010]若检测到调用指令后,解析该指令调用的函数入口地址对应的函数名,统计指令次数,输出函数名、调用发生时间、所属进程号、所属线程号、所属栈顶指针以及模块号,并保存到跟踪数据文件;[0011]若检测的指令是返回指令后,输出时间、所属进程号、所属线程号以及所属栈顶指针到跟踪数据文件;[0012]对所述跟踪数据文件进行处理获得预定标准的数据格式,将所述跟踪数据文件传输至静态函数调用关系生成装置中,生成描述函数调用关系的动态函数调用图。[0013]进一步地,所述跟踪数据文件的生成方法包括:[0014]通过对目标文件的解析获取函数入口地址对应的函数名、函数所在文件及行号的列表;[0015]解析操作系统内核符号表,获取函数入口地址和函数名,根据地址和函数名查找获取的函数所在文件及行号补全函数信息,并记录到函数列表数组中;[0016]读取模块符号表,获取模块的函数符号表,并记录到模块函数列表数组中;[0017]若给定的地址在函数列表数组中找到,则返回模块名和函数名;[0018]若给定的地址在模块函数列表数组中找到,则返回系统的模块名和函数名;[0019]若给定的地址在所述函数列表数组和所述模块函数列表数组均找不到,则判断函数是否在内存中存在,[0020]若在内存中存在,则从内存中读取内存地址,内存的内存地址之后一次读取符号地址判断与需查询地址是否一致,并记录内存位置,从记录位置读取内存信息为函数名索引地址,若查到则根据索引地址和符号地址起始地址读取符号名长度,计算出所查地址的偏移,获取函数名长度,进而返回函数名;[0021]若在内存中不存在,则在加载模块的符号表中读取相应的函数名,从模块中读取内存地址,通过模块地址获取模块的函数名。[0022]进一步地,对记录的跟踪数据文件的处理方法包括:补充函数调用关系并转化标准格式和数据上传至静态函数调用图生成装置,[0023]其中所述补充函数调用关系包括:根据调用和返回的进程号、线程号以及栈顶指针来把函数的调用和返回进行配对,同时获取函数的执行时间,同时把数据转化为标准格式;[0024]所述数据上传至静态函数调用图生成装置包括:[0025]按FROM字段提供的目录和文件名,把记录分别复制到相应源代码文件的动态函数调用关系文件中;[0026]按照每个动态函数调用关系文件中的TO字段判断该函数调用是否源自该文件,[0027]若不是该文件的则写入到对应源代码文件的动态函数对外调用关系文件,同时判断该文件是否源自该目录,若不是写入到对应目录的动态函数对外调用关系文件;[0028]根据最后一层目录的外调用关系文件逐层返回上一级目录填写上一级目录的外调用关系文件。[0029]进一步地,所述方法还包括显示所述函数调用关系图的步骤,以及对已生成的函数调用关系图和列表进行缓存的步骤。[0030]本发明,结合了函数调用图工具的动态工具能够将指定时间内的文件间、模块间调用关系通过连线的形式表示,能够发现运行时绑定的指针型函数调用以及可加载模块的函数调用。而且由于动态跟踪的方法给予调用与返回指令,能够跟踪系统启动一部分时间的函数调用关系,并且该工具还可以和其他动态函数跟踪工具结合,将其他工具的结果显/Jnο【专利附图】【附图说明】[0031]图1是本发明基于寄存器传输语言确定动态函数调用关系的方法获取函数名和模块名的流程图。[0032]图2是本发明基于寄存器传输语言确定动态函数调用关系的方法的数据获取算法流程图。【具体实施方式】[0033]下面结合说明书附图对本发明做进一步的描述。[0034]本实施例基于寄存器传输语言确定动态函数调用关系的方法,所述方法包括:[0035]在系统运行过程中检测函数调用执行事件,[0036]若检测到调用指令后,解析该指令调用的函数入口地址对应的函数名,统计指令次数,输出函数名、调用发生时间、所属进程号、所属线程号、所属栈顶指针以及模块号,并保存到跟踪数据文件;[0037]若检测的指令是返回指令后,输出时间、所属进程号、所属线程号以及所属栈顶指针到跟踪数据文件;[0038]对所述跟踪数据文件进行处理获得预定标准的数据格式,将所述跟踪数据文件传输至静态函数调用关系生成装置中,生成描述函数调用关系的动态函数调用图。[0039]所述跟踪数据文件的生成方法包括:[0040]通过对目标文件的解析获取函数入口地址对应的函数名、函数所在文件及行号的列表;[0041]解析操作系统内核(system,map)符号表,获取函数入口地址和函数名,根据地址和函数名查找获取的函数所在文件及行号补全函数信息,并记录到函数列表数组中;[0042]读取模块符号表,获取模块的函数符号表,并记录到模块函数列表数组中;[0043]若给定的地址在函数列表数组中找到,则返回模块名和函数名;[0044]若给定的地址在模块函数列表数组中找到,则返回系统的模块名(kernel)和函数名,函数名通过查表得到;[0045]若给定的地址在所述函数列表数组和所述模块函数列表数组均找不到,则判断函数是否在内存中存在,[0046]若在内存中存在,则从内存中读取内存地址,内存的内存地址之后一次读取符号地址判断与需查询地址是否一致,并记录内存位置,从记录位置读取内存信息为函数名索引地址,若查到则根据索引地址和符号地址起始地址读取符号名长度,计算出所查地址的偏移,获取函数名长度,进而返回函数名;[0047]若在内存中不存在,则在加载模块的符号表中读取相应的函数名,从模块中读取内存地址,通过模块地址获取模块的函数名。[0048]本实施例,利用内核函数调用检测插件(LinuxCallMonitor)获取函数调用过程中产生的调用和返回指令,然后通过获取的内核符号和解析elf文件得到的函数路径和行号信息与指令信息对应,获得更新后的记录。LinuxCallMonitor插件功能的实现依赖于内核符号表解析插件(SymbolResolver^PFunctionMonitor插件(虚拟机-S2E里提供了用于获取系统内函数调用指令的探测),通过注册块翻译事件之后可以使用CallSignal来检测函数调用执行事件,检测到调用指令后对该指令调用的函数入口地址解析出地址对应的函数名,统计指令次数,输出函数名、调用发生时间、所属进程号、所属线程号、所属栈顶指针以及模块号,并保存到跟踪数据文件。当检测的指令是返回指令时仅输出时间、所属进程号、所属线程号以及所属栈顶指针到跟踪数据文件[0049]利用SymbolResovler插件与内核编译时提供的模块符号表及解析操作系统可执行文件(vmlinux)elf格式获取函数名和函数所在文件行号,获得包含函数入口地址、函数名以及行号的符号表。虚拟机-S2E获取函数调用仅是函数入口地址,如何正确解析出地址对应的函数名是动态跟踪的关键问题。获取符号表的方式主要可以分为两种目标文件中的符号表和系统运行时内存中的符号表。目标文件中的符号表可分为三类:本文件中的全局符号、本文件引用了的其它文件中的全区符号以及本文件中的本地符号,对于这些符号表可以使用GNUbinutils提供的nm获取,但只能获取内核自身的符号表,对内核运行时加载的模块内的符号则无能为力,而S2E作为运行在主机上的虚拟机,我们可以通过从客户机的内存里直接获取和查找符号表。[0050]Linux2.6以后的版本引入了kallsyms功能,kallsyms是把内核中所用到的所有符号地址和名称连接进了内核文件,从而使得内核启动后会把kallsyms的信息加载到内存中方便内核调试。因此通过kallsyms机制的实现使得内存也存有一份System,map,因而可以利用kallsyms机制的原理来提取内核中的符号表,并且此表包含了系统启动后加载模块的符号信息。`[0051]对记录的跟踪数据文件的处理方法包括:补充函数调用关系并转化标准格式和数据上传至静态函数调用图生成装置,[0052]其中所述补充函数调用关系包括:根据调用和返回的进程号、线程号以及栈顶指针来把函数的调用和返回进行配对,同时获取函数的执行时间,同时把数据转化为标准格式;[0053]所述数据上传至静态函数调用图生成装置包括:[0054]按FROM字段提供的目录和文件名,把记录分别复制到相应源代码文件的动态函数调用关系文件dynfctrlt)中;[0055]按照每个动态函数调用关系文件(.dynfctrlt)文件中的TO字段函数是否源自该文件,[0056]若不是该文件的则写入到相应源代码文件的动态函数对外调用关系文件(.dynoutlst),同时判断该文件是否源自该目录,若不是写入到对应目录的动态函数对外调用关系文件CdynDoutlst);[0057]根据最后一层目录的动态函数对外调用关系文件(.dynDoutlst)文件逐层返回上一级目录填写上一级目录的.dynDoutlst文件。[0058]进一步地,所述方法还包括显示所述函数调用关系图的步骤,以及对已生成的函数调用关系图和列表进行缓存的步骤,该步骤通过浏览器实现。[0059]所述方法具体运行过程如下:[0060]第一步,环境要求以及必要的软件包的安装:[0061]1、主机环境ubuntu6412.0464位[0062]2、需要安装的包:[0063]$sudoapt-getinstallbuild-essential[0064]$sudoapt-getinstallsubversion[0065]$sudoapt-getinstallgit[0066]$sudoapt-getinstallgettext[0067]$sudoapt-getinstallliblua5.l_dev[0068]$sudoapt-getinstalllibsdll.2~dev[0069]$sudoapt-getinstalllibsigc++_2.0_dev[0070]$sudoapt-getinstallbinutils-dev[0071]$sudoapt-getinstallpython-docutils[0072]$sudoapt-getinstallpython-pygments[0073]$sudoapt-getinstallnasm[0074]$sudoapt-getbuild-depllvm-3.0[0075]$sudoapt-getbuild-depqemu[0076]3、下载s2e的安装包并安装s2e[0077]$mkdirS2EDIR[0078]$cd$S2EDIR[0079]$gitclonehttps://github.com/dslab_epfl/s2e.git[0080]$mkdirbuild[0081]$cdbuild[0082]$make-f/s2e/Makefile[0083]第二步,环境要求以及必要的软件包的安装:[0084]1、LinuxCallMonitor插件[0085]A、注册onTranslateBlockStart事件[0086]B、获取调用和返回指令[0087]C、调用符号表获取函数名、模块名[0088]D、输出调用事件包含函数名、模块名、事件发生时间、当前进程号、当前线程号以及当前栈顶指针[0089]具体内容如下:[0090]1.1初始化initialize[0091]装载SymbolResolver,FunctionMonitor:(s2e()->getPlugin(“,,));[0092]打开第一个写文件XXX-FuncTracer—00000.dat[0093]注册块翻译事件(onTranslateBlockStart)[0094]1.2s1tTransIateBlockStart事件[0095]通过CallSignal检测call执行事件,[0096]检测到call事件通过calIHandler处理该事件[0097]1.3calIHandler[0098]通过地址获取相应函数名:SymbolResolver中的symtab_search函数实现;[0099]同时计算call和ret事件的次数,如果次数超过20000000,则打开一个新的写文件,同时计数清零;[0100]注册ret执行的回调函数,检测函数的返回并处理返回信息retHandler;[0101]输出事件发生时间、进程号、线程号、栈顶指针、模块名、函数名。[0102]1.4retHandler[0103]计算calI和ret的次数,如果次数超过20000000,则打开一个新的写文件,同时计数清零;[0104]输出事件发生时间、进程号、线程号、栈顶指针。[0105]2、SymbolResolver插件[0106]A、获取内核、模块符号表[0107]B、解析vmlinux文件elf格式获取函数名和函数所在行号[0108]C、形成符号列表:地址,函数名,函数所在行号[0109]2.1初始化initialize[0110]加载system,map、vmlinux;[0111]调用reacLvmO获取函数入口地址对应的函数名、函数所在文件及行号的列表;[0112]读取system,map,解析该符号表,根据地址和函数名以及read_vm()获取的函数所在文件及行号,补全函数信息,并记录到函数列表数组中addrsym_tab[];根据符号信息记录符号表开始结束以及其它地址信息,主要有:—start—ksymtab、—stop—ksmtab、kallsyms_addresses(所有符号地址)、kallsyms_num_syms(符号总数)、kallsyms_names、kalIsyms_markers、kailsyms_token_table、kailsyms_token_index、modules,它们对应的地址信息。[0113]读取模块符号表,获取模块的函数符号表,并记录到数组中addrmod_tab[]。[0114]2.2symtab_search[0115]根据地址返回函数名、函数所在文件及行号。[0116]如果给定的地址在addrsym_tab[]找到,则返回kernel(模块名)和函数名;[0117]如果给定的地址在addrmod_tab[]找到,则返回模块名(系统)和函数名;[0118]否则:[0119]从内存中读取kallsyms_num_syms(符号总数);从存中kallsyms_addresses之后一次读取符号地址判断与需查询地址是否一致,并记录内存位置;从记录位置读取内存信息为函数名索引地址,如果查到则根据索引地址和符号地址起始地址读取符号名长度,计算出所查地址的偏移,获取函数名长度,进而返回函数名;否则在加载模块的符号表中读取相应的函数名[0120]2.3search_func_line、read_vm、describe_elf_hdr>dir_file_table>describe_one_section、describe_elf_sections>dwarf_tag_name>dwarf_attr_name>is_compile_unit、output_compiIation_unit_header>read_l_byte、read_signed_lebl28、describe_one—compile—unit等是解析vmlinux文件的elf格式获取函数入口地址以及对应函数名及函数所在文件行号的函数和依赖函数,这里不在叙述其原理和过程。[0121]第三步,编译s2e、运行s2e:[0122]1、修改Makefile,target[0123]2、修改s2e/qemu/Makefile.target文件:[0124]在s2eobj-y+=s2e/Plugins/ConsistencyModels.ο之后添加两行:[0125]s2eobj-y+=s2e/Plugins/LinuxCalIMonitor.ο[0126]s2eobj-y+=s2e/Plugins/SymboIeResoIver.ο[0127]3、编译s2e[0128]4、编译对应版本的内核,本项目的是linux-3.5.4,cpu是32位:即makei386—defconfig0[0129]为了获取函数定义行号,因而在编译内核时需要勾选debug信息:menuconfig中Kernelhacking选项,选中kerneldebugging和Compilekernelwithdebuginfo。[0130]编译完成后arch/x86/boot/bzIamge,vmliux,System,map,在s2e运行中需要用到的。[0131]5、配置config.1ua[0132]【权利要求】1.一种基于寄存器传输语言确定动态函数调用关系的方法,其特征在于:所述方法包括:在系统运行过程中检测函数调用执行事件,若检测到调用指令后,解析该指令调用的函数入口地址对应的函数名,统计指令次数,输出函数名、调用发生时间、所属进程号、所属线程号、所属栈顶指针以及模块号,并保存到跟踪数据文件;若检测的指令是返回指令后,输出时间、所属进程号、所属线程号以及所属栈顶指针到跟踪数据文件;对所述跟踪数据文件进行处理获得预定标准的数据格式,将所述跟踪数据文件传输至静态函数调用关系生成装置中,生成描述函数调用关系的动态函数调用图。2.根据权利要求1所述的基于寄存器传输语言确定动态函数调用关系的方法,其特征在于:所述跟踪数据文件的生成方法包括:通过对目标文件的解析获取函数入口地址对应的函数名、函数所在文件及行号的列表;解析操作系统内核符号表,获取函数入口地址和函数名,根据地址和函数名查找获取的函数所在文件及行号补全函数信息,并记录到函数列表数组中;读取模块符号表,获取模块的函数符号表,并记录到模块函数列表数组中;若给定的地址在函数列表数组中找到,则返回模块名和函数名;若给定的地址在模块函数列表数组中找到,则返回系统的模块名和函数名;若给定的地址在所述函数列表数组和所述模块函数列表数组均找不到,则判断函数是否在内存中存在,若在内存中存在,则从内存中读取内存地址,内存的内存地址之后一次读取符号地址判断与需查询地址是否一致,并记录内存位置,从记录位置读取内存信息为函数名索引地址,若查到则根据索引地址和符号地址起始地址读取符号名长度,计算出所查地址的偏移,获取函数名长度,进而返回函数名;若在内存中不存在,则在加载模块的符号表中读取相应的函数名,从模块中读取内存地址,通过模块地址获取模块的函数名。3.根据权利要求1所述的基于寄存器传输语言确定动态函数调用关系的方法,其特征在于:对记录的跟踪数据文件的处理方法包括:补充函数调用关系并转化标准格式和数据上传至静态函数调用图生成装置,其中所述补充函数调用关系包括:根据调用和返回的进程号、线程号以及栈顶指针来把函数的调用和返回进行配对,同时获取函数的执行时间,同时把数据转化为标准格式;所述数据上传至静态函数调用图生成装置包括:按FROM字段提供的目录和文件名,把记录分别复制到相应源代码文件的动态函数调用关系文件中;按照每个动态函数调用关系文件文件中的TO字段判断该函数调用是否源自该文件,若不是该文件的则写入到对应源代码文件的动态函数对外调用关系文件,同时判断该文件是否源自该目录,若不是写入到对应目录的动态函数对外调用关系文件;根据最后一层目录的动态函数对外调用关系文件逐层返回上一级目录填写上一级目录的动态函数对外调用关系文件。4.根据权利要求1所述的基于寄存器传输语言确定动态函数调用关系的方法,其特征在于:所述方法还包括显示所述函数调用关系图的步骤,以及对已生成的函数调用关系图和列表进行缓存的步骤。【文档编号】G06F9/44GK103761089SQ201410015881【公开日】2014年4月30日申请日期:2014年1月14日优先权日:2014年1月14日【发明者】向勇,汤卫东,杜香燕,孙卫真,马东超,邓雪峰申请人:清华大学
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1