1# 2# This file contains a few gdb macros (user defined commands) to extract 3# useful information from kernel crashdump (kdump) like stack traces of 4# all the processes or a particular process and trapinfo. 5# 6# These macros can be used by copying this file in .gdbinit (put in home 7# directory or current directory) or by invoking gdb command with 8# --command=<command-file-name> option 9# 10# Credits: 11# Alexander Nyberg <alexn@telia.com> 12# V Srivatsa <vatsa@in.ibm.com> 13# Maneesh Soni <maneesh@in.ibm.com> 14# 15 16define bttnobp 17 set $tasks_off=((size_t)&((struct task_struct *)0)->tasks) 18 set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next) 19 set $init_t=&init_task 20 set $next_t=(((char *)($init_t->tasks).next) - $tasks_off) 21 set var $stacksize = sizeof(union thread_union) 22 while ($next_t != $init_t) 23 set $next_t=(struct task_struct *)$next_t 24 printf "\npid %d; comm %s:\n", $next_t.pid, $next_t.comm 25 printf "===================\n" 26 set var $stackp = $next_t.thread.sp 27 set var $stack_top = ($stackp & ~($stacksize - 1)) + $stacksize 28 29 while ($stackp < $stack_top) 30 if (*($stackp) > _stext && *($stackp) < _sinittext) 31 info symbol *($stackp) 32 end 33 set $stackp += 4 34 end 35 set $next_th=(((char *)$next_t->thread_group.next) - $pid_off) 36 while ($next_th != $next_t) 37 set $next_th=(struct task_struct *)$next_th 38 printf "\npid %d; comm %s:\n", $next_t.pid, $next_t.comm 39 printf "===================\n" 40 set var $stackp = $next_t.thread.sp 41 set var $stack_top = ($stackp & ~($stacksize - 1)) + stacksize 42 43 while ($stackp < $stack_top) 44 if (*($stackp) > _stext && *($stackp) < _sinittext) 45 info symbol *($stackp) 46 end 47 set $stackp += 4 48 end 49 set $next_th=(((char *)$next_th->thread_group.next) - $pid_off) 50 end 51 set $next_t=(char *)($next_t->tasks.next) - $tasks_off 52 end 53end 54document bttnobp 55 dump all thread stack traces on a kernel compiled with !CONFIG_FRAME_POINTER 56end 57 58define btthreadstack 59 set var $pid_task = $arg0 60 61 printf "\npid %d; comm %s:\n", $pid_task.pid, $pid_task.comm 62 printf "task struct: " 63 print $pid_task 64 printf "===================\n" 65 set var $stackp = $pid_task.thread.sp 66 set var $stacksize = sizeof(union thread_union) 67 set var $stack_top = ($stackp & ~($stacksize - 1)) + $stacksize 68 set var $stack_bot = ($stackp & ~($stacksize - 1)) 69 70 set $stackp = *((unsigned long *) $stackp) 71 while (($stackp < $stack_top) && ($stackp > $stack_bot)) 72 set var $addr = *(((unsigned long *) $stackp) + 1) 73 info symbol $addr 74 set $stackp = *((unsigned long *) $stackp) 75 end 76end 77document btthreadstack 78 dump a thread stack using the given task structure pointer 79end 80 81 82define btt 83 set $tasks_off=((size_t)&((struct task_struct *)0)->tasks) 84 set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next) 85 set $init_t=&init_task 86 set $next_t=(((char *)($init_t->tasks).next) - $tasks_off) 87 while ($next_t != $init_t) 88 set $next_t=(struct task_struct *)$next_t 89 btthreadstack $next_t 90 91 set $next_th=(((char *)$next_t->thread_group.next) - $pid_off) 92 while ($next_th != $next_t) 93 set $next_th=(struct task_struct *)$next_th 94 btthreadstack $next_th 95 set $next_th=(((char *)$next_th->thread_group.next) - $pid_off) 96 end 97 set $next_t=(char *)($next_t->tasks.next) - $tasks_off 98 end 99end 100document btt 101 dump all thread stack traces on a kernel compiled with CONFIG_FRAME_POINTER 102end 103 104define btpid 105 set var $pid = $arg0 106 set $tasks_off=((size_t)&((struct task_struct *)0)->tasks) 107 set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next) 108 set $init_t=&init_task 109 set $next_t=(((char *)($init_t->tasks).next) - $tasks_off) 110 set var $pid_task = 0 111 112 while ($next_t != $init_t) 113 set $next_t=(struct task_struct *)$next_t 114 115 if ($next_t.pid == $pid) 116 set $pid_task = $next_t 117 end 118 119 set $next_th=(((char *)$next_t->thread_group.next) - $pid_off) 120 while ($next_th != $next_t) 121 set $next_th=(struct task_struct *)$next_th 122 if ($next_th.pid == $pid) 123 set $pid_task = $next_th 124 end 125 set $next_th=(((char *)$next_th->thread_group.next) - $pid_off) 126 end 127 set $next_t=(char *)($next_t->tasks.next) - $tasks_off 128 end 129 130 btthreadstack $pid_task 131end 132document btpid 133 backtrace of pid 134end 135 136 137define trapinfo 138 set var $pid = $arg0 139 set $tasks_off=((size_t)&((struct task_struct *)0)->tasks) 140 set $pid_off=((size_t)&((struct task_struct *)0)->thread_group.next) 141 set $init_t=&init_task 142 set $next_t=(((char *)($init_t->tasks).next) - $tasks_off) 143 set var $pid_task = 0 144 145 while ($next_t != $init_t) 146 set $next_t=(struct task_struct *)$next_t 147 148 if ($next_t.pid == $pid) 149 set $pid_task = $next_t 150 end 151 152 set $next_th=(((char *)$next_t->thread_group.next) - $pid_off) 153 while ($next_th != $next_t) 154 set $next_th=(struct task_struct *)$next_th 155 if ($next_th.pid == $pid) 156 set $pid_task = $next_th 157 end 158 set $next_th=(((char *)$next_th->thread_group.next) - $pid_off) 159 end 160 set $next_t=(char *)($next_t->tasks.next) - $tasks_off 161 end 162 163 printf "Trapno %ld, cr2 0x%lx, error_code %ld\n", $pid_task.thread.trap_no, \ 164 $pid_task.thread.cr2, $pid_task.thread.error_code 165 166end 167document trapinfo 168 Run info threads and lookup pid of thread #1 169 'trapinfo <pid>' will tell you by which trap & possibly 170 address the kernel panicked. 171end 172 173define dump_record 174 set var $desc = $arg0 175 set var $info = $arg1 176 if ($argc > 2) 177 set var $prev_flags = $arg2 178 else 179 set var $prev_flags = 0 180 end 181 182 set var $prefix = 1 183 set var $newline = 1 184 185 set var $begin = $desc->text_blk_lpos.begin % (1U << prb->text_data_ring.size_bits) 186 set var $next = $desc->text_blk_lpos.next % (1U << prb->text_data_ring.size_bits) 187 188 # handle data-less record 189 if ($begin & 1) 190 set var $text_len = 0 191 set var $log = "" 192 else 193 # handle wrapping data block 194 if ($begin > $next) 195 set var $begin = 0 196 end 197 198 # skip over descriptor id 199 set var $begin = $begin + sizeof(long) 200 201 # handle truncated message 202 if ($next - $begin < $info->text_len) 203 set var $text_len = $next - $begin 204 else 205 set var $text_len = $info->text_len 206 end 207 208 set var $log = &prb->text_data_ring.data[$begin] 209 end 210 211 # prev & LOG_CONT && !(info->flags & LOG_PREIX) 212 if (($prev_flags & 8) && !($info->flags & 4)) 213 set var $prefix = 0 214 end 215 216 # info->flags & LOG_CONT 217 if ($info->flags & 8) 218 # (prev & LOG_CONT && !(prev & LOG_NEWLINE)) 219 if (($prev_flags & 8) && !($prev_flags & 2)) 220 set var $prefix = 0 221 end 222 # (!(info->flags & LOG_NEWLINE)) 223 if (!($info->flags & 2)) 224 set var $newline = 0 225 end 226 end 227 228 if ($prefix) 229 printf "[%5lu.%06lu] ", $info->ts_nsec / 1000000000, $info->ts_nsec % 1000000000 230 end 231 if ($text_len) 232 eval "printf \"%%%d.%ds\", $log", $text_len, $text_len 233 end 234 if ($newline) 235 printf "\n" 236 end 237 238 # handle dictionary data 239 240 set var $dict = &$info->dev_info.subsystem[0] 241 set var $dict_len = sizeof($info->dev_info.subsystem) 242 if ($dict[0] != '\0') 243 printf " SUBSYSTEM=" 244 set var $idx = 0 245 while ($idx < $dict_len) 246 set var $c = $dict[$idx] 247 if ($c == '\0') 248 loop_break 249 else 250 if ($c < ' ' || $c >= 127 || $c == '\\') 251 printf "\\x%02x", $c 252 else 253 printf "%c", $c 254 end 255 end 256 set var $idx = $idx + 1 257 end 258 printf "\n" 259 end 260 261 set var $dict = &$info->dev_info.device[0] 262 set var $dict_len = sizeof($info->dev_info.device) 263 if ($dict[0] != '\0') 264 printf " DEVICE=" 265 set var $idx = 0 266 while ($idx < $dict_len) 267 set var $c = $dict[$idx] 268 if ($c == '\0') 269 loop_break 270 else 271 if ($c < ' ' || $c >= 127 || $c == '\\') 272 printf "\\x%02x", $c 273 else 274 printf "%c", $c 275 end 276 end 277 set var $idx = $idx + 1 278 end 279 printf "\n" 280 end 281end 282document dump_record 283 Dump a single record. The first parameter is the descriptor, 284 the second parameter is the info, the third parameter is 285 optional and specifies the previous record's flags, used for 286 properly formatting continued lines. 287end 288 289define dmesg 290 # definitions from kernel/printk/printk_ringbuffer.h 291 set var $desc_committed = 1 292 set var $desc_finalized = 2 293 set var $desc_sv_bits = sizeof(long) * 8 294 set var $desc_flags_shift = $desc_sv_bits - 2 295 set var $desc_flags_mask = 3 << $desc_flags_shift 296 set var $id_mask = ~$desc_flags_mask 297 298 set var $desc_count = 1U << prb->desc_ring.count_bits 299 set var $prev_flags = 0 300 301 set var $id = prb->desc_ring.tail_id.counter 302 set var $end_id = prb->desc_ring.head_id.counter 303 304 while (1) 305 set var $desc = &prb->desc_ring.descs[$id % $desc_count] 306 set var $info = &prb->desc_ring.infos[$id % $desc_count] 307 308 # skip non-committed record 309 set var $state = 3 & ($desc->state_var.counter >> $desc_flags_shift) 310 if ($state == $desc_committed || $state == $desc_finalized) 311 dump_record $desc $info $prev_flags 312 set var $prev_flags = $info->flags 313 end 314 315 if ($id == $end_id) 316 loop_break 317 end 318 set var $id = ($id + 1) & $id_mask 319 end 320end 321document dmesg 322 print the kernel ring buffer 323end 324