我正在尝试在gdb中调试一些基于STL的C ++代码。 代码有类似
的内容int myfunc()
{
std::map<int,int> m;
...
}
现在在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 );
}
}
然后在gdb做
(gdb) call printmap( m )
这似乎是一个处理问题的好方法......但我可以将printmap放入一个单独的目标文件(甚至是动态库),然后我在运行时加载到gdb而不是将其编译成我的二进制文件 - 作为重新编译每次我想查看另一个STL变量的二进制文件都不好玩..编译和加载打印例程的单个.o文件可能是可以接受的。
更新:
Nikolais的建议提示我正在看dlopen / dlsym。
所以我还没有开始工作,但感觉我越来越近了。
在printit.cpp
#include <stdio.h>
extern "C" void printit()
{
printf("OMG Fuzzies");
}
使用
编译成.sog++ -Wall -g -fPIC -c printit.cpp
g++ -shared -Wl,-undefined,dynamic_lookup -o printit.so printit.o
启动我的测试应用程序并使用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
如此接近,但由于某种原因,我无法得到那个符号......(如果我放了它,我甚至无法得到它 dlopen / dlsym在我的可执行文件中调用)我猜我正在编译lib错误或使用dlsym错误。
如果我能得到符号,我假设我可以用
之类的东西调用函数(gdb) print (( void(*)() )(0x....))()
我正在OS X 10.4上编译这个,这可能会导致我的一些.so困境......任何指针都会受到赞赏。
了解如何使所有这些工作。已在下面发布解决方案。
答案 0 :(得分:6)
所以我的解决方案是使用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
应用程序上的-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);
}
不知道为什么我选择使用stdio而不是iostream的东西......我猜你也可以使用它们。 (只是不要忘记刷新流......)
现在我的应用程序文件如下所示:
#include <map>
int main()
{
std::map<int,int> m;
m[1]=2;
m[2]=5;
m[3]=10;
m[4]=17;
}
这是一个示例调试会话(删除了一些输出)
启动应用程序并在有趣的地方休息
(gdb) break main
(gdb) run
Reading symbols for shared libraries +++. done
Breakpoint 1, main () at test.cpp:5
5 std::map<int,int> m;
加载调试助手库
(gdb) print (void*) dlopen("debug_helper.so",2)
Reading symbols for shared libraries . done
$1 = (void *) 0x100270
(gdb) n
6 m[1]=2;
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
让我们为printMap添加更多信息。 首先卸载库。
(gdb) print (int) dlclose($1)
$2 = 0
编辑源以添加条目总和。重新编译然后加载 将新库重新导入gdb(无需重新启动可执行文件或gdb)
(gdb) print (void*) dlopen("debug_helper.so",2)
Reading symbols for shared libraries . done
使用修改后的功能
$3 = (void *) 0x100270
(gdb) call printMap(m)
Map of size 3
1 : 2
2 : 5
3 : 10
SUM = 17
我认为这可以做我需要的一切。
答案 1 :(得分:2)
据我所知,你所要求的并不是直接可能的。虽然有一个接近的替代方案(谁说另外一个级别的间接?:)
使用所有打印机例程构建单独的动态库,然后将延迟加载打印包装器添加到您的程序中。我的意思是:
/// this is in your program, lazy print wrapper
void print_map( const std::map<int,int>& m ) // NOTE THE CONST REFERENCE
{
static bool loaded = false;
static void* print_lib = 0;
static void (*print_func_ptr)( const std::map<int,int>& ) = 0;
if ( !loaded )
{
// dlopen dynamic lib, check for errors, assign to print_lib
// dlsym the library function by name, assign to print_func_ptr
loaded = true;
}
print_func_ptr( m );
}
然后你可以在gdb会话中调用print_map
,库就会自动加载。
请注意,上面的代码通过 const reference 接受地图。您在问题中添加的功能将使其参数的 副本 。
另外,请查看here以获取某些方法,使gdb为STL容器生成更好的输出。
答案 2 :(得分:1)
我建议你看一下:http://sourceware.org/gdb/wiki/STLSupport
有几种不同的方式来显示STL容器(腿部工作已经为你完成)。任何选项都需要您在配置后重新启动gdb,但最好还是以后再使用。