/* * Copyright (c) 2014-2018 Remy Noel. * Copyright (c) 2014 Richard Braun. * * 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 3 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, see . * * Performance monitoring based on hardware performance counters. */ #ifndef KERN_PERFMON_H #define KERN_PERFMON_H #include #include #include /* * Performance event types. */ #define PERFMON_ET_GENERIC 0 #define PERFMON_ET_RAW 1 /* * IDs of generic performance events. */ #define PERFMON_EV_CYCLE 0 #define PERFMON_EV_REF_CYCLE 1 #define PERFMON_EV_INSTRUCTION 2 #define PERFMON_EV_CACHE_REF 3 #define PERFMON_EV_CACHE_MISS 4 #define PERFMON_EV_BRANCH 5 #define PERFMON_EV_BRANCH_MISS 6 #define PERFMON_NR_GENERIC_EVENTS 7 /* * Event flags. */ #define PERFMON_EF_KERN 0x1 /* Monitor events in kernel mode */ #define PERFMON_EF_USER 0x2 /* Monitor events in user mode */ #define PERFMON_EF_MASK (PERFMON_EF_KERN | PERFMON_EF_USER) /* * Pmu operations. * * Set by calling perfmon_register_pmu_ops. */ struct perfmon_pmu_ops { void (*info)(void); int (*translate)(unsigned int *raw_event_idp, unsigned int event_id); int (*alloc)(unsigned int *pmc_idp, unsigned int raw_event_id); void (*free)(unsigned int pmc_id); void (*start)(unsigned int pmc_id, unsigned int raw_event_id); void (*stop)(unsigned int pmc_id); uint64_t (*read)(unsigned int pmc_id); void (*write)(unsigned int pmc_id, uint64_t value); /* If set, of_max_ticks should be set to 0. */ void (*handle_of_intr)(void); }; /* * Pmu device description. */ struct perfmon_pmu_driver { uint8_t pmc_width; /* width in bits of a pmc */ /* * Maximum number of clock ticks between two overflow ckecks. * Should be set to 0 if handle_of_intr is set. */ uint64_t of_max_ticks; struct perfmon_pmu_ops ops; }; /* * Performance monitoring event. * * An event describes a single, well-defined state and records its * occurrences over a period of time. It must be added to exactly * one group before being used. */ struct perfmon_event; /* * Group of performance monitoring events. * * A group must be attached to either a thread or a processor, and abstracts * all operations on hardware counters. * * Until a group is actually attached, it is assumed there is only one * reference on it, owned by the caller. * * For a thread-attached group, it is the user's responsability to make sure * that perfmon_stop is always called before the monitored thread is deleted. */ struct perfmon_group; /* * Create an event. */ int perfmon_event_create(struct perfmon_event **eventp, unsigned int type, unsigned int id, int flags); /* * Destroy an event. * * Once an event is added to a group, it can only be destroyed by destroying * the group. */ void perfmon_event_destroy(struct perfmon_event *event); /* * Obtain the number of occurrences of an event. * * Events are updated at specific points in time, which means the value * returned by this function can be outdated. * * See perfmon_group_update() and perfmon_group_stop(). */ uint64_t perfmon_event_read(const struct perfmon_event *event); /* * Reset the number of occurrences of an event to 0. * * The group containing the given event should be stopped when calling * this function. */ void perfmon_event_reset(struct perfmon_event *event); /* * Create an event group. * * Events must be added to the group, which must then be attached to a * processor or a thread. */ int perfmon_group_create(struct perfmon_group **groupp); /* * Destroy a group and all its events. * * A group can only be destroyed once stopped and detached. * * Will return EINVAL if the group is not detached. */ int perfmon_group_destroy(struct perfmon_group *group); /* * Add an event into a group. * * Events can only be added when a group isn't attached. */ void perfmon_group_add(struct perfmon_group *group, struct perfmon_event *event); /* * Attach a group to, respectively, a thread or a processor, reserving * associated logical counter. * * A group can only be attached to one thread or processor at a time. */ int perfmon_group_attach(struct perfmon_group *group, struct thread *thread); int perfmon_group_attach_cpu(struct perfmon_group *group, unsigned int cpu); /* * Detach a group from a thread or a processor. * * It frees associated logical counters.. * * returns EINVAL if the group is still enabled (not stopped). */ int perfmon_group_detach(struct perfmon_group *group); /* * Start performance monitoring. * * A group must be attached before being started. */ int perfmon_group_start(struct perfmon_group *group); /* * Update all events in the given group. */ void perfmon_group_update(struct perfmon_group *group); /* * Stop performance monitoring. * * A group can't be detached before it's stopped. Events are implicitely * updated when calling this function. */ int perfmon_group_stop(struct perfmon_group *group); /* * Initialize perfmon thread-specific data for the given thread. */ int perfmon_thread_init(struct thread *thread); /* * Destroy perfmon thread-specific data for the given thread. */ void perfmon_thread_destroy(struct thread *thread); /* * Load/unload the events associated to a thread on the current processor. * * These functions should only be used by the scheduler during context switch. * Interrupts and preemption must be disabled when calling this function. */ void perfmon_thread_load(struct thread *thread); void perfmon_thread_unload(struct thread *thread); /* * This init operation provides : * - perfmon_thread_init() */ INIT_OP_DECLARE(perfmon_bootstrap); /* * This init operation provides : * - module fully initialized */ INIT_OP_DECLARE(perfmon_setup); /* * Handle overflow interrupt. */ void perfmon_of_intr(void); /* * Register an architecture-specific driver. */ int perfmon_pmu_register(struct perfmon_pmu_driver *driver); /* * Get the last value of given pmc. */ uint64_t perfmon_cpu_pmc_get_prev(unsigned int pmc_id); /* * Set the last value of given pmc. */ void perfmon_cpu_pmc_set_prev(unsigned int pmc_id, uint64_t prev); /* * Increment overflow counter for given pmc. */ void perfmon_cpu_pmc_inc_of(unsigned int pmc_id); #endif /* KERN_PERFMON_H */