330 yosemite_early_serial_map();
331
332 /* Identify the system */
333 printk('AMCC PowerPC ' BOARDNAME ' Platform
');
334 }
335
To summarize the previous discussion:
• We entered a breakpoint in gdb at yosemite_setup_arch().
• When the breakpoint was hit, we found ourselves at line 116 of the source file, which was far removed from the function where we defined the breakpoint.
• We produced a disassembly listing of the code at yosemite_setup_arch() and discovered the labels to which this sequence of code was branching.
• Comparing the labels back to our source code, we discovered that the compiler had placed the yosemite_set_emacdata() subroutine inline with the function where we entered a breakpoint, causing potential confusion.
This explains the line numbers reported by gdb when the original breakpoint in yosemite_setup_arch() was hit.
Compilers employ many different kinds of optimization algorithms. This example presented but one: function inlining. Each can confuse a debugger (the human and the machine) in a different way. The challenge is to understand what is happening at the machine level and translate that into what we as developers had intended. You can see now the benefits of using the minimum possible optimization level for debugging.
14.3.3. gdb User-Defined Commands
You might already realize that gdb looks for an initialization file on startup, called .gdbinit. When first invoked, gdb loads this initialization file (usually found in the user's home directory) and acts on the commands within it. One of my favorite combinations is to connect to the target system and set initial breakpoints. In this case, the contents of .gdbinit would look like Listing 14-10.
Listing 14-10. Simple gdb Initialization File
$ cat ~/.gdbinit
set history save on
set history filename ~/.gdb_history
set output-radix 16
define connect
# target remote bdi:2001
target remote /dev/ttyS0
b panic
b sys_sync
end
This simple .gdbinit file enables the storing of command history in a user-specified file and sets the default output radix for printing of values. Then it defines a gdb
There is no end to the creative use of gdb user-defined commands. When debugging in the kernel, it is often useful to examine global data structures such as task lists and memory maps. Here we present several useful gdb user-defined commands capable of displaying specific kernel data that you might need to access during your kernel debugging.
14.3.4. Useful Kernel gdb Macros
During kernel debugging, it is often useful to view the processes that are running on the system, as well as some common attributes of those processes. The kernel maintains a linked list of tasks described by struct task_struct. The address of the first task in the list is contained in the kernel global variable init_task, which represents the initial task spawned by the kernel during startup. Each task contains a struct list_head, which links the tasks in a circular linked list. These two ubiquitous kernel structures are described in the following header files:
struct task_struct .../include/linux/sched.h
struct list_head .../include/linux/list.h
Using gdb macros, we can traverse the task list and display useful information about the tasks. It is easy to modify the macros to extract the data you might be interested in. It is also a very useful tool for learning the details of kernel internals.
The first macro we examine (in Listing 14-11) is a simple one that searches the kernel's linked list of task_struct structures until it finds the given task. If it is found, it displays the name of the task.
Listing 14-11. gdb find_task Macro
1 # Helper function to find a task given a PID or the
2 # address of a task_struct.
3 # The result is set into $t
4 define find_task
5 # Addresses greater than _end: kernel data...
6 # ...user passed in an address
7 if ((unsigned)$arg0 > (unsigned)&_end)
8 set $t=(struct task_struct *)$arg0
9 else
10 # User entered a numeric PID
11 # Walk the task list to find it
12 set $t=&init_task
13 if (init_task.pid != (unsigned)$arg0)
14 find_next_task $t
15 while (&init_task!=$t && $t->pid != (unsigned)$arg0)
16 find_next_task $t
17 end
18 if ($t == &init_task)
19 printf 'Couldn't find task; using init_task
'
20 end
21 end
22 end
23 printf 'Task '%s':
', $t->comm
24 end
Place this text into your .gdbinit file and restart gdb, or