如何处理内存、.so文件名和十六进制偏移量

2024-05-29 11:25:18 发布

您现在位置:Python中文网/ 问答频道 /正文

别因此炒我鱿鱼,但这是真的。我正在编写一个多线程的python应用程序,运行时间非常长,通常为2-3小时,包含10个进程。这台机器不慢,只是计算量很大。你知道吗

问题是,由于外部工具的原因,有时应用程序会挂起85-90%的路径。你知道吗

我已经将这个测试分解成更小的部分,然后可以成功运行,但是长时间运行的程序挂起。你知道吗

例如,假设我必须分析一个长度为100000000项的列表上的一些数据。你知道吗

把它分解成20个5000000,列出了所有小零件的完成情况。你知道吗

试图完成一个悬在最后的100000000项目。我使用一些我无法改变的外部工具,所以我只是想看看发生了什么。你知道吗

我设置了Dtrace并运行

sudo dtrace -n 'syscall:::entry / execname == "python2.7" / { @[ustack()] = count() }'

当我的程序挂起时,我得到一个输出,如下面的代码示例。你知道吗

          libc.so.7`__sys_recvfrom+0xa
          _socket.so`0x804086ecd
          _socket.so`0x8040854ac
          libpython2.7.so.1`PyEval_EvalFrameEx+0x52d7
          libpython2.7.so.1`PyEval_EvalCodeEx+0x665
          libpython2.7.so.1`0x800b3317d
          libpython2.7.so.1`PyEval_EvalFrameEx+0x4e2f
          libpython2.7.so.1`0x800b33250
          libpython2.7.so.1`PyEval_EvalFrameEx+0x4e2f
          libpython2.7.so.1`PyEval_EvalCodeEx+0x665
          libpython2.7.so.1`0x800abb5a1
          libpython2.7.so.1`PyObject_Call+0x64
          libpython2.7.so.1`0x800aa3855
          libpython2.7.so.1`PyObject_Call+0x64
          libpython2.7.so.1`PyEval_EvalFrameEx+0x4de2
          libpython2.7.so.1`PyEval_EvalCodeEx+0x665
          libpython2.7.so.1`0x800abb5a1
          libpython2.7.so.1`PyObject_Call+0x64
          libpython2.7.so.1`0x800aa3855
          libpython2.7.so.1`PyObject_Call+0x64

那代码只是一遍又一遍地重复。我试着研究Dtrace python探测器,但是从星期二开始,这两个探测器似乎都被破坏了,所以这可能是我能得到的最接近的。你知道吗

我的问题是,我有一个模糊的想法libpython2.7.so.1是共享库,它以十六进制偏移量0x64保存函数pyObject_Call

是这样吗?你知道吗

我怎样才能破译这个?我甚至不知道该怎么称呼它,这样我就可以在谷歌上搜索答案或指南了。你知道吗


Tags: 工具代码程序应用程序sosocketcall鱿鱼
1条回答
网友
1楼 · 发布于 2024-05-29 11:25:18

你应该从阅读Showing the stack trace from a running Python application开始。你知道吗

您的特定 问题是关于DTrace的ustack()操作和 所以这个回复可能比你需要的更多。这是因为 DTrace的设计原则是显示系统的精确状态。 所以,即使您对 DTrace正在揭示它的底层实现。你知道吗

您提供的输出是一个堆栈,这是一种 描述线程在其特定时间点的状态 执行。例如,如果你有代码

void c(void) { pause(); }
void b(void) { c(); }
void a(void) { b(); }

当执行在pause()内时,您请求了一个堆栈 你可能会看到

pause()
c()
b()
a()

无论您使用什么工具,都可以找到当前指令及其 在找到“返回地址”之前封闭函数,即 该函数最终将返回的点;重复此步骤 过程产生一个堆栈。因此,尽管应该读取堆栈 从上到下是一系列返回地址,通常 作为一系列的呼叫者从另一个方向读。请注意 程序对应的 指令汇编意味着第二种解释 有时会产生误导。你知道吗

为了扩展上面的示例,a()、b()和c()可能是 都存在于同一个图书馆里 在其他库中具有相同名称的函数。因此它是 有助于为每个函数显示它所指向的对象 属于。因此,上面的堆栈可能成为

libc.so`pause()
libfoo.so`c()
libfoo.so`b()
libfoo.so`a()

这有助于开发人员确定 程序以特定的状态结束:libfoo中的函数c() 已调用pause()。但是,还有更多的工作要做:如果c() 看起来像

void c() {
    pause();
    pause();
}

那么程序在等待哪个pause()调用?你知道吗

函数a()、b()和c()将是序列 指令的一部分,通常占据 记忆。调用其中一个函数只需要 记录完成后返回的位置(即返回 地址),然后跳到与之对应的内存地址 函数的开始。函数的起始地址和大小是 记录在嵌入对象的“符号表”中;它是 通过读取此表,调试器能够找到函数 包含给定位置(如回信地址)的。因此 函数中的特定点可以用偏移量来描述, 通常从一开始就用十六进制表示。所以一个更好的 上面堆栈的版本可能是

libc.so`pause()+0x12
libfoo.so`c()+0x42
libfoo.so`b()+0x12
libfoo.so`a()+0x12

此时,开发人员可以在上使用“反汇编程序”libfoo.so公司 显示c()中的指令;与c()的比较 源代码可以让他透露 已调用pause()。你知道吗

在结束对堆栈的描述之前,值得一提 再观察一次。如果存在足够的“调试 在libfoo这样的库中,更好的调试器将能够 显示源代码文件名和 行号,而不是中每个“帧”的十六进制偏移量 堆叠。你知道吗

现在,回到你的问题, libpython(2.7.so.1)是一个函数执行此任务的库 执行Python脚本。Python脚本中的函数是 动态转换成可执行指令,所以我猜 那碎片

libpython2.7.so.1`0x800b33250
libpython2.7.so.1`PyEval_EvalFrameEx+0x4e2f
libpython2.7.so.1`PyEval_EvalCodeEx+0x665

意味着PyEval\u EvalFrameEx()是libpython中的功能 调用Python函数(即用 Python),它驻留在地址0x800b33250附近的内存中。A 简单的调试器可以看到这个地址属于libpython,但是 在库的符号表中找不到相应的条目; 左没有选择,它只是打印“原始”地址。你知道吗

所以,您需要看看Python脚本,看看它是什么 但不幸的是没有显示 堆栈的Python组件中的函数。你知道吗

有几种方法可以继续。首先是找一个 libpython的版本,如果存在的话,带有"DTrace helper"。这个 是一些额外的功能,让DTrace看到 Python程序本身除了周围的 实施。结果是每个Python帧 在Python源代码中用相应的点进行注释。你知道吗

如果您使用的是Solaris,另一个方法是使用pstack(1);这是 对Python的本机支持。你知道吗

最后,尝试一个特定的Python调试器。你知道吗

同样值得指出的是,dtrace调用将显示 你看到了所有的书堆,按人气排序,每当 程序“python2.7”进行系统调用。从你的描述来看, 这可能不是你想要的。如果你想了解 挂起的行为那么你可能要从 测试时python2.7进程的单个快照 挂起。你知道吗

相关问题 更多 >

    热门问题