Logo Search packages:      
Sourcecode: qprof version File versions  Download package

prof_main.c

/*
 * Copyright (c) 2003 Hewlett-Packard Development Company, L.P.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE. 
 */

/*
 * The driver for the LD_PRELOAD-based profiler.
 * We initiate profiling in the main thread from a constructor.
 * We also intercept pthread_create calls to start up profiling
 * in other threads.
 */

#include "config.h"

#ifndef _GNU_SOURCE
#   define _GNU_SOURCE
#endif
# include <stdio.h>
# include <stdlib.h>
# include <pthread.h>
# include "prof_utils.h"
#ifdef HAVE_LIBUNWIND_H
# include <libunwind.h>
#else
# define NO_UNWIND
#endif
void QPROF_start(void) __attribute__ ((constructor));

static int profiling_started = 0;

static int prof_signal;

static FILE * QPROF_file = NULL;
static int QPROF_file_specified = 0;
static const char * QPROF_dir = 0;  /* Directory for Q_ style data.     */

static int QPROF_sigchld_caught = 0;
      /* The application catches SIGCHLD. */

static void QPROF_print() {
    if (QPROF_dir)
      QPROF_pc_sample_list_write_q_profile();
    if (!QPROF_dir || QPROF_file_specified)
      {
        /* Printing the profile may fork processes.  Make sure that     */
        /* any resulting SIGCHLD is ignored.                      */
          if (QPROF_sigchld_caught)
          {
                  QPROF_start_color(QPROF_file);
              fprintf(QPROF_file, "qprof: Forcing SIGCHLD to be ignored\n");
                  QPROF_end_color(QPROF_file);
            signal(SIGCHLD, SIG_IGN);
                }
        QPROF_pc_sample_list_print_profile(QPROF_file);
      }
}

/* We assume this is called before any threads are started. */
void QPROF_start()
{
    const char *QPROF_file_name = getenv("QPROF_FILE");
    
    QPROF_dir = getenv("Q_DIR");
    if (profiling_started) QPROF_error("Profiling restarted");
    profiling_started = 1;

    if (QPROF_file_name != NULL)
      {
      QPROF_file_specified = 1;
        QPROF_file = fopen(QPROF_file_name, "a");
        if (NULL == QPROF_file)
        {
          QPROF_warn("Can't open QPROF_FILE: using stderr.\n");
          QPROF_file = stderr; 
        }
      }
    else
      QPROF_file = stderr;
    QPROF_pc_sample_list_init();
    atexit(QPROF_print);
#   ifdef NO_UNWIND
      prof_signal = QPROF_setup_signals(QPROF_pc_sample_list_handler);
#   else 
      if (getenv("QPROF_STACK"))
        prof_signal = QPROF_setup_signals(QPROF_pc_sample_list_stack_handler);
      else
        prof_signal = QPROF_setup_signals(QPROF_pc_sample_list_handler);
#   endif /* !NO_UNWIND */
    profiling_started = 2;
}

typedef void * (* ptr_func)(void *);

struct startinfo {
    ptr_func si_start_routine;
    void * si_arg;
};

static void * my_start(void * arg)
{
    struct startinfo *si = (struct startinfo *)arg;
    ptr_func f = si -> si_start_routine;
    void * a = si -> si_arg;

    free(si);
    if (QPROF_setup_signals(QPROF_pc_sample_list_handler) != prof_signal)
      QPROF_error("Internal error: inconsistent profiling signal");
    return f(a);
}

/*
 * Intercept pthread_create, so thet we can set up profiling in the child.
 */

#define PRELOAD_WRAP

#include "wrap.h"

#define pre_pthread_create(thread, attr, start_routine, arg) \
  struct startinfo *si = malloc(sizeof(struct startinfo)); \
  si -> si_start_routine = start_routine; \
  si -> si_arg = arg; \
  start_routine = my_start; \
  arg = si;

#define post_pthread_create(result)

/* Initialization doesn't call pthread_create.  */
#define alt_pthread_create(thread, attr, start_routine, arg) (abort(), -1)

WRAP4(int, pthread_t *, const pthread_attr_t *, ptr_func, void *,
      pthread_create, "libpthread.so.0", pre_pthread_create,
      post_pthread_create, alt_pthread_create)

/*
 * Intercept sigaction and friends, so that we can handle executables
 * like gvim that try to shut down gracefully on unknown signals like SIGPROF.
 */

typedef void (*handler_ptr)(int);

#define sig_check(name, signum) \
  if (QPROF_file == NULL) QPROF_file = stderr; \
  if (signum == prof_signal && profiling_started != 1) \
    { \
      QPROF_start_color(QPROF_file); \
      fprintf(QPROF_file, "qprof: Ignoring %s(%d, ...)\n", name, signum); \
      QPROF_end_color(QPROF_file); \
      return 0; \
    } \
  if (signum == SIGCHLD && !QPROF_sigchld_caught) \
    { \
      QPROF_sigchld_caught = 1; \
    }

#define pre_sigaction(signum, act, oldact) \
  sig_check("sigaction", signum);

#define post_sigaction(result)

#define pre_sigvec(signum, vec, oldvec) \
  sig_check("sigvec", signum);

#define post_sigvec(result)

#define pre_signal(signum, handler) \
  sig_check("signal", signum);

#define post_signal(result)

#define pre_sigset(signum, handler) \
  sig_check("sigset", signum);

#define post_sigset(result)

/* Initialization doesn't call sigaction, we hope.    */
#define alt_sigaction(signum, act, oldact) (abort(), -1)
#define alt_sigvec(signum, vec, oldvec) (abort(), -1)
#define alt_signal(signum, handler) (abort(), (handler_ptr)(-1))
#define alt_sigset(signum, handler) (abort(), (handler_ptr)(-1))


WRAP3(int, int, const struct sigaction *, struct sigaction *,
      sigaction, "libc.so.6.1", pre_sigaction,
      post_sigaction, alt_sigaction)
WRAP3(int, int, const struct sigvec *, struct sigvec *,
      sigvec, "libc.so.6.1", pre_sigvec,
      post_sigvec, alt_sigvec)
WRAP2(handler_ptr, int, handler_ptr,
      signal, "libc.so.6.1", pre_signal,
      post_signal, alt_signal)
WRAP2(handler_ptr, int, handler_ptr,
      sigset, "libc.so.6.1", pre_sigset,
      post_sigset, alt_sigset)

/* Many linux kernels seem to have a bug in that timers are left  */
/* running after an exec call.  This may cause the child to die   */
/* before we get a chance to set up the profiling signal handler  */
/* again.  Thus we also intercept execve to explicitly ignore SIGPROF.  */
/* We perhaps should be intercepting other exec variants as well. */
#define pre_execve(path, argv, envp) \
  profiling_started = 1;  /* Make it clear that this is one of our calls. */ \
  signal(prof_signal, SIG_IGN)
#define post_execve(result)
#define alt_execve(signum, act, oldact) (abort(), -1)
WRAP3(int, const char *, char * const *, char * const *,
      execve, "libc.so.6.1", pre_execve,
      post_execve, alt_execve)

Generated by  Doxygen 1.6.0   Back to index