|
|
|
|
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Today, I spent the entire day tracking down memory leaks.
VS is very helpful in that when it runs your C++ code, if at the end of
the program there's any memory still allocated, it prints out a bunch of
unintelligible gibberish about it. Stuff like the memory address that's
allocated, and what data it happens to contain now.
What this does not do, of course, is give you even the vaguest clue
where the hell in the entire codebase to start looking!
The approach we eventually took - and by "we" I mean my college came up
with this - is to comment out the entire main program, and progressively
uncomment it. And it turns out memory leaks can hide in the most
unexpected places...
The first bug was reasonably easy to flatten. There's a giant global
variable [Wait! Come back!] that holds a few pointers. One of them gets
initialised on startup. Guess when it gets released? Exactly...
As it turns out, adding a destructor to the globals class allows me to
free it. (I wasn't 100% sure that C++ actually runs destructors at
program exit... but it seems to work.)
Not, of course, that anybody really cares about the 48 bytes of memory
that was being "leaked". It never increases, and when the program stops
running *everything* is released anyway. But it shuts VS up.
The second bug was harder. Basically there's some code that creates a
local variable that's a pointer type, and calls new on it to create an
object. It then passes it to some system functions who's purpose is not
entirely clear to me. Nowhere is this object released.
So I inserted a call to release the object immediately after calling the
system functions. I was fully expecting this to break spectacularly, but
it has no apparent effect at all. Presumably the system call *copies*
the data pointed to or something.
(Either that, or the system is now utilising unallocated memory which by
some miracle doesn't get reused in my test scenario. But I'm reasonably
sure VS deliberately scribbles over memory when you delete it - or at
least, it does in debug mode...)
Again, hardly a show-stopper; it's several dozen bytes allocated on
startup and never released. Well, it's released now. That kept VS quiet
for a bit.
The next bug took me literally an *entire day* to find. Me and two other
people spent hours on end pouring over the code trying to decipher what
the actual **** is going on!
Basically, VS reports a *huge* number of objects not being freed. Some
of then contain ASCII text which suggests it's something to do with the
task configuration objects. That's newly-added code; basically we create
a vector of pointers, populate it, and at the end we iterate over it and
delete everything it contains.
We disabled everything between populate and clean. So it's not like an
object is being accidentally dropped somewhere. Again and again we
inspected the code, commented and uncommented stuff, stepped through it
in a debugger... we were getting nowhere.
The *only reason* we're even using pointers in the first place is that
C++ demands that you dangle stuff off the end of a pointer if you want
any kind of polymorphism at all. And there are 4 sorts of task object,
which each do something different. You build a task list, give the user
the option to modify its contents, and then run everything in the list.
Yeah, you guys have already worked this out haven't you? The solution to
this massive memory leak is a single line of code:
virtual ~BaseTask() {}
That's it. Just add that to BaseTask.h and suddenly the memory leak
vanishes.
Basically, it turns out that delete doesn't work properly unless you
declare a virtual destructor. The default one doesn't do the right
thing. Without this line of code, delete blindly assumes that the type
of the pointer specifies the size of the memory chunk to delete. *facepalm*
The next leak was stranger. If I uncomment a particular bit of code, a
leak appears. But this code doesn't seem to do anything special.
Certainly it doesn't allocate any memory. So... wuh?
Eventually, I find that there's a class that subclasses wxThread (from
the wxWidgets library). In particular, running a wxThread causes the
wxThread::Enter() method to run, and when this method exits, the thread
stops, and all its resources are freed automatically.
Our implementation for Enter() calls Kill() right at the end. A quick
glance at the wxWidgets documentation warns you to never, ever do this
because "the memory allocated to the thread will not be freed". What's
more, the return statement on the next line exits the method which
causes the thread to stop *anyway*.
In summary, delete Kill(), and another memory leak vanishes.
All is not well, however. While the actual application runs quite
happily (as far as I can tell), now when I run the test project, it
exist with return code -1657845. And on Linux, glibc reports something
about a double-free. (Then again, the Windows and Linux source code is
radically different in places...)
I wonder if valgrind would be any help... (But then of course, I'd have
to figure out how to build the application by hand!)
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Le 17/01/2014 19:54, Orchid Win7 v1 nous fit lire :
> I wonder if valgrind would be any help... (But then of course, I'd have
> to figure out how to build the application by hand!)
for using valgrind, both at home & work, yes it is useful (but you need
to allow a few options). Very usefull.
valgrind only need a -g (and possibly -O0) to run your program.
Nothing like purify which requires a special compilation.
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
On 17/01/2014 07:35 PM, Le_Forgeron wrote:
> Le 17/01/2014 19:54, Orchid Win7 v1 nous fit lire :
>> I wonder if valgrind would be any help... (But then of course, I'd have
>> to figure out how to build the application by hand!)
>
> for using valgrind, both at home& work, yes it is useful (but you need
> to allow a few options). Very usefull.
>
> valgrind only need a -g (and possibly -O0) to run your program.
> Nothing like purify which requires a special compilation.
The problem is the difficulty of building the program on Linux. Provided
I can persuade our regular build server to do this for me, it shouldn't
be too bad.
The rest question is, will I be able to understand what the hell the
program is trying to tell me? From the documentation I've seen, it looks
reasonably intelligible. (Not like the raw hex dump that VS gives me...)
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Le 17/01/2014 20:41, Orchid Win7 v1 nous fit lire :
> On 17/01/2014 07:35 PM, Le_Forgeron wrote:
>> Le 17/01/2014 19:54, Orchid Win7 v1 nous fit lire :
>>> I wonder if valgrind would be any help... (But then of course, I'd have
>>> to figure out how to build the application by hand!)
>>
>> for using valgrind, both at home& work, yes it is useful (but you need
>> to allow a few options). Very usefull.
>>
>> valgrind only need a -g (and possibly -O0) to run your program.
>> Nothing like purify which requires a special compilation.
>
> The problem is the difficulty of building the program on Linux. Provided
> I can persuade our regular build server to do this for me, it shouldn't
> be too bad.
>
> The rest question is, will I be able to understand what the hell the
> program is trying to tell me? From the documentation I've seen, it looks
> reasonably intelligible. (Not like the raw hex dump that VS gives me...)
If valgrind/memcheck can find the symbols of your code (-g), with option
like -leak-check=full and -track-origin=yes, it will show you where the
leak was allocated (hint: with deep program, you might want to use
--num-callers=100 (default = 12))
The first line is about the allocating line... further, the hierarchy of
the functions up to the top function.
Also fancy: --malloc-fill=A5 --free-fill=8A (or other hex value), to
kill uninitialised variables happily at 0 on normal run.
--log-file is a must have too (--log-file=whateverfile.txt )
The only downside: runtime is multiplied by 50 to 200.
(and multicore/threads get serialised)
Also intersecting: --show-reachable=yes (block referenced by lost blocks...)
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
Orchid Win7 v1 <voi### [at] devnull> wrote:
> I wonder if valgrind would be any help... (But then of course, I'd have
> to figure out how to build the application by hand!)
valgrind exists precisely for this kind of thing. Last time I checked,
however, it hadn't been ported to Windows, but that was a long time ago,
so things might have changed.
There are other, commercial products that do the same task. However,
given that you are working for a company, it shouldn't be such a big
deal.
I have experience with AQtime, and it worked pretty well. (It also
helps finding performance bottlenecks.) I'm not trying to endorse any
particular commercial product, but it just happens to be the one I have
used.
But then, I seldom need to use any such memory debuggers. 99.99% of such
bugs can be avoided by writing the C++ code properly. I don't even remember
when was the last time I had a memory leak in any C++ program (and I write
C++ constantly as my payjob, and have been doing so for quite a long time.)
--
- Warp
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
| |
|
|
>> I wonder if valgrind would be any help... (But then of course, I'd have
>> to figure out how to build the application by hand!)
>
> valgrind exists precisely for this kind of thing. Last time I checked,
> however, it hadn't been ported to Windows, but that was a long time ago,
> so things might have changed.
Well, the application does get built for Windows and Linux. (And there
are some fairly drastic code differences between the two.) It would
certainly be useful to test the Linux version - and to get something
more than just a page of hex codes to tell me what the **** is going on.
Then again, it does mean I have to master building the thing on Linux.
(Not as simple as clicking "rebuild solution".)
> There are other, commercial products that do the same task. However,
> given that you are working for a company, it shouldn't be such a big
> deal.
Working for a company with very little money. ;-)
> But then, I seldom need to use any such memory debuggers. 99.99% of such
> bugs can be avoided by writing the C++ code properly. I don't even remember
> when was the last time I had a memory leak in any C++ program (and I write
> C++ constantly as my payjob, and have been doing so for quite a long time.)
That's probably the problem. We write C# all day. C++ has drastically
different semantics, which can trip you up in all sorts of ways.
Look at the post I wrote. Two bugs were due to a conspicuous use of
"new" for which no corresponding "delete" exists. That's pretty easy to
recognise, even if fixing it isn't necessarily easy. But the other two
leaks were due to more unusual causes. In one case, we allocate objects
and explicitly release them again; the only reason it leaked is because
(unknown to any of us) you have to explicitly ask for a virtual
destructor. The compiler doesn't figure that out for you. And the final
leak was to do with somebody not reading the fine print on an external
library.
I suppose if you write this kind of code all day, every day, you would
*know* that destructors need to be virtual and so forth...
Post a reply to this message
|
|
| |
| |
|
|
|
|
| |
|
|