nim-gdb.py 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805
  1. import gdb
  2. import re
  3. import sys
  4. import traceback
  5. # some feedback that the nim runtime support is loading, isn't a bad
  6. # thing at all.
  7. gdb.write("Loading Nim Runtime support.\n", gdb.STDERR)
  8. # When error occure they occur regularly. This 'caches' known errors
  9. # and prevents them from being reprinted over and over again.
  10. errorSet = set()
  11. def printErrorOnce(id, message):
  12. global errorSet
  13. if id not in errorSet:
  14. errorSet.add(id)
  15. gdb.write("printErrorOnce: " + message, gdb.STDERR)
  16. ################################################################################
  17. ##### Type pretty printers
  18. ################################################################################
  19. type_hash_regex = re.compile("^([A-Za-z0-9]*)_([A-Za-z0-9]*)_+([A-Za-z0-9]*)$")
  20. def getNimRti(type_name):
  21. """ Return a ``gdb.Value`` object for the Nim Runtime Information of ``type_name``. """
  22. # Get static const TNimType variable. This should be available for
  23. # every non trivial Nim type.
  24. m = type_hash_regex.match(type_name)
  25. lookups = [
  26. "NTI" + m.group(2).lower() + "__" + m.group(3) + "_",
  27. "NTI" + "__" + m.group(3) + "_",
  28. "NTI" + m.group(2).replace("colon", "58").lower() + "__" + m.group(3) + "_"
  29. ]
  30. if m:
  31. for l in lookups:
  32. try:
  33. return gdb.parse_and_eval(l)
  34. except:
  35. pass
  36. None
  37. def getNameFromNimRti(rti):
  38. """ Return name (or None) given a Nim RTI ``gdb.Value`` """
  39. try:
  40. # sometimes there isn't a name field -- example enums
  41. return rti['name'].string(encoding="utf-8", errors="ignore")
  42. except:
  43. return None
  44. class NimTypeRecognizer:
  45. # this type map maps from types that are generated in the C files to
  46. # how they are called in nim. To not mix up the name ``int`` from
  47. # system.nim with the name ``int`` that could still appear in
  48. # generated code, ``NI`` is mapped to ``system.int`` and not just
  49. # ``int``.
  50. type_map_static = {
  51. 'NI': 'system.int', 'NI8': 'int8', 'NI16': 'int16', 'NI32': 'int32',
  52. 'NI64': 'int64',
  53. 'NU': 'uint', 'NU8': 'uint8','NU16': 'uint16', 'NU32': 'uint32',
  54. 'NU64': 'uint64',
  55. 'NF': 'float', 'NF32': 'float32', 'NF64': 'float64',
  56. 'NIM_BOOL': 'bool',
  57. 'NIM_CHAR': 'char', 'NCSTRING': 'cstring', 'NimStringDesc': 'string'
  58. }
  59. # object_type_pattern = re.compile("^(\w*):ObjectType$")
  60. def recognize(self, type_obj):
  61. # skip things we can't handle like functions
  62. if type_obj.code in [gdb.TYPE_CODE_FUNC, gdb.TYPE_CODE_VOID]:
  63. return None
  64. tname = None
  65. if type_obj.tag is not None:
  66. tname = type_obj.tag
  67. elif type_obj.name is not None:
  68. tname = type_obj.name
  69. # handle pointer types
  70. if not tname:
  71. target_type = type_obj
  72. if type_obj.code in [gdb.TYPE_CODE_PTR]:
  73. target_type = type_obj.target()
  74. if target_type.name:
  75. # visualize 'string' as non pointer type (unpack pointer type).
  76. if target_type.name == "NimStringDesc":
  77. tname = target_type.name # could also just return 'string'
  78. else:
  79. rti = getNimRti(target_type.name)
  80. if rti:
  81. return getNameFromNimRti(rti)
  82. if tname:
  83. result = self.type_map_static.get(tname, None)
  84. if result:
  85. return result
  86. rti = getNimRti(tname)
  87. if rti:
  88. return getNameFromNimRti(rti)
  89. return None
  90. class NimTypePrinter:
  91. """Nim type printer. One printer for all Nim types."""
  92. # enabling and disabling of type printers can be done with the
  93. # following gdb commands:
  94. #
  95. # enable type-printer NimTypePrinter
  96. # disable type-printer NimTypePrinter
  97. # relevant docs: https://sourceware.org/gdb/onlinedocs/gdb/Type-Printing-API.html
  98. name = "NimTypePrinter"
  99. def __init__(self):
  100. self.enabled = True
  101. def instantiate(self):
  102. return NimTypeRecognizer()
  103. ################################################################################
  104. ##### GDB Function, equivalent of Nim's $ operator
  105. ################################################################################
  106. class DollarPrintFunction (gdb.Function):
  107. "Nim's equivalent of $ operator as a gdb function, available in expressions `print $dollar(myvalue)"
  108. dollar_functions = re.findall(
  109. 'NimStringDesc \*(dollar__[A-z0-9_]+?)\(([^,)]*)\);',
  110. gdb.execute("info functions dollar__", True, True)
  111. )
  112. def __init__ (self):
  113. super (DollarPrintFunction, self).__init__("dollar")
  114. @staticmethod
  115. def invoke_static(arg):
  116. if arg.type.code == gdb.TYPE_CODE_PTR and arg.type.target().name == "NimStringDesc":
  117. return arg
  118. argTypeName = str(arg.type)
  119. for func, arg_typ in DollarPrintFunction.dollar_functions:
  120. # this way of overload resolution cannot deal with type aliases,
  121. # therefore it won't find all overloads.
  122. if arg_typ == argTypeName:
  123. func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value()
  124. return func_value(arg)
  125. elif arg_typ == argTypeName + " *":
  126. func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value()
  127. return func_value(arg.address)
  128. printErrorOnce(argTypeName, "No suitable Nim $ operator found for type: " + argTypeName + "\n")
  129. return None
  130. def invoke(self, arg):
  131. return self.invoke_static(arg)
  132. DollarPrintFunction()
  133. ################################################################################
  134. ##### GDB Function, Nim string comparison
  135. ################################################################################
  136. class NimStringEqFunction (gdb.Function):
  137. """Compare Nim strings for example in conditionals for breakpoints."""
  138. def __init__ (self):
  139. super (NimStringEqFunction, self).__init__("nimstreq")
  140. @staticmethod
  141. def invoke_static(arg1,arg2):
  142. if arg1.type.code == gdb.TYPE_CODE_PTR and arg1.type.target().name == "NimStringDesc":
  143. str1 = NimStringPrinter(arg1).to_string()
  144. else:
  145. str1 = arg1.string()
  146. if arg2.type.code == gdb.TYPE_CODE_PTR and arg2.type.target().name == "NimStringDesc":
  147. str2 = NimStringPrinter(arg1).to_string()
  148. else:
  149. str2 = arg2.string()
  150. return str1 == str2
  151. def invoke(self, arg1, arg2):
  152. return self.invoke_static(arg1, arg2)
  153. NimStringEqFunction()
  154. ################################################################################
  155. ##### GDB Command, equivalent of Nim's $ operator
  156. ################################################################################
  157. class DollarPrintCmd (gdb.Command):
  158. """Dollar print command for Nim, `$ expr` will invoke Nim's $ operator and print the result."""
  159. def __init__ (self):
  160. super (DollarPrintCmd, self).__init__ ("$", gdb.COMMAND_DATA, gdb.COMPLETE_EXPRESSION)
  161. def invoke(self, arg, from_tty):
  162. param = gdb.parse_and_eval(arg)
  163. strValue = DollarPrintFunction.invoke_static(param)
  164. if strValue:
  165. gdb.write(
  166. NimStringPrinter(strValue).to_string() + "\n",
  167. gdb.STDOUT
  168. )
  169. # could not find a suitable dollar overload. This here is the
  170. # fallback to get sensible output of basic types anyway.
  171. elif param.type.code == gdb.TYPE_CODE_ARRAY and param.type.target().name == "char":
  172. gdb.write(param.string("utf-8", "ignore") + "\n", gdb.STDOUT)
  173. elif param.type.code == gdb.TYPE_CODE_INT:
  174. gdb.write(str(int(param)) + "\n", gdb.STDOUT)
  175. elif param.type.name == "NIM_BOOL":
  176. if int(param) != 0:
  177. gdb.write("true\n", gdb.STDOUT)
  178. else:
  179. gdb.write("false\n", gdb.STDOUT)
  180. DollarPrintCmd()
  181. ################################################################################
  182. ##### GDB Commands to invoke common nim tools.
  183. ################################################################################
  184. import subprocess, os
  185. class KochCmd (gdb.Command):
  186. """Command that invokes ``koch'', the build tool for the compiler."""
  187. def __init__ (self):
  188. super (KochCmd, self).__init__ ("koch",
  189. gdb.COMMAND_USER, gdb.COMPLETE_FILENAME)
  190. self.binary = os.path.join(
  191. os.path.dirname(os.path.dirname(__file__)), "koch")
  192. def invoke(self, argument, from_tty):
  193. import os
  194. subprocess.run([self.binary] + gdb.string_to_argv(argument))
  195. KochCmd()
  196. class NimCmd (gdb.Command):
  197. """Command that invokes ``nim'', the nim compiler."""
  198. def __init__ (self):
  199. super (NimCmd, self).__init__ ("nim",
  200. gdb.COMMAND_USER, gdb.COMPLETE_FILENAME)
  201. self.binary = os.path.join(
  202. os.path.dirname(os.path.dirname(__file__)), "bin/nim")
  203. def invoke(self, argument, from_tty):
  204. subprocess.run([self.binary] + gdb.string_to_argv(argument))
  205. NimCmd()
  206. class NimbleCmd (gdb.Command):
  207. """Command that invokes ``nimble'', the nim package manager and build tool."""
  208. def __init__ (self):
  209. super (NimbleCmd, self).__init__ ("nimble",
  210. gdb.COMMAND_USER, gdb.COMPLETE_FILENAME)
  211. self.binary = os.path.join(
  212. os.path.dirname(os.path.dirname(__file__)), "bin/nimble")
  213. def invoke(self, argument, from_tty):
  214. subprocess.run([self.binary] + gdb.string_to_argv(argument))
  215. NimbleCmd()
  216. ################################################################################
  217. ##### Value pretty printers
  218. ################################################################################
  219. class NimBoolPrinter:
  220. pattern = re.compile(r'^NIM_BOOL$')
  221. def __init__(self, val):
  222. self.val = val
  223. def to_string(self):
  224. if self.val == 0:
  225. return "false"
  226. else:
  227. return "true"
  228. ################################################################################
  229. class NimStringPrinter:
  230. pattern = re.compile(r'^NimStringDesc \*$')
  231. def __init__(self, val):
  232. self.val = val
  233. def display_hint(self):
  234. return 'string'
  235. def to_string(self):
  236. if self.val:
  237. l = int(self.val['Sup']['len'])
  238. return self.val['data'].lazy_string(encoding="utf-8", length=l)
  239. else:
  240. return ""
  241. class NimRopePrinter:
  242. pattern = re.compile(r'^tyObject_RopeObj__([A-Za-z0-9]*) \*$')
  243. def __init__(self, val):
  244. self.val = val
  245. def display_hint(self):
  246. return 'string'
  247. def to_string(self):
  248. if self.val:
  249. left = NimRopePrinter(self.val["left"]).to_string()
  250. data = NimStringPrinter(self.val["data"]).to_string()
  251. right = NimRopePrinter(self.val["right"]).to_string()
  252. return left + data + right
  253. else:
  254. return ""
  255. ################################################################################
  256. # proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} =
  257. # ## Return string representation for enumeration values
  258. # var n = typ.node
  259. # if ntfEnumHole notin typ.flags:
  260. # let o = e - n.sons[0].offset
  261. # if o >= 0 and o <% typ.node.len:
  262. # return $n.sons[o].name
  263. # else:
  264. # # ugh we need a slow linear search:
  265. # var s = n.sons
  266. # for i in 0 .. n.len-1:
  267. # if s[i].offset == e:
  268. # return $s[i].name
  269. # result = $e & " (invalid data!)"
  270. def reprEnum(e, typ):
  271. """ this is a port of the nim runtime function `reprEnum` to python """
  272. e = int(e)
  273. n = typ["node"]
  274. flags = int(typ["flags"])
  275. # 1 << 6 is {ntfEnumHole}
  276. if ((1 << 6) & flags) == 0:
  277. o = e - int(n["sons"][0]["offset"])
  278. if o >= 0 and 0 < int(n["len"]):
  279. return n["sons"][o]["name"].string("utf-8", "ignore")
  280. else:
  281. # ugh we need a slow linear search:
  282. s = n["sons"]
  283. for i in range(0, int(n["len"])):
  284. if int(s[i]["offset"]) == e:
  285. return s[i]["name"].string("utf-8", "ignore")
  286. return str(e) + " (invalid data!)"
  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. def __init__(self, val):
  297. self.val = val
  298. typeName = self.val.type.name
  299. match = self.pattern.match(typeName)
  300. self.typeNimName = match.group(1)
  301. typeInfoName, self.nti = enumNti(self.typeNimName, match.group(2))
  302. if self.nti is None:
  303. printErrorOnce(typeInfoName, f"NimEnumPrinter: lookup global symbol: '{typeInfoName}' failed for {typeName}.\n")
  304. def to_string(self):
  305. if self.nti:
  306. arg0 = self.val
  307. arg1 = self.nti.value(gdb.newest_frame())
  308. return reprEnum(arg0, arg1)
  309. else:
  310. return self.typeNimName + "(" + str(int(self.val)) + ")"
  311. ################################################################################
  312. class NimSetPrinter:
  313. ## the set printer is limited to sets that fit in an integer. Other
  314. ## sets are compiled to `NU8 *` (ptr uint8) and are invisible to
  315. ## gdb (currently).
  316. pattern = re.compile(r'^tySet_tyEnum_([A-Za-z0-9]+)__([A-Za-z0-9]*)$')
  317. def __init__(self, val):
  318. self.val = val
  319. typeName = self.val.type.name
  320. match = self.pattern.match(typeName)
  321. self.typeNimName = match.group(1)
  322. typeInfoName, self.nti = enumNti(self.typeNimName, match.group(2))
  323. if self.nti is None:
  324. printErrorOnce(typeInfoName, f"NimSetPrinter: lookup global symbol: '{typeInfoName}' failed for {typeName}.\n")
  325. def to_string(self):
  326. if self.nti:
  327. nti = self.nti.value(gdb.newest_frame())
  328. enumStrings = []
  329. val = int(self.val)
  330. i = 0
  331. while val > 0:
  332. if (val & 1) == 1:
  333. enumStrings.append(reprEnum(i, nti))
  334. val = val >> 1
  335. i += 1
  336. return '{' + ', '.join(enumStrings) + '}'
  337. else:
  338. return str(int(self.val))
  339. ################################################################################
  340. class NimHashSetPrinter:
  341. pattern = re.compile(r'^tyObject_(HashSet)__([A-Za-z0-9]*)$')
  342. def __init__(self, val):
  343. self.val = val
  344. def display_hint(self):
  345. return 'array'
  346. def to_string(self):
  347. counter = 0
  348. capacity = 0
  349. if self.val:
  350. counter = int(self.val['counter'])
  351. if self.val['data']:
  352. capacity = int(self.val['data']['Sup']['len'])
  353. return 'HashSet({0}, {1})'.format(counter, capacity)
  354. def children(self):
  355. if self.val:
  356. data = NimSeqPrinter(self.val['data'])
  357. for idxStr, entry in data.children():
  358. if int(entry['Field0']) > 0:
  359. yield ("data." + idxStr + ".Field1", str(entry['Field1']))
  360. ################################################################################
  361. class NimSeqPrinter:
  362. # the pointer is explicity part of the type. So it is part of
  363. # ``pattern``.
  364. pattern = re.compile(r'^tySequence_\w* \*$')
  365. def __init__(self, val):
  366. self.val = val
  367. def display_hint(self):
  368. return 'array'
  369. def to_string(self):
  370. len = 0
  371. cap = 0
  372. if self.val:
  373. len = int(self.val['Sup']['len'])
  374. cap = int(self.val['Sup']['reserved'])
  375. return 'seq({0}, {1})'.format(len, cap)
  376. def children(self):
  377. if self.val:
  378. val = self.val
  379. valType = val.type
  380. length = int(val['Sup']['len'])
  381. if length <= 0:
  382. return
  383. dataType = valType['data'].type
  384. data = val['data']
  385. if self.val.type.name is None:
  386. dataType = valType['data'].type.target().pointer()
  387. data = val['data'].cast(dataType)
  388. inaccessible = False
  389. for i in range(length):
  390. if inaccessible:
  391. return
  392. try:
  393. str(data[i])
  394. yield "data[{0}]".format(i), data[i]
  395. except RuntimeError:
  396. inaccessible = True
  397. yield "data[{0}]".format(i), "inaccessible"
  398. ################################################################################
  399. class NimArrayPrinter:
  400. pattern = re.compile(r'^tyArray_\w*$')
  401. def __init__(self, val):
  402. self.val = val
  403. def display_hint(self):
  404. return 'array'
  405. def to_string(self):
  406. return 'array'
  407. def children(self):
  408. length = self.val.type.sizeof // self.val[0].type.sizeof
  409. align = len(str(length-1))
  410. for i in range(length):
  411. yield ("[{0:>{1}}]".format(i, align), self.val[i])
  412. ################################################################################
  413. class NimStringTablePrinter:
  414. pattern = re.compile(r'^tyObject_(StringTableObj)__([A-Za-z0-9]*)(:? \*)?$')
  415. def __init__(self, val):
  416. self.val = val
  417. def display_hint(self):
  418. return 'map'
  419. def to_string(self):
  420. counter = 0
  421. capacity = 0
  422. if self.val:
  423. counter = int(self.val['counter'])
  424. if self.val['data']:
  425. capacity = int(self.val['data']['Sup']['len'])
  426. return 'StringTableObj({0}, {1})'.format(counter, capacity)
  427. def children(self):
  428. if self.val:
  429. data = NimSeqPrinter(self.val['data'].referenced_value())
  430. for idxStr, entry in data.children():
  431. if int(entry['Field0']) != 0:
  432. yield (idxStr + ".Field0", entry['Field0'])
  433. yield (idxStr + ".Field1", entry['Field1'])
  434. ################################################################
  435. class NimTablePrinter:
  436. pattern = re.compile(r'^tyObject_(Table)__([A-Za-z0-9]*)(:? \*)?$')
  437. def __init__(self, val):
  438. self.val = val
  439. def display_hint(self):
  440. return 'map'
  441. def to_string(self):
  442. counter = 0
  443. capacity = 0
  444. if self.val:
  445. counter = int(self.val['counter'])
  446. if self.val['data']:
  447. capacity = int(self.val['data']['Sup']['len'])
  448. return 'Table({0}, {1})'.format(counter, capacity)
  449. def children(self):
  450. if self.val:
  451. data = NimSeqPrinter(self.val['data'])
  452. for idxStr, entry in data.children():
  453. if int(entry['Field0']) != 0:
  454. yield (idxStr + '.Field1', entry['Field1'])
  455. yield (idxStr + '.Field2', entry['Field2'])
  456. ################################################################
  457. # this is untested, therefore disabled
  458. # class NimObjectPrinter:
  459. # pattern = re.compile(r'^tyObject_([A-Za-z0-9]+)__(_?[A-Za-z0-9]*)(:? \*)?$')
  460. # def __init__(self, val):
  461. # self.val = val
  462. # self.valType = None
  463. # self.valTypeNimName = None
  464. # def display_hint(self):
  465. # return 'object'
  466. # def _determineValType(self):
  467. # if self.valType is None:
  468. # vt = self.val.type
  469. # if vt.name is None:
  470. # target = vt.target()
  471. # self.valType = target.pointer()
  472. # self.fields = target.fields()
  473. # self.valTypeName = target.name
  474. # self.isPointer = True
  475. # else:
  476. # self.valType = vt
  477. # self.fields = vt.fields()
  478. # self.valTypeName = vt.name
  479. # self.isPointer = False
  480. # def to_string(self):
  481. # if self.valTypeNimName is None:
  482. # self._determineValType()
  483. # match = self.pattern.match(self.valTypeName)
  484. # self.valTypeNimName = match.group(1)
  485. # return self.valTypeNimName
  486. # def children(self):
  487. # self._determineValType()
  488. # if self.isPointer and int(self.val) == 0:
  489. # return
  490. # self.baseVal = self.val.referenced_value() if self.isPointer else self.val
  491. # for c in self.handleFields(self.baseVal, getNimRti(self.valTypeName)):
  492. # yield c
  493. # def handleFields(self, currVal, rti, fields = None):
  494. # rtiSons = None
  495. # discField = (0, None)
  496. # seenSup = False
  497. # if fields is None:
  498. # fields = self.fields
  499. # try: # XXX: remove try after finished debugging this method
  500. # for (i, field) in enumerate(fields):
  501. # if field.name == "Sup": # inherited data
  502. # seenSup = True
  503. # baseRef = rti['base']
  504. # if baseRef:
  505. # baseRti = baseRef.referenced_value()
  506. # baseVal = currVal['Sup']
  507. # baseValType = baseVal.type
  508. # if baseValType.name is None:
  509. # baseValType = baseValType.target().pointer()
  510. # baseValFields = baseValType.target().fields()
  511. # else:
  512. # baseValFields = baseValType.fields()
  513. # for c in self.handleFields(baseVal, baseRti, baseValFields):
  514. # yield c
  515. # else:
  516. # if field.type.code == gdb.TYPE_CODE_UNION:
  517. # # if not rtiSons:
  518. # rtiNode = rti['node'].referenced_value()
  519. # rtiSons = rtiNode['sons']
  520. # if not rtiSons and int(rtiNode['len']) == 0 and str(rtiNode['name']) != "0x0":
  521. # rtiSons = [rti['node']] # sons are dereferenced by the consumer
  522. # if not rtiSons:
  523. # printErrorOnce(self.valTypeName, f"NimObjectPrinter: UNION field can't be displayed without RTI {self.valTypeName}, using fallback.\n")
  524. # # yield (field.name, self.baseVal[field]) # XXX: this fallback seems wrong
  525. # return # XXX: this should probably continue instead?
  526. # if int(rtiNode['len']) != 0 and str(rtiNode['name']) != "0x0":
  527. # gdb.write(f"wtf IT HAPPENED {self.valTypeName}\n", gdb.STDERR)
  528. # discNode = rtiSons[discField[0]].referenced_value()
  529. # if not discNode:
  530. # raise ValueError("Can't find union discriminant field in object RTI")
  531. # discNodeLen = int(discNode['len'])
  532. # discFieldVal = int(currVal[discField[1].name])
  533. # unionNodeRef = None
  534. # if discFieldVal < discNodeLen:
  535. # unionNodeRef = discNode['sons'][discFieldVal]
  536. # if not unionNodeRef:
  537. # unionNodeRef = discNode['sons'][discNodeLen]
  538. # if not unionNodeRef:
  539. # printErrorOnce(self.valTypeName + "no union node", f"wtf is up with sons {self.valTypeName} {unionNodeRef} {rtiNode['offset']} {discNode} {discFieldVal} {discNodeLen} {discField[1].name} {field.name} {field.type}\n")
  540. # continue
  541. # unionNode = unionNodeRef.referenced_value()
  542. # fieldName = "" if field.name == None else field.name.lower()
  543. # unionNodeName = "" if not unionNode['name'] else unionNode['name'].string("utf-8", "ignore")
  544. # if not unionNodeName or unionNodeName.lower() != fieldName:
  545. # unionFieldName = f"_{discField[1].name.lower()}_{int(rti['node'].referenced_value()['len'])}"
  546. # gdb.write(f"wtf i: {i} union: {unionFieldName} field: {fieldName} type: {field.type.name} tag: {field.type.tag}\n", gdb.STDERR)
  547. # else:
  548. # unionFieldName = unionNodeName
  549. # if discNodeLen == 0:
  550. # yield (unionFieldName, currVal[unionFieldName])
  551. # else:
  552. # unionNodeLen = int(unionNode['len'])
  553. # if unionNodeLen > 0:
  554. # for u in range(unionNodeLen):
  555. # un = unionNode['sons'][u].referenced_value()['name'].string("utf-8", "ignore")
  556. # yield (un, currVal[unionFieldName][un])
  557. # else:
  558. # yield(unionNodeName, currVal[unionFieldName])
  559. # else:
  560. # discIndex = i - 1 if seenSup else i
  561. # discField = (discIndex, field) # discriminant field is the last normal field
  562. # yield (field.name, currVal[field.name])
  563. # except GeneratorExit:
  564. # raise
  565. # except:
  566. # gdb.write(f"wtf {self.valTypeName} {i} fn: {field.name} df: {discField} rti: {rti} rtiNode: {rti['node'].referenced_value()} rtiSons: {rtiSons} {sys.exc_info()} {traceback.format_tb(sys.exc_info()[2], limit = 10)}\n", gdb.STDERR)
  567. # gdb.write(f"wtf {self.valTypeName} {i} {field.name}\n", gdb.STDERR)
  568. # # seenSup = False
  569. # # for (i, field) in enumerate(fields):
  570. # # # if field.name:
  571. # # # val = currVal[field.name]
  572. # # # else:
  573. # # # val = None
  574. # # rtiNode = rti['node'].referenced_value()
  575. # # rtiLen = int(rtiNode['len'])
  576. # # if int(rtiNode['len']) > 0:
  577. # # sons = rtiNode['sons']
  578. # # elif int(rti['len']) == 0 and str(rti['name']) != "0x0":
  579. # # sons = [rti['node']] # sons are dereferenced by the consumer
  580. # # sonsIdx = i - 1 if seenSup else i
  581. # # s = sons[sonsIdx].referenced_value()
  582. # # addr = int(currVal.address)
  583. # # off = addr + int(rtiNode['offset'])
  584. # # seenSup = seenSup or field.name == "Sup"
  585. # # gdb.write(f"wtf: i: {i} sonsIdx: {sonsIdx} field: {field.name} rtiLen: {rtiLen} rti: {rti} rtiNode: {rtiNode} isUnion: {field.type.code == gdb.TYPE_CODE_UNION} s: {s}\n", gdb.STDERR)
  586. # raise
  587. ################################################################################
  588. class NimFrameFilter:
  589. def __init__(self):
  590. self.name = "nim-frame-filter"
  591. self.enabled = True
  592. self.priority = 100
  593. self.hidden = {"NimMainInner","NimMain", "main"}
  594. def filter(self, iterator):
  595. for framedecorator in iterator:
  596. if framedecorator.function() not in self.hidden:
  597. yield framedecorator
  598. ################################################################################
  599. def makematcher(klass):
  600. def matcher(val):
  601. typeName = str(val.type)
  602. try:
  603. if hasattr(klass, 'pattern') and hasattr(klass, '__name__'):
  604. # print(typeName + " <> " + klass.__name__)
  605. if klass.pattern.match(typeName):
  606. return klass(val)
  607. except Exception as e:
  608. print(klass)
  609. printErrorOnce(typeName, "No matcher for type '" + typeName + "': " + str(e) + "\n")
  610. return matcher
  611. def register_nim_pretty_printers_for_object(objfile):
  612. nimMainSym = gdb.lookup_global_symbol("NimMain", gdb.SYMBOL_FUNCTIONS_DOMAIN)
  613. if nimMainSym and nimMainSym.symtab.objfile == objfile:
  614. print("set Nim pretty printers for ", objfile.filename)
  615. gdb.types.register_type_printer(objfile, NimTypePrinter())
  616. objfile.pretty_printers = [makematcher(var) for var in list(globals().values()) if hasattr(var, 'pattern')]
  617. # Register pretty printers for all objfiles that are already loaded.
  618. for old_objfile in gdb.objfiles():
  619. register_nim_pretty_printers_for_object(old_objfile)
  620. # Register an event handler to register nim pretty printers for all future objfiles.
  621. def new_object_handler(event):
  622. register_nim_pretty_printers_for_object(event.new_objfile)
  623. gdb.events.new_objfile.connect(new_object_handler)
  624. gdb.frame_filters = {"nim-frame-filter": NimFrameFilter()}