Skip to content

Creating Plugins

James Price edited this page Jan 29, 2017 · 14 revisions

Creating Plugins

Oclgrind provides a simple plugin interface that allows third-party developers to extend its functionality. This interface allows a plugin to be notified when various events occur within the simulator, such as an instruction being executed, memory being accessed, or work-group synchronisation constructs occurring.

To create a plugin, simply extend the oclgrind::Plugin class, and override any of the virtual callback functions that you wish to handle. To produce a plugin that is available as a dynamically loadable library, you should also implement the function void initializePlugins(oclgrind::Context *context) with extern "C" linkage. This function should create an instance of your plugin class and register it with the Oclgrind context by calling context->registerPlugin(myPlugin). This dynamic library can then be passed to the --plugins option (or OCLGRIND_PLUGINS environment variable) when running Oclgrind.

The following callback functions are available to override:

void hostMemoryLoad(const Memory *memory, size_t address, size_t size);
void hostMemoryStore(const Memory *memory, size_t address, size_t size, const uint8_t *storeData);
void instructionExecuted(const WorkItem *workItem, const llvm::Instruction *instruction, const TypedValue& result);
void kernelBegin(const KernelInvocation *kernelInvocation);
void kernelEnd(const KernelInvocation *kernelInvocation);
void log(MessageType type, const char *message);
void memoryAllocated(const Memory *memory, size_t address, size_t size, cl_mem_flags flags, const uint8_t *initData);
void memoryAtomicLoad(const Memory *memory, const WorkItem *workItem, AtomicOp op, size_t address, size_t size);
void memoryAtomicStore(const Memory *memory, const WorkItem *workItem, AtomicOp op, size_t address, size_t size);
void memoryDeallocated(const Memory *memory, size_t address);
void memoryLoad(const Memory *memory, const WorkItem *workItem, size_t address, size_t size);
void memoryLoad(const Memory *memory, const WorkGroup *workGroup, size_t address, size_t size);
void memoryMap(const Memory *memory, size_t address, size_t offset, size_t size, cl_map_flags flags);
void memoryStore(const Memory *memory, const WorkItem *workItem, size_t address, size_t size, const uint8_t *storeData);
void memoryStore(const Memory *memory, const WorkGroup *workGroup, size_t address, size_t size, const uint8_t *storeData);
void memoryUnmap(const Memory *memory, size_t address, const void *ptr);
void workGroupBarrier(const WorkGroup *workGroup, uint32_t flags);
void workGroupBegin(const WorkGroup *workGroup);
void workGroupComplete(const WorkGroup *workGroup);
void workItemBegin(const WorkItem *workItem);
void workItemComplete(const WorkItem *workItem);

Here is an example plugin that simply prints out each instruction that is executed, and the work-item responsible:

#include "oclgrind/Context.h"
#include "oclgrind/Plugin.h"
#include "oclgrind/WorkItem.h"

class InstPrinter : public oclgrind::Plugin
{
public:
  InstPrinter(const oclgrind::Context *context) : oclgrind::Plugin(context){};
  void instructionExecuted(const oclgrind::WorkItem *workItem,
                           const llvm::Instruction *instruction,
                           const oclgrind::TypedValue& result) override
  {
    std::cout << "Work-Item " << workItem->getGlobalID() << ": ";
    oclgrind::dumpInstruction(std::cout, instruction);
    std::cout << std::endl;
  }
};

extern "C"
{
  void initializePlugins(oclgrind::Context *context)
  {
    context->registerPlugin(new InstPrinter(context));
  }
}

On Linux we can compile this plugin into a dynamic library with the following command:

c++ -shared -fPIC -fno-rtti -std=c++11 -O3 -Wall InstTracer.cpp -loclgrind -o libInstTracer.so

We can then load this plugin when running Oclgrind as follows:

oclgrind --plugins /path/to/plugin/libInstTracer.so ./my_application

Additional example plugins can be found in src/plugins/ (note that these plugins are compiled into Oclgrind and do not have dynamic plugin loading points).

The simulation performed by Oclgrind uses multithreading to improve performance, which means that the plugin callbacks may be called from multiple threads concurrently. Plugins need to implement their callbacks in a thread safe manner, or signal to Oclgrind that they are not thread safe by overriding the Plugin::isThreadSafe() function and returning false. The latter approach will cause the entire simulation to be serialised.

Clone this wiki locally