# # This file contains a few gdb macros (user defined commands) to extract # useful information from kernel crashdump (kdump) like stack traces of # all the processes or a particular process and trapinfo. # # These macros can be used by copying this file in .gdbinit (put in home # directory or current directory) or by invoking gdb command with # --command= option # # Credits: # Alexander Nyberg # V Srivatsa # Maneesh Soni # define bttnobp set $tasks_off=((size_t)&((struct task_struct *)0)->tasks) set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next) set $init_t=&init_task set $next_t=(((char *)($init_t->tasks).next) - $tasks_off) set var $stacksize = sizeof(union thread_union) while ($next_t != $init_t) set $next_t=(struct task_struct *)$next_t printf "\npid %d; comm %s:\n", $next_t.pid, $next_t.comm printf "===================\n" set var $stackp = $next_t.thread.sp set var $stack_top = ($stackp & ~($stacksize - 1)) + $stacksize while ($stackp < $stack_top) if (*($stackp) > _stext && *($stackp) < _sinittext) info symbol *($stackp) end set $stackp += 4 end set $next_th=(((char *)$next_t->thread_group.next) - $pid_off) while ($next_th != $next_t) set $next_th=(struct task_struct *)$next_th printf "\npid %d; comm %s:\n", $next_t.pid, $next_t.comm printf "===================\n" set var $stackp = $next_t.thread.sp set var $stack_top = ($stackp & ~($stacksize - 1)) + stacksize while ($stackp < $stack_top) if (*($stackp) > _stext && *($stackp) < _sinittext) info symbol *($stackp) end set $stackp += 4 end set $next_th=(((char *)$next_th->thread_group.next) - $pid_off) end set $next_t=(char *)($next_t->tasks.next) - $tasks_off end end document bttnobp dump all thread stack traces on a kernel compiled with !CONFIG_FRAME_POINTER end define btthreadstack set var $pid_task = $arg0 printf "\npid %d; comm %s:\n", $pid_task.pid, $pid_task.comm printf "task struct: " print $pid_task printf "===================\n" set var $stackp = $pid_task.thread.sp set var $stacksize = sizeof(union thread_union) set var $stack_top = ($stackp & ~($stacksize - 1)) + $stacksize set var $stack_bot = ($stackp & ~($stacksize - 1)) set $stackp = *((unsigned long *) $stackp) while (($stackp < $stack_top) && ($stackp > $stack_bot)) set var $addr = *(((unsigned long *) $stackp) + 1) info symbol $addr set $stackp = *((unsigned long *) $stackp) end end document btthreadstack dump a thread stack using the given task structure pointer end define btt set $tasks_off=((size_t)&((struct task_struct *)0)->tasks) set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next) set $init_t=&init_task set $next_t=(((char *)($init_t->tasks).next) - $tasks_off) while ($next_t != $init_t) set $next_t=(struct task_struct *)$next_t btthreadstack $next_t set $next_th=(((char *)$next_t->thread_group.next) - $pid_off) while ($next_th != $next_t) set $next_th=(struct task_struct *)$next_th btthreadstack $next_th set $next_th=(((char *)$next_th->thread_group.next) - $pid_off) end set $next_t=(char *)($next_t->tasks.next) - $tasks_off end end document btt dump all thread stack traces on a kernel compiled with CONFIG_FRAME_POINTER end define btpid set var $pid = $arg0 set $tasks_off=((size_t)&((struct task_struct *)0)->tasks) set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next) set $init_t=&init_task set $next_t=(((char *)($init_t->tasks).next) - $tasks_off) set var $pid_task = 0 while ($next_t != $init_t) set $next_t=(struct task_struct *)$next_t if ($next_t.pid == $pid) set $pid_task = $next_t end set $next_th=(((char *)$next_t->thread_group.next) - $pid_off) while ($next_th != $next_t) set $next_th=(struct task_struct *)$next_th if ($next_th.pid == $pid) set $pid_task = $next_th end set $next_th=(((char *)$next_th->thread_group.next) - $pid_off) end set $next_t=(char *)($next_t->tasks.next) - $tasks_off end btthreadstack $pid_task end document btpid backtrace of pid end define trapinfo set var $pid = $arg0 set $tasks_off=((size_t)&((struct task_struct *)0)->tasks) set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next) set $init_t=&init_task set $next_t=(((char *)($init_t->tasks).next) - $tasks_off) set var $pid_task = 0 while ($next_t != $init_t) set $next_t=(struct task_struct *)$next_t if ($next_t.pid == $pid) set $pid_task = $next_t end set $next_th=(((char *)$next_t->thread_group.next) - $pid_off) while ($next_th != $next_t) set $next_th=(struct task_struct *)$next_th if ($next_th.pid == $pid) set $pid_task = $next_th end set $next_th=(((char *)$next_th->thread_group.next) - $pid_off) end set $next_t=(char *)($next_t->tasks.next) - $tasks_off end printf "Trapno %ld, cr2 0x%lx, error_code %ld\n", $pid_task.thread.trap_no, \ $pid_task.thread.cr2, $pid_task.thread.error_code end document trapinfo Run info threads and lookup pid of thread #1 'trapinfo ' will tell you by which trap & possibly address the kernel panicked. end define dump_record set var $desc = $arg0 if ($argc > 1) set var $prev_flags = $arg1 else set var $prev_flags = 0 end set var $info = &$desc->info set var $prefix = 1 set var $newline = 1 set var $begin = $desc->text_blk_lpos.begin % (1U << prb->text_data_ring.size_bits) set var $next = $desc->text_blk_lpos.next % (1U << prb->text_data_ring.size_bits) # handle data-less record if ($begin & 1) set var $text_len = 0 set var $log = "" else # handle wrapping data block if ($begin > $next) set var $begin = 0 end # skip over descriptor id set var $begin = $begin + sizeof(long) # handle truncated message if ($next - $begin < $info->text_len) set var $text_len = $next - $begin else set var $text_len = $info->text_len end set var $log = &prb->text_data_ring.data[$begin] end # prev & LOG_CONT && !(info->flags & LOG_PREIX) if (($prev_flags & 8) && !($info->flags & 4)) set var $prefix = 0 end # info->flags & LOG_CONT if ($info->flags & 8) # (prev & LOG_CONT && !(prev & LOG_NEWLINE)) if (($prev_flags & 8) && !($prev_flags & 2)) set var $prefix = 0 end # (!(info->flags & LOG_NEWLINE)) if (!($info->flags & 2)) set var $newline = 0 end end if ($prefix) printf "[%5lu.%06lu] ", $info->ts_nsec / 1000000000, $info->ts_nsec % 1000000000 end if ($text_len) eval "printf \"%%%d.%ds\", $log", $text_len, $text_len end if ($newline) printf "\n" end # handle dictionary data set var $begin = $desc->dict_blk_lpos.begin % (1U << prb->dict_data_ring.size_bits) set var $next = $desc->dict_blk_lpos.next % (1U << prb->dict_data_ring.size_bits) # handle data-less record if ($begin & 1) set var $dict_len = 0 set var $dict = "" else # handle wrapping data block if ($begin > $next) set var $begin = 0 end # skip over descriptor id set var $begin = $begin + sizeof(long) # handle truncated message if ($next - $begin < $info->dict_len) set var $dict_len = $next - $begin else set var $dict_len = $info->dict_len end set var $dict = &prb->dict_data_ring.data[$begin] end if ($dict_len > 0) set var $idx = 0 set var $line = 1 while ($idx < $dict_len) if ($line) printf " " set var $line = 0 end set var $c = $dict[$idx] if ($c == '\0') printf "\n" set var $line = 1 else if ($c < ' ' || $c >= 127 || $c == '\\') printf "\\x%02x", $c else printf "%c", $c end end set var $idx = $idx + 1 end printf "\n" end end document dump_record Dump a single record. The first parameter is the descriptor sequence number, the second is optional and specifies the previous record's flags, used for properly formatting continued lines. end define dmesg # definitions from kernel/printk/printk_ringbuffer.h set var $desc_committed = 1 set var $desc_finalized = 2 set var $desc_sv_bits = sizeof(long) * 8 set var $desc_flags_shift = $desc_sv_bits - 2 set var $desc_flags_mask = 3 << $desc_flags_shift set var $id_mask = ~$desc_flags_mask set var $desc_count = 1U << prb->desc_ring.count_bits set var $prev_flags = 0 set var $id = prb->desc_ring.tail_id.counter set var $end_id = prb->desc_ring.head_id.counter while (1) set var $desc = &prb->desc_ring.descs[$id % $desc_count] # skip non-committed record set var $state = 3 & ($desc->state_var.counter >> $desc_flags_shift) if ($state == $desc_committed || $state == $desc_finalized) dump_record $desc $prev_flags set var $prev_flags = $desc->info.flags end set var $id = ($id + 1) & $id_mask if ($id == $end_id) loop_break end end end document dmesg print the kernel ring buffer end