如何转储正在运行的Python程序的对象?

时间:2019-02-12 07:04:40

标签: python gdb pickle

我有一个Python程序,该程序基本上保留list个对象中的Counter个,然后将它们写入磁盘。四天后,计数工作已经结束,但是系统的内存几乎已不足63GB,并且已经换出了50GB,没有任何进展。

这是我的代码的简化版本。

import os
import time
from collections import Counter

print(os.getpid())

counters = [Counter() for i in range(4)]

while True:
    for i in range(1024):
        for counter in counters:
            counter[i] = 1
    time.sleep(5)
    with open('/tmp/counter.txt', 'w') as f:
        for counter in counters:
            f.write('\n'.join(map(str, counter.most_common())))

我猜它卡在了最后一行,因为它是OOM,所以无法对字典进行排序。

我需要将这些Counter对象安全地写入磁盘以供以后处理

在其他线程中,我发现了some answers,但无法解决。这是我到目前为止尝试过的:

  1. 将gdb附加到我的Python程序中:gdb python3 32610
  2. 显示回溯:bt
  3. 尝试腌制。转储Counter对象的可能候选对象

    (gdb) bt
    ...
    #21 0x0000000000504c28 in PyEval_EvalFrameEx (throwflag=0,
    f=Frame 0x7f70a45d0cf8, for file /usr/lib/python3.6/collections/__init__.py, line 553, in most_common (self=<Counter at remote 0x7f70a2d7be60>, n=1048575)) at ../Python/ceval.c:4166
    ...
    (gdb) python i = gdb.inferiors()[0]
    (gdb) python m = i.read_memory(0x7f70a45d0cf8, 4)
    (gdb) python print(m.tobytes())
    b'\x02\x00\x00\x00'
    (gdb) python import pickle
    (gdb) python pickle.dump(m, open('/tmp/02.pickle', 'wb'))
    Traceback (most recent call last):
      File "<string>", line 1, in <module>
    TypeError: can't pickle memoryview objects
    Error while executing Python code.
    (gdb) dump value 0x7f70a45d0cf8
    No value to dump.
    
  4. 我不确定如何找到我感兴趣的对象的开始/结束地址。

  5. 计数器并不总是显示在回溯中。如何选择正确的镜框?
  6. 腌菜不会腌制吗?我无法安装任何suggested cPickle或Garlicsim或meliae

1 个答案:

答案 0 :(得分:0)

我找到了解决方案。

zcat /usr/share/doc/python3.6/gdbinit.gz > ~/.gdbinit

此gdbinit文件具有一个名为pyg的宏,该宏将对象的repr打印到stderr。

在同一文件中的

pylocals会为框架中找到的每个本地调用此方法。

# gdb -p 12912
(gdb) bt
(gdb) f 21 # select relavant frame
(gdb) pylocals 

这将这样的内容打印到Python程序控制台的stderr

object  : [Counter({'foobar': 321}), Counter(), Counter(), Counter()]
type    : list
refcount: 1
address : 0x7f02578ea5c8

对于我的真实代码,由于我要转储的对象很大,因此我暂停了Python程序并将其输出重定向到一个新文件。

fg &> gdb.out.pylocals.txt

我想这需要一些时间。

同时希望这对其他人也有帮助。