This article is a part of working with bug reports.
Here are few moments, which you should have in mind, when you are going to read call stacks.
First: one bug-report file may contain several bug-reports, and one bug-report may contain several call stacks (for example: one call stack for each thread). So, before actually reading – make sure, that you’re going to read the required (“the one”) call stack.
Usually, call stacks should be read from the bottom to the top, if you want to go in execution order (that is: from top to bottom, if you rather want to "unwind" from error's location): the first called routine is placed at their bottom. The next called routine is above it, on the next line. And so on. A current location (the last called routine) is located at the top of call stack (call stack’s first line). Sometimes current location indicate the location of the problem (but it is not always so – see searching bug's reason), and the rest of the call stack indicate execution path – i.e. the way, how we got to the current place.
First few routines (from bottom) usually are system’s ones or belongs to Delphi’s RTL and, therefore, aren’t very interesting. Often there are only partial information available for them – due to partial or missing debug information. Examples are things like 'BaseThreadInitThunk', 'Controls.TWinControl.WndProc' and so on. And sometimes the call stack itself has a limited depth (length/capacity) – i.e. it can not contain more than N lines or M characters. In that case the bottom part is simply cut.
On the other side: top part of the call stack may mention some RTL tool routines along with error’s location and your code. That depends on many factors. For example, it depends on stage (level of processing), at which exception was caught and the call stack was created. The examples of such tool routines are RTL’s exception processing helpers (like KiUserExceptionDispatcher), GetMem’s calls, etc, etc.
Some tools also may specially add an additional line to the very top of the call stack. This line represents the exact error’s location, if it is available (for example, it is an code instruction’s address for exceptions). If this is the case, then if call stack already contains this address as its first line, then nothing happens. But if there are lines with RTL’s tool routines at the top – then this address will be duplicated in the call stack. First time it’ll be listed in the very first line (as special added) and the second time it’ll be listed few lines below in the call stack (after RTL’s tool routines).
The next thing: you should know that on x86-32 there is no reliable way to build exact call stack. All methods use some kind of assumptions, heuristics and guessing. And sometimes methods may be confused, so you may have a false-positive entries in call stack or missed entries. See also: different stack tracing methods.
So, if you’re analyzing the call stack and see strange thing (“Hey? What the…? This routine can not be called here by my code!”) – you should take this into account and suppose that: either there is call missed or there is unnecessary (false-positive) call. You should also remember about this difference, if you use call stack in your application directly (for example, to get textual description of your caller).
Now, if you've read and understand this - you may call yourself an expert with call stacks (well, a bit of a practice will not hurt).
See also:
|