Page 94 - 《软件学报》2021年第9期
P. 94
2718 Journal of Software 软件学报 Vol.32, No.9, September 2021
PLST 中的节点表示代码块,能够有效降低复杂性并提高特征提取的效率.令待生成特征向量的代码块为目标代
码块,则在 PLST 基础上如何提取目标代码块的附近的代码特征,即如何组合目标代码块附近的代码块子向量
是一个难题.原因在于:
• 第一,影响日志打印位置的程序范围很难确定.从编程习惯的角度来看,日志打印语句被用来记录刚刚
发生的某个事件或行为,因此对于目标代码块而言,最影响日志打印决策的程序片段应该是该代码块
之前的程序片段;另外,代码块的日志打印决策也受到程序功能和代码逻辑的影响,而函数是程序中最
小的功能逻辑单元,因此本方法将函数内目标代码块及其之前的代码块子向量进行组合,从而完整地
表示目标代码块;
• 第二,邻近代码块的上下文信息极其相似,会极大地混淆日志打印决策模型.两个邻近代码块周围的程
序文本非常相似,甚至大部分程序文本是相同的,但其数据标签却很有可能是完全不同的.这将极大地
混淆决策模型,降低决策效果.针对该问题,本节提出一种逆序组合的方法,采用相对位置代替绝对位
置,组合目标代码块前的代码块子向量,从而生成目标代码块的特征向量.
每棵 PLST 代表一个函数,根表示函数名称,节点表示代码块.每一个代码块所处的层由其位于程序中的嵌
套层次决定,同一层中的代码块严格遵循其在代码中的先后顺序排布.每个 PLST 通过深度优先遍历可以恢复
成原程序代码.为获取 PLST,不同编程语言的代码块由于语法不同而需要少量适应性处理.例如,Java 中的分层
标识为“{”,Python 中则为 4 个空格.图 3 展示了程序结构和功能完全相同而编程语言不同的两段代码,这两段代
码可以被转换成同一棵 PLST.
(a) Java 代码示例 (b) Python 代码示例 (c) 程序层次树
Fig.3 Example of PLST of different programming languages
图 3 不同编程语言代码的相同程序层次树表示
接下来,利用每个代码块的子向量及其所属的 PLST,提出一种逆序组合的方法为每个代码块生成完整的特
征向量.令每个代码块的子向量长度为 l,逆序组合方法首先遍历目标代码块的左兄弟节点,以目标代码块起始,
逆序组合其所有左兄弟节点的子向量,设置一个节点上限 m:如果逆序组合的节点数量超过节点上限 m,则删除
超过部分的子向量;如果不足 m,以零向量补齐.组合完毕的向量被称为层向量,其长度为 l×m.然后,以相同的方
法处理目标代码块的父节点,即逆序组合目标代码块的父节点的左兄弟节点,直到节点上限 m,得到目标代码块
父节点的层向量.然后,将该层向量排布在目标节点的层向量之后,设置一个层数上限 n.以此类推,组合每层向量
直到层数达到上限 n.如果从目标代码块到 PLST 的根节点的总层数小于 n,则以零向量补齐至完整特征向量长
度达到 l×m×n.
基于逆序组合的特征向量生成方法的细节如算法 1 所述,输入是程序层次树和待生成特征向量的目标代
码块,输出是目标代码块的特征向量.算法从目标代码块在程序层次树中的层起始,由下至上遍历,直到根节点
或达到层数上限 n(第 2 行).对于每一层,以临时目标节点(temp_tb)为起始依次遍历其左兄弟节点,临时目标节点
被定义为目标节点及其祖先节点,上层的临时目标节点为下层临时目标节点的父亲节点.依照遍历顺序依次排
列临时目标节点和其左兄弟节点,直到达到节点上限 m(第 5 行~第 10 行).
在逆序组合的过程中,当某节点存在孩子节点时,如果仅组合该节点对应的子向量是不合理的.原因在于:
从代码角度看,其孩子节点也同样位于目标代码块之前,因此会对目标代码块的日志打印决策产生影响.为解决