diff options
author | Will Cohen <wcohen@redhat.com> | 2010-02-17 10:30:15 -0500 |
---|---|---|
committer | Josh Stone <jistone@redhat.com> | 2010-03-17 11:50:34 -0700 |
commit | 5d8efa6b59a399815e065d4f29ed64d01bdb6182 (patch) | |
tree | 7f70b243a56c9a88b0f339d3b0beae0630c26178 | |
parent | 6ccb243c59eff3ac89072b112414c50d6ef6cf3b (diff) | |
download | systemtap-steved-5d8efa6b59a399815e065d4f29ed64d01bdb6182.tar.gz systemtap-steved-5d8efa6b59a399815e065d4f29ed64d01bdb6182.tar.xz systemtap-steved-5d8efa6b59a399815e065d4f29ed64d01bdb6182.zip |
PR909: Runtime for Performance Event Sampling
Implements a very simple sampling runtime to using the performance
events kernel API. An perf event attribute describing the setup and a
function to handle the counter overflows are passed into
_stp_perf_init(). This function sets up the event on each processor.
If successfully initialized, a pointer data structure is
returned. When the sampling is no longer needed _stp_perf_del() is
called to shutdown the sampling.
* runtime/perf.h: Add declarations for data structures and functions
* runtime/perf.c: Remove old perfmon runtime runtime.
Add _stp_perf_init() and _stp_perf_del() functions.
-rw-r--r-- | runtime/perf.c | 160 | ||||
-rw-r--r-- | runtime/perf.h | 17 |
2 files changed, 69 insertions, 108 deletions
diff --git a/runtime/perf.c b/runtime/perf.c index 9ac8b481..06ed551a 100644 --- a/runtime/perf.c +++ b/runtime/perf.c @@ -1,6 +1,7 @@ /* -*- linux-c -*- * Perf Functions * Copyright (C) 2006 Red Hat Inc. + * Copyright (C) 2010 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 @@ -11,7 +12,7 @@ #ifndef _PERF_C_ #define _PERF_C_ -#include <linux/perfmon.h> +#include <linux/perf_event.h> #include "perf.h" @@ -19,117 +20,72 @@ * @brief Implements performance monitoring hardware support */ -/* TODO fix so this works on SMP machines - * Need to do context load, register setup, and start on each processor +/** Initialize performance sampling + * Call this during probe initialization to set up performance event sampling * - * Similarly need to stop and unload on each processor + * @param attr description of event to sample + * @param callback function to call when perf event overflows */ - -/* TODO make this work with sampling. There needs to be a help thread - * handling the sampling. */ - - -static int _stp_pfm_register_setup(void *desc, - struct pfarg_pmc pmc[], int pmc_count, - struct pfarg_pmd pmd[], int pmd_count) -{ - int err = 0; - - if (desc == 0) return -EINVAL; - err = pfmk_write_pmcs(desc, pmc, pmc_count); - if (err) return err; - - err = pfmk_write_pmds(desc, pmd, pmd_count); - return err; -} - -static struct completion c; -static struct pfarg_load load_args; -static struct pfarg_start start_args; - -/** Sets up the performance monitoring hardware. - * The locations desc and context point to are modified as - * side-effects of the setup. desc is a unique pointer used - * by the various routines. - * @param desc pointer to void *, handle to describe perfmon config - * @param context pointer to context information - * @param pmc, pointer to array describing control register setup - * @param pmc_count, number of entries in pmc - * @param pmd, pointer to array describing data register setup - * @param pmd_count, number of entries in pmd - * @returns an int, 0 if no errors encountered during setup - */ -static int _stp_perfmon_setup(void **desc, - struct pfarg_ctx *context, - struct pfarg_pmc pmc[], int pmc_count, - struct pfarg_pmd pmd[], int pmd_count) +static Perf _stp_perf_init (struct perf_event_attr *attr, + perf_overflow_handler_t callback) { - int err = 0; - - /* create a context */ - err = pfmk_create_context(context, NULL, 0, &c, desc, NULL); - if (err) goto cleanup; - - /* set up the counters */ - err = _stp_pfm_register_setup(*desc, pmc, pmc_count, pmd, pmd_count); - if (err) goto cleanup2; - - /* start measuring */ - err = pfmk_load_context(*desc, &load_args); - if (err) { - printk("pfmk_load_context error\n"); - goto cleanup2; + int cpu; + Perf pe; + + pe = (Perf) _stp_kmalloc (sizeof(struct _Perf)); + if (pe == NULL) + return NULL; + pe->callback = callback; + + /* allocate space for the event descriptor for each cpu */ + pe->pd = (struct perf_event **) + _stp_alloc_percpu (sizeof(struct perf_event *)); + if (pe->pd == NULL) + goto exit1; + + /* initialize event on each processor */ + stp_for_each_cpu(cpu) { + struct perf_event **event = per_cpu_ptr (pe->pd, cpu); + *event = perf_event_create_kernel_counter(attr, cpu, -1, + callback); + + if (IS_ERR(*event)) { + *event = NULL; + goto exit2; + } } - err = pfmk_start(*desc, &start_args); - if (err) { - printk("pfmk_start error\n"); - goto cleanup3; - } - - return err; - -cleanup3: pfmk_unload_context(*desc); -cleanup2: pfmk_close(*desc); -cleanup: *desc=NULL; - return err; -} - -/** Shuts down the performance monitoring hardware. - * @param desc unique pointer to describe configuration - * @returns an int, 0 if no errors encountered during shutdown - */ -static int _stp_perfmon_shutdown(void *desc) -{ - int err=0; + return pe; - if (desc == 0) return -EINVAL; - /* stop the counters */ - err=pfmk_stop(desc); - if (err) return err; - err=pfmk_unload_context(desc); - if (err) return err; - err=pfmk_close(desc); - return err; +exit2: + stp_for_each_cpu(cpu) { + struct perf_event **event = per_cpu_ptr (pe->pd, cpu); + if (*event) perf_event_release_kernel(*event); + } + _stp_free_percpu(pe->pd); +exit1: + _stp_kfree(pe); + return NULL; } -/** Reads the performance counter - * @param desc unique pointer to describe configuration - * @returns an int64, raw value of counter +/** Delete performance event. + * Call this to shutdown performance event sampling + * + * @param pe */ -static int64_t _stp_perfmon_read(void *desc, int counter) +static void _stp_perf_del (Perf pe) { - struct pfarg_pmd storage; - - storage.reg_set = 0; - storage.reg_num = counter; - - if ( desc != NULL) { - if (pfmk_read_pmds(desc, &storage, 1)) - printk( "pfm_read_pmds error\n"); + if (pe) { + int cpu; + /* shut down performance event sampling */ + stp_for_each_cpu(cpu) { + struct perf_event **event = per_cpu_ptr (pe->pd, cpu); + if (*event) { + perf_event_release_kernel(*event); + } + } + _stp_free_percpu (pe->pd); + _stp_kfree (pe); } - - return storage.reg_value; } #endif /* _PERF_C_ */ - diff --git a/runtime/perf.h b/runtime/perf.h index 6a87bff0..32383665 100644 --- a/runtime/perf.h +++ b/runtime/perf.h @@ -1,6 +1,7 @@ /* -*- linux-c -*- * Perf Header File * Copyright (C) 2006 Red Hat Inc. + * Copyright (C) 2010 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 @@ -15,13 +16,17 @@ * @brief Header file for performance monitoring hardware support */ -static int _stp_perfmon_setup(void **desc, - struct pfarg_ctx *context, - struct pfarg_pmc pmc[], int pmc_count, - struct pfarg_pmd pmd[], int pmd_count); +struct _Perf { + /* per-cpu data. allocated with _stp_alloc_percpu() */ + struct perf_event **pd; + perf_overflow_handler_t callback; +}; -static int _stp_perfmon_shutdown(void *desc); +typedef struct _Perf *Perf; -static int64_t _stp_perfmon_read(void *desc, int counter); +static Perf _stp_perf_init (struct perf_event_attr *attr, + perf_overflow_handler_t callback); + +static void _stp_perf_del (Perf pe); #endif /* _PERF_H_ */ |