tags:

views:

256

answers:

6

I am looking for tips on how to aid my debugging by adding code to my application. An example so that it becomes more clear what I'm after: in order to detect dangling objects held by shared_ptrs I have created a tracker class that allows me to keep track of how many objects are alive and where they where originally created, which is then used like this:

class MyClass {
    TRACK_THIS_TYPE(MyClass);
};

boost::shared_ptr<MyClass> myObj(new MyClass);
TRACK_THIS_OBJECT(myObj);

where TRACK_THIS_TYPE(t) is a macro that makes sure that I get an instance count (and count of how many objects have been created) for a class and TRACK_THIS_OBJECT is a macro that stores the file and line where the object was created together with a weak_ptr to the object.

This allows me to detect dangling objects and where they where created. It does not allow me to find out what objects are holding a shared_ptr to my objects, which could be an improvement to the above. I guess one could create something like a TRACK_THIS_PTR(T) macro that would store the file and line for where the shared_ptr instance is created.

Another example would be the old

assert(condition && "My descriptive text");

that allows you to put meaningful comments directly into your assert.

Does anyone have any neat c++ tricks to collect stats, automatic stack traces, tracking of objects/pointers/resources, deadlock/starvation or other threading issues detection, making sure that exceptions get handled somewhere, documentation help or similar? Anything goes really, whether it is something that helps prevent errors or something that helps after the fact.

Edit: In addition to the replies to this question, I've received tips about google-glog as a logging utility.

+1  A: 

You could look into logging the stats you are producing with something like "log4cxx"

http://logging.apache.org/log4cxx/index.html

This should let you control the level of tracing you are doing at runtime (or at least via a configuration file read in at runtime).

It will automatically timestamp your logging, and let you format your output to suit other tools (say Excel or a database) to allow you to do statistical analysis of your logging data.

monojohnny
+4  A: 

I use BOOST_ASSERT a lot, to check input, intermediate calculations and before returns, even if it seems obvious.

It forces you to think about what values can take you data, and can make it easier to rapidly find out a refactoring that left some stupid error.

If you're really concerned about performances, you can disable them for a release build.

Concerning memory managing, I massively use RAII and try to do my best to use as few pointers and manual memory allocation as possible. When only having 2 or 3 pointers in your code, it's easier to avoid doing an error.

Tristram Gräbener
+2  A: 

I would personally prefer to aim at not writing bugs in the first place. Adding reams of debugging code to an application can have unfortunate side effects. It obviously affects performance and, in the case of multi-threaded apps, can change the timing of the application, resulting in MT bugs being hidden.

I find I normally spend very little time debugging my code - and any time I do spend in the debugger I count as time wasted. What I find does help is having tests I can run after every change I make - you don't have to embrace TDD to use this approach.

anon
I think most of us aim at not writing bugs... :) Affecting performance is ok, normally any such debugging code is only included in debug builds. Your point about having tests is very valid though. You also have a valid point that debug code may affect the behaviour of the program - that does not mean that it is always a bad thing to include debugging code (for debug builds).
villintehaspam
I take it you don't do much MT work? Seriously, I've seen cases where even writing a simple debug log hid MT bugs.
anon
@Neil Butterworth: I do quite a lot of multithreaded coding and yes, I've seen cases where debug code affects the timing, such as for instance when writing to a log or something. There can be lots of other things that affect the timing as well - debug and release builds can behave quite differently even without the presence of your own debugging code. You should normally be able to include/exclude debugging code with a define or similar so that you can test with or without it for both debug and release builds.
villintehaspam
@Neil (answer and comment) absolutely correct. Writing code in preparation for debugging is wasting time twice. And, again, any method of measuring a system is bound to affect it. Running tests is indeed a far better solution.
KevinDTimm
@villintehaspam - but if your debug build is full of self inflicted debug code, your testing after the debug code is removed ultimately must start again from the beginning.
KevinDTimm
@kevindtimm: yes - I'd assume that tests are run on both debug builds and release builds and on release builds the debugging code isn't present anyway.
villintehaspam
@Neil and kevindtimm: Would you consider the use of asserts to be debugging code? I.e. a waste of time? I would also count time spent in the debugger as wasted time, which is precisely why I want to apply coding techniques that catch errors earlier on. Unlike you guys, us mere mortals occasionally make mistakes or have coworkers that do... ;)
villintehaspam
I've actually more or less given up on asserts, I'm afraid :-)
anon
Some bugs just can't be detected by unit tests. For example, if each unit works fine by itself, but all units together form a circular reference, breaking reference counting smart pointers. A simple assert in the right place can prevent that. As for MT bugs: I personally prefer to aim at writing thread-safe code in the first place ;-) Anyway, I don't think MT bugs are relevant here. Sure, debug code might hide a MT bug, but it might as well reveal an MT bug that woudn't be found otherwise.
nikie
I said nothing about UNIT tests. And debug code is more likely to hide then not - for example, writing a log will often serialise things.
anon
+2  A: 

The following are mainly for ease of debugging after the release.

Stackwalker and similar tools provide an easy way to get usable callstacks on end-user machines, with no debugger being active. Rather similar, Google Breakpad can be used to easily extract mini dumps from crashed processes.

Alexander Gessler
I did not know about Google Breakpad before. It does look very interesting I think and might be a very good way to get information from potential crashes "in the wild".
villintehaspam
+3  A: 

Interestingly we have quite a similar project at work in order to track memory issues.

Many people swear by RAII, but even using shared_ptr you can create leaks, the problem being mainly due to references cycles (that's why reference counted based garbage collectors have special algorithms to detect cycles) :x

The company I work for (Amadeus) is currently developping AMPolice which ressemble valgrind. It is still work in progress, especially in the documentation department.

As you can see, this is completely different from your own approach: The binary is left unchanged, and the memory allocation is tracked by switching to a "debug" memory management (at runtime) when necessary (using a command-line API).

This is therefore much easier to use, although from our tests it does affect timings (4x or 5x).

The tool is pretty generic, so normally could be used by many people, but of course the main issue remains the shear size of logs since tracing every single new is quite costly :x

Matthieu M.
A: 

My c++ is a bit rusty now, but I do remember that whenever I had to allocate an object I had a macro that performed it something like:

NEW(class)

and then a macro for deallocation of objects called RELEASE. Then each macro would store the class and line number of where the object was created and destroyed.

This would allow easy detection of memory leaks by being able to see where objects were not being released.

Zubair