summaryrefslogtreecommitdiff
path: root/tools/perf/util/thread_map.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2015-07-04 08:17:29 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2015-07-04 08:17:29 -0700
commitc1776a18e3b5a3559f3dff5df0ecce570abd3a9f (patch)
tree5b97defe199f434f5d9829bf7d8df6124e6ce5f1 /tools/perf/util/thread_map.c
parent91cca0f0ffa3e485ad9d5b44744f23318c8f99ab (diff)
parentb9df84fd7c05cc300d6d14f022b8a00773ebcf8c (diff)
Merge branch 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull perf updates from Ingo Molnar: "This tree includes an x86 PMU scheduling fix, but most changes are late breaking tooling fixes and updates: User visible fixes: - Create config.detected into OUTPUT directory, fixing parallel builds sharing the same source directory (Aaro Kiskinen) - Allow to specify custom linker command, fixing some MIPS64 builds. (Aaro Kiskinen) - Fix to show proper convergence stats in 'perf bench numa' (Srikar Dronamraju) User visible changes: - Validate syscall list passed via -e argument to 'perf trace'. (Arnaldo Carvalho de Melo) - Introduce 'perf stat --per-thread' (Jiri Olsa) - Check access permission for --kallsyms and --vmlinux (Li Zhang) - Move toggling event logic from 'perf top' and into hists browser, allowing freeze/unfreeze with event lists with more than one entry (Namhyung Kim) - Add missing newlines when dumping PERF_RECORD_FINISHED_ROUND and showing the Aggregated stats in 'perf report -D' (Adrian Hunter) Infrastructure fixes: - Add missing break for PERF_RECORD_ITRACE_START, which caused those events samples to be parsed as well as PERF_RECORD_LOST_SAMPLES. ITRACE_START only appears when Intel PT or BTS are present, so.. (Jiri Olsa) - Call the perf_session destructor when bailing out in the inject, kmem, report, kvm and mem tools (Taeung Song) Infrastructure changes: - Move stuff out of 'perf stat' and into the lib for further use (Jiri Olsa) - Reference count the cpu_map and thread_map classes (Jiri Olsa) - Set evsel->{cpus,threads} from the evlist, if not set, allowing the generalization of some 'perf stat' functions that previously were accessing private static evlist variable (Jiri Olsa) - Delete an unnecessary check before the calling free_event_desc() (Markus Elfring) - Allow auxtrace data alignment (Adrian Hunter) - Allow events with dot (Andi Kleen) - Fix failure to 'perf probe' events on arm (He Kuang) - Add testing for Makefile.perf (Jiri Olsa) - Add test for make install with prefix (Jiri Olsa) - Fix single target build dependency check (Jiri Olsa) - Access thread_map entries via accessors, prep patch to hold more info per entry, for ongoing 'perf stat --per-thread' work (Jiri Olsa) - Use __weak definition from compiler.h (Sukadev Bhattiprolu) - Split perf_pmu__new_alias() (Sukadev Bhattiprolu)" * 'perf-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (54 commits) perf tools: Allow to specify custom linker command perf tools: Create config.detected into OUTPUT directory perf mem: Fill in the missing session freeing after an error occurs perf kvm: Fill in the missing session freeing after an error occurs perf report: Fill in the missing session freeing after an error occurs perf kmem: Fill in the missing session freeing after an error occurs perf inject: Fill in the missing session freeing after an error occurs perf tools: Add missing break for PERF_RECORD_ITRACE_START perf/x86: Fix 'active_events' imbalance perf symbols: Check access permission when reading symbol files perf stat: Introduce --per-thread option perf stat: Introduce print_counters function perf stat: Using init_stats instead of memset perf stat: Rename print_interval to process_interval perf stat: Remove perf_evsel__read_cb function perf stat: Move perf_stat initialization counter process code perf stat: Move zero_per_pkg into counter process code perf stat: Separate counters reading and processing perf stat: Introduce read_counters function perf stat: Introduce perf_evsel__read function ...
Diffstat (limited to 'tools/perf/util/thread_map.c')
-rw-r--r--tools/perf/util/thread_map.c132
1 files changed, 117 insertions, 15 deletions
diff --git a/tools/perf/util/thread_map.c b/tools/perf/util/thread_map.c
index f4822bd03709..da7646d767fe 100644
--- a/tools/perf/util/thread_map.c
+++ b/tools/perf/util/thread_map.c
@@ -8,8 +8,11 @@
#include <unistd.h>
#include "strlist.h"
#include <string.h>
+#include <api/fs/fs.h>
+#include "asm/bug.h"
#include "thread_map.h"
#include "util.h"
+#include "debug.h"
/* Skip "." and ".." directories */
static int filter(const struct dirent *dir)
@@ -20,11 +23,26 @@ static int filter(const struct dirent *dir)
return 1;
}
+static void thread_map__reset(struct thread_map *map, int start, int nr)
+{
+ size_t size = (nr - start) * sizeof(map->map[0]);
+
+ memset(&map->map[start], 0, size);
+}
+
static struct thread_map *thread_map__realloc(struct thread_map *map, int nr)
{
- size_t size = sizeof(*map) + sizeof(pid_t) * nr;
+ size_t size = sizeof(*map) + sizeof(map->map[0]) * nr;
+ int start = map ? map->nr : 0;
+
+ map = realloc(map, size);
+ /*
+ * We only realloc to add more items, let's reset new items.
+ */
+ if (map)
+ thread_map__reset(map, start, nr);
- return realloc(map, size);
+ return map;
}
#define thread_map__alloc(__nr) thread_map__realloc(NULL, __nr)
@@ -45,8 +63,9 @@ struct thread_map *thread_map__new_by_pid(pid_t pid)
threads = thread_map__alloc(items);
if (threads != NULL) {
for (i = 0; i < items; i++)
- threads->map[i] = atoi(namelist[i]->d_name);
+ thread_map__set_pid(threads, i, atoi(namelist[i]->d_name));
threads->nr = items;
+ atomic_set(&threads->refcnt, 1);
}
for (i=0; i<items; i++)
@@ -61,8 +80,9 @@ struct thread_map *thread_map__new_by_tid(pid_t tid)
struct thread_map *threads = thread_map__alloc(1);
if (threads != NULL) {
- threads->map[0] = tid;
- threads->nr = 1;
+ thread_map__set_pid(threads, 0, tid);
+ threads->nr = 1;
+ atomic_set(&threads->refcnt, 1);
}
return threads;
@@ -84,6 +104,7 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)
goto out_free_threads;
threads->nr = 0;
+ atomic_set(&threads->refcnt, 1);
while (!readdir_r(proc, &dirent, &next) && next) {
char *end;
@@ -123,8 +144,10 @@ struct thread_map *thread_map__new_by_uid(uid_t uid)
threads = tmp;
}
- for (i = 0; i < items; i++)
- threads->map[threads->nr + i] = atoi(namelist[i]->d_name);
+ for (i = 0; i < items; i++) {
+ thread_map__set_pid(threads, threads->nr + i,
+ atoi(namelist[i]->d_name));
+ }
for (i = 0; i < items; i++)
zfree(&namelist[i]);
@@ -201,7 +224,7 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
threads = nt;
for (i = 0; i < items; i++) {
- threads->map[j++] = atoi(namelist[i]->d_name);
+ thread_map__set_pid(threads, j++, atoi(namelist[i]->d_name));
zfree(&namelist[i]);
}
threads->nr = total_tasks;
@@ -210,6 +233,8 @@ static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
out:
strlist__delete(slist);
+ if (threads)
+ atomic_set(&threads->refcnt, 1);
return threads;
out_free_namelist:
@@ -227,8 +252,9 @@ struct thread_map *thread_map__new_dummy(void)
struct thread_map *threads = thread_map__alloc(1);
if (threads != NULL) {
- threads->map[0] = -1;
- threads->nr = 1;
+ thread_map__set_pid(threads, 0, -1);
+ threads->nr = 1;
+ atomic_set(&threads->refcnt, 1);
}
return threads;
}
@@ -267,10 +293,12 @@ static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
goto out_free_threads;
threads = nt;
- threads->map[ntasks - 1] = tid;
- threads->nr = ntasks;
+ thread_map__set_pid(threads, ntasks - 1, tid);
+ threads->nr = ntasks;
}
out:
+ if (threads)
+ atomic_set(&threads->refcnt, 1);
return threads;
out_free_threads:
@@ -290,9 +318,30 @@ struct thread_map *thread_map__new_str(const char *pid, const char *tid,
return thread_map__new_by_tid_str(tid);
}
-void thread_map__delete(struct thread_map *threads)
+static void thread_map__delete(struct thread_map *threads)
{
- free(threads);
+ if (threads) {
+ int i;
+
+ WARN_ONCE(atomic_read(&threads->refcnt) != 0,
+ "thread map refcnt unbalanced\n");
+ for (i = 0; i < threads->nr; i++)
+ free(thread_map__comm(threads, i));
+ free(threads);
+ }
+}
+
+struct thread_map *thread_map__get(struct thread_map *map)
+{
+ if (map)
+ atomic_inc(&map->refcnt);
+ return map;
+}
+
+void thread_map__put(struct thread_map *map)
+{
+ if (map && atomic_dec_and_test(&map->refcnt))
+ thread_map__delete(map);
}
size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
@@ -301,7 +350,60 @@ size_t thread_map__fprintf(struct thread_map *threads, FILE *fp)
size_t printed = fprintf(fp, "%d thread%s: ",
threads->nr, threads->nr > 1 ? "s" : "");
for (i = 0; i < threads->nr; ++i)
- printed += fprintf(fp, "%s%d", i ? ", " : "", threads->map[i]);
+ printed += fprintf(fp, "%s%d", i ? ", " : "", thread_map__pid(threads, i));
return printed + fprintf(fp, "\n");
}
+
+static int get_comm(char **comm, pid_t pid)
+{
+ char *path;
+ size_t size;
+ int err;
+
+ if (asprintf(&path, "%s/%d/comm", procfs__mountpoint(), pid) == -1)
+ return -ENOMEM;
+
+ err = filename__read_str(path, comm, &size);
+ if (!err) {
+ /*
+ * We're reading 16 bytes, while filename__read_str
+ * allocates data per BUFSIZ bytes, so we can safely
+ * mark the end of the string.
+ */
+ (*comm)[size] = 0;
+ rtrim(*comm);
+ }
+
+ free(path);
+ return err;
+}
+
+static void comm_init(struct thread_map *map, int i)
+{
+ pid_t pid = thread_map__pid(map, i);
+ char *comm = NULL;
+
+ /* dummy pid comm initialization */
+ if (pid == -1) {
+ map->map[i].comm = strdup("dummy");
+ return;
+ }
+
+ /*
+ * The comm name is like extra bonus ;-),
+ * so just warn if we fail for any reason.
+ */
+ if (get_comm(&comm, pid))
+ pr_warning("Couldn't resolve comm name for pid %d\n", pid);
+
+ map->map[i].comm = comm;
+}
+
+void thread_map__read_comms(struct thread_map *threads)
+{
+ int i;
+
+ for (i = 0; i < threads->nr; ++i)
+ comm_init(threads, i);
+}