Files
carbon-lang/scripts/lldbinit.py
T
Dana Jansens ce109708bf Add dumping for NamedConstraintId and shorten untagged id printing (#6319)
Adds support to the `dump` debugger command for named constraint ids,
which are printed as `constraint<number>`. While doing so, we print
whether the `constraint` is complete or not, and add the same to
`interface` to match.

And we noticed that the printing of name and name scope ids, which are
not tagged, are very verbose by adding 7 `0`s to them for no reason. So
make the dump output easier to read by dropping 0 prefixes.

Before:
```
name_scope00000000: {inst: inst0000000E, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name00000000: inst6000000F, name00000001: inst60000011}} {kind: Namespace, arg0: name_scope00000000, arg1: inst<none>, type: type(inst(NamespaceType))} `package`
```

After:
```
name_scope0: {inst: instE, parent_scope: name_scope<none>, has_error: false, extended_scopes: [], names: {name0: inst6000000F, name1: inst60000011}} {kind: Namespace, arg0: name_scope0, arg1: inst<none>, type: type(inst(NamespaceType))} `package`
```
2025-11-05 21:36:42 +00:00

138 lines
4.7 KiB
Python

#!/usr/bin/env python3
"""Initialization for lldb."""
__copyright__ = """
Part of the Carbon Language project, under the Apache License v2.0 with LLVM
Exceptions. See /LICENSE for license information.
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
"""
# This script is only meant to be used from LLDB.
import lldb # type: ignore
import os
import re
from typing import Any
project_root = os.path.dirname(os.path.realpath(__file__))
ci = lldb.debugger.GetCommandInterpreter()
result = lldb.SBCommandReturnObject()
def RunCommand(cmd: str, print_command: bool = True) -> Any:
"""Runs a command and prints it to the console to show that it ran."""
if print_command:
print(f"(lldb) {cmd}")
ci.HandleCommand(cmd, result)
return result.GetOutput()
RunCommand(f"settings append target.source-map . {project_root}")
RunCommand(f"settings append target.source-map /proc/self/cwd {project_root}")
# Matches the output of `print Dump(...)` and captures the stuff from inside the
# std::string while discarding the std::string type.
dump_re = re.compile(r'\(std::string\) "([\s\S]+)"', re.MULTILINE)
# A helper to ease calling the Dump() free functions.
def cmd_dump(debugger: Any, command: Any, result: Any, dict: Any) -> None:
def print_usage() -> None:
print(
"""
Dumps the value of an associated ID, using the C++ Dump() functions.
Usage:
dump <CONTEXT> [<EXPR>|-- <EXPR>|<TYPE><ID>]
Args:
CONTEXT is the dump context, such a SemIR::Context reference, a SemIR::File,
a Parse::Context, or a Lex::TokenizeBuffer.
EXPR is a C++ expression such as a variable name. Use `--` to prevent it from
being treated as a TYPE and ID.
TYPE can be `inst`, `constant`, `generic`, `impl`, `entity_name`, etc. See
the `Label` string in `IdBase` classes to find possible TYPE names,
though only Id types that have a matching `Make...Id()` function are
supported.
ID is an integer number, such as `42`, in hex, such as in `inst6000000A`.
Example usage:
# Dumps the `inst_id` local variable, with a `context` local variable.
dump context inst_id
# Dumps the instruction with id 42, with a `context()` method for accessing
# the `Check::Context&`.
dump context() inst42
"""
)
args = command.split(" ")
if len(args) < 2:
print_usage()
return
context = args[0]
# The set of "Make" functions in dump.cpp.
id_types = {
"class": "SemIR::MakeClassId",
"constant": "SemIR::MakeConstantId",
"constraint": "SemIR::MakeNamedConstraintId",
"symbolic_constant": "SemIR::MakeSymbolicConstantId",
"entity_name": "SemIR::MakeEntityNameId",
"facet_type": "SemIR::MakeFacetTypeId",
"function": "SemIR::MakeFunctionId",
"generic": "SemIR::MakeGenericId",
"impl": "SemIR::MakeImplId",
"inst_block": "SemIR::MakeInstBlockId",
"inst": "SemIR::MakeInstId",
"interface": "SemIR::MakeInterfaceId",
"name": "SemIR::MakeNameId",
"name_scope": "SemIR::MakeNameScopeId",
"identified_facet_type": "SemIR::MakeIdentifiedFacetTypeId",
"specific": "SemIR::MakeSpecificId",
"specific_interface": "SemIR::MakeSpecificInterfaceId",
"struct_type_fields": "SemIR::MakeStructTypeFieldsId",
"type": "SemIR::MakeTypeId",
}
def print_dump(context: str, expr: str) -> None:
cmd = f"p Dump({context}, {expr})"
out = RunCommand(cmd, print_command=False)
if m := re.match(dump_re, out):
# Use the `dump_re` match to print just the interesting part of the
# dump output.
print(m[1])
else:
# Unexpected output, show the command that was run.
print(f"(lldb) {cmd}")
print(out)
# Try to find a type + id from the input args. If not, the id will be passed
# through directly to C++, as it can be a variable name.
found_id_type = False
# Look for <type><id> as a single argument.
if m := re.fullmatch("([a-z_]+)([0-9A-Fa-f]+)", args[1]):
if m[1] in id_types:
if len(args) != 2:
print_usage()
return
make_id_fn = id_types[m[1]]
id = int(m[2], 16)
print_dump(context, f"{make_id_fn}({id})")
found_id_type = True
if not found_id_type:
# Use `--` to escape a variable name like `inst22`.
if args[1] == "--":
expr = " ".join(args[2:])
else:
expr = " ".join(args[1:])
print_dump(context, expr)
def __lldb_init_module(debugger: Any, internal_dict: Any) -> None:
RunCommand("command script add -f lldbinit.cmd_dump dump")