// SPDX-License-Identifier: GPL-2.0-only /* * kgdbts is a test suite for kgdb for the sole purpose of validating * that key pieces of the kgdb internals are working properly such as * HW/SW breakpoints, single stepping, and NMI. * * Created by: Jason Wessel <jason.wessel@windriver.com> * * Copyright (c) 2008 Wind River Systems, Inc.
*/ /* Information about the kgdb test suite. * ------------------------------------- * * The kgdb test suite is designed as a KGDB I/O module which * simulates the communications that a debugger would have with kgdb. * The tests are broken up in to a line by line and referenced here as * a "get" which is kgdb requesting input and "put" which is kgdb * sending a response. * * The kgdb suite can be invoked from the kernel command line * arguments system or executed dynamically at run time. The test * suite uses the variable "kgdbts" to obtain the information about * which tests to run and to configure the verbosity level. The * following are the various characters you can use with the kgdbts= * line: * * When using the "kgdbts=" you only choose one of the following core * test types: * A = Run all the core tests silently * V1 = Run all the core tests with minimal output * V2 = Run all the core tests in debug mode * * You can also specify optional tests: * N## = Go to sleep with interrupts of for ## seconds * to test the HW NMI watchdog * F## = Break at kernel_clone for ## iterations * S## = Break at sys_open for ## iterations * I## = Run the single step test ## iterations * * NOTE: that the kernel_clone and sys_open tests are mutually exclusive. * * To invoke the kgdb test suite from boot you use a kernel start * argument as follows: * kgdbts=V1 kgdbwait * Or if you wanted to perform the NMI test for 6 seconds and kernel_clone * test for 100 forks, you could use: * kgdbts=V1N6F100 kgdbwait * * The test suite can also be invoked at run time with: * echo kgdbts=V1N6F100 > /sys/module/kgdbts/parameters/kgdbts * Or as another example: * echo kgdbts=V2 > /sys/module/kgdbts/parameters/kgdbts * * When developing a new kgdb arch specific implementation or * using these tests for the purpose of regression testing, * several invocations are required. * * 1) Boot with the test suite enabled by using the kernel arguments * "kgdbts=V1F100 kgdbwait" * ## If kgdb arch specific implementation has NMI use * "kgdbts=V1N6F100 * * 2) After the system boot run the basic test. * echo kgdbts=V1 > /sys/module/kgdbts/parameters/kgdbts * * 3) Run the concurrency tests. It is best to use n+1 * while loops where n is the number of cpus you have * in your system. The example below uses only two * loops. * * ## This tests break points on sys_open * while [ 1 ] ; do find / > /dev/null 2>&1 ; done & * while [ 1 ] ; do find / > /dev/null 2>&1 ; done & * echo kgdbts=V1S10000 > /sys/module/kgdbts/parameters/kgdbts * fg # and hit control-c * fg # and hit control-c * ## This tests break points on kernel_clone * while [ 1 ] ; do date > /dev/null ; done & * while [ 1 ] ; do date > /dev/null ; done & * echo kgdbts=V1F1000 > /sys/module/kgdbts/parameters/kgdbts * fg # and hit control-c *
*/
struct test_state { char *name; struct test_struct *tst; int idx; int (*run_test) (int, int); int (*validate_put) (char *);
};
staticstruct test_state ts;
staticint kgdbts_unreg_thread(void *ptr)
{ /* Wait until the tests are complete and then ungresiter the I/O * driver.
*/ while (!final_ack)
msleep_interruptible(1500); /* Pause for any other threads to exit after final ack. */
msleep_interruptible(1000); if (configured)
kgdb_unregister_io_module(&kgdbts_io_ops);
configured = 0;
return 0;
}
/* This is noinline such that it can be used for a single location to * place a breakpoint
*/ static noinline void kgdbts_break_test(void)
{
v2printk("kgdbts: breakpoint complete\n");
}
/* * This is a cached wrapper for kallsyms_lookup_name(). * * The cache is a big win for several tests. For example it more the doubles * the cycles per second during the sys_open test. This is not theoretic, * the performance improvement shows up at human scale, especially when * testing using emulators. * * Obviously neither re-entrant nor thread-safe but that is OK since it * can only be called from the debug trap (and therefore all other CPUs * are halted).
*/ staticunsignedlong lookup_addr(char *arg)
{ staticchar cached_arg[KSYM_NAME_LEN]; staticunsignedlong cached_addr;
kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs,
NUMREGBYTES);
gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs);
ip = instruction_pointer(&kgdbts_regs);
v2printk("Stopped at IP: %lx\n", ip); #ifdef GDB_ADJUSTS_BREAK_OFFSET /* On some arches, a breakpoint stop requires it to be decremented */ if (addr + BREAK_INSTR_SIZE == ip)
offset = -BREAK_INSTR_SIZE; #endif
if (arch_needs_sstep_emulation && sstep_addr &&
ip + offset == sstep_addr &&
((!strcmp(arg, "do_sys_openat2") || !strcmp(arg, "kernel_clone")))) { /* This is special case for emulated single step */
v2printk("Emul: rewind hit single step bp\n");
restart_from_top_after_write = 1;
} elseif (strcmp(arg, "silent") && ip + offset != addr) {
eprintk("kgdbts: BP mismatch %lx expected %lx\n",
ip + offset, addr); return 1;
} /* Readjust the instruction pointer if needed */
ip += offset;
cont_addr = ip; #ifdef GDB_ADJUSTS_BREAK_OFFSET
instruction_pointer_set(&kgdbts_regs, ip); #endif return 0;
}
/* * From an arch indepent point of view the instruction pointer * should be on a different instruction
*/
kgdb_hex2mem(&put_str[1], (char *)kgdbts_gdb_regs,
NUMREGBYTES);
gdb_regs_to_pt_regs(kgdbts_gdb_regs, &kgdbts_regs);
v2printk("Singlestep stopped at IP: %lx\n",
instruction_pointer(&kgdbts_regs));
if (sstep_thread_id != cont_thread_id) { /* * Ensure we stopped in the same thread id as before, else the * debugger should continue until the original thread that was * single stepped is scheduled again, emulating gdb's behavior.
*/
v2printk("ThrID does not match: %lx\n", cont_thread_id); if (arch_needs_sstep_emulation) { if (matched_id &&
instruction_pointer(&kgdbts_regs) != addr) goto continue_test;
matched_id++;
ts.idx -= 2;
sstep_state = 0; return 0;
}
cont_instead_of_sstep = 1;
ts.idx -= 4; return 0;
}
continue_test:
matched_id = 0; if (instruction_pointer(&kgdbts_regs) == addr) {
eprintk("kgdbts: SingleStep failed at %lx\n",
instruction_pointer(&kgdbts_regs)); return 1;
}
staticvoid get_cont_catch(char *arg)
{ /* Always send detach because the test is completed at this point */
fill_get_buf("D");
}
staticint put_cont_catch(char *put_str, char *arg)
{ /* This is at the end of the test and we catch any and all input */
v2printk("kgdbts: cleanup task: %lx\n", sstep_thread_id);
ts.idx--; return 0;
}
if (ts.tst[ts.idx].put_handler) return ts.tst[ts.idx].put_handler(put_str,
ts.tst[ts.idx].put);
chk_str = ts.tst[ts.idx].put; if (*put_str == '$')
put_str++;
while (*chk_str != '\0' && *put_str != '\0') { /* If someone does a * to match the rest of the string, allow * it, or stop if the received string is complete.
*/ if (*put_str == '#' || *chk_str == '*') return 0; if (*put_str != *chk_str) return 1;
staticint run_simple_test(int is_get_char, int chr)
{ int ret = 0; if (is_get_char) { /* Send an ACK on the get if a prior put completed and set the * send ack variable
*/ if (send_ack) {
send_ack = 0; return'+';
} /* On the first get char, fill the transmit buffer and then * take from the get_string.
*/ if (get_buf_cnt == 0) { if (ts.tst[ts.idx].get_handler)
ts.tst[ts.idx].get_handler(ts.tst[ts.idx].get); else
fill_get_buf(ts.tst[ts.idx].get);
}
if (get_buf[get_buf_cnt] == '\0') {
eprintk("kgdbts: ERROR GET: EOB on '%s' at %i\n",
ts.name, ts.idx);
get_buf_cnt = 0;
fill_get_buf("D");
}
ret = get_buf[get_buf_cnt];
get_buf_cnt++; return ret;
}
/* This callback is a put char which is when kgdb sends data to * this I/O module.
*/ if (ts.tst[ts.idx].get[0] == '\0' && ts.tst[ts.idx].put[0] == '\0' &&
!ts.tst[ts.idx].get_handler) {
eprintk("kgdbts: ERROR: beyond end of test on" " '%s' line %i\n", ts.name, ts.idx); return 0;
}
if (put_buf_cnt >= BUFMAX) {
eprintk("kgdbts: ERROR: put buffer overflow on" " '%s' line %i\n", ts.name, ts.idx);
put_buf_cnt = 0; return 0;
} /* Ignore everything until the first valid packet start '$' */ if (put_buf_cnt == 0 && chr != '$') return 0;
put_buf[put_buf_cnt] = chr;
put_buf_cnt++;
/* End of packet == #XX so look for the '#' */ if (put_buf_cnt > 3 && put_buf[put_buf_cnt - 3] == '#') { if (put_buf_cnt >= BUFMAX) {
eprintk("kgdbts: ERROR: put buffer overflow on" " '%s' line %i\n", ts.name, ts.idx);
put_buf_cnt = 0; return 0;
}
put_buf[put_buf_cnt] = '\0';
v2printk("put%i: %s\n", ts.idx, put_buf); /* Trigger check here */ if (ts.validate_put && ts.validate_put(put_buf)) {
eprintk("kgdbts: ERROR PUT: end of test " "buffer on '%s' line %i expected %s got %s\n",
ts.name, ts.idx, ts.tst[ts.idx].put, put_buf);
}
ts.idx++;
put_buf_cnt = 0;
get_buf_cnt = 0;
send_ack = 1;
} return 0;
}
/* If the kernel_clone test is run it will be the last test that is * executed because a kernel thread will be spawned at the very * end to unregister the debug hooks.
*/ if (clone_test) {
repeat_test = clone_test;
printk(KERN_INFO "kgdbts:RUN kernel_clone for %i breakpoints\n",
repeat_test);
kthread_run(kgdbts_unreg_thread, NULL, "kgdbts_unreg");
run_kernel_clone_test(); return;
}
/* If the sys_open test is run it will be the last test that is * executed because a kernel thread will be spawned at the very * end to unregister the debug hooks.
*/ if (do_sys_open_test) {
repeat_test = do_sys_open_test;
printk(KERN_INFO "kgdbts:RUN sys_open for %i breakpoints\n",
repeat_test);
kthread_run(kgdbts_unreg_thread, NULL, "kgdbts_unreg");
run_sys_open_test(); return;
} /* Shutdown and unregister */
kgdb_unregister_io_module(&kgdbts_io_ops);
configured = 0;
}
if (len >= MAX_CONFIG_LEN) {
printk(KERN_ERR "kgdbts: config string too long\n"); return -ENOSPC;
}
/* Only copy in the string if the init function has not run yet */ if (configured < 0) {
strcpy(config, kmessage); return 0;
}
if (configured == 1) {
printk(KERN_ERR "kgdbts: ERROR: Already configured and running.\n"); return -EBUSY;
}
strcpy(config, kmessage); /* Chop out \n char as a result of echo */ if (len && config[len - 1] == '\n')
config[len - 1] = '\0';
/* Go and configure with the new params. */ return configure_kgdbts();
}
staticvoid kgdbts_pre_exp_handler(void)
{ /* Increment the module count when the debugger is active */ if (!kgdb_connected)
try_module_get(THIS_MODULE);
}
staticvoid kgdbts_post_exp_handler(void)
{ /* decrement the module count when the debugger detaches */ if (!kgdb_connected)
module_put(THIS_MODULE);
}
/* * not really modular, but the easiest way to keep compat with existing * bootargs behaviour is to continue using module_param here.
*/
module_param_call(kgdbts, param_set_kgdbts_var, param_get_string, &kps, 0644);
MODULE_PARM_DESC(kgdbts, "[F#|S#][N#]");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.