/* -*- linux-c -*- * Print Functions * Copyright (C) 2007-2011 Red Hat Inc. * * This file is part of systemtap, and is free software. You can * redistribute it and/or modify it under the terms of the GNU General * Public License (GPL); either version 2, or (at your option) any * later version. */ #ifndef _STAPLINUX_PRINT_C_ #define _STAPLINUX_PRINT_C_ #include "stp_string.h" #include "print.h" #include "transport/transport.c" #include "vsprintf.c" /** @file print.c * Printing Functions. */ /** @addtogroup print Print Functions * The print buffer is for collecting output to send to the user daemon. * This is a per-cpu static buffer. The buffer is sent when * _stp_print_flush() is called. * * The reason to do this is to allow multiple small prints to be combined then * timestamped and sent together to staprun. This is more efficient than sending * numerous small packets. * * This function is called automatically when the print buffer is full. * It MUST also be called at the end of every probe that prints something. * @{ */ typedef struct __stp_pbuf { uint32_t len; /* bytes used in the buffer */ char buf[STP_BUFFER_SIZE]; } _stp_pbuf; static void *Stp_pbuf = NULL; /** private buffer for _stp_vlog() */ #ifndef STP_LOG_BUF_LEN #define STP_LOG_BUF_LEN 256 #endif typedef char _stp_lbuf[STP_LOG_BUF_LEN]; static void *Stp_lbuf = NULL; /* create percpu print and io buffers */ static int _stp_print_init (void) { Stp_pbuf = _stp_alloc_percpu(sizeof(_stp_pbuf)); if (unlikely(Stp_pbuf == 0)) return -1; /* now initialize IO buffer used in io.c */ Stp_lbuf = _stp_alloc_percpu(sizeof(_stp_lbuf)); if (unlikely(Stp_lbuf == 0)) { _stp_free_percpu(Stp_pbuf); return -1; } return 0; } static void _stp_print_cleanup (void) { if (Stp_pbuf) _stp_free_percpu(Stp_pbuf); if (Stp_lbuf) _stp_free_percpu(Stp_lbuf); } #define __DEF_EXPORT_FN(fn, postfix) fn ## _ ## postfix #define DEF_EXPORT_FN(fn, postfix) __DEF_EXPORT_FN(fn, postfix) #if defined(RELAY_GUEST) #if defined(RELAY_HOST) #error "Cannot specify both RELAY_HOST and RELAY_GUEST" #endif #define EXPORT_FN(fn) DEF_EXPORT_FN(fn, RELAY_GUEST) #elif defined(RELAY_HOST) #define EXPORT_FN(fn) DEF_EXPORT_FN(fn, RELAY_HOST) #else /* defined(RELAY_GUEST) || defined(RELAY_HOST) */ #define EXPORT_FN(fn) fn #endif #if !defined(RELAY_GUEST) #include "print_flush.c" #if defined(RELAY_HOST) EXPORT_SYMBOL_GPL(EXPORT_FN(stp_print_flush)); #endif #endif /*!RELAY_GUEST*/ #if defined(RELAY_GUEST) || defined(RELAY_HOST) /* Prohibit irqs to avoid racing on a relayfs */ extern void EXPORT_FN(stp_print_flush) (_stp_pbuf *); static inline void _stp_print_flush(void) { unsigned long flags; local_irq_save(flags); EXPORT_FN(stp_print_flush) (per_cpu_ptr(Stp_pbuf, smp_processor_id())); local_irq_restore(flags); } #else static inline void _stp_print_flush(void) { EXPORT_FN(stp_print_flush)(per_cpu_ptr(Stp_pbuf, smp_processor_id())); } #endif #ifndef STP_MAXBINARYARGS #define STP_MAXBINARYARGS 127 #endif /** Reserves space in the output buffer for direct I/O. */ static void * _stp_reserve_bytes (int numbytes) { _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id()); int size = STP_BUFFER_SIZE - pb->len; void * ret; if (unlikely(numbytes == 0 || numbytes > STP_BUFFER_SIZE)) return NULL; if (unlikely(numbytes > size)) _stp_print_flush(); ret = pb->buf + pb->len; pb->len += numbytes; return ret; } static void _stp_unreserve_bytes (int numbytes) { _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id()); if (unlikely(numbytes == 0 || numbytes > pb->len)) return; pb->len -= numbytes; } /** Write 64-bit args directly into the output stream. * This function takes a variable number of 64-bit arguments * and writes them directly into the output stream. Marginally faster * than doing the same in _stp_vsnprintf(). * @sa _stp_vsnprintf() */ static void _stp_print_binary (int num, ...) { va_list vargs; int i; int64_t *args; if (unlikely(num > STP_MAXBINARYARGS)) num = STP_MAXBINARYARGS; args = _stp_reserve_bytes(num * sizeof(int64_t)); if (likely(args != NULL)) { va_start(vargs, num); for (i = 0; i < num; i++) { args[i] = va_arg(vargs, int64_t); } va_end(vargs); } } /** Print into the print buffer. * Like C printf. * * @sa _stp_print_flush() */ static void _stp_printf (const char *fmt, ...) { va_list args; va_start(args, fmt); _stp_vsnprintf(NULL, 0, fmt, args); va_end(args); } /** Write a string into the print buffer. * @param str A C string (char *) */ static void _stp_print (const char *str) { _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id()); char *end = pb->buf + STP_BUFFER_SIZE; char *ptr = pb->buf + pb->len; char *instr = (char *)str; while (ptr < end && *instr) *ptr++ = *instr++; /* Did loop terminate due to lack of buffer space? */ if (unlikely(*instr)) { /* Don't break strings across subbufs. */ /* Restart after flushing. */ _stp_print_flush(); end = pb->buf + STP_BUFFER_SIZE; ptr = pb->buf + pb->len; instr = (char *)str; while (ptr < end && *instr) *ptr++ = *instr++; } pb->len = ptr - pb->buf; } static void _stp_print_char (const char c) { _stp_pbuf *pb = per_cpu_ptr(Stp_pbuf, smp_processor_id()); int size = STP_BUFFER_SIZE - pb->len; if (unlikely(1 >= size)) _stp_print_flush(); pb->buf[pb->len] = c; pb->len ++; } static void _stp_print_kernel_info(char *vstr, int ctx, int num_probes) { printk(KERN_DEBUG "%s: systemtap: %s, base: %p" ", memory: %ludata/%lutext/%uctx/%unet/%ualloc kb" ", probes: %d" #if ! STP_PRIVILEGE_CONTAINS (STP_PRIVILEGE, STP_PR_STAPDEV) ", unpriv-uid: %d" #endif "\n", THIS_MODULE->name, vstr, #ifndef STAPCONF_GRSECURITY THIS_MODULE->module_core, (unsigned long) (THIS_MODULE->core_size - THIS_MODULE->core_text_size)/1024, (unsigned long) (THIS_MODULE->core_text_size)/1024, #else THIS_MODULE->module_core_rx, (unsigned long) (THIS_MODULE->core_size_rw - THIS_MODULE->core_size_rx)/1024, (unsigned long) (THIS_MODULE->core_size_rx)/1024, #endif ctx/1024, _stp_allocated_net_memory/1024, (_stp_allocated_memory - _stp_allocated_net_memory - ctx)/1024, /* (un-double-counting net/ctx because they're also stp_alloc'd) */ num_probes #if ! STP_PRIVILEGE_CONTAINS (STP_PRIVILEGE, STP_PR_STAPDEV) , _stp_uid #endif ); } /** @} */ #endif /* _STAPLINUX_PRINT_C_ */