Python跟踪和条件跳转

Dan*_*ano 6 python testing trace branch

我正在使用该功能为Python 编写一个concolic引擎sys.settrace().

这种执行过程中的主要任务是记录输入变量的约束.约束只不过是if语句的条件,它创建了两个分支('then'和'else'分支).

当执行完成时,引擎选择一个约束并为输入找到适当的值,以便执行将沿着另一个分支向下执行(在执行x时,它进入'then'分支,在执行x + 1时它继续执行'else'分支).

这是为了说明我为什么要做我想做的事情......

通过组合settrace()dis模块,我可以看到每个源代码行的字节码,就在它执行之前.通过这种方式,我可以轻松记录执行期间出现的if条件.

但后来我遇到了大问题.我需要知道if是哪种方式,执行所采用的分支.所以,如果我的代码是这样的:

if x > a:
  print x
else:
  print a
Run Code Online (Sandbox Code Playgroud)

在某一点上,我的追踪事情会看到:

t: if x > 0: 
Run Code Online (Sandbox Code Playgroud)

然后python解释器将执行if和跳转(或不跳转)某处.我会看到:

t + 1: print x
Run Code Online (Sandbox Code Playgroud)

t + 1"then"分支或"else"分支中的指令也是如此?请记住,跟踪功能只能看到当前块中的某些字节码.

我知道有两种方法可以做到这一点.一种是评估条件,以确切地看出它是真还是假.只有在没有副作用时才有效.

另一种方法是尝试查看和指令指针,t + 1并尝试了解我们在代码中的位置.这是我现在使用的方式,但它非常微妙,因为t + 1我可以发现自己完全不同(另一个模块,内置函数等).

所以最后,我的问题是:有没有办法从Python本身,或从C模块/扩展/什么,最后条件跳转的结果?

另外,是否有更细粒度的跟踪选项?类似于一次执行一个操作码的字节码.凭借settrace()功能,我获得的最大分辨率是整个源代码行.

在最坏的情况下,我认为我可以修改Python解释器来公开这些信息,但出于显而易见的原因,我会将其作为最后的手段.

Ned*_*der 6

跟踪工具中没有关于最后一个分支的信息.

我在coverage.py中实现分支覆盖测量的做法是为最后一行执行的每个堆栈帧保留一条记录,然后在下次调用跟踪函数时,我可以记录一对形成一个from-的行号.执行弧.

关于更细粒度的跟踪:您可以欺骗Python解释器为您提供字节代码信息.我在这里描述了我的实验:Wicked hack:Python字节码跟踪

我很想知道这项工作的进展情况!


Dan*_*ano 4

最后这就是我所做的。我实现了 AST 工具,它运行得很好。

通过使用 AST,您需要getattr()通过创建临时变量将所有函数调用(还有属性和订阅、归因于 和 朋友)移出 if 条件。此外,您还需要拆分andor运算符。

然后在每个分支的开头添加对您自己的函数的调用,并Truethen分支和elseFalse分支添加一个布尔参数。

之后,我编写了一个 AST 到源转换器(网上有一个,但不适用于当前的 Python 版本)。

使用 AST 非常容易且非常简单,我最终做了三个转换过程,还添加了一些import语句。

作为示例,这是第一遍。如果条件包含ororand运算符,则它会拆分:

class SplitBoolOpPass1(ast.NodeTransformer):
  def visit_If(self, node):
      while isinstance(node.test, ast.BoolOp):
        new_node = ast.If(test=node.test.values.pop(), body=node.body, orelse=node.orelse)
        if isinstance(node.test.op, ast.And):
          if len(node.test.values) == 1:
            node.test = node.test.values[0]
          node.body = [new_node]
        else:
          if len(node.test.values) == 1:
            node.test = node.test.values[0]
          node.orelse = [new_node]
      node = self.generic_visit(node) # recusion
      return node
Run Code Online (Sandbox Code Playgroud)

可能它对于代码覆盖应用程序来说不是很有用,因为它会严重扰乱代码。