21 Şubat 2019 Perşembe

Pointer To Array

Giriş
Array'e pointer elde etmek için
array
&array
&array[0]
kullanılabilir.

Görsel olarak şöyledir.
+----------+----------+----------+----------+----------+
| array[0] | array[1] | array[2] | array[3] | array[4] |
+----------+----------+----------+----------+----------+
^
|
&array[0]
|
&array
Örnek
Şöyle yaparız. p pointer to array ve daha sonra istesek başka bir yere de point ettirebiliriz.
int a[5] = {1,2,3,4,5}
int * p = a;
Örnek
Şöyle yaparız.
int array[5] = { 10,11,12,13,14};

std::cout << array << std::endl;
std::cout << &array << std::endl;
std::cout << &array[0] << std::endl;
Örnek
Şu ise derlenmez.
int a[5] = {1,2,3,4,5};
int*& ref = a;
Açıklaması şöyle
a is not a pointer, it is an array. It has the type int[5]. What it can do it is decay to a pointer int*, which is what happens in the first case. So, taking a reference to p is ok.

Now for the second case. Remember that a is not a pointer. So, there is an implicit conversion happening from int[5] to int*. The result of that conversion is a prvalue. But you can't bind a non-const lvalue reference (which is what ref is) to an rvalue! So the code fails to compile.
Örnek
Pointer To Array'in adresini almak. Elimizde şöyle bir kod olsun.
int *pArray = new int[5];

std::cout << pArray << std::endl; //İlk elemanın adresi
std::cout << &pArray << std::endl; //Pointer'ın adresi
std::cout << &pArray[0] << std::endl;//İlk elemanın adresi
Görsel olarak şöyledir.
+--------+       +-----------+-----------+-----------+-----------+-----------+-----+
| pArray | ----> | pArray[0] | pArray[1] | pArray[2] | pArray[3] | pArray[4] | ... |
+--------+       +-----------+-----------+-----------+-----------+-----------+-----+
^                ^
|                |
&pArray          &pArray[0]

20 Şubat 2019 Çarşamba

std::add_lvalue_reference

Bu metodun ne işe yaradığını anlamadım. İlk gördüğüm yer burası

Reference Collapsing

Giriş
const T& şeklindeki parametre kullanan template kod, eğer <int&> şeklinde çağrılırsa parametrenin const olmaması anlamına gelir.

Kodu her zaman şöyle çağırmak gerekir.
g<int>(n);
// or just
g(n);
Şu şekildeki çağrılar yanlış
 int n{};
g<int&>(n);
Açıklaması şöyle.
...so you have specified that T is a int&. When we apply a reference to an lvalue reference, the two references collapse to a single one, so int& & becomes just int&. Then we get to the rule from [dcl.ref]/1, which states that if you apply const to a reference it is discarded, so int& const just becomes int&...
Örnek
Elimizde şöyle bir kod olsun.
template<typename T>
void f(T a, const T& b)
{
  ++a; // ok
  ++b; // also ok!
}

template<typename T>
void g(T n)
{
  f<T>(n, n);
}

int main()
{
  int n{};
  g<int&>(n);
}

10 Şubat 2019 Pazar

gdb seçenekleri

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.
ç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.

core Dosyası
gdb myprogram core

komutu ile core dosyasının nerede çöktüğü incelenebilir.

debug için
Uygulamamızı debug için şöyle yaparız. gdb komut satırına düşer.
gdb myprogram
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. Ö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
Şöyle yaparız.
(gdb) catch throw 
Catchpoint 2 (throw)
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 - 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
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
Şöyle yaparız.
(gdb) set disable-randomization off
Örnek
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.

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