Adding tracing to your code

One thing you might want in your code is to add tracing to it so that your application log file will contain entries for all function entries and exits. This is tremendously useful when debugging a running application especially when you cannot connect a debugger to it for any reason. It is also very helpful when your application behaved unexpectedly once and you can’t get to repro it while you have a debugger attached. In those cases your log file is your best friend. Assuming that your log file has all the information that you need to debug the issue. Tracing all the function calls is a big help.

You don’t want to do this:

bool SaveUserInfo(User* pUser)
{
    log.Trace("Entering function SaveUserInfo");

    Database.Connect(connectionString);
    Database.Update(GetUserUpdateSql(pUser));
    Database.Disconnect();

    log.Trace("Exiting function SaveUserInfo");

    return true;
}

It should be obvious that this approach is both tedious and error prone, especially with future changes. For example if we add a parameter validation such as the one given below:

bool SaveUserInfo(User* pUser)
{
    log.Trace("Entering function SaveUserInfo");

    if (nullptr == pUser)
    {
        return false;
    }

    Database.Connect(connectionString);
    Database.Update(GetUserUpdateSql(pUser));
    Database.Disconnect();

    log.Trace("Exiting function SaveUserInfo");

    return true;
}

Ofcourse we can fix this in multiple ways. We could add a trace just before returning from the validation failure block. Or we could add a goto towards the end of the program where we trace the function exit. Or we could just restructure the program as below:

bool SaveUserInfo(User* pUser)
{
    log.Trace("Entering function SaveUserInfo");

    bool retValue = false;

    if (nullptr != pUser)
    {
        Database.Connect(connectionString);
        Database.Update(GetUserUpdateSql(pUser));
        Database.Disconnect();

        retValue = true;

    }

    log.Trace("Exiting function SaveUserInfo");
    return retValue;
}

But you get the point, this is not a good solution.

So what is the solution. I had talked about RAII in a previous post. We could employ the same concept here. We can have an object created on the function stack frame. The constructor will trace the function entry and destructor (which is guaranteed to be invoked on function exit) can trace the function exit. The only thing the developer need to know is that a function trace object should be created on the stack and it should be the first line of the function. Here is some sample code.

class FunctionTrace
{
    CString _functionName;

public:
    FunctionTrace(CString functionName)
    {
        _functionName = functionName;
        log.TraceEnter(_functionName);
    }

    ~FunctionTrace()
    {
        log.TraceExit(_functionName);
    }
};

Using the FunctionTrace class is trivial.

bool SaveUserInfo(User* pUser)
{
    FunctionTrace ft("SaveUserInfo");

    if (nullptr == pUser)
    {
        return false;
    }

    Database.Connect(connectionString);
    Database.Update(GetUserUpdateSql(pUser));
    Database.Disconnect();

    return true;
}

As you can see, exiting from the function in the middle is not a problem anymore. The code is much more compact and clean.

One thing I would like to avoid is having to specify the name of the function myself. It is a problem because it is more typing and also because there is a chance that I would rename the function some time in the future and forget to update the string parameter to the FunctionTrace constructor. It is just too easy to miss that. The compiler will not be able to help me catch that oversight.

But the preprocessor can help. Lucky for us, Visual C++ defines a macro __FUNCTIONW__ that we can use to get the function name. One could easily write a macro that encapsulates this knowledge and makes sure that a FunctionTrace instance is created on the stack and passes the __FUNCTIONW__ to the constructor of FunctionTrace. We could even modify the trace to include the file name and line number using the __FILE__ and __LINE__ macros. Now the client code becomes much more simple.

bool SaveUserInfo(User* pUser)
{
    // macro that creates FunctionTrace instance
 // on stack and passes in the name of the function. 
    FUNCTION_TRACE(); 

    if (nullptr == pUser)
    {
        return false;
    }

    Database.Connect(connectionString);
    Database.Update(GetUserUpdateSql(pUser));
    Database.Disconnect();

    return true;
}

The FUNCTION_TRACE could be implemented like this:

#define FUNCTION_TRACE() FunctionTrace fn(__FUNCTIONW__)

If we modify FunctionTrace class to trace the line number and file name, we could pass those information from FUNCTION_TRACE as follows:

#define FUNCTION_TRACE() FunctionTrace fn(__FUNCTIONW__, __LINE__, __FILEW__)

This assumes that FunctionTrace constructor now takes two strings and an integer.

class FunctionTrace
{
public:
    FunctionTrace(
        LPWSTR functionName,
        int lineNumber,
        LPWSTR fileName);

    ~FunctionTrace();
};

Have fun tracing.

Broken Windows

Once upon a time, in a place far far away, there was an idyllic small town. The town had a beautiful street with elegant houses on either side and big green trees gave shade to the sidewalks. The houses had large french doors and windows made with expensive panes of glass. Kids played ball on the street. It was the most desirable neighborhood in the town.

One day, the kids playing on the streets accidentally broke one of the glass window of his house. The kids fled fearing that the house owner would get mad and would complain to their parents. But that did not happen because the house owner was away on a long vacation. The window remained broken . The kids stayed away from the street for a few days and then they started to return and saw that the window was still broken. Nobody was mad, nobody had complained to their parents. Nobody seemed to care about the broken window. They could take a hint. More windows were broken. Graffiti appeared on the walls. Anti social elements started hanging around. Windows of other houses started to get broken. People started to move to other parts of the town leaving behind houses with broken windows and graffiti on the walls.

You might have got a hint of what I am getting at here with this little story showcasing the broken windows theory.

Wikipedia – “The broken windows theory is a criminological theory of the norm setting and signaling effects of urban disorder and vandalism on additional crime and anti-social behavior.”

So what is my point, you ask.

Well, I am trying to make a case for fixing the broken windows in your codebase.

Poorly designed classes, sloppy coding, deviations from established coding standards and idioms and best practices, cutting corners with error checking etc. are all broken windows in your code. Just one occurrence is all it needs to start the degradation of your code quality. A new person to the team will subconsciously feel what goes and what does not in the codebase and starts behaving accordingly. Fixing the broken windows should be as much of a priority as other engineering excellence activities. You can’t really prevent windows from getting broken. You might write some sloppy code if you were tight on time. Your design might turn out to be unsuitable or insufficient because you didn’t have all the information when you were designing your code. Or the requirements could have changed. Whatever the reason, the quality of the design and code will continue to degrade as the project moves forward unless conscious effort is made to keep it pristine. This rotting of the software over time, when no effort is made to prevent it, is known as software entropy.

So why don’t we all do it ? Why is broken windows in our code more common than it should be ?

Because it is usually a hard sell to your manager. Managers have a commitment to deliver something on time. Their performance reviews, bonus and sometimes even their job depend on delivering. So it is not too hard to understand that from the manager’s perspective delivering is usually more important than refactoring existing code to maintain the code and design quality. I understand that I am making some generalization here. Not all managers are like that.

What is important is that you should make the case for maintaining the code in a pristine condition. You should argue for it. If you don’t win, that is OK. At least you tried. You should be able to sleep better at night. What is unacceptable is knowing that something is broken and pretending that it is not or pretending not to see it or acknowledging it. There is no excuse for that.