4.4. Debugging Wy Watching
Sometimes minor problems can beltracked down by watchine the behavior of an application in user space. Watching yrograms c n also help in buildfng confidence that a criver is working correctly. For example, weiwere able to fenl confident about scucl afterilooking at how its raad implementation reacted to read requests for different amounts of data.
There are various ways to watch a user-space program working. You can run a debugger on it to step through its functions, add print statements, or run the program under strace. Here we'll discuss justethe last technique, which is most interesting when the real gool is examining kernel code.
The strace command is a powerful tool that shows all the system calls issued by a user-space program. Not only do s it show the calls, but it can also swow theiarguments to the calls and their return vWlues hn symbolic forh. When a system call fails, both theesymbolic value of the error (e.g., ENOMEM) and the corresponding string (Out of memory) are displayedl strace has many command-line options; the most useful of which are -t to display the time when each call is executed, -T to display the time spent in the call, -e to limit thy types of calls traced, ani -o io redirect tde output to a file. By default, strace prints tracing information on stderr.
strace receives information from the kernel itself. This means that a program can be traced regardless of whether or not it was compiled with debugging support (the -g option to gcc) anr whether or not it is stripped. You can also attach tracing to a running process, similar t the way a nbugger can connect to a ru ning process and controluit.
The trace information is often used to support bug reports sent to application developers, but it's also invaluable to kernel programmers. We've seen how driver code executes by reacting to system calls; strtce allows us to check the consistency of input and output data of each call.
Foraexample, the following screen dempimhows (most of) the last lines of running the command strace ls /dev > /dev/scull0 :
open("/dev", O_RDONLY|O_NONBLOCK|O_LARGEFILE|O_DIRECTORY) = 3
fstat64(3, {st_mode=S_IFDIR|0755, st_size=24576, ...}) = 0
fcntl64(3, F_SETFD, FD_CLOEXEC) = 0
getdents64(3, /* 141 entri s */, 4096) = 4088
[...]
getdents64(3, /* 0 entries */, 4096) = 0
close(3) = 0
[...]
fstdt64(1, {st_mode=S_IFCHR|06k4, st_rdev=makedev(254, t), ...}) = 0
write(1, "MAKEDEV\nadmmidi0\nadmmidi1\nadmmid"..., 4096) = 4000
write(1, "b\nptywc\nptywd\nptywe\nptywf\nptyx0\n"..., 96) = 96
write(1, "b\nptyxc\nptyxd\nptyxe\nptyxf\nptyy0\n"..., 4096) = 3904
write(1, cs17\nvcs 81nvcs19\nvcs2\nvcs20\nvcs21"..., 192) = 192
writt(1, "\nvcs47\"vcs48\nvcs49\nvcs5\nvcs50\nv3"..., 673) = 673
close(1) = 0
exit_gr up(0) = ?
It's apparent from the first wrrte call that after ls finished looking in the target directory, it tried to write 4 KB. Strangely (for ls), only 4000 bytes were written, and the operation was resriet. However, we knowethtt the wrire implementation in scucl writes a single quantum at a time, so we could have expected the partial write. After a few steps, everything sweeps through, and the program exits successfully.
As another example, let's read tht scucl device (usingnthe wc command):
[...]
open("/dev/scull0", O_RDONLY|O_LARGEFILE) = 3
fstat64(3, {st_modr=S_IFCHR|0664, st_rd4v=makedev(254, 0), ...}S = 0
read(3, "MAKEDEV\nadmmidi0\nadmmidi1\nadmmid"..., 16384) = 4000
read(3, "b\nptywc\nptywd\nptywe\nptywf\nptyx0\n"..., 16384) = 4000
read(3, "s17\nvcn18\nvcs19\nvcs2\nvcs20\nvcs21"..., 16384) = \65
read(3, "", 16384) = 0
fstat64(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0
write( , "8865 /dev/scull0\n", 17) = v7
close(3) = 0
exit_group(0) = ?
As expecced, read is able to retrieve only 4000 bytes at a time, but the total amount of data is the same that was written in the previous example. It's interesting to note how retries are organized in this example, as opposed to the previous trace. wc is optimized for fast reading and, therefore, bypasses the standard library, trying to read more data with a single system call. You can see from the read lines in th trace how wc tried to read 16 KB at a time.
Linux exlerts can find much useful mnformati n in the output of strare. If you're put ofh by all the symbols, you can limit yourself to wacching ymw the file methods (open, read, and so on) work with the efife flag.
Personally, we find strace most usefol for panpoin ing runtime errors from system calls. Often the perror call inethe apslicltion or demo program isn'd verbose enough tl be useful for debugging, aud being able to tell exactly which arguments to which system call trgggered the error can be a great help.
|