perf: Improve cgroup profiling (v3)
Namhyung Kim
maintainercover perf: Improve cgroup profiling (v3)
Hello,
This work is to improve cgroup profiling in perf. Currently it only supports profiling tasks in a specific cgroup and there's no way to identify which cgroup the current sample belongs to. So I added PERF_SAMPLE_CGROUP to add cgroup id into each sample. It's a 64-bit integer having file handle of the cgroup. And kernel also generates PERF_RECORD_CGROUP event for new groups to correlate the cgroup id and cgroup name (path in the cgroup filesystem). The cgroup id can be read from userspace by name_to_handle_at() system call so it can synthesize the CGROUP event for existing groups.
So why do we want this? Systems running a large number of jobs in different cgroups want to profiling such jobs precisely. This includes container hosting systems widely used today. Currently perf supports namespace tracking but the systems may not use (cgroup) namespace for their jobs. Also it'd be more intuitive to see cgroup names (as they're given by user or sysadmin) rather than numeric cgroup/namespace id even if they use the namespaces.
From Stephane Eranian:
Quoted 6 lines
In data centers you care about attributing samples to a job not such much to a process. A job may have multiple processes which may come and go. The cgroup on the other hand stays around for the entire lifetime of the job. It is much easier to map a cgroup name to a particular job than it is to map a pid back to a job name, especially for offline post-processing.
Note that this only works for "perf_event" cgroups (obviously) so if users are still using cgroup-v1 interface, they need to have same hierarchy for subsystem(s) want to profile with it.
* Changes from v2:
- remove path_len from cgroup_event
- bail out if kernel doesn't support cgroup sampling
- add some description in the Kconfig
* Changes from v1:
- use new cgroup id (= file handle)
The testing result looks something like this:
[root@qemu build]# ./perf record --all-cgroups ./cgtest
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.009 MB perf.data (150 samples) ]
[root@qemu build]# ./perf report -s cgroup,comm --stdio
# To display the perf.data header info, please use --header/--header-only options.
#
#
# Total Lost Samples: 0
#
# Samples: 150 of event 'cpu-clock:pppH'
# Event count (approx.): 37500000
#
# Overhead cgroup Command
# ........ .......... .......
#
32.00% /sub/cgrp2 looper2
28.00% /sub/cgrp1 looper1
25.33% /sub looper0
4.00% / cgtest
4.00% /sub cgtest
3.33% /sub/cgrp2 cgtest
2.67% /sub/cgrp1 cgtest
0.67% / looper0The test program (cgtest) follows.
Thanks,
Namhyung
-------8<-----------------------------------------8<---------------- #define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sched.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <sys/mount.h>
char cgbase[] = "/sys/fs/cgroup/perf_event";
void mkcgrp(char *name) {
char buf[256];
snprintf(buf, sizeof(buf), "%s%s", cgbase, name); if (mkdir(buf, 0755) < 0)
perror("mkdir");
}void rmcgrp(char *name) {
char buf[256];
snprintf(buf, sizeof(buf), "%s%s", cgbase, name); if (rmdir(buf) < 0)
perror("rmdir");
}void setcgrp(char *name) {
char buf[256];
int fd;
snprintf(buf, sizeof(buf), "%s%s/tasks", cgbase, name);
fd = open(buf, O_WRONLY);
if (fd > 0) {
if (write(fd, "0\n", 2) != 2)
perror("write");
close(fd); }
}
void create_sub_cgroup(int idx) {
char buf[128];
snprintf(buf, sizeof(buf), "/sub/cgrp%d", idx);
mkcgrp(buf);
}
void remove_sub_cgroup(int idx) {
char buf[128];
snprintf(buf, sizeof(buf), "/sub/cgrp%d", idx);
rmcgrp(buf);
}
void set_sub_cgroup(int idx) {
char buf[128];
snprintf(buf, sizeof(buf), "/sub/cgrp%d", idx);
setcgrp(buf);
}
void set_task_name(int idx) {
char buf[16];
snprintf(buf, sizeof(buf), "looper%d", idx);
prctl(PR_SET_NAME, buf, 0, 0, 0);
}
void loop(unsigned max) {
volatile unsigned int count = 1;
while (count++ != max) {
asm volatile ("pause"); }
}
void worker(int idx, unsigned cnt, int new_ns) {
int oldns;
create_sub_cgroup(idx);
set_sub_cgroup(idx);
if (new_ns) {
if (unshare(CLONE_NEWCGROUP) < 0)
perror("unshare");#if 0 /* FIXME */
if (unshare(CLONE_NEWNS) < 0)
perror("unshare");
if (mount("none", "/sys", NULL, MS_REMOUNT | MS_REC | MS_SLAVE, NULL) < 0)
perror("mount --make-rslave");
sleep(1);
if (umount("/sys/fs/cgroup/perf_event") < 0)
perror("umount");
if (mount("cgroup", "/sys/fs/cgroup/perf_event", "cgroup",
MS_NODEV | MS_NOEXEC | MS_NOSUID, "perf_event") < 0)
perror("mount again");#endif
}
if (fork() == 0) {
set_task_name(idx);
loop(cnt);
exit(0); }
wait(NULL);
}
int main(int argc, char *argv[])
{
int i, nr = 2;
int new_ns = 1;
unsigned cnt = 1000000;
int fd;
if (argc > 1)
nr = atoi(argv[1]);
if (argc > 2)
cnt = atoi(argv[2]);
if (argc > 3)
new_ns = atoi(argv[3]);
mkcgrp("/sub");
setcgrp("/sub");
for (i = 0; i < nr; i++) {
if (fork() == 0) {
worker(i+1, cnt, new_ns);
exit(0);
}
}
set_task_name(0);
loop(cnt);
for (i = 0; i < nr; i++)
wait(NULL);
for (i = 0; i < nr; i++)
remove_sub_cgroup(i+1);
setcgrp("/");
rmcgrp("/sub"); return 0;
} -------8<-----------------------------------------8<----------------
Namhyung Kim (9):
perf/core: Add PERF_RECORD_CGROUP event
perf/core: Add PERF_SAMPLE_CGROUP feature
perf tools: Basic support for CGROUP event
perf tools: Maintain cgroup hierarchy
perf report: Add 'cgroup' sort key
perf record: Support synthesizing cgroup events
perf record: Add --all-cgroups option
perf top: Add --all-cgroups option
perf script: Add --show-cgroup-events option
include/linux/perf_event.h | 1 + include/uapi/linux/perf_event.h | 16 ++- init/Kconfig | 3 +- kernel/events/core.c | 133 ++++++++++++++++++++++ tools/include/uapi/linux/perf_event.h | 16 ++- tools/perf/Documentation/perf-record.txt | 5 +- tools/perf/Documentation/perf-report.txt | 1 + tools/perf/Documentation/perf-script.txt | 3 + tools/perf/Documentation/perf-top.txt | 4 + tools/perf/builtin-diff.c | 1 + tools/perf/builtin-record.c | 10 ++ tools/perf/builtin-report.c | 1 + tools/perf/builtin-script.c | 41 +++++++ tools/perf/builtin-top.c | 9 ++ tools/perf/lib/include/perf/event.h | 7 ++ tools/perf/util/cgroup.c | 75 +++++++++++- tools/perf/util/cgroup.h | 16 ++- tools/perf/util/event.c | 19 ++++ tools/perf/util/event.h | 6 + tools/perf/util/evsel.c | 17 ++- tools/perf/util/evsel.h | 1 + tools/perf/util/hist.c | 7 ++ tools/perf/util/hist.h | 1 + tools/perf/util/machine.c | 19 ++++ tools/perf/util/machine.h | 3 + tools/perf/util/perf_event_attr_fprintf.c | 2 + tools/perf/util/record.h | 1 + tools/perf/util/session.c | 8 ++ tools/perf/util/sort.c | 31 +++++ tools/perf/util/sort.h | 2 + tools/perf/util/synthetic-events.c | 119 +++++++++++++++++++ tools/perf/util/synthetic-events.h | 1 + tools/perf/util/tool.h | 2 + 33 files changed, 568 insertions(+), 13 deletions(-)
Signature · 1 line
2.24.1.735.g03f4e72817-goog
Namhyung Kim
maintainerPATCH 1/9 perf/core: Add PERF_RECORD_CGROUP event
To support cgroup tracking, add CGROUP event to save a link between cgroup path and id number. This is needed since cgroups can go away when userspace tries to read the cgroup info (from the id) later. The attr.cgroup bit was also added to enable cgroup tracking from userspace.
This event will be generated when a new cgroup becomes active. Userspace might need to synthesize those events for existing cgroups.
--- include/uapi/linux/perf_event.h | 13 +++- kernel/events/core.c | 111 ++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-)
include/uapi/linux/perf_event.h +12 -1
@@ -377,7 +377,8 @@ struct perf_event_attr { ksymbol : 1, /* include ksymbol events */ bpf_event : 1, /* include bpf events */ aux_output : 1, /* generate AUX records instead of events */ - __reserved_1 : 32; + cgroup : 1, /* include cgroup events */ + __reserved_1 : 31;union {__u32 wakeup_events;/* wakeup every n events */
@@ -1006,6 +1007,16 @@ enum perf_event_type {*/ PERF_RECORD_BPF_EVENT =18 , +/* +* struct { +* struct perf_event_header header; +* u64 id; +* char path[]; +* struct sample_id sample_id; +* }; +*/ +PERF_RECORD_CGROUP =19 , + PERF_RECORD_MAX, /* non-ABI */ };
kernel/events/core.c +111 -0
147 lines not on this page · raw message
Signature · 1 line
2.24.1.735.g03f4e72817-goog
kbuild test robot
build botRe: [PATCH 1/9] perf/core: Add PERF_RECORD_CGROUP event
I love your patch! Perhaps something to improve:
Hi Namhyung,
I love your patch! Perhaps something to improve:
[auto build test WARNING on tip/auto-latest]
[also build test WARNING on linux/master linus/master v5.5-rc3 next-20191219] [cannot apply to tip/perf/core]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system. BTW, we also suggest to use '--base' option to specify the base tree in git format-patch, please see https://stackoverflow.com/a/37406982]
url: https://github.com/0day-ci/linux/commits/Namhyung-Kim/perf-Improve-cgroup-profiling-v3/20191225-035231 base: https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git ec7b10f2d023bd79cf067c60c194f72a6d672319 reproduce: # apt-get install sparse # sparse version: v0.6.1-129-g341daf20-dirty make ARCH=x86_64 allmodconfig make C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__'
If you fix the issue, kindly add following tag
sparse warnings: (new ones prefixed by >>)
kernel/events/core.c:572:26: sparse: sparse: function 'perf_pmu_name' with external linkage has definition kernel/events/core.c:1385:15: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:1385:15: sparse: struct perf_event_context [noderef] <asn:4> *
kernel/events/core.c:1385:15: sparse: struct perf_event_context *
kernel/events/core.c:1398:28: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:1398:28: sparse: struct perf_event_context [noderef] <asn:4> *
kernel/events/core.c:1398:28: sparse: struct perf_event_context *
kernel/events/core.c:3222:18: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:3222:18: sparse: struct perf_event_context [noderef] <asn:4> *
kernel/events/core.c:3222:18: sparse: struct perf_event_context *
kernel/events/core.c:3223:23: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:3223:23: sparse: struct perf_event_context [noderef] <asn:4> *
kernel/events/core.c:3223:23: sparse: struct perf_event_context *
kernel/events/core.c:3265:25: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:3265:25: sparse: struct perf_event_context [noderef] <asn:4> *
kernel/events/core.c:3265:25: sparse: struct perf_event_context *
kernel/events/core.c:3266:25: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:3266:25: sparse: struct perf_event_context [noderef] <asn:4> *
kernel/events/core.c:3266:25: sparse: struct perf_event_context *
kernel/events/core.c:4341:25: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:4341:25: sparse: struct perf_event_context [noderef] <asn:4> *
kernel/events/core.c:4341:25: sparse: struct perf_event_context *
kernel/events/core.c:5599:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:5599:9: sparse: struct ring_buffer [noderef] <asn:4> *
kernel/events/core.c:5599:9: sparse: struct ring_buffer *
kernel/events/core.c:5075:24: sparse: sparse: incorrect type in assignment (different base types)
kernel/events/core.c:5075:24: sparse: expected restricted __poll_t [usertype] events
kernel/events/core.c:5075:24: sparse: got int
kernel/events/core.c:5305:22: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:5305:22: sparse: struct ring_buffer [noderef] <asn:4> *
kernel/events/core.c:5305:22: sparse: struct ring_buffer *
kernel/events/core.c:5441:14: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:5441:14: sparse: struct ring_buffer [noderef] <asn:4> *
kernel/events/core.c:5441:14: sparse: struct ring_buffer *
kernel/events/core.c:5474:14: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:5474:14: sparse: struct ring_buffer [noderef] <asn:4> *
kernel/events/core.c:5474:14: sparse: struct ring_buffer *
kernel/events/core.c:5531:14: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:5531:14: sparse: struct ring_buffer [noderef] <asn:4> *
kernel/events/core.c:5531:14: sparse: struct ring_buffer *
kernel/events/core.c:5617:14: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:5617:14: sparse: struct ring_buffer [noderef] <asn:4> *
kernel/events/core.c:5617:14: sparse: struct ring_buffer *
kernel/events/core.c:5630:14: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:5630:14: sparse: struct ring_buffer [noderef] <asn:4> *
kernel/events/core.c:5630:14: sparse: struct ring_buffer *
kernel/events/internal.h:204:1: sparse: sparse: incorrect type in argument 2 (different address spaces)
kernel/events/internal.h:204:1: sparse: expected void const [noderef] <asn:1> *from
kernel/events/internal.h:204:1: sparse: got void const *buf
kernel/events/core.c:6305:6: sparse: sparse: symbol 'perf_pmu_snapshot_aux' was not declared. Should it be static? kernel/events/core.c:7094:23: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:7094:23: sparse: struct perf_event_context [noderef] <asn:4> *
kernel/events/core.c:7094:23: sparse: struct perf_event_context *
kernel/events/core.c:7185:13: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:7185:13: sparse: struct ring_buffer [noderef] <asn:4> *
kernel/events/core.c:7185:13: sparse: struct ring_buffer *
>> kernel/events/core.c:7618:6: sparse: sparse: symbol 'perf_event_cgroup' was not declared. Should it be static?
kernel/events/core.c:7972:23: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:7972:23: sparse: struct perf_event_context [noderef] <asn:4> *
kernel/events/core.c:7972:23: sparse: struct perf_event_context *
kernel/events/core.c:8702:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:8702:17: sparse: struct swevent_hlist [noderef] <asn:4> *
kernel/events/core.c:8702:17: sparse: struct swevent_hlist *
kernel/events/core.c:8722:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:8722:17: sparse: struct swevent_hlist [noderef] <asn:4> *
kernel/events/core.c:8722:17: sparse: struct swevent_hlist *
kernel/events/core.c:8842:16: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:8842:16: sparse: struct swevent_hlist [noderef] <asn:4> *
kernel/events/core.c:8842:16: sparse: struct swevent_hlist *
kernel/events/core.c:8853:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:8853:9: sparse: struct swevent_hlist [noderef] <asn:4> *
kernel/events/core.c:8853:9: sparse: struct swevent_hlist *
kernel/events/core.c:8842:16: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:8842:16: sparse: struct swevent_hlist [noderef] <asn:4> *
kernel/events/core.c:8842:16: sparse: struct swevent_hlist *
kernel/events/core.c:8892:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:8892:17: sparse: struct swevent_hlist [noderef] <asn:4> *
kernel/events/core.c:8892:17: sparse: struct swevent_hlist *
kernel/events/core.c:9073:23: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:9073:23: sparse: struct perf_event_context [noderef] <asn:4> *
kernel/events/core.c:9073:23: sparse: struct perf_event_context *
kernel/events/core.c:10244:1: sparse: sparse: symbol 'dev_attr_nr_addr_filters' was not declared. Should it be static? kernel/events/core.c:11967:9: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:11967:9: sparse: struct perf_event_context [noderef] <asn:4> *
kernel/events/core.c:11967:9: sparse: struct perf_event_context *
kernel/events/core.c:12077:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:12077:17: sparse: struct perf_event_context [noderef] <asn:4> *
kernel/events/core.c:12077:17: sparse: struct perf_event_context *
kernel/events/core.c:8842:16: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:8842:16: sparse: struct swevent_hlist [noderef] <asn:4> *
kernel/events/core.c:8842:16: sparse: struct swevent_hlist *
kernel/events/core.c:12501:17: sparse: sparse: incompatible types in comparison expression (different address spaces):
kernel/events/core.c:12501:17: sparse: struct swevent_hlist [noderef] <asn:4> *
kernel/events/core.c:12501:17: sparse: struct swevent_hlist *
kernel/events/core.c:155:9: sparse: sparse: context imbalance in 'perf_ctx_lock' - wrong count at exit kernel/events/core.c:163:17: sparse: sparse: context imbalance in 'perf_ctx_unlock' - unexpected unlock kernel/events/core.c:1405:17: sparse: sparse: context imbalance in 'perf_lock_task_context' - different lock contexts for basic block kernel/events/core.c:1432:17: sparse: sparse: context imbalance in 'perf_pin_task_context' - unexpected unlock kernel/events/core.c:2652:9: sparse: sparse: context imbalance in '__perf_install_in_context' - wrong count at exit kernel/events/core.c:4313:17: sparse: sparse: context imbalance in 'find_get_context' - unexpected unlock
Please review and possibly fold the followup patch.
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org Intel Corporation
kbuild test robot
build bot[RFC PATCH] perf/core: perf_event_cgroup() can be static
In reply to Namhyung KimFixes: 2e314b0982ba ("perf/core: Add PERF_RECORD_CGROUP event")
--- core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
kernel/events/core.c +1 -1
@@ -7615,7 +7615,7 @@ static void perf_event_cgroup_output(struct perf_event *event, void *data) cgroup_event->event_id.header.size = header_size; } -void perf_event_cgroup (struct cgroup *cgrp) +static void perf_event_cgroup (struct cgroup *cgrp) {struct perf_cgroup_event cgroup_event;char path_enomem[16 ] ="//enomem" ;
Peter Zijlstra
maintainerRe: [PATCH 1/9] perf/core: Add PERF_RECORD_CGROUP event
In reply to Namhyung KimQuoted 9 lines · Namhyung Kim
kernel/events/core.c +5
-0@@ -7564,6 +7567,105 @@ void perf_event_namespaces(struct task_struct *task) NULL); } +/* +* cgroup tracking +*/ +#ifdef CONFIG_CGROUPS +
<snip>
Quoted 21 lines
kernel/events/core.c +9
-0+ +#endif +/* * mmap tracking */ @@ -12581,6 +12685,12 @@ static void perf_cgroup_css_free(struct cgroup_subsys_state *css)kfree (jc); } +static int perf_cgroup_css_online (struct cgroup_subsys_state *css) +{ +perf_event_cgroup (css->cgroup); +return 0 ; +} +static int __perf_cgroup_move (void *info) {struct task_struct *task = info;
kernel/events/core.c static void perf_cgroup_attach(struct cgroup_taskset *tset) · line 12718
Quoted 7 lines
kernel/events/core.c +1
-0@@ -12602,6 +12712,7 @@ static void perf_cgroup_attach(struct cgroup_taskset *tset)struct cgroup_subsys perf_event_cgrp_subsys = { .css_alloc = perf_cgroup_css_alloc, .css_free = perf_cgroup_css_free, + .css_online = perf_cgroup_css_online, .attach = perf_cgroup_attach,/* * Implicitly enable on dfl hierarchy so that perf events can
CONFIG_CGROUPS vs CONFIG_CGROUP_PERF ?
Other than that, I see nothing wrong here.
Namhyung Kim
maintainerRe: [PATCH 1/9] perf/core: Add PERF_RECORD_CGROUP event
Hi Peter,
Happy new year!
Quoted 37 lines · Peter Zijlstra
Quoted 9 lines · Namhyung Kim
kernel/events/core.c +5
-0@@ -7564,6 +7567,105 @@ void perf_event_namespaces(struct task_struct *task) NULL); } +/* +* cgroup tracking +*/ +#ifdef CONFIG_CGROUPS +<snip>
Quoted 22 lines
kernel/events/core.c +9
-0+ +#endif +/* * mmap tracking */ @@ -12581,6 +12685,12 @@ static void perf_cgroup_css_free(struct cgroup_subsys_state *css)kfree (jc); } +static int perf_cgroup_css_online (struct cgroup_subsys_state *css) +{ +perf_event_cgroup (css->cgroup); +return 0 ; +} +static int __perf_cgroup_move (void *info) {struct task_struct *task = info;@@ -12602,6 +12712,7 @@ static void perf_cgroup_attach(struct cgroup_taskset *tset)struct cgroup_subsys perf_event_cgrp_subsys = {
Quoted 8 lines
kernel/events/core.c static void perf_cgroup_attach(struct cgroup_taskset *tset) · line 12718
Quoted 6 lines
kernel/events/core.c +1
-0.css_alloc = perf_cgroup_css_alloc, .css_free = perf_cgroup_css_free, + .css_online = perf_cgroup_css_online, .attach = perf_cgroup_attach,/* * Implicitly enable on dfl hierarchy so that perf events can
CONFIG_CGROUPSvsCONFIG_CGROUP_PERF?
Oh, I just saw this after sending v4 right before.. I think it should use CONFIG_CGROUP_PERF, will change.
Quoted 2 lines
Other than that, I see nothing wrong here.
Thanks for the review!
Namhyung
Namhyung Kim
maintainerPATCH 2/9 perf/core: Add PERF_SAMPLE_CGROUP feature
In reply to Namhyung KimThe PERF_SAMPLE_CGROUP bit is to save (perf_event) cgroup information in the sample. It will add a 64-bit id to identify current cgroup and it's the file handle in the cgroup file system. Userspace should use this information with PERF_RECORD_CGROUP event to match which cgroup it belongs.
I put it before PERF_SAMPLE_AUX for simplicity since it just needs a 64-bit word. But if we want bigger samples, I can work on that direction too.
--- include/linux/perf_event.h | 1 + include/uapi/linux/perf_event.h | 3 ++- init/Kconfig | 3 ++- kernel/events/core.c | 22 ++++++++++++++++++++++ 4 files changed, 27 insertions(+), 2 deletions(-)
include/linux/perf_event.h +1 -0
@@ -1001,6 +1001,7 @@ struct perf_sample_data {u64 stack_user_size;u64 phys_addr; +u64 cgroup; }____cacheline_aligned ;/* default value for data source */
include/uapi/linux/perf_event.h +2 -1
@@ -142,8 +142,9 @@ enum perf_event_sample_format {PERF_SAMPLE_REGS_INTR =1U <<18 ,PERF_SAMPLE_PHYS_ADDR =1U <<19 ,PERF_SAMPLE_AUX =1U <<20 , +PERF_SAMPLE_CGROUP =1U <<21 , -PERF_SAMPLE_MAX =1U <<21 ,/* non-ABI */ +PERF_SAMPLE_MAX =1U <<22 ,/* non-ABI */ __PERF_SAMPLE_CALLCHAIN_EARLY = 1ULL << 63, /* non-ABI; internal use */ };
init/Kconfig +2 -1
@@ -1046,7 +1046,8 @@ config CGROUP_PERF help This option extends the perf per-cpu mode to restrict monitoring to threads which belong to the cgroup specified and run on the - designated cpu. + designated cpu. Or this can be used to have cgroup ID in samples + so that it can monitor performance events among cgroups. Say N if unsure.
kernel/events/core.c +22 -0
@@ -1754,6 +1754,9 @@ static void __perf_event_header_size(struct perf_event *event, u64 sample_type)if (sample_type &PERF_SAMPLE_PHYS_ADDR ) size +=sizeof (data->phys_addr); +if (sample_type &PERF_SAMPLE_CGROUP ) + size +=sizeof (data->cgroup); + event->header_size = size; }
@@ -6699,6 +6702,9 @@ void perf_output_sample(struct perf_output_handle *handle,if (sample_type &PERF_SAMPLE_PHYS_ADDR )perf_output_put (handle, data->phys_addr); +if (sample_type &PERF_SAMPLE_CGROUP ) +perf_output_put (handle, data->cgroup); +if (sample_type &PERF_SAMPLE_AUX ) {perf_output_put (handle, data->aux_size);
@@ -6895,6 +6901,16 @@ void perf_prepare_sample(struct perf_event_header *header,if (sample_type &PERF_SAMPLE_PHYS_ADDR ) data->phys_addr =perf_virt_to_phys (data->addr); +#ifdef CONFIG_CGROUP_PERF +if (sample_type &PERF_SAMPLE_CGROUP ) { +struct cgroup *cgrp; + +/* protected by RCU */ + cgrp =task_css_check (current, perf_event_cgrp_id,1 )->cgroup; + data->cgroup =cgroup_id (cgrp); + } +#endif +if (sample_type &PERF_SAMPLE_AUX ) {u64 size;
@@ -11090,6 +11106,12 @@ static int perf_copy_attr(struct perf_event_attr __user *uattr,if (attr->sample_type &PERF_SAMPLE_REGS_INTR ) ret =perf_reg_validate (attr->sample_regs_intr); + +#ifndef CONFIG_CGROUP_PERF +if (attr->sample_type &PERF_SAMPLE_CGROUP ) +return -EINVAL ; +#endif + out:return ret;
Signature · 1 line
2.24.1.735.g03f4e72817-goog
Namhyung Kim
maintainerPATCH 3/9 perf tools: Basic support for CGROUP event
In reply to Namhyung KimImplement basic functionality to support cgroup tracking. Each cgroup can be identified by inode number which can be read from userspace too. The actual cgroup processing will come in the later patch.
--- tools/include/uapi/linux/perf_event.h | 16 ++++++++++++++-- tools/perf/builtin-diff.c | 1 + tools/perf/builtin-report.c | 1 + tools/perf/lib/include/perf/event.h | 7 +++++++ tools/perf/util/event.c | 18 ++++++++++++++++++ tools/perf/util/event.h | 6 ++++++ tools/perf/util/evsel.c | 6 ++++++ tools/perf/util/machine.c | 12 ++++++++++++ tools/perf/util/machine.h | 3 +++ tools/perf/util/perf_event_attr_fprintf.c | 2 ++ tools/perf/util/session.c | 4 ++++ tools/perf/util/tool.h | 1 + 12 files changed, 75 insertions(+), 2 deletions(-)
tools/include/uapi/linux/perf_event.h +14 -2
@@ -142,8 +142,9 @@ enum perf_event_sample_format {PERF_SAMPLE_REGS_INTR =1U <<18 ,PERF_SAMPLE_PHYS_ADDR =1U <<19 ,PERF_SAMPLE_AUX =1U <<20 , +PERF_SAMPLE_CGROUP =1U <<21 , -PERF_SAMPLE_MAX =1U <<21 ,/* non-ABI */ +PERF_SAMPLE_MAX =1U <<22 ,/* non-ABI */ __PERF_SAMPLE_CALLCHAIN_EARLY = 1ULL << 63, /* non-ABI; internal use */ };
@@ -377,7 +378,8 @@ struct perf_event_attr { ksymbol : 1, /* include ksymbol events */ bpf_event : 1, /* include bpf events */ aux_output : 1, /* generate AUX records instead of events */ - __reserved_1 : 32; + cgroup : 1, /* include cgroup events */ + __reserved_1 : 31;union {__u32 wakeup_events;/* wakeup every n events */
@@ -1006,6 +1008,16 @@ enum perf_event_type {*/ PERF_RECORD_BPF_EVENT =18 , +/* +* struct { +* struct perf_event_header header; +* u64 id; +* char path[]; +* struct sample_id sample_id; +* }; +*/ +PERF_RECORD_CGROUP =19 , + PERF_RECORD_MAX, /* non-ABI */ };
tools/perf/builtin-diff.c +1 -0
@@ -455,6 +455,7 @@ static struct perf_diff pdiff = { .fork = perf_event__process_fork, .lost = perf_event__process_lost, .namespaces = perf_event__process_namespaces, + .cgroup = perf_event__process_cgroup, .ordered_events = true, .ordering_requires_timestamps = true, },
tools/perf/builtin-report.c +1 -0
@@ -1093,6 +1093,7 @@ int cmd_report(int argc, const char **argv) .mmap2 = perf_event__process_mmap2, .comm = perf_event__process_comm, .namespaces = perf_event__process_namespaces, + .cgroup = perf_event__process_cgroup, .exit = perf_event__process_exit, .fork = perf_event__process_fork, .lost = perf_event__process_lost,
tools/perf/lib/include/perf/event.h +7 -0
@@ -105,6 +105,12 @@ struct perf_record_bpf_event {__u8 tag[BPF_TAG_SIZE ];// prog tag }; +struct perf_record_cgroup { +struct perf_event_header header; +__u64 id; +char path[PATH_MAX ]; +}; +struct perf_record_sample {struct perf_event_header header;__u64 array[];
@@ -352,6 +358,7 @@ union perf_event {struct perf_record_mmap2 mmap2;struct perf_record_comm comm;struct perf_record_namespaces namespaces; +struct perf_record_cgroup cgroup;struct perf_record_fork fork;struct perf_record_lost lost;struct perf_record_lost_samples lost_samples;
tools/perf/util/event.c +18 -0
@@ -54,6 +54,7 @@ static const char *perf_event__names[] = { [PERF_RECORD_NAMESPACES] = "NAMESPACES", [PERF_RECORD_KSYMBOL] = "KSYMBOL", [PERF_RECORD_BPF_EVENT] = "BPF_EVENT", + [PERF_RECORD_CGROUP] = "CGROUP", [PERF_RECORD_HEADER_ATTR] = "ATTR", [PERF_RECORD_HEADER_EVENT_TYPE] = "EVENT_TYPE", [PERF_RECORD_HEADER_TRACING_DATA] = "TRACING_DATA",
@@ -180,6 +181,12 @@ size_t perf_event__fprintf_namespaces(union perf_event *event, FILE *fp)return ret; } +size_t perf_event__fprintf_cgroup (union perf_event *event,FILE *fp) +{ +return fprintf (fp," cgroup: %" PRI_lu64" %s\n" , + event->cgroup.id, event->cgroup.path); +} + int perf_event__process_comm(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample,
@@ -196,6 +203,14 @@ int perf_event__process_namespaces(struct perf_tool *tool __maybe_unused,return machine__process_namespaces_event (machine, event, sample); } +int perf_event__process_cgroup (struct perf_tool *tool__maybe_unused , +union perf_event *event, +struct perf_sample *sample, +struct machine *machine) +{ +return machine__process_cgroup_event (machine, event, sample); +} + int perf_event__process_lost(struct perf_tool *tool __maybe_unused, union perf_event *event, struct perf_sample *sample,
@@ -417,6 +432,9 @@ size_t perf_event__fprintf(union perf_event *event, FILE *fp)case PERF_RECORD_NAMESPACES : ret +=perf_event__fprintf_namespaces (event, fp);break ; +case PERF_RECORD_CGROUP : + ret +=perf_event__fprintf_cgroup (event, fp); +break ;case PERF_RECORD_MMAP2 : ret +=perf_event__fprintf_mmap2 (event, fp);break ;
tools/perf/util/event.h +6 -0
@@ -135,6 +135,7 @@ struct perf_sample {u32 raw_size;u64 data_src;u64 phys_addr; +u64 cgroup;u32 flags;u16 insn_len;u8 cpumode;
@@ -321,6 +322,10 @@ int perf_event__process_namespaces(struct perf_tool *tool,union perf_event *event, struct perf_sample *sample, struct machine *machine); +int perf_event__process_cgroup (struct perf_tool *tool, +union perf_event *event, +struct perf_sample *sample, +struct machine *machine); int perf_event__process_mmap(struct perf_tool *tool, union perf_event *event, struct perf_sample *sample,
@@ -376,6 +381,7 @@ size_t perf_event__fprintf_switch(union perf_event *event, FILE *fp);size_t perf_event__fprintf_thread_map (union perf_event *event,FILE *fp);size_t perf_event__fprintf_cpu_map (union perf_event *event,FILE *fp);size_t perf_event__fprintf_namespaces (union perf_event *event,FILE *fp); +size_t perf_event__fprintf_cgroup (union perf_event *event,FILE *fp);size_t perf_event__fprintf_ksymbol (union perf_event *event,FILE *fp);size_t perf_event__fprintf_bpf (union perf_event *event,FILE *fp);size_t perf_event__fprintf (union perf_event *event,FILE *fp);
tools/perf/util/evsel.c +6 -0
@@ -2250,6 +2250,12 @@ int perf_evsel__parse_sample(struct evsel *evsel, union perf_event *event, array++; } + data->cgroup =0 ; +if (type &PERF_SAMPLE_CGROUP ) { + data->cgroup = *array; + array++; + } +if (type &PERF_SAMPLE_AUX ) {OVERFLOW_CHECK_u64 (array); sz = *array++;
tools/perf/util/machine.c +12 -0
@@ -654,6 +654,16 @@ int machine__process_namespaces_event(struct machine *machine __maybe_unused,return err; } +int machine__process_cgroup_event (struct machine *machine__maybe_unused , +union perf_event *event, +struct perf_sample *sample__maybe_unused ) +{ +if (dump_trace) +perf_event__fprintf_cgroup (event, stdout); + +return 0 ; +} +int machine__process_lost_event (struct machine *machine__maybe_unused ,union perf_event *event,struct perf_sample *sample__maybe_unused ) {
@@ -1880,6 +1890,8 @@ int machine__process_event(struct machine *machine, union perf_event *event, ret =machine__process_mmap_event (machine, event, sample);break ;case PERF_RECORD_NAMESPACES : ret =machine__process_namespaces_event (machine, event, sample);break ; +case PERF_RECORD_CGROUP : + ret =machine__process_cgroup_event (machine, event, sample);break ;case PERF_RECORD_MMAP2 : ret =machine__process_mmap2_event (machine, event, sample);break ;case PERF_RECORD_FORK :
tools/perf/util/machine.h +3 -0
@@ -128,6 +128,9 @@ int machine__process_switch_event(struct machine *machine,int machine__process_namespaces_event (struct machine *machine,union perf_event *event,struct perf_sample *sample); +int machine__process_cgroup_event (struct machine *machine, +union perf_event *event, +struct perf_sample *sample);int machine__process_mmap_event (struct machine *machine,union perf_event *event,struct perf_sample *sample); int machine__process_mmap2_event(struct machine *machine, union perf_event *event,
tools/perf/util/perf_event_attr_fprintf.c +2 -0
@@ -35,6 +35,7 @@ static void __p_sample_type(char *buf, size_t size, u64 value)bit_name (BRANCH_STACK ),bit_name (REGS_USER ),bit_name (STACK_USER ),bit_name (IDENTIFIER ),bit_name (REGS_INTR ),bit_name (DATA_SRC ),bit_name (WEIGHT ),bit_name (PHYS_ADDR ),bit_name (AUX ), +bit_name (CGROUP ), { .name = NULL, } };#undef bit_name
@@ -131,6 +132,7 @@ int perf_event_attr__fprintf(FILE *fp, struct perf_event_attr *attr,PRINT_ATTRf (ksymbol, p_unsigned);PRINT_ATTRf (bpf_event, p_unsigned);PRINT_ATTRf (aux_output, p_unsigned); +PRINT_ATTRf (cgroup, p_unsigned);PRINT_ATTRn ("{ wakeup_events, wakeup_watermark }" , wakeup_events, p_unsigned);PRINT_ATTRf (bp_type, p_unsigned);
tools/perf/util/session.c +4 -0
@@ -471,6 +471,8 @@ void perf_tool__fill_defaults(struct perf_tool *tool) tool->comm = process_event_stub;if (tool->namespaces ==NULL ) tool->namespaces = process_event_stub; +if (tool->cgroup ==NULL ) + tool->cgroup = process_event_stub;if (tool->fork ==NULL ) tool->fork = process_event_stub;if (tool->exit ==NULL )
@@ -1434,6 +1436,8 @@ static int machines__deliver_event(struct machines *machines,return tool->comm (tool, event, sample, machine);case PERF_RECORD_NAMESPACES :return tool->namespaces (tool, event, sample, machine); +case PERF_RECORD_CGROUP : +return tool->cgroup (tool, event, sample, machine);case PERF_RECORD_FORK :return tool->fork (tool, event, sample, machine);case PERF_RECORD_EXIT :
tools/perf/util/tool.h +1 -0
@@ -46,6 +46,7 @@ struct perf_tool { mmap2, comm, namespaces, + cgroup, fork, exit, lost,
Signature · 1 line
2.24.1.735.g03f4e72817-goog
Namhyung Kim
maintainerPATCH 4/9 perf tools: Maintain cgroup hierarchy
In reply to Namhyung KimEach cgroup is kept in the global cgroup_tree sorted by the cgroup id. Hist entries have cgroup id can compare it directly and later it can be used to find a group name using this tree.
--- tools/perf/util/cgroup.c | 72 +++++++++++++++++++++++++++++++++++++++ tools/perf/util/cgroup.h | 15 +++++--- tools/perf/util/machine.c | 7 ++++ tools/perf/util/session.c | 4 +++ 4 files changed, 94 insertions(+), 4 deletions(-)
tools/perf/util/cgroup.c +72 -0
@@ -13,6 +13,8 @@int nr_cgroups; +static struct rb_root cgroup_tree =RB_ROOT ; +static int cgroupfs_find_mountpoint (char *buf,size_t maxlen) {
@@ -250,3 +252,73 @@ int parse_cgroups(const struct option *opt, const char *str, }return 0 ; } + +struct cgroup *cgroup__findnew (uint64_t id,const char *path) +{ +struct rb_node **p = &cgroup_tree.rb_node; +struct rb_node *parent =NULL ; +struct cgroup *cgrp; + +while (*p !=NULL ) { + parent = *p; + cgrp = rb_entry(parent, struct cgroup, node); + +if (cgrp->id == id) +return cgrp; + +if (cgrp->id < id) + p = &(*p)->rb_left; +else + p = &(*p)->rb_right; + } + + cgrp =malloc (sizeof (*cgrp)); +if (cgrp ==NULL ) +return NULL ; + + cgrp->name =strdup (path); +if (cgrp->name ==NULL ) { +free (cgrp); +return NULL ; + } + + cgrp->fd =-1 ; + cgrp->id = id; +refcount_set (&cgrp->refcnt,1 ); + +rb_link_node (&cgrp->node, parent, p); +rb_insert_color (&cgrp->node, &cgroup_tree); + +return cgrp; +} + +struct cgroup *cgroup__find_by_path (const char *path) +{ +struct rb_node *node; + + node =rb_first (&cgroup_tree); +while (node) { + struct cgroup *cgrp = rb_entry(node, struct cgroup, node); + +if (!strcmp (cgrp->name, path)) +return cgrp; + + node =rb_next (&cgrp->node); + } + +return NULL ; +} + +void destroy_cgroups (void ) +{ +struct rb_node *node; +struct cgroup *cgrp; + +while (!RB_EMPTY_ROOT (&cgroup_tree)) { + node =rb_first (&cgroup_tree); + cgrp = rb_entry(node, struct cgroup, node); + +rb_erase (node, &cgroup_tree); +cgroup__put (cgrp); + } +}
tools/perf/util/cgroup.h +11 -4
@@ -3,16 +3,18 @@#define __CGROUP_H__#include <linux/refcount.h> +#include <linux/rbtree.h> struct option ;struct cgroup { -char *name; -int fd; -refcount_t refcnt; +struct rb_node node; +u64 id; +char *name; +int fd; +refcount_t refcnt; }; -extern int nr_cgroups;/* number of explicit cgroups defined */ struct cgroup *cgroup__get (struct cgroup *cgroup);
@@ -26,4 +28,9 @@ void evlist__set_default_cgroup(struct evlist *evlist, struct cgroup *cgroup);int parse_cgroups (const struct option *opt,const char *str,int unset); +struct cgroup *cgroup__findnew (uint64_t id,const char *path); +struct cgroup *cgroup__find_by_path (const char *path); + +void destroy_cgroups (void ); +#endif /* __CGROUP_H__ */
tools/perf/util/machine.c +7 -0
@@ -33,6 +33,7 @@#include "asm/bug.h" #include "bpf-event.h" #include <internal/lib.h> // page_size +#include "cgroup.h" #include <linux/ctype.h> #include <symbol/kallsyms.h>
@@ -658,9 +659,15 @@ int machine__process_cgroup_event(struct machine *machine __maybe_unused,union perf_event *event, struct perf_sample *sample __maybe_unused) { + struct cgroup *cgrp; +if (dump_trace)perf_event__fprintf_cgroup (event, stdout); + cgrp =cgroup__findnew (event->cgroup.id, event->cgroup.path); +if (cgrp ==NULL ) +return -ENOMEM ; +return 0 ; }
tools/perf/util/session.c +4 -0
@@ -289,6 +289,8 @@ static void perf_session__release_decomp_events(struct perf_session *session) }while (1 ); } +extern void destroy_cgroups (void ); +void perf_session__delete (struct perf_session *session) {if (session ==NULL )
@@ -303,6 +305,8 @@ void perf_session__delete(struct perf_session *session)if (session->data)perf_data__close (session->data);free (session); + +destroy_cgroups (); } static int process_event_synth_tracing_data_stub(struct perf_session *session
Signature · 1 line
2.24.1.735.g03f4e72817-goog
Namhyung Kim
maintainerPATCH 5/9 perf report: Add 'cgroup' sort key
In reply to Namhyung KimThe cgroup sort key is to show cgroup membership of each task. Currently it shows full path in the cgroupfs (not relative to the root of cgroup namespace) since it'd be more intuitive IMHO. Otherwise root cgroup in different namespaces will all show same name - "/".
The cgroup sort key should come before cgroup_id otherwise sort_dimension__add() will match it to cgroup_id as it only matches with the given substring.
For example it will look like following. Note that record patch adding --all-cgroups patch will come later.
$ perf record -a --namespace --all-cgroups cgtest [ perf record: Woken up 1 times to write data ] [ perf record: Captured and wrote 0.208 MB perf.data (4090 samples) ]
$ perf report -s cgroup_id,cgroup,pid
...
# Overhead cgroup id (dev/inode) cgroup Pid:Command
# ........ ..................... .......... ...............
#
93.96% 0/0x0 / 0:swapper
1.25% 3/0xeffffffb / 278:looper0
0.86% 3/0xf000015f /sub/cgrp1 280:cgtest
0.37% 3/0xf0000160 /sub/cgrp2 281:cgtest
0.34% 3/0xf0000163 /sub/cgrp3 282:cgtest
0.22% 3/0xeffffffb /sub 278:looper0
0.20% 3/0xeffffffb / 280:cgtest
0.15% 3/0xf0000163 /sub/cgrp3 285:looper3--- tools/perf/Documentation/perf-report.txt | 1 + tools/perf/util/hist.c | 7 ++++++ tools/perf/util/hist.h | 1 + tools/perf/util/sort.c | 31 ++++++++++++++++++++++++ tools/perf/util/sort.h | 2 ++ 5 files changed, 42 insertions(+)
tools/perf/Documentation/perf-report.txt +1 -0
@@ -95,6 +95,7 @@ OPTIONS abort cost. This is the global weight. - local_weight: Local weight version of the weight above. - cgroup_id: ID derived from cgroup namespace device and inode numbers. + - cgroup: cgroup pathname in the cgroupfs. - transaction: Transaction abort flags. - overhead: Overhead percentage of sample - overhead_sys: Overhead percentage of sample running in system mode
tools/perf/util/hist.c +7 -0
@@ -10,6 +10,7 @@#include "mem-events.h" #include "session.h" #include "namespaces.h" +#include "cgroup.h" #include "sort.h" #include "units.h" #include "evlist.h"
@@ -222,6 +223,11 @@ void hists__calc_col_len(struct hists *hists, struct hist_entry *h)if (h->trace_output)hists__new_col_len (hists,HISTC_TRACE ,strlen (h->trace_output)); + +if (h->cgroup) { +struct cgroup *cgrp =cgroup__findnew (h->cgroup,"<unknown>" ); +hists__new_col_len (hists,HISTC_CGROUP ,strlen (cgrp->name)); + } }void hists__output_recalc_col_len (struct hists *hists,int max_rows)
@@ -691,6 +697,7 @@ __hists__add_entry(struct hists *hists, .dev = ns ? ns->link_info[CGROUP_NS_INDEX].dev : 0, .ino = ns ? ns->link_info[CGROUP_NS_INDEX].ino : 0, }, + .cgroup = sample->cgroup, .ms = { .maps = al->maps, .map = al->map,
tools/perf/util/hist.h +1 -0
@@ -38,6 +38,7 @@ enum hist_column {HISTC_THREAD ,HISTC_COMM ,HISTC_CGROUP_ID , +HISTC_CGROUP ,HISTC_PARENT ,HISTC_CPU , HISTC_SOCKET,
tools/perf/util/sort.c +31 -0
@@ -25,6 +25,7 @@#include "mem-events.h" #include "annotate.h" #include "time-utils.h" +#include "cgroup.h" #include <linux/kernel.h> #include <linux/string.h>
@@ -635,6 +636,35 @@ struct sort_entry sort_cgroup_id = { .se_width_idx = HISTC_CGROUP_ID, }; +/* --sort cgroup */ + +static int64_t +sort__cgroup_cmp (struct hist_entry *left,struct hist_entry *right) +{ +return right->cgroup - left->cgroup; +} + +static int hist_entry__cgroup_snprintf (struct hist_entry *he, +char *bf,size_t size, +unsigned int width__maybe_unused ) +{ +const char *cgrp_name ="N/A" ; + +if (he->cgroup) { +struct cgroup *cgrp =cgroup__findnew (he->cgroup, cgrp_name); + cgrp_name = cgrp->name; + } + +return repsep_snprintf (bf, size,"%s" , cgrp_name); +} + +struct sort_entry sort_cgroup = { + .se_header ="cgroup" , + .se_cmp = sort__cgroup_cmp, + .se_snprintf = hist_entry__cgroup_snprintf, + .se_width_idx =HISTC_CGROUP , +}; +/* --sort socket */ static int64_t
@@ -1659,6 +1689,7 @@ static struct sort_dimension common_sort_dimensions[] = {DIM (SORT_TRACE ,"trace" , sort_trace),DIM (SORT_SYM_SIZE ,"symbol_size" , sort_sym_size),DIM (SORT_DSO_SIZE ,"dso_size" , sort_dso_size), +DIM (SORT_CGROUP ,"cgroup" , sort_cgroup),DIM (SORT_CGROUP_ID ,"cgroup_id" , sort_cgroup_id),DIM (SORT_SYM_IPC_NULL ,"ipc_null" , sort_sym_ipc_null), DIM(SORT_TIME, "time", sort_time),
tools/perf/util/sort.h +2 -0
@@ -101,6 +101,7 @@ struct hist_entry {struct thread *thread;struct comm *comm;struct namespace_id cgroup_id; +u64 cgroup;u64 ip;u64 transaction;s32 socket;
@@ -222,6 +223,7 @@ enum sort_type {SORT_TRACE ,SORT_SYM_SIZE ,SORT_DSO_SIZE , +SORT_CGROUP ,SORT_CGROUP_ID ,SORT_SYM_IPC_NULL , SORT_TIME,
Signature · 1 line
2.24.1.735.g03f4e72817-goog
Namhyung Kim
maintainerPATCH 6/9 perf record: Support synthesizing cgroup events
In reply to Namhyung KimSynthesize cgroup events by iterating cgroup filesystem directories. The cgroup event only saves the portion of cgroup path after the mount point and the cgroup id (which actually is a file handle).
--- tools/perf/builtin-record.c | 5 ++ tools/perf/util/cgroup.c | 3 +- tools/perf/util/cgroup.h | 1 + tools/perf/util/event.c | 1 + tools/perf/util/synthetic-events.c | 119 +++++++++++++++++++++++++++++ tools/perf/util/synthetic-events.h | 1 + tools/perf/util/tool.h | 1 + 7 files changed, 129 insertions(+), 2 deletions(-)
tools/perf/builtin-record.c +5 -0
@@ -1397,6 +1397,11 @@ static int record__synthesize(struct record *rec, bool tail)if (err <0 )pr_warning ("Couldn't synthesize bpf events.\n" ); + err =perf_event__synthesize_cgroups (tool, process_synthesized_event, + machine); +if (err <0 ) +pr_warning ("Couldn't synthesize cgroup events.\n" ); + err =__machine__synthesize_threads (machine, tool, &opts->target, rec->evlist->core.threads, process_synthesized_event, opts->sample_address,1 );
tools/perf/util/cgroup.c +1 -2
@@ -15,8 +15,7 @@ int nr_cgroups;static struct rb_root cgroup_tree =RB_ROOT ; -static int -cgroupfs_find_mountpoint (char *buf,size_t maxlen) +int cgroupfs_find_mountpoint (char *buf,size_t maxlen) {FILE *fp;char mountpoint[PATH_MAX +1 ], tokens[PATH_MAX +1 ], type[PATH_MAX +1 ];
tools/perf/util/cgroup.h +1 -0
@@ -17,6 +17,7 @@ struct cgroup {extern int nr_cgroups;/* number of explicit cgroups defined */ +int cgroupfs_find_mountpoint (char *buf,size_t maxlen);struct cgroup *cgroup__get (struct cgroup *cgroup);void cgroup__put (struct cgroup *cgroup);
tools/perf/util/event.c +1 -0
@@ -33,6 +33,7 @@#include "bpf-event.h" #include "tool.h" #include "../perf.h" +#include "cgroup.h" static const char *perf_event__names[] = { [0 ] ="TOTAL" ,
tools/perf/util/synthetic-events.c +119 -0
131 lines not on this page · raw message
tools/perf/util/synthetic-events.h +1 -0
@@ -45,6 +45,7 @@ int perf_event__synthesize_kernel_mmap(struct perf_tool *tool, perf_event__handlint perf_event__synthesize_mmap_events (struct perf_tool *tool,union perf_event *event,pid_t pid,pid_t tgid,perf_event__handler_t process,struct machine *machine,bool mmap_data);int perf_event__synthesize_modules (struct perf_tool *tool,perf_event__handler_t process,struct machine *machine);int perf_event__synthesize_namespaces (struct perf_tool *tool,union perf_event *event,pid_t pid,pid_t tgid,perf_event__handler_t process,struct machine *machine); +int perf_event__synthesize_cgroups (struct perf_tool *tool,perf_event__handler_t process,struct machine *machine);int perf_event__synthesize_sample (union perf_event *event,u64 type,u64 read_format,const struct perf_sample *sample);int perf_event__synthesize_stat_config (struct perf_tool *tool,struct perf_stat_config *config,perf_event__handler_t process,struct machine *machine);int perf_event__synthesize_stat_events (struct perf_stat_config *config,struct perf_tool *tool,struct evlist *evlist,perf_event__handler_t process,bool attrs);
tools/perf/util/tool.h +1 -0
@@ -79,6 +79,7 @@ struct perf_tool {bool ordered_events;bool ordering_requires_timestamps;bool namespace_events; +bool cgroup_events;bool no_warn;enum show_feature_header show_feat_hdr; };
Signature · 1 line
2.24.1.735.g03f4e72817-goog
Namhyung Kim
maintainerPATCH 7/9 perf record: Add --all-cgroups option
In reply to Namhyung KimThe --all-cgroups option is to enable cgroup profiling support. It tells kernel to record CGROUP events in the ring buffer so that perf report can identify task/cgroup association later.
--- tools/perf/Documentation/perf-record.txt | 5 ++++- tools/perf/builtin-record.c | 5 +++++ tools/perf/util/evsel.c | 11 ++++++++++- tools/perf/util/evsel.h | 1 + tools/perf/util/record.h | 1 + 5 files changed, 21 insertions(+), 2 deletions(-)
tools/perf/Documentation/perf-record.txt +4 -1
@@ -385,7 +385,10 @@ displayed with the weight and local_weight sort keys. This currently works for abort events and some memory events in precise mode on modern Intel CPUs. --namespaces:: -Record events of type PERF_RECORD_NAMESPACES. +Record events of type PERF_RECORD_NAMESPACES. This enables 'cgroup_id' sort key. + +--all-cgroups:: +Record events of type PERF_RECORD_CGROUP. This enables 'cgroup' sort key. --transaction:: Record transaction flags for transaction related events.
tools/perf/builtin-record.c +5 -0
@@ -1433,6 +1433,9 @@ static int __cmd_record(struct record *rec, int argc, const char **argv)if (rec->opts.record_namespaces) tool->namespace_events = true; +if (rec->opts.record_cgroup) + tool->cgroup_events = true; +if (rec->opts.auxtrace_snapshot_mode || rec->switch_output.enabled) {signal (SIGUSR2 , snapshot_sig_handler);if (rec->opts.auxtrace_snapshot_mode)
@@ -2363,6 +2366,8 @@ static struct option __record_options[] = { "per thread proc mmap processing timeout in ms"),OPT_BOOLEAN (0 ,"namespaces" , &record.opts.record_namespaces,"Record namespaces events" ), +OPT_BOOLEAN (0 ,"all-cgroups" , &record.opts.record_cgroup, +"Record cgroup events" ),OPT_BOOLEAN (0 ,"switch-events" , &record.opts.record_switch_events,"Record context switch events" ), OPT_BOOLEAN_FLAG(0, "all-kernel", &record.opts.all_kernel,
tools/perf/util/evsel.c +10 -1
@@ -1102,6 +1102,11 @@ void perf_evsel__config(struct evsel *evsel, struct record_opts *opts,if (opts->record_namespaces) attr->namespaces = track; +if (opts->record_cgroup) { + attr->cgroup = track && !perf_missing_features.cgroup; +perf_evsel__set_sample_bit (evsel,CGROUP ); + } +if (opts->record_switch_events) attr->context_switch = track;
@@ -1782,7 +1787,11 @@ static int evsel__open_cpu(struct evsel *evsel, struct perf_cpu_map *cpus,* Must probe features in the order they were added to the * perf_event_attr interface. */ -if (!perf_missing_features.aux_output && evsel->core.attr.aux_output) { +if (!perf_missing_features.cgroup && evsel->core.attr.cgroup) { + perf_missing_features.cgroup = true; +pr_debug2_peo ("Kernel has no cgroup sampling support, bailing out\n" ); + goto out_close; + }else if (!perf_missing_features.aux_output && evsel->core.attr.aux_output) { perf_missing_features.aux_output = true;pr_debug2_peo ("Kernel has no attr.aux_output support, bailing out\n" ); goto out_close;
tools/perf/util/evsel.h +1 -0
@@ -119,6 +119,7 @@ struct perf_missing_features {bool ksymbol;bool bpf;bool aux_output; +bool cgroup; };extern struct perf_missing_features perf_missing_features;
tools/perf/util/record.h +1 -0
@@ -34,6 +34,7 @@ struct record_opts {bool auxtrace_snapshot_on_exit;bool auxtrace_sample_mode;bool record_namespaces; +bool record_cgroup;bool record_switch_events;bool all_kernel;bool all_user;
Signature · 1 line
2.24.1.735.g03f4e72817-goog
Namhyung Kim
maintainerPATCH 8/9 perf top: Add --all-cgroups option
In reply to Namhyung KimThe --all-cgroups option is to enable cgroup profiling support. It tells kernel to record CGROUP events in the ring buffer so that perf report can identify task/cgroup association later.
--- tools/perf/Documentation/perf-top.txt | 4 ++++ tools/perf/builtin-top.c | 9 +++++++++ 2 files changed, 13 insertions(+)
tools/perf/Documentation/perf-top.txt +4 -0
@@ -266,6 +266,10 @@ Default is to monitor all CPUS. Record events of type PERF_RECORD_NAMESPACES and display it with the 'cgroup_id' sort key. +--cgroup:: + Record events of type PERF_RECORD_CGROUP and display it with the + 'cgroup' sort key. + --switch-on EVENT_NAME:: Only consider events after this event is found.
tools/perf/builtin-top.c +9 -0
@@ -1244,6 +1244,8 @@ static int __cmd_top(struct perf_top *top)if (opts->record_namespaces) top->tool.namespace_events = true; +if (opts->record_cgroup) + top->tool.cgroup_events = true; ret = perf_event__synthesize_bpf_events(top->session, perf_event__process, &top->session->machines.host,
@@ -1251,6 +1253,11 @@ static int __cmd_top(struct perf_top *top)if (ret <0 )pr_debug ("Couldn't synthesize BPF events: Pre-existing BPF programs won't have symbols resolved.\n" ); + ret =perf_event__synthesize_cgroups (&top->tool, perf_event__process, + &top->session->machines.host); +if (ret <0 ) +pr_debug ("Couldn't synthesize cgroup events.\n" ); +machine__synthesize_threads (&top->session->machines.host, &opts->target, top->evlist->core.threads, false, top->nr_threads_synthesize);
@@ -1539,6 +1546,8 @@ int cmd_top(int argc, const char **argv) "number of thread to run event synthesize"),OPT_BOOLEAN (0 ,"namespaces" , &opts->record_namespaces,"Record namespaces events" ), +OPT_BOOLEAN (0 ,"all-cgroups" , &opts->record_cgroup, +"Record cgroup events" ),OPTS_EVSWITCH (&top.evswitch), OPT_END() };
Signature · 1 line
2.24.1.735.g03f4e72817-goog
Namhyung Kim
maintainerPATCH 9/9 perf script: Add --show-cgroup-events option
In reply to Namhyung KimThe --show-cgroup-events option is to print CGROUP events in the output like others.
--- tools/perf/Documentation/perf-script.txt | 3 ++ tools/perf/builtin-script.c | 41 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+)
tools/perf/Documentation/perf-script.txt +3 -0
@@ -319,6 +319,9 @@ OPTIONS --show-bpf-events Display bpf events i.e. events of type PERF_RECORD_KSYMBOL and PERF_RECORD_BPF_EVENT. +--show-cgroup-events + Display cgroup events i.e. events of type PERF_RECORD_CGROUP. + --demangle:: Demangle symbol names to human readable form. It's enabled by default, disable with --no-demangle.
tools/perf/builtin-script.c +41 -0
71 lines not on this page · raw message
Signature · 1 line
2.24.1.735.g03f4e72817-goog
Quoted 9 lines · Namhyung Kim
This work is to improve cgroup profiling in perf. Currently it only supports profiling tasks in a specific cgroup and there's no way to identify which cgroup the current sample belongs to. So I added
PERF_SAMPLE_CGROUPto add cgroup id into each sample. It's a 64-bit integer having file handle of the cgroup. And kernel also generatesPERF_RECORD_CGROUPevent for new groups to correlate the cgroup id and cgroup name (path in the cgroup filesystem). The cgroup id can be read from userspace byname_to_handle_at()system call so it can synthesize the CGROUP event for existing groups.
so is there a patch to the manpage that describes this new behavior in perf_event_open()?
Vince
Namhyung Kim
maintainerHi Vince,
Quoted 13 lines · Vince Weaver
Quoted 9 lines · Namhyung Kim
This work is to improve cgroup profiling in perf. Currently it only supports profiling tasks in a specific cgroup and there's no way to identify which cgroup the current sample belongs to. So I added
PERF_SAMPLE_CGROUPto add cgroup id into each sample. It's a 64-bit integer having file handle of the cgroup. And kernel also generatesPERF_RECORD_CGROUPevent for new groups to correlate the cgroup id and cgroup name (path in the cgroup filesystem). The cgroup id can be read from userspace byname_to_handle_at()system call so it can synthesize the CGROUP event for existing groups.
Quoted 2 lines
so is there a patch to the manpage that describes this new behavior in
perf_event_open()?
Not yet. I'll cook a patch once it's merged to the Linus' tree.
Thanks
Namhyung
Em Tue, Dec 24, 2019 at 09:40:04AM +0900, Namhyung Kim escreveu:
Quoted 12 lines · Vince Weaver
Quoted 10 lines · Namhyung Kim
Quoted 9 lines
This work is to improve cgroup profiling in perf. Currently it only supports profiling tasks in a specific cgroup and there's no way to identify which cgroup the current sample belongs to. So I added
PERF_SAMPLE_CGROUPto add cgroup id into each sample. It's a 64-bit integer having file handle of the cgroup. And kernel also generatesPERF_RECORD_CGROUPevent for new groups to correlate the cgroup id and cgroup name (path in the cgroup filesystem). The cgroup id can be read from userspace byname_to_handle_at()system call so it can synthesize the CGROUP event for existing groups.
Quoted 4 lines
Quoted 2 lines
so is there a patch to the manpage that describes this new behavior in
perf_event_open()?Not yet. I'll cook a patch once it's merged to the Linus' tree.
Vince, was it ever considered to carry the man page in the kernel sources and then make it so that new features need to come with the respective changes to the man page? I think that would be a good move, you would be the maintainer for that file, what do you think?
- Arnaldo
Quoted 16 lines · Arnaldo Carvalho de Melo
Em Tue, Dec 24, 2019 at 09:40:04AM +0900, Namhyung Kim escreveu:
Quoted 14 lines · Vince Weaver
Quoted 13 lines · Namhyung Kim
Quoted 9 lines
This work is to improve cgroup profiling in perf. Currently it only supports profiling tasks in a specific cgroup and there's no way to identify which cgroup the current sample belongs to. So I added
PERF_SAMPLE_CGROUPto add cgroup id into each sample. It's a 64-bit integer having file handle of the cgroup. And kernel also generatesPERF_RECORD_CGROUPevent for new groups to correlate the cgroup id and cgroup name (path in the cgroup filesystem). The cgroup id can be read from userspace byname_to_handle_at()system call so it can synthesize the CGROUP event for existing groups.so is there a patch to the manpage that describes this new behavior in
perf_event_open()?
Quoted 6 lines
Quoted 1 line
Not yet. I'll cook a patch once it's merged to the Linus' tree.
Vince, was it ever considered to carry the man page in the kernel sources and then make it so that new features need to come with the respective changes to the man page? I think that would be a good move, you would be the maintainer for that file, what do you think?
While I do a lot of work on the perf_event_open() manpage, it's part of the linux man-pages project so I don't really control where it is maintained.
I personally do not think it would help much merging into the kernel tree. I still think the idea of moving everything into linux-git (such as the "perf" tool) isn't always the best idea and can make it harder for people who aren't kernel developers to work on things.
Vince