内核调试
Windows的系统调试技术

Windows的系统调试技术Windows是我们日常使用的主流操作系统之一,其稳定性和可靠性对于用户来说非常重要。
然而,由于软件和硬件的组合多种多样,系统调试成为了确保Windows系统正常运行的关键环节。
本文将介绍一些常用的Windows系统调试技术,以帮助读者深入了解和解决可能出现的问题。
一、用户模式调试技术用户模式调试技术是一种以应用程序和驱动程序为主要调试对象的技术。
在用户模式下,开发人员可以使用各种工具和方法来追踪和分析应用程序的执行过程,以及可能导致错误和异常的原因。
1. 调试器工具Windows提供了许多强大的调试器工具,如Visual Studio、WinDbg 等。
这些工具可以附加到正在运行的应用程序上,允许开发人员在执行过程中观察和分析代码。
通过设置断点、查看变量的值以及跟踪函数的调用,开发人员可以更容易地定位和解决问题。
2. 日志记录除了使用调试器工具外,开发人员还可以通过日志记录来追踪应用程序的执行过程。
Windows提供了事件跟踪日志(Event Tracing for Windows)和Windows事件日志等机制,开发人员可以将关键信息记录在日志中,以便后续分析。
3. 异常处理在应用程序开发中,错误和异常是难以避免的。
为了更好地调试和处理异常,开发人员可以使用异常处理机制。
通过捕获和处理异常,开发人员可以在应用程序出现错误时采取相应的措施,防止系统崩溃或数据丢失。
二、内核模式调试技术除了用户模式下的调试技术外,内核模式调试技术对于解决Windows系统层面的问题也非常重要。
在内核模式下,开发人员可以追踪和分析驱动程序和操作系统的执行过程,以及可能导致系统故障的原因。
1. 内核调试器内核调试器是一种强大的工具,用于在内核模式下调试驱动程序和操作系统。
Windows提供了内核调试器WinDbg,开发人员可以通过串口、网络或虚拟机的方式将调试器连接到目标系统。
通过设置断点、查看寄存器状态以及分析内存内容,开发人员可以深入了解内核的执行情况,并找出潜在的问题。
Linux内核调试方法总结之反汇编

Linux内核调试⽅法总结之反汇编Linux反汇编调试⽅法Linux内核模块或者应⽤程序经常因为各种各样的原因⽽崩溃,⼀般情况下都会打印函数调⽤栈信息,那么,这种情况下,我们怎么去定位问题呢?本⽂档介绍了⼀种反汇编的⽅法辅助定位此类问题。
代码⽰例如下:#include <signal.h>#include <stdio.h>#include <stdlib.h>#include <execinfo.h>#include <fcntl.h>#include <string.h>#include <unistd.h>#include <sys/types.h>#include <sys/stat.h>#define PRINT_DEBUG#define MAX_BACKTRACE_LEVEL 10#define BACKTRACE_LOG_NAME "backtrace.log"static void show_reason(int sig, siginfo_t *info, void *secret){void *array[MAX_BACKTRACE_LEVEL];size_t size;#ifdef PRINT_DEBUGchar **strings;size_t i;size = backtrace(array, MAX_BACKTRACE_LEVEL);strings = backtrace_symbols(array, size);printf("Obtain %zd stack frames.\n", size);for(i = 0; i < size; i++)printf("%s\n", strings[i]);free(strings);#elseint fd = open(BACKSTRACE_LOG_NAME, O_CREAT | O_WRONLY);size = backtrace(array, MAX_BACKTRACE_LEVEL);backtrace_symbols_fd(array, size, fd);close(fd);#endifexit(0);}void die() {char *str1;char *str2;char *str3;char *str4 = NULL;strcpy(str4, "ab");}void let_it_die() {die();}int main(int argc, char **argv){struct sigaction act;act.sa_sigaction = show_reason;sigemptyset(&act.sa_mask);act.sa_flags = SA_RESTART | SA_SIGINFO;sigaction(SIGSEGV, &act, NULL);sigaction(SIGUSR1, &act, NULL);sigaction(SIGFPE, &act, NULL);sigaction(SIGILL, &act, NULL);sigaction(SIGBUS, &act, NULL);sigaction(SIGABRT, &act, NULL);sigaction(SIGSYS, &act, NULL);let_it_die();return 0;}在该⽰例中,我们通过⾃定义的信号处理函数,在程序异常时通过调⽤backtrace()和backtrace_symbols()函数获取并打印函数调⽤栈信息。
内核调试器kdb代码分析

这是一个很冷门的话题,它受关注的程度永远也比不上陈冠希老师的摄影作品.不过人在江湖身不由己,因为工作原因我不得不接触它,不得不了解一点kdb的代码.Kdb,也叫kernel debugger.是Linux系统的内核调试器,它是一个开源工具,乃是SGI公司开发的.kdb适用于调试内核空间的程序代码,譬如进行设备驱动程序调试,内核模块的调试.Official的Linux kernel并不包含kdb,所以我们必须从sgi的网站上去下载kdb.关于如何安装如何使用kdb,并非本系列文章的重点.实际上,关于kdb的入门,关于kdb的应用,许多年前,当我还在复旦念书的时候,当我才刚学会几个简单的Unix 命令的时候,我就注意到网络上有人发表过这类文章了,所以介绍kdb,推广kdb,并不是我写此文的目的.我所写的是针对kdb本身的分析,针对的是kdb的源代码,或者说kdb的实现.Linux内核每隔一段时间就会发布一个最新的版本,而kdb则会相应的发布与之匹配的补丁.比如你Linux发布了2.6.10的kernel,那么我sgi这边就发布针对2.6.10的kernel的补丁.你发布了2.6.20的kernel,我就发布针对你2.6.20的补丁.比如我之前为了看2.6.22.1的kernel,我就下载了针对2.6.22.1的补丁,或者叫做patch.一共有三个patch:localhost:/usr/src # ls kdb-v4.4-2.6.22-*kdb-v4.4-2.6.22-common-1 kdb-v4.4-2.6.22-i386-1 kdb-v4.4-2.6.22-x86_64-1这三个patch有多长呢:localhost:/usr/src # wc -l kdb-v4.4-2.6.22-*17943 kdb-v4.4-2.6.22-common-114753 kdb-v4.4-2.6.22-i386-114249 kdb-v4.4-2.6.22-x86_64-146945 total我的神,46945行.第一个patch是最基本的patch.第二个patch是针对i386的.第三个patch是针对x86_64的.如果你只对i386感兴趣,那么只关注前两个patch就可以了.首先我们就来看一下这个common的patch.这个patch开门见山的介绍了它对内核中哪些文件进行了修改,或者增加了哪些文件.---Documentation/kdb/kdb.mm | 492 +++++Documentation/kdb/kdb_bp.man | 197 ++Documentation/kdb/kdb_bt.man | 228 ++Documentation/kdb/kdb_env.man | 46Documentation/kdb/kdb_ll.man | 134 +Documentation/kdb/kdb_md.man | 136 +Documentation/kdb/kdb_ps.man | 96Documentation/kdb/kdb_rd.man | 170 +Documentation/kdb/kdb_sr.man | 68Documentation/kdb/kdb_ss.man | 109 +Documentation/kdb/slides | 1382 ++++++++++++++Makefile | 1drivers/char/keyboard.c | 10drivers/hid/usbhid/hid-core.c | 20drivers/hid/usbhid/usbkbd.c | 15drivers/serial/8250.c | 53drivers/serial/8250_early.c | 34drivers/serial/sn_console.c | 73drivers/usb/host/ohci-hcd.c | 47drivers/usb/host/ohci-pci.c | 10drivers/usb/host/ohci-q.c | 62fs/proc/mmu.c | 16fs/proc/proc_misc.c | 114 +include/linux/console.h | 5include/linux/dis-asm.h | 347 +++include/linux/kdb.h | 166 +include/linux/kdbprivate.h | 485 +++++include/linux/sysctl.h | 1init/main.c | 32kdb/ChangeLog | 1672 +++++++++++++++++kdb/Makefile | 28kdb/kdb_bp.c | 619 ++++++kdb/kdb_bt.c | 179 +kdb/kdb_cmds | 32kdb/kdb_id.c | 236 ++kdb/kdb_io.c | 671 ++++++kdb/kdbmain.c | 4034 ++++++++++++++++++++++++++++++++++++++++++kdb/kdbsupport.c | 1134 +++++++++++kdb/modules/Makefile | 14kdb/modules/kdbm_pg.c | 645 ++++++kdb/modules/kdbm_sched.c | 57kdb/modules/kdbm_task.c | 199 ++kdb/modules/kdbm_vm.c | 841 ++++++++kdb/modules/kdbm_x86.c | 1096 +++++++++++kdb/modules/kdbm_xpc.c | 1105 +++++++++++kernel/exit.c | 3kernel/kallsyms.c | 22kernel/module.c | 19kernel/printk.c | 14kernel/sched.c | 79kernel/signal.c | 49mm/hugetlb.c | 19mm/swapfile.c | 2253 files changed, 17330 insertions(+), 8 deletions(-)Kdb一个很突出的特点就是它牵涉的模块倍儿多.很明显,这其中有两个文件可能是我们故事的起点.一个是init/main.c,一个是kdb/kdbmain.c.我们先看这个patch对init/main.c的修改.4909 Index: linux/init/main.c4910 =================================================================== 4911 --- linux.orig/init/main.c4912 +++ linux/init/main.c4913 @@ -66,6 +66,10 @@4914 #include <asm/smp.h>4915 #endif49164917 +#ifdef CONFIG_KDB4918 +#include <linux/kdb.h>4919 +#endif /* CONFIG_KDB */4920 +4921 /*4922 * This is one of the first .c files built. Error out early if we have compiler4923 * trouble.4924 @@ -187,6 +191,26 @@ static const char *panic_later, *panic_p49254926 extern struct obs_kernel_param __setup_start[], __setup_end[];49274928 +#ifdef CONFIG_KDB4929 +static int __init kdb_setup(char *str)4930 +{4931 + if (strcmp(str, "on") == 0) {4932 + kdb_on = 1;4933 + } else if (strcmp(str, "on-nokey") == 0) {4934 + kdb_on = 2;4935 + } else if (strcmp(str, "off") == 0) {4936 + kdb_on = 0;4937 + } else if (strcmp(str, "early") == 0) {4938 + kdb_on = 1;4939 + kdb_flags |= KDB_FLAG_EARLYKDB;4940 + } else4941 + printk("kdb flag %s not recognised/n", str);4942 + return 0;4943 +}4944 +4945 +__setup("kdb=", kdb_setup);4946 +#endif /* CONFIG_KDB */4947 +4948 static int __init obsolete_checksetup(char *line)4949 {4950 struct obs_kernel_param *p;4951 @@ -606,6 +630,14 @@ asmlinkage void __init start_kernel(void4952 pgtable_cache_init();4953 prio_tree_init();4954 anon_vma_init();4955 +4956 +#ifdef CONFIG_KDB4957 + kdb_init();4958 + if (KDB_FLAG(EARLYKDB)) {4959 + KDB_ENTER();4960 + }4961 +#endif /* CONFIG_KDB */4962 +4963 #ifdef CONFIG_X864964 if (efi_enabled)4965 efi_enter_virtual_mode();很明显,增加了两个函数kdb_setup()和kdb_init().kdb_setup()的作用是让你在启动的时候可以传递一个类似于”kdb=on”或者”kdb=off”的内核参数,换言之,它相当于一个开关,给你提供了一种选择,在启动系统的时候你就可以选择打开kdb或者索性关掉kdb.如果你选择了kdb=off,那么就相当于艳照门事件之前陈冠希老师把他的硬盘格式化掉,后面所有的故事都不可能再发生了.来看另一个函数kdb_init(),显然这个函数就是整个kdb的入口,或者说初始化函数.12393 +12394 +/*12395 + * kdb_init12396 + *12397 + * Initialize the kernel debugger environment.12398 + *12399 + * Parameters:12400 + * None.12401 + * Returns:12402 + * None.12403 + * Locking:12404 + * None.12405 + * Remarks:12406 + * None.12407 + */12408 +12409 +void __init12410 +kdb_init(void)12411 +{12412 + kdb_initial_cpu = smp_processor_id();12413 + /*12414 + * This must be called before any calls to kdb_printf.12415 + */12416 + kdb_io_init();12417 +12418 + kdb_inittab(); /* Initialize Command Table */12419 + kdb_initbptab(); /* Initialize Breakpoint Table */12420 + kdb_id_init(); /* Initialize Disassembler */12421 + kdba_init(); /* Architecture Dependent Initialization */12422 +12423 + /*12424 + * Use printk() to get message in log_buf[];12425 + */12426 + printk("kdb version %d.%d%s by Keith Owens, Scott Lurndal. "/12427 + "Copyright SGI, All Rights Reserved/n",12428 + KDB_MAJOR_VERSION, KDB_MINOR_VERSION, KDB_TEST_VERSION);12429 +12430 + kdb_cmd_init(); /* Preset commands from kdb_cmds */12431 + kdb_initial_cpu = -1; /* Avoid recursion problems */12432 + kdb(KDB_REASON_CPU_UP, 0, NULL); /* do kdb setup on boot cpu */12433 + kdb_initial_cpu = smp_processor_id();12434 + atomic_notifier_chain_register(&panic_notifier_list, &kdb_block);12435 + register_cpu_notifier(&kdb_cpu_nfb);12436 +12437 +#ifdef kdba_setjmp12438 + kdbjmpbuf = vmalloc(NR_CPUS * sizeof(*kdbjmpbuf));12439 + if (!kdbjmpbuf)12440 + printk(KERN_ERR "Cannot allocate kdbjmpbuf, no kdb recovery will be possible/n");12441 +#endif /* kdba_setjmp */12442 +12443 + kdb_initial_cpu = -1;12444 + kdb_wait_for_cpus_secs = max(10, 2*num_online_cpus());12445 +}kdb究竟有多复杂,从这个初始化函数就可见一斑.说实话Linux Kernel中光初始化就这么复杂的模块还真的不多.一共五个名字里带”init”的函数.咱们一个一个来看:1.kdb_io_init()8377 +/*8378 + * kdb_io_init8379 + *8380 + * Initialize kernel debugger output environment.8381 + *8382 + * Parameters:8383 + * None.8384 + * Returns:8385 + * None.8386 + * Locking:8387 + * None.8388 + * Remarks:8389 + * Select a console device. Only use a VT console if the user specified8390 + * or defaulted console= /^tty[0-9]*$/8391 + *8392 + * FIXME: 2.6.22-rc1 initializes the serial console long after kdb starts,8393 + * so booting with 'console=tty console=ttyS0' does not create the console8394 + * entry for ttyS0 in time. For now simply assume that we have a working8395 + * console, until a better solution can be found.8396 + */8397 +8398 +void __init8399 +kdb_io_init(void)8400 +{8401 + /*8402 + * Select a console.8403 + */8404 + struct console *c = console_drivers;8405 + int vt_console = 0;8406 +8407 + while (c) {8408 +#if 0 /* FIXME: we don't register serial consoles in time */8409 + if ((c->flags & CON_CONSDEV) && !kdbcons)8410 + kdbcons = c;8411 +#else8412 + if (!kdbcons)8413 + kdbcons = c;8414 +#endif8415 + if ((c->flags & CON_ENABLED) &&8416 + strncmp(c->name, "tty", 3) == 0) {8417 + char *p = c->name + 3;8418 + while (isdigit(*p))8419 + ++p;8420 + if (*p == '/0')8421 + vt_console = 1;8422 + }8423 + c = c->next;8424 + }8425 +8426 + if (kdbcons == NULL) {8427 + printk(KERN_ERR "kdb: Initialization failed - no console. kdb is disabled./n");8428 + KDB_FLAG_SET(NO_CONSOLE);8429 + kdb_on = 0;8430 + }8431 + if (!vt_console)8432 + KDB_FLAG_SET(NO_VT_CONSOLE);8433 + kdb_input_flush();8434 + return;8435 +}所谓的vt_console就是tty0到tty9这些个.console_drivers是一个全局变量,各种各样的console drivers通过调用register_console()来向这个变量注册.比如drivers/serial目录下面各种串口的驱动都有调用这一函数,像8250串口驱动就是注册了一个serial8250_console.kdbcons也是全局变量.它将被赋予console_drivers的第一个console,比如tty0,比如ttyS0.kdb_input_flush()就是延时0.5s.2.kdb_inittab()12212 +/*12213 + * kdb_inittab12214 + *12215 + * This function is called by the kdb_init function to initialize12216 + * the kdb command table. It must be called prior to any other12217 + * call to kdb_register_repeat.12218 + *12219 + * Inputs:12220 + * None.12221 + * Outputs:12222 + * None.12223 + * Returns:12224 + * None.12225 + * Locking:12226 + * None.12227 + * Remarks:12228 + *12229 + */12230 +12231 +static void __init12232 +kdb_inittab(void)12233 +{12234 + int i;12235 + kdbtab_t *kp;12236 +12237 + for(i=0, kp=kdb_commands; i < kdb_max_commands; i++,kp++) {12238 + kp->cmd_name = NULL;12239 + }12240 +12241 + kdb_register_repeat("md", kdb_md, "<vaddr>", "Display Memory Contents, also mdWcN, e.g. md8c1", 1, KDB_REPEAT_NO_ARGS);12242 + kdb_register_repeat("mdr", kdb_md, "<vaddr> <bytes>", "Display Raw Memory", 0, KDB_REPEAT_NO_ARGS);12243 + kdb_register_repeat("mdp", kdb_md, "<paddr> <bytes>", "Display Physical Memory", 0, KDB_REPEAT_NO_ARGS);12244 + kdb_register_repeat("mds", kdb_md, "<vaddr>", "Display Memory Symbolically", 0, KDB_REPEAT_NO_ARGS);12245 + kdb_register_repeat("mm", kdb_mm, "<vaddr> <contents>", "Modify Memory Contents", 0, KDB_REPEAT_NO_ARGS);12246 + kdb_register_repeat("id", kdb_id, "<vaddr>", "Display Instructions", 1, KDB_REPEAT_NO_ARGS);12247 + kdb_register_repeat("go", kdb_go, "[<vaddr>]", "Continue Execution", 1, KDB_REPEAT_NONE);12248 + kdb_register_repeat("rd", kdb_rd, "", "Display Registers", 1, KDB_REPEAT_NONE);12249 + kdb_register_repeat("rm", kdb_rm, "<reg> <contents>", "Modify Registers", 0, KDB_REPEAT_NONE);12250 + kdb_register_repeat("ef", kdb_ef, "<vaddr>", "Display exception frame", 0, KDB_REPEAT_NONE);12251 + kdb_register_repeat("bt", kdb_bt, "[<vaddr>]", "Stack traceback", 1, KDB_REPEAT_NONE);12252 + kdb_register_repeat("btp", kdb_bt, "<pid>", "Display stack for process <pid>", 0, KDB_REPEAT_NONE);12253 + kdb_register_repeat("bta", kdb_bt, "[DRSTCZEUIMA]", "Display stack all processes", 0, KDB_REPEAT_NONE);12254 + kdb_register_repeat("btc", kdb_bt, "", "Backtrace current process on each cpu", 0, KDB_REPEAT_NONE);12255 + kdb_register_repeat("btt", kdb_bt, "<vaddr>", "Backtrace process given its struct task address", 0, KDB_REPEAT_NONE);12256 + kdb_register_repeat("ll", kdb_ll, "<first-element> <linkoffset> <cmd>", "Execute cmd for each element in linked list", 0, KDB_REPEAT_NONE);12257 + kdb_register_repeat("env", kdb_env, "", "Show environment variables", 0, KDB_REPEAT_NONE);12258 + kdb_register_repeat("set", kdb_set, "", "Set environment variables", 0, KDB_REPEAT_NONE);12259 + kdb_register_repeat("help", kdb_help, "", "Display Help Message", 1, KDB_REPEAT_NONE);12260 + kdb_register_repeat("?", kdb_help, "", "Display Help Message", 0, KDB_REPEAT_NONE);12261 + kdb_register_repeat("cpu", kdb_cpu, "<cpunum>","Switch to new cpu", 0, KDB_REPEAT_NONE);12262 + kdb_register_repeat("ps", kdb_ps, "", "Display active task list", 0, KDB_REPEAT_NONE);12263 + kdb_register_repeat("pid", kdb_pid, "<pidnum>", "Switch to another task", 0, KDB_REPEAT_NONE);12264 + kdb_register_repeat("reboot", kdb_reboot, "", "Reboot the machine immediately", 0, KDB_REPEAT_NONE);12265 +#if defined(CONFIG_MODULES)12266 + kdb_register_repeat("lsmod", kdb_lsmod, "", "List loaded kernel modules", 0, KDB_REPEAT_NONE);12267 +#endif12268 +#if defined(CONFIG_MAGIC_SYSRQ)12269 + kdb_register_repeat("sr", kdb_sr, "<key>", "Magic SysRq key", 0, KDB_REPEAT_NONE);12270 +#endif12271 + kdb_register_repeat("dmesg", kdb_dmesg, "[lines]", "Display syslog buffer", 0, KDB_REPEAT_NONE);12272 + kdb_register_repeat("defcmd", kdb_defcmd, "name /"usage/" /"help/"", "Define a set of commands, down to endefcmd", 0, KDB_REPEAT_NONE);12273 + kdb_register_repeat("kill", kdb_kill, "<-signal> <pid>", "Send a signal to a process", 0, KDB_REPEAT_NONE);12274 + kdb_register_repeat("summary", kdb_summary, "", "Summarize the system", 4, KDB_REPEAT_NONE);12275 + kdb_register_repeat("per_cpu", kdb_per_cpu, "", "Display per_cpu variables", 3, KDB_REPEAT_NONE);12276 +}这个函数究竟在干嘛呢?就看见它在不停的调用kdb_register_repeat()函数.如果你使用过kdb,熟悉它的那些个命令,你就会发现,每个命令都在这个函数中出现过,很显然,kdb_register_repeat()不干别的,就为了注册每个命令,kdb_register_repeat()的第二个参数就是与该命令相关的函数,比如dmesg,对应kdb_dmesg,这就意味着一旦你执行dmesg命令,背后真正会被调用的函数就是kdb_dmesg().所有的命令最终被加入到一个全局变量kdb_commands中,这相当于一张表.它默认的大小是50,换言之,你可以定义50个命令,当然你也可以定义更多,有一个变量记录了最大的命令数,这就是kdb_max_commands.8540 + /*8541 + * kdb_commands describes the available commands.8542 + */8543 +static kdbtab_t *kdb_commands;8544 +static int kdb_max_commands;3.kdb_id_init()7724 +/*7725 + * kdb_disinit7726 + *7727 + * Initialize the disassembly information structure7728 + * for the GNU disassembler.7729 + *7730 + * Parameters:7731 + * None.7732 + * Outputs:7733 + * None.7734 + * Returns:7735 + * Zero for success, a kdb diagnostic if failure.7736 + * Locking:7737 + * None.7738 + * Remarks:7739 + */7740 +7741 +void __init7742 +kdb_id_init(void)7743 +{7744 + kdb_di.stream = NULL;7745 + kdb_di.application_data = NULL;7746 + kdb_di.symbols = NULL;7747 + kdb_di.num_symbols = 0;7748 + kdb_di.flags = 0;7749 + kdb_di.private_data = NULL;7750 + kdb_di.buffer = NULL;7751 + kdb_di.buffer_vma = 0;7752 + kdb_di.buffer_length = 0;7753 + kdb_di.bytes_per_line = 0;7754 + kdb_di.bytes_per_chunk = 0;7755 + kdb_di.insn_info_valid = 0;7756 + kdb_di.branch_delay_insns = 0;7757 + kdb_di.data_size = 0;7758 + kdb_di.insn_type = 0;7759 + kdb_di.target = 0;7760 + kdb_di.target2 = 0;7761 +}最没技术含量的一个函数.这其中kdb_di是定义在kdb/kdb_id.c中的一个结构体变量:7544 +disassemble_info kdb_di;一个全局变量.4.kdba_init()kdb后面带个a,a表示arch,说明这个函数是体系结构相关的.换言之,那三个patch中的后两个都会包含这个函数.i386需要它自己的这个函数,x86_64需要属于它的同一个函数.我们来看i386的.8604 +/*8605 + * kdba_init8606 + *8607 + * Architecture specific initialization.8608 + *8609 + * Parameters:8610 + * None.8611 + * Returns:8612 + * None.8613 + * Locking:8614 + * None.8615 + * Remarks:8616 + * None.8617 + */8618 +8619 +void __init8620 +kdba_init(void)8621 +{8622 + kdba_arch_init(); /* Need to register KDBENTER_VECTOR early */8623 + kdb_register("pt_regs", kdba_pt_regs, "address", "Format struct pt_regs", 0);8624 + kdb_register("stackdepth", kdba_stackdepth, "[percentage]", "Print processes using >= stack percentage", 0);8625 +8626 + return;8627 +}这里有包含两个函数.kdba_arch_init().8592 +static int __init8593 +kdba_arch_init(void)8594 +{8595 +#ifdef CONFIG_SMP8596 + set_intr_gate(KDB_VECTOR, kdb_interrupt);8597 +#endif8598 + set_intr_gate(KDBENTER_VECTOR, kdb_call);8599 + return 0;8600 +}原来是设置两个中断门.这里涉及了两个中断向量.KDB_VECTOR和KDBENTER_VECTOR.与之想绑定的分别是两个函数,kdb_interrupt和kdb_call.而kdb_register()其实就是kdb_register_repeat()的另一版本.等于说这里又注册了pt_regs和stackdepth这两个命令.12139 +/*12140 + * kdb_register12141 + *12142 + * Compatibility register function for commands that do not need to12143 + * specify a repeat state. Equivalent to kdb_register_repeat with12144 + * KDB_REPEAT_NONE.12145 + *12146 + * Inputs:12147 + * cmd Command name12148 + * func Function to execute the command12149 + * usage A simple usage string showing arguments12150 + * help A simple help string describing command12151 + * Outputs:12152 + * None.12153 + * Returns:12154 + * zero for success, one if a duplicate command.12155 + * Locking:12156 + * none.12157 + * Remarks:12158 + *12159 + */12160 +12161 +int12162 +kdb_register(char *cmd,12163 + kdb_func_t func,12164 + char *usage,12165 + char *help,12166 + short minlen)12167 +{12168 + return kdb_register_repeat(cmd, func, usage, help, minlen, KDB_REPEAT_NONE);12169 +}Linux内核中像这种注册或者说登记的函数到处都是,从本质上来说,无非就是有一张表,然后同一类型的变量或者数据或者函数或者指针都登记到这张表里去,便于统一管理.这种思想应用到现实中来,就好比北京市公安局打着迎奥运的名义,要求外来人口去办暂住证一样,都办了暂住证,为奥运操心的公仆们就能很好的管理我们这些刁民,贱民和草民了.5.kdb_cmd_init()12278 +/*12279 + * kdb_cmd_init12280 + *12281 + * This function is called by the kdb_init function to execute any12282 + * commands defined in kdb_cmds.12283 + *12284 + * Inputs:12285 + * Commands in *kdb_cmds[];12286 + * Outputs:12287 + * None.12288 + * Returns:12289 + * None.12290 + * Locking:12291 + * None.12292 + * Remarks:12293 + *12294 + */12295 +12296 +static void __init12297 +kdb_cmd_init(void)12298 +{12299 + int i, diag;12300 + for (i = 0; kdb_cmds[i]; ++i) {12301 + if (!defcmd_in_progress)12302 + kdb_printf("kdb_cmd[%d]: %s", i, kdb_cmds[i]);12303 + diag = kdb_parse(kdb_cmds[i]);12304 + if (diag)12305 + kdb_printf("command failed, kdb diag %d/n", diag);12306 + }12307 + if (defcmd_in_progress) {12308 + kdb_printf("Incomplete 'defcmd' set, forcing endefcmd/n");12309 + kdb_parse("endefcmd");12310 + }12311 +}这又是初始化什么呢?kdb_cmds是一个数组.所有的命令都被添加到这里.35 extern char *kdb_cmds[]; char __initdata *kdb_cmds[]它有许许多多个成员,每一个成员表示一条命令,当然你也可以说每一个成员表示一个字符串.defcmd_in_progress是一个全局变量,默认当然是0.而kdb_parse()将解析这个命令,如果这个命令能够在kdb_commands这张表里面找到,那么与这个命令相关的函数将会执行.比如说你执行dmesg命令,那么kdb_dmesg()函数就会被调用.初始化基本上要好了,咱们调用kdb()函数.这个函数是kdb中最核心的函数之一.这个函数我们以后会有大把的机会和它打交道.这里由于传递进来的参数比较特别,实际上它不会做什么事情.紧接着, atomic_notifier_chain_register是注册一个notifier chain.kdb_block.notifier chain是一种通知机制,我们不去深究它,事实上要深究也没问题,网上关于notifier chain的文章也不少.这里我们只想说,我们通过这行代码实际上绑定了一个kdb_panic()函数,在系统崩溃的时候,这个函数会被调用,从而进入kdb.kdb_panic以及kdb_block的定义都在下面:12313 +/*12314 + * kdb_panic12315 + *12316 + * Invoked via the panic_notifier_list.12317 + *12318 + * Inputs:12319 + * None.12320 + * Outputs:12321 + * None.12322 + * Returns:12323 + * Zero.12324 + * Locking:12325 + * None.12326 + * Remarks:12327 + * When this function is called from panic(), the other cpus have already12328 + * been stopped.12329 + *12330 + */12331 +12332 +static int12333 +kdb_panic(struct notifier_block *self, unsigned long command, void *ptr)12334 +{12335 + KDB_FLAG_SET(CATASTROPHIC); /* kernel state is dubious now */12336 + KDB_ENTER();12337 + return 0;12338 +}12339 +12340 +static struct notifier_block kdb_block = { kdb_panic, NULL, 0 };以后我们会看到KDB_ENTER()这个宏将引领我们进入kdb.只是此刻我想,kernel崩溃了,可以进入kdb,看代码的我崩溃了,找谁去?再然后是register_cpu_notifier,这又是注册啥呢?暂时不得而知.接下来,kdbjmpbuf,很明显,这是一个setjmp buffer,用于异常处理.setjmp和longjmp通常组合起来用,前者用于保存程序的运行时的堆栈环境,后者则用来恢复先前保存的程序堆栈环境.这二者一起使用就能提供传说中那种在程序中实现非本地跳转的机制(non-local goto).关于setjmp和longjmp,网上相关的文章那是比阿娇的艳照还要多,这里就不多说了.(当然,阿娇的照片之所以少,主要是警察叔叔有功!)与kdbjmpbuf相对应的setjmp函数我们以后会看到,叫做kdba_setjmp(),与之相对应的longjmp函数则叫做kdba_longjmp.简而言之,我们会先调用kdba_setjmp()来初始化kdbjmpbuf,或者说将CPU中大部分影响到程序执行的寄存器存入kdbjmpbuf,而日后等我们使用kdba_longjmp()函数之后,能够使程序再次跳转会kdba_setjmp()的位置.需要多说一句,从这里的申请内存时使用的NR_CPUS我们知道,这个buffer实际上是为每个CPU都准备了一个.最后,kdb_initial_cpu设置为-1.kdb_wait_for_cpus_secs也赋了一个值,如果你是单cpu那么这个变量就被设置为10.你可以用kdb察看一下.比如下面我的机器里看到的那样:kdb> md kdb_wait_for_cpus_secs0xffffffff8062db18 000000000000000a 0000000000000000 ................0xffffffff8062db28 0000000000000000 0000000000000000 ................0xffffffff8062db38-0xffffffff8062db87 zero suppressed0xffffffff8062db88 0000000000000000 0000000000000000 ................0a当然就是10.这样,kdb的初始化就算是完成了.Linux中有些模块,你看明白它怎么初始化的你基本上就能明白它是怎么工作了,比如usb-storage,以及usb hub driver,但有些模块就没有这么简单了,就比如uhci/ehci,就比如kdb.初始化完了之后故事才刚刚拉开帷幕,如果拿近期百家讲坛热播的纪连海老师讲的李连英的故事对比,那么现在也就相当于李连英公公刚刚进宫,刚刚开始他那伟大的太监生涯.像usb-storage那样的模块,你可以很清楚它的结构,从哪里开始到哪里结束,整个就是一条直线.而kdb就不一样了,它初始化完成了之后,就将准备应对多种情况了,比如你进入kdb,这就有多种情形,你可以直接调用相关的宏进入kdb,也可以设置断点来进入kdb,你可以按pause键进入kdb,也可以在serial console上按ctrl-a进入kdb,还可能是系统崩溃了自动进入kdb.总而言之有诸多的可能,所以就要有相应的代码来应付.下面我们首先就先来看一下,从串行终端上按了ctrl-a之后,为什么就可以进入kdb.很明显,这里牵涉到了serial console的驱动,更准确地说其实是Intel 8250串口芯片驱动.虽然串口芯片很多,但是Intel 8250无疑是最有名的,大多数服务器上的串口都是8250芯片.kdb-v4.4-2.6.22-common-1这个patch中说了,以下四个文件是作了修改的.17 drivers/serial/8250.c | 5318 drivers/serial/8250_early.c | 3419 drivers/serial/sn_console.c | 7325 include/linux/console.h | 5很长一段时间我一直困惑,计算机怎么知道我按了”control-a”呢,后来才明白,键盘上的control-a实际上对应的是ASCII码中的001.(control-b对应002,control-c对应003,…另外,control-@对应000)所以从键盘驱动来说,它就把这个组合键当作一个字符来处理.而kdb的patch在drivers/serial/8250.c中加了这么一段:3295 Index: linux/drivers/serial/8250.c3296 =================================================================== 3297 --- linux.orig/drivers/serial/8250.c3298 +++ linux/drivers/serial/8250.c3299 @@ -45,6 +45,19 @@3300 #include <asm/irq.h>33013302 #include "8250.h"3303 +#include <linux/kdb.h>3304 +#ifdef CONFIG_KDB3305 +/*3306 + * kdb_serial_line records the serial line number of the first serial console.3307 + * NOTE: The kernel ignores characters on the serial line unless a user space3308 + * program has opened the line first. To enter kdb before user space has opened3309 + * the serial line, you can use the 'kdb=early' flag to lilo and set the3310 + * appropriate breakpoints.3311 + */3312 +3313 +static int kdb_serial_line = -1;3314 +static const char *kdb_serial_ptr = kdb_serial_str;3315 +#endif /* CONFIG_KDB */33163317 /*3318 * Configuration:3319 @@ -1287,6 +1300,20 @@ receive_chars(struct uart_8250_port *up,33203321 do {3322 ch = serial_inp(up, UART_RX);3323 +#ifdef CONFIG_KDB3324 + if ((up->port.line == kdb_serial_line) && kdb_on == 1) {3325 + if (ch == *kdb_serial_ptr) {3326 + if (!(*++kdb_serial_ptr)) {3327 + atomic_inc(&kdb_8250);3328 + kdb(KDB_REASON_KEYBOARD, 0, get_irq_regs());3329 + atomic_dec(&kdb_8250);3330 + kdb_serial_ptr = kdb_serial_str;3331 + break;3332 + }3333 + } else3334 + kdb_serial_ptr = kdb_serial_str;3335 + }3336 +#endif /* CONFIG_KDB */3337 flag = TTY_NORMAL;3338 up->port.icount.rx++;3339这里的kdb_serial_str其实就是control-a,或者说用ascii码的形式表示为”/001”.这里的receive_char很明显,就是串行终端接收字符时调用的函数.ch就是接收到的字符,如果它是control-a,那么3328添加的代码就会执行,换言之,kdb()函数会被调用.不过这里我们需要注意的是,当我还是青春期的时候,当我还在看琼瑶剧的时候,当我还迷恋快乐大本营的时候,人们要从串行终端进入kdb得按control-a键,但后来人们发现control-a进入不了kdb了,要进入kdb得按另外的键,这就是escape键后接KDB.关于这一点,原因是kdb_serial_str这个字符串经过了修改,曾几何时,它是被定义为/001,但现在你会发现,这个字符串被定义为”/eKDB”,”/e”实际上对应你敲击的键盘就是escape键.关于这个字符串的定义,我们可以从patch里面找到:8639 +/*8640 + * kdb_serial_str is the sequence that the user must enter on a serial8641 + * console to invoke kdb. It can be a single character such as "/001"8642 + * (control-A) or multiple characters such as "/eKDB". NOTE: All except the8643 + * last character are passed through to the application reading from the serial8644 + * console.8645 + *8646 + * I tried to make the sequence a CONFIG_ option but most of CML1 cannot cope8647 + * with '/' in strings. CML2 would have been able to do it but we lost CML2.8648 + * KAO.8649 + */8650 +const char kdb_serial_str[] = "/eKDB";8651 +EXPORT_SYMBOL(kdb_serial_str);所以结合上面的代码来看,kdb_serial_str表示一个const的字符串,而kdb_serial_ptr则是一个char型指针,指针开始指向kdb_serial_str,然后不停的游荡,每次串行终端上有输入,换言之,ch有值,就拿它和kdb_serial_ptr所指向的字符相比较,如果相同就令kdb_serial_ptr指向下一个字符,然后接着如果你继续输入,就继续比较,直到比较完了以后发现,你输入的恰恰就是<escape>KDB,那么调用kdb(),从而进入kdb.下面是效果图:(在串行终端上K和B都没有回显出来,只有D回显了.)[root@localhost ~]# DEntering kdb (current=0xffffffff805563a0, pid 0) due to Keyboard Entrykdb>不过像我这种习惯了按control-a进kdb的人,一般会把这里/eKDB手工改为/001.知道了serial console这边是如何进入kdb的,我们再来看本地键盘,在这里只要你按Pause键就可以进入kdb,这又是为什么呢?看人家的patch改了什么:3182 Index: linux/drivers/char/keyboard.c3183 =================================================================== 3184 --- linux.orig/drivers/char/keyboard.c3185 +++ linux/drivers/char/keyboard.c3186 @@ -40,6 +40,9 @@3187 #include <linux/sysrq.h>3188 #include <linux/input.h>3189 #include <linux/reboot.h>3190 +#ifdef CONFIG_KDB3191 +#include <linux/kdb.h>3192 +#endif /* CONFIG_KDB */31933194 extern void ctrl_alt_del(void);31953196 @@ -1138,6 +1141,13 @@ static void kbd_keycode(unsigned int key3197 if (keycode < BTN_MISC && printk_ratelimit())3198 printk(KERN_WARNING "keyboard.c: can't emulate rawmode for keycode %d/n", keycode);31993200 +#ifdef CONFIG_KDB3201 + if (down && !rep && keycode == KEY_PAUSE && kdb_on == 1) {3202 + kdb(KDB_REASON_KEYBOARD, 0, get_irq_regs());3203 + return;3204 + }3205 +#endif /* CONFIG_KDB */3206 +3207 #ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */3208 if (keycode == KEY_SYSRQ && (sysrq_down || (down == 1 && sysrq_alt))) {3209 if (!sysrq_down) {很显然,就是修改drivers/char/keyboard.c,这就是键盘驱动,我们看到会比较keycode与KEY_PAUSE,如果相同,就说明你输入的是pause键,于是3202行这里我们看到kdb()再一次被调用.这样我们就明白了为什么从串行终端按control-a以及从本地键盘按pause键会触发kdb了.但这些方式都太直接了,而且是你主动要进kdb,颇有一种纸上谈兵的味道.须知有的时候,进入kdb并不是你主观上期望的,往往是系统崩溃的时候自动进入的,这又是怎么回事儿呢?来看一个关键的函数,来自kernel/panic.c:60 NORET_TYPE void panic(const char * fmt, ...)61 {62 long i;63 static char buf[1024];64 va_list args;65 #if defined(CONFIG_S390)66 unsigned long caller = (unsigned long) __builtin_return_address(0);67 #endif6869 /*70 * It's possible to come here directly from a panic-assertion and not71 * have preempt disabled. Some functions called from here want72 * preempt to be disabled. No point enabling it later though...73 */74 preempt_disable();7576 bust_spinlocks(1);77 va_start(args, fmt);78 vsnprintf(buf, sizeof(buf), fmt, args);79 va_end(args);80 printk(KERN_EMERG "Kernel panic - not syncing: %s/n",buf);81 bust_spinlocks(0);8283 /*84 * If we have crashed and we have a crash kernel loaded let it handle85 * everything else.86 * Do we want to call this before we try to display a message?87 */88 crash_kexec(NULL);8990 #ifdef CONFIG_SMP91 /*92 * Note smp_send_stop is the usual smp shutdown function, which93 * unfortunately means it may not be hardened to work in a panic94 * situation.95 */96 smp_send_stop();97 #endif9899 atomic_notifier_call_chain(&panic_notifier_list, 0, buf);100101 if (!panic_blink)102 panic_blink = no_blink;103104 if (panic_timeout > 0) {105 /*106 * Delay timeout seconds before rebooting the machine.107 * We can't use the "normal" timers since we just panicked..108 */109 printk(KERN_EMERG "Rebooting in %d seconds..",panic_timeout);110 for (i = 0; i < panic_timeout*1000; ) {111 touch_nmi_watchdog();112 i += panic_blink(i);113 mdelay(1);114 i++;115 }116 /* This will not be a clean reboot, with everything117 * shutting down. But if there is a chance of118 * rebooting the system it will be rebooted.119 */120 emergency_restart();121 }122 #ifdef __sparc__。
kernel debug级别

kernel debug级别摘要:1.介绍kernel debug 级别的概念和作用2.kernel debug 级别的设置方法和技巧3.kernel debug 级别的使用案例和注意事项4.kernel debug 级别的优缺点分析正文:1.介绍kernel debug 级别的概念和作用在Linux 系统中,kernel debug 级别是一种用于控制内核调试信息输出的机制。
通过调整kernel debug 级别,可以控制内核打印哪些调试信息,从而帮助开发者调试内核代码和驱动程序。
kernel debug 级别分为以下几个级别:- KERN_DEBUG:输出内核调试信息。
- KERN_INFO:输出内核信息。
- KERN_WARNING:输出内核警告信息。
- KERN_ERROR:输出内核错误信息。
- KERN_CRIT:输出内核严重错误信息。
2.kernel debug 级别的设置方法和技巧要设置kernel debug 级别,可以使用以下方法:- 在内核配置文件(通常位于/usr/src/linux/.config)中修改相应的选项。
例如,要开启KERN_DEBUG级别,需要将CONFIG_KERNEL_DEBUG设置为Y。
- 在编译内核时,使用make 命令添加debug 选项。
例如,要开启KERN_DEBUG 级别,可以使用make KERN_DEBUG=y 命令。
- 在运行时,通过echo 命令修改/proc/sys/kernel/debug 字符串。
例如,要开启KERN_DEBUG 级别,可以使用echo "KERN_DEBUG" >/proc/sys/kernel/debug命令。
3.kernel debug 级别的使用案例和注意事项在使用kernel debug 级别时,需要注意以下几点:- 合理选择debug 级别。
过高的debug 级别可能导致系统性能下降,而过低的debug 级别可能无法提供足够的调试信息。
在vmware虚拟机上调试内核

安装系统(在创建一个新的虚拟机时一定要用IDE硬盘)1、在vmware上安装一个linux虚拟机(我用的软件版本:vmware 5.5, fedora 6)从Internet下载源代码1、linux-2.6.162、kgdb-2.6.16编译内核1、将kgdb-2.6.16补丁打到Linux内核源码里。
2、编译内核(make menuconfig, make, make modules_install, make install)在内核配置菜单的Kernel hacking选项中选择kgdb调试项,例如:[*] KGDB: kernel debugging with remote gdb[*] KGDB: Console messages through gdbMethod for KGDB communication (KGDB: On generic serial port (8250)) --->< > KGDB: On ethernet[*] Simple selection of KGDB serial port(115200) Debug serial port baud rate(0) Serial port number for KGDB3、修改grub.conf例如:title Fedora Core (2.6.16)root (hd0,0)kernel /vmlinuz-2.6.16 ro root=/dev/VolGroup00/LogVol00initrd /initrd-2.6.16_debug.img改为:title Fedora Core (2.6.16)root (hd0,0)kernel /vmlinuz-2.6.16 ro root=/dev/VolGroup00/LogVol00 kgdb8250=0,115200initrd /initrd-2.6.16_debug.img4、请检验新编译的内核是否可用。
kgdb内核调试原理

kgdb内核调试原理
kgdb内核调试原理基于以下步骤:
1. kgdb在Linux内核中添加一个调试stub,这是内核中的一小段代码,充当运行gdb的开发机和目标机内核之间的媒介。
2. gdb和调试stub之间通过gdb串行协议进行通信,这是一种基于消息的ASCII码协议,包含了各种调试命令。
3. 当设置断点时,kgdb将断点的指令替换为一条trap指令。
当执行到断点时,控制权就转移到调试stub中去。
4. 调试stub的任务是使用远程串行通信协议将当前内核环境传送给gdb,然后从gdb处接收命令。
5. kgdb本身运行在内核空间,可以访问内核空间或者用户空间,获取相应的被调试程序的数据信息。
6. 在连接通信过程中,target运行配置了kgdb的内核,host运行gdb,中间使用RSP协议进行通信。
gdb向kgdb发送RSP命令包,kgdb解析命令包,从而实现target的内核调试。
以上是kgdb内核调试的基本原理,如需更深入了解其工作原理,建议查阅相关的技术文档或咨询专业技术人员。
内核卡死调试方法-概述说明以及解释
内核卡死调试方法-概述说明以及解释1.引言1.1 概述内核卡死是指操作系统的内核无法继续执行下去,在特定的情景下,系统停止响应并无法正常运行。
这可能会导致系统崩溃、程序无法执行或者无法正常操作。
内核卡死调试方法是帮助我们解决这类问题的一种重要工具。
在本文中,我们将讨论内核卡死调试的方法和技巧,帮助读者更好地定位和解决内核卡死的问题。
首先,我们将介绍内核卡死的原因,包括硬件故障、软件错误和不兼容性等。
然后,我们将探讨内核卡死的常见表现,例如系统停止响应、屏幕冻结和错误信息等。
接着,我们将强调内核卡死调试的重要性。
内核卡死不仅会影响系统的稳定性和性能,还可能导致数据丢失和未完成的任务。
因此,在及时发现和解决内核卡死问题的同时,可以提高系统的可靠性和用户的体验。
最后,我们将总结内核卡死调试的方法和技巧。
这些方法包括使用系统日志和调试工具来分析和追踪系统状态,以及查找和修复可能的错误。
我们还将介绍一些常用的内核卡死调试工具和技术,如调试模式和堆栈跟踪。
通过本文的阅读,读者将能够更好地理解内核卡死调试的重要性和方法,使其能够快速解决内核卡死的问题,并提高系统的稳定性和可靠性。
1.2 文章结构文章结构是指对文章主要内容进行整体的组织和安排,以便读者能够清晰地了解文章的层次结构和脉络。
在讲述内核卡死调试方法的长文中,文章结构应该包括以下几个主要部分:1. 引言:在引言部分,我们将对内核卡死调试方法的重要性和必要性进行概述。
解释为什么需要调试内核卡死问题,以及通过本文能够掌握哪些相关的调试方法。
2. 内核卡死的原因:该部分将详细介绍导致内核卡死的各种原因,如硬件故障、软件错误、驱动冲突等。
通过对这些原因的分析,读者能够更好地理解内核卡死的根本问题。
3. 内核卡死的常见表现:在这一部分,我们将列举内核卡死的一些常见表现,如系统崩溃、黑屏、任务无响应等。
通过对这些表现的描述,读者能够判断出系统是否卡死,并能更好地定位具体问题。
windbg 内核调试原理(一)
windbg 内核调试原理(一)windbg 内核调试什么是 windbg 内核调试?•windbg 是一款强大的调试工具,可以用来调试 Windows 操作系统及其应用程序。
•内核调试是 windbg 的一个功能,用于调试操作系统内核,可以帮助开发人员定位和解决系统崩溃、死锁、性能问题等。
内核调试原理1.内核调试利用 Windows 的调试接口和调试驱动程序实现。
2.它通过在目标系统中安装一个调试驱动程序,与 windbg 配合使用,来进行调试。
3.此外,还需要使用串行线缆或网络连接将目标系统和调试主机连接起来。
准备工作•在进行内核调试之前,需要安装 windbg 和调试符号文件。
•调试符号文件包含了操作系统的调试信息,是进行调试的必要文件。
配置目标系统1.在目标系统中配置调试选项:•打开“控制面板”,找到“系统和安全”。
•点击“系统”,然后选择“高级系统设置”。
•在“高级” 选项卡下,点击“设置” 按钮。
•在“启动和故障恢复” 对话框中,点击“设置”。
•将“调试信息” 设置为“完全内存转储”。
2.进行启动选项配置:•打开“命令提示符”,输入以下命令:bcdedit /debug onbcdedit /dbgsettings serial debugport:1 baudrate:115200•这样将启用调试,并配置串行端口为 COM1,波特率为 115200。
配置调试主机1.在调试主机上打开 windbg:•首先,确保已经安装了 windbg。
•然后,在开始菜单中找到 windbg,并打开它。
2.配置串口调试选项:•点击“文件” -> “选项” -> “调试选项”。
•在“调试选项” 对话框中,选择“串行” 选项卡。
•选择正确的串行端口和波特率(与目标系统配置一致)。
连接目标系统和调试主机•使用串行线缆或网络连接将目标系统和调试主机连接起来。
开始内核调试•在 windbg 中,点击“调试” -> “启动调试”。
arm调试内核寄存器的方法
arm调试内核寄存器的方法使用ARM调试内核寄存器的方法引言:在软件开发和调试过程中,经常需要查看和修改CPU的寄存器值。
而对于ARM架构的处理器,了解如何调试内核寄存器值是非常重要的。
本文将介绍一些常用的方法和工具,帮助开发者在ARM平台上进行内核寄存器的调试。
一、使用GDB调试器GDB是一款功能强大的开源调试器,可以用于调试多种编程语言和平台。
在ARM架构上,可以使用GDB调试器来查看和修改内核寄存器的值。
1. 准备工作需要安装GDB工具链,并将其添加到系统的环境变量中。
然后,在编译内核时,需要在配置文件中启用GDB调试支持选项,以生成可调试的内核映像。
2. 连接调试器将目标设备与主机通过调试接口(如JTAG、SWD)连接起来。
然后,在主机上运行GDB,并使用以下命令连接到目标设备:```$ gdb <kernel_image>```3. 设置断点在GDB中,可以使用`break`命令设置断点。
例如,要在内核函数`foo`的入口处设置断点,可以使用以下命令:```(gdb) break foo```4. 运行程序使用`run`命令启动目标设备上的程序,并让其执行到断点处。
当程序停止时,可以使用`info registers`命令查看所有寄存器的值。
5. 查看寄存器值使用`info registers`命令可以列出所有寄存器的值。
例如,要查看R0寄存器的值,可以使用以下命令:```(gdb) info registers r0```6. 修改寄存器值使用`set`命令可以修改寄存器的值。
例如,要将R0寄存器的值修改为0x1234,可以使用以下命令:```(gdb) set $r0 = 0x1234```二、使用JTAG调试器JTAG调试器是一种常用的硬件调试工具,也可以用于调试ARM架构的处理器。
它可以提供更低层次的访问权限,可以直接读写寄存器的值。
1. 准备工作需要将目标设备与JTAG调试器连接起来,并确保JTAG调试器的驱动程序已正确安装。
kernel debug级别
kernel debug级别摘要:1.引言2.kernel debug 级别简介3.kernel debug 级别的应用场景4.如何设置kernel debug 级别5.结论正文:kernel debug 级别是Linux 内核中的一种调试工具,它能够帮助开发者更深入地了解系统运行时的内部状态,以排查和解决各种问题。
在本文中,我们将详细介绍kernel debug 级别及其应用。
kernel debug 级别简介kernel debug 级别是一种用于控制内核调试信息输出的机制。
它共分为10 个级别,从0 到9,其中0 表示关闭所有调试信息,10 表示输出最详细的调试信息。
通过调整kernel debug 级别,开发者可以根据实际需求获取不同程度的信息,以提高问题排查的效率。
kernel debug 级别的应用场景kernel debug 级别广泛应用于以下场景:1.内核开发:在编写和调试内核代码时,开发者可以通过调整kernel debug 级别来获取详细的调试信息,以帮助理解代码的执行过程。
2.系统性能分析:通过调整kernel debug 级别,开发者可以获取到系统运行时的性能数据,从而发现性能瓶颈,优化系统性能。
3.故障排查:在遇到系统问题时,可以通过提高kernel debug 级别,获取更多的调试信息,从而快速定位问题原因。
如何设置kernel debug 级别设置kernel debug 级别的方法有多种,以下列举了两种常用方法:1.使用sysctl 命令:通过sysctl 命令可以动态地调整kernel debug 级别。
例如,将kernel debug 级别设置为5,可以执行以下命令:```sudo sysctl -w kernel.printk=5```2.修改/proc/sys/kernel/printk文件:该文件保存了当前kernel debug 级别的设置。
可以通过编辑该文件来修改kernel debug 级别。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
实验十一内核调试
【实验目的】
由于在驱动开发中经常看到内核崩溃的问题,最常见的就是OOPS错误,本实验要求学员掌握这种调试方法。
【实验环境】
1、Ubuntu10.10发行版
2、u-boot-2010.03
3、FS2410平台
4、交叉编译器arm-none-linux-gnueabi-gcc-4.3.2
【实验步骤】
1、通过OOPS信息中PC寄存器的值可以知道出错指令的地址,通过栈回朔信息可以
知道出错时的函数调用的关系,根据这两点可以很快定位错误。
2、修改driver/video/s3c2410fb.c,在s3c2410fb_probe函数的开头这条语句u32lcdcon1
下增加下面两条语句:
int*ptest=NULL;
*ptest=0x1234;
3、编译内核下载到开发板上,内核启动会出现如下信息:
Unable to handle kernel NULL pointer dereference at virtual address00000000
pgd=c0004000
[00000000]*pgd=00000000
Internal error:Oops:805[#1]
Modules linked in:
CPU:0Not tainted(2.6.22.6#18)
PC is at s3c2410fb_probe+0x18/0x560
LR is at platform_drv_probe+0x20/0x24
pc:[<c001abc4>]lr:[<c0185f28>]psr:a0000013
sp:c042fe64ip:c042fea0fp:c042fe9c
r10:00000000r9:c0025864r8:c03892ec
r7:00000000r6:c0353358r5:00000000r4:c032c560
r3:00001234r2:00000001r1:c047bd84r0:c032c558
4、Unable to handle kernel NULL pointer dereference at virtual address00000000
可以看出使用了空指针。
找出函数调用关系:PC is at s3c2410fb_probe+0x18/0x560
,表示出错指令为s3c2410fb_probe函数中偏移为0X18的指令。
pc:[<c001abc4>]表示出错指令的地址为c001abc4.
5、结合内核源代码和反汇编代码定位问题。
arm-none-linux-gnueabi-objdump-D vmlinux>vmlinux.dis
出错地址c001abc4附近的部分汇编代码如下:
c001abac<s3c2410fb_probe>:
c001abac:e1a0c00d mov ip,sp
c001abb0:e92ddff0stmdb sp!,{r4,r5,r6,r7,r8,r9,sl,fp,ip,lr,pc}
c001abb4:e24cb004sub fp,ip,#4;0x4
c001abb8:e24dd010sub sp,sp,#16;0x10
c001abbc:e59f34e0ldr r3,[pc,#1248];c001b0a4<.init+0x12d04>
c001abc0:e3a07000mov r7,#0;0x0
c001abc4:e5873000str r3,[r7]
c001abc8:e59030fc ldr r3,[r0,#252]
对应C代码如下:
struct s3c2410fb_info*info;
struct fb_info*fbinfo;
struct s3c2410fb_hw*mregs;
int ret;
int irq;
int i;
u32lcdcon1;
int*ptest=NULL;
*ptest=0x1234;
6、对于大多数情况,从反汇编代码定位到C代码并不会如此容易,需要有较强的阅读汇编代码的能力。
7、另外一种方法是通过addr2line去定位
arm-none-linux-gnueabi-addr2line0xc001abc4-e vmlinux-f
实验十二内存调试
【实验目的】
段错误和内存错误是C语言编程经常遇到的问题,使用memwatch是由johan lindh编写,是一个开发源代码C语言内存错误检测工具。
能检测双重释放,错误释放,没有释放内存,溢出等等情况。
【实验环境】
1、Ubuntu10.10发行版
2、u-boot-2010.03
3、FS2410平台
4、交叉编译器arm-none-linux-gnueabi-gcc-4.3.2
【实验步骤】
1、添加测试代码memtest.c
#include<stdlib.h>
#include<stdio.h>
#include"memwatch.h"
int main(int argc,char**argv)
{
char*ptr1;
char*ptr2;
ptr1=malloc(512);
ptr2=malloc(512);
ptr1[512]="A";
ptr2=ptr1;
free(ptr2);
free(ptr1);
return0;
}
2、修改Makefile
Makefile文件为:
CC=arm-none-linux-gnueabi-gcc
memtest:
$(CC)-DMEMWATCH-DMW_STDIO memtest.c memwatch.c
5、编译并将可执行程序拷贝到开发板的跟文件系统执行,会生成一个记录文件memwatch.log,内容如下:
=============MEMWATCH2.71Copyright(C)1992-1999Johan Lindh=========
Started at Thu Jan100:08:331970
Modes:__STDC__32-bit mwDWORD==(unsigned long)
mwROUNDALLOC==4sizeof(mwData)==32mwDataSize==32
overflow:<3>memtest.c(12),512bytes alloc'd at<1>memtest.c(8)
double-free:<4>memtest.c(13),0x1a1b4was freed from memtest.c(12)
Stopped at Thu Jan100:08:331970
unfreed:<2>test.c(9),512bytes at0x1a3e4{FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE FE................}
Memory usage statistics(global):
N)umber of allocations made:2
L)argest memory usage:1024
T)otal of all alloc()calls:1024
U)nfreed bytes totals:512
//overflow:<3>memtest.c(12)缓冲区溢出,当程序执行到第15行free(ptr2)才检测到的;512bytes alloc'd at<1>memtest.c(9)
//表示出错缓冲区的大小为512字节,是在memtest.c的第9行分配的。
很容易发现代码的ptr1[512]="A"出现错误。
double-free:<4>memtest.c(13),0x1a7f4was freed from memtest.c(12)
//double-free:<4>memtest.c(16)是一个双重释放的错误,表示程序执行到16行的时候才检测到。
0x1a7f4was freed from memtest.c(12)
//表示首地址为0x1a7f4的内存在15行已经被释放。
Stopped at Wed Dec3119:00:381969
unfreed:<2>memtest.c(10),512bytes at0x1aa24{FE FE FE FE FE FE FE FE
//表示一块内存没有释放,表示这块内存是在memtest.c的第10行分配,大小为512字节,首地址为0x1aa24。
Memory usage statistics(global):
N)umber of allocations made:2
L)argest memory usage:1024
//程序结束时能够是使用的最大动态内存
T)otal of all alloc()calls:1024//总共分配的动态内存
U)nfreed bytes totals:512
//表示未释放的内存。