Focus

CLI Interface Deep-Dive

How oclgrind-kernel.cpp parses command-line arguments, maps them to OCLGRIND_* environment variables, and drives the Simulation class.

Architecture

graph LR
  CLI[oclgrind-kernel] -->|parseArguments| ENV[setenv OCLGRIND_*]
  CLI -->|positional arg| SIM[Simulation::load]
  SIM -->|.sim file| RUN[Simulation::run]
  ENV -.->|read by| Core[Core subsystem]

The oclgrind-kernel binary is the standalone kernel simulator. Unlike oclgrind (which wraps an existing app via LD_PRELOAD), this tool reads a .sim file directly and executes the kernel outside any OpenCL runtime.

Flag → Environment Variable Mapping

Every CLI flag calls setEnvironment(name, value) which wraps setenv() (POSIX) or _putenv_s() (Windows). The Core subsystem reads these variables at initialisation.

CLI FlagEnvironment VariableValue
--build-options OPTOCLGRIND_BUILD_OPTIONSOPT string
--data-racesOCLGRIND_DATA_RACES"1"
--global-mem-size NOCLGRIND_GLOBAL_MEM_SIZEN bytes
--local-mem-size NOCLGRIND_LOCAL_MEM_SIZEN bytes
--max-wgsize NOCLGRIND_MAX_WGSIZEN
--interactive / -iOCLGRIND_INTERACTIVE"1"
--inst-countsOCLGRIND_INST_COUNTS"1"
--plugins LIBSOCLGRIND_PLUGINScolon-separated
--quick / -qOCLGRIND_QUICK"1"
--uninitializedOCLGRIND_UNINITIALIZED"1"
--num-threads NOCLGRIND_NUM_THREADSN
--log FILEOCLGRIND_LOGpath
--max-errors NOCLGRIND_MAX_ERRORSN
-g / --global-mem(local flag)sets outputGlobalMemory
Note: The -g / --global-mem flag is unique to oclgrind-kernel — it sets a local boolean rather than an environment variable, passing it as the dumpGlobalMemory parameter to Simulation::run().

Argument Parsing Loop

The parser is a simple strcmp chain — no getopt or library dependency. The first positional (non-flag) argument is taken as the .sim file path; any extra positional argument triggers an error.

// Simplified parseArguments logic from oclgrind-kernel.cpp
static bool parseArguments(int argc, char* argv[]) {
  for (int i = 1; i < argc; i++) {
    if (!strcmp(argv[i], "--data-races")) {
      setEnvironment("OCLGRIND_DATA_RACES", "1");
    }
    else if (!strcmp(argv[i], "--global-mem-size")) {
      if (++i >= argc) { cerr << "Missing argument"; return false; }
      setEnvironment("OCLGRIND_GLOBAL_MEM_SIZE", argv[i]);
    }
    else if (argv[i][0] == '-') {
      cerr << "Unrecognised option '" << argv[i] << "'"; return false;
    }
    else {
      if (simfile == NULL) simfile = argv[i];
      else { cerr << "Unexpected positional argument"; return false; }
    }
  }
  if (simfile == NULL) { printUsage(); return false; }
  return true;
}

Execution Flow

sequenceDiagram
  participant U as User
  participant CLI as oclgrind-kernel
  participant SIM as Simulation
  participant CTX as Core::Context
  U->>CLI: oclgrind-kernel --data-races test.sim
  CLI->>CLI: setenv(OCLGRIND_DATA_RACES, 1)
  CLI->>SIM: simulation.load('test.sim')
  SIM->>CTX: new Context()
  SIM->>SIM: parse .sim header
  SIM->>SIM: build program & create kernel
  SIM->>SIM: parseArgument() × N
  SIM-->>CLI: true
  CLI->>SIM: simulation.run(false)
  SIM->>CTX: KernelInvocation::run
  SIM-->>U: dump results to stdout

Usage Examples

// Basic kernel run
// $ oclgrind-kernel vecadd.sim

// Enable data-race detection and interactive debugging
// $ oclgrind-kernel --data-races --interactive vecadd.sim

// Limit memory and dump global memory at exit
// $ oclgrind-kernel --global-mem-size 67108864 -g vecadd.sim

// Load custom plugins, limit errors, log to file
// $ oclgrind-kernel --plugins myplugin.so --max-errors 10 \
//     --log errors.txt vecadd.sim
Difference from oclgrind: The oclgrind wrapper launches an arbitrary application with LD_PRELOAD injection. oclgrind-kernel runs a single kernel from a .sim file — no OpenCL runtime, no ICD loader, and no host application needed.

Environment Variable Helper

The setEnvironment() function abstracts the platform difference between POSIX and Windows:

static void setEnvironment(const char* name, const char* value) {
#if defined(_WIN32) && !defined(__MINGW32__)
  _putenv_s(name, value);
#else
  setenv(name, value, 1);
#endif
}
Overwrite semantics: The third argument to setenv is 1, meaning CLI flags always override any pre-existing environment variables. This lets users combine shell-level defaults with per-invocation overrides.

Error Behaviour

Argument parsing failures produce specific messages and return false, causing main() to return exit code 1. Common errors include:

Plugin Loading

The --plugins flag accepts a colon-separated list of shared library paths. These are stored in OCLGRIND_PLUGINS and loaded by the Core subsystem at context creation time. Plugins can hook into memory accesses, kernel invocations, and other simulation events to implement custom analysis passes.

Interactive Debugging Mode

When --interactive (or -i) is passed, the Core subsystem enters a GDB-like command loop at each kernel invocation. Users can inspect work-item state, set breakpoints on specific global IDs, and step through LLVM IR instructions. This mode is invaluable for debugging complex race conditions or out-of-bounds accesses.

Quick Mode

The --quick flag sets OCLGRIND_QUICK=1, causing the simulator to only execute the first and last work-groups. This dramatically reduces simulation time for large NDRanges while still catching boundary-related bugs.

Quick mode limitation: Bugs that only manifest in middle work-groups will be missed. Use quick mode for rapid iteration during development, but run the full simulation before release.

Version and Help Output

Both -v / --version and -h / --help cause the program to print information and call exit(0) immediately, skipping simulation entirely. The version output includes the PACKAGE_VERSION macro set at build time, the copyright notice, and a link to the GitHub repository.

Comparison: oclgrind vs oclgrind-kernel

Featureoclgrindoclgrind-kernel
InputArbitrary application.sim file
InjectionLD_PRELOAD / DLLDirect Core API
OpenCL runtimeFull ICD-compatibleNone
Global mem dumpNoYes (-g)
Argument dumpNoYes (per-arg dump)

Edge Cases & Common Pitfalls

ScenarioBehaviourResolution
No .sim file argumentPrints usage text, exits with code 1Provide a valid .sim file as the last positional argument
Two positional arguments"Unexpected positional argument" errorOnly one .sim file is accepted per invocation
--global-mem-size without value"Missing argument" error, exit 1Always pass the byte count after the flag
Flag after positional argumentTreated as a second positional → errorPlace all flags before the .sim path
Environment variable already setCLI flag silently overwrites it (setenv overwrite=1)Intended behaviour — CLI takes precedence
Invalid .sim file pathSimulation::load() fails with an I/O errorVerify the path exists and is readable
Plugin shared library not foundCore logs a dlopen / LoadLibrary error at context creationUse absolute paths or ensure LD_LIBRARY_PATH is set

Troubleshooting

Simulation loads but produces no output

Ensure at least one kernel argument has the dump modifier in its header, or pass -g to dump all global memory. Without either, oclgrind-kernel runs silently.

Interactive mode does not start

Verify that --interactive is spelled correctly. A common mistake is passing -I (uppercase) which is not recognised and triggers an "Unrecognised option" error. The short form is lowercase -i.

Memory limit appears to have no effect

The --global-mem-size value is in bytes, not megabytes. Passing --global-mem-size 64 gives you 64 bytes, not 64 MiB. Use 67108864 for 64 MiB.

Plugin causes segfault at startup

Plugins are loaded via dlopen during Core::Context creation. If a plugin has unresolved symbols or an incompatible ABI version, the process crashes before any simulation output. Test plugins in isolation by loading them with LD_PRELOAD first to check for link errors.

Related Topics