nim-gdb.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699
  1. import gdb
  2. import gdb.types
  3. import re
  4. import sys
  5. import traceback
  6. # some feedback that the nim runtime support is loading, isn't a bad
  7. # thing at all.
  8. gdb.write("Loading Nim Runtime support.\n", gdb.STDERR)
  9. # When error occure they occur regularly. This 'caches' known errors
  10. # and prevents them from being reprinted over and over again.
  11. errorSet = set()
  12. def printErrorOnce(id, message):
  13. global errorSet
  14. if id not in errorSet:
  15. errorSet.add(id)
  16. gdb.write("printErrorOnce: " + message, gdb.STDERR)
  17. def debugPrint(x):
  18. gdb.write(str(x) + "\n", gdb.STDERR)
  19. NIM_STRING_TYPES = ["NimStringDesc", "NimStringV2"]
  20. ################################################################################
  21. ##### Type pretty printers
  22. ################################################################################
  23. type_hash_regex = re.compile("^([A-Za-z0-9]*)_([A-Za-z0-9]*)_+([A-Za-z0-9]*)$")
  24. def getNimName(typ):
  25. if m := type_hash_regex.match(typ):
  26. return m.group(2)
  27. return f"unknown <{typ}>"
  28. def getNimRti(type_name):
  29. """ Return a ``gdb.Value`` object for the Nim Runtime Information of ``type_name``. """
  30. # Get static const TNimType variable. This should be available for
  31. # every non trivial Nim type.
  32. m = type_hash_regex.match(type_name)
  33. if m:
  34. lookups = [
  35. "NTI" + m.group(2).lower() + "__" + m.group(3) + "_",
  36. "NTI" + "__" + m.group(3) + "_",
  37. "NTI" + m.group(2).replace("colon", "58").lower() + "__" + m.group(3) + "_"
  38. ]
  39. for l in lookups:
  40. try:
  41. return gdb.parse_and_eval(l)
  42. except:
  43. pass
  44. None
  45. def getNameFromNimRti(rti):
  46. """ Return name (or None) given a Nim RTI ``gdb.Value`` """
  47. try:
  48. # sometimes there isn't a name field -- example enums
  49. return rti['name'].string(encoding="utf-8", errors="ignore")
  50. except:
  51. return None
  52. class NimTypeRecognizer:
  53. # this type map maps from types that are generated in the C files to
  54. # how they are called in nim. To not mix up the name ``int`` from
  55. # system.nim with the name ``int`` that could still appear in
  56. # generated code, ``NI`` is mapped to ``system.int`` and not just
  57. # ``int``.
  58. type_map_static = {
  59. 'NI': 'system.int', 'NI8': 'int8', 'NI16': 'int16', 'NI32': 'int32',
  60. 'NI64': 'int64',
  61. 'NU': 'uint', 'NU8': 'uint8','NU16': 'uint16', 'NU32': 'uint32',
  62. 'NU64': 'uint64',
  63. 'NF': 'float', 'NF32': 'float32', 'NF64': 'float64',
  64. 'NIM_BOOL': 'bool',
  65. 'NIM_CHAR': 'char', 'NCSTRING': 'cstring', 'NimStringDesc': 'string', 'NimStringV2': 'string'
  66. }
  67. # object_type_pattern = re.compile("^(\w*):ObjectType$")
  68. def recognize(self, type_obj):
  69. # skip things we can't handle like functions
  70. if type_obj.code in [gdb.TYPE_CODE_FUNC, gdb.TYPE_CODE_VOID]:
  71. return None
  72. tname = None
  73. if type_obj.tag is not None:
  74. tname = type_obj.tag
  75. elif type_obj.name is not None:
  76. tname = type_obj.name
  77. # handle pointer types
  78. if not tname:
  79. target_type = type_obj
  80. if type_obj.code in [gdb.TYPE_CODE_PTR]:
  81. target_type = type_obj.target()
  82. if target_type.name:
  83. # visualize 'string' as non pointer type (unpack pointer type).
  84. if target_type.name == "NimStringDesc":
  85. tname = target_type.name # could also just return 'string'
  86. else:
  87. rti = getNimRti(target_type.name)
  88. if rti:
  89. return getNameFromNimRti(rti)
  90. if tname:
  91. result = self.type_map_static.get(tname, None)
  92. if result:
  93. return result
  94. elif tname.startswith("tyEnum_"):
  95. return getNimName(tname)
  96. elif tname.startswith("tyTuple__"):
  97. # We make the name be the field types (Just like in Nim)
  98. fields = ", ".join([self.recognize(field.type) for field in type_obj.fields()])
  99. return f"({fields})"
  100. rti = getNimRti(tname)
  101. if rti:
  102. return getNameFromNimRti(rti)
  103. return None
  104. class NimTypePrinter:
  105. """Nim type printer. One printer for all Nim types."""
  106. # enabling and disabling of type printers can be done with the
  107. # following gdb commands:
  108. #
  109. # enable type-printer NimTypePrinter
  110. # disable type-printer NimTypePrinter
  111. # relevant docs: https://sourceware.org/gdb/onlinedocs/gdb/Type-Printing-API.html
  112. name = "NimTypePrinter"
  113. def __init__(self):
  114. self.enabled = True
  115. def instantiate(self):
  116. return NimTypeRecognizer()
  117. ################################################################################
  118. ##### GDB Function, equivalent of Nim's $ operator
  119. ################################################################################
  120. class DollarPrintFunction (gdb.Function):
  121. "Nim's equivalent of $ operator as a gdb function, available in expressions `print $dollar(myvalue)"
  122. dollar_functions = re.findall(
  123. r'(?:NimStringDesc \*|NimStringV2)\s?([A-z0-9_]+?dollar_[A-z0-9_]+?)\(([^,)]*)\);',
  124. gdb.execute("info functions dollar_", True, True)
  125. )
  126. def __init__ (self):
  127. super (DollarPrintFunction, self).__init__("dollar")
  128. @staticmethod
  129. def invoke_static(arg, ignore_errors = False):
  130. if arg.type.code == gdb.TYPE_CODE_PTR and arg.type.target().name in NIM_STRING_TYPES:
  131. return arg
  132. argTypeName = str(arg.type)
  133. for func, arg_typ in DollarPrintFunction.dollar_functions:
  134. # this way of overload resolution cannot deal with type aliases,
  135. # therefore it won't find all overloads.
  136. if arg_typ == argTypeName:
  137. func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTION_DOMAIN).value()
  138. return func_value(arg)
  139. elif arg_typ == argTypeName + " *":
  140. func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTION_DOMAIN).value()
  141. return func_value(arg.address)
  142. if not ignore_errors:
  143. debugPrint(f"No suitable Nim $ operator found for type: {getNimName(argTypeName)}\n")
  144. return None
  145. def invoke(self, arg):
  146. return self.invoke_static(arg)
  147. DollarPrintFunction()
  148. ################################################################################
  149. ##### GDB Function, Nim string comparison
  150. ################################################################################
  151. class NimStringEqFunction (gdb.Function):
  152. """Compare Nim strings for example in conditionals for breakpoints."""
  153. def __init__ (self):
  154. super (NimStringEqFunction, self).__init__("nimstreq")
  155. @staticmethod
  156. def invoke_static(arg1,arg2):
  157. if arg1.type.code == gdb.TYPE_CODE_PTR and arg1.type.target().name in NIM_STRING_TYPES:
  158. str1 = NimStringPrinter(arg1).to_string()
  159. else:
  160. str1 = arg1.string()
  161. if arg2.type.code == gdb.TYPE_CODE_PTR and arg2.type.target().name in NIM_STRING_TYPES:
  162. str2 = NimStringPrinter(arg1).to_string()
  163. else:
  164. str2 = arg2.string()
  165. return str1 == str2
  166. def invoke(self, arg1, arg2):
  167. return self.invoke_static(arg1, arg2)
  168. NimStringEqFunction()
  169. ################################################################################
  170. ##### GDB Command, equivalent of Nim's $ operator
  171. ################################################################################
  172. class DollarPrintCmd (gdb.Command):
  173. """Dollar print command for Nim, `$ expr` will invoke Nim's $ operator and print the result."""
  174. def __init__ (self):
  175. super (DollarPrintCmd, self).__init__ ("$", gdb.COMMAND_DATA, gdb.COMPLETE_EXPRESSION)
  176. def invoke(self, arg, from_tty):
  177. param = gdb.parse_and_eval(arg)
  178. strValue = DollarPrintFunction.invoke_static(param)
  179. if strValue:
  180. gdb.write(
  181. str(NimStringPrinter(strValue)) + "\n",
  182. gdb.STDOUT
  183. )
  184. # could not find a suitable dollar overload. This here is the
  185. # fallback to get sensible output of basic types anyway.
  186. elif param.type.code == gdb.TYPE_CODE_ARRAY and param.type.target().name == "char":
  187. gdb.write(param.string("utf-8", "ignore") + "\n", gdb.STDOUT)
  188. elif param.type.code == gdb.TYPE_CODE_INT:
  189. gdb.write(str(int(param)) + "\n", gdb.STDOUT)
  190. elif param.type.name == "NIM_BOOL":
  191. if int(param) != 0:
  192. gdb.write("true\n", gdb.STDOUT)
  193. else:
  194. gdb.write("false\n", gdb.STDOUT)
  195. DollarPrintCmd()
  196. ################################################################################
  197. ##### GDB Commands to invoke common nim tools.
  198. ################################################################################
  199. import subprocess, os
  200. class KochCmd (gdb.Command):
  201. """Command that invokes ``koch'', the build tool for the compiler."""
  202. def __init__ (self):
  203. super (KochCmd, self).__init__ ("koch",
  204. gdb.COMMAND_USER, gdb.COMPLETE_FILENAME)
  205. self.binary = os.path.join(
  206. os.path.dirname(os.path.dirname(__file__)), "koch")
  207. def invoke(self, argument, from_tty):
  208. subprocess.run([self.binary] + gdb.string_to_argv(argument))
  209. KochCmd()
  210. class NimCmd (gdb.Command):
  211. """Command that invokes ``nim'', the nim compiler."""
  212. def __init__ (self):
  213. super (NimCmd, self).__init__ ("nim",
  214. gdb.COMMAND_USER, gdb.COMPLETE_FILENAME)
  215. self.binary = os.path.join(
  216. os.path.dirname(os.path.dirname(__file__)), "bin/nim")
  217. def invoke(self, argument, from_tty):
  218. subprocess.run([self.binary] + gdb.string_to_argv(argument))
  219. NimCmd()
  220. class NimbleCmd (gdb.Command):
  221. """Command that invokes ``nimble'', the nim package manager and build tool."""
  222. def __init__ (self):
  223. super (NimbleCmd, self).__init__ ("nimble",
  224. gdb.COMMAND_USER, gdb.COMPLETE_FILENAME)
  225. self.binary = os.path.join(
  226. os.path.dirname(os.path.dirname(__file__)), "bin/nimble")
  227. def invoke(self, argument, from_tty):
  228. subprocess.run([self.binary] + gdb.string_to_argv(argument))
  229. NimbleCmd()
  230. ################################################################################
  231. ##### Value pretty printers
  232. ################################################################################
  233. class NimBoolPrinter:
  234. pattern = re.compile(r'^NIM_BOOL$')
  235. def __init__(self, val):
  236. self.val = val
  237. def to_string(self):
  238. if self.val == 0:
  239. return "false"
  240. else:
  241. return "true"
  242. ################################################################################
  243. def strFromLazy(strVal):
  244. if isinstance(strVal, str):
  245. return strVal
  246. else:
  247. return strVal.value().string("utf-8")
  248. class NimStringPrinter:
  249. pattern = re.compile(r'^(NimStringDesc \*|NimStringV2)$')
  250. def __init__(self, val):
  251. self.val = val
  252. def display_hint(self):
  253. return 'string'
  254. def to_string(self):
  255. if self.val:
  256. if self.val.type.name == "NimStringV2":
  257. l = int(self.val["len"])
  258. data = self.val["p"]["data"]
  259. else:
  260. l = int(self.val['Sup']['len'])
  261. data = self.val["data"]
  262. return data.lazy_string(encoding="utf-8", length=l)
  263. else:
  264. return ""
  265. def __str__(self):
  266. return strFromLazy(self.to_string())
  267. class NimRopePrinter:
  268. pattern = re.compile(r'^tyObject_RopeObj__([A-Za-z0-9]*) \*$')
  269. def __init__(self, val):
  270. self.val = val
  271. def display_hint(self):
  272. return 'string'
  273. def to_string(self):
  274. if self.val:
  275. left = NimRopePrinter(self.val["left"]).to_string()
  276. data = NimStringPrinter(self.val["data"]).to_string()
  277. right = NimRopePrinter(self.val["right"]).to_string()
  278. return left + data + right
  279. else:
  280. return ""
  281. ################################################################################
  282. def reprEnum(e, typ):
  283. # Casts the value to the enum type and then calls the enum printer
  284. e = int(e)
  285. val = gdb.Value(e).cast(typ)
  286. return strFromLazy(NimEnumPrinter(val).to_string())
  287. def enumNti(typeNimName, idString):
  288. typeInfoName = "NTI" + typeNimName.lower() + "__" + idString + "_"
  289. nti = gdb.lookup_global_symbol(typeInfoName)
  290. if nti is None:
  291. typeInfoName = "NTI" + "__" + idString + "_"
  292. nti = gdb.lookup_global_symbol(typeInfoName)
  293. return (typeInfoName, nti)
  294. class NimEnumPrinter:
  295. pattern = re.compile(r'^tyEnum_([A-Za-z0-9]+)__([A-Za-z0-9]*)$')
  296. enumReprProc = gdb.lookup_global_symbol("reprEnum", gdb.SYMBOL_FUNCTION_DOMAIN)
  297. def __init__(self, val):
  298. self.val = val
  299. typeName = self.val.type.name
  300. match = self.pattern.match(typeName)
  301. self.typeNimName = match.group(1)
  302. typeInfoName, self.nti = enumNti(self.typeNimName, match.group(2))
  303. def to_string(self):
  304. if NimEnumPrinter.enumReprProc and self.nti:
  305. # Use the old runtimes enumRepr function.
  306. # We call the Nim proc itself so that the implementation is correct
  307. f = gdb.newest_frame()
  308. # We need to strip the quotes so it looks like an enum instead of a string
  309. reprProc = NimEnumPrinter.enumReprProc.value()
  310. return str(reprProc(self.val, self.nti.value(f).address)).strip('"')
  311. elif dollarResult := DollarPrintFunction.invoke_static(self.val):
  312. # New runtime doesn't use enumRepr so we instead try and call the
  313. # dollar function for it
  314. return str(NimStringPrinter(dollarResult))
  315. else:
  316. return self.typeNimName + "(" + str(int(self.val)) + ")"
  317. ################################################################################
  318. class NimSetPrinter:
  319. ## the set printer is limited to sets that fit in an integer. Other
  320. ## sets are compiled to `NU8 *` (ptr uint8) and are invisible to
  321. ## gdb (currently).
  322. pattern = re.compile(r'^tySet_tyEnum_([A-Za-z0-9]+)__([A-Za-z0-9]*)$')
  323. def __init__(self, val):
  324. self.val = val
  325. typeName = self.val.type.name
  326. match = self.pattern.match(typeName)
  327. self.typeNimName = match.group(1)
  328. def to_string(self):
  329. # Remove the tySet from the type name
  330. typ = gdb.lookup_type(self.val.type.name[6:])
  331. enumStrings = []
  332. val = int(self.val)
  333. i = 0
  334. while val > 0:
  335. if (val & 1) == 1:
  336. enumStrings.append(reprEnum(i, typ))
  337. val = val >> 1
  338. i += 1
  339. return '{' + ', '.join(enumStrings) + '}'
  340. ################################################################################
  341. class NimHashSetPrinter:
  342. pattern = re.compile(r'^tyObject_(HashSet)__([A-Za-z0-9]*)$')
  343. def __init__(self, val):
  344. self.val = val
  345. def display_hint(self):
  346. return 'array'
  347. def to_string(self):
  348. counter = 0
  349. capacity = 0
  350. if self.val:
  351. counter = int(self.val['counter'])
  352. if self.val['data']:
  353. capacity = int(self.val['data']['Sup']['len'])
  354. return 'HashSet({0}, {1})'.format(counter, capacity)
  355. def children(self):
  356. if self.val:
  357. data = NimSeqPrinter(self.val['data'])
  358. for idxStr, entry in data.children():
  359. if int(entry['Field0']) > 0:
  360. yield ("data." + idxStr + ".Field1", str(entry['Field1']))
  361. ################################################################################
  362. class NimSeq:
  363. # Wrapper around sequences.
  364. # This handles the differences between old and new runtime
  365. def __init__(self, val):
  366. self.val = val
  367. # new runtime has sequences on stack, old has them on heap
  368. self.new = val.type.code != gdb.TYPE_CODE_PTR
  369. if self.new:
  370. # Some seqs are just the content and to save repeating ourselves we do
  371. # handle them here. Only thing that needs to check this is the len/data getters
  372. self.isContent = val.type.name.endswith("Content")
  373. def __bool__(self):
  374. if self.new:
  375. return self.val is not None
  376. else:
  377. return bool(self.val)
  378. def __len__(self):
  379. if not self:
  380. return 0
  381. if self.new:
  382. if self.isContent:
  383. return int(self.val["cap"])
  384. else:
  385. return int(self.val["len"])
  386. else:
  387. return self.val["Sup"]["len"]
  388. @property
  389. def data(self):
  390. if self.new:
  391. if self.isContent:
  392. return self.val["data"]
  393. elif self.val["p"]:
  394. return self.val["p"]["data"]
  395. else:
  396. return self.val["data"]
  397. @property
  398. def cap(self):
  399. if not self:
  400. return 0
  401. if self.new:
  402. if self.isContent:
  403. return int(self.val["cap"])
  404. elif self.val["p"]:
  405. return int(self.val["p"]["cap"])
  406. else:
  407. return 0
  408. return int(self.val['Sup']['reserved'])
  409. class NimSeqPrinter:
  410. pattern = re.compile(r'^tySequence_\w*\s?\*?$')
  411. def __init__(self, val):
  412. self.val = NimSeq(val)
  413. def display_hint(self):
  414. return 'array'
  415. def to_string(self):
  416. return f'seq({len(self.val)}, {self.val.cap})'
  417. def children(self):
  418. if self.val:
  419. val = self.val
  420. length = len(val)
  421. if length <= 0:
  422. return
  423. data = val.data
  424. inaccessible = False
  425. for i in range(length):
  426. if inaccessible:
  427. return
  428. try:
  429. str(data[i])
  430. yield "data[{0}]".format(i), data[i]
  431. except RuntimeError:
  432. inaccessible = True
  433. yield "data[{0}]".format(i), "inaccessible"
  434. ################################################################################
  435. class NimArrayPrinter:
  436. pattern = re.compile(r'^tyArray_\w*$')
  437. def __init__(self, val):
  438. self.val = val
  439. def display_hint(self):
  440. return 'array'
  441. def to_string(self):
  442. return 'array'
  443. def children(self):
  444. length = self.val.type.sizeof // self.val[0].type.sizeof
  445. align = len(str(length-1))
  446. for i in range(length):
  447. yield ("[{0:>{1}}]".format(i, align), self.val[i])
  448. ################################################################################
  449. class NimStringTablePrinter:
  450. pattern = re.compile(r'^tyObject_(StringTableObj)__([A-Za-z0-9]*)(:? \*)?$')
  451. def __init__(self, val):
  452. self.val = val
  453. def display_hint(self):
  454. return 'map'
  455. def to_string(self):
  456. counter = 0
  457. capacity = 0
  458. if self.val:
  459. counter = int(self.val['counter'])
  460. if self.val['data']:
  461. capacity = int(self.val['data']['Sup']['len'])
  462. return 'StringTableObj({0}, {1})'.format(counter, capacity)
  463. def children(self):
  464. if self.val:
  465. data = NimSeqPrinter(self.val['data'].referenced_value())
  466. for idxStr, entry in data.children():
  467. if int(entry['Field0']) != 0:
  468. yield (idxStr + ".Field0", entry['Field0'])
  469. yield (idxStr + ".Field1", entry['Field1'])
  470. ################################################################
  471. class NimTablePrinter:
  472. pattern = re.compile(r'^tyObject_(Table)__([A-Za-z0-9]*)(:? \*)?$')
  473. def __init__(self, val):
  474. self.val = val
  475. def display_hint(self):
  476. return 'map'
  477. def to_string(self):
  478. counter = 0
  479. capacity = 0
  480. if self.val:
  481. counter = int(self.val['counter'])
  482. if self.val['data']:
  483. capacity = NimSeq(self.val["data"]).cap
  484. return 'Table({0}, {1})'.format(counter, capacity)
  485. def children(self):
  486. if self.val:
  487. data = NimSeqPrinter(self.val['data'])
  488. for idxStr, entry in data.children():
  489. if int(entry['Field0']) != 0:
  490. yield (idxStr + '.Field1', entry['Field1'])
  491. yield (idxStr + '.Field2', entry['Field2'])
  492. ################################################################################
  493. class NimTuplePrinter:
  494. pattern = re.compile(r"^tyTuple__([A-Za-z0-9]*)")
  495. def __init__(self, val):
  496. self.val = val
  497. def to_string(self):
  498. # We don't have field names so just print out the tuple as if it was anonymous
  499. tupleValues = [str(self.val[field.name]) for field in self.val.type.fields()]
  500. return f"({', '.join(tupleValues)})"
  501. ################################################################################
  502. class NimFrameFilter:
  503. def __init__(self):
  504. self.name = "nim-frame-filter"
  505. self.enabled = True
  506. self.priority = 100
  507. self.hidden = {"NimMainInner","NimMain", "main"}
  508. def filter(self, iterator):
  509. for framedecorator in iterator:
  510. if framedecorator.function() not in self.hidden:
  511. yield framedecorator
  512. ################################################################################
  513. def makematcher(klass):
  514. def matcher(val):
  515. typeName = str(val.type)
  516. try:
  517. if hasattr(klass, 'pattern') and hasattr(klass, '__name__'):
  518. # print(typeName + " <> " + klass.__name__)
  519. if klass.pattern.match(typeName):
  520. return klass(val)
  521. except Exception as e:
  522. print(klass)
  523. printErrorOnce(typeName, "No matcher for type '" + typeName + "': " + str(e) + "\n")
  524. return matcher
  525. def register_nim_pretty_printers_for_object(objfile):
  526. nimMainSym = gdb.lookup_global_symbol("NimMain", gdb.SYMBOL_FUNCTION_DOMAIN)
  527. if nimMainSym and nimMainSym.symtab.objfile == objfile:
  528. print("set Nim pretty printers for ", objfile.filename)
  529. gdb.types.register_type_printer(objfile, NimTypePrinter())
  530. objfile.pretty_printers = [makematcher(var) for var in list(globals().values()) if hasattr(var, 'pattern')]
  531. # Register pretty printers for all objfiles that are already loaded.
  532. for old_objfile in gdb.objfiles():
  533. register_nim_pretty_printers_for_object(old_objfile)
  534. # Register an event handler to register nim pretty printers for all future objfiles.
  535. def new_object_handler(event):
  536. register_nim_pretty_printers_for_object(event.new_objfile)
  537. gdb.events.new_objfile.connect(new_object_handler)
  538. gdb.frame_filters = {"nim-frame-filter": NimFrameFilter()}