diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-05 18:59:48 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-08-05 18:59:48 -0400 |
| commit | 89a6c8cb9e6e11b6e3671dce7e037789b8f7cf62 (patch) | |
| tree | 12b16a8abe303fd86c156ddfbb86caa469e45a98 /kernel | |
| parent | 03c0c29aff7e56b722eb6c47eace222b140d0377 (diff) | |
| parent | 3fa43aba08c5b5a4b407e402606fbe463239b14a (diff) | |
Merge branch 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb
* 'for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jwessel/linux-2.6-kgdb:
debug_core,kdb: fix crash when arch does not have single step
kgdb,x86: use macro HBP_NUM to replace magic number 4
kgdb,mips: remove unused kgdb_cpu_doing_single_step operations
mm,kdb,kgdb: Add a debug reference for the kdb kmap usage
KGDB: Remove set but unused newPC
ftrace,kdb: Allow dumping a specific cpu's buffer with ftdump
ftrace,kdb: Extend kdb to be able to dump the ftrace buffer
kgdb,powerpc: Replace hardcoded offset by BREAK_INSTR_SIZE
arm,kgdb: Add ability to trap into debugger on notify_die
gdbstub: do not directly use dbg_reg_def[] in gdb_cmd_reg_set()
gdbstub: Implement gdbserial 'p' and 'P' packets
kgdb,arm: Individual register get/set for arm
kgdb,mips: Individual register get/set for mips
kgdb,x86: Individual register get/set for x86
kgdb,kdb: individual register set and and get API
gdbstub: Optimize kgdb's "thread:" response for the gdb serial protocol
kgdb: remove custom hex_to_bin()implementation
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/debug/debug_core.c | 2 | ||||
| -rw-r--r-- | kernel/debug/gdbstub.c | 189 | ||||
| -rw-r--r-- | kernel/debug/kdb/kdb_main.c | 132 | ||||
| -rw-r--r-- | kernel/trace/Makefile | 3 | ||||
| -rw-r--r-- | kernel/trace/trace.c | 43 | ||||
| -rw-r--r-- | kernel/trace/trace.h | 19 | ||||
| -rw-r--r-- | kernel/trace/trace_kdb.c | 136 |
7 files changed, 437 insertions, 87 deletions
diff --git a/kernel/debug/debug_core.c b/kernel/debug/debug_core.c index 51d14fe8764..3c2d4972d23 100644 --- a/kernel/debug/debug_core.c +++ b/kernel/debug/debug_core.c | |||
| @@ -605,6 +605,8 @@ cpu_master_loop: | |||
| 605 | if (dbg_kdb_mode) { | 605 | if (dbg_kdb_mode) { |
| 606 | kgdb_connected = 1; | 606 | kgdb_connected = 1; |
| 607 | error = kdb_stub(ks); | 607 | error = kdb_stub(ks); |
| 608 | if (error == -1) | ||
| 609 | continue; | ||
| 608 | kgdb_connected = 0; | 610 | kgdb_connected = 0; |
| 609 | } else { | 611 | } else { |
| 610 | error = gdb_serial_stub(ks); | 612 | error = gdb_serial_stub(ks); |
diff --git a/kernel/debug/gdbstub.c b/kernel/debug/gdbstub.c index 6e81fd59566..481a7bd2dfe 100644 --- a/kernel/debug/gdbstub.c +++ b/kernel/debug/gdbstub.c | |||
| @@ -52,17 +52,6 @@ static unsigned long gdb_regs[(NUMREGBYTES + | |||
| 52 | * GDB remote protocol parser: | 52 | * GDB remote protocol parser: |
| 53 | */ | 53 | */ |
| 54 | 54 | ||
| 55 | static int hex(char ch) | ||
| 56 | { | ||
| 57 | if ((ch >= 'a') && (ch <= 'f')) | ||
| 58 | return ch - 'a' + 10; | ||
| 59 | if ((ch >= '0') && (ch <= '9')) | ||
| 60 | return ch - '0'; | ||
| 61 | if ((ch >= 'A') && (ch <= 'F')) | ||
| 62 | return ch - 'A' + 10; | ||
| 63 | return -1; | ||
| 64 | } | ||
| 65 | |||
| 66 | #ifdef CONFIG_KGDB_KDB | 55 | #ifdef CONFIG_KGDB_KDB |
| 67 | static int gdbstub_read_wait(void) | 56 | static int gdbstub_read_wait(void) |
| 68 | { | 57 | { |
| @@ -123,8 +112,8 @@ static void get_packet(char *buffer) | |||
| 123 | buffer[count] = 0; | 112 | buffer[count] = 0; |
| 124 | 113 | ||
| 125 | if (ch == '#') { | 114 | if (ch == '#') { |
| 126 | xmitcsum = hex(gdbstub_read_wait()) << 4; | 115 | xmitcsum = hex_to_bin(gdbstub_read_wait()) << 4; |
| 127 | xmitcsum += hex(gdbstub_read_wait()); | 116 | xmitcsum += hex_to_bin(gdbstub_read_wait()); |
| 128 | 117 | ||
| 129 | if (checksum != xmitcsum) | 118 | if (checksum != xmitcsum) |
| 130 | /* failed checksum */ | 119 | /* failed checksum */ |
| @@ -236,7 +225,7 @@ void gdbstub_msg_write(const char *s, int len) | |||
| 236 | * buf. Return a pointer to the last char put in buf (null). May | 225 | * buf. Return a pointer to the last char put in buf (null). May |
| 237 | * return an error. | 226 | * return an error. |
| 238 | */ | 227 | */ |
| 239 | int kgdb_mem2hex(char *mem, char *buf, int count) | 228 | char *kgdb_mem2hex(char *mem, char *buf, int count) |
| 240 | { | 229 | { |
| 241 | char *tmp; | 230 | char *tmp; |
| 242 | int err; | 231 | int err; |
| @@ -248,17 +237,16 @@ int kgdb_mem2hex(char *mem, char *buf, int count) | |||
| 248 | tmp = buf + count; | 237 | tmp = buf + count; |
| 249 | 238 | ||
| 250 | err = probe_kernel_read(tmp, mem, count); | 239 | err = probe_kernel_read(tmp, mem, count); |
| 251 | if (!err) { | 240 | if (err) |
| 252 | while (count > 0) { | 241 | return NULL; |
| 253 | buf = pack_hex_byte(buf, *tmp); | 242 | while (count > 0) { |
| 254 | tmp++; | 243 | buf = pack_hex_byte(buf, *tmp); |
| 255 | count--; | 244 | tmp++; |
| 256 | } | 245 | count--; |
| 257 | |||
| 258 | *buf = 0; | ||
| 259 | } | 246 | } |
| 247 | *buf = 0; | ||
| 260 | 248 | ||
| 261 | return err; | 249 | return buf; |
| 262 | } | 250 | } |
| 263 | 251 | ||
| 264 | /* | 252 | /* |
| @@ -280,8 +268,8 @@ int kgdb_hex2mem(char *buf, char *mem, int count) | |||
| 280 | tmp_hex = tmp_raw - 1; | 268 | tmp_hex = tmp_raw - 1; |
| 281 | while (tmp_hex >= buf) { | 269 | while (tmp_hex >= buf) { |
| 282 | tmp_raw--; | 270 | tmp_raw--; |
| 283 | *tmp_raw = hex(*tmp_hex--); | 271 | *tmp_raw = hex_to_bin(*tmp_hex--); |
| 284 | *tmp_raw |= hex(*tmp_hex--) << 4; | 272 | *tmp_raw |= hex_to_bin(*tmp_hex--) << 4; |
| 285 | } | 273 | } |
| 286 | 274 | ||
| 287 | return probe_kernel_write(mem, tmp_raw, count); | 275 | return probe_kernel_write(mem, tmp_raw, count); |
| @@ -304,7 +292,7 @@ int kgdb_hex2long(char **ptr, unsigned long *long_val) | |||
| 304 | (*ptr)++; | 292 | (*ptr)++; |
| 305 | } | 293 | } |
| 306 | while (**ptr) { | 294 | while (**ptr) { |
| 307 | hex_val = hex(**ptr); | 295 | hex_val = hex_to_bin(**ptr); |
| 308 | if (hex_val < 0) | 296 | if (hex_val < 0) |
| 309 | break; | 297 | break; |
| 310 | 298 | ||
| @@ -339,6 +327,32 @@ static int kgdb_ebin2mem(char *buf, char *mem, int count) | |||
| 339 | return probe_kernel_write(mem, c, size); | 327 | return probe_kernel_write(mem, c, size); |
| 340 | } | 328 | } |
| 341 | 329 | ||
| 330 | #if DBG_MAX_REG_NUM > 0 | ||
| 331 | void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) | ||
| 332 | { | ||
| 333 | int i; | ||
| 334 | int idx = 0; | ||
| 335 | char *ptr = (char *)gdb_regs; | ||
| 336 | |||
| 337 | for (i = 0; i < DBG_MAX_REG_NUM; i++) { | ||
| 338 | dbg_get_reg(i, ptr + idx, regs); | ||
| 339 | idx += dbg_reg_def[i].size; | ||
| 340 | } | ||
| 341 | } | ||
| 342 | |||
| 343 | void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) | ||
| 344 | { | ||
| 345 | int i; | ||
| 346 | int idx = 0; | ||
| 347 | char *ptr = (char *)gdb_regs; | ||
| 348 | |||
| 349 | for (i = 0; i < DBG_MAX_REG_NUM; i++) { | ||
| 350 | dbg_set_reg(i, ptr + idx, regs); | ||
| 351 | idx += dbg_reg_def[i].size; | ||
| 352 | } | ||
| 353 | } | ||
| 354 | #endif /* DBG_MAX_REG_NUM > 0 */ | ||
| 355 | |||
| 342 | /* Write memory due to an 'M' or 'X' packet. */ | 356 | /* Write memory due to an 'M' or 'X' packet. */ |
| 343 | static int write_mem_msg(int binary) | 357 | static int write_mem_msg(int binary) |
| 344 | { | 358 | { |
| @@ -378,28 +392,31 @@ static void error_packet(char *pkt, int error) | |||
| 378 | * remapped to negative TIDs. | 392 | * remapped to negative TIDs. |
| 379 | */ | 393 | */ |
| 380 | 394 | ||
| 381 | #define BUF_THREAD_ID_SIZE 16 | 395 | #define BUF_THREAD_ID_SIZE 8 |
| 382 | 396 | ||
| 383 | static char *pack_threadid(char *pkt, unsigned char *id) | 397 | static char *pack_threadid(char *pkt, unsigned char *id) |
| 384 | { | 398 | { |
| 385 | char *limit; | 399 | unsigned char *limit; |
| 400 | int lzero = 1; | ||
| 401 | |||
| 402 | limit = id + (BUF_THREAD_ID_SIZE / 2); | ||
| 403 | while (id < limit) { | ||
| 404 | if (!lzero || *id != 0) { | ||
| 405 | pkt = pack_hex_byte(pkt, *id); | ||
| 406 | lzero = 0; | ||
| 407 | } | ||
| 408 | id++; | ||
| 409 | } | ||
| 386 | 410 | ||
| 387 | limit = pkt + BUF_THREAD_ID_SIZE; | 411 | if (lzero) |
| 388 | while (pkt < limit) | 412 | pkt = pack_hex_byte(pkt, 0); |
| 389 | pkt = pack_hex_byte(pkt, *id++); | ||
| 390 | 413 | ||
| 391 | return pkt; | 414 | return pkt; |
| 392 | } | 415 | } |
| 393 | 416 | ||
| 394 | static void int_to_threadref(unsigned char *id, int value) | 417 | static void int_to_threadref(unsigned char *id, int value) |
| 395 | { | 418 | { |
| 396 | unsigned char *scan; | 419 | put_unaligned_be32(value, id); |
| 397 | int i = 4; | ||
| 398 | |||
| 399 | scan = (unsigned char *)id; | ||
| 400 | while (i--) | ||
| 401 | *scan++ = 0; | ||
| 402 | put_unaligned_be32(value, scan); | ||
| 403 | } | 420 | } |
| 404 | 421 | ||
| 405 | static struct task_struct *getthread(struct pt_regs *regs, int tid) | 422 | static struct task_struct *getthread(struct pt_regs *regs, int tid) |
| @@ -463,8 +480,7 @@ static void gdb_cmd_status(struct kgdb_state *ks) | |||
| 463 | pack_hex_byte(&remcom_out_buffer[1], ks->signo); | 480 | pack_hex_byte(&remcom_out_buffer[1], ks->signo); |
| 464 | } | 481 | } |
| 465 | 482 | ||
| 466 | /* Handle the 'g' get registers request */ | 483 | static void gdb_get_regs_helper(struct kgdb_state *ks) |
| 467 | static void gdb_cmd_getregs(struct kgdb_state *ks) | ||
| 468 | { | 484 | { |
| 469 | struct task_struct *thread; | 485 | struct task_struct *thread; |
| 470 | void *local_debuggerinfo; | 486 | void *local_debuggerinfo; |
| @@ -505,6 +521,12 @@ static void gdb_cmd_getregs(struct kgdb_state *ks) | |||
| 505 | */ | 521 | */ |
| 506 | sleeping_thread_to_gdb_regs(gdb_regs, thread); | 522 | sleeping_thread_to_gdb_regs(gdb_regs, thread); |
| 507 | } | 523 | } |
| 524 | } | ||
| 525 | |||
| 526 | /* Handle the 'g' get registers request */ | ||
| 527 | static void gdb_cmd_getregs(struct kgdb_state *ks) | ||
| 528 | { | ||
| 529 | gdb_get_regs_helper(ks); | ||
| 508 | kgdb_mem2hex((char *)gdb_regs, remcom_out_buffer, NUMREGBYTES); | 530 | kgdb_mem2hex((char *)gdb_regs, remcom_out_buffer, NUMREGBYTES); |
| 509 | } | 531 | } |
| 510 | 532 | ||
| @@ -527,13 +549,13 @@ static void gdb_cmd_memread(struct kgdb_state *ks) | |||
| 527 | char *ptr = &remcom_in_buffer[1]; | 549 | char *ptr = &remcom_in_buffer[1]; |
| 528 | unsigned long length; | 550 | unsigned long length; |
| 529 | unsigned long addr; | 551 | unsigned long addr; |
| 530 | int err; | 552 | char *err; |
| 531 | 553 | ||
| 532 | if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' && | 554 | if (kgdb_hex2long(&ptr, &addr) > 0 && *ptr++ == ',' && |
| 533 | kgdb_hex2long(&ptr, &length) > 0) { | 555 | kgdb_hex2long(&ptr, &length) > 0) { |
| 534 | err = kgdb_mem2hex((char *)addr, remcom_out_buffer, length); | 556 | err = kgdb_mem2hex((char *)addr, remcom_out_buffer, length); |
| 535 | if (err) | 557 | if (!err) |
| 536 | error_packet(remcom_out_buffer, err); | 558 | error_packet(remcom_out_buffer, -EINVAL); |
| 537 | } else { | 559 | } else { |
| 538 | error_packet(remcom_out_buffer, -EINVAL); | 560 | error_packet(remcom_out_buffer, -EINVAL); |
| 539 | } | 561 | } |
| @@ -550,6 +572,60 @@ static void gdb_cmd_memwrite(struct kgdb_state *ks) | |||
| 550 | strcpy(remcom_out_buffer, "OK"); | 572 | strcpy(remcom_out_buffer, "OK"); |
| 551 | } | 573 | } |
| 552 | 574 | ||
| 575 | #if DBG_MAX_REG_NUM > 0 | ||
| 576 | static char *gdb_hex_reg_helper(int regnum, char *out) | ||
| 577 | { | ||
| 578 | int i; | ||
| 579 | int offset = 0; | ||
| 580 | |||
| 581 | for (i = 0; i < regnum; i++) | ||
| 582 | offset += dbg_reg_def[i].size; | ||
| 583 | return kgdb_mem2hex((char *)gdb_regs + offset, out, | ||
| 584 | dbg_reg_def[i].size); | ||
| 585 | } | ||
| 586 | |||
| 587 | /* Handle the 'p' individual regster get */ | ||
| 588 | static void gdb_cmd_reg_get(struct kgdb_state *ks) | ||
| 589 | { | ||
| 590 | unsigned long regnum; | ||
| 591 | char *ptr = &remcom_in_buffer[1]; | ||
| 592 | |||
| 593 | kgdb_hex2long(&ptr, ®num); | ||
| 594 | if (regnum >= DBG_MAX_REG_NUM) { | ||
| 595 | error_packet(remcom_out_buffer, -EINVAL); | ||
| 596 | return; | ||
| 597 | } | ||
| 598 | gdb_get_regs_helper(ks); | ||
| 599 | gdb_hex_reg_helper(regnum, remcom_out_buffer); | ||
| 600 | } | ||
| 601 | |||
| 602 | /* Handle the 'P' individual regster set */ | ||
| 603 | static void gdb_cmd_reg_set(struct kgdb_state *ks) | ||
| 604 | { | ||
| 605 | unsigned long regnum; | ||
| 606 | char *ptr = &remcom_in_buffer[1]; | ||
| 607 | int i = 0; | ||
| 608 | |||
| 609 | kgdb_hex2long(&ptr, ®num); | ||
| 610 | if (*ptr++ != '=' || | ||
| 611 | !(!kgdb_usethread || kgdb_usethread == current) || | ||
| 612 | !dbg_get_reg(regnum, gdb_regs, ks->linux_regs)) { | ||
| 613 | error_packet(remcom_out_buffer, -EINVAL); | ||
| 614 | return; | ||
| 615 | } | ||
| 616 | memset(gdb_regs, 0, sizeof(gdb_regs)); | ||
| 617 | while (i < sizeof(gdb_regs) * 2) | ||
| 618 | if (hex_to_bin(ptr[i]) >= 0) | ||
| 619 | i++; | ||
| 620 | else | ||
| 621 | break; | ||
| 622 | i = i / 2; | ||
| 623 | kgdb_hex2mem(ptr, (char *)gdb_regs, i); | ||
| 624 | dbg_set_reg(regnum, gdb_regs, ks->linux_regs); | ||
| 625 | strcpy(remcom_out_buffer, "OK"); | ||
| 626 | } | ||
| 627 | #endif /* DBG_MAX_REG_NUM > 0 */ | ||
| 628 | |||
| 553 | /* Handle the 'X' memory binary write bytes */ | 629 | /* Handle the 'X' memory binary write bytes */ |
| 554 | static void gdb_cmd_binwrite(struct kgdb_state *ks) | 630 | static void gdb_cmd_binwrite(struct kgdb_state *ks) |
| 555 | { | 631 | { |
| @@ -612,7 +688,7 @@ static void gdb_cmd_query(struct kgdb_state *ks) | |||
| 612 | { | 688 | { |
| 613 | struct task_struct *g; | 689 | struct task_struct *g; |
| 614 | struct task_struct *p; | 690 | struct task_struct *p; |
| 615 | unsigned char thref[8]; | 691 | unsigned char thref[BUF_THREAD_ID_SIZE]; |
| 616 | char *ptr; | 692 | char *ptr; |
| 617 | int i; | 693 | int i; |
| 618 | int cpu; | 694 | int cpu; |
| @@ -632,8 +708,7 @@ static void gdb_cmd_query(struct kgdb_state *ks) | |||
| 632 | for_each_online_cpu(cpu) { | 708 | for_each_online_cpu(cpu) { |
| 633 | ks->thr_query = 0; | 709 | ks->thr_query = 0; |
| 634 | int_to_threadref(thref, -cpu - 2); | 710 | int_to_threadref(thref, -cpu - 2); |
| 635 | pack_threadid(ptr, thref); | 711 | ptr = pack_threadid(ptr, thref); |
| 636 | ptr += BUF_THREAD_ID_SIZE; | ||
| 637 | *(ptr++) = ','; | 712 | *(ptr++) = ','; |
| 638 | i++; | 713 | i++; |
| 639 | } | 714 | } |
| @@ -642,8 +717,7 @@ static void gdb_cmd_query(struct kgdb_state *ks) | |||
| 642 | do_each_thread(g, p) { | 717 | do_each_thread(g, p) { |
| 643 | if (i >= ks->thr_query && !finished) { | 718 | if (i >= ks->thr_query && !finished) { |
| 644 | int_to_threadref(thref, p->pid); | 719 | int_to_threadref(thref, p->pid); |
| 645 | pack_threadid(ptr, thref); | 720 | ptr = pack_threadid(ptr, thref); |
| 646 | ptr += BUF_THREAD_ID_SIZE; | ||
| 647 | *(ptr++) = ','; | 721 | *(ptr++) = ','; |
| 648 | ks->thr_query++; | 722 | ks->thr_query++; |
| 649 | if (ks->thr_query % KGDB_MAX_THREAD_QUERY == 0) | 723 | if (ks->thr_query % KGDB_MAX_THREAD_QUERY == 0) |
| @@ -858,11 +932,14 @@ int gdb_serial_stub(struct kgdb_state *ks) | |||
| 858 | int error = 0; | 932 | int error = 0; |
| 859 | int tmp; | 933 | int tmp; |
| 860 | 934 | ||
| 861 | /* Clear the out buffer. */ | 935 | /* Initialize comm buffer and globals. */ |
| 862 | memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer)); | 936 | memset(remcom_out_buffer, 0, sizeof(remcom_out_buffer)); |
| 937 | kgdb_usethread = kgdb_info[ks->cpu].task; | ||
| 938 | ks->kgdb_usethreadid = shadow_pid(kgdb_info[ks->cpu].task->pid); | ||
| 939 | ks->pass_exception = 0; | ||
| 863 | 940 | ||
| 864 | if (kgdb_connected) { | 941 | if (kgdb_connected) { |
| 865 | unsigned char thref[8]; | 942 | unsigned char thref[BUF_THREAD_ID_SIZE]; |
| 866 | char *ptr; | 943 | char *ptr; |
| 867 | 944 | ||
| 868 | /* Reply to host that an exception has occurred */ | 945 | /* Reply to host that an exception has occurred */ |
| @@ -876,10 +953,6 @@ int gdb_serial_stub(struct kgdb_state *ks) | |||
| 876 | put_packet(remcom_out_buffer); | 953 | put_packet(remcom_out_buffer); |
| 877 | } | 954 | } |
| 878 | 955 | ||
| 879 | kgdb_usethread = kgdb_info[ks->cpu].task; | ||
| 880 | ks->kgdb_usethreadid = shadow_pid(kgdb_info[ks->cpu].task->pid); | ||
| 881 | ks->pass_exception = 0; | ||
| 882 | |||
| 883 | while (1) { | 956 | while (1) { |
| 884 | error = 0; | 957 | error = 0; |
| 885 | 958 | ||
| @@ -904,6 +977,14 @@ int gdb_serial_stub(struct kgdb_state *ks) | |||
| 904 | case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA */ | 977 | case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA..AA */ |
| 905 | gdb_cmd_memwrite(ks); | 978 | gdb_cmd_memwrite(ks); |
| 906 | break; | 979 | break; |
| 980 | #if DBG_MAX_REG_NUM > 0 | ||
| 981 | case 'p': /* pXX Return gdb register XX (in hex) */ | ||
| 982 | gdb_cmd_reg_get(ks); | ||
| 983 | break; | ||
| 984 | case 'P': /* PXX=aaaa Set gdb register XX to aaaa (in hex) */ | ||
| 985 | gdb_cmd_reg_set(ks); | ||
| 986 | break; | ||
| 987 | #endif /* DBG_MAX_REG_NUM > 0 */ | ||
| 907 | case 'X': /* XAA..AA,LLLL: Write LLLL bytes at address AA..AA */ | 988 | case 'X': /* XAA..AA,LLLL: Write LLLL bytes at address AA..AA */ |
| 908 | gdb_cmd_binwrite(ks); | 989 | gdb_cmd_binwrite(ks); |
| 909 | break; | 990 | break; |
diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index ebe4a287419..8577e45a9a5 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c | |||
| @@ -312,7 +312,7 @@ int kdbgetularg(const char *arg, unsigned long *value) | |||
| 312 | 312 | ||
| 313 | if (endp == arg) { | 313 | if (endp == arg) { |
| 314 | /* | 314 | /* |
| 315 | * Try base 16, for us folks too lazy to type the | 315 | * Also try base 16, for us folks too lazy to type the |
| 316 | * leading 0x... | 316 | * leading 0x... |
| 317 | */ | 317 | */ |
| 318 | val = simple_strtoul(arg, &endp, 16); | 318 | val = simple_strtoul(arg, &endp, 16); |
| @@ -325,6 +325,25 @@ int kdbgetularg(const char *arg, unsigned long *value) | |||
| 325 | return 0; | 325 | return 0; |
| 326 | } | 326 | } |
| 327 | 327 | ||
| 328 | int kdbgetu64arg(const char *arg, u64 *value) | ||
| 329 | { | ||
| 330 | char *endp; | ||
| 331 | u64 val; | ||
| 332 | |||
| 333 | val = simple_strtoull(arg, &endp, 0); | ||
| 334 | |||
| 335 | if (endp == arg) { | ||
| 336 | |||
| 337 | val = simple_strtoull(arg, &endp, 16); | ||
| 338 | if (endp == arg) | ||
| 339 | return KDB_BADINT; | ||
| 340 | } | ||
| 341 | |||
| 342 | *value = val; | ||
| 343 | |||
| 344 | return 0; | ||
| 345 | } | ||
| 346 | |||
| 328 | /* | 347 | /* |
| 329 | * kdb_set - This function implements the 'set' command. Alter an | 348 | * kdb_set - This function implements the 'set' command. Alter an |
| 330 | * existing environment variable or create a new one. | 349 | * existing environment variable or create a new one. |
| @@ -1770,11 +1789,65 @@ static int kdb_go(int argc, const char **argv) | |||
| 1770 | */ | 1789 | */ |
| 1771 | static int kdb_rd(int argc, const char **argv) | 1790 | static int kdb_rd(int argc, const char **argv) |
| 1772 | { | 1791 | { |
| 1773 | int diag = kdb_check_regs(); | 1792 | int len = kdb_check_regs(); |
| 1774 | if (diag) | 1793 | #if DBG_MAX_REG_NUM > 0 |
| 1775 | return diag; | 1794 | int i; |
| 1795 | char *rname; | ||
| 1796 | int rsize; | ||
| 1797 | u64 reg64; | ||
| 1798 | u32 reg32; | ||
| 1799 | u16 reg16; | ||
| 1800 | u8 reg8; | ||
| 1801 | |||
| 1802 | if (len) | ||
| 1803 | return len; | ||
| 1804 | |||
| 1805 | for (i = 0; i < DBG_MAX_REG_NUM; i++) { | ||
| 1806 | rsize = dbg_reg_def[i].size * 2; | ||
| 1807 | if (rsize > 16) | ||
| 1808 | rsize = 2; | ||
| 1809 | if (len + strlen(dbg_reg_def[i].name) + 4 + rsize > 80) { | ||
| 1810 | len = 0; | ||
| 1811 | kdb_printf("\n"); | ||
| 1812 | } | ||
| 1813 | if (len) | ||
| 1814 | len += kdb_printf(" "); | ||
| 1815 | switch(dbg_reg_def[i].size * 8) { | ||
| 1816 | case 8: | ||
| 1817 | rname = dbg_get_reg(i, ®8, kdb_current_regs); | ||
| 1818 | if (!rname) | ||
| 1819 | break; | ||
| 1820 | len += kdb_printf("%s: %02x", rname, reg8); | ||
| 1821 | break; | ||
| 1822 | case 16: | ||
| 1823 | rname = dbg_get_reg(i, ®16, kdb_current_regs); | ||
| 1824 | if (!rname) | ||
| 1825 | break; | ||
| 1826 | len += kdb_printf("%s: %04x", rname, reg16); | ||
| 1827 | break; | ||
| 1828 | case 32: | ||
| 1829 | rname = dbg_get_reg(i, ®32, kdb_current_regs); | ||
| 1830 | if (!rname) | ||
| 1831 | break; | ||
| 1832 | len += kdb_printf("%s: %08x", rname, reg32); | ||
| 1833 | break; | ||
| 1834 | case 64: | ||
| 1835 | rname = dbg_get_reg(i, ®64, kdb_current_regs); | ||
| 1836 | if (!rname) | ||
| 1837 | break; | ||
| 1838 | len += kdb_printf("%s: %016llx", rname, reg64); | ||
| 1839 | break; | ||
| 1840 | default: | ||
| 1841 | len += kdb_printf("%s: ??", dbg_reg_def[i].name); | ||
| 1842 | } | ||
| 1843 | } | ||
| 1844 | kdb_printf("\n"); | ||
| 1845 | #else | ||
| 1846 | if (len) | ||
| 1847 | return len; | ||
| 1776 | 1848 | ||
| 1777 | kdb_dumpregs(kdb_current_regs); | 1849 | kdb_dumpregs(kdb_current_regs); |
| 1850 | #endif | ||
| 1778 | return 0; | 1851 | return 0; |
| 1779 | } | 1852 | } |
| 1780 | 1853 | ||
| @@ -1782,32 +1855,67 @@ static int kdb_rd(int argc, const char **argv) | |||
| 1782 | * kdb_rm - This function implements the 'rm' (register modify) command. | 1855 | * kdb_rm - This function implements the 'rm' (register modify) command. |
| 1783 | * rm register-name new-contents | 1856 | * rm register-name new-contents |
| 1784 | * Remarks: | 1857 | * Remarks: |
| 1785 | * Currently doesn't allow modification of control or | 1858 | * Allows register modification with the same restrictions as gdb |
| 1786 | * debug registers. | ||
| 1787 | */ | 1859 | */ |
| 1788 | static int kdb_rm(int argc, const char **argv) | 1860 | static int kdb_rm(int argc, const char **argv) |
| 1789 | { | 1861 | { |
| 1862 | #if DBG_MAX_REG_NUM > 0 | ||
| 1790 | int diag; | 1863 | int diag; |
| 1791 | int ind = 0; | 1864 | const char *rname; |
| 1792 | unsigned long contents; | 1865 | int i; |
| 1866 | u64 reg64; | ||
| 1867 | u32 reg32; | ||
| 1868 | u16 reg16; | ||
| 1869 | u8 reg8; | ||
| 1793 | 1870 | ||
| 1794 | if (argc != 2) | 1871 | if (argc != 2) |
| 1795 | return KDB_ARGCOUNT; | 1872 | return KDB_ARGCOUNT; |
| 1796 | /* | 1873 | /* |
| 1797 | * Allow presence or absence of leading '%' symbol. | 1874 | * Allow presence or absence of leading '%' symbol. |
| 1798 | */ | 1875 | */ |
| 1799 | if (argv[1][0] == '%') | 1876 | rname = argv[1]; |
| 1800 | ind = 1; | 1877 | if (*rname == '%') |
| 1878 | rname++; | ||
| 1801 | 1879 | ||
| 1802 | diag = kdbgetularg(argv[2], &contents); | 1880 | diag = kdbgetu64arg(argv[2], ®64); |
| 1803 | if (diag) | 1881 | if (diag) |
| 1804 | return diag; | 1882 | return diag; |
| 1805 | 1883 | ||
| 1806 | diag = kdb_check_regs(); | 1884 | diag = kdb_check_regs(); |
| 1807 | if (diag) | 1885 | if (diag) |
| 1808 | return diag; | 1886 | return diag; |
| 1887 | |||
| 1888 | diag = KDB_BADREG; | ||
| 1889 | for (i = 0; i < DBG_MAX_REG_NUM; i++) { | ||
| 1890 | if (strcmp(rname, dbg_reg_def[i].name) == 0) { | ||
| 1891 | diag = 0; | ||
| 1892 | break; | ||
| 1893 | } | ||
| 1894 | } | ||
| 1895 | if (!diag) { | ||
| 1896 | switch(dbg_reg_def[i].size * 8) { | ||
| 1897 | case 8: | ||
| 1898 | reg8 = reg64; | ||
| 1899 | dbg_set_reg(i, ®8, kdb_current_regs); | ||
| 1900 | break; | ||
| 1901 | case 16: | ||
| 1902 | reg16 = reg64; | ||
| 1903 | dbg_set_reg(i, ®16, kdb_current_regs); | ||
| 1904 | break; | ||
| 1905 | case 32: | ||
| 1906 | reg32 = reg64; | ||
| 1907 | dbg_set_reg(i, ®32, kdb_current_regs); | ||
| 1908 | break; | ||
| 1909 | case 64: | ||
| 1910 | dbg_set_reg(i, ®64, kdb_current_regs); | ||
| 1911 | break; | ||
| 1912 | } | ||
| 1913 | } | ||
| 1914 | return diag; | ||
| 1915 | #else | ||
| 1809 | kdb_printf("ERROR: Register set currently not implemented\n"); | 1916 | kdb_printf("ERROR: Register set currently not implemented\n"); |
| 1810 | return 0; | 1917 | return 0; |
| 1918 | #endif | ||
| 1811 | } | 1919 | } |
| 1812 | 1920 | ||
| 1813 | #if defined(CONFIG_MAGIC_SYSRQ) | 1921 | #if defined(CONFIG_MAGIC_SYSRQ) |
diff --git a/kernel/trace/Makefile b/kernel/trace/Makefile index ffb1a5b0550..4215530b490 100644 --- a/kernel/trace/Makefile +++ b/kernel/trace/Makefile | |||
| @@ -57,5 +57,8 @@ obj-$(CONFIG_EVENT_TRACING) += trace_events_filter.o | |||
| 57 | obj-$(CONFIG_KPROBE_EVENT) += trace_kprobe.o | 57 | obj-$(CONFIG_KPROBE_EVENT) += trace_kprobe.o |
| 58 | obj-$(CONFIG_KSYM_TRACER) += trace_ksym.o | 58 | obj-$(CONFIG_KSYM_TRACER) += trace_ksym.o |
| 59 | obj-$(CONFIG_EVENT_TRACING) += power-traces.o | 59 | obj-$(CONFIG_EVENT_TRACING) += power-traces.o |
| 60 | ifeq ($(CONFIG_TRACING),y) | ||
| 61 | obj-$(CONFIG_KGDB_KDB) += trace_kdb.o | ||
| 62 | endif | ||
| 60 | 63 | ||
| 61 | libftrace-y := ftrace.o | 64 | libftrace-y := ftrace.o |
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 086d3631680..d6736b93dc2 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c | |||
| @@ -101,10 +101,7 @@ static inline void ftrace_enable_cpu(void) | |||
| 101 | preempt_enable(); | 101 | preempt_enable(); |
| 102 | } | 102 | } |
| 103 | 103 | ||
| 104 | static cpumask_var_t __read_mostly tracing_buffer_mask; | 104 | cpumask_var_t __read_mostly tracing_buffer_mask; |
| 105 | |||
| 106 | #define for_each_tracing_cpu(cpu) \ | ||
| 107 | for_each_cpu(cpu, tracing_buffer_mask) | ||
| 108 | 105 | ||
| 109 | /* | 106 | /* |
| 110 | * ftrace_dump_on_oops - variable to dump ftrace buffer on oops | 107 | * ftrace_dump_on_oops - variable to dump ftrace buffer on oops |
| @@ -1539,11 +1536,6 @@ int trace_vprintk(unsigned long ip, const char *fmt, va_list args) | |||
| 1539 | } | 1536 | } |
| 1540 | EXPORT_SYMBOL_GPL(trace_vprintk); | 1537 | EXPORT_SYMBOL_GPL(trace_vprintk); |
| 1541 | 1538 | ||
| 1542 | enum trace_file_type { | ||
| 1543 | TRACE_FILE_LAT_FMT = 1, | ||
| 1544 | TRACE_FILE_ANNOTATE = 2, | ||
| 1545 | }; | ||
| 1546 | |||
| 1547 | static void trace_iterator_increment(struct trace_iterator *iter) | 1539 | static void trace_iterator_increment(struct trace_iterator *iter) |
| 1548 | { | 1540 | { |
| 1549 | /* Don't allow ftrace to trace into the ring buffers */ | 1541 | /* Don't allow ftrace to trace into the ring buffers */ |
| @@ -1641,7 +1633,7 @@ struct trace_entry *trace_find_next_entry(struct trace_iterator *iter, | |||
| 1641 | } | 1633 | } |
| 1642 | 1634 | ||
| 1643 | /* Find the next real entry, and increment the iterator to the next entry */ | 1635 | /* Find the next real entry, and increment the iterator to the next entry */ |
| 1644 | static void *find_next_entry_inc(struct trace_iterator *iter) | 1636 | void *trace_find_next_entry_inc(struct trace_iterator *iter) |
| 1645 | { | 1637 | { |
| 1646 | iter->ent = __find_next_entry(iter, &iter->cpu, | 1638 | iter->ent = __find_next_entry(iter, &iter->cpu, |
| 1647 | &iter->lost_events, &iter->ts); | 1639 | &iter->lost_events, &iter->ts); |
| @@ -1676,19 +1668,19 @@ static void *s_next(struct seq_file *m, void *v, loff_t *pos) | |||
| 1676 | return NULL; | 1668 | return NULL; |
| 1677 | 1669 | ||
| 1678 | if (iter->idx < 0) | 1670 | if (iter->idx < 0) |
| 1679 | ent = find_next_entry_inc(iter); | 1671 | ent = trace_find_next_entry_inc(iter); |
| 1680 | else | 1672 | else |
| 1681 | ent = iter; | 1673 | ent = iter; |
| 1682 | 1674 | ||
| 1683 | while (ent && iter->idx < i) | 1675 | while (ent && iter->idx < i) |
| 1684 | ent = find_next_entry_inc(iter); | 1676 | ent = trace_find_next_entry_inc(iter); |
| 1685 | 1677 | ||
| 1686 | iter->pos = *pos; | 1678 | iter->pos = *pos; |
| 1687 | 1679 | ||
| 1688 | return ent; | 1680 | return ent; |
| 1689 | } | 1681 | } |
| 1690 | 1682 | ||
| 1691 | static void tracing_iter_reset(struct trace_iterator *iter, int cpu) | 1683 | void tracing_iter_reset(struct trace_iterator *iter, int cpu) |
| 1692 | { | 1684 | { |
| 1693 | struct trace_array *tr = iter->tr; | 1685 | struct trace_array *tr = iter->tr; |
| 1694 | struct ring_buffer_event *event; | 1686 | struct ring_buffer_event *event; |
| @@ -2049,7 +2041,7 @@ int trace_empty(struct trace_iterator *iter) | |||
| 2049 | } | 2041 | } |
| 2050 | 2042 | ||
| 2051 | /* Called with trace_event_read_lock() held. */ | 2043 | /* Called with trace_event_read_lock() held. */ |
| 2052 | static enum print_line_t print_trace_line(struct trace_iterator *iter) | 2044 | enum print_line_t print_trace_line(struct trace_iterator *iter) |
| 2053 | { | 2045 | { |
| 2054 | enum print_line_t ret; | 2046 | enum print_line_t ret; |
| 2055 | 2047 | ||
| @@ -3211,7 +3203,7 @@ waitagain: | |||
| 3211 | 3203 | ||
| 3212 | trace_event_read_lock(); | 3204 | trace_event_read_lock(); |
| 3213 | trace_access_lock(iter->cpu_file); | 3205 | trace_access_lock(iter->cpu_file); |
| 3214 | while (find_next_entry_inc(iter) != NULL) { | 3206 | while (trace_find_next_entry_inc(iter) != NULL) { |
| 3215 | enum print_line_t ret; | 3207 | enum print_line_t ret; |
| 3216 | int len = iter->seq.len; | 3208 | int len = iter->seq.len; |
| 3217 | 3209 | ||
| @@ -3294,7 +3286,7 @@ tracing_fill_pipe_page(size_t rem, struct trace_iterator *iter) | |||
| 3294 | if (ret != TRACE_TYPE_NO_CONSUME) | 3286 | if (ret != TRACE_TYPE_NO_CONSUME) |
| 3295 | trace_consume(iter); | 3287 | trace_consume(iter); |
| 3296 | rem -= count; | 3288 | rem -= count; |
| 3297 | if (!find_next_entry_inc(iter)) { | 3289 | if (!trace_find_next_entry_inc(iter)) { |
| 3298 | rem = 0; | 3290 | rem = 0; |
| 3299 | iter->ent = NULL; | 3291 | iter->ent = NULL; |
| 3300 | break; | 3292 | break; |
| @@ -3350,7 +3342,7 @@ static ssize_t tracing_splice_read_pipe(struct file *filp, | |||
| 3350 | if (ret <= 0) | 3342 | if (ret <= 0) |
| 3351 | goto out_err; | 3343 | goto out_err; |
| 3352 | 3344 | ||
| 3353 | if (!iter->ent && !find_next_entry_inc(iter)) { | 3345 | if (!iter->ent && !trace_find_next_entry_inc(iter)) { |
| 3354 | ret = -EFAULT; | 3346 | ret = -EFAULT; |
| 3355 | goto out_err; | 3347 | goto out_err; |
| 3356 | } | 3348 | } |
| @@ -4414,7 +4406,7 @@ static struct notifier_block trace_die_notifier = { | |||
| 4414 | */ | 4406 | */ |
| 4415 | #define KERN_TRACE KERN_EMERG | 4407 | #define KERN_TRACE KERN_EMERG |
| 4416 | 4408 | ||
| 4417 | static void | 4409 | void |
| 4418 | trace_printk_seq(struct trace_seq *s) | 4410 | trace_printk_seq(struct trace_seq *s) |
| 4419 | { | 4411 | { |
| 4420 | /* Probably should print a warning here. */ | 4412 | /* Probably should print a warning here. */ |
| @@ -4429,6 +4421,13 @@ trace_printk_seq(struct trace_seq *s) | |||
| 4429 | trace_seq_init(s); | 4421 | trace_seq_init(s); |
| 4430 | } | 4422 | } |
| 4431 | 4423 | ||
| 4424 | void trace_init_global_iter(struct trace_iterator *iter) | ||
| 4425 | { | ||
| 4426 | iter->tr = &global_trace; | ||
| 4427 | iter->trace = current_trace; | ||
| 4428 | iter->cpu_file = TRACE_PIPE_ALL_CPU; | ||
| 4429 | } | ||
| 4430 | |||
| 4432 | static void | 4431 | static void |
| 4433 | __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode) | 4432 | __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode) |
| 4434 | { | 4433 | { |
| @@ -4454,8 +4453,10 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode) | |||
| 4454 | if (disable_tracing) | 4453 | if (disable_tracing) |
| 4455 | ftrace_kill(); | 4454 | ftrace_kill(); |
| 4456 | 4455 | ||
| 4456 | trace_init_global_iter(&iter); | ||
| 4457 | |||
| 4457 | for_each_tracing_cpu(cpu) { | 4458 | for_each_tracing_cpu(cpu) { |
| 4458 | atomic_inc(&global_trace.data[cpu]->disabled); | 4459 | atomic_inc(&iter.tr->data[cpu]->disabled); |
| 4459 | } | 4460 | } |
| 4460 | 4461 | ||
| 4461 | old_userobj = trace_flags & TRACE_ITER_SYM_USEROBJ; | 4462 | old_userobj = trace_flags & TRACE_ITER_SYM_USEROBJ; |
| @@ -4504,7 +4505,7 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode) | |||
| 4504 | iter.iter_flags |= TRACE_FILE_LAT_FMT; | 4505 | iter.iter_flags |= TRACE_FILE_LAT_FMT; |
| 4505 | iter.pos = -1; | 4506 | iter.pos = -1; |
| 4506 | 4507 | ||
| 4507 | if (find_next_entry_inc(&iter) != NULL) { | 4508 | if (trace_find_next_entry_inc(&iter) != NULL) { |
| 4508 | int ret; | 4509 | int ret; |
| 4509 | 4510 | ||
| 4510 | ret = print_trace_line(&iter); | 4511 | ret = print_trace_line(&iter); |
| @@ -4526,7 +4527,7 @@ __ftrace_dump(bool disable_tracing, enum ftrace_dump_mode oops_dump_mode) | |||
| 4526 | trace_flags |= old_userobj; | 4527 | trace_flags |= old_userobj; |
| 4527 | 4528 | ||
| 4528 | for_each_tracing_cpu(cpu) { | 4529 | for_each_tracing_cpu(cpu) { |
| 4529 | atomic_dec(&global_trace.data[cpu]->disabled); | 4530 | atomic_dec(&iter.tr->data[cpu]->disabled); |
| 4530 | } | 4531 | } |
| 4531 | tracing_on(); | 4532 | tracing_on(); |
| 4532 | } | 4533 | } |
diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 2cd96399463..0605fc00c17 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h | |||
| @@ -338,6 +338,14 @@ struct trace_entry *tracing_get_trace_entry(struct trace_array *tr, | |||
| 338 | struct trace_entry *trace_find_next_entry(struct trace_iterator *iter, | 338 | struct trace_entry *trace_find_next_entry(struct trace_iterator *iter, |
| 339 | int *ent_cpu, u64 *ent_ts); | 339 | int *ent_cpu, u64 *ent_ts); |
| 340 | 340 | ||
| 341 | int trace_empty(struct trace_iterator *iter); | ||
| 342 | |||
| 343 | void *trace_find_next_entry_inc(struct trace_iterator *iter); | ||
| 344 | |||
| 345 | void trace_init_global_iter(struct trace_iterator *iter); | ||
| 346 | |||
| 347 | void tracing_iter_reset(struct trace_iterator *iter, int cpu); | ||
| 348 | |||
| 341 | void default_wait_pipe(struct trace_iterator *iter); | 349 | void default_wait_pipe(struct trace_iterator *iter); |
| 342 | void poll_wait_pipe(struct trace_iterator *iter); | 350 | void poll_wait_pipe(struct trace_iterator *iter); |
| 343 | 351 | ||
| @@ -380,6 +388,15 @@ void tracing_start_sched_switch_record(void); | |||
| 380 | int register_tracer(struct tracer *type); | 388 | int register_tracer(struct tracer *type); |
| 381 | void unregister_tracer(struct tracer *type); | 389 | void unregister_tracer(struct tracer *type); |
| 382 | int is_tracing_stopped(void); | 390 | int is_tracing_stopped(void); |
| 391 | enum trace_file_type { | ||
| 392 | TRACE_FILE_LAT_FMT = 1, | ||
| 393 | TRACE_FILE_ANNOTATE = 2, | ||
| 394 | }; | ||
| 395 | |||
| 396 | extern cpumask_var_t __read_mostly tracing_buffer_mask; | ||
| 397 | |||
| 398 | #define for_each_tracing_cpu(cpu) \ | ||
| 399 | for_each_cpu(cpu, tracing_buffer_mask) | ||
| 383 | 400 | ||
| 384 | extern int process_new_ksym_entry(char *ksymname, int op, unsigned long addr); | 401 | extern int process_new_ksym_entry(char *ksymname, int op, unsigned long addr); |
| 385 | 402 | ||
| @@ -471,6 +488,8 @@ trace_array_vprintk(struct trace_array *tr, | |||
| 471 | unsigned long ip, const char *fmt, va_list args); | 488 | unsigned long ip, const char *fmt, va_list args); |
| 472 | int trace_array_printk(struct trace_array *tr, | 489 | int trace_array_printk(struct trace_array *tr, |
| 473 | unsigned long ip, const char *fmt, ...); | 490 | unsigned long ip, const char *fmt, ...); |
| 491 | void trace_printk_seq(struct trace_seq *s); | ||
| 492 | enum print_line_t print_trace_line(struct trace_iterator *iter); | ||
| 474 | 493 | ||
| 475 | extern unsigned long trace_flags; | 494 | extern unsigned long trace_flags; |
| 476 | 495 | ||
diff --git a/kernel/trace/trace_kdb.c b/kernel/trace/trace_kdb.c new file mode 100644 index 00000000000..7b8ecd751d9 --- /dev/null +++ b/kernel/trace/trace_kdb.c | |||
| @@ -0,0 +1,136 @@ | |||
| 1 | /* | ||
| 2 | * kdb helper for dumping the ftrace buffer | ||
| 3 | * | ||
| 4 | * Copyright (C) 2010 Jason Wessel <jason.wessel@windriver.com> | ||
| 5 | * | ||
| 6 | * ftrace_dump_buf based on ftrace_dump: | ||
| 7 | * Copyright (C) 2007-2008 Steven Rostedt <srostedt@redhat.com> | ||
| 8 | * Copyright (C) 2008 Ingo Molnar <mingo@redhat.com> | ||
| 9 | * | ||
| 10 | */ | ||
| 11 | #include <linux/init.h> | ||
| 12 | #include <linux/kgdb.h> | ||
| 13 | #include <linux/kdb.h> | ||
| 14 | #include <linux/ftrace.h> | ||
| 15 | |||
| 16 | #include "../debug/kdb/kdb_private.h" | ||
| 17 | #include "trace.h" | ||
| 18 | #include "trace_output.h" | ||
| 19 | |||
| 20 | static void ftrace_dump_buf(int skip_lines, long cpu_file) | ||
| 21 | { | ||
| 22 | /* use static because iter can be a bit big for the stack */ | ||
| 23 | static struct trace_iterator iter; | ||
| 24 | unsigned int old_userobj; | ||
| 25 | int cnt = 0, cpu; | ||
| 26 | |||
| 27 | trace_init_global_iter(&iter); | ||
| 28 | |||
| 29 | for_each_tracing_cpu(cpu) { | ||
| 30 | atomic_inc(&iter.tr->data[cpu]->disabled); | ||
| 31 | } | ||
| 32 | |||
| 33 | old_userobj = trace_flags; | ||
| 34 | |||
| 35 | /* don't look at user memory in panic mode */ | ||
| 36 | trace_flags &= ~TRACE_ITER_SYM_USEROBJ; | ||
| 37 | |||
| 38 | kdb_printf("Dumping ftrace buffer:\n"); | ||
| 39 | |||
| 40 | /* reset all but tr, trace, and overruns */ | ||
| 41 | memset(&iter.seq, 0, | ||
| 42 | sizeof(struct trace_iterator) - | ||
| 43 | offsetof(struct trace_iterator, seq)); | ||
| 44 | iter.iter_flags |= TRACE_FILE_LAT_FMT; | ||
| 45 | iter.pos = -1; | ||
| 46 | |||
| 47 | if (cpu_file == TRACE_PIPE_ALL_CPU) { | ||
| 48 | for_each_tracing_cpu(cpu) { | ||
| 49 | iter.buffer_iter[cpu] = | ||
| 50 | ring_buffer_read_prepare(iter.tr->buffer, cpu); | ||
| 51 | ring_buffer_read_start(iter.buffer_iter[cpu]); | ||
| 52 | tracing_iter_reset(&iter, cpu); | ||
| 53 | } | ||
| 54 | } else { | ||
| 55 | iter.cpu_file = cpu_file; | ||
| 56 | iter.buffer_iter[cpu_file] = | ||
| 57 | ring_buffer_read_prepare(iter.tr->buffer, cpu_file); | ||
| 58 | ring_buffer_read_start(iter.buffer_iter[cpu_file]); | ||
| 59 | tracing_iter_reset(&iter, cpu_file); | ||
| 60 | } | ||
| 61 | if (!trace_empty(&iter)) | ||
| 62 | trace_find_next_entry_inc(&iter); | ||
| 63 | while (!trace_empty(&iter)) { | ||
| 64 | if (!cnt) | ||
| 65 | kdb_printf("---------------------------------\n"); | ||
| 66 | cnt++; | ||
| 67 | |||
| 68 | if (trace_find_next_entry_inc(&iter) != NULL && !skip_lines) | ||
| 69 | print_trace_line(&iter); | ||
| 70 | if (!skip_lines) | ||
| 71 | trace_printk_seq(&iter.seq); | ||
| 72 | else | ||
| 73 | skip_lines--; | ||
| 74 | if (KDB_FLAG(CMD_INTERRUPT)) | ||
| 75 | goto out; | ||
| 76 | } | ||
| 77 | |||
| 78 | if (!cnt) | ||
| 79 | kdb_printf(" (ftrace buffer empty)\n"); | ||
| 80 | else | ||
| 81 | kdb_printf("---------------------------------\n"); | ||
| 82 | |||
| 83 | out: | ||
| 84 | trace_flags = old_userobj; | ||
| 85 | |||
| 86 | for_each_tracing_cpu(cpu) { | ||
| 87 | atomic_dec(&iter.tr->data[cpu]->disabled); | ||
| 88 | } | ||
| 89 | |||
| 90 | for_each_tracing_cpu(cpu) | ||
| 91 | if (iter.buffer_iter[cpu]) | ||
| 92 | ring_buffer_read_finish(iter.buffer_iter[cpu]); | ||
| 93 | } | ||
| 94 | |||
| 95 | /* | ||
| 96 | * kdb_ftdump - Dump the ftrace log buffer | ||
| 97 | */ | ||
| 98 | static int kdb_ftdump(int argc, const char **argv) | ||
| 99 | { | ||
| 100 | int skip_lines = 0; | ||
| 101 | long cpu_file; | ||
| 102 | char *cp; | ||
| 103 | |||
| 104 | if (argc > 2) | ||
| 105 | return KDB_ARGCOUNT; | ||
| 106 | |||
| 107 | if (argc) { | ||
| 108 | skip_lines = simple_strtol(argv[1], &cp, 0); | ||
| 109 | if (*cp) | ||
| 110 | skip_lines = 0; | ||
| 111 | } | ||
| 112 | |||
| 113 | if (argc == 2) { | ||
| 114 | cpu_file = simple_strtol(argv[2], &cp, 0); | ||
| 115 | if (*cp || cpu_file >= NR_CPUS || cpu_file < 0 || | ||
| 116 | !cpu_online(cpu_file)) | ||
| 117 | return KDB_BADINT; | ||
| 118 | } else { | ||
| 119 | cpu_file = TRACE_PIPE_ALL_CPU; | ||
| 120 | } | ||
| 121 | |||
| 122 | kdb_trap_printk++; | ||
| 123 | ftrace_dump_buf(skip_lines, cpu_file); | ||
| 124 | kdb_trap_printk--; | ||
| 125 | |||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | static __init int kdb_ftrace_register(void) | ||
| 130 | { | ||
| 131 | kdb_register_repeat("ftdump", kdb_ftdump, "[skip_#lines] [cpu]", | ||
| 132 | "Dump ftrace log", 0, KDB_REPEAT_NONE); | ||
| 133 | return 0; | ||
| 134 | } | ||
| 135 | |||
| 136 | late_initcall(kdb_ftrace_register); | ||
