Skip to content

Commit

Permalink
perf tools: Introduce per user view
Browse files Browse the repository at this point in the history
The new --uid command line option will show only the tasks for a given
user, using the proc interface to figure out the existing tasks.

Kernel work is needed to close races at startup, but this should already
be useful in many use cases.

Cc: David Ahern <dsahern@gmail.com>
Cc: Frederic Weisbecker <fweisbec@gmail.com>
Cc: Mike Galbraith <efault@gmx.de>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Stephane Eranian <eranian@google.com>
Link: http://lkml.kernel.org/n/tip-bdnspm000gw2l984a2t53o8z@git.kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
  • Loading branch information
Arnaldo Carvalho de Melo committed Jan 24, 2012
1 parent 9ae7d33 commit 0d37aa3
Show file tree
Hide file tree
Showing 18 changed files with 200 additions and 22 deletions.
4 changes: 4 additions & 0 deletions tools/perf/Documentation/perf-record.txt
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,10 @@ OPTIONS
--tid=::
Record events on existing thread ID.

-u::
--uid=::
Record events in threads owned by uid. Name or number.

-r::
--realtime=::
Collect data with this RT SCHED_FIFO priority.
Expand Down
4 changes: 4 additions & 0 deletions tools/perf/Documentation/perf-top.txt
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ Default is to monitor all CPUS.
--tid=<tid>::
Profile events on existing thread ID.

-u::
--uid=::
Record events in threads owned by uid. Name or number.

-r <priority>::
--realtime=<priority>::
Collect data with this RT SCHED_FIFO priority.
Expand Down
12 changes: 10 additions & 2 deletions tools/perf/builtin-record.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ struct perf_record {
struct perf_evlist *evlist;
struct perf_session *session;
const char *progname;
const char *uid_str;
int output;
unsigned int page_size;
int realtime_prio;
Expand Down Expand Up @@ -727,6 +728,7 @@ const struct option record_options[] = {
OPT_CALLBACK('G', "cgroup", &record.evlist, "name",
"monitor event in cgroup name only",
parse_cgroups),
OPT_STRING('u', "uid", &record.uid_str, "user", "user to profile"),
OPT_END()
};

Expand All @@ -748,7 +750,7 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
argc = parse_options(argc, argv, record_options, record_usage,
PARSE_OPT_STOP_AT_NON_OPTION);
if (!argc && rec->opts.target_pid == -1 && rec->opts.target_tid == -1 &&
!rec->opts.system_wide && !rec->opts.cpu_list)
!rec->opts.system_wide && !rec->opts.cpu_list && !rec->uid_str)
usage_with_options(record_usage, record_options);

if (rec->force && rec->append_file) {
Expand Down Expand Up @@ -788,11 +790,17 @@ int cmd_record(int argc, const char **argv, const char *prefix __used)
goto out_symbol_exit;
}

rec->opts.uid = parse_target_uid(rec->uid_str, rec->opts.target_tid,
rec->opts.target_pid);
if (rec->uid_str != NULL && rec->opts.uid == UINT_MAX - 1)
goto out_free_fd;

if (rec->opts.target_pid != -1)
rec->opts.target_tid = rec->opts.target_pid;

if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid,
rec->opts.target_tid, rec->opts.cpu_list) < 0)
rec->opts.target_tid, rec->opts.uid,
rec->opts.cpu_list) < 0)
usage_with_options(record_usage, record_options);

list_for_each_entry(pos, &evsel_list->entries, node) {
Expand Down
2 changes: 1 addition & 1 deletion tools/perf/builtin-stat.c
Original file line number Diff line number Diff line change
Expand Up @@ -1201,7 +1201,7 @@ int cmd_stat(int argc, const char **argv, const char *prefix __used)
if (target_pid != -1)
target_tid = target_pid;

evsel_list->threads = thread_map__new(target_pid, target_tid);
evsel_list->threads = thread_map__new(target_pid, target_tid, UINT_MAX);
if (evsel_list->threads == NULL) {
pr_err("Problems finding threads of monitor\n");
usage_with_options(stat_usage, options);
Expand Down
8 changes: 4 additions & 4 deletions tools/perf/builtin-test.c
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ static int test__open_syscall_event(void)
return -1;
}

threads = thread_map__new(-1, getpid());
threads = thread_map__new(-1, getpid(), UINT_MAX);
if (threads == NULL) {
pr_debug("thread_map__new\n");
return -1;
Expand Down Expand Up @@ -342,7 +342,7 @@ static int test__open_syscall_event_on_all_cpus(void)
return -1;
}

threads = thread_map__new(-1, getpid());
threads = thread_map__new(-1, getpid(), UINT_MAX);
if (threads == NULL) {
pr_debug("thread_map__new\n");
return -1;
Expand Down Expand Up @@ -490,7 +490,7 @@ static int test__basic_mmap(void)
expected_nr_events[i] = random() % 257;
}

threads = thread_map__new(-1, getpid());
threads = thread_map__new(-1, getpid(), UINT_MAX);
if (threads == NULL) {
pr_debug("thread_map__new\n");
return -1;
Expand Down Expand Up @@ -1054,7 +1054,7 @@ static int test__PERF_RECORD(void)
* we're monitoring, the one forked there.
*/
err = perf_evlist__create_maps(evlist, opts.target_pid,
opts.target_tid, opts.cpu_list);
opts.target_tid, UINT_MAX, opts.cpu_list);
if (err < 0) {
pr_debug("Not enough memory to create thread/cpu maps\n");
goto out_delete_evlist;
Expand Down
22 changes: 19 additions & 3 deletions tools/perf/builtin-top.c
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
#include <linux/unistd.h>
#include <linux/types.h>


void get_term_dimensions(struct winsize *ws)
{
char *s = getenv("LINES");
Expand Down Expand Up @@ -537,10 +536,20 @@ static void perf_top__sort_new_samples(void *arg)

static void *display_thread_tui(void *arg)
{
struct perf_evsel *pos;
struct perf_top *top = arg;
const char *help = "For a higher level overview, try: perf top --sort comm,dso";

perf_top__sort_new_samples(top);

/*
* Initialize the uid_filter_str, in the future the TUI will allow
* Zooming in/out UIDs. For now juse use whatever the user passed
* via --uid.
*/
list_for_each_entry(pos, &top->evlist->entries, node)
pos->hists.uid_filter_str = top->uid_str;

perf_evlist__tui_browse_hists(top->evlist, help,
perf_top__sort_new_samples,
top, top->delay_secs);
Expand Down Expand Up @@ -949,7 +958,7 @@ static int __cmd_top(struct perf_top *top)
if (ret)
goto out_delete;

if (top->target_tid != -1)
if (top->target_tid != -1 || top->uid != UINT_MAX)
perf_event__synthesize_thread_map(&top->tool, top->evlist->threads,
perf_event__process,
&top->session->host_machine);
Expand Down Expand Up @@ -1089,6 +1098,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
.delay_secs = 2,
.target_pid = -1,
.target_tid = -1,
.uid = UINT_MAX,
.freq = 1000, /* 1 KHz */
.sample_id_all_avail = true,
.mmap_pages = 128,
Expand Down Expand Up @@ -1162,6 +1172,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
"Display raw encoding of assembly instructions (default)"),
OPT_STRING('M', "disassembler-style", &disassembler_style, "disassembler style",
"Specify disassembler style (e.g. -M intel for intel syntax)"),
OPT_STRING('u', "uid", &top.uid_str, "user", "user to profile"),
OPT_END()
};

Expand All @@ -1187,6 +1198,10 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)

setup_browser(false);

top.uid = parse_target_uid(top.uid_str, top.target_tid, top.target_pid);
if (top.uid_str != NULL && top.uid == UINT_MAX - 1)
goto out_delete_evlist;

/* CPU and PID are mutually exclusive */
if (top.target_tid > 0 && top.cpu_list) {
printf("WARNING: PID switch overriding CPU\n");
Expand All @@ -1198,7 +1213,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)
top.target_tid = top.target_pid;

if (perf_evlist__create_maps(top.evlist, top.target_pid,
top.target_tid, top.cpu_list) < 0)
top.target_tid, top.uid, top.cpu_list) < 0)
usage_with_options(top_usage, options);

if (!top.evlist->nr_entries &&
Expand Down Expand Up @@ -1262,6 +1277,7 @@ int cmd_top(int argc, const char **argv, const char *prefix __used)

status = __cmd_top(&top);

out_delete_evlist:
perf_evlist__delete(top.evlist);

return status;
Expand Down
1 change: 1 addition & 0 deletions tools/perf/perf.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ void pthread__unblock_sigwinch(void);
struct perf_record_opts {
pid_t target_pid;
pid_t target_tid;
uid_t uid;
bool call_graph;
bool group;
bool inherit_stat;
Expand Down
6 changes: 3 additions & 3 deletions tools/perf/util/evlist.c
Original file line number Diff line number Diff line change
Expand Up @@ -594,14 +594,14 @@ int perf_evlist__mmap(struct perf_evlist *evlist, unsigned int pages,
}

int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid,
pid_t target_tid, const char *cpu_list)
pid_t target_tid, uid_t uid, const char *cpu_list)
{
evlist->threads = thread_map__new(target_pid, target_tid);
evlist->threads = thread_map__new(target_pid, target_tid, uid);

if (evlist->threads == NULL)
return -1;

if (cpu_list == NULL && target_tid != -1)
if (uid != UINT_MAX || (cpu_list == NULL && target_tid != -1))
evlist->cpus = cpu_map__dummy_new();
else
evlist->cpus = cpu_map__new(cpu_list);
Expand Down
2 changes: 1 addition & 1 deletion tools/perf/util/evlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ static inline void perf_evlist__set_maps(struct perf_evlist *evlist,
}

int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid,
pid_t target_tid, const char *cpu_list);
pid_t tid, uid_t uid, const char *cpu_list);
void perf_evlist__delete_maps(struct perf_evlist *evlist);
int perf_evlist__set_filters(struct perf_evlist *evlist);

Expand Down
1 change: 1 addition & 0 deletions tools/perf/util/hist.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ struct hists {
u64 nr_entries;
const struct thread *thread_filter;
const struct dso *dso_filter;
const char *uid_filter_str;
pthread_mutex_t lock;
struct events_stats stats;
u64 event_stream;
Expand Down
10 changes: 5 additions & 5 deletions tools/perf/util/python.c
Original file line number Diff line number Diff line change
Expand Up @@ -425,14 +425,14 @@ struct pyrf_thread_map {
static int pyrf_thread_map__init(struct pyrf_thread_map *pthreads,
PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = { "pid", "tid", NULL };
int pid = -1, tid = -1;
static char *kwlist[] = { "pid", "tid", "uid", NULL };
int pid = -1, tid = -1, uid = UINT_MAX;

if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|ii",
kwlist, &pid, &tid))
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|iii",
kwlist, &pid, &tid, &uid))
return -1;

pthreads->threads = thread_map__new(pid, tid);
pthreads->threads = thread_map__new(pid, tid, uid);
if (pthreads->threads == NULL)
return -1;
return 0;
Expand Down
98 changes: 96 additions & 2 deletions tools/perf/util/thread_map.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
#include <dirent.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "thread_map.h"

/* Skip "." and ".." directories */
Expand All @@ -23,7 +28,7 @@ struct thread_map *thread_map__new_by_pid(pid_t pid)
sprintf(name, "/proc/%d/task", pid);
items = scandir(name, &namelist, filter, NULL);
if (items <= 0)
return NULL;
return NULL;

threads = malloc(sizeof(*threads) + sizeof(pid_t) * items);
if (threads != NULL) {
Expand Down Expand Up @@ -51,10 +56,99 @@ struct thread_map *thread_map__new_by_tid(pid_t tid)
return threads;
}

struct thread_map *thread_map__new(pid_t pid, pid_t tid)
struct thread_map *thread_map__new_by_uid(uid_t uid)
{
DIR *proc;
int max_threads = 32, items, i;
char path[256];
struct dirent dirent, *next, **namelist = NULL;
struct thread_map *threads = malloc(sizeof(*threads) +
max_threads * sizeof(pid_t));
if (threads == NULL)
goto out;

proc = opendir("/proc");
if (proc == NULL)
goto out_free_threads;

threads->nr = 0;

while (!readdir_r(proc, &dirent, &next) && next) {
char *end;
bool grow = false;
struct stat st;
pid_t pid = strtol(dirent.d_name, &end, 10);

if (*end) /* only interested in proper numerical dirents */
continue;

snprintf(path, sizeof(path), "/proc/%s", dirent.d_name);

if (stat(path, &st) != 0)
continue;

if (st.st_uid != uid)
continue;

snprintf(path, sizeof(path), "/proc/%d/task", pid);
items = scandir(path, &namelist, filter, NULL);
if (items <= 0)
goto out_free_closedir;

while (threads->nr + items >= max_threads) {
max_threads *= 2;
grow = true;
}

if (grow) {
struct thread_map *tmp;

tmp = realloc(threads, (sizeof(*threads) +
max_threads * sizeof(pid_t)));
if (tmp == NULL)
goto out_free_namelist;

threads = tmp;
}

for (i = 0; i < items; i++)
threads->map[threads->nr + i] = atoi(namelist[i]->d_name);

for (i = 0; i < items; i++)
free(namelist[i]);
free(namelist);

threads->nr += items;
}

out_closedir:
closedir(proc);
out:
return threads;

out_free_threads:
free(threads);
return NULL;

out_free_namelist:
for (i = 0; i < items; i++)
free(namelist[i]);
free(namelist);

out_free_closedir:
free(threads);
threads = NULL;
goto out_closedir;
}

struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid)
{
if (pid != -1)
return thread_map__new_by_pid(pid);

if (tid == -1 && uid != UINT_MAX)
return thread_map__new_by_uid(uid);

return thread_map__new_by_tid(tid);
}

Expand Down
3 changes: 2 additions & 1 deletion tools/perf/util/thread_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ struct thread_map {

struct thread_map *thread_map__new_by_pid(pid_t pid);
struct thread_map *thread_map__new_by_tid(pid_t tid);
struct thread_map *thread_map__new(pid_t pid, pid_t tid);
struct thread_map *thread_map__new_by_uid(uid_t uid);
struct thread_map *thread_map__new(pid_t pid, pid_t tid, uid_t uid);
void thread_map__delete(struct thread_map *threads);

size_t thread_map__fprintf(struct thread_map *threads, FILE *fp);
Expand Down
3 changes: 3 additions & 0 deletions tools/perf/util/top.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ size_t perf_top__header_snprintf(struct perf_top *top, char *bf, size_t size)
else if (top->target_tid != -1)
ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %d",
top->target_tid);
else if (top->uid_str != NULL)
ret += SNPRINTF(bf + ret, size - ret, " (uid: %s",
top->uid_str);
else
ret += SNPRINTF(bf + ret, size - ret, " (all");

Expand Down
Loading

0 comments on commit 0d37aa3

Please sign in to comment.