131 lines
3.9 KiB
Python
131 lines
3.9 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
|
||
|
"""Set of data classes for representing the complete debug program state at a
|
||
|
fixed point in execution.
|
||
|
"""
|
||
|
|
||
|
import os
|
||
|
|
||
|
from collections import OrderedDict
|
||
|
from pathlib import PurePath
|
||
|
from typing import List
|
||
|
|
||
|
|
||
|
class SourceLocation:
|
||
|
def __init__(self, path: str = None, lineno: int = None, column: int = None):
|
||
|
if path:
|
||
|
path = os.path.normcase(path)
|
||
|
self.path = path
|
||
|
self.lineno = lineno
|
||
|
self.column = column
|
||
|
|
||
|
def __str__(self):
|
||
|
return "{}({}:{})".format(self.path, self.lineno, self.column)
|
||
|
|
||
|
def match(self, other) -> bool:
|
||
|
"""Returns true iff all the properties that appear in `self` have the
|
||
|
same value in `other`, but not necessarily vice versa.
|
||
|
"""
|
||
|
if not other or not isinstance(other, SourceLocation):
|
||
|
return False
|
||
|
|
||
|
if self.path and (
|
||
|
other.path is None or (PurePath(self.path) != PurePath(other.path))
|
||
|
):
|
||
|
return False
|
||
|
|
||
|
if self.lineno and (self.lineno != other.lineno):
|
||
|
return False
|
||
|
|
||
|
if self.column and (self.column != other.column):
|
||
|
return False
|
||
|
|
||
|
return True
|
||
|
|
||
|
|
||
|
class StackFrame:
|
||
|
def __init__(
|
||
|
self,
|
||
|
function: str = None,
|
||
|
is_inlined: bool = None,
|
||
|
location: SourceLocation = None,
|
||
|
watches: OrderedDict = None,
|
||
|
):
|
||
|
if watches is None:
|
||
|
watches = {}
|
||
|
|
||
|
self.function = function
|
||
|
self.is_inlined = is_inlined
|
||
|
self.location = location
|
||
|
self.watches = watches
|
||
|
|
||
|
def __str__(self):
|
||
|
return "{}{}: {} | {}".format(
|
||
|
self.function,
|
||
|
" (inlined)" if self.is_inlined else "",
|
||
|
self.location,
|
||
|
{k: str(self.watches[k]) for k in self.watches},
|
||
|
)
|
||
|
|
||
|
def match(self, other) -> bool:
|
||
|
"""Returns true iff all the properties that appear in `self` have the
|
||
|
same value in `other`, but not necessarily vice versa.
|
||
|
"""
|
||
|
if not other or not isinstance(other, StackFrame):
|
||
|
return False
|
||
|
|
||
|
if self.location and not self.location.match(other.location):
|
||
|
return False
|
||
|
|
||
|
if self.watches:
|
||
|
for name in iter(self.watches):
|
||
|
try:
|
||
|
if isinstance(self.watches[name], dict):
|
||
|
for attr in iter(self.watches[name]):
|
||
|
if (
|
||
|
getattr(other.watches[name], attr, None)
|
||
|
!= self.watches[name][attr]
|
||
|
):
|
||
|
return False
|
||
|
else:
|
||
|
if other.watches[name].value != self.watches[name]:
|
||
|
return False
|
||
|
except KeyError:
|
||
|
return False
|
||
|
|
||
|
return True
|
||
|
|
||
|
|
||
|
class ProgramState:
|
||
|
def __init__(self, frames: List[StackFrame] = None):
|
||
|
self.frames = frames
|
||
|
|
||
|
def __str__(self):
|
||
|
return "\n".join(
|
||
|
map(
|
||
|
lambda enum: "Frame {}: {}".format(enum[0], enum[1]),
|
||
|
enumerate(self.frames),
|
||
|
)
|
||
|
)
|
||
|
|
||
|
def match(self, other) -> bool:
|
||
|
"""Returns true iff all the properties that appear in `self` have the
|
||
|
same value in `other`, but not necessarily vice versa.
|
||
|
"""
|
||
|
if not other or not isinstance(other, ProgramState):
|
||
|
return False
|
||
|
|
||
|
if self.frames:
|
||
|
for idx, frame in enumerate(self.frames):
|
||
|
try:
|
||
|
if not frame.match(other.frames[idx]):
|
||
|
return False
|
||
|
except (IndexError, KeyError):
|
||
|
return False
|
||
|
|
||
|
return True
|