From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752368AbbERAsU (ORCPT ); Sun, 17 May 2015 20:48:20 -0400 Received: from LGEMRELSE6Q.lge.com ([156.147.1.121]:55046 "EHLO lgemrelse6q.lge.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752177AbbERAlg (ORCPT ); Sun, 17 May 2015 20:41:36 -0400 X-Original-SENDERIP: 10.177.220.203 X-Original-MAILFROM: namhyung@kernel.org From: Namhyung Kim To: Arnaldo Carvalho de Melo Cc: Ingo Molnar , Peter Zijlstra , Jiri Olsa , LKML , David Ahern , Adrian Hunter , Andi Kleen , Frederic Weisbecker , Stephane Eranian Subject: [PATCH 17/40] perf tools: Maintain map groups list in a leader thread Date: Mon, 18 May 2015 09:30:32 +0900 Message-Id: <1431909055-21442-18-git-send-email-namhyung@kernel.org> X-Mailer: git-send-email 2.4.0 In-Reply-To: <1431909055-21442-1-git-send-email-namhyung@kernel.org> References: <1431909055-21442-1-git-send-email-namhyung@kernel.org> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org To support multi-threaded perf report, we need to maintain time-sorted map groups. Add ->mg_list member to struct thread and sort the list by time. Now leader threads have one more refcnt for map groups in the list so also update the thread-mg-share test case. Currently only add a new map groups when an exec (comm) event is received. Cc: Frederic Weisbecker Signed-off-by: Namhyung Kim --- tools/perf/tests/thread-mg-share.c | 7 ++- tools/perf/util/event.c | 2 + tools/perf/util/machine.c | 4 +- tools/perf/util/map.c | 3 ++ tools/perf/util/map.h | 2 + tools/perf/util/thread.c | 108 ++++++++++++++++++++++++++++++++++++- tools/perf/util/thread.h | 3 ++ 7 files changed, 124 insertions(+), 5 deletions(-) diff --git a/tools/perf/tests/thread-mg-share.c b/tools/perf/tests/thread-mg-share.c index c0ed56f7efc6..50a2c68d6379 100644 --- a/tools/perf/tests/thread-mg-share.c +++ b/tools/perf/tests/thread-mg-share.c @@ -23,6 +23,9 @@ int test__thread_mg_share(void) * with several threads and checks they properly share and * maintain map groups info (struct map_groups). * + * Note that a leader thread has one more refcnt for its + * (current) map groups. + * * thread group (pid: 0, tids: 0, 1, 2, 3) * other group (pid: 4, tids: 4, 5) */ @@ -43,7 +46,7 @@ int test__thread_mg_share(void) leader && t1 && t2 && t3 && other); mg = leader->mg; - TEST_ASSERT_EQUAL("wrong refcnt", mg->refcnt, 4); + TEST_ASSERT_EQUAL("wrong refcnt", mg->refcnt, 5); /* test the map groups pointer is shared */ TEST_ASSERT_VAL("map groups don't match", mg == t1->mg); @@ -71,7 +74,7 @@ int test__thread_mg_share(void) machine__remove_thread(machine, other_leader); other_mg = other->mg; - TEST_ASSERT_EQUAL("wrong refcnt", other_mg->refcnt, 2); + TEST_ASSERT_EQUAL("wrong refcnt", other_mg->refcnt, 3); TEST_ASSERT_VAL("map groups don't match", other_mg == other_leader->mg); diff --git a/tools/perf/util/event.c b/tools/perf/util/event.c index 819a2d75411c..0ad76b06cd48 100644 --- a/tools/perf/util/event.c +++ b/tools/perf/util/event.c @@ -851,6 +851,8 @@ void thread__find_addr_map(struct thread *thread, u8 cpumode, return; } + BUG_ON(mg == NULL); + if (cpumode == PERF_RECORD_MISC_KERNEL && perf_host) { al->level = 'k'; mg = &machine->kmaps; diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 9e2f4e8663d5..99fb14926351 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -341,7 +341,7 @@ static void machine__update_thread_pid(struct machine *machine, goto out_err; if (!leader->mg) - leader->mg = map_groups__new(machine); + thread__set_map_groups(leader, map_groups__new(machine), 0); if (!leader->mg) goto out_err; @@ -358,7 +358,7 @@ static void machine__update_thread_pid(struct machine *machine, if (!map_groups__empty(th->mg)) pr_err("Discarding thread maps for %d:%d\n", th->pid_, th->tid); - map_groups__delete(th->mg); + map_groups__put(th->mg); } th->mg = map_groups__get(leader->mg); diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c index cd0e335008b4..b794c3561995 100644 --- a/tools/perf/util/map.c +++ b/tools/perf/util/map.c @@ -427,6 +427,8 @@ void map_groups__init(struct map_groups *mg, struct machine *machine) } mg->machine = machine; mg->refcnt = 1; + mg->timestamp = 0; + INIT_LIST_HEAD(&mg->list); } static void maps__delete(struct rb_root *maps) @@ -489,6 +491,7 @@ struct map_groups *map_groups__new(struct machine *machine) void map_groups__delete(struct map_groups *mg) { map_groups__exit(mg); + list_del(&mg->list); free(mg); } diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h index 4e0c729841ab..074453d332dd 100644 --- a/tools/perf/util/map.h +++ b/tools/perf/util/map.h @@ -61,7 +61,9 @@ struct map_groups { struct rb_root maps[MAP__NR_TYPES]; struct list_head removed_maps[MAP__NR_TYPES]; struct machine *machine; + u64 timestamp; int refcnt; + struct list_head list; }; struct map_groups *map_groups__new(struct machine *machine); diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index c8c927488ea0..fc4e51afaf18 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -11,13 +11,76 @@ #include "unwind.h" #include "machine.h" +struct map_groups *thread__get_map_groups(struct thread *thread, u64 timestamp) +{ + struct map_groups *mg; + struct thread *leader = thread; + + BUG_ON(thread->mg == NULL); + + if (thread->tid != thread->pid_) { + leader = machine__find_thread_by_time(thread->mg->machine, + thread->pid_, thread->pid_, + timestamp); + if (leader == NULL) + goto out; + } + + list_for_each_entry(mg, &leader->mg_list, list) + if (timestamp >= mg->timestamp) + return mg; + +out: + return thread->mg; +} + +int thread__set_map_groups(struct thread *thread, struct map_groups *mg, + u64 timestamp) +{ + struct list_head *pos; + struct map_groups *old; + + if (mg == NULL) + return -ENOMEM; + + /* + * Only a leader thread can have map groups list - others + * reference it through map_groups__get. This means the + * leader thread will have one more refcnt than others. + */ + if (thread->tid != thread->pid_) + return -EINVAL; + + if (thread->mg) { + BUG_ON(thread->mg->refcnt <= 1); + map_groups__put(thread->mg); + } + + /* sort by time */ + list_for_each(pos, &thread->mg_list) { + old = list_entry(pos, struct map_groups, list); + if (timestamp > old->timestamp) + break; + } + + list_add_tail(&mg->list, pos); + mg->timestamp = timestamp; + + /* set current ->mg to most recent one */ + thread->mg = list_first_entry(&thread->mg_list, struct map_groups, list); + /* increase one more refcnt for current */ + map_groups__get(thread->mg); + + return 0; +} + int thread__init_map_groups(struct thread *thread, struct machine *machine) { struct thread *leader; pid_t pid = thread->pid_; if (pid == thread->tid || pid == -1) { - thread->mg = map_groups__new(machine); + thread__set_map_groups(thread, map_groups__new(machine), 0); } else { leader = __machine__findnew_thread(machine, pid, pid); if (leader) @@ -40,6 +103,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) thread->cpu = -1; INIT_LIST_HEAD(&thread->comm_list); INIT_LIST_HEAD(&thread->tid_node); + INIT_LIST_HEAD(&thread->mg_list); if (unwind__prepare_access(thread) < 0) goto err_thread; @@ -70,6 +134,7 @@ struct thread *thread__new(pid_t pid, pid_t tid) void thread__delete(struct thread *thread) { struct comm *comm, *tmp; + struct map_groups *mg, *tmp_mg; BUG_ON(!RB_EMPTY_NODE(&thread->rb_node)); BUG_ON(!list_empty(&thread->tid_node)); @@ -80,6 +145,10 @@ void thread__delete(struct thread *thread) map_groups__put(thread->mg); thread->mg = NULL; } + /* only leader threads have mg list */ + list_for_each_entry_safe(mg, tmp_mg, &thread->mg_list, list) + map_groups__put(mg); + list_for_each_entry_safe(comm, tmp, &thread->comm_list, list) { list_del(&comm->list); comm__free(comm); @@ -153,6 +222,9 @@ struct comm *thread__comm_by_time(const struct thread *thread, u64 timestamp) return list_last_entry(&thread->comm_list, struct comm, list); } +static int thread__clone_map_groups(struct thread *thread, + struct thread *parent); + int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, bool exec) { @@ -183,6 +255,40 @@ int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp, unwind__flush_access(thread); } + if (exec) { + struct machine *machine; + + BUG_ON(thread->mg == NULL || thread->mg->machine == NULL); + + machine = thread->mg->machine; + + if (thread->tid != thread->pid_) { + struct map_groups *old = thread->mg; + struct thread *leader; + + leader = machine__findnew_thread(machine, thread->pid_, + thread->pid_); + + /* now it'll be a new leader */ + thread->pid_ = thread->tid; + + thread->mg = map_groups__new(old->machine); + if (thread->mg == NULL) + return -ENOMEM; + + /* save current mg in the new leader */ + thread__clone_map_groups(thread, leader); + + /* current mg of leader thread needs one more refcnt */ + map_groups__get(thread->mg); + + thread__set_map_groups(thread, thread->mg, old->timestamp); + } + + /* create a new mg for newly executed binary */ + thread__set_map_groups(thread, map_groups__new(machine), timestamp); + } + thread->comm_set = true; return 0; diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h index 60250db7f77b..468bb785f061 100644 --- a/tools/perf/util/thread.h +++ b/tools/perf/util/thread.h @@ -16,6 +16,7 @@ struct thread { struct rb_node rb_node; struct list_head tid_node; struct map_groups *mg; + struct list_head mg_list; pid_t pid_; /* Not all tools update this */ pid_t tid; pid_t ppid; @@ -71,6 +72,8 @@ struct comm *thread__exec_comm(const struct thread *thread); struct comm *thread__comm_by_time(const struct thread *thread, u64 timestamp); const char *thread__comm_str(const struct thread *thread); const char *thread__comm_str_by_time(const struct thread *thread, u64 timestamp); +struct map_groups *thread__get_map_groups(struct thread *thread, u64 timestamp); +int thread__set_map_groups(struct thread *thread, struct map_groups *mg, u64 timestamp); void thread__insert_map(struct thread *thread, struct map *map); int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp); size_t thread__fprintf(struct thread *thread, FILE *fp); -- 2.4.0