linux/tools/perf/util/dsos.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2#include "debug.h"
   3#include "dsos.h"
   4#include "dso.h"
   5#include "vdso.h"
   6#include "namespaces.h"
   7#include <libgen.h>
   8#include <stdlib.h>
   9#include <string.h>
  10#include <symbol.h> // filename__read_build_id
  11
  12static int __dso_id__cmp(struct dso_id *a, struct dso_id *b)
  13{
  14        if (a->maj > b->maj) return -1;
  15        if (a->maj < b->maj) return 1;
  16
  17        if (a->min > b->min) return -1;
  18        if (a->min < b->min) return 1;
  19
  20        if (a->ino > b->ino) return -1;
  21        if (a->ino < b->ino) return 1;
  22
  23        if (a->ino_generation > b->ino_generation) return -1;
  24        if (a->ino_generation < b->ino_generation) return 1;
  25
  26        return 0;
  27}
  28
  29static bool dso_id__empty(struct dso_id *id)
  30{
  31        if (!id)
  32                return true;
  33
  34        return !id->maj && !id->min && !id->ino && !id->ino_generation;
  35}
  36
  37static void dso__inject_id(struct dso *dso, struct dso_id *id)
  38{
  39        dso->id.maj = id->maj;
  40        dso->id.min = id->min;
  41        dso->id.ino = id->ino;
  42        dso->id.ino_generation = id->ino_generation;
  43}
  44
  45static int dso_id__cmp(struct dso_id *a, struct dso_id *b)
  46{
  47        /*
  48         * The second is always dso->id, so zeroes if not set, assume passing
  49         * NULL for a means a zeroed id
  50         */
  51        if (dso_id__empty(a) || dso_id__empty(b))
  52                return 0;
  53
  54        return __dso_id__cmp(a, b);
  55}
  56
  57int dso__cmp_id(struct dso *a, struct dso *b)
  58{
  59        return __dso_id__cmp(&a->id, &b->id);
  60}
  61
  62bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
  63{
  64        bool have_build_id = false;
  65        struct dso *pos;
  66        struct nscookie nsc;
  67
  68        list_for_each_entry(pos, head, node) {
  69                if (with_hits && !pos->hit && !dso__is_vdso(pos))
  70                        continue;
  71                if (pos->has_build_id) {
  72                        have_build_id = true;
  73                        continue;
  74                }
  75                nsinfo__mountns_enter(pos->nsinfo, &nsc);
  76                if (filename__read_build_id(pos->long_name, pos->build_id,
  77                                            sizeof(pos->build_id)) > 0) {
  78                        have_build_id     = true;
  79                        pos->has_build_id = true;
  80                }
  81                nsinfo__mountns_exit(&nsc);
  82        }
  83
  84        return have_build_id;
  85}
  86
  87static int __dso__cmp_long_name(const char *long_name, struct dso_id *id, struct dso *b)
  88{
  89        int rc = strcmp(long_name, b->long_name);
  90        return rc ?: dso_id__cmp(id, &b->id);
  91}
  92
  93static int __dso__cmp_short_name(const char *short_name, struct dso_id *id, struct dso *b)
  94{
  95        int rc = strcmp(short_name, b->short_name);
  96        return rc ?: dso_id__cmp(id, &b->id);
  97}
  98
  99static int dso__cmp_short_name(struct dso *a, struct dso *b)
 100{
 101        return __dso__cmp_short_name(a->short_name, &a->id, b);
 102}
 103
 104/*
 105 * Find a matching entry and/or link current entry to RB tree.
 106 * Either one of the dso or name parameter must be non-NULL or the
 107 * function will not work.
 108 */
 109struct dso *__dsos__findnew_link_by_longname_id(struct rb_root *root, struct dso *dso,
 110                                                const char *name, struct dso_id *id)
 111{
 112        struct rb_node **p = &root->rb_node;
 113        struct rb_node  *parent = NULL;
 114
 115        if (!name)
 116                name = dso->long_name;
 117        /*
 118         * Find node with the matching name
 119         */
 120        while (*p) {
 121                struct dso *this = rb_entry(*p, struct dso, rb_node);
 122                int rc = __dso__cmp_long_name(name, id, this);
 123
 124                parent = *p;
 125                if (rc == 0) {
 126                        /*
 127                         * In case the new DSO is a duplicate of an existing
 128                         * one, print a one-time warning & put the new entry
 129                         * at the end of the list of duplicates.
 130                         */
 131                        if (!dso || (dso == this))
 132                                return this;    /* Find matching dso */
 133                        /*
 134                         * The core kernel DSOs may have duplicated long name.
 135                         * In this case, the short name should be different.
 136                         * Comparing the short names to differentiate the DSOs.
 137                         */
 138                        rc = dso__cmp_short_name(dso, this);
 139                        if (rc == 0) {
 140                                pr_err("Duplicated dso name: %s\n", name);
 141                                return NULL;
 142                        }
 143                }
 144                if (rc < 0)
 145                        p = &parent->rb_left;
 146                else
 147                        p = &parent->rb_right;
 148        }
 149        if (dso) {
 150                /* Add new node and rebalance tree */
 151                rb_link_node(&dso->rb_node, parent, p);
 152                rb_insert_color(&dso->rb_node, root);
 153                dso->root = root;
 154        }
 155        return NULL;
 156}
 157
 158void __dsos__add(struct dsos *dsos, struct dso *dso)
 159{
 160        list_add_tail(&dso->node, &dsos->head);
 161        __dsos__findnew_link_by_longname_id(&dsos->root, dso, NULL, &dso->id);
 162        /*
 163         * It is now in the linked list, grab a reference, then garbage collect
 164         * this when needing memory, by looking at LRU dso instances in the
 165         * list with atomic_read(&dso->refcnt) == 1, i.e. no references
 166         * anywhere besides the one for the list, do, under a lock for the
 167         * list: remove it from the list, then a dso__put(), that probably will
 168         * be the last and will then call dso__delete(), end of life.
 169         *
 170         * That, or at the end of the 'struct machine' lifetime, when all
 171         * 'struct dso' instances will be removed from the list, in
 172         * dsos__exit(), if they have no other reference from some other data
 173         * structure.
 174         *
 175         * E.g.: after processing a 'perf.data' file and storing references
 176         * to objects instantiated while processing events, we will have
 177         * references to the 'thread', 'map', 'dso' structs all from 'struct
 178         * hist_entry' instances, but we may not need anything not referenced,
 179         * so we might as well call machines__exit()/machines__delete() and
 180         * garbage collect it.
 181         */
 182        dso__get(dso);
 183}
 184
 185void dsos__add(struct dsos *dsos, struct dso *dso)
 186{
 187        down_write(&dsos->lock);
 188        __dsos__add(dsos, dso);
 189        up_write(&dsos->lock);
 190}
 191
 192static struct dso *__dsos__findnew_by_longname_id(struct rb_root *root, const char *name, struct dso_id *id)
 193{
 194        return __dsos__findnew_link_by_longname_id(root, NULL, name, id);
 195}
 196
 197static struct dso *__dsos__find_id(struct dsos *dsos, const char *name, struct dso_id *id, bool cmp_short)
 198{
 199        struct dso *pos;
 200
 201        if (cmp_short) {
 202                list_for_each_entry(pos, &dsos->head, node)
 203                        if (__dso__cmp_short_name(name, id, pos) == 0)
 204                                return pos;
 205                return NULL;
 206        }
 207        return __dsos__findnew_by_longname_id(&dsos->root, name, id);
 208}
 209
 210struct dso *__dsos__find(struct dsos *dsos, const char *name, bool cmp_short)
 211{
 212        return __dsos__find_id(dsos, name, NULL, cmp_short);
 213}
 214
 215static void dso__set_basename(struct dso *dso)
 216{
 217        char *base, *lname;
 218        int tid;
 219
 220        if (sscanf(dso->long_name, "/tmp/perf-%d.map", &tid) == 1) {
 221                if (asprintf(&base, "[JIT] tid %d", tid) < 0)
 222                        return;
 223        } else {
 224              /*
 225               * basename() may modify path buffer, so we must pass
 226               * a copy.
 227               */
 228                lname = strdup(dso->long_name);
 229                if (!lname)
 230                        return;
 231
 232                /*
 233                 * basename() may return a pointer to internal
 234                 * storage which is reused in subsequent calls
 235                 * so copy the result.
 236                 */
 237                base = strdup(basename(lname));
 238
 239                free(lname);
 240
 241                if (!base)
 242                        return;
 243        }
 244        dso__set_short_name(dso, base, true);
 245}
 246
 247static struct dso *__dsos__addnew_id(struct dsos *dsos, const char *name, struct dso_id *id)
 248{
 249        struct dso *dso = dso__new_id(name, id);
 250
 251        if (dso != NULL) {
 252                __dsos__add(dsos, dso);
 253                dso__set_basename(dso);
 254                /* Put dso here because __dsos_add already got it */
 255                dso__put(dso);
 256        }
 257        return dso;
 258}
 259
 260struct dso *__dsos__addnew(struct dsos *dsos, const char *name)
 261{
 262        return __dsos__addnew_id(dsos, name, NULL);
 263}
 264
 265static struct dso *__dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id)
 266{
 267        struct dso *dso = __dsos__find_id(dsos, name, id, false);
 268
 269        if (dso && dso_id__empty(&dso->id) && !dso_id__empty(id))
 270                dso__inject_id(dso, id);
 271
 272        return dso ? dso : __dsos__addnew_id(dsos, name, id);
 273}
 274
 275struct dso *dsos__findnew_id(struct dsos *dsos, const char *name, struct dso_id *id)
 276{
 277        struct dso *dso;
 278        down_write(&dsos->lock);
 279        dso = dso__get(__dsos__findnew_id(dsos, name, id));
 280        up_write(&dsos->lock);
 281        return dso;
 282}
 283
 284size_t __dsos__fprintf_buildid(struct list_head *head, FILE *fp,
 285                               bool (skip)(struct dso *dso, int parm), int parm)
 286{
 287        struct dso *pos;
 288        size_t ret = 0;
 289
 290        list_for_each_entry(pos, head, node) {
 291                if (skip && skip(pos, parm))
 292                        continue;
 293                ret += dso__fprintf_buildid(pos, fp);
 294                ret += fprintf(fp, " %s\n", pos->long_name);
 295        }
 296        return ret;
 297}
 298
 299size_t __dsos__fprintf(struct list_head *head, FILE *fp)
 300{
 301        struct dso *pos;
 302        size_t ret = 0;
 303
 304        list_for_each_entry(pos, head, node) {
 305                ret += dso__fprintf(pos, fp);
 306        }
 307
 308        return ret;
 309}
 310