aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc
diff options
context:
space:
mode:
authorJason Wessel <jason.wessel@windriver.com>2012-03-29 18:41:24 -0400
committerJason Wessel <jason.wessel@windriver.com>2012-03-29 18:41:24 -0400
commit23bbd8e346f1ef3fc1219c79cea53d8d52b207d8 (patch)
treeb135674cee5a441902b8a1f0c42cd887578b8cc3 /drivers/misc
parent486c5987a00a89d56c2c04c506417ef8f823ca2e (diff)
kgdbts: (2 of 2) fix single step awareness to work correctly with SMP
The do_fork and sys_open tests have never worked properly on anything other than a UP configuration with the kgdb test suite. This is because the test suite did not fully implement the behavior of a real debugger. A real debugger tracks the state of what thread it asked to single step and can correctly continue other threads of execution or conditionally stop while waiting for the original thread single step request to return. Below is a simple method to cause a fatal kernel oops with the kgdb test suite on a 2 processor ARM system: while [ 1 ] ; do ls > /dev/null 2> /dev/null; done& while [ 1 ] ; do ls > /dev/null 2> /dev/null; done& echo V1I1F100 > /sys/module/kgdbts/parameters/kgdbts Very soon after starting the test the kernel will start warning with messages like: kgdbts: BP mismatch c002487c expected c0024878 ------------[ cut here ]------------ WARNING: at drivers/misc/kgdbts.c:317 check_and_rewind_pc+0x9c/0xc4() [<c01f6520>] (check_and_rewind_pc+0x9c/0xc4) [<c01f595c>] (validate_simple_test+0x3c/0xc4) [<c01f60d4>] (run_simple_test+0x1e8/0x274) The kernel will eventually recovers, but the test suite has completely failed to test anything useful. This patch implements behavior similar to a real debugger that does not rely on hardware single stepping by using only software planted breakpoints. In order to mimic a real debugger, the kgdb test suite now tracks the most recent thread that was continued (cont_thread_id), with the intent to single step just this thread. When the response to the single step request stops in a different thread that hit the original break point that thread will now get continued, while the debugger waits for the thread with the single step pending. Here is a high level description of the sequence of events. cont_instead_of_sstep = 0; 1) set breakpoint at do_fork 2) continue 3) Save the thread id where we stop to cont_thread_id 4) Remove breakpoint at do_fork 5) Reset the PC if needed depending on kernel exception type 6) soft single step 7) Check where we stopped if current thread != cont_thread_id { if (here for more than 2 times for the same thead) { ### must be a really busy system, start test again ### goto step 1 } goto step 5 } else { cont_instead_of_sstep = 0; } 8) clean up and run test again if needed 9) Clear out any threads that were waiting on a break point at the point in time the test is ended with get_cont_catch(). This happens sometimes because breakpoints are used in place of single stepping and some threads could have been in the debugger exception handling queue because breakpoints were hit concurrently on different CPUs. This also means we wait at least one second before unplumbing the debugger connection at the very end, so as respond to any debug threads waiting to be serviced. Cc: stable@vger.kernel.org # >= 3.0 Signed-off-by: Jason Wessel <jason.wessel@windriver.com>
Diffstat (limited to 'drivers/misc')
-rw-r--r--drivers/misc/kgdbts.c73
1 files changed, 62 insertions, 11 deletions
diff --git a/drivers/misc/kgdbts.c b/drivers/misc/kgdbts.c
index 3cad9fce8054..d087456ba089 100644
--- a/drivers/misc/kgdbts.c
+++ b/drivers/misc/kgdbts.c
@@ -142,7 +142,9 @@ static int arch_needs_sstep_emulation = 1;
142#else 142#else
143static int arch_needs_sstep_emulation; 143static int arch_needs_sstep_emulation;
144#endif 144#endif
145static unsigned long cont_addr;
145static unsigned long sstep_addr; 146static unsigned long sstep_addr;
147static int restart_from_top_after_write;
146static int sstep_state; 148static int sstep_state;
147 149
148/* Storage for the registers, in GDB format. */ 150/* Storage for the registers, in GDB format. */
@@ -190,7 +192,8 @@ static int kgdbts_unreg_thread(void *ptr)
190 */ 192 */
191 while (!final_ack) 193 while (!final_ack)
192 msleep_interruptible(1500); 194 msleep_interruptible(1500);
193 195 /* Pause for any other threads to exit after final ack. */
196 msleep_interruptible(1000);
194 if (configured) 197 if (configured)
195 kgdb_unregister_io_module(&kgdbts_io_ops); 198 kgdb_unregister_io_module(&kgdbts_io_ops);
196 configured = 0; 199 configured = 0;
@@ -312,13 +315,21 @@ static int check_and_rewind_pc(char *put_str, char *arg)
312 if (addr + BREAK_INSTR_SIZE == ip) 315 if (addr + BREAK_INSTR_SIZE == ip)
313 offset = -BREAK_INSTR_SIZE; 316 offset = -BREAK_INSTR_SIZE;
314#endif 317#endif
315 if (strcmp(arg, "silent") && ip + offset != addr) { 318
319 if (arch_needs_sstep_emulation && sstep_addr &&
320 ip + offset == sstep_addr &&
321 ((!strcmp(arg, "sys_open") || !strcmp(arg, "do_fork")))) {
322 /* This is special case for emulated single step */
323 v2printk("Emul: rewind hit single step bp\n");
324 restart_from_top_after_write = 1;
325 } else if (strcmp(arg, "silent") && ip + offset != addr) {
316 eprintk("kgdbts: BP mismatch %lx expected %lx\n", 326 eprintk("kgdbts: BP mismatch %lx expected %lx\n",
317 ip + offset, addr); 327 ip + offset, addr);
318 return 1; 328 return 1;
319 } 329 }
320 /* Readjust the instruction pointer if needed */ 330 /* Readjust the instruction pointer if needed */
321 ip += offset; 331 ip += offset;
332 cont_addr = ip;
322#ifdef GDB_ADJUSTS_BREAK_OFFSET 333#ifdef GDB_ADJUSTS_BREAK_OFFSET
323 instruction_pointer_set(&kgdbts_regs, ip); 334 instruction_pointer_set(&kgdbts_regs, ip);
324#endif 335#endif
@@ -328,6 +339,8 @@ static int check_and_rewind_pc(char *put_str, char *arg)
328static int check_single_step(char *put_str, char *arg) 339static int check_single_step(char *put_str, char *arg)
329{ 340{
330 unsigned long addr = lookup_addr(arg); 341 unsigned long addr = lookup_addr(arg);
342 static int matched_id;
343
331 /* 344 /*
332 * From an arch indepent point of view the instruction pointer 345 * From an arch indepent point of view the instruction pointer
333 * should be on a different instruction 346 * should be on a different instruction
@@ -338,17 +351,28 @@ static int check_single_step(char *put_str, char *arg)
338 v2printk("Singlestep stopped at IP: %lx\n", 351 v2printk("Singlestep stopped at IP: %lx\n",
339 instruction_pointer(&kgdbts_regs)); 352 instruction_pointer(&kgdbts_regs));
340 353
341 if (sstep_thread_id != cont_thread_id && !arch_needs_sstep_emulation) { 354 if (sstep_thread_id != cont_thread_id) {
342 /* 355 /*
343 * Ensure we stopped in the same thread id as before, else the 356 * Ensure we stopped in the same thread id as before, else the
344 * debugger should continue until the original thread that was 357 * debugger should continue until the original thread that was
345 * single stepped is scheduled again, emulating gdb's behavior. 358 * single stepped is scheduled again, emulating gdb's behavior.
346 */ 359 */
347 v2printk("ThrID does not match: %lx\n", cont_thread_id); 360 v2printk("ThrID does not match: %lx\n", cont_thread_id);
361 if (arch_needs_sstep_emulation) {
362 if (matched_id &&
363 instruction_pointer(&kgdbts_regs) != addr)
364 goto continue_test;
365 matched_id++;
366 ts.idx -= 2;
367 sstep_state = 0;
368 return 0;
369 }
348 cont_instead_of_sstep = 1; 370 cont_instead_of_sstep = 1;
349 ts.idx -= 4; 371 ts.idx -= 4;
350 return 0; 372 return 0;
351 } 373 }
374continue_test:
375 matched_id = 0;
352 if (instruction_pointer(&kgdbts_regs) == addr) { 376 if (instruction_pointer(&kgdbts_regs) == addr) {
353 eprintk("kgdbts: SingleStep failed at %lx\n", 377 eprintk("kgdbts: SingleStep failed at %lx\n",
354 instruction_pointer(&kgdbts_regs)); 378 instruction_pointer(&kgdbts_regs));
@@ -390,6 +414,31 @@ static int got_break(char *put_str, char *arg)
390 return 1; 414 return 1;
391} 415}
392 416
417static void get_cont_catch(char *arg)
418{
419 /* Always send detach because the test is completed at this point */
420 fill_get_buf("D");
421}
422
423static int put_cont_catch(char *put_str, char *arg)
424{
425 /* This is at the end of the test and we catch any and all input */
426 v2printk("kgdbts: cleanup task: %lx\n", sstep_thread_id);
427 ts.idx--;
428 return 0;
429}
430
431static int emul_reset(char *put_str, char *arg)
432{
433 if (strncmp(put_str, "$OK", 3))
434 return 1;
435 if (restart_from_top_after_write) {
436 restart_from_top_after_write = 0;
437 ts.idx = -1;
438 }
439 return 0;
440}
441
393static void emul_sstep_get(char *arg) 442static void emul_sstep_get(char *arg)
394{ 443{
395 if (!arch_needs_sstep_emulation) { 444 if (!arch_needs_sstep_emulation) {
@@ -443,8 +492,7 @@ static int emul_sstep_put(char *put_str, char *arg)
443 v2printk("Stopped at IP: %lx\n", 492 v2printk("Stopped at IP: %lx\n",
444 instruction_pointer(&kgdbts_regs)); 493 instruction_pointer(&kgdbts_regs));
445 /* Want to stop at IP + break instruction size by default */ 494 /* Want to stop at IP + break instruction size by default */
446 sstep_addr = instruction_pointer(&kgdbts_regs) + 495 sstep_addr = cont_addr + BREAK_INSTR_SIZE;
447 BREAK_INSTR_SIZE;
448 break; 496 break;
449 case 2: 497 case 2:
450 if (strncmp(put_str, "$OK", 3)) { 498 if (strncmp(put_str, "$OK", 3)) {
@@ -456,6 +504,9 @@ static int emul_sstep_put(char *put_str, char *arg)
456 if (strncmp(put_str, "$T0", 3)) { 504 if (strncmp(put_str, "$T0", 3)) {
457 eprintk("kgdbts: failed continue sstep\n"); 505 eprintk("kgdbts: failed continue sstep\n");
458 return 1; 506 return 1;
507 } else {
508 char *ptr = &put_str[11];
509 kgdb_hex2long(&ptr, &sstep_thread_id);
459 } 510 }
460 break; 511 break;
461 case 4: 512 case 4:
@@ -558,13 +609,13 @@ static struct test_struct do_fork_test[] = {
558 { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ 609 { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */
559 { "do_fork", "OK", sw_rem_break }, /*remove breakpoint */ 610 { "do_fork", "OK", sw_rem_break }, /*remove breakpoint */
560 { "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */ 611 { "g", "do_fork", NULL, check_and_rewind_pc }, /* check location */
561 { "write", "OK", write_regs }, /* Write registers */ 612 { "write", "OK", write_regs, emul_reset }, /* Write registers */
562 { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ 613 { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
563 { "g", "do_fork", NULL, check_single_step }, 614 { "g", "do_fork", NULL, check_single_step },
564 { "do_fork", "OK", sw_break, }, /* set sw breakpoint */ 615 { "do_fork", "OK", sw_break, }, /* set sw breakpoint */
565 { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */ 616 { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */
566 { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */ 617 { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */
567 { "", "" }, 618 { "", "", get_cont_catch, put_cont_catch },
568}; 619};
569 620
570/* Test for hitting a breakpoint at sys_open for what ever the number 621/* Test for hitting a breakpoint at sys_open for what ever the number
@@ -576,13 +627,13 @@ static struct test_struct sys_open_test[] = {
576 { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */ 627 { "c", "T0*", NULL, get_thread_id_continue }, /* Continue */
577 { "sys_open", "OK", sw_rem_break }, /*remove breakpoint */ 628 { "sys_open", "OK", sw_rem_break }, /*remove breakpoint */
578 { "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */ 629 { "g", "sys_open", NULL, check_and_rewind_pc }, /* check location */
579 { "write", "OK", write_regs }, /* Write registers */ 630 { "write", "OK", write_regs, emul_reset }, /* Write registers */
580 { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */ 631 { "s", "T0*", emul_sstep_get, emul_sstep_put }, /* Single step */
581 { "g", "sys_open", NULL, check_single_step }, 632 { "g", "sys_open", NULL, check_single_step },
582 { "sys_open", "OK", sw_break, }, /* set sw breakpoint */ 633 { "sys_open", "OK", sw_break, }, /* set sw breakpoint */
583 { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */ 634 { "7", "T0*", skip_back_repeat_test }, /* Loop based on repeat_test */
584 { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */ 635 { "D", "OK", NULL, final_ack_set }, /* detach and unregister I/O */
585 { "", "" }, 636 { "", "", get_cont_catch, put_cont_catch },
586}; 637};
587 638
588/* 639/*
@@ -725,8 +776,8 @@ static int run_simple_test(int is_get_char, int chr)
725 /* This callback is a put char which is when kgdb sends data to 776 /* This callback is a put char which is when kgdb sends data to
726 * this I/O module. 777 * this I/O module.
727 */ 778 */
728 if (ts.tst[ts.idx].get[0] == '\0' && 779 if (ts.tst[ts.idx].get[0] == '\0' && ts.tst[ts.idx].put[0] == '\0' &&
729 ts.tst[ts.idx].put[0] == '\0') { 780 !ts.tst[ts.idx].get_handler) {
730 eprintk("kgdbts: ERROR: beyond end of test on" 781 eprintk("kgdbts: ERROR: beyond end of test on"
731 " '%s' line %i\n", ts.name, ts.idx); 782 " '%s' line %i\n", ts.name, ts.idx);
732 return 0; 783 return 0;