Troubleshooting: Difference between revisions

added source tags
[unchecked revision][unchecked revision]
m (→‎See Also: small fix, removed link)
(added source tags)
Line 9:
 
<!-- The following code example should be more generalized (e.g. exc_0d_handler, gpfExcHandler renamed to more meaningful names).-->
<source lang="asm">
<pre>
exc_0d_handler:
push gs
Line 37:
pop gs
iret
</presource>
 
Once you have implemented such a technique, it may be wise to test it, deliberately issuing 'faulty' instructions to see if the correct code is displayed. Having the 'double fault' exception (08) displayed somewhere else on the screen may also be a smart move.
Line 56:
In order to avoid recursive exceptions to occur endlessly, you can easily maintain a 'nested exceptions counter' that will be incremented every time you enter an exception handler and decremented just before you leave that handler. If the counter is above a certain threshold of a few units (3 should give interesting enough results), the kernel will abort trying to solve the exception and enter a 'panic' mode (red background, flashing LED, whatever).
 
<source lang="c">
<pre>
int nestexc = 0;
 
Line 70:
return;
}
</presource>
 
You need to know, of course, that some exceptions are not 'resumable'. If your kernel issued a division by zero, trying to return to the 'div' instruction will only trigger the exception one more time (yeah! altogether, now :). Such loops cannot be solved by the 'nestexc' counter
Line 79:
The stack content is still in memory. The [[EBP]] value of the erroring process is still in memory, and points to the start of the stack frame for the current function. Everything from this address and up was the current stack. Now, you can use the value in ebp as the source. Just use the following call:
 
<source lang="asm">
<pre>
stack_dump:
push ebp
Line 86:
pop ebp
ret ; note that this is not going to work, but it should be here for completion.
</presource>
 
and use <tt>void dump_hex(char *stack)</tt>.
Line 117:
Now, we can use <tt>objdump -drS bin/init.o</tt> to get a look at the disassembled output. Note that this step will work properly only if you had enabled debug information in those separated <tt>.o</tt> files...
 
<source lang="c">
<pre>
#ifdef __DEBUG__
kprint("kernel in debug-mode(%x) press [SHIFT+SPACE] to bypass anykey()\n",
Line 130:
DbMsk);
#endif
</presource>
 
Of course, as I picked up a random address, there's nothing wrong to see at +21f, but I guess you got my point. :)
Line 142:
Each time a function is called it gets the following head/tail: ([[GCC]] 3.3.2)
 
<source lang="asm">
<pre>
push ebp
mov ebp, esp
Line 148:
leave
ret
</presource>
 
On the place of the ... the rest of the code is filled in. Now, if you analyze the stack output, it looks something like:
Line 180:
If your function <tt>x()</tt> wreaks havoc only after 1000 calls it may not suffice to put a <tt>panic()</tt> statement inside the functions to see where the functions breaks. You may want to know which call is malignant. To do this, one might use a global or static var to count calls and panic() after an amount to see if it managed to crash. If not, you try twice that amount; if it does crash, you try bisection to find the amount.
 
<source lang="c">
<pre>
void scheduler_choose_task() {
static uint32 Z=0;
Line 189:
...
}
</presource>
...and then check how far does it go:
<source lang="c">
<pre>
Z++;
uint32 N = 1000;
Line 197:
if (in_critical_section()) return;
if (Z > N) panic(); //do we get here to panic() before a crash?
</presource>
However as complexity rises or multithreading is involved, it is less probable that a crash would be consistently occurring at the same point, after the same amount of calls every time. Then it would not be possible to find the number of the call to <tt>scheduler_choose_talk()</tt> that crashes it (because that number changes). Debugging needs some imagination; what if you knew, by tracing the program flow with <tt>print(__LINE__)</tt> that <tt>scheduler_choose_task()</tt> crashes only when a call to <tt>fun1()</tt> is in progress? You might use a global var <tt>uint32 dbg</tt> or an array (<tt>uint32 dbg[20]</tt>) of various <tt>dbg</tt> vars (which are used only in debugging code which is cleaned after the programmer ceases to debug) in a manner such as:
<source lang="c">
<pre>
void fun1() {
dbg[3] = 1;
Line 207:
dbg[3] = 0;
}
</presource>
...and:
<source lang="c">
<pre>
void scheduler_choose_task() {
// if (dbg[3]==1) panic(); //check here.. a panic saves the day from crashing!
Line 215:
if (dbg[3]==1) panic(); //check here.. it crashes
}
</source>
(Or mix it with a call count, <tt>Z++; if (Z>5 && dbg[3] == 1) panic()</tt>.)
 
Using the <tt>__LINE__</tt> aids tracing the program flow:
<source lang="c">
<pre>
print(__LINE__);
</presource>
See [[C preprocessor#Uses for debugging|Uses for debugging]] for more info.
 
Anonymous user