17 Ocak 2020 Cuma

gdb seçenekleri

Giriş
gdb yerine UDB de kullanılabilir. Açıklaması şöyle
UDB is another reverse debugger that works just like GDB. It is meant to be a drop-in replacement for GDB and is made to integrate with the development environment and command line.
.gdbinit Dosyası
Örnek
Şöyle yaparız
define sx
  si
  x /1i $pc
end
document sx
    Step one instruction and print next instruction
end
define nx
  ni
  x /1i $pc
end
document nx
    Step one instruction running through calls and print next instruction
end
Invoking GDB
GDB c ve C++ uygulamaları için kullanılabilir. Açıklaması şöyle.
You can use GDB to debug programs written in C, C++, Fortran and Modula-2.
1. Çalışan Uygulamaya Bağlanma
Şöyle yaparız.
gdb -p "pid"
Şöyle yaparız
gdb -p ./myprogram 1234 komutu ile PID'si 1234 olan programa bağlanılabilir.

2. Core Dosyası
Örnek
komutu ile core dosyasının nerede çöktüğü incelenebilir.
gdb myprogram core

3. Uygulamak Debug için
Örnek
Uygulamamızı debug için şöyle yaparız. gdb komut satırına düşer.
gdb myprogram
Örnek
Önce gdb başlatılır
prompt$ gdb
GNU gdb (GDB) Red Hat Enterprise Linux (7.2-50.el6)
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Daha sonra debug edilecek uygulama belirtilir
(gdb) file /home/me/build-path/my-program
Reading symbols from /home/me/build-path/my-program...done.
İstenilen noktaya breakpoint konulur
(gdb) my-program-src.cpp:57
breakpoint 1 at 0x819df9b: file src/my-program-src.cpp, line 57
Debug başlatılır
(gdb) run 
 Starting program: /home/me/build-path/my-program
4. Ortam değişkeni
Çoğu debugger Linux'ta "_" ortam değişkenine değer atar. Elimizde şöyle bir kod olsun.
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
  char *path = getenv("_");
  printf("%s\n", argv[0]);
  printf("%s\n", path);
  return 0;
}
Çıktı olarak şunu alırız.
$ gcc -o main main.c
$ ./main
./main
./main
$ gdb main
...
(gdb) r
Starting program: /home/user/tmp/main
/home/user/tmp/main
/usr/bin/gdb
[Inferior 1 (process 21694) exited normally]
(gdb)

Seçenekler
--args seçeneği
Şöyle yaparız. Kabuk önümüze düşünce run komutu ile program başlatılır.
gdb --args myprogram param1 param2
(gdb) run
Bir diğer seçenek ise şöyle yapmak
gdb myprogram
(gdb) run param1 param2
-ex seçeneği
Tüm thread'lerin call stacklerini görmek için şöyle yaparız
gdb myprogram core.1234 - ex 'thread apply all bt'

-q seçeneği
quite çalıştırır. Böylece başlangıçta yazılan bir sürü satırı görmeyiz. Şöyle yaparız.
gdb -q myprogram

Reading symbols from myprogram...done.
Kabuk Komutları
yanlış komut
gdb komutu anlayamazsa şu çıktıyı verir.
(gdb) by
Undefined command: "by".  Try "help".
add-symbol-file komutu
debug symbol dosyası olmayan .so için sembol yüklemeyi sağlar.
Örnek
Şöyle yaparız.
(gdb) add-symbol-file  /home/me/build-path/lib/libMySharedLib2.so
Örnek
Önce şöyle yaparız.
(gdb) info shared
foo için from adresi $addr olsun. Daha sonra şöyle yaparız.
(gdb) add-symbol-file /path/to/foo.so.debug $addr
breakpoint komutu
b ya da break olarak kullanılır. Breakpoint gdb'nin yaptığı bir şey değildir, işletim sistem seviyesindedir.
Örnek
Adresini bildiğimiz bir satıra breakpoint koyabiliriz. Buradaki adres loader çalıştıktan yani gdb run yapıldıktan sonraki adres. Açıklaması şöyle.
(gdb) b *0x080495a3
Örnek
Metod ismine göre koyabiliriz. Şöyle yaparız
(gdb) b main
Breakpoint 1 at 0x40053c: file foo.c, line 5.
Örnek
İmzasını bildiğimiz bir sınıfın metoduna koyabiliriz. Şöyle yaparız.
break myclass::~myclass()
Örnek
Belli bir nesne için koymak istersek şöyle yaparız
b myclass::~myclass if (this==0x9b993e4)
bt komutu
Call stack'i gösterir. where komutu ile aynıdır. Şöyle yaparız.
(gdb) bt
catch komutu
Sistem çağrısına breakpoint koymak için kullanılabilir.

Örnek
Şöyle yaparız.
(gdb) catch throw 
Catchpoint 2 (throw)
Örnek
Uygulamayı başlatırız
Catchpoint 2 (throw)aagdb --args /path/to/your/your-program and its args
unlink() vs gibi sistem çağrısına breakpoint koyarız. Şöyle yaparız
catch syscall unlink
catch syscall unlinkat
catch syscall rmdir
run
Tümü şöyle
$ gdb -q --args rm -rf /tmp/tmp.HudBncQ4Ni
Reading symbols from rm...
Reading symbols from /usr/lib/debug/.build-id/f6/7ac1d7304650a51950992d074f98ec88fe2f49.debug...
(gdb) catch syscall unlink
Catchpoint 1 (syscall 'unlink' [87])
(gdb) catch syscall unlinkat
Catchpoint 2 (syscall 'unlinkat' [263])
(gdb) catch syscall rmdir
Catchpoint 3 (syscall 'rmdir' [84])
(gdb) run
Starting program: /bin/rm -rf /tmp/tmp.HudBncQ4Ni

Catchpoint 2 (call to syscall unlinkat), 0x00007ffff7eb6fa7 in __GI_unlinkat () at ../sysdeps/unix/syscall-template.S:120
120     ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) info registers
rax            0xffffffffffffffda  -38
rbx            0x555555569830      93824992319536
rcx            0x7ffff7eb6fa7      140737352789927
rdx            0x0                 0
rsi            0x555555569938      93824992319800
rdi            0x4                 4
rbp            0x555555568440      0x555555568440
rsp            0x7fffffffda48      0x7fffffffda48
r8             0x3                 3
r9             0x0                 0
r10            0xfffffffffffffa9c  -1380
r11            0x206               518
r12            0x0                 0
r13            0x7fffffffdc30      140737488346160
r14            0x0                 0
r15            0x555555569830      93824992319536
rip            0x7ffff7eb6fa7      0x7ffff7eb6fa7 <__GI_unlinkat+7>
eflags         0x206               [ PF IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
(gdb) x/s $rsi
0x555555569938: "test"
(gdb) info proc
process 7524
cmdline = '/bin/rm -rf /tmp/tmp.HudBncQ4Ni'
cwd = '/export/home/stephane'
exe = '/bin/rm'
(gdb) !readlink /proc/7524/fd/4
/tmp/tmp.HudBncQ4Ni
(gdb) !find /tmp/tmp.HudBncQ4Ni -ls
  1875981      4 drwx------   2 stephane stephane     4096 Aug  8 09:30 /tmp/tmp.HudBncQ4Ni
  1835128      4 -rw-r--r--   1 stephane stephane        5 Aug  8 09:30 /tmp/tmp.HudBncQ4Ni/test

disable komutu
Şöyle yaparız. Ne olduğunu anlamadım.
(gdb) disable  
disas komutu
Assembly kodunu gösterir.
(gdb) disas /m main
Dump of assembler code for function main:
5       {
0x08048330 :    push   %ebp
0x08048331 :    mov    %esp,%ebp
0x08048333 :    sub    $0x8,%esp
0x08048336 :    and    $0xfffffff0,%esp
0x08048339 :    sub    $0x10,%esp

6         printf ("Hello.\n");
0x0804833c :   movl   $0x8048440,(%esp)
0x08048343 :   call   0x8048284 

7         return 0;
8       }
0x08048348 :   mov    $0x0,%eax
0x0804834d :   leave
0x0804834e :   ret

End of assembler dump.
fin komutu
Şöyle yaparız. Ne olduğunu anlamadım.
(gdb) fin
Run till exit from #0 ...
informatino komutu - breakpoints
Örnek
Şöyle yaparız.
(gdb) b _start
Breakpoint 1 at 0x520
(gdb) info b
Num     Type           Disp Enb Address            What
1       breakpoint     keep y   0x0000000000000520 <_start>
2       breakpoint     keep y   0x0000000000000520 <_start>
information komutu - file
Örnek
Uygulamanın EntryPoint'ini öğrenmek için şöyle yaparız.
(gdb) info file
Çıktı olarak bir sürü satır gelir. Entry point şöyledir.
Entry point : 0x804762c
Örnek
Şöyle yaparız.
(gdb) info file
Symbols from "/home/user202729/PINCE/a".
Local exec file:
        `/home/user202729/PINCE/a', file type elf64-x86-64.
        Entry point: 0x520
        [...]
information komutu - threads
Şöyle yaparız.
info threads
information komutu - shared
shared veya sharedlibrary olarak kullanılabilir. Yüklenen kütüphanelerin debug symbol durumunu gösterir. Şöyle yaparız.
(gdb) info sharedlibrary
From                To                  Syms Read   Shared Object Library
0x00007ff8e77c68b0  0x00007ff8e7919ac4  Yes (*)     ./libc.so.6
0x00007ff8e7b71ac0  0x00007ff8e7b8f810  Yes         /lib64/ld-linux-x86-64.so.2
(*): Shared library is missing debugging information.
information komutu - vtable
Belli bir nesnenin vtable satırları şöyle görülebilir.
(gdb) i vtbl b
vtable for 'A' @ 0x400cf0 (subobject @ 0x603010):
[0]: 0x0
[1]: 0x0
[2]: 0x4008e0 <__cxa_pure_virtual@plt>
next komutu
Bir satırı çalıştırır. Sadece enter'a basmak ta aynı şey sanırım. Elimizde şöyle bir kod olsun
unsigned int i = 2;
const int &r = i;
std::cout << r << "\n";
i = 100;
std::cout << r << "\n";
Şöyle yaparız.
(gdb) n
6   const int &r = i;
(gdb) 
7   std::cout << r << "\n";
print komutu
array
Örnek
Elimizde int a[100] olsun. Şöyle yaparız.
(gdb) print a
Örnek
Elimizde int a[100] olsun ancak int* parametre olarak geçilmiş olsun. Şöyle yaparız.
(gdb) print *a@100
Örnek
Elimizde int a[100][100] olsun ancak int* parametre olarak geçişlmiş olsun. Belli bir boyutu görmek için şöyle yaparız.
print *arr[0]@n
Tamamını yazdırmak için şöyle kodumuz olsun.
void print(int arr[][], n, m)
{
    int i, j;
    for (i = 0; i < n; ++i)
    {
        for (j = 0; j < m; ++j)
            printf("%d ", arr[i][j]);
        printf("\n");
    }
}
Çağırmak için şöyle yaparız
call print(arr, n, m)
int
Parametre pointer değilse int i için şöyle bir çıktı alırız.
(gdb) print i
$42 = 1
metod
Örnek
Parametre metod ise imzası ve adresi gösterilir. Main metodu şöyledir.
int main(){...}
Çıktı olarak şunu görürüz. Return Type (int) + Parametreler şeklindedir.
(gdb) print main
$1 = {int ()} 0x4005ab <main>
Başka bir function olsun
void function(char *ls, int a, int b, int c){...}
Çıktı olarak şunu görürüz. Return Type (void) + Parametreler
(gdb) print function
$2 = {void (char *, int, int, int)} 0x400573 <function>
Örnek
Template kodlarda işe yaramıyor. Şöyle yaparız.
(gdb) p std::max_element(v.begin(), v.end())
 No symbol "max_element" in namespace "std".
pointer
Parametre pointer ise tipi ve bellek adresi gösterilir.
(gdb) print &j
$1 = (int *) 0x7fffffffde80
(gdb) print &i
$2 = (int *) 0x7fffffffde84
register
Örnek
Parametre register ise şöyle yaparız.
(gdb) p $sp
$1 = (void *) 0x7fffffffde40

(gdb) p $rbp
$2 = (void *) 0x7fffffffde60
Örnek
Şöyle yaparız
(gdb) p/x $rsp

(gdb) x/x $rbp
Açıklaması şöyle
p/x $rsp displays the value of the rsp register, i.e.: the address of the top of the stack
since  rsp points to the top of the stack.

x/x $rsp displays the memory contents located at the top of stack (i.e.: the contents
located at the address pointed by rsp)
sizeof
Parametre ile sizeof yapılabilir.
(gdb) p sizeof(obj_a)
$1 = 32
struct
Parametre struct ise içi gösterilir.Elimizde global olan ve ismi mutex olan bir değişken olsun.
pthread_mutex_t mutex =...;
Yapının içini görmek için şöyle yaparız.
(gdb) p mutex
$1 = {__data = {__lock = 1, __count = 0, __owner = 6596, ...},
template
Template bir nesne için şöyle yaparız. Çıktı okunaklı değil. Okunaklı çıktı için "pretty printer" tanımlamak gerekir.
(gdb) p server
$11 = (const mpn::SER_ID &) @0x756660: {, std::allocator >, boost::detail::empty_base > >> = {, std::allocator >, boost::detail::empty_base > > >> = {, std::allocator >, boost::detail::empty_base > >> = {, std::allocator >, boost::detail::empty_base >> = {, std::allocator >, boost::equality_comparable2, std::allocator >, boost::detail::empty_base > >> = {, std::allocator >, boost::detail::empty_base >> = {> = {}, }, }, }, }, }, }, t = "B"...}
quit komudu
gdb'den çıkar. Şöyle yaparız.
(gdb) q
run komutu
run veya r kısaltmasını kullanabiliriz. Şöyle yaparız.
(gdb) run
Starting program: ...
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Şöyle yaparız.
Reading symbols from ./a.out...done.
...
(gdb) r
Starting program: /tmp/a.out 
shell
gdb ile shell kullanmak mümkün. Şöyle yaparız.
(gdb) while (1)
 >step
 >shell sleep 1
 >end
python kullanmak için şöyle yaparız.
(gdb) python import time
(gdb) while (1)
 >step
 >python time.sleep(1)
 >end
macro tanımlamak için şöyle yaparız.
(gdb) define runslowly
Type commands for definition of "runslowly".
End with a line saying just "end".
>python import time
>while (1)
 >step
 >python time.sleep(1)
 >end
>end
(gdb) document runslowly
Type documentation for "runslowly".
End with a line saying just "end".
>step a line at a time, every 1 second
>end

(gdb) runslowly
set komutu
Örnek - solib-search path
.so dosyasını yüklemeyez ise şu mesajı verir.
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
Şöyle yaparız.
(gdb) set solib-search-path /lib:/usr/adowdy/lib:/usr/adowdy/arm/lib
Örnek
thread kütüphanelerinde karışıklık varsa şu mesajı verir.
warning: Unable to find libthread_db matching inferior's thread library, thread debugging
will not be available.
Şöyle yaparız.
set libthread-db-search-path [path]
Örnek - değişkene değer atamak
Elimizde şöyle bir dizi olsun.
float** P_i=
Bu diziyi dolaşmak için bir değişken yaratıp kullanabiliriz.
(gdb) set $pos=0
(gdb) print P_i[$pos++]
$51 = (float *) 0x804d500
(gdb) print P_i[$pos++]
$52 = (float *) 0x804d148
Örnek - disable-randomization-off
Açıklaması şöyle.
When you run a program inside gdb, gdb tries to help you debugging by disabling address randomization. You can use the following command to enable it (effective from the next run of the program):
Örneğin gdb kullanırken __libc_start_main adresini hep 0x00007ffff7de8060 olarak görebiliriz. Bu yardımı kapatmak için şöyle yaparız.
(gdb) set disable-randomization off
Örnek - değişkene değer atamak
Program Counter (PC) değerini bir önceki değere atamak için şöyle yaparız. RSP stack'i gösterir.
(gdb) set $pc = *(void **)$rsp
(gdb) set $rsp = $rsp + 8
si komutu
Tek bir makine kodu (Single Instruction) çalıştırır. C kodundaki satırında çalıştırılmasından farklıdır.
(gdb) si
0x08048366 in main ()
0x0804836c in main ()
start komutu
run komutunun aksine ilk satır'da uygulamayı durdurur ve bekler. Böylece breakpoint kopyma şansımız olur. Şöyle yaparız.
#include <stdlib.h>
int main()
{
  void *p = calloc(1, 10);
  return p == 0;
}

gdb -q ./a.out

Reading symbols from ./a.out...done.
(gdb) start
Temporary breakpoint 1 at 0x8048426: file foo.c, line 4.
Starting program: /tmp/a.out 

Temporary breakpoint 1, main () at foo.c:4
4         void *p = calloc(1, 10);
symbol-file
GDB ya da herhangi bir debugger ile çalışırken symbol table verisini yüklemek gerekir. Bu tablonun üretilmesi için yazılımın "gcc -g" seçeneği ile derlenmesi gerekmektedir. Symbol table kabaca makine kodu içindeki offsetlerin değişkenlere dönüştürülebilmesine yarar.

Yani kaynak kod ile assembly arasında ilişki kurar. Debug yaparken kaynak kod içinde 1 satır atlansa bile, 50 tane assembly satırı atlanılmış olabilir.

Bir başka örnek olarak, gdb içinde print x komutu verilince x değişkeninin hangi offsette bulunduğunu symbol table gösterir.

Symbol file debug yaparken çıkarılabilir veya tekrar eklenebilir. Bunun için symbol-file komutu kullanılır. Örnek:
Reading symbols from /tmp/foo...done.
(gdb) symbol-file
Discard symbol table from `/tmp/foo'? (y or n) y
No symbol file now.
(gdb) symbol-file foo
Reading symbols from /tmp/foo...done.
(gdb) symbol-file
Discard symbol table from `/tmp/foo'? (y or n) y
No symbol file now.
Visual Studio'da sembol tabloları şöyle kontrol edilir.
Debug >Windows> Modules penceresinde pdb dosyasının olduğuna bakılır.

target komutu
Remove debug için kullanılır
Örnek
Şöyle yaparız
target remote 192.168.1.20:9134
thread komutu
Örnek
Bir thread'e geçmek için şöyle yaparız.
(gdb) thread 45  
[Switching to thread 45 (Thread 909)]  
...
Örnek
Tüm threadlerin stack çıktısını görmek için şöyle yaparız.
(gdb) thread apply all bt
where komutu
Call stack'i gösterir.

x komutu
Adresteki değer gösterir.
a
Ne olduğunu anlamadım. Şöyle yaparız.
(gdb) x/14ag 0x400d08
...
i - instruction
Belirtilen adresten sonraki 3 instruction'ı gösterir.
(gdb) x/3i 0x400b8f
0x400b8f <virtual thunk to A::show()>:  mov    (%rdi),%r10
0x400b92 <virtual thunk to A::show()+3>:    add    -0x18(%r10),%rdi
0x400b96 <virtual thunk to A::show()+7>:    jmp    0x400b46 <A::show()>
register
Register'ın değerini gösterir. Şöyle yaparız. x ve x/1xw arasındaki farkı bilmiyorum
(gdb) x $rbp+8
0x7fffffffddb8: 0xf7a2e830
(gdb) x/1xw $rbp+8
0x7fffffffddb8: 0xf7a2e830
Şöyle yaparız.
(gdb) p/x $rdi
$1 = 0x3
(gdb) p/x $rsi
$2 = 0x7fffffffe3a8
x
hexadecimal gösterir. Şöyle bir komut olsun
(gdb) call memset(0x8d9d50, 0, 32)
$89 = 9280848
Bu adresten itibaren 8 tane word'ü hexadecimal görmek istersek şöyle yaparız.
(gdb) x/8xw 0x8d9d50
0x8d9d50:       0x00000000      0x00000000      0x00000000      0x00000000
0x8d9d60:       0x00000000      0x00000000      0x00000000      0x00000000

4 yorum:

  1. Merhaba hocam, ben C++ öğreniyorum daha doğrusu öğrenmeye çalışıyorum. Bir video gördüm ve izledim o video da "C++ artık öldü, arkasında güçlü şirket kalmadı, diğer diller (Java, C#) gibi arkası sağlam değil" diyordu içime kurt düştü ve aklıma bir tek burası geldi burada gerçek bir cevap bulabileceğimi düşündüm.

    C++ Öğrenmeye devam etmeli miyim ? (İleri zamanlar için düşünüyorum bunu ayrıca C++'ı seviyorum)

    Bir de C++'a devam etmek gerekiyorsa hangi kaynağı kullanmalıyım ? (Şuan da Robert Lafore'un kitabınından çalışıyorum ama kitap uzun zaman önce yazıldığı için güncelliğini yitirmiştir diye düşünüyorum sizce güncelliğini yitirmiş midir ? Yitirmişse hangi kaynağı önerirsiniz ?)

    Hocam gerçekten bu soruların cevaplarına çok ihtiyacım var aklım son 2 haftadır çok meşgul çok teşekkür ederim şimdiden.

    YanıtlaSil
    Yanıtlar
    1. Merhaba, C++ kesinlikle ölmedi. Tam tersi standart komitesi çok hareketli. C+11, C++14, C++17 standartları arka arka çıktı ve dil gelişmeye devam ediyor. Arkasında ise GNU , Microsoft gibi güçlü kuruluşlar var. Dolayısıyla bence öğrenmeye devam etmek gerekir. Kaynak olarak şu linke bakınız. The Definitive C++ Book Guide and List

      Sil
    2. Çok teşekkür ederim kafam çok karışmıştı ama siz böyle diyorsanız böyledir zira uzun zamandır takip ediyorum sizi mükemmel bir blogunuz var ayrıca İngilizcem de başlangıç seviyesinin biraz üstünde İngilizcenizi nasıl geliştirdiniz ? Bende çok istiyorum ama yapamıyorum yol gösterenim yok ? Teşekkür ederim.

      Sil
    3. Yardımcı olabildiysem sevindim. İngilizce zamanla gelişiyor. Aynı kod okumak ve yazmak gibi. Okumaya ve çalışmaya devam edeceğiz, daha da iyisine ulaşmak için başka yol yok :)

      Sil