runtime/linux/print.c - systemtap

Global variables defined

Data types defined

Functions defined

Macros defined

Source code

/* -*- 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_ */