/*
* Copyright (c) 2020 Red Hat, Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* $Id: //eng/vdo-releases/aluminum/src/c++/vdo/base/trace.h#1 $
*/
#ifndef TRACE_H
#define TRACE_H
#ifndef __KERNEL__
#include "cpu.h"
#endif
#include "threads.h"
/*
* We need these records to be glued together with no intervening
* bytes. That makes it rather sensitive to how the compiler,
* assembler, and linker may add padding. Force extra alignment to
* make it more reliable.
*
* Trace point descriptor language:
*
* The descriptor string provided at a trace point can have one or
* more components, separated by ";". The first (or only) component is
* a string to be formatted and shown in the flowchart graph. The
* remaining components must be of the form "var=string", and assign
* string values to "variables" that last through the processing of
* the remainder of the current trace being read.
*
* The string displayed has variable substitutions done for any
* occurrences of "$var" in the string.
*
* So, the descriptor sequence:
* kvdoWriteVIO;io=writeData;j=normal
* submitBio($io)
* writeJournalBlock($j)
* would cause the graph generator to show the strings:
* kvdoWriteVIO
* submitBio(writeData)
* writeJournalBlock(normal)
*
* Substitutions are done in the variable assignment strings when
* they're processed, so "foo=x($bar)" sets "foo" using the current
* value of "bar"; it doesn't cause "bar" to be looked up when "$foo"
* is seen later.
*
* The variable named "F" is automatically updated with the name of
* the function associated with the descriptor, so you don't have to
* explicitly repeat the name of the function if you just want to
* augment it with more information. This may be desirable if a trace
* point is expected to be reached more than once at different stages
* of processing, or in a static function with a generic-sounding name
* that needs disambiguation for graphing.
*
* If no descriptor string is provided, the
* function:lineNumber:threadName string reported via systemtap will
* be used in the graph.
*
* Current variable names used:
* cb=(various) random info to log when enqueueing VIO callback
* dup=post,update deduplication operation
* io=(various) kind of I/O and data it's being done on
* j=normal,dedupe kind of journal update being done
* js=mapWrite,writeZero,unmap which step of journaling we're doing
*/
typedef const struct __attribute__((aligned(16))) traceLocationRecord {
const char *function;
int line;
const char *description;
} TraceLocationRecord;
/*
* With well under 100 locations defined at the moment, even with no
* idea where &baseTraceLocation will fall relative to the others, we
* only need to support a range of -100..+100.
*/
typedef int32_t TraceLocationNumber;
/* The type to pass around */
typedef TraceLocationRecord *TraceLocation;
/*
* N.B.: This code uses GCC extensions to create static, initialized
* objects inline, describing the current function and line number.
* The objects are collected into a table we can index with small
* signed integers relative to &baseTraceLocation.
*
* We need baseTraceLocation because there's no standard way to get
* the address of the start of this array we're defining. And because
* we're not playing any (additional) special linker tricks to ensure
* ordering of the object files, the offsets may be signed, and we
* don't know the range beyond the fact that we don't have hundreds of
* these records lying around.
*
* By specifying a name that starts with neither .data nor .rodata, we
* leave it to the toolchain to pick a location for us, based on
* things like whether the section needs write access, which it does
* for a PIC library but not for a kernel module.
*/
#define TRACE_LOCATION_SECTION \
__attribute__((section(".kvdo_trace_locations")))
extern TRACE_LOCATION_SECTION TraceLocationRecord baseTraceLocation[];
#define TRACE_JOIN2(a,b) a##b
#define TRACE_JOIN(a,b) TRACE_JOIN2(a,b)
#define THIS_LOCATION(DESCRIPTION) \
__extension__ \
({ \
static TRACE_LOCATION_SECTION \
TraceLocationRecord TRACE_JOIN(loc,__LINE__) = { \
.function = __func__, \
.line = __LINE__, \
.description = DESCRIPTION, \
}; \
&TRACE_JOIN(loc,__LINE__); \
})
typedef struct traceRecord {
uint64_t when; // counted in usec
pid_t tid;
TraceLocationNumber location;
} TraceRecord;
enum { NUM_TRACE_RECORDS = 71 };
typedef struct trace {
unsigned int used;
TraceRecord records[NUM_TRACE_RECORDS];
} Trace;
/**
* Store a new record in the trace data.
*
* @param trace The trace data to be updated
* @param location The source-location descriptor to be recorded
**/
void addTraceRecord(Trace *trace, TraceLocation location);
/**
* Format trace data into a string for logging.
*
* @param [in] trace The trace data to be logged
* @param [in] buffer The buffer in which to store the string
* @param [in] bufferLength Length of the buffer
* @param [out] msgLen Length of the formatted string
**/
void formatTrace(Trace *trace,
char *buffer,
size_t bufferLength,
size_t *msgLen);
#endif /* TRACE_H */