过去4年来,我一直在开发Python调试器(对它进行断断续续的工作),它在帮助我调试复杂的Python代码方面非常有用。调试器的作用类似于程序跟踪,使我可以查看程序的所有操作。它通过使用等于一组打印语句的方式来完成此操作。
例如,以下Python代码:
if(x == True):
Trace.If("User has enabled 'x'")
n = Trace.Variable(n + x + y)
else:
Trace.Else("User has not enabled anything")
pass
将以下内容输出到日志文件或数据库(假设满足“ if”语句条件):
Counter - Timestamp - Filename - Function - Line of Code - If - User has enabled 'x'
Counter - Timestamp - Filename - Function - Line of Code - Variable - n = 13
我的调试器的旧版本在主进程/线程(与我的调试器正在跟踪的程序相关联)中执行每个跟踪操作。尽管对于时间紧迫性不高的应用程序来说这不是问题,但无法正确跟踪那些程序。因此,我开始研究Python调试器的多线程版本(大约一年)。尽管从调试程序库的单线程版本到调试程序库的多线程版本的速度改进很大(因为我的很多库都是I / O繁重的),但这还不足以确保时间紧迫的Python应用程序。
然后,我开始开发调试库的多处理版本(大约0.5年),并且除与Python对象分析有关的调试语句外,所有内容都可以使用。例如,以下内容:
n = Trace.Variable(...some Python object...)
...将尝试分析Python对象,返回等于所讨论的Python对象的序列化版本的值。例如,以下代码:
testABC = Trace.Variable([1, 2, [3, 4, {'Bacon': 0, 'Ham': 1, 'Cheese': 2, 'Eggs': 3456}]])
...将输出以下跟踪/调试数据:
Counter - Timestamp - Filename - Function - Line of Code - Variable - testABC : (list) - [0] : (integer) - 1, [1] : (integer) - 2, [2] : (list) - [2][0] : (integer) - 3, [2][1] : (integer) - 4, [2][2] : (dictionary) - [2][2]{Bacon} : (integer) - 0, [2][2]{Ham} : (integer) - 1, [2][2]{Cheese} : (integer) - 2 and [2][2]{Eggs} : (integer) - 3456
不幸的是,库中处理Python对象分析的部分可能很慢(取决于所分析的Python对象)。为了防止主执行进程/线程变慢,我尝试在单独的Python进程中分析Python对象。问题在于某些Python对象无法序列化(例如框架或检查)。
我尝试了每种类型的序列化库(例如Pickle,Dill等)以及分布式计算库(例如ExecNet,RPyC等),但是无法将无法序列化的Python对象传递给单独的进程。这是一个问题,原因是我的Python调试库旨在调试/跟踪所有Python脚本。如果脚本碰巧分析了无法序列化的Python对象,那么我需要在主执行进程/线程中执行该Python对象的分析(如前所述,这可能非常慢)。
我研究过使用Python解释器,该解释器允许我禁用GIL(全局解释器锁),以期在一个单独的/并发线程中执行此昂贵的分析。诸如Jython或IronPython之类的解释器目前仅支持Python 2.7(这是一个问题,因为我的库调试了Python 3.0+脚本)。我还研究了可以加快Python执行速度的解释器(以希望减少在主进程/线程中执行昂贵的分析的成本),例如PyPy,但是我对PyPy的主要问题是它对Windows的支持。 (和Python 3在某种程度上)似乎很脆弱。再加上要求为PyPy重新安装所有库的要求也是一个问题(特别是考虑到并非所有Python库都完全受支持-尽管我确定它们正在工作)。
说了这么多,我有最后一个解决方案,我想尝试一下。在这样做之前,我希望先在Stack Overflow论坛上与某人讨论该解决方案,然后再对其进行编码(因为我不确定它是否会起作用)。该解决方案的伪代码如下:
1. Process 1 - Passes Python object (to be analyzed) to a Cython function.
2. Process 1 - Cython function stores Python object in memory and returns the memory address of the Python object.
3. Process 1 - Passes memory address of Python object (stored by Cython function) to a separate Python process (Process 2).
4. Process 2 - Receives memory address of Python object (stored by Cython function from Process 1) and passes the address to a Cython function.
5. Process 2 - Cython function pulls the Cython object pointing to the memory address, and returns a proxy of the original object.
6. Process 2 - Analyzes the Python object (returned by Cython function).
问题1-是否可能(假设Python本身不支持通过引用另一个进程来传递对象)?
问题2-如果是这样,Python的垃圾收集器将在我退出Cython函数(从步骤2开始)后立即释放该对象,还是将其保留在内存中(假设Cython依赖于程序员释放内存,就像在C中)?
问题3-第5步会返回Python对象,还是我必须将C对象转换为Python对象(如果是,我该怎么做)?
很长的问题的道歉。任何帮助将不胜感激。
谢谢。