DextIR.py
4.33 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# DExTer : Debugging Experience Tester
# ~~~~~~ ~ ~~ ~ ~~
#
# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
# See https://llvm.org/LICENSE.txt for license information.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
from collections import OrderedDict
import os
from typing import List
from dex.dextIR.BuilderIR import BuilderIR
from dex.dextIR.DebuggerIR import DebuggerIR
from dex.dextIR.StepIR import StepIR, StepKind
def _step_kind_func(context, step):
if (step.current_location.path is None or
not os.path.exists(step.current_location.path)):
return StepKind.FUNC_UNKNOWN
if any(os.path.samefile(step.current_location.path, f)
for f in context.options.source_files):
return StepKind.FUNC
return StepKind.FUNC_EXTERNAL
class DextIR:
"""A full Dexter test report.
This is composed of all the other *IR classes. They are used together to
record Dexter inputs and the resultant debugger steps, providing a single
high level access container.
The Heuristic class works with dexter commands and the generated DextIR to
determine the debugging score for a given test.
Args:
commands: { name (str), commands (list[CommandIR])
"""
def __init__(self,
dexter_version: str,
executable_path: str,
source_paths: List[str],
builder: BuilderIR = None,
debugger: DebuggerIR = None,
commands: OrderedDict = None):
self.dexter_version = dexter_version
self.executable_path = executable_path
self.source_paths = source_paths
self.builder = builder
self.debugger = debugger
self.commands = commands
self.steps: List[StepIR] = []
def __str__(self):
colors = 'rgby'
st = '## BEGIN ##\n'
color_idx = 0
for step in self.steps:
if step.step_kind in (StepKind.FUNC, StepKind.FUNC_EXTERNAL,
StepKind.FUNC_UNKNOWN):
color_idx += 1
color = colors[color_idx % len(colors)]
st += '<{}>{}</>\n'.format(color, step)
st += '## END ({} step{}) ##\n'.format(
self.num_steps, '' if self.num_steps == 1 else 's')
return st
@property
def num_steps(self):
return len(self.steps)
def _get_prev_step_in_this_frame(self, step):
"""Find the most recent step in the same frame as `step`.
Returns:
StepIR or None if there is no previous step in this frame.
"""
return next((s for s in reversed(self.steps)
if s.current_function == step.current_function
and s.num_frames == step.num_frames), None)
def _get_new_step_kind(self, context, step):
if step.current_function is None:
return StepKind.UNKNOWN
if len(self.steps) == 0:
return _step_kind_func(context, step)
prev_step = self.steps[-1]
if prev_step.current_function is None:
return StepKind.UNKNOWN
if prev_step.num_frames < step.num_frames:
return _step_kind_func(context, step)
if prev_step.num_frames > step.num_frames:
frame_step = self._get_prev_step_in_this_frame(step)
prev_step = frame_step if frame_step is not None else prev_step
# We're in the same func as prev step, check lineo.
if prev_step.current_location.lineno > step.current_location.lineno:
return StepKind.VERTICAL_BACKWARD
if prev_step.current_location.lineno < step.current_location.lineno:
return StepKind.VERTICAL_FORWARD
# We're on the same line as prev step, check column.
if prev_step.current_location.column > step.current_location.column:
return StepKind.HORIZONTAL_BACKWARD
if prev_step.current_location.column < step.current_location.column:
return StepKind.HORIZONTAL_FORWARD
# This step is in exactly the same location as the prev step.
return StepKind.SAME
def new_step(self, context, step):
assert isinstance(step, StepIR), type(step)
step.step_kind = self._get_new_step_kind(context, step)
self.steps.append(step)
return step
def clear_steps(self):
self.steps.clear()