Search code examples
c++openclopencl-c

OpenCL - How to suppress build errors from going to Standard Error?


In my application, I have a single OpenCL C program that gets built with several swappable modules that change parts of the code—notably, it changes some macros so that different arithmetic types are used for different versions of the program, and there's also some swappable layers that are also switched depending on which arithmetic types are being used (i.e. integer types vs floating point types).

Some of these configurations are valid, but some of them may not be valid, depending on what the hardware actually supports. So my solution is to build all of them, and whichever ones fail, I mark them in my code as being invalid so that they don't get use (and return an error if a user tries to use them).

This functionality works fine. However, whenever I ask the OpenCL C online compiler to compile some of these invalid programs, the compiler is automatically generating error messages that are then automatically getting routed to the Standard Error of my program, which clutters the output of my application. I'm already logging the errors from failed compiles with my own logging mechanism, I don't need the errors to also populate the standard error of my application.

What can I use to suppress those error messages from being populated in the standard error of my application?

Sample code below; I'm running this code on Ubuntu, with an Nvidia graphics card.

//An obviously invalid program source, consisting only of an orphaned integer literal
const std::string Sources::programSrc5 = "-1"; 

std::optional<cl::Program> buildProgram(cl::Context & context, std::ostream * log) {
  //Normally log is a pointer to a std::ofstream
  if(log == nullptr) {
    log = &std::cerr;
  }
  cl::Program program{context, programSrc5, false};
  try {
    //I tried using -w to suppress warnings to see if it would help, but doesn't.
    program.build("-w");
    return program;
  } catch (cl::Error const& e) {
    //This is the logging that matters to me
    *log << e.what() << ": " << e.err() << std::endl;
    auto devices = context.getInfo<CL_CONTEXT_DEVICES>();
    for(auto const& device : devices) {
      *log << program.getBuildInfo<CL_PROGRAM_BUILD_LOG>(device);
    }
    return {};
  }
}

Output (Standard Error):

2 errors generated.

Output (log.txt):

clBuildProgram: -11
<kernel>:1:1: error: expected external declaration
-1
^
<kernel>:1:2: error: expected identifier or '('
-1
 ^

What I want is for the log file to stay exactly the same, and for the standard error to be empty. How do I achieve this?


Solution

  • // just in case you had pending output
    std::cerr << std::flush;
    
    int orig_err = ::dup(2);
    int devnull = ::open("/dev/null", O_WRONLY);
    ::dup2(devnull, 2);
    
    program.build("-w");
    
    ::dup2(orig_err, 2);
    ::close(orig_err);
    ::close(devnull);
    

    You can, of course, wrap this in a RAII helper:

    struct redirect_stderr {
      redirect_stderr(const char *path = "/dev/null") {
        std::cerr << std::flush;
    
        orig_err = ::dup(2);
        redirected = ::open(path, O_WRONLY);
        ::dup2(redirected, 2);
      }
      ~redirect_stderr() {
        ::dup2(orig_err, 2);
        ::close(redirected);
        ::close(orig_err);
      }
      int orig_err, redirected;
    };
    

    and then the usage is simply

    {
      redirect_stderr to{"/dev/null"};
      program.build("-w");
    }