qemu/simpletrace.c
<<
>>
Prefs
   1/*
   2 * Simple trace backend
   3 *
   4 * Copyright IBM, Corp. 2010
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2.  See
   7 * the COPYING file in the top-level directory.
   8 *
   9 */
  10
  11#include <stdlib.h>
  12#include <stdint.h>
  13#include <stdio.h>
  14#include <time.h>
  15#include "qemu-timer.h"
  16#include "trace.h"
  17
  18/** Trace file header event ID */
  19#define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */
  20
  21/** Trace file magic number */
  22#define HEADER_MAGIC 0xf2b177cb0aa429b4ULL
  23
  24/** Trace file version number, bump if format changes */
  25#define HEADER_VERSION 0
  26
  27/** Trace buffer entry */
  28typedef struct {
  29    uint64_t event;
  30    uint64_t timestamp_ns;
  31    uint64_t x1;
  32    uint64_t x2;
  33    uint64_t x3;
  34    uint64_t x4;
  35    uint64_t x5;
  36    uint64_t x6;
  37} TraceRecord;
  38
  39enum {
  40    TRACE_BUF_LEN = 64 * 1024 / sizeof(TraceRecord),
  41};
  42
  43static TraceRecord trace_buf[TRACE_BUF_LEN];
  44static unsigned int trace_idx;
  45static FILE *trace_fp;
  46static char *trace_file_name = NULL;
  47static bool trace_file_enabled = false;
  48
  49void st_print_trace_file_status(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
  50{
  51    stream_printf(stream, "Trace file \"%s\" %s.\n",
  52                  trace_file_name, trace_file_enabled ? "on" : "off");
  53}
  54
  55static bool write_header(FILE *fp)
  56{
  57    static const TraceRecord header = {
  58        .event = HEADER_EVENT_ID,
  59        .timestamp_ns = HEADER_MAGIC,
  60        .x1 = HEADER_VERSION,
  61    };
  62
  63    return fwrite(&header, sizeof header, 1, fp) == 1;
  64}
  65
  66/**
  67 * set_trace_file : To set the name of a trace file.
  68 * @file : pointer to the name to be set.
  69 *         If NULL, set to the default name-<pid> set at config time.
  70 */
  71bool st_set_trace_file(const char *file)
  72{
  73    st_set_trace_file_enabled(false);
  74
  75    free(trace_file_name);
  76
  77    if (!file) {
  78        if (asprintf(&trace_file_name, CONFIG_TRACE_FILE, getpid()) < 0) {
  79            trace_file_name = NULL;
  80            return false;
  81        }
  82    } else {
  83        if (asprintf(&trace_file_name, "%s", file) < 0) {
  84            trace_file_name = NULL;
  85            return false;
  86        }
  87    }
  88
  89    st_set_trace_file_enabled(true);
  90    return true;
  91}
  92
  93static void flush_trace_file(void)
  94{
  95    /* If the trace file is not open yet, open it now */
  96    if (!trace_fp) {
  97        trace_fp = fopen(trace_file_name, "w");
  98        if (!trace_fp) {
  99            /* Avoid repeatedly trying to open file on failure */
 100            trace_file_enabled = false;
 101            return;
 102        }
 103        write_header(trace_fp);
 104    }
 105
 106    if (trace_fp) {
 107        size_t unused; /* for when fwrite(3) is declared warn_unused_result */
 108        unused = fwrite(trace_buf, trace_idx * sizeof(trace_buf[0]), 1, trace_fp);
 109    }
 110}
 111
 112void st_flush_trace_buffer(void)
 113{
 114    if (trace_file_enabled) {
 115        flush_trace_file();
 116    }
 117
 118    /* Discard written trace records */
 119    trace_idx = 0;
 120}
 121
 122void st_set_trace_file_enabled(bool enable)
 123{
 124    if (enable == trace_file_enabled) {
 125        return; /* no change */
 126    }
 127
 128    /* Flush/discard trace buffer */
 129    st_flush_trace_buffer();
 130
 131    /* To disable, close trace file */
 132    if (!enable) {
 133        fclose(trace_fp);
 134        trace_fp = NULL;
 135    }
 136
 137    trace_file_enabled = enable;
 138}
 139
 140static void trace(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3,
 141                  uint64_t x4, uint64_t x5, uint64_t x6)
 142{
 143    TraceRecord *rec = &trace_buf[trace_idx];
 144
 145    if (!trace_list[event].state) {
 146        return;
 147    }
 148
 149    rec->event = event;
 150    rec->timestamp_ns = get_clock();
 151    rec->x1 = x1;
 152    rec->x2 = x2;
 153    rec->x3 = x3;
 154    rec->x4 = x4;
 155    rec->x5 = x5;
 156    rec->x6 = x6;
 157
 158    if (++trace_idx == TRACE_BUF_LEN) {
 159        st_flush_trace_buffer();
 160    }
 161}
 162
 163void trace0(TraceEventID event)
 164{
 165    trace(event, 0, 0, 0, 0, 0, 0);
 166}
 167
 168void trace1(TraceEventID event, uint64_t x1)
 169{
 170    trace(event, x1, 0, 0, 0, 0, 0);
 171}
 172
 173void trace2(TraceEventID event, uint64_t x1, uint64_t x2)
 174{
 175    trace(event, x1, x2, 0, 0, 0, 0);
 176}
 177
 178void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3)
 179{
 180    trace(event, x1, x2, x3, 0, 0, 0);
 181}
 182
 183void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4)
 184{
 185    trace(event, x1, x2, x3, x4, 0, 0);
 186}
 187
 188void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5)
 189{
 190    trace(event, x1, x2, x3, x4, x5, 0);
 191}
 192
 193void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6)
 194{
 195    trace(event, x1, x2, x3, x4, x5, x6);
 196}
 197
 198/**
 199 * Flush the trace buffer on exit
 200 */
 201static void __attribute__((constructor)) st_init(void)
 202{
 203    atexit(st_flush_trace_buffer);
 204}
 205
 206void st_print_trace(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
 207{
 208    unsigned int i;
 209
 210    for (i = 0; i < trace_idx; i++) {
 211        stream_printf(stream, "Event %" PRIu64 " : %" PRIx64 " %" PRIx64
 212                      " %" PRIx64 " %" PRIx64 " %" PRIx64 " %" PRIx64 "\n",
 213                      trace_buf[i].event, trace_buf[i].x1, trace_buf[i].x2,
 214                      trace_buf[i].x3, trace_buf[i].x4, trace_buf[i].x5,
 215                      trace_buf[i].x6);
 216    }
 217}
 218
 219void st_print_trace_events(FILE *stream, int (*stream_printf)(FILE *stream, const char *fmt, ...))
 220{
 221    unsigned int i;
 222
 223    for (i = 0; i < NR_TRACE_EVENTS; i++) {
 224        stream_printf(stream, "%s [Event ID %u] : state %u\n",
 225                      trace_list[i].tp_name, i, trace_list[i].state);
 226    }
 227}
 228
 229static TraceEvent* find_trace_event_by_name(const char *tname)
 230{
 231    unsigned int i;
 232
 233    if (!tname) {
 234        return NULL;
 235    }
 236
 237    for (i = 0; i < NR_TRACE_EVENTS; i++) {
 238        if (!strcmp(trace_list[i].tp_name, tname)) {
 239            return &trace_list[i];
 240        }
 241    }
 242    return NULL; /* indicates end of list reached without a match */
 243}
 244
 245bool st_change_trace_event_state(const char *tname, bool tstate)
 246{
 247    TraceEvent *tp;
 248
 249    tp = find_trace_event_by_name(tname);
 250    if (tp) {
 251        tp->state = tstate;
 252        return true;
 253    }
 254    return false;
 255}
 256