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,
|
dereference() -> Value # Dereference if value is pointer,
|
||||||
# remove reference if value is reference.
|
# remove reference if value is reference.
|
||||||
hasChildren() -> bool # Whether this object has subobjects.
|
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)
|
childFromName(string name) -> Value # (optional)
|
||||||
childFromField(Field field) -> Value # (optional)
|
childFromField(Field field) -> Value # (optional)
|
||||||
@@ -122,6 +124,7 @@ class Field:
|
|||||||
parseAndEvaluate(string: expr) -> Value # or None if not possible.
|
parseAndEvaluate(string: expr) -> Value # or None if not possible.
|
||||||
lookupType(string: name) -> Type # or None if not possible.
|
lookupType(string: name) -> Type # or None if not possible.
|
||||||
listOfLocals() -> [ Value ] # List of items currently in scope.
|
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)
|
= range(0, 14)
|
||||||
|
|
||||||
|
|
||||||
# Internal codes for types
|
# Internal codes for types keep in sync with cdbextensions pytype.cpp
|
||||||
TypeCodeTypedef, \
|
TypeCodeTypedef, \
|
||||||
TypeCodeStruct, \
|
TypeCodeStruct, \
|
||||||
TypeCodeVoid, \
|
TypeCodeVoid, \
|
||||||
|
|||||||
@@ -680,6 +680,9 @@ class Dumper(DumperBase):
|
|||||||
# We get i686-w64-mingw32
|
# We get i686-w64-mingw32
|
||||||
return 'mingw' in gdb.TARGET_CONFIG.lower()
|
return 'mingw' in gdb.TARGET_CONFIG.lower()
|
||||||
|
|
||||||
|
def isMsvcTarget(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def qtVersionString(self):
|
def qtVersionString(self):
|
||||||
try:
|
try:
|
||||||
return str(gdb.lookup_symbol("qVersion")[0].value()())
|
return str(gdb.lookup_symbol("qVersion")[0].value()())
|
||||||
|
|||||||
@@ -474,6 +474,9 @@ class Dumper(DumperBase):
|
|||||||
def isArmArchitecture(self):
|
def isArmArchitecture(self):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def isMsvcTarget(self):
|
||||||
|
return False
|
||||||
|
|
||||||
def qtVersionAndNamespace(self):
|
def qtVersionAndNamespace(self):
|
||||||
for func in self.target.FindFunctions('qVersion'):
|
for func in self.target.FindFunctions('qVersion'):
|
||||||
name = func.GetSymbol().GetName()
|
name = func.GetSymbol().GetName()
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
|
|
||||||
#ifdef WITH_PYTHON
|
#ifdef WITH_PYTHON
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
#include "pycdbextmodule.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// wdbgexts.h declares 'extern WINDBG_EXTENSION_APIS ExtensionApis;'
|
// wdbgexts.h declares 'extern WINDBG_EXTENSION_APIS ExtensionApis;'
|
||||||
@@ -160,7 +161,10 @@ HRESULT ExtensionContext::initialize(PULONG Version, PULONG Flags)
|
|||||||
*Flags = 0;
|
*Flags = 0;
|
||||||
|
|
||||||
#ifdef WITH_PYTHON
|
#ifdef WITH_PYTHON
|
||||||
|
initCdbextPythonModule();
|
||||||
Py_Initialize();
|
Py_Initialize();
|
||||||
|
PyRun_SimpleString("import cdbext");
|
||||||
|
PyRun_SimpleString("import sys");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
IInterfacePointer<CIDebugClient> client;
|
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
|
INCLUDEPATH += $$PYTHON_INSTALL_DIR/include
|
||||||
DEPENDPATH += $$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
|
#TODO: parse version number for a generic approach
|
||||||
CONFIG(release, debug|release): LIBS += -L$$PYTHON_INSTALL_DIR/libs -lpython35
|
CONFIG(release, debug|release): LIBS += -L$$PYTHON_INSTALL_DIR/libs -lpython35
|
||||||
else:CONFIG(debug, debug|release): LIBS += -L$$PYTHON_INSTALL_DIR/libs -lpython35_d
|
else:CONFIG(debug, debug|release): LIBS += -L$$PYTHON_INSTALL_DIR/libs -lpython35_d
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
|
|
||||||
#ifdef WITH_PYTHON
|
#ifdef WITH_PYTHON
|
||||||
#include <Python.h>
|
#include <Python.h>
|
||||||
|
#include "pystdoutredirect.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@@ -579,26 +580,17 @@ extern "C" HRESULT CALLBACK script(CIDebugClient *client, PCSTR argsIn)
|
|||||||
for (std::string arg : commandTokens<StringList>(argsIn, &token))
|
for (std::string arg : commandTokens<StringList>(argsIn, &token))
|
||||||
command << arg << ' ';
|
command << arg << ' ';
|
||||||
|
|
||||||
if (PyRun_SimpleString(command.str().c_str()) == 0) {
|
PyObject *ptype = NULL;
|
||||||
ExtensionContext::instance().reportLong('R', token, "script", "");
|
PyObject *pvalue = NULL;
|
||||||
} else {
|
PyObject *ptraceback = NULL;
|
||||||
ExtensionContext::instance().report('N', token, 0, "script",
|
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
|
||||||
"Error while executing Python code.");
|
startCapturePyStdout();
|
||||||
}
|
const char result = (PyRun_SimpleString(command.str().c_str()) == 0) ? 'R' : 'N';
|
||||||
|
if (PyErr_Occurred())
|
||||||
_Py_IDENTIFIER(stdout);
|
PyErr_Print();
|
||||||
_Py_IDENTIFIER(flush);
|
ExtensionContext::instance().reportLong(result, token, "script", getPyStdout().c_str());
|
||||||
|
endCapturePyStdout();
|
||||||
PyObject *fout = _PySys_GetObjectId(&PyId_stdout);
|
PyErr_Restore(ptype, pvalue, ptraceback);
|
||||||
PyObject *tmp;
|
|
||||||
|
|
||||||
if (fout != NULL && fout != Py_None) {
|
|
||||||
tmp = _PyObject_CallMethodId(fout, &PyId_flush, "");
|
|
||||||
if (tmp == NULL)
|
|
||||||
PyErr_WriteUnraisable(fout);
|
|
||||||
else
|
|
||||||
Py_DECREF(tmp);
|
|
||||||
}
|
|
||||||
#else
|
#else
|
||||||
commandTokens<StringList>(argsIn, &token);
|
commandTokens<StringList>(argsIn, &token);
|
||||||
ExtensionContext::instance().report('N', token, 0, "script",
|
ExtensionContext::instance().report('N', token, 0, "script",
|
||||||
|
|||||||
@@ -657,6 +657,8 @@ void CdbEngine::setupInferior()
|
|||||||
+ " maxStackDepth="
|
+ " maxStackDepth="
|
||||||
+ action(MaximalStackDepth)->value().toString(), NoFlags});
|
+ action(MaximalStackDepth)->value().toString(), NoFlags});
|
||||||
|
|
||||||
|
runCommand({"print(sys.version)", ScriptCommand, CB(setupScripting)});
|
||||||
|
|
||||||
runCommand({"pid", ExtensionCommand, [this](const DebuggerResponse &response) {
|
runCommand({"pid", ExtensionCommand, [this](const DebuggerResponse &response) {
|
||||||
// Fails for core dumps.
|
// Fails for core dumps.
|
||||||
if (response.resultClass == ResultDone)
|
if (response.resultClass == ResultDone)
|
||||||
@@ -1162,6 +1164,14 @@ void CdbEngine::runCommand(const DebuggerCommand &dbgCmd)
|
|||||||
str << ' ' << dbgCmd.argsToString();
|
str << ' ' << dbgCmd.argsToString();
|
||||||
cmd = fullCmd.arg("", "");
|
cmd = fullCmd.arg("", "");
|
||||||
fullCmd = fullCmd.arg(" -t ").arg(token);
|
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);
|
m_commandForToken.insert(token, dbgCmd);
|
||||||
}
|
}
|
||||||
@@ -1204,6 +1214,44 @@ void CdbEngine::activateFrame(int index)
|
|||||||
|
|
||||||
void CdbEngine::doUpdateLocals(const UpdateParameters &updateParameters)
|
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;
|
typedef QHash<QString, int> WatcherHash;
|
||||||
|
|
||||||
const bool partialUpdate = !updateParameters.partialVariable.isEmpty();
|
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); };
|
cmd.callback = [this, partialUpdate](const DebuggerResponse &r) { handleLocals(r, partialUpdate); };
|
||||||
runCommand(cmd);
|
runCommand(cmd);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CdbEngine::updateAll()
|
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",
|
qDebug("### Completed extension command '%s' for token=%d, pending=%d",
|
||||||
qPrintable(command.function), token, m_commandForToken.size());
|
qPrintable(command.function), token, m_commandForToken.size());
|
||||||
|
|
||||||
if (!command.callback)
|
if (!command.callback) {
|
||||||
|
if (!message.isEmpty()) // log unhandled output
|
||||||
|
showMessage(message, LogMisc);
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
DebuggerResponse response;
|
DebuggerResponse response;
|
||||||
response.data.m_name = "data";
|
response.data.m_name = "data";
|
||||||
if (t == 'R') {
|
if (t == 'R') {
|
||||||
@@ -2847,6 +2899,46 @@ void CdbEngine::handleAdditionalQmlStack(const DebuggerResponse &response)
|
|||||||
showMessage("Unable to obtain QML stack trace: " + errorMessage, LogError);
|
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()
|
void CdbEngine::mergeStartParametersSourcePathMap()
|
||||||
{
|
{
|
||||||
const DebuggerRunParameters &rp = runParameters();
|
const DebuggerRunParameters &rp = runParameters();
|
||||||
|
|||||||
@@ -158,6 +158,7 @@ private:
|
|||||||
NoFlags = 0,
|
NoFlags = 0,
|
||||||
BuiltinCommand,
|
BuiltinCommand,
|
||||||
ExtensionCommand,
|
ExtensionCommand,
|
||||||
|
ScriptCommand
|
||||||
};
|
};
|
||||||
|
|
||||||
bool startConsole(const DebuggerRunParameters &sp, QString *errorMessage);
|
bool startConsole(const DebuggerRunParameters &sp, QString *errorMessage);
|
||||||
@@ -210,6 +211,7 @@ private:
|
|||||||
void handleWidgetAt(const DebuggerResponse &response);
|
void handleWidgetAt(const DebuggerResponse &response);
|
||||||
void handleBreakPoints(const DebuggerResponse &response);
|
void handleBreakPoints(const DebuggerResponse &response);
|
||||||
void handleAdditionalQmlStack(const DebuggerResponse &response);
|
void handleAdditionalQmlStack(const DebuggerResponse &response);
|
||||||
|
void setupScripting(const DebuggerResponse &response);
|
||||||
NormalizedSourceFileName sourceMapNormalizeFileNameFromDebugger(const QString &f);
|
NormalizedSourceFileName sourceMapNormalizeFileNameFromDebugger(const QString &f);
|
||||||
void doUpdateLocals(const UpdateParameters ¶ms) override;
|
void doUpdateLocals(const UpdateParameters ¶ms) override;
|
||||||
void updateAll() override;
|
void updateAll() override;
|
||||||
@@ -258,6 +260,7 @@ private:
|
|||||||
QVariantList m_customSpecialStopData;
|
QVariantList m_customSpecialStopData;
|
||||||
QList<SourcePathMapping> m_sourcePathMappings;
|
QList<SourcePathMapping> m_sourcePathMappings;
|
||||||
QScopedPointer<GdbMi> m_coreStopReason;
|
QScopedPointer<GdbMi> m_coreStopReason;
|
||||||
|
int m_pythonVersion = 0; // 0xMMmmpp MM = major; mm = minor; pp = patch
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
|
|||||||
Reference in New Issue
Block a user