serenity_gdb.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. # Copyright (c) 2021, Gunnar Beutner <gunnar@beutner.name>
  2. #
  3. # SPDX-License-Identifier: BSD-2-Clause
  4. import gdb
  5. import gdb.types
  6. import re
  7. def handler_class_for_type(type, re=re.compile('^([^<]+)(<.*>)?$')):
  8. typename = str(type.tag)
  9. match = re.match(typename)
  10. if not match:
  11. return UnhandledType
  12. klass = match.group(1)
  13. if klass == 'AK::Array':
  14. return AKArray
  15. elif klass == 'AK::Atomic':
  16. return AKAtomic
  17. elif klass == 'AK::DistinctNumeric':
  18. return AKDistinctNumeric
  19. elif klass == 'AK::HashMap':
  20. return AKHashMapPrettyPrinter
  21. elif klass == 'AK::RefCounted':
  22. return AKRefCounted
  23. elif klass == 'AK::RefPtr':
  24. return AKRefPtr
  25. elif klass == 'AK::OwnPtr':
  26. return AKOwnPtr
  27. elif klass == 'AK::NonnullRefPtr':
  28. return AKRefPtr
  29. elif klass == 'AK::SinglyLinkedList':
  30. return AKSinglyLinkedList
  31. elif klass == 'AK::String':
  32. return AKString
  33. elif klass == 'AK::StringView':
  34. return AKStringView
  35. elif klass == 'AK::StringImpl':
  36. return AKStringImpl
  37. elif klass == 'AK::Variant':
  38. return AKVariant
  39. elif klass == 'AK::Vector':
  40. return AKVector
  41. elif klass == 'VirtualAddress':
  42. return VirtualAddress
  43. else:
  44. return UnhandledType
  45. class UnhandledType:
  46. @classmethod
  47. def prettyprint_type(cls, type):
  48. return type.name
  49. class AKAtomic:
  50. def __init__(self, val):
  51. self.val = val
  52. def to_string(self):
  53. return self.val["m_value"]
  54. @classmethod
  55. def prettyprint_type(cls, type):
  56. contained_type = type.template_argument(0)
  57. return f'AK::Atomic<{handler_class_for_type(contained_type).prettyprint_type(contained_type)}>'
  58. class AKDistinctNumeric:
  59. def __init__(self, val):
  60. self.val = val
  61. def to_string(self):
  62. return self.val["m_value"]
  63. @classmethod
  64. def prettyprint_type(cls, type):
  65. actual_name = type.template_argument(1)
  66. parts = actual_name.name.split("::")
  67. unqualified_name = re.sub(r'__(\w+)_tag', r'\1', actual_name.name)
  68. if unqualified_name != actual_name.name:
  69. qualified_name = '::'.join(parts[:-2] + [unqualified_name])
  70. return qualified_name
  71. # If the tag is malformed, just print DistinctNumeric<T>
  72. contained_type = type.template_argument(0)
  73. return f'AK::DistinctNumeric<{handler_class_for_type(contained_type).prettyprint_type(contained_type)}>'
  74. class AKRefCounted:
  75. def __init__(self, val):
  76. self.val = val
  77. def to_string(self):
  78. return self.val["m_ref_count"]
  79. @classmethod
  80. def prettyprint_type(cls, type):
  81. contained_type = type.template_argument(0)
  82. return f'AK::RefCounted<{handler_class_for_type(contained_type).prettyprint_type(contained_type)}>'
  83. class AKString:
  84. def __init__(self, val):
  85. self.val = val
  86. def to_string(self):
  87. if int(self.val["m_impl"]["m_ptr"]) == 0:
  88. return '""'
  89. else:
  90. impl = AKRefPtr(self.val["m_impl"]).get_pointee().dereference()
  91. return AKStringImpl(impl).to_string()
  92. @classmethod
  93. def prettyprint_type(cls, type):
  94. return 'AK::String'
  95. class AKStringView:
  96. def __init__(self, val):
  97. self.val = val
  98. def to_string(self):
  99. if int(self.val["m_length"]) == 0:
  100. return '""'
  101. else:
  102. characters = self.val["m_characters"]
  103. str_type = characters.type.target().array(self.val["m_length"]).pointer()
  104. return str(characters.cast(str_type).dereference())
  105. @classmethod
  106. def prettyprint_type(cls, type):
  107. return 'AK::StringView'
  108. def get_field_unalloced(val, member, type):
  109. # Trying to access a variable-length field seems to fail with
  110. # Python Exception <class 'gdb.error'> value requires 4294967296 bytes, which is more than max-value-size
  111. # This works around that issue.
  112. return gdb.parse_and_eval(f"*({type}*)(({val.type.name}*){int(val.address)})->{member}")
  113. class AKStringImpl:
  114. def __init__(self, val):
  115. self.val = val
  116. def to_string(self):
  117. if int(self.val["m_length"]) == 0:
  118. return '""'
  119. else:
  120. str_type = gdb.lookup_type("char").array(self.val["m_length"])
  121. return get_field_unalloced(self.val, "m_inline_buffer", str_type)
  122. @classmethod
  123. def prettyprint_type(cls, type):
  124. return 'AK::StringImpl'
  125. class AKOwnPtr:
  126. def __init__(self, val):
  127. self.val = val
  128. def to_string(self):
  129. return AKOwnPtr.prettyprint_type(self.val.type)
  130. def children(self):
  131. return [('*', self.val["m_ptr"])]
  132. @classmethod
  133. def prettyprint_type(cls, type):
  134. contained_type = type.template_argument(0)
  135. return f'AK::OwnPtr<{handler_class_for_type(contained_type).prettyprint_type(contained_type)}>'
  136. class AKRefPtr:
  137. def __init__(self, val):
  138. self.val = val
  139. def to_string(self):
  140. return AKRefPtr.prettyprint_type(self.val.type)
  141. def get_pointee(self):
  142. inner_type = self.val.type.template_argument(0)
  143. inner_type_ptr = inner_type.pointer()
  144. return self.val["m_ptr"].cast(inner_type_ptr)
  145. def children(self):
  146. return [('*', self.get_pointee())]
  147. @classmethod
  148. def prettyprint_type(cls, type):
  149. contained_type = type.template_argument(0)
  150. return f'AK::RefPtr<{handler_class_for_type(contained_type).prettyprint_type(contained_type)}>'
  151. class AKVariant:
  152. def __init__(self, val):
  153. self.val = val
  154. self.index = int(self.val["m_index"])
  155. self.contained_types = self.resolve_types(self.val.type)
  156. def to_string(self):
  157. return AKVariant.prettyprint_type(self.val.type)
  158. def children(self):
  159. data = self.val["m_data"]
  160. ty = self.contained_types[self.index]
  161. return [(ty.name, data.cast(ty.pointer()).referenced_value())]
  162. @classmethod
  163. def resolve_types(cls, ty):
  164. contained_types = []
  165. type_resolved = ty.strip_typedefs()
  166. index = 0
  167. while True:
  168. try:
  169. arg = type_resolved.template_argument(index)
  170. index += 1
  171. contained_types.append(arg)
  172. except RuntimeError:
  173. break
  174. return contained_types
  175. @classmethod
  176. def prettyprint_type(cls, ty):
  177. names = ", ".join(handler_class_for_type(t).prettyprint_type(t) for t in AKVariant.resolve_types(ty))
  178. return f'AK::Variant<{names}>'
  179. class AKVector:
  180. def __init__(self, val):
  181. self.val = val
  182. def to_string(self):
  183. return f'{AKVector.prettyprint_type(self.val.type)} of len {int(self.val["m_size"])}'
  184. def children(self):
  185. vec_len = int(self.val["m_size"])
  186. if vec_len == 0:
  187. return []
  188. outline_buf = self.val["m_outline_buffer"]
  189. inner_type_ptr = self.val.type.template_argument(0).pointer()
  190. if int(outline_buf) != 0:
  191. elements = outline_buf.cast(inner_type_ptr)
  192. else:
  193. elements = get_field_unalloced(self.val, "m_inline_buffer_storage", inner_type_ptr)
  194. return [(f"[{i}]", elements[i]) for i in range(vec_len)]
  195. @classmethod
  196. def prettyprint_type(cls, type):
  197. template_type = type.template_argument(0)
  198. return f'AK::Vector<{handler_class_for_type(template_type).prettyprint_type(template_type)}>'
  199. class AKArray:
  200. def __init__(self, val):
  201. self.val = val
  202. self.storage_type = self.val.type.template_argument(0)
  203. self.array_size = self.val.type.template_argument(1)
  204. def to_string(self):
  205. return AKArray.prettyprint_type(self.val.type)
  206. def children(self):
  207. data_array = self.val["__data"]
  208. storage_type_ptr = self.storage_type.pointer()
  209. elements = data_array.cast(storage_type_ptr)
  210. return [(f"[{i}]", elements[i]) for i in range(self.array_size)]
  211. @classmethod
  212. def prettyprint_type(cls, type):
  213. template_type = type.template_argument(0)
  214. template_size = type.template_argument(1)
  215. return f'AK::Array<{template_type}, {template_size}>'
  216. class AKHashMapPrettyPrinter:
  217. def __init__(self, val):
  218. self.val = val
  219. @staticmethod
  220. def _iter_hashtable(val, cb):
  221. entry_type_ptr = val.type.template_argument(0).pointer()
  222. buckets = val["m_buckets"]
  223. for i in range(0, val["m_capacity"]):
  224. bucket = buckets[i]
  225. if bucket["used"]:
  226. cb(bucket["storage"].cast(entry_type_ptr))
  227. @staticmethod
  228. def _iter_hashmap(val, cb):
  229. table = val["m_table"]
  230. AKHashMapPrettyPrinter._iter_hashtable(table, lambda entry: cb(entry["key"], entry["value"]))
  231. def to_string(self):
  232. return AKHashMapPrettyPrinter.prettyprint_type(self.val.type)
  233. def children(self):
  234. elements = []
  235. def cb(key, value):
  236. nonlocal elements
  237. elements.append((f"[{key}]", value))
  238. AKHashMapPrettyPrinter._iter_hashmap(self.val, cb)
  239. return elements
  240. @classmethod
  241. def prettyprint_type(cls, type):
  242. template_types = list(type.template_argument(i) for i in (0, 1))
  243. key, value = list(handler_class_for_type(t).prettyprint_type(t) for t in template_types)
  244. return f'AK::HashMap<{key}, {value}>'
  245. class AKSinglyLinkedList:
  246. def __init__(self, val):
  247. self.val = val
  248. def to_string(self):
  249. return AKSinglyLinkedList.prettyprint_type(self.val.type)
  250. def children(self):
  251. elements = []
  252. node = self.val["m_head"]
  253. while node != 0:
  254. elements.append(node["value"])
  255. node = node["next"]
  256. return [(f"[{i}]", elements[i]) for i in range(len(elements))]
  257. @classmethod
  258. def prettyprint_type(cls, type):
  259. template_type = type.template_argument(0)
  260. return f'AK::SinglyLinkedList<{handler_class_for_type(template_type).prettyprint_type(template_type)}>'
  261. class VirtualAddress:
  262. def __init__(self, val):
  263. self.val = val
  264. def to_string(self):
  265. return self.val["m_address"]
  266. @classmethod
  267. def prettyprint_type(cls, type):
  268. return 'VirtualAddress'
  269. class SerenityPrettyPrinterLocator(gdb.printing.PrettyPrinter):
  270. def __init__(self):
  271. super(SerenityPrettyPrinterLocator, self).__init__("serenity_pretty_printers", [])
  272. def __call__(self, val):
  273. type = gdb.types.get_basic_type(val.type)
  274. handler = handler_class_for_type(type)
  275. if handler is UnhandledType:
  276. return None
  277. return handler(val)
  278. gdb.printing.register_pretty_printer(None, SerenityPrettyPrinterLocator(), replace=True)
  279. class FindThreadCmd(gdb.Command):
  280. """
  281. Find SerenityOS thread for the specified TID.
  282. find_thread TID
  283. """
  284. def __init__(self):
  285. super(FindThreadCmd, self).__init__(
  286. "find_thread", gdb.COMMAND_USER
  287. )
  288. def _find_thread(self, tid):
  289. threads = gdb.parse_and_eval("Kernel::Thread::g_tid_map")
  290. thread = None
  291. def cb(key, value):
  292. nonlocal thread
  293. if int(key["m_value"]) == tid:
  294. thread = value
  295. AKHashMapPrettyPrinter._iter_hashmap(threads, cb)
  296. return thread
  297. def complete(self, text, word):
  298. return gdb.COMPLETE_SYMBOL
  299. def invoke(self, args, from_tty):
  300. argv = gdb.string_to_argv(args)
  301. if len(argv) == 0:
  302. gdb.write("Argument required (TID).\n")
  303. return
  304. tid = int(argv[0])
  305. thread = self._find_thread(tid)
  306. if not thread:
  307. gdb.write(f"No thread with TID {tid} found.\n")
  308. else:
  309. gdb.write(f"{thread}\n")
  310. FindThreadCmd()