Добавить функции в GDB во время выполнения - PullRequest
8 голосов
/ 09 апреля 2010

Я пытаюсь отладить некоторый код C ++ на основе STL в gdb. Код имеет что-то вроде

int myfunc()
{
   std::map<int,int> m;
   ...
}

Теперь в gdb, внутри myfunc использование print m дает что-то очень ужасное. То, что я видел рекомендованным, это компилирование что-то вроде

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 файла для процедуры печати могут быть приемлемыми.


UPDATE:

По подсказке Николая я смотрю на dlopen / dlsym.

Так что у меня еще не получилось, но мне кажется, что я все ближе.

In 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

Запустите мое тестовое приложение и загрузите .so, используя dlopen (2 = RTLD_NOW), затем попытайтесь получить символ для функции отладки, используя 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 ]

7 голосов
/ 10 апреля 2010

Таким образом, мое решение - загрузить общий объект, содержащий мои процедуры отладки, во время выполнения, используя 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

Я думаю, что это делает все, что мне нужно.

2 голосов
/ 09 апреля 2010

Насколько я знаю, то, что вы просите, напрямую невозможно. Хотя есть близкая альтернатива (кто сказал, что еще один уровень косвенности?)

Создайте отдельную динамическую библиотеку со всеми процедурами вашего принтера, затем добавьте lazy load упаковщики для печати в вашу программу Под этим я подразумеваю что-то вроде:

/// 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 );
}

Затем вы можете вызвать print_map в сеансе GDB, и библиотека загрузится автоматически. Обратите внимание, что приведенный выше код принимает карту как постоянная ссылка . Функция, которую вы задали в вопросе, создаст копию своего аргумента.

Также посмотрите здесь о некоторых способах заставить GDB производить лучший вывод для контейнеров STL.

1 голос
/ 10 апреля 2010

Я бы посоветовал взглянуть здесь: http://sourceware.org/gdb/wiki/STLSupport

Существует пара различных способов отображения контейнеров STL (и работа с ногами для вас уже сделана). Любая опция потребует от вас перезапустить GDB после того, как вы настроите его, но это будет хорошо для дальнейшего использования.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...