// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "pyvalue.h" #include "extensioncontext.h" #include "symbolgroupvalue.h" #include constexpr bool debugPyValue = false; constexpr bool debuggingValueEnabled() { return debugPyValue || debugPyCdbextModule; } static std::map> valuesForSymbolGroup; void dumpSymbolGroup(CIDebugSymbolGroup *symbolGroup) { if (!debuggingValueEnabled()) return; ULONG count; if (FAILED(symbolGroup->GetNumberSymbols(&count))) return; DebugPrint() << "Symbol group " << symbolGroup << " has " << count << " symbols"; for (ULONG i = 0; i < count; ++i) DebugPrint() << " " << i << ": " << PyValue(i, symbolGroup).name(); } void PyValue::indicesMoved(CIDebugSymbolGroup *symbolGroup, ULONG start, ULONG delta) { if (debuggingValueEnabled()) DebugPrint() << "PyValue::indicesMoved " << symbolGroup << " start " << start << " delta " << delta << "\n"; if (delta == 0) return; ULONG count; if (FAILED(symbolGroup->GetNumberSymbols(&count))) return; if (count <= start) return; for (PyValue *val : valuesForSymbolGroup[symbolGroup]) { if (val->m_index >= start && val->m_index + delta < count) val->m_index += delta; } dumpSymbolGroup(symbolGroup); } PyValue::PyValue(unsigned long index, CIDebugSymbolGroup *symbolGroup) : m_index(index) , m_symbolGroup(symbolGroup) { if (m_symbolGroup) valuesForSymbolGroup[symbolGroup].push_back(this); } PyValue::PyValue(const PyValue &other) : m_index(other.m_index) , m_symbolGroup(other.m_symbolGroup) { if (m_symbolGroup) valuesForSymbolGroup[m_symbolGroup].push_back(this); } PyValue::~PyValue() { if (m_symbolGroup) valuesForSymbolGroup[m_symbolGroup].remove(this); } std::string PyValue::name() const { if (!m_symbolGroup) return std::string(); ULONG size = 0; m_symbolGroup->GetSymbolName(m_index, NULL, 0, &size); if (size == 0) return std::string(); std::string name(size - 1, '\0'); if (FAILED(m_symbolGroup->GetSymbolName(m_index, &name[0], size, &size))) name.clear(); return name; } PyType PyValue::type() { if (!m_symbolGroup) return PyType(); DEBUG_SYMBOL_PARAMETERS params; if (FAILED(m_symbolGroup->GetSymbolParameters(m_index, 1, ¶ms))) return PyType(); ULONG size = 0; m_symbolGroup->GetSymbolTypeName(m_index, NULL, 0, &size); std::string typeName; if (size != 0) { typeName = std::string(size - 1, '\0'); if (FAILED(m_symbolGroup->GetSymbolTypeName(m_index, &typeName[0], size, NULL))) typeName.clear(); } return PyType(params.Module, params.TypeId, typeName, tag()); } ULONG64 PyValue::bitsize() { if (!m_symbolGroup) return 0; ULONG size; if (FAILED(m_symbolGroup->GetSymbolSize(m_index, &size))) return 0; return size * 8; } Bytes PyValue::asBytes() { if (!m_symbolGroup) return Bytes(); ULONG64 address = 0; if (FAILED(m_symbolGroup->GetSymbolOffset(m_index, &address))) return Bytes(); ULONG size; if (FAILED(m_symbolGroup->GetSymbolSize(m_index, &size))) return Bytes(); Bytes bytes(size); unsigned long received; auto data = ExtensionCommandContext::instance()->dataSpaces(); if (FAILED(data->ReadVirtual(address, bytes.data(), size, &received))) return Bytes(); bytes.resize(received); return bytes; } ULONG64 PyValue::address() { ULONG64 address = 0; if (!m_symbolGroup || FAILED(m_symbolGroup->GetSymbolOffset(m_index, &address))) return 0; if (debuggingValueEnabled()) DebugPrint() << "Address of " << name() << ": " << std::hex << std::showbase << address; return address; } int PyValue::childCount() { if (!m_symbolGroup || !expand()) return 0; DEBUG_SYMBOL_PARAMETERS params; HRESULT hr = m_symbolGroup->GetSymbolParameters(m_index, 1, ¶ms); return SUCCEEDED(hr) ? params.SubElements : 0; } bool PyValue::hasChildren() { return childCount() != 0; } bool PyValue::expand() { if (!m_symbolGroup) return false; DEBUG_SYMBOL_PARAMETERS params; if (FAILED(m_symbolGroup->GetSymbolParameters(m_index, 1, ¶ms))) return false; if (params.Flags & DEBUG_SYMBOL_EXPANDED) return true; dumpSymbolGroup(m_symbolGroup); if (FAILED(m_symbolGroup->ExpandSymbol(m_index, TRUE))) return false; if (FAILED(m_symbolGroup->GetSymbolParameters(m_index, 1, ¶ms))) return false; if (params.Flags & DEBUG_SYMBOL_EXPANDED) { if (debuggingValueEnabled()) DebugPrint() << "Expanded " << name(); indicesMoved(m_symbolGroup, m_index + 1, params.SubElements); return true; } return false; } std::string PyValue::nativeDebuggerValue() { if (!m_symbolGroup) std::string(); ULONG size = 0; m_symbolGroup->GetSymbolValueText(m_index, NULL, 0, &size); std::string text(size - 1, '\0'); if (FAILED(m_symbolGroup->GetSymbolValueText(m_index, &text[0], size, &size))) return std::string(); return text; } int PyValue::isValid() { return m_symbolGroup != nullptr; } int PyValue::tag() { if (!m_symbolGroup) return -1; DEBUG_SYMBOL_ENTRY info; if (FAILED(m_symbolGroup->GetSymbolEntryInformation(m_index, &info))) return -1; return info.Tag; } PyValue PyValue::childFromName(const std::string &name) { const ULONG endIndex = m_index + childCount(); for (ULONG childIndex = m_index + 1; childIndex <= endIndex; ++childIndex) { PyValue child(childIndex, m_symbolGroup); if (child.name() == name) return child; } return PyValue(); } std::string pointedToSymbolName(ULONG64 address, const std::string &type) { std::ostringstream str; str << "*(" << type; if (!type.empty() && type.at(type.size() - 1) == '*') str << ' '; str << "*)" << std::showbase << std::hex << address; return str.str(); } PyValue PyValue::childFromField(const PyField &field) { if (!m_symbolGroup) return PyValue(); ULONG64 childAddress = address(); if (!childAddress) return PyValue(); childAddress += field.bitpos(); const std::string childTypeName = field.type().name(); if (childTypeName.empty()) return PyValue(); const std::string name = pointedToSymbolName(childAddress, childTypeName); ULONG index = DEBUG_ANY_ID; if (FAILED(m_symbolGroup->AddSymbol(name.c_str(), &index))) return PyValue(); return PyValue(index, m_symbolGroup); } ULONG currentNumberOfChildren(ULONG index, IDebugSymbolGroup2 *sg) { DEBUG_SYMBOL_PARAMETERS params; if (SUCCEEDED(sg->GetSymbolParameters(index, 1, ¶ms))) { if (params.Flags & DEBUG_SYMBOL_EXPANDED) return params.SubElements; } return 0; } ULONG currentNumberOfDescendants(ULONG index, IDebugSymbolGroup2 *sg) { ULONG descendantCount = currentNumberOfChildren(index, sg); for (ULONG childIndex = index + 1; childIndex <= descendantCount + index; ) { const ULONG childDescendantCount = currentNumberOfDescendants(childIndex, sg); childIndex += childDescendantCount + 1; descendantCount += childDescendantCount; } return descendantCount; } PyValue PyValue::childFromIndex(int index) { if (childCount() <= index) return PyValue(); int offset = index + 1; for (ULONG childIndex = m_index + 1; childIndex < m_index + offset; ) { const ULONG childDescendantCount = ::currentNumberOfDescendants(childIndex, m_symbolGroup); childIndex += childDescendantCount + 1; offset += childDescendantCount; } return PyValue(m_index + offset, m_symbolGroup); } ULONG PyValue::currentNumberOfDescendants() { return ::currentNumberOfDescendants(m_index, m_symbolGroup); } PyValue PyValue::createValue(ULONG64 address, const PyType &type) { if (debuggingValueEnabled()) { DebugPrint() << "Create Value address: 0x" << std::hex << address << " type name: " << type.name(); } IDebugSymbolGroup2 *symbolGroup = CurrentSymbolGroup::get(); if (symbolGroup == nullptr) return PyValue(); ULONG numberOfSymbols = 0; symbolGroup->GetNumberSymbols(&numberOfSymbols); ULONG index = 0; for (;index < numberOfSymbols; ++index) { ULONG64 offset; symbolGroup->GetSymbolOffset(index, &offset); if (offset == address) { DEBUG_SYMBOL_PARAMETERS params; if (SUCCEEDED(symbolGroup->GetSymbolParameters(index, 1, ¶ms))) { if (params.TypeId == type.getTypeId() && params.Module == type.moduleId()) return PyValue(index, symbolGroup); } } } const std::string name = SymbolGroupValue::pointedToSymbolName(address, type.name(true)); if (debuggingValueEnabled()) DebugPrint() << "Create Value expression: " << name; index = DEBUG_ANY_ID; if (FAILED(symbolGroup->AddSymbol(name.c_str(), &index))) return PyValue(); return PyValue(index, symbolGroup); } int PyValue::tag(const std::string &typeName) { CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols(); IDebugSymbolGroup2 *sg = 0; if (FAILED(symbols->CreateSymbolGroup2(&sg))) return -1; int tag = -1; const std::string name = SymbolGroupValue::pointedToSymbolName(0, typeName); ULONG index = DEBUG_ANY_ID; if (SUCCEEDED(sg->AddSymbol(name.c_str(), &index))) tag = PyValue(index, sg).tag(); sg->Release(); return tag; } // Python interface implementation namespace PyValueInterface { #define PY_OBJ_NAME ValuePythonObject PY_NEW_FUNC(PY_OBJ_NAME) PY_DEALLOC_FUNC(PY_OBJ_NAME) PY_FUNC_RET_STD_STRING(name, PY_OBJ_NAME) PY_FUNC_RET_OBJECT(type, PY_OBJ_NAME) PY_FUNC(bitsize, PY_OBJ_NAME, "K") PY_FUNC_RET_BYTES(asBytes, PY_OBJ_NAME) PY_FUNC(address, PY_OBJ_NAME, "K") PY_FUNC_RET_BOOL(hasChildren, PY_OBJ_NAME) PY_FUNC_RET_BOOL(expand, PY_OBJ_NAME) PY_FUNC_RET_STD_STRING(nativeDebuggerValue, PY_OBJ_NAME) PY_FUNC_DECL_WITH_ARGS(childFromName, PY_OBJ_NAME) { PY_IMPL_GUARD; char *name; if (!PyArg_ParseTuple(args, "s", &name)) Py_RETURN_NONE; return createPythonObject(self->impl->childFromName(name)); } PY_FUNC_DECL_WITH_ARGS(childFromField, PY_OBJ_NAME) { PY_IMPL_GUARD; FieldPythonObject *field; if (!PyArg_ParseTuple(args, "O", &field)) Py_RETURN_NONE; return createPythonObject(self->impl->childFromField(*(field->impl))); } PY_FUNC_DECL_WITH_ARGS(childFromIndex, PY_OBJ_NAME) { PY_IMPL_GUARD; unsigned int index; if (!PyArg_ParseTuple(args, "I", &index)) Py_RETURN_NONE; return createPythonObject(self->impl->childFromIndex(index)); } static PyMethodDef valueMethods[] = { {"name", PyCFunction(name), METH_NOARGS, "Name of this thing or None"}, {"type", PyCFunction(type), METH_NOARGS, "Type of this value"}, {"bitsize", PyCFunction(bitsize), METH_NOARGS, "Return the size of the value in bits"}, {"asBytes", PyCFunction(asBytes), METH_NOARGS, "Memory contents of this object, or None"}, {"address", PyCFunction(address), METH_NOARGS, "Address of this object, or None"}, {"hasChildren", PyCFunction(hasChildren), METH_NOARGS, "Whether this object has subobjects"}, {"expand", PyCFunction(expand), METH_NOARGS, "Make sure that children are accessible."}, {"nativeDebuggerValue", PyCFunction(nativeDebuggerValue), METH_NOARGS, "Value string returned by the debugger"}, {"childFromName", PyCFunction(childFromName), METH_VARARGS, "Return the name of this value"}, {"childFromField", PyCFunction(childFromField), METH_VARARGS, "Return the name of this value"}, {"childFromIndex", PyCFunction(childFromIndex), METH_VARARGS, "Return the name of this value"}, {NULL} /* Sentinel */ }; } // namespace PyValueInterface PyTypeObject *value_pytype() { static PyTypeObject cdbext_ValueType = { PyVarObject_HEAD_INIT(NULL, 0) "cdbext.Value", /* tp_name */ sizeof(ValuePythonObject), /* tp_basicsize */ 0, (destructor)PyValueInterface::dealloc, /* tp_dealloc */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, Py_TPFLAGS_DEFAULT, /* tp_flags */ "Value objects", /* tp_doc */ 0, 0, 0, 0, 0, 0, PyValueInterface::valueMethods, /* tp_methods */ 0, 0, 0, 0, 0, 0, 0, 0, 0, PyValueInterface::newObject, /* tp_new */ }; return &cdbext_ValueType; } PyObject *createPythonObject(PyValue implClass) { if (!implClass.isValid()) Py_RETURN_NONE; ValuePythonObject *newPyValue = PyObject_New(ValuePythonObject, value_pytype()); newPyValue->impl = new PyValue(implClass); return reinterpret_cast(newPyValue); }