forked from qt-creator/qt-creator
Cdb: Introduce interface for python dumpers
Change-Id: I0ef2fd8a44232d65b0d772fd6c65230266d586a8 Reviewed-by: hjk <hjk@qt.io> Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -80,6 +80,8 @@ class Value:
|
||||
dereference() -> Value # Dereference if value is pointer,
|
||||
# remove reference if value is reference.
|
||||
hasChildren() -> bool # Whether this object has subobjects.
|
||||
expand() -> bool # Make sure that children are accessible.
|
||||
nativeDebuggerValue() -> string # Dumper value returned from the debugger
|
||||
|
||||
childFromName(string name) -> Value # (optional)
|
||||
childFromField(Field field) -> Value # (optional)
|
||||
@@ -122,6 +124,7 @@ class Field:
|
||||
parseAndEvaluate(string: expr) -> Value # or None if not possible.
|
||||
lookupType(string: name) -> Type # or None if not possible.
|
||||
listOfLocals() -> [ Value ] # List of items currently in scope.
|
||||
readRawMemory(ULONG64 address, ULONG size) -> bytes # Read a block of data from the virtual address space
|
||||
|
||||
|
||||
|
||||
|
||||
231
share/qtcreator/debugger/cdbbridge.py
Normal file
231
share/qtcreator/debugger/cdbbridge.py
Normal file
@@ -0,0 +1,231 @@
|
||||
############################################################################
|
||||
#
|
||||
# Copyright (C) 2016 The Qt Company Ltd.
|
||||
# Contact: https://www.qt.io/licensing/
|
||||
#
|
||||
# This file is part of Qt Creator.
|
||||
#
|
||||
# Commercial License Usage
|
||||
# Licensees holding valid commercial Qt licenses may use this file in
|
||||
# accordance with the commercial license agreement provided with the
|
||||
# Software or, alternatively, in accordance with the terms contained in
|
||||
# a written agreement between you and The Qt Company. For licensing terms
|
||||
# and conditions see https://www.qt.io/terms-conditions. For further
|
||||
# information use the contact form at https://www.qt.io/contact-us.
|
||||
#
|
||||
# GNU General Public License Usage
|
||||
# Alternatively, this file may be used under the terms of the GNU
|
||||
# General Public License version 3 as published by the Free Software
|
||||
# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
# included in the packaging of this file. Please review the following
|
||||
# information to ensure the GNU General Public License requirements will
|
||||
# be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
import threading
|
||||
import cdbext
|
||||
|
||||
sys.path.insert(1, os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe()))))
|
||||
|
||||
from dumper import *
|
||||
|
||||
class Dumper(DumperBase):
|
||||
def __init__(self):
|
||||
DumperBase.__init__(self)
|
||||
|
||||
self.outputLock = threading.Lock()
|
||||
|
||||
self.isCdb = True
|
||||
self.expandedINames = {}
|
||||
self.passExceptions = False
|
||||
self.showQObjectNames = False
|
||||
self.autoDerefPointers = True
|
||||
self.useDynamicType = True
|
||||
self.useFancy = True
|
||||
self.formats = {}
|
||||
self.typeformats = {}
|
||||
self.currentContextValue = None
|
||||
|
||||
self.currentIName = None
|
||||
self.currentValue = ReportItem()
|
||||
self.currentType = ReportItem()
|
||||
self.currentNumChild = None
|
||||
self.currentMaxNumChild = None
|
||||
self.currentPrintsAddress = True
|
||||
self.currentChildType = None
|
||||
self.currentChildNumChild = -1
|
||||
self.currentWatchers = {}
|
||||
|
||||
def fromNativeValue(self, nativeValue):
|
||||
val = self.Value(self)
|
||||
val.nativeValue = nativeValue
|
||||
val.type = self.fromNativeType(nativeValue.type())
|
||||
val.lIsInScope = True
|
||||
val.laddress = nativeValue.address()
|
||||
return val
|
||||
|
||||
def fromNativeType(self, nativeType):
|
||||
typeobj = self.Type(self)
|
||||
typeobj.nativeType = nativeType
|
||||
typeobj.name = nativeType.name()
|
||||
typeobj.lbitsize = nativeType.bitsize()
|
||||
typeobj.code = nativeType.code()
|
||||
return typeobj
|
||||
|
||||
def nativeTypeFields(self, nativeType):
|
||||
fields = []
|
||||
for nativeField in nativeType.fields():
|
||||
field = self.Field(self)
|
||||
field.name = nativeField.name()
|
||||
field.parentType = self.fromNativeType(nativeType)
|
||||
field.ltype = self.fromNativeType(nativeField.type())
|
||||
field.lbitsize = nativeField.bitsize()
|
||||
field.lbitpos = nativeField.bitpos()
|
||||
fields.append(field)
|
||||
return fields
|
||||
|
||||
def nativeTypeUnqualified(self, nativeType):
|
||||
return self.fromNativeType(nativeType.unqualified())
|
||||
|
||||
def nativeTypePointer(self, nativeType):
|
||||
return self.fromNativeType(nativeType.target())
|
||||
|
||||
def nativeTypeStripTypedefs(self, typeobj):
|
||||
return self.fromNativeType(nativeType.stripTypedef())
|
||||
|
||||
def nativeTypeFirstBase(self, nativeType):
|
||||
return None
|
||||
|
||||
def nativeTypeEnumDisplay(self, nativeType, intval):
|
||||
# TODO: generate fake value
|
||||
return None
|
||||
|
||||
def enumExpression(self, enumType, enumValue):
|
||||
ns = self.qtNamespace()
|
||||
return ns + "Qt::" + enumType + "(" \
|
||||
+ ns + "Qt::" + enumType + "::" + enumValue + ")"
|
||||
|
||||
def pokeValue(self, typeName, *args):
|
||||
return None
|
||||
|
||||
def parseAndEvaluate(self, exp):
|
||||
return cdbext.parseAndEvaluate(exp)
|
||||
|
||||
def nativeTypeTemplateArgument(self, nativeType, position, numeric = False):
|
||||
return self.fromNativeType(nativeType.templateArgument(position, numeric))
|
||||
|
||||
def nativeTypeDereference(self, nativeType):
|
||||
return self.fromNativeType(nativeType.target())
|
||||
|
||||
def nativeTypeTarget(self, nativeType):
|
||||
return self.fromNativeType(nativeType.target())
|
||||
|
||||
def isWindowsTarget(self):
|
||||
return True
|
||||
|
||||
def isQnxTarget(self):
|
||||
return False
|
||||
|
||||
def isArmArchitecture(self):
|
||||
return False
|
||||
|
||||
def isMsvcTarget(self):
|
||||
return True
|
||||
|
||||
def qtVersionAndNamespace(self):
|
||||
return ('', 0x50700) #FIXME: use a general approach in dumper or qttypes
|
||||
|
||||
def qtNamespace(self):
|
||||
return self.qtVersionAndNamespace()[0]
|
||||
|
||||
def qtVersion(self):
|
||||
self.qtVersionAndNamespace()
|
||||
return self.qtVersionAndNamespace()[1]
|
||||
|
||||
def qtTypeInfoVersion(self):
|
||||
return None
|
||||
|
||||
def ptrSize(self):
|
||||
return cdbext.pointerSize()
|
||||
|
||||
def put(self, stuff):
|
||||
self.output += stuff
|
||||
|
||||
def lookupNativeType(self, name):
|
||||
return cdbext.lookupType(name)
|
||||
|
||||
def currentThread(self):
|
||||
return None
|
||||
|
||||
def currentFrame(self):
|
||||
return None
|
||||
|
||||
def reportResult(self, result, args):
|
||||
self.report('result={%s}' % (result))
|
||||
|
||||
def readRawMemory(self, address, size):
|
||||
return cdbext.readRawMemory(address, size)
|
||||
|
||||
def findStaticMetaObject(self, typeName):
|
||||
symbolName = self.mangleName(typeName + '::staticMetaObject')
|
||||
symbol = self.target.FindFirstGlobalVariable(symbolName)
|
||||
return symbol.AddressOf().GetValueAsUnsigned() if symbol.IsValid() else 0
|
||||
|
||||
def warn(self, msg):
|
||||
self.put('{name="%s",value="",type="",numchild="0"},' % msg)
|
||||
|
||||
def fetchVariables(self, args):
|
||||
(ok, res) = self.tryFetchInterpreterVariables(args)
|
||||
if ok:
|
||||
self.reportResult(res, args)
|
||||
return
|
||||
|
||||
self.expandedINames = set(args.get('expanded', []))
|
||||
self.autoDerefPointers = int(args.get('autoderef', '0'))
|
||||
self.useDynamicType = int(args.get('dyntype', '0'))
|
||||
self.useFancy = int(args.get('fancy', '0'))
|
||||
self.passExceptions = int(args.get('passexceptions', '0'))
|
||||
self.showQObjectNames = int(args.get('qobjectnames', '0'))
|
||||
self.currentWatchers = args.get('watchers', {})
|
||||
self.typeformats = args.get('typeformats', {})
|
||||
self.formats = args.get('formats', {})
|
||||
|
||||
self.output = ''
|
||||
partialVariable = args.get('partialvar', "")
|
||||
isPartial = len(partialVariable) > 0
|
||||
|
||||
self.currentIName = 'local'
|
||||
self.put('data=[')
|
||||
self.anonNumber = 0
|
||||
|
||||
variables = []
|
||||
for val in cdbext.listOfLocals():
|
||||
self.currentContextValue = val
|
||||
name = val.name()
|
||||
value = self.fromNativeValue(val)
|
||||
value.name = name
|
||||
variables.append(value)
|
||||
|
||||
self.handleLocals(variables)
|
||||
self.handleWatches(args)
|
||||
|
||||
self.put('],partial="%d"' % isPartial)
|
||||
self.reportResult(self.output, args)
|
||||
|
||||
def report(self, stuff):
|
||||
with self.outputLock:
|
||||
sys.stdout.write(stuff + "\n")
|
||||
|
||||
def loadDumpers(self, args):
|
||||
msg = self.setupDumpers()
|
||||
self.reportResult(msg, args)
|
||||
|
||||
def findValueByExpression(self, exp):
|
||||
return cdbext.parseAndEvaluate(exp)
|
||||
|
||||
def nativeDynamicTypeName(self, address, baseType):
|
||||
return None # FIXME: Seems sufficient, no idea why.
|
||||
@@ -94,7 +94,7 @@ BreakpointAtJavaScriptThrow, \
|
||||
= range(0, 14)
|
||||
|
||||
|
||||
# Internal codes for types
|
||||
# Internal codes for types keep in sync with cdbextensions pytype.cpp
|
||||
TypeCodeTypedef, \
|
||||
TypeCodeStruct, \
|
||||
TypeCodeVoid, \
|
||||
|
||||
@@ -680,6 +680,9 @@ class Dumper(DumperBase):
|
||||
# We get i686-w64-mingw32
|
||||
return 'mingw' in gdb.TARGET_CONFIG.lower()
|
||||
|
||||
def isMsvcTarget(self):
|
||||
return False
|
||||
|
||||
def qtVersionString(self):
|
||||
try:
|
||||
return str(gdb.lookup_symbol("qVersion")[0].value()())
|
||||
|
||||
@@ -474,6 +474,9 @@ class Dumper(DumperBase):
|
||||
def isArmArchitecture(self):
|
||||
return False
|
||||
|
||||
def isMsvcTarget(self):
|
||||
return False
|
||||
|
||||
def qtVersionAndNamespace(self):
|
||||
for func in self.target.FindFunctions('qVersion'):
|
||||
name = func.GetSymbol().GetName()
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
#include <Python.h>
|
||||
#include "pycdbextmodule.h"
|
||||
#endif
|
||||
|
||||
// wdbgexts.h declares 'extern WINDBG_EXTENSION_APIS ExtensionApis;'
|
||||
@@ -160,7 +161,10 @@ HRESULT ExtensionContext::initialize(PULONG Version, PULONG Flags)
|
||||
*Flags = 0;
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
initCdbextPythonModule();
|
||||
Py_Initialize();
|
||||
PyRun_SimpleString("import cdbext");
|
||||
PyRun_SimpleString("import sys");
|
||||
#endif
|
||||
|
||||
IInterfacePointer<CIDebugClient> client;
|
||||
|
||||
187
src/libs/qtcreatorcdbext/pycdbextmodule.cpp
Normal file
187
src/libs/qtcreatorcdbext/pycdbextmodule.cpp
Normal file
@@ -0,0 +1,187 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "pycdbextmodule.h"
|
||||
|
||||
#include "extensioncontext.h"
|
||||
#include "symbolgroup.h"
|
||||
|
||||
#include "pyfield.h"
|
||||
#include "pystdoutredirect.h"
|
||||
#include "pytype.h"
|
||||
#include "pyvalue.h"
|
||||
|
||||
#include <Python.h>
|
||||
#include <structmember.h>
|
||||
|
||||
// cdbext python module
|
||||
static PyObject *cdbext_parseAndEvaluate(PyObject *, PyObject *args) // -> Value
|
||||
{
|
||||
char *expr;
|
||||
if (!PyArg_ParseTuple(args, "s", &expr))
|
||||
return NULL;
|
||||
CIDebugControl *control = ExtensionCommandContext::instance()->control();
|
||||
control->SetExpressionSyntax(DEBUG_EXPR_CPLUSPLUS);
|
||||
DEBUG_VALUE value;
|
||||
if (FAILED(control->Evaluate(expr, DEBUG_VALUE_INT64, &value, NULL)))
|
||||
Py_RETURN_NONE;
|
||||
return Py_BuildValue("K", value.I64);
|
||||
}
|
||||
|
||||
static PyObject *cdbext_lookupType(PyObject *, PyObject *args) // -> Type
|
||||
{
|
||||
char *type;
|
||||
if (!PyArg_ParseTuple(args, "s", &type))
|
||||
return NULL;
|
||||
return lookupType(type);
|
||||
}
|
||||
|
||||
static PyObject *cdbext_listOfLocals(PyObject *, PyObject *) // -> [ Value ]
|
||||
{
|
||||
ExtensionCommandContext *extCmdCtx = ExtensionCommandContext::instance();
|
||||
ULONG frame;
|
||||
if (FAILED(extCmdCtx->symbols()->GetCurrentScopeFrameIndex(&frame)))
|
||||
return NULL;
|
||||
|
||||
std::string errorMessage;
|
||||
LocalsSymbolGroup *sg = ExtensionContext::instance().symbolGroup(
|
||||
extCmdCtx->symbols(), extCmdCtx->threadId(), int(frame), &errorMessage);
|
||||
if (!sg)
|
||||
return NULL;
|
||||
|
||||
const auto children = sg->root()->children();
|
||||
auto locals = PyList_New(0);
|
||||
for (AbstractSymbolGroupNode *abstractChild : children) {
|
||||
Value *childValue = PyObject_New(Value, value_pytype());
|
||||
if (childValue != NULL) {
|
||||
if (SymbolGroupNode* child = abstractChild->asSymbolGroupNode()) {
|
||||
childValue->m_index = child->index();
|
||||
childValue->m_symbolGroup = sg->debugSymbolGroup();
|
||||
}
|
||||
}
|
||||
PyList_Append(locals, reinterpret_cast<PyObject*>(childValue));
|
||||
}
|
||||
|
||||
return locals;
|
||||
}
|
||||
|
||||
static PyObject *cdbext_pointerSize(PyObject *, PyObject *)
|
||||
{
|
||||
HRESULT isPointer64Bit = ExtensionCommandContext::instance()->control()->IsPointer64Bit();
|
||||
return Py_BuildValue("i", isPointer64Bit == S_OK ? 8 : 4);
|
||||
}
|
||||
|
||||
static PyObject *cdbext_readRawMemory(PyObject *, PyObject *args)
|
||||
{
|
||||
ULONG64 address = 0;
|
||||
ULONG size = 0;
|
||||
if (!PyArg_ParseTuple(args, "Kk", &address, &size))
|
||||
return NULL;
|
||||
|
||||
char *buffer = new char[size];
|
||||
|
||||
CIDebugDataSpaces *data = ExtensionCommandContext::instance()->dataSpaces();
|
||||
ULONG bytesWritten = 0;
|
||||
HRESULT hr = data->ReadVirtual(address, buffer, size, &bytesWritten);
|
||||
if (FAILED(hr))
|
||||
bytesWritten = 0;
|
||||
PyObject *ret = Py_BuildValue("y#", buffer, bytesWritten);
|
||||
delete[] buffer;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static PyMethodDef cdbextMethods[] = {
|
||||
{"parseAndEvaluate", cdbext_parseAndEvaluate, METH_VARARGS,
|
||||
"Returns value of expression or None if the expression can not be resolved"},
|
||||
{"lookupType", cdbext_lookupType, METH_VARARGS,
|
||||
"Returns type object or None if the type can not be resolved"},
|
||||
{"listOfLocals", cdbext_listOfLocals, METH_NOARGS,
|
||||
"Returns list of values that are currently in scope"},
|
||||
{"pointerSize", cdbext_pointerSize, METH_NOARGS,
|
||||
"Returns the size of a pointer"},
|
||||
{"readRawMemory", cdbext_readRawMemory, METH_VARARGS,
|
||||
"Read a block of data from the virtual address space"},
|
||||
{NULL, NULL, 0,
|
||||
NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static struct PyModuleDef cdbextModule = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"cdbext", /* name of module */
|
||||
"bridge to the creator cdb extension", /* module documentation */
|
||||
-1, /* size of per-interpreter state of the module,
|
||||
or -1 if the module keeps state in global variables. */
|
||||
cdbextMethods
|
||||
};
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit_cdbext(void)
|
||||
{
|
||||
if (PyType_Ready(field_pytype()) < 0)
|
||||
return NULL;
|
||||
|
||||
if (PyType_Ready(type_pytype()) < 0)
|
||||
return NULL;
|
||||
|
||||
if (PyType_Ready(value_pytype()) < 0)
|
||||
return NULL;
|
||||
|
||||
stdoutRedirect_pytype()->tp_new = PyType_GenericNew;
|
||||
if (PyType_Ready(stdoutRedirect_pytype()) < 0)
|
||||
return NULL;
|
||||
|
||||
PyObject *module = PyModule_Create(&cdbextModule);
|
||||
if (module == NULL)
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(field_pytype());
|
||||
Py_INCREF(stdoutRedirect_pytype());
|
||||
Py_INCREF(type_pytype());
|
||||
Py_INCREF(value_pytype());
|
||||
|
||||
PyModule_AddObject(module, "Field",
|
||||
reinterpret_cast<PyObject *>(field_pytype()));
|
||||
PyModule_AddObject(module, "StdoutRedirect",
|
||||
reinterpret_cast<PyObject *>(stdoutRedirect_pytype()));
|
||||
PyModule_AddObject(module, "Type",
|
||||
reinterpret_cast<PyObject *>(type_pytype()));
|
||||
PyModule_AddObject(module, "Value",
|
||||
reinterpret_cast<PyObject *>(value_pytype()));
|
||||
|
||||
return module;
|
||||
}
|
||||
|
||||
void initCdbextPythonModule()
|
||||
{
|
||||
PyImport_AppendInittab("cdbext", PyInit_cdbext);
|
||||
}
|
||||
|
||||
PyObject *pyBool(bool b)
|
||||
{
|
||||
if (b)
|
||||
Py_RETURN_TRUE;
|
||||
else
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
40
src/libs/qtcreatorcdbext/pycdbextmodule.h
Normal file
40
src/libs/qtcreatorcdbext/pycdbextmodule.h
Normal file
@@ -0,0 +1,40 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
void initCdbextPythonModule();
|
||||
|
||||
PyObject *pyBool(bool);
|
||||
|
||||
/* TODO's
|
||||
class Field:
|
||||
isBaseClass() -> bool # Whether this is a base class or normal member
|
||||
|
||||
parseAndEvaluate(string: expr) -> Value # or None if not possible.
|
||||
|
||||
*/
|
||||
167
src/libs/qtcreatorcdbext/pyfield.cpp
Normal file
167
src/libs/qtcreatorcdbext/pyfield.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "pyfield.h"
|
||||
|
||||
#include "pytype.h"
|
||||
#include "extensioncontext.h"
|
||||
|
||||
PyObject *field_Name(Field *self)
|
||||
{
|
||||
return Py_BuildValue("s", self->m_name);
|
||||
}
|
||||
|
||||
PyObject *field_isBaseClass(Field *)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool initTypeAndOffset(Field *field)
|
||||
{
|
||||
auto extcmd = ExtensionCommandContext::instance();
|
||||
field->m_initialized = SUCCEEDED(extcmd->symbols()->GetFieldTypeAndOffset(
|
||||
field->m_module, field->m_parentTypeId,
|
||||
field->m_name, &field->m_typeId, &field->m_offset));
|
||||
return field->m_initialized;
|
||||
}
|
||||
|
||||
PyObject *field_Type(Field *self)
|
||||
{
|
||||
if (!self->m_initialized)
|
||||
if (!initTypeAndOffset(self))
|
||||
return NULL;
|
||||
return createType(self->m_module, self->m_typeId);
|
||||
}
|
||||
|
||||
PyObject *field_ParentType(Field *self)
|
||||
{
|
||||
return createType(self->m_module, self->m_parentTypeId);
|
||||
}
|
||||
|
||||
PyObject *field_Bitsize(Field *self)
|
||||
{
|
||||
if (!self->m_initialized)
|
||||
if (!initTypeAndOffset(self))
|
||||
return NULL;
|
||||
ULONG byteSize;
|
||||
auto extcmd = ExtensionCommandContext::instance();
|
||||
if (FAILED(extcmd->symbols()->GetTypeSize(self->m_module, self->m_typeId, &byteSize)))
|
||||
return NULL;
|
||||
return Py_BuildValue("k", byteSize * 8);
|
||||
}
|
||||
|
||||
PyObject *field_Bitpos(Field *self)
|
||||
{
|
||||
if (!self->m_initialized)
|
||||
if (!initTypeAndOffset(self))
|
||||
return NULL;
|
||||
return Py_BuildValue("k", self->m_offset * 8);
|
||||
}
|
||||
|
||||
PyObject *field_New(PyTypeObject *type, PyObject *, PyObject *)
|
||||
{
|
||||
Field *self = reinterpret_cast<Field *>(type->tp_alloc(type, 0));
|
||||
if (self != NULL)
|
||||
initField(self);
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
void field_Dealloc(Field *self)
|
||||
{
|
||||
delete[] self->m_name;
|
||||
}
|
||||
|
||||
void initField(Field *field)
|
||||
{
|
||||
field->m_name = 0;
|
||||
field->m_initialized = false;
|
||||
field->m_typeId = 0;
|
||||
field->m_offset = 0;
|
||||
field->m_module = 0;
|
||||
field->m_parentTypeId = 0;
|
||||
}
|
||||
|
||||
static PyMethodDef fieldMethods[] = {
|
||||
{"name", PyCFunction(field_Name), METH_NOARGS,
|
||||
"Return the name of this field or None for anonymous fields"},
|
||||
{"isBaseClass", PyCFunction(field_isBaseClass), METH_NOARGS,
|
||||
"Whether this is a base class or normal member"},
|
||||
{"type", PyCFunction(field_Type), METH_NOARGS,
|
||||
"Type of this member"},
|
||||
{"parentType", PyCFunction(field_ParentType), METH_NOARGS,
|
||||
"Type of class this member belongs to"},
|
||||
{"bitsize", PyCFunction(field_Bitsize), METH_NOARGS,
|
||||
"Size of member in bits"},
|
||||
{"bitpos", PyCFunction(field_Bitpos), METH_NOARGS,
|
||||
"Offset of member in parent type in bits"},
|
||||
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyTypeObject *field_pytype()
|
||||
{
|
||||
static PyTypeObject cdbext_FieldType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"cdbext.Field", /* tp_name */
|
||||
sizeof(Field), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)field_Dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_as_async */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
"Field objects", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
fieldMethods, /* tp_methods */
|
||||
0, /* tp_members */
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
field_New, /* tp_new */
|
||||
};
|
||||
|
||||
return &cdbext_FieldType;
|
||||
}
|
||||
46
src/libs/qtcreatorcdbext/pyfield.h
Normal file
46
src/libs/qtcreatorcdbext/pyfield.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Field
|
||||
{
|
||||
PyObject_HEAD
|
||||
const char *m_name; // owned
|
||||
bool m_initialized;
|
||||
unsigned long m_typeId;
|
||||
unsigned long m_offset;
|
||||
unsigned long m_parentTypeId;
|
||||
ULONG64 m_module;
|
||||
};
|
||||
|
||||
PyTypeObject *field_pytype();
|
||||
|
||||
void initField(Field *field);
|
||||
bool initTypeAndOffset(Field *field);
|
||||
116
src/libs/qtcreatorcdbext/pystdoutredirect.cpp
Normal file
116
src/libs/qtcreatorcdbext/pystdoutredirect.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "pystdoutredirect.h"
|
||||
|
||||
static std::string output;
|
||||
static PyObject *stdoutRedirect = nullptr;
|
||||
|
||||
struct StdoutRedirect
|
||||
{
|
||||
PyObject_HEAD
|
||||
};
|
||||
|
||||
PyObject *stdoutRedirect_write(PyObject * /*self*/, PyObject *args)
|
||||
{
|
||||
char *string;
|
||||
PyArg_ParseTuple(args, "s", &string);
|
||||
output += string;
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
|
||||
PyObject *stdoutRedirect_flush(PyObject * /*self*/, PyObject * /*args*/)
|
||||
{
|
||||
return Py_BuildValue("");
|
||||
}
|
||||
|
||||
static PyMethodDef StdoutRedirectMethods[] =
|
||||
{
|
||||
{"write", stdoutRedirect_write, METH_VARARGS, "sys.stdout.write"},
|
||||
{"flush", stdoutRedirect_flush, METH_VARARGS, "sys.stdout.flush"},
|
||||
{0, 0, 0, 0} // sentinel
|
||||
};
|
||||
|
||||
PyTypeObject *stdoutRedirect_pytype()
|
||||
{
|
||||
static PyTypeObject cdbext_StdoutRedirectType =
|
||||
{
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"cdbext.StdoutRedirect", /* tp_name */
|
||||
sizeof(StdoutRedirect), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
0, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_reserved */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
"stdout redirector", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
StdoutRedirectMethods, /* tp_methods */
|
||||
};
|
||||
return &cdbext_StdoutRedirectType;
|
||||
}
|
||||
|
||||
void startCapturePyStdout()
|
||||
{
|
||||
if (stdoutRedirect != nullptr)
|
||||
endCapturePyStdout();
|
||||
stdoutRedirect = _PyObject_New(stdoutRedirect_pytype());
|
||||
if (PySys_SetObject("stdout", stdoutRedirect) != 0)
|
||||
PySys_SetObject("stdout", PySys_GetObject("__stdout__"));
|
||||
if (PySys_SetObject("stderr", stdoutRedirect) != 0)
|
||||
PySys_SetObject("stderr", PySys_GetObject("__stderr__"));
|
||||
PyObject_CallMethod(stdoutRedirect, "write", "s", "text");
|
||||
output.clear();
|
||||
}
|
||||
|
||||
std::string getPyStdout()
|
||||
{
|
||||
return output;
|
||||
}
|
||||
|
||||
void endCapturePyStdout()
|
||||
{
|
||||
PySys_SetObject("stdout", PySys_GetObject("__stdout__"));
|
||||
PySys_SetObject("stderr", PySys_GetObject("__stderr__"));
|
||||
Py_DecRef(stdoutRedirect);
|
||||
stdoutRedirect = nullptr;
|
||||
}
|
||||
35
src/libs/qtcreatorcdbext/pystdoutredirect.h
Normal file
35
src/libs/qtcreatorcdbext/pystdoutredirect.h
Normal file
@@ -0,0 +1,35 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <Python.h>
|
||||
#include <string>
|
||||
|
||||
PyTypeObject *stdoutRedirect_pytype();
|
||||
|
||||
void startCapturePyStdout();
|
||||
std::string getPyStdout();
|
||||
void endCapturePyStdout();
|
||||
356
src/libs/qtcreatorcdbext/pytype.cpp
Normal file
356
src/libs/qtcreatorcdbext/pytype.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "pytype.h"
|
||||
|
||||
#include <Python.h>
|
||||
|
||||
#include "extensioncontext.h"
|
||||
#include "pycdbextmodule.h"
|
||||
#include "pyfield.h"
|
||||
#include "stringutils.h"
|
||||
#include "symbolgroupvalue.h"
|
||||
|
||||
enum TypeCodes {
|
||||
TypeCodeTypedef,
|
||||
TypeCodeStruct,
|
||||
TypeCodeVoid,
|
||||
TypeCodeIntegral,
|
||||
TypeCodeFloat,
|
||||
TypeCodeEnum,
|
||||
TypeCodePointer,
|
||||
TypeCodeArray,
|
||||
TypeCodeComplex,
|
||||
TypeCodeReference,
|
||||
TypeCodeFunction,
|
||||
TypeCodeMemberPointer,
|
||||
TypeCodeFortranString
|
||||
};
|
||||
|
||||
PyObject *lookupType(const std::string &typeNameIn)
|
||||
{
|
||||
std::string typeName = typeNameIn;
|
||||
CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
|
||||
ULONG64 module;
|
||||
ULONG typeId;
|
||||
if (FAILED(symbols->GetSymbolTypeId(typeName.c_str(), &typeId, &module)))
|
||||
Py_RETURN_NONE;
|
||||
return createType(module, typeId);
|
||||
}
|
||||
|
||||
char *getTypeName(ULONG64 module, ULONG typeId)
|
||||
{
|
||||
char *typeName = 0;
|
||||
auto symbols = ExtensionCommandContext::instance()->symbols();
|
||||
ULONG size = 0;
|
||||
symbols->GetTypeName(module, typeId, NULL, 0, &size);
|
||||
if (size > 0) {
|
||||
typeName = new char[size];
|
||||
if (FAILED(symbols->GetTypeName(module, typeId, typeName, size, &size))) {
|
||||
delete[] typeName;
|
||||
typeName = new char[1];
|
||||
typeName[0] = 0;
|
||||
}
|
||||
}
|
||||
return typeName;
|
||||
}
|
||||
|
||||
const char *getTypeName(Type *type)
|
||||
{
|
||||
if (type->m_name == 0)
|
||||
type->m_name = getTypeName(type->m_module, type->m_typeId);
|
||||
return type->m_name;
|
||||
}
|
||||
|
||||
PyObject *type_Name(Type *self)
|
||||
{
|
||||
return Py_BuildValue("s", getTypeName(self));
|
||||
}
|
||||
|
||||
PyObject *type_bitSize(Type *self)
|
||||
{
|
||||
ULONG size;
|
||||
auto extcmd = ExtensionCommandContext::instance();
|
||||
if (FAILED(extcmd->symbols()->GetTypeSize(self->m_module, self->m_typeId, &size)))
|
||||
return NULL;
|
||||
return Py_BuildValue("k", size * 8);
|
||||
}
|
||||
|
||||
bool isType(const std::string &typeName, const std::vector<std::string> &types)
|
||||
{
|
||||
return std::find(types.begin(), types.end(), typeName) != types.end();
|
||||
}
|
||||
|
||||
PyObject *type_Code(Type *self)
|
||||
{
|
||||
static const std::vector<std::string> integralTypes({"bool",
|
||||
"char", "unsigned char", "char16_t", "char32_t", "wchar_t",
|
||||
"short", "unsigned short", "int", "unsigned int",
|
||||
"long", "unsigned long", "int64", "unsigned int64"});
|
||||
static const std::vector<std::string> floatTypes({"float", "double"});
|
||||
|
||||
TypeCodes code = TypeCodeStruct;
|
||||
const char *typeNameCstr = getTypeName(self);
|
||||
if (typeNameCstr == 0)
|
||||
Py_RETURN_NONE;
|
||||
const std::string typeName(typeNameCstr);
|
||||
if (SymbolGroupValue::isArrayType(typeName))
|
||||
code = TypeCodeArray;
|
||||
else if (endsWith(typeName, "*"))
|
||||
code = TypeCodePointer;
|
||||
else if (typeName.find("<function>") != std::string::npos)
|
||||
code = TypeCodeFunction;
|
||||
else if (isType(typeName, integralTypes))
|
||||
code = TypeCodeIntegral;
|
||||
else if (isType(typeName, floatTypes))
|
||||
code = TypeCodeFloat;
|
||||
|
||||
return Py_BuildValue("k", code);
|
||||
}
|
||||
|
||||
PyObject *type_Unqualified(Type *self)
|
||||
{
|
||||
Py_XINCREF(self);
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
PyObject *type_Target(Type *self)
|
||||
{
|
||||
std::string typeName(getTypeName(self));
|
||||
if (!endsWith(typeName, "*")) {
|
||||
Py_XINCREF(self);
|
||||
return (PyObject *)self;
|
||||
}
|
||||
typeName = typeName.substr(0, typeName.length() - 1);
|
||||
|
||||
CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
|
||||
ULONG typeId;
|
||||
if (FAILED(symbols->GetTypeId(self->m_module, typeName.c_str(), &typeId)))
|
||||
return NULL;
|
||||
return createType(self->m_module, typeId);
|
||||
}
|
||||
|
||||
PyObject *type_StripTypedef(Type *self)
|
||||
{
|
||||
Py_XINCREF(self);
|
||||
return (PyObject *)self;
|
||||
}
|
||||
|
||||
PyObject *type_Fields(Type *self)
|
||||
{
|
||||
CIDebugSymbols *symbols = ExtensionCommandContext::instance()->symbols();
|
||||
auto fields = PyList_New(0);
|
||||
for (ULONG fieldIndex = 0;; ++fieldIndex) {
|
||||
ULONG fieldNameSize = 0;
|
||||
symbols->GetFieldName(self->m_module, self->m_typeId, fieldIndex, NULL, 0, &fieldNameSize);
|
||||
if (fieldNameSize == 0)
|
||||
break;
|
||||
char *name = new char[fieldNameSize];
|
||||
if (FAILED(symbols->GetFieldName(self->m_module, self->m_typeId, fieldIndex, name,
|
||||
fieldNameSize, NULL))) {
|
||||
delete[] name;
|
||||
break;
|
||||
}
|
||||
|
||||
Field *field = PyObject_New(Field, field_pytype());
|
||||
if (field == NULL)
|
||||
return fields;
|
||||
initField(field);
|
||||
field->m_name = name;
|
||||
field->m_parentTypeId = self->m_typeId;
|
||||
field->m_module = self->m_module;
|
||||
PyList_Append(fields, reinterpret_cast<PyObject*>(field));
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
std::vector<std::string> innerTypesOf(const std::string &t)
|
||||
{
|
||||
std::vector<std::string> rc;
|
||||
|
||||
std::string::size_type pos = t.find('<');
|
||||
if (pos == std::string::npos)
|
||||
return rc;
|
||||
|
||||
rc.reserve(5);
|
||||
const std::string::size_type size = t.size();
|
||||
// Record all elements of level 1 to work correctly for
|
||||
// 'std::map<std::basic_string< .. > >'
|
||||
unsigned level = 0;
|
||||
std::string::size_type start = 0;
|
||||
for ( ; pos < size ; pos++) {
|
||||
const char c = t.at(pos);
|
||||
switch (c) {
|
||||
case '<':
|
||||
if (++level == 1)
|
||||
start = pos + 1;
|
||||
break;
|
||||
case '>':
|
||||
if (--level == 0) { // last element
|
||||
std::string innerType = t.substr(start, pos - start);
|
||||
trimFront(innerType);
|
||||
trimBack(innerType);
|
||||
rc.push_back(innerType);
|
||||
return rc;
|
||||
}
|
||||
break;
|
||||
case ',':
|
||||
if (level == 1) { // std::map<a, b>: start anew at ','.
|
||||
std::string innerType = t.substr(start, pos - start);
|
||||
trimFront(innerType);
|
||||
trimBack(innerType);
|
||||
rc.push_back(innerType);
|
||||
start = pos + 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
PyObject *type_TemplateArgument(Type *self, PyObject *args)
|
||||
{
|
||||
unsigned int index;
|
||||
bool numeric;
|
||||
if (!PyArg_ParseTuple(args, "Ib", &index, &numeric))
|
||||
return NULL;
|
||||
|
||||
std::vector<std::string> innerTypes = innerTypesOf(getTypeName(self));
|
||||
if (innerTypes.size() <= index)
|
||||
Py_RETURN_NONE;
|
||||
|
||||
const std::string &innerType = innerTypes.at(index);
|
||||
if (numeric) {
|
||||
try {
|
||||
return Py_BuildValue("i", std::stoi(innerType));
|
||||
}
|
||||
catch (std::invalid_argument) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return lookupType(innerType);
|
||||
}
|
||||
|
||||
PyObject *type_New(PyTypeObject *type, PyObject *, PyObject *)
|
||||
{
|
||||
Type *self = reinterpret_cast<Type *>(type->tp_alloc(type, 0));
|
||||
if (self != NULL) {
|
||||
self->m_name = nullptr;
|
||||
self->m_typeId = 0;
|
||||
self->m_module = 0;
|
||||
}
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
void type_Dealloc(Type *self)
|
||||
{
|
||||
delete[] self->m_name;
|
||||
}
|
||||
|
||||
PyObject *createType(ULONG64 module, ULONG typeId)
|
||||
{
|
||||
Type *type = PyObject_New(Type, type_pytype());
|
||||
type->m_module = module;
|
||||
type->m_typeId = typeId;
|
||||
type->m_name = nullptr;
|
||||
return reinterpret_cast<PyObject *>(type);
|
||||
}
|
||||
|
||||
static PyMethodDef typeMethods[] = {
|
||||
{"name", PyCFunction(type_Name), METH_NOARGS,
|
||||
"Return the type name"},
|
||||
{"bitsize", PyCFunction(type_bitSize), METH_NOARGS,
|
||||
"Return the size of the type in bits"},
|
||||
{"code", PyCFunction(type_Code), METH_NOARGS,
|
||||
"Return type code"},
|
||||
{"unqualified", PyCFunction(type_Unqualified), METH_NOARGS,
|
||||
"Type without const/volatile"},
|
||||
{"target", PyCFunction(type_Target), METH_NOARGS,
|
||||
"Type dereferenced if it is a pointer type, element if array etc"},
|
||||
{"stripTypedef", PyCFunction(type_StripTypedef), METH_NOARGS,
|
||||
"Type with typedefs removed"},
|
||||
{"fields", PyCFunction(type_Fields), METH_NOARGS,
|
||||
"List of fields (member and base classes) of this type"},
|
||||
|
||||
{"templateArgument", PyCFunction(type_TemplateArgument), METH_VARARGS,
|
||||
"Returns template argument at position"},
|
||||
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyMemberDef typeMembers[] = {
|
||||
{const_cast<char *>("id"), T_ULONG, offsetof(Type, m_typeId), 0,
|
||||
const_cast<char *>("type id")},
|
||||
{const_cast<char *>("moduleBase"), T_ULONGLONG, offsetof(Type, m_module), 0,
|
||||
const_cast<char *>("module base address")},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyTypeObject *type_pytype()
|
||||
{
|
||||
static PyTypeObject cdbext_TypeType = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"cdbext.Type", /* tp_name */
|
||||
sizeof(Type), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)type_Dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_as_async */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
"Type objects", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
typeMethods, /* tp_methods */
|
||||
typeMembers, /* tp_members (just for debugging)*/
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
type_New, /* tp_new */
|
||||
};
|
||||
|
||||
return &cdbext_TypeType;
|
||||
}
|
||||
47
src/libs/qtcreatorcdbext/pytype.h
Normal file
47
src/libs/qtcreatorcdbext/pytype.h
Normal file
@@ -0,0 +1,47 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "symbolgroup.h"
|
||||
|
||||
#include <Python.h>
|
||||
#include "structmember.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Type
|
||||
{
|
||||
PyObject_HEAD
|
||||
ULONG m_typeId;
|
||||
ULONG64 m_module;
|
||||
char *m_name; // owned
|
||||
};
|
||||
|
||||
PyTypeObject *type_pytype();
|
||||
char *getTypeName(ULONG64 module, ULONG typeId);
|
||||
|
||||
PyObject *lookupType(const std::string &typeName);
|
||||
PyObject *createType(ULONG64 module, ULONG typeId);
|
||||
372
src/libs/qtcreatorcdbext/pyvalue.cpp
Normal file
372
src/libs/qtcreatorcdbext/pyvalue.cpp
Normal file
@@ -0,0 +1,372 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "pyvalue.h"
|
||||
|
||||
#include "extensioncontext.h"
|
||||
#include "pycdbextmodule.h"
|
||||
#include "pytype.h"
|
||||
#include "pyfield.h"
|
||||
#include "stringutils.h"
|
||||
|
||||
std::string getSymbolName(CIDebugSymbolGroup *sg, ULONG index)
|
||||
{
|
||||
ULONG size = 0;
|
||||
sg->GetSymbolName(index, NULL, 0, &size);
|
||||
if (size == 0)
|
||||
return std::string();
|
||||
std::string name(size, '\0');
|
||||
sg->GetSymbolName(index, &name[0], size, &size);
|
||||
return name;
|
||||
}
|
||||
|
||||
PyObject *value_Name(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
const std::string &symbolName = getSymbolName(self->m_symbolGroup, self->m_index);
|
||||
if (symbolName.empty())
|
||||
Py_RETURN_NONE;
|
||||
return Py_BuildValue("s", symbolName.c_str());
|
||||
}
|
||||
|
||||
PyObject *value_Type(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
DEBUG_SYMBOL_PARAMETERS params;
|
||||
const HRESULT hr = self->m_symbolGroup->GetSymbolParameters(self->m_index, 1, ¶ms);
|
||||
if (FAILED(hr))
|
||||
return NULL;
|
||||
return createType(params.Module, params.TypeId);
|
||||
}
|
||||
|
||||
PyObject *value_AsBytes(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
ULONG64 address = 0;
|
||||
if (FAILED(self->m_symbolGroup->GetSymbolOffset(self->m_index, &address)))
|
||||
return NULL;
|
||||
ULONG size;
|
||||
if (FAILED(self->m_symbolGroup->GetSymbolSize(self->m_index, &size)))
|
||||
return NULL;
|
||||
|
||||
char *buffer = new char[size];
|
||||
auto data = ExtensionCommandContext::instance()->dataSpaces();
|
||||
ULONG received = 0;
|
||||
if (FAILED(data->ReadVirtual(address, buffer, size, &received)))
|
||||
return NULL;
|
||||
|
||||
return PyByteArray_FromStringAndSize(buffer, received);
|
||||
}
|
||||
|
||||
ULONG64 valueAddress(Value *value)
|
||||
{
|
||||
ULONG64 address = 0;
|
||||
if (value->m_symbolGroup)
|
||||
value->m_symbolGroup->GetSymbolOffset(value->m_index, &address);
|
||||
return address;
|
||||
}
|
||||
|
||||
PyObject *value_Address(Value *self)
|
||||
{
|
||||
const ULONG64 address = valueAddress(self);
|
||||
return address == 0 ? NULL : Py_BuildValue("K", address);
|
||||
}
|
||||
|
||||
bool expandValue(Value *v)
|
||||
{
|
||||
DEBUG_SYMBOL_PARAMETERS params;
|
||||
if (FAILED(v->m_symbolGroup->GetSymbolParameters(v->m_index, 1, ¶ms)))
|
||||
return false;
|
||||
if (params.Flags & DEBUG_SYMBOL_EXPANDED)
|
||||
return true;
|
||||
return SUCCEEDED(v->m_symbolGroup->ExpandSymbol(v->m_index, TRUE));
|
||||
}
|
||||
|
||||
ULONG numberOfChildren(Value *v)
|
||||
{
|
||||
DEBUG_SYMBOL_PARAMETERS params;
|
||||
HRESULT hr = v->m_symbolGroup->GetSymbolParameters(v->m_index, 1, ¶ms);
|
||||
return SUCCEEDED(hr) ? params.SubElements : 0;
|
||||
}
|
||||
|
||||
PyObject *value_Dereference(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
DEBUG_SYMBOL_PARAMETERS params;
|
||||
const HRESULT hr = self->m_symbolGroup->GetSymbolParameters(self->m_index, 1, ¶ms);
|
||||
if (FAILED(hr))
|
||||
return NULL;
|
||||
|
||||
char *name = getTypeName(params.Module, params.TypeId);
|
||||
|
||||
Value *ret = self;
|
||||
if (endsWith(std::string(name), "*")) {
|
||||
if (numberOfChildren(self) > 0 && expandValue(self)) {
|
||||
ULONG symbolCount = 0;
|
||||
self->m_symbolGroup->GetNumberSymbols(&symbolCount);
|
||||
if (symbolCount > self->m_index + 1) {
|
||||
ret = PyObject_New(Value, value_pytype());
|
||||
if (ret != NULL) {
|
||||
ret->m_index = self->m_index + 1;
|
||||
ret->m_symbolGroup = self->m_symbolGroup;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete[] name;
|
||||
return reinterpret_cast<PyObject*>(ret);
|
||||
}
|
||||
|
||||
PyObject *value_HasChildren(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
return pyBool(numberOfChildren(self) != 0);
|
||||
}
|
||||
|
||||
PyObject *value_Expand(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
return pyBool(expandValue(self));
|
||||
}
|
||||
|
||||
PyObject *value_NativeDebuggerValue(Value *self)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
ULONG size = 0;
|
||||
self->m_symbolGroup->GetSymbolValueText(self->m_index, NULL, 0, &size);
|
||||
char *name = new char[size];
|
||||
if (FAILED(self->m_symbolGroup->GetSymbolValueText(self->m_index, name, size, &size))) {
|
||||
delete[] name;
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
PyObject *ret = Py_BuildValue("s", name);
|
||||
delete[] name;
|
||||
return ret;
|
||||
}
|
||||
|
||||
PyObject *value_ChildFromName(Value *self, PyObject *args)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
char *name;
|
||||
if (!PyArg_ParseTuple(args, "s", &name))
|
||||
return NULL;
|
||||
|
||||
const ULONG childCount = numberOfChildren(self);
|
||||
if (childCount == 0 || !expandValue(self))
|
||||
Py_RETURN_NONE;
|
||||
|
||||
for (ULONG childIndex = self->m_index + 1 ; childIndex <= self->m_index + childCount; ++childIndex) {
|
||||
if (getSymbolName(self->m_symbolGroup, childIndex) == name) {
|
||||
Value *childValue = PyObject_New(Value, value_pytype());
|
||||
if (childValue != NULL) {
|
||||
childValue->m_index = childIndex;
|
||||
childValue->m_symbolGroup = self->m_symbolGroup;
|
||||
}
|
||||
return reinterpret_cast<PyObject*>(childValue);
|
||||
}
|
||||
}
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
PyObject *value_ChildFromField(Value *self, PyObject *args)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
Field *field;
|
||||
if (!PyArg_ParseTuple(args, "O", &field))
|
||||
return NULL;
|
||||
|
||||
if (!field->m_initialized && !initTypeAndOffset(field))
|
||||
return NULL;
|
||||
|
||||
ULONG64 address = valueAddress(self);
|
||||
if (address == 0)
|
||||
return NULL;
|
||||
address += field->m_offset;
|
||||
|
||||
auto symbols = ExtensionCommandContext::instance()->symbols();
|
||||
ULONG childTypeNameSize = 0;
|
||||
symbols->GetTypeName(field->m_module, field->m_typeId, NULL, 0, &childTypeNameSize);
|
||||
std::string childTypeName(childTypeNameSize, '\0');
|
||||
symbols->GetTypeName(field->m_module, field->m_typeId, &childTypeName[0],
|
||||
childTypeNameSize, &childTypeNameSize);
|
||||
|
||||
if (childTypeName.empty())
|
||||
return NULL;
|
||||
|
||||
std::string name = pointedToSymbolName(address, childTypeName);
|
||||
ULONG index = DEBUG_ANY_ID;
|
||||
if (FAILED(self->m_symbolGroup->AddSymbol(name.c_str(), &index)))
|
||||
return NULL;
|
||||
|
||||
Value *childValue = PyObject_New(Value, value_pytype());
|
||||
if (childValue != NULL) {
|
||||
childValue->m_index = index;
|
||||
childValue->m_symbolGroup = self->m_symbolGroup;
|
||||
}
|
||||
return reinterpret_cast<PyObject*>(childValue);
|
||||
}
|
||||
|
||||
PyObject *value_ChildFromIndex(Value *self, PyObject *args)
|
||||
{
|
||||
if (!self->m_symbolGroup)
|
||||
return NULL;
|
||||
unsigned int index;
|
||||
if (!PyArg_ParseTuple(args, "I", &index))
|
||||
return NULL;
|
||||
|
||||
if (index < 0)
|
||||
return NULL;
|
||||
|
||||
const ULONG childCount = numberOfChildren(self);
|
||||
if (childCount <= index || !expandValue(self))
|
||||
Py_RETURN_NONE;
|
||||
|
||||
Value *childValue = PyObject_New(Value, value_pytype());
|
||||
if (childValue != NULL) {
|
||||
childValue->m_index = self->m_index + index + 1;
|
||||
childValue->m_symbolGroup = self->m_symbolGroup;
|
||||
}
|
||||
|
||||
return reinterpret_cast<PyObject*>(childValue);
|
||||
}
|
||||
|
||||
void value_Dealloc(Value *)
|
||||
{ }
|
||||
|
||||
PyObject *value_New(PyTypeObject *type, PyObject *, PyObject *)
|
||||
{
|
||||
Value *self = reinterpret_cast<Value *>(type->tp_alloc(type, 0));
|
||||
if (self != NULL)
|
||||
initValue(self);
|
||||
return reinterpret_cast<PyObject *>(self);
|
||||
}
|
||||
|
||||
void initValue(Value *value)
|
||||
{
|
||||
value->m_index = 0;
|
||||
value->m_symbolGroup = nullptr;
|
||||
}
|
||||
|
||||
static PyMethodDef valueMethods[] = {
|
||||
{"name", PyCFunction(value_Name), METH_NOARGS,
|
||||
"Name of this thing or None"},
|
||||
{"type", PyCFunction(value_Type), METH_NOARGS,
|
||||
"Type of this value"},
|
||||
{"asBytes", PyCFunction(value_AsBytes), METH_NOARGS,
|
||||
"Memory contents of this object, or None"},
|
||||
{"address", PyCFunction(value_Address), METH_NOARGS,
|
||||
"Address of this object, or None"},
|
||||
{"dereference", PyCFunction(value_Dereference), METH_NOARGS,
|
||||
"Dereference if value is pointer"},
|
||||
{"hasChildren", PyCFunction(value_HasChildren), METH_NOARGS,
|
||||
"Whether this object has subobjects"},
|
||||
{"expand", PyCFunction(value_Expand), METH_NOARGS,
|
||||
"Make sure that children are accessible."},
|
||||
{"nativeDebuggerValue", PyCFunction(value_NativeDebuggerValue), METH_NOARGS,
|
||||
"Value string returned by the debugger"},
|
||||
|
||||
{"childFromName", PyCFunction(value_ChildFromName), METH_VARARGS,
|
||||
"Return the name of this value"},
|
||||
{"childFromField", PyCFunction(value_ChildFromField), METH_VARARGS,
|
||||
"Return the name of this value"},
|
||||
{"childFromIndex", PyCFunction(value_ChildFromIndex), METH_VARARGS,
|
||||
"Return the name of this value"},
|
||||
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
static PyMemberDef valueMembers[] = {
|
||||
{const_cast<char *>("index"), T_ULONG, offsetof(Value, m_index), 0,
|
||||
const_cast<char *>("value index in symbolgroup")},
|
||||
{NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
PyTypeObject *value_pytype()
|
||||
{
|
||||
static PyTypeObject cdbext_ValueType =
|
||||
{
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"cdbext.Value", /* tp_name */
|
||||
sizeof(Value), /* tp_basicsize */
|
||||
0, /* tp_itemsize */
|
||||
(destructor)value_Dealloc, /* tp_dealloc */
|
||||
0, /* tp_print */
|
||||
0, /* tp_getattr */
|
||||
0, /* tp_setattr */
|
||||
0, /* tp_reserved */
|
||||
0, /* tp_repr */
|
||||
0, /* tp_as_number */
|
||||
0, /* tp_as_sequence */
|
||||
0, /* tp_as_mapping */
|
||||
0, /* tp_hash */
|
||||
0, /* tp_call */
|
||||
0, /* tp_str */
|
||||
0, /* tp_getattro */
|
||||
0, /* tp_setattro */
|
||||
0, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
"Value objects", /* tp_doc */
|
||||
0, /* tp_traverse */
|
||||
0, /* tp_clear */
|
||||
0, /* tp_richcompare */
|
||||
0, /* tp_weaklistoffset */
|
||||
0, /* tp_iter */
|
||||
0, /* tp_iternext */
|
||||
valueMethods, /* tp_methods */
|
||||
valueMembers, /* tp_members (just for debugging)*/
|
||||
0, /* tp_getset */
|
||||
0, /* tp_base */
|
||||
0, /* tp_dict */
|
||||
0, /* tp_descr_get */
|
||||
0, /* tp_descr_set */
|
||||
0, /* tp_dictoffset */
|
||||
0, /* tp_init */
|
||||
0, /* tp_alloc */
|
||||
value_New, /* tp_new */
|
||||
};
|
||||
|
||||
return &cdbext_ValueType;
|
||||
}
|
||||
42
src/libs/qtcreatorcdbext/pyvalue.h
Normal file
42
src/libs/qtcreatorcdbext/pyvalue.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "symbolgroupnode.h"
|
||||
#include "symbolgroup.h"
|
||||
|
||||
#include <Python.h>
|
||||
#include "structmember.h"
|
||||
|
||||
struct Value
|
||||
{
|
||||
PyObject_HEAD
|
||||
ULONG m_index;
|
||||
CIDebugSymbolGroup *m_symbolGroup; // not owned
|
||||
};
|
||||
|
||||
PyTypeObject *value_pytype();
|
||||
void initValue(Value *value);
|
||||
@@ -103,6 +103,20 @@ exists($$PYTHON_INSTALL_DIR) {
|
||||
INCLUDEPATH += $$PYTHON_INSTALL_DIR/include
|
||||
DEPENDPATH += $$PYTHON_INSTALL_DIR/include
|
||||
|
||||
SOURCES += \
|
||||
pycdbextmodule.cpp \
|
||||
pyfield.cpp \
|
||||
pystdoutredirect.cpp \
|
||||
pytype.cpp \
|
||||
pyvalue.cpp
|
||||
|
||||
HEADERS += \
|
||||
pycdbextmodule.h \
|
||||
pyfield.h \
|
||||
pystdoutredirect.h \
|
||||
pytype.h \
|
||||
pyvalue.h
|
||||
|
||||
#TODO: parse version number for a generic approach
|
||||
CONFIG(release, debug|release): LIBS += -L$$PYTHON_INSTALL_DIR/libs -lpython35
|
||||
else:CONFIG(debug, debug|release): LIBS += -L$$PYTHON_INSTALL_DIR/libs -lpython35_d
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
#include <Python.h>
|
||||
#include "pystdoutredirect.h"
|
||||
#endif
|
||||
|
||||
#include <cstdio>
|
||||
@@ -579,26 +580,17 @@ extern "C" HRESULT CALLBACK script(CIDebugClient *client, PCSTR argsIn)
|
||||
for (std::string arg : commandTokens<StringList>(argsIn, &token))
|
||||
command << arg << ' ';
|
||||
|
||||
if (PyRun_SimpleString(command.str().c_str()) == 0) {
|
||||
ExtensionContext::instance().reportLong('R', token, "script", "");
|
||||
} else {
|
||||
ExtensionContext::instance().report('N', token, 0, "script",
|
||||
"Error while executing Python code.");
|
||||
}
|
||||
|
||||
_Py_IDENTIFIER(stdout);
|
||||
_Py_IDENTIFIER(flush);
|
||||
|
||||
PyObject *fout = _PySys_GetObjectId(&PyId_stdout);
|
||||
PyObject *tmp;
|
||||
|
||||
if (fout != NULL && fout != Py_None) {
|
||||
tmp = _PyObject_CallMethodId(fout, &PyId_flush, "");
|
||||
if (tmp == NULL)
|
||||
PyErr_WriteUnraisable(fout);
|
||||
else
|
||||
Py_DECREF(tmp);
|
||||
}
|
||||
PyObject *ptype = NULL;
|
||||
PyObject *pvalue = NULL;
|
||||
PyObject *ptraceback = NULL;
|
||||
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
|
||||
startCapturePyStdout();
|
||||
const char result = (PyRun_SimpleString(command.str().c_str()) == 0) ? 'R' : 'N';
|
||||
if (PyErr_Occurred())
|
||||
PyErr_Print();
|
||||
ExtensionContext::instance().reportLong(result, token, "script", getPyStdout().c_str());
|
||||
endCapturePyStdout();
|
||||
PyErr_Restore(ptype, pvalue, ptraceback);
|
||||
#else
|
||||
commandTokens<StringList>(argsIn, &token);
|
||||
ExtensionContext::instance().report('N', token, 0, "script",
|
||||
|
||||
@@ -657,6 +657,8 @@ void CdbEngine::setupInferior()
|
||||
+ " maxStackDepth="
|
||||
+ action(MaximalStackDepth)->value().toString(), NoFlags});
|
||||
|
||||
runCommand({"print(sys.version)", ScriptCommand, CB(setupScripting)});
|
||||
|
||||
runCommand({"pid", ExtensionCommand, [this](const DebuggerResponse &response) {
|
||||
// Fails for core dumps.
|
||||
if (response.resultClass == ResultDone)
|
||||
@@ -1162,6 +1164,14 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
|
||||
str << ' ' << dbgCmd.argsToString();
|
||||
cmd = fullCmd.arg("", "");
|
||||
fullCmd = fullCmd.arg(" -t ").arg(token);
|
||||
} else if (dbgCmd.flags == ScriptCommand) {
|
||||
// Add extension prefix and quotes the script command
|
||||
// pass along token for identification in hash.
|
||||
str << m_extensionCommandPrefix + "script %1%2 " << dbgCmd.function;
|
||||
if (!dbgCmd.args.isNull())
|
||||
str << '(' << dbgCmd.argsToPython() << ')';
|
||||
cmd = fullCmd.arg("", "");
|
||||
fullCmd = fullCmd.arg(" -t ").arg(token);
|
||||
}
|
||||
m_commandForToken.insert(token, dbgCmd);
|
||||
}
|
||||
@@ -1204,6 +1214,44 @@ void CdbEngine::activateFrame(int index)
|
||||
|
||||
void CdbEngine::doUpdateLocals(const UpdateParameters &updateParameters)
|
||||
{
|
||||
if (m_pythonVersion > 0x030000) {
|
||||
watchHandler()->notifyUpdateStarted(updateParameters.partialVariables());
|
||||
|
||||
DebuggerCommand cmd("theDumper.fetchVariables", ScriptCommand);
|
||||
watchHandler()->appendFormatRequests(&cmd);
|
||||
watchHandler()->appendWatchersAndTooltipRequests(&cmd);
|
||||
|
||||
const static bool alwaysVerbose = !qgetenv("QTC_DEBUGGER_PYTHON_VERBOSE").isEmpty();
|
||||
cmd.arg("passexceptions", alwaysVerbose);
|
||||
cmd.arg("fancy", boolSetting(UseDebuggingHelpers));
|
||||
cmd.arg("autoderef", boolSetting(AutoDerefPointers));
|
||||
cmd.arg("dyntype", boolSetting(UseDynamicType));
|
||||
cmd.arg("partialvar", updateParameters.partialVariable);
|
||||
cmd.arg("qobjectnames", boolSetting(ShowQObjectNames));
|
||||
|
||||
StackFrame frame = stackHandler()->currentFrame();
|
||||
cmd.arg("context", frame.context);
|
||||
cmd.arg("nativemixed", isNativeMixedActive());
|
||||
|
||||
cmd.arg("stringcutoff", action(MaximalStringLength)->value().toString());
|
||||
cmd.arg("displaystringlimit", action(DisplayStringLimit)->value().toString());
|
||||
|
||||
//cmd.arg("resultvarname", m_resultVarName);
|
||||
cmd.arg("partialvar", updateParameters.partialVariable);
|
||||
|
||||
cmd.callback = [this](const DebuggerResponse &response) {
|
||||
if (response.resultClass == ResultDone) {
|
||||
showMessage(response.data.toString(), LogMisc);
|
||||
updateLocalsView(response.data);
|
||||
} else {
|
||||
showMessage(response.data["msg"].data(), LogError);
|
||||
}
|
||||
watchHandler()->notifyUpdateFinished();
|
||||
};
|
||||
|
||||
runCommand(cmd);
|
||||
} else {
|
||||
|
||||
typedef QHash<QString, int> WatcherHash;
|
||||
|
||||
const bool partialUpdate = !updateParameters.partialVariable.isEmpty();
|
||||
@@ -1296,6 +1344,7 @@ void CdbEngine::doUpdateLocals(const UpdateParameters &updateParameters)
|
||||
cmd.callback = [this, partialUpdate](const DebuggerResponse &r) { handleLocals(r, partialUpdate); };
|
||||
runCommand(cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void CdbEngine::updateAll()
|
||||
{
|
||||
@@ -2192,8 +2241,11 @@ void CdbEngine::handleExtensionMessage(char t, int token, const QString &what, c
|
||||
qDebug("### Completed extension command '%s' for token=%d, pending=%d",
|
||||
qPrintable(command.function), token, m_commandForToken.size());
|
||||
|
||||
if (!command.callback)
|
||||
if (!command.callback) {
|
||||
if (!message.isEmpty()) // log unhandled output
|
||||
showMessage(message, LogMisc);
|
||||
return;
|
||||
}
|
||||
DebuggerResponse response;
|
||||
response.data.m_name = "data";
|
||||
if (t == 'R') {
|
||||
@@ -2847,6 +2899,46 @@ void CdbEngine::handleAdditionalQmlStack(const DebuggerResponse &response)
|
||||
showMessage("Unable to obtain QML stack trace: " + errorMessage, LogError);
|
||||
}
|
||||
|
||||
void CdbEngine::setupScripting(const DebuggerResponse &response)
|
||||
{
|
||||
GdbMi data = response.data;
|
||||
if (response.resultClass != ResultDone) {
|
||||
showMessage(data["msg"].data(), LogMisc);
|
||||
return;
|
||||
}
|
||||
const QString &verOutput = data.data();
|
||||
const QStringList pythonVersion = verOutput.split(' ').first().split('.');
|
||||
|
||||
bool ok = false;
|
||||
if (pythonVersion.size() == 3) {
|
||||
m_pythonVersion |= pythonVersion[0].toInt(&ok);
|
||||
if (ok) {
|
||||
m_pythonVersion = m_pythonVersion << 8;
|
||||
m_pythonVersion |= pythonVersion[1].toInt(&ok);
|
||||
if (ok) {
|
||||
m_pythonVersion = m_pythonVersion << 8;
|
||||
m_pythonVersion |= pythonVersion[2].toInt(&ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ok) {
|
||||
m_pythonVersion = 0;
|
||||
showMessage(QString("Can not parse sys.version:\n%1").arg(verOutput), LogWarning);
|
||||
return;
|
||||
}
|
||||
|
||||
QString dumperPath = QDir::toNativeSeparators(Core::ICore::resourcePath() + "/debugger");
|
||||
dumperPath.replace('\\', "\\\\");
|
||||
runCommand({"sys.path.insert(1, '" + dumperPath + "')", ScriptCommand});
|
||||
runCommand({"from cdbbridge import Dumper", ScriptCommand});
|
||||
runCommand({"print(dir())", ScriptCommand});
|
||||
runCommand({"theDumper = Dumper()", ScriptCommand});
|
||||
runCommand({"theDumper.loadDumpers(None)", ScriptCommand,
|
||||
[this](const DebuggerResponse &response) {
|
||||
watchHandler()->addDumpers(response.data["dumpers"]);
|
||||
}});
|
||||
}
|
||||
|
||||
void CdbEngine::mergeStartParametersSourcePathMap()
|
||||
{
|
||||
const DebuggerRunParameters &rp = runParameters();
|
||||
|
||||
@@ -158,6 +158,7 @@ private:
|
||||
NoFlags = 0,
|
||||
BuiltinCommand,
|
||||
ExtensionCommand,
|
||||
ScriptCommand
|
||||
};
|
||||
|
||||
bool startConsole(const DebuggerRunParameters &sp, QString *errorMessage);
|
||||
@@ -210,6 +211,7 @@ private:
|
||||
void handleWidgetAt(const DebuggerResponse &response);
|
||||
void handleBreakPoints(const DebuggerResponse &response);
|
||||
void handleAdditionalQmlStack(const DebuggerResponse &response);
|
||||
void setupScripting(const DebuggerResponse &response);
|
||||
NormalizedSourceFileName sourceMapNormalizeFileNameFromDebugger(const QString &f);
|
||||
void doUpdateLocals(const UpdateParameters ¶ms) override;
|
||||
void updateAll() override;
|
||||
@@ -258,6 +260,7 @@ private:
|
||||
QVariantList m_customSpecialStopData;
|
||||
QList<SourcePathMapping> m_sourcePathMappings;
|
||||
QScopedPointer<GdbMi> m_coreStopReason;
|
||||
int m_pythonVersion = 0; // 0xMMmmpp MM = major; mm = minor; pp = patch
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
Reference in New Issue
Block a user