1 #ifndef _PY_SETTRACE_HPP_
2 #define _PY_SETTRACE_HPP_
3 
4 #include "ref_utils.hpp"
5 #include "py_utils.hpp"
6 #include "python.h"
7 #include "py_custom_pyeval_settrace.hpp"
8 #include <unordered_set>
9 
10 
11 #ifdef _WIN32
12 
13 typedef HMODULE MODULE_TYPE;
14 #else // LINUX -----------------------------------------------------------------
15 
16 typedef void* MODULE_TYPE;
17 typedef ssize_t SSIZE_T;
18 typedef unsigned int DWORD;
19 
20 #endif
21 
GetPythonThreadId(PythonVersion version,PyThreadState * curThread)22 DWORD GetPythonThreadId(PythonVersion version, PyThreadState* curThread) {
23     DWORD threadId = 0;
24     if (PyThreadState_25_27::IsFor(version)) {
25         threadId = (DWORD)((PyThreadState_25_27*)curThread)->thread_id;
26     } else if (PyThreadState_30_33::IsFor(version)) {
27         threadId = (DWORD)((PyThreadState_30_33*)curThread)->thread_id;
28     } else if (PyThreadState_34_36::IsFor(version)) {
29         threadId = (DWORD)((PyThreadState_34_36*)curThread)->thread_id;
30     } else if (PyThreadState_37_38::IsFor(version)) {
31         threadId = (DWORD)((PyThreadState_37_38*)curThread)->thread_id;
32     } else if (PyThreadState_39::IsFor(version)) {
33         threadId = (DWORD)((PyThreadState_39*)curThread)->thread_id;
34     }
35     return threadId;
36 }
37 
38 
39 /**
40  * This function may be called to set a tracing function to existing python threads.
41  */
InternalSetSysTraceFunc(MODULE_TYPE module,bool isDebug,bool showDebugInfo,PyObjectHolder * traceFunc,PyObjectHolder * setTraceFunc,unsigned int threadId,PyObjectHolder * pyNone)42 int InternalSetSysTraceFunc(
43     MODULE_TYPE module,
44     bool isDebug,
45     bool showDebugInfo,
46     PyObjectHolder* traceFunc,
47     PyObjectHolder* setTraceFunc,
48     unsigned int threadId,
49     PyObjectHolder* pyNone)
50 {
51 
52     if(showDebugInfo){
53         PRINT("InternalSetSysTraceFunc started.");
54     }
55 
56     DEFINE_PROC(isInit, Py_IsInitialized*, "Py_IsInitialized", 100);
57     if (!isInit()) {
58         PRINT("Py_IsInitialized returned false.");
59         return 110;
60     }
61 
62     auto version = GetPythonVersion(module);
63 
64     // found initialized Python runtime, gather and check the APIs we need.
65 
66     DEFINE_PROC(interpHead, PyInterpreterState_Head*, "PyInterpreterState_Head", 120);
67     DEFINE_PROC(gilEnsure, PyGILState_Ensure*, "PyGILState_Ensure", 130);
68     DEFINE_PROC(gilRelease, PyGILState_Release*, "PyGILState_Release", 140);
69     DEFINE_PROC(threadHead, PyInterpreterState_ThreadHead*, "PyInterpreterState_ThreadHead", 150);
70     DEFINE_PROC(threadNext, PyThreadState_Next*, "PyThreadState_Next", 160);
71     DEFINE_PROC(threadSwap, PyThreadState_Swap*, "PyThreadState_Swap", 170);
72     DEFINE_PROC(call, PyObject_CallFunctionObjArgs*, "PyObject_CallFunctionObjArgs", 180);
73 
74     PyInt_FromLong* intFromLong;
75 
76     if (version >= PythonVersion_30) {
77         DEFINE_PROC(intFromLongPy3, PyInt_FromLong*, "PyLong_FromLong", 190);
78         intFromLong = intFromLongPy3;
79     } else {
80         DEFINE_PROC(intFromLongPy2, PyInt_FromLong*, "PyInt_FromLong", 200);
81         intFromLong = intFromLongPy2;
82     }
83 
84     DEFINE_PROC(pyGetAttr, PyObject_GetAttrString*, "PyObject_GetAttrString", 250);
85     DEFINE_PROC(pyHasAttr, PyObject_HasAttrString*, "PyObject_HasAttrString", 260);
86     DEFINE_PROC_NO_CHECK(PyCFrame_Type, PyTypeObject*, "PyCFrame_Type", 300);  // optional
87 
88     DEFINE_PROC_NO_CHECK(curPythonThread, PyThreadState**, "_PyThreadState_Current", 310);  // optional
89     DEFINE_PROC_NO_CHECK(getPythonThread, _PyThreadState_UncheckedGet*, "_PyThreadState_UncheckedGet", 320);  // optional
90 
91     if (curPythonThread == nullptr && getPythonThread == nullptr) {
92         // we're missing some APIs, we cannot attach.
93         PRINT("Error, missing Python threading API!!");
94         return 330;
95     }
96 
97     auto head = interpHead();
98     if (head == nullptr) {
99         // this interpreter is loaded but not initialized.
100         PRINT("Interpreter not initialized!");
101         return 340;
102     }
103 
104     GilHolder gilLock(gilEnsure, gilRelease);   // acquire and hold the GIL until done...
105 
106     int retVal = 0;
107     // find what index is holding onto the thread state...
108     auto curPyThread = getPythonThread ? getPythonThread() : *curPythonThread;
109 
110     if(curPyThread == nullptr){
111         PRINT("Getting the current python thread returned nullptr.");
112         return 345;
113     }
114 
115 
116     // We do what PyEval_SetTrace does, but for any target thread.
117     PyUnicode_InternFromString* pyUnicode_InternFromString;
118     if (version >= PythonVersion_30) {
119         DEFINE_PROC(unicodeFromString, PyUnicode_InternFromString*, "PyUnicode_InternFromString", 520);
120         pyUnicode_InternFromString = unicodeFromString;
121     } else {
122         DEFINE_PROC(stringFromString, PyUnicode_InternFromString*, "PyString_InternFromString", 525);
123         pyUnicode_InternFromString = stringFromString;
124     }
125 
126     DEFINE_PROC_NO_CHECK(pyObject_FastCallDict, _PyObject_FastCallDict*, "_PyObject_FastCallDict", 530);
127     DEFINE_PROC(pyTuple_New, PyTuple_New*, "PyTuple_New", 531);
128     DEFINE_PROC(pyEval_CallObjectWithKeywords, PyEval_CallObjectWithKeywords*, "PyEval_CallObjectWithKeywords", 532);
129 
130     if(pyObject_FastCallDict == nullptr) {
131         // we have to use PyObject_FastCallDictCustom for older versions of CPython (pre 3.7).
132         pyObject_FastCallDict = reinterpret_cast<_PyObject_FastCallDict*>(&PyObject_FastCallDictCustom);
133     }
134 
135 
136     DEFINE_PROC(pyTraceBack_Here, PyTraceBack_Here*, "PyTraceBack_Here", 540);
137     DEFINE_PROC(pyEval_SetTrace, PyEval_SetTrace*, "PyEval_SetTrace", 550);
138 
139     bool found = false;
140     for (PyThreadState* curThread = threadHead(head); curThread != nullptr; curThread = threadNext(curThread)) {
141         if (GetPythonThreadId(version, curThread) != threadId) {
142             continue;
143         }
144         found = true;
145 
146         if(showDebugInfo){
147             printf("setting trace for thread: %d\n", threadId);
148         }
149 
150         if(!InternalIsTraceInitialized())
151         {
152             InternalInitializeCustomPyEvalSetTrace *internalInitializeCustomPyEvalSetTrace = new InternalInitializeCustomPyEvalSetTrace();
153 
154             IncRef(pyNone->ToPython());
155             internalInitializeCustomPyEvalSetTrace->pyNone = pyNone->ToPython();
156 
157             internalInitializeCustomPyEvalSetTrace->pyUnicode_InternFromString = pyUnicode_InternFromString;
158             internalInitializeCustomPyEvalSetTrace->pyObject_FastCallDict = pyObject_FastCallDict;
159             internalInitializeCustomPyEvalSetTrace->isDebug = isDebug;
160             internalInitializeCustomPyEvalSetTrace->pyTraceBack_Here = pyTraceBack_Here;
161             internalInitializeCustomPyEvalSetTrace->pyEval_SetTrace = pyEval_SetTrace;
162             internalInitializeCustomPyEvalSetTrace->pyTuple_New = pyTuple_New;
163             internalInitializeCustomPyEvalSetTrace->pyEval_CallObjectWithKeywords = pyEval_CallObjectWithKeywords;
164 
165             InternalTraceInit(internalInitializeCustomPyEvalSetTrace);
166         }
167         InternalPySetTrace(curThread, traceFunc, isDebug, version);
168         break;
169     }
170     if(!found) {
171         retVal = 501;
172     }
173 
174     return retVal;
175 
176 }
177 
178 #endif // _PY_SETTRACE_HPP_
179