linux/tools/lib/traceevent/trace-seq.c
<<
>>
Prefs
   1// SPDX-License-Identifier: LGPL-2.1
   2/*
   3 * Copyright (C) 2009 Red Hat Inc, Steven Rostedt <srostedt@redhat.com>
   4 *
   5 */
   6#include "trace-seq.h"
   7
   8#include <stdio.h>
   9#include <stdlib.h>
  10#include <string.h>
  11#include <stdarg.h>
  12
  13#include <asm/bug.h>
  14#include "event-parse.h"
  15#include "event-utils.h"
  16
  17/*
  18 * The TRACE_SEQ_POISON is to catch the use of using
  19 * a trace_seq structure after it was destroyed.
  20 */
  21#define TRACE_SEQ_POISON        ((void *)0xdeadbeef)
  22#define TRACE_SEQ_CHECK(s)                                              \
  23do {                                                                    \
  24        if (WARN_ONCE((s)->buffer == TRACE_SEQ_POISON,                  \
  25                      "Usage of trace_seq after it was destroyed"))     \
  26                (s)->state = TRACE_SEQ__BUFFER_POISONED;                \
  27} while (0)
  28
  29#define TRACE_SEQ_CHECK_RET_N(s, n)             \
  30do {                                            \
  31        TRACE_SEQ_CHECK(s);                     \
  32        if ((s)->state != TRACE_SEQ__GOOD)      \
  33                return n;                       \
  34} while (0)
  35
  36#define TRACE_SEQ_CHECK_RET(s)   TRACE_SEQ_CHECK_RET_N(s, )
  37#define TRACE_SEQ_CHECK_RET0(s)  TRACE_SEQ_CHECK_RET_N(s, 0)
  38
  39/**
  40 * trace_seq_init - initialize the trace_seq structure
  41 * @s: a pointer to the trace_seq structure to initialize
  42 */
  43void trace_seq_init(struct trace_seq *s)
  44{
  45        s->len = 0;
  46        s->readpos = 0;
  47        s->buffer_size = TRACE_SEQ_BUF_SIZE;
  48        s->buffer = malloc(s->buffer_size);
  49        if (s->buffer != NULL)
  50                s->state = TRACE_SEQ__GOOD;
  51        else
  52                s->state = TRACE_SEQ__MEM_ALLOC_FAILED;
  53}
  54
  55/**
  56 * trace_seq_reset - re-initialize the trace_seq structure
  57 * @s: a pointer to the trace_seq structure to reset
  58 */
  59void trace_seq_reset(struct trace_seq *s)
  60{
  61        if (!s)
  62                return;
  63        TRACE_SEQ_CHECK(s);
  64        s->len = 0;
  65        s->readpos = 0;
  66}
  67
  68/**
  69 * trace_seq_destroy - free up memory of a trace_seq
  70 * @s: a pointer to the trace_seq to free the buffer
  71 *
  72 * Only frees the buffer, not the trace_seq struct itself.
  73 */
  74void trace_seq_destroy(struct trace_seq *s)
  75{
  76        if (!s)
  77                return;
  78        TRACE_SEQ_CHECK_RET(s);
  79        free(s->buffer);
  80        s->buffer = TRACE_SEQ_POISON;
  81}
  82
  83static void expand_buffer(struct trace_seq *s)
  84{
  85        char *buf;
  86
  87        buf = realloc(s->buffer, s->buffer_size + TRACE_SEQ_BUF_SIZE);
  88        if (WARN_ONCE(!buf, "Can't allocate trace_seq buffer memory")) {
  89                s->state = TRACE_SEQ__MEM_ALLOC_FAILED;
  90                return;
  91        }
  92
  93        s->buffer = buf;
  94        s->buffer_size += TRACE_SEQ_BUF_SIZE;
  95}
  96
  97/**
  98 * trace_seq_printf - sequence printing of trace information
  99 * @s: trace sequence descriptor
 100 * @fmt: printf format string
 101 *
 102 * It returns 0 if the trace oversizes the buffer's free
 103 * space, the number of characters printed, or a negative
 104 * value in case of an error.
 105 *
 106 * The tracer may use either sequence operations or its own
 107 * copy to user routines. To simplify formating of a trace
 108 * trace_seq_printf is used to store strings into a special
 109 * buffer (@s). Then the output may be either used by
 110 * the sequencer or pulled into another buffer.
 111 */
 112int
 113trace_seq_printf(struct trace_seq *s, const char *fmt, ...)
 114{
 115        va_list ap;
 116        int len;
 117        int ret;
 118
 119 try_again:
 120        TRACE_SEQ_CHECK_RET0(s);
 121
 122        len = (s->buffer_size - 1) - s->len;
 123
 124        va_start(ap, fmt);
 125        ret = vsnprintf(s->buffer + s->len, len, fmt, ap);
 126        va_end(ap);
 127
 128        if (ret >= len) {
 129                expand_buffer(s);
 130                goto try_again;
 131        }
 132
 133        if (ret > 0)
 134                s->len += ret;
 135
 136        return ret;
 137}
 138
 139/**
 140 * trace_seq_vprintf - sequence printing of trace information
 141 * @s: trace sequence descriptor
 142 * @fmt: printf format string
 143 *
 144 * It returns 0 if the trace oversizes the buffer's free
 145 * space, the number of characters printed, or a negative
 146 * value in case of an error.
 147 * *
 148 * The tracer may use either sequence operations or its own
 149 * copy to user routines. To simplify formating of a trace
 150 * trace_seq_printf is used to store strings into a special
 151 * buffer (@s). Then the output may be either used by
 152 * the sequencer or pulled into another buffer.
 153 */
 154int
 155trace_seq_vprintf(struct trace_seq *s, const char *fmt, va_list args)
 156{
 157        int len;
 158        int ret;
 159
 160 try_again:
 161        TRACE_SEQ_CHECK_RET0(s);
 162
 163        len = (s->buffer_size - 1) - s->len;
 164
 165        ret = vsnprintf(s->buffer + s->len, len, fmt, args);
 166
 167        if (ret >= len) {
 168                expand_buffer(s);
 169                goto try_again;
 170        }
 171
 172        if (ret > 0)
 173                s->len += ret;
 174
 175        return ret;
 176}
 177
 178/**
 179 * trace_seq_puts - trace sequence printing of simple string
 180 * @s: trace sequence descriptor
 181 * @str: simple string to record
 182 *
 183 * The tracer may use either the sequence operations or its own
 184 * copy to user routines. This function records a simple string
 185 * into a special buffer (@s) for later retrieval by a sequencer
 186 * or other mechanism.
 187 */
 188int trace_seq_puts(struct trace_seq *s, const char *str)
 189{
 190        int len;
 191
 192        TRACE_SEQ_CHECK_RET0(s);
 193
 194        len = strlen(str);
 195
 196        while (len > ((s->buffer_size - 1) - s->len))
 197                expand_buffer(s);
 198
 199        TRACE_SEQ_CHECK_RET0(s);
 200
 201        memcpy(s->buffer + s->len, str, len);
 202        s->len += len;
 203
 204        return len;
 205}
 206
 207int trace_seq_putc(struct trace_seq *s, unsigned char c)
 208{
 209        TRACE_SEQ_CHECK_RET0(s);
 210
 211        while (s->len >= (s->buffer_size - 1))
 212                expand_buffer(s);
 213
 214        TRACE_SEQ_CHECK_RET0(s);
 215
 216        s->buffer[s->len++] = c;
 217
 218        return 1;
 219}
 220
 221void trace_seq_terminate(struct trace_seq *s)
 222{
 223        TRACE_SEQ_CHECK_RET(s);
 224
 225        /* There's always one character left on the buffer */
 226        s->buffer[s->len] = 0;
 227}
 228
 229int trace_seq_do_fprintf(struct trace_seq *s, FILE *fp)
 230{
 231        TRACE_SEQ_CHECK(s);
 232
 233        switch (s->state) {
 234        case TRACE_SEQ__GOOD:
 235                return fprintf(fp, "%.*s", s->len, s->buffer);
 236        case TRACE_SEQ__BUFFER_POISONED:
 237                fprintf(fp, "%s\n", "Usage of trace_seq after it was destroyed");
 238                break;
 239        case TRACE_SEQ__MEM_ALLOC_FAILED:
 240                fprintf(fp, "%s\n", "Can't allocate trace_seq buffer memory");
 241                break;
 242        }
 243        return -1;
 244}
 245
 246int trace_seq_do_printf(struct trace_seq *s)
 247{
 248        return trace_seq_do_fprintf(s, stdout);
 249}
 250