py_settrace.hpp
  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  
 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      } else if (PyThreadState_310::IsFor(version)) {
 35          threadId = (DWORD)((PyThreadState_310*)curThread)->thread_id;
 36      } else if (PyThreadState_311::IsFor(version)) {
 37          threadId = (DWORD)((PyThreadState_311*)curThread)->thread_id;
 38      }
 39      return threadId;
 40  }
 41  
 42  
 43  /**
 44   * This function may be called to set a tracing function to existing python threads.
 45   */
 46  int InternalSetSysTraceFunc(
 47      MODULE_TYPE module,
 48      bool isDebug,
 49      bool showDebugInfo,
 50      PyObjectHolder* traceFunc,
 51      PyObjectHolder* setTraceFunc,
 52      unsigned int threadId,
 53      PyObjectHolder* pyNone)
 54  {
 55  
 56      if(showDebugInfo){
 57          PRINT("InternalSetSysTraceFunc started.");
 58      }
 59  
 60      DEFINE_PROC(isInit, Py_IsInitialized*, "Py_IsInitialized", 100);
 61      if (!isInit()) {
 62          PRINT("Py_IsInitialized returned false.");
 63          return 110;
 64      }
 65  
 66      auto version = GetPythonVersion(module);
 67  
 68      // found initialized Python runtime, gather and check the APIs we need.
 69  
 70      DEFINE_PROC(interpHead, PyInterpreterState_Head*, "PyInterpreterState_Head", 120);
 71      DEFINE_PROC(gilEnsure, PyGILState_Ensure*, "PyGILState_Ensure", 130);
 72      DEFINE_PROC(gilRelease, PyGILState_Release*, "PyGILState_Release", 140);
 73      DEFINE_PROC(threadHead, PyInterpreterState_ThreadHead*, "PyInterpreterState_ThreadHead", 150);
 74      DEFINE_PROC(threadNext, PyThreadState_Next*, "PyThreadState_Next", 160);
 75      DEFINE_PROC(threadSwap, PyThreadState_Swap*, "PyThreadState_Swap", 170);
 76      DEFINE_PROC(call, PyObject_CallFunctionObjArgs*, "PyObject_CallFunctionObjArgs", 180);
 77  
 78      PyInt_FromLong* intFromLong;
 79  
 80      if (version >= PythonVersion_30) {
 81          DEFINE_PROC(intFromLongPy3, PyInt_FromLong*, "PyLong_FromLong", 190);
 82          intFromLong = intFromLongPy3;
 83      } else {
 84          DEFINE_PROC(intFromLongPy2, PyInt_FromLong*, "PyInt_FromLong", 200);
 85          intFromLong = intFromLongPy2;
 86      }
 87  
 88      DEFINE_PROC(pyGetAttr, PyObject_GetAttrString*, "PyObject_GetAttrString", 250);
 89      DEFINE_PROC(pyHasAttr, PyObject_HasAttrString*, "PyObject_HasAttrString", 260);
 90      DEFINE_PROC_NO_CHECK(PyCFrame_Type, PyTypeObject*, "PyCFrame_Type", 300);  // optional
 91  
 92      DEFINE_PROC_NO_CHECK(curPythonThread, PyThreadState**, "_PyThreadState_Current", 310);  // optional
 93      DEFINE_PROC_NO_CHECK(getPythonThread, _PyThreadState_UncheckedGet*, "_PyThreadState_UncheckedGet", 320);  // optional
 94  
 95      if (curPythonThread == nullptr && getPythonThread == nullptr) {
 96          // we're missing some APIs, we cannot attach.
 97          PRINT("Error, missing Python threading API!!");
 98          return 330;
 99      }
100  
101      auto head = interpHead();
102      if (head == nullptr) {
103          // this interpreter is loaded but not initialized.
104          PRINT("Interpreter not initialized!");
105          return 340;
106      }
107  
108      GilHolder gilLock(gilEnsure, gilRelease);   // acquire and hold the GIL until done...
109  
110      int retVal = 0;
111      // find what index is holding onto the thread state...
112      auto curPyThread = getPythonThread ? getPythonThread() : *curPythonThread;
113  
114      if(curPyThread == nullptr){
115          PRINT("Getting the current python thread returned nullptr.");
116          return 345;
117      }
118  
119  
120      // We do what PyEval_SetTrace does, but for any target thread.
121      PyUnicode_InternFromString* pyUnicode_InternFromString;
122      if (version >= PythonVersion_30) {
123          DEFINE_PROC(unicodeFromString, PyUnicode_InternFromString*, "PyUnicode_InternFromString", 520);
124          pyUnicode_InternFromString = unicodeFromString;
125      } else {
126          DEFINE_PROC(stringFromString, PyUnicode_InternFromString*, "PyString_InternFromString", 525);
127          pyUnicode_InternFromString = stringFromString;
128      }
129  
130      DEFINE_PROC_NO_CHECK(pyObject_FastCallDict, _PyObject_FastCallDict*, "_PyObject_FastCallDict", 530);
131      DEFINE_PROC(pyTuple_New, PyTuple_New*, "PyTuple_New", 531);
132      DEFINE_PROC(pyEval_CallObjectWithKeywords, PyEval_CallObjectWithKeywords*, "PyEval_CallObjectWithKeywords", 532);
133  
134      if(pyObject_FastCallDict == nullptr) {
135          DEFINE_PROC_NO_CHECK(pyObject_FastCallDict, _PyObject_FastCallDict*, "PyObject_VectorcallDict", 533);
136      }
137                                                                                                           
138      if(pyObject_FastCallDict == nullptr) {
139          // we have to use PyObject_FastCallDictCustom for older versions of CPython (pre 3.7).
140          pyObject_FastCallDict = reinterpret_cast<_PyObject_FastCallDict*>(&PyObject_FastCallDictCustom);
141      }
142  
143  
144      DEFINE_PROC(pyTraceBack_Here, PyTraceBack_Here*, "PyTraceBack_Here", 540);
145      DEFINE_PROC(pyEval_SetTrace, PyEval_SetTrace*, "PyEval_SetTrace", 550);
146      
147      // These are defined mostly for printing info while debugging, so, if they're not there, don't bother reporting.
148      DEFINE_PROC_NO_CHECK(pyObject_Repr, PyObject_Repr*, "PyObject_Repr", 551);
149      DEFINE_PROC_NO_CHECK(pyUnicode_AsUTF8, PyUnicode_AsUTF8*, "PyUnicode_AsUTF8", 552);
150  
151  
152      bool found = false;
153      for (PyThreadState* curThread = threadHead(head); curThread != nullptr; curThread = threadNext(curThread)) {
154          if (GetPythonThreadId(version, curThread) != threadId) {
155              continue;
156          }
157          found = true;
158  
159          if(showDebugInfo){
160              printf("setting trace for thread: %d\n", threadId);
161          }
162  
163          if(!InternalIsTraceInitialized())
164          {
165              InternalInitializeCustomPyEvalSetTrace *internalInitializeCustomPyEvalSetTrace = new InternalInitializeCustomPyEvalSetTrace();
166  
167              IncRef(pyNone->ToPython());
168              internalInitializeCustomPyEvalSetTrace->pyNone = pyNone->ToPython();
169  
170              internalInitializeCustomPyEvalSetTrace->pyUnicode_InternFromString = pyUnicode_InternFromString;
171              internalInitializeCustomPyEvalSetTrace->pyObject_FastCallDict = pyObject_FastCallDict;
172              internalInitializeCustomPyEvalSetTrace->isDebug = isDebug;
173              internalInitializeCustomPyEvalSetTrace->pyTraceBack_Here = pyTraceBack_Here;
174              internalInitializeCustomPyEvalSetTrace->pyEval_SetTrace = pyEval_SetTrace;
175              internalInitializeCustomPyEvalSetTrace->pyTuple_New = pyTuple_New;
176              internalInitializeCustomPyEvalSetTrace->pyEval_CallObjectWithKeywords = pyEval_CallObjectWithKeywords;
177              internalInitializeCustomPyEvalSetTrace->pyObject_Repr = pyObject_Repr;
178              internalInitializeCustomPyEvalSetTrace->pyUnicode_AsUTF8 = pyUnicode_AsUTF8;
179  
180              InternalTraceInit(internalInitializeCustomPyEvalSetTrace);
181          }
182          InternalPySetTrace(curThread, traceFunc, isDebug, version);
183          break;
184      }
185      if(!found) {
186          retVal = 501;
187      }
188  
189      return retVal;
190  
191  }
192  
193  #endif // _PY_SETTRACE_HPP_