-Поиск по дневнику

Поиск сообщений в rss_rss_hh_new

 -Подписка по e-mail

 

 -Статистика

Статистика LiveInternet.ru: показано количество хитов и посетителей
Создан: 17.03.2011
Записей:
Комментариев:
Написано: 51


gdb-дуэль — списки, деревья и хэш таблицы против командной строки

Среда, 14 Июня 2017 г. 13:56 + в цитатник

Первый раз я увидел команду duel в gdb на каком-то древнем IRIX-е, лет пятнадцать назад. Это была невероятно крутая штука для просмотра разных связанных списков, массивов структур, и прочих подобных конструкций. Помечтал, мол, если б в Линуксе такая была, и забыл. Лет десять назад вспомнил, погуглил — оказалось, что DUEL, это вообще-то патч 93-го года для gdb 4.6, а вовсе не что-то уникальное в IRIX. Только автор по идейным соображениям выпустил его как public domain, а gdb-шники были тоже идейные и хотели GPL, так что попасть в upstream этому патчу не грозило. Я портировал его на тогдашний gdb 6.6.2, отдал в gentoo и до выхода 7-го gdb наслаждался жизнью. Потом duel из gentoo выкинули, портировать на новый gdb было сложно, никто не взялся. А недавно я его попробовал оживить. Только вместо патча (надо собирать вместе с gdb из исходников, использует всякие внутренние gdb-шные функции) я его написал с нуля на питоне. Теперь Duel.py (так называется новая реализация Duel-а) грузится в gdb на лету, и, надеюсь, Python API не будет меняться от версии к версии так, как недокументированные gdb-шные потроха. Итак, встречайте: DUEL — высокоуровневый язык анализа данных для gdb.


Примеры


Сразу, чтоб показать, на что он способен:

(gdb) dl table_list-->next_local->table_name
tables->table_name = 0x7fffc40126b8 "t2"
tables->next_local->table_name = 0x7fffc4012d18 "t1"
tables-->next_local[[2]]->table_name = 0x7fffc4013388 "t1"

Это из отладки MariaDB. Команда проходит односвязный список структур TABLE_LIST и для каждого элемента списка выводит TABLE_LIST::table_name.

(gdb) dl longopts[0..].name @0
longopts[0].name = "help"
longopts[1].name = "allow-suspicious-udfs"
longopts[2].name = "ansi"
<... cut ...>
longopts[403].name = "session_track_schema"
longopts[404].name = "session_track_transaction_info"
longopts[405].name = "session_track_state_change"

Оттуда же (я вырезал адреса, чтоб не захламлять текст). Есть массив структур, задающий опции командной строки. Команда выводит только имена опций, проходя весь массив до name == 0. А можно просто посчитать, сколько их:

(gdb) dl #/longopts[0..].name @0
#/longopts[0..].name@0 = 406


Основная идея


Duel построен на том, что выражение может возвращать много значений. Например,

(gdb) dl 1..4
1 = 1
2 = 2
3 = 3
4 = 4

или вот

(gdb) dl my_long_options[1..4].(name,def_value)
my_long_options[1].(name) = "allow-suspicious-udfs"
my_long_options[1].(def_value) = 0
my_long_options[2].(name) = "ansi"
my_long_options[2].(def_value) = 0
my_long_options[3].(name) = "autocommit"
my_long_options[3].(def_value) = 1
my_long_options[4].(name) = "bind-address"
my_long_options[4].(def_value) = 0

При этом такие выражения-генераторы допускается использовать везде, где ожидается одно значение. Это можно рассматривать как автоматический цикл по всем возможным значениям. А во втором примере даже два цикла, по индексам массива и по полям структуры. Такая штука работает очень хорошо, когда надо пройтись по массиву, или списку, или, например, дереву. Специальные операторы позволяют удобно записывать условия остановки генератора.

Операторы


Синтаксис похож на С, и C-шные операторы работают, как обычно. Гораздо интереснее, новые, специфичные для DUEL, операторы. Рассмотрим самые полезные из них:

Диапазон и перечисление, .. и ,


Выше я приводил пример обоих операторов. Это знакомые конструкции, они есть и в других языках. При этом в диапазоне можно опустить один из концов. Если указать только конец диапазона, например, ..20, то диапазон начнется с нуля и в нем будет 20 значений, так же, как если бы было написано 0..19. Если же указать только начало, то получится открытый диапазон! Чтобы duel не продолжал генерировать числа до тепловой смерти вселенной (или до переполнения счетчика, смотря что случится раньше), вместе с открытым диапазоном обычно используют оператор остановки по условию, @.

Остановка по условию, @


В выражении x@y, выражение x будет генерировать значения до тех пор, пока y ложно. Например,

(gdb) dl arr[0..]@(count > 10)

И duel будет выводить элементы массива arr[] до тех пор, пока arr[i].count будет не больше десяти.

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

(gdb) dl str[0..]@0

вернет все символы строки, вплоть до '\0'. Более практичный пример — вывести все опции командной строки из argv:

(gdb) dl argv[0..]@0
argv[0] = "./mysqld"
argv[1] = "--log-output=file"
argv[2] = "--gdb"
argv[3] = "--core-file"


Хотя тот же эффект достигается и

(gdb) dl argv[..argc]
argv[0] = "./mysqld"
argv[1] = "--log-output=file"
argv[2] = "--gdb"
argv[3] = "--core-file"


Перейти по указателю, -->


Генератор a-->b порождает множество значений a, a->b, a->b->b, и так далее, пока не уткнется в NULL. Я уже приводил пример, как таким образом можно пройтись по односвязному списку. Но это точно так же работает и для деревьев, например:

(gdb) dl tree-->(left,right)->info


Вычисляющие скобки {}


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

(gdb) dl i:=5
i = 5
(gdb) dl i+6
i+6 = 11
(gdb) dl {i}+6
5+6 = 11
(gdb) dl {i+6}
11 = 11

Это, в основном, нужно для массивов:

(gdb) dl if (my_long_options[i:=1..20].name[0] == 'd') my_long_options[i].name
if(my_long_options[i].name[0] == 'd') my_long_options[i].name = "debug-abort-slave-event-count"
if(my_long_options[i].name[0] == 'd') my_long_options[i].name = "debug-assert-on-error"
if(my_long_options[i].name[0] == 'd') my_long_options[i].name = "debug-assert-if-crashed-table"
if(my_long_options[i].name[0] == 'd') my_long_options[i].name = "debug-disconnect-slave-event-count"
if(my_long_options[i].name[0] == 'd') my_long_options[i].name = "debug-exit-info"
(gdb) dl if (my_long_options[i:=1..20].name[0] == 'd') my_long_options[{i}].name 
if(my_long_options[i].name[0] == 'd') my_long_options[16].name = "debug-abort-slave-event-count"
if(my_long_options[i].name[0] == 'd') my_long_options[17].name = "debug-assert-on-error"
if(my_long_options[i].name[0] == 'd') my_long_options[18].name = "debug-assert-if-crashed-table"
if(my_long_options[i].name[0] == 'd') my_long_options[19].name = "debug-disconnect-slave-event-count"
if(my_long_options[i].name[0] == 'd') my_long_options[20].name = "debug-exit-info"

Тут фигурные скобки сразу показывают, какие элементы массива удовлетворяют условию.

Фильтры

https://habrahabr.ru/post/328180/

Метки:  

 

Добавить комментарий:
Текст комментария: смайлики

Проверка орфографии: (найти ошибки)

Прикрепить картинку:

 Переводить URL в ссылку
 Подписаться на комментарии
 Подписать картинку