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

> Effortlessly extend libCacheSim for custom trace types. Follow our 4-step implementation guide to define new entries, register mappings, wire dispatchers, and implement interface functions.

- Repository: [Juncheng Yang/libcachesim](https://github.com/1a1a11a/libcachesim)
- Tags: how-to-guide
- Published: 2026-02-23

---

**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`](https://github.com/1a1a11a/libcachesim/blob/main/bin/cli_reader_utils.c).
2. **Reader initialization**: The `setup_reader()` function in [`traceReader/reader.c`](https://github.com/1a1a11a/libcachesim/blob/main/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`](https://github.com/1a1a11a/libcachesim/blob/main/libCacheSim/include/libCacheSim/enum.h) and add your entry to the `trace_type_e` typedef enum, ensuring it appears **before** `UNKNOWN_TRACE`:

```c
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`](https://github.com/1a1a11a/libcachesim/blob/main/libCacheSim/bin/cli_reader_utils.c), locate the `trace_type_str_to_enum()` function and add an else-if clause:

```c
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`](https://github.com/1a1a11a/libcachesim/blob/main/libCacheSim/traceReader/reader.c) acts as the central factory that initializes format-specific state. Add a case to the `switch (trace_type)` statement:

```c
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`](https://github.com/1a1a11a/libcachesim/blob/main/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:

```c
/* 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:

```c
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`](https://github.com/1a1a11a/libcachesim/blob/main/libCacheSim/traceReader/customizedReader/CMakeLists.txt):

```cmake
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:

```bash
./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`](https://github.com/1a1a11a/libcachesim/blob/main/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`](https://github.com/1a1a11a/libcachesim/blob/main/bin/cli_reader_utils.c)**: Register the CLI string-to-enum mapping in `trace_type_str_to_enum()`.
- **Edit [`traceReader/reader.c`](https://github.com/1a1a11a/libcachesim/blob/main/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`](https://github.com/1a1a11a/libcachesim/blob/main/include/libCacheSim/reader.h) and are called by the generic reader dispatcher in [`traceReader/reader.c`](https://github.com/1a1a11a/libcachesim/blob/main/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`](https://github.com/1a1a11a/libcachesim/blob/main/lcs.h) and [`oracleGeneralBin.h`](https://github.com/1a1a11a/libcachesim/blob/main/oracleGeneralBin.h). You must also update [`libCacheSim/traceReader/customizedReader/CMakeLists.txt`](https://github.com/1a1a11a/libcachesim/blob/main/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.