一种Android程序的执行路径的还原方法与流程

文档序号:12550585阅读:364来源:国知局

本发明涉及一种Android程序的执行路径的还原方法,属于程序分析和测试领域。



背景技术:

Android是一个以Linux为基础的开源移动设备操作系统,主要用于智能手机和平板电脑,由Google成立的Open Handset Alliance(OHA,开放手持设备联盟)持续领导与开发中。Android以其开放性受到广大软件开发者的喜爱,众多新颖别致的Android应用纷至沓来。为了保证Android能够对用户产生持续的吸引力,开发者需要及时地发现并解决Android的性能问题。在分发测试版的Android给用户使用的过程中,记录详细的程序流日志数据会影响用户使用,阻碍应用测试进程;减少日志的记录内容又会损失必要的分析数据,降低问题诊断的效率,因此如何通过制定恰当的规则对应用的程序流日志进行编码,是Android测试的重要支撑技术之一。

软件测试主要包括4个步骤:1)选择恰当的编码规则根据应用的程序流生成日志记录的代码;2)在应用程序中插入指定的日志记录代码;3)将插桩后的应用分发给用户使用;4)收集、分析应用产生的程序流日志,还原出现性能问题的场景,找到并解决性能问题。在步骤3)中,应用在用户使用的过程中执行步骤1)中制定的编码规则,产生程序流的编码结果。步骤4)根据程序流的编码结果和程序流的结构还原出应用程序执行的路径,从而找到问题的根源所在。制定应用的程序流编码的方法又称为路径还原。

现有的路径还原方法主要有B.L.(Ball和Larus)和PAP(Profiling All Path)。其中,对于n条无环路径,B.L.的编码范围为整数区间[0,n-1],但在处理包含多次执行次数的有环路径时,该算法将有环路径拆分成多个无环子路径,由多个无环子路径的编码组成有环路径的编码,产生了巨大的存储开销;PAP借助乘法与加法区分同一个代码块的不同入边,利用不同入边区分不同路径,从而还原无环路径和包含多次执行次数的有环路径,但随着被还原程序的执行,编码的数值不断增大,PAP需要记录包含编码值和代码块唯一标识的断点以表示一次完整的执行路径,记录代码块唯一标识降低了存储效率,产生了较大开销。此外,上述两种方案都采用了固定模式的编码方案,没有根据不同路径执行的概率调整编码方案,造成执行概率高的路径使用较长的编码,执行概率低的路径使用较短的编码。



技术实现要素:

本发明的目的是提供一种Android程序的执行路径的还原方法,以降低日志存储开销、减少对程序功能的影响以及提高程序测试效率。

为实现上述目的,本发明所采取的技术方案是:

本发明的Android程序的执行路径的还原方法包括以下步骤:

(1)对Android程序进行分析与插桩,包括:

1)将Android程序包转化成Android虚拟机字节码文件;

2)从Android虚拟机字节码文件中得到Android程序组件的字节码文件;

3)根据Android生命周期函数对Android程序组件的字节码文件分块,生成Android程序的生命周期控制流文件;

4)从Android程序的生命周期控制流文件中得到用户自定义函数,并根据Android虚拟机字节码语法对用户自定义函数分块,生成用户自定义函数的控制流文件;

5)从Android程序的生命周期控制流文件和用户自定义函数的控制流文件中得到Android程序的初始代码块、Android程序的终止代码块、所有多出边起点代码块、所有多出边终点代码块,从当前出边概率模型文件中得到所有多出边起点代码块的各条出边的执行概率;

6)若当前出边概率模型文件为初始时的出边概率模型文件,则先将当前出边概率模型文件中的所有多出边起点代码块的各条出边的概率设定为相等后再执行步骤7),否则,直接执行步骤7);

7)利用编码算法得到所有多出边起点代码块、所有多出边终点代码块、Android程序的初始代码块以及Android程序的终止代码块的插桩内容;

8)依据所有多出边起点代码块、所有多出边终点代码块、Android程序的初始代码块以及Android程序的终止代码块的插桩内容,对Android虚拟机字节码文件进行插桩;

9)将插桩后的Android虚拟机字节码文件打包成Android程序包;

(2)用户安装Android程序包;

(3)依据用户使用Android程序产生的程序控制流日志文件,还原Android程序的执行路径,包括以下步骤:

(i)从程序控制流日志文件中得到Android程序的执行路径编码数组;

(ii)根据Android程序的执行路径编码数组、Android程序的生命周期控制流文件、以及用户自定义函数的控制流文件,利用解码算法还原Android程序的执行路径;

(iii)根据还原的Android程序的执行路径,统计执行路径中所有多出边起点代码块的出边的执行频率,从而计算出每条出边的执行概率,更新当前出边概率模型文件。

进一步地,本发明所述步骤7)执行有以下内容:

在Android程序的初始代码块中最后一句代码之后,生成插桩内容,该插桩内容按先后顺序含有第一代码、第二代码,所述第一代码用于输出表示当前Android程序开始执行的信号,所述第二代码用于将当前编码区间初始化;

在Android程序中的所有多出边起点代码块中的最后一句代码之后,分别生成同样的插桩内容,各插桩内容含有第三代码,各所述第三代码用于将其所在的多出边起点代码块配置为该多出边起点代码块的所有出边的起点;

在Android程序中的所有多出边终点代码块中的第一句代码之前,分别生成同样的插桩内容,各插桩内容按先后顺序依次包含以下代码:

a)第四代码,所述第四代码用于将其所在的多出边终点代码块配置为该多出边终点代码块的所有入边的终点;

b)第五代码,所述第五代码用于将其所在的多出边终点代码块的Android程序执行时经过的入边配置为该多出边终点代码块的当前边;

c)第六代码,所述第六代码用于从当前出边概率模型文件中检索出该第六代码所在的多出边终点代码块的当前边的执行概率;

d)第七代码,所述第七代码用于将当前编码区间更新为从当前出边概率模型文件中检索出的该第七代码所在的多出边终点代码块的当前边所对应的编码子区间;

e)第八代码,所述第八代码用于在当前编码区间的小数位数大于寄存器的容量时,记录当前编码区间内二进制形式下的最小值、并按比例逐步扩大该当前编码区间至其满足以下公式(3):

公式(1)中,beg_bin表示当前编码区间[beg,end)扩大后的beg的小数位数,end_bin表示当前编码区间[beg,end)扩大后的end的小数位数,L表示寄存器的容量,max(x)表示x的最大小数位数,sub.(x)表示beg_bin或end_bin满足条件x的所有小数位数;

f)第九代码,所述第九代码用于将其所在的多出边终点代码块的当前边在当前出边概率模型文件中的执行概率增加;

g)第十代码,所述第十代码用于将其所在的多出边终点代码块的当前边的起点代码块的所有非当前边在当前出边概率模型文件中的执行概率减少;

在Android程序的终止代码块中的最后一句代码块之前,生成插桩内容,该插桩内容含有第十一代码,所述第十一代码用于记录当前编码区间内二进制形式下的最小值。

进一步地,在本发明所述步骤(ii)中,“利用解码算法还原Android程序的执行路径”包括以下步骤:

①进行Android程序的执行路径还原的初始化操作,包含以下内容:

初始化当前编码区间;

从Android程序的执行路径编码数组中取出第一个元素作为当前执行路径编码值;

从Android程序的生命周期控制流文件中得到Android程序启动时执行的初始函数,并以该初始函数为当前函数;

②从Android程序的生命周期控制流文件和用户自定义函数的控制流文件中提取当前函数中的初始代码块,并以该初始代码块为当前代码块;

③查找当前代码块是否含有调用函数的代码,若有,则记录调用函数的代码所在文件名及其在该文件中所在的行数,找到被调用函数所在的文件,将记录的调用函数的代码所在文件名及其该文件中所在的行数添加到被调用函数的所有终止代码块中,以该被调用函数作为当前函数,返回执行步骤②;否则,执行步骤④;

④判断当前代码块的类别是属于仅有一条出边的代码块、多出边起点代码块、当前函数的终止代码块中的哪一种,并执行步骤⑤:

⑤如果判断当前代码块为仅有一条出边的代码块且该判断结论系首次得到,则将当前代码块的出边记录在可变长的数组空间中;

如果判断当前代码块为仅有一条出边的代码块且该判断结论并非首先得到,则将当前代码块的出边记录在可变长的数组空间中前一次记录的出边之后,并以该出边直接指向的代码块作为当前代码块,返回执行步骤③;

如果判断当前代码块为多出边起点代码块,则执行有以下步骤:

i)从当前出边概率模型文件中选择与当前执行路径编码值所在的编码子区间对应的出边;

ii)将当前编码区间更新为当前执行路径编码值所在的编码子区间;

iii)如果当前编码区间的小数位数大于寄存器的容量,则从执行路径编码数组中取出下一个路径编码值作为当前执行路径编码值,且按所述步骤e)所述的比例逐步扩大当前编码区间至其满足所述公式(1);

iv)以步骤i)中所选择的出边直接指向的代码块为当前代码块,返回执行步骤③;

如果判断当前代码块为当前函数的终止代码块,则进一步判断当前代码块是否记录有调用函数的代码所在文件名及其在该文件中所在的行数,若有,则根据该记录找到调用函数的代码,以该调用函数的代码作为当前代码块,返回执行步骤④;若没有,则执行步骤(iii)。

进一步地,本发明所述“按比例逐步扩大当前编码区间”中的“比例”满足以下公式(4):

公式(4)中,scale表示当前编码区间扩大的比例;Bin(x)表示得到数值x的小数位数;beg_bin表示当前编码区间扩大后的beg的小数位数;end_bin表示当前编码区间扩大后的end的小数位数;L表示寄存器的容量;表示按照比例scale扩大编码区间一次以上后,能令和同时成立。

进一步地,在本发明所述步骤7)中,所述编码算法为基于熵编码的编码算法。

进一步地,本发明所述编码算法为算术编码的编码算法或哈夫曼编码的编码算法。

进一步地,在本发明所述步骤(ii)中,所述解码算法为基于熵编码的解码算法。

进一步地,本发明所述解码算法为算术编码的解码算法或哈夫曼编码的解码算法。

与现有技术相比,本发明的有益效果为:(1)本发明通过向Android应用程序插桩并还原执行路径的方式,实现了半自动化的Android应用程序测试,相比于开发者手动测试程序的方式,减少了需要测试的程序路径空间,从而降低日志存储开销、减少对程序功能的影响并提高了程序测试的效率。(2)本发明在还原执行路径时,能够通过编码区间的小数位数大于寄存器的最大容量这一条件来推测断点,去除了利用PAP(Profiling All Path)算法时记录断点的开销。(3)本发明根据Android程序不同路径的执行概率调整多出边起点代码块每条出边的概率,使得执行概率高的路径使用占用存储空间少的编码,与B.L.(Ball和Larus)和PAP算法对所有执行路径使用固定编码相比,减少了日志的期望存储开销。(4)本发明仅在编码区间的小数位数大于寄存器的最大容量以及Android程序终止时记录编码数据,与B.L算法在每个循环结束以及Android程序终止时记录编码数据相比,特别是在处理循环次数多的Android程序时,降低了寄存器的访问次数,减少了对Android程序功能的影响,提高了程序测试的效率。

附图说明

图1是本发明Android程序的执行路径的还原方法的流程图。

具体实施方式

以下以搭载了Android 4.4的Nexus 5智能手机为测试平台,以测试一款在GooglePlay上下载了超过一百万次的开源项目Android Wifi Tether为例详细说明本发明,具体如下:

(1)对Android Wifi Tether进行分析和插桩,包括以下步骤:

(1.1)使用Apktool工具将Android Wifi Tether转化成Android虚拟机(即,Dalvik)字节码文件。

(1.2)从Android虚拟机字节码文件中得到Android程序组件的字节码文件(Android程序组件包括:Activity、Service、Broadcast Receiver和Content Provider)。

(1.3)根据Android生命周期函数(如,“onCreate()”、“onStop()”等)之间的控制流关系,对Android程序组件的字节码文件进行分块,生成Android生命周期控制流文件。如,当Android程序启动之后,将依次运行Android生命周期函数“onCreate()”和“onStart()”,则这两个Android生命周期函数之间存在一条由“onCreate()”直接指向“onStart()”的边。

(1.4)从Android程序的生命周期控制流文件中得到用户自定义函数,并根据Android虚拟机字节码语法(如“:cond_0”、“:goto_0”等)对用户自定义函数分块,生成用户自定义函数的控制流文件。其中,每个代码块中记录与程序控制流相关的信息(如出边数、入边数、可插桩代码的地址等),每个代码块之间依据代码块中的第一句代码和最后一句代码块的关键字进行连接。如,代码块C1最后一句代码为“goto:goto_0”,代码块C2第一句代码为“:goto_0”,则将代码块C1和代码块C2连接。

(1.5)从Android程序的生命周期控制流文件和用户自定义函数的控制流文件中得到Android程序的初始代码块、Android程序的终止代码块、所有多出边起点代码块、所有多出边终点代码块。其中,Android程序的初始代码块指Android程序启动时执行的第一个函数的第一个代码块,Android程序的终止代码块指Android程序结束时执行的最后一个函数的最后一个代码块,多出边起点代码块指含有两条以上出边的代码块,多出边终点代码块指多出边起点代码块直接指向的代码块。

(1.6)从Android Wifi Tether的/data/目录下的当前出边概率模型中得到所有多出边起点代码块的各条出边的执行概率,若当前出边概率模型文件为初始时的出边概率模型文件,则先将当前出边概率模型文件中的所有多出边起点代码块的各条出边的概率设定为相等后再执行步骤(1.7),否则,直接执行步骤(1.7)。

(1.7)在主Activity的“OnCreate()”函数的Android程序初始代码块中最后一句代码之后,生成插桩内容,该插桩内容按先后顺序含有第一代码、第二代码,所述第一代码用于输出表示当前Android Wifi Tether开始执行的信号(即,“Android Wifi Tether Start”),所述第二代码用于将当前编码区间[beg,end)初始化为[0,1)。

在Android Wifi Tether中的所有多出边起点代码块中最后一句代码之后,分别生成同样的插桩内容,各插桩内容第三代码,各所述第三代码用于将其所在多出边起点代码块outedge_start_id配置该多出边起点代码块的所有出边的起点。

在Android Wifi Tether中的所有多出边终点代码块中第一句代码之前,生成插桩内容,各插桩内容按先后顺序依次包含以下第四至第十代码:

a)第四代码,所述第四代码用于将其所在的多出边终点代码块outedge_end_id配置为该多出边终点代码块的所有入边的终点。

b)第五代码,所述第五代码用于将其所在的多出边终点代码块的Android Wifi Tether执行时经过的入边配置为该多出边终点代码块的当前边(即,outedge_current),当前边outedge_current由多出边起点代码块和多出边终点代码块的笛卡尔积<outedge_start_id,outedge_end_id>唯一表示。

c)第六代码,所述第六代码用于从当前出边概率模型文件中检索出outedge_current的执行概率。

d)第七代码,所述第七代码用于将当前编码区间[beg,end)更新为从当前出边概率模型文件中检索出的outedge_current对应的编码子区间[beg_outedge,end_outedge),在出边概率模型文件中,对于所有以同一个多出边起点代码块为起点的出边,满足(5)所述条件:

其中,outedgei表示所有以同一个多出边起点代码块为起点的出边的集合outedge中的第i个出边,Pr(outedgei)表示outedgei所对应的执行概率,CDF(outedgei)表示outedgei的累积分布函数,且定义CDF(outedge-1)的值为0,按公式(6)所述方法计算outedge_current对应的编码子区间:

其中CDF(outedgei)即为outedge_current所对应的累积分布函数。

e)第八代码,所述第八代码用于在beg或end的小数位数大于寄存器的容量时,记录当前编码区间范围内二进制形式下的最小值、并按扩大比例scale逐步扩大beg和end的数值至满足公式(8)所示的条件,扩大比例scale应满足条件(7):

其中,beg和end表示当前编码区间[beg,end),Bin(x)表示得到数值x的小数位数,beg_bin和end_bin分别表示扩大后beg和end的小数位数,L表示寄存器的容量,max(x)表示x最大的小数位数,sub.(x)表示beg_bin或end_bin满足条件x的所有小数位数,表示按照比例scale扩大编码区间一次以上后,能令和同时成立。

f)第九代码,所述第九代码用于将outedge_current在当前出边概率模型文件中的执行概率增加。

g)第十代码,所述第十代码用于减少当前出边概率模型文件中outedge_start_id的所有非outedge_current边的执行概率,其中,增加与减少执行概率的方法按照公式(9)所述方法:

其中,Pr(outedgei)表示outedgei所对应的执行概率,inc表示增加的执行概率,Count(outedge)表示集合outedge的个数,outedgei表示当前出边outedge_current,outedgej表示所有非当前出边。

该方法通过增加该outedge_current边的执行概率,减少非outedge_current边的执行概率,便能在Android程序执行编码时使用较少的编码开销代表执行概率高的路径,从而减少了期望的路径编码开销。

在Android Wifi Tether中所有组件的“OnStop()”、“OnDestroy()”函数的终止代码块中最后一句代码块之前,生成插桩内容,该插桩内容含有第十一代码,所述第十一代码用于记录当前编码区间中二进制形式下的最小值。

(1.8)根据分析得到的所有代码块的插桩内容,对所有Dalvik字节码文件进行插桩。

(1.9)使用Apktool工具将插桩后的Dalvik字节码文件打包成Android程序包Android Wifi Tether Beta。

(2)将打包生成的Android Wifi Tether Beta装入Nexus 5手机,参与测试的用户按照自己的习惯使用一周。

(3)在计算机端读取收集到的程序控制流日志,还原Android Wifi Tether Beta的执行路径,包括以下步骤:

(3.1)从程序控制流日志文件中得到Android Wifi Tether Beta的执行路径编码数组Encoded_Path。

(3.2)根据Android Wifi Tether Beta的Encoded_Path、Android程序的生命周期控制流文件、用户自定义函数控制流文件,利用算术编码的解码算法还原Android程序的执行路径,具体的,按照以下步骤进行:

(3.2.1)进行Android Wifi Tether Beta的执行路径还原的初始化操作,包含以下内容:

将当前编码区间[beg,end)初始化为[0,1)。

从Android Wifi Tether Beta的执行路径编码数组Encoded_Path中取出第一个元素作为当前执行路径编码值。(其中,执行路径编码数组中的第一个元素,是指当前编码区间的小数位数第一次大于寄存器的容量时所记录的当前编码区间内二进制形式下的最小值);

从Android Wifi Tether Beta的生命周期控制流文件中得到Android程序启动时执行的主Activity的“OnCreate()”函数,并以此初始函数为当前函数。

(3.2.2)依据Android Wifi Tether Beta的生命周期控制流文件和用户自定义函数的控制流文件中得到当前函数中的初始代码块,并以此初始代码块为当前代码块。

(3.2.3)查找当前代码块是否含有调用函数的代码(即,是否含有smali关键字“invoke-”),若有,则记录调用函数的代码所在文件名ClassPath_Activity及其在该文件中所在的行数LineNum,找到被调用函数所在的文件,将记录的调用函数的代码的ClassPath_Activity和LineNum添加到被调用函数的所有终止代码块中,以该被调用函数作为当前函数,返回执行步骤(3.2.2);否则,执行步骤(3.2.4);

(3.2.4)判断当前代码块的类别是属于仅有一条出边的代码块、多出边起点代码块、当前函数的终止代码块中的哪一种,并执行步骤(3.2.5):

(3.2.5)如果判断当前代码块为仅有一条出边的代码块且该判断结论系首次得到,则将当前代码块的出边记录在可变长的数组空间中;

如果判断当前代码块为仅有一条出边的代码块且该判断结论并非首先得到,则将当前代码块的出边记录在可变长的数组空间中前一次记录的出边之后,并以该出边直接指向的代码块作为当前代码块,返回执行步骤(3.2.3);

如果判断当前代码块为多出边起点代码块,则执行有以下步骤:

(3.2.5.1)从当前出边概率模型文件中选择与当前执行路径编码值所在的编码子区间[beg,end)对应的出边outedge_current;

(3.2.5.2)令outedgei为出边outedge_current,按照公式(6)所述方法,将当前编码区间更新为当前执行路径编码值所在的编码子区间;

(3.2.5.3)如果当前编码区间的小数位数大于寄存器的容量,则从执行路径编码数组中Encoded_Path取出下一个路径编码值作为当前执行路径编码值,且按所述步骤e)所述的扩大比例scale逐步扩大当前编码区间至其满足公式(8)所述条件,借助这种方式,本发明能够在每次beg或者end的小数位数大于寄存器的容量时,触发路径编码值取出的操作,从而去除了记录表明路径编码值取出操作的断点的开销;

(3.5.2.4)以步骤(3.2.5.1)中所选择的出边直接指向的代码块为当前代码块,返回执行步骤(3.2.3);

如果判断当前代码块为当前函数的终止代码块,则进一步判断当前代码块是否记录有调用函数的代码所在文件名ClassPath_Activity及其在该文件中所在的行数LineNum,若有,则根据该记录找到调用函数的代码,以该调用函数的代码作为当前代码块,返回执行步骤(3.2.4);若没有,则执行步骤(3.3)。

(3.3)根据还原的Android Wifi Tether Beta的执行路径,统计路径中所有多出边起点代码块的出边的执行频率,从而计算出每条出边的执行概率,更新当前出边概率模型文件。

在本发明中,编码算法优选基于熵编码的编码算法,解码算法优选基于熵编码的解码算法。其中,编码算法除了上述实施例中的算术编码的编码算法,还可使用哈夫曼编码的编码算法、PAP(Profiling All Path)的编码算法和B.L(Ball和Larus)的编码算法等;解码算法除了上述实施例中的算术编码的解码算法外,还可使用哈夫曼编码的解码算法、PAP的解码算法和B.L的解码算法等。

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