How to Extend libCacheSim to Support New Custom Trace Types: A 4-Step Implementation Guide

Extending libCacheSim to support a new custom trace type requires four coordinated modifications: defining a new entry in the trace_type_e enumeration, registering a CLI string mapping, wiring the setup dispatcher, and implementing the *_setup and *_read_one_req interface functions.

The libCacheSim framework (available at 1a1a11a/libcachesim) processes workload traces through an abstraction layer that isolates format-specific parsing from cache simulation algorithms. Because all higher-level components—simulators, profilers, and plugins—consume the generic reader_t interface, adding support for a new custom trace type involves only reader-level changes without touching the core simulation logic.

Understanding the Trace Reader Architecture

libCacheSim follows a strict dispatch pattern that routes trace data from the command line to the specific parsing implementation. When a user invokes the simulator with -t mytrace, the framework executes a chain of lookups:

  1. String-to-enum conversion: The CLI argument maps to a trace_type_e value via trace_type_str_to_enum() in bin/cli_reader_utils.c.
  2. Reader initialization: The setup_reader() function in traceReader/reader.c creates a generic reader_t structure and dispatches to the format-specific setup routine through a switch statement.
  3. Request streaming: The simulator repeatedly calls the format-specific *_read_one_req() function, which decodes raw bytes into the request_t structure containing clock_time, obj_id, obj_size, and op fields.

This architecture ensures that once you implement the four integration points, the rest of the library treats your custom trace exactly like built-in formats such as LCS or Oracle General Binary.

Step 1 — Extend the Central Trace Type Enumeration

First, declare the new trace type identifier in the central enumeration. Open libCacheSim/include/libCacheSim/enum.h and add your entry to the trace_type_e typedef enum, ensuring it appears before UNKNOWN_TRACE:

typedef enum {
    /* existing entries … */
    VALPIN_TRACE,
    /* NEW entry – must be before UNKNOWN_TRACE */
    MYTRACE_TRACE,
    UNKNOWN_TRACE,
} __attribute__((__packed__)) trace_type_e;

In the same file, append the corresponding string name to the g_trace_type_name array, placing it at the same index as your enum value (before the "UNKNOWN_TRACE" entry). This array indexes up to UNKNOWN_TRACE + 2, so the string automatically aligns with your new enum value.

Step 2 — Register the CLI String Mapping

Next, map the user-facing CLI string to your enum value. In libCacheSim/bin/cli_reader_utils.c, locate the trace_type_str_to_enum() function and add an else-if clause:

else if (strcasecmp(trace_type_str, "mytrace") == 0) {
    return MYTRACE_TRACE;
}

This enables users to invoke your reader using the -t mytrace command-line option.

Step 3 — Wire the Reader Dispatcher

The setup_reader() function in libCacheSim/traceReader/reader.c acts as the central factory that initializes format-specific state. Add a case to the switch (trace_type) statement:

case MYTRACE_TRACE:
    myReader_setup(reader);
    break;

This dispatch ensures that when the CLI specifies your trace type, the framework calls your custom initialization routine before beginning the simulation loop.

Step 4 — Implement the Custom Reader Functions

Create a new reader implementation in libCacheSim/traceReader/customizedReader/. You must implement two mandatory functions that conform to the interface defined in include/libCacheSim/reader.h.

Setup Function

The setup routine initializes format-specific state, such as record sizes for binary files or delimiter settings for text parsers. Implement myReader_setup() to configure the reader_t structure:

/* myReader.c */
#include "myReader.h"

#define MY_RECORD_SIZE 24  /* 8-byte time, 8-byte obj_id, 4-byte size, 4-byte op */

int myReader_setup(reader_t *reader) {
    reader->item_size = MY_RECORD_SIZE;  /* Required for binary offset calculations */
    return 0;
}

Read Function

The read function decodes one request from the trace buffer and populates the request_t structure. It returns 0 on success and 1 on EOF:

int myReader_read_one_req(reader_t *reader, request_t *req) {
    const unsigned char *p = (const unsigned char *)reader->mapped_file
                            + reader->mmap_offset;

    if (reader->mmap_offset + MY_RECORD_SIZE > reader->file_size) {
        return 1;  /* EOF reached */
    }

    /* Little-endian unpacking – adapt byte order to your format */
    uint64_t time = *((uint64_t *)p);
    uint64_t obj  = *((uint64_t *)(p + 8));
    uint32_t size = *((uint32_t *)(p + 16));
    uint32_t op   = *((uint32_t *)(p + 20));

    req->clock_time = time;
    req->obj_id     = obj;
    req->obj_size   = (uint64_t)size;
    req->op         = (req_op_e)op;  /* Map to libCacheSim operation enum */

    reader->mmap_offset += MY_RECORD_SIZE;
    return 0;
}

Build Configuration

Finally, register your source files with the build system. Update libCacheSim/traceReader/customizedReader/CMakeLists.txt:

add_library(customReaders OBJECT
    myReader.c
    # … other custom readers

)
target_include_directories(customReaders PRIVATE ${PROJECT_SOURCE_DIR}/include)

After recompiling, invoke the simulator with your new trace type:

./cachesim -t mytrace -p /path/to/mytrace.bin

Summary

To extend libCacheSim to support new custom trace types, implement these four integration points:

  • Modify include/libCacheSim/enum.h: Add your trace type to trace_type_e and g_trace_type_name before the UNKNOWN_TRACE sentinel.
  • Update bin/cli_reader_utils.c: Register the CLI string-to-enum mapping in trace_type_str_to_enum().
  • Edit traceReader/reader.c: Add a dispatch case in setup_reader() to call your format-specific setup function.
  • Create reader implementation: Implement *_setup() and *_read_one_req() in traceReader/customizedReader/, then update the CMakeLists.txt to include your source files.

Once integrated, your trace reader operates transparently with all libCacheSim simulators, profilers, and analysis plugins via the generic reader_t interface.

Frequently Asked Questions

What are the mandatory functions required for a custom trace reader?

Every custom trace reader must implement two functions: int myReader_setup(reader_t *reader), which initializes format-specific state such as item_size or parser helpers, and int myReader_read_one_req(reader_t *reader, request_t *req), which decodes one request from the trace buffer and returns 0 on success or 1 on EOF. These signatures are defined in include/libCacheSim/reader.h and are called by the generic reader dispatcher in traceReader/reader.c.

Where should I place my custom reader source files?

Place your implementation in libCacheSim/traceReader/customizedReader/, following the pattern of existing readers like lcs.h and oracleGeneralBin.h. You must also update libCacheSim/traceReader/customizedReader/CMakeLists.txt to include your .c files in the customReaders object library so the linker resolves your symbols during the build.

Do I need to modify the cache simulator logic to use a new trace type?

No. The libCacheSim architecture decouples trace parsing from simulation logic. All simulators, profilers, and plugins consume the generic reader_t interface and request_t structure. Once you implement the four integration steps, existing tools like cachesim automatically support your new trace format through the standard -t CLI argument without further code changes.

How does libCacheSim handle different trace formats in custom readers?

The reader_t structure provides format-agnostic hooks. For binary traces, set reader->item_size in your setup function to enable memory-mapped offset calculations. For text or CSV traces, omit item_size and implement parsing logic directly in *_read_one_req() using standard I/O or string parsing. The framework supports both approaches through the same request_t output contract.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →