Is it true that the program is incorrectly written only during compilation? Is there any tool that allows the machine to help you check for errors when it is not compiled?
The answer is: yes !!
Splint tool. Expressed in the simplest hello world:
============================================
Copy codeThe Code is as follows:/* the error is obvious */
# Include <stdio. h>
Int main (void)
{
Print ("hello world \ n", s );
Return
}
-----------------------------------------------------
Casio $ splint-strict foo. c
Splint 3.1.1 --- 03 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... <--------- the prinf function does not exist
Statement has no visible effect --- no values are modified. It may modify
Something through a call to an unconstrained function. (Use-now.tuncon
Inhibit warning)
Foo. c: 7: 2: Parse Error. (For help on parse errors, see splint-help <------ corresponding return syntax Error
Parseerrors .)
* ** Cannot continue.
========================================================== =====
Cxref
The cxref program analyzes the C source code and generates a cross reference. It shows where each symbol is mentioned in the program. It uses each symbol that marks the asterisk to define a sorting list, as shown below:
SYMBOL FILE FUNCTION LINE
BASENID prog. c-* 12*96 124 126 146 156
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
Callprog. c-* 19
Prog. c main 54
On the author's machine, the previous input is generated using the following command in the program's source code directory:
$ Cxref *. c *. h
However, the actual syntax varies with versions. View our system documentation or man manual for more information.
Cflow <enter cflow *. c for use. You can immediately clear what function is called.>
The cflow program outputs a function call tree, which is a chart showing the function call relationship. This is useful for viewing the program structure to understand how it operates and to understand the impact on a function. Some versions of cflow can work on the target file and source code at the same time. View the manual page for more detailed operations.
The following is an example output from a cflow version (cflow-2.0) where the cflow version 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}
10 fprintf {}
11 exit {}
From this output, we can see that the main function calls show_all_lists, while show_all_lists calls display_list and display_list calls printf.
A cflow option in this version is-I, which generates a reverse flow chart. For each function, cflow lists other functions that call it. This may sound complicated, but it is not. The following is an example.
19 display_list {prcc. c 1056}
20 show_all_lists {prcc. c 1070}
21 exit {}
22 main {prcc. c 70}
23 show_all_lists {prcc. c 1070}
24 usage {prcc. c 59}
...
74 printf {}
75 display_list {prcc. c 1056}
76 maketag {prcc. c 487}
77 show_all_lists {prcc. c 1070}
78 main {prcc. c 70}
...
99 usage {prcc. c 59}
100 main {prcc. c 70}
For example, this tells us that the exit function has main, show_all_lists, and usage.
Use prof/gprof for Performance Testing
When we try to track the performance of a program, a very useful technique is to execute performance testing (execution profiling ). It is usually supported by special compiler options and auxiliary programs. The performance of a program shows where it takes time.
The prof Program (and its GNU version gprof) will generate a report in the execution Tracing file generated when the performance test program is running. An executable performance test is generated by the specified-p option (for prof) or-pg option (for gprof:
$ Cc-pg-o program. c
This program uses a special version of C library for Link and is modified to include the monitoring code. Different system results may be different, but they are usually achieved by arranging programs with frequent interruptions and recording the execution location. Monitoring data is written to a file in the current directory, mon. out (gprof is gmon. out ).
$./Program
$ Ls-ls
2-rw-r -- 1 neil users 1294 Feb 4 gmon. out
Run gprof./program to view the following report.
The prof/gprof program reads the monitoring data and generates a report. View its manual page to learn more about its program options. The following uses gprof output as an example:
Cumulative self total
Time seconds cils ms/call name
18.5 0.10 0.10 8664 0.01 _ doscan [4]
18.5 0.20 0.10 mcount (60)
14.8 0.28 0.08 43320 0.00 _ number [5]
9.3 0.33 0.05 8664 0.01 _ format_arg [6]
7.4 0.37 0.04 112632 0.00 _ ungetc [8]
7.4 0.41 0.04 8757 0.00 _ memccpy [9]
7.4 0.45 0.04 1 40.00 _ main [2]
3.7 0.47 0.02 53 0.38 _ read [12]
3.7 0.49 0.02 w4str [10]
1.9 0.50 0.01 26034 0.00 _ strlen [16]
1.9 0.51 0.01 8664 0.00 0.00 strncmp [17]
Memory debugging
A region rich in bugs and difficult to track and debug is dynamic memory allocation. If we compile a program that uses malloc and free to allocate memory, it is very important that we track the memory blocks we allocate and ensure that we do not use the released memory blocks.
Generally, the 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, it will become an inaccessible memory block. This is a memory leak that will make our program larger. If we leak a large amount of memory, our system will slow down and eventually use up the memory.
If we write data into the end part of an allocated memory block (or at the beginning of a memory block, we are very likely to break the malloc library to track the data structures used for allocation. In this situation, calling malloc or even free at a certain time in the future will cause a segment error, and our program will crash. The accuracy of tracking errors is very difficult, because it is very likely that he has occurred many times before the event that caused the crash.
It's not surprising that some tools, commercial or free, can help to deal with these two types of problems. For example, there are many different versions of malloc and free, some of these include additional code in the allocation and collection checks try to detect a memory block is released twice or some other abuse type.
ElectricFence
The ElectricFence library is developed by Bruce Perens and is provided as an optional component in some Linux releases, such as RedHat, and can be obtained on the network. He tried to use Linux's virtual pseudo-Memory tool to protect the memory used by malloc and free, thus terminating the program when the memory is damaged.
Test-ElectricFence
The following program, efence. c, uses malloc to allocate a memory block, and then writes data at the end of the excess block. Let's take a look at what will happen.
Copy codeThe Code is 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;/* The write is invalid */
Exit (0 );
}
When we compile and run this program, we will not see further behavior. However, it seems that there are some problems with the memory areas allocated by malloc, And we have encountered problems.
$ Cc-o efence. c
$./Efence
$
However, if we use the javasicfence library libefence. a to link this program, we will get an immediate response.
$ Cc-o efence. c-lefence
$./Efence
Electric Fence 2.2.0 Copyright (C) 1987-1999 Bruce Perens <bruce@perens.com>
Segmentation fault
$
Run the tool in the debugger to locate the problem:
$ Cc-g-o 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
10 ptr [1024] = 0;
(Gdb)
Working Principle
Electric replaces malloc and associates functions with virtual memory features of computer processors to prevent illegal memory access. When such an access occurs, a segment error message is thrown to terminate the program.
Valgrind
Valgrind is a tool that can detect many issues we have discussed. In fact, he can detect data access errors and memory leaks. Maybe he hasn't been included in our Linux release, but we can do it in http://developer.kde.org /~ Sewardj.
The program does not need to be re-compiled using valgrind, but we can even call the memory access of a running program. It is worth noting that he has been used in major development projects, including KDE version 3.
Test-valgrind
The following program, checker. c, allocates some memory, reads the location that exceeds the memory limit, writes data outside its end, and makes it inaccessible.
Copy codeThe Code is as follows: # include <stdio. h>
# Include <stdlib. h>
Int main (void)
{
Char * ptr = (char *) malloc (1024 );
Char ch;
/* Uninitialized read */
Ch = ptr [1024];/* illegal read */
/* Write beyond the block */
Ptr [1024] = 0;/* The write is invalid */
/* Orphan the block */
Ptr = 0;/* wild pointer */
Exit (0 );
}
To use valgrind, we only need to simply run the valgrind command, pass the options we want to detect, and then run the program using its parameters.
When we use valgrind to run our program, we can see that many problems have been 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 more 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 is 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 is 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 allocated.
= 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 in 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 was found) are not shown.
= 3436 = To see them, rerun with: -- show-reachable = yes
= 3436 = $
Here we can see that the error reading and writing have been captured, and the memory block concerned is associated with their allocated location. We can use the debugger to disconnect the program at an error point.
Valgrind has many options, including specific error type expressions and Memory Leak Detection. To detect leakage of our example, we must use an option passed to valgrind. When the program ends to detect memory leakage, We need to specify -- leak-check = yes. We can use valgrind -- help to get a list of options.
Working Principle
Our program is executed under the control of valgrind. This will detect the various actions performed by our program and perform many checks, including memory access. If the program accesses an allocated memory block and the access is illegal, valgrind will output a message. At the end of the program, a garbage collection routine runs to check whether allocated memory blocks are not released. The orphan memory will also be reported.