179 lines
		
	
	
	
		
			3.1 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			179 lines
		
	
	
	
		
			3.1 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								#include "util.h"
							 | 
						||
| 
								 | 
							
								#include "../perf.h"
							 | 
						||
| 
								 | 
							
								#include "parse-options.h"
							 | 
						||
| 
								 | 
							
								#include "evsel.h"
							 | 
						||
| 
								 | 
							
								#include "cgroup.h"
							 | 
						||
| 
								 | 
							
								#include "debugfs.h" /* MAX_PATH, STR() */
							 | 
						||
| 
								 | 
							
								#include "evlist.h"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int nr_cgroups;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int
							 | 
						||
| 
								 | 
							
								cgroupfs_find_mountpoint(char *buf, size_t maxlen)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									FILE *fp;
							 | 
						||
| 
								 | 
							
									char mountpoint[MAX_PATH+1], tokens[MAX_PATH+1], type[MAX_PATH+1];
							 | 
						||
| 
								 | 
							
									char *token, *saved_ptr;
							 | 
						||
| 
								 | 
							
									int found = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									fp = fopen("/proc/mounts", "r");
							 | 
						||
| 
								 | 
							
									if (!fp)
							 | 
						||
| 
								 | 
							
										return -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/*
							 | 
						||
| 
								 | 
							
									 * in order to handle split hierarchy, we need to scan /proc/mounts
							 | 
						||
| 
								 | 
							
									 * and inspect every cgroupfs mount point to find one that has
							 | 
						||
| 
								 | 
							
									 * perf_event subsystem
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
									while (fscanf(fp, "%*s %"STR(MAX_PATH)"s %"STR(MAX_PATH)"s %"
							 | 
						||
| 
								 | 
							
												STR(MAX_PATH)"s %*d %*d\n",
							 | 
						||
| 
								 | 
							
												mountpoint, type, tokens) == 3) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if (!strcmp(type, "cgroup")) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											token = strtok_r(tokens, ",", &saved_ptr);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											while (token != NULL) {
							 | 
						||
| 
								 | 
							
												if (!strcmp(token, "perf_event")) {
							 | 
						||
| 
								 | 
							
													found = 1;
							 | 
						||
| 
								 | 
							
													break;
							 | 
						||
| 
								 | 
							
												}
							 | 
						||
| 
								 | 
							
												token = strtok_r(NULL, ",", &saved_ptr);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										if (found)
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									fclose(fp);
							 | 
						||
| 
								 | 
							
									if (!found)
							 | 
						||
| 
								 | 
							
										return -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (strlen(mountpoint) < maxlen) {
							 | 
						||
| 
								 | 
							
										strcpy(buf, mountpoint);
							 | 
						||
| 
								 | 
							
										return 0;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return -1;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int open_cgroup(char *name)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									char path[MAX_PATH+1];
							 | 
						||
| 
								 | 
							
									char mnt[MAX_PATH+1];
							 | 
						||
| 
								 | 
							
									int fd;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (cgroupfs_find_mountpoint(mnt, MAX_PATH+1))
							 | 
						||
| 
								 | 
							
										return -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									snprintf(path, MAX_PATH, "%s/%s", mnt, name);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									fd = open(path, O_RDONLY);
							 | 
						||
| 
								 | 
							
									if (fd == -1)
							 | 
						||
| 
								 | 
							
										fprintf(stderr, "no access to cgroup %s\n", path);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return fd;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int add_cgroup(struct perf_evlist *evlist, char *str)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct perf_evsel *counter;
							 | 
						||
| 
								 | 
							
									struct cgroup_sel *cgrp = NULL;
							 | 
						||
| 
								 | 
							
									int n;
							 | 
						||
| 
								 | 
							
									/*
							 | 
						||
| 
								 | 
							
									 * check if cgrp is already defined, if so we reuse it
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
									list_for_each_entry(counter, &evlist->entries, node) {
							 | 
						||
| 
								 | 
							
										cgrp = counter->cgrp;
							 | 
						||
| 
								 | 
							
										if (!cgrp)
							 | 
						||
| 
								 | 
							
											continue;
							 | 
						||
| 
								 | 
							
										if (!strcmp(cgrp->name, str))
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										cgrp = NULL;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (!cgrp) {
							 | 
						||
| 
								 | 
							
										cgrp = zalloc(sizeof(*cgrp));
							 | 
						||
| 
								 | 
							
										if (!cgrp)
							 | 
						||
| 
								 | 
							
											return -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										cgrp->name = str;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										cgrp->fd = open_cgroup(str);
							 | 
						||
| 
								 | 
							
										if (cgrp->fd == -1) {
							 | 
						||
| 
								 | 
							
											free(cgrp);
							 | 
						||
| 
								 | 
							
											return -1;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/*
							 | 
						||
| 
								 | 
							
									 * find corresponding event
							 | 
						||
| 
								 | 
							
									 * if add cgroup N, then need to find event N
							 | 
						||
| 
								 | 
							
									 */
							 | 
						||
| 
								 | 
							
									n = 0;
							 | 
						||
| 
								 | 
							
									list_for_each_entry(counter, &evlist->entries, node) {
							 | 
						||
| 
								 | 
							
										if (n == nr_cgroups)
							 | 
						||
| 
								 | 
							
											goto found;
							 | 
						||
| 
								 | 
							
										n++;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									if (cgrp->refcnt == 0)
							 | 
						||
| 
								 | 
							
										free(cgrp);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									return -1;
							 | 
						||
| 
								 | 
							
								found:
							 | 
						||
| 
								 | 
							
									cgrp->refcnt++;
							 | 
						||
| 
								 | 
							
									counter->cgrp = cgrp;
							 | 
						||
| 
								 | 
							
									return 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void close_cgroup(struct cgroup_sel *cgrp)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									if (!cgrp)
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									/* XXX: not reentrant */
							 | 
						||
| 
								 | 
							
									if (--cgrp->refcnt == 0) {
							 | 
						||
| 
								 | 
							
										close(cgrp->fd);
							 | 
						||
| 
								 | 
							
										free(cgrp->name);
							 | 
						||
| 
								 | 
							
										free(cgrp);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								int parse_cgroups(const struct option *opt __used, const char *str,
							 | 
						||
| 
								 | 
							
										  int unset __used)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
									struct perf_evlist *evlist = *(struct perf_evlist **)opt->value;
							 | 
						||
| 
								 | 
							
									const char *p, *e, *eos = str + strlen(str);
							 | 
						||
| 
								 | 
							
									char *s;
							 | 
						||
| 
								 | 
							
									int ret;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if (list_empty(&evlist->entries)) {
							 | 
						||
| 
								 | 
							
										fprintf(stderr, "must define events before cgroups\n");
							 | 
						||
| 
								 | 
							
										return -1;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									for (;;) {
							 | 
						||
| 
								 | 
							
										p = strchr(str, ',');
							 | 
						||
| 
								 | 
							
										e = p ? p : eos;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										/* allow empty cgroups, i.e., skip */
							 | 
						||
| 
								 | 
							
										if (e - str) {
							 | 
						||
| 
								 | 
							
											/* termination added */
							 | 
						||
| 
								 | 
							
											s = strndup(str, e - str);
							 | 
						||
| 
								 | 
							
											if (!s)
							 | 
						||
| 
								 | 
							
												return -1;
							 | 
						||
| 
								 | 
							
											ret = add_cgroup(evlist, s);
							 | 
						||
| 
								 | 
							
											if (ret) {
							 | 
						||
| 
								 | 
							
												free(s);
							 | 
						||
| 
								 | 
							
												return -1;
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										/* nr_cgroups is increased een for empty cgroups */
							 | 
						||
| 
								 | 
							
										nr_cgroups++;
							 | 
						||
| 
								 | 
							
										if (!p)
							 | 
						||
| 
								 | 
							
											break;
							 | 
						||
| 
								 | 
							
										str = p+1;
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									return 0;
							 | 
						||
| 
								 | 
							
								}
							 |