linux/tools/testing/selftests/powerpc/pmu/ebb/trace.c
<<
>>
Prefs
   1/*
   2 * Copyright 2014, Michael Ellerman, IBM Corp.
   3 * Licensed under GPLv2.
   4 */
   5
   6#include <errno.h>
   7#include <stdio.h>
   8#include <stdlib.h>
   9#include <string.h>
  10#include <sys/mman.h>
  11
  12#include "trace.h"
  13
  14
  15struct trace_buffer *trace_buffer_allocate(u64 size)
  16{
  17        struct trace_buffer *tb;
  18
  19        if (size < sizeof(*tb)) {
  20                fprintf(stderr, "Error: trace buffer too small\n");
  21                return NULL;
  22        }
  23
  24        tb = mmap(NULL, size, PROT_READ | PROT_WRITE,
  25                  MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
  26        if (tb == MAP_FAILED) {
  27                perror("mmap");
  28                return NULL;
  29        }
  30
  31        tb->size = size;
  32        tb->tail = tb->data;
  33        tb->overflow = false;
  34
  35        return tb;
  36}
  37
  38static bool trace_check_bounds(struct trace_buffer *tb, void *p)
  39{
  40        return p < ((void *)tb + tb->size);
  41}
  42
  43static bool trace_check_alloc(struct trace_buffer *tb, void *p)
  44{
  45        /*
  46         * If we ever overflowed don't allow any more input. This prevents us
  47         * from dropping a large item and then later logging a small one. The
  48         * buffer should just stop when overflow happened, not be patchy. If
  49         * you're overflowing, make your buffer bigger.
  50         */
  51        if (tb->overflow)
  52                return false;
  53
  54        if (!trace_check_bounds(tb, p)) {
  55                tb->overflow = true;
  56                return false;
  57        }
  58
  59        return true;
  60}
  61
  62static void *trace_alloc(struct trace_buffer *tb, int bytes)
  63{
  64        void *p, *newtail;
  65
  66        p = tb->tail;
  67        newtail = tb->tail + bytes;
  68        if (!trace_check_alloc(tb, newtail))
  69                return NULL;
  70
  71        tb->tail = newtail;
  72
  73        return p;
  74}
  75
  76static struct trace_entry *trace_alloc_entry(struct trace_buffer *tb, int payload_size)
  77{
  78        struct trace_entry *e;
  79
  80        e = trace_alloc(tb, sizeof(*e) + payload_size);
  81        if (e)
  82                e->length = payload_size;
  83
  84        return e;
  85}
  86
  87int trace_log_reg(struct trace_buffer *tb, u64 reg, u64 value)
  88{
  89        struct trace_entry *e;
  90        u64 *p;
  91
  92        e = trace_alloc_entry(tb, sizeof(reg) + sizeof(value));
  93        if (!e)
  94                return -ENOSPC;
  95
  96        e->type = TRACE_TYPE_REG;
  97        p = (u64 *)e->data;
  98        *p++ = reg;
  99        *p++ = value;
 100
 101        return 0;
 102}
 103
 104int trace_log_counter(struct trace_buffer *tb, u64 value)
 105{
 106        struct trace_entry *e;
 107        u64 *p;
 108
 109        e = trace_alloc_entry(tb, sizeof(value));
 110        if (!e)
 111                return -ENOSPC;
 112
 113        e->type = TRACE_TYPE_COUNTER;
 114        p = (u64 *)e->data;
 115        *p++ = value;
 116
 117        return 0;
 118}
 119
 120int trace_log_string(struct trace_buffer *tb, char *str)
 121{
 122        struct trace_entry *e;
 123        char *p;
 124        int len;
 125
 126        len = strlen(str);
 127
 128        /* We NULL terminate to make printing easier */
 129        e = trace_alloc_entry(tb, len + 1);
 130        if (!e)
 131                return -ENOSPC;
 132
 133        e->type = TRACE_TYPE_STRING;
 134        p = (char *)e->data;
 135        memcpy(p, str, len);
 136        p += len;
 137        *p = '\0';
 138
 139        return 0;
 140}
 141
 142int trace_log_indent(struct trace_buffer *tb)
 143{
 144        struct trace_entry *e;
 145
 146        e = trace_alloc_entry(tb, 0);
 147        if (!e)
 148                return -ENOSPC;
 149
 150        e->type = TRACE_TYPE_INDENT;
 151
 152        return 0;
 153}
 154
 155int trace_log_outdent(struct trace_buffer *tb)
 156{
 157        struct trace_entry *e;
 158
 159        e = trace_alloc_entry(tb, 0);
 160        if (!e)
 161                return -ENOSPC;
 162
 163        e->type = TRACE_TYPE_OUTDENT;
 164
 165        return 0;
 166}
 167
 168static void trace_print_header(int seq, int prefix)
 169{
 170        printf("%*s[%d]: ", prefix, "", seq);
 171}
 172
 173static char *trace_decode_reg(int reg)
 174{
 175        switch (reg) {
 176                case 769: return "SPRN_MMCR2"; break;
 177                case 770: return "SPRN_MMCRA"; break;
 178                case 779: return "SPRN_MMCR0"; break;
 179                case 804: return "SPRN_EBBHR"; break;
 180                case 805: return "SPRN_EBBRR"; break;
 181                case 806: return "SPRN_BESCR"; break;
 182                case 800: return "SPRN_BESCRS"; break;
 183                case 801: return "SPRN_BESCRSU"; break;
 184                case 802: return "SPRN_BESCRR"; break;
 185                case 803: return "SPRN_BESCRRU"; break;
 186                case 771: return "SPRN_PMC1"; break;
 187                case 772: return "SPRN_PMC2"; break;
 188                case 773: return "SPRN_PMC3"; break;
 189                case 774: return "SPRN_PMC4"; break;
 190                case 775: return "SPRN_PMC5"; break;
 191                case 776: return "SPRN_PMC6"; break;
 192                case 780: return "SPRN_SIAR"; break;
 193                case 781: return "SPRN_SDAR"; break;
 194                case 768: return "SPRN_SIER"; break;
 195        }
 196
 197        return NULL;
 198}
 199
 200static void trace_print_reg(struct trace_entry *e)
 201{
 202        u64 *p, *reg, *value;
 203        char *name;
 204
 205        p = (u64 *)e->data;
 206        reg = p++;
 207        value = p;
 208
 209        name = trace_decode_reg(*reg);
 210        if (name)
 211                printf("register %-10s = 0x%016llx\n", name, *value);
 212        else
 213                printf("register %lld = 0x%016llx\n", *reg, *value);
 214}
 215
 216static void trace_print_counter(struct trace_entry *e)
 217{
 218        u64 *value;
 219
 220        value = (u64 *)e->data;
 221        printf("counter = %lld\n", *value);
 222}
 223
 224static void trace_print_string(struct trace_entry *e)
 225{
 226        char *str;
 227
 228        str = (char *)e->data;
 229        puts(str);
 230}
 231
 232#define BASE_PREFIX     2
 233#define PREFIX_DELTA    8
 234
 235static void trace_print_entry(struct trace_entry *e, int seq, int *prefix)
 236{
 237        switch (e->type) {
 238        case TRACE_TYPE_REG:
 239                trace_print_header(seq, *prefix);
 240                trace_print_reg(e);
 241                break;
 242        case TRACE_TYPE_COUNTER:
 243                trace_print_header(seq, *prefix);
 244                trace_print_counter(e);
 245                break;
 246        case TRACE_TYPE_STRING:
 247                trace_print_header(seq, *prefix);
 248                trace_print_string(e);
 249                break;
 250        case TRACE_TYPE_INDENT:
 251                trace_print_header(seq, *prefix);
 252                puts("{");
 253                *prefix += PREFIX_DELTA;
 254                break;
 255        case TRACE_TYPE_OUTDENT:
 256                *prefix -= PREFIX_DELTA;
 257                if (*prefix < BASE_PREFIX)
 258                        *prefix = BASE_PREFIX;
 259                trace_print_header(seq, *prefix);
 260                puts("}");
 261                break;
 262        default:
 263                trace_print_header(seq, *prefix);
 264                printf("entry @ %p type %d\n", e, e->type);
 265                break;
 266        }
 267}
 268
 269void trace_buffer_print(struct trace_buffer *tb)
 270{
 271        struct trace_entry *e;
 272        int i, prefix;
 273        void *p;
 274
 275        printf("Trace buffer dump:\n");
 276        printf("  address  %p \n", tb);
 277        printf("  tail     %p\n", tb->tail);
 278        printf("  size     %llu\n", tb->size);
 279        printf("  overflow %s\n", tb->overflow ? "TRUE" : "false");
 280        printf("  Content:\n");
 281
 282        p = tb->data;
 283
 284        i = 0;
 285        prefix = BASE_PREFIX;
 286
 287        while (trace_check_bounds(tb, p) && p < tb->tail) {
 288                e = p;
 289
 290                trace_print_entry(e, i, &prefix);
 291
 292                i++;
 293                p = (void *)e + sizeof(*e) + e->length;
 294        }
 295}
 296
 297void trace_print_location(struct trace_buffer *tb)
 298{
 299        printf("Trace buffer 0x%llx bytes @ %p\n", tb->size, tb);
 300}
 301