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