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 <[email protected]> 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. 13# It cannot handle complex eigen types and it doesn't support many of the other eigen types 14# 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 31from bisect import bisect_left 32 33# 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) 70 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(',') 89 template_params = [x.replace(" ", "") for x in template_params] 90 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 117 class _iterator(_MatrixEntryIterator): 118 def __init__ (self, rows, cols, dataPtr, rowMajor): 119 super(EigenMatrixPrinter._iterator, self).__init__(rows, cols, rowMajor) 120 121 self.dataPtr = dataPtr 122 123 def __next__(self): 124 125 row, col = super(EigenMatrixPrinter._iterator, self).__next__() 126 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 142class 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 231class 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 256 257 def next(self): 258 return self.__next__() # Python 2.x compatibility 259 260 def __next__(self): 261 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) 282 pretty_printers_dict[re.compile('^Eigen::SparseMatrix<.*>$')] = lambda val: EigenSparseMatrixPrinter(val) 283 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 () 315