跳转到主要内容

Python 3 运行进程内存分析器

项目描述

Python3 内存分析器,用于运行进程

正在追踪内存泄漏?不想添加调试代码,甚至停止进程?

你来了对的地方。

memory_analyzer

运行内存分析器不需要你停止进程或添加任何特殊的调试代码或标志。运行分析不会(呃,不应该)中断你的进程,尽管你的进程(及其所有线程!)将在内存分析器收集内存中对象信息时被暂停。

你需要在目标二进制文件的库路径中安装 objgraph 和 pympler,除了运行前端所需的 requirements*.txt 中的依赖项。

许可证

此源代码根据 MIT 许可证授权。有关更多信息,请参阅根目录中的 LICENSE。

你可以找到的信息

  1. 内存中每种对象的数量

  2. 所有这些对象的总大小

  3. 对象的前向引用

  4. 对象的反向引用

  5. 对象的大小/数量随时间的变化(通过快照比较)

示例输出

Example Output Screenshot

如何运行

  1. 找到你的进程 PID

    ps aux | grep <your process>
    
  2. 对它运行内存分析器

    memory_analyzer run $PID
    

这将让你进入一个 ncurses UI,并在 memory_analyzer_out/memory_analyzer_snapshot-{TIMESTAMP} 位置创建一个二进制输出文件

如果你正在分析以 root 运行的进程,你也需要以 root 权限运行分析器。

在现代 ptrace 限制系统上运行

至少Ubuntu和Arch的现代版本都有一个补丁,禁止非root用户ptracing非子进程。

你可以使用以下命令来禁用这个功能

echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope

或者以root身份运行 memory_analyzer

不重新分析查看输出

如果你退出了UI但还想再次查看快照数据而不重新运行分析,你可以使用'view'命令。

memory_analyzer view <snapshot output file>

分析多个进程

你可以通过简单地提供由空格分隔的PID列表来同时分析多个进程。

memory_analyzer run 1234 4567 890

如果使用 --snapshot,它会尝试将列出的PID与快照文件中的PID配对。如果它们可以配对,将为每个类似PID创建一个新页面,比较新旧版本。如果找不到任何可以配对的PID,它将仅比较第一个新对象和第一个旧对象。

如果使用引用标志,将为列出的所有PID找到引用。

查看前向和后向引用

你可以查看前N个对象(按对象大小排序),或者你可以查找特定的对象。内存分析器将生成显示图表的PNG文件,并为你上传到phabricator以便于查看。如果你不希望它们被上传,可以使用 —no-upload 标志。

警告:获取引用是一个昂贵的操作!你获取的引用越多,你的进程暂停的时间就越长。

查看前两个对象

memory_analyzer run -s 2 $PID

查看特定对象

要查看特定对象,你必须知道它的完全限定域名。如果你不确定,我建议你首先不带标志运行分析器,并在那里识别你最感兴趣的对象名称。

memory_analyzer run $PID -ss __main__.Foo -ss builtins.str

这将获取Foo和str对象的正向和反向引用。

比较快照

比较快照可以用来显示对象随时间增长的情况。要创建初始快照,只需正常运行分析器即可

memory_analyzer run $PID --snapshot <previous snapshot file>

默认情况下,你的快照文件将保存在 memory_analyzer_out/ 目录下。快照分析将位于ncurses UI的第二页,因此当你进入UI时,请按右箭头键滚动到快照页面。

指定可执行文件

内存分析器使用在 sys.executable 中找到的可执行文件启动GDB。这可能不是你用来分析二进制文件的可执行文件。例如,你可能需要使用调试信息二进制文件。你可以使用 -e 标志指定可执行文件

memory_analyzer run $PID -e /usr/local/bin/python3.7-debuginfo

自定义输出文件

如果你不希望文件使用默认名称,请传入你自己的自定义名称。

 memory_analyzer run $PID -f test_output1

导航UI

导航UI应该是相对直观的。你可以使用箭头键或wasd上下滚动。

向上滚动:向上箭头键或 'w'

向下滚动:向下箭头键或 's'

切换到下一页:右箭头键或 'd'

切换到上一页:左箭头键或 'a'

向上翻页:Page Up 键

向下翻页:Page Down 键

跳转到页面底部:G

跳转到页面顶部:H

退出:'q'

处理多线程

目前程序不是在不停机模式下运行的,这意味着如果有多个线程,它们将在分析期间全部暂停。这可能是一些需要线程在后台实时完成的服务的问题。可能可以支持不停机模式(只暂停一个线程),但这只有在进程被内存分析器停止和重新启动的情况下才能工作(这是GDB的限制)。如果你对这个功能感兴趣,请联系我(lisroach)。

重要的是,这意味着调试多线程进程的速度比单线程慢得多,因为需要花费时间来找到和暂停每个单独的线程。如果你的进程有可能会导致暂停线程触发的超时(例如,通过网络交谈的东西),请谨慎使用内存分析器。

常见问题解答

没有返回数据

尝试启用--debug标志运行过程以获取更多信息。如果没有出现明显的错误,则程序可能已挂起且无法分析。

未加载符号表。请使用“file”命令。

这是来自GDB自身的错误,可能意味着几件事情

  1. 您未安装正确的调试信息,或者
  2. 您正在使用错误的Python可执行文件。

要修复错误的调试信息,安装与分析过程使用的Python运行时关联的调试信息。例如

sudo yum install python3-debuginfo

或sudo apt-get install python3-dbg

要解决错误的Python可执行文件,找出您需要的可执行文件(分析过程正在使用哪个Python版本),并使用--exec标志指定它

memory_analyzer run $PID -e /usr/local/bin/python3.6

它正在挂起

尝试使用--debug标志运行memory_analyzer。这将向您显示GDB当前正在做什么。

您不应该看到

$5 = <一些数字>

Inferior Detached

这些问题。请提交错误报告。memory_analyzer是一个新工具,因此尚未对所有场景进行测试,您可能遇到了一些新情况。请继续阅读以获取有关理解调试输出的更多信息。

如果您看到

GDB Stopping Many Threads

这意味着您的进程有大量的线程,并且GDB可能需要很长时间来暂停它们。

之后您将看到

Thread Debugging Finished

这意味着GDB仍在执行GDB操作,加载符号等。这通常是最耗时的,而且目前我还不知道如何解决这个问题。如果您直接运行gdb并附加到您的进程,这也会花费同样长的时间 :(

直到您看到

Detached

这表示分析器本身正在运行。通常这运行得非常快,您几乎看不到这些消息闪烁。

如果您没有看到上面的内容,但看到了

Analyzing and Detached

这通常意味着您没有分析正确的进程。也许您的进程不是Python(也许您在buck构建时分析了)?

它在Windows上运行吗

不。如果您有兴趣让这个功能在Windows上可用,应该可以添加这个功能,让lisroach知道。

它支持Python 2吗

不,很遗憾,这仅限于Python 3,并且可能保持这种状态。

开发

任何帮助都受欢迎并受到赞赏!

由以下支持