Reputation: 6781
Suppose there is a function with 1000 code of line named LongFunction, and we used it:
bool bSuccess = LongFunction();
assert(bSuccess);
Here I got an assert when debugging, and I know there is something wrong with LongFunction, so I need to find where the function meet problems and returns:
I could probably debug it step by step, it works but is time-consuming, we don't what to do it this way.
I could search the keyword "return" (or even more refined search use RegExp), and set breakpoint at those returns, it should be faster, but it is still tedious manual work which can't be automated.
#define return TRACE(LINE); return
It works but have following problems:
Do you have any other creative ideas on how to pinpoint the problem?
Edit: Here are some details to let us focus on the problem.
It is about C++, and not platform specfic.
We don't want to refactoring the functions(Yea, I know we should), we even don't want to change any code - at this point we just want to provide some facility to make our application debugging easier. I also believe this should be a common requirement, don't you ever run into this?
The LongFunction() have multiple exit point, and the return type is not necessay bool(HRESULT, user defined errorcode...)
Edit: A summary of current discussions:
We have some controversies:
You should refactor the function.
Yea, everyone know that we should, but that is not the point.If I had make the call to refactor the function, I won't be here to ask the question.
Find where the LongFunction() returns failure doesn't help.
It is always the first thing I do to locate where the error occurs to know what happened, I am curious why this doesn't help, what did you do in this situation? (Assume I am already familiar with how the function works)
And we have 2 reasonable solutions:
ReturnMarker from Crashworks, a stack object in the function will destruct when function returns, set the breakpoint at the destructor will show you where it returns in debuger
CMyBool(x) from Binary & Sadsido, change the return type of LongFunction to CMyBool which can contruct from a bool, return from LongFunction will contruct that object, so just set a breakpoint at the constructor will work.
Upvotes: 5
Views: 530
Reputation: 7667
In 1982, I was tasked with fixing a broken flowchart program. The program, written in machine-dependent Harris FORTRAN (I do not now recall whether it was in FORTRAN IV or FORTRAN 77), consisted of an 1100-line main program, a 900-line subroutine, and about a dozen subroutines that were each 10 to 20 lines long.
Oh, and there was almost no whitespace (blank lines) in the program, and the comments were not useful at all.
It took me 160 hours - four weeks, FULL TIME, with NOTHING else on my desk - to grok that code sufficiently to make proper repairs.
You are in a similar situation. It is going to take a real investment of CPU time on your part for you to grok that 1000-line creature, well enough to have a handle on what all might go wrong, and what to do to fix it.
Finding the returns is easy enough. For Microsoft AbysmalC++: Search for every instance of "return" and preface it with
"printf("\n\n--->>> punching out at line %d\n\n", __LINE__);"
(or the equivalent on your system). Obviously, you can't just do this automatically; you'll have to look at the local bracketing. Then run your test and see where it tells you to look.
The fact of the matter is this: In the real world of computing, there are almost no real routines out there that actually need to be 1000 lines long. In nearly 40 years in this racket, as student and professional, I have encountered exactly one routine that NEEDED to be longer than one printer page (about 60 lines) long, and that one was a very special case. It was about three pages, all told.
On the other hand, I've seen a LOT of run-on modules, where the guy who wrote it was too lazy, or too incompetent, to factor it properly, and the maintenance programmers were too lazy, too incompetent, or too scared of their manager to go back and refactor it.
Finally: Consider that this is probably not going to be the last time someone has to work on this module, and it may well not be the last time YOU have to work on it. These kinds of things tend to be tar babies: once you touch them, you never get unstuck from them. It might very well be worth your time to refactor at least, and quite possibly redesign/rewrite it FROM SCRATCH and first principles.
Upvotes: 0
Reputation: 1687
"I could probably debug it step by step, it works but is time-consuming, we don't what to do it this way."
How else do you debug?
I used to debug by:
1) finding a set of parameters that reproduce the problem.
2) step through the code with that set of parameters.
If there are a 1000 lines of code then how are you going to "refactor" without knowing EXACTLY what the function does and is supposed to do.
And how are you going to do this without stepping through the function. I thought that was what an IDE with a good debugger was for.
Quite frankly, I find the question almost humorous in a very sad way.
Upvotes: 0
Reputation: 41434
Obviously you ought to refactor this function, but in C++ you can use this simple expedient to deal with this in five minutes:
class ReturnMarker
{
public:
ReturnMarker() {};
~ReturnMarker()
{
dummy += 1; //<-- put your breakpoint here
}
static int dummy;
}
int ReturnMarker::dummy = 0;
and then instance a single ReturnMarker at the top of your function. When it returns, that instance will go out of scope, and you'll hit the destructor.
void LongFunction()
{
ReturnMarker foo;
// ...
}
Upvotes: 12
Reputation: 62333
#define return { TRACE(LINE); return; }
That fixes your problem 4.
As far as the rest goes thats tjust the problem with coding. This is why many systems return more complex errors (such as an HRESULT from a COM object) and/or spam something to the debug stream when a problem occurs.
A 1000 line function should be re-fectored though. As you are seeing a long function proves incredibly hard to maintain.
Edit: Would the following work better than the above?
#define return TRACE(LINE), return
Have had a few drinks so it may well not.
Upvotes: 0
Reputation: 2053
It visual studio I would place a breakpoint on the assert, then using the stack trace window click the next row up and that'll take you into the exit point of the method.
Upvotes: 0
Reputation: 21721
You haven't said what platform your on. Depending on the contents of LongFunction
the following is how I'd approach this using gdb:
Let's imagine that your file 'f.cc' has the following lines:
1: bool LongFunction () { /* ... */ }
2:
3: void bar ()
4: {
5: bool bSuccess = LongFunction ();
6: assert (bSuccess);
7: }
Here are the steps in gdb:
Add a breakpoint on the same line as the assert: break f.cc:6
Add a condition to that breakpoint for when bSuccess is false: condition 1 (bSuccess==0)
Run your program until that breakpoint is reached
Set a breakpoint at the beginning of the function body: break f.cc:4
Jump back to that location (and it will stop at the breakpoint): jump f.cc:4
Debug the contents of LongFunction to see why it's failing.
The reason I say that it depends on the contents of LongFunction is that if LongFunction reads input from a stream, or modifies global variables etc then its behaviour the second time round may be different. You should consider the above steps as being the same as if the code was:
3: void bar ()
4: {
5: bool bSuccess = LongFunction ();
5: bSuccess = LongFunction ();
6: assert (bSuccess);
7: }
Upvotes: 0
Reputation: 12910
If your problem is just plain laziness (nothing wrong with that btw), make sure that all return statements in LongFunction are of the form
return(value);
rather than
return value;
(for instance using a regex search-and-replace)
Then, use a slighlty modified preprocessor macro than your original suggestion:
#define return(value) { if (!value) TRACE(__LINE__); return(value); }
...or even
#define return(value) { assert(value); return(value); }
...or whatever you feel is appropriate
Upvotes: 4
Reputation: 51711
Is this C or C++?
If C++, create a new class that wraps bool (say CMyBool), that has an automatic cast to bool.
Now have LongFunction return CMyBool (a quick search and replace will change all returns in LongFuntion to "return CMyBool(x)".
Now put a break point in the ctor for CMyBool, the debugger will now stop when CMyBool is created, which will be on the correct return statement in LongFunction.
The automatic cast to bool will stop CMyBool from breaking the code that uses CMyBool.
That will get you over the initial problem, but you larger problem is that LongFunction needs to be refactored.
Hope this helps.
Upvotes: 4
Reputation: 68053
Sounds like it's time to refactor LongFunction()...
A 1000 line function is a bad code smell. Spend the time refactoring it into smaller, more maintainable functions. You'll find the bug(s) while you're at it, and it will be a worthwhile investment for the future.
Upvotes: 7