lldbinit.py 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. #!/usr/bin/env python3
  2. """Initialization for lldb."""
  3. __copyright__ = """
  4. Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  5. Exceptions. See /LICENSE for license information.
  6. SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  7. """
  8. # This script is only meant to be used from LLDB.
  9. import lldb # type: ignore
  10. import os
  11. import re
  12. from typing import Any
  13. project_root = os.path.dirname(os.path.realpath(__file__))
  14. ci = lldb.debugger.GetCommandInterpreter()
  15. result = lldb.SBCommandReturnObject()
  16. def RunCommand(cmd: str, print_command: bool = True) -> Any:
  17. """Runs a command and prints it to the console to show that it ran."""
  18. if print_command:
  19. print(f"(lldb) {cmd}")
  20. ci.HandleCommand(cmd, result)
  21. return result.GetOutput()
  22. RunCommand(f"settings append target.source-map . {project_root}")
  23. RunCommand(f"settings append target.source-map /proc/self/cwd {project_root}")
  24. # Matches the output of `print Dump(...)` and captures the stuff from inside the
  25. # std::string while discarding the std::string type.
  26. dump_re = re.compile(r'\(std::string\) "([\s\S]+)"', re.MULTILINE)
  27. # A helper to ease calling the Dump() free functions.
  28. def cmd_dump(debugger: Any, command: Any, result: Any, dict: Any) -> None:
  29. def print_usage() -> None:
  30. print("""
  31. Dumps the value of an associated ID, using the C++ Dump() functions.
  32. Usage:
  33. dump <CONTEXT> [<EXPR>|-- <EXPR>|<TYPE><ID>|<TYPE> <ID>]
  34. Args:
  35. CONTEXT is the dump context, such a SemIR::Context reference, a SemIR::File,
  36. a Parse::Context, or a Lex::TokenizeBuffer.
  37. EXPR is a C++ expression such as a variable name. Use `--` to prevent it from
  38. being treated as a TYPE and ID.
  39. TYPE can be `inst`, `constant`, `generic`, `impl`, `entity_name`, etc. See
  40. the `Label` string in `IdBase` classes to find possible TYPE names,
  41. though only Id types that have a matching `Make...Id()` function are
  42. supported.
  43. ID is an integer number, such as `42`, in hex, such as in `inst6000000A`. It
  44. can come with a `0x` prefix, allowing easier copy-paste from raw printed
  45. hex values (such as via the `p/x` lldb command).
  46. Example usage:
  47. # Dumps the `inst_id` local variable, with a `context` local variable.
  48. dump context inst_id
  49. # Dumps the instruction with id 42, with a `context()` method for accessing
  50. # the `Check::Context&`.
  51. dump context() inst42
  52. """)
  53. args = command.split()
  54. if len(args) < 2:
  55. print_usage()
  56. return
  57. context = args[0]
  58. # The set of "Make" functions in dump.cpp.
  59. id_types = {
  60. "class": "SemIR::MakeClassId",
  61. "constant": "SemIR::MakeConstantId",
  62. "constraint": "SemIR::MakeNamedConstraintId",
  63. "symbolic_constant": "SemIR::MakeSymbolicConstantId",
  64. "entity_name": "SemIR::MakeEntityNameId",
  65. "facet_type": "SemIR::MakeFacetTypeId",
  66. "function": "SemIR::MakeFunctionId",
  67. "generic": "SemIR::MakeGenericId",
  68. "impl": "SemIR::MakeImplId",
  69. "inst_block": "SemIR::MakeInstBlockId",
  70. "inst": "SemIR::MakeInstId",
  71. "interface": "SemIR::MakeInterfaceId",
  72. "name": "SemIR::MakeNameId",
  73. "name_scope": "SemIR::MakeNameScopeId",
  74. "identified_facet_type": "SemIR::MakeIdentifiedFacetTypeId",
  75. "require_block": "SemIR::MakeRequireImplsBlockId",
  76. "require": "SemIR::MakeRequireImplsId",
  77. "specific": "SemIR::MakeSpecificId",
  78. "specific_interface": "SemIR::MakeSpecificInterfaceId",
  79. "struct_type_fields": "SemIR::MakeStructTypeFieldsId",
  80. "type": "SemIR::MakeTypeId",
  81. }
  82. def print_dump(context: str, expr: str) -> None:
  83. cmd = f"p Dump({context}, {expr})"
  84. out = RunCommand(cmd, print_command=False)
  85. if m := re.match(dump_re, out):
  86. # Use the `dump_re` match to print just the interesting part of the
  87. # dump output.
  88. print(m[1])
  89. else:
  90. # Unexpected output, show the command that was run.
  91. print(f"(lldb) {cmd}")
  92. print(out)
  93. # Try to find a type + id from the input args. If not, the id will be passed
  94. # through directly to C++, as it can be a variable name.
  95. found_id_type = False
  96. # Look for <type><id> as a single argument.
  97. if m := re.fullmatch("([a-z_]+)(?:0x)?([0-9A-Fa-f]+)", args[1]):
  98. if m[1] in id_types:
  99. if len(args) > 2:
  100. print_usage()
  101. return
  102. make_id_fn = id_types[m[1]]
  103. id = int(m[2], 16)
  104. print_dump(context, f"{make_id_fn}({id})")
  105. found_id_type = True
  106. # Look for <type> <id> as two arguments. If there's no <id>, the <type>
  107. # should just be treated as a variable name.
  108. if args[1] in id_types:
  109. if len(args) > 3:
  110. print_usage()
  111. return
  112. elif len(args) == 3:
  113. if m := re.fullmatch("(?:0x)?([0-9A-Fa-f]+)", args[2]):
  114. make_id_fn = id_types[args[1]]
  115. id = int(m[1], 16)
  116. print_dump(context, f"{make_id_fn}({id})")
  117. found_id_type = True
  118. if not found_id_type:
  119. # Use `--` to escape a variable name like `inst22`.
  120. if args[1] == "--":
  121. expr = " ".join(args[2:])
  122. else:
  123. expr = " ".join(args[1:])
  124. print_dump(context, expr)
  125. def __lldb_init_module(debugger: Any, internal_dict: Any) -> None:
  126. RunCommand("command script add -f lldbinit.cmd_dump dump")