Files
scc/runtime/runtime_gdb.py
zzy 0e7dec202a refactor(lex_parser): 移除旧的词法解析器实现并更新依赖
移除了 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 结构体的字段。
2026-02-16 16:56:40 +08:00

309 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"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()