makefile编写、GDB调试、so库⽂件⽣成
多⽂件⽬录makefile:
层级⽬录makefile :
makefile模板⼀
.SUFFIXES:.c .o  //.c和.o建⽴关联
CC=gcc
SRCS=hello.c\
add.c  //同时编译两个.c⽂件
OBJS=$(SRCS:.c=.o)//ORJS=hello.o add.o
EXEC=hello
start: $(OBJS)  //依赖hello.o add.o
$(CC) -o $(EXEC) $(OBJS)
@echo '----------------ok------------'
.c.o:
$(CC) -Wall -g -o $@ -c $<  //⾃动根据$(OBJS) 中.o的数量循环编译, 直到编译完所有的.c⽂件;; 加-g 调试
clean:
rm -f $(OBJS)
-----------------------------------------------------------------------------------------------
< 在编译时(-c的时候)加上 -g 参数就是加⼊调试信息
$(CC) -Wall -g -o $@ -c $<
-g 放在 -o 前⾯或者最后⾯
-Wall 表⽰提⽰警告
----------------------------------------------------------------------------------------------
-Wall// ⼤部分警告
-Wextra // ⼀些额外的警告,如未使⽤的参数
-Werror // 当出现警告时转为错误,停⽌编译
-Wconversion // ⼀些可能改变值的隐式转换,给出警告。
-Wno-unused-parameter // 函数中出现未使⽤的参数,不给出警告。
-Wold-style-cast // C风格的转换,给出警告
-Woverloaded-virtual // 如果(重载)函数的声明(与基类虚函数同名不同参)隐藏住了基类的虚函数,就给出警告。
-Wpointer-arith// 对函数指针或者void *类型的指针进⾏算术操作时给出警告
-Wshadow // 当⼀个局部变量遮盖住了另⼀个局部变量,或者全局变量时,给出警告。
-
Wwrite-strings // 规定字符串常量的类型是const char[length],因此,把这样的地址复制给 non-const char *指针将产⽣警告.这些警告能够帮助你在编译期间发现企图写⼊字符串常量 的代码
-march=native // 指定cpu体系结构为本地平台
-----------------------------------------------------------------------------------------------
makefile模板⼆
.PHONY:clean
CC=g++
CPPFLAGS=-Wall -g
BIN=echosrv_poll echocli
all:$(BIN)
%.o:%.c
$(CC) $(CFLAGS) -c $< -o $@
clean:
rm -f *.o $(BIN)
2.调试第⼀种:⽤core⽂件
linux默认不⽣成core⽂件,在宿主⽬录下的.bashrc⽂件中末尾加⼊
ulimit -c ulimited
保存后,执⾏: . .bashr ,使修改⽣效。
这样当执⾏程序出错了,会⽣成 core.数字 的⽂件
执⾏:gdb 执⾏⽂件名 core.数字 ;加 -q 取出gdb版权信息
第⼆种调试:直接在gdb中运⾏程序,不⽤corefile。
gdb 执⾏程序名
run
gdb中常⽤命令:
break [⽂件名:]⾏号
break [⽂件名:]函数名
info break :查看所有断点信息
delete 断点编号
list:显⽰10⾏代码,list m,n  显⽰m⾏和n⾏之间的
step:单步调试进⼊函数
next;单步不进⼊函数
continue:断点后继续
return [返回值]:提前结束当前函数
print 变量名
set variable 变量名 = 值
where:打印出错的地⽅
----------------------------------------------------------------------------
GDB调试release
⽅法1:  如果现在有同⼀个⼯程构建出的Debug版和Release版可执⾏⽂件projectD和projectR,如下的命令可以帮助你把Debug版中的符号表加到Release版中,实现对Release版的调试。⽤发布时的原代码,在原有的编译选项上(不要改变任何编译参数,即使有"-O2"参数,也不要动),只加上"-g"选项,编译出对应的debug程序。
  1、objcopy --only-keep-debug projectD projectsymbol.dbg #⽣成符号表;projectsymbol.dbg是⾃⼰命名的符号⽂件名
  2、gdb -q --symbol=projectsymbol.dbg -exec=projectR #加载符号表;
  这下在gdb中就可以看到源代码了。
⽅法2:在编译发布版本时加上"-g -O2",  这样就有符号表了,⽽且不影响性能。
-----------------------------------------------------------------------------
转⾃:
⼀、多线程调试
1. 多线程调试,最重要的⼏个命令:
info threads    查看当前进程的线程。
GDB会为每个线程分配⼀个ID, 后⾯操作线程的时候会⽤到这个ID.
前⾯有*的是当前调试的线程.
thread                              切换调试的线程为指定ID的线程。
break file.c:100 thread al l        在file.c⽂件第100⾏处为所有经过这⾥的线程设置断点。
set scheduler-locking off|on|step
在使⽤step或者continue命令调试当前被调试线程的时候,其他线程也是同时执⾏的,
怎么只让被调试程序执⾏呢?
通过这个命令就可以实现这个需求。
off      不锁定任何线程,也就是所有线程都执⾏,这是默认值。
on      只有当前被调试程序会执⾏。
step    在单步的时候,除了next过⼀个函数的情况
(熟悉情况的⼈可能知道,这其实是⼀个设置断点然后continue的⾏为)以外,
只有当前线程会执⾏。
thread apply ID1 ID2 command让⼀个或者多个线程执⾏GDB命令command
thread apply all command 让所有被调试线程执⾏GDB命令command。
2. 使⽤⽰例:
线程产⽣通知:在产⽣新的线程时, gdb会给出提⽰信息
(gdb) r
Starting program: /root/thread
[New Thread 1073951360 (LWP 12900)]
[New Thread 1082342592 (LWP 12907)]---以下三个为新产⽣的线程
[New Thread 1090731072 (LWP 12908)]
[New Thread 1099119552 (LWP 12909)]
查看线程:使⽤info threads可以查看运⾏的线程。
(gdb) info threads
4 Thread 1099119552 (LWP 12940)  0xffffe002 in ?? ()
3 Thread 1090731072 (LWP 12939)  0xffffe002 in ?? ()
2 Thread 1082342592 (LWP 12938)  0xffffe002 in ?? ()
* 1 Thread 1073951360 (LWP 12931)  main (argc=1, argv=0xbfffda04) at thread.c:21
(gdb)
注意,⾏⾸为gdb分配的线程ID号,对线程进⾏切换时,使⽤该ID号码。
另外,⾏⾸的星号标识了当前活动的线程
切换线程:
使⽤ thread THREADNUMBER 进⾏切换,THREADNUMBER 为上⽂提到的线程ID号。
下例显⽰将活动线程从 1 切换⾄ 4。
(gdb) info threads
4 Thread 1099119552 (LWP 12940)  0xffffe002 in ?? ()
3 Thread 1090731072 (LWP 12939)  0xffffe002 in ?? ()
2 Thread 1082342592 (LWP 12938)  0xffffe002 in ?? ()
* 1 Thread 1073951360 (LWP 12931)  main (argc=1, argv=0xbfffda04) at thread.c:21 (gdb) thread 4
[Switching to thread 4 (Thread 1099119552 (LWP 12940))]#0  0xffffe002 in ?? () (gdb) info threads
* 4 Thread 1099119552 (LWP 12940)  0xffffe002 in ?? ()
3 Thread 1090731072 (LWP 12939)  0xffffe002 in ?? ()
2 Thread 1082342592 (LWP 12938)  0xffffe002 in ?? ()
1 Thread 1073951360 (LWP 12931)  main (argc=1, argv=0xbfffda04) at thread.c:21 (gdb)
以上即为使⽤gdb提供的对多线程进⾏调试的⼀些基本命令。
另外,gdb也提供对线程的断点设置以及对指定或所有线程发布命令的命令
⼆、调试宏
在GDB下, 我们⽆法print宏定义,因为宏是预编译的。
但是我们还是有办法来调试宏,这个需要GCC的配合。
在GCC编译程序的时候,加上
-ggdb3参数,这样,你就可以调试宏了。
另外,你可以使⽤下述的GDB的宏调试命令来查看相关的宏。
info macro 查看这个宏在哪些⽂件⾥被引⽤了,以及宏定义是什么样的。
macro  查看宏展开的样⼦。
三、源⽂件
GDB时,提⽰不到源⽂件。
需要做下⾯的检查:
编译程序员是否加上了 -g参数以包含debug信息。
路径是否设置正确了。
使⽤GDB的directory命令来设置源⽂件的⽬录。
下⾯给⼀个调试/bin/ls的⽰例(ubuntu下)
$ apt-get source coreutils
$ sudo apt-get install coreutils-dbgsym
$ gdb /bin/ls
GNU gdb (GDB) 7.1-ubuntu
(gdb) list main
1192    ls.c: No such file or directory.
in ls.c
(gdb) directory ~/src/coreutils-7.4/src/
Source directories searched: /home/hchen/src/coreutils-7.4:$cdir:$cwd
(gdb) list main
1192        }
1193    }
1194
1195    int
1196    main (int argc, char **argv)
1197    {
1198      int i;
1199      struct pending *thispend;
1200      int n_files;
1201
四、条件断点
条件断点是语法是:
break  [where] if [condition]
这种断点真是⾮常管⽤。
尤其是在⼀个循环或递归中,或是要监视某个变量。
注意,这个设置是在GDB中的,只不过每经过那个断点时GDB会帮你检查⼀下条件是否满⾜。
五、命令⾏参数
有时候,我们需要调试的程序需要有命令⾏参数, 有三种⽅法:
gdb命令⾏的 -args 参数
gdb环境中  set args命令。
gdb环境中  run 参数
六、gdb的变量
makefile phony
有时候,在调试程序时,我们不单单只是查看运⾏时的变量,
我们还可以直接设置程序中的变量,以模拟⼀些很难在测试中出现的情况,⽐较⼀些出错,
或是switch的分⽀语句。使⽤set命令可以修改程序中的变量。
另外,你知道gdb中也可以有变量吗?
就像shell⼀样,gdb中的变量以$开头,⽐如你想打印⼀个数组中的个个元素,你可以这样:
(gdb) set $i = 0
(gdb) p a[$i++]
...  #然后就⼀路回车下去了
当然,这⾥只是给⼀个⽰例,表⽰程序的变量和gdb的变量是可以交互的。
七、x命令
也许,你很喜欢⽤p命令。
所以,当你不知道变量名的时候,你可能会⼿⾜⽆措,因为p命令总是需要⼀个变量名的。
x命令是⽤来查看内存的,在gdb中 “help x” 你可以查看其帮助。
x/x以⼗六进制输出
x/d 以⼗进制输出
x/c以单字符输出
x/i  反汇编 – 通常,我们会使⽤ x/10i $ip-20 来查看当前的汇编($ip是指令寄存器)
x/s 以字符串输出
⼋、command命令
如何⾃动化调试。
这⾥向⼤家介绍command命令,简单的理解⼀下,其就是把⼀组gdb的命令打包,有点像字处理软件的“宏”。下⾯是⼀个⽰例:
(gdb) break func
Breakpoint 1 at 0x3475678: file test.c, line 12.
(gdb) command 1
Type commands for when breakpoint 1 is hit, one per line.
End with a line saying just "end".
>print arg1
>print arg2
>print arg3
>end
(gdb)
当我们的断点到达时,⾃动执⾏command中的三个命令,把func的三个参数值打出来。————————————————————————————————
⼀.编写linux下的so库⽂件
1.⽣成库⽂件.so
不需要main函数,但是要修改⽣成库函数的makefile
⽣成的⽂件名必须是 lib库名称.so  如 lib mytest.so
编译时 $(CC) -Wall -g -fPIC -o $@ -c $<

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。