移除了 libs/lex_parser 目录下的所有头文件和源文件,包括: - lex_parser.h 和 lex_parser.c 核心解析功能 - 所有测试文件(test_char.c, test_identifier.c, test_number.c, test_skip_block_comment.c, test_skip_line.c, test_string.c) 更新了 lexer 模块的依赖配置,将 lex_parser 替换为 sstream, 同时更新了 lexer.h 中的相关包含头文件和数据结构定义, 简化了 scc_lexer_t 结构体的字段。
309 lines
10 KiB
Python
309 lines
10 KiB
Python
"https://sourceware.org/gdb/current/onlinedocs/gdb.html/Python-API.html#Python-API"
|
||
|
||
import gdb # type: ignore
|
||
|
||
|
||
class VectorPrinter(gdb.ValuePrinter):
|
||
"""兼容新旧注册方式的最终方案"""
|
||
|
||
def __init__(self, val: gdb.Value):
|
||
self.val: gdb.Value = val
|
||
|
||
@staticmethod
|
||
def check_type(val: gdb.Value) -> bool:
|
||
"""类型检查(兼容匿名结构体)"""
|
||
try:
|
||
if val.type.code not in (gdb.TYPE_CODE_STRUCT, gdb.TYPE_CODE_TYPEDEF):
|
||
return False
|
||
if val.type.name == "scc_cstring_t":
|
||
return False
|
||
fields = val.type.fields()
|
||
if not fields:
|
||
return False
|
||
exp = ["size", "cap", "data"]
|
||
for t in fields:
|
||
if t.name in exp:
|
||
exp.remove(t.name)
|
||
else:
|
||
return False
|
||
return True
|
||
except gdb.error:
|
||
return False
|
||
except ValueError:
|
||
return False
|
||
except TypeError:
|
||
return False
|
||
except Exception as e:
|
||
print(f"[DEBUG] Unknown exception type: {type(e).__name__}")
|
||
print(f"[DEBUG] Exception details: {e}")
|
||
print(
|
||
f"[DEBUG] val type: {val.type if hasattr(val, 'type') else 'no type attr'}"
|
||
)
|
||
return False
|
||
|
||
def to_string(self):
|
||
"""
|
||
GDB will call this method to display the string representation
|
||
of the value passed to the object's constructor.
|
||
|
||
This is a basic method, and is optional.
|
||
|
||
When printing from the CLI, if the to_string method exists,
|
||
then GDB will prepend its result to the values returned by children.
|
||
Exactly how this formatting is done is dependent on the display hint,
|
||
and may change as more hints are added. Also, depending on the print settings
|
||
(see Print Settings), the CLI may print just the result of to_string
|
||
in a stack trace, omitting the result of children.
|
||
|
||
If this method returns a string, it is printed verbatim.
|
||
|
||
Otherwise, if this method returns an instance of gdb.Value,
|
||
then GDB prints this value. This may result in a call to another pretty-printer.
|
||
|
||
If instead the method returns a Python value which is convertible to a gdb.Value,
|
||
then GDB performs the conversion and prints the resulting value. Again,
|
||
this may result in a call to another pretty-printer. Python scalars
|
||
(integers, floats, and booleans) and strings are convertible to gdb.Value;
|
||
other types are not.
|
||
|
||
Finally, if this method returns None then no further operations are performed
|
||
in this method and nothing is printed.
|
||
|
||
If the result is not one of these types, an exception is raised.
|
||
|
||
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html#Pretty-Printing-API
|
||
"""
|
||
return (
|
||
f"vector({self.val.address} size={self.val['size']}, cap={self.val['cap']})"
|
||
)
|
||
|
||
def display_hint(self):
|
||
"""
|
||
The CLI may call this method and use its result to change the formatting of a value.
|
||
The result will also be supplied to an MI consumer as a 'displayhint'
|
||
attribute of the variable being printed.
|
||
|
||
This is a basic method, and is optional. If it does exist,
|
||
this method must return a string or the special value None.
|
||
|
||
Some display hints are predefined by GDB:
|
||
|
||
'array'
|
||
Indicate that the object being printed is “array-like”.
|
||
The CLI uses this to respect parameters such as set print elements and set print array.
|
||
|
||
'map'
|
||
Indicate that the object being printed is “map-like”,
|
||
and that the children of this value can be assumed to alternate between keys and values.
|
||
|
||
'string'
|
||
Indicate that the object being printed is “string-like”.
|
||
If the printer's to_string method returns a Python string of some kind,
|
||
then GDB will call its internal language-specific string-printing function
|
||
to format the string. For the CLI this means adding quotation marks, possibly
|
||
escaping some characters, respecting set print elements, and the like.
|
||
|
||
The special value None causes GDB to apply the default display rules.
|
||
|
||
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html#Pretty-Printing-API
|
||
"""
|
||
return "array"
|
||
|
||
def num_children(self):
|
||
"""
|
||
This is not a basic method, so GDB will only ever call it for objects
|
||
derived from gdb.ValuePrinter.
|
||
|
||
If available, this method should return the number of children.
|
||
|
||
None may be returned if the number can't readily be computed.
|
||
|
||
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html#Pretty-Printing-API
|
||
"""
|
||
return int(self.val["size"])
|
||
|
||
def children(self):
|
||
"""
|
||
This is not a basic method, so GDB will only ever call it for objects
|
||
derived from gdb.ValuePrinter.
|
||
|
||
If available, this method should return the child item (that is,
|
||
a tuple holding the name and value of this child) indicated by n.
|
||
Indices start at zero.
|
||
|
||
GDB provides a function which can be used to look up the default
|
||
pretty-printer for a gdb.Value:
|
||
|
||
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Pretty-Printing-API.html#Pretty-Printing-API
|
||
"""
|
||
size = int(self.val["size"])
|
||
cap = int(self.val["cap"])
|
||
data_ptr = self.val["data"]
|
||
|
||
if cap == 0 or data_ptr == 0:
|
||
return []
|
||
|
||
# 使用 GDB 内置数组转换
|
||
array = data_ptr.dereference()
|
||
array = array.cast(data_ptr.type.target().array(cap - 1))
|
||
|
||
for i in range(size):
|
||
# state = "<used>" if i < size else "<unused>"
|
||
try:
|
||
value = array[i]
|
||
yield (f"[{i}] {value.type} {value.address}", value)
|
||
except gdb.MemoryError:
|
||
yield (f"[{i}]", "<invalid>")
|
||
|
||
|
||
class HashTablePrinter(gdb.ValuePrinter):
|
||
"""打印 scc_hashtable_t 结构"""
|
||
|
||
def __init__(self, val):
|
||
self.val = val
|
||
|
||
@staticmethod
|
||
def check_type(val):
|
||
# 通过类型名或关键字段检查
|
||
type_name = val.type.name
|
||
if type_name and type_name in ("scc_hashtable_t", "scc_hashtable"):
|
||
return True
|
||
try:
|
||
fields = {f.name for f in val.type.fields()}
|
||
required = {"entries", "count", "tombstone_count", "hash_func", "key_cmp"}
|
||
if required.issubset(fields):
|
||
return True
|
||
except:
|
||
pass
|
||
return False
|
||
|
||
def to_string(self):
|
||
count = self.val["count"]
|
||
tombstone = self.val["tombstone_count"]
|
||
cap = self.val["entries"]["size"] # 总槽位数
|
||
return f"hashtable(count={count}, tombstone={tombstone}, capacity={cap})"
|
||
|
||
def display_hint(self):
|
||
return "map"
|
||
|
||
def num_children(self):
|
||
return int(self.val["count"])
|
||
|
||
def children(self):
|
||
entries = self.val["entries"]
|
||
size = int(entries["size"])
|
||
data = entries["data"]
|
||
if size == 0 or data == 0:
|
||
return
|
||
# ENTRY_ACTIVE = 1(根据枚举定义)
|
||
for i in range(size):
|
||
entry = data[i]
|
||
state = int(entry["state"])
|
||
if state == 1: # 只输出有效条目
|
||
yield (f"[{i}]", entry)
|
||
|
||
|
||
class StrPoolPrinter(gdb.ValuePrinter):
|
||
"""打印 scc_strpool_t,将键值作为字符串展示"""
|
||
|
||
def __init__(self, val):
|
||
self.val = val
|
||
self.ht = val["ht"] # 内部哈希表
|
||
|
||
@staticmethod
|
||
def check_type(val):
|
||
type_name = val.type.name
|
||
if type_name and type_name == "scc_strpool_t":
|
||
return True
|
||
try:
|
||
fields = {f.name for f in val.type.fields()}
|
||
if "ht" in fields:
|
||
# 可进一步检查 ht 的类型,但非必须
|
||
return True
|
||
except:
|
||
pass
|
||
return False
|
||
|
||
def to_string(self):
|
||
count = self.ht["count"]
|
||
cap = self.ht["entries"]["size"]
|
||
return f"strpool(count={count}, capacity={cap})"
|
||
|
||
def display_hint(self):
|
||
return "map"
|
||
|
||
def num_children(self):
|
||
return int(self.ht["count"])
|
||
|
||
def children(self):
|
||
entries = self.ht["entries"]
|
||
size = int(entries["size"])
|
||
data = entries["data"]
|
||
if size == 0 or data == 0:
|
||
return
|
||
const_char_ptr = gdb.lookup_type("const char").pointer()
|
||
char_ptr = gdb.lookup_type("char").pointer()
|
||
|
||
for i in range(size):
|
||
entry = data[i]
|
||
state = int(entry["state"])
|
||
if state == 1: # ACTIVE
|
||
key_val = entry["key"]
|
||
value_val = entry["value"]
|
||
|
||
# 尝试将 void* 转为字符串
|
||
try:
|
||
key_str = key_val.cast(const_char_ptr).string()
|
||
except:
|
||
key_str = str(key_val) # 失败则回退到地址
|
||
|
||
try:
|
||
value_str = value_val.cast(char_ptr).string()
|
||
except:
|
||
value_str = str(value_val)
|
||
|
||
# 使用带引号的字符串作为名称,值直接是字符串
|
||
yield (repr(key_str), value_str)
|
||
|
||
|
||
def register_pretty_printers():
|
||
"""统一的查找函数,注册所有打印机"""
|
||
|
||
def lookup_function(val):
|
||
if VectorPrinter.check_type(val):
|
||
return VectorPrinter(val)
|
||
if HashTablePrinter.check_type(val):
|
||
return HashTablePrinter(val)
|
||
if StrPoolPrinter.check_type(val):
|
||
return StrPoolPrinter(val)
|
||
return None
|
||
|
||
gdb.printing.register_pretty_printer(gdb.current_objfile(), lookup_function)
|
||
|
||
|
||
class VectorInfoCommand(gdb.Command):
|
||
"""保持原有命令不变"""
|
||
|
||
def __init__(self):
|
||
super().__init__("vector_info", gdb.COMMAND_USER)
|
||
|
||
def invoke(self, argument, from_tty):
|
||
val = gdb.parse_and_eval(argument)
|
||
|
||
if not VectorPrinter.check_type(val):
|
||
print("Invalid vector")
|
||
return
|
||
|
||
printer = VectorPrinter(val)
|
||
print("=== Vector Details ===")
|
||
print("Size:", val["size"])
|
||
print("Capacity:", val["cap"])
|
||
print("Elements:")
|
||
for name, value in printer.children():
|
||
print(f" {name}: {value}")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
register_pretty_printers()
|
||
VectorInfoCommand()
|