forked from OSchip/llvm-project
134 lines
4.5 KiB
Python
134 lines
4.5 KiB
Python
# 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
|
|
|
|
# If we're missing line numbers to compare then the step kind has to be UNKNOWN.
|
|
if prev_step.current_location.lineno is None or step.current_location.lineno is None:
|
|
return StepKind.UNKNOWN
|
|
|
|
# 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()
|