linux/tools/perf/util/thread.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include "../perf.h"
   3#include <errno.h>
   4#include <stdlib.h>
   5#include <stdio.h>
   6#include <string.h>
   7#include <linux/kernel.h>
   8#include <linux/zalloc.h>
   9#include "session.h"
  10#include "thread.h"
  11#include "thread-stack.h"
  12#include "debug.h"
  13#include "namespaces.h"
  14#include "comm.h"
  15#include "map.h"
  16#include "symbol.h"
  17#include "unwind.h"
  18#include "callchain.h"
  19
  20#include <api/fs/fs.h>
  21
  22int thread__init_map_groups(struct thread *thread, struct machine *machine)
  23{
  24        pid_t pid = thread->pid_;
  25
  26        if (pid == thread->tid || pid == -1) {
  27                thread->mg = map_groups__new(machine);
  28        } else {
  29                struct thread *leader = __machine__findnew_thread(machine, pid, pid);
  30                if (leader) {
  31                        thread->mg = map_groups__get(leader->mg);
  32                        thread__put(leader);
  33                }
  34        }
  35
  36        return thread->mg ? 0 : -1;
  37}
  38
  39struct thread *thread__new(pid_t pid, pid_t tid)
  40{
  41        char *comm_str;
  42        struct comm *comm;
  43        struct thread *thread = zalloc(sizeof(*thread));
  44
  45        if (thread != NULL) {
  46                thread->pid_ = pid;
  47                thread->tid = tid;
  48                thread->ppid = -1;
  49                thread->cpu = -1;
  50                INIT_LIST_HEAD(&thread->namespaces_list);
  51                INIT_LIST_HEAD(&thread->comm_list);
  52                init_rwsem(&thread->namespaces_lock);
  53                init_rwsem(&thread->comm_lock);
  54
  55                comm_str = malloc(32);
  56                if (!comm_str)
  57                        goto err_thread;
  58
  59                snprintf(comm_str, 32, ":%d", tid);
  60                comm = comm__new(comm_str, 0, false);
  61                free(comm_str);
  62                if (!comm)
  63                        goto err_thread;
  64
  65                list_add(&comm->list, &thread->comm_list);
  66                refcount_set(&thread->refcnt, 1);
  67                RB_CLEAR_NODE(&thread->rb_node);
  68                /* Thread holds first ref to nsdata. */
  69                thread->nsinfo = nsinfo__new(pid);
  70                srccode_state_init(&thread->srccode_state);
  71        }
  72
  73        return thread;
  74
  75err_thread:
  76        free(thread);
  77        return NULL;
  78}
  79
  80void thread__delete(struct thread *thread)
  81{
  82        struct namespaces *namespaces, *tmp_namespaces;
  83        struct comm *comm, *tmp_comm;
  84
  85        BUG_ON(!RB_EMPTY_NODE(&thread->rb_node));
  86
  87        thread_stack__free(thread);
  88
  89        if (thread->mg) {
  90                map_groups__put(thread->mg);
  91                thread->mg = NULL;
  92        }
  93        down_write(&thread->namespaces_lock);
  94        list_for_each_entry_safe(namespaces, tmp_namespaces,
  95                                 &thread->namespaces_list, list) {
  96                list_del_init(&namespaces->list);
  97                namespaces__free(namespaces);
  98        }
  99        up_write(&thread->namespaces_lock);
 100
 101        down_write(&thread->comm_lock);
 102        list_for_each_entry_safe(comm, tmp_comm, &thread->comm_list, list) {
 103                list_del_init(&comm->list);
 104                comm__free(comm);
 105        }
 106        up_write(&thread->comm_lock);
 107
 108        unwind__finish_access(thread);
 109        nsinfo__zput(thread->nsinfo);
 110        srccode_state_free(&thread->srccode_state);
 111
 112        exit_rwsem(&thread->namespaces_lock);
 113        exit_rwsem(&thread->comm_lock);
 114        free(thread);
 115}
 116
 117struct thread *thread__get(struct thread *thread)
 118{
 119        if (thread)
 120                refcount_inc(&thread->refcnt);
 121        return thread;
 122}
 123
 124void thread__put(struct thread *thread)
 125{
 126        if (thread && refcount_dec_and_test(&thread->refcnt)) {
 127                /*
 128                 * Remove it from the dead threads list, as last reference is
 129                 * gone, if it is in a dead threads list.
 130                 *
 131                 * We may not be there anymore if say, the machine where it was
 132                 * stored was already deleted, so we already removed it from
 133                 * the dead threads and some other piece of code still keeps a
 134                 * reference.
 135                 *
 136                 * This is what 'perf sched' does and finally drops it in
 137                 * perf_sched__lat(), where it calls perf_sched__read_events(),
 138                 * that processes the events by creating a session and deleting
 139                 * it, which ends up destroying the list heads for the dead
 140                 * threads, but before it does that it removes all threads from
 141                 * it using list_del_init().
 142                 *
 143                 * So we need to check here if it is in a dead threads list and
 144                 * if so, remove it before finally deleting the thread, to avoid
 145                 * an use after free situation.
 146                 */
 147                if (!list_empty(&thread->node))
 148                        list_del_init(&thread->node);
 149                thread__delete(thread);
 150        }
 151}
 152
 153static struct namespaces *__thread__namespaces(const struct thread *thread)
 154{
 155        if (list_empty(&thread->namespaces_list))
 156                return NULL;
 157
 158        return list_first_entry(&thread->namespaces_list, struct namespaces, list);
 159}
 160
 161struct namespaces *thread__namespaces(struct thread *thread)
 162{
 163        struct namespaces *ns;
 164
 165        down_read(&thread->namespaces_lock);
 166        ns = __thread__namespaces(thread);
 167        up_read(&thread->namespaces_lock);
 168
 169        return ns;
 170}
 171
 172static int __thread__set_namespaces(struct thread *thread, u64 timestamp,
 173                                    struct namespaces_event *event)
 174{
 175        struct namespaces *new, *curr = __thread__namespaces(thread);
 176
 177        new = namespaces__new(event);
 178        if (!new)
 179                return -ENOMEM;
 180
 181        list_add(&new->list, &thread->namespaces_list);
 182
 183        if (timestamp && curr) {
 184                /*
 185                 * setns syscall must have changed few or all the namespaces
 186                 * of this thread. Update end time for the namespaces
 187                 * previously used.
 188                 */
 189                curr = list_next_entry(new, list);
 190                curr->end_time = timestamp;
 191        }
 192
 193        return 0;
 194}
 195
 196int thread__set_namespaces(struct thread *thread, u64 timestamp,
 197                           struct namespaces_event *event)
 198{
 199        int ret;
 200
 201        down_write(&thread->namespaces_lock);
 202        ret = __thread__set_namespaces(thread, timestamp, event);
 203        up_write(&thread->namespaces_lock);
 204        return ret;
 205}
 206
 207struct comm *thread__comm(const struct thread *thread)
 208{
 209        if (list_empty(&thread->comm_list))
 210                return NULL;
 211
 212        return list_first_entry(&thread->comm_list, struct comm, list);
 213}
 214
 215struct comm *thread__exec_comm(const struct thread *thread)
 216{
 217        struct comm *comm, *last = NULL, *second_last = NULL;
 218
 219        list_for_each_entry(comm, &thread->comm_list, list) {
 220                if (comm->exec)
 221                        return comm;
 222                second_last = last;
 223                last = comm;
 224        }
 225
 226        /*
 227         * 'last' with no start time might be the parent's comm of a synthesized
 228         * thread (created by processing a synthesized fork event). For a main
 229         * thread, that is very probably wrong. Prefer a later comm to avoid
 230         * that case.
 231         */
 232        if (second_last && !last->start && thread->pid_ == thread->tid)
 233                return second_last;
 234
 235        return last;
 236}
 237
 238static int ____thread__set_comm(struct thread *thread, const char *str,
 239                                u64 timestamp, bool exec)
 240{
 241        struct comm *new, *curr = thread__comm(thread);
 242
 243        /* Override the default :tid entry */
 244        if (!thread->comm_set) {
 245                int err = comm__override(curr, str, timestamp, exec);
 246                if (err)
 247                        return err;
 248        } else {
 249                new = comm__new(str, timestamp, exec);
 250                if (!new)
 251                        return -ENOMEM;
 252                list_add(&new->list, &thread->comm_list);
 253
 254                if (exec)
 255                        unwind__flush_access(thread);
 256        }
 257
 258        thread->comm_set = true;
 259
 260        return 0;
 261}
 262
 263int __thread__set_comm(struct thread *thread, const char *str, u64 timestamp,
 264                       bool exec)
 265{
 266        int ret;
 267
 268        down_write(&thread->comm_lock);
 269        ret = ____thread__set_comm(thread, str, timestamp, exec);
 270        up_write(&thread->comm_lock);
 271        return ret;
 272}
 273
 274int thread__set_comm_from_proc(struct thread *thread)
 275{
 276        char path[64];
 277        char *comm = NULL;
 278        size_t sz;
 279        int err = -1;
 280
 281        if (!(snprintf(path, sizeof(path), "%d/task/%d/comm",
 282                       thread->pid_, thread->tid) >= (int)sizeof(path)) &&
 283            procfs__read_str(path, &comm, &sz) == 0) {
 284                comm[sz - 1] = '\0';
 285                err = thread__set_comm(thread, comm, 0);
 286        }
 287
 288        return err;
 289}
 290
 291static const char *__thread__comm_str(const struct thread *thread)
 292{
 293        const struct comm *comm = thread__comm(thread);
 294
 295        if (!comm)
 296                return NULL;
 297
 298        return comm__str(comm);
 299}
 300
 301const char *thread__comm_str(struct thread *thread)
 302{
 303        const char *str;
 304
 305        down_read(&thread->comm_lock);
 306        str = __thread__comm_str(thread);
 307        up_read(&thread->comm_lock);
 308
 309        return str;
 310}
 311
 312/* CHECKME: it should probably better return the max comm len from its comm list */
 313int thread__comm_len(struct thread *thread)
 314{
 315        if (!thread->comm_len) {
 316                const char *comm = thread__comm_str(thread);
 317                if (!comm)
 318                        return 0;
 319                thread->comm_len = strlen(comm);
 320        }
 321
 322        return thread->comm_len;
 323}
 324
 325size_t thread__fprintf(struct thread *thread, FILE *fp)
 326{
 327        return fprintf(fp, "Thread %d %s\n", thread->tid, thread__comm_str(thread)) +
 328               map_groups__fprintf(thread->mg, fp);
 329}
 330
 331int thread__insert_map(struct thread *thread, struct map *map)
 332{
 333        int ret;
 334
 335        ret = unwind__prepare_access(thread, map, NULL);
 336        if (ret)
 337                return ret;
 338
 339        map_groups__fixup_overlappings(thread->mg, map, stderr);
 340        map_groups__insert(thread->mg, map);
 341
 342        return 0;
 343}
 344
 345static int __thread__prepare_access(struct thread *thread)
 346{
 347        bool initialized = false;
 348        int err = 0;
 349        struct maps *maps = &thread->mg->maps;
 350        struct map *map;
 351
 352        down_read(&maps->lock);
 353
 354        for (map = maps__first(maps); map; map = map__next(map)) {
 355                err = unwind__prepare_access(thread, map, &initialized);
 356                if (err || initialized)
 357                        break;
 358        }
 359
 360        up_read(&maps->lock);
 361
 362        return err;
 363}
 364
 365static int thread__prepare_access(struct thread *thread)
 366{
 367        int err = 0;
 368
 369        if (dwarf_callchain_users)
 370                err = __thread__prepare_access(thread);
 371
 372        return err;
 373}
 374
 375static int thread__clone_map_groups(struct thread *thread,
 376                                    struct thread *parent,
 377                                    bool do_maps_clone)
 378{
 379        /* This is new thread, we share map groups for process. */
 380        if (thread->pid_ == parent->pid_)
 381                return thread__prepare_access(thread);
 382
 383        if (thread->mg == parent->mg) {
 384                pr_debug("broken map groups on thread %d/%d parent %d/%d\n",
 385                         thread->pid_, thread->tid, parent->pid_, parent->tid);
 386                return 0;
 387        }
 388        /* But this one is new process, copy maps. */
 389        return do_maps_clone ? map_groups__clone(thread, parent->mg) : 0;
 390}
 391
 392int thread__fork(struct thread *thread, struct thread *parent, u64 timestamp, bool do_maps_clone)
 393{
 394        if (parent->comm_set) {
 395                const char *comm = thread__comm_str(parent);
 396                int err;
 397                if (!comm)
 398                        return -ENOMEM;
 399                err = thread__set_comm(thread, comm, timestamp);
 400                if (err)
 401                        return err;
 402        }
 403
 404        thread->ppid = parent->tid;
 405        return thread__clone_map_groups(thread, parent, do_maps_clone);
 406}
 407
 408void thread__find_cpumode_addr_location(struct thread *thread, u64 addr,
 409                                        struct addr_location *al)
 410{
 411        size_t i;
 412        const u8 cpumodes[] = {
 413                PERF_RECORD_MISC_USER,
 414                PERF_RECORD_MISC_KERNEL,
 415                PERF_RECORD_MISC_GUEST_USER,
 416                PERF_RECORD_MISC_GUEST_KERNEL
 417        };
 418
 419        for (i = 0; i < ARRAY_SIZE(cpumodes); i++) {
 420                thread__find_symbol(thread, cpumodes[i], addr, al);
 421                if (al->map)
 422                        break;
 423        }
 424}
 425
 426struct thread *thread__main_thread(struct machine *machine, struct thread *thread)
 427{
 428        if (thread->pid_ == thread->tid)
 429                return thread__get(thread);
 430
 431        if (thread->pid_ == -1)
 432                return NULL;
 433
 434        return machine__find_thread(machine, thread->pid_, thread->pid_);
 435}
 436
 437int thread__memcpy(struct thread *thread, struct machine *machine,
 438                   void *buf, u64 ip, int len, bool *is64bit)
 439{
 440       u8 cpumode = PERF_RECORD_MISC_USER;
 441       struct addr_location al;
 442       long offset;
 443
 444       if (machine__kernel_ip(machine, ip))
 445               cpumode = PERF_RECORD_MISC_KERNEL;
 446
 447       if (!thread__find_map(thread, cpumode, ip, &al) || !al.map->dso ||
 448           al.map->dso->data.status == DSO_DATA_STATUS_ERROR ||
 449           map__load(al.map) < 0)
 450               return -1;
 451
 452       offset = al.map->map_ip(al.map, ip);
 453       if (is64bit)
 454               *is64bit = al.map->dso->is_64_bit;
 455
 456       return dso__data_read_offset(al.map->dso, machine, offset, buf, len);
 457}
 458