Skip to content

Instantly share code, notes, and snippets.

@pkhuong
Created February 18, 2020 02:21
Show Gist options
  • Save pkhuong/97ab1f9fea2dfb972094ae7bdf664f73 to your computer and use it in GitHub Desktop.
Save pkhuong/97ab1f9fea2dfb972094ae7bdf664f73 to your computer and use it in GitHub Desktop.
Trigger a unix signal based on the HW instruction counter PMC
#define RUN_ME /*
exec cc -g -ggdb -O2 -W -Wall -std=c99 $0 -o "$(basename $0 .c)"
*/
/*
* Copyright 2020 Paul Khuong
* SPDX-License-Identifier: BSD-2-Clause
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include <assert.h>
#include <fcntl.h>
#include <linux/perf_event.h>
#include <stdint.h>
#include <sys/ioctl.h>
#include <sys/syscall.h>
#include <unistd.h>
/*
* We want to use perf to count HW events (userspace instructions),
* and trigger an interrupt on overflow. That used to be a common
* setup, less so these days, so 2010-era documentation might come in
* handy.
*/
static pid_t gettid_(void)
{
return syscall(__NR_gettid);
}
static long perf_event_open(struct perf_event_attr* hw_event, pid_t pid, int cpu,
int group_fd, unsigned long flags)
{
return syscall(__NR_perf_event_open, hw_event, (uintptr_t)pid, (uintptr_t)cpu,
(uintptr_t)group_fd, (uintptr_t)flags);
}
/*
* Opens a perf fd that counts instructions for the current thread.
*
* The counter is initially disabled, and triggers an interrupt after
* every instruction; the sample period must be overridden to
* something useful, and the counter enabled.
*/
static int open_perf_fd(void)
{
struct perf_event_attr pe = {
.size = sizeof(pe),
.exclude_kernel = 1,
.disabled = 1,
.exclude_hv = 1,
.type = PERF_TYPE_HARDWARE,
.config = PERF_COUNT_HW_INSTRUCTIONS,
.sample_period = 1
};
return perf_event_open(&pe, gettid_(), /*cpu=*/-1,
/*group_fd=*/-1, PERF_FLAG_FD_CLOEXEC);
}
/*
* Triggers a signal when the perf fd encounters an event, and
* overrides the sample period at which events are triggered,
* before enabling the perf counter.
*/
static void signal_on_perf_fd(int fd, uint64_t period, int signal)
{
int r;
/* Enable async io signals. */
r = fcntl(fd, F_SETFL, O_ASYNC);
assert(r == 0);
/* We want the signal. */
r = fcntl(fd, F_SETOWN, getpid());
assert(r == 0);
/* Set the counter overflow signal. */
r = fcntl(fd, F_SETSIG, signal);
assert(r == 0);
/* Reset the counter period. */
r = ioctl(fd, PERF_EVENT_IOC_PERIOD, &period);
assert(r == 0);
/* Do the thing! */
r = ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
assert(r == 0);
return;
}
int main()
{
int perf_fd = open_perf_fd();
/* Ask for SIG50 (a real-time signal) after ~10K instructions. */
signal_on_perf_fd(perf_fd, 10000, 50);
for (size_t i = 0;; i++)
__asm__ volatile("" : "+r"(i));
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment