|
|
8b506c |
commit 775bd386c2b9a6e553c61a5c0f86d29d9c2320a8
|
|
|
8b506c |
Author: Mark Goodwin <mgoodwin@redhat.com>
|
|
|
8b506c |
Date: Thu Jul 25 13:30:05 2019 +1000
|
|
|
8b506c |
|
|
|
8b506c |
pmdaproc: fix memory leak in pidlist refresh
|
|
|
8b506c |
|
|
|
8b506c |
RHBZ #1721107
|
|
|
8b506c |
|
|
|
8b506c |
The pidlist refresh function was leaking instance name buffers for
|
|
|
8b506c |
processes that had exited between refreshes. Fix this by storing the
|
|
|
8b506c |
instance name (truncated psargs) in a new field of the hash table
|
|
|
8b506c |
entry, and harvest them correctly if they've exited after each refresh.
|
|
|
8b506c |
The indom table instance names now share a pointer to the new field
|
|
|
8b506c |
in the hash table entry so they dont need to be separately free'd.
|
|
|
8b506c |
This similifies the code and memory management.
|
|
|
8b506c |
|
|
|
8b506c |
Also put a 4K cap on the maximum length of a psargs string for the
|
|
|
8b506c |
unlikely scenario of a psargs string that is not NULL terminated,
|
|
|
8b506c |
i.e. use strndup() rather than strdup(). This may be possible if
|
|
|
8b506c |
a rogue process modified it's argv[0] at run-time.
|
|
|
8b506c |
|
|
|
8b506c |
For testing, the recipe in RHBZ#1721107 was repro'd using valgind
|
|
|
8b506c |
and a workload with lots of process churn (i.e. a couple of large
|
|
|
8b506c |
builds running). Before the fix, valgrind reported numerous similar
|
|
|
8b506c |
leaks as reported in the bug. With the fix applied, those leaks
|
|
|
8b506c |
are gone. The refresh is more efficient now too because we only
|
|
|
8b506c |
have one copy of the psargs strings (in the hash table entry).
|
|
|
8b506c |
|
|
|
8b506c |
Also thoroughly exercised all tests in the QA pmda.proc and
|
|
|
8b506c |
pmda.hotproc groups. A new test might be possible, but the nature
|
|
|
8b506c |
of the required workload would make it difficult (and expensive)
|
|
|
8b506c |
to run.
|
|
|
8b506c |
|
|
|
8b506c |
diff --git a/src/pmdas/linux_proc/proc_pid.c b/src/pmdas/linux_proc/proc_pid.c
|
|
|
8b506c |
index 2e8f705b9..dcbc28850 100644
|
|
|
8b506c |
--- a/src/pmdas/linux_proc/proc_pid.c
|
|
|
8b506c |
+++ b/src/pmdas/linux_proc/proc_pid.c
|
|
|
8b506c |
@@ -820,7 +820,7 @@ disable_hotproc(void)
|
|
|
8b506c |
static void
|
|
|
8b506c |
refresh_proc_pidlist(proc_pid_t *proc_pid, proc_pid_list_t *pids)
|
|
|
8b506c |
{
|
|
|
8b506c |
- int i;
|
|
|
8b506c |
+ int i, numinst, idx;
|
|
|
8b506c |
int fd;
|
|
|
8b506c |
char *p;
|
|
|
8b506c |
char buf[MAXPATHLEN];
|
|
|
8b506c |
@@ -828,14 +828,6 @@ refresh_proc_pidlist(proc_pid_t *proc_pid, proc_pid_list_t *pids)
|
|
|
8b506c |
proc_pid_entry_t *ep;
|
|
|
8b506c |
pmdaIndom *indomp = proc_pid->indom;
|
|
|
8b506c |
|
|
|
8b506c |
- if (indomp->it_numinst < pids->count) {
|
|
|
8b506c |
- indomp->it_set = (pmdaInstid *)realloc(indomp->it_set,
|
|
|
8b506c |
- pids->count * sizeof(pmdaInstid));
|
|
|
8b506c |
- memset(&indomp->it_set[indomp->it_numinst], 0,
|
|
|
8b506c |
- (pids->count - indomp->it_numinst) * sizeof(pmdaInstid));
|
|
|
8b506c |
- }
|
|
|
8b506c |
- indomp->it_numinst = pids->count;
|
|
|
8b506c |
-
|
|
|
8b506c |
/*
|
|
|
8b506c |
* invalidate all entries so we can harvest pids that have exited
|
|
|
8b506c |
*/
|
|
|
8b506c |
@@ -852,7 +844,9 @@ refresh_proc_pidlist(proc_pid_t *proc_pid, proc_pid_list_t *pids)
|
|
|
8b506c |
*/
|
|
|
8b506c |
for (i=0; i < pids->count; i++) {
|
|
|
8b506c |
node = __pmHashSearch(pids->pids[i], &proc_pid->pidhash);
|
|
|
8b506c |
- if (node == NULL) {
|
|
|
8b506c |
+ if (node)
|
|
|
8b506c |
+ ep = (proc_pid_entry_t *)node->data;
|
|
|
8b506c |
+ else {
|
|
|
8b506c |
int k = 0;
|
|
|
8b506c |
|
|
|
8b506c |
ep = (proc_pid_entry_t *)malloc(sizeof(proc_pid_entry_t));
|
|
|
8b506c |
@@ -940,57 +934,52 @@ refresh_proc_pidlist(proc_pid_t *proc_pid, proc_pid_list_t *pids)
|
|
|
8b506c |
__pmHashAdd(pids->pids[i], (void *)ep, &proc_pid->pidhash);
|
|
|
8b506c |
//fprintf(stderr, "key %d : ADDED \"%s\" to hash table\n", pids->pids[i], buf);
|
|
|
8b506c |
}
|
|
|
8b506c |
- else
|
|
|
8b506c |
- ep = (proc_pid_entry_t *)node->data;
|
|
|
8b506c |
-
|
|
|
8b506c |
- /* mark pid as still existing */
|
|
|
8b506c |
- ep->flags |= PROC_PID_FLAG_VALID;
|
|
|
8b506c |
|
|
|
8b506c |
- /* refresh the indom pointer */
|
|
|
8b506c |
- indomp->it_set[i].i_inst = ep->id;
|
|
|
8b506c |
- if (indomp->it_set[i].i_name) {
|
|
|
8b506c |
- int len = strlen(indomp->it_set[i].i_name);
|
|
|
8b506c |
- if (strncmp(indomp->it_set[i].i_name, ep->name, len) != 0) {
|
|
|
8b506c |
- free(indomp->it_set[i].i_name);
|
|
|
8b506c |
- indomp->it_set[i].i_name = NULL;
|
|
|
8b506c |
- }
|
|
|
8b506c |
- else if (pmDebugOptions.libpmda) {
|
|
|
8b506c |
- fprintf(stderr, "refresh_proc_pidlist: Instance id=%d \"%s\" no change\n",
|
|
|
8b506c |
- ep->id, indomp->it_set[i].i_name);
|
|
|
8b506c |
- }
|
|
|
8b506c |
- }
|
|
|
8b506c |
- if (indomp->it_set[i].i_name == NULL) {
|
|
|
8b506c |
- /*
|
|
|
8b506c |
- * The external instance name is the pid followed by
|
|
|
8b506c |
- * a copy of the psargs truncated at the first space.
|
|
|
8b506c |
- * e.g. "012345 /path/to/command". Command line args,
|
|
|
8b506c |
- * if any, are truncated. The full command line is
|
|
|
8b506c |
- * available in the proc.psinfo.psargs metric.
|
|
|
8b506c |
- */
|
|
|
8b506c |
- if ((p = strchr(ep->name, ' ')) != NULL) {
|
|
|
8b506c |
- if ((p = strchr(p+1, ' ')) != NULL) {
|
|
|
8b506c |
- int len = p - ep->name;
|
|
|
8b506c |
- indomp->it_set[i].i_name = (char *)malloc(len+1);
|
|
|
8b506c |
- strncpy(indomp->it_set[i].i_name, ep->name, len);
|
|
|
8b506c |
- indomp->it_set[i].i_name[len] = '\0';
|
|
|
8b506c |
- }
|
|
|
8b506c |
- }
|
|
|
8b506c |
- if (indomp->it_set[i].i_name == NULL)
|
|
|
8b506c |
- indomp->it_set[i].i_name = strdup(ep->name);
|
|
|
8b506c |
+ if (ep->instname == NULL) {
|
|
|
8b506c |
+ /*
|
|
|
8b506c |
+ * The external instance name is the pid followed by
|
|
|
8b506c |
+ * a copy of the psargs truncated at the first space.
|
|
|
8b506c |
+ * e.g. "012345 /path/to/command". Command line args,
|
|
|
8b506c |
+ * if any, are truncated. The full command line is
|
|
|
8b506c |
+ * available in the proc.psinfo.psargs metric.
|
|
|
8b506c |
+ */
|
|
|
8b506c |
+ if ((p = strchr(ep->name, ' ')) != NULL) {
|
|
|
8b506c |
+ if ((p = strchr(p+1, ' ')) != NULL) {
|
|
|
8b506c |
+ int len = p - ep->name;
|
|
|
8b506c |
+ if (len > PROC_PID_STAT_CMD_MAXLEN)
|
|
|
8b506c |
+ len = PROC_PID_STAT_CMD_MAXLEN;
|
|
|
8b506c |
+ ep->instname = (char *)malloc(len+1);
|
|
|
8b506c |
+ strncpy(ep->instname, ep->name, len);
|
|
|
8b506c |
+ ep->instname[len] = '\0';
|
|
|
8b506c |
+ }
|
|
|
8b506c |
+ }
|
|
|
8b506c |
+ if (ep->instname == NULL) /* no spaces found, so use the full name */
|
|
|
8b506c |
+ ep->instname = strndup(ep->name, PROC_PID_STAT_CMD_MAXLEN);
|
|
|
8b506c |
}
|
|
|
8b506c |
+
|
|
|
8b506c |
+ /* mark pid as valid (new or still running) */
|
|
|
8b506c |
+ ep->flags |= PROC_PID_FLAG_VALID;
|
|
|
8b506c |
}
|
|
|
8b506c |
|
|
|
8b506c |
/*
|
|
|
8b506c |
- * harvest exited pids from the pid hash table
|
|
|
8b506c |
+ * harvest pids that have exit'ed
|
|
|
8b506c |
*/
|
|
|
8b506c |
+ numinst = 0;
|
|
|
8b506c |
for (i=0; i < proc_pid->pidhash.hsize; i++) {
|
|
|
8b506c |
for (prev=NULL, node=proc_pid->pidhash.hash[i]; node != NULL;) {
|
|
|
8b506c |
next = node->next;
|
|
|
8b506c |
ep = (proc_pid_entry_t *)node->data;
|
|
|
8b506c |
// fprintf(stderr, "CHECKING key=%d node=" PRINTF_P_PFX "%p prev=" PRINTF_P_PFX "%p next=" PRINTF_P_PFX "%p ep=" PRINTF_P_PFX "%p valid=%d\n",
|
|
|
8b506c |
// ep->id, node, prev, node->next, ep, ep->valid);
|
|
|
8b506c |
- if (!(ep->flags & PROC_PID_FLAG_VALID)) {
|
|
|
8b506c |
+ if (ep->flags & PROC_PID_FLAG_VALID) {
|
|
|
8b506c |
+ numinst++;
|
|
|
8b506c |
+ prev = node;
|
|
|
8b506c |
+ }
|
|
|
8b506c |
+ else {
|
|
|
8b506c |
+ // This process has exited.
|
|
|
8b506c |
//fprintf(stderr, "DELETED key=%d name=\"%s\"\n", ep->id, ep->name);
|
|
|
8b506c |
+ if (ep->instname != NULL)
|
|
|
8b506c |
+ free(ep->instname);
|
|
|
8b506c |
if (ep->name != NULL)
|
|
|
8b506c |
free(ep->name);
|
|
|
8b506c |
if (ep->stat_buf != NULL)
|
|
|
8b506c |
@@ -1017,13 +1006,25 @@ refresh_proc_pidlist(proc_pid_t *proc_pid, proc_pid_list_t *pids)
|
|
|
8b506c |
free(ep);
|
|
|
8b506c |
free(node);
|
|
|
8b506c |
}
|
|
|
8b506c |
- else {
|
|
|
8b506c |
- prev = node;
|
|
|
8b506c |
- }
|
|
|
8b506c |
if ((node = next) == NULL)
|
|
|
8b506c |
break;
|
|
|
8b506c |
}
|
|
|
8b506c |
}
|
|
|
8b506c |
+
|
|
|
8b506c |
+ /*
|
|
|
8b506c |
+ * At this point, the hash table contains only valid pids. Refresh the indom table,
|
|
|
8b506c |
+ * based on the updated process hash table. Indom table instance names are shared
|
|
|
8b506c |
+ * with their hash table entry, do not free!
|
|
|
8b506c |
+ */
|
|
|
8b506c |
+ indomp->it_numinst = numinst;
|
|
|
8b506c |
+ indomp->it_set = (pmdaInstid *)realloc(indomp->it_set, numinst * sizeof(pmdaInstid));
|
|
|
8b506c |
+ for (idx=0, i=0; i < proc_pid->pidhash.hsize; i++) {
|
|
|
8b506c |
+ for (node=proc_pid->pidhash.hash[i]; node != NULL; node=node->next, idx++) {
|
|
|
8b506c |
+ ep = (proc_pid_entry_t *)node->data;
|
|
|
8b506c |
+ indomp->it_set[idx].i_inst = ep->id; /* internal instid is pid */
|
|
|
8b506c |
+ indomp->it_set[idx].i_name = ep->instname; /* ptr ref, do not free */
|
|
|
8b506c |
+ }
|
|
|
8b506c |
+ }
|
|
|
8b506c |
}
|
|
|
8b506c |
|
|
|
8b506c |
int
|
|
|
8b506c |
diff --git a/src/pmdas/linux_proc/proc_pid.h b/src/pmdas/linux_proc/proc_pid.h
|
|
|
8b506c |
index 344f4efc0..582965b3a 100644
|
|
|
8b506c |
--- a/src/pmdas/linux_proc/proc_pid.h
|
|
|
8b506c |
+++ b/src/pmdas/linux_proc/proc_pid.h
|
|
|
8b506c |
@@ -20,6 +20,10 @@
|
|
|
8b506c |
#include "proc_runq.h"
|
|
|
8b506c |
#include "hotproc.h"
|
|
|
8b506c |
|
|
|
8b506c |
+/*
|
|
|
8b506c |
+ * Maximim length of psargs and proc instance name.
|
|
|
8b506c |
+ */
|
|
|
8b506c |
+#define PROC_PID_STAT_CMD_MAXLEN 4096
|
|
|
8b506c |
|
|
|
8b506c |
/*
|
|
|
8b506c |
* /proc/<pid>/stat metrics
|
|
|
8b506c |
@@ -252,7 +256,8 @@ enum {
|
|
|
8b506c |
typedef struct {
|
|
|
8b506c |
int id; /* pid, hash key and internal instance id */
|
|
|
8b506c |
int flags; /* combinations of PROC_PID_FLAG_* values */
|
|
|
8b506c |
- char *name; /* external instance name (<pid> cmdline) */
|
|
|
8b506c |
+ char *name; /* full command line and args */
|
|
|
8b506c |
+ char *instname; /* external instance name (truncated <pid> cmdline) */
|
|
|
8b506c |
|
|
|
8b506c |
/* /proc/<pid>/stat cluster */
|
|
|
8b506c |
int stat_buflen;
|