性能瓶颈的定位方法、定位装置、电子设备及存储介质与流程

文档序号:21408018发布日期:2020-07-07 14:42阅读:217来源:国知局
性能瓶颈的定位方法、定位装置、电子设备及存储介质与流程

本申请涉及数据处理领域,尤其涉及一种性能瓶颈的定位方法、定位装置、电子设备及存储介质。



背景技术:

游戏开发中,出于运行性能与开发效率的综合考虑,通常会采用多种语言结合的方式,比如一些游戏引擎底层均采用了c++实现底层框架,而上层业务逻辑则采用各种灵活的脚本语言,通过将各种语言结合以达到软件运行性能与开发效率的完美兼顾。

在实际开发中,很容易因为一些特殊的业务需求或代码的缺陷导致代码产生性能瓶颈,然而现有的工具仅能定位到c/c++函数级别的性能瓶颈,没有办法定位到脚本语言代码级别的性能瓶颈。要定位到代码级别的性能瓶颈,一般的处理方法是通过不断缩小代码范围,最终定位到瓶颈处,这种方案成本高,需要技术人员花费大量的时间挨个排查,工作量大,容易出错。



技术实现要素:

本申请提供一种性能瓶颈的定位方法、定位装置、电子设备及存储介质,以实现多种语言混合使用场景下的性能瓶颈的定位。

为实现上述目的,本申请提供的技术方案如下:

本申请提供一种性能瓶颈的定位方法,其包括:

获取待测试的目标进程的寄存器信息,得到所述目标进程内过程的源代码名称,所述目标进程包括多个过程,至少两个所述过程的函数语言不同,所述寄存器信息包括用于确定所述过程对应所述源代码名称的参数;

展示所述过程的源代码名称以及耗时占比,根据所述耗时占比确定性能瓶颈对应的目标过程的目标源代码名称,并使光标位于所述目标源代码名称上;

在检测到分析指令时,进行所述目标源代码名称与虚拟机主函数名称的匹配;

在所述源代码名称与所述虚拟机的主函数名称相匹配时,分析所述虚拟机的调用栈,获取所述虚拟机的状态机信息;

根据所述虚拟机的状态机信息,获取所述虚拟机的主函数对应的子函数的源码信息;

展示所述子函数的源码信息以及耗时占比,并确定性能瓶颈对应的子函数信息。

一种性能瓶颈的定位装置,其包括:

第一获取模块,用于获取待测试的目标进程的寄存器信息,得到所述目标进程内过程的源代码名称,所述目标进程包括多个过程,至少两个所述过程的函数语言不同,所述寄存器信息包括用于确定所述过程对应所述源代码名称的参数;

第一展示模块,用于展示所述过程的源代码名称以及耗时占比,同时用于根据所述耗时占比确定性能瓶颈对应的目标过程的目标源代码名称,并使光标位于所述目标源代码名称上;

检测模块,用于检测分析指令;

匹配模块,用于匹配所述目标源代码名称与虚拟机主函数名称;

第二获取模块,用于分析所述虚拟机的调用栈,获取所述虚拟机的状态机信息;

第三获取模块,用于获取所述虚拟机的主函数对应的子函数的源码信息;

第二展示模块,用于展示所述子函数的源码信息、以及所述子函数的耗时占比,同时用于确定性能瓶颈对应的子函数信息。

一种电子设备,其包括:

存储器,用于存储程序;

处理器,用于执行所述程序,实现上所述性能瓶颈的定位方法的各个步骤。

一种存储介质,其存储计算机程序,所述计算机程序使得计算机执行上述性能瓶颈的定位方法中的各个步骤。

本申请实施例提供了一种性能瓶颈的定位方法、定位装置、电子设备及计算机可读存储介质,通过获取待测试的目标进程的寄存器信息,得到目标进程内过程的源代码名称,并与过程的耗时占比一同展示,得到c/c++函数级别的性能瓶颈分析定位;通过进一步的分析虚拟机的调用栈,获取虚拟机的状态机信息,进而获取虚拟机的主函数对应的子函数的源码信息,得到脚本语言代码级别的性能瓶颈定位分析;实现了多种语言混合使用场景下,同时定位到c/c++函数级别和脚本语言代码级别的性能瓶颈方案,且该方案由计算机运行,简单快速、准确率高、成本低。

附图说明

下面结合附图,通过对本申请的具体实施方式详细描述,将使本申请的技术方案及其它有益效果显而易见。

图1为本申请实施例所提供的性能瓶颈的定位系统的场景示意图。

图2为本申请实施例提供的性能瓶颈的定位方法的第一种流程示意图。

图3为本申请实施例提供的性能瓶颈的定位方法的第二种流程示意图。

图4为本申请实施例提供的性能瓶颈的定位方法的第三种流程示意图。

图5为本申请实施例提供的性能瓶颈的定位方法的第四种流程示意图。

图6为本申请实施例提供的性能瓶颈的定位方法的第一页面。

图7为本申请实施例提供的x86-64架构下栈帧的内存布局图。

图8为本申请实施例提供的寄存器和参数的关系图。

图9为本申请实施例提供的性能瓶颈的定位方法的映射关系图。

图10为本申请实施例提供的性能瓶颈的定位方法的第二页面。

图11为本申请实施例提供的性能瓶颈的定位装置的结构框图。

图12为本申请实施例提供的电子设备的结构示意图。

具体实施方式

下面将结合本申请的具体实施方案,对本申请实施方案和/或实施例中的技术方案进行清楚、完整的描述,显而易见的,下面所描述的实施方案和/或实施例仅仅是本申请一部分实施方案和/或实施例,而不是全部的实施方案和/或实施例。基于本申请中的实施方案和/或实施例,本领域普通技术人员在没有做出创造性劳动前提下所获得的所有其他实施方案和/或实施例,都属于本申请保护范围。

本申请所提到的术语“第一”、“第二”等仅用于描述目的,而不能理解为指示或是暗示其相对重要性或者隐含指明所指示的技术特征的数量。由此,限定有“第一”、“第二”等的特征可以明示或者隐含地包括一个或者更多个该特征。

为了解决多种语言混合使用场景下,性能瓶颈定位只能定位到c/c++函数级别,无法定位到脚本语言代码级别的问题,本申请提供了一种性能瓶颈的定位方法,能够同时定位到c/c++函数级别和脚本语言的代码级别,为多种语言混合使用场景下的性能瓶颈定位提供了可行方案。

为了方便对本申请方案进行理解,首先对本申请涉及到的一些概念进行解释说明:

性能瓶颈:执行最耗时的代码块,软件的性能缺陷。

进程:一个具有一定独立功能的程序关于某个数据集合的一次运行活动,即运行中的应用程序,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。每个独立的进程有一个程序运行的入口、顺序执行序列和程序的出口,这就是程序中的主函数。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(textregion)、数据区域(dataregion)和堆栈(stackregion)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。

c/c++:一种静态编译型程序设计语言,有高性能、操作底层的能力,集高级语言和低级语言的功能于一体,实现了对硬件的编程操作,既可用于系统软件的开发,也适合于应用软件的开发。

脚本语言:又被称为扩建的语言,或者动态语言,是一种为了缩短传统的编写-编译-链接-运行过程而创建的计算机编程语言,用来控制软件应用程序,脚本通常以文本保存,只在被调用时进行解释或编译。

lua:一种动态解释型程序设计语言,具有使用灵活、代码量小等优点。该语言的设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。lua脚本可以很容易的被c/c++代码调用,也可以反过来调用c/c++的函数,这使得lua在应用程序中可以被广泛应用。不仅仅作为扩展脚本,也可以作为普通的配置文件,并且更容易理解和维护。

虚拟机:解释执行脚本语言的解释器。

源代码:是指一系列人类可读的计算机语言指令。源代码是相对目标代码和可执行代码而言的,源代码是指用汇编语言和高级语言写出来的地代码,目标代码是指源代码经过编译程序产生的能被cpu直接识别的二进制代码,可执行代码就是将目标代码连接后形成的二进制的可执行文件。

ptrace:linux/unix上的一种系统调用,可以用于调试跟踪进程,读写指定进程的内存。ptrace提供了一种使父进程得以监视和控制其它进程的方式,它还能够改变子进程中的寄存器和内核映像,因而可以实现断点调试和系统调用的跟踪。

libunwind:一个第三方库,可以扩展被挂接进程当前的栈帧,并获得当前进程的寄存器信息。libunwind库为基于64位cpu和操作系统的程序提供了基本的堆栈辗转开解功能,其中包括用于输出堆栈跟踪的接口、用于以编程方式辗转开解堆栈的接口、以及支持c++异常处理机制的接口。libunwind库不适用32位操作系统。

unwind:一个第三方库,用于获取程序的调用栈。

寄存器:cpu(中央处理器)中存储数据的器件,是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和地址。在x86体系汇编中,寄存器可以分为通用寄存器、变址寄存器、指令指针寄存器、标志寄存器与段寄存器等几类。

通用寄存器,于传送和暂存数据,也可参与算术逻辑运算,并保存运算结果,包括:rdi(destinationindexregister,目的变址寄存器),与res段寄存器联用,可以访问附加段中的任一个存储单元,是函数调用时的第1个参数;rsi(sourceindexregister,源变址寄存器),与rds段寄存器联用,可以访问数据段中的任一个存储单元,是函数调用时的第2个参数;rdx(i/o指针),i/o操作时提供外部设备接口的端口地址,是函数调用时的第3个参数;rcx(countregister,计数寄存器)循环操作和字串处理的计数控制,函数调用时的第4个参数;r8,函数调用时的第5个参数;r9,函数调用时的第6个参数。

变址寄存器,主要用于存放存储单元在段内的偏移量,实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便,包括:rsi(sourceindexregister,源变址寄存器),用来存放相对于rds段之源变址指针;rdi(destinationindexregister,目的变址寄存器),用来存放在对于res段之目的变址指针。

指令指针寄存器,主要用于存放堆栈内存储单元的偏移量,包括:rsp(stackpointerregister,堆栈指针寄存器),提供堆栈栈顶单元的偏移地址,与rss段寄存器联用,以控制数据进栈和出栈;rbp(basepointerregister,基址指针寄存器),用于提供堆栈内某个单元的偏移地址,与rss段寄存器联用,可以访问堆栈中的任一个存储单元,被调用者保存;rip(instructionpointerregister,指令指针寄存器),指向指令地址的段内偏移量,用于控制程序中指令的执行顺序,一般情况下,每从内存中存取一次指令码,rip就自动加1,从而保证指令的顺序执行,rip实际上是指令机器码存放内存单元的地址指针。

标志寄存器用于存放反映处理器和进程执行结果状态的控制标志和条件码标志。

段寄存器是根据内存分段的管理模式而设置的,和一个偏移量组合成内存单元的物理地址由,包括:rcs(codesegmentregister,代码段寄存器),存放代码段的起始地址;rds(datasegmentregister,数据段寄存器),存放数据段的起始地址;res(extrasegmentregister,附加段寄存器),存放附加段的起始地址;rss(stacksegmentregister,堆栈段寄存器),存放堆栈段的起始地址。

栈帧:也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构,描述了正在执行的函数信息:函数参数、函数的局部变量、函数执行完后返回地址等等。

调用栈:存储有关正在运行的过程的信息的栈。

指针:一般指向一个函数或一个变量,是一个用来指示是一个内存地址的计算机语言的变量或中央处理器中的寄存器。

callinfo:lua虚拟机的状态机结构中的调用数组。

func;一个功能函数,用于调用一个有自己的局部变量的函数返回到调用进程。

closure:闭包,指的是一个拥有许多变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这些变量也是该表达式的一部分。通俗的讲是指运行中的信息集合。

proto:隐式原型,是指创建对象的构造函数。

函数:一组计算机指令。

ncurses:一套提供字符终端用户交互的第三方编程库,它提供了一系列的函数以便使用者调用它们去生成基于文本的用户界面。简单说就是纯字符界面下的ui(userinterface,用户界面)库,用于响应按键输入以及内容展示。

请参阅图1,图1为本申请实施例所提供的性能瓶颈的定位系统的场景示意图,该系统可以包括用户侧设备以及服务侧设备,用户侧设备与服务侧设备通过各种网关组成的互联网等方式连接,不再赘述,其中,用户侧设备包括终端11,服务侧设备包括服务器12;其中:

终端11包括但不局限于手机、平板等便携终端,以及电脑、查询机、广告机等固定终端,是用户可以使用并操作的服务端口,在本申请中,终端为用户提供性能瓶颈定位分析的页面展示等功能,同时还能提供性能瓶颈的定位分析功能;

服务器12包括处理器和存储器,存储器存储程序运行的各种指令,指令适于处理器进行加载,以执行本申请实施例所述的性能瓶颈的定位方法的各个步骤。

需要说明的是,图1所示的系统场景示意图仅仅是一个示例,本申请实施例描述的服务器以及场景是为了更加清楚的说明本申请实施例的技术方案,并不构成对于本申请实施例提供的技术方案的限定,本领域普通技术人员可知,随着系统的演变和新业务场景的出现,本申请实施例提供的技术方案对于类似的技术问题,同样适用。

请参照图2,图2示出了本申请实施例提供的性能瓶颈的定位方法的第一种流程示意图,本申请实施例所提供的定位方法可以应用于上述电子设备中,电子设备包括服务器和终端,该电子设备安装有需要进行性能瓶颈定位的应用程序,该定位方法包括:

步骤s201、获取待测试的目标进程的寄存器信息,得到目标进程内过程的源代码名称,目标进程包括多个过程,至少两个过程的函数语言不同,寄存器信息包括用于确定过程对应源代码名称的参数。

其中,待测试的目标进程为需要定位其性能瓶颈的进程,目标进程又包括多个不同的子进程,每个子进程又包括自己的函数语言、代码信息、以及堆栈,子进程共同使用目标进程的上下文环境。由于本申请的实施例所针对的语言环境,为至少两种不同语言混合使用的语言环境,因此目标进程中,存在至少两个过程的函数语言为不同种类的函数语言。

在一种实施例中,获取待测试的目标进程的寄存器信息,得到目标进程内过程的源代码名称的步骤,包括:

确定待测试的目标进程,将跟踪调试工具以定时采样的方式挂接到目标进程上;跟踪调试工具用于调试跟踪目标进程,读写目标进程的内存。

通过栈桢展开库,扩展被挂接的目标进程当前的栈桢,获取目标进程当前的寄存器信息;这些寄存器信息包括cpu的指令指针寄存器、目的变址寄存器、堆栈指针寄存器等寄存器内目标进程的相关信息,其中指针指令寄存器指向各个过程的地址,该地址即为对应过程的二进制函数标识名。

将各个过程的二进制函数标识名转换为源代码名称;二进制函数标识名就是一个地址信息,是源代码经过编译程序产生的能够被cpu直接识别的二进制代码,但是二进制函数标识名属于计算机可识别信息,不是用户可识别信息,因此需要将其转换为用户可识别的源代码信息,即将过程的二进制函数标识名转换为用户可识别的源代码名称,该转换过程可以通过上述栈桢展开库进行。

在本实施例中,通过跟踪调试工具以定时采样的方式挂接到目标进程中,以固定的间隔,去采样目标进程的运行信息,当采样足够多、间隔足够小时,便能保证性能瓶颈的准确性;通过栈桢展开库获取目标进程的寄存器信息,并将各个过程的二进制函数标识名转换为源代码名称,为c++函数级别性能瓶颈定位分析做好了准备。

步骤s202、展示过程的源代码名称以及耗时占比,根据耗时占比确定性能瓶颈对应的目标过程的目标源代码名称,并使光标位于目标源代码名称上。

在一种实施例中,展示过程的源代码名称以及耗时占比,根据耗时占比确定性能瓶颈对应的目标过程的目标源代码名称,并使光标位于目标源代码名称上的步骤,包括:

展示第一页面,第一页面显示各个过程的源代码名称、以及过程的耗时占比;将各个过程的源代码名称和耗时占比进行匹配后进行显示,在第一页面上,过程的源代码名称,可以按照过程耗时占比由大到小的顺序,从上到下排列;。

确定耗时占比最大的源代码名称,为性能瓶颈对应的目标过程的目标源代码名称;当第一页面上,过程的源代码名称按照过程耗时占比由大到小的顺序,从上到下排列时,位于第一位的源代码名称即为性能瓶颈对应的目标过程的目标源代码名称。

使光标位于目标源代码名称上。

在本实施例中,得到展示c++函数级别性能瓶颈定位分析的第一页面,至此完成了对目标进程c/c++函数级别的性能瓶颈定位。

步骤s203、在检测到分析指令时,进行目标源代码名称与虚拟机主函数名称的匹配。

其中,分析指令由用户发出,用于触发目标源代码名称与虚拟机主函数名称的匹配操作。该分析指令可以是当光标位于目标源代码名称上时,用户点击键盘右键发出的指令。

进行目标源代码名称与虚拟机主函数名称的匹配具体为,将目标源代码名称的字符与虚拟机主函数名称的字符进行逐一匹配;若目标源代码名称的所有字符均与虚拟机主函数名称的字符相匹配,则目标源代码名称与虚拟机主函数名称相匹配;若目标源代码名称的字符中存在至少一个,均与虚拟机主函数名称的字符不匹配,则目标源代码名称与虚拟机主函数名称不匹配。

步骤s204、在源代码名称与虚拟机的主函数名称相匹配时,分析虚拟机的调用栈,获取虚拟机的状态机信息。

在源代码名称与虚拟机的主函数名称相匹配时,基于虚拟机的主函数,展开虚拟机的调用栈进行分析,获取目的变址寄存器的参数,得到虚拟机的状态机的指针信息,然后进行下一步的分析工作。

步骤s205、根据虚拟机的状态机信息,获取虚拟机的主函数对应的子函数的源码信息。

在一种实施例中,根据虚拟机的状态机信息,获取虚拟机的主函数对应的子函数的源码信息的步骤,包括:

基于虚拟机的状态机函数,获取虚拟机的状态机的调用信息;

根据虚拟机的状态机的调用信息,通过映射关系,定位到完整的源码信息,源码信息包括子函数的文件名、起始行和结束行信息。

在本实施例中,根据上一步骤中获取到的虚拟机的状态机的指针信息,通过映射关系,获取到虚拟机的主函数对应的子函数的源码信息,将性能瓶颈对应的目标过程进一步分析到了脚本代码级别。

步骤s206、展示子函数的源码信息以及耗时占比,并确定性能瓶颈对应的子函数信息。

在一种实施例中,展示子函数的源码信息以及耗时占比,并确定性能瓶颈对应的子函数信息的步骤,包括:

展示第二页面,第二页面显示子函数的文件名、起始行和结束行信息,以及子函数的耗时占比,子函数的文件名、起始行和结束行信息,按耗时占比由大到小的顺序,从上到下排列,与步骤s202中展示过程的源代码名称以及耗时占比相类似。

确定排在第一位的子函数为性能瓶颈对应的子函数,该子函数显示的函数信息即为性能瓶颈对应的子函数信息。

在本实施中,通过进一步的分析展示,将性能瓶颈定位到脚本代码级别的子函数,通过展示的子函数信息就能够准确知道进程的性能瓶颈所在,从而进行调优。

至此,整个目标进程的性能瓶颈定位完成,本申请实施例提供的性能瓶颈的定位方法,既能够定位到c/c++函数级别的性能瓶颈,又能够进一步定位到脚本语言代码级别的性能瓶颈,为多种语言混合使用场景下的性能瓶颈定位提供了可行方案。

请参照图3,图3示出了本申请实施例提供的性能瓶颈的定位方法的第二种流程示意图,本申请实施例所提供的定位方法可以应用于上述电子设备中,电子设备包括服务器和终端,该电子设备安装有需要进行性能瓶颈定位的应用程序,该定位方法包括:

步骤s301、获取待测试的目标进程的寄存器信息,得到目标进程内过程的源代码名称,目标进程包括多个过程,至少两个过程的函数语言不同,寄存器信息包括用于确定过程对应源代码名称的参数。

步骤s302、展示过程的源代码名称以及耗时占比,根据耗时占比确定性能瓶颈对应的目标过程的目标源代码名称,并使光标位于目标源代码名称上。

步骤s303、在检测到分析指令时,进行目标源代码名称与虚拟机主函数名称的匹配。

步骤s304、在源代码名称与虚拟机的主函数名称相匹配时,分析虚拟机的调用栈,获取虚拟机的状态机信息。

步骤s305、根据虚拟机的状态机信息,获取虚拟机的主函数对应的子函数的源码信息。

步骤s306、展示子函数的源码信息以及耗时占比,并确定性能瓶颈对应的子函数信息。

步骤s307、在收到返回指令时,重新展示过程的源代码名称以及耗时占比,并重新确定性能瓶颈对应的目标过程的目标源代码名称。

返回指令由用户发出,用于触发c++函数级别性能瓶颈定位分析。该返回指令可以是用户点击键盘左键发出的指令。

在一种实施例中,在收到返回指令时,重新展示过程的源代码名称以及耗时占比,并重新确定性能瓶颈对应的目标过程的目标源代码名称的步骤,包括:

在收到返回指令时,重新获取目标进程的寄存器信息,并将过程的二进制函数标识名转换为源代码名称;具体的实现方式与步骤s201中相类似,可参照步骤s201,在此不做赘述。

重新展示过程的源代码名称以及耗时占比,确定性能瓶颈对应的目标过程的目标源代码名称,并使光标位于目标源代码名称上;具体的实现方式与步骤s202中相类似,可参照步骤s202,在此不做赘述。

在本实施中,通过对目标进程进行第一步的性能瓶颈定位分析,使目标进程的性能瓶颈定位到c/c++函数级别,通过对性能瓶颈对应的过程进行进一步的性能瓶颈定位,将性能瓶颈定位到脚本代码级别的子函数,通过返回操作又可以再次将性能瓶颈定位到c/c++函数级别,完成了c/c++函数和脚本语言混合使用情况下的性能瓶颈定位,且满足了对c/c++函数级别的性能瓶颈定位分析页面,和脚本代码级别子函数的性能瓶颈定位分析页面的切换展示,使用户能够随时掌握目标进程的性能瓶颈定位。

为了便于理解,下面以c++语言和lua脚本语言混合使用、且运行环境为终端的场景为例,对本申请实施例提供的性能瓶颈的定位方法进行示意性说明。请参照图4,图4示出了本申请实施例提供的性能瓶颈的定位方法的第三种流程示意图,该定位方法包括:

步骤s401、展示进程列表,确定目标进程。

终端将所有的进程以列表的方式展示出来,并根据用户选择确定待测试的目标进程。

步骤s402、将ptrace以定时采样的方式挂接到目标进程上。

目标进程包括多个过程,其中,至少一个过程的函数语言为c++语言,至少一个过程的函数语言为lua脚本语言。

ptrace本身是个非常复杂的系统调用,用于调试跟踪目标进程,读写目标进程的内存。其函数的原型是longptrace(enum_ptrace_requestrequest,pid_tpid,void*addr,void*data),挂接到目标进程上时,传入ptrace_attach作为第一个参数,传入目标进程的id作为第二个参数。对于windows环境下,ptrace可以使用etw(eventtraceforforwindows)代替,可以实现跨平台。

步骤s403、通过libunwind,扩展被挂接的目标进程当前的栈桢,获取目标进程当前的寄存器信息,并将过程的二进制函数标识名转换为源代码名称。

这些寄存器信息包括指令指针寄存器、目的变址寄存器、堆栈指针寄存器等寄存器内目标进程的相关信息,其中指针指令寄存器指向各个过程的地址,该地址即为对应过程的二进制函数标识名。libunwind获取目标进程当前的寄存器信息,并将过程的二进制函数标识名转换为源代码名称可以通过以下几个函数实现,步骤包括:

通过函数

unw_addr_space_tunw_create_addr_space(unw_accessors_t*ap,intbyteorder),创建初始化unwind地址空间;

通过函数

intunw_init_remote(unw_cursor_t*c,unw_addr_space_tas,void*arg),初始化unwind指针

通过函数

intunw_get_reg(unw_cursor_t*cp,unw_regnum_treg,unw_word_t*valp),读取unwind指针所指向的寄存器信息;

通过函数

intunw_get_proc_name(unw_cursor_t*cp,char*bufp,size_tlen,unw_word_t*offp),将unwind指针所指向的指令指针寄存器的地址信息转换为编译后的c++代码并返回,即返回unwind指针所指向的过程的二进制函数标识名;

通过函数abi::_cxa_demangle将编译后的二进制函数名称转为编译前的函数名称,即源代码名称。

步骤s404、展示过程的源代码名称以及耗时占比,确定性能瓶颈对应的目标过程的目标源代码名称,并使光标位于目标源代码名称上。

请参照图6,图6示出了本申请实施例提供的性能瓶颈的定位方法的第一页面,第一页面显示各个过程的源代码名称、以及过程的耗时占比。在第一页面上,第一列显示各个过程的耗时占比,第二列显示各个过程的源代码名称,过程的源代码名称,可以按照过程耗时占比由大到小的顺序,从上到下排列。第一页面显示有第一过程的耗时占比95.61%及其源代码名称luav_exeucte,第二过程的耗时占比4.28%及其源代码名称luav_tonumber,第三过程的耗时占比0.06%及其源代码名称heropart::ontick(),第四过程的耗时占比0.05%及其源代码名称skillpart::ontick()。第一页面上的显示信息可以通过ncurses进行实时刷新。

确定耗时占比最大的源代码名称,为性能瓶颈对应的目标过程的目标源代码名称。即确定排在第一位的第一过程的源代码名称luav_exeucte,为性能瓶颈对应的目标过程的目标源代码名称,并使光标位于目标源代码名称luav_exeucte上。

至此,完整了c/c++函数级别的性能瓶颈定位。

步骤s405、分析lua虚拟机的调用栈,获取lua虚拟机的状态机信息。

当光标位于luav_exeucte上,用户点击键盘右键时,就会触发目标源代码名称luav_exeucte分析指令。当检测到目标源代码名称luav_exeucte分析指令时,将目标源代码名称luav_exeucte的字符与lua虚拟机主函数名称的字符进行逐一匹配,lua虚拟机主函数名称为luav_exeucte,其字符与目标源代码名称luav_exeucte完全匹配,则判定目标过程对应lua虚拟机主函数,进而展开lua虚拟机的分析。lua虚拟机信息存放在lua虚拟机的状态机lua_state结构中,lua虚拟机的状态机lua_state结构可以通过目的变址寄存器rdi获取得到。

请参照图7和图8,图7示出了在x86-64架构下栈帧的内存布局,其中参数n-1、参数n、以及返回地址为调用者的栈桢信息,rbp、局部变量1、局部变量2、rsp为被调用者的栈桢信息;图8示出了寄存器和参数的关系,其中l目的变址寄存器rdi的参数,也是函数调用的第一个参数,在lua的主函数中,有且仅有一个参数—目的变址寄存器rdi的参数。基于lua虚拟机的主函数,展开lua虚拟机的调用栈进行lua虚拟机分析,获取目的变址寄存器rdi的参数l,参数l为lua虚拟机的状态机lua_state的指针信息,该指针信息指向lua虚拟机的状态机lua_state结构,然后进行下一步的分析工作。

步骤s406、根据lua虚拟机的状态机lua_state信息,进一步获取lua虚拟机的主函数对应的子函数的源码信息。

请参照图9,示出了lua虚拟机的状态机lua_state结构、调用信息的callinfo结构与源码信息的proto结构之间的映射关系。首先,基于lua虚拟机的状态机lua_state的函数,获取lua虚拟机的状态机lua_state结构,从而获取lua虚拟机的状态机lua_state的调用信息ci;ci指向一个callinfo栈,确定指向callinfo栈内当前正在运行的子函数;展开当前子函数的callinfo结构,获取到func函数信息;根据func函数信息,得到闭包closure,运行中的信息集合;根据closure,最终定位到proto结构,获取到proto结构中的子函数的文件名,起始行,结束行信息。

步骤s407、展示子函数的源码信息以及耗时占比,并确定性能瓶颈对应的子函数信息。

请参照图10,图10示出了本申请实施例提供的性能瓶颈的定位方法的第二页面,第二页面显示各个子函数的文件名、起始行和结束行信息,以及各个子函数的耗时占比。在第二页面上,第一列显示各个子函数的耗时占比,第二列显示各个子函数的文件名、起始行和结束行信息,子函数的文件名、起始行和结束行信息,按耗时占比由大到小的顺序,从上到下排列。第一页面显示有第一子函数的耗时占比99.58%,第一子函数的文件名@task.lua、起始行5、结束行10,以及第二子函数的耗时占比0.42%,第二子函数的文件名@active.lua、起始行5、结束行10。第二页面上的显示信息可以通过ncurses进行实时刷新。

确定耗时占比最大的子函数,为性能瓶颈对应的子函数,性能瓶颈对应的子函数的文件名为@task.lua,起始行为第5行,结束行为10行。

至此,完整了对lua脚本语言性能瓶颈的定位,并最终将性能瓶颈定位到了lua脚本语言的代码级别。如果没有得到正确的结果,可能是没有调试进程的权限,可以进行权限添加,或者可能不是x86_64类型的cpu,可根据各cpu官方手册去查找对应的栈帧布局进行相应的改动。

步骤s408、在收到返回指令时,重新展示过程的源代码名称以及耗时占比,并确定性能瓶颈对应的目标过程的目标源代码名称。

返回指令由用户发出,用于触发页面切换,由第二页面切换为第一页面,重新回到c/c++函数级别的性能瓶颈定位页面,重新展示过程的源代码名称以及耗时占比,并确定性能瓶颈对应的目标过程的目标源代码名称。该返回指令可以是用户点击键盘左键发出的指令。

本实施例提供的性能瓶颈的定位方法,既能够定位到c/c++函数级别的性能瓶颈,又能够进一步定位到lua脚本语言代码级别的性能瓶颈,快速准确的完成了c/c++函数语言与lua脚本语言混合使用场景下的性能瓶颈定位。除了c/c++函数语言与lua脚本语言混合使用的场景,本申请提供的性能瓶颈的定位方法同样适用于c/c++与python的混合使用、c/c++与javascript的混合使用等。

为了便于理解,下面以c++语言和lua脚本语言混合使用、且电子设备为服务器的场景为例,对本申请实施例提供的性能瓶颈的定位方法进行示意性说明。请参照图5,图5示出了本申请实施例提供的性能瓶颈的定位方法的第四种流程示意图,该定位方法包括:

s501:发送性能瓶颈定位指令

终端向服务器发送性能瓶颈定位指令,要求对在服务器上运行的程序进行性能瓶颈定位。

s502:发送程序列表

服务器向终端发送在服务器上运行的所有程序的列表。

s503:发送第一性能瓶颈定位分析请求。

终端根据用户选择确定待测试的目标进程,并向服务器发送对目标进程的性能瓶颈定位请求。

s504:发送第一性能瓶颈定位分析结果。

服务器对目标进程进行c/c++函数级别的性能瓶颈定位。

首先,将ptrace以定时采样的方式挂接到目标进程上。目标进程包括多个过程,其中,至少一个过程的函数语言为c++语言,至少一个过程的函数语言为lua脚本语言。

ptrace本身是个非常复杂的系统调用,用于调试跟踪目标进程,读写目标进程的内存。其函数的原型是longptrace(enum_ptrace_requestrequest,pid_tpid,void*addr,void*data),挂接到目标进程上时,传入ptrace_attach作为第一个参数,传入目标进程的id作为第二个参数。对于windows环境下,ptrace可以使用etw(eventtraceforforwindows)代替,可以实现跨平台。

然后,通过libunwind,扩展被挂接的目标进程当前的栈桢,获取目标进程当前的寄存器信息,并将过程的二进制函数标识名转换为源代码名称。

这些寄存器信息包括指令指针寄存器、目的变址寄存器、堆栈指针寄存器等寄存器内目标进程的相关信息,其中指针指令寄存器指向各个过程的地址,该地址即为对应过程的二进制函数标识名。libunwind获取目标进程当前的寄存器信息,并将过程的二进制函数标识名转换为源代码名称可以通过以下几个函数实现,包括:

通过函数

unw_addr_space_tunw_create_addr_space(unw_accessors_t*ap,intbyteorder),创建初始化unwind地址空间;

通过函数

intunw_init_remote(unw_cursor_t*c,unw_addr_space_tas,void*arg),初始化unwind指针

通过函数

intunw_get_reg(unw_cursor_t*cp,unw_regnum_treg,unw_word_t*valp),读取unwind指针所指向的寄存器信息;

通过函数

intunw_get_proc_name(unw_cursor_t*cp,char*bufp,size_tlen,unw_word_t*offp),将unwind指针所指向的指令指针寄存器的地址信息转换为编译后的c++代码并返回,即返回unwind指针所指向的过程的二进制函数标识名;

通过函数abi::_cxa_demangle将编译后的二进制函数名称转为编译前的函数名称,即源代码名称。

最后,将获取到的过程的源代码名称及耗时占比发送给终端。

s505:展示第一性能瓶颈定位分析结果。

请参照图6,图6示出了本申请实施例提供的性能瓶颈的定位方法的第一页面,第一页面显示各个过程的源代码名称、以及过程的耗时占比。在第一页面上,第一列显示各个过程的耗时占比,第二列显示各个过程的源代码名称,过程的源代码名称,可以按照过程耗时占比由大到小的顺序,从上到下排列。第一页面显示有第一过程的耗时占比95.61%及其源代码名称luav_exeucte,第二过程的耗时占比4.28%及其源代码名称luav_tonumber,第三过程的耗时占比0.06%及其源代码名称heropart::ontick(),第四过程的耗时占比0.05%及其源代码名称skillpart::ontick()。第一页面上的显示信息可以通过ncurses进行实时刷新。

确定耗时占比最大的源代码名称,为性能瓶颈对应的目标过程的目标源代码名称。即确定排在第一位的第一过程的源代码名称luav_exeucte,为性能瓶颈对应的目标过程的目标源代码名称,并使光标位于目标源代码名称luav_exeucte上。

至此,完整了c/c++函数级别的性能瓶颈定位。

s506:发送第二性能瓶颈定位分析请求。

当光标位于luav_exeucte上,用户点击键盘右键时,就会触发目标源代码名称luav_exeucte分析指令。当终端检测到目标源代码名称luav_exeucte分析指令时,就会向服务器发送对luav_exeucte的进一步分析请求,即第二性能瓶颈定位分析请求。

s507:发送第二性能瓶颈定位分析结果。

当服务器接收到对luav_exeucte进一步的性能瓶颈定位分析请求时,就会触发目标源代码名称luav_exeucte与lua虚拟机主函数名称匹配,将目标源代码名称luav_exeucte的字符与lua虚拟机主函数名称的字符进行逐一匹配,lua虚拟机主函数名称为luav_exeucte,其字符与目标源代码名称luav_exeucte完全匹配,判定目标过程对应lua虚拟机主函数,进而展开lua虚拟机的分析。

lua虚拟机信息存放在lua虚拟机的状态机lua_state结构中,lua虚拟机的状态机lua_state结构可以通过目的变址寄存器rdi获取得到。请参照图7和图8,图7示出了在x86-64架构下栈帧的内存布局,其中参数n-1、参数n、以及返回地址为调用者的栈桢信息,rbp、局部变量1、局部变量2、rsp为被调用者的栈桢信息;图8示出了寄存器和参数的关系,其中l目的变址寄存器rdi的参数,也是函数调用的第一个参数,在lua的主函数中,有且仅有一个参数—目的变址寄存器rdi的参数。

基于lua虚拟机的主函数,展开lua虚拟机的调用栈进行lua虚拟机分析,获取目的变址寄存器rdi的参数l,参数l为lua虚拟机的状态机lua_state的指针信息,该指针信息指向lua虚拟机的状态机lua_state结构,然后进行下一步的分析工作。

请参照图9;图9示出了lua虚拟机的状态机lua_state结构、调用信息的callinfo结构与源码信息的proto结构之间的映射关系。

基于lua虚拟机的状态机lua_state的函数,获取lua虚拟机的状态机lua_state结构,从而获取lua虚拟机的状态机lua_state的调用信息ci;ci指向一个callinfo栈,确定指向callinfo栈内当前正在运行的子函数;展开当前子函数的callinfo结构,获取到func函数信息;根据func函数信息,得到闭包closure,运行中的信息集合;根据closure,最终定位到proto结构,获取到proto结构中的子函数的文件名,起始行,结束行信息。

最后,将获取到的子函数的文件名、起始行、结束行信息、以及子函数的耗时占比发送给终端。

s508:展示第二性能瓶颈定位分析结果。

请参照图10,图10示出了本申请实施例提供的性能瓶颈的定位方法的第二页面,第二页面显示各个子函数的文件名、起始行和结束行信息,以及各个子函数的耗时占比。在第二页面上,第一列显示各个子函数的耗时占比,第二列显示各个子函数的文件名、起始行和结束行信息,子函数的文件名、起始行和结束行信息,按耗时占比由大到小的顺序,从上到下排列。第一页面显示有第一子函数的耗时占比99.58%,第一子函数的文件名@task.lua、起始行5、结束行10,以及第二子函数的耗时占比0.42%,第二子函数的文件名@active.lua、起始行5、结束行10。第二页面上的显示信息可以通过ncurses进行实时刷新。

确定耗时占比最大的子函数,为性能瓶颈对应的子函数,性能瓶颈对应的子函数的文件名为@task.lua,起始行为第5行,结束行为10行。

至此,完整了对lua脚本语言性能瓶颈的定位,并最终将性能瓶颈定位到了lua脚本语言的代码级别。如果没有得到正确的结果,可能是没有调试进程的权限,可以进行权限添加,或者可能不是x86_64类型的cpu,可根据各cpu官方手册去查找对应的栈帧布局进行相应的改动。

本实施例提供的性能瓶颈的定位方法,既能够定位到c/c++函数级别的性能瓶颈,又能够进一步定位到lua脚本语言代码级别的性能瓶颈,快速准确的完成了c/c++函数语言与lua脚本语言混合使用场景下的性能瓶颈定位。除了c/c++函数语言与lua脚本语言混合使用的场景,本申请提供的性能瓶颈的定位方法同样适用于c/c++与python的混合使用、c/c++与javascript的混合使用等。

快速定位性能瓶颈,有利于尽早发现隐藏在软件中的设计或实现缺陷,为最终产品提供流畅的优越体验。本申请提供的性能瓶颈的定位方法通过获取待测试的目标进程的寄存器信息,将过程的二进制函数标识名转换为源代码名称,并与过程的耗时占比一同展示,得到c/c++函数级别的性能瓶颈分析定位;通过进一步的分析虚拟机的调用栈,获取虚拟机的状态机信息,进而获取虚拟机的主函数对应的子函数的源码信息,得到脚本语言代码级别的性能瓶颈定位分析;实现了多种语言混合使用场景下,同时定位到c/c++函数级别和脚本语言代码级别的性能瓶颈方案,且该方案由计算机运行,简单快速、准确率高、成本低。

同时,本申请实施例还提供一种性能瓶颈的定位装置,定位装置设置在电子设备中,用于执行本申请实施例提供的性能瓶颈的定位方法。请参照图11,图11示出了本申请实施例提供的性能瓶颈的定位装置的结构框图,该定位装置包括:

第一获取模块1101,用于获取待测试的目标进程的寄存器信息,得到目标进程内过程的源代码名称,目标进程包括多个过程,至少两个过程的函数语言不同,寄存器信息包括用于确定过程对应源代码名称的参数;

第一展示模块1102,用于展示过程的源代码名称、以及过程的耗时占比,同时用于根据耗时占比确定性能瓶颈对应的目标过程的目标源代码名称,并使光标位于所述目标源代码名称上;

检测模块1103,用于检测分析指令;

匹配模块1104,用于匹配目标源代码名称与虚拟机主函数名称;

第二获取模块1105,用于分析虚拟机的调用栈,获取虚拟机的状态机信息;

第三获取模块1106,用于获取虚拟机的主函数对应的子函数的源码信息;

第二展示模块1107,用于展示子函数的源码信息、以及子函数的耗时占比,同时用于确定性能瓶颈对应的子函数信息。

本申请实施例提供的性能瓶颈的定位装置,通过获取待测试的目标进程的寄存器信息,得到目标进程内过程的源代码名称,并与过程的耗时占比一同展示,得到c/c++函数级别的性能瓶颈分析定位;通过进一步的分析虚拟机的调用栈,获取虚拟机的状态机信息,进而获取虚拟机的主函数对应的子函数的源码信息,得到脚本语言代码级别的性能瓶颈定位分析;实现了多种语言混合使用场景下,同时定位到c/c++函数级别和脚本语言代码级别的性能瓶颈方案。

本申请实施例还提供一种电子设备,请参阅图12,如图12示出了本申请实施例所涉及的电子设备的结构示意图,具体来讲:

该电子设备可以包括一个或者一个以上处理核心的处理器1201、一个或一个以上计算机可读存储介质的存储器1202、电源1403和输入子模块1204等部件。本领域技术人员可以理解,图12中示出的电子设备结构并不构成对电子设备的限定,可以包括比图示更多或更少的部件,或者组合某些部件,或者不同的部件布置。其中:

处理器1201是该电子设置的控制中心,利用各种接口和线路连接整个服务器的各个部分,通过运行或执行存储在存储器1202内的软件程序和/或模块,以及调用存储在存储器1202内的数据,执行电子设备的各种功能和处理数据,从而对电子设备进行整体监控。可选的,处理器1201可包括一个或多个处理核心;优选的,处理器1201可集成应用处理器和调制解调处理器,其中,应用处理器主要处理操作系统、用户界面和应用程序等,调制解调处理器主要处理无线通信。可以理解的是,上述调制解调处理器也可以不集成到处理器1201中。

存储器1202可用于存储软件程序以及模块,处理器1401通过运行存储在存储器1202的软件程序以及模块,从而执行各种功能应用以及数据处理。存储器1202可主要包括存储程序区和存储数据区,其中,存储程序区可存储操作系统、至少一个功能所需的应用程序(比如声音播放功能、图像播放功能等)等;存储数据区可存储根据电子设备的使用所创建的数据等。此外,存储器1202可以包括高速随机存取存储器,还可以包括非易失性存储器,例如至少一个磁盘存储器件、闪存器件、或其他易失性固态存储器件。相应地,存储器1202还可以包括存储器控制器,以提供处理器1201对存储器1202的访问。

电子设备还包括给各个部件供电的电源1203,优选的,电源1203可以通过电源管理系统与处理器1201逻辑相连,从而通过电源管理系统实现管理充电、放电、以及功耗管理等功能。电源1203还可以包括一个或一个以上的直流或交流电源、再充电系统、电源故障检测电路、电源转换器或者逆变器、电源状态指示器等任意组件。

该电子设备还可包括输入子模块1204,该输入子模块1204可用于接收输入的数字或字符信息,以及产生与用户设置以及功能控制有关的键盘、鼠标、操作杆、光学或者轨迹球信号输入。

尽管未示出,电子设备还可以包括显示子模块等,在此不再赘述。具体在本申请实施例中,电子设备中的处理器1201会按照如下的指令,将一个或一个以上的应用程序的进程对应的可执行文件加载到存储器1202中,并由处理器1201来运行存储在存储器1202中的应用程序,从而实现各种功能,如下:

获取待测试的目标进程的寄存器信息,得到所述目标进程内过程的源代码名称,目标进程包括多个过程,至少两个过程的函数语言不同,寄存器信息包括用于确定所述过程对应所述源代码名称的参数;

展示过程的源代码名称以及耗时占比,根据耗时占比确定性能瓶颈对应的目标过程的目标源代码名称,并使光标位于目标源代码名称上;

在检测到分析指令时,进行目标源代码名称与虚拟机主函数名称的匹配;

在源代码名称与虚拟机的主函数名称相匹配时,分析虚拟机的调用栈,获取虚拟机的状态机信息;

根据虚拟机的状态机信息,获取虚拟机的主函数对应的子函数的源码信息;

展示子函数的源码信息以及耗时占比,并确定性能瓶颈对应的子函数信息。

以上各个操作的具体实施可参见前面的实施例,在此不再赘述。

在本申请实施例提供的电子设备,存储器中存储有定位目标进程的性能瓶颈的程序,处理器可以执行这一程序,实现本申请实施例所提供的任意一种定位方法中的步骤,取得本申请实施例所提供的任一种方法所能实现的有益效果,具体实现方式详见前面的实施例,在此不再赘述。

本领域普通技术人员可以理解,上述实施例的各种方法中的全部或部分步骤可以通过指令来完成,或通过指令控制相关的硬件来完成,该指令可以存储于一存储介质中,并由处理器进行加载和执行。

为此,本申请实施例还提供一种存储介质,其中存储有计算机程序,该计算机程序使得计算机执行本申请实施例所提供的任意一种定位方法中的步骤。例如,该指令可以执行如下步骤:

获取待测试的目标进程的寄存器信息,得到目标进程内过程的源代码名称,目标进程包括多个过程,至少两个过程的函数语言不同,寄存器信息包括用于确定过程对应源代码名称的参数;

展示过程的源代码名称以及耗时占比,根据耗时占比确定性能瓶颈对应的目标过程的目标源代码名称,并使光标位于目标源代码名称上;

在检测到分析指令时,进行目标源代码名称与虚拟机主函数名称的匹配;

在源代码名称与虚拟机的主函数名称相匹配时,分析虚拟机的调用栈,获取虚拟机的状态机信息;

根据虚拟机的状态机信息,获取虚拟机的主函数对应的子函数的源码信息;

展示子函数的源码信息以及耗时占比,并确定性能瓶颈对应的子函数信息。

其中,该计算机可读存储介质可以包括:只读存储器(rom,readonlymemory)、随机存取记忆体(ram,randomaccessmemory)、磁盘或光盘等。

在本申请实施例提供的存储介质存储有计算机程序,该计算机程序使得计算机执行本申请实施例所提供的任意一种定位方法中的步骤,取得本申请实施例所提供的任一种方法所能实现的有益效果,具体实现方式详见前面的实施例,在此不再赘述。

以上对本申请实施例所提供的一种性能瓶颈的定位方法、定位装置、电子设备及存储介质进行了详细介绍,本文中应用了具体个例对本申请的原理及实施方式进行了阐述,以上实施例的说明只是用于帮助理解本申请的方法及其核心思想;同时,对于本领域的技术人员,依据本申请的思想,在具体实施方式及应用范围上均会有改变之处,综上所述,本说明书内容不应理解为对本申请的限制。

当前第1页1 2 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1