基于c语言中调试工具的用法汇总(不包含gdb)【转】
转自:http://www.jb51.net/article/36829.htm
是不是只有编译的时候才知道程序写了错误?有没有在未编译的时候就让机器帮你检查错误的工具呢?
答案是:有!! splint工具.用一个最简单的HELLO WORLD来表述:
=====================================
复制代码 代码如下: /*错误很明显*/
#include <stdio.h> int main(void)
{
print("hello world\n", s);
return
} -----------------------------------------------------
casio$ splint -strict foo.c
Splint 3.1. --- Nov foo.c: (in function main)
foo.c::: Unrecognized identifier: print <-------找到print不是printf
Identifier used in code has not been declared. (Use -unrecog to inhibit
warning)
foo.c::: Unrecognized identifier: s <-------未定义变量s
foo.c::: Statement has no effect (possible undected modification through call
to unconstrained function print): print("hello wor... <---------不存在prinf函数
Statement has no visible effect --- no values are modified. It may modify
something through a call to an unconstrained function. (Use -noeffectuncon to
inhibit warning)
foo.c::: Parse Error. (For help on parse errors, see splint -help <------对应return语法错误
parseerrors.)
*** Cannot continue. ============================================= cxref cxref程序分析C源代码并且生成一个交叉引用。他显示了每一个符号在程序中何处被提到。他使用标记星号的每一个符号定义位置生成一个排序列表,如下所示: SYMBOL FILE FUNCTION LINE
BASENID prog.c — * *
BINSIZE prog.c — *
BUFMAX prog.c — *
BUFSIZ /usr/include/stdio.h — *
EOF /usr/include/stdio.h — *
argc prog.c —
prog.c main *
argv prog.c —
prog.c main *
calldata prog.c — *
prog.c main
calls prog.c — *
prog.c main 在作者的机子上,前面的输入在程序的源码目录中使用下面的命令来生成的: $ cxref *.c *.h 但是实际的语法因为版本的不同而不同。查看我们系统的文档或是man手册可以得到更多的信息。 cflow <使用时输入cflow *.c就可以了.可以马上搞清除什么函数调用了什么.> cflow程序会输出一个函数调用树,这是一个显示函数调用关系的图表。这对于查看程序结构来了解他是如何操作的以及了解对于一个函数有哪些影响是十分有用的。一些版本的cflow可以同时作用于目标文件与源代码。查看手册页我们可以了解更为详细的操作。 下面是由一个cflow版本(cflow-2.0)所获得的例子输出,这个版本的cflow版本是由Marty Leisner维护的,并且可以网上得到。 file_ungetc {prcc.c }
main {prcc.c }
getopt {}
show_all_lists {prcc.c }
display_list {prcc.c }
printf {}
exit {}
exit {}
usage {prcc.c }
fprintf {}
exit {} 从这个输出中我们可以看到main函数调用show_all_lists,而show_all_lists调用display_list,display_list本身调用printf。 这个版本cflow的一个选项就是-i,这会生成一个反转的流程图。对于每一个函数,cflow列出调用他的其他函数。这听起来有些复杂,但是实际上并不是这样。下面是一个例子。 display_list {prcc.c }
show_all_lists {prcc.c }
exit {}
main {prcc.c }
show_all_lists {prcc.c }
usage {prcc.c }
...
printf {}
display_list {prcc.c }
maketag {prcc.c }
show_all_lists {prcc.c }
main {prcc.c }
...
usage {prcc.c }
main {prcc.c } 例如,这告诉我们调用exit的函数有main,show_all_lists与usage。 使用prof/gprof执行性能测试 当我们试着追踪一个程序的性能问题时一个十分有用的技术就是执行性能测试(execution profiling)。通常被特殊的编译器选项以及辅助程序所支持,一个程序的性能显示他在哪里花费时间。 prof程序(以及其GNU版本gprof)会由性能测试程序运行时所生成的执行追踪文件中输出报告。一个可执行的性能测试是由指定-p选项(对prof)或是-pg选项(对gprof)所生成的: $ cc -pg -o program program.c 这个程序是使用一个特殊版本的C库进行链接的并且被修改来包含监视代码。对于不同的系统结果也许不同,但是通常是由安排频繁被中断的程序以及记录执行位置来做到的。监视数据被写入当前目录中的一个文件,mon.out(对于gprof为gmon.out)。 $ ./program
$ ls -ls
-rw-r--r-- neil users Feb : gmon.out 然后用命令:gprof ./program可以查看到下面的报告 prof/gprof程序读取这些监视数据并且生成一个报告。查看其手册页可以详细了解其程序选项。下面以gprof输出作为一个例子: cumulative self self total
time seconds seconds calls ms/call ms/call name
18.5 0.10 0.10 0.01 0.03 _doscan []
18.5 0.20 0.10 mcount ()
14.8 0.28 0.08 0.00 0.00 _number []
9.3 0.33 0.05 0.01 0.01 _format_arg []
7.4 0.37 0.04 0.00 0.00 _ungetc []
7.4 0.41 0.04 0.00 0.00 _memccpy []
7.4 0.45 0.04 40.00 390.02 _main []
3.7 0.47 0.02 0.38 0.38 _read []
3.7 0.49 0.02 w4str []
1.9 0.50 0.01 0.00 0.00 _strlen []
1.9 0.51 0.01 0.00 0.00 strncmp [] 内存调试 富含bug而且难于跟踪调试的一个区域就是动态内存分配。如果我们编译一个使用malloc与free来分配内存的程序,很重要的一点就是我们要跟踪我们所分配的内存块,并且保证不要使用已经释放的内存块。 通常,内存是由malloc分配并且赋给一个指针变量的。如果指针变量被修改了,而又没有其他的指针来指向这个内存块,他就会变为不可访问的内存块。这就是一个内存泄露,而且会使得我们程序尺寸变大。如果我们泄露了大量的内存,那么我们的系统就会变慢并且会最终用尽内存。 如 果我们在超出一个分配的内存块的结束部分(或是在一个内存块的开始部分)写入数据,我们很有可能会破坏malloc库来跟踪分配所用的数据结构。在这种情 况下,在将来的某个时刻,调用malloc,或者甚至是free,就会引起段错误,而我们的程序就会崩溃。跟踪错误发生的精确点是非常困难的,因为很可能 他在引起崩溃的事件发生以前很一段时间就已经发生了。 不必奇怪的是,有一些工具,商业或是自由的,可以有助于处理这两种问题类型。例如,有许多不同的malloc与free版本,其中的一些包含额外的代码在分配与回收上进行检测尝试检测一个内存块被释放两次或是其他一些滥用类型的情况。 ElectricFence ElectricFence 库是由Bruce Perens开发的,并且在一些Linux发行版本中作为一个可选的组件来提供,例如RedHat,而且已经可以在网络上获得。他尝试使用Linux的虚 拟内存工具来保护malloc与free所使用的内存,从而在内存被破坏时终止程序。 试验--ElectricFence 下面的程序,efence.c,使用malloc分配一个内存块,然后在超出块结束处写入数据。让我们看一下会发生什么情况。
复制代码 代码如下: #include <stdio.h>
#include <stdlib.h>
int main()
{
char *ptr = (char *) malloc();
ptr[] = ;
/* Now write beyond the block */
ptr[] = ;/*写非法*/
exit();
} 当我们编译运行这个程序时,我们并不会看到进一步的行为。然而,似乎malloc所分配的内存区域有一些问题,而我们实际上已经遇到了麻烦。 $ cc -o efence efence.c
$ ./efence
$ 然而,如果我们使用ElectricFence库,libefence.a来链接这个程序,我们就会得到一个即时的响应。 $ cc -o efence efence.c -lefence
$ ./efence
Electric Fence 2.2. Copyright (C) - Bruce Perens <bruce@perens.com>
Segmentation fault
$ 在调试器下运行可以定位这个问题: $ cc -g -o efence efence.c -lefence
$ gdb efence
(gdb) run
Starting program: /home/neil/BLP3/chapter10/efence
[New Thread (LWP )]
Electric Fence 2.2. Copyright (C) - Bruce Perens <bruce@perens.com>
Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread (LWP )]
0x080484ad in main () at efence.c:
ptr[] = ;
(gdb) 工作原理 Electric替换malloc并且将函数与计算机处理器的虚拟内存特性相关联来阻止非法的内存访问。当这样的访问发生时,就会抛出一个段错误信息从而可以终止程序。 valgrind valgrind是一个可以检测我们已经讨论过的许多问题的工具。事实上,他可以检测数据访问错误与内存泄露。也许他并没有被包含在我们的Linux发行版本中,但是我们可以在http://developer.kde.org/~sewardj处得到。 程序并不需要使用valgrind重新编译,而我们甚至可以调用一个正在运行的程序的内存访问。他很值得一看,他已经用在主要的开发上,包含KDE版本3。 试验--valgrind 下面的程序,checker.c,分配一些内存,读取超过那块内存限制的位置,在其结束处之外写入数据,然后使其不能访问。
复制代码 代码如下: #include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *ptr = (char *) malloc();
char ch;
/* Uninitialized read */
ch = ptr[];/*读非法*/
/* Write beyond the block */
ptr[] = ;/*写非法*/
/* Orphan the block */
ptr = ;/*野指针*/
exit();
} 要使用valgrind,我们只需要简单的运行valgrind命令,传递我们希望检测的选项,其后是使用其参数运行的程序。 当我们使用valgrind来运行我们的程序时,我们可以看到诊断出许多问题: $ valgrind --leak-check=yes -v ./checker
==== valgrind-1.0., a memory error detector for x86 GNU/Linux.
==== Copyright (C) -, and GNU GPL'd, by Julian Seward.
==== Estimated CPU clock rate is MHz
==== For more details, rerun with: -v
====
==== Invalid read of size
==== at 0x8048397: main (checker.c:)
==== by 0x402574F2: __libc_start_main (in /lib/libc.so.)
==== by 0x80482D1: exit@@GLIBC_2. (in /home/neil/BLP3/chapter10/checker)
==== Address 0x42AD1424 is bytes after a block of size alloc'd
==== at 0x4003CA75: malloc (vg_clientfuncs.c:)
==== by 0x8048389: main (checker.c:)
==== by 0x402574F2: __libc_start_main (in /lib/libc.so.)
==== by 0x80482D1: exit@@GLIBC_2. (in /home/neil/BLP3/chapter10/checker)
====
==== Invalid write of size
==== at 0x80483A4: main (checker.c:)
==== by 0x402574F2: __libc_start_main (in /lib/libc.so.)
==== by 0x80482D1: exit@@GLIBC_2. (in /home/neil/BLP3/chapter10/checker)
==== Address 0x42AD1424 is bytes after a block of size alloc'd
==== at 0x4003CA75: malloc (vg_clientfuncs.c:)
==== by 0x8048389: main (checker.c:)
==== by 0x402574F2: __libc_start_main (in /lib/libc.so.)
==== by 0x80482D1: exit@@GLIBC_2. (in /home/neil/BLP3/chapter10/checker)
====
==== ERROR SUMMARY: errors from contexts (suppressed: from )
==== malloc/free: in use at exit: bytes in blocks.
==== malloc/free: allocs, frees, bytes allocated.
==== For counts of detected errors, rerun with: -v
==== searching for pointers to not-freed blocks.
==== checked bytes.
====
==== definitely lost: bytes in blocks.
==== possibly lost: bytes in blocks.
==== still reachable: bytes in blocks.
====
==== bytes in blocks are definitely lost in loss record of
==== at 0x4003CA75: malloc (vg_clientfuncs.c:)
==== by 0x8048389: main (checker.c:)
==== by 0x402574F2: __libc_start_main (in /lib/libc.so.)
==== by 0x80482D1: exit@@GLIBC_2. (in /home/neil/BLP3/chapter10/checker)
====
==== LEAK SUMMARY:
==== definitely lost: bytes in blocks.
==== possibly lost: bytes in blocks.
==== still reachable: bytes in blocks.
==== Reachable blocks (those to which a pointer was found) are not shown.
==== To see them, rerun with: --show-reachable=yes
==== $ 这里我们可以看到错误的读取与写入已经被捕获,而所关注的内存块与他们被分配的位置相关联。我们可以使用调试器在出错点断开程序。 valgrind 有许多选项,包含特定的错误类型表达式与内存泄露检测。要检测我们的例子泄露,我们必须使用一个传递给valgrind的选项。当程序结束时要检测内存泄 露,我们需要指定 --leak-check=yes。我们可以使用valgrind --help得到一个选项列表。 工作原理 我们的程序在valgrind的控制下执行,这会检测我们程序所执行的各种动作,并且执行许多检测,包括内存访问。如果程序访问一个已分配的内存块并且访问 是非法的,valgrind就会输出一条信息。在程序结束时,一个垃圾收集例程就会运行来检测是否在存在分配的内存块没有被释放。这些孤儿内存也会被报告。
基于c语言中调试工具的用法汇总(不包含gdb)【转】的更多相关文章
- JAVA语言中冒号的用法
近来由于本人要介入android平台的开发,所以就买了本JAVA语言的书学习.学习一段时间来,我的感觉是谭浩强就是厉害,编写的<C编程语言>系列丛书不愧是经典.书中对C语言的介绍既系统又全 ...
- js中typeof的用法汇总[转载]
http://www.jb51.net/article/43187.htm JavaScript中的typeof其实非常复杂,它可以用来做很多事情,但同时也有很多怪异的表现.本文列举出了它的多个用法, ...
- C++中cin的用法汇总
cin可以用于接收输入,最常见的是从控制台接收.在刚学习C++的时候经常会用cin来接收数据,这里想要系统的总结一下cin的用法,保证不灌水. C++中的cin是一个 istream对象,从标准输入中 ...
- c语言中#和##的用法
一.一般用法 我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起. 用法: #include<cstdio> #include<climits> using nam ...
- Vue3中插槽(slot)用法汇总
Vue中的插槽相信使用过Vue的小伙伴或多或少的都用过,但是你是否了解它全部用法呢?本篇文章就为大家带来Vue3中插槽的全部用法来帮助大家查漏补缺. 什么是插槽 简单来说就是子组件中的提供给父组件使用 ...
- C++中const指针用法汇总
这里以int类型为例,进行说明,在C++中const是类型修饰符: int a; 定义一个普通的int类型变量a,可对此变量的值进行修改. const int a = 3;与 int const a ...
- js中typeof的用法汇总
- Python3中的列表用法,看这一篇就够了
类似C语言中的列表用法 ---------------------------------------------------------------------------------------- ...
- 理解C语言中几个常见修饰符
写在前面 今天下午一个同事问「register」关键字是什么作用?噢,你说的是「register」啊,它的作用是……脑袋突然断片儿,我擦,啥意思来着,这么熟悉的陌生感.做C语言开发时间也不短了,不过好 ...
随机推荐
- Python_深浅拷贝
深浅拷贝 ‘copy’和'='的区别:copy会开辟一个新的空间,而‘=’不会. 浅copy只会copy第一层,再里边的就进行共享了. 需要记住的是copy之后记住的是内存寻址地址,而浅copy时如果 ...
- Ansible学习 安装
对于运维人员来说,自动化工具是日常工作中比不可少的.Ansible是一个很好的自动化工具. Ansible默认使用SSH协议管理机器,在管理主机上安装Ansible,管理主机和被管理主机只要安装了py ...
- OpenFaceswap 入门教程(2):软件使用篇!
安装完OpenFaceswap之后,是不是就迫不及待的想要“见证奇迹”了呢? 都说磨刀不误砍柴工.开始之前请先做一个准备.然后大致了解一下换脸的过程 换脸基本步骤是: 把视频切成很多图片 把图片中的人 ...
- JZOJ 4272. 【NOIP2015模拟10.28B组】序章-弗兰德的秘密
272. [NOIP2015模拟10.28B组]序章-弗兰德的秘密 (File IO): input:frand.in output:frand.out Time Limits: 1000 ms M ...
- 17.VUE学习之- v-for指令的使用方法
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 【转】iPhone通讯录AddressBook.framework和AddressBookUI.framework的应用
通讯录中联系人相关的应用iPhone提供了两个框架:AddressBook.framework和AddressBookUI.framework,使用这两个框架我们可以在程序中访问并显示iPhone数据 ...
- 面试常问 Java基础 冒泡排序
冒泡排序就是对一个数组进行排序. 用双层for循环就可以解决. 第一层,决定排序的次数. n个数,进行n-1次排序就行了. 第二层,把相邻的两个数比较,谁小,放到后面和下一个数字比较,谁小谁放到后面, ...
- Redis实现之压缩列表
压缩列表 压缩列表(ziplist)是列表键和哈希键的底层实现之一,当一个列表键只包含少量列表项,并且每个列表项要嘛是整数值,要嘛是比较短的字符串,那么Redis就会使用压缩列表来做列表键的底层实现. ...
- itchat 动态注册
动态注册时可以选择将itchat.run()放入另一线程或使用configured_reply()方法处理消息. 两种方法分别是: # 使用另一线程,但注意不要让程序运行终止 import threa ...
- mysql插入、修改、删除
联合查询: union:合并.联合,将多次查询结果合并成一个结果 语法: 查询语句1: union[all] 查询语句2: union [all] ... 意义 1.将一条比较复杂的查询语句可拆分成多 ...