2018-08-16 18:10:15 +03:00
// SPDX-License-Identifier: LGPL-2.1
2012-04-06 02:47:53 +04:00
/*
* Copyright ( C ) 2009 Red Hat Inc , Steven Rostedt < srostedt @ redhat . com >
*
*/
2018-08-29 01:50:38 +03:00
# include "trace-seq.h"
2012-04-06 02:47:53 +04:00
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# include <stdarg.h>
2013-12-19 13:17:44 +04:00
# include <asm/bug.h>
2012-04-06 02:47:53 +04:00
# include "event-parse.h"
2012-04-06 02:47:55 +04:00
# include "event-utils.h"
2012-04-06 02:47:53 +04:00
/*
* The TRACE_SEQ_POISON is to catch the use of using
* a trace_seq structure after it was destroyed .
*/
# define TRACE_SEQ_POISON ((void *)0xdeadbeef)
# define TRACE_SEQ_CHECK(s) \
do { \
2013-12-19 13:17:44 +04:00
if ( WARN_ONCE ( ( s ) - > buffer = = TRACE_SEQ_POISON , \
" Usage of trace_seq after it was destroyed " ) ) \
( s ) - > state = TRACE_SEQ__BUFFER_POISONED ; \
2012-04-06 02:47:53 +04:00
} while ( 0 )
2013-12-19 13:17:44 +04:00
# define TRACE_SEQ_CHECK_RET_N(s, n) \
do { \
TRACE_SEQ_CHECK ( s ) ; \
if ( ( s ) - > state ! = TRACE_SEQ__GOOD ) \
return n ; \
} while ( 0 )
# define TRACE_SEQ_CHECK_RET(s) TRACE_SEQ_CHECK_RET_N(s, )
# define TRACE_SEQ_CHECK_RET0(s) TRACE_SEQ_CHECK_RET_N(s, 0)
2012-04-06 02:47:53 +04:00
/**
* trace_seq_init - initialize the trace_seq structure
* @ s : a pointer to the trace_seq structure to initialize
*/
void trace_seq_init ( struct trace_seq * s )
{
s - > len = 0 ;
s - > readpos = 0 ;
s - > buffer_size = TRACE_SEQ_BUF_SIZE ;
2014-01-15 05:45:26 +04:00
s - > buffer = malloc ( s - > buffer_size ) ;
if ( s - > buffer ! = NULL )
s - > state = TRACE_SEQ__GOOD ;
else
s - > state = TRACE_SEQ__MEM_ALLOC_FAILED ;
2012-04-06 02:47:53 +04:00
}
2013-06-04 09:20:19 +04:00
/**
* trace_seq_reset - re - initialize the trace_seq structure
* @ s : a pointer to the trace_seq structure to reset
*/
void trace_seq_reset ( struct trace_seq * s )
{
if ( ! s )
return ;
TRACE_SEQ_CHECK ( s ) ;
s - > len = 0 ;
s - > readpos = 0 ;
}
2012-04-06 02:47:53 +04:00
/**
* trace_seq_destroy - free up memory of a trace_seq
* @ s : a pointer to the trace_seq to free the buffer
*
* Only frees the buffer , not the trace_seq struct itself .
*/
void trace_seq_destroy ( struct trace_seq * s )
{
if ( ! s )
return ;
2013-12-19 13:17:44 +04:00
TRACE_SEQ_CHECK_RET ( s ) ;
2012-04-06 02:47:53 +04:00
free ( s - > buffer ) ;
s - > buffer = TRACE_SEQ_POISON ;
}
static void expand_buffer ( struct trace_seq * s )
{
2014-01-15 05:45:25 +04:00
char * buf ;
buf = realloc ( s - > buffer , s - > buffer_size + TRACE_SEQ_BUF_SIZE ) ;
if ( WARN_ONCE ( ! buf , " Can't allocate trace_seq buffer memory " ) ) {
2013-12-19 13:17:44 +04:00
s - > state = TRACE_SEQ__MEM_ALLOC_FAILED ;
2014-01-15 05:45:25 +04:00
return ;
}
s - > buffer = buf ;
s - > buffer_size + = TRACE_SEQ_BUF_SIZE ;
2012-04-06 02:47:53 +04:00
}
/**
* trace_seq_printf - sequence printing of trace information
* @ s : trace sequence descriptor
* @ fmt : printf format string
*
* It returns 0 if the trace oversizes the buffer ' s free
2018-12-01 07:08:10 +03:00
* space , the number of characters printed , or a negative
* value in case of an error .
2012-04-06 02:47:53 +04:00
*
* The tracer may use either sequence operations or its own
* copy to user routines . To simplify formating of a trace
* trace_seq_printf is used to store strings into a special
* buffer ( @ s ) . Then the output may be either used by
* the sequencer or pulled into another buffer .
*/
int
trace_seq_printf ( struct trace_seq * s , const char * fmt , . . . )
{
va_list ap ;
int len ;
int ret ;
try_again :
2013-12-19 13:17:44 +04:00
TRACE_SEQ_CHECK_RET0 ( s ) ;
2012-04-06 02:47:53 +04:00
len = ( s - > buffer_size - 1 ) - s - > len ;
va_start ( ap , fmt ) ;
ret = vsnprintf ( s - > buffer + s - > len , len , fmt , ap ) ;
va_end ( ap ) ;
if ( ret > = len ) {
expand_buffer ( s ) ;
goto try_again ;
}
2018-12-01 07:08:10 +03:00
if ( ret > 0 )
s - > len + = ret ;
2012-04-06 02:47:53 +04:00
2018-12-01 07:08:10 +03:00
return ret ;
2012-04-06 02:47:53 +04:00
}
/**
* trace_seq_vprintf - sequence printing of trace information
* @ s : trace sequence descriptor
* @ fmt : printf format string
*
2018-12-01 07:08:10 +03:00
* It returns 0 if the trace oversizes the buffer ' s free
* space , the number of characters printed , or a negative
* value in case of an error .
* *
2012-04-06 02:47:53 +04:00
* The tracer may use either sequence operations or its own
* copy to user routines . To simplify formating of a trace
* trace_seq_printf is used to store strings into a special
* buffer ( @ s ) . Then the output may be either used by
* the sequencer or pulled into another buffer .
*/
int
trace_seq_vprintf ( struct trace_seq * s , const char * fmt , va_list args )
{
int len ;
int ret ;
try_again :
2013-12-19 13:17:44 +04:00
TRACE_SEQ_CHECK_RET0 ( s ) ;
2012-04-06 02:47:53 +04:00
len = ( s - > buffer_size - 1 ) - s - > len ;
ret = vsnprintf ( s - > buffer + s - > len , len , fmt , args ) ;
if ( ret > = len ) {
expand_buffer ( s ) ;
goto try_again ;
}
2018-12-01 07:08:10 +03:00
if ( ret > 0 )
s - > len + = ret ;
2012-04-06 02:47:53 +04:00
2018-12-01 07:08:10 +03:00
return ret ;
2012-04-06 02:47:53 +04:00
}
/**
* trace_seq_puts - trace sequence printing of simple string
* @ s : trace sequence descriptor
* @ str : simple string to record
*
* The tracer may use either the sequence operations or its own
* copy to user routines . This function records a simple string
* into a special buffer ( @ s ) for later retrieval by a sequencer
* or other mechanism .
*/
int trace_seq_puts ( struct trace_seq * s , const char * str )
{
int len ;
2013-12-19 13:17:44 +04:00
TRACE_SEQ_CHECK_RET0 ( s ) ;
2012-04-06 02:47:53 +04:00
len = strlen ( str ) ;
while ( len > ( ( s - > buffer_size - 1 ) - s - > len ) )
expand_buffer ( s ) ;
2013-12-19 13:17:44 +04:00
TRACE_SEQ_CHECK_RET0 ( s ) ;
2012-04-06 02:47:53 +04:00
memcpy ( s - > buffer + s - > len , str , len ) ;
s - > len + = len ;
return len ;
}
int trace_seq_putc ( struct trace_seq * s , unsigned char c )
{
2013-12-19 13:17:44 +04:00
TRACE_SEQ_CHECK_RET0 ( s ) ;
2012-04-06 02:47:53 +04:00
while ( s - > len > = ( s - > buffer_size - 1 ) )
expand_buffer ( s ) ;
2013-12-19 13:17:44 +04:00
TRACE_SEQ_CHECK_RET0 ( s ) ;
2012-04-06 02:47:53 +04:00
s - > buffer [ s - > len + + ] = c ;
return 1 ;
}
void trace_seq_terminate ( struct trace_seq * s )
{
2013-12-19 13:17:44 +04:00
TRACE_SEQ_CHECK_RET ( s ) ;
2012-04-06 02:47:53 +04:00
/* There's always one character left on the buffer */
s - > buffer [ s - > len ] = 0 ;
}
2015-02-03 18:44:09 +03:00
int trace_seq_do_fprintf ( struct trace_seq * s , FILE * fp )
2012-04-06 02:47:53 +04:00
{
TRACE_SEQ_CHECK ( s ) ;
2013-12-19 13:17:44 +04:00
switch ( s - > state ) {
case TRACE_SEQ__GOOD :
2015-02-03 18:44:09 +03:00
return fprintf ( fp , " %.*s " , s - > len , s - > buffer ) ;
2013-12-19 13:17:44 +04:00
case TRACE_SEQ__BUFFER_POISONED :
2015-02-03 18:44:09 +03:00
fprintf ( fp , " %s \n " , " Usage of trace_seq after it was destroyed " ) ;
2013-12-19 13:17:44 +04:00
break ;
case TRACE_SEQ__MEM_ALLOC_FAILED :
2015-02-03 18:44:09 +03:00
fprintf ( fp , " %s \n " , " Can't allocate trace_seq buffer memory " ) ;
2013-12-19 13:17:44 +04:00
break ;
}
return - 1 ;
2012-04-06 02:47:53 +04:00
}
2015-02-03 18:44:09 +03:00
int trace_seq_do_printf ( struct trace_seq * s )
{
return trace_seq_do_fprintf ( s , stdout ) ;
}