Skip to content

Commit

Permalink
provider: add 'profile' provider
Browse files Browse the repository at this point in the history
The profile provider supports profiling by allowing the user to specify
how many times it will fire pre-second. Values of 1-1000 are supported,
and the profile provider supports two probe formats as it's original
implementation commit 12b8ca2("profile provider support").

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

Test this changes with 4 cores x86-64 PC as below.

$ sudo ./ply 'profile:2:100hz { @[cpu]=count(); } i:1s { print(@); }'
@:
{  2 }: 100

@:
{  2 }: 200

@:
{  2 }: 300

$ sudo ./ply 'profile:100hz { @[cpu]=count(); } i:1s { print(@); }'
@:
{  0 }: 100
{  1 }: 100
{  2 }: 100
{  3 }: 100

@:
{  0 }: 200
{  1 }: 200
{  2 }: 200
{  3 }: 200

@:
{  0 }: 300
{  1 }: 300
{  2 }: 300
{  3 }: 300

Change-Id: Ie669db20ed52947f5c75bf91c34f385a64029b19
Signed-off-by: Ism Hong <[email protected]>
  • Loading branch information
ismhong committed Aug 28, 2024
1 parent 8e1d79e commit 1ecde77
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 0 deletions.
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 <[email protected]>
*
* 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,
};
9 changes: 9 additions & 0 deletions test/rootfs/lib/ply/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -84,4 +84,13 @@ cat /tmp/tracepoint-dyn | awk '
END { exit(!(unames >= 10)); }' \
|| fail "at least 10 unames" "$(cat /tmp/tracepoint-dyn)"

case=profile
ply 'profile:0:100hz { @["profile_test"]=count(); }
i:1s { exit(0);}' >/tmp/profile \
&& \
cat /tmp/profile | awk -F': ' '
/profile_test/ { count = $2; }
END { exit(count!=100); }' \
|| fail "count should be 100 for 100hz sampling in 1 second" "$(cat /tmp/profile)"

exit $total_fails

0 comments on commit 1ecde77

Please sign in to comment.