在运行时在gdb中添加函数

Mic*_*son 8 c++ gdb

我正在尝试在gdb中调试一些基于STL的C++代码.代码有类似的东西

int myfunc()
{
   std::map<int,int> m;
   ...
}
Run Code Online (Sandbox Code Playgroud)

现在在gdb中,使用"print m"在myfunc里面给出了非常难看的东西.我所看到的建议是编译类似的东西

void printmap( std::map<int,int> m )
{
   for( std::map<int,int>::iterator it = ... )
   {
      printf("%d : %d", it->first, it->second );
   }
}
Run Code Online (Sandbox Code Playgroud)

然后在gdb中做

(gdb) call printmap( m )
Run Code Online (Sandbox Code Playgroud)

这似乎是一个处理问题的好方法...但是我可以将printmap放入一个单独的目标文件(甚至是动态库),然后我在运行时加载到gdb而不是将其编译成我的二进制文件 - 因为每次重新编译二进制文件时间我想看看另一个STL变量并不好玩..同时为打印例程编译和加载单个.o文件是可以接受的.


更新:

提出尼古拉斯的建议我正在看dlopen/dlsym.

所以我还没有这个工作,但感觉我越来越近了.

在printit.cpp中

#include <stdio.h>

extern "C" void printit()
{
  printf("OMG Fuzzies");
}
Run Code Online (Sandbox Code Playgroud)

使用编译为.so

g++ -Wall -g -fPIC -c printit.cpp
g++ -shared -Wl,-undefined,dynamic_lookup -o printit.so printit.o
Run Code Online (Sandbox Code Playgroud)

启动我的测试应用程序并使用dlopen(2 = RTLD_NOW)加载.so然后尝试使用dlsym获取调试函数的符号.

(gdb) break main
(gdb) run
(gdb) print (void*) dlopen("printit.so", 2 )
$1 = (void *) 0x100270
(gdb) print (void*) dlsym( 0x100270, "_printit" )
$2 = (void *) 0x0
Run Code Online (Sandbox Code Playgroud)

如此接近,但由于某种原因,我不能得到那个符号......(如果我把dlopen/dlsym调用放在我的可执行文件中,我甚至无法得到它)我猜测我要么编译错误的lib,要么错误地使用dlsym.

如果我能得到符号我假设我可以用类似的东西来调用函数

(gdb) print (( void(*)() )(0x....))()
Run Code Online (Sandbox Code Playgroud)

我在OS X 10.4上编译它,这可能会导致我的一些.so困境......任何指针都会受到赞赏.


找到了如何使这一切工作.已在下面发布解决方案.

Mic*_*son 7

所以我的解决方案是使用dlopen在运行时加载包含我的调试例程的共享对象.事实证明,当你获得所有编译标志时,它比我想象的更简单.

在OS X上,这意味着您可以像这样编译应用程序和调试对象:

all : application.x debug_helper.so

application.x : application.cpp
    g++ -g application.cpp -o application.x -fPIC

debug_helper.so : debug_helper.o
    g++ -dynamiclib -o debug_helper.so debug_helper.o

debug_helper.o : debug_helper.cpp
    g++ -Wall -g -fPIC -c debug_helper.cpp
Run Code Online (Sandbox Code Playgroud)

应用程序上的-fPIC是关键的,-dynamiclib也是如此(而不是尝试linux -shared标志)

一个示例debug_helper.cpp可能如下所示

#include <map>
#include <stdio.h>

extern "C" 
void printMap( const std::map<int,int> &m )
{
  printf("Map of size %d\n", int(m.size()) );
  for( std::map<int,int>::const_iterator it = m.begin(); it!=m.end(); ++it )
  {
    printf("%d : %d \n", it->first, it->second );
  }
  fflush(stdout);
}
Run Code Online (Sandbox Code Playgroud)

不知道为什么我选择使用stdio而不是iostream的东西......我猜你也可以使用它们.(只是不要忘记冲洗溪流......)

现在我的应用程序文件如下所示:

#include <map>

int main()
{
  std::map<int,int> m;
  m[1]=2;
  m[2]=5;
  m[3]=10;
  m[4]=17;
}
Run Code Online (Sandbox Code Playgroud)

并且继承了示例调试会话(删除了一些输出)

启动应用程序并在有趣的点上休息

(gdb) break main
(gdb) run
Reading symbols for shared libraries +++. done   
Breakpoint 1, main () at test.cpp:5
5     std::map<int,int> m;
Run Code Online (Sandbox Code Playgroud)

加载调试助手库

(gdb) print (void*) dlopen("debug_helper.so",2)
Reading symbols for shared libraries . done
$1 = (void *) 0x100270
(gdb) n
6     m[1]=2;
Run Code Online (Sandbox Code Playgroud)

GDB非常智能,可以为我们捕获所有新符号,因此我们不需要使用dlsym等.我们可以直接调用这些函数.

(gdb) call printMap(m)
Map of size 0
(gdb) n
(gdb) n
(gdb) n
9     m[4]=17;
(gdb) call printMap(m)
Map of size 3
1 : 2 
2 : 5 
3 : 10 
Run Code Online (Sandbox Code Playgroud)

让我们为printMap添加更多信息.首先卸载库.

(gdb) print (int) dlclose($1)
$2 = 0
Run Code Online (Sandbox Code Playgroud)

编辑源以添加条目总和.重新编译然后将新库加载回gdb(无需重新启动可执行文件或gdb)

(gdb) print (void*) dlopen("debug_helper.so",2)
Reading symbols for shared libraries . done
Run Code Online (Sandbox Code Playgroud)

使用修改后的功能

$3 = (void *) 0x100270
(gdb) call printMap(m)
Map of size 3
1 : 2 
2 : 5 
3 : 10 
SUM = 17
Run Code Online (Sandbox Code Playgroud)

我认为这可以做我需要的一切.