From: chenggang <chenggang....@taobao.com>

Many applications will fork threads on-the-fly, these threads could exit before
the main thread exit. The perf top tool should perceive the new forked threads
while we profile a special application.
If the target process fork a thread or a thread exit, we will get a 
PERF_RECORD_FORK
 or PERF_RECORD_EXIT events. The following callback functions can process these 
events.
1) perf_top__process_event_fork()
   Open a new fd for the new forked, and expend the related data structures.
2) perf_top__process_event_exit()
   Close the fd of exit threadsd, and destroy the nodes in the related data 
structures.
Cc: David Ahern <dsah...@gmail.com>
Cc: Peter Zijlstra <a.p.zijls...@chello.nl>
Cc: Paul Mackerras <pau...@samba.org>
Cc: Ingo Molnar <mi...@redhat.com>
Cc: Arnaldo Carvalho de Melo <a...@ghostprotocols.net>
Cc: Arjan van de Ven <ar...@linux.intel.com>
Cc: Namhyung Kim <namhy...@gmail.com>
Cc: Yanmin Zhang <yanmin.zh...@intel.com>
Cc: Wu Fengguang <fengguang...@intel.com>
Cc: Mike Galbraith <efa...@gmx.de>
Cc: Andrew Morton <a...@linux-foundation.org>
Cc: linux-kernel <linux-kernel@vger.kernel.org>
Signed-off-by: Chenggang Qin <chenggang....@taobao.com>

---
 tools/perf/builtin-top.c     |  100 +++++++++++++++++++++++++++++++++++++++++-
 tools/perf/util/evlist.c     |   30 ++++++-------
 tools/perf/util/evsel.c      |   13 +++---
 tools/perf/util/thread_map.c |   13 ++++++
 tools/perf/util/thread_map.h |    3 --
 5 files changed, 133 insertions(+), 26 deletions(-)

diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 72f6eb7..94aab11 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -806,7 +806,7 @@ static void perf_top__mmap_read_idx(struct perf_top *top, 
int idx)
        struct perf_evsel *evsel;
        struct perf_session *session = top->session;
        union perf_event *event;
-       struct machine *machine;
+       struct machine *machine = NULL;
        u8 origin;
        int ret;
 
@@ -825,6 +825,20 @@ static void perf_top__mmap_read_idx(struct perf_top *top, 
int idx)
                if (event->header.type == PERF_RECORD_SAMPLE)
                        ++top->samples;
 
+               if (cpu_map__all(top->evlist->cpus) &&
+                   event->header.type == PERF_RECORD_FORK)
+                       (&top->tool)->fork(&top->tool, event, &sample, machine);
+
+               if (cpu_map__all(top->evlist->cpus) &&
+                   event->header.type == PERF_RECORD_EXIT) {
+                       int tidx;
+
+                       tidx = (&top->tool)->exit(&top->tool, event,
+                               &sample, machine);
+                       if (tidx == idx)
+                               break;
+               }
+
                switch (origin) {
                case PERF_RECORD_MISC_USER:
                        ++top->us_samples;
@@ -1024,11 +1038,95 @@ parse_callchain_opt(const struct option *opt, const 
char *arg, int unset)
        return record_parse_callchain_opt(opt, arg, unset);
 }
 
+static int perf_top__append_thread(struct perf_top *top, int tidx)
+{
+       struct perf_evsel *counter;
+       struct perf_evlist *evlist = top->evlist;
+       struct cpu_map *cpus = evlist->cpus;
+
+       list_for_each_entry(counter, &evlist->entries, node)
+               if (perf_evsel__open_thread(counter, cpus, evlist->threads, 
tidx) < 0) {
+                       printf("errno: %d\n", errno);
+                       return -1;
+               }
+
+       if (perf_evlist__mmap_thread(evlist, false, tidx) < 0)
+               return -1;
+
+       return 0;
+}
+
+static int perf_top__process_event_fork(struct perf_tool *tool __maybe_unused,
+                                       union perf_event *event __maybe_unused,
+                                       struct perf_sample *sample 
__maybe_unused,
+                                       struct machine *machine __maybe_unused)
+{
+       pid_t tid = event->fork.tid;
+       pid_t ptid = event->fork.ptid;
+       struct perf_top *top = container_of(tool, struct perf_top, tool);
+       struct thread_map *threads = top->evlist->threads;
+       struct perf_evsel *evsel;
+       int i, ret;
+
+       if (!cpu_map__all(top->evlist->cpus))
+               return -1;
+
+       ret = thread_map__append(threads, tid);
+       if (ret == 1)
+               return ret;
+       if (ret == -1)
+               return ret;
+
+       for(i = 0; i < threads->nr; i++) {
+               if (ptid == thread_map__get_pid(threads, i)) {
+                       if (perf_top__append_thread(top, threads->nr - 1) < 0)
+                               goto free_new_thread;
+                       break;
+               }
+       }
+
+       return 0;
+
+free_new_thread:
+       list_for_each_entry(evsel, &top->evlist->entries, node)
+               perf_evsel__close_thread(evsel, top->evlist->cpus->nr, 
threads->nr - 1);
+       thread_map__remove(threads, threads->nr - 1);
+       return -1;
+}
+
+static int perf_top__process_event_exit(struct perf_tool *tool __maybe_unused,
+                                       union perf_event *event __maybe_unused,
+                                       struct perf_sample *sample 
__maybe_unused,
+                                       struct machine *machine __maybe_unused)
+{
+       pid_t tid = event->fork.tid;
+       struct perf_top *top = container_of(tool, struct perf_top, tool);
+       struct perf_evsel *evsel;
+       struct thread_map *threads = top->evlist->threads;
+       int tidx = thread_map__get_idx_by_pid(threads, tid);
+
+       if (!cpu_map__all(top->evlist->cpus) || tidx < 0) //ignore
+               return -1;
+
+       perf_evlist__munmap_thread(top->evlist, tidx);
+
+       list_for_each_entry(evsel, &top->evlist->entries, node)
+               perf_evsel__close_thread(evsel, top->evlist->cpus->nr, tidx);
+
+       thread_map__remove(threads, tidx);
+
+       return tidx;
+}
+
 int cmd_top(int argc, const char **argv, const char *prefix __maybe_unused)
 {
        int status;
        char errbuf[BUFSIZ];
        struct perf_top top = {
+               .tool = {
+                       .fork           = perf_top__process_event_fork,
+                       .exit           = perf_top__process_event_exit,
+               },
                .count_filter        = 5,
                .delay_secs          = 2,
                .record_opts = {
diff --git a/tools/perf/util/evlist.c b/tools/perf/util/evlist.c
index 90cfbb6..eb07dbb 100644
--- a/tools/perf/util/evlist.c
+++ b/tools/perf/util/evlist.c
@@ -264,24 +264,24 @@ void perf_evlist__enable(struct perf_evlist *evlist)
  */
 static int perf_evlist__append_pollfd_thread(struct perf_evlist *evlist)
 {
-        int new_nfds;
+       int new_nfds;
 
-        if (cpu_map__all(evlist->cpus)) {
-                struct pollfd *pfd;
+       if (cpu_map__all(evlist->cpus)) {
+               struct pollfd *pfd;
 
-                new_nfds = evlist->threads->nr * evlist->nr_entries;
-                pfd = zalloc(sizeof(struct pollfd) * new_nfds); //FIXME
+               new_nfds = evlist->threads->nr * evlist->nr_entries;
+               pfd = zalloc(sizeof(struct pollfd) * new_nfds);
 
-                if (!pfd)
-                        return -1;
+               if (!pfd)
+                       return -1;
 
-                memcpy(pfd, evlist->pollfd, (evlist->threads->nr - 1) * 
evlist->nr_entries);
+               memcpy(pfd, evlist->pollfd, (evlist->threads->nr - 1) * 
evlist->nr_entries);
 
-                evlist->pollfd = pfd;
-                return 0;
-        }
+               evlist->pollfd = pfd;
+               return 0;
+       }
 
-        return 1;
+       return 1;
 }
 
 static int perf_evlist__alloc_pollfd(struct perf_evlist *evlist)
@@ -448,7 +448,7 @@ static int perf_evlist__append_mmap_thread(struct 
perf_evlist *evlist)
                return -1;
        evlist->nr_mmaps++;
 
-       return 1;
+       return 0;
 }
 
 static int perf_evlist__alloc_mmap(struct perf_evlist *evlist)
@@ -573,8 +573,7 @@ int perf_evlist__mmap_thread(struct perf_evlist *evlist, 
bool overwrite, int tid
                goto free_append_mmap;
 
        list_for_each_entry(evsel, &evlist->entries, node)
-               if ((evsel->attr.read_format & PERF_FORMAT_ID) &&
-                   evsel->sample_id == NULL)
+               if (evsel->attr.read_format & PERF_FORMAT_ID)
                        if (perf_evsel__append_id_thread(evsel, tidx) < 0)
                                goto free_append_pollfd;
 
@@ -633,6 +632,7 @@ void perf_evlist__munmap_thread(struct perf_evlist *evlist, 
int tidx)
 
        list_for_each_entry(evsel, &evlist->entries, node) {
                xyarray__remove(evsel->id, tidx);
+               evsel->ids--;
                xyarray__remove(evsel->sample_id, tidx);
        }
 
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index c439027..68b2813 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -851,10 +851,9 @@ int perf_evsel__open_thread(struct perf_evsel *evsel, 
struct cpu_map *cpus,
        int cpu;
        int pid = -1;
        unsigned long flags = 0;
-       int err;
 
        if (perf_evsel__append_fd(evsel, tidx) < 0)
-               return 1;
+               return -1;
 
        if (evsel->cgrp) {
                flags = PERF_FLAG_PID_CGROUP;
@@ -868,15 +867,15 @@ int perf_evsel__open_thread(struct perf_evsel *evsel, 
struct cpu_map *cpus,
                        pid = tid;
 
                group_fd = get_group_fd(evsel, cpu, tidx);
+               evsel->attr.disabled = 0;
                FD(evsel, cpu, tidx) = sys_perf_event_open(&evsel->attr,
                                                           pid,
                                                           cpus->map[cpu],
                                                           group_fd, flags);
-               if (FD(evsel, cpu, tidx) < 0) {
-                       printf("error: cannot open counter for: %d\n", tid);
-                       err = -errno;
-                       printf("errno: %d\n", errno);
-                       return err;
+               if (FD(evsel, cpu, tidx) < 0) {
+                       pr_warning("error: cannot open counter for: %d\n", tid);
+                       pr_warning("errno: %d\n", errno);
+                       return -errno;
                }
        }
 
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c
index 5f96fdf..0d3ec3f 100644
--- a/tools/perf/util/thread_map.c
+++ b/tools/perf/util/thread_map.c
@@ -322,6 +322,19 @@ struct thread_map *thread_map__new_str(const char *pid, 
const char *tid,
        return thread_map__new_by_tid_str(tid);
 }
 
+int thread_map__get_idx_by_pid(struct thread_map *threads, pid_t pid)
+{
+       struct thread_pid *tp;
+       int count = 0;
+
+       list_for_each_entry(tp, &threads->head, next) {
+               if (tp->pid == pid)
+                       return count;
+               count++;
+       }
+       return -1;
+}
+
 struct thread_map *thread_map__empty_thread_map(void)
 {
        struct thread_map *empty_thread_map = NULL;
diff --git a/tools/perf/util/thread_map.h b/tools/perf/util/thread_map.h
index e5a3013..cfe586b 100644
--- a/tools/perf/util/thread_map.h
+++ b/tools/perf/util/thread_map.h
@@ -27,10 +27,7 @@ struct thread_map *thread_map__new_str(const char *pid,
                const char *tid, uid_t uid);
 
 int thread_map__append(struct thread_map *threads, pid_t pid);
-int thread_map__remove_by_pid(struct thread_map *threads, pid_t pid);
 int thread_map__remove(struct thread_map *threads, int idx);
-int thread_map__set_xy_pid(struct xyarray *xy, struct thread_map *threads);
-int thread_map__set_pid(struct thread_map *threads, int index, pid_t pid);
 int thread_map__get_pid(struct thread_map *threads, int index);
 int thread_map__get_idx_by_pid(struct thread_map *threads, pid_t pid);
 
-- 
1.7.9.5

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to