为什么 Android 应用程序使用的本机共享库的调试速度很慢?

发布于 2024-12-14 02:21:45 字数 119 浏览 6 评论 0原文

我经常使用 gdbServer 来调试远程 Android 应用程序。我设置断点的区域是一个共享库,用 C++ 编写。

单步执行代码非常慢。有谁知道这是为什么?我的假设是对库的 JNI 调用会造成很大的延迟。

I am frequently using gdbServer to debug a remote Android application. The area I have set breakpoints in is a shared library, written in c++.

Stepping through the code is extremely slow. Does anyone know why this is? My assumption is that the JNI calls to the library impose a large delay.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一抹淡然 2024-12-21 02:21:45

我的假设是对库的 JNI 调用会造成很大的延迟。

当您处于断点处,并在 GDB 中执行 step 命令时,实际上没有发生任何 JNI 调用(您已经处于本机代码中,只需继续直到下一行,或单步进入下一个函数,什么是JNI 与它有关吗?)

不幸的是,即使在本机执行时,step 也可能很慢;对于优化的代码尤其如此。

step 命令如何工作?理论上,GDB可以检查当前行的指令,发现没有CALLJMP,并设置下一行第一条指令的临时中断,并继续。这会很快,但 GDB 实际工作原理并非如此。

相反,它所做的事情更简单:它单步执行处理器,并在每条指令处询问“我现在停止在上次停止的同一行上吗?”。如果“是”,则再次单步执行,直到答案为“否”。您可以通过设置 set debug infrun 1 来观察此行为。

根据当前行的指令数量,可能需要 100 个单步才能完成 step 命令。对于本机调试来说,这可能会很慢,当使用远程 gdbserver 时,它可能会变得更慢,因为每次单步完成时,GDB 都需要询问 gdbserver“我现在在哪里”。 GDB 和 gdbserver 之间有大量数据包传输。您可以使用set debug remote 1观察这些数据包。

的因素

  • 因此, GDB 远程协议是“喋喋不休”
  • ,每个数据包都需要通过(相对)较慢的链路发送到设备并返回,并且
  • 单个步骤可能涉及其中的 100 个

因素产生您观察到的非常慢的step执行。

一种可能的解决方法是避免执行步骤。相反,设置断点,并检查每个断点的程序状态。最终您将到达错误“下游”的断点(即程序已经处于错误状态)。现在在其“上游”某处设置一个新断点,然后查看那里的状态。使用“分而治之”的方法,您很快就会将问题归零。

My assumption is that the JNI calls to the library impose a large delay.

When you are at a breakpoint, and execute step command in GDB, no JNI calls are actually happening (you are already in native code, and just continue until next line, or step into the next function, what's JNI got to do with it?)

Unfortunately, step could be slow even when executing natively; especially so for optimized code.

How could step command work? In theory, GDB could examine instructions for the current line, discover that there are no CALLs and JMPs, set a temporary break of the first instruction on the next line, and continue. That would be fast, but that's not how GDB actually works.

What it does instead is something simpler: it single-steps the processor, and at each instruction asks "am I now stopped on the same line I was last time I stopped?". If "yes", single-step again, until the answer is "no". You can observe this behavior by setting set debug infrun 1.

Depending on how many instructions your current line has, it could take a 100 single-steps to complete your step command. That can be slow with native debugging, it can become much slower still when using remote gdbserver, as every time single-step completes, GDB needs to ask gdbserver "where am I now". That's a lot of packets flying between GDB and gdbserver. You can observe these packets with set debug remote 1.

Thus the factors that

  • GDB remote protocol is "chatty",
  • that each packet needs to go to the device and back over a (relatively) slow link, and
  • that a single step may involve a 100 of these

combine to produce the very slow step executions you've observed.

A possible workaround is to avoid doing steps. Instead, set breakpoints, and examine program state at each. Eventually you'll get to a breakpoint that is "downstream" from the bug (i.e. the program is already in a bad state). Now set a new breakpoint somewhere "upstream" from that, and look at the state there. Using "divide and conquer" approach, you'll pretty soon zero in on the problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文