Saturday, 6 November 2010

The 9 indispensable DEBUGGING RULES

It's been a while since i've posted on this blog (glad to be back ;-)

As I was recently in touch with some young developers that seemed lost when encountering issues with libraries or technologies they just discovered, I thought that it could be interesting to refresh/share a synthesis on the 9 indispensable DEBUGGING RULES of the MUST READ: Debugging—The Nine Indispensable Rules for Finding Even the Most Elusive Software and Hardware Problems (David J. Agans - ©2002)

Ok then, here are the priceless rules:


[quote starts here]

Memorize them. Tape them to your wall. Tape them to all of your walls.

THE 9 DEBUGGING RULES

  1. UNDERSTAND THE SYSTEM
  2. MAKE IT FAIL
  3. QUIT THINKING AND LOOK
  4. DIVIDE AND CONQUER
  5. CHANGE ONE THING AT A TIME
  6. KEEP AN AUDIT TRAIL
  7. CHECK THE PLUG
  8. GET A FRESH VIEW
  9. IF YOU DIDN'T FIX IT, IT AIN'T FIXED

1. UNDERSTAND THE SYSTEM

This is the first rule because it's the most important. Understand?

  • Read the manual. It'll tell you to lubricate the trimmer head on your weed whacker so that the lines don't fuse together.
  • Read everything in depth. The section about the interrupt getting to your microcomputer is buried on page 37.
  • Know the fundamentals. Chain saws are supposed to be loud.
  • Know the road map. Engine speed can be different from tire speed, and the difference is in the transmission.
  • Understand your tools. Know which end of the thermometer is which, and how to use the fancy features on your Glitch−O−Matic logic analyzer.
  • Look up the details. Even Einstein looked up the details. Kneejerk, on the other hand, trusted his memory.

2. MAKE IT FAIL

It seems easy, but if you don't do it, debugging is hard.

  • Do it again. Do it again so you can look at it, so you can focus on the cause, and so you can tell if you fixed it.
  • Start at the beginning. The mechanic needs to know that the car went through the car wash before the windows froze.
  • Stimulate the failure. Spray a hose on that leaky window.
  • But don't simulate the failure. Spray a hose on the leaky window, not on a different, "similar" one.
  • Find the uncontrolled condition that makes it intermittent. Vary everything you can—shake it, rattle it, roll it, and twist it until it shouts.
  • Record everything and find the signature of intermittent bugs. Our bonding system always and only failed on jumbled calls.
  • Don't trust statistics too much. The bonding problem seemed to be related to the time of day, but it was actually the local teenagers tying up the phone lines.
  • Know that "that" can happen. Even the ice cream flavor can matter.
  • Never throw away a debugging tool. A robot paddle might come in handy someday.

3. QUIT THINKING AND LOOK

You can think up thousands of possible reasons for a failure. You can see only the actual cause.

  • See the failure. The senior engineer saw the real failure and was able to find the cause. The junior guys thought they knew what the failure was and fixed something that wasn't broken.
  • See the details. Don't stop when you hear the pump. Go down to the basement and find out which pump.
  • Build instrumentation in. Use source code debuggers, debug logs, status messages, flashing lights, and rotten egg odors.
  • Add instrumentation on. Use analyzers, scopes, meters, metal detectors, electrocardiography machines, and soap bubbles.
  • Don't be afraid to dive in. So it's production software. It's broken, and you'll have to open it up to fix it.
  • Watch out for Heisenberg. Don't let your instruments overwhelm your system.
  • Guess only to focus the search. Go ahead and guess that the memory timing is bad, but look at it before you build a timing fixer.

4. DIVIDE AND CONQUER

It's hard for a bug to keep hiding when its hiding place keeps getting cut in half.

  • Narrow the search with successive approximation. Guess a number from 1 to 100, in seven guesses.
  • Get the range. If the number is 135 and you think the range is 1 to 100, you'll have to widen the range.
  • Determine which side of the bug you are on. If there's goo, the pipe is upstream. If there's no goo, the pipe is downstream.
  • Use easy−to−spot test patterns. Start with clean, clear water so the goo is obvious when it enters the stream.
  • Start with the bad. There are too many good parts to verify. Start where it's broken and work your way back up to the cause.
  • Fix the bugs you know about. Bugs defend and hide one another. Take 'em out as soon as you find 'em.
  • Fix the noise first. Watch for stuff that you know will make the rest of the system go crazy. But don't get carried away on marginal problems or aesthetic changes.

5. CHANGE ONE THING AT A TIME

You need some predictability in your life. Remove the changes that didn't do what you expected. They probably did something you didn't expect.

  • Isolate the key factor. Don't change the watering schedule if you're looking for the effect of the sunlight.
  • Grab the brass bar with both hands. If you try to fix the nuke without knowing what's wrong first, you may have an underwater Chernobyl on your hands.
  • Change one test at a time. I knew my VGA capture phase was broken because nothing else was changing.
  • Compare it with a good one. If the bad ones all have something that the good ones don't, you're onto the problem.
  • Determine what you changed since the last time it worked. My friend had changed the cartridge on the turntable, so that was a good place to start.

6. KEEP AN AUDIT TRAIL

Better yet, don't remember "Keep an Audit Trail." Write down "Keep an Audit Trail."

  • Write down what you did, in what order, and what happened as a result. When did you last drink coffee? When did the headache start?
  • Understand that any detail could be the important one. It had to be a plaid shirt to crash the video chip.
  • Correlate events. "It made a noise for four seconds starting at 21:04:53" is better than "It made a noise."
  • Understand that audit trails for design are also good for testing. Software configuration control tools can tell you which revision introduced the bug.
  • Write it down! No matter how horrible the moment, make a memorandum of it.

7. CHECK THE PLUG

Obvious assumptions are often wrong. And to rub it in, assumption bugs are usually the easiest to fix.

  • Question your assumptions. Are you running the right code? Are you out of gas? Is it plugged in?
  • Start at the beginning. Did you initialize memory properly? Did you squeeze the primer bulb? Did you turn it on?
  • Test the tool. Are you running the right compiler? Is the fuel gauge stuck? Does the meter have a dead battery?

8. GET A FRESH VIEW

You need to take a break and get some coffee, anyway.

  • Ask for fresh insights. Even a dummy can help you see something you didn't see before.
  • Tap expertise. Only the VGA capture vendor could confirm that the phase function was broken.
  • Listen to the voice of experience. It will tell you the dome light wire gets pinched all the time.
  • Know that help is all around you. Coworkers, vendors, the Web, and the bookstore are waiting for you to ask.
  • Don't be proud. Bugs happen. Take pride in getting rid of them, not in getting rid of them by yourself.
  • Report symptoms, not theories. Don't drag a crowd into your rut.
  • Realize that you don't have to be sure. Mention that the shirt was plaid.

9. IF YOU DIDN'T FIX IT, IT AIN'T FIXED

And now that you have all these techniques, there's no excuse for leaving it unfixed.

  • Check that it's really fixed. Don't assume that it was the wires and send that dirty fuel filter back onto the road.
  • Check that it's really your fix that fixed it. "Wubba!" might not be the thing that did the trick.
  • Know that it never just goes away by itself. Make it come back by using the original Make It Fail methods. If you have to ship it, ship it with a trap to catch it when it happens in the field.
  • Fix the cause. Tear out the useless eight−track deck before you burn out another transformer.
  • Fix the process. Don't settle for just cleaning up the oil. Fix the way you design machines.

(David J. Agans - ©2002)

[quote ends here]

Wednesday, 3 September 2008

The cleaning wiimote

A japanese guy found a way to control a vacuum cleaner with a wiimote. It's fun to see what can be done with this awesome device. Click here to see the video.

This reminds me an old post dedicated to a Managed Library for Nintendo's Wiimote

Tuesday, 2 September 2008

The vicious deadlock situation (the one that does not freeze the GUI but leaks memory)

This post is dedicated to windows forms UI deadlock situations.

I) The classical deadlock situation (the one that freeze the UI)

This happens when the synchronization with the UI thread is made in a synchronous manner (Control.Invoke(...), SynchronizationContext.Send(...) etc.).
Solution: To fix this kind of deadlock, you may use asynchronous API to delegate tasks execution to the UI thread.

Those asynchronous API may be :

  • System.Threading.SynchronizationContext.Post(...)
  • Control.BeginInvoke(...) - but this may require too much CPU due to the underneath .NET reflection usage - I'll write a post on that topic later)
  • ...

II) The vicious deadlock situation (the one that does not freeze the UI but leaks memory)

This kind of deadlock is slightly more difficult to diagnostic. It happens when a "non UI" thread is making a call to the Invoke method of a Control that belongs to a closed Windows Form (this may happen just after a close to the Form).

In that case, the execution blocks indefinitely on the call to Control.Invoke() and the delegate supply to it never begins (see here). => this prevent the lock from being released by the blocked thread !


Solution: Prevent from situations where "zombie controls" are accessed (beware to unsubscribe all the .NET events to prevent from maintaining object alive) and use one of the asynchronous API to delegate tasks execution to the UI thread.

Tuesday, 10 April 2007

How to make unit testing from another project in .NET

WARNING: all this blog post is crap (error of youth ;-). Don't do this. Never. Ever. Do not test implementation details.  Follow: https://tpierrain.blogspot.com/2021/03/outside-in-diamond-tdd-1-style-made.html instead.




There is a recurrent debate around the notion of unit testing: should we only test the contracts, the visible part? or should we test the "internal affairs" of our class? In my opinion, the first proposition isn't enough. Since our teams decided to write unit tests in separate .NET assemblies (because we do not deploy test code in production, but tested code ;-), I was forced to find a trick in order to be able to query the private state of tested objects, without breaking the encapsulation. This trick is the .NET explicit interface implementation mecanism. "An interface member that is explicitly implemented cannot be accessed from a class instance, but only if the class instance is cast into the given interface." eg:
// Interface for testing purpose only, // (we always use a name like: // I<ClassToTestName>ForTesting) public interface IBoyForTesting { bool AlreadySpentAdolescentCrisis {get;} } // The class to test public class Boy : IBoyForTesting { private int age; private bool alreadySpentAdolescentCrisis; public int Age { get { return this.age; } } // Explicit interface member implementation: bool IBoyForTesting.AlreadySpentAdolescentCrisis { get { return this. alreadySpentAdolescentCrisis; } } /* ... */ } // Unit testing sample int age = 20; Boy aBoy = new Boy(age); // The following commented line would produce // compilation error because it try to access an // explicitly implemented member // bool isAdult = aBoy.AlreadySpentAdolescentCrisis; // To test this "almost private" information, you can write: Assert.IsTrue( ((IBoyForTesting)aBoy). AlreadySpentAdolescentCrisis );
With the .NET interface explicit implementation feature, you can test your classes from the outside, without too much having to break the encapsulation.

Wednesday, 28 March 2007

My personal motto for software development

Here are my four commandments for software development. Nothing is really new, but these are the basis criteria I try to follow each time I code a class :
  1. Make it testable
  2. Make it simple
  3. Make it readable
  4. Make it cohesive
Make it testable: because the test act like a specification and helps us to focus on design before writing our implementation code (cf Test Driven Development).

Make it simple: because complexity has several costs (difficulties to enter the code for every newcomer, painful sessions of debugage, bigger slowness for modification, ... ).

Make it readable: because source code must be human-readable (whereas binaries must be computer-executable), and because we usually do not work alone !
The choice of our method/variable names, the adoption of common naming and coding guidelines, and the quality of our source code comments (which must expose our intentions) is crucial.

Make it cohesive: because cohesion (the measure of how strongly-related and focused the responsibilities of a single class are) brings reliability, reusability, and understandability. It is essential to be capable of formalizing the responsibilities of a class in order to ensure its cohesion (I usually ask my teammates to make their best in order to write a precise and clear "summary comment" at the top of each one ot them). Highly cohesive classes are also usually easy to test (cf. Make it testable).


Like the Test-Driven Development motto (Red, Green, Refactor), I think this kind of reminder may be useful in our daily work.

Saturday, 24 February 2007

Bump top rocks !!!

Here, you can discover a demo of Bump top which is a real 3D (C++/OpenGL) innovant way of representing a desktop. I really do like this kind of "new UI experience" initiative.

Friday, 23 February 2007

Long, low-energy stand up meetings tend to distract and mute the day

Martin Fowler has recently post a nice paper about patterns of daily Stand-up meetings (It's Not Just Standing Up).

I completely agree with him when he says that sharing commitment is one of the stand-up meeting success keys.

"Making daily commitments to each other as a team is the most important goal of daily stand-ups. Sharing commitment is more important than sharing progress or status. This is not to say that an observer will not have a sense of progress and status from the stand-up, but this is secondary to team members publicly committing to each other, and identifying obstacles that prevent them from meeting their commitments."
Obviously, when this commitment objective is not targeted, this may lead to situations like:
"Team members are facing and talking to the manager or meeting facilitator instead of to the team. This indicates that the daily stand-up is for the manager/facilitator when it is actually supposed to be for the team. "

To avoid the skids and to fully benefit from this kind of meeting, it is useful to keep in mind the three points that every participant must quickly address:
  • What did I accomplish yesterday?
  • What will I do today?
  • What obstacles are impeding my progress

Saturday, 27 January 2007

Juval Lowy's white papers

The well-know .NET expert Juval Lowy (author of the must-read "Programming .NET Components") has recently published some white papers such as C# coding standard, SOA design guidelines, Windows Communication Foundation (WCF) Essentials, etc.

This is available in the Resources section of the IDesign web site.

I recommand the use of the first part of the "IDesign C# Coding Standard, for development guidelines and best practices" for any new .NET project which you would begin.

On the contrary, the multi-threading guidelines part of this document is bulsh#%^$ - sorry- I mean: isn't fit to all kind of development (especially not for real-time one !!!)
=> It would have been more logical to associate it to the ASP.NET section, for instance ...

Friday, 29 December 2006

The "message bound" command pattern

How can a server dynamically handle all incoming requests without performance drawback ?

I'd been searching for a satisfactory solution to that problematic since a long time... After I recently found one, I decided to document it as a Pattern:


The "message bound" Command pattern
"A controller that dynamically handles all incoming requests without performance drawback."

Reflection is useful and more elegant than static conditional logic to decide which command a server Front Controller should run from a given message/request.

Compares to the static implementation, reflection significantly improves the productivity of the development each time we need to add a new kind of message or request type to handle.

However, this elegance has a cost named performance… The "message bound" command pattern purpose is to solve this performance drawback.

Make it simple !

Contrary to appearances, the simplicity is difficult to obtain. When we develop systems, we tend to complexifier with excess. Then, the cost of the complexity of a system is directly tangible: difficulties to enter the code for every newcomer, painful sessions of debugage, bigger slowness for modification, etc.

Of course, pair programming and refactoring sessions can help us to aim towards this indispensable simplicity: It still have to be part of our objectives!

This is why I recommend you the post of Brad Abrams: "New Job Title: Senior Simplicity Engineer", and also the classical but always so relevant "The Parable of the Two Programmers".