Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

provider: add 'profile' provider #90

Merged
merged 1 commit into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/ply/perf_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ int perf_event_attach(struct ply_probe *pb, const char *name,
int task_mode);
int perf_event_attach_raw(struct ply_probe *pb, int type, unsigned long config,
unsigned long long period, int task_mode);
int perf_event_attach_profile(struct ply_probe *pb, int cpu,
unsigned long long freq);

int perf_event_enable (int group_fd);
int perf_event_disable(int group_fd);
Expand Down
9 changes: 9 additions & 0 deletions man/ply.1.ronn
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,15 @@ Examples:
* _interval:1_: Called for every second
* _interval:500ms_: Called for every 500 milli-second

### profile

The profile provider supports profiling by allowing the user to specify
how many times it will fire per-second. Values of 1-1000 are supported,
and the profile provider supports two probe formats:

* profile:[N]hz: Profile on all CPUs N times per second
* profile:[C]:[N]hz: Profile on CPU C N times per second


## EXAMPLE

Expand Down
1 change: 1 addition & 0 deletions src/libply/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ libply_la_SOURCES = \
provider/kprobe.c \
provider/kprobe.h \
provider/kretprobe.c \
provider/profile.c \
provider/special.c \
provider/tracepoint.c \
provider/xprobe.c \
Expand Down
23 changes: 23 additions & 0 deletions src/libply/aux/perf_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,29 @@ int perf_event_attach_raw(struct ply_probe *pb, int type, unsigned long config,
return fd;
}

int perf_event_attach_profile(struct ply_probe *pb, int cpu,
unsigned long long freq)
{
struct perf_event_attr attr = {};
int fd;

attr.type = PERF_TYPE_SOFTWARE;
attr.config = PERF_COUNT_SW_CPU_CLOCK;
attr.sample_freq = freq;
attr.freq = 1;

fd = perf_event_open(&attr, -1, cpu, -1, 0);
if (fd < 0)
return -errno;

if (ioctl(fd, PERF_EVENT_IOC_SET_BPF, pb->bpf_fd)) {
close(fd);
return -errno;
}

return fd;
}

int perf_event_enable(int group_fd)
{
if (ioctl(group_fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP))
Expand Down
2 changes: 2 additions & 0 deletions src/libply/provider.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ extern struct provider built_in;
extern struct provider begin_provider;
extern struct provider end_provider;
extern struct provider interval;
extern struct provider profile;

struct provider *provider_get(const char *name)
{
Expand All @@ -45,6 +46,7 @@ void provider_init(void)
SLIST_INSERT_HEAD(&heads, &begin_provider, entry);
SLIST_INSERT_HEAD(&heads, &built_in, entry);
SLIST_INSERT_HEAD(&heads, &interval, entry);
SLIST_INSERT_HEAD(&heads, &profile, entry);
SLIST_INSERT_HEAD(&heads, &tracepoint, entry);
SLIST_INSERT_HEAD(&heads, &kretprobe, entry);
/* place kprobe at head so that 'k' can match first. */
Expand Down
123 changes: 123 additions & 0 deletions src/libply/provider/profile.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright Ism Hong <ism.hong@gmail.com>
*
* SPDX-License-Identifier: GPL-2.0
*/

#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include <ply/ply.h>
#include <ply/internal.h>


struct profile_data {
int cpu;
int ncpus;
unsigned long long freq;
int *evfds;
};

static int profile_sym_alloc(struct ply_probe *pb, struct node *n)
{
return -ENOENT;
}

static int profile_probe(struct ply_probe *pb)
{
int cpu = -1, ncpus = 0;
struct profile_data *data;
int freq = -1;

/*
* Expected format is either profile:[n]hz where n is a number between
* 1 and 1000, or profile:[c]:[n]hz where c is the CPU to profile.
*/
if (sscanf(pb->probe, "profile:%d:%dhz", &cpu, &freq) != 2) {
cpu = -1;
if (sscanf(pb->probe, "profile:%dhz", &freq) != 1)
return -EINVAL;
}

if (freq < 0 || freq > 1000)
return -EINVAL;

ncpus = sysconf(_SC_NPROCESSORS_ONLN);

if (cpu < -1 || cpu > ncpus)
return -EINVAL;

if (cpu >= 0)
ncpus = 1;

data = calloc(1, sizeof(*data));
if (!data)
return -ENOMEM;

data->evfds = calloc(ncpus, sizeof (int));
if (!data->evfds) {
free(data);
return -ENOMEM;
}

data->freq = (unsigned long long)freq;
data->cpu = cpu;
data->ncpus = ncpus ;

pb->provider_data = data;
return 0;
}

static int profile_attach(struct ply_probe *pb)
{
struct profile_data *data = pb->provider_data;
int cpu;

if (data->cpu != -1) {
data->evfds[0] = perf_event_attach_profile(pb, data->cpu,
data->freq);
if (data->evfds[0] < 0) {
_e("%s: Unable to attach profile probe: %s\n",
pb->probe, strerror(errno));
return data->evfds[0];
}
} else {
for (cpu = 0; cpu < data->ncpus; cpu++) {
data->evfds[cpu] = perf_event_attach_profile(pb, cpu, data->freq);
if (data->evfds[cpu] < 0) {
_e("%s: Unable to attach profile probe: %s\n",
pb->probe, strerror(errno));
return data->evfds[cpu];
}
}
}

return 0;
}

static int profile_detach(struct ply_probe *pb)
{
struct profile_data *data = pb->provider_data;

for (int i = 0; i < data->ncpus; i++) {
if (data->evfds[i] > 0)
close(data->evfds[i]);
}
free(data->evfds);
free(data);

return 0;
}

struct provider profile = {
.name = "profile",
.prog_type = BPF_PROG_TYPE_PERF_EVENT,

.sym_alloc = profile_sym_alloc,
.probe = profile_probe,

.attach = profile_attach,
.detach = profile_detach,
};
15 changes: 15 additions & 0 deletions test/rootfs/lib/ply/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,19 @@ cat /tmp/tracepoint-dyn | awk '
END { exit(!(unames >= 10)); }' \
|| fail "at least 10 unames" "$(cat /tmp/tracepoint-dyn)"

case=profile
ply 'BEGIN { printf("profile provider unit test\n"); c["profile_test"] = 0; }
profile:0:100hz
{
if (c["profile_test"] == 100)
exit(0);
else
c["profile_test"] = c["profile_test"] + 1;
}' >/tmp/profile \
&& \
cat /tmp/profile | awk -F': ' '
/profile_test/ { count = $2; }
END { exit(count != 100); }' \
|| fail "count should be 100 for profile provider test" "$(cat /tmp/profile)"

exit $total_fails