LINUX.ORG.RU

GDB: можно ли отследить изменение ячейки памяти?

 , , , ,


0

1

В GDB есть возможность поставить breakpoint либо на конкретную команду, либо на изменение выражения (то есть переменной). Вроде я других возможностей не увидел. Но я могу ошибаться и потому спрошу.

А можно ли в DGB так сделать, чтобы он остановил выполнение программы в тот момент, когда у меня изменилось значение в определенной ячейке памяти? Мне нужно отследить изменение значения регистра (но не аппаратного типа r1-r15, а регистра периферии, в отладчике они выглядят как область памяти с заданным адресом).

Я знаю адрес этой ячейки, но не могу превратить его в переменную и поставить на нее watch, так как этот адрес зарезервирован под нужный регистр.

ARM11.

Может есть извращенный способ такое сделать?

(gdb) help watch Set a watchpoint for an expression. Usage: watch [-l|-location] EXPRESSION A watchpoint stops execution of your program whenever the value of an expression changes. If -l or -location is given, this evaluates EXPRESSION and watches the memory to which it refers.

anonymous ()

чтобы он остановил выполнение программы в тот момент, когда у меня изменилось значение в определенной ячейке памяти?

Это называется hardware breakpoint (hbreak в gdb). Требует поддержки в железе. В x86 можно ставить такие точки останова на запись, чтение-запись и выполнение. В ARM'ах, судя по гуглу, что-то тоже есть.

i-rinat ★★★★★ ()

watch *(int*)0x80801000 - отличное выражение,

вот только будет ли работать с аппаратным регистром? У watch есть два режима работы аппаратный (если возможно и не указано иное) или софтварный. В первом случае обращения процессора (а вовсе не все обращения) анализируются на использование адреса (посему возможен часто даже rwatch). Изменение аппаратного регистра (если мы не пытаемся его записать) извне мы не заметим. Во втором случае делается остановка после каждой операции (что на ARM без JTAG м.б. гумозно) и проверка состояния выражения, если лишнее чтение решистра ни к чему фатальному не приводит, то можем и заметить, вот только скорость будет ниже плинтуса.

Т.е. главный вопрос в том как/кем меняется регистр. Если процессором - то ловите на здоровье.

io ★★ ()
Ответ на: комментарий от i-rinat

В x86 можно ставить такие точки останова на запись, чтение-запись и выполнение.

Причём количество одновременно действующих точек ограничено.

anonymous ()
Ответ на: комментарий от anonymous

количество одновременно действующих точек ограничено.

Да, четыре на x86.

i-rinat ★★★★★ ()
Ответ на: комментарий от io

Так и сделал: watch *(int*)0xcb010024, теперь выполнение постоянно останавливается и память при этом не меняет своего содержания. Хотя watch по документации должен реагировать только на запись.

Заметил что меняется часто значение в 0xcb010064, может он смещение какое-то добавляет?

elusive ()
Ответ на: комментарий от elusive

В консоли gdb говорит что аппаратная метка:

(FME-Gdb)watch *(int*)0xcb010024 Hardware watchpoint 4: *(int *) 3405840420

elusive ()
Ответ на: комментарий от elusive

Это же от типа регистра зависит, что за тип устройства? Формально gdb волен для аппаратных watch поступить по разному. Либо отметить сам факт записи (это в debug-регистре есть), либо дополнительно проверить что значение поменялось. Если чтение из аппаратного регистра меняет его содержимое, или вообще чтение не предусмотрено, то можно ничего и не заметить. Обычно в соседних регистрах всегда что-то моргает - аппаратура однако :-)

io ★★ ()
Ответ на: комментарий от io

«Либо отметить сам факт записи (это в debug-регистре есть), либо дополнительно проверить что значение поменялось. »

Я не понял разницы: если установлен факт записи, то и в окошке, где просматривается память, все должно измениться на новое. А где найти debug-регистр?

«Это же от типа регистра зависит, что за тип устройства? »

Имеется ввиду чтение/запись? В данном случае регистр R/W.

elusive ()
Ответ на: комментарий от elusive

Как работает gdb при использовании аппаратного watch? Очень просто. Процессор имеет несколько регистров для указания адресов которые надо анализировать на обращение или запись. Набор регистров перегружается при переключении процессов. gdb через системный вызов указывает какие адреса в отлаживаемом процессе его интересуют. При переключении на отлаживаемый процесс соответсвующие адреса загружаются и в статусных регистрах (для каждого из адресов) сбрасываетс факт события. При выполнении операции с соответсвующим адресом генерируется прерывание, что и позволяет поймать соответсвующую инструкцию. При этом не факт, что было записано другое значение или возможно чтение другого регистра.

В чем фокус аппаратных регистров:

1. Для каждого из битов может быть свое правило поведения. Он может быть readonly/writeonly, может сбрасываться в 0 записью 1 (типично для регистра прерывания) или указанием маски при записи (GPIO на NVIDIA). Может менятся и при чтении. Может (serial interface) после записи переключатся на вторую половину регистра.

2. Из этого ясно, что после записи некоторого значения мы можем наблюдать в общем случае что угодно.

3. Значения аппаратного регистра могут менятся внешней логикой, которая через отладочные регистры не анализируется.

4. Возможно с данными регистрами еще как-то работает ОС. Ее изменения gdb тоже покажутся атомарными. Например, разрешаем прерывание, оно частично обрабатывается драйвером, внешний процесс замечает запись, но обнаруживает что прерывание не разрешено.

Если хочется поиграть с аппаратным регистром, то можно прямо в gdb проимитировать запись в регистр и посмотреть что будет.

io ★★ ()
Ответ на: комментарий от elusive

Так и сделал: watch *(int*)0xcb010024, теперь выполнение постоянно останавливается и память при этом не меняет своего содержания. Хотя watch по документации должен реагировать только на запись.

Делай после остановов

(gdb) p *(int*)0xcb010024

Точно не меняется?

anonymous ()
Ответ на: комментарий от anonymous

Поглядел так, точно не меняется:

(FME-Gdb)watch *(int*)0xcb010024
Hardware watchpoint 3: *(int *) 3405840420

(FME-Gdb)p *(int*)0xcb010024
$1 = 0

Program received signal SIGTRAP, Trace/breakpoint trap.
0x21707474 in main () at src/main.c:3124

(FME-Gdb)p *(int*)0xcb010024
$2 = 0

Program received signal SIGTRAP, Trace/breakpoint trap.
0x21707478 in main () at src/main.c:3124

(FME-Gdb)p *(int*)0xcb010024
$3 = 0

Program received signal SIGTRAP, Trace/breakpoint trap.
0x21867678 in memset (m=0x21e00020, c=0, n=2339824) at ../../../../../../src/newlib-1.19.0/newlib/libc/string/memset.c:80

(FME-Gdb)p *(int*)0xcb010024
$4 = 0

Program received signal SIGTRAP, Trace/breakpoint trap.
Lmemcpy_bloop32 () at src/arm1176_memcpy_gcc.s:258
Current language:  auto; currently asm

(FME-Gdb)p *(int*)0xcb010024
$5 = 0
elusive ()
Ответ на: комментарий от anonymous

Кстати

Могут ли на работу watch в GDB влиять используемая ОС, наличие мультитрединга? Ось RTOS.

В моем отладчике не работают все функции, относящиеся к отладке многопоточной программы, например, просмотреть список тредов и т.д. Кстати я так и не понял можно ли как-то настроить эти функции или нет.

Таким отладчиком многопоточку отлаживать сложно, но кроме него нет других средств. Возможно watch с ума сходит оттого, что функции GDB здесь ограниченны.

elusive ()
Ответ на: Кстати от elusive

Если на каждый p *(int*)0xcb010024 выдается Program received signal SIGTRAP, Trace/breakpoint trap, то у ОС большие проблемы с поддержкой отладчика. Это было бы логичнее для rwatch. Т.ч. возможно некорректно проинициализированы DEBUG-регистры. Вообще, вероятно, не происходит их перезагрузка при переключении межде процессами.

Кстати, а почему вызывается дцать раз p (и почему ожидается изменение?) и ни разу continue или что иное?

Настроить функцию просмотра нитей и т.п. нельзя! gdb должен быть корректно портирован и собран для работы с системой, ну и сама система должна позволять все посмотреть тем или иным способом - способов для RTOS много! Бывают нетривиальные извраты. Что за RTOS?

io ★★ ()
Ответ на: комментарий от io

Если на каждый p *(int*)0xcb010024 выдается Program received signal SIGTRAP, Trace/breakpoint trap, то у ОС большие проблемы с поддержкой отладчика.

В консоли не отображаются команды continue, т.к. они случаются в другом окне. На самом деле последовательность такая:

set breakpoint p continue stop p continue stop p

и т.д.

Возможно некорректно проинициализированы DEBUG-регистры.

А кто и где их инициализирует?

gdb должен быть корректно портирован и собран для работы с системой, ну и сама система должна позволять все посмотреть тем или иным способом - способов для RTOS много!

То есть встроить функции можно, но не тривиально, как я понял? Запуск gdb у меня идет из системы makeфайлов для проекта. RTOS специально написанная производителем микросхемы, но могу сказать что включает в себя самые простейшие основные возможности RTOS и ничего лишнего.

elusive ()
Ответ на: комментарий от elusive

Кто и как инициализирует DEBUG-регистры.

Реально это делает планировщик задач в ядре при переключении на соответствующую нить. Вся цепочка следующая:

1. Пользователь задает watch.

2. gdb решает использовать hw-режим и передает ptrace запрос ядру. Пример кода можно найти в arm-linux-nat.c. При этом задается tid, регистры и его значение. В стиле:

       if (ptrace (PTRACE_SETHBPREGS, tid, dir * ((i << 1) + 1),
                    &bpt->address) < 0)

3. Ядро запоминает данные для планировщика. Можно найти ptrace_sethbpregs в arch/arm/kernel/ptrace.c.

tsk->thread.debug.hbp[idx]

4. Как часть действий планировщика выполняется и инициализация DEBUG-регистров.

5. Если приходит прерывание то обработчик его анализирует и отправляет сообщение gdb. arm_linux_stopped_by_watchpoint проверяет что watchpoint сработал.

В нашем случае возможно не очень корректно в gdb задаются значения регистров и судя по диагностики факт срабатывания не обнаруживается.

В источниках gdb и ядра (вот кроме linux сейчас ни на что не сошлюсь) можно найти и все про многозадачность на основе того же ptrace.

io ★★ ()
Ответ на: комментарий от io

io, если я правильно понял то DEBUG-регистры это некоторые ячейки памяти камня, наравне с периферийными, которые используются при активации jtag. Навряд ли такое описание дается в datasheet, надо в код смотреть. Код чего? Был бы линукс, то в код ядра. У меня в распоряжении только RTOS и мое приложение с различными модулями и драйверами.

elusive ()
Ответ на: комментарий от elusive

Эти регистры являются частью CP14. Описание можно найти в Technical Reference Manual для соответствующего процессора. Впрочем как всегда можно действовать по образцу. Да, может оказаться, что что-то должно быть выдано на пин разрешения отладки под JTAG (был фокус на OMAP). Но, судя по диагностики, это м.б. и включено. Смотреть надо в код ядра и gdb.

io ★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.