aboutsummaryrefslogtreecommitdiff
path: root/simpletrace.c
blob: 477730871c70950b36c80dfc9184ad0085fbd0b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/*
 * Simple trace backend
 *
 * Copyright IBM, Corp. 2010
 *
 * This work is licensed under the terms of the GNU GPL, version 2.  See
 * the COPYING file in the top-level directory.
 *
 */

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <time.h>
#include "trace.h"

/** Trace file header event ID */
#define HEADER_EVENT_ID (~(uint64_t)0) /* avoids conflicting with TraceEventIDs */

/** Trace file magic number */
#define HEADER_MAGIC 0xf2b177cb0aa429b4ULL

/** Trace file version number, bump if format changes */
#define HEADER_VERSION 0

/** Trace buffer entry */
typedef struct {
    uint64_t event;
    uint64_t timestamp_ns;
    uint64_t x1;
    uint64_t x2;
    uint64_t x3;
    uint64_t x4;
    uint64_t x5;
    uint64_t x6;
} TraceRecord;

enum {
    TRACE_BUF_LEN = 64 * 1024 / sizeof(TraceRecord),
};

static TraceRecord trace_buf[TRACE_BUF_LEN];
static unsigned int trace_idx;
static FILE *trace_fp;

static bool write_header(FILE *fp)
{
    static const TraceRecord header = {
        .event = HEADER_EVENT_ID,
        .timestamp_ns = HEADER_MAGIC,
        .x1 = HEADER_VERSION,
    };

    return fwrite(&header, sizeof header, 1, fp) == 1;
}

static void flush_trace_buffer(void)
{
    if (!trace_fp) {
        trace_fp = fopen("trace.log", "w");
        if (trace_fp) {
            write_header(trace_fp);
        }
    }
    if (trace_fp) {
        size_t unused; /* for when fwrite(3) is declared warn_unused_result */
        unused = fwrite(trace_buf, trace_idx * sizeof(trace_buf[0]), 1, trace_fp);
    }

    /* Discard written trace records */
    trace_idx = 0;
}

void st_set_trace_file_enabled(bool enable)
{
    if (enable == trace_file_enabled) {
        return; /* no change */
    }

    /* Flush/discard trace buffer */
    st_flush_trace_buffer();

    /* To disable, close trace file */
    if (!enable) {
        fclose(trace_fp);
        trace_fp = NULL;
    }

    trace_file_enabled = enable;
}

static void trace(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3,
                  uint64_t x4, uint64_t x5, uint64_t x6)
{
    TraceRecord *rec = &trace_buf[trace_idx];
    struct timespec ts;

    /* TODO Windows?  It would be good to use qemu-timer here but that isn't
     * linked into qemu-tools.  Also we should avoid recursion in the tracing
     * code, therefore it is useful to be self-contained.
     */
    clock_gettime(CLOCK_MONOTONIC, &ts);

    rec->event = event;
    rec->timestamp_ns = ts.tv_sec * 1000000000LL + ts.tv_nsec;
    rec->x1 = x1;
    rec->x2 = x2;
    rec->x3 = x3;
    rec->x4 = x4;
    rec->x5 = x5;
    rec->x6 = x6;

    if (++trace_idx == TRACE_BUF_LEN) {
        flush_trace_buffer();
    }
}

void trace0(TraceEventID event)
{
    trace(event, 0, 0, 0, 0, 0, 0);
}

void trace1(TraceEventID event, uint64_t x1)
{
    trace(event, x1, 0, 0, 0, 0, 0);
}

void trace2(TraceEventID event, uint64_t x1, uint64_t x2)
{
    trace(event, x1, x2, 0, 0, 0, 0);
}

void trace3(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3)
{
    trace(event, x1, x2, x3, 0, 0, 0);
}

void trace4(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4)
{
    trace(event, x1, x2, x3, x4, 0, 0);
}

void trace5(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5)
{
    trace(event, x1, x2, x3, x4, x5, 0);
}

void trace6(TraceEventID event, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, uint64_t x5, uint64_t x6)
{
    trace(event, x1, x2, x3, x4, x5, x6);
}

/**
 * Flush the trace buffer on exit
 */
static void __attribute__((constructor)) st_init(void)
{
    atexit(st_flush_trace_buffer);
}