printers.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. # -*- coding: utf-8 -*-
  2. # This file is part of Eigen, a lightweight C++ template library
  3. # for linear algebra.
  4. #
  5. # Copyright (C) 2009 Benjamin Schindler <bschindler@inf.ethz.ch>
  6. #
  7. # This Source Code Form is subject to the terms of the Mozilla Public
  8. # License, v. 2.0. If a copy of the MPL was not distributed with this
  9. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  10. # Pretty printers for Eigen::Matrix
  11. # This is still pretty basic as the python extension to gdb is still pretty basic.
  12. # It cannot handle complex eigen types and it doesn't support many of the other eigen types
  13. # This code supports fixed size as well as dynamic size matrices
  14. # To use it:
  15. #
  16. # * Create a directory and put the file as well as an empty __init__.py in
  17. # that directory.
  18. # * Create a ~/.gdbinit file, that contains the following:
  19. # python
  20. # import sys
  21. # sys.path.insert(0, '/path/to/eigen/printer/directory')
  22. # from printers import register_eigen_printers
  23. # register_eigen_printers (None)
  24. # end
  25. import gdb
  26. import re
  27. import itertools
  28. from bisect import bisect_left
  29. # Basic row/column iteration code for use with Sparse and Dense matrices
  30. class _MatrixEntryIterator(object):
  31. def __init__ (self, rows, cols, rowMajor):
  32. self.rows = rows
  33. self.cols = cols
  34. self.currentRow = 0
  35. self.currentCol = 0
  36. self.rowMajor = rowMajor
  37. def __iter__ (self):
  38. return self
  39. def next(self):
  40. return self.__next__() # Python 2.x compatibility
  41. def __next__(self):
  42. row = self.currentRow
  43. col = self.currentCol
  44. if self.rowMajor == 0:
  45. if self.currentCol >= self.cols:
  46. raise StopIteration
  47. self.currentRow = self.currentRow + 1
  48. if self.currentRow >= self.rows:
  49. self.currentRow = 0
  50. self.currentCol = self.currentCol + 1
  51. else:
  52. if self.currentRow >= self.rows:
  53. raise StopIteration
  54. self.currentCol = self.currentCol + 1
  55. if self.currentCol >= self.cols:
  56. self.currentCol = 0
  57. self.currentRow = self.currentRow + 1
  58. return (row, col)
  59. class EigenMatrixPrinter:
  60. "Print Eigen Matrix or Array of some kind"
  61. def __init__(self, variety, val):
  62. "Extract all the necessary information"
  63. # Save the variety (presumably "Matrix" or "Array") for later usage
  64. self.variety = variety
  65. # The gdb extension does not support value template arguments - need to extract them by hand
  66. type = val.type
  67. if type.code == gdb.TYPE_CODE_REF:
  68. type = type.target()
  69. self.type = type.unqualified().strip_typedefs()
  70. tag = self.type.tag
  71. regex = re.compile('\<.*\>')
  72. m = regex.findall(tag)[0][1:-1]
  73. template_params = m.split(',')
  74. template_params = [x.replace(" ", "") for x in template_params]
  75. if template_params[1] == '-0x00000000000000001' or template_params[1] == '-0x000000001' or template_params[1] == '-1':
  76. self.rows = val['m_storage']['m_rows']
  77. else:
  78. self.rows = int(template_params[1])
  79. if template_params[2] == '-0x00000000000000001' or template_params[2] == '-0x000000001' or template_params[2] == '-1':
  80. self.cols = val['m_storage']['m_cols']
  81. else:
  82. self.cols = int(template_params[2])
  83. self.options = 0 # default value
  84. if len(template_params) > 3:
  85. self.options = template_params[3];
  86. self.rowMajor = (int(self.options) & 0x1)
  87. self.innerType = self.type.template_argument(0)
  88. self.val = val
  89. # Fixed size matrices have a struct as their storage, so we need to walk through this
  90. self.data = self.val['m_storage']['m_data']
  91. if self.data.type.code == gdb.TYPE_CODE_STRUCT:
  92. self.data = self.data['array']
  93. self.data = self.data.cast(self.innerType.pointer())
  94. class _iterator(_MatrixEntryIterator):
  95. def __init__ (self, rows, cols, dataPtr, rowMajor):
  96. super(EigenMatrixPrinter._iterator, self).__init__(rows, cols, rowMajor)
  97. self.dataPtr = dataPtr
  98. def __next__(self):
  99. row, col = super(EigenMatrixPrinter._iterator, self).__next__()
  100. item = self.dataPtr.dereference()
  101. self.dataPtr = self.dataPtr + 1
  102. if (self.cols == 1): #if it's a column vector
  103. return ('[%d]' % (row,), item)
  104. elif (self.rows == 1): #if it's a row vector
  105. return ('[%d]' % (col,), item)
  106. return ('[%d,%d]' % (row, col), item)
  107. def children(self):
  108. return self._iterator(self.rows, self.cols, self.data, self.rowMajor)
  109. def to_string(self):
  110. return "Eigen::%s<%s,%d,%d,%s> (data ptr: %s)" % (self.variety, self.innerType, self.rows, self.cols, "RowMajor" if self.rowMajor else "ColMajor", self.data)
  111. class EigenSparseMatrixPrinter:
  112. "Print an Eigen SparseMatrix"
  113. def __init__(self, val):
  114. "Extract all the necessary information"
  115. type = val.type
  116. if type.code == gdb.TYPE_CODE_REF:
  117. type = type.target()
  118. self.type = type.unqualified().strip_typedefs()
  119. tag = self.type.tag
  120. regex = re.compile('\<.*\>')
  121. m = regex.findall(tag)[0][1:-1]
  122. template_params = m.split(',')
  123. template_params = [x.replace(" ", "") for x in template_params]
  124. self.options = 0
  125. if len(template_params) > 1:
  126. self.options = template_params[1];
  127. self.rowMajor = (int(self.options) & 0x1)
  128. self.innerType = self.type.template_argument(0)
  129. self.val = val
  130. self.data = self.val['m_data']
  131. self.data = self.data.cast(self.innerType.pointer())
  132. class _iterator(_MatrixEntryIterator):
  133. def __init__ (self, rows, cols, val, rowMajor):
  134. super(EigenSparseMatrixPrinter._iterator, self).__init__(rows, cols, rowMajor)
  135. self.val = val
  136. def __next__(self):
  137. row, col = super(EigenSparseMatrixPrinter._iterator, self).__next__()
  138. # repeat calculations from SparseMatrix.h:
  139. outer = row if self.rowMajor else col
  140. inner = col if self.rowMajor else row
  141. start = self.val['m_outerIndex'][outer]
  142. end = ((start + self.val['m_innerNonZeros'][outer]) if self.val['m_innerNonZeros'] else
  143. self.val['m_outerIndex'][outer+1])
  144. # and from CompressedStorage.h:
  145. data = self.val['m_data']
  146. if start >= end:
  147. item = 0
  148. elif (end > start) and (inner == data['m_indices'][end-1]):
  149. item = data['m_values'][end-1]
  150. else:
  151. # create Python index list from the target range within m_indices
  152. indices = [data['m_indices'][x] for x in range(int(start), int(end)-1)]
  153. # find the index with binary search
  154. idx = int(start) + bisect_left(indices, inner)
  155. if ((idx < end) and (data['m_indices'][idx] == inner)):
  156. item = data['m_values'][idx]
  157. else:
  158. item = 0
  159. return ('[%d,%d]' % (row, col), item)
  160. def children(self):
  161. if self.data:
  162. return self._iterator(self.rows(), self.cols(), self.val, self.rowMajor)
  163. return iter([]) # empty matrix, for now
  164. def rows(self):
  165. return self.val['m_outerSize'] if self.rowMajor else self.val['m_innerSize']
  166. def cols(self):
  167. return self.val['m_innerSize'] if self.rowMajor else self.val['m_outerSize']
  168. def to_string(self):
  169. if self.data:
  170. status = ("not compressed" if self.val['m_innerNonZeros'] else "compressed")
  171. else:
  172. status = "empty"
  173. dimensions = "%d x %d" % (self.rows(), self.cols())
  174. layout = "row" if self.rowMajor else "column"
  175. return "Eigen::SparseMatrix<%s>, %s, %s major, %s" % (
  176. self.innerType, dimensions, layout, status )
  177. class EigenQuaternionPrinter:
  178. "Print an Eigen Quaternion"
  179. def __init__(self, val):
  180. "Extract all the necessary information"
  181. # The gdb extension does not support value template arguments - need to extract them by hand
  182. type = val.type
  183. if type.code == gdb.TYPE_CODE_REF:
  184. type = type.target()
  185. self.type = type.unqualified().strip_typedefs()
  186. self.innerType = self.type.template_argument(0)
  187. self.val = val
  188. # Quaternions have a struct as their storage, so we need to walk through this
  189. self.data = self.val['m_coeffs']['m_storage']['m_data']['array']
  190. self.data = self.data.cast(self.innerType.pointer())
  191. class _iterator:
  192. def __init__ (self, dataPtr):
  193. self.dataPtr = dataPtr
  194. self.currentElement = 0
  195. self.elementNames = ['x', 'y', 'z', 'w']
  196. def __iter__ (self):
  197. return self
  198. def next(self):
  199. return self.__next__() # Python 2.x compatibility
  200. def __next__(self):
  201. element = self.currentElement
  202. if self.currentElement >= 4: #there are 4 elements in a quanternion
  203. raise StopIteration
  204. self.currentElement = self.currentElement + 1
  205. item = self.dataPtr.dereference()
  206. self.dataPtr = self.dataPtr + 1
  207. return ('[%s]' % (self.elementNames[element],), item)
  208. def children(self):
  209. return self._iterator(self.data)
  210. def to_string(self):
  211. return "Eigen::Quaternion<%s> (data ptr: %s)" % (self.innerType, self.data)
  212. def build_eigen_dictionary ():
  213. pretty_printers_dict[re.compile('^Eigen::Quaternion<.*>$')] = lambda val: EigenQuaternionPrinter(val)
  214. pretty_printers_dict[re.compile('^Eigen::Matrix<.*>$')] = lambda val: EigenMatrixPrinter("Matrix", val)
  215. pretty_printers_dict[re.compile('^Eigen::SparseMatrix<.*>$')] = lambda val: EigenSparseMatrixPrinter(val)
  216. pretty_printers_dict[re.compile('^Eigen::Array<.*>$')] = lambda val: EigenMatrixPrinter("Array", val)
  217. def register_eigen_printers(obj):
  218. "Register eigen pretty-printers with objfile Obj"
  219. if obj == None:
  220. obj = gdb
  221. obj.pretty_printers.append(lookup_function)
  222. def lookup_function(val):
  223. "Look-up and return a pretty-printer that can print va."
  224. type = val.type
  225. if type.code == gdb.TYPE_CODE_REF:
  226. type = type.target()
  227. type = type.unqualified().strip_typedefs()
  228. typename = type.tag
  229. if typename == None:
  230. return None
  231. for function in pretty_printers_dict:
  232. if function.search(typename):
  233. return pretty_printers_dict[function](val)
  234. return None
  235. pretty_printers_dict = {}
  236. build_eigen_dictionary ()