【问题标题】:Debugging techniques without debugging tools无需调试工具的调试技术
【发布时间】:2012-03-21 07:25:50
【问题描述】:

我发现自己不得不在几乎没有任何调试工具的情况下调试 Qt 应用程序:该应用程序似乎开始使用越来越多的 CPU,因为它一次又一次地运行相同的操作;几个小时后,CPU 完全饱和。

应用程序在 gdb 似乎无法工作的 ARM Linux 嵌入式设备上运行,可能是提供的工具链难以发现问题。 strace 似乎只报告计时器活动(这是一个 OpenGL 应用程序,所以这是预期的)。 ltrace 不可用,编译它导致了一项艰巨的任务,也许没用。应用程序不是我写的,但有源代码。

我还能做些什么来发现应用程序在消耗这么多资源时忙于做什么?我必须以任何方式跟踪应用程序所做的所有方法调用吗?是否有任何其他技术可以用来尝试猜测问题或将注意力集中在哪里?

编辑:这是 gdb 的问题之一:Only question marks in backtrace reported by gdb on ARM。即使编写一个十行的应用程序来模拟段错误也会导致这种情况。

【问题讨论】:

  • 你试过远程调试吗?
  • 用gdb远程调试?尝试了几个小时没有成功。 valgrind 也有问题。没有人能够让这些工具在这个平台上工作。还要考虑系统库都被剥离了。
  • 能否通过外部端口输出文本,例如串口还是网络?然后你可以添加日志记录。或者只是将日志写入文件。
  • 是的,我可以随心所欲地放置日志。但是考虑到这是一个很大的代码,有数千行代码还使用了一些可能隐藏错误的外部库。有没有办法自动放置日志?
  • 你提到源是可用的,我会认真考虑在支持良好的平台上重新编译(实际上是任何 *nix 系统),看看那里是否发生错误。该错误很可能是适用的,而不是依赖于平台的。作为奖励,您将获得调试符号。

标签: c++ debugging qt4 embedded


【解决方案1】:

你能在机器上启用核心转储吗?然后,当它正在播放时,您可以向它发送 SIGABRT 并将核心转储复制到您的开发机器,并使用交叉调试器检查它,并提供源代码和未剥离的可执行文件。

下次吸取教训也很重要,不要使用这么糟糕的工具链。

如果它是一个选项,如果不支持 gdb,您可以尝试另一个至少具有 gdbserver 的工具链。我对 CodeSourcery ARM Lite 工具链非常满意。

编辑:适合您情况的 gdb 有两种风格:

  • 在您的开发主机上运行的跨 gdb
  • 在您的目标上运行的本机 gdb

gdbserver 允许您在开发主机上运行跨 gdb 并连接到目标以远程调试在其上运行的东西。因此,核心转储或 gdbserver 是使用跨 gdb 来检查目标上的某些内容的两种方法,但单独使用 gdbserver 对您没有多大帮助。

如果您的交叉编译器类似于 arm-none-linux-gnueabi-gcc,请查看您的开发主机上是否有可用的 arm-none-linux-gnueabi-gdb。

【讨论】:

  • 这很有趣!但我看到我需要 gdb 来读取转储。我认为我没有可用于该平台的 gdb,只有 gdbserver 用于远程调试,这似乎也无法正常工作。我可以用那个工具试试这个吗?
  • 这是一些非常不寻常的 ARM 设备吗?如果没有,您应该能够轻松地从源代码为它构建 gdb。
  • 这是一个很好的建议。谢谢!这是一个从头开始构建嵌入式 Linux 的设备。
【解决方案2】:

您可以尝试在您的应用程序中放置一些调试代码。

选择一些信号,例如 SIGINT。为此信号添加信号处理程序。在此处理程序中打印堆栈跟踪或至少是指令指针值。然后启动应用程序并多次发送 SIGINT 以查看您的应用程序在做什么。

【讨论】:

  • 是的,我已经尝试过了。但就像 gdb 一样,我似乎无法在回溯中获得函数的名称,这使得回溯毫无用处。 backtrace_symbols 似乎无法匹配指向字符串的指针,即使符号在我的二进制文件中也是如此。
  • 您可以随时打印指令指针。然后在地图文件中找到。
  • 我在示例代码上试过这个,并且在使用 rdynamic 导出符号时似乎可以工作!现在的问题是,由于我正在分析的应用程序中的某些未知原因,我无法捕获 SIGINT。
  • 可能你无法捕捉到它,因为它已经在应用程序的某个地方捕捉到了...然后尝试其他信号。
【解决方案3】:

尝试记录不同函数的执行时间。首先记录最有可能的候选者的执行时间,如果您已经消除了它们,请继续在您的程序中使用其他不太可能的功能。

记录消息的最简单方法是使用 std::cout(或 printf)并将输出重定向到文件,以便以后可以查看日志。

【讨论】:

    【解决方案4】:

    您可以尝试运行 ARM 版本的 Zoom 分析器 - 这应该会告诉您代码大部分时间花在哪里 - 您可以免费下载它并获得 30 天评估许可证。

    【讨论】:

      【解决方案5】:

      假设 gcc 有类似 MSVC 文件和行宏的东西,可以扩展到当前文件和当前行,你可以制作自己的伪分析函数。将其放在标题中:

      void MyPseudoProfileLog(const char* file, int line, int* count);
      #define MY_PSEUDO_PROFILE_LOG(file, line) { static int count = 0; MyPseudoProfileLog(file, line, &count); }
      

      这在一个 cpp 文件中(如果你把它放在头文件中,你将获得静态变量的多个副本,每个包含头文件的 cpp 文件一个):

      void MyPseudoProfileLog(const char* file, int line, int* count)
      {
          static FILE* f = NULL;
          const int max_count = 1000;
          if (++(*count) == max_count)
          {
              *count = 0;
              if (!f)
                  f = fopen("/tmp/my_pseudo_profile_log.log");
              fprintf(f, "file=\"%s\", line=%d was passed %d times\n", file, line, max_count);
              fflush(f);
          }
      }
      

      然后就可以粘贴了

      MY_PSEUDO_PROFILE_LOG(__FILE__, __LINE__);
      

      到代码中的各个地方查看它们被调用的频率。请记住,这不是线程安全的,因此只能在主线程中使用。

      【讨论】:

      • 放置日志行是我正在做的,是的,问题是各种库中有数百个方法调用。可能需要数周时间才能找到加载 CPU 的那些。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-11
      • 2011-11-16
      • 2010-10-14
      • 1970-01-01
      • 2012-07-09
      • 2017-05-24
      • 1970-01-01
      相关资源
      最近更新 更多