本发明涉及程序分析领域,尤其涉及一种程序分析过程可视化方法及系统。
背景技术:
目前程序分析工具或软件都着重于程序分析的方法,具体的分析过程较抽象不直观,不适合教学。在教学过程中,我们发现常规的程序分析软件无法将程序分析的过程及结果直观形象地展现给学生,分析过程也不可控。
例如代码缺陷静态检查工具cppcheck,能对c程序进行严格的逻辑缺陷检测,但该软件只能给出检测结果,中间的分析结果不可见,分析过程不可控。
技术实现要素:
本发明要解决的问题是提供一种程序分析过程可视化方法及系统。
为了解决上述技术问题,本发明提供了程序分析过程可视化方法,包括:
步骤一、程序转换为中间表示。其特征在于:使用开源编译器gcc携带参数-fdump-translation-unit,对源程序进行词法分析、语法分析,并生成程序的以节点为单位的中间表示;
步骤二、中间表示标准化处理。其特征在于中间表示的标准化处理方法:
将程序的中间表示规范化,包括规范每个节点成一行、删除节点各个字段内部多余的空格;
消除中间表示里与数据流、控制流无关的信息。首先将srcp字段值为源程序文件名的节点标注为有用节点,将srcp字段为其它值的节点标注为无用节点,其它节点为待定节点;其次将有用节点的标为待定的节点标记为有用节点,将无用节点的标为待定的节点标记为无用节点,将冲突节点(父节点既有有用节点又有无用节点)标记为有用节点;最后删除无用节点,并删除每个节点中的srcp、algn、type字段及指向节点已被删除的字段;
步骤三、中间表示的元级操作。其特征在于元级操作方法:在中间表示一级上对程序中的各种语法成分进行增删改查操作;
首先,将程序的中间表示转换为邻接表形式的抽象语法树ast,其方法如下:
设程序的原始中间表示有n个节点信息,经过标准化处理后剩余m个节点,则将m个中间表示节点分别转换为邻接表的m个元素:
1)将字符串形式的中间表示节点拆分成“@kiname_type{attr:value}+”格式的子串,其中1<=ki<=n,1<=i<=m;
2)将非连续编号的m个ki映射为邻接表的m个连续编号,即建立编号映射表map={k1->1,k2->2,......km->m};
3)取map(ki)作为邻接表元素的编号;name_type作为邻接表元素的类型;若value是“@kj”形式的子串,其中1<=kj<=n,1<=j<=m,则将编号为map(kj)的邻接表元素作为当前元素的孩子节点,否则value直接作为当前邻接表元素的一个属性值;
其次,在邻接表上对程序进行元级操作,其方法如下:
1)建立邻接表形式的抽象语法树与.net中的树控件treenode之间的映射关系,将抽象语法树以便于操作的控件的形式呈现给用户。映射关系建立规则为:邻接表的一个元素建立treenode中的一个node节点,邻接表元素属性作为node的文本,邻接表元素的孩子作为node的子节点;同时设置node的tag值为指向邻接表当前元素的指针;
2)修改操作。在treenode中选择一个node,将该node的值显示于文本控件中供用户进行修改,并将修改后的值填写回treenode的node节点,同时修改node节点的tag值所指向的邻接表元素的相应的属性;
3)删除操作。删除treenode中选中的节点node,同时删除node节点的tag值所指向的邻接表元素的节点及其孩子节点;
4)添加操作。设用户需要添加的信息为attr:value格式,则,若value不是以“@”开始的字符串,那么attr:value直接添加于treenode控件中,使其为当前选中node的孩子节点,同时在node节点的tag值所指向的邻接表元素中增加value属性值;若value是以“@k”开始的字符串,那么需判定k的范围,若k<=m即表示k是已经存在的节点,则在treenode中找到编号为k的node1节点,并将node1节点为根的子树添加到treenode控件中,使node1作为当前选中节点node的孩子节点,同时在node节点的tag值节点所指向的邻接表元素中增加“@k”属性即可;若k>m即表示k是新添加的节点,则构造一个新的node1节点,并使其为当前选中node的孩子节点,同时在node的tag值所指向的邻接表元素中增加属性值“@k”并在邻接表的最后增加一个编号为k的元素;
5)查找操作。根据用户提供的关键字,在treenode的各个节点中进行模糊匹配,若匹配成功则突出显示该节点;
最后,将邻接表形式的抽象语法树可视化,具体方法如下:
1)广度优先遍历邻接表形式的抽象语法树,根据语法树的节点信息及节点之间的关系,构造如下形式的ast.dot文档:
digraphast{
nodesep=1.0
node[shape=record]
structinfolist
edgeinfolist
}
其中,
structinfolist=structinfo*
structinfo=structname[label=”attrinfo”]
structname=string
attrinfo=<fn>attr(|<fk>attr)*
attr=string
edgeinfolist=edgeinfo*
edgeinfo=structname1:fn->structname2:fk
生成ast.dot文档的具体方法是:遍历抽象语法树过程中当访问一个节点时,首先,以struct加节点编号structk的形式建立.dot的一个名字structname,节点的所有属性value按照<f0>value0|<f1>value1|…|<fn>valuen的形式建立structname的attrinfo值;其次,对当前节点的每个孩子节点建立一个edgeinfo,即structname1:fn->structname2:fk,其中,structname1为当前节点的名字,fn为其属性编号,structname2为孩子节点的名字,fk为孩子节点的属性编号;按照以上方法广度遍历抽象语法树的每个节点,最终形成.dot格式的文档;
2)调用图形可视化工具graphviz生成抽象语法树的图片文件。命令格式为:“dot–tjpgast.dot–oast.jpg”;
打开ast.jpg文件,实现图片的浏览、缩放、移动等功能;
步骤四、程序分析结果可视化。其特征在于程序分析结果可视化方法具体包括:
1)构造控制流图:新建一个基本块,按照广度优先遍历邻接表形式的抽象语法树,针对语法树中的各类节点分别处理:
a.“function_decl”节点与“bind_expr”节点查找其“body”字段指向的孩子节点继续遍历;
b.“statement_list”节点查找其所有孩子节点,依次递归遍历每个孩子节点为根的子树;
c.“cond_expr”节点,首先将分支条件“op0”字段的信息加入到当前基本快,并结束当前基本块b1,其次分别递归遍历then分支“op1”字段和else分支“op2”字段所指向的孩子节点为根的子树,分别形成以b2和b3为根的子图,最后设置基本块b1的后继节点分别为b2和b3;
d.“goto_expr”节点,结束当前基本块,在label栈中查找该跳转语句的目标标号所在的基本块是否已经创建,若已创建则设置当前基本块的孩子节点为目标标号所在基本块,否则将该基本块信息保存如goto栈;
e.“label_expe”节点,结束当前基本块,新建一个基本块,设置当前基本块的后继节点为新建的基本块,在goto栈中查找是否存在需要跳转到新建基本块的语句,若找到,则将找到的基本块的后继设置为新建基本块,否则将新建块的信息保存入label栈;
f.其它节点,将语句信息加入到当前基本块;
2)构造控制依赖图:创建控制依赖图的根节点,广度优先遍历抽象语法树,针对语法树中不同节点分别处理:
a.“function_decl”节点,创建函数信息为内容的新节点并使其成为根节点的孩子节点,将新建节点设为当前节点,继续遍历以“body”字段所指向的节点为根的抽象语法树;
b.“bind_expr”节点,创建分程序信息为内容的新节点并使其成为当前节点的孩子节点,将新建节点设为当前节点,继续遍历以“body”字段所指向的节点为根的抽象语法树;
c.“statement_list”节点,创建语句列表信息为内容的新节点并使其成为当前节点的孩子节点,将新建节点设为当前节点,递归遍历所有字段的孩子节点为根的子树;
d.“cond_expr”节点,创建分支语句op0字段为内容的新节点并使其成为当前节点的孩子节点,将新建节点设为当前节点,分别递归遍历op1和op2字段所指向的孩子节点为根的子树;
e.其它节点,创建相应的新节点并使其成为当前节点的孩子节点;
3)广度优先遍历控制流图(控制依赖图),依据控制流图中节点信息和节点之间的关系,生成如下形式的cfg.dot文档:
digraphast{
nodesep=1.0
start[label=”entry”,shape=box]
nodeinfolist
edgeinfolist
}
其中,
nodeinfolist=nodeinfo*
nodeinfo=nodename[label=”attrinfo”,shape=box]
edgeinfolist=edgeinfo*
edgeinfo=nodenamei->nodenamej([label=”t|f”])?
生成cfg.dot文档的具体方法是:遍历控制流图过程中当访问一个节点时,首先,以node加节点编号nodek的形式建立cfg.dot的一个名字nodename,节点的所有属性value按照“value0\nvalue1\n…\nvaluek”的形式建立nodename的attrinfo值。其次,对当前节点的每个孩子节点建立一个edgeinfo,若当前节点只有一个孩子节点,则建立如下形式的edgeinfo,即nodename1->nodename2,其中,nodename1为当前节点的名字,nodename2为孩子节点的名字;若当前节点有两个孩子节点,则对真分支节点建立如下形式的edgeinfo,nodename1->nodename2[label=”t”],对假分支建立如下形式的edgeinfo,nodename1->nodename2[label=”f”]。按照以上方法广度遍历抽象语法树的每个节点,最终形成cfg.dot格式的文档;
4)调用图形可视化工具graphviz生成抽象语法树的图片文件。命令格式为:“dot–tjpgast.dot–oast.jpg”;
5)打开ast.jpg文件,实现图片的浏览、缩放、移动等功能。
本发明提供了程序分析过程可视化软件,包括:
输入模块,用于读入待分析的源程序,
转换模块,用于将源程序转换为中间表示,
标准化模块,用于将中间表示规范化和化简,
元级操作模块,提供程序中间表示一级的各成分的增删改查等操作,
图形化展示分析结果模块,用于图像化展示程序分析的抽象语法树、控制流图、控制依赖图。
进一步地,所属展示模块具体包括:
图形化输出程序的抽象语法树,
图形化输出程序的控制流图,
图形化输出程序的控制依赖图。
由上可知,本发明方法和系统能够直观地展示程序分析的过程和分析结果。
附图说明
图1为本发明所述的一种程序分析过程可视化方法及系统所采用的程序整体功能流程图。
图2为本发明所述的一种程序分析过程可视化方法及系统中的实施例一所揭示的输入源程序。
图3为本发明所述的一种程序分析过程可视化方法及系统中的实施例一所揭示的程序抽象语法树。
图4为本发明所述的一种程序分析过程可视化方法及系统中的实施例二所揭示的程序控制流图。
具体实施方式
为使本发明的目的、技术方案、及优点更加清楚明白,下面结合附图对本发明涉及的一种服务功能授权的方法及系统的具体实施实例进行进一步详细描述。
本发明所述的程序过程可视化方法的基本原理为:
参见图1,本发明所采用的程序过程可视化方法由四个模块构成,即程序转换模块、标准化模块、元级操作模块、分析结果可视化模块;
程序转换模块:将输入的源程序转换为中间表示;
标准化模块:删除中间表示中的与数据流、控制流无关的信息;
元级操作模块:在抽象语法树上对程序的各个语法成分进行增删改查等操作;
分析结果可视化模块:构造程序的控制流图、控制依赖图,并以图片的形式直观地展示。
本发明所述的一种程序分析过程可视化方法的步骤如下:
步骤一、程序转换为中间表示。其特征在于:使用开源编译器gcc携带参数-fdump-translation-unit,对源程序进行词法分析、语法分析,并生成程序的以节点为单位的中间表示;
步骤二、中间表示标准化处理。其特征在于中间表示的标准化处理方法:
将程序的中间表示规范化,包括规范每个节点成一行、删除节点各个字段内部多余的空格;
消除中间表示里与数据流、控制流无关的信息。首先将srcp字段值为源程序文件名的节点标注为有用节点,将srcp字段为其它值的节点标注为无用节点,其它节点为待定节点;其次将有用节点的标为待定的节点标记为有用节点,将无用节点的标为待定的节点标记为无用节点,将冲突节点(父节点既有有用节点又有无用节点)标记为有用节点;最后删除无用节点,并删除每个节点中的srcp、algn、type字段及指向节点已被删除的字段;
步骤三、中间表示的元级操作。其特征在于元级操作方法:在中间表示一级上对程序中的各种语法成分进行增删改查操作;
首先,将程序的中间表示转换为邻接表形式的抽象语法树ast,其方法如下:
设程序的原始中间表示有n个节点信息,经过标准化处理后剩余m个节点,则将m个中间表示节点分别转换为邻接表的m个元素:
1)将字符串形式的中间表示节点拆分成“@kiname_type{attr:value}+”格式的子串,其中1<=ki<=n,1<=i<=m;
2)将非连续编号的m个ki映射为邻接表的m个连续编号,即建立编号映射表map={k1->1,k2->2,......km->m};
3)取map(ki)作为邻接表元素的编号;name_type作为邻接表元素的类型;若value是“@kj”形式的子串,其中1<=kj<=n,1<=j<=m,则将编号为map(kj)的邻接表元素作为当前元素的孩子节点,否则value直接作为当前邻接表元素的一个属性值;
其次,在邻接表上对程序进行元级操作,其方法如下:
1)建立邻接表形式的抽象语法树与.net中的树控件treenode之间的映射关系,将抽象语法树以便于操作的控件的形式呈现给用户。映射关系建立规则为:邻接表的一个元素建立treenode中的一个node节点,邻接表元素属性作为node的文本,邻接表元素的孩子作为node的子节点;同时设置node的tag值为指向邻接表当前元素的指针;
2)修改操作。在treenode中选择一个node,将该node的值显示于文本控件中供用户进行修改,并将修改后的值填写回treenode的node节点,同时修改node节点的tag值所指向的邻接表元素的相应的属性;
3)删除操作。删除treenode中选中的节点node,同时删除node节点的tag值所指向的邻接表元素的节点及其孩子节点;
4)添加操作。设用户需要添加的信息为attr:value格式,则,若value不是以“@”开始的字符串,那么attr:value直接添加于treenode控件中,使其为当前选中node的孩子节点,同时在node节点的tag值所指向的邻接表元素中增加value属性值;若value是以“@k”开始的字符串,那么需判定k的范围,若k<=m即表示k是已经存在的节点,则在treenode中找到编号为k的node1节点,并将node1节点为根的子树添加到treenode控件中,使node1作为当前选中节点node的孩子节点,同时在node节点的tag值节点所指向的邻接表元素中增加“@k”属性即可;若k>m即表示k是新添加的节点,则构造一个新的node1节点,并使其为当前选中node的孩子节点,同时在node的tag值所指向的邻接表元素中增加属性值“@k”并在邻接表的最后增加一个编号为k的元素;
5)查找操作。根据用户提供的关键字,在treenode的各个节点中进行模糊匹配,若匹配成功则突出显示该节点;
最后,将邻接表形式的抽象语法树可视化,具体方法如下:
1)广度优先遍历邻接表形式的抽象语法树,根据语法树的节点信息及节点之间的关系,构造如下形式的ast.dot文档:
digraphast{
nodesep=1.0
node[shape=record]
structinfolist
edgeinfolist
}
其中,
structinfolist=structinfo*
structinfo=structname[label=”attrinfo”]
structname=string
attrinfo=<fn>attr(|<fk>attr)*
attr=string
edgeinfolist=edgeinfo*
edgeinfo=structname1:fn->structname2:fk
生成ast.dot文档的具体方法是:遍历抽象语法树过程中当访问一个节点时,首先,以struct加节点编号structk的形式建立.dot的一个名字structname,节点的所有属性value按照<f0>value0|<f1>value1|…|<fn>valuen的形式建立structname的attrinfo值;其次,对当前节点的每个孩子节点建立一个edgeinfo,即structname1:fn->structname2:fk,其中,structname1为当前节点的名字,fn为其属性编号,structname2为孩子节点的名字,fk为孩子节点的属性编号;按照以上方法广度遍历抽象语法树的每个节点,最终形成.dot格式的文档;
2)调用图形可视化工具graphviz生成抽象语法树的图片文件。命令格式为:“dot–tjpgast.dot–oast.jpg”;
打开ast.jpg文件,实现图片的浏览、缩放、移动等功能;
步骤四、程序分析结果可视化。其特征在于程序分析结果可视化方法具体包括:
1)构造控制流图:新建一个基本块,按照广度优先遍历邻接表形式的抽象语法树,针对语法树中的各类节点分别处理:
a.“function_decl”节点与“bind_expr”节点查找其“body”字段指向的孩子节点继续遍历;
b.“statement_list”节点查找其所有孩子节点,依次递归遍历每个孩子节点为根的子树;
c.“cond_expr”节点,首先将分支条件“op0”字段的信息加入到当前基本快,并结束当前基本块b1,其次分别递归遍历then分支“op1”字段和else分支“op2”字段所指向的孩子节点为根的子树,分别形成以b2和b3为根的子图,最后设置基本块b1的后继节点分别为b2和b3;
d.“goto_expr”节点,结束当前基本块,在label栈中查找该跳转语句的目标标号所在的基本块是否已经创建,若已创建则设置当前基本块的孩子节点为目标标号所在基本块,否则将该基本块信息保存如goto栈;
e.“label_expe”节点,结束当前基本块,新建一个基本块,设置当前基本块的后继节点为新建的基本块,在goto栈中查找是否存在需要跳转到新建基本块的语句,若找到,则将找到的基本块的后继设置为新建基本块,否则将新建块的信息保存入label栈;
f.其它节点,将语句信息加入到当前基本块;
2)构造控制依赖图:创建控制依赖图的根节点,广度优先遍历抽象语法树,针对语法树中不同节点分别处理:
a.“function_decl”节点,创建函数信息为内容的新节点并使其成为根节点的孩子节点,将新建节点设为当前节点,继续遍历以“body”字段所指向的节点为根的抽象语法树;
b.“bind_expr”节点,创建分程序信息为内容的新节点并使其成为当前节点的孩子节点,将新建节点设为当前节点,继续遍历以“body”字段所指向的节点为根的抽象语法树;
c.“statement_list”节点,创建语句列表信息为内容的新节点并使其成为当前节点的孩子节点,将新建节点设为当前节点,递归遍历所有字段的孩子节点为根的子树;
d.“cond_expr”节点,创建分支语句op0字段为内容的新节点并使其成为当前节点的孩子节点,将新建节点设为当前节点,分别递归遍历op1和op2字段所指向的孩子节点为根的子树;
e.其它节点,创建相应的新节点并使其成为当前节点的孩子节点;
3)广度优先遍历控制流图(控制依赖图),依据控制流图中节点信息和节点之间的关系,生成如下形式的cfg.dot文档:
digraphast{
nodesep=1.0
start[label=”entry”,shape=box]
nodeinfolist
edgeinfolist
}
其中,
nodeinfolist=nodeinfo*
nodeinfo=nodename[label=”attrinfo”,shape=box]
edgeinfolist=edgeinfo*
edgeinfo=nodenamei->nodenamej([label=”t|f”])?
生成cfg.dot文档的具体方法是:遍历控制流图过程中当访问一个节点时,首先,以node加节点编号nodek的形式建立cfg.dot的一个名字nodename,节点的所有属性value按照“value0\nvalue1\n…\nvaluek”的形式建立nodename的attrinfo值。其次,对当前节点的每个孩子节点建立一个edgeinfo,若当前节点只有一个孩子节点,则建立如下形式的edgeinfo,即nodename1->nodename2,其中,nodename1为当前节点的名字,nodename2为孩子节点的名字;若当前节点有两个孩子节点,则对真分支节点建立如下形式的edgeinfo,nodename1->nodename2[label=”t”],对假分支建立如下形式的edgeinfo,nodename1->nodename2[label=”f”]。按照以上方法广度遍历抽象语法树的每个节点,最终形成cfg.dot格式的文档;
4)调用图形可视化工具graphviz生成抽象语法树的图片文件。命令格式为:“dot–tjpgast.dot–oast.jpg”;
5)打开ast.jpg文件,实现图片的浏览、缩放、移动等功能。
下面提供两个具体实施方式对本发明所述的程序分析过程可视化方法及系统的具体过程进行说明:
一、本实施例为构造程序抽象语法树。本实施例中,我们对一段c程序进行元级操作并输出其抽象语法树,具体过程如下:
步骤一、输入图2所示的源程序,将其转换为中间表示;
步骤二、将中间表示规范化并化简;
步骤三、构造抽象语法树,并在其上进行所需的元级操作;
步骤四、图形化输出抽象语法树,参见图3;
二、本实施例为构造控制流图。本实施例中,我们构造了一段c程序的控制流图,具体过程如下:
步骤一、输入图2所示的源程序,将其转换为中间表示;
步骤二、将中间表示规范化并化简;
步骤三、构造程序的抽象语法树;
步骤四、构造程序的控制流图并图形化输出控制流图,参见图4;
以上具体实施方式仅用于说明本发明,而非用于限定本发明。