Shuo
Shuo

Reputation: 4847

Compare execution paths of same code under different inputs

I'm debugging a very complex C++ function that gives me some unexpected results under some inputs. I'd like to compare code executions under different input so that I find out what part causes me bug. The tool that can compare code execution paths is what I am looking for. Please let me know if such a tool exists. Or otherwise if there's some techniques I can employ to do the same thing?

To describe my problem concretely, here I'm using a contrived example.

Say this is the function in pseudocode,

double payTax(double income)
{
   if (income < 10000)
      return noTax();
   else if ( 10000 < income < 30000)
      return levelOneTax();
   else if (30000 < income < 48000)
      return levelTwoTax();
   else  
      return levelThreeAboveTax();
}

Given input 15000, the function computes the correct amount of tax, but somehow input 16000 gives an erroneous tax amount. Supposedly, input 15000 and 16000 would cause the function to go through exactly the same execution paths; on the other hand, if they go different paths, then something must have gone wrong within the function. Therefore, a tool that compares execution paths would reveal enough information that could help me quickly identify the bug. I'm looking for such a tool. Preferably compatible with Visual Studio 2010. It would be better if such a tool also keeps values of variables.

P.S. debugging is the last thing I want to do because the code base I am working with is much much bigger and complex than the trivial payTax example.

Please help. Thanks.

Upvotes: 7

Views: 1152

Answers (3)

Alex Che
Alex Che

Reputation: 7132

I know that this question is almost ten years old now, but I'll still give my answer here, since it may be useful for a random googler. The approach does not require any additional 3rd party tools, except maybe a usual text diff application to compare text files with execution paths.

You'll need to output the execution path yourself from your application, but instead of adding logging code in every function, you'll use special support from the compiler to make it call your handlers upon each function entry and exit. Microsoft compiler calls it Hook Functions and require separate flags for hooking function entering (/Gh) and exiting (/GH). GNU C++ compiler calls it instrumentation functions and requires a single -finstrument-functions flag for both.

Given those flags the compilers will add special prologue and epilogue code for each function being compiled, which will call special handlers, one for enter and one for exit. You'll need to provide the implementation of those handlers yourself. On GNU C++ the handlers are already passed with the pointers to the function being entered into or exited from. If you're on MSVC++, you'll need to use the return of _ReturnAddress intrinsic with some modification to get the address of the function.

Then you can just output the address as is and then use something line add2line tool to translate the address to function name. Or you can go one step further and make the translation yourself.

On MSVC++ you can use DbgHelp API and specifically SymInitialize and SymFromAddr helpers to translate the address into the function name. This will require your application to be compiled with debug information.

On GNU C++ you may probably want to use backtrace_symbols to translate the address into function name, and then maybe __cxa_demangle to demangle the returned name. This will probably require your executable to be built with -rdynamic.

Having all this in place you can output the name of each called function with the needed indent and thus have the call path. Or even do some fancier stuff like this, this or this.

You can use this MSVC++ code or this GCC code as a starting point, or just use your favorite search engine for other examples which are plenty.

Upvotes: 0

Frunsi
Frunsi

Reputation: 7157

The tool you want is printf or std::cerr!

And you have a substantial error in your code: a statement like if ( 10000 < income < 30000) will not work as expected! You want to write it like if( 10000 < income && income < 30000 ).

And to keep testing simple, please use curly brackets as in:

if( 10000 < income && income < 30000 ) {
    return levelOneTax();
} else if( ...

Because then it will be much easier to add debug output, as in:

if( 10000 < income && income < 30000 ) {
    std::cerr << "using levelOneTax for income=" << income << std::endl;
    return levelOneTax();
} else if( ...

EDIT

BTW: "a tool that compares execution paths would reveal enough information [...]", BUT in the sense you are expecting, such a tool would reveal TOO MUCH information to handle. The best thing you can do is debugging and verifying that your code is doing what you expect it to do. A "code coverage" tool would probably be too big for your case (and also such tools are not cheap).

Upvotes: -1

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275800

The keywords you are looking for is "code coverage" or "coverage analysis" or "code coverage analysis".

Which tool you use will naturally depend on the rest of your environment.

Upvotes: 3

Related Questions