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:
David Schulz
2016-10-06 13:36:02 +02:00
parent c05fd099e9
commit a83d539551
20 changed files with 1853 additions and 100 deletions

View File

@@ -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

View 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.

View File

@@ -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, \

View File

@@ -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()())

View File

@@ -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()

View File

@@ -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;

View 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;
}

View 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.
*/

View 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;
}

View 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);

View 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;
}

View 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();

View 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;
}

View 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);

View 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, &params);
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, &params)))
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, &params);
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, &params);
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;
}

View 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);

View File

@@ -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

View File

@@ -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",

View File

@@ -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();

View File

@@ -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 &params) 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