我如何处理内存、.so 文件名和十六进制偏移量
How do I proceed with memory, .so filenames and hex offsets
不要因此而喷我,但这是真的。我正在编写一个多线程 python 应用程序,该应用程序 运行 持续了很长时间,通常有 10 个进程需要 2-3 小时。这台机器并不慢,只是计算量大。
问题是有时由于外部工具,应用程序会挂起大约 85-90%。
我已将此测试分解成更小的部分,然后可以 运行 成功,但长 运行ning 程序挂起。
例如,假设我必须分析一个包含 100,000,000 个项目的列表中的一些数据。
将它分成 20 个 5,000,000 列出所有较小的部分 运行s 完成。
正在努力完成它悬而未决的 100,000,000 项目。我使用了一些我无法更改的外部工具,所以我只是想看看发生了什么。
我设置了 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
的共享库
对吗?
我怎样才能破译这个?我什至不知道该怎么称呼它,这样我就可以 google 寻求答案或指导。
您可能应该先阅读 Showing the stack trace from a running Python application。
你的具体
问题是关于 DTrace 的 ustack() 操作的解释和
所以这个回复可能比你需要的更多。这是因为其中之一
DTrace 的设计原则是显示系统的准确状态。
因此,即使您对 Python 方面感兴趣
程序,DTrace 正在揭示其底层实现。
您提供的输出是一个堆栈,这是一种
描述线程在其特定点的状态
执行。例如,如果您有代码
void c(void) { pause(); }
void b(void) { c(); }
void a(void) { b(); }
然后你在 pause() 内执行时请求了一个堆栈
你可能看到类似
的东西
pause()
c()
b()
a()
无论您使用什么工具都会找到当前指令及其
在找到 "return address" 之前封闭函数,即
指向该函数最终 return;重复这个
过程产生一个堆栈。因此,尽管应该读取堆栈
从上到下作为一系列 return 地址,通常是
作为一系列来电者从另一个方向阅读。注意
程序相应的方式的微妙之处
说明组装意味着这第二个解释
有时会产生误导。
为了扩展上面的示例,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()将是序列
通常会占据连续区域的指令
记忆。调用其中一个函数仅涉及
记下完成后 return 的位置(即 return
地址),然后跳转到对应的内存地址
到函数的开始。函数的起始地址和大小是
记录在嵌入对象中的 "symbol table" 中;它是
通过阅读这个 table 调试器能够找到函数
包含给定位置,例如 return 地址。因此一个
在中的特定点一个函数可以用一个偏移量来描述,
通常以十六进制表示,从头开始。所以一个更好的
上面堆栈的版本可能是
libc.so`pause()+0x12
libfoo.so`c()+0x42
libfoo.so`b()+0x12
libfoo.so`a()+0x12
此时,开发者可以在libfoo.so上使用"disassembler"
显示 c() 中的指令;与 c() 的比较
源代码将允许他揭示具体的行
调用了 pause()。
在结束对堆栈的描述之前,值得做的
再观察一次。鉴于存在足够的“调试
data" 在库中,例如 libfoo,更好的调试器将能够
加倍努力并显示源代码文件名和
行号而不是每个 "frame" 中的十六进制偏移量
堆栈。
所以现在,return 到你问题中的堆栈,
libpython(2.7.so.1) 是一个库,其函数执行该作业
执行 Python 脚本。 Python 脚本中的函数是
即时转换为 executable 指令,所以我猜是
片段
libpython2.7.so.1`0x800b33250
libpython2.7.so.1`PyEval_EvalFrameEx+0x4e2f
libpython2.7.so.1`PyEval_EvalCodeEx+0x665
表示 PyEval_EvalFrameEx() 是 libpython 中的功能
本身调用 Python 函数(即写在
Python) 驻留在地址 0x800b33250 附近的内存中。一种
简单的调试器可以看到这个地址属于 libpython 但是
不会在库的符号 table 中找到相应的条目;
别无选择,它只打印 "raw" 地址。
所以,您需要查看 Python 脚本,看看它是什么
正在做,但不幸的是,没有迹象表明
堆栈的 Python 组件中的函数。
有几种方法可以继续。首先是找到一个
libpython 的版本,如果存在,带有 "DTrace helper"。这个
是一些额外的功能,可以让 DTrace 查看
Python 程序本身除了周围
执行。结果是每个 Python 帧将是
在Python源代码中用相应的点进行注释。
如果您使用的是 Solaris,另一个方法是使用 pstack(1);这有
对 Python.
的原生支持
最后,尝试特定的 Python 调试器。
同样值得指出的是,您的 dtrace 调用将显示
你看到的所有堆栈,按受欢迎程度排序,无论何时
程序 "python2.7" 进行系统调用。从你的描述来看,
这可能不是你想要的。如果你想了解
挂起的行为然后你可能想从一个开始
python2.7 进程 在
挂.
不要因此而喷我,但这是真的。我正在编写一个多线程 python 应用程序,该应用程序 运行 持续了很长时间,通常有 10 个进程需要 2-3 小时。这台机器并不慢,只是计算量大。
问题是有时由于外部工具,应用程序会挂起大约 85-90%。
我已将此测试分解成更小的部分,然后可以 运行 成功,但长 运行ning 程序挂起。
例如,假设我必须分析一个包含 100,000,000 个项目的列表中的一些数据。
将它分成 20 个 5,000,000 列出所有较小的部分 运行s 完成。
正在努力完成它悬而未决的 100,000,000 项目。我使用了一些我无法更改的外部工具,所以我只是想看看发生了什么。
我设置了 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
的共享库
对吗?
我怎样才能破译这个?我什至不知道该怎么称呼它,这样我就可以 google 寻求答案或指导。
您可能应该先阅读 Showing the stack trace from a running Python application。
你的具体 问题是关于 DTrace 的 ustack() 操作的解释和 所以这个回复可能比你需要的更多。这是因为其中之一 DTrace 的设计原则是显示系统的准确状态。 因此,即使您对 Python 方面感兴趣 程序,DTrace 正在揭示其底层实现。
您提供的输出是一个堆栈,这是一种 描述线程在其特定点的状态 执行。例如,如果您有代码
void c(void) { pause(); }
void b(void) { c(); }
void a(void) { b(); }
然后你在 pause() 内执行时请求了一个堆栈 你可能看到类似
的东西pause()
c()
b()
a()
无论您使用什么工具都会找到当前指令及其 在找到 "return address" 之前封闭函数,即 指向该函数最终 return;重复这个 过程产生一个堆栈。因此,尽管应该读取堆栈 从上到下作为一系列 return 地址,通常是 作为一系列来电者从另一个方向阅读。注意 程序相应的方式的微妙之处 说明组装意味着这第二个解释 有时会产生误导。
为了扩展上面的示例,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()将是序列 通常会占据连续区域的指令 记忆。调用其中一个函数仅涉及 记下完成后 return 的位置(即 return 地址),然后跳转到对应的内存地址 到函数的开始。函数的起始地址和大小是 记录在嵌入对象中的 "symbol table" 中;它是 通过阅读这个 table 调试器能够找到函数 包含给定位置,例如 return 地址。因此一个 在中的特定点一个函数可以用一个偏移量来描述, 通常以十六进制表示,从头开始。所以一个更好的 上面堆栈的版本可能是
libc.so`pause()+0x12
libfoo.so`c()+0x42
libfoo.so`b()+0x12
libfoo.so`a()+0x12
此时,开发者可以在libfoo.so上使用"disassembler" 显示 c() 中的指令;与 c() 的比较 源代码将允许他揭示具体的行 调用了 pause()。
在结束对堆栈的描述之前,值得做的 再观察一次。鉴于存在足够的“调试 data" 在库中,例如 libfoo,更好的调试器将能够 加倍努力并显示源代码文件名和 行号而不是每个 "frame" 中的十六进制偏移量 堆栈。
所以现在,return 到你问题中的堆栈, libpython(2.7.so.1) 是一个库,其函数执行该作业 执行 Python 脚本。 Python 脚本中的函数是 即时转换为 executable 指令,所以我猜是 片段
libpython2.7.so.1`0x800b33250
libpython2.7.so.1`PyEval_EvalFrameEx+0x4e2f
libpython2.7.so.1`PyEval_EvalCodeEx+0x665
表示 PyEval_EvalFrameEx() 是 libpython 中的功能 本身调用 Python 函数(即写在 Python) 驻留在地址 0x800b33250 附近的内存中。一种 简单的调试器可以看到这个地址属于 libpython 但是 不会在库的符号 table 中找到相应的条目; 别无选择,它只打印 "raw" 地址。
所以,您需要查看 Python 脚本,看看它是什么 正在做,但不幸的是,没有迹象表明 堆栈的 Python 组件中的函数。
有几种方法可以继续。首先是找到一个 libpython 的版本,如果存在,带有 "DTrace helper"。这个 是一些额外的功能,可以让 DTrace 查看 Python 程序本身除了周围 执行。结果是每个 Python 帧将是 在Python源代码中用相应的点进行注释。
如果您使用的是 Solaris,另一个方法是使用 pstack(1);这有 对 Python.
的原生支持最后,尝试特定的 Python 调试器。
同样值得指出的是,您的 dtrace 调用将显示 你看到的所有堆栈,按受欢迎程度排序,无论何时 程序 "python2.7" 进行系统调用。从你的描述来看, 这可能不是你想要的。如果你想了解 挂起的行为然后你可能想从一个开始 python2.7 进程 在 挂.