首页> 中国专利> 并发程序的数据竞争检查方法及装置

并发程序的数据竞争检查方法及装置

摘要

本说明书的实施例提供一种并发程序的数据竞争检查方法及装置。在该数据竞争检查方法中,通过使用自底向上的数据流分析,为并行程序的每个程序函数构建各自的线程流图。在进行数据竞争检查时,通过使用自底向上的数据流分析,确定出与所关注的数据访问操作语句对应的候选检查路径集。随后,利用所构建出的各个程序函数的线程流图来确定出候选检查路径集中的各条候选检查路径的开始节点和结束节点的全局标注,并使用所确定出的全局标注来确定并发程序的数据竞争检查结果。

著录项

  • 公开/公告号CN112965838A

    专利类型发明专利

  • 公开/公告日2021-06-15

    原文格式PDF

  • 申请/专利权人 支付宝(杭州)信息技术有限公司;

    申请/专利号CN202110280390.2

  • 发明设计人 周金果;

    申请日2021-03-16

  • 分类号G06F9/54(20060101);G06F11/30(20060101);

  • 代理机构11376 北京永新同创知识产权代理有限公司;

  • 代理人林锦辉;刘景峰

  • 地址 310000 浙江省杭州市西湖区西溪路556号8层B段801-11

  • 入库时间 2023-06-19 11:26:00

说明书

技术领域

本说明书实施例通常涉及计算机技术领域,尤其涉及并发程序的数据竞争检查方法及装置。

背景技术

随着多核处理器的普及和众核处理器的发展,并发编程被越来越多地使用来提高程序的性能。利用并发编程,不仅可以减少程序的运行时间,而且可以提高程序的吞吐量和多核处理器的利用率。虽然并发编程可以带来上述好处,但是并发程序内部的并发性和不确定性仍然会导致一些难以避免的并发问题,比如,死锁、数据竞争、原子性违背和顺序违背等。

在上述并发问题中,数据竞争是指在多线程程序中,两个或多个线程在无时序限制情况下访问同一内存位置,并且至少有一个线程执行数据写操作。数据竞争是引起其他非死锁并发缺陷的根本原因,并且是并发程序的常见并发问题,由此,如何高效地实现并发程序的数据竞争检查成为亟待解决的问题。

发明内容

鉴于上述,本说明书实施例提供一种并发程序的数据竞争检查方法和数据竞争检查装置。利用该数据竞争检查方法和数据竞争检查装置,可以高效地实现并发程序的数据竞争检查。

根据本说明书实施例的一个方面,提供一种并发程序的数据竞争检查方法,包括:通过对并发程序的程序源代码执行自底向上的数据流分析,确定候选检查路径集,所述候选检查路径集中的每条候选检查路径的开始节点代表针对内存位置的一个数据访问操作,结束节点代表针对所述内存位置的另一数据访问操作,所述开始节点和所述结束节点中的至少一个代表针对所述内存位置的数据写操作;使用各个程序函数的线程流图TFG(N,E,nentry,nexit),确定所述候选检查路径集中的各条候选检查路径的开始节点和结束节点的全局标注;以及根据各条候选检查路径的开始节点和结束节点的全局标注,确定所述并发程序的数据竞争检查结果,其中,各个程序函数的线程流图TFG(N,E,nentry,nexit)通过对程序源代码进行自底向上的数据流分析而构建,N代表线程流图节点集{n

可选地,在上述方面的一个示例中,所述标注是操作数和操作组成的表达式,所述操作数包括常量线程标签、变量线程标签和路径条件中的至少一种,所述操作包括加操作、减操作和绑定操作中的至少一种。

可选地,在上述方面的一个示例中,通过对程序源代码进行自底向上的数据流分析,为各个程序函数构建对应的线程流图可以包括:自并发程序的最底层程序函数起,基于自底向上的数据流分析来执行下述处理过程,直到完成最顶层程序函数的处理:创建当前程序函数的线程流图节点,所述线程流图节点包括与每条程序语句对应的真实节点、根据线程创建和/或函数调用创建的虚拟节点、入口节点和出口节点;根据对该当前程序函数的程序源代码的数据流分析,确定该当前程序函数的各个线程流图节点之间的有向边关系;以及使用所确定出的有向边关系对应的标注处理规则以及下层程序函数的节点标注,确定该当前程序函数的各个线程流图节点的标注。

可选地,在上述方面的一个示例中,所述有向边包括下述中的至少一种:有向边→flow、有向边→create,有向边→join和有向边→call。

可选地,在上述方面的一个示例中,在对并发程序的程序源代码进行自底向上的数据流分析时,将彼此之间的数据流向不会发生变化的多个真实节点合并为单个线程流图节点。

可选地,在上述方面的一个示例中,通过对所述并发程序的程序源代码进行自底向上的数据流分析,确定候选检查路径集可以包括:通过对所述并发程序的程序源代码进行自底向上的数据流分析,获取与数据访问操作对应的完整数据流向路径集,所述完整数据流向路径集中的每条完整数据流向路径的开始节点代表针对内存位置的一个数据访问操作,结束节点代表针对所述内存位置的另一数据访问操作;以及从所筛选出的完整数据流向路径集中去除开始节点和结束节点都与非数据写操作对应的数据流向路径,得到候选检查路径集。

可选地,在上述方面的一个示例中,从所筛选出的完整数据流向路径集中去除开始节点和结束节点都与非数据写操作对应的数据流向路径,得到候选检查路径集可以包括:从所筛选出的完整数据流向路径集中去除不可达数据流向路径以及开始节点和结束节点都与非数据写操作对应的数据流向路径,得到候选检查路径集。

可选地,在上述方面的一个示例中,根据各条候选检查路径的开始节点和结束节点的全局标注,确定数据竞争检查结果可以包括:针对各条候选检查路径,在开始节点和结束节点的全局标注中存在相同的线程标签或者具有对应关系的线程标签时,确定该开始节点和该结束节点代表的程序语句存在数据竞争关系。

可选地,在上述方面的一个示例中,所述线程流图的构建与所述候选检查路径的确定并行执行。

根据本说明书的实施例的另一方面,提供一种并发程序的数据竞争检查装置,所述数据竞争检查装置包括:至少一个处理器,与所述至少一个处理器耦合的存储器,以及存储在所述存储器中的计算机程序,所述至少一个处理器执行所述计算机程序来实现:通过对并发程序的程序源代码进行自底向上的数据流分析,确定候选检查路径集,所述候选检查路径集中的每条候选检查路径的开始节点代表针对内存位置的一个数据访问操作,结束节点代表针对所述内存位置的另一数据访问操作,所述开始节点和所述结束节点中的至少一个代表针对所述内存位置的数据写操作;使用各个程序函数的线程流图TFG(N,E,nentry,nexit),确定所述候选检查路径集中的各条候选检查路径的开始节点和结束节点的全局标注;以及根据各条候选检查路径的开始节点和结束节点的全局标注,确定所述并发程序的数据竞争检查结果,其中,各个程序函数的线程流图TFG(N,E,nentry,nexit)通过对程序源代码进行自底向上的数据流分析而构建,N代表线程流图节点集{n

根据本说明书的实施例的另一方面,提供一种计算机可读存储介质,其存储有计算机程序,所述计算机程序被处理器执行来实现如上所述的数据竞争检查方法。

根据本说明书的实施例的另一方面,提供一种计算机程序产品,包括计算机程序,所述计算机程序被处理器执行来实现如上所述的数据竞争检查方法。

附图说明

通过参照下面的附图,可以实现对于本说明书内容的本质和优点的进一步理解。在附图中,类似组件或特征可以具有相同的附图标记。

图1示出了根据本说明书的实施例的数据竞争检查过程的流程图。

图2示出了并发程序的程序源代码的示例示意图。

图3示出了根据本说明书的实施例的线程流图构建过程的流程图。

图4A-4D示出了根据本说明书的实施例的线程流图的示例示意图。

图5示出了根据本说明书的实施例的候选检查路径集确定过程的流程图。

图6示出了根据本说明书的实施例的数据竞争检查装置的方框图。

图7示出了根据本说明书的实施例的线程流图构建单元的一个实现示例的方框图。

图8示出了根据本说明书的实施例的检查路径集确定单元的一个实现示例的方框图。

图9示出了根据本说明书的实施例的基于计算机实现的数据竞争检查装置的示意图。

具体实施方式

现在将参考示例实施方式讨论本文描述的主题。应该理解,讨论这些实施方式只是为了使得本领域技术人员能够更好地理解从而实现本文描述的主题,并非是对权利要求书中所阐述的保护范围、适用性或者示例的限制。可以在不脱离本说明书内容的保护范围的情况下,对所讨论的元素的功能和排列进行改变。各个示例可以根据需要,省略、替代或者添加各种过程或组件。例如,所描述的方法可以按照与所描述的顺序不同的顺序来执行,以及各个步骤可以被添加、省略或者组合。另外,相对一些示例所描述的特征在其它例子中也可以进行组合。

如本文中使用的,术语“包括”及其变型表示开放的术语,含义是“包括但不限于”。术语“基于”表示“至少部分地基于”。术语“一个实施例”和“一实施例”表示“至少一个实施例”。术语“另一个实施例”表示“至少一个其他实施例”。术语“第一”、“第二”等可以指代不同的或相同的对象。下面可以包括其他的定义,无论是明确的还是隐含的。除非上下文中明确地指明,否则一个术语的定义在整个说明书中是一致的。

依据数据竞争检查时机,数据竞争检查方案可以分为动态分析方案和静态分析方案。动态分析方案通过插桩来获取变量和别名的准确信息,但是由于线程调度策略不同,程序的执行结果不同,从而使得动态分析方案的覆盖面不全,存在很多漏报,并且具有很大的检查开销。与动态分析方案相比,静态分析方案具有速度快、检查全面的优点。

静态分析方案也可以称为静态分析工具。静态分析方案的示例可以包括LOCKSMITH、Goblint、RELAY、IteRace、ECHO、和RacerX等。LOCKSMITH是针对数据竞争的上下文敏感的相关性分析工具。它使用基于约束的技术来计算描述保护左值的锁的关联。LOCKSMITH仅仅被应用于检测100K行代码的程序。Goblint将指针分析和值分析相结合,从而使其能够处理复杂的加锁情况。RELAY是一种使用自底向上分析方法的静态可扩展算法。它采用符号执行、指针分析、锁集分析、受保护访问分析的方法来进行数据竞争检查。IteRace是针对Java并行循环的静态数据竞争检测工具,它通过专门处理lambda式并行循环、跟踪、汇总,实现了较低的误报率,但是IteRace只能分析并行循环。ECHO能够在编写代码期间在IDE中实时检测数据竞争。为了能在IDE中使用,ECHO采用的指针分析在程序有修改时会增量分析。然而由于对分析速度存在的要求,ECHO不能实现上下文敏感和路径敏感。RacerX使用流敏感的过程间分析检测数据竞争以及死锁。然而为了实现可扩展,RacerX丢弃了一些程序信息,例如使用类型表示所有左值,这就导致了RacerX的分析不完善。

本说明书的实施例提供一种并发程序的数据竞争检查方案。在该数据竞争检查方案中,首先,通过使用自底向上的数据流分析,为并行程序的每个程序函数构建用于反映该程序函数的线程信息的线程流图,该线程流图考虑了下层程序函数引入的线程信息影响。在进行数据竞争检查时,通过使用自底向上的数据流分析,确定出与所关注的数据访问操作语句对应的候选检查路径集。随后,利用所构建出的各个程序函数的线程流图来确定出候选检查路径集中的各条候选检查路径的开始节点和结束节点的全局标注,所确定出的全局标注被使用来确定数据竞争检查结果。按照上述数据竞争检查方案,由于在构建各个程序函数的线程流图时考虑了下层程序函数引入的线程信息影响,从而使得在利用线程流图进行数据竞争检查时无需考虑下层程序函数,进而可以提升数据竞争检查时的检查效率。此外,在上述数据竞争检查方案中,在确定开始节点和结束节点的全局标注时引入了上层程序函数的影响,从而使得所确定出的全局标注考虑了上下文影响,由此实现基于上下文敏感的数据竞争检查。另外,所构建的线程流图包含流关系和路径条件,从而使得该数据竞争检查方案是基于流敏感和路径敏感的数据竞争检查方案。此外,在该数据竞争检查方案中,仅仅对根据数据流分析确定出的部分数据流向路径而不是并发程序的全部数据流向路径进行数据竞争检查,由此可以提升数据竞争检查的检查效率。

下面将参照附图来详细描述根据本说明书的实施例的用于并发程序的数据竞争检查方法及数据竞争检查装置。

图1示出了根据本说明书的实施例的数据竞争检查过程100的流程图。

如图1所示,在110,通过对并发程序的程序源代码进行自底向上的数据流分析构建出各个程序函数的线程流图(Thread Flow Graph)TFG(N,E,nentry,nexit)。在该自底向上的数据流分析中,不考虑递归调用。如果在数据流分析时碰到递归调用,则直接忽略递归调用,仅仅处理为普通操作,而不作为函数调用处理。

在所构建出的线程流图TFG中,N代表线程流图节点集{n

可选地,在一个示例中,所述标注可以是操作数和操作组成的表达式,所述操作数包括常量线程标签、变量线程标签和路径条件中的至少一种,所述操作包括加操作、减操作和绑定操作中的至少一种。这里,常量线程标签是指确定性线程标签,例如,图4C中的用于指示主线程T3的线程标签“T3”。变量线程标签是指与变量操作对应的非确定性标签,例如,图4A中的出口节点D的线程标签“arg2”,该线程标签是用于指示与参数arg2对应的线程,具体对应哪个线程,需要根据具体操作来确定。路径条件用于指示该线程执行所需的路径条件,例如,图4D中的“g>0”和“g<=0”。加操作“+”用于指示添加线程的操作,减操作“-”用于指示去除线程的操作,以及绑定操作“()”用于指示两个线程标签(或带操作的线程标签)绑定的操作。

在一些实施例中,有向边可以包括下述中的至少一种:有向边→flow、有向边→create,有向边→join和有向边→call。有向边→flow用于指示程序函数的内部节点之间的数据流向。有向边→create用于将thread_create的调用点连接至线程的入口节点。有向边→join用于将线程的出口节点连接至thread_join操作的调用点。有向边→call用于将其它函数调用的调用点连接至对应的被调用者的入口节点。此外,有向边→create和有向边→join需要通过线程标识符(thread ID)匹配对应的操作,例如,thread_join操作需要匹配与其参数(即线程标识符)对应、且由thread_create操作创建的线程。

图2示出了并发程序的程序源代码的示例示意图。如图2所示,并发程序包括4个程序函数:Func A、Func B、Func C以及Func D。各个程序函数之间的调用关系是Func A调用Func B和Func D,Func B调用Func C,以及Func C调用Func D。此外,在该并发程序中存在三个变量:全局变量g和两个内存地址x,y。全局变量g用于示例路径条件,以及内存地址(内存位置)x和y用于示例数据竞争分析,即,分析针对内存地址x和y的数据访问操作。在该并发程序中进行三种操作:对内存地址的数据写操作(Write)、函数调用操作(Call)和线程创建操作(NewThread,也可以称为thread_create)。要说明的是,图2中给出的仅仅是示例,在其它实施例中,还可以包括其它操作,比如,thread_Join操作、thread_Notify操作、thread_wait操作、thread_lock操作或thread_unlock操作等。

图3示出了根据本说明书的实施例的线程流图构建过程的流程图。

如图3所示,自并发程序的最底层程序函数起,基于自底向上的数据流分析来执行线程流图构建过程,直到完成最顶层程序函数的线程流图构建处理。

具体地,在310,选择最底层程序函数作为初始的当前程序函数,例如,图2中示出的函数Func D。接着,在320,创建当前程序函数的线程流图节点。所创建的线程流图节点包括真实节点、虚拟节点、入口节点和出口节点。当前程序函数的每条程序语句对应一个真实节点,并且针对用于线程创建和函数调用的程序语句创建虚拟节点。例如,针对函数FuncD,创建真实节点25和26、入口节点D和出口节点D。针对函数Func C,创建真实节点18、19、21以及入口节点C和出口节点C。此外,由于节点21用于开线程(线程创建),从而需要创建对应的虚拟节点21’。针对函数Func B,创建真实节点14、入口节点B和出口节点B。此外,由于节点14用于函数调用,从而需要创建对应的虚拟节点14’。针对函数Func A,创建真实节点4、5、6、7、8和9、入口节点A和出口节点A。此外,由于节点5和8用于线程创建,从而需要创建对应的虚拟节点5’和8’。由于节点7和9用于函数调用,从而需要创建对应的虚拟节点7’和9’。

在330,根据对该当前程序函数的程序源代码的数据流分析,确定该当前程序函数的各个线程流图节点之间的有向边关系,如图4A-图4D中所示。

在340,使用所确定出的有向边关系所对应的标注处理规则以及下层程序函数的节点标注,确定该当前程序函数的各个线程流图节点的标注。

在本说明书的实施例中,线程标签可以被定义为如图4A-图4D中所示的简单标签T(主线程标签)和t(子线程标签)。

在本说明书的实施例中,有向边→flow、有向边→create,有向边→join和有向边→call分别对应不同的标注处理规则,即,Flow规则、Create规则、Join规则和Call规则。此外,标注处理规则还可以包括Wait/Notify规则、Lock规则和UnLock规则。

Flow规则表示一次基本的标注转换。针对节点n

Create规则是对应thread_create操作的转换规则。在Create规则中,线程创建会引入主线程标签T和对应的子线程标签t,其中,主线程标签T被赋予创建线程的节点的后继节点,以及对应的子线程标签t被赋予创建线程的节点的虚拟节点。例如,针对函数Func C,节点21用于创建线程,从而引入主线程标签T3和子线程标签t3。按照Create规则,主线程标签T3被赋予出口节点C,以及子线程标签t3被赋予节点21的虚拟节点21’。针对函数Func A,节点5用于创建线程,从而引入主线程标签T1和子线程标签t1。按照Create规则,主线程标签T1被赋予节点5的后继节点6,以及子线程标签t1被赋予节点5的虚拟节点5’。节点8用于创建线程,从而引入主线程标签T2和子线程标签t2。按照Create规则,主线程标签T2被赋予节点8的后继节点9,以及子线程标签t2被赋予节点8的虚拟节点8’。

Join规则表示如果一个线程标签等待终止,则会对该线程标签执行减操作。Wait/Notify规则表示将一个ID(即,这两种操作的唯一标识)加入thread_notify的所有前驱节点以及thread_wait的所有后继节点(包括过程间节点)的标注集中。Lock规则和UnLock规则用于描述锁分析。当thread_lock函数被调用时,会将一个标注添加至锁集中,对应地,当thread_unlock函数被调用时,会从相应锁集中去除对应的标注。这两条标注处理规则模拟了加锁和去锁的语义。

Call规则用于函数调用处的标注处理。当一个标注到达一处函数调用时,调用点将其当前的标注传递给被调用者,调用点的输出标注则是合并被调用者的出口节点的标注。在函数调用中,被调用者被视为内联函数,这意味着调用点之前的标注与被调用者的出口节点之前的标注相同。类似地,调用点之后的标注和被调用者的出口节点之后的标注相同。使用Call规则来对标注进行转换处理,使得可以将调用者和被调用者的上下文信息引入数据流分析中。

在如上得到当前程序函数的线程流图后,在350,判断是否还存在未完成线程流图构建的程序函数。如果不存在未完成线程流图构建的程序函数,则线程流图构建结束。如果存在未完成线程流图构建的程序函数,则在360,选择该当前程序函数的一个上层程序函数或一个同层未处理程序函数作为下一处理过程的当前程序函数,返回到320来执行下一处理过程。

下面将参照图2中示出的程序源代码描述程序函数的各个线程流图节点的标注确定过程。

例如,针对程序函数Func D,由于该程序函数中不涉及线程创建和函数调用,从而创建真实节点25和26、入口节点D和出口节点D。入口节点D的标注为空,入口节点D与真实节点25之间的有向边以及真实节点25与真实节点26之间的有向边都为有向边→flow,根据Flow规则,真实节点25和26的标注为空。真实节点26与出口节点D之间的有向边为→Join,根据Join规则,将对第二个参数相关的线程标签执行减操作,从而出口节点D的标注为-arg2。这里,标注arg2是变量线程标签,“-”是减操作。按照上述处理,得到如图4A所示的程序函数Func D的线程流图。

针对程序函数Func C,创建真实节点18、19和21以及入口节点C和出口节点C。此外,由于节点21用于线程创建,从而创建对应的虚拟节点21’。入口节点C和节点18、节点18和19,节点19和21、节点18和21以及节点21和出口节点C之间的有向边都为有向边→flow,根据Flow规则,真实节点18、19和21的标注为空。此外,由于真实节点21用于线程创建,从而创建对应的虚拟节点21’,换言之,真实节点21利用有向边→creat来与虚拟节点21’连接,并且用于调用函数Func D。真实节点21是线程创建(T3),从而虚拟节点21’的标注为t3。此外,由于函数Func D的出口节点D的标注为-arg2(即,下层程序函数的节点标注),而函数Func D的arg2为实参t,该实参是函数Func C的arg1,从而真实节点21的后继节点“出口节点C”的标注是-arg1,并且由于函数Func D是以创建线程的方式开启的,所以-arg1绑定在主线程T3上,如果T3被join,则arg1同时也被join,从而得到“出口节点C”的标注为T3(-arg1)。按照上述处理,得到如图4B所示的程序函数Func C的线程流图。

针对程序函数Func B,创建真实节点14、入口节点B和出口节点B。此外,由于真实节点14用于函数调用,从而创建对应的虚拟节点14’。入口节点B和节点14,节点14和出口节点B之间的有向边都为有向边→flow,根据Flow规则,真实节点14的标注为空。节点14和节点14’之间的有向边为→call,根据Call规则,虚拟节点14’的标注与节点14的相同,由此虚拟节点14’的标注为空。节点14调用程序函数Func C,而程序函数Func C返回标注T3(-arg1),而在程序函数Func B中的arg1为Null,即,不存在,从而节点14只能将标注T3到后继节点,即,出口节点B,由此,出口节点B的标注为T3。按照上述处理,得到如图4C所示的程序函数Func B的线程流图。

针对程序函数Func A,创建真实节点4、5、6、7、8和9,虚拟节点5’、7’、8’、9’以及入口节点A和出口节点A。入口节点A和节点4以及节点4和5之间的有向边都为有向边→flow,入口节点A的标注为空,则根据Flow规则,真实节点4和5的标注为空。由于节点5和6之间的有向边为有向边→flow,并且节点5利用线程创建的方式调用程序函数Func B,根据Create规则,虚拟节点5’的标注为t1,以及后继节点6被赋予标签T1,此外,由于程序函数Func B返回标注T3,从而节点6的标注为T1+T3。节点6和7之间的有向边为有向边→flow,则根据Flow规则,节点7的标注与节点6的标注相同,即,T1+T3。节点7和7’之间的有向边为有向边→call,则根据Call规则,节点7’的标注为T1+T3。此外,节点7采用函数调用的方式来调用程序函数Func D,而程序函数Func D返回标注-arg2(下层程序函数的节点标注),这里,arg2为节点5所产生的T1/t1,从而需要减去线程标签T1,并且由于节点7的操作的路径条件为(g>0),从而节点8的标注为T1(g<=0)+T3。此外,节点8采用线程创建的方式调用程序函数Func B,根据Creat规则,虚拟节点8’的标注为t2(g>0)+T1(g<=0)+T3。节点8和9之间的有向边为有向边→flow,即,节点9是节点8的后继节点,由于节点8采用线程创建的方式调用程序函数Func B,而程序函数Func B返回标注T3,由此节点8产生新的标注T2(g>0),从而节点9的标注为T1(g<=0)+T2(g>0)+T3。由于节点9采用函数调用的方式调用程序函数FuncD,而程序函数Func D返回标注-arg2,这里,arg2为节点8产生的T2/t2,该线程标签需要被减去,并且节点8的路径条件为g>0,从而出口节点A的标注为T1(g<=0)+T3。按照上述处理,得到图4D所示的程序函数Func A的线程流图。

此外,可选地,在上述线程流图构建过程中,还可以执行线程流图节点合并处理。具体地,在对并发程序的程序源代码进行自底向上的数据流分析时,将彼此之间的数据流向不会发生变化的多个真实节点合并为单个线程流图节点。例如,针对程序函数Func D,可以合并节点25和26。针对程序函数Func C,可以合并节点18,19和21。针对程序函数Func B,由于唯一节点14是函数调用,从而无法合并。针对程序函数Func A,由于节点5,7,8,9都是函数调用/线程创建,节点6与7和出口节点A之间存在两条边,并且节点7和出口节点A没有合并,从而节点6不能与节点7和出口节点A,由此,在程序函数Func A中,仅仅可以合并节点4和5。

在上述线程流图构建过程中,采用自底向上的方式执行,并且所构建出的每个程序函数的线程流图都考虑了其下层程序函数对其节点标注的影响,从而所构建的线程流图引入了下层程序函数的上下文影响,由此使得所构建的线程流图支持上下文敏感分析。此外,所构建的线程流图具有用于反映控制流向(数据流向)的边关系以及包含路径条件的标注,从而使得所构建的线程流图支持流敏感分析和路径敏感分析。

上述线程流图构建过程可以预先处理,并且将所构建的各个程序函数的线程流图存储在数据库或者存储设备中,供后续数据竞争检查过程使用。在另一实施例中,上述线程流图构建过程也可以在数据竞争检查过程中实时执行。

回到图1,在120,通过对并发程序的程序源代码进行自底向上的数据流分析,确定候选检查路径集,所述候选检查路径集中的每条候选检查路径的开始节点代表针对内存位置的一个数据访问操作,结束节点代表针对所述内存位置的另一数据访问操作,并且,开始节点和结束节点所对应的数据访问操作中的至少一个数据访问操作为数据写操作。此外,所得到的候选检查路径包含该路径中存在的各种函数调用关系。在本说明书中,数据访问操作的示例可以包括但不限于:数据写操作、数据读操作、数据查询操作等。

图5示出了根据本说明书的实施例的候选检查路径集确定过程500的流程图。

如图5所示,在510,通过对并发程序的程序源代码进行自底向上的数据流分析,确定与数据访问操作对应的完整数据流向路径集,所述完整数据流向路径集中的每条完整数据流向路径的开始节点代表针对内存位置的一个数据访问操作,结束节点代表针对所述内存位置的另一数据访问操作。这里,完整数据流向路径集的确定是自并发程序的最底层程序函数的数据流分析开始。

例如,针对图2中示出的示例并发程序,从程序函数Func D的数据流分析开始。针对程序函数Func D分析,得到一条分析结果:arg1->25,该分析结果不是完整路径,在程序函数Func D被调用时,可向上代入。

针对程序函数Func C,得到三条分析结果:(1)arg2->19:g<0,该分析结果不是完整路径,在程序函数Func C被调用时,可向上代入;(2)19->21(arg1):g<0,针对该分析结果,代入程序函数Func D的分析结果,由此得到分析结果:19->21(arg1)->25:g<0,该分析结果是完整路径;(3)arg2->21(arg1),针对该条分析结果,代入程序函数Func D的分析结果,得到分析结果:arg2->21(arg1)->25,该分析结果不是完整路径,在程序函数Func C被调用时,可向上代入。

针对程序函数Func B,可以得到分析结果arg1->14(arg2),代入程序函数Func C的分析结果,可以得到两条分析结果:(1)arg1->14(arg2)->19:g<0,该分析结果不是完整路径,在程序函数Func B被调用时,可向上代入;以及(2)arg1->14(arg2)->21(arg1)->25,该分析结果不是完整路径,在程序函数Func B被调用时,可向上代入。

针对程序函数Func A,得到分析结果:(1)5(arg1)->7(arg1):g>0;(2)5(arg1)->9(arg1):g>0;以及(3)7(arg1)->9(arg1):g>0。针对分析结果5(arg1)->7(arg1):g>0,代入程序函数Func B和Func D的分析结果,可以得到完整路径19->14(arg2)->5(arg1)->7(arg1)->25:g<0&&g>0以及完整路径25->21(arg1)->14(arg2)->5(arg1)->7(arg1)->25:g>0。针对分析结果5(arg1)->9(arg1):g>0,代入程序函数Func B和Func D的分析结果,可以得到完整路径19->14(arg2)->5(arg1)->9(arg1)->25:g<0&&g>0以及完整路径25->21(arg1)->14(arg2)->5(arg1)->9(arg1)->25:g>0。针对7(arg1)->9(arg1):g>0,代入程序函数Func D的分析结果,得到完整路径25->7(arg1)->9(arg1)->25:g>0。

综上所述,找到下述6条完整路径,其中,1条来自程序函数Func C,5条来自程序函数Func A:

完整路径1:25->7(arg1)->9(arg1)->25:g>0,

完整路径2:25->21(arg1)->14(arg2)->5(arg1)->7(arg1)->25:g>0

完整路径3:25->21(arg1)->14(arg2)->5(arg1)->9(arg1)->25:g>0

完整路径4:19->21(arg1)->25:g<0

完整路径5:19->14(arg2)->5(arg1)->7(arg1)->25:g<0&&g>0

完整路径6:19->14(arg2)->5(arg1)->9(arg1)->25:g<0&&g>0。

在520,从所筛选出的完整数据流向路径集中去除开始节点和结束节点都与非数据写操作对应的数据流向路径,得到候选检查路径集。在图2中示出的数据访问操作都是数据写操作,从而上述6条完整数据流向路径都可以作为候选检查路径。

此外,可选地,在从所筛选出的完整数据流向路径集中得到候选检查路径集时,除了去除开始节点和结束节点都与非数据写操作对应的数据流向路径,还可以去除不可达数据流向路径。术语“不可达数据流向路径”是指路径条件存在矛盾的数据流向路径。例如,上述的完整路径5和6。经过上述处理,最终可以得到4条候选检查路径,即,完整路径1到4。

回到图1,在如上得到候选检查路径集后,在130,使用各个程序函数的线程流图,确定各条候选检查路径的开始节点和结束节点的全局标注。例如,可以使用各个程序函数的线程流图,根据各条候选检查路径中的函数调用关系确定各条候选检查路径的开始节点和结束节点的全局标注。这里,全局标注是考虑了上层程序函数和下层程序函数(即,完整的上下文)引入的标注影响后的节点标注。同样,在130中的标注处理过程也会采用上述标注处理规则。下面参照图2中示出的程序源代码来描述上述全局标注确定过程。上述全局标注确定过程同样采用自底向上的分析方法。

首先,分析程序函数Func D,可以得到分析结果:arg1->25(节点25的标注为空),在程序函数Func D被调用时,该分析结果可向上代入。

接着,分析程序函数Func C,可以得到分析结果arg2->19(节点19的标注为空):g<0,在程序函数Func C被调用时,该分析结果可向上代入。分析程序函数Func C,还可以得到分析结果19->21(arg1)(节点19的标注为空):g<0,代入程序函数Func D的分析结果(附加节点21调用程序函数FuncD所产生的标注t3),得到19->21(arg1)->25(节点19的标注为空,节点25的标注为t3):g<0,由此得到完整路径4的开始节点19和结束节点25的全局标注,即,节点19的全局标注为空,节点25的全局标注为t3。此外,分析程序函数Func C,还可以得到分析结果arg2->21(arg1),代入程序函数Func D的分析结果(附加节点21调用程序函数FuncD所产生的标注t3),由此得到arg2->21(arg1)->25(节点25的标注为t3),在程序函数Func C被调用时,该分析结果可向上代入。

随后,分析程序函数Func B,可以得到分析结果arg1->14(arg2),代入程序函数Func C的分析结果(标注为空),得到arg1->14(arg2)->19(节点19的标注为空):g<0。在程序函数Func B被调用时,该分析结果可向上代入。此外,分析程序函数Func B,还可以得到分析结果arg1->14(arg2)->21(arg1)->25(关注点25行标注为t3)。在程序函数Func B被调用时,可向上代入

分析程序函数Func A,可以得到分析结果5(arg1)->7(arg1):g>0,代入程序函数Func B和程序函数Func D的分析结果,程序函数Func B的分析结果为+t1,以及程序函数Func D的分析结果为+(T1+T3)),从而得到完整路径25->21(arg1)->14(arg2)->5(arg1)->7(arg1)->25:g>0(完整路径2)的开始节点25和结束节点25的全局标注,即,开始节点25的全局标注为t1+t3,以及结束节点25的全局标注为T1+T3。

分析程序函数Func A,还可以得到分析结果5(arg1)->9(arg1):g>0,代入程序函数Func B和程序函数Func D的分析结果,程序函数Func B的分析结果为+t1,以及程序函数Func D的分析结果为+(T1(g<=0)+T2(g>0)+T3),从而得到完整路径25->21(arg1)->14(arg2)->5(arg1)->9(arg1)->25:g>0(完整路径3)的开始节点25和结束节点25的全局标注,即,开始节点25的全局标注为t1+t3,以及结束节点25的全局标注为T1(g<=0)+T2(g>0)+T3。

分析程序函数Func A,还可以得到分析结果7(arg1)->9(arg1):g>0,代入程序函数Func D的分析结果(节点7+(T1+T3),节点9+(T1(g<=0)+T2(g>0)+T3)),从而得到完整路径25->7(arg1)->9(arg1)->25:g>0(完整路径1)的开始节点25和结束节点25的全局标注,即,开始节点25的全局标注为T1+T3,以及结束节点25的全局标注为T1(g<=0)+T2(g>0)+T3。

按照上述处理方法,在程序函数被调用时,将该程序函数的分析结果(非完整路径所对应的分析)代入调用者,从而使得数据流分析中引入了调用者到被调用者的上下文影响。此外,如上所述,在构建各个程序函数的线程流图时使用自底向上的分析,已经引入被调用者到调用者的上下文影响,从而使得数据流分析引入完整的上下文影响,进而使得数据流分析可以支持完整的上下文敏感分析。

接着,在140,根据各条候选检查路径的开始节点和结束节点的全局标注,确定数据竞争检查结果。在一个示例中,针对各条候选检查路径,在开始节点和结束节点的全局标注中存在相同的线程标签或者具有对应关系的线程标签时,确定该开始节点和该结束节点代表的程序语句存在数据竞争关系。这里,具有对应关系的线程标签是指具有对应关系的主线程标签和子线程标签。

例如,针对上述完整路径1到4,在完整路径1中,没有同时出现具有对应关系的主线程标签和子线程标签,从而开始节点25和结束节点25没有数据竞争。在完整路径2中,出现了T1/t1:g>0以及T3/t3:g>0,从而开始节点25和结束节点25通过T1/t1和T3/t3都可以竞争。在完整路径3中,出现T1/t1:g<=0&&g>0矛盾(不存在),以及T3/t3:g>0,从而开始节点25和结束节点25可以通过T3/t3可以竞争。在完整路径4中,没有同时出现具有对应关系的主线程标签和子线程标签,从而开始节点19和结束节点25没有数据竞争。综上所述,在图2中示出的并发程序的程序源代码中,第19行程序语句与第25行程序语句所代表的数据访问操作之间不存在数据竞争,但是第25行程序语句所代表的数据访问操作与自己之间存在数据竞争。

在上述数据竞争检查方法中,所构建的线程流图具有用于反映控制流向(数据流向)的边关系以及包含路径条件的标注,从而使得所构建的线程流图支持流敏感分析和路径敏感分析。此外,在构建各个程序函数的线程流图时,采用自底向上的方式执行,从而使得所构建的线程流图引入了下层程序函数的上下文影响。而且,在进行全局标注确定时,如果存在程序函数调用,则将该程序函数的分析结果(非完整路径所对应的分析)代入调用者,从而使得所确定出的全局标注引入了调用者到被调用者的上下文影响,由此使得所确定出的全局标注引入了完整的上下文影响。换言之,本说明书的实施例提供了一种支持流敏感、路径敏感和上下文敏感的数据竞争分析方案。

此外,利用上述数据竞争检查方法,通过在对并发程序的程序源代码进行自底向上的数据流分析时,将彼此之间的数据流向不会发生变化的多个真实节点合并为单个线程流图节点,可以减少所构建的线程流图的节点数目,由此提升数据竞争检查效率。

此外,利用上述数据竞争检查方法,通过对所述程序源代码进行自底向上的数据流分析,获取与数据访问操作对应的完整数据流向路径集,并从所筛选出的完整数据流向路径集中去除开始节点和结束节点都与非数据写操作对应的数据流向路径得到候选检查路径集,使得仅仅针对部分路径而非全部路径进行分析,由此减少数据竞争检查的工作量,提升数据竞争检查效率。此外,通过从所筛选出的完整数据流向路径集中进一步去除不可达数据流向路径,从而可以进一步减少所分析的路径数量,由此进一步减少数据竞争检查的工作量,提升数据竞争检查效率。

图6示出了根据本说明书的实施例的数据竞争检查装置600的方框图。如图6所示,数据竞争检查装置600包括线程流图构建单元610、检查路径集确定单元620、全局标注确定单元630和数据竞争检查单元640。

线程流图构建单元610被配置为通过对并发程序的程序源代码进行自底向上的数据流分析,为各个程序函数构建出各自的线程流图TFG(N,E,nentry,nexit),N代表线程流图节点集{n

可选地,在一个示例中,所述标注可以是操作数和操作组成的表达式,所述操作数包括常量线程标签、变量线程标签和路径条件中的至少一种,所述操作包括加操作、减操作和绑定操作中的至少一种。

检查路径集确定单元620被配置为通过对并发程序的程序源代码进行自底向上的数据流分析,确定候选检查路径集,所述候选检查路径集中的每条候选检查路径的开始节点代表针对内存位置的一个数据访问操作,结束节点代表针对所述内存位置的另一数据访问操作,其中,所述开始节点和结束节点中的至少一个代表针对所述内存位置的数据写操作。

全局标注确定单元630被配置为使用各个程序函数的线程流图TFG(N,E,nentry,nexit),确定候选检查路径集中的各条候选检查路径的开始节点和结束节点的全局标注。

数据竞争检查单元640被配置为根据各条候选检查路径的开始节点和结束节点的全局标注,确定并发程序的数据竞争检查结果。

图7示出了根据本说明书的实施例的线程流图构建单元700的一个实现示例的方框图。如图7所示,线程流图构建单元700包括节点创建模块710、边关系确定模块720和节点标注确定模块730。

节点创建模块710、边关系确定模块720和节点标注确定模块730被配置为自并发程序的最底层程序函数起,基于自底向上的数据流分析来执行线程流图构建过程,直到完成最顶层程序函数的线程流图构建处理。在未完成最顶层程序函数的处理时,选择该当前程序函数的一个上层程序函数或一个同层未处理程序函数作为下一处理过程的当前程序函数。

具体地,针对每个当前程序函数,节点创建模块710被配置为创建当前程序函数的线程流图节点,所述线程流图节点包括与每条程序语句对应的真实节点、根据线程创建和/或函数调用创建的虚拟节点、入口节点和出口节点。

边关系确定模块720被配置为根据对该当前程序函数的程序源代码的数据流分析,确定该当前程序函数的各个线程流图节点之间的有向边关系。

节点标注确定模块730被配置为使用所确定出的有向边关系所对应的标注处理规则以及下层程序函数的节点标注,确定该当前程序函数的各个线程流图节点的标注。

图8示出了根据本说明书的实施例的检查路径集确定单元800的一个实现示例的方框图。如图8所示,检查路径集确定单元800包括完整路径集获取模块810和检查路径集确定模块820。

完整路径集获取模块810被配置为通过对程序源代码进行自底向上的数据流分析,获取与数据访问操作对应的完整数据流向路径集,所述完整数据流向路径集中的每条完整数据流向路径的开始节点代表针对内存位置的一个数据访问操作,结束节点代表针对所述内存位置的另一数据访问操作。

检查路径集确定模块820被配置为从所筛选出的完整数据流向路径集中去除开始节点和结束节点都与非数据写操作对应的数据流向路径,得到候选检查路径集。

如上参照图1到图8,对根据本说明书实施例的数据竞争检查方法和数据竞争检查装置进行了描述。上面的数据竞争检查装置可以采用硬件实现,也可以采用软件或者硬件和软件的组合来实现。

图9示出了根据本说明书的实施例的基于计算机实现的数据竞争检查装置900的示意图。如图9所示,数据竞争检查装置900可以包括至少一个处理器910、存储器(例如,非易失性存储器)920、内存930和通信接口940,并且至少一个处理器910、存储器920、内存930和通信接口940经由总线960连接在一起。至少一个处理器910执行在存储器中存储或编码的计算机程序(即,上述以软件形式实现的元素)。

在一个实施例中,在存储器中存储计算机程序,其当执行时使得至少一个处理器910:通过对并发程序的程序源代码执行自底向上的数据流分析,确定候选检查路径集,候选检查路径集中的每条候选检查路径的开始节点代表针对内存位置的一个数据访问操作,结束节点代表针对所述内存位置的另一数据访问操作,其中,所述开始节点和结束节点中的至少一个代表针对所述内存位置的数据写操作;使用各个程序函数的线程流图TFG(N,E,nentry,nexit),确定候选检查路径集中的各条候选检查路径的开始节点和结束节点的全局标注;以及根据各条候选检查路径的开始节点和结束节点的全局标注,确定并发程序的数据竞争检查结果,其中,各个程序函数的线程流图TFG(N,E,nentry,nexit)通过对程序源代码进行自底向上的数据流分析构建,N代表线程流图节点集{n

应该理解,在存储器中存储的计算机程序被执行时使得至少一个处理器910执行本说明书的各个实施例中的结合图1-图8描述的各种操作和功能。

根据一个实施例,提供了一种比如计算机可读介质(例如,非暂时性计算机可读介质)的程序产品。计算机可读介质可以具有计算机程序(即,上述以软件形式实现的元素),该计算机程序被处理器执行时,使得处理器执行本说明书的各个实施例中的结合图1-图8描述的各种操作和功能。具体地,可以提供配有可读存储介质的系统或者装置,在该可读存储介质上存储着实现上述实施例中任一实施例的功能的软件程序代码,且使该系统或者装置的计算机或处理器读出并执行存储在该可读存储介质中的计算机程序。

在这种情况下,从可读介质读取的程序代码本身可实现上述实施例中任何一项实施例的功能,因此计算机可读代码和存储计算机可读代码的可读存储介质构成了本说明书的一部分。

可读存储介质的实施例包括软盘、硬盘、磁光盘、光盘(如CD-ROM、CD-R、CD-RW、DVD-ROM、DVD-RAM、DVD-RW、DVD-RW)、磁带、非易失性存储卡和ROM。可选择地,可以由通信网络从服务器计算机上或云上下载程序代码。

根据一个实施例,提供一种计算机程序产品,该计算机程序产品包括计算机程序,该计算机程序当被处理器执行时,使得处理器执行本说明书的各个实施例中以上结合图1-图8描述的各种操作和功能。

本领域技术人员应当理解,上面公开的各个实施例可以在不偏离发明实质的情况下做出各种变形和修改。因此,本发明的保护范围应当由所附的权利要求书来限定。

需要说明的是,上述各流程和各系统结构图中不是所有的步骤和单元都是必须的,可以根据实际的需要忽略某些步骤或单元。各步骤的执行顺序不是固定的,可以根据需要进行确定。上述各实施例中描述的装置结构可以是物理结构,也可以是逻辑结构,即,有些单元可能由同一物理实体实现,或者,有些单元可能分由多个物理实体实现,或者,可以由多个独立设备中的某些部件共同实现。

以上各实施例中,硬件单元或模块可以通过机械方式或电气方式实现。例如,一个硬件单元、模块或处理器可以包括永久性专用的电路或逻辑(如专门的处理器,FPGA或ASIC)来完成相应操作。硬件单元或处理器还可以包括可编程逻辑或电路(如通用处理器或其它可编程处理器),可以由软件进行临时的设置以完成相应操作。具体的实现方式(机械方式、或专用的永久性电路、或者临时设置的电路)可以基于成本和时间上的考虑来确定。

上面结合附图阐述的具体实施方式描述了示例性实施例,但并不表示可以实现的或者落入权利要求书的保护范围的所有实施例。在整个本说明书中使用的术语“示例性”意味着“用作示例、实例或例示”,并不意味着比其它实施例“优选”或“具有优势”。出于提供对所描述技术的理解的目的,具体实施方式包括具体细节。然而,可以在没有这些具体细节的情况下实施这些技术。在一些实例中,为了避免对所描述的实施例的概念造成难以理解,公知的结构和装置以框图形式示出。

本公开内容的上述描述被提供来使得本领域任何普通技术人员能够实现或者使用本公开内容。对于本领域普通技术人员来说,对本公开内容进行的各种修改是显而易见的,并且,也可以在不脱离本公开内容的保护范围的情况下,将本文所定义的一般性原理应用于其它变型。因此,本公开内容并不限于本文所描述的示例和设计,而是与符合本文公开的原理和新颖性特征的最广范围相一致。

去获取专利,查看全文>

相似文献

  • 专利
  • 中文文献
  • 外文文献
获取专利

客服邮箱:kefu@zhangqiaokeyan.com

京公网安备:11010802029741号 ICP备案号:京ICP备15016152号-6 六维联合信息科技 (北京) 有限公司©版权所有
  • 客服微信

  • 服务号