[DTrace-devel] [PATCH v4 11/25] libproc: add Pinode_to_file_map()
Kris Van Hees
kris.van.hees at oracle.com
Sat Oct 8 03:20:19 UTC 2022
On Fri, Oct 07, 2022 at 11:25:10AM +0100, Nick Alcock via DTrace-devel wrote:
> This lets you rapidly look up the prmap_file_t that corresponds to a
> (dev, ino) pair in a given process's mappings. This is useful when
> dealing with uprobes, which work on an *inode* basis, not a process
> basis or even a mapping basis: we can use this to see whether a process
> we are otherwise interested in has ownership of a given (dev, ino)
> mapping rapidly, and what the details of the mapping are.
>
> Do it with a hash table rather than linear iteration because these days
> it is getting increasingly common to have processes with a great many
> mappings. Chromium has 1638 mappings on my system, of which 411 are
> file-backed, but the real mapping king these days is Emacs with native
> compilation, which maps every Lisp file as a separate shared object:
> 3618 mappings, of which 3135 are file-backed!
>
> While we're at it, rename the (Psymtab.c-private) Pprmap_file_by_name
> to Pfilename_to_file_map, so that all prmap_file_t functions have
> consistent names (and names less terrible than Pprmap_file_by_name).
>
> Signed-off-by: Nick Alcock <nick.alcock at oracle.com>
Reviewed-by: Kris Van Hees <kris.van.hees at oracle.com>
> ---
> include/sys/sol_procfs.h | 8 +--
> libproc/Pcontrol.h | 1 +
> libproc/Psymtab.c | 125 ++++++++++++++++++++++++++++++---------
> libproc/libproc.h | 5 +-
> 4 files changed, 106 insertions(+), 33 deletions(-)
>
> diff --git a/include/sys/sol_procfs.h b/include/sys/sol_procfs.h
> index 2f7c6a4eafe5..d195a108da2b 100644
> --- a/include/sys/sol_procfs.h
> +++ b/include/sys/sol_procfs.h
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2006, 2016, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2006, 2022, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -17,17 +17,17 @@ extern "C" {
> #include <sys/types.h>
> #include <sys/dtrace_types.h>
> #include <sys/procfs_isa.h>
> -#include <dt_list.h>
> #include <link.h>
>
> /*
> * The prmap_file points to all mappings corresponding to a single file, sorted
> * by address. prmap_files are hashed by name (including the terminating \0,
> - * so anonymous maps are all hashed together).
> + * so anonymous maps are all hashed together) and by dev/inode number.
> */
> struct prmap;
> typedef struct prmap_file {
> - struct prmap_file *prf_next; /* next in hash chain */
> + struct prmap_file *prf_name_next; /* next in filename hash chain */
> + struct prmap_file *prf_inum_next; /* next in inode hash chain */
> char *prf_mapname; /* name in /proc/<pid>/maps */
> struct prmap **prf_mappings; /* sorted by address */
> size_t prf_num_mappings; /* number of mappings */
> diff --git a/libproc/Pcontrol.h b/libproc/Pcontrol.h
> index 9c3340d04d54..77d71d98abf0 100644
> --- a/libproc/Pcontrol.h
> +++ b/libproc/Pcontrol.h
> @@ -247,6 +247,7 @@ struct ps_prochandle {
> map_info_t *mappings; /* process mappings, sorted by address */
> size_t num_mappings; /* number of mappings */
> prmap_file_t **map_files; /* hash of mappings by filename */
> + prmap_file_t **map_inum; /* hash of mappings by (dev, inode) */
> uint_t num_files; /* number of file elements in file_list */
> dt_list_t file_list; /* list of mapped files w/ symbol table info */
> auxv_t *auxv; /* the process's aux vector */
> diff --git a/libproc/Psymtab.c b/libproc/Psymtab.c
> index aca3382bdde5..3e2f08928eca 100644
> --- a/libproc/Psymtab.c
> +++ b/libproc/Psymtab.c
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -36,6 +36,8 @@
> static map_info_t *object_to_map(struct ps_prochandle *, Lmid_t, const char *);
> static map_info_t *object_name_to_map(struct ps_prochandle *,
> Lmid_t, const char *);
> +static prmap_file_t *Pfilename_to_file_map(struct ps_prochandle *P,
> + const char *name);
> static GElf_Sym *sym_by_name(sym_tbl_t *, const char *, GElf_Sym *, uint_t *);
> static file_info_t *file_info_new(struct ps_prochandle *, map_info_t *);
> static int byaddr_cmp_common(GElf_Sym *a, char *aname, GElf_Sym *b, char *bname);
> @@ -70,6 +72,39 @@ string_hash(const char *key)
> return h;
> }
>
> +/*
> + * This is Pelle Evensen's NASAM mixer. Since we are only mixing in one value
> + * we don't need anything but a mixing step.
> + */
> +
> +#define ror(x,y) ((x) >> (y) | (x) << (64 - (y)))
> +
> +static uint64_t
> +nasam_mix(uint64_t h)
> +{
> +
> + h ^= ror(h, 25) ^ ror(h, 47);
> + h *= 0x9E6C63D0676A9A99UL;
> + h ^= h >> 23 ^ h >> 51;
> + h *= 0x9E6D62D06F6A9A9BUL;
> + h ^= h >> 23 ^ h >> 51;
> +
> + return h;
> +}
> +#undef ror
> +
> +static ino_t
> +dev_inum_hash(dev_t dev, ino_t inum)
> +{
> + uint64_t h;
> +
> + h = nasam_mix(inum);
> + h += dev;
> + h = nasam_mix(h);
> +
> + return (ino_t) h;
> +}
> +
> /*
> * Allocation function for a new file_info_t.
> */
> @@ -173,7 +208,7 @@ mapping_purge(struct ps_prochandle *P)
> prmap_file_t *old_prf = NULL;
>
> for (prf = P->map_files[i]; prf != NULL;
> - old_prf = prf, prf = prf->prf_next) {
> + old_prf = prf, prf = prf->prf_name_next) {
> free(old_prf);
> free(prf->prf_mappings);
> free(prf->prf_mapname);
> @@ -182,6 +217,7 @@ mapping_purge(struct ps_prochandle *P)
> }
>
> memset(P->map_files, 0, sizeof(struct prmap_file *) * MAP_HASH_BUCKETS);
> + memset(P->map_inum, 0, sizeof(struct prmap_file *) * MAP_HASH_BUCKETS);
>
> for (i = 0, fptr = dt_list_next(&P->file_list);
> i < P->num_files; i++, fptr = dt_list_next(fptr)) {
> @@ -208,6 +244,11 @@ Psym_init(struct ps_prochandle *P)
> _dprintf("Out of memory initializing map_files hash\n");
> return -ENOMEM;
> }
> + P->map_inum = calloc(MAP_HASH_BUCKETS, sizeof(struct prmap_file_t *));
> + if (!P->map_inum) {
> + _dprintf("Out of memory initializing map_inum hash\n");
> + return -ENOMEM;
> + }
> return 0;
> }
>
> @@ -219,23 +260,8 @@ Psym_free(struct ps_prochandle *P)
> {
> free(P->map_files);
> P->map_files = NULL;
> -}
> -
> -/*
> - * Given a process handle, find a corresponding prmap_file by file name, or NULL
> - * if none.
> - */
> -static prmap_file_t *Pprmap_file_by_name(struct ps_prochandle *P,
> - const char *name)
> -{
> - uint_t h = string_hash(name) % MAP_HASH_BUCKETS;
> - prmap_file_t *prf;
> -
> - for (prf = P->map_files[h]; prf != NULL; prf = prf->prf_next)
> - if (strcmp(prf->prf_mapname, name) == 0)
> - return prf;
> -
> - return NULL;
> + free(P->map_inum);
> + P->map_inum = NULL;
> }
>
> /*
> @@ -552,9 +578,13 @@ Pupdate_maps(struct ps_prochandle *P)
> goto err;
> pmptr = mptr->map_pmap;
> memset(pmptr, 0, sizeof(struct prmap));
> + pmptr->pr_dev = makedev(major, minor);
> + pmptr->pr_inum = inode;
>
> - if ((prf = Pprmap_file_by_name(P, fn)) == NULL) {
> - uint_t h = string_hash(fn) % MAP_HASH_BUCKETS;
> + if ((prf = Pfilename_to_file_map(P, fn)) == NULL) {
> + uint_t fn_h = string_hash(fn) % MAP_HASH_BUCKETS;
> + uint_t inum_h = dev_inum_hash(pmptr->pr_dev, pmptr->pr_inum) %
> + MAP_HASH_BUCKETS;
>
> if ((prf = malloc(sizeof(struct prmap_file))) == NULL) {
> free(mptr->map_pmap);
> @@ -563,10 +593,11 @@ Pupdate_maps(struct ps_prochandle *P)
>
> memset(prf, 0, sizeof(struct prmap_file));
> prf->prf_mapname = fn;
> - prf->prf_next = P->map_files[h];
> - P->map_files[h] = prf;
> - }
> - else {
> + prf->prf_name_next = P->map_files[fn_h];
> + P->map_files[fn_h] = prf;
> + prf->prf_inum_next = P->map_inum[inum_h];
> + P->map_inum[inum_h] = prf;
> + } else {
> free(fn);
> fn = NULL;
> }
> @@ -644,8 +675,6 @@ Pupdate_maps(struct ps_prochandle *P)
> (strcmp(prf->prf_mapname, exefile) == 0))
> P->map_exec = P->num_mappings;
> }
> - pmptr->pr_dev = makedev(major, minor);
> - pmptr->pr_inum = inode;
> pmptr->pr_file = prf;
>
> /*
> @@ -849,12 +878,52 @@ Plmid_to_map(struct ps_prochandle *P, Lmid_t lmid, const char *name)
> return NULL;
> }
>
> +/*
> + * Given a process handle, find a corresponding mapping by hunting across all lmids.
> + */
> const prmap_t *
> Pname_to_map(struct ps_prochandle *P, const char *name)
> {
> return Plmid_to_map(P, PR_LMID_EVERY, name);
> }
>
> +/*
> + * Given a process handle, find a corresponding prmap_file by file name, or NULL
> + * if none.
> + */
> +static prmap_file_t *Pfilename_to_file_map(struct ps_prochandle *P,
> + const char *name)
> +{
> + uint_t h = string_hash(name) % MAP_HASH_BUCKETS;
> + prmap_file_t *prf;
> +
> + for (prf = P->map_files[h]; prf != NULL; prf = prf->prf_name_next)
> + if (strcmp(prf->prf_mapname, name) == 0)
> + return prf;
> +
> + return NULL;
> +}
> +
> +
> +/*
> + * Given a process handle, find a corresponding prmap_file by (dev_t, inode
> + * number), or NULL if none.
> + */
> +const prmap_file_t *
> +Pinode_to_file_map(struct ps_prochandle *P, dev_t dev, ino_t inum)
> +{
> + uint_t h = dev_inum_hash(dev, inum) % MAP_HASH_BUCKETS;
> +
> + prmap_file_t *prf;
> +
> + for (prf = P->map_inum[h]; prf != NULL; prf = prf->prf_inum_next)
> + if (prf->first_segment->pr_inum == inum &&
> + prf->first_segment->pr_dev == dev)
> + return prf;
> +
> + return NULL;
> +}
> +
> /*
> * We wouldn't need these if qsort(3C) took an argument for the callback...
> */
> @@ -1384,7 +1453,7 @@ Pbuild_file_symtab(struct ps_prochandle *P, file_info_t *fptr)
> size_t nphdrs;
> GElf_Phdr hdr;
> GElf_Phdr *phdr = &hdr;
> - prmap_file_t *prf = Pprmap_file_by_name(P, fptr->file_pname);
> + prmap_file_t *prf = Pfilename_to_file_map(P, fptr->file_pname);
>
> if (!prf) {
> _dprintf("%s: mapping not known (internal error)\n.",
> diff --git a/libproc/libproc.h b/libproc/libproc.h
> index 1f8683e25b31..30d4bd8add5e 100644
> --- a/libproc/libproc.h
> +++ b/libproc/libproc.h
> @@ -1,6 +1,6 @@
> /*
> * Oracle Linux DTrace.
> - * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved.
> + * Copyright (c) 2009, 2022, Oracle and/or its affiliates. All rights reserved.
> * Licensed under the Universal Permissive License v 1.0 as shown at
> * http://oss.oracle.com/licenses/upl.
> */
> @@ -32,6 +32,7 @@
> #include <sys/socket.h>
> #include <sys/utsname.h>
> #include <sys/time.h>
> +#include <sys/types.h>
> #include <sys/user.h>
>
> #include <sys/compiler.h>
> @@ -242,6 +243,8 @@ extern const prmap_t *Paddr_to_map(struct ps_prochandle *, uintptr_t);
> extern const prmap_t *Pname_to_map(struct ps_prochandle *, const char *);
> extern const prmap_t *Plmid_to_map(struct ps_prochandle *,
> Lmid_t, const char *);
> +extern const prmap_file_t *Pinode_to_file_map(struct ps_prochandle *,
> + dev_t, ino_t);
>
> extern char *Pobjname(struct ps_prochandle *, uintptr_t, char *, size_t);
> extern int Plmid(struct ps_prochandle *, uintptr_t, Lmid_t *);
> --
> 2.35.1
>
>
> _______________________________________________
> DTrace-devel mailing list
> DTrace-devel at oss.oracle.com
> https://oss.oracle.com/mailman/listinfo/dtrace-devel
More information about the DTrace-devel
mailing list