Something I've found helpful is to bear in mind that, when dealing with the really interesting/nasty bugs, you don't have to hit the right solution all at once. Anything that tells you something you didn't already know represents a step in the right direction, and thus gets you closer to the answer. Even figuring out what the problem isn't helps, because you know to look someplace else.
Suspect that maybe the bug may be caused by condition X? Then try dialing X up to 11. If the bug gets worse (or shows up more frequently), X may indeed be your culprit. If it has no effect, look elsewhere.
Is the bug intermittent? Then temporarily forget about trying to figure out why it happens and focus on when. If you can come up with a test case that reliably reproduces the bug, you're well on your way to figuring it out.
Think one particular part of the code looks suspicious? Then sprinkle it liberally with breakpoints or print statements (as your environment allows) and prove to yourself it's behaving properly. Check the input variables. Check the output variables. Hell, maybe even check the environment variables.
When I'm stumped, I try to fall back on basic experimentation: gather data, form hypothesis, test hypothesis, lather, rinse, repeat. You know, toss some "science" into my Computer Science. :-)