Documents/DebugWithoutPrintk
From: Keith Owens < kaos@ocs.com.au > Date: Fri, 11 Jan 2002 16:50:42 +1100
又到了我的另一版VIDEO_CHAR除錯修補程式的時間.
如果核心在程開機過程過早當掉(hangs)(在螢幕被啟始前) 那麼 printk 一點用處都沒有 因為你 看不到輸出. 有一個技巧針對影像呈現秀出開機過程 如此一來你可以 使問題局部化. 回報 "我的核心開機時當掉在第nnn行的xyz程序"比 "我的核心開機時當掉了"好得多.
這樣的想法是直接寫字元到顯示螢幕在開機時使用一個稱作VIDEO_CHAR巨集. 這個巨集需要一個字元的位置和一個單一字元值來顯示. 使用螢幕上不同位置給不同級別的碼且在一個位置使用不同字元表示水平到達何種狀態. 舉例來說, 底下的 修補程式, 串字串 EAC 表示 parse_options(), checksetup().
其他人 有修補程式 給 先期printk 輸出 且那些修補程式對你的需求而言可能比較好. VIDEO_CHAR漂亮的地方在於它不要求任何ix86註冊機碼(registers), 所以它可以在組合語言 中被使用而不需要 儲存/回復(save/restore) 麻煩.
底下是一般的修補程式, 除了VIDEO_CHAR的定義 是ix86 特定的. 如果這份修補程式變成主要核心的一部份 那麼 VIDEO_CHAR 需要被移到一個 特定架構的標頭檔(header). 如果任何除了ix86架構使用這個技巧, 請寫信到 < kaos@ocs.com.au > 附上自訂的VIDEO_CHAR定義.
你可以移植 VIDEO_CHAR 呼叫 任何你喜歡的地方, 到mem_init()呼叫. 之後 mem_init 完成它的作業且記憶體已被重新配置, VIDEO_CHAR 不可以寫到顯示記憶, 它會發生錯誤. 然而到那時 螢幕已經被初始化所以你可以使用printk.
Demonstration patch against 2.4.18-pre3. This only uses screen positions 0, 1, 2. If you want to drill down into lower level routines, just use screen positions 3 onwards. To activate the debugging, add
- . #define DEBUG_VIDEO_CHAR
to the start of init/main.c. If the machine is hanging then a zero delay is fine, if it is rebooting then you need a delay to note the characters in the top left hand corner of the screen before it reboots.
- . #define VIDEO_CHAR_DELAY_COUNT 100000000
A value around your clock speed gives a delay of approx. 1 second.
Index: 18-pre3.1/init/main.c --- 18-pre3.1/init/main.c Sat, 01 Dec 2001 11:29:21 +1100 kaos (linux-2.4/k/11_main.c 1.1.5.1.1.8.1.3.1.8 644) +++ 18-pre3.1(w)/init/main.c Fri, 11 Jan 2002 16:46:41 +1100 kaos (linux-2.4/k/11_main.c 1.1.5.1.1.8.1.3.1.8 644) @@ -79,6 +79,16 @@ extern int irda_device_init(void); #error Sorry, your GCC is too old. It builds incorrect kernels. #endif +#ifdef DEBUG_VIDEO_CHAR +#ifndef VIDEO_CHAR_DELAY_COUNT +#define VIDEO_CHAR_DELAY_COUNT 0 +#endif +/* ix86 specific */ +#define VIDEO_CHAR(c, v) { int i; *((volatile char *)(0xb8000 + 2*(c))) = (v); for (i = 0; i < VIDEO_CHAR_DELAY_COUNT; ++i) ; } +#else +#define VIDEO_CHAR(c, v) +#endif + extern char _stext, _etext; extern char *linux_banner; @@ -425,12 +435,14 @@ static void __init parse_options(char *l char *next,*quote; int args, envs; + VIDEO_CHAR(1, 'A'); if (!*line) return; args = 0; envs = 1; /* TERM is set to 'linux' by default */ next = line; while ((line = next) != NULL) { + VIDEO_CHAR(2, 'A'); quote = strchr(line,'"'); next = strchr(line, ' '); while (next != NULL && quote != NULL && quote < next) { @@ -443,9 +455,11 @@ static void __init parse_options(char *l next = strchr(next+1, ' '); } } + VIDEO_CHAR(2, 'B'); if (next != NULL) *next++ = 0; if (!strncmp(line,"init=",5)) { + VIDEO_CHAR(3, 'A'); line += 5; execute_command = line; /* In case LILO is going to boot us with default command line, @@ -456,8 +470,10 @@ static void __init parse_options(char *l args = 0; continue; } + VIDEO_CHAR(2, 'C'); if (checksetup(line)) continue; + VIDEO_CHAR(2, 'D'); /* * Then check if it's an environment variable or @@ -473,9 +489,12 @@ static void __init parse_options(char *l if (*line) argv_init[++args] = line; } + VIDEO_CHAR(2, 'E'); } + VIDEO_CHAR(1, 'B'); argv_init[args+1] = NULL; envp_init[envs+1] = NULL; + VIDEO_CHAR(1, 'C'); } @@ -548,16 +567,27 @@ asmlinkage void __init start_kernel(void * Interrupts are still disabled. Do necessary setups, then * enable them */ + VIDEO_CHAR(0, 'A'); lock_kernel(); + VIDEO_CHAR(0, 'B'); printk(linux_banner); + VIDEO_CHAR(0, 'C'); setup_arch(&command_line); + VIDEO_CHAR(0, 'D'); printk("Kernel command line: %s\n", saved_command_line); + VIDEO_CHAR(0, 'E'); parse_options(command_line); + VIDEO_CHAR(0, 'F'); trap_init(); + VIDEO_CHAR(0, 'G'); init_IRQ(); + VIDEO_CHAR(0, 'H'); sched_init(); + VIDEO_CHAR(0, 'I'); softirq_init(); + VIDEO_CHAR(0, 'J'); time_init(); + VIDEO_CHAR(0, 'K'); /* * HACK ALERT! This is early. We're enabling the console before @@ -565,8 +595,10 @@ asmlinkage void __init start_kernel(void * this. But we do want output early, in case something goes wrong. */ console_init(); + VIDEO_CHAR(0, 'L'); #ifdef CONFIG_MODULES init_modules(); + VIDEO_CHAR(0, 'M'); #endif if (prof_shift) { unsigned int size; @@ -577,10 +609,14 @@ asmlinkage void __init start_kernel(void size = prof_len * sizeof(unsigned int) + PAGE_SIZE-1; prof_buffer = (unsigned int *) alloc_bootmem(size); } + VIDEO_CHAR(0, 'N'); kmem_cache_init(); + VIDEO_CHAR(0, 'O'); sti(); + VIDEO_CHAR(0, 'P'); calibrate_delay(); + VIDEO_CHAR(0, 'Q'); #ifdef CONFIG_BLK_DEV_INITRD if (initrd_start && !initrd_below_start_ok && initrd_start < min_low_pfn << PAGE_SHIFT) { @@ -588,6 +624,7 @@ asmlinkage void __init start_kernel(void "disabling it.\n",initrd_start,min_low_pfn << PAGE_SHIFT); initrd_start = 0; } + VIDEO_CHAR(0, 'R'); #endif mem_init(); kmem_cache_sizes_init();