Back Tracing
Back tracing is locating an endpoint to an event associated with the feature (see below). Once there, a breakpoint is placed in the debugger. The feature is triggered and when the debugger stops. The call stack is reviewed to back trace the calling path. While walking up the call stack you can take notes on variable states, or place new breakpoints to inspect the event again.
The feature is trigger again and the debugger stops at the new breakpoints. You can then repeat back tracing or perform forward tracing until the goal is found.
Pros & Cons
- It's always easier to walk up the call stack and see how you got somewhere.
- There could be millions of conditions that need to be true before reaching an endpoint. If you know the endpoint already you've saved yourself lots of work.
- If the feature is broken. You may never reach the endpoint, and time can be wasted trying to figure out why.
Endpoint Discovery
To debug a feature you have to know where in the source code the final goal is achieved. Only from this point can you backtrace to see how the code got there. An example; To understand how undo is performed. You know where in the code things are undone, but you don't know how things get there. This would be a candidate for backtracing to figure out how the feature works.
Forward Tracing
Forward tracing is locating a start point for an event associated with a feature (see below). Once there, logging messages are inserted into the source code or breakpoints are set. This process is repeated as you progress further away from the start point until you discover the goal for the feature.
Pros & Cons
- It's the easiest starting point for finding a feature.
- Code complexity reduces the effectiveness of forward tracing. The more conditions there are in the code the greater the chance you'll go in the wrong direction.
- Forward tracing often results in setting breakpoints that will be triggered by unrelated events. Interrupting the debugging process and interfering with your search.
Start Point Discovery
You can use keywords, user interface identifiers (button IDs, window names) or easy to find event listeners associated with the feature. For example, you might start with the button used to trigger an undo feature.
Process Of Elimination
You can think of this as the middle point compared to start point and end point positions. You perform a process of elimination when you already know a piece of code is used in a feature, but it is neither the start or end of the feature.
The direction you take from the middle point depends upon the number of entries and exits. If the code chunk is used in many places, then back tracing from this position could be very time consuming as they all have to be inspected. You then employ a process of elimination to reduce this list. Alternative, you can perform a forward trace from this point, but again if the code chunk branches out to many places this can also be a problem.
You have to reduce position directions by not following paths that clearly wouldn't be executed for the feature. Moving past this code and only placing breakpoints where it's likely related to the feature.
Middle point debugging often requires more advance IDE features. The ability to see code hierarchy and dependencies. Without those tools it's difficult to do.
Pros & Cons
- Middle points are often the first peice of code that pops into your head when you think of the feature. You say to yourself "Ah, that has to use XXXX to work."
- Middle points can reveal start points the easiest.
- Middle points can be an easy way to pick up the trail to a feature when lost by synchronization or threading changes.
- Middle points can take you to code you are not familiar with. Costing you time to learn what is going on.