Raw File
tracing.cpp
#include "HalideRuntime.h"
#include "printer.h"
#include "scoped_spin_lock.h"

extern "C" {

typedef int32_t (*trace_fn)(void *, const halide_trace_event *);

}

namespace Halide { namespace Runtime { namespace Internal {

WEAK int halide_trace_file = 0;
WEAK int halide_trace_file_lock = 0;
WEAK bool halide_trace_file_initialized = false;
WEAK bool halide_trace_file_internally_opened = false;

WEAK int32_t default_trace(void *user_context, const halide_trace_event *e) {
    static int32_t ids = 1;

    int32_t my_id = __sync_fetch_and_add(&ids, 1);

    // If we're dumping to a file, use a binary format
    int fd = halide_get_trace_file(user_context);
    if (fd > 0) {
        // A 48-byte header. The first 6 bytes are metadata, then the rest is a zero-terminated string.
        uint8_t clamped_width = e->type.lanes < 256 ? e->type.lanes : 255;
        uint8_t clamped_dimensions = e->dimensions < 256 ? e->dimensions : 255;

        // Upgrade the bit count to a power of two, because that's
        // how it will be stored on the stack.
        int bytes = 1;
        while (bytes*8 < e->type.bits) bytes <<= 1;

        // Compute the size of each portion of the tracing packet
        size_t header_bytes = 48;
        size_t value_bytes = clamped_width * bytes;
        size_t int_arg_bytes = clamped_dimensions * sizeof(int32_t);
        size_t total_bytes = header_bytes + value_bytes + int_arg_bytes;
        uint8_t buffer[4096];
        halide_assert(user_context, total_bytes <= 4096 && "Tracing packet too large");

        ((int32_t *)buffer)[0] = my_id;
        ((int32_t *)buffer)[1] = e->parent_id;
        buffer[8] = e->event;
        buffer[9] = e->type.code;
        buffer[10] = e->type.bits;
        buffer[11] = clamped_width;
        buffer[12] = e->value_index;
        buffer[13] = clamped_dimensions;

        // Use up to 33 bytes for the function name
        size_t i = 14;
        for (; i < header_bytes-1; i++) {
            buffer[i] = e->func[i-14];
            if (buffer[i] == 0) break;
        }
        // Fill the rest with zeros
        for (; i < header_bytes; i++) {
            buffer[i] = 0;
        }

        // Next comes the value
        for (size_t i = 0; i < value_bytes; i++) {
            buffer[header_bytes + i] = ((uint8_t *)(e->value))[i];
        }

        // Then the int args
        for (size_t i = 0; i < int_arg_bytes; i++) {
            buffer[header_bytes + value_bytes + i] = ((uint8_t *)(e->coordinates))[i];
        }

        {
            ScopedSpinLock lock(&halide_trace_file_lock);
            size_t written = write(fd, &buffer[0], total_bytes);
            halide_assert(user_context, written == total_bytes && "Can't write to trace file");
        }

    } else {
        stringstream ss(user_context);

        // Round up bits to 8, 16, 32, or 64
        int print_bits = 8;
        while (print_bits < e->type.bits) print_bits <<= 1;
        halide_assert(user_context, print_bits <= 64 && "Tracing bad type");

        // Otherwise, use halide_print and a plain-text format
        const char *event_types[] = {"Load",
                                     "Store",
                                     "Begin realization",
                                     "End realization",
                                     "Produce",
                                     "Consume",
                                     "End consume",
                                     "Begin pipeline",
                                     "End pipeline"};

        // Only print out the value on stores and loads.
        bool print_value = (e->event < 2);

        ss << event_types[e->event] << " " << e->func << "." << e->value_index << "(";
        if (e->type.lanes > 1) {
            ss << "<";
        }
        for (int i = 0; i < e->dimensions; i++) {
            if (i > 0) {
                if ((e->type.lanes > 1) && (i % e->type.lanes) == 0) {
                    ss << ">, <";
                } else {
                    ss << ", ";
                }
            }
            ss << e->coordinates[i];
        }
        if (e->type.lanes > 1) {
            ss << ">)";
        } else {
            ss << ")";
        }

        if (print_value) {
            if (e->type.lanes > 1) {
                ss << " = <";
            } else {
                ss << " = ";
            }
            for (int i = 0; i < e->type.lanes; i++) {
                if (i > 0) {
                    ss << ", ";
                }
                if (e->type.code == 0) {
                    if (print_bits == 8) {
                        ss << ((int8_t *)(e->value))[i];
                    } else if (print_bits == 16) {
                        ss << ((int16_t *)(e->value))[i];
                    } else if (print_bits == 32) {
                        ss << ((int32_t *)(e->value))[i];
                    } else {
                        ss << ((int64_t *)(e->value))[i];
                    }
                } else if (e->type.code == 1) {
                    if (print_bits == 8) {
                        ss << ((uint8_t *)(e->value))[i];
                    } else if (print_bits == 16) {
                        ss << ((uint16_t *)(e->value))[i];
                    } else if (print_bits == 32) {
                        ss << ((uint32_t *)(e->value))[i];
                    } else {
                        ss << ((uint64_t *)(e->value))[i];
                    }
                } else if (e->type.code == 2) {
                    halide_assert(user_context, print_bits >= 16 && "Tracing a bad type");
                    if (print_bits == 32) {
                        ss << ((float *)(e->value))[i];
                    } else if (print_bits == 16) {
                        ss.write_float16_from_bits( ((uint16_t *)(e->value))[i]);
                    } else {
                        ss << ((double *)(e->value))[i];
                    }
                } else if (e->type.code == 3) {
                    ss << ((void **)(e->value))[i];
                }
            }
            if (e->type.lanes > 1) {
                ss << ">";
            }
        }
        ss << "\n";

        {
            ScopedSpinLock lock(&halide_trace_file_lock);
            halide_print(user_context, ss.str());
        }
    }

    return my_id;
}

WEAK trace_fn halide_custom_trace = default_trace;

}}} // namespace Halide::Runtime::Internal

extern "C" {

WEAK trace_fn halide_set_custom_trace(trace_fn t) {
    trace_fn result = halide_custom_trace;
    halide_custom_trace = t;
    return result;
}

WEAK void halide_set_trace_file(int fd) {
    halide_trace_file = fd;
    halide_trace_file_initialized = true;
}

extern int errno;

#define O_APPEND 1024
#define O_CREAT 64
#define O_WRONLY 1
WEAK int halide_get_trace_file(void *user_context) {
    // Prevent multiple threads both trying to initialize the trace
    // file at the same time.
    ScopedSpinLock lock(&halide_trace_file_lock);
    if (!halide_trace_file_initialized) {
        const char *trace_file_name = getenv("HL_TRACE_FILE");
        if (trace_file_name) {
            int fd = open(trace_file_name, O_APPEND | O_CREAT | O_WRONLY, 0644);
            halide_assert(user_context, (fd > 0) && "Failed to open trace file\n");
            halide_set_trace_file(fd);
            halide_trace_file_internally_opened = true;
        } else {
            halide_set_trace_file(0);
        }
    }
    return halide_trace_file;
}

WEAK int32_t halide_trace(void *user_context, const halide_trace_event *e) {
    return (*halide_custom_trace)(user_context, e);
}

WEAK int halide_shutdown_trace() {
    if (halide_trace_file_internally_opened) {
        int ret = close(halide_trace_file);
        halide_trace_file = 0;
        halide_trace_file_initialized = false;
        halide_trace_file_internally_opened = false;
        return ret;
    } else {
        return 0;
    }
}

namespace {
__attribute__((destructor))
WEAK void halide_trace_cleanup() {
    halide_shutdown_trace();
}
}

}
back to top