trace-seq.c (5523B)
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}