mirror of
https://github.com/microsoft/debugpy.git
synced 2025-12-23 08:48:12 +00:00
This commit is contained in:
parent
81ac055f3f
commit
152edebd4e
17 changed files with 365 additions and 88 deletions
|
|
@ -103,6 +103,7 @@ else:
|
|||
IS_PY3K = False
|
||||
IS_PY34_OR_GREATER = False
|
||||
IS_PY36_OR_GREATER = False
|
||||
IS_PY37_OR_GREATER = False
|
||||
IS_PY2 = True
|
||||
IS_PY27 = False
|
||||
IS_PY24 = False
|
||||
|
|
@ -112,6 +113,7 @@ try:
|
|||
IS_PY2 = False
|
||||
IS_PY34_OR_GREATER = sys.version_info >= (3, 4)
|
||||
IS_PY36_OR_GREATER = sys.version_info >= (3, 6)
|
||||
IS_PY37_OR_GREATER = sys.version_info >= (3, 7)
|
||||
elif sys.version_info[0] == 2 and sys.version_info[1] == 7:
|
||||
IS_PY27 = True
|
||||
elif sys.version_info[0] == 2 and sys.version_info[1] == 4:
|
||||
|
|
|
|||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -4,6 +4,7 @@
|
|||
#include "ref_utils.hpp"
|
||||
#include "py_utils.hpp"
|
||||
#include "python.h"
|
||||
#include "py_settrace_37.hpp"
|
||||
#include <unordered_set>
|
||||
|
||||
|
||||
|
|
@ -36,7 +37,14 @@ DWORD GetPythonThreadId(PythonVersion version, PyThreadState* curThread) {
|
|||
/**
|
||||
* This function may be called to set a tracing function to existing python threads.
|
||||
*/
|
||||
int InternalSetSysTraceFunc(MODULE_TYPE module, bool isDebug, bool showDebugInfo, PyObjectHolder* traceFunc, PyObjectHolder* setTraceFunc, unsigned int threadId)
|
||||
int InternalSetSysTraceFunc(
|
||||
MODULE_TYPE module,
|
||||
bool isDebug,
|
||||
bool showDebugInfo,
|
||||
PyObjectHolder* traceFunc,
|
||||
PyObjectHolder* setTraceFunc,
|
||||
unsigned int threadId,
|
||||
PyObjectHolder* pyNone)
|
||||
{
|
||||
|
||||
if(showDebugInfo){
|
||||
|
|
@ -71,15 +79,8 @@ int InternalSetSysTraceFunc(MODULE_TYPE module, bool isDebug, bool showDebugInfo
|
|||
intFromLong = intFromLongPy2;
|
||||
}
|
||||
|
||||
DEFINE_PROC(errOccurred, PyErr_Occurred*, "PyErr_Occurred", 210);
|
||||
DEFINE_PROC(pyErrFetch, PyErr_Fetch*, "PyErr_Fetch", 220);
|
||||
DEFINE_PROC(pyErrRestore, PyErr_Restore*, "PyErr_Restore", 230);
|
||||
DEFINE_PROC(pyImportMod, PyImport_ImportModule*, "PyImport_ImportModule", 240);
|
||||
DEFINE_PROC(pyGetAttr, PyObject_GetAttrString*, "PyObject_GetAttrString", 250);
|
||||
DEFINE_PROC(pyHasAttr, PyObject_HasAttrString*, "PyObject_HasAttrString", 260);
|
||||
DEFINE_PROC(getThreadTls, PyThread_get_key_value*, "PyThread_get_key_value", 270);
|
||||
DEFINE_PROC(setThreadTls, PyThread_set_key_value*, "PyThread_set_key_value", 280);
|
||||
DEFINE_PROC(delThreadTls, PyThread_delete_key_value*, "PyThread_delete_key_value", 290);
|
||||
DEFINE_PROC_NO_CHECK(PyCFrame_Type, PyTypeObject*, "PyCFrame_Type", 300); // optional
|
||||
|
||||
DEFINE_PROC_NO_CHECK(curPythonThread, PyThreadState**, "_PyThreadState_Current", 310); // optional
|
||||
|
|
@ -111,67 +112,117 @@ int InternalSetSysTraceFunc(MODULE_TYPE module, bool isDebug, bool showDebugInfo
|
|||
}
|
||||
|
||||
|
||||
int threadStateIndex = -1;
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
void* value = getThreadTls(i);
|
||||
if (value == curPyThread) {
|
||||
threadStateIndex = i;
|
||||
if (version < PythonVersion_37)
|
||||
{
|
||||
DEFINE_PROC(errOccurred, PyErr_Occurred*, "PyErr_Occurred", 210);
|
||||
DEFINE_PROC(pyErrFetch, PyErr_Fetch*, "PyErr_Fetch", 220);
|
||||
DEFINE_PROC(pyErrRestore, PyErr_Restore*, "PyErr_Restore", 230);
|
||||
DEFINE_PROC(getThreadTls, PyThread_get_key_value*, "PyThread_get_key_value", 270);
|
||||
DEFINE_PROC(setThreadTls, PyThread_set_key_value*, "PyThread_set_key_value", 280);
|
||||
DEFINE_PROC(delThreadTls, PyThread_delete_key_value*, "PyThread_delete_key_value", 290);
|
||||
int threadStateIndex = -1;
|
||||
for (int i = 0; i < 100000; i++) {
|
||||
void* value = getThreadTls(i);
|
||||
if (value == curPyThread) {
|
||||
threadStateIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(threadStateIndex == -1){
|
||||
printf("Unable to find threadStateIndex for the current thread. curPyThread: %p\n", curPyThread);
|
||||
return 350;
|
||||
}
|
||||
|
||||
|
||||
bool found = false;
|
||||
for (auto curThread = threadHead(head); curThread != nullptr; curThread = threadNext(curThread)) {
|
||||
if (GetPythonThreadId(version, curThread) != threadId) {
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
|
||||
|
||||
// switch to our new thread so we can call sys.settrace on it...
|
||||
// all of the work here needs to be minimal - in particular we shouldn't
|
||||
// ever evaluate user defined code as we could end up switching to this
|
||||
// thread on the main thread and corrupting state.
|
||||
delThreadTls(threadStateIndex);
|
||||
setThreadTls(threadStateIndex, curThread);
|
||||
auto prevThread = threadSwap(curThread);
|
||||
|
||||
// save and restore the error in case something funky happens...
|
||||
auto errOccured = errOccurred();
|
||||
PyObject* type = nullptr;
|
||||
PyObject* value = nullptr;
|
||||
PyObject* traceback = nullptr;
|
||||
if (errOccured) {
|
||||
pyErrFetch(&type, &value, &traceback);
|
||||
retVal = 1;
|
||||
}
|
||||
|
||||
if(showDebugInfo){
|
||||
printf("setting trace for thread: %d\n", threadId);
|
||||
}
|
||||
|
||||
DecRef(call(setTraceFunc->ToPython(), traceFunc->ToPython(), nullptr), isDebug);
|
||||
|
||||
if (errOccured) {
|
||||
pyErrRestore(type, value, traceback);
|
||||
}
|
||||
|
||||
delThreadTls(threadStateIndex);
|
||||
setThreadTls(threadStateIndex, prevThread);
|
||||
threadSwap(prevThread);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(threadStateIndex == -1){
|
||||
printf("Unable to find threadStateIndex for the current thread. curPyThread: %p\n", curPyThread);
|
||||
return 350;
|
||||
}
|
||||
|
||||
|
||||
bool found = false;
|
||||
for (auto curThread = threadHead(head); curThread != nullptr; curThread = threadNext(curThread)) {
|
||||
if (GetPythonThreadId(version, curThread) != threadId) {
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
|
||||
|
||||
// switch to our new thread so we can call sys.settrace on it...
|
||||
// all of the work here needs to be minimal - in particular we shouldn't
|
||||
// ever evaluate user defined code as we could end up switching to this
|
||||
// thread on the main thread and corrupting state.
|
||||
delThreadTls(threadStateIndex);
|
||||
setThreadTls(threadStateIndex, curThread);
|
||||
auto prevThread = threadSwap(curThread);
|
||||
|
||||
// save and restore the error in case something funky happens...
|
||||
auto errOccured = errOccurred();
|
||||
PyObject* type = nullptr;
|
||||
PyObject* value = nullptr;
|
||||
PyObject* traceback = nullptr;
|
||||
if (errOccured) {
|
||||
pyErrFetch(&type, &value, &traceback);
|
||||
retVal = 1;
|
||||
if(!found) {
|
||||
retVal = 500;
|
||||
}
|
||||
|
||||
if(showDebugInfo){
|
||||
printf("setting trace for thread: %d\n", threadId);
|
||||
}
|
||||
else
|
||||
{
|
||||
// See comments on py_settrace_37.hpp for why we need a different implementation in Python 3.7 onwards.
|
||||
DEFINE_PROC(pyUnicode_InternFromString, PyUnicode_InternFromString*, "PyUnicode_InternFromString", 520);
|
||||
DEFINE_PROC(pyObject_FastCallDict, _PyObject_FastCallDict*, "_PyObject_FastCallDict", 530);
|
||||
DEFINE_PROC(pyTraceBack_Here, PyTraceBack_Here*, "PyTraceBack_Here", 540);
|
||||
DEFINE_PROC(pyEval_SetTrace, PyEval_SetTrace*, "PyEval_SetTrace", 550);
|
||||
|
||||
bool found = false;
|
||||
for (PyThreadState* curThread = threadHead(head); curThread != nullptr; curThread = threadNext(curThread)) {
|
||||
if (GetPythonThreadId(version, curThread) != threadId) {
|
||||
continue;
|
||||
}
|
||||
found = true;
|
||||
|
||||
if(showDebugInfo){
|
||||
printf("setting trace for thread: %d\n", threadId);
|
||||
}
|
||||
|
||||
if(!InternalIsTraceInitialized_37())
|
||||
{
|
||||
InternalInitializeSettrace_37 *internalInitializeSettrace_37 = new InternalInitializeSettrace_37();
|
||||
|
||||
IncRef(pyNone->ToPython());
|
||||
internalInitializeSettrace_37->pyNone = pyNone->ToPython();
|
||||
|
||||
internalInitializeSettrace_37->pyUnicode_InternFromString = pyUnicode_InternFromString;
|
||||
internalInitializeSettrace_37->pyObject_FastCallDict = pyObject_FastCallDict;
|
||||
internalInitializeSettrace_37->isDebug = isDebug;
|
||||
internalInitializeSettrace_37->pyTraceBack_Here = pyTraceBack_Here;
|
||||
internalInitializeSettrace_37->pyEval_SetTrace = pyEval_SetTrace;
|
||||
|
||||
InternalTraceInit_37(internalInitializeSettrace_37);
|
||||
}
|
||||
InternalPySetTrace_37(curThread, traceFunc, isDebug);
|
||||
break;
|
||||
}
|
||||
|
||||
DecRef(call(setTraceFunc->ToPython(), traceFunc->ToPython(), nullptr), isDebug);
|
||||
|
||||
if (errOccured) {
|
||||
pyErrRestore(type, value, traceback);
|
||||
if(!found) {
|
||||
retVal = 501;
|
||||
}
|
||||
|
||||
delThreadTls(threadStateIndex);
|
||||
setThreadTls(threadStateIndex, prevThread);
|
||||
threadSwap(prevThread);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!found) {
|
||||
retVal = 500;
|
||||
}
|
||||
|
||||
|
||||
return retVal;
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,176 @@
|
|||
#ifndef _PY_SETTRACE_37_HPP_
|
||||
#define _PY_SETTRACE_37_HPP_
|
||||
|
||||
#include "python.h"
|
||||
#include "py_utils.hpp"
|
||||
|
||||
// On Python 3.7 onwards the thread state is not kept in PyThread_set_key_value (rather
|
||||
// it uses PyThread_tss_set using PyThread_tss_set(&_PyRuntime.gilstate.autoTSSkey, (void *)tstate)
|
||||
// and we don't have access to that key from here (thus, we can't use the previous approach which
|
||||
// made CPython think that the current thread had the thread state where we wanted to set the tracing).
|
||||
//
|
||||
// So, the solution implemented here is not faking that change and reimplementing PyEval_SetTrace.
|
||||
// The implementation is mostly the same from the one in CPython, but we have one shortcoming:
|
||||
//
|
||||
// When CPython sets the tracing for a thread it increments _Py_TracingPossible (actually
|
||||
// _PyRuntime.ceval.tracing_possible). This implementation has one issue: it only works on
|
||||
// deltas when the tracing is set (so, a settrace(func) will increase the _Py_TracingPossible global value and a
|
||||
// settrace(None) will decrease it, but when a thread dies it's kept as is and is not decreased).
|
||||
// -- as we don't currently have access to _PyRuntime we have to create a thread, set the tracing
|
||||
// and let it die so that the count is increased (this is really hacky, but better than having
|
||||
// to create a local copy of the whole _PyRuntime (defined in pystate.h with several inner structs)
|
||||
// which would need to be kept up to date for each new CPython version just to increment that variable).
|
||||
|
||||
|
||||
struct InternalInitializeSettrace_37 {
|
||||
PyUnicode_InternFromString* pyUnicode_InternFromString;
|
||||
PyObject* pyNone;
|
||||
_PyObject_FastCallDict* pyObject_FastCallDict;
|
||||
PyTraceBack_Here* pyTraceBack_Here;
|
||||
PyEval_SetTrace* pyEval_SetTrace;
|
||||
bool isDebug;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper information to access CPython internals.
|
||||
*/
|
||||
static InternalInitializeSettrace_37 *internalInitializeSettrace_37 = NULL;
|
||||
|
||||
/*
|
||||
* Cached interned string objects used for calling the profile and
|
||||
* trace functions. Initialized by InternalTraceInit_37().
|
||||
*/
|
||||
static PyObject *InternalWhatstrings_37[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
|
||||
|
||||
|
||||
static int
|
||||
InternalIsTraceInitialized_37()
|
||||
{
|
||||
return internalInitializeSettrace_37 != NULL;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
InternalTraceInit_37(InternalInitializeSettrace_37 *p_internalInitializeSettrace_37)
|
||||
{
|
||||
internalInitializeSettrace_37 = p_internalInitializeSettrace_37;
|
||||
static const char * const whatnames[8] = {
|
||||
"call", "exception", "line", "return",
|
||||
"c_call", "c_exception", "c_return",
|
||||
"opcode"
|
||||
};
|
||||
PyObject *name;
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
if (InternalWhatstrings_37[i] == NULL) {
|
||||
name = internalInitializeSettrace_37->pyUnicode_InternFromString(whatnames[i]);
|
||||
if (name == NULL)
|
||||
return -1;
|
||||
InternalWhatstrings_37[i] = name;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static PyObject *
|
||||
InternalCallTrampoline_37(PyObject* callback,
|
||||
PyFrameObject *frame, int what, PyObject *arg)
|
||||
{
|
||||
PyObject *result;
|
||||
PyObject *stack[3];
|
||||
|
||||
// Note: this is commented out from CPython (we shouldn't need it and it adds a reasonable overhead).
|
||||
// if (PyFrame_FastToLocalsWithError(frame) < 0) {
|
||||
// return NULL;
|
||||
// }
|
||||
//
|
||||
stack[0] = (PyObject *)frame;
|
||||
stack[1] = InternalWhatstrings_37[what];
|
||||
stack[2] = (arg != NULL) ? arg : internalInitializeSettrace_37->pyNone;
|
||||
|
||||
// call the Python-level function
|
||||
// result = _PyObject_FastCall(callback, stack, 3);
|
||||
//
|
||||
// Note that _PyObject_FastCall is actually a define:
|
||||
// #define _PyObject_FastCall(func, args, nargs) _PyObject_FastCallDict((func), (args), (nargs), NULL)
|
||||
|
||||
result = internalInitializeSettrace_37->pyObject_FastCallDict(callback, stack, 3, NULL);
|
||||
|
||||
|
||||
// Note: this is commented out from CPython (we shouldn't need it and it adds a reasonable overhead).
|
||||
// PyFrame_LocalsToFast(frame, 1);
|
||||
|
||||
if (result == NULL) {
|
||||
internalInitializeSettrace_37->pyTraceBack_Here(frame);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int
|
||||
InternalTraceTrampoline_37(PyObject *self, PyFrameObject *frame,
|
||||
int what, PyObject *arg)
|
||||
{
|
||||
PyObject *callback;
|
||||
PyObject *result;
|
||||
|
||||
if (what == PyTrace_CALL){
|
||||
callback = self;
|
||||
} else {
|
||||
callback = frame->f_trace;
|
||||
}
|
||||
|
||||
if (callback == NULL){
|
||||
return 0;
|
||||
}
|
||||
|
||||
result = InternalCallTrampoline_37(callback, frame, what, arg);
|
||||
if (result == NULL) {
|
||||
// Note: calling the original sys.settrace here.
|
||||
internalInitializeSettrace_37->pyEval_SetTrace(NULL, NULL);
|
||||
PyObject *temp_f_trace = frame->f_trace;
|
||||
frame->f_trace = NULL;
|
||||
if(temp_f_trace != NULL){
|
||||
DecRef(temp_f_trace, internalInitializeSettrace_37->isDebug);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
if (result != internalInitializeSettrace_37->pyNone) {
|
||||
PyObject *tmp = frame->f_trace;
|
||||
frame->f_trace = result;
|
||||
DecRef(tmp, internalInitializeSettrace_37->isDebug);
|
||||
}
|
||||
else {
|
||||
DecRef(result, internalInitializeSettrace_37->isDebug);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void InternalPySetTrace_37(PyThreadState* curThread, PyObjectHolder* traceFunc, bool isDebug)
|
||||
{
|
||||
PyThreadState_37* tstate = reinterpret_cast<PyThreadState_37*>(curThread);
|
||||
PyObject *temp = tstate->c_traceobj;
|
||||
|
||||
// We can't increase _Py_TracingPossible. Everything else should be equal to CPython.
|
||||
// runtime->ceval.tracing_possible += (func != NULL) - (tstate->c_tracefunc != NULL);
|
||||
|
||||
PyObject *arg = traceFunc->ToPython();
|
||||
IncRef(arg);
|
||||
tstate->c_tracefunc = NULL;
|
||||
tstate->c_traceobj = NULL;
|
||||
/* Must make sure that profiling is not ignored if 'temp' is freed */
|
||||
tstate->use_tracing = tstate->c_profilefunc != NULL;
|
||||
if(temp != NULL){
|
||||
DecRef(temp, isDebug);
|
||||
}
|
||||
tstate->c_tracefunc = InternalTraceTrampoline_37;
|
||||
tstate->c_traceobj = arg;
|
||||
/* Flag that tracing or profiling is turned on */
|
||||
tstate->use_tracing = ((InternalTraceTrampoline_37 != NULL)
|
||||
|| (tstate->c_profilefunc != NULL));
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //_PY_SETTRACE_37_HPP_
|
||||
|
|
@ -25,6 +25,13 @@ typedef int (PyThread_set_key_value)(int, void*);
|
|||
typedef void (PyThread_delete_key_value)(int);
|
||||
typedef int (PyObject_Not) (PyObject *o);
|
||||
typedef PyObject* (PyDict_New)();
|
||||
typedef PyObject* (PyUnicode_InternFromString)(const char *u);
|
||||
typedef PyObject * (_PyObject_FastCallDict)(
|
||||
PyObject *callable, PyObject *const *args, Py_ssize_t nargs, PyObject *kwargs);
|
||||
typedef int (PyTraceBack_Here)(PyFrameObject *frame);
|
||||
|
||||
typedef void (PyEval_SetTrace)(Py_tracefunc, PyObject *);
|
||||
typedef int (*Py_tracefunc)(PyObject *, PyFrameObject *frame, int, PyObject *);
|
||||
|
||||
// holder to ensure we release the GIL even in error conditions
|
||||
class GilHolder {
|
||||
|
|
|
|||
|
|
@ -40,13 +40,6 @@ int hello()
|
|||
}
|
||||
|
||||
|
||||
// This is the function which enables us to set the sys.settrace for all the threads
|
||||
// which are already running.
|
||||
// isDebug is pretty important! Must be true on python debug builds (python_d)
|
||||
// If this value is passed wrongly the program will crash.
|
||||
extern "C" int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId);
|
||||
extern "C" int DoAttach(bool isDebug, const char *command, bool showDebugInfo);
|
||||
|
||||
// Internal function to keep on the tracing
|
||||
int _PYDEVD_ExecWithGILSetSysStrace(bool showDebugInfo, bool isDebug);
|
||||
|
||||
|
|
@ -56,6 +49,9 @@ typedef int (*PyEval_ThreadsInitialized)();
|
|||
typedef unsigned long (*_PyEval_GetSwitchInterval)(void);
|
||||
typedef void (*_PyEval_SetSwitchInterval)(unsigned long microseconds);
|
||||
|
||||
// isDebug is pretty important! Must be true on python debug builds (python_d)
|
||||
// If this value is passed wrongly the program will crash.
|
||||
extern "C" int DoAttach(bool isDebug, const char *command, bool showDebugInfo);
|
||||
|
||||
int DoAttach(bool isDebug, const char *command, bool showDebugInfo)
|
||||
{
|
||||
|
|
@ -99,12 +95,17 @@ int DoAttach(bool isDebug, const char *command, bool showDebugInfo)
|
|||
}
|
||||
|
||||
|
||||
int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId)
|
||||
// This is the function which enables us to set the sys.settrace for all the threads
|
||||
// which are already running.
|
||||
extern "C" int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId, void* pPyNone);
|
||||
|
||||
int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId, void* pPyNone)
|
||||
{
|
||||
void *module = dlopen(nullptr, 0x2);
|
||||
bool isDebug = false;
|
||||
PyObjectHolder traceFunc(isDebug, (PyObject*) pTraceFunc, true);
|
||||
PyObjectHolder setTraceFunc(isDebug, (PyObject*) pSetTraceFunc, true);
|
||||
return InternalSetSysTraceFunc(module, isDebug, showDebugInfo, &traceFunc, &setTraceFunc, threadId);
|
||||
PyObjectHolder pyNone(isDebug, reinterpret_cast<PyObject*>(pPyNone), true);
|
||||
return InternalSetSysTraceFunc(module, isDebug, showDebugInfo, &traceFunc, &setTraceFunc, threadId, &pyNone);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -463,11 +463,15 @@ extern "C"
|
|||
SuspendThreads(suspendedThreads, addPendingCall, threadsInited);
|
||||
|
||||
if(!threadsInited()){ // Check again with threads suspended.
|
||||
std::cout << "ENTERED if (!threadsInited()) {" << std::endl << std::flush;
|
||||
if (showDebugInfo) {
|
||||
std::cout << "ENTERED if (!threadsInited()) {" << std::endl << std::flush;
|
||||
}
|
||||
auto curPyThread = getPythonThread ? getPythonThread() : *curPythonThread;
|
||||
|
||||
if (curPyThread == nullptr) {
|
||||
std::cout << "ENTERED if (curPyThread == nullptr) {" << std::endl << std::flush;
|
||||
if (showDebugInfo) {
|
||||
std::cout << "ENTERED if (curPyThread == nullptr) {" << std::endl << std::flush;
|
||||
}
|
||||
// no threads are currently running, it is safe to initialize multi threading.
|
||||
PyGILState_STATE gilState;
|
||||
if (version >= PythonVersion_34) {
|
||||
|
|
@ -497,7 +501,9 @@ extern "C"
|
|||
gilState = PyGILState_LOCKED; // prevent compiler warning
|
||||
}
|
||||
|
||||
std::cout << "Called initThreads()" << std::endl << std::flush;
|
||||
if (showDebugInfo) {
|
||||
std::cout << "Called initThreads()" << std::endl << std::flush;
|
||||
}
|
||||
// Initialize threads in our secondary thread (this is NOT ideal because
|
||||
// this thread will be the thread head), but is still better than not being
|
||||
// able to attach if the main thread is not actually running any code.
|
||||
|
|
@ -670,7 +676,7 @@ extern "C"
|
|||
/**
|
||||
* This function may be called to set a tracing function to existing python threads.
|
||||
**/
|
||||
DECLDIR int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId)
|
||||
DECLDIR int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId, void* pPyNone)
|
||||
{
|
||||
ModuleInfo moduleInfo = GetPythonModule();
|
||||
if (moduleInfo.errorGettingModule != 0) {
|
||||
|
|
@ -683,7 +689,9 @@ extern "C"
|
|||
int attached = 0;
|
||||
PyObjectHolder traceFunc(moduleInfo.isDebug, reinterpret_cast<PyObject*>(pTraceFunc), true);
|
||||
PyObjectHolder setTraceFunc(moduleInfo.isDebug, reinterpret_cast<PyObject*>(pSetTraceFunc), true);
|
||||
int temp = InternalSetSysTraceFunc(module, moduleInfo.isDebug, showDebugInfo, &traceFunc, &setTraceFunc, threadId);
|
||||
PyObjectHolder pyNone(moduleInfo.isDebug, reinterpret_cast<PyObject*>(pPyNone), true);
|
||||
|
||||
int temp = InternalSetSysTraceFunc(module, moduleInfo.isDebug, showDebugInfo, &traceFunc, &setTraceFunc, threadId, &pyNone);
|
||||
if (temp == 0) {
|
||||
// we've successfully attached the debugger
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,8 @@ extern "C"
|
|||
bool showDebugInfo,
|
||||
void* pSetTraceFunc, // Actually PyObject*, but we don't want to include it here.
|
||||
void* pTraceFunc, // Actually PyObject*, but we don't want to include it here.
|
||||
unsigned int threadId
|
||||
unsigned int threadId,
|
||||
void* pPyNone // Actually PyObject*, but we don't want to include it here.
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
|
||||
from _pydevd_bundle.pydevd_constants import get_frame, IS_CPYTHON, IS_64BIT_PROCESS, IS_WINDOWS, \
|
||||
IS_LINUX, IS_MAC, IS_PY2, DebugInfoHolder
|
||||
IS_LINUX, IS_MAC, IS_PY2, IS_PY37_OR_GREATER, DebugInfoHolder
|
||||
from _pydev_imps._pydev_saved_modules import thread, threading
|
||||
from _pydev_bundle import pydev_log
|
||||
from _pydev_bundle import pydev_log, pydev_monkey
|
||||
from os.path import os
|
||||
try:
|
||||
import ctypes
|
||||
|
|
@ -146,7 +146,10 @@ def load_python_helper_lib():
|
|||
lib = ctypes.pydll.LoadLibrary(filename)
|
||||
return lib
|
||||
except:
|
||||
pydev_log.exception('Error loading: %s', filename)
|
||||
if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1:
|
||||
# Only show message if tracing is on (we don't have pre-compiled
|
||||
# binaries for all architectures -- i.e.: ARM).
|
||||
pydev_log.exception('Error loading: %s', filename)
|
||||
return None
|
||||
|
||||
|
||||
|
|
@ -163,9 +166,12 @@ def set_trace_to_threads(tracing_func):
|
|||
prev_value = get_interval()
|
||||
ret = 0
|
||||
try:
|
||||
# Prevent going to any other thread... if we switch the thread during this operation we
|
||||
# could potentially corrupt the interpreter.
|
||||
set_interval(2 ** 15)
|
||||
if not IS_PY37_OR_GREATER:
|
||||
# Prevent going to any other thread... if we switch the thread during this operation we
|
||||
# could potentially corrupt the interpreter.
|
||||
# Note: on CPython 3.7 onwards this is not needed (we have a different implementation
|
||||
# for setting the tracing for other threads in this case).
|
||||
set_interval(2 ** 15)
|
||||
|
||||
set_trace_func = TracingFunctionHolder._original_tracing or sys.settrace
|
||||
|
||||
|
|
@ -216,12 +222,37 @@ def set_trace_to_threads(tracing_func):
|
|||
# show_debug_info = 1 if DebugInfoHolder.DEBUG_TRACE_LEVEL >= 1 else 0
|
||||
show_debug_info = 0
|
||||
|
||||
result = lib.AttachDebuggerTracing(ctypes.c_int(show_debug_info), ctypes.py_object(set_trace_func), ctypes.py_object(tracing_func), ctypes.c_uint(thread_ident))
|
||||
if IS_PY37_OR_GREATER:
|
||||
# Hack to increase _Py_TracingPossible.
|
||||
# See comments on py_settrace_37.hpp
|
||||
proceed = thread.allocate_lock()
|
||||
proceed.acquire()
|
||||
|
||||
def dummy_trace_on_py37(frame, event, arg):
|
||||
return dummy_trace_on_py37
|
||||
|
||||
def increase_tracing_count_on_py37():
|
||||
SetTrace(dummy_trace_on_py37)
|
||||
proceed.release()
|
||||
|
||||
start_new_thread = pydev_monkey.get_original_start_new_thread(thread)
|
||||
start_new_thread(increase_tracing_count_on_py37, ())
|
||||
proceed.acquire() # Only proceed after the release() is done.
|
||||
proceed = None
|
||||
|
||||
result = lib.AttachDebuggerTracing(
|
||||
ctypes.c_int(show_debug_info),
|
||||
ctypes.py_object(set_trace_func),
|
||||
ctypes.py_object(tracing_func),
|
||||
ctypes.c_uint(thread_ident),
|
||||
ctypes.py_object(None),
|
||||
)
|
||||
if result != 0:
|
||||
pydev_log.info('Unable to set tracing for existing threads. Result: %s', result)
|
||||
ret = result
|
||||
finally:
|
||||
set_interval(prev_value)
|
||||
if not IS_PY37_OR_GREATER:
|
||||
set_interval(prev_value)
|
||||
|
||||
return ret
|
||||
|
||||
|
|
|
|||
|
|
@ -728,7 +728,7 @@ def test_case_skipping_filters(case_setup, custom_setup):
|
|||
other_filename = os.path.join(not_my_code_dir, 'other.py')
|
||||
response = json_facade.write_set_breakpoints(1, filename=other_filename, verified=False)
|
||||
assert response.body.breakpoints == [
|
||||
{'verified': False, 'message': 'Breakpoint in file excluded by filters.', 'source': {}, 'line': 1}]
|
||||
{'verified': False, 'message': 'Breakpoint in file excluded by filters.', 'source': {'path': other_filename}, 'line': 1}]
|
||||
# Note: there's actually a use-case where we'd hit that breakpoint even if it was excluded
|
||||
# by filters, so, we must actually clear it afterwards (the use-case is that when we're
|
||||
# stepping into the context with the breakpoint we wouldn't skip it).
|
||||
|
|
@ -737,7 +737,7 @@ def test_case_skipping_filters(case_setup, custom_setup):
|
|||
other_filename = os.path.join(not_my_code_dir, 'file_that_does_not_exist.py')
|
||||
response = json_facade.write_set_breakpoints(1, filename=other_filename, verified=False)
|
||||
assert response.body.breakpoints == [
|
||||
{'verified': False, 'message': 'Breakpoint in file that does not exist.', 'source': {}, 'line': 1}]
|
||||
{'verified': False, 'message': 'Breakpoint in file that does not exist.', 'source': {'path': other_filename}, 'line': 1}]
|
||||
|
||||
elif custom_setup == 'set_exclude_launch_module_full':
|
||||
json_facade.write_launch(
|
||||
|
|
@ -767,7 +767,7 @@ def test_case_skipping_filters(case_setup, custom_setup):
|
|||
assert response.body.breakpoints == [{
|
||||
'verified': False,
|
||||
'message': 'Breakpoint in file excluded by filters.\nNote: may be excluded because of "justMyCode" option (default == true).',
|
||||
'source': {},
|
||||
'source': {'path': other_filename},
|
||||
'line': 14
|
||||
}]
|
||||
elif custom_setup == 'set_just_my_code_and_include':
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue