blob: 24961d1159bf95db201ccd79bbd97a2866d54a2b [file] [log] [blame]
Brian Silverman72890c22015-09-19 14:37:37 -04001# -*- 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
11# Pretty printers for Eigen::Matrix
12# This is still pretty basic as the python extension to gdb is still pretty basic.
Austin Schuhc55b0172022-02-20 17:52:35 -080013# It cannot handle complex eigen types and it doesn't support many of the other eigen types
Brian Silverman72890c22015-09-19 14:37:37 -040014# This code supports fixed size as well as dynamic size matrices
15
16# To use it:
17#
18# * Create a directory and put the file as well as an empty __init__.py in
19# that directory.
20# * Create a ~/.gdbinit file, that contains the following:
21# python
22# import sys
23# sys.path.insert(0, '/path/to/eigen/printer/directory')
24# from printers import register_eigen_printers
25# register_eigen_printers (None)
26# end
27
28import gdb
29import re
30import itertools
Austin Schuhc55b0172022-02-20 17:52:35 -080031from bisect import bisect_left
Brian Silverman72890c22015-09-19 14:37:37 -040032
Austin Schuhc55b0172022-02-20 17:52:35 -080033# Basic row/column iteration code for use with Sparse and Dense matrices
34class _MatrixEntryIterator(object):
35
36 def __init__ (self, rows, cols, rowMajor):
37 self.rows = rows
38 self.cols = cols
39 self.currentRow = 0
40 self.currentCol = 0
41 self.rowMajor = rowMajor
42
43 def __iter__ (self):
44 return self
45
46 def next(self):
47 return self.__next__() # Python 2.x compatibility
48
49 def __next__(self):
50 row = self.currentRow
51 col = self.currentCol
52 if self.rowMajor == 0:
53 if self.currentCol >= self.cols:
54 raise StopIteration
55
56 self.currentRow = self.currentRow + 1
57 if self.currentRow >= self.rows:
58 self.currentRow = 0
59 self.currentCol = self.currentCol + 1
60 else:
61 if self.currentRow >= self.rows:
62 raise StopIteration
63
64 self.currentCol = self.currentCol + 1
65 if self.currentCol >= self.cols:
66 self.currentCol = 0
67 self.currentRow = self.currentRow + 1
68
69 return (row, col)
Brian Silverman72890c22015-09-19 14:37:37 -040070
71class EigenMatrixPrinter:
72 "Print Eigen Matrix or Array of some kind"
73
74 def __init__(self, variety, val):
75 "Extract all the necessary information"
76
77 # Save the variety (presumably "Matrix" or "Array") for later usage
78 self.variety = variety
79
80 # The gdb extension does not support value template arguments - need to extract them by hand
81 type = val.type
82 if type.code == gdb.TYPE_CODE_REF:
83 type = type.target()
84 self.type = type.unqualified().strip_typedefs()
85 tag = self.type.tag
86 regex = re.compile('\<.*\>')
87 m = regex.findall(tag)[0][1:-1]
88 template_params = m.split(',')
Austin Schuh189376f2018-12-20 22:11:15 +110089 template_params = [x.replace(" ", "") for x in template_params]
Brian Silverman72890c22015-09-19 14:37:37 -040090
91 if template_params[1] == '-0x00000000000000001' or template_params[1] == '-0x000000001' or template_params[1] == '-1':
92 self.rows = val['m_storage']['m_rows']
93 else:
94 self.rows = int(template_params[1])
95
96 if template_params[2] == '-0x00000000000000001' or template_params[2] == '-0x000000001' or template_params[2] == '-1':
97 self.cols = val['m_storage']['m_cols']
98 else:
99 self.cols = int(template_params[2])
100
101 self.options = 0 # default value
102 if len(template_params) > 3:
103 self.options = template_params[3];
104
105 self.rowMajor = (int(self.options) & 0x1)
106
107 self.innerType = self.type.template_argument(0)
108
109 self.val = val
110
111 # Fixed size matrices have a struct as their storage, so we need to walk through this
112 self.data = self.val['m_storage']['m_data']
113 if self.data.type.code == gdb.TYPE_CODE_STRUCT:
114 self.data = self.data['array']
115 self.data = self.data.cast(self.innerType.pointer())
116
Austin Schuhc55b0172022-02-20 17:52:35 -0800117 class _iterator(_MatrixEntryIterator):
Brian Silverman72890c22015-09-19 14:37:37 -0400118 def __init__ (self, rows, cols, dataPtr, rowMajor):
Austin Schuhc55b0172022-02-20 17:52:35 -0800119 super(EigenMatrixPrinter._iterator, self).__init__(rows, cols, rowMajor)
Austin Schuh189376f2018-12-20 22:11:15 +1100120
Austin Schuhc55b0172022-02-20 17:52:35 -0800121 self.dataPtr = dataPtr
Austin Schuh189376f2018-12-20 22:11:15 +1100122
123 def __next__(self):
Brian Silverman72890c22015-09-19 14:37:37 -0400124
Austin Schuhc55b0172022-02-20 17:52:35 -0800125 row, col = super(EigenMatrixPrinter._iterator, self).__next__()
Brian Silverman72890c22015-09-19 14:37:37 -0400126
127 item = self.dataPtr.dereference()
128 self.dataPtr = self.dataPtr + 1
129 if (self.cols == 1): #if it's a column vector
130 return ('[%d]' % (row,), item)
131 elif (self.rows == 1): #if it's a row vector
132 return ('[%d]' % (col,), item)
133 return ('[%d,%d]' % (row, col), item)
134
135 def children(self):
136
137 return self._iterator(self.rows, self.cols, self.data, self.rowMajor)
138
139 def to_string(self):
140 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)
141
Austin Schuhc55b0172022-02-20 17:52:35 -0800142class EigenSparseMatrixPrinter:
143 "Print an Eigen SparseMatrix"
144
145 def __init__(self, val):
146 "Extract all the necessary information"
147
148 type = val.type
149 if type.code == gdb.TYPE_CODE_REF:
150 type = type.target()
151 self.type = type.unqualified().strip_typedefs()
152 tag = self.type.tag
153 regex = re.compile('\<.*\>')
154 m = regex.findall(tag)[0][1:-1]
155 template_params = m.split(',')
156 template_params = [x.replace(" ", "") for x in template_params]
157
158 self.options = 0
159 if len(template_params) > 1:
160 self.options = template_params[1];
161
162 self.rowMajor = (int(self.options) & 0x1)
163
164 self.innerType = self.type.template_argument(0)
165
166 self.val = val
167
168 self.data = self.val['m_data']
169 self.data = self.data.cast(self.innerType.pointer())
170
171 class _iterator(_MatrixEntryIterator):
172 def __init__ (self, rows, cols, val, rowMajor):
173 super(EigenSparseMatrixPrinter._iterator, self).__init__(rows, cols, rowMajor)
174
175 self.val = val
176
177 def __next__(self):
178
179 row, col = super(EigenSparseMatrixPrinter._iterator, self).__next__()
180
181 # repeat calculations from SparseMatrix.h:
182 outer = row if self.rowMajor else col
183 inner = col if self.rowMajor else row
184 start = self.val['m_outerIndex'][outer]
185 end = ((start + self.val['m_innerNonZeros'][outer]) if self.val['m_innerNonZeros'] else
186 self.val['m_outerIndex'][outer+1])
187
188 # and from CompressedStorage.h:
189 data = self.val['m_data']
190 if start >= end:
191 item = 0
192 elif (end > start) and (inner == data['m_indices'][end-1]):
193 item = data['m_values'][end-1]
194 else:
195 # create Python index list from the target range within m_indices
196 indices = [data['m_indices'][x] for x in range(int(start), int(end)-1)]
197 # find the index with binary search
198 idx = int(start) + bisect_left(indices, inner)
199 if ((idx < end) and (data['m_indices'][idx] == inner)):
200 item = data['m_values'][idx]
201 else:
202 item = 0
203
204 return ('[%d,%d]' % (row, col), item)
205
206 def children(self):
207 if self.data:
208 return self._iterator(self.rows(), self.cols(), self.val, self.rowMajor)
209
210 return iter([]) # empty matrix, for now
211
212
213 def rows(self):
214 return self.val['m_outerSize'] if self.rowMajor else self.val['m_innerSize']
215
216 def cols(self):
217 return self.val['m_innerSize'] if self.rowMajor else self.val['m_outerSize']
218
219 def to_string(self):
220
221 if self.data:
222 status = ("not compressed" if self.val['m_innerNonZeros'] else "compressed")
223 else:
224 status = "empty"
225 dimensions = "%d x %d" % (self.rows(), self.cols())
226 layout = "row" if self.rowMajor else "column"
227
228 return "Eigen::SparseMatrix<%s>, %s, %s major, %s" % (
229 self.innerType, dimensions, layout, status )
230
Brian Silverman72890c22015-09-19 14:37:37 -0400231class EigenQuaternionPrinter:
232 "Print an Eigen Quaternion"
233
234 def __init__(self, val):
235 "Extract all the necessary information"
236 # The gdb extension does not support value template arguments - need to extract them by hand
237 type = val.type
238 if type.code == gdb.TYPE_CODE_REF:
239 type = type.target()
240 self.type = type.unqualified().strip_typedefs()
241 self.innerType = self.type.template_argument(0)
242 self.val = val
243
244 # Quaternions have a struct as their storage, so we need to walk through this
245 self.data = self.val['m_coeffs']['m_storage']['m_data']['array']
246 self.data = self.data.cast(self.innerType.pointer())
247
248 class _iterator:
249 def __init__ (self, dataPtr):
250 self.dataPtr = dataPtr
251 self.currentElement = 0
252 self.elementNames = ['x', 'y', 'z', 'w']
253
254 def __iter__ (self):
255 return self
Austin Schuh189376f2018-12-20 22:11:15 +1100256
Brian Silverman72890c22015-09-19 14:37:37 -0400257 def next(self):
Austin Schuhc55b0172022-02-20 17:52:35 -0800258 return self.__next__() # Python 2.x compatibility
Austin Schuh189376f2018-12-20 22:11:15 +1100259
260 def __next__(self):
Brian Silverman72890c22015-09-19 14:37:37 -0400261 element = self.currentElement
262
263 if self.currentElement >= 4: #there are 4 elements in a quanternion
264 raise StopIteration
265
266 self.currentElement = self.currentElement + 1
267
268 item = self.dataPtr.dereference()
269 self.dataPtr = self.dataPtr + 1
270 return ('[%s]' % (self.elementNames[element],), item)
271
272 def children(self):
273
274 return self._iterator(self.data)
275
276 def to_string(self):
277 return "Eigen::Quaternion<%s> (data ptr: %s)" % (self.innerType, self.data)
278
279def build_eigen_dictionary ():
280 pretty_printers_dict[re.compile('^Eigen::Quaternion<.*>$')] = lambda val: EigenQuaternionPrinter(val)
281 pretty_printers_dict[re.compile('^Eigen::Matrix<.*>$')] = lambda val: EigenMatrixPrinter("Matrix", val)
Austin Schuhc55b0172022-02-20 17:52:35 -0800282 pretty_printers_dict[re.compile('^Eigen::SparseMatrix<.*>$')] = lambda val: EigenSparseMatrixPrinter(val)
Brian Silverman72890c22015-09-19 14:37:37 -0400283 pretty_printers_dict[re.compile('^Eigen::Array<.*>$')] = lambda val: EigenMatrixPrinter("Array", val)
284
285def register_eigen_printers(obj):
286 "Register eigen pretty-printers with objfile Obj"
287
288 if obj == None:
289 obj = gdb
290 obj.pretty_printers.append(lookup_function)
291
292def lookup_function(val):
293 "Look-up and return a pretty-printer that can print va."
294
295 type = val.type
296
297 if type.code == gdb.TYPE_CODE_REF:
298 type = type.target()
299
300 type = type.unqualified().strip_typedefs()
301
302 typename = type.tag
303 if typename == None:
304 return None
305
306 for function in pretty_printers_dict:
307 if function.search(typename):
308 return pretty_printers_dict[function](val)
309
310 return None
311
312pretty_printers_dict = {}
313
314build_eigen_dictionary ()