Is it only when you compile that you know that your program has written errors? Did you let the machine help you check the wrong tool without compiling it?
The answer is: YES!!
Splint tool. In one of the simplest Hello world to express:
=====================================
Copy Code code as follows:
/* ERROR is very obvious * *
#include <stdio.h>
int main (void)
{
Print ("Hello world\n", s);
Return
}
-----------------------------------------------------
casio$ splint-strict foo.c
Splint 3.1.1---Nov 2006
FOO.C: (in function Main)
Foo.c:5:2:unrecognized Identifier:print <-------find Print not printf
Identifier used in code has not been declared. (Use-unrecog to inhibit
Warning
Foo.c:5:25:unrecognized identifier:s <-------undefined variable s
Foo.c:5:2:statement has no effect (possible undected modification through call
To unconstrained function print: print ("Hello wor ... <---------does not exist Prinf function"
Statement has no visible effect---No values are modified. It may Modify
Something through a call to a unconstrained function. (Use-noeffectuncon to
Inhibit warning)
Foo.c:7:2:parse Error. (For parse errors, splint-help <------corresponding return syntax error
Parseerrors.)
Cannot continue.
=============================================
Cxref
Cxref program analyzes C source code and generates a cross-reference. He shows where each symbol is mentioned in the program. He generates a list of rows using each symbol definition location that marks the asterisk, as follows:
SYMBOL FILE FUNCTION Line
Basenid prog.c-*12 *96 124 126 146 156 166
Binsize prog.c-*30 197 198 199 206
Bufmax PROG.C-*44 45 90
Bufsiz/usr/include/stdio.h-*4
Eof/usr/include/stdio.h-*27
ARGC prog.c-36
PROG.C Main *37 61 81
argv prog.c-36
PROG.C Main *38 61
Calldata prog.c-*5
PROG.C Main 64 188
Calls Prog.c-*19
PROG.C Main 54
On the author's machine, the previous input is generated by using the following command in the source directory of the program:
$ cxref *.c *.h
But the actual syntax differs depending on the version. More information can be obtained by looking at our system documentation or the man manual.
Cflow < When you use the input Cflow *.c on it. You can get rid of it right away. What functions are called .>
The Cflow program outputs a function call tree, which is a diagram that shows the function call relationship. This is useful for viewing the structure of a program to see how it works and how it affects a function. Some versions of Cflow can work on both the target file and the source code. View the man page we can learn more about the operation.
The following is an example output obtained by a cflow version (cflow-2.0), the Cflow version of which is maintained by Marty Leisner and can be obtained online.
1 file_ungetc {PRCC.C 997}
2 main {PRCC.C 70}
3 Getopt {}
4 show_all_lists {PRCC.C 1070}
5 display_list {PRCC.C 1056}
6 printf {}
7 Exit {}
8 Exit {}
9 Usage {PRCC.C 59}
Ten fprintf {}
One exit {}
From this output we can see the main function call Show_all_lists, while the show_all_lists call Display_list,display_list itself invokes printf.
One option for this version of Cflow is-I, which generates an inverted flowchart. For each function, Cflow lists the other functions that call him. This may sound complicated, but it's not actually the case. Here is an example.
display_list {PRCC.C 1056}
show_all_lists {PRCC.C 1070}
Exit {}
Main {PRCC.C 70}
show_all_lists {PRCC.C 1070}
Usage {PRCC.C 59}
...
The "printf" {}
display_list {PRCC.C 1056}
Maketag {PRCC.C 487}
show_all_lists {PRCC.C 1070}
Main {PRCC.C 70}
...
Usage {PRCC.C 59}
Main {PRCC.C 70}
For example, this tells us that the function to call exit has main,show_all_lists and usage.
Performing performance tests with Prof/gprof
A very useful technique when we try to track the performance of a program is to perform a performance test (execution profiling). Typically supported by special compiler options and helper programs, the performance of a program shows where he spends his time.
The Prof program (and its GNU version gprof) outputs reports from the execution trace file generated by the performance test program when it runs. An executable performance test is generated by specifying the-p option (for Prof) or the-PG option (for gprof):
$ cc-pg-o Program PROGRAM.C
This program is linked using a special version of C library and is modified to contain the monitoring code. Different system results may be different, but usually by scheduling frequently interrupted programs and record execution locations. The monitoring data is written to a file in the current directory, Mon.out (for Gprof gmon.out).
$./program
$ ls-ls
2-rw-r--r--1 Neil users 1294 Feb 4 11:48 gmon.out
Then use the command: Gprof./program can view the following report
The PROF/GPROF program reads these monitoring data and generates a report. View their man pages for detailed information about their program options. The following is an example of gprof output:
Cumulative self Self Total
Time seconds seconds calls Ms/call Ms/call name
18.5 0.10 0.10 8664 0.01 0.03 _doscan [4]
18.5 0.20 0.10 Mcount (60)
14.8 0.28 0.08 43320 0.00 0.00 _number [5]
9.3 0.33 0.05 8664 0.01 0.01 _format_arg [6]
7.4 0.37 0.04 112632 0.00 0.00 _UNGETC [8]
7.4 0.41 0.04 8757 0.00 0.00 _memccpy [9]
7.4 0.45 0.04 1 40.00 390.02 _main [2]
3.7 0.47 0.02 0.38 0.38 _read [12]
3.7 0.49 0.02 W4str [10]
1.9 0.50 0.01 26034 0.00 0.00 _strlen [16]
1.9 0.51 0.01 8664 0.00 0.00 STRNCMP [17]
Memory debugging
One area that is rich in bugs and difficult to track debugging is dynamic memory allocation. If we compile a program that allocates memory using malloc and free, it is important that we keep track of the chunks of memory we allocate and that we do not use the memory blocks that have been freed.
Typically, memory is allocated by malloc and assigned to a pointer variable. If the pointer variable is modified and no other pointer points to the memory block, he becomes an inaccessible block of memory. This is a memory leak and will make our program size larger. If we leak a lot of memory, our system slows down and eventually runs out of memory.
If we write data at the end of an allocated block of memory (or at the beginning of a block of memory), it is very likely that we will break the malloc library to track the data structure used for the allocation. In this case, calling malloc, or even free, at some point in the future can cause a segment error, and our program crashes. It is very difficult to trace the exact point of the error, as it is likely that he has been around for some time before the event that caused the crash occurred.
It's not surprising that there are tools, businesses, or freedoms that can help deal with both types of problems. For example, there are many different versions of malloc and free, some of which contain additional code for detection on allocations and recoveries trying to detect a block of memory being released two times or some other type of abuse.
Electricfence
The Electricfence library is developed by Bruce Perens and is available as an optional component in some Linux distributions, such as Redhat, and is already available on the web. He tried to use the Linux virtual Memory tool to protect the memory used by malloc and free, thereby terminating the program when memory is corrupted.
Test--electricfence
The following program, EFENCE.C, allocates a block of memory using malloc, and then writes the data at the end of the block. Let's see what happens.
Copy Code code as follows:
#include <stdio.h>
#include <stdlib.h>
int main ()
{
Char *ptr = (char *) malloc (1024);
Ptr[0] = 0;
* Now write beyond the block * *
PTR[1024] = 0;/* Write illegal *
Exit (0);
}
When we compile and run this program, we do not see further behavior. However, it seems that there are some problems with the memory area allocated by malloc, and we are actually in trouble.
$ cc-o efence efence.c
$./efence
$
However, if we use the Electricfence Library and libefence.a to link to the program, we get an immediate response.
$ cc-o efence efence.c-lefence
$./efence
Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>
Segmentation fault
$
Running under the debugger can locate the problem:
$ cc-g-O efence efence.c-lefence
$ gdb Efence
(GDB) Run
Starting program:/home/neil/blp3/chapter10/efence
[New Thread 1024 (LWP 1869)]
Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>
Program received signal SIGSEGV, segmentation fault.
[Switching to Thread 1024 (LWP 1869)]
0X080484AD in Main () at Efence.c:10
Ten ptr[1024] = 0;
(GDB)
Working principle
Electric replaces malloc and prevents illegal memory access by associated functions with the virtual memory attributes of the computer's processor. When such an access occurs, a segment error message is thrown to terminate the program.
Valgrind
Valgrind is a tool that can detect many of the problems we have discussed. In fact, he can detect data access errors and memory leaks. Maybe he's not included in our Linux distribution, but we can get it at HTTP://DEVELOPER.KDE.ORG/~SEWARDJ.
The program does not need to be recompiled with Valgrind, and we can even invoke the memory access of a running program. He is well worth a look, he has been used in major development, including KDE version 3.
Test--valgrind
The following program, CHECKER.C, allocates some memory, reads beyond that memory limit, writes data outside its end, and then makes it inaccessible.
Copy Code code as follows:
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
Char *ptr = (char *) malloc (1024);
Char ch;
/* Uninitialized Read * *
CH = ptr[1024];/* Read illegal */
/* Write Beyond the block * *
PTR[1024] = 0;/* Write illegal *
* * Orphan The BLOCK * *
ptr = 0;/* Wild pointer * *
Exit (0);
}
To use Valgrind, we simply run the Valgrind command, pass the option we want to detect, and then run the program with its parameters.
When we use Valgrind to run our programs, we can see that many problems are diagnosed:
$ valgrind--leak-check=yes-v./checker
==3436== valgrind-1.0.4, a memory error detector for x86 gnu/linux.
==3436== Copyright (C) 2000-2002, and GNU GPL ' d, by Julian Seward.
==3436== estimated CPU clock rate is 452 MHz
==3436== for further details, rerun with:-V
==3436==
==3436== Invalid Read of size 1
==3436== at 0x8048397:main (checker.c:10)
==3436== by 0x402574f2: __libc_start_main (in/lib/libc.so.6)
==3436== by 0x80482d1:exit@ @GLIBC_2.0 (In/home/neil/blp3/chapter10/checker)
==3436== address 0x42ad1424 's 0 bytes after a block of size 1024 Alloc ' d
==3436== at 0x4003ca75:malloc (vg_clientfuncs.c:100)
==3436== by 0x8048389:main (Checker.c:6)
==3436== by 0x402574f2: __libc_start_main (in/lib/libc.so.6)
==3436== by 0x80482d1:exit@ @GLIBC_2.0 (In/home/neil/blp3/chapter10/checker)
==3436==
==3436== Invalid Write of size 1
==3436== at 0x80483a4:main (checker.c:13)
==3436== by 0x402574f2: __libc_start_main (in/lib/libc.so.6)
==3436== by 0x80482d1:exit@ @GLIBC_2.0 (In/home/neil/blp3/chapter10/checker)
==3436== address 0x42ad1424 's 0 bytes after a block of size 1024 Alloc ' d
==3436== at 0x4003ca75:malloc (vg_clientfuncs.c:100)
==3436== by 0x8048389:main (Checker.c:6)
==3436== by 0x402574f2: __libc_start_main (in/lib/libc.so.6)
==3436== by 0x80482d1:exit@ @GLIBC_2.0 (In/home/neil/blp3/chapter10/checker)
==3436==
==3436== ERROR Summary:2 errors from 2 contexts (suppressed:0 from 0)
==3436== malloc/free:in use at exit:1024 bytes in 1 blocks.
==3436== Malloc/free:1 Allocs, 0 frees, 1024 bytes.
==3436== for counts of detected errors, rerun with:-V
==3436== searching for pointers to 1 not-freed blocks.
==3436== checked 3468724 bytes.
==3436==
==3436== definitely lost:1024 bytes in 1 blocks.
==3436== possibly lost:0 bytes in 0 blocks.
==3436== still reachable:0 bytes in 0 blocks.
==3436==
==3436== 1024 bytes in 1 blocks are definitely lost into loss record 1 of 1
==3436== at 0x4003ca75:malloc (vg_clientfuncs.c:100)
==3436== by 0x8048389:main (Checker.c:6)
==3436== by 0x402574f2: __libc_start_main (in/lib/libc.so.6)
==3436== by 0x80482d1:exit@ @GLIBC_2.0 (In/home/neil/blp3/chapter10/checker)
==3436==
==3436== leak SUMMARY:
==3436== definitely lost:1024 bytes in 1 blocks.
==3436== possibly lost:0 bytes in 0 blocks.
==3436== still reachable:0 bytes in 0 blocks.
==3436== reachable blocks (those to which a pointer is found) are not shown.
==3436== to-them, rerun with:--show-reachable=yes
==3436== $
Here we can see that the wrong reads and writes have been captured, and that the memory blocks that are concerned are associated with the location where they are assigned. We can use the debugger to disconnect the program at the error point.
Valgrind has a number of options, including specific error type expressions and memory leak detection. To detect our example leaks, we must use an option passed to Valgrind. When the program ends to detect memory leaks, we need to specify--leak-check=yes. We can use Valgrind--help to get a list of options.
Working principle
Our program executes under Valgrind control, which detects the various actions that our program performs, and performs many tests, including memory access. If the program accesses an allocated block of memory and the access is illegal, valgrind prints a message. At the end of the program, a garbage collection routine runs to detect if the allocated memory block is not freed. These orphan memory will also be reported.