Esempio n. 1
0
int
QmcContext::lookupName(pmID pmid, QString **name)
{
    char *value;
    int sts = 0;

    if ((sts = pmUseContext(my.context)) < 0)
        return sts;

    if (my.pmidCache.contains(pmid) == false) {
        if ((sts = pmNameID(pmid, &value)) >= 0) {
            *name = new QString(value);
            my.pmidCache.insert(pmid, *name);
            free(value);
        }
    } else {
        QString *np = my.pmidCache.value(pmid);
        if (pmDebug & DBG_TRACE_PMC) {
            QTextStream cerr(stderr);
            cerr << "QmcContext::lookupName: Matched id "
                 << pmIDStr(pmid) << " to \"" << *np << "\"" << endl;
        }
        *name = np;
    }
    return sts;
}
Esempio n. 2
0
static void
myvaluesetdump(pmValueSet *xvsp, int idx, int *flagsp)
{
    int			sts, flags = *flagsp;
    DescHash		*hp;
    __pmHashNode	*hnp;
    static __pmHashCtl	hash =  { 0, 0, NULL };

    if ((hnp = __pmHashSearch((unsigned int)xvsp->pmid, &hash)) == NULL) {
	/* first time for this pmid */
	hp = (DescHash *)malloc(sizeof(DescHash));
	if (hp == NULL) {
	    __pmNoMem("DescHash", sizeof(DescHash), PM_FATAL_ERR);
	    /*NOTREACHED*/
	}
	if ((sts = pmNameID(xvsp->pmid, &hp->name)) < 0) {
	    printf("	%s: pmNameID: %s\n", pmIDStr(xvsp->pmid), pmErrStr(sts));
	    free(hp);
	    return;
	}
	else {
	    if (xvsp->pmid != pmid_flags &&
		xvsp->pmid != pmid_missed &&
		(sts = pmLookupDesc(xvsp->pmid, &hp->desc)) < 0) {
		printf("	%s: pmLookupDesc: %s\n", hp->name, pmErrStr(sts));
		free(hp->name);
		free(hp);
		return;
	    }
	    if ((sts = __pmHashAdd((unsigned int)xvsp->pmid, (void *)hp, &hash)) < 0) {
		printf("	%s: __pmHashAdd: %s\n", hp->name, pmErrStr(sts));
		free(hp->name);
		free(hp);
		return;
	    }
	}
    }
    else
	hp = (DescHash *)hnp->data;

    if (idx == 0) {
	if (xvsp->pmid == pmid_flags) {
	    flags = *flagsp = xvsp->vlist[0].value.lval;
	    printf(" flags 0x%x", flags);
	    printf(" (%s) ---\n", pmEventFlagsStr(flags));
	    return;
	}
	else
	    printf(" ---\n");
    }
    if ((flags & PM_EVENT_FLAG_MISSED) &&
	(idx == 1) &&
	(xvsp->pmid == pmid_missed)) {
	printf("    ==> %d missed event records\n",
		    xvsp->vlist[0].value.lval);
	return;
    }
    mydump(hp->name, &hp->desc, xvsp);
}
Esempio n. 3
0
static void *
func1(void *arg)
{
    int		sts;
    char	*fn = "func1";
    char	**offspring;
    int		*status;
    int		i;
    int		j;
    char	*namelist[] = { "b", "a.three.foo", "d.f.fumble" };
    pmID	pmidlist[3];

    pthread_barrier_wait(&barrier);

    __pmDumpNameSpace(stdout, 1);

    sts = pmGetChildrenStatus("", &offspring, &status);
    if (sts >= 0) {
	printf("%s: pmGetChildrenStatus -> %d\n", fn, sts);
	for (i = 0; i < sts; i++) {
	    printf("[%d] %s %s\n", i, offspring[i], status[i] == PMNS_LEAF_STATUS ? "leaf" : "non-leaf");
	}
	free(offspring);
	free(status);
    }
    else
	printf("%s: pmGetChildrenStatus -> %s\n", fn, pmErrStr(sts));

    for (i = 0; i < 5; i++) {
	pthread_barrier_wait(&barrier);
	sts = pmLookupName(sizeof(namelist)/sizeof(namelist[0]), namelist, pmidlist);
	if (sts < 0)
	    printf("%s: pmGetChildrenStatus[%d] -> %s\n", fn, i, pmErrStr(sts));
	else {
	    for (j = 0; j < sizeof(namelist)/sizeof(namelist[0]); j++) {
		printf("%s: [%d] %s -> %s", fn, i, namelist[j], pmIDStr(pmidlist[j]));
		if (pmidlist[j] == PM_ID_NULL)
		    printf("\n");
		else {
		    char	*name;
		    sts = pmNameID(pmidlist[j], &name);
		    if (sts < 0)
			printf(": pmNameID -> %s\n", pmErrStr(sts));
		    else {
			printf(" -> %s\n", name);
			free(name);
		    }
		}
	    }
	}
	pthread_barrier_wait(&barrier);
    }

    pthread_exit(NULL);
}
Esempio n. 4
0
static void
foo(FILE *f, char *fn, int i, void *closure)
{
    int		sts;
    int		numnames;
    int		j;
    int		leaf;
    pmID	pmids[NMETRIC];
    char	**names;
    char	*name;
    int		*stsset;
    char	strbuf[20];

    if ((sts = pmLookupName(NMETRIC-i, &namelist[i], pmids)) < 0) {
	if (sts != PM_ERR_NONLEAF) {
	    fprintf(f, "%s: %s ...: pmLookupName Error: %s\n", fn, namelist[i], pmErrStr(sts));
	    pthread_exit("botch");
	}
    }
    for (j = i; j < NMETRIC; j++) {
	if (pmids[j-i] != pmidlist[j]) {
	    fprintf(f, "%s: %s: Botch: expecting %s", fn, namelist[j], pmIDStr_r(pmidlist[j], strbuf, sizeof(strbuf)));
	    fprintf(f, ", got %s\n", pmIDStr_r(pmids[j-i], strbuf, sizeof(strbuf)));
	    pthread_exit("botch");
	}
    }
    fprintf(f, "%s: %s ... pmLookupName OK\n", fn, namelist[i]);

    fprintf(f, "%s: %s", fn, namelist[i]);
    if (pmidlist[i] != PM_ID_NULL) {
	/* leaf node in PMNS */
	if ((numnames = pmNameAll(pmidlist[i], &names)) < 0) {
	    fprintf(f, "\n%s: %s ...: pmNameAll Error: %s\n", fn, namelist[i], pmErrStr(numnames));
	    pthread_exit("botch");
	}
	for (j = 0; j < numnames; j++) {
	    if (strcmp(names[j], namelist[i]) == 0)
		break;
	}
	if (j == numnames) {
	    fprintf(f, "\n%s: %s: Botch: expecting %s, got {", fn, pmIDStr_r(pmidlist[i], strbuf, sizeof(strbuf)), namelist[i]);
	    __pmPrintMetricNames(f, numnames, names, ",");
	    fprintf(f, "}\n");
	    pthread_exit("botch");
	}
	fprintf(f, " pmNameAll OK");
	if ((sts = pmNameID(pmidlist[i], &name)) < 0) {
	    fprintf(f, "\n%s: %s ...: pmNameID Error: %s\n", fn, namelist[i], pmErrStr(sts));
	    pthread_exit("botch");
	}
	for (j = 0; j < numnames; j++) {
	    if (strcmp(name, names[j]) == 0)
		break;
	}
	if (j == numnames) {
	    fprintf(f, "\n%s: %s: Botch: expecting one of {", fn, pmIDStr_r(pmidlist[i], strbuf, sizeof(strbuf)));
	    __pmPrintMetricNames(f, numnames, names, ",");
	    fprintf(f, "}, got %s\n", name);
	    pthread_exit("botch");
	}
	free(names);
	free(name);
	fprintf(f, " pmNameID OK");
    }
    else {
	/* non-leaf node in PMNS */
	int	keep = 0;
	if ((sts = pmGetChildrenStatus(namelist[i], &names, &stsset)) < 0) {
	    fprintf(f, "\n%s: %s ...: pmGetChildrenStatus Error: %s\n", fn, namelist[i], pmErrStr(sts));
	    pthread_exit("botch");
	}
	leaf = 0;
	for (j = 0; j < sts; j++) {
	    if (stsset[j] == PMNS_LEAF_STATUS) leaf++;
	}
	PM_LOCK(chn_lock);
	if (leaf_chn[i] == -1) {
	    leaf_chn[i] = leaf;
	    nonleaf_chn[i] = sts - leaf;
	    chn[i] = names;
	    keep = 1;
	}
	else {
	    if (leaf != leaf_chn[i] || sts - leaf != nonleaf_chn[i]) {
		fprintf(f, "\n%s: %s: Botch: expecting %d leaf & %d non-leaf, got %d leaf & %d non-leaf\n", fn, namelist[i], leaf_chn[i], nonleaf_chn[i], leaf, sts - leaf);
		pthread_exit("botch");
	    }
	    for (j = 0; j < sts; j++) {
		if (strcmp(chn[i][j], names[j]) != 0) {
		    fprintf(f, "\n%s: %s: Botch: child[%d] expecting %s, got %s\n", fn, namelist[i], j, chn[i][j], names[j]);
		    PM_UNLOCK(chn_lock);
		    pthread_exit("botch");
		}
	    }
	}
	if (keep == 0) {
	    free(names);
	    names = NULL; /* silence coverity */
	}
	free(stsset);
	fprintf(f, " pmGetChildrenStatus OK");
	if ((sts = pmGetChildren(namelist[i], &names)) < 0) {
	    fprintf(f, "\n%s: %s ...: pmGetChildren Error: %s\n", fn, namelist[i], pmErrStr(sts));
	    PM_UNLOCK(chn_lock);
	    pthread_exit("botch");
	}
	if (sts != leaf_chn[i] + nonleaf_chn[i]) {
	    fprintf(f, "\n%s: %s: Botch: expecting %d children, got %d\n", fn, namelist[i], leaf_chn[i] + nonleaf_chn[i], sts);
	    PM_UNLOCK(chn_lock);
	    pthread_exit("botch");
	}
	for (j = 0; j < sts; j++) {
	    if (strcmp(chn[i][j], names[j]) != 0) {
		fprintf(f, "\n%s: %s: Botch: child[%d] expecting %s, got %s\n", fn, namelist[i], j, chn[i][j], names[j]);
		PM_UNLOCK(chn_lock);
		pthread_exit("botch");
	    }
	}
	PM_UNLOCK(chn_lock);
	free(names);
	fprintf(f, " pmGetChildren OK");
	*((int *)closure) = 0;
	if ((sts = pmTraversePMNS_r(namelist[i], dometric, closure)) < 0) {
	    fprintf(f, "\n%s: %s ...: pmTraversePMNS_r Error: %s\n", fn, namelist[i], pmErrStr(sts));
	    pthread_exit("botch");
	}
	if (sum_traverse[i] != *((int *)closure)) {
	    fprintf(f, "\n%s: %s: Botch: sum strlen(descendent names) expecting %d, got %d\n", fn, namelist[i], sum_traverse[i], *((int *)closure));
	    pthread_exit("botch");
	}
	fprintf(f, " pmTraversePMNS_r OK");
    }
    fputc('\n', f);

}
Esempio n. 5
0
File: dopdu.c Progetto: aeppert/pcp
/* Update an existing metric (given a pmValueSet) adding it to the specified
 * task. Allocate and return a new task_t if the specified task pointer is nil.
 */
static int
update_metric(pmValueSet *vsp, int reqstate, int mflags, task_t **result)
{
    pmID	pmid = vsp->pmid;
    task_t	*ntp = *result;		/* pointer to new task */
    task_t	*ctp;			/* pointer to current task */
    optreq_t	*rqp;
    pmDesc	*dp;
    int		i, j, inst;
    int		sts, need = 0;
    int		addpmid = 0;
    int		freedp;

    /* allocate a new task if null task pointer passed in */
    if (ntp == NULL) {
	ntp = calloc(1, sizeof(task_t));
	if (ntp == NULL) {
	    __pmNoMem("update_metric: new task calloc", sizeof(task_t),
		     PM_FATAL_ERR);
	}
	*result = ntp;
    }

    if ((mflags & MF_HAS_INDOM) == 0) {
	rqp = findoptreq(pmid, 0);
	ctp = (task_t *)(rqp->r_fetch->f_aux);
	if (!update_ok(ctp->t_state, reqstate))
	    return 1;

	/* if the new state is advisory off, just remove the metric */
	if ((PMLC_GET_MAYBE(reqstate)) ||
	    (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0))
	    del_optreq(rqp);
	else {
	    /* update the optreq.  For single valued metrics there are no
	     * instances involved so the sole optreq can just be re-used.
	     */
	    if ((sts = __pmOptFetchDel(&ctp->t_fetch, rqp)) < 0)
		die("update_metric: 1 metric __pmOptFetchDel", sts);
	    __pmOptFetchAdd(&ntp->t_fetch, rqp);
	    linkback(ntp);
	    addpmid = 1;
	}
    }
    else {
	/* metric has an instance domain */
	if (vsp->numval > 0) {
	    /* tricky: since optFetch can't handle instance profiles of the
	     * form "all except these specific instances", and managing it
	     * manually is just too hard, reject requests for specific
	     * metric instances if "all instances" of the metric are already
	     * being logged.
	     * Note: advisory off "all instances" is excepted since ANY request
	     * overrides and advisory off.  E.g. "advisory off all" followed by
	     * "advisory on someinsts" turns on advisory logging for
	     * "someinsts".  mflags will be zero for "advisory off" metrics.
	     */
	    if (mflags & MF_HAS_ALL)
		return 1;		/* can't turn "all" into specific insts */

	    for (i = 0; i < vsp->numval; i++) {
		dp = NULL;
		freedp = 0;
		inst = vsp->vlist[i].inst;
		rqp = findoptreq(pmid, inst);
		if (rqp != NULL) {
		    dp = rqp->r_desc;
		    ctp = (task_t *)(rqp->r_fetch->f_aux);
		    /* no work required if new task and current are the same */
		    if (ntp == ctp)
			continue;
		    if (!update_ok(ctp->t_state, reqstate))
			continue;

		    /* remove inst's group from current task */
		    if ((sts = __pmOptFetchDel(&ctp->t_fetch, rqp)) < 0)
			die("update_metric: instance add __pmOptFetchDel", sts);

		    /* put group back if there are any instances left */
		    if (rqp->r_numinst > 1) {
			/* remove inst from group */
			for (j = 0; j < rqp->r_numinst; j++)
			    if (inst == rqp->r_instlist[j])
				break;
			/* don't call memmove to move zero bytes */
			if (j < rqp->r_numinst - 1)
			    memmove(&rqp->r_instlist[j], &rqp->r_instlist[j+1],
				    (rqp->r_numinst - 1 - j) *
				    sizeof(rqp->r_instlist[0]));
			rqp->r_numinst--;
			/* (don't bother realloc-ing the instlist to a smaller size) */

			__pmOptFetchAdd(&ctp->t_fetch, rqp);
			linkback(ctp);
			/* no need to update hash list, rqp already there */
		    }
		    /* if that was the last instance, free the group */
		    else {
			if (( sts = __pmHashDel(pmid, (void *)rqp, &pm_hash)) < 0)
			    die("update_metric: instance __pmHashDel", sts);
			freedp = 1;
			free(rqp->r_instlist);
			free(rqp);
		    }
		}

		/* advisory off (mandatory maybe) metrics don't get put into
		 * the data structure
		 */
		if (PMLC_GET_MAYBE(reqstate) ||
		    (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0)) {
		    if (freedp)
			free(dp);
		    continue;
		}
		addpmid = 1;

		/* try to find an existing optreq_t for the instance */
		rqp = find_instoptreq(ntp, pmid);
		if (rqp != NULL) {
		    if ((sts = __pmOptFetchDel(&ntp->t_fetch, rqp)) < 0)
			die("update_metric: instance add __pmOptFetchDel", sts);
		}
		/* no existing optreq_t found, allocate & populate a new one */
		else {
		    rqp = (optreq_t *)calloc(1, sizeof(optreq_t));
		    if (rqp == NULL) {
			__pmNoMem("update_metric: optreq calloc",
				 sizeof(optreq_t), PM_FATAL_ERR);
		    }
		    /* if the metric existed but the instance didn't, we don't
		     * have a valid pmDesc (dp), so find one.
		     */
		    if (dp == NULL)  {
			/* find metric and associated pmDesc */
			__pmHashNode	*hp;

			for (hp = __pmHashSearch(pmid, &pm_hash);
			     hp != NULL; hp = hp->next) {
			    if (pmid == (pmID)hp->key)
				break;
			}
			assert(hp != NULL);
			dp = ((optreq_t *)hp->data)->r_desc;
		    }
		    /* recycle pmDesc from the old group, if possible */
		    if (freedp) {
			rqp->r_desc = dp;
			freedp = 0;
		    }
		    /* otherwise allocate & copy a new pmDesc via dp */
		    else {
			need = sizeof(pmDesc);
			rqp->r_desc = (pmDesc *)malloc(need);
			if (rqp->r_desc == NULL) {
			    __pmNoMem("update_metric: new inst pmDesc malloc",
				     need, PM_FATAL_ERR);
			}
			memcpy(rqp->r_desc, dp, need);
		    }
		    if ((sts = __pmHashAdd(pmid, (void *)rqp, &pm_hash)) < 0)
			die("update_metric: __pmHashAdd", sts);
		}
		    
		need = (rqp->r_numinst + 1) * sizeof(rqp->r_instlist[0]);
		rqp->r_instlist = (int *)realloc(rqp->r_instlist, need);
		if (rqp->r_instlist == NULL) {
		    __pmNoMem("update_metric: inst list resize", need,
			     PM_FATAL_ERR);
		}
		rqp->r_instlist[rqp->r_numinst++] = inst;
		__pmOptFetchAdd(&ntp->t_fetch, rqp);
		linkback(ntp);
		if (freedp)
		    free(dp);
	    }
	}
	/* the vset has numval == 0, a request for "all instances" */
	else {
	    /* if the metric is a singular instance that has mandatory logging
	     * or has at least one instance with mandatory logging on, a
	     * request for advisory logging cannot be honoured
	     */
	    if ((mflags & MF_HAS_MAND) &&
		PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_MAYBE(reqstate) == 0)
		return 1;

	    if (mflags & MF_HAS_ALL) {
		/* if there is an "all instances" for the metric, it will be
		 * the only optreq_t for the metric
		 */
		rqp = findoptreq(pmid, 0);
		ctp = (task_t *)rqp->r_fetch->f_aux;

		/* if the metric is "advisory on, all instances"  and the
		 * request is for "mandatory maybe, all instances" the current
		 * advisory logging state of the metric is retained
		 */
		if (PMLC_GET_MAND(ctp->t_state) == 0 && PMLC_GET_MAYBE(reqstate))
		    return 0;

		/* advisory off & mandatory maybe metrics don't get put into
		 * the data structure
		 */
		if (PMLC_GET_MAYBE(reqstate) ||
		    (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0)) {
		    del_optreq(rqp);
		    return 0;
		}

		addpmid = 1;
		if ((sts = __pmOptFetchDel(&ctp->t_fetch, rqp)) < 0)
		    die("update_metric: all inst __pmOptFetchDel", sts);
		/* don't delete from hash list, rqp re-used */
		__pmOptFetchAdd(&ntp->t_fetch, rqp);
		linkback(ntp);
	    }
	    else {
		/* there are one or more specific instances for the metric.
		 * The metric cannot have an "all instances" at the same time.
		 *
		 * if the request is for "mandatory maybe, all instances" and
		 * the only instances of the metric all have advisory logging
		 * on, retain the current advisory semantics.
		 */
		if (PMLC_GET_MAYBE(reqstate) &&
		    (mflags & MF_HAS_INST) && !(mflags & MF_HAS_MAND))
		    return 0;

		dp = del_insts(pmid);

		/* advisory off (mandatory maybe) metrics don't get put into
		 * the data structure
		 */
		if (PMLC_GET_MAYBE(reqstate) ||
		    (PMLC_GET_MAND(reqstate) == 0 && PMLC_GET_ON(reqstate) == 0)) {
		    free(dp);
		    return 0;
		}

		addpmid = 1;
		rqp = (optreq_t *)calloc(1, sizeof(optreq_t));
		if (rqp == NULL) {
		    __pmNoMem("update_metric: all inst calloc",
			     sizeof(optreq_t), PM_FATAL_ERR);
		}
		rqp->r_desc = dp;
		__pmOptFetchAdd(&ntp->t_fetch, rqp);
		linkback(ntp);
		if ((sts = __pmHashAdd(pmid, (void *)rqp, &pm_hash)) < 0)
		    die("update_metric: all inst __pmHashAdd", sts);
	    }
	}
    }

    if (!addpmid)
	return 0;

    /* add pmid to new task if not already there */
    for (i = 0; i < ntp->t_numpmid; i++)
	if (pmid == ntp->t_pmidlist[i])
	    break;
    if (i >= ntp->t_numpmid) {
	pmDesc	desc;
	char	*name;
	int	need;

	if ((sts = pmLookupDesc(pmid, &desc)) < 0)
	    die("update_metric: cannot lookup desc", sts);
	if ((sts = pmNameID(pmid, &name)) < 0)
	    die("update_metric: cannot lookup name", sts);

	need = (ntp->t_numpmid + 1) * sizeof(pmID);
	if (!(ntp->t_pmidlist = (pmID *)realloc(ntp->t_pmidlist, need)))
	    __pmNoMem("update_metric: grow task pmidlist", need, PM_FATAL_ERR);
	need = (ntp->t_numpmid + 1) * sizeof(char *);
	if (!(ntp->t_namelist = (char **)realloc(ntp->t_namelist, need)))
	    __pmNoMem("update_metric: grow task namelist", need, PM_FATAL_ERR);
	need = (ntp->t_numpmid + 1) * sizeof(pmDesc);
	if (!(ntp->t_desclist = (pmDesc *)realloc(ntp->t_desclist, need)))
	    __pmNoMem("update_metric: grow task desclist", need, PM_FATAL_ERR);
	i = ntp->t_numpmid;
	ntp->t_pmidlist[i] = pmid;
	ntp->t_namelist[i] = name;
	ntp->t_desclist[i] = desc;
	ntp->t_numpmid++;
    }
    return 0;
}
Esempio n. 6
0
File: dopdu.c Progetto: aeppert/pcp
/* Add a new metric (given a pmValueSet and a pmDesc) to the specified task.
 * Allocate and return a new task_t if the specified task pointer is nil.
 *
 * Note that this should only be called for metrics not currently in the
 * logging data structure.  All instances in the pmValueSet are added!
 */
static int
add_metric(pmValueSet *vsp, task_t **result)
{
    pmID	pmid = vsp->pmid;
    task_t	*tp = *result;
    optreq_t	*rqp;
    pmDesc	*dp;
    char	*name;
    int		sts, i, need = 0;

    dp = (pmDesc *)malloc(sizeof(pmDesc));
    if (dp == NULL) {
	__pmNoMem("add_metric: new pmDesc malloc", sizeof(pmDesc), PM_FATAL_ERR);
    }
    if ((sts = pmLookupDesc(pmid, dp)) < 0)
	die("add_metric: lookup desc", sts);
    if ((sts = pmNameID(pmid, &name)) < 0)
	die("add_metric: lookup name", sts);

    /* allocate a new task if null task pointer passed in */
    if (tp == NULL) {
	tp = calloc(1, sizeof(task_t));
	if (tp == NULL) {
	    __pmNoMem("add_metric: new task calloc", sizeof(task_t), PM_FATAL_ERR);
	}
	*result = tp;
    }

    /* add metric (and any instances specified) to task */
    i = tp->t_numpmid++;
    need = tp->t_numpmid * sizeof(pmID);
    if (!(tp->t_pmidlist = (pmID *)realloc(tp->t_pmidlist, need)))
	__pmNoMem("add_metric: new task pmidlist realloc", need, PM_FATAL_ERR);
    need = tp->t_numpmid * sizeof(char *);
    if (!(tp->t_namelist = (char **)realloc(tp->t_namelist, need)))
	__pmNoMem("add_metric: new task namelist realloc", need, PM_FATAL_ERR);
    need = tp->t_numpmid * sizeof(pmDesc);
    if (!(tp->t_desclist = (pmDesc *)realloc(tp->t_desclist, need)))
	__pmNoMem("add_metric: new task desclist realloc", need, PM_FATAL_ERR);
    tp->t_pmidlist[i] = pmid;
    tp->t_namelist[i] = name;
    tp->t_desclist[i] = *dp;	/* struct assignment */

    rqp = (optreq_t *)calloc(1, sizeof(optreq_t));
    if (rqp == NULL) {
	__pmNoMem("add_metric: new task optreq calloc", need, PM_FATAL_ERR);
    }
    rqp->r_desc = dp;

    /* Now copy instances if required.  Remember that metrics with singular
     * values actually have one instance specified to distinguish them from the
     * "all instances" case (which has no instances).  Use the pmDesc to check
     * for this.
     */
    if (dp->indom != PM_INDOM_NULL)
	need = rqp->r_numinst = vsp->numval;
    if (need) {
	need *= sizeof(rqp->r_instlist[0]);
	rqp->r_instlist = (int *)malloc(need);
	if (rqp->r_instlist == NULL) {
	    __pmNoMem("add_metric: new task optreq instlist malloc", need,
		     PM_FATAL_ERR);
	}
	for (i = 0; i < vsp->numval; i++)
	    rqp->r_instlist[i] = vsp->vlist[i].inst;
    }

    /* Add new metric to task's fetchgroup(s) and global hash table */
    __pmOptFetchAdd(&tp->t_fetch, rqp);
    linkback(tp);
    if ((sts = __pmHashAdd(pmid, (void *)rqp, &pm_hash)) < 0)
	die("add_metric: __pmHashAdd", sts);
    return 0;
}