1. Procedural thinking. We very often perform the same actions in the same order.
2. A good memory. Experience is that which enables us to recognise a mistake when we make it again. When debugging, you are looking for things that are unusual. To spot them, you need to remember what the usual is like.
3. An associative mind. Often a new problem is similar to but not identical with previous problems. Seeing similarities can short circuit the troubleshooting process allowing you to leap forward. Of course, sometimes this will mislead you.
4. Pattern recognition. A lot of time when debugging is spent looking at apparent noise. We sometimes refer to this as fishing. Imagine that you are debugging a leak and all that you have is a hang dump. Looking at the activity of each thread is unlikely to be revealing. Looking at the assembler will not be revealing. That leaves only one thing to look at – the data. If you can spot patterns in the data then you are more than half way to solving the problem. For example, largish hex numbers that consistently rise by smallish amounts as you look forward in memory are immediately interesting. They could represent a VTable or a table of objects allocated sequentially. Data structures make patterns as well although they are less obvious. Look for order in the chaos. Equally, look for chaos in the order.
5. A suspicious mind. When looking at code, all code is guilty of doing something wrong until you have established that it is innocent. A good programmer will see the intention. A good debugger will try to see what is unintentional.
6. Flexibility. Sometimes one approach works and sometime you have to try something else.
7. The ability to work backwards. Very often, the question to ask is "How did I get here?"
8. Patience. Stepping through code is dull. Sometimes it is the best way to get a feel for the flow of a routine.
9. A rather obsessive nature. No well balanced person could spent weeks hunting for a tiny error
10. A fondness for coffee. The hours are long.