一种用于对本地方法调用进行性能测试的方法和装置 【技术领域】
本发明涉及性能测试(profiling)技术,并且更具体地涉及一种用于对本地方法调用进行性能测试的方法和装置。
背景技术
性能测试技术是这样一种技术,其通过跟踪应用程序的执行过程来获得执行过程中的各种性能参数,以便定位程序性能瓶颈,从而有针对性地对应用程序进行优化。
本地方法是一种可以不经由虚拟机(VM,Virtual Machine)解释而直接在本地硬件平台上运行的方法。通常,可以使用本地方法用于直接操纵计算机硬件、提升程序执行性能,遗留代码重用等目的。众所周知的是,在诸如Java应用的应用中,本地方法调用是非常普遍的,其原因主要在于有许多功能都需要利用本地方法来实现。例如,像是用于分布式计算的对象序列化的I/O实现,在高速网络(多层次应用中的关键链路的)上的通信、Java虚拟机(JVM,Java VirtualMachine)框架,数学计算等等,他们的实现都要依赖于对本地方法的使用。
实践证明,在使用本地方法的情况下,应用瓶颈很大可能是存在于本地代码中或者出现在本地层。因此,对本地方法调用进行性能测试是应用程序性能测试中不可或缺的内容。
在现有技术中,已经存在几种对于本地函数进行性能测试的技术,其中有一种是字节码级别注入技术,还有一种是Java虚拟机工具接口(JVM Tool Interface,JVMTI)方法级别事件跟踪技术。
其中,字节码级别注入技术又可进一步分成静态注入方法和动态注入方法。静态注入方法是一种在应用执行之前对所有JDK类、所有应用类进行字节码注入的方法。由于静态字节码注入需要进行大工作量的类搜索,因此注入过程需要花费很长时间,尤其对于较大应用程序,所耗费的时间更长。另外,静态注入方法是在应用程序执行之前进行字节码注入,因此无法对应用程序执行过程中动态装载的库进行性能测试。此外,还存在另外一个令人无法接受的缺陷,即,在使用静态注入方法的情况下,需要维护两个类集合,一个是经注入版本的类集合,一个原始版本的类集合。
与静态注入方法不同,动态注入方法是一种在装载类的同时对类进行字节码注入的方法。然而,对于动态注入方法存在有若干技术约束。例如,有些调用方法代码是不允许被改变的,而还有些调用方法代码在开始进行字节码注入之前已经被加载,这些都造成无法对这些调用方法代码进行性能测试。另外,动态地对类进行这种修改,即进行字节码注入,会对类装载器以及动态(JIT,Just-In-Time)编译器的行为/性能都有很大影响,诸如,系统开销会显著增加,这也不是所期望的。
另一方面,在JVMTI方法级别事件跟踪技术中,提供了方法入口事件和方法出口事件的回调,因此可以利用JVMTI规范中定义的“IsMethodNative()”函数来检查一个方法是否是本地方法。这种方式与字节码级别注入相比,简单且易于实现。然而,由于JVMTI级别事件跟踪技术需要捕获应用程序运行过程中的所有方法并且执行对该方法的判断,因此使系统开销变得很大,速度甚至能下降100至1000倍,从而使系统性能受到显著影响。
除上述技术以外,还存在对本地方法调用进行性能测试的另外一种已知技术。诸如在现有的JVMTI中,提供了与本地方法调用关联的事件,即如下所示的本地方法绑定(NativeMethodBind)事件:
void JNICALL NativeMethodBind(jvmtiEnv*jvmti_env,JNIEnv*jni_env,jthread thread,jmethodID method,void*address,void**new_address_ptr)
本地方法绑定事件是指JVM将Java程序中的本地方法的定义和具体的本地方法代码的地址绑定在一起的事件,其通常是在本地方法第一次被调用的时候触发。每当启动上述本地方法绑定事件时,可通过该事件中定义的参数返回一些重要信息。例如,“thread”可以返回提出绑定要求的线程,“method”可以返回被绑定的方法,“address”可以返回被绑定的本地方法的地址。而且,如果“new_adress_ptr”被设置,则VM会将本地方法调用绑定到new_address_ptr所指定的代码地址上。
因此,可以从该事件得到被绑定的本地方法的地址address。并且如图1中所示,通过将新地址参数new_address_ptr设置为诸如性能测试代码的地址,就可以自动地将Java的输入输出文件流中本地方法的地址替换为性能测试代码的地址,从而执行性能测试代码,实现对本地方法调用的性能测试。
在使用这种机制时,需为每个本地方法设置一个关联代理或者包装器,以便执行性能测试任务和调用本地方法。在相关代理能够正确执行时,可以得到较好的结果,应用运行时对系统的性能影响也不大。然而,事实上每个本地方法的参数类型和返回类型都是不同地,因此要执行上述替换,每个关联代理必须具有与待进行性能测试的方法完全相同的签名。但是根据目前的技术,无法实现预测动态装载方法的签名,因此使得这种机制在实际上并不实用。
【发明内容】
有鉴于此,本发明的目的在于提供一种改进的用于对本地方法调用进行性能测试的方法和装置。
根据本发明的一个方面,提供了一种用于对本地方法调用进行性能测试的方法。该方法可以包括:响应于本地方法调用关联事件,由用于对本地方法调用进行性能测试的通用性能测试模板生成性能测试模板副本;将对所述本地方法调用进行性能测试所需的信息填写到所述性能测试模板副本中的相应位置;以及改变代码执行流程,以便执行所述性能测试模板副本。
根据本发明的另一方面,提供了一种用于对本地方法调用进行性能测试的装置,包括:副本生成装置,用于响应于本地方法调用关联事件,由用于对本地方法调用进行性能测试的通用性能测试模板生成性能测试模板副本;信息填写装置,用于将对所述本地方法调用进行性能测试所需的信息填写到所述性能测试模板副本中的相应位置;以及流程改变装置,用于改变代码执行流程,以便执行所述性能测试模板副本。
此外,本发明还可以通过一种计算机程序产品来实现。该计算机程序产品其上具有计算机程序代码,当该计算机程序代码在计算机上运行时,能够执行根据本发明的用于对本地方法调用进行性能测试的方法的步骤。
根据本发明,在本地方法被调用时,通过在汇编级别将性能测试模板副本动态地插入在执行路径中,来实现对本地方法的性能测试。因此,相对于现有技术而言,不需要针对每个本地方法提供关联代理,具有很高的可行性,并且几乎未对系统性能造成任何影响。
【附图说明】
通过结合附图对实施方式进行详细说明,本发明的上述以及其他特征将更加明显,在本发明附图中,相同的标号表示相同或相似的部件,其中:
图1是示出了JVMTI提供的本地方法绑定事中利用新地址参数替换本地方法地址的示意图;
图2示出根据本发明的实施例的用于对本地方法调用进行性能测试的方法的流程图;
图3示出了根据本发明的实施例在入口模板副本中执行的操作的流程图;
图4示出了根据本发明的实施例在出口模板副本中执行的操作的流程图;
图5示出了根据本发明的一个实施例的用于对本地方法调用进行性能测试的装置;以及
图6示出了其中可以实现根据本发明的实施例的计算机系统的示例性结构方框图。
【具体实施方式】
下面,将参考附图详细地描述本发明提供的用于对本地方法调用进行测试的方法和装置。
首先,将参考图2来描述根据本发明的方法的实施例。图2示出根据本发明一个实施例的用于对本地方法调用进行性能测试的方法的流程图。
如图2所示,在步骤201,响应于本地方法调用关联事件,由用于对本地方法调用进行性能测试的通用性能测试模板生成性能测试模板副本。
在Java虚拟机中,在调用方法对本地方法进行调用后,本地方法的地址将会被绑定,此时将会启动诸如上述本地方法绑定事件(NativeMethodBind)的本地方法调用关联事件。
需要说明的是,在本发明的上下文中将以本地方法绑定事件为例对本发明的实施例进行描述。然而,除了上述的本地方法绑定事件以外,其他任何能够提供本地方法的地址的其他本地方法调用关联事件也能够实现本发明的目的。另外,在除Java虚拟机之外存在本地方法调用的其他环境中,在调用本地方法时被触发的事件可以是与本地方法绑定事件类似的事件,或者是能够提供本地方法的地址的其他本地方法调用关联事件。
在本地方法调用关联事件被启动后,可以由用于对本地方法调用进行性能测试的通用性能测试模板生成性能测试模板副本。
根据本发明的方法,为所有的本地方法提供了用于对本地方法调用进行测试的通用性能测试模板。在本地方法调用关联事件启动时,从该通用性能测试模板复制出用于该本地方法调用的性能测试模板副本。性能测试模板副本是通用性能测试模板的一个拷贝,它们在形式上完全相同。不同的是,通用性能测试模板将被持续地存储,而性能测试模板副本专用于特定本地方法,在完成性能测试之后,存储性能测试模板副本的空间将被释放。
出于说明的目的,在下面给出了一个通用测试模板的实例。需要说明的是,下面给出的实例只是一个实例,本发明并不局限于此,而是可以具有多种变型。
通用性能测试模板的实例:
入口模板:
static unsigned char prologTemplate[]={
/*0*/0x50,0x52,0x53,0x8b,0x1d,0x00,0x00,0x00,
/*8*/0x00,0x85,0xdb,0x74,0x34,0x8b,0x44,0x24,
/*10*/0x0c,0xa3,0x00,0x00,0x00,0x00,0xb8,0x00,
/*18*/0x00,0x00,0x00,0x89,0x44,0x24,0x0c,0x0f,
/*20*/0x31,0xa3,0x00,0x00,0x00,0x00,0x89,0xd0,
/*28*/0xa3,0x00,0x00,0x00,0x00,0x68,0x00,0x00,
/*30*/0x00,0x00,0xbb,0x00,0x00,0x00,0x00,0xff,
/*38*/0x13,0x83,0xc4,0x04,0x5b,0x5a,0x58,0xe9,
/*40*/0xfc,0xff,0xff,0xff
};
出口模板:
static unsigned char epilogTemplate[]={
/*0*/0x6a,0x00,0x50,0x52,0x53,0x0f,0x31,0xa3,
/*8*/0x00,0x00,0x00,0x00,0x89,0xd0,0xa3,0x00,
/*10*/0x00,0x00,0x00,0xa1,0x00,0x00,0x00,0x00,
/*18*/0x89,0x44,0x24,0x0c,0x68,0x00,0x00,0x00,
/*20*/0x00,0xbb,0x00,0x00,0x00,0x00,0xff,0x13,
/*28*/0x83,0xc4,0x04,0x5b,0x5a,0x58,0xc3
};
在上面给出的实例中,示出了机器码级别的通用的入口模板和出口模板。为了使得该通用性能测试模板的实例更加容易理解,在下面的表1和表2中列出了与之对应的汇编级别代码和相应解释。
表1-入口模板的汇编代码及其相应解释
表2-出口模板的汇编代码及其相应说明
从上面给出的通用性能测试模板的实例和以及表1和表2可以看出,在通用性能测试模板中有若干个需要在本地方法被调用时确定的代码(以下划线示出),这些代码对应于汇编代码中的参数(以粗体示出)。关于这些待确定的代码和参数,以及通用测试模板副本的操作将在下文中更加详细地描述。
然后,在步骤202,将对所述本地方法调用进行性能测试所需的信息填写到所述性能测试模板副本中的相应位置。
在步骤201中由通用性能测试模板得到的性能测试副本是通用性能测试模板的一个拷贝,其中包括若干有待确定的代码。用于确定这些代码相关的信息即为对所述本地方法调用进行性能测试所需的信息,该信息包括:
-本地方法的地址,对应于表1中汇编代码的变量“target_ip”,其应设置为由诸如本地方法绑定时间返回的参数“address”的值。在性能测试模板副本中需调用本地方法,因此需要得到本地方法的地址。本地方法的地址由诸如本地方法绑定事件的本地方法调用关联事件提供。
-性能测试开关的地址,对应于表1中汇编代码的“active”。性能测试开关是用于确定是否执行性能测试的参数。可以为开发人员或者用户提供用户接口以便输入其所关注(或不关注)的本地方法,然后,可以在该步骤通过确定被调用的本地方法是不是用户关注的方法来为该开关赋值。这样,就可以在性能测试模板中,基于该性能测试开关的值来确定是否执行性能测试,使得开发人员或者用户能够仅对关注的本地方法进行性能测试,或者对不关心的方法不进行性能测试,从而为开发人员进行性能测试提供更大的便利性和灵活性。性能测试开关的地址是可选的,如果不以选择性的方式来执行性能测试,则不需要该参数。
-存储本地方法返回地址的地址,对应于表1中汇编代码的变量“caller_ip”。在需对本地方法调用进行出口性能测试的情况下,需要在完成本地方法调用后执行出口模板副本,因此需要用于保存本地方法的返回地址的空间,以便将该返回地址替换为出口模板副本的地址。存储本地方法返回地址的地址是可选的,在不进行出口性能测试时,则不需要该参数。
-性能测试模板副本的地址,尤其在需要出口性能测试的情况下,需要在入口模板副本中填写出口模板副本的地址,对应于表1中汇编代码的变量“epilog”,以便能在完成本地方法调用后执行出口性能测试。性能测试模板副本的地址是可选的,在不进行出口性能测试时,则不需要该参数。
-存储性能测试的各项信息(诸如,调用本地方法的开始时间和调用本地方法的结束时间,本地方法的调用次数等)的地址,如表1和表2所示汇编代码中的变量“timestamp_low”和“timestamp_high”,该地址指定了用于存储性能测试过程中得到的各项信息的空间的起始地址,可以在适当时间将存储的信息从该空间中导出。存储性能测试的各项信息的地址可以根据性能测试的需求而改变,在无需进行相应测试的情况下,可以不包括存储相应的信息的地址。
-自定义的入口性能测试方法的地址,对应于表1的汇编代码中的变量“callback”,该地址指定了存储自定义的入口性能测试方法的空间的首地址。自定义的入口性能测试方法的地址是可选的,在不进行自定义的入口性能测试的情况下,可以不包括该信息。
-自定义的出口性能测试方法的地址,对应于表2的汇编代码中的变量“callback”,该地址指定了存储自定义的出口性能测试方法的空间的首地址。自定义的出口性能测试方法的地址是可选的,在不进行自定义的出口性能测试的情况下,可以不包括该信息。
-本地方法的索引,用于识别不同的本地方法,对应于表1和2的汇编代码中的变量“index”,可以作为自定义的入口性能测试和/或自定义的出口性能测试的参数,以使得可以对不同的本地方法执行不同的性能测试。在无需对不同的本地方法执行不同性能测试的情况下,无需该索引。
在该步骤中,将上述信息填写到性能测试模板副本中对应的位置。诸如上述性能测试模板的带有下划线的位置。
需要说明的是,并非上述所有信息都是必要的,根据应用可以选择其中的一种或者多种。
然后,在步骤203,改变代码执行流程,以便执行所述性能测试模板副本。
如果要对本地方法调用进行性能测试,就需在调用本地方法之前和/或之后执行与性能测试相关的一些工作。因此,需要改变原来的代码执行流程,即在执行路径中插入性能测试相关代码,以便在执行本地方法的调用之前和/或之后执行性能测试模板副本。
在诸如JVM的情况下,如上所述,JVMTI提供的本地方法绑定(NativeMethodBind)事件具有将本地方法的地址(address)替换为新地址参数(new_address_ptr)的功能。因此,通过将新地址参数指定为本发明的性能测试模板副本的地址,尤其是与入口测试模板副本的地址,即可以实现代码执行流程的改变。
另外,可以在执行对本地方法的调用之前,通过将所述本地方法代码的最开始字节修改为跳转到所述性能测试模板副本的代码,以及在开始本地方法调用之前恢复所述本地方法代码的最开始字节,来改变代码执行流程。
在根据本发明的一个示例性实施例中,首先,复制所述本地方法代码的最开始字节并将其存储到适当的位置。最开始字节的数目至少等于实现跳转到所述性能测试模板副本所需的代码的字节数目,即,跳转指令所需的字节数与所述性能测试模板副本的起始地址的字节数之和。例如,对于32位的X86的CPU,跳转指令需要1个字节,地址需要4个字节(32位),因此至少复制最开始的5个字节。需要说明的是,对于不同的指令系统和地址位数不同的CPU,由于实现调转指令所需的字节数以及地址位数不同,该字节数目是不同的。然后,例如可以使用操作系统提供的已知的mprotect()系统调用来去除对本地方法所在页面的写保护。接着,将所述最开始字节修改为跳转到性能测试模板副本的代码。此后,可以在入口模板副本中,在调用本地方法之前,根据已预先保存的最开始字节来恢复本地方法代码的最开始字节。通过这样的方式,实现了对代码执行流程的改变。在本地方法的地址不允许被改变的情况下,这种方式是特别有利的。另外,在本地方法调用关联事件不能提供改变本地方法地址的功能时,可以通过该方法实现对代码执行流程的改变。
上述步骤202和203的操作可以通过如下所示以C/C++编程语言实现的包装器来完成。
包装器的实例
mt
JNIWrapper_Do(jvmtiEnv*jvmti,jmethodID method,void*address,
void**newAddress)
{
unsigned char*p;
TJNIRecord*pjc;
int i;
unsigned char str;//用于由用户指定所关心的方法名
i=idxJniFunc;
pjc=&JNIRecords[i];
pjc->addrJniFunc=(unsigned long)address;
//根据MethodID获得本地方法名称
err=(jvmtiError)jvmti->GetMethodName(methodID,
(char**)&pMethodName,
(char**)&pSignature,(char**)&pGeneric);
if(err==JVMTI_ERROR_NONE){
if(pMethodName!=NULL){
//如果方法名中包含变量str中的字符,则将性能测试开关打开
if(strstr(pMethodName,str)==NULL)return 0;
else{
sprintf(JNIRecords[idxJniFunc].name,″%s″,
pMethodName);
pjc->active=1;
}
//释放本地方法所占用的内存
jvmti->Deallocate((unsigned char*)pMethodName);
}
}
p=&pjc->prolog[0];
//复制入口模板
memcpy(p,prologTemplate,SIZE_OF_PROLOG);
//填写入口模板副本信息
//填写存储性能测试开关的地址
*(unsigned int*)(p+5)+=(unsigned int)(&pjc->active);
//填写用以保存返回地址的地址
*(unsigned int*)(p+18)+=(unsigned int)(&pjc->addrCaller);
//填写出口模板副本的地址
*(unsigned int*)(p+23)+=(unsigned int)(&pjc->epilog[0]);
//填写保存时间戳低32位的地址
*(unsigned int*)(p+34)+=(unsigned int)(&pjc->timeEnter);
//填写保存时间戳高32位的地址
*(unsigned int*)(p+41)+=(unsigned int)(&pjc->timeEnter)+4;
//填写本地方法的索引
*(unsigned int*)(p+46)+=i;
//填写自定义入口性能测试方法的地址
*(unsigned int*)(p+51)+=(int)(&pfCallbackEnterNative);
//填写本地方法的地址
*(unsigned int*)(p+64)+=(int)address-(int)(p+64);
p=&pjc->epilog[0];
//复制出口模板
memcpy(p,epilogTemplate,SIZE_OF_EPILOG);
//填写出口模板副本信息
//填写保存时间戳低32位的地址
*(unsigned int*)(p+8)+=(unsigned int)(&pjc->timeLeave);
//填写保存时间戳高32位的地址
*(unsigned int*)(p+15)+=(unsigned int)(&pjc->timeLeave)+4;
//填写保存返回地址的地址
*(unsigned int*)(p+20)+=(int)(&pjc->addrCaller);
//填写本地方法的索引
*(unsigned int*)(p+29)+=i;
//填写自定义出口性能测试方法的地址
*(unsigned int*)(p+34)+=(int)(&pfCallbackLeaveNative);
//改变代码执行流程
//将newAddress的值设置为入口模板副本的地址
*newAddress=&pjc->prolog[0];
return 0;
}
需要说明的是,上面给出的包装器只是一个实例,本发明并不局限于此,而是可以具有多种变型。例如可以先复制和填写出口模板副本,然后复制和填写入口模板副本,或者可以先复制入口模板和出口模板,然后填写入口模板副本和出口模板副本。另外根据应用的需求,需要填写的信息并不局限于上述信息,而是可以更多或者更少。这些变型都在本发明的范围内。
接下来,将参考图3和图4详细描述根据本发明的一个实施例在性能测试模板副本中执行的操作的实例。
图3示出了根据本发明的实施例在入口性能测试模板副本中执行的操作的流程。
如图3所示,在进入已填写的性能测试模板副本后,在步骤301,首先保存此刻的寄存器状态,诸如累加器状态、数据寄存器状态、基址寄存器状态等,以便为随后的与性能测试相关操作做准备,以使得性能测试不会对本地方法的调用产生影响。
接着,在步骤302,从存储性能测试开关的地址读出性能测试开关的值,并根据该开关值来确定性能测试开关是否打开。在本发明的一个实施例中,可以由开发人员通过用户接口输入所关心(或不关心)的本地函数名称,然后在填写信息时确定被调用的本地函数是否是用户输入的本地函数,并据此设置开关值。
如果确定性能测试开关打开,则过程进行至步骤303;否则,过程进行至步骤306。
然后,在步骤303,保存本地方法的返回地址,并替换该返回地址。
在本发明的一个实施例中,首先通过得到堆栈指针所在位置的内容来获得本地方法的返回地址,即调用该本地方法的调用方法的地址。然后,将其存储在上述步骤202中所填写的用于存储本地方法返回地址的地址处。接着,用在上述步骤202中填写的出口模板副本的地址,替换堆栈指针所在位置处的内容,以使得在本地函数调用执行完成后,执行该出口模板副本。
随后,在步骤304,记录本地方法调用的时间和次数。例如,可以得到当前CPU的内部定时器的值,以便得到调用该本地方法的开始时间。并且,可以记录该本地方法调用的次数。
接着,在步骤305,获得在步骤202中填写的自定义入口性能测试方法的地址,并且调用该自定义的入口性能测试方法。自定义的入口性能测试方法,诸如可以以C/C++语言来编写,例如用以记录访问存储器的次数或时间,访问网络的次数、流量或时间,或者CPU的高速缓存的命中率等。另外,还可以将本地方法的索引作为该自定义的性能测试方法的参数,以便实现对于不同的本地方法执行不同的入口性能测试。
接下来,在步骤306,将寄存器状态恢复到进入该性能测试模板副本时的状态,为执行对本地方法的调用做准备。
然后,在步骤307执行对本地方法的调用。
接下来参考图4,图4示出了根据本发明的实施例在出口性能测试模板副本中执行的操作的流程。由于在入口性能测试模板副本中,本地方法的返回地址被替换为出口模板副本的地址,因此在本地方法调用结束后将自动执行出口模板副本。
如图4所示,在进入出口模板副本之后,首先在步骤401,为恢复本地方法的返回地址(即,调用本地方法的调用方法的地址)而在栈中预留栈空间。
接着,在步骤402,保存寄存器状态,为随后与性能测试相关的操作做准备。
然后,在步骤403,得到当前CPU的内部定时器的值,以便得到调用该方法的结束时间。
接下来,可以将当前的堆栈指针所在位置,即预留的栈空间设置为调用该本地方法的调用方法的地址,即本地方法的原始返回地址。
此后,在步骤404,执行自定义的出口性能测试方法。与入口性能测试方法类似,出口性能测试方法可以以C/C++编写,可以用来记录访问存储器的次数或时间,访问网络的次数、流量或时间,或者CPU的高速缓存的命中率等。另外,也可以将本地方法的索引作为该自定义的性能测试方法的参数,以便实现对于不同的本地方法执行不同的出口性能测试。
接着,在步骤405,将寄存器状态恢复到本地方法调用结束时的状态,然后在步骤406执行返回,以便返回本地方法的调用方法。
根据上面描述的本发明的方法,通过在执行路径中动态地插入汇编级别的性能测试模板副本代码,可以容易地实现对本地方法调用的性能测试。而且,添加到执行路径中的机器指令数量较少,至多40条,因此对本地方法调用的性能测试不会对本地方法调用的执行产生明显影响。并且与现有技术的方法相比,也不会对诸如类装载器、JIT编译器等JVM组件造成影响。另外,在进行性能测试的过程中,也不会触发中断处理和系统调用,并且对系统性能的影响很小。
为了检验本发明的方法对于系统性能的影响,本发明人利用本发明提供的方法对标准性能评估机构(Standard PerformanceEvaluation Corporation,SPEC)发布的Java虚拟机基准程序SPECjvm2008中I/O操作最密集的应用Complier.compiler进行了性能测试,结果表明在没有进行性能测试的情况下,每秒操作数平均为515.45,而在执行了性能测试的情况下,每秒钟的平均操作数为504.68。由此可见,与现有技术相比,本发明的方法对于系统性能操作影响非常小。
在上文中,已经参考图2至图4、性能测试模板的实例、包装器实例等对本发明提供的方法进行了详细描述。应当说明的是,上述描述仅仅是出于说明的目的,本发明不仅限于此。根据应用需求,本发明的方法可以存在很多实施例。
在上面描述的实施例中,性能测试模板包括入口性能测试模板和出口性能测试模板,然而本发明并不局限于此。而是可以根据性能测试的需要来设计性能测试模板。在根据本发明的另一实施例中,只对本地方法调用进行入口性能测试,因此就无需复制出口性能测试模板。在这种情况下,可以省略执行保存本地方法的返回地址以及将本地方法的返回地址替换为出口模板副本的步骤。而在另外的实施例中,只对本地方法调用进行出口性能测试,这只需在本地方法调用之前执行保存本地方法的返回地址以及将其替换为出口模板副本的步骤。
另外,在根据本发明的又一实施例中,不存在自定义的入口性能测试方法和/或自定义的出口性能测试方法。可仅仅执行对本地方法调用次数的记录和/或对本地方法调用开始时间、结束时间的记录。并且,记录本地方法调用次数可以在入口模板副本中执行,也可以在出口模板副本中执行。此外,在执行性能测试不会改变寄存器状态时,可以不用执行保存和恢复寄存器状态的步骤。
另外,在上述本发明的实施例中,是在入口模板副本中判定性能测试开关的值来确定是否执行性能测试。然而,本领域技术人员应该理解的是,也可以在诸如本地方法绑定事件的本地方法关联时间启动后,首先确定性能测试开关的值。在确定改性能开关打开时,才执行随后的复制性能测试副本和填写信息等步骤。另外,也可以在填写信息的步骤中,首先确定性能测试开关的值,在确定该性能测试开关打开时,才执行随后的填写以及改变代码执行流程的步骤。
需要说明的上述的这些变型以及本领域技术人员能够设想的其他变型都属于本发明的范围。
下面,将参考图5来描述本发明的装置的实施例。图5示出了根据本发明的一个实施例的用于对本地方法调用进行性能测试的装置500。
如图5所示,装置500包括:副本生成装置501,用于响应于本地方法调用关联事件,由用于对本地方法调用进行性能测试的通用性能测试模板生成性能测试模板副本;信息填写装置502,用于将对所述本地方法调用进行性能测试所需的信息填写到所述性能测试模板副本中的相应位置;以及流程改变装置503,用于改变代码执行流程,以便执行所述性能测试模板副本。
在根据本发明的一个实施例中,所述性能测试模板副本可以包括在开始所述本地方法调用之前进行性能测试的入口模板副本。该入口模板副本用于执行对所述本地方法调用的入口性能测试;以及调用所述本地方法。
在根据本发明的另一实施例中,所述入口模板副本可以进一步用于:保存寄存器状态;对性能测试开关进行判定、记录本地方法调用的次数以及执行自定义的入口性能测试方法中的一种或多种;以及恢复寄存器状态。
在根据本发明的又一实施例中,所述性能测试模板副本可以进一步包括在完成本地方法调用后进行性能测试的出口模板副本。在该实施例中,所述本地方法调用的返回地址在执行本地方法调用之前被替换为所述出口模板副本的地址,以便在完成本地方法的调用后执行所述出口模板副本。在该实施例中,所述出口模板副本可以用于执行对本地方法调用的出口性能测试以及恢复所述本地方法调用的返回地址,以便在执行所述出口模板副本之后,返回至调用所述本地方法的调用方法。
在根据本发明的再一实施例中,所述入口模板副本可以进一步用于记录所述本地方法调用的开始时间,所述出口模板副本可以进一步用于记录所述本法方法调用的结束时间。
在根据本发明的另一实施例中,所述出口模板副本可以进一步用于保存寄存器状态;执行自定义的出口性能测试方法;以及恢复寄存器状态。
在根据本发明的又一实施例中,对所述本地方法调用进行性能测试所需的信息可以包括以下一个或多个:本地方法的地址;存储本地方法的返回地址的地址;性能测试模板副本的地址;性能测试开关值的地址;存储性能测试的各项信息的地址;用于本地方法的索引;自定义的入口性能测试方法的地址;以及自定义的出口性能测试方法的地址。
在根据本发明的又一实施例中,所述本地方法调用关联事件可以是Java虚拟机工具接口所提供的本地方法绑定事件。在该实施例的情况下,所述流程改变装置503可以用于通过将所述本地方法绑定事件的新地址参数设定为所述性能测试模板副本的地址,来改变代码执行流程。
在根据本发明的再一实施例中,所述流程改变装置503可以用于通过将所述本地方法的代码的最开始字节修改为至所述性能测试模板副本的跳转,以及在开始本地方法调用之前恢复所述本地方法的代码的最开始字节,来改变代码执行流程。
关于上述实施方式中的副本生成装置501、信息填写装置502以及流程改变装置503的具体操作,可以参考上面结合2至图4对于本发明的方法的描述。
根据本发明的装置,在本地方法被调用时,通过在汇编级别动态地在执行路径中插入性能测试模板副本,来实现对本地方法的性能测试。因此,相对于现有技术而言,本发明提供的装置不需要针对每个本地方法提供关联代理,具有很高的可行性,并且几乎未对系统性能造成任何影响。
下面,将参考图6来描述可以实现本发明的计算机设备。图6示意性示出了可以实现根据本发明的实施方式的计算设备的结构方框图。
图6中所示的计算机系统包括CPU(中央处理单元)601、RAM(随机存取存储器)602、ROM(只读存储器)603、系统总线604、硬盘控制器605、键盘控制器606、串行接口控制器607、并行接口控制器608、显示器控制器609、硬盘610、键盘611、串行外部设备612、并行外部设备613和显示器614。在这些部件中,与系统总线604相连的有CPU 601、RAM 602、ROM 603、硬盘控制器605、键盘控制器606、串行接口控制器607、并行接口控制器608和显示器控制器609。硬盘610与硬盘控制器605相连,键盘611与键盘控制器606相连,串行外部设备612与串行接口控制器607相连,并行外部设备613与并行接口控制器608相连,以及显示器614与显示器控制器609相连。
图6所述的结构方框图仅仅为了示例的目的而示出的,并非是对本发明的限制。在一些情况下,可以根据需要添加或者减少其中的一些设备。
此外,本发明的实施方式可以以软件、硬件或者软件和硬件的结合来实现。硬件部分可以利用专用逻辑来实现;软件部分可以存储在存储器中,由适当的指令执行系统,例如微处理器或者专用设计硬件来执行。
虽然已经参考目前考虑到的实施方式描述了本发明,但是应该理解本发明不限于所公开的实施方式。相反,本发明旨在涵盖所附权利要求的精神和范围内所包括的各种修改和等同布置。以下权利要求的范围符合最广泛解释,以便包含所有这样的修改及等同结构和功能。