我正在尝试在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困境......任何指针都会受到赞赏.
找到了如何使这一切工作.已在下面发布解决方案.
所以我的解决方案是使用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)
我认为这可以做我需要的一切.