Add LLDB Pretty Printer

This commit is contained in:
Huang, Zhaoquan 2021-09-07 17:28:24 +00:00 committed by Rasmus Munk Larsen
parent 7792b1e909
commit 558b3d4fb8

234
debug/lldb/eigenlldb.py Normal file
View File

@ -0,0 +1,234 @@
# -*- coding: utf-8 -*-
# This file is part of Eigen, a lightweight C++ template library
# for linear algebra.
#
# Copyright (C) 2021 Huang, Zhaoquan <zhaoquan2008@hotmail.com>
#
# 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 to use with LLDB debugger
#
# Usage:
# 1. Add the following line (change it according to the path to this file)
# to the file ~/.lldbinit (create one if it doesn't exist):
# `command script import /path/to/eigenlldb.py`
# 2. Inspect the variables in LLDB command line
# `frame variable`
import lldb
from typing import List
import bisect
def __lldb_init_module(debugger, internal_dict):
debugger.HandleCommand("type synthetic add -x Eigen::Matrix<.*> --python-class eigenlldb.EigenMatrixChildProvider")
debugger.HandleCommand(
"type synthetic add -x Eigen::SparseMatrix<.*> --python-class eigenlldb.EigenSparseMatrixChildProvider")
class EigenMatrixChildProvider:
_valobj: lldb.SBValue
_scalar_type: lldb.SBType
_scalar_size: int
_rows_compile_time: int
_cols_compile_time: int
_row_major: bool
_fixed_storage: bool
def __init__(self, valobj, internal_dict):
self._valobj = valobj
valtype = valobj.GetType().GetCanonicalType()
scalar_type = valtype.GetTemplateArgumentType(0)
if not scalar_type.IsValid():
# In the case that scalar_type is invalid on LLDB 9.0 on Windows with CLion
storage = valobj.GetChildMemberWithName("m_storage")
data = storage.GetChildMemberWithName("m_data")
data_type = data.GetType()
if data_type.IsPointerType():
scalar_type = data.GetType().GetPointeeType()
else:
scalar_type = data.GetChildMemberWithName("array").GetType().GetArrayElementType()
self._scalar_type = scalar_type
self._scalar_size = self._scalar_type.GetByteSize()
name = valtype.GetName()
template_begin = name.find("<")
template_end = name.find(">")
template_args = name[(template_begin + 1):template_end].split(",")
self._rows_compile_time = int(template_args[1])
self._cols_compile_time = int(template_args[2])
self._row_major = (int(template_args[3]) & 1) != 0
max_rows = int(template_args[4])
max_cols = int(template_args[5])
self._fixed_storage = (max_rows != -1 and max_cols != -1)
def num_children(self):
return self._cols() * self._rows()
def get_child_index(self, name):
pass
def get_child_at_index(self, index):
storage = self._valobj.GetChildMemberWithName("m_storage")
data = storage.GetChildMemberWithName("m_data")
offset = self._scalar_size * index
if self._row_major:
row = index // self._cols()
col = index % self._cols()
else:
row = index % self._rows()
col = index // self._rows()
if self._fixed_storage:
data = data.GetChildMemberWithName("array")
if self._cols() == 1:
name = '[{}]'.format(row)
elif self._rows() == 1:
name = '[{}]'.format(col)
else:
name = '[{},{}]'.format(row, col)
return data.CreateChildAtOffset(
name, offset, self._scalar_type
)
def _cols(self):
if self._cols_compile_time == -1:
storage = self._valobj.GetChildMemberWithName("m_storage")
cols = storage.GetChildMemberWithName("m_cols")
return cols.GetValueAsUnsigned()
else:
return self._cols_compile_time
def _rows(self):
if self._rows_compile_time == -1:
storage = self._valobj.GetChildMemberWithName("m_storage")
rows = storage.GetChildMemberWithName("m_rows")
return rows.GetValueAsUnsigned()
else:
return self._rows_compile_time
class EigenSparseMatrixChildProvider:
_valobj: lldb.SBValue
_scalar_type: lldb.SBType
_scalar_size: int
_index_type: lldb.SBType
_index_size: int
_row_major: bool
_outer_size: int
_nnz: int
_values: lldb.SBValue
_inner_indices: lldb.SBValue
_outer_starts: lldb.SBValue
_inner_nnzs: lldb.SBValue
_compressed: bool
# Index of the first synthetic child under each outer index
_child_indices: List[int]
def __init__(self, valobj, internal_dict):
self._valobj = valobj
valtype = valobj.GetType().GetCanonicalType()
scalar_type = valtype.GetTemplateArgumentType(0)
if not scalar_type.IsValid():
# In the case that scalar_type is invalid on LLDB 9.0 on Windows with CLion
data = valobj.GetChildMemberWithName("m_data")
values = data.GetChildMemberWithName("m_values")
scalar_type = values.GetType().GetPointeeType()
self._scalar_type = scalar_type
self._scalar_size = scalar_type.GetByteSize()
index_type = valtype.GetTemplateArgumentType(2)
if not index_type.IsValid():
# In the case that scalar_type is invalid on LLDB 9.0 on Windows with CLion
outer_starts = valobj.GetChildMemberWithName("m_outerIndex")
index_type = outer_starts.GetType().GetPointeeType()
self._index_type = index_type
self._index_size = index_type.GetByteSize()
name = valtype.GetName()
template_begin = name.find("<")
template_end = name.find(">")
template_args = name[(template_begin + 1):template_end].split(",")
self._row_major = (int(template_args[1]) & 1) != 0
def num_children(self):
return self._nnz + 2
def get_child_index(self, name):
pass
def get_child_at_index(self, index):
if index == 0:
name = "rows" if self._row_major else "cols"
return self._valobj.GetChildMemberWithName("m_outerSize") \
.CreateChildAtOffset(name, 0, self._index_type)
elif index == 1:
name = "cols" if self._row_major else "rows"
return self._valobj.GetChildMemberWithName("m_innerSize") \
.CreateChildAtOffset(name, 0, self._index_type)
else:
index = index - 2
outer_index = bisect.bisect_right(self._child_indices, index) - 1
total_nnzs = self._child_indices[outer_index]
if self._compressed:
item_index = index
inner_index = self._inner_indices \
.CreateChildAtOffset("", item_index * self._index_size, self._index_type) \
.GetValueAsUnsigned()
return self._values \
.CreateChildAtOffset(self._child_name(outer_index, inner_index),
item_index * self._scalar_size,
self._scalar_type)
else:
index_begin = self._outer_starts \
.CreateChildAtOffset("", outer_index * self._index_size, self._index_type) \
.GetValueAsUnsigned()
item_index = index - total_nnzs + index_begin
inner_index = self._inner_indices \
.CreateChildAtOffset("", item_index * self._index_size, self._index_type) \
.GetValueAsUnsigned()
return self._values \
.CreateChildAtOffset(self._child_name(outer_index, inner_index),
item_index * self._scalar_size,
self._scalar_type)
def update(self):
valobj = self._valobj
self._outer_size = valobj.GetChildMemberWithName("m_outerSize").GetValueAsUnsigned()
data = valobj.GetChildMemberWithName("m_data")
self._values = data.GetChildMemberWithName("m_values")
self._inner_indices = data.GetChildMemberWithName("m_indices")
self._outer_starts = valobj.GetChildMemberWithName("m_outerIndex")
self._inner_nnzs = valobj.GetChildMemberWithName("m_innerNonZeros")
self._compressed = self._inner_nnzs.GetValueAsUnsigned() == 0
total_nnzs = 0
child_indices = [0]
for outer_index in range(self._outer_size):
if self._compressed:
index_end = self._outer_starts \
.CreateChildAtOffset("", (outer_index + 1) * self._index_size, self._index_type) \
.GetValueAsUnsigned()
total_nnzs = index_end
child_indices.append(total_nnzs)
else:
nnzs = self._inner_nnzs \
.CreateChildAtOffset("", outer_index * self._index_size, self._index_type) \
.GetValueAsUnsigned()
total_nnzs = total_nnzs + nnzs
child_indices.append(total_nnzs)
self._child_indices = child_indices
self._nnz = total_nnzs
def _child_name(self, outer_index, inner_index):
if self._row_major:
return "[{0},{1}]".format(outer_index, inner_index)
else:
return "[{1},{0}]".format(outer_index, inner_index)