drgn/tests/libdrgn.py
Kevin Svetlitski 4213bea149 libdrgn: add limited support for looking up types with template arguments
Currently, looking up a type with template arguments results in an
"invalid character" syntax error on the "<" character. The DWARF index
includes template arguments in indexed names, so we need to do lookups
including the template arguments. Full support for this would require
parsing the template argument list syntax and normalizing it or looking
it up as an AST in some way. For now, it's at least an improvement to
pass the user's string verbatim. To do so, kludge it by adding a token
containing everything from "<" to the matching ">" to the C++ lexer and
appending that to the identifier.

Co-authored-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Omar Sandoval <osandov@osandov.com>
Signed-off-by: Kevin Svetlitski <svetlitski@meta.com>
2022-12-14 20:55:03 -08:00

285 lines
7.2 KiB
Python

# Copyright (c) Meta Platforms, Inc. and affiliates.
# SPDX-License-Identifier: LGPL-2.1-or-later
import ctypes
import enum
from enum import auto
import os
import _drgn
_drgn_pydll = ctypes.PyDLL(_drgn.__file__)
_drgn_cdll = ctypes.CDLL(_drgn.__file__)
class _drgn_error(ctypes.Structure):
_fields_ = [
("code", ctypes.c_uint),
("errnum", ctypes.c_int),
("path", ctypes.c_char_p),
("msg", ctypes.c_char_p),
]
_drgn_pydll.set_drgn_error.restype = ctypes.c_void_p
_drgn_pydll.set_drgn_error.argtypes = [ctypes.POINTER(_drgn_error)]
def _check_err(err):
if err:
_drgn_pydll.set_drgn_error(err)
class _path_iterator_component(ctypes.Structure):
_fields_ = [
("path", ctypes.c_char_p),
("len", ctypes.c_size_t),
]
class _path_iterator(ctypes.Structure):
_fields_ = [
("components", ctypes.POINTER(_path_iterator_component)),
("num_components", ctypes.c_size_t),
("dot_dot", ctypes.c_size_t),
]
_drgn_cdll.drgn_test_path_iterator_next.restype = ctypes.c_bool
_drgn_cdll.drgn_test_path_iterator_next.argtypes = [
ctypes.POINTER(_path_iterator),
ctypes.POINTER(ctypes.POINTER(ctypes.c_char)),
ctypes.POINTER(ctypes.c_size_t),
]
class PathIterator:
def __init__(self, *paths):
components = (_path_iterator_component * len(paths))()
for i, path in enumerate(paths):
path = os.fsencode(path)
components[i].path = path
components[i].len = len(path)
self._it = _path_iterator(components, len(paths))
def __iter__(self):
return self
def __next__(self):
component = ctypes.POINTER(ctypes.c_char)()
component_len = ctypes.c_size_t()
if _drgn_cdll.drgn_test_path_iterator_next(
ctypes.pointer(self._it),
ctypes.pointer(component),
ctypes.pointer(component_len),
):
return os.fsdecode(ctypes.string_at(component, component_len.value))
else:
raise StopIteration()
_drgn_cdll.drgn_test_path_ends_with.restype = ctypes.c_bool
_drgn_cdll.drgn_test_path_ends_with.argtypes = [
ctypes.POINTER(_path_iterator),
ctypes.POINTER(_path_iterator),
]
def path_ends_with(path1: PathIterator, path2: PathIterator):
return _drgn_cdll.drgn_test_path_ends_with(
ctypes.pointer(path1._it), ctypes.pointer(path2._it)
)
class _drgn_type(ctypes.Structure):
pass
class _drgn_qualified_type(ctypes.Structure):
_fields_ = [
("type", ctypes.POINTER(_drgn_type)),
("qualifiers", ctypes.c_uint),
]
class _drgn_token(ctypes.Structure):
_fields_ = [
("kind", ctypes.c_int),
("value", ctypes.c_void_p),
("len", ctypes.c_size_t),
]
class _drgn_lexer(ctypes.Structure):
_fields_ = [
("func", ctypes.c_void_p),
("p", ctypes.c_void_p),
("stack", ctypes.POINTER(_drgn_token)),
("stack_len", ctypes.c_size_t),
("stack_capacity", ctypes.c_size_t),
]
class _drgn_c_family_lexer(ctypes.Structure):
_fields_ = [
("lexer", _drgn_lexer),
("cpp", ctypes.c_bool),
]
drgn_lexer_func = ctypes.CFUNCTYPE(
ctypes.POINTER(_drgn_error),
ctypes.POINTER(_drgn_lexer),
ctypes.POINTER(_drgn_token),
)
_drgn_cdll.drgn_test_lexer_init.restype = None
_drgn_cdll.drgn_test_lexer_init.argtypes = [
ctypes.POINTER(_drgn_lexer),
ctypes.POINTER(drgn_lexer_func),
ctypes.c_char_p,
]
_drgn_cdll.drgn_test_lexer_deinit.restype = None
_drgn_cdll.drgn_test_lexer_deinit.argtypes = [ctypes.POINTER(_drgn_lexer)]
_drgn_cdll.drgn_test_lexer_pop.restype = ctypes.POINTER(_drgn_error)
_drgn_cdll.drgn_test_lexer_pop.argtypes = [
ctypes.POINTER(_drgn_lexer),
ctypes.POINTER(_drgn_token),
]
_drgn_cdll.drgn_test_lexer_push.restype = ctypes.POINTER(_drgn_error)
_drgn_cdll.drgn_test_lexer_push.argtypes = [
ctypes.POINTER(_drgn_lexer),
ctypes.POINTER(_drgn_token),
]
_drgn_cdll.drgn_test_lexer_peek.restype = ctypes.POINTER(_drgn_error)
_drgn_cdll.drgn_test_lexer_peek.argtypes = [
ctypes.POINTER(_drgn_lexer),
ctypes.POINTER(_drgn_token),
]
drgn_c_family_lexer_func = drgn_lexer_func.in_dll(_drgn_cdll, "drgn_test_lexer_c")
drgn_test_lexer_func = drgn_lexer_func.in_dll(_drgn_cdll, "drgn_test_lexer_func")
class C_TOKEN(enum.IntEnum):
EOF = -1
VOID = auto()
CHAR = auto()
SHORT = auto()
INT = auto()
LONG = auto()
SIGNED = auto()
UNSIGNED = auto()
BOOL = auto()
FLOAT = auto()
DOUBLE = auto()
COMPLEX = auto()
CONST = auto()
RESTRICT = auto()
VOLATILE = auto()
ATOMIC = auto()
STRUCT = auto()
UNION = auto()
CLASS = auto()
ENUM = auto()
LPAREN = auto()
RPAREN = auto()
LBRACKET = auto()
RBRACKET = auto()
ASTERISK = auto()
DOT = auto()
NUMBER = auto()
IDENTIFIER = auto()
TEMPLATE_ARGUMENTS = auto()
class Token:
def __init__(self, token):
self._token = token
@property
def kind(self):
return self._token.kind
@property
def value(self):
return ctypes.string_at(self._token.value, self._token.len).decode()
def __repr__(self):
return f"Token({self.kind}, {self.value!r})"
class Lexer:
def __init__(self, func, str, cpp=False):
self._c_family_lexer = _drgn_c_family_lexer()
self._lexer = self._c_family_lexer.lexer
self._func = func
self._str = str.encode()
self._c_family_lexer.cpp = cpp
_drgn_cdll.drgn_test_lexer_init(
ctypes.pointer(self._lexer), self._func, self._str
)
def __del__(self):
_drgn_cdll.drgn_test_lexer_deinit(ctypes.pointer(self._lexer))
def pop(self):
token = _drgn_token()
_check_err(
_drgn_cdll.drgn_test_lexer_pop(
ctypes.pointer(self._lexer), ctypes.pointer(token)
)
)
return Token(token)
def push(self, token):
_check_err(
_drgn_cdll.drgn_test_lexer_push(
ctypes.pointer(self._lexer), ctypes.pointer(token._token)
)
)
def peek(self):
token = _drgn_token()
_check_err(
_drgn_cdll.drgn_test_lexer_peek(
ctypes.pointer(self._lexer), ctypes.pointer(token)
)
)
return Token(token)
_drgn_cdll.drgn_test_serialize_bits.restype = None
_drgn_cdll.drgn_test_serialize_bits.argtypes = [
ctypes.c_void_p,
ctypes.c_uint64,
ctypes.c_uint64,
ctypes.c_uint8,
ctypes.c_bool,
]
_drgn_cdll.drgn_test_deserialize_bits.restype = ctypes.c_uint64
_drgn_cdll.drgn_test_deserialize_bits.argtypes = [
ctypes.c_void_p,
ctypes.c_uint64,
ctypes.c_uint8,
ctypes.c_bool,
]
def serialize_bits(buf, bit_offset, uvalue, bit_size, little_endian):
assert (bit_offset + bit_size + 7) // 8 <= len(buf)
c_buf = (ctypes.c_char * len(buf)).from_buffer(buf)
return _drgn_cdll.drgn_test_serialize_bits(
c_buf, bit_offset, uvalue, bit_size, little_endian
)
def deserialize_bits(buf, bit_offset, bit_size, little_endian):
assert (bit_offset + bit_size + 7) // 8 <= len(buf)
c_buf = (ctypes.c_char * len(buf)).from_buffer_copy(buf)
return _drgn_cdll.drgn_test_deserialize_bits(
c_buf, bit_offset, bit_size, little_endian
)