-p::
 --pid=::
-       Record events on existing process ID.
+       Record events on existing process ID (comma separated list).
 
 -t::
 --tid=::
-        Record events on existing thread ID.
+        Record events on existing thread ID (comma separated list).
 
 -u::
 --uid=::
 
         child tasks do not inherit counters
 -p::
 --pid=<pid>::
-        stat events on existing process id
+        stat events on existing process id (comma separated list)
 
 -t::
 --tid=<tid>::
-        stat events on existing thread id
+        stat events on existing thread id (comma separated list)
 
 
 -a::
 
 
 -p <pid>::
 --pid=<pid>::
-       Profile events on existing Process ID.
+       Profile events on existing Process ID (comma separated list).
 
 -t <tid>::
 --tid=<tid>::
-        Profile events on existing thread ID.
+        Profile events on existing thread ID (comma separated list).
 
 -u::
 --uid=::
 
  */
 static struct perf_record record = {
        .opts = {
-               .target_pid          = -1,
-               .target_tid          = -1,
                .mmap_pages          = UINT_MAX,
                .user_freq           = UINT_MAX,
                .user_interval       = ULLONG_MAX,
                     parse_events_option),
        OPT_CALLBACK(0, "filter", &record.evlist, "filter",
                     "event filter", parse_filter),
-       OPT_INTEGER('p', "pid", &record.opts.target_pid,
+       OPT_STRING('p', "pid", &record.opts.target_pid, "pid",
                    "record events on existing process id"),
-       OPT_INTEGER('t', "tid", &record.opts.target_tid,
+       OPT_STRING('t', "tid", &record.opts.target_tid, "tid",
                    "record events on existing thread id"),
        OPT_INTEGER('r', "realtime", &record.realtime_prio,
                    "collect data with this RT SCHED_FIFO priority"),
 
        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 &&
+       if (!argc && !rec->opts.target_pid && !rec->opts.target_tid &&
                !rec->opts.system_wide && !rec->opts.cpu_list && !rec->uid_str)
                usage_with_options(record_usage, record_options);
 
        if (rec->uid_str != NULL && rec->opts.uid == UINT_MAX - 1)
                goto out_free_fd;
 
-       if (rec->opts.target_pid != -1)
+       if (rec->opts.target_pid)
                rec->opts.target_tid = rec->opts.target_pid;
 
        if (perf_evlist__create_maps(evsel_list, rec->opts.target_pid,
 
 static bool                    no_inherit                      = false;
 static bool                    scale                           =  true;
 static bool                    no_aggr                         = false;
-static pid_t                   target_pid                      = -1;
-static pid_t                   target_tid                      = -1;
+static const char              *target_pid;
+static const char              *target_tid;
 static pid_t                   child_pid                       = -1;
 static bool                    null_run                        =  false;
 static int                     detailed_run                    =  0;
        if (system_wide)
                return perf_evsel__open_per_cpu(evsel, evsel_list->cpus,
                                                group, group_fd);
-       if (target_pid == -1 && target_tid == -1) {
+       if (!target_pid && !target_tid) {
                attr->disabled = 1;
                attr->enable_on_exec = 1;
        }
                        exit(-1);
                }
 
-               if (target_tid == -1 && target_pid == -1 && !system_wide)
+               if (!target_tid && !target_pid && !system_wide)
                        evsel_list->threads->map[0] = child_pid;
 
                /*
        if (!csv_output) {
                fprintf(output, "\n");
                fprintf(output, " Performance counter stats for ");
-               if(target_pid == -1 && target_tid == -1) {
+               if (!target_pid && !target_tid) {
                        fprintf(output, "\'%s", argv[0]);
                        for (i = 1; i < argc; i++)
                                fprintf(output, " %s", argv[i]);
-               } else if (target_pid != -1)
-                       fprintf(output, "process id \'%d", target_pid);
+               } else if (target_pid)
+                       fprintf(output, "process id \'%s", target_pid);
                else
-                       fprintf(output, "thread id \'%d", target_tid);
+                       fprintf(output, "thread id \'%s", target_tid);
 
                fprintf(output, "\'");
                if (run_count > 1)
                     "event filter", parse_filter),
        OPT_BOOLEAN('i', "no-inherit", &no_inherit,
                    "child tasks do not inherit counters"),
-       OPT_INTEGER('p', "pid", &target_pid,
-                   "stat events on existing process id"),
-       OPT_INTEGER('t', "tid", &target_tid,
-                   "stat events on existing thread id"),
+       OPT_STRING('p', "pid", &target_pid, "pid",
+                  "stat events on existing process id"),
+       OPT_STRING('t', "tid", &target_tid, "tid",
+                  "stat events on existing thread id"),
        OPT_BOOLEAN('a', "all-cpus", &system_wide,
                    "system-wide collection from all CPUs"),
        OPT_BOOLEAN('g', "group", &group,
        } else if (big_num_opt == 0) /* User passed --no-big-num */
                big_num = false;
 
-       if (!argc && target_pid == -1 && target_tid == -1)
+       if (!argc && !target_pid && !target_tid)
                usage_with_options(stat_usage, options);
        if (run_count <= 0)
                usage_with_options(stat_usage, options);
        if (add_default_attributes())
                goto out;
 
-       if (target_pid != -1)
+       if (target_pid)
                target_tid = target_pid;
 
-       evsel_list->threads = thread_map__new(target_pid, target_tid, UINT_MAX);
+       evsel_list->threads = thread_map__new_str(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);
 
 static int test__PERF_RECORD(void)
 {
        struct perf_record_opts opts = {
-               .target_pid = -1,
-               .target_tid = -1,
                .no_delay   = true,
                .freq       = 10,
                .mmap_pages = 256,
 
        if (ret)
                goto out_delete;
 
-       if (top->target_tid != -1 || top->uid != UINT_MAX)
+       if (top->target_tid || top->uid != UINT_MAX)
                perf_event__synthesize_thread_map(&top->tool, top->evlist->threads,
                                                  perf_event__process,
                                                  &top->session->host_machine);
        struct perf_top top = {
                .count_filter        = 5,
                .delay_secs          = 2,
-               .target_pid          = -1,
-               .target_tid          = -1,
                .uid                 = UINT_MAX,
                .freq                = 1000, /* 1 KHz */
                .sample_id_all_avail = true,
                     parse_events_option),
        OPT_INTEGER('c', "count", &top.default_interval,
                    "event period to sample"),
-       OPT_INTEGER('p', "pid", &top.target_pid,
+       OPT_STRING('p', "pid", &top.target_pid, "pid",
                    "profile events on existing process id"),
-       OPT_INTEGER('t', "tid", &top.target_tid,
+       OPT_STRING('t', "tid", &top.target_tid, "tid",
                    "profile events on existing thread id"),
        OPT_BOOLEAN('a', "all-cpus", &top.system_wide,
                            "system-wide collection from all CPUs"),
                goto out_delete_evlist;
 
        /* CPU and PID are mutually exclusive */
-       if (top.target_tid > 0 && top.cpu_list) {
+       if (top.target_tid && top.cpu_list) {
                printf("WARNING: PID switch overriding CPU\n");
                sleep(1);
                top.cpu_list = NULL;
        }
 
-       if (top.target_pid != -1)
+       if (top.target_pid)
                top.target_tid = top.target_pid;
 
        if (perf_evlist__create_maps(top.evlist, top.target_pid,
 
 void pthread__unblock_sigwinch(void);
 
 struct perf_record_opts {
-       pid_t        target_pid;
-       pid_t        target_tid;
+       const char   *target_pid;
+       const char   *target_tid;
        uid_t        uid;
        bool         call_graph;
        bool         group;
 
        return perf_evlist__mmap_per_cpu(evlist, prot, mask);
 }
 
-int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid,
-                            pid_t target_tid, uid_t uid, const char *cpu_list)
+int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid,
+                            const char *target_tid, uid_t uid, const char *cpu_list)
 {
-       evlist->threads = thread_map__new(target_pid, target_tid, uid);
+       evlist->threads = thread_map__new_str(target_pid, target_tid, uid);
 
        if (evlist->threads == NULL)
                return -1;
 
-       if (uid != UINT_MAX || (cpu_list == NULL && target_tid != -1))
+       if (uid != UINT_MAX || (cpu_list == NULL && target_tid))
                evlist->cpus = cpu_map__dummy_new();
        else
                evlist->cpus = cpu_map__new(cpu_list);
                exit(-1);
        }
 
-       if (!opts->system_wide && opts->target_tid == -1 && opts->target_pid == -1)
+       if (!opts->system_wide && !opts->target_tid && !opts->target_pid)
                evlist->threads->map[0] = evlist->workload.pid;
 
        close(child_ready_pipe[1]);
 
        evlist->threads = threads;
 }
 
-int perf_evlist__create_maps(struct perf_evlist *evlist, pid_t target_pid,
-                            pid_t tid, uid_t uid, const char *cpu_list);
+int perf_evlist__create_maps(struct perf_evlist *evlist, const char *target_pid,
+                            const char *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);
 
 
        attr->mmap = track;
        attr->comm = track;
 
-       if (opts->target_pid == -1 && opts->target_tid == -1 && !opts->system_wide) {
+       if (!opts->target_pid && !opts->target_tid && !opts->system_wide) {
                attr->disabled = 1;
                attr->enable_on_exec = 1;
        }
 
 util/xyarray.c
 util/cgroup.c
 util/debugfs.c
+util/strlist.c
+../../lib/rbtree.c
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
+#include "strlist.h"
+#include <string.h>
 #include "thread_map.h"
 
 /* Skip "." and ".." directories */
        return thread_map__new_by_tid(tid);
 }
 
+static struct thread_map *thread_map__new_by_pid_str(const char *pid_str)
+{
+       struct thread_map *threads = NULL, *nt;
+       char name[256];
+       int items, total_tasks = 0;
+       struct dirent **namelist = NULL;
+       int i, j = 0;
+       pid_t pid, prev_pid = INT_MAX;
+       char *end_ptr;
+       struct str_node *pos;
+       struct strlist *slist = strlist__new(false, pid_str);
+
+       if (!slist)
+               return NULL;
+
+       strlist__for_each(pos, slist) {
+               pid = strtol(pos->s, &end_ptr, 10);
+
+               if (pid == INT_MIN || pid == INT_MAX ||
+                   (*end_ptr != '\0' && *end_ptr != ','))
+                       goto out_free_threads;
+
+               if (pid == prev_pid)
+                       continue;
+
+               sprintf(name, "/proc/%d/task", pid);
+               items = scandir(name, &namelist, filter, NULL);
+               if (items <= 0)
+                       goto out_free_threads;
+
+               total_tasks += items;
+               nt = realloc(threads, (sizeof(*threads) +
+                                      sizeof(pid_t) * total_tasks));
+               if (nt == NULL)
+                       goto out_free_threads;
+
+               threads = nt;
+
+               if (threads) {
+                       for (i = 0; i < items; i++)
+                               threads->map[j++] = atoi(namelist[i]->d_name);
+                       threads->nr = total_tasks;
+               }
+
+               for (i = 0; i < items; i++)
+                       free(namelist[i]);
+               free(namelist);
+
+               if (!threads)
+                       break;
+       }
+
+out:
+       strlist__delete(slist);
+       return threads;
+
+out_free_threads:
+       free(threads);
+       threads = NULL;
+       goto out;
+}
+
+static struct thread_map *thread_map__new_by_tid_str(const char *tid_str)
+{
+       struct thread_map *threads = NULL, *nt;
+       int ntasks = 0;
+       pid_t tid, prev_tid = INT_MAX;
+       char *end_ptr;
+       struct str_node *pos;
+       struct strlist *slist;
+
+       /* perf-stat expects threads to be generated even if tid not given */
+       if (!tid_str) {
+               threads = malloc(sizeof(*threads) + sizeof(pid_t));
+               if (threads != NULL) {
+                       threads->map[1] = -1;
+                       threads->nr     = 1;
+               }
+               return threads;
+       }
+
+       slist = strlist__new(false, tid_str);
+       if (!slist)
+               return NULL;
+
+       strlist__for_each(pos, slist) {
+               tid = strtol(pos->s, &end_ptr, 10);
+
+               if (tid == INT_MIN || tid == INT_MAX ||
+                   (*end_ptr != '\0' && *end_ptr != ','))
+                       goto out_free_threads;
+
+               if (tid == prev_tid)
+                       continue;
+
+               ntasks++;
+               nt = realloc(threads, sizeof(*threads) + sizeof(pid_t) * ntasks);
+
+               if (nt == NULL)
+                       goto out_free_threads;
+
+               threads = nt;
+               threads->map[ntasks - 1] = tid;
+               threads->nr              = ntasks;
+       }
+out:
+       return threads;
+
+out_free_threads:
+       free(threads);
+       threads = NULL;
+       goto out;
+}
+
+struct thread_map *thread_map__new_str(const char *pid, const char *tid,
+                                      uid_t uid)
+{
+       if (pid)
+               return thread_map__new_by_pid_str(pid);
+
+       if (!tid && uid != UINT_MAX)
+               return thread_map__new_by_uid(uid);
+
+       return thread_map__new_by_tid_str(tid);
+}
+
 void thread_map__delete(struct thread_map *threads)
 {
        free(threads);
 
 struct thread_map *thread_map__new_by_tid(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);
+
+struct thread_map *thread_map__new_str(const char *pid,
+               const char *tid, uid_t uid);
+
 void thread_map__delete(struct thread_map *threads);
 
 size_t thread_map__fprintf(struct thread_map *threads, FILE *fp);
 
 
        ret += SNPRINTF(bf + ret, size - ret, "], ");
 
-       if (top->target_pid != -1)
-               ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %d",
+       if (top->target_pid)
+               ret += SNPRINTF(bf + ret, size - ret, " (target_pid: %s",
                                top->target_pid);
-       else if (top->target_tid != -1)
-               ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %d",
+       else if (top->target_tid)
+               ret += SNPRINTF(bf + ret, size - ret, " (target_tid: %s",
                                top->target_tid);
        else if (top->uid_str != NULL)
                ret += SNPRINTF(bf + ret, size - ret, " (uid: %s",
                ret += SNPRINTF(bf + ret, size - ret, ", CPU%s: %s)",
                                top->evlist->cpus->nr > 1 ? "s" : "", top->cpu_list);
        else {
-               if (top->target_tid != -1)
+               if (top->target_tid)
                        ret += SNPRINTF(bf + ret, size - ret, ")");
                else
                        ret += SNPRINTF(bf + ret, size - ret, ", %d CPU%s)",
 
        u64                guest_us_samples, guest_kernel_samples;
        int                print_entries, count_filter, delay_secs;
        int                freq;
-       pid_t              target_pid, target_tid;
+       const char         *target_pid, *target_tid;
        uid_t              uid;
        bool               hide_kernel_symbols, hide_user_symbols, zero;
        bool               system_wide;
 
        va_end(params);
 }
 
-uid_t parse_target_uid(const char *str, pid_t tid, pid_t pid)
+uid_t parse_target_uid(const char *str, const char *tid, const char *pid)
 {
        struct passwd pwd, *result;
        char buf[1024];
        if (str == NULL)
                return UINT_MAX;
 
-       /* CPU and PID are mutually exclusive */
-       if (tid > 0 || pid > 0) {
+       /* UID and PID are mutually exclusive */
+       if (tid || pid) {
                ui__warning("PID/TID switch overriding UID\n");
                sleep(1);
                return UINT_MAX;
 
 
 void event_attr_init(struct perf_event_attr *attr);
 
-uid_t parse_target_uid(const char *str, pid_t tid, pid_t pid);
+uid_t parse_target_uid(const char *str, const char *tid, const char *pid);
 
 #define _STR(x) #x
 #define STR(x) _STR(x)