[DTrace-devel] [PATCH v4 11/25] libproc: add Pinode_to_file_map()

Nick Alcock nick.alcock at oracle.com
Fri Oct 7 10:25:10 UTC 2022


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>
---
 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




More information about the DTrace-devel mailing list