google / benchmark

A microbenchmark support library
Apache License 2.0
8.59k stars 1.57k forks source link

[BUG] Console created through AllocConsole does not receive colored output #1731

Closed bstordrup closed 6 months ago

bstordrup commented 6 months ago

Describe the bug I use benchmark.dll inside a non-console application because I want to benchmark different implementations of ways to solve the same problem. And it is not possible to extract the code into a separate project, as the benchmarked code calls several other functions.

Therefore, I rolled my own BenchmarkMain method that does the same as BENCHMARK_MAIN macro, but also creates a console with AllocConsole, creates std::ostream instances for STD_OUTPUT_HANDLE and STD_ERROR_HANDLE, creates a ConsoleReporter instance and calls RunSpecificBenchmarks with this displayReporter.

The console did receive output, but it reported that Color printing was only supported on Windows - which makes no sense as the console being written to is on Windows.

Along the way, the std::cout.rdbuf() is saved, and replaced with the the same .rdbuf() that is used for the std::ostream out instance.

System

To reproduce Inside a non-console application, add the following function:

int BenchmarkMain (int argc, char **argv)
{
    char arg0_default[] = "benchmark";
    char* args_default = arg0_default;
    if (!argv) {
      argc = 1;
      argv = &args_default;
    }

    if (!argv)
    {
        argc = 2;
        argv = (char **) args_default;
    }

    ::benchmark::Initialize (&argc, argv);
    if (::benchmark::ReportUnrecognizedArguments (argc, argv))
    {
        return 1;
    }

    // Create console reporter
    AllocConsole ();
    HANDLE stdOutHandle = GetStdHandle (STD_OUTPUT_HANDLE);
    HANDLE stdErrHandle = GetStdHandle (STD_ERROR_HANDLE);

    // Get file descriptors
    int fdOut = _open_osfhandle ((intptr_t) stdOutHandle, _O_TEXT);
    int fdErr = _open_osfhandle ((intptr_t) stdErrHandle, _O_TEXT);

    // Get FILE*
    FILE *fpOut = _fdopen (fdOut, "w");
    FILE *fpErr = _fdopen (fdErr, "w");

    // Create ofstream
    std::ofstream outStream (fpOut);
    std::ofstream errStream (fpErr);

    // Create ostream
    std::ostream out (outStream.rdbuf ());
    std::ostream err (errStream.rdbuf ());

    auto savedBuf = std::cout.rdbuf ();
    std::cout.rdbuf (outStream.rdbuf ());

    auto displayReporter = std::make_unique<::benchmark::ConsoleReporter> ();
    displayReporter->SetOutputStream (&out);
    displayReporter->SetErrorStream (&err);

    ::benchmark::RunSpecifiedBenchmarks (displayReporter.get());

    // Restore std::cout.rdbuf() - otherwise an exception is thrown at exit.
    std::cout.rdbuf (savedBuf);

    return 0;
}

Make sure that the function is being called when the application starts - I control this with a command line parameter.

Note that the output written to the console is without any color formatting.

Expected behavior Output from benchmarking should get color-enabled output in the created console window

Additional context The application, where I call the benchmark library, is a MFC Windows application created with use of an external library (CodeJock).

dmah42 commented 6 months ago

can you point to where the bug in the library is that is causing the issue, as opposed to an issue with how you're redirecting the various streaming buffers?

bstordrup commented 6 months ago

Updated the issue to more precisely describe the problem.

There are two places in code that contributes to this error:

  1. In console_reporter.cc, there is a check on the output stream where std::cout is directly compared to GetOutputStream(), and if different, the "Color printing is only supported..." is reported.
  2. The ColorPrintf (std::ostream& out, LogColor color, const char* fmt, va_list args) method operates directly on stdout for flushing the stream and uses vprintf to make the actual output.

I have a branch in my local fork with changes in these two places that makes color printing work in a Console window created with AllocConsole.

dmah42 commented 6 months ago

ah great, thank you for the clarification! :)