-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
353 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,338 @@ | ||
# -*- coding: utf-8 -*- | ||
# This file is part of Eigen, a lightweight C++ template library | ||
# for linear algebra. | ||
# | ||
# Copyright (C) 2009 Benjamin Schindler <[email protected]> | ||
# | ||
# This Source Code Form is subject to the terms of the Mozilla Public | ||
# License, v. 2.0. If a copy of the MPL was not distributed with this | ||
# file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||
|
||
# Pretty printers for Eigen::Matrix | ||
# This is still pretty basic as the python extension to gdb is still pretty basic. | ||
# It cannot handle complex eigen types and it doesn't support many of the other eigen types | ||
# This code supports fixed size as well as dynamic size matrices | ||
|
||
# To use it: | ||
# | ||
# * Create a directory and put the file as well as an empty __init__.py in | ||
# that directory. | ||
# * Create a ~/.gdbinit file, that contains the following: | ||
# python | ||
# import sys | ||
# sys.path.insert(0, '/path/to/eigen/printer/directory') | ||
# from printers import register_eigen_printers | ||
# register_eigen_printers(None) | ||
# end | ||
|
||
import gdb | ||
import re | ||
from bisect import bisect_left | ||
|
||
|
||
# Basic row/column iteration code for use with Sparse and Dense matrices | ||
class _MatrixEntryIterator(object): | ||
|
||
def __init__(self, rows, cols, row_major): | ||
self.rows = rows | ||
self.cols = cols | ||
self.currentRow = 0 | ||
self.currentCol = 0 | ||
self.rowMajor = row_major | ||
|
||
def __iter__(self): | ||
return self | ||
|
||
def next(self): | ||
return self.__next__() # Python 2.x compatibility | ||
|
||
def __next__(self): | ||
row = self.currentRow | ||
col = self.currentCol | ||
if self.rowMajor == 0: | ||
if self.currentCol >= self.cols: | ||
raise StopIteration | ||
|
||
self.currentRow += 1 | ||
if self.currentRow >= self.rows: | ||
self.currentRow = 0 | ||
self.currentCol += 1 | ||
else: | ||
if self.currentRow >= self.rows: | ||
raise StopIteration | ||
|
||
self.currentCol += 1 | ||
if self.currentCol >= self.cols: | ||
self.currentCol = 0 | ||
self.currentRow += 1 | ||
|
||
return row, col | ||
|
||
|
||
class EigenMatrixPrinter: | ||
"""Print Eigen Matrix or Array of some kind""" | ||
|
||
def __init__(self, variety, val): | ||
"""Extract all the necessary information""" | ||
|
||
# Save the variety (presumably "Matrix" or "Array") for later usage | ||
self.variety = variety | ||
|
||
# The gdb extension does not support value template arguments - need to extract them by hand | ||
typeinfo = val.type | ||
if typeinfo.code == gdb.TYPE_CODE_REF: | ||
typeinfo = typeinfo.target() | ||
self.type = typeinfo.unqualified().strip_typedefs() | ||
tag = self.type.tag | ||
regex = re.compile('<.*>') | ||
m = regex.findall(tag)[0][1:-1] | ||
template_params = m.split(',') | ||
template_params = [x.replace(" ", "") for x in template_params] | ||
|
||
if template_params[1] in ['-0x00000000000000001', '-0x000000001', '-1']: | ||
self.rows = val['m_storage']['m_rows'] | ||
else: | ||
self.rows = int(template_params[1]) | ||
|
||
if template_params[2] in ['-0x00000000000000001', '-0x000000001', '-1']: | ||
self.cols = val['m_storage']['m_cols'] | ||
else: | ||
self.cols = int(template_params[2]) | ||
|
||
self.options = 0 # default value | ||
if len(template_params) > 3: | ||
self.options = template_params[3] | ||
|
||
self.rowMajor = (int(self.options) & 0x1) | ||
|
||
self.innerType = self.type.template_argument(0) | ||
|
||
self.val = val | ||
|
||
# Fixed size matrices have a struct as their storage, so we need to walk through this | ||
self.data = self.val['m_storage']['m_data'] | ||
if self.data.type.code == gdb.TYPE_CODE_STRUCT: | ||
self.data = self.data['array'] | ||
self.data = self.data.cast(self.innerType.pointer()) | ||
|
||
class _Iterator(_MatrixEntryIterator): | ||
def __init__(self, rows, cols, data_ptr, row_major): | ||
super(EigenMatrixPrinter._Iterator, self).__init__(rows, cols, row_major) | ||
|
||
self.dataPtr = data_ptr | ||
|
||
def __next__(self): | ||
row, col = super(EigenMatrixPrinter._Iterator, self).__next__() | ||
|
||
item = self.dataPtr.dereference() | ||
self.dataPtr += 1 | ||
if self.cols == 1: # if it's a column vector | ||
return '[%d]' % (row,), item | ||
elif self.rows == 1: # if it's a row vector | ||
return '[%d]' % (col,), item | ||
return '[%d,%d]' % (row, col), item | ||
|
||
def children(self): | ||
return self._Iterator(self.rows, self.cols, self.data, self.rowMajor) | ||
|
||
def to_string(self): | ||
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) | ||
|
||
|
||
class EigenSparseMatrixPrinter: | ||
"""Print an Eigen SparseMatrix""" | ||
|
||
def __init__(self, val): | ||
"""Extract all the necessary information""" | ||
|
||
typeinfo = val.type | ||
if typeinfo.code == gdb.TYPE_CODE_REF: | ||
typeinfo = typeinfo.target() | ||
self.type = typeinfo.unqualified().strip_typedefs() | ||
tag = self.type.tag | ||
regex = re.compile('<.*>') | ||
m = regex.findall(tag)[0][1:-1] | ||
template_params = m.split(',') | ||
template_params = [x.replace(" ", "") for x in template_params] | ||
|
||
self.options = 0 | ||
if len(template_params) > 1: | ||
self.options = template_params[1] | ||
|
||
self.rowMajor = (int(self.options) & 0x1) | ||
|
||
self.innerType = self.type.template_argument(0) | ||
|
||
self.val = val | ||
|
||
self.data = self.val['m_data'] | ||
self.data = self.data.cast(self.innerType.pointer()) | ||
|
||
class _Iterator(_MatrixEntryIterator): | ||
def __init__(self, rows, cols, val, row_major): | ||
super(EigenSparseMatrixPrinter._Iterator, self).__init__(rows, cols, row_major) | ||
|
||
self.val = val | ||
|
||
def __next__(self): | ||
row, col = super(EigenSparseMatrixPrinter._Iterator, self).__next__() | ||
|
||
# repeat calculations from SparseMatrix.h: | ||
outer = row if self.rowMajor else col | ||
inner = col if self.rowMajor else row | ||
start = self.val['m_outerIndex'][outer] | ||
end = ( | ||
(start + self.val['m_innerNonZeros'][outer]) | ||
if self.val['m_innerNonZeros'] else self.val['m_outerIndex'][outer+1] | ||
) | ||
|
||
# and from CompressedStorage.h: | ||
data = self.val['m_data'] | ||
if start >= end: | ||
item = 0 | ||
elif (end > start) and (inner == data['m_indices'][end-1]): | ||
item = data['m_values'][end-1] | ||
else: | ||
# create Python index list from the target range within m_indices | ||
indices = [data['m_indices'][x] for x in range(int(start), int(end)-1)] | ||
# find the index with binary search | ||
idx = int(start) + bisect_left(indices, inner) | ||
if idx < end and data['m_indices'][idx] == inner: | ||
item = data['m_values'][idx] | ||
else: | ||
item = 0 | ||
|
||
return '[%d,%d]' % (row, col), item | ||
|
||
def children(self): | ||
if self.data: | ||
return self._Iterator(self.rows(), self.cols(), self.val, self.rowMajor) | ||
|
||
return iter([]) # empty matrix, for now | ||
|
||
def rows(self): | ||
return self.val['m_outerSize'] if self.rowMajor else self.val['m_innerSize'] | ||
|
||
def cols(self): | ||
return self.val['m_innerSize'] if self.rowMajor else self.val['m_outerSize'] | ||
|
||
def to_string(self): | ||
|
||
if self.data: | ||
status = ("not compressed" if self.val['m_innerNonZeros'] else "compressed") | ||
else: | ||
status = "empty" | ||
dimensions = "%d x %d" % (self.rows(), self.cols()) | ||
layout = "row" if self.rowMajor else "column" | ||
|
||
return "Eigen::SparseMatrix<%s>, %s, %s major, %s" % ( | ||
self.innerType, dimensions, layout, status) | ||
|
||
|
||
class EigenQuaternionPrinter: | ||
"""Print an Eigen Quaternion""" | ||
|
||
def __init__(self, val): | ||
"""Extract all the necessary information""" | ||
# The gdb extension does not support value template arguments - need to extract them by hand | ||
typeinfo = val.type | ||
if typeinfo.code == gdb.TYPE_CODE_REF: | ||
typeinfo = typeinfo.target() | ||
self.type = typeinfo.unqualified().strip_typedefs() | ||
self.innerType = self.type.template_argument(0) | ||
self.val = val | ||
|
||
# Quaternions have a struct as their storage, so we need to walk through this | ||
self.data = self.val['m_coeffs']['m_storage']['m_data']['array'] | ||
self.data = self.data.cast(self.innerType.pointer()) | ||
|
||
class _Iterator: | ||
def __init__(self, data_ptr): | ||
self.dataPtr = data_ptr | ||
self.currentElement = 0 | ||
self.elementNames = ['x', 'y', 'z', 'w'] | ||
|
||
def __iter__(self): | ||
return self | ||
|
||
def next(self): | ||
return self.__next__() # Python 2.x compatibility | ||
|
||
def __next__(self): | ||
element = self.currentElement | ||
|
||
if self.currentElement >= 4: # there are 4 elements in a quaternion | ||
raise StopIteration | ||
|
||
self.currentElement += 1 | ||
|
||
item = self.dataPtr.dereference() | ||
self.dataPtr += 1 | ||
return '[%s]' % (self.elementNames[element],), item | ||
|
||
def children(self): | ||
return self._Iterator(self.data) | ||
|
||
def to_string(self): | ||
return "Eigen::Quaternion<%s> (data ptr: %s)" % (self.innerType, self.data) | ||
|
||
|
||
def cast_eigen_block_to_matrix(val): | ||
# Get the type of the variable (and convert to a string) | ||
# Example: 'const Eigen::Block<Eigen::Block<Eigen::Matrix<double, -1, -1, 0, -1, -1>, -1, -1, false> const, -1, -1, false>' | ||
val_type = str(val.type) | ||
|
||
# Extract the Eigen::Matrix type from the Block: | ||
# From the previous example: Eigen::Matrix<double, -1, -1, 0, -1, -1> | ||
begin = val_type.find('Eigen::Matrix<') | ||
end = val_type.find('>', begin) + 1 | ||
|
||
# Convert the Eigen::Block to an Eigen::Matrix | ||
return val.cast(gdb.lookup_type(val_type[begin:end])) | ||
|
||
|
||
def build_eigen_dictionary(): | ||
pretty_printers_dict[re.compile('^Eigen::Quaternion<.*>$')] = lambda val: EigenQuaternionPrinter(val) | ||
pretty_printers_dict[re.compile('^Eigen::Matrix<.*>$')] = lambda val: EigenMatrixPrinter("Matrix", val) | ||
pretty_printers_dict[re.compile('^Eigen::Block<.*>$')] =\ | ||
lambda val: EigenMatrixPrinter("Matrix", cast_eigen_block_to_matrix(val)) | ||
pretty_printers_dict[re.compile('^Eigen::VectorBlock<.*>$')] =\ | ||
lambda val: EigenMatrixPrinter("Matrix", cast_eigen_block_to_matrix(val)) | ||
pretty_printers_dict[re.compile('^Eigen::SparseMatrix<.*>$')] = lambda val: EigenSparseMatrixPrinter(val) | ||
pretty_printers_dict[re.compile('^Eigen::Array<.*>$')] = lambda val: EigenMatrixPrinter("Array", val) | ||
|
||
|
||
def register_eigen_printers(obj): | ||
"""Register eigen pretty-printers with objfile Obj""" | ||
|
||
if obj is None: | ||
obj = gdb | ||
obj.pretty_printers.append(lookup_function) | ||
|
||
|
||
def lookup_function(val): | ||
"""Look-up and return a pretty-printer that can print val.""" | ||
|
||
typeinfo = val.type | ||
|
||
if typeinfo.code == gdb.TYPE_CODE_REF: | ||
typeinfo = typeinfo.target() | ||
|
||
typeinfo = typeinfo.unqualified().strip_typedefs() | ||
|
||
typename = typeinfo.tag | ||
if typename is None: | ||
return None | ||
|
||
for function in pretty_printers_dict: | ||
if function.search(typename): | ||
return pretty_printers_dict[function](val) | ||
|
||
return None | ||
|
||
|
||
pretty_printers_dict = {} | ||
|
||
build_eigen_dictionary() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
* About | ||
These are scripts and other pretty-printers for making GDB more | ||
tractable. | ||
|
||
** Eigen | ||
The `printers.py` is sourced from the [[https://gitlab.com/libeigen/eigen/-/blob/master/debug/gdb/printers.py][Eigen GitLab]] repository. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
python | ||
import sys | ||
import os | ||
# For eigen | ||
eigprint = f"{os.environ['HOME']}/.config/gdb/eigen" | ||
sys.path.insert(0, eigprint) | ||
from printers import register_eigen_printers | ||
register_eigen_printers(None) | ||
end |
aeed3d6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This needs to be documented better.