1 /* ****************************************************************************
2 *
3 * Copyright (c) Microsoft Corporation.
4 *
5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
6 * copy of the license can be found in the License.html file at the root of this distribution. If
7 * you cannot locate the Apache License, Version 2.0, please send an email to
8 * vspython@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
9 * by the terms of the Apache License, Version 2.0.
10 *
11 * You must not remove this notice, or any other, from this software.
12 *
13 * Contributor: Fabio Zadrozny
14 *
15 * Based on PyDebugAttach.cpp from PVTS. Windows only.
16 *
17 * https://github.com/Microsoft/PTVS/blob/master/Python/Product/PyDebugAttach/PyDebugAttach.cpp
18 *
19 * Initially we did an attach completely based on shellcode which got the
20 * GIL called PyRun_SimpleString with the needed code and was done with it
21 * (so, none of this code was needed).
22 * Now, newer version of Python don't initialize threading by default, so,
23 * most of this code is done only to overcome this limitation (and as a plus,
24 * if there's no code running, we also pause the threads to make our code run).
25 *
26 * On Linux the approach is still the simpler one (using gdb), so, on newer
27 * versions of Python it may not work unless the user has some code running
28 * and threads are initialized.
29 * I.e.:
30 *
31 * The user may have to add the code below in the start of its script for
32 * a successful attach (if he doesn't already use threads).
33 *
34 * from threading import Thread
35 * Thread(target=str).start()
36 *
37 * -- this is the workaround for the fact that we can't get the gil
38 * if there aren't any threads (PyGILState_Ensure gives an error).
39 * ***************************************************************************/
40
41
42 // Access to std::cout and std::endl
43 #include <iostream>
44 #include <mutex>
45 // DECLDIR will perform an export for us
46 #define DLL_EXPORT
47
48 #include "attach.h"
49 #include "stdafx.h"
50
51 #include "../common/python.h"
52 #include "../common/ref_utils.hpp"
53 #include "../common/py_utils.hpp"
54 #include "../common/py_settrace.hpp"
55
56
57 #pragma comment(lib, "kernel32.lib")
58 #pragma comment(lib, "user32.lib")
59 #pragma comment(lib, "advapi32.lib")
60 #pragma comment(lib, "psapi.lib")
61
62 #include "py_win_helpers.hpp"
63 #include "run_code_in_memory.hpp"
64
65 // _Always_ is not defined for all versions, so make it a no-op if missing.
66 #ifndef _Always_
67 #define _Always_(x) x
68 #endif
69
70
71 typedef void (PyEval_Lock)(); // Acquire/Release lock
72 typedef void (PyThreadState_API)(PyThreadState *); // Acquire/Release lock
73 typedef PyObject* (Py_CompileString)(const char *str, const char *filename, int start);
74 typedef PyObject* (PyEval_EvalCode)(PyObject *co, PyObject *globals, PyObject *locals);
75 typedef PyObject* (PyDict_GetItemString)(PyObject *p, const char *key);
76 typedef PyObject* (PyEval_GetBuiltins)();
77 typedef int (PyDict_SetItemString)(PyObject *dp, const char *key, PyObject *item);
78 typedef int (PyEval_ThreadsInitialized)();
79 typedef int (Py_AddPendingCall)(int (*func)(void *), void*);
80 typedef PyObject* (PyString_FromString)(const char* s);
81 typedef void PyEval_SetTrace(Py_tracefunc func, PyObject *obj);
82 typedef PyObject* (PyErr_Print)();
83 typedef PyObject* (PyObject_SetAttrString)(PyObject *o, const char *attr_name, PyObject* value);
84 typedef PyObject* (PyBool_FromLong)(long v);
85 typedef unsigned long (_PyEval_GetSwitchInterval)(void);
86 typedef void (_PyEval_SetSwitchInterval)(unsigned long microseconds);
87 typedef PyGILState_STATE PyGILState_EnsureFunc(void);
88 typedef void PyGILState_ReleaseFunc(PyGILState_STATE);
89 typedef PyThreadState *PyThreadState_NewFunc(PyInterpreterState *interp);
90
91 typedef PyObject *PyList_New(Py_ssize_t len);
92 typedef int PyList_Append(PyObject *list, PyObject *item);
93
94
95
GetCurrentModuleFilename()96 std::wstring GetCurrentModuleFilename() {
97 HMODULE hModule = nullptr;
98 if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCTSTR)GetCurrentModuleFilename, &hModule) != 0) {
99 wchar_t filename[MAX_PATH];
100 GetModuleFileName(hModule, filename, MAX_PATH);
101 return filename;
102 }
103 return std::wstring();
104 }
105
106
107 struct InitializeThreadingInfo {
108 PyImport_ImportModule* pyImportMod;
109 PyEval_Lock* initThreads;
110
111 std::mutex mutex;
112 HANDLE initedEvent; // Note: only access with mutex locked (and check if not already nullptr).
113 bool completed; // Note: only access with mutex locked
114 };
115
116
AttachCallback(void * voidInitializeThreadingInfo)117 int AttachCallback(void *voidInitializeThreadingInfo) {
118 // initialize us for threading, this will acquire the GIL if not already created, and is a nop if the GIL is created.
119 // This leaves us in the proper state when we return back to the runtime whether the GIL was created or not before
120 // we were called.
121 InitializeThreadingInfo* initializeThreadingInfo = reinterpret_cast<InitializeThreadingInfo*>(voidInitializeThreadingInfo);
122 initializeThreadingInfo->initThreads(); // Note: calling multiple times is ok.
123 initializeThreadingInfo->pyImportMod("threading");
124
125 initializeThreadingInfo->mutex.lock();
126 if(initializeThreadingInfo->initedEvent != nullptr) {
127 SetEvent(initializeThreadingInfo->initedEvent);
128 }
129 initializeThreadingInfo->completed = true;
130 initializeThreadingInfo->mutex.unlock();
131 return 0;
132 }
133
134
135 // create a custom heap for our unordered map. This is necessary because if we suspend a thread while in a heap function
136 // then we could deadlock here. We need to be VERY careful about what we do while the threads are suspended.
137 static HANDLE g_heap = 0;
138
139 template<typename T>
140 class PrivateHeapAllocator {
141 public:
142 typedef size_t size_type;
143 typedef ptrdiff_t difference_type;
144 typedef T* pointer;
145 typedef const T* const_pointer;
146 typedef T& reference;
147 typedef const T& const_reference;
148 typedef T value_type;
149
150 template<class U>
151 struct rebind {
152 typedef PrivateHeapAllocator<U> other;
153 };
154
PrivateHeapAllocator()155 explicit PrivateHeapAllocator() {}
156
PrivateHeapAllocator(PrivateHeapAllocator const &)157 PrivateHeapAllocator(PrivateHeapAllocator const&) {}
158
~PrivateHeapAllocator()159 ~PrivateHeapAllocator() {}
160
161 template<typename U>
PrivateHeapAllocator(PrivateHeapAllocator<U> const &)162 PrivateHeapAllocator(PrivateHeapAllocator<U> const&) {}
163
allocate(size_type size,std::allocator<void>::const_pointer hint=0)164 pointer allocate(size_type size, std::allocator<void>::const_pointer hint = 0) {
165 UNREFERENCED_PARAMETER(hint);
166
167 if (g_heap == nullptr) {
168 g_heap = HeapCreate(0, 0, 0);
169 }
170 auto mem = HeapAlloc(g_heap, 0, size * sizeof(T));
171 return static_cast<pointer>(mem);
172 }
173
deallocate(pointer p,size_type n)174 void deallocate(pointer p, size_type n) {
175 UNREFERENCED_PARAMETER(n);
176
177 HeapFree(g_heap, 0, p);
178 }
179
max_size() const180 size_type max_size() const {
181 return (std::numeric_limits<size_type>::max)() / sizeof(T);
182 }
183
construct(pointer p,const T & t)184 void construct(pointer p, const T& t) {
185 new(p) T(t);
186 }
187
destroy(pointer p)188 void destroy(pointer p) {
189 p->~T();
190 }
191 };
192
193 typedef std::unordered_map<DWORD, HANDLE, std::hash<DWORD>, std::equal_to<DWORD>, PrivateHeapAllocator<std::pair<DWORD, HANDLE>>> ThreadMap;
194
ResumeThreads(ThreadMap & suspendedThreads)195 void ResumeThreads(ThreadMap &suspendedThreads) {
196 for (auto start = suspendedThreads.begin(); start != suspendedThreads.end(); start++) {
197 ResumeThread((*start).second);
198 CloseHandle((*start).second);
199 }
200 suspendedThreads.clear();
201 }
202
203 // Suspends all threads ensuring that they are not currently in a call to Py_AddPendingCall.
SuspendThreads(ThreadMap & suspendedThreads,Py_AddPendingCall * addPendingCall,PyEval_ThreadsInitialized * threadsInited)204 void SuspendThreads(ThreadMap &suspendedThreads, Py_AddPendingCall* addPendingCall, PyEval_ThreadsInitialized* threadsInited) {
205 DWORD curThreadId = GetCurrentThreadId();
206 DWORD curProcess = GetCurrentProcessId();
207 // suspend all the threads in the process so we can do things safely...
208 bool suspended;
209
210 do {
211 suspended = false;
212 HANDLE h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
213 if (h != INVALID_HANDLE_VALUE) {
214
215 THREADENTRY32 te;
216 te.dwSize = sizeof(te);
217 if (Thread32First(h, &te)) {
218 do {
219 if (te.dwSize >= FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(te.th32OwnerProcessID) && te.th32OwnerProcessID == curProcess) {
220
221
222 if (te.th32ThreadID != curThreadId && suspendedThreads.find(te.th32ThreadID) == suspendedThreads.end()) {
223 auto hThread = OpenThread(THREAD_ALL_ACCESS, FALSE, te.th32ThreadID);
224 if (hThread != nullptr) {
225 SuspendThread(hThread);
226
227 bool addingPendingCall = false;
228
229 CONTEXT context;
230 memset(&context, 0x00, sizeof(CONTEXT));
231 context.ContextFlags = CONTEXT_ALL;
232 GetThreadContext(hThread, &context);
233
234 #if defined(_X86_)
235 if (context.Eip >= *(reinterpret_cast<DWORD*>(addPendingCall)) && context.Eip <= (*(reinterpret_cast<DWORD*>(addPendingCall))) + 0x100) {
236 addingPendingCall = true;
237 }
238 #elif defined(_AMD64_)
239 if (context.Rip >= *(reinterpret_cast<DWORD64*>(addPendingCall)) && context.Rip <= *(reinterpret_cast<DWORD64*>(addPendingCall) + 0x100)) {
240 addingPendingCall = true;
241 }
242 #endif
243
244 if (addingPendingCall) {
245 // we appear to be adding a pending call via this thread - wait for this to finish so we can add our own pending call...
246 ResumeThread(hThread);
247 SwitchToThread(); // yield to the resumed thread if it's on our CPU...
248 CloseHandle(hThread);
249 } else {
250 suspendedThreads[te.th32ThreadID] = hThread;
251 }
252 suspended = true;
253 }
254 }
255 }
256
257 te.dwSize = sizeof(te);
258 } while (Thread32Next(h, &te) && !threadsInited());
259 }
260 CloseHandle(h);
261 }
262 } while (suspended && !threadsInited());
263 }
264
265
266
267 extern "C"
268 {
269
270 /**
271 * The returned value signals the error that happened!
272 *
273 * Return codes:
274 * 0 = all OK.
275 * 1 = Py_IsInitialized not found
276 * 2 = Py_IsInitialized returned false
277 * 3 = Missing Python API
278 * 4 = Interpreter not initialized
279 * 5 = Python version unknown
280 * 6 = Connect timeout
281 **/
DoAttach(HMODULE module,bool isDebug,const char * command,bool showDebugInfo)282 int DoAttach(HMODULE module, bool isDebug, const char *command, bool showDebugInfo )
283 {
284 auto isInit = reinterpret_cast<Py_IsInitialized*>(GetProcAddress(module, "Py_IsInitialized"));
285
286 if (isInit == nullptr) {
287 std::cerr << "Py_IsInitialized not found. " << std::endl << std::flush;
288 return 1;
289 }
290 if (!isInit()) {
291 std::cerr << "Py_IsInitialized returned false. " << std::endl << std::flush;
292 return 2;
293 }
294
295 auto version = GetPythonVersion(module);
296
297 // found initialized Python runtime, gather and check the APIs we need for a successful attach...
298 DEFINE_PROC(addPendingCall, Py_AddPendingCall*, "Py_AddPendingCall", -100);
299 DEFINE_PROC(interpHead, PyInterpreterState_Head*, "PyInterpreterState_Head", -110);
300 DEFINE_PROC(gilEnsure, PyGILState_Ensure*, "PyGILState_Ensure", -120);
301 DEFINE_PROC(gilRelease, PyGILState_Release*, "PyGILState_Release", -130);
302 DEFINE_PROC(threadHead, PyInterpreterState_ThreadHead*, "PyInterpreterState_ThreadHead", -140);
303 DEFINE_PROC(initThreads, PyEval_Lock*, "PyEval_InitThreads", -150);
304 DEFINE_PROC(releaseLock, PyEval_Lock*, "PyEval_ReleaseLock", -160);
305 DEFINE_PROC(threadsInited, PyEval_ThreadsInitialized*, "PyEval_ThreadsInitialized", -170);
306 DEFINE_PROC(threadNext, PyThreadState_Next*, "PyThreadState_Next", -180);
307 DEFINE_PROC(pyImportMod, PyImport_ImportModule*, "PyImport_ImportModule", -190);
308 DEFINE_PROC(pyNone, PyObject*, "_Py_NoneStruct", -2000);
309 DEFINE_PROC(pyRun_SimpleString, PyRun_SimpleString*, "PyRun_SimpleString", -210);
310
311 // Either _PyThreadState_Current or _PyThreadState_UncheckedGet are required
312 DEFINE_PROC_NO_CHECK(curPythonThread, PyThreadState**, "_PyThreadState_Current", -220); // optional
313 DEFINE_PROC_NO_CHECK(getPythonThread, _PyThreadState_UncheckedGet*, "_PyThreadState_UncheckedGet", -230); // optional
314
315 if (curPythonThread == nullptr && getPythonThread == nullptr) {
316 // we're missing some APIs, we cannot attach.
317 std::cerr << "Error, missing Python threading API!!" << std::endl << std::flush;
318 return -240;
319 }
320
321 // Either _Py_CheckInterval or _PyEval_[GS]etSwitchInterval are useful, but not required
322 DEFINE_PROC_NO_CHECK(intervalCheck, int*, "_Py_CheckInterval", -250); // optional
323 DEFINE_PROC_NO_CHECK(getSwitchInterval, _PyEval_GetSwitchInterval*, "_PyEval_GetSwitchInterval", -260); // optional
324 DEFINE_PROC_NO_CHECK(setSwitchInterval, _PyEval_SetSwitchInterval*, "_PyEval_SetSwitchInterval", -270); // optional
325
326 auto head = interpHead();
327 if (head == nullptr) {
328 // this interpreter is loaded but not initialized.
329 std::cerr << "Interpreter not initialized! " << std::endl << std::flush;
330 return 4;
331 }
332
333 // check that we're a supported version
334 if (version == PythonVersion_Unknown) {
335 std::cerr << "Python version unknown! " << std::endl << std::flush;
336 return 5;
337 } else if (version == PythonVersion_25 || version == PythonVersion_26 ||
338 version == PythonVersion_30 || version == PythonVersion_31 || version == PythonVersion_32) {
339 std::cerr << "Python version unsupported! " << std::endl << std::flush;
340 return 5;
341 }
342
343
344 // We always try to initialize threading and import the threading module in the main thread in the code
345 // below...
346 //
347 // We need to initialize multiple threading support but we need to do so safely, so we call
348 // Py_AddPendingCall and have our callback then initialize multi threading. This is completely safe on 2.7
349 // and up. Unfortunately that doesn't work if we're not actively running code on the main thread (blocked on a lock
350 // or reading input).
351 //
352 // Another option is to make sure no code is running - if there is no active thread then we can safely call
353 // PyEval_InitThreads and we're in business. But to know this is safe we need to first suspend all the other
354 // threads in the process and then inspect if any code is running (note that this is still not ideal because
355 // this thread will be the thread head for Python, but still better than not attach at all).
356 //
357 // Finally if code is running after we've suspended the threads then we can go ahead and do Py_AddPendingCall
358 // on down-level interpreters as long as we're sure no one else is making a call to Py_AddPendingCall at the same
359 // time.
360 //
361 // Therefore our strategy becomes: Make the Py_AddPendingCall on interpreters and wait for it. If it doesn't
362 // call after a timeout, suspend all threads - if a threads is in Py_AddPendingCall resume and try again. Once we've got all of the threads
363 // stopped and not in Py_AddPendingCall (which calls no functions its self, you can see this and it's size in the
364 // debugger) then see if we have a current thread. If not go ahead and initialize multiple threading (it's now safe,
365 // no Python code is running).
366
367 InitializeThreadingInfo *initializeThreadingInfo = new InitializeThreadingInfo();
368 initializeThreadingInfo->pyImportMod = pyImportMod;
369 initializeThreadingInfo->initThreads = initThreads;
370 initializeThreadingInfo->initedEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
371
372 // Add the call to initialize threading.
373 addPendingCall(&AttachCallback, initializeThreadingInfo);
374
375 ::WaitForSingleObject(initializeThreadingInfo->initedEvent, 5000);
376
377 // Whether this completed or not, release the event handle as we won't use it anymore.
378 initializeThreadingInfo->mutex.lock();
379 CloseHandle(initializeThreadingInfo->initedEvent);
380 bool completed = initializeThreadingInfo->completed;
381 initializeThreadingInfo->initedEvent = nullptr;
382 initializeThreadingInfo->mutex.unlock();
383
384 if(completed) {
385 // Note that this structure will leak if addPendingCall did not complete in the timeout
386 // (we can't release now because it's possible that it'll still be called).
387 delete initializeThreadingInfo;
388 if (showDebugInfo) {
389 std::cout << "addPendingCall to initialize threads/import threading completed. " << std::endl << std::flush;
390 }
391 } else {
392 if (showDebugInfo) {
393 std::cout << "addPendingCall to initialize threads/import threading did NOT complete. " << std::endl << std::flush;
394 }
395 }
396
397 if (threadsInited()) {
398 // Note that since Python 3.7, threads are *always* initialized!
399 if (showDebugInfo) {
400 std::cout << "Threads initialized! " << std::endl << std::flush;
401 }
402
403 } else {
404 int saveIntervalCheck;
405 unsigned long saveLongIntervalCheck;
406 if (intervalCheck != nullptr) {
407 // not available on 3.2
408 saveIntervalCheck = *intervalCheck;
409 *intervalCheck = -1; // lower the interval check so pending calls are processed faster
410 saveLongIntervalCheck = 0; // prevent compiler warning
411 } else if (getSwitchInterval != nullptr && setSwitchInterval != nullptr) {
412 saveLongIntervalCheck = getSwitchInterval();
413 setSwitchInterval(0);
414 saveIntervalCheck = 0; // prevent compiler warning
415 }
416 else {
417 saveIntervalCheck = 0; // prevent compiler warning
418 saveLongIntervalCheck = 0; // prevent compiler warning
419 }
420
421 // If threads weren't initialized in our pending call, instead of giving a timeout, try
422 // to initialize it in this thread.
423 for(int attempts = 0; !threadsInited() && attempts < 20; attempts++) {
424 if(attempts > 0){
425 // If we haven't been able to do it in the first time, wait a bit before retrying.
426 Sleep(10);
427 }
428
429 ThreadMap suspendedThreads;
430 if (showDebugInfo) {
431 std::cout << "SuspendThreads(suspendedThreads, addPendingCall, threadsInited);" << std::endl << std::flush;
432 }
433 SuspendThreads(suspendedThreads, addPendingCall, threadsInited);
434
435 if(!threadsInited()){ // Check again with threads suspended.
436 if (showDebugInfo) {
437 std::cout << "ENTERED if (!threadsInited()) {" << std::endl << std::flush;
438 }
439 auto curPyThread = getPythonThread ? getPythonThread() : *curPythonThread;
440
441 if (curPyThread == nullptr) {
442 if (showDebugInfo) {
443 std::cout << "ENTERED if (curPyThread == nullptr) {" << std::endl << std::flush;
444 }
445 // no threads are currently running, it is safe to initialize multi threading.
446 PyGILState_STATE gilState;
447 if (version >= PythonVersion_34) {
448 // in 3.4 due to http://bugs.python.org/issue20891,
449 // we need to create our thread state manually
450 // before we can call PyGILState_Ensure() before we
451 // can call PyEval_InitThreads().
452
453 // Don't require this function unless we need it.
454 auto threadNew = (PyThreadState_NewFunc*)GetProcAddress(module, "PyThreadState_New");
455 if (threadNew != nullptr) {
456 threadNew(head);
457 }
458 }
459
460 if (version >= PythonVersion_32) {
461 // in 3.2 due to the new GIL and later we can't call Py_InitThreads
462 // without a thread being initialized.
463 // So we use PyGilState_Ensure here to first
464 // initialize the current thread, and then we use
465 // Py_InitThreads to bring up multi-threading.
466 // Some context here: http://bugs.python.org/issue11329
467 // http://pytools.codeplex.com/workitem/834
468 gilState = gilEnsure();
469 }
470 else {
471 gilState = PyGILState_LOCKED; // prevent compiler warning
472 }
473
474 if (showDebugInfo) {
475 std::cout << "Called initThreads()" << std::endl << std::flush;
476 }
477 // Initialize threads in our secondary thread (this is NOT ideal because
478 // this thread will be the thread head), but is still better than not being
479 // able to attach if the main thread is not actually running any code.
480 initThreads();
481
482 if (version >= PythonVersion_32) {
483 // we will release the GIL here
484 gilRelease(gilState);
485 } else {
486 releaseLock();
487 }
488 }
489 }
490 ResumeThreads(suspendedThreads);
491 }
492
493
494 if (intervalCheck != nullptr) {
495 *intervalCheck = saveIntervalCheck;
496 } else if (setSwitchInterval != nullptr) {
497 setSwitchInterval(saveLongIntervalCheck);
498 }
499
500 }
501
502 if (g_heap != nullptr) {
503 HeapDestroy(g_heap);
504 g_heap = nullptr;
505 }
506
507 if (!threadsInited()) {
508 std::cerr << "Unable to initialize threads in the given timeout! " << std::endl << std::flush;
509 return 8;
510 }
511
512 GilHolder gilLock(gilEnsure, gilRelease); // acquire and hold the GIL until done...
513
514 pyRun_SimpleString(command);
515 return 0;
516
517 }
518
519
520
521
522 // ======================================== Code related to setting tracing to existing threads.
523
524
525 /**
526 * This function is meant to be called to execute some arbitrary python code to be
527 * run. It'll initialize threads as needed and then run the code with pyRun_SimpleString.
528 *
529 * @param command: the python code to be run
530 * @param attachInfo: pointer to an int specifying whether we should show debug info (1) or not (0).
531 **/
AttachAndRunPythonCode(const char * command,int * attachInfo)532 DECLDIR int AttachAndRunPythonCode(const char *command, int *attachInfo )
533 {
534
535 int SHOW_DEBUG_INFO = 1;
536
537 bool showDebugInfo = (*attachInfo & SHOW_DEBUG_INFO) != 0;
538
539 if (showDebugInfo) {
540 std::cout << "AttachAndRunPythonCode started (showing debug info). " << std::endl << std::flush;
541 }
542
543 ModuleInfo moduleInfo = GetPythonModule();
544 if (moduleInfo.errorGettingModule != 0) {
545 return moduleInfo.errorGettingModule;
546 }
547 HMODULE module = moduleInfo.module;
548 int attached = DoAttach(module, moduleInfo.isDebug, command, showDebugInfo);
549
550 if (attached != 0) {
551 std::cerr << "Error when injecting code in target process. Error code (on windows): " << attached << std::endl << std::flush;
552 }
553 return attached;
554 }
555
556
PrintDebugInfo()557 DECLDIR int PrintDebugInfo() {
558 PRINT("Getting debug info...");
559 ModuleInfo moduleInfo = GetPythonModule();
560 if (moduleInfo.errorGettingModule != 0) {
561 PRINT("Error getting python module");
562 return 0;
563 }
564 HMODULE module = moduleInfo.module;
565
566 DEFINE_PROC(interpHead, PyInterpreterState_Head*, "PyInterpreterState_Head", 0);
567 DEFINE_PROC(threadHead, PyInterpreterState_ThreadHead*, "PyInterpreterState_ThreadHead", 0);
568 DEFINE_PROC(threadNext, PyThreadState_Next*, "PyThreadState_Next", 160);
569 DEFINE_PROC(gilEnsure, PyGILState_Ensure*, "PyGILState_Ensure", 0);
570 DEFINE_PROC(gilRelease, PyGILState_Release*, "PyGILState_Release", 0);
571
572 auto head = interpHead();
573 if (head == nullptr) {
574 // this interpreter is loaded but not initialized.
575 PRINT("Interpreter not initialized!");
576 return 0;
577 }
578
579 auto version = GetPythonVersion(module);
580 printf("Python version: %d\n", version);
581
582 GilHolder gilLock(gilEnsure, gilRelease); // acquire and hold the GIL until done...
583 auto curThread = threadHead(head);
584 if (curThread == nullptr) {
585 PRINT("Thread head is NULL.")
586 return 0;
587 }
588
589 for (auto curThread = threadHead(head); curThread != nullptr; curThread = threadNext(curThread)) {
590 printf("Found thread id: %d\n", GetPythonThreadId(version, curThread));
591 }
592
593 PRINT("Finished getting debug info.")
594 return 0;
595 }
596
597
598 /**
599 * This function may be called to set a tracing function to existing python threads.
600 **/
AttachDebuggerTracing(bool showDebugInfo,void * pSetTraceFunc,void * pTraceFunc,unsigned int threadId,void * pPyNone)601 DECLDIR int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId, void* pPyNone)
602 {
603 ModuleInfo moduleInfo = GetPythonModule();
604 if (moduleInfo.errorGettingModule != 0) {
605 return moduleInfo.errorGettingModule;
606 }
607 HMODULE module = moduleInfo.module;
608 if (showDebugInfo) {
609 std::cout << "Setting sys trace for existing threads." << std::endl << std::flush;
610 }
611 int attached = 0;
612 PyObjectHolder traceFunc(moduleInfo.isDebug, reinterpret_cast<PyObject*>(pTraceFunc), true);
613 PyObjectHolder setTraceFunc(moduleInfo.isDebug, reinterpret_cast<PyObject*>(pSetTraceFunc), true);
614 PyObjectHolder pyNone(moduleInfo.isDebug, reinterpret_cast<PyObject*>(pPyNone), true);
615
616 int temp = InternalSetSysTraceFunc(module, moduleInfo.isDebug, showDebugInfo, &traceFunc, &setTraceFunc, threadId, &pyNone);
617 if (temp == 0) {
618 // we've successfully attached the debugger
619 return 0;
620 } else {
621 if (temp > attached) {
622 //I.e.: the higher the value the more significant it is.
623 attached = temp;
624 }
625 }
626
627 if (showDebugInfo) {
628 std::cout << "Setting sys trace for existing threads failed with code: " << attached << "." << std::endl << std::flush;
629 }
630 return attached;
631 }
632
633 }
634
635