attach.cpp
  1  // This is much simpler than the windows version because we're using gdb and
  2  // we assume that gdb will call things in the correct thread already.
  3  
  4  //compile with: g++ -shared -o attach_linux.so -fPIC -nostartfiles attach_linux.c
  5  
  6  
  7  #include <stdio.h>
  8  #include <stdlib.h>
  9  #include <dlfcn.h>
 10  #include <stdbool.h>
 11  
 12  #include "../common/python.h"
 13  #include "../common/ref_utils.hpp"
 14  #include "../common/py_utils.hpp"
 15  #include "../common/py_settrace.hpp"
 16  //#include <unistd.h> used for usleep
 17  
 18  // Exported function: hello(): Just to print something and check that we've been
 19  // able to connect.
 20  extern "C" int hello(void);
 21  
 22  int hello()
 23  {
 24      printf("Hello world!\n");
 25  
 26      void *module = dlopen(nullptr, 0x2);
 27  
 28      void *hndl = dlsym (module, "PyGILState_Ensure");
 29      if(hndl == nullptr){
 30          printf("nullptr\n");
 31  
 32      }else{
 33          printf("Worked (found PyGILState_Ensure)!\n");
 34      }
 35  
 36      printf("%d", GetPythonVersion(module));
 37  
 38  
 39      return 2;
 40  }
 41  
 42  
 43  // Internal function to keep on the tracing
 44  int _PYDEVD_ExecWithGILSetSysStrace(bool showDebugInfo, bool isDebug);
 45  
 46  // Implementation details below
 47  typedef PyObject* (PyImport_ImportModuleNoBlock) (const char *name);
 48  typedef int (*PyEval_ThreadsInitialized)();
 49  typedef unsigned long (*_PyEval_GetSwitchInterval)(void);
 50  typedef void (*_PyEval_SetSwitchInterval)(unsigned long microseconds);
 51  
 52  // isDebug is pretty important! Must be true on python debug builds (python_d)
 53  // If this value is passed wrongly the program will crash.
 54  extern "C" int DoAttach(bool isDebug, const char *command, bool showDebugInfo);
 55  
 56  int DoAttach(bool isDebug, const char *command, bool showDebugInfo)
 57  {
 58      void *module = dlopen(nullptr, 0x2);
 59      DEFINE_PROC(isInitFunc, Py_IsInitialized*, "Py_IsInitialized", 1);
 60      DEFINE_PROC(gilEnsure, PyGILState_Ensure*, "PyGILState_Ensure", 51);
 61      DEFINE_PROC(gilRelease, PyGILState_Release*, "PyGILState_Release", 51);
 62  
 63  
 64      if(!isInitFunc()){
 65          if(showDebugInfo){
 66              printf("Py_IsInitialized returned false.\n");
 67          }
 68          return 2;
 69      }
 70  
 71      PythonVersion version = GetPythonVersion(module);
 72  
 73      DEFINE_PROC(interpHead, PyInterpreterState_Head*, "PyInterpreterState_Head", 51);
 74  
 75      auto head = interpHead();
 76      if (head == nullptr) {
 77          // this interpreter is loaded but not initialized.
 78          if(showDebugInfo){
 79              printf("Interpreter not initialized!\n");
 80          }
 81          return 54;
 82      }
 83  
 84      // Note: unlike windows where we have to do many things to enable threading
 85      // to work to get the gil, here we'll be executing in an existing thread,
 86      // so, it's mostly a matter of getting the GIL and running it and we shouldn't
 87      // have any more problems.
 88  
 89      DEFINE_PROC(pyRun_SimpleString, PyRun_SimpleString*, "PyRun_SimpleString", 51);
 90      
 91      GilHolder gilLock(gilEnsure, gilRelease);   // acquire and hold the GIL until done...
 92      
 93      pyRun_SimpleString(command);
 94      return 0;
 95  }
 96  
 97  
 98  // This is the function which enables us to set the sys.settrace for all the threads
 99  // which are already running.
100  extern "C" int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId, void* pPyNone);
101  
102  int AttachDebuggerTracing(bool showDebugInfo, void* pSetTraceFunc, void* pTraceFunc, unsigned int threadId, void* pPyNone)
103  {
104      void *module = dlopen(nullptr, 0x2);
105      bool isDebug = false;
106      PyObjectHolder traceFunc(isDebug, (PyObject*) pTraceFunc, true);
107      PyObjectHolder setTraceFunc(isDebug, (PyObject*) pSetTraceFunc, true);
108      PyObjectHolder pyNone(isDebug, reinterpret_cast<PyObject*>(pPyNone), true);
109      return InternalSetSysTraceFunc(module, isDebug, showDebugInfo, &traceFunc, &setTraceFunc, threadId, &pyNone);
110  }
111