在运行时在gdb中添加函数

时间:2010-04-09 02:14:44

标签: c++ gdb

我正在尝试在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");
}

使用

编译成.so
g++ -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困境......任何指针都会受到赞赏。


了解如何使所有这些工作。已在下面发布解决方案。

3 个答案:

答案 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,但最好还是以后再使用。