1    	/*
2    	 * Copyright (c) 2009 Ken McDonell.  All Rights Reserved.
3    	 * 
4    	 * This library is free software; you can redistribute it and/or modify it
5    	 * under the terms of the GNU Lesser General Public License as published
6    	 * by the Free Software Foundation; either version 2.1 of the License, or
7    	 * (at your option) any later version.
8    	 * 
9    	 * This library is distributed in the hope that it will be useful, but
10   	 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11   	 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
12   	 * License for more details.
13   	 * 
14   	 * Debug Flags
15   	 *	DERIVE - high-level diagnostics
16   	 *	DERIVE & APPL0 - configuration and static syntax analysis
17   	 *	DERIVE & APPL1 - expression binding, semantic analysis, PMNS ops
18   	 *	DERIVE & APPL2 - fetch handling
19   	 *
20   	 * Caveats
21   	 * 0.	No unary negation operator.
22   	 * 1.	No derived metrics for pmFetchArchive() as this routine does
23   	 *	not use a target pmidlist[]
24   	 * 2.	Derived metrics will not work with pmRequestTraversePMNS() and
25   	 *	pmReceiveTraversePMNS() because the by the time the list of
26   	 *	names is received, the original name at the root of the search
27   	 *	is no longer available.
28   	 * 3.	pmRegisterDerived() does not apply retrospectively to any open
29   	 * 	contexts, so the normal use would be to make all calls to
30   	 * 	pmRegisterDerived() (possibly via pmLoadDerivedConfig()) and then
31   	 * 	call pmNewContext().
32   	 * 4.	There is no pmUnregisterDerived(), so once registered a derived
33   	 * 	metric persists for the life of the application.
34   	 */
35   	
36   	#include <inttypes.h>
37   	#include "derive.h"
38   	
39   	static int	need_init = 1;
40   	static ctl_t	registered;
41   	
42   	/* parser and lexer variables */
43   	static char	*tokbuf = NULL;
44   	static int	tokbuflen;
45   	static char	*this;		/* start of current lexicon */
46   	static int	lexpeek = 0;
47   	static char	*string;
48   	static char	*errmsg;
49   	
50   	static char *type_dbg[] = { "ERROR", "EOF", "UNDEF", "NUMBER", "NAME", "PLUS", "MINUS", "STAR", "SLASH", "LPAREN", "RPAREN", "AVG", "COUNT", "DELTA", "MAX", "MIN", "SUM", "ANON" };
51   	static char type_c[] = { '\0', '\0', '\0', '\0', '\0', '+', '-', '*', '/', '(', ')', '\0' };
52   	
53   	/* function table for lexer */
54   	static struct {
55   	    int		f_type;
56   	    char	*f_name;
57   	} func[] = {
58   	    { L_AVG,	"avg" },
59   	    { L_COUNT,	"count" },
60   	    { L_DELTA,	"delta" },
61   	    { L_MAX,	"max" },
62   	    { L_MIN,	"min" },
63   	    { L_SUM,	"sum" },
64   	    { L_ANON,	"anon" },
65   	    { L_UNDEF,	NULL }
66   	};
67   	
68   	/* parser states */
69   	#define P_INIT		0
70   	#define P_LEAF		1
71   	#define P_LEAF_PAREN	2
72   	#define P_BINOP		3
73   	#define P_FUNC_OP	4
74   	#define P_FUNC_END	5
75   	#define P_END		99
76   	
77   	static char *state_dbg[] = { "INIT", "LEAF", "LEAF_PAREN", "BINOP", "FUNC_OP", "FUNC_END" };
78   	
79   	/* Register an anonymous metric */
80   	int
81   	__pmRegisterAnon(char *name, int type)
82   	{
83   	    char	*msg;
84   	    char	buf[21];	/* anon(PM_TYPE_XXXXXX) */
85   	
86   	    switch (type) {
87   		case PM_TYPE_32:
88   		    snprintf(buf, sizeof(buf), "anon(PM_TYPE_32)");
89   		    break;
90   		case PM_TYPE_U32:
91   		    snprintf(buf, sizeof(buf), "anon(PM_TYPE_U32)");
92   		    break;
93   		case PM_TYPE_64:
94   		    snprintf(buf, sizeof(buf), "anon(PM_TYPE_64)");
95   		    break;
96   		case PM_TYPE_U64:
97   		    snprintf(buf, sizeof(buf), "anon(PM_TYPE_U64)");
98   		    break;
99   		case PM_TYPE_FLOAT:
100  		    snprintf(buf, sizeof(buf), "anon(PM_TYPE_FLOAT)");
101  		    break;
102  		case PM_TYPE_DOUBLE:
103  		    snprintf(buf, sizeof(buf), "anon(PM_TYPE_DOUBLE)");
104  		    break;
105  		default:
106  		    return PM_ERR_TYPE;
107  	    }
Event returned_pointer: Pointer "msg" returned by "pmRegisterDerived(name, buf)" is never used.
108  	    if ((msg = pmRegisterDerived(name, buf)) != NULL) {
109  		pmprintf("__pmRegisterAnon(%s, %d): Error: %s\n", name, type, pmDerivedErrStr());
110  		pmflush();
111  		return PM_ERR_GENERIC;
112  	    }
113  	    return 0;
114  	}
115  	
116  	static void
117  	init(void)
118  	{
119  	    char	*configpath;
120  	
121  	    if ((configpath = getenv("PCP_DERIVED_CONFIG")) != NULL) {
122  		int	sts;
123  	#ifdef PCP_DEBUG
124  		if (pmDebug & DBG_TRACE_DERIVE) {
125  		    fprintf(stderr, "Derived metric initialization from $PCP_DERIVED_CONFIG\n");
126  		}
127  	#endif
128  		sts = pmLoadDerivedConfig(configpath);
129  	#ifdef PCP_DEBUG
130  		if (sts < 0 && (pmDebug & DBG_TRACE_DERIVE)) {
131  		    fprintf(stderr, "pmLoadDerivedConfig -> %s\n", pmErrStr(sts));
132  		}
133  	#endif
134  	    }
135  	
136  	    need_init = 0;
137  	}
138  	
139  	static void
140  	unget(int c)
141  	{
142  	    lexpeek = c;
143  	}
144  	
145  	static int
146  	get()
147  	{
148  	    static int	eof = 0;
149  	    int		c;
150  	    if (lexpeek != 0) {
151  		c = lexpeek;
152  		lexpeek = 0;
153  		return c;
154  	    }
155  	    if (eof) return L_EOF;
156  	    c = *string;
157  	    if (c == '\0') {
158  		return L_EOF;
159  		eof = 1;
160  	    }
161  	    string++;
162  	    return c;
163  	}
164  	
165  	static int
166  	lex(void)
167  	{
168  	    int		c;
169  	    char	*p = tokbuf;
170  	    int		ltype = L_UNDEF;
171  	    int		i;
172  	    int		firstch = 1;
173  	
174  	    for ( ; ; ) {
175  		c = get();
176  		if (firstch) {
177  		    if (isspace(c)) continue;
178  		    this = &string[-1];
179  		    firstch = 0;
180  		}
181  		if (c == L_EOF) {
182  		    if (ltype != L_UNDEF) {
183  			/* force end of last token */
184  			c = 0;
185  		    }
186  		    else {
187  			/* really the end of the input */
188  			return L_EOF;
189  		    }
190  		}
191  		if (p == NULL) {
192  		    tokbuflen = 128;
193  		    if ((p = tokbuf = (char *)malloc(tokbuflen)) == NULL) {
194  			__pmNoMem("pmRegisterDerived: alloc tokbuf", tokbuflen, PM_FATAL_ERR);
195  			/*NOTREACHED*/
196  		    }
197  		}
198  		else if (p >= &tokbuf[tokbuflen]) {
199  		    int		x = p - tokbuf;
200  		    tokbuflen *= 2;
201  		    if ((tokbuf = (char *)realloc(tokbuf, tokbuflen)) == NULL) {
202  			__pmNoMem("pmRegisterDerived: realloc tokbuf", tokbuflen, PM_FATAL_ERR);
203  			/*NOTREACHED*/
204  		    }
205  		    p = &tokbuf[x];
206  		}
207  	
208  		*p++ = (char)c;
209  	
210  		if (ltype == L_UNDEF) {
211  		    if (isdigit(c))
212  			ltype = L_NUMBER;
213  		    else if (isalpha(c))
214  			ltype = L_NAME;
215  		    else {
216  			switch (c) {
217  			    case '+':
218  				*p = '\0';
219  				return L_PLUS;
220  				break;
221  	
222  			    case '-':
223  				*p = '\0';
224  				return L_MINUS;
225  				break;
226  	
227  			    case '*':
228  				*p = '\0';
229  				return L_STAR;
230  				break;
231  	
232  			    case '/':
233  				*p = '\0';
234  				return L_SLASH;
235  				break;
236  	
237  			    case '(':
238  				*p = '\0';
239  				return L_LPAREN;
240  				break;
241  	
242  			    case ')':
243  				*p = '\0';
244  				return L_RPAREN;
245  				break;
246  	
247  			    default:
248  				return L_ERROR;
249  				break;
250  			}
251  		    }
252  		}
253  		else {
254  		    if (ltype == L_NUMBER) {
255  			if (!isdigit(c)) {
256  			    unget(c);
257  			    p[-1] = '\0';
258  			    return L_NUMBER;
259  			}
260  		    }
261  		    else if (ltype == L_NAME) {
262  			if (isalpha(c) || isdigit(c) || c == '_' || c == '.')
263  			    continue;
264  			if (c == '(') {
265  			    /* check for functions ... */
266  			    int		namelen = p - tokbuf - 1;
267  			    for (i = 0; func[i].f_name != NULL; i++) {
268  				if (namelen == strlen(func[i].f_name) &&
269  				    strncmp(tokbuf, func[i].f_name, namelen) == 0) {
270  				    *p = '\0';
271  				    return func[i].f_type;
272  				}
273  			    }
274  			}
275  			/* current character is end of name */
276  			unget(c);
277  			p[-1] = '\0';
278  			return L_NAME;
279  		    }
280  		}
281  	
282  	    }
283  	}
284  	
285  	static node_t *
286  	newnode(int type)
287  	{
288  	    node_t	*np;
289  	    np = (node_t *)malloc(sizeof(node_t));
290  	    if (np == NULL) {
291  		__pmNoMem("pmRegisterDerived: newnode", sizeof(node_t), PM_FATAL_ERR);
292  		/*NOTREACHED*/
293  	    }
294  	    np->type = type;
295  	    np->save_last = 0;
296  	    np->left = NULL;
297  	    np->right = NULL;
298  	    np->value = NULL;
299  	    np->info = NULL;
300  	    return np;
301  	}
302  	
303  	static void
304  	free_expr(node_t *np)
305  	{
306  	    if (np == NULL) return;
307  	    if (np->left != NULL) free_expr(np->left);
308  	    if (np->right != NULL) free_expr(np->right);
309  	    /* value is only allocated once for the static nodes */
310  	    if (np->info == NULL && np->value != NULL) free(np->value);
311  	    if (np->info != NULL) free(np->info);
312  	    free(np);
313  	}
314  	
315  	/*
316  	 * copy a static expression tree to make the dynamic per context
317  	 * expression tree and initialize the info block
318  	 */
319  	static node_t *
320  	bind_expr(int n, node_t *np)
321  	{
322  	    node_t	*new;
323  	
324  	    assert(np != NULL);
325  	    new = newnode(np->type);
326  	    if (np->left != NULL) {
327  		if ((new->left = bind_expr(n, np->left)) == NULL) {
328  		    /* error, reported deeper in the recursion, clean up */
329  		    free(new);
330  		    return(NULL);
331  		}
332  	    }
333  	    if (np->right != NULL) {
334  		if ((new->right = bind_expr(n, np->right)) == NULL) {
335  		    /* error, reported deeper in the recursion, clean up */
336  		    free(new);
337  		    return(NULL);
338  		}
339  	    }
340  	    if ((new->info = (info_t *)malloc(sizeof(info_t))) == NULL) {
341  		__pmNoMem("bind_expr: info block", sizeof(info_t), PM_FATAL_ERR);
342  		/*NOTREACHED*/
343  	    }
344  	    new->info->pmid = PM_ID_NULL;
345  	    new->info->numval = 0;
346  	    new->info->mul_scale = new->info->div_scale = 1;
347  	    new->info->ivlist = NULL;
348  	    new->info->last_numval = 0;
349  	    new->info->last_ivlist = NULL;
350  	
351  	    /* need info to be non-null to protect copy of value in free_expr */
352  	    new->value = np->value;
353  	
354  	    new->save_last = np->save_last;
355  	
356  	    if (new->type == L_NAME) {
357  		int	sts;
358  	
359  		sts = pmLookupName(1, &new->value, &new->info->pmid);
360  		if (sts < 0) {
361  	#ifdef PCP_DEBUG
362  		    if (pmDebug & DBG_TRACE_DERIVE) {
363  			fprintf(stderr, "bind_expr: error: derived metric %s: operand: %s: %s\n", registered.mlist[n].name, new->value, pmErrStr(sts));
364  		    }
365  	#endif
366  		    free(new->info);
367  		    free(new);
368  		    return NULL;
369  		}
370  		sts = pmLookupDesc(new->info->pmid, &new->desc);
371  		if (sts < 0) {
372  	#ifdef PCP_DEBUG
373  		    if (pmDebug & DBG_TRACE_DERIVE) {
374  			fprintf(stderr, "bind_expr: error: derived metric %s: operand (%s [%s]): %s\n", registered.mlist[n].name, new->value, pmIDStr(new->info->pmid), pmErrStr(sts));
375  		    }
376  	#endif
377  		    free(new->info);
378  		    free(new);
379  		    return NULL;
380  		}
381  	    }
382  	    else if (new->type == L_NUMBER) {
383  		new->desc = np->desc;
384  	    }
385  	
386  	    return new;
387  	}
388  	
389  	static
390  	void report_sem_error(char *name, node_t *np)
391  	{
392  	    pmprintf("Semantic error: derived metric %s: ", name);
393  	    switch (np->type) {
394  		case L_PLUS:
395  		case L_MINUS:
396  		case L_STAR:
397  		case L_SLASH:
398  		    if (np->left->type == L_NUMBER || np->left->type == L_NAME)
399  			pmprintf("%s ", np->left->value);
400  		    else
401  			pmprintf("<expr> ");
402  		    pmprintf("%c ", type_c[np->type+2]);
403  		    if (np->right->type == L_NUMBER || np->right->type == L_NAME)
404  			pmprintf("%s", np->right->value);
405  		    else
406  			pmprintf("<expr>");
407  		    break;
408  		case L_AVG:
409  		case L_COUNT:
410  		case L_DELTA:
411  		case L_MAX:
412  		case L_MIN:
413  		case L_SUM:
414  		case L_ANON:
415  		    pmprintf("%s(%s)", type_dbg[np->type+2], np->left->value);
416  		    break;
417  		default:
418  		    /* should never get here ... */
419  		    if (np->type+2 >= 0 && np->type+2 < sizeof(type_dbg)/sizeof(type_dbg[0]))
420  			pmprintf("botch @ node type %s?", type_dbg[np->type+2]);
421  		    else
422  			pmprintf("botch @ node type #%d?", np->type);
423  		    break;
424  	    }
425  	    pmprintf(": %s\n", errmsg);
426  	    pmflush();
427  	    errmsg = NULL;
428  	}
429  	
430  	/* type promotion */
431  	static int promote[6][6] = {
432  	    { PM_TYPE_32, PM_TYPE_U32, PM_TYPE_64, PM_TYPE_U64, PM_TYPE_FLOAT, PM_TYPE_DOUBLE },
433  	    { PM_TYPE_U32, PM_TYPE_U32, PM_TYPE_64, PM_TYPE_U64, PM_TYPE_FLOAT, PM_TYPE_DOUBLE },
434  	    { PM_TYPE_64, PM_TYPE_64, PM_TYPE_64, PM_TYPE_U64, PM_TYPE_FLOAT, PM_TYPE_DOUBLE },
435  	    { PM_TYPE_U64, PM_TYPE_U64, PM_TYPE_U64, PM_TYPE_U64, PM_TYPE_FLOAT, PM_TYPE_DOUBLE },
436  	    { PM_TYPE_FLOAT, PM_TYPE_FLOAT, PM_TYPE_FLOAT, PM_TYPE_FLOAT, PM_TYPE_FLOAT, PM_TYPE_DOUBLE },
437  	    { PM_TYPE_DOUBLE, PM_TYPE_DOUBLE, PM_TYPE_DOUBLE, PM_TYPE_DOUBLE, PM_TYPE_DOUBLE, PM_TYPE_DOUBLE }
438  	};
439  	
440  	/* time scale conversion factors */
441  	static int timefactor[] = {
442  	    1000,		/* NSEC -> USEC */
443  	    1000,		/* USEC -> MSEC */
444  	    1000,		/* MSEC -> SEC */
445  	    60,			/* SEC -> MIN */
446  	    60,			/* MIN -> HOUR */
447  	};
448  	
449  	/*
450  	 * mapping pmUnits for the result, and refining pmDesc as we go ...
451  	 * we start with the pmDesc from the left operand and adjust as
452  	 * necessary
453  	 *
454  	 * scale conversion rules ...
455  	 * Count - choose larger, divide/multiply smaller by 10^(difference)
456  	 * Time - choose larger, divide/multiply smaller by appropriate scale
457  	 * Space - choose larger, divide/multiply smaller by 1024^(difference)
458  	 * and result is of type PM_TYPE_DOUBLE
459  	 *
460  	 * Need inverted logic to deal with numerator (dimension > 0) and
461  	 * denominator (dimension < 0) cases.
462  	 */
463  	static void
464  	map_units(node_t *np)
465  	{
466  	    pmDesc	*right = &np->right->desc;
467  	    pmDesc	*left = &np->left->desc;
468  	    int		diff;
469  	    int		i;
470  	
471  	    if (left->units.dimCount != 0 && right->units.dimCount != 0) {
472  		diff = left->units.scaleCount - right->units.scaleCount;
473  		if (diff > 0) {
474  		    /* use the left scaleCount, scale the right operand */
475  		    for (i = 0; i < diff; i++) {
476  			if (right->units.dimCount > 0)
477  			    np->right->info->div_scale *= 10;
478  			else
479  			    np->right->info->mul_scale *= 10;
480  		    }
481  		    np->desc.type = PM_TYPE_DOUBLE;
482  		}
483  		else if (diff < 0) {
484  		    /* use the right scaleCount, scale the left operand */
485  		    np->desc.units.scaleCount = right->units.scaleCount;
486  		    for (i = diff; i < 0; i++) {
487  			if (left->units.dimCount > 0)
488  			    np->left->info->div_scale *= 10;
489  			else
490  			    np->left->info->mul_scale *= 10;
491  		    }
492  		    np->desc.type = PM_TYPE_DOUBLE;
493  		}
494  	    }
495  	    if (left->units.dimTime != 0 && right->units.dimTime != 0) {
496  		diff = left->units.scaleTime - right->units.scaleTime;
497  		if (diff > 0) {
498  		    /* use the left scaleTime, scale the right operand */
499  		    for (i = right->units.scaleTime; i < left->units.scaleTime; i++) {
500  			if (right->units.dimTime > 0)
501  			    np->right->info->div_scale *= timefactor[i];
502  			else
503  			    np->right->info->mul_scale *= timefactor[i];
504  		    }
505  		    np->desc.type = PM_TYPE_DOUBLE;
506  		}
507  		else if (diff < 0) {
508  		    /* use the right scaleTime, scale the left operand */
509  		    np->desc.units.scaleTime = right->units.scaleTime;
510  		    for (i = left->units.scaleTime; i < right->units.scaleTime; i++) {
511  			if (right->units.dimTime > 0)
512  			    np->left->info->div_scale *= timefactor[i];
513  			else
514  			    np->left->info->mul_scale *= timefactor[i];
515  		    }
516  		    np->desc.type = PM_TYPE_DOUBLE;
517  		}
518  	    }
519  	    if (left->units.dimSpace != 0 && right->units.dimSpace != 0) {
520  		diff = left->units.scaleSpace - right->units.scaleSpace;
521  		if (diff > 0) {
522  		    /* use the left scaleSpace, scale the right operand */
523  		    for (i = 0; i < diff; i++) {
524  			if (right->units.dimSpace > 0)
525  			    np->right->info->div_scale *= 1024;
526  			else
527  			    np->right->info->mul_scale *= 1024;
528  		    }
529  		    np->desc.type = PM_TYPE_DOUBLE;
530  		}
531  		else if (diff < 0) {
532  		    /* use the right scaleSpace, scale the left operand */
533  		    np->desc.units.scaleSpace = right->units.scaleSpace;
534  		    for (i = diff; i < 0; i++) {
535  			if (right->units.dimSpace > 0)
536  			    np->left->info->div_scale *= 1024;
537  			else
538  			    np->left->info->mul_scale *= 1024;
539  		    }
540  		    np->desc.type = PM_TYPE_DOUBLE;
541  		}
542  	    }
543  	
544  	    if (np->type == L_STAR) {
545  		np->desc.units.dimCount = left->units.dimCount + right->units.dimCount;
546  		np->desc.units.dimTime = left->units.dimTime + right->units.dimTime;
547  		np->desc.units.dimSpace = left->units.dimSpace + right->units.dimSpace;
548  	    }
549  	    else if (np->type == L_SLASH) {
550  		np->desc.units.dimCount = left->units.dimCount - right->units.dimCount;
551  		np->desc.units.dimTime = left->units.dimTime - right->units.dimTime;
552  		np->desc.units.dimSpace = left->units.dimSpace - right->units.dimSpace;
553  	    }
554  	    
555  	    /*
556  	     * for division and multiplication, dimension may have come from
557  	     * right operand, need to pick up scale from there also
558  	     */
559  	    if (np->desc.units.dimCount != 0 && left->units.dimCount == 0)
560  		np->desc.units.scaleCount = right->units.scaleCount;
561  	    if (np->desc.units.dimTime != 0 && left->units.dimTime == 0)
562  		np->desc.units.scaleTime = right->units.scaleTime;
563  	    if (np->desc.units.dimSpace != 0 && left->units.dimSpace == 0)
564  		np->desc.units.scaleSpace = right->units.scaleSpace;
565  	
566  	}
567  	
568  	static int
569  	map_desc(int n, node_t *np)
570  	{
571  	    /*
572  	     * pmDesc mapping for binary operators ...
573  	     *
574  	     * semantics		acceptable operators
575  	     * counter, counter		+ -
576  	     * non-counter, non-counter	+ - * /
577  	     * counter, non-counter	* /
578  	     * non-counter, counter	*
579  	     *
580  	     * in the non-counter and non-counter case, the semantics for the
581  	     * result are PM_SEM_INSTANT, unless both operands are
582  	     * PM_SEM_DISCRETE in which case the result is also PM_SEM_DISCRETE
583  	     *
584  	     * type promotion (similar to ANSI C)
585  	     * PM_TYPE_STRING, PM_TYPE_AGGREGATE, PM_TYPE_AGGREGATE_STATIC
586  	     * and PM_TYPE_EVENT are illegal operands except for renaming
587  	     * (where no operator is involved)
588  	     * for all operands, division => PM_TYPE_DOUBLE
589  	     * else PM_TYPE_DOUBLE & any type => PM_TYPE_DOUBLE
590  	     * else PM_TYPE_FLOAT & any type => PM_TYPE_FLOAT
591  	     * else PM_TYPE_U64 & any type => PM_TYPE_U64
592  	     * else PM_TYPE_64 & any type => PM_TYPE_64
593  	     * else PM_TYPE_U32 & any type => PM_TYPE_U32
594  	     * else PM_TYPE_32 & any type => PM_TYPE_32
595  	     *
596  	     * units mapping
597  	     * operator			checks
598  	     * +, -			same dimension
599  	     * *, /			if only one is a counter, non-counter must
600  	     *				have pmUnits of "none"
601  	     */
602  	    pmDesc	*right = &np->right->desc;
603  	    pmDesc	*left = &np->left->desc;
604  	
605  	    if (left->sem == PM_SEM_COUNTER) {
606  		if (right->sem == PM_SEM_COUNTER) {
607  		    if (np->type != L_PLUS && np->type != L_MINUS) {
608  			errmsg = "Illegal operator for counters";
609  			goto bad;
610  		    }
611  		}
612  		else {
613  		    if (np->type != L_STAR && np->type != L_SLASH) {
614  			errmsg = "Illegal operator for counter and non-counter";
615  			goto bad;
616  		    }
617  		}
618  	    }
619  	    else {
620  		if (right->sem == PM_SEM_COUNTER) {
621  		    if (np->type != L_STAR) {
622  			errmsg = "Illegal operator for non-counter and counter";
623  			goto bad;
624  		    }
625  		}
626  		else {
627  		    if (np->type != L_PLUS && np->type != L_MINUS &&
628  			np->type != L_STAR && np->type != L_SLASH) {
629  			/*
630  			 * this is not possible at the present since only
631  			 * arithmetic operators are supported and all are
632  			 * acceptable here ... check added for completeness
633  			 */
634  			errmsg = "Illegal operator for non-counters";
635  			goto bad;
636  		    }
637  		}
638  	    }
639  	
640  	    /*
641  	     * Choose candidate descriptor ... prefer metric or expression
642  	     * over constant
643  	     */
644  	    if (np->left->type != L_NUMBER)
645  		np->desc = *left;	/* struct copy */
646  	    else
647  		np->desc = *right;	/* struct copy */
648  	
649  	    /*
650  	     * most non-counter expressions produce PM_SEM_INSTANT results
651  	     */
652  	    if (left->sem != PM_SEM_COUNTER && right->sem != PM_SEM_COUNTER) {
653  		if (left->sem == PM_SEM_DISCRETE && right->sem == PM_SEM_DISCRETE)
654  		    np->desc.sem = PM_SEM_DISCRETE;
655  		else
656  		    np->desc.sem = PM_SEM_INSTANT;
657  	    }
658  	
659  	    /*
660  	     * type checking and promotion
661  	     */
662  	    switch (left->type) {
663  		case PM_TYPE_32:
664  		case PM_TYPE_U32:
665  		case PM_TYPE_64:
666  		case PM_TYPE_U64:
667  		case PM_TYPE_FLOAT:
668  		case PM_TYPE_DOUBLE:
669  		    break;
670  		default:
671  		    errmsg = "Non-arithmetic type for left operand";
672  		    goto bad;
673  	    }
674  	    switch (right->type) {
675  		case PM_TYPE_32:
676  		case PM_TYPE_U32:
677  		case PM_TYPE_64:
678  		case PM_TYPE_U64:
679  		case PM_TYPE_FLOAT:
680  		case PM_TYPE_DOUBLE:
681  		    break;
682  		default:
683  		    errmsg = "Non-arithmetic type for right operand";
684  		    goto bad;
685  	    }
686  	    np->desc.type = promote[left->type][right->type];
687  	    if (np->type == L_SLASH) {
688  		/* for division result is real number */
689  		np->desc.type = PM_TYPE_DOUBLE;
690  	    }
691  	
692  	    if (np->type == L_PLUS || np->type == L_MINUS) {
693  		/*
694  		 * unit dimensions have to be identical
695  		 */
696  		if (left->units.dimCount != right->units.dimCount ||
697  		    left->units.dimTime != right->units.dimTime ||
698  		    left->units.dimSpace != right->units.dimSpace) {
699  		    errmsg = "Dimensions are not the same";
700  		    goto bad;
701  		}
702  		map_units(np);
703  	    }
704  	
705  	    if (np->type == L_STAR || np->type == L_SLASH) {
706  		/*
707  		 * if multiply or divide and operands are a counter and a non-counter,
708  		 * then non-counter needs to be dimensionless
709  		 */
710  		if (left->sem == PM_SEM_COUNTER && right->sem != PM_SEM_COUNTER) {
711  		    if (right->units.dimCount != 0 ||
712  		        right->units.dimTime != 0 ||
713  		        right->units.dimSpace != 0) {
714  			errmsg = "Non-counter and not dimensionless for right operand";
715  			goto bad;
716  		    }
717  		}
718  		if (left->sem != PM_SEM_COUNTER && right->sem == PM_SEM_COUNTER) {
719  		    if (left->units.dimCount != 0 ||
720  		        left->units.dimTime != 0 ||
721  		        left->units.dimSpace != 0) {
722  			errmsg = "Non-counter and not dimensionless for left operand";
723  			goto bad;
724  		    }
725  		}
726  		map_units(np);
727  	    }
728  	
729  	    /*
730  	     * if not both singular, then both operands must have the same
731  	     * instance domain
732  	     */
733  	    if (left->indom != PM_INDOM_NULL && right->indom != PM_INDOM_NULL && left->indom != right->indom) {
734  		errmsg = "Operands should have the same instance domain";
735  		goto bad;
736  	    }
737  	
738  	    return 0;
739  	
740  	bad:
741  	    report_sem_error(registered.mlist[n].name, np);
742  	    return -1;
743  	}
744  	
745  	static int
746  	check_expr(int n, node_t *np)
747  	{
748  	    int		sts;
749  	
750  	    assert(np != NULL);
751  	    if (np->type == L_NUMBER || np->type == L_NAME)
752  		return 0;
753  	    if (np->left != NULL)
754  		if ((sts = check_expr(n, np->left)) < 0)
755  		    return sts;
756  	    if (np->right != NULL)
757  		if ((sts = check_expr(n, np->right)) < 0)
758  		    return sts;
759  	    /*
760  	     * np->left is never NULL ...
761  	     */
762  	    if (np->right == NULL) {
763  		np->desc = np->left->desc;	/* struct copy */
764  		/*
765  		 * special cases for functions ...
766  		 * delta	expect numeric operand, result is instantaneous
767  		 * aggr funcs	most expect numeric operand, result is instantaneous
768  		 *		and singular
769  		 */
770  		if (np->type == L_AVG || np->type == L_COUNT || np->type == L_DELTA
771  		    || np->type == L_MAX || np->type == L_MIN || np->type == L_SUM) {
772  		    if (np->type == L_COUNT) {
773  			/* count() has its own type and units */
774  			np->desc.type = PM_TYPE_U32;
775  			memset((void *)&np->desc.units, 0, sizeof(np->desc.units));
776  			np->desc.units.dimCount = 1;
777  			np->desc.units.scaleCount = PM_COUNT_ONE;
778  		    }
779  		    else {
780  			/* others inherit, but need arithmetic operand */
781  			switch (np->left->desc.type) {
782  			    case PM_TYPE_32:
783  			    case PM_TYPE_U32:
784  			    case PM_TYPE_64:
785  			    case PM_TYPE_U64:
786  			    case PM_TYPE_FLOAT:
787  			    case PM_TYPE_DOUBLE:
788  				break;
789  			    default:
790  				errmsg = "Non-arithmetic operand for function";
791  				report_sem_error(registered.mlist[n].name, np);
792  				return -1;
793  			}
794  		    }
795  		    np->desc.sem = PM_SEM_INSTANT;
796  		    if (np->type != L_DELTA)
797  			/* all the others are aggregate funcs with a singular value */
798  			np->desc.indom = PM_INDOM_NULL;
799  		    if (np->type == L_AVG) {
800  			/* avg() returns float result */
801  			np->desc.type = PM_TYPE_FLOAT;
802  		    }
803  		}
804  		else if (np->type == L_ANON) {
805  		    /* do nothing, pmDesc inherited "as is" from left node */
806  		    ;
807  		}
808  	    }
809  	    else {
810  		/* build pmDesc from pmDesc of both operands */
811  		if ((sts = map_desc(n, np)) < 0) {
812  		    return sts;
813  		}
814  	    }
815  	    return 0;
816  	}
817  	
818  	static void
819  	dump_value(int type, pmAtomValue *avp)
820  	{
821  	    switch (type) {
822  		case PM_TYPE_32:
823  		    fprintf(stderr, "%i", avp->l);
824  		    break;
825  	
826  		case PM_TYPE_U32:
827  		    fprintf(stderr, "%u", avp->ul);
828  		    break;
829  	
830  		case PM_TYPE_64:
831  		    fprintf(stderr, "%" PRId64, avp->ll);
832  		    break;
833  	
834  		case PM_TYPE_U64:
835  		    fprintf(stderr, "%" PRIu64, avp->ull);
836  		    break;
837  	
838  		case PM_TYPE_FLOAT:
839  		    fprintf(stderr, "%g", (double)avp->f);
840  		    break;
841  	
842  		case PM_TYPE_DOUBLE:
843  		    fprintf(stderr, "%g", avp->d);
844  		    break;
845  	
846  		case PM_TYPE_STRING:
847  		    fprintf(stderr, "%s", avp->cp);
848  		    break;
849  	
850  		case PM_TYPE_AGGREGATE:
851  		case PM_TYPE_AGGREGATE_STATIC:
852  		case PM_TYPE_EVENT:
853  		case PM_TYPE_UNKNOWN:
854  		    fprintf(stderr, "[blob]");
855  		    break;
856  	
857  		case PM_TYPE_NOSUPPORT:
858  		    fprintf(stderr, "dump_value: bogus value, metric Not Supported\n");
859  		    break;
860  	
861  		default:
862  		    fprintf(stderr, "dump_value: unknown value type=%d\n", type);
863  	    }
864  	}
865  	
866  	void
867  	__dmdumpexpr(node_t *np, int level)
868  	{
869  	    if (level == 0) fprintf(stderr, "Derived metric expr dump from " PRINTF_P_PFX "%p...\n", np);
870  	    if (np == NULL) return;
871  	    fprintf(stderr, "expr node " PRINTF_P_PFX "%p type=%s left=" PRINTF_P_PFX "%p right=" PRINTF_P_PFX "%p save_last=%d", np, type_dbg[np->type+2], np->left, np->right, np->save_last);
872  	    if (np->type == L_NAME || np->type == L_NUMBER)
873  		fprintf(stderr, " [%s] master=%d", np->value, np->info == NULL ? 1 : 0);
874  	    fputc('\n', stderr);
875  	    if (np->info) {
876  		fprintf(stderr, "    PMID: %s", pmIDStr(np->info->pmid));
877  		fprintf(stderr, " (%s from pmDesc) numval: %d", pmIDStr(np->desc.pmid), np->info->numval);
878  		if (np->info->div_scale != 1)
879  		    fprintf(stderr, " div_scale: %d", np->info->div_scale);
880  		if (np->info->mul_scale != 1)
881  		    fprintf(stderr, " mul_scale: %d", np->info->mul_scale);
882  		fputc('\n', stderr);
883  		__pmPrintDesc(stderr, &np->desc);
884  		if (np->info->ivlist) {
885  		    int		j;
886  		    int		max;
887  	
888  		    max = np->info->numval > np->info->last_numval ? np->info->numval : np->info->last_numval;
889  	
890  		    for (j = 0; j < max; j++) {
891  			fprintf(stderr, "[%d]", j);
892  			if (j < np->info->numval) {
893  			    fprintf(stderr, " inst=%d, val=", np->info->ivlist[j].inst);
894  			    dump_value(np->desc.type, &np->info->ivlist[j].value);
895  			}
896  			if (j < np->info->last_numval) {
897  			    fprintf(stderr, " (last inst=%d, val=", np->info->last_ivlist[j].inst);
898  			    dump_value(np->desc.type, &np->info->last_ivlist[j].value);
899  			    fputc(')', stderr);
900  			}
901  			fputc('\n', stderr);
902  		    }
903  		}
904  	    }
905  	    if (np->left != NULL) __dmdumpexpr(np->left, level+1);
906  	    if (np->right != NULL) __dmdumpexpr(np->right, level+1);
907  	}
908  	
909  	/*
910  	 * Parser FSA
911  	 * state	lex		new state
912  	 * P_INIT	L_NAME or	P_LEAF
913  	 * 		L_NUMBER
914  	 * P_INIT	L_<func>	P_FUNC_OP
915  	 * P_INIT	L_LPAREN	if parse() != NULL then P_LEAF
916  	 * P_LEAF	L_PLUS or	P_BINOP
917  	 * 		L_MINUS or
918  	 * 		L_STAR or
919  	 * 		L_SLASH
920  	 * P_BINOP	L_NAME or	P_LEAF
921  	 * 		L_NUMBER
922  	 * P_BINOP	L_LPAREN	if parse() != NULL then P_LEAF
923  	 * P_BINOP	L_<func>	P_FUNC_OP
924  	 * P_LEAF_PAREN	same as P_LEAF, but no precedence rules at next operator
925  	 * P_FUNC_OP	L_NAME		P_FUNC_END
926  	 * P_FUNC_END	L_RPAREN	P_LEAF
927  	 */
928  	static node_t *
929  	parse(int level)
930  	{
931  	    int		state = P_INIT;
932  	    int		type;
933  	    node_t	*expr = NULL;
934  	    node_t	*curr = NULL;
935  	    node_t	*np;
936  	
937  	    for ( ; ; ) {
938  		type = lex();
939  	#ifdef PCP_DEBUG
940  		if ((pmDebug & DBG_TRACE_DERIVE) && (pmDebug & DBG_TRACE_APPL0)) {
941  		    fprintf(stderr, "parse(%d) state=P_%s type=L_%s \"%s\"\n", level, state_dbg[state], type_dbg[type+2], type == L_EOF ? "" : tokbuf);
942  		}
943  	#endif
944  		/* handle lexicons that terminate the parsing */
945  		switch (type) {
946  		    case L_ERROR:
947  			errmsg = "Illegal character";
948  			free_expr(expr);
949  			return NULL;
950  			break;
951  		    case L_EOF:
952  			if (level == 1 && (state == P_LEAF || state == P_LEAF_PAREN))
953  			    return expr;
954  			errmsg = "End of input";
955  			free_expr(expr);
956  			return NULL;
957  			break;
958  		    case L_RPAREN:
959  			if (state == P_FUNC_END) {
960  			    state = P_LEAF;
961  			    continue;
962  			}
963  			if ((level > 1 && state == P_LEAF_PAREN) || state == P_LEAF)
964  			    return expr;
965  			errmsg = "Unexpected ')'";
966  			free_expr(expr);
967  			return NULL;
968  			break;
969  		}
970  	
971  		switch (state) {
972  		    case P_INIT:
973  			if (type == L_NAME || type == L_NUMBER) {
974  			    expr = curr = newnode(type);
975  			    if ((curr->value = strdup(tokbuf)) == NULL) {
976  				__pmNoMem("pmRegisterDerived: leaf node", strlen(tokbuf)+1, PM_FATAL_ERR);
977  				/*NOTREACHED*/
978  			    }
979  			    if (type == L_NUMBER) {
980  				char		*endptr;
981  				__uint64_t	check;
982  				check = strtoull(tokbuf, &endptr, 10);
983  				if (*endptr != '\0' || check > 0xffffffffUL) {
984  				    errmsg = "Constant value too large";
985  				    free_expr(expr);
986  				    return NULL;
987  				}
988  				curr->desc.pmid = PM_ID_NULL;
989  				curr->desc.type = PM_TYPE_U32;
990  				curr->desc.indom = PM_INDOM_NULL;
991  				curr->desc.sem = PM_SEM_DISCRETE;
992  				memset(&curr->desc.units, 0, sizeof(pmUnits));
993  			    }
994  			    state = P_LEAF;
995  			}
996  			else if (type == L_LPAREN) {
997  			    expr = curr = parse(level+1);
998  			    if (expr == NULL)
999  				return NULL;
1000 			    state = P_LEAF_PAREN;
1001 			}
1002 			else if (type == L_AVG || type == L_COUNT || type == L_DELTA
1003 			         || type == L_MAX || type == L_MIN || type == L_SUM 
1004 				 || type == L_ANON) {
1005 			    expr = curr = newnode(type);
1006 			    state = P_FUNC_OP;
1007 			}
1008 			else
1009 			    return NULL;
1010 			break;
1011 	
1012 		    case P_LEAF_PAREN:	/* fall through */
1013 		    case P_LEAF:
1014 			if (type == L_PLUS || type == L_MINUS || type == L_STAR || type == L_SLASH) {
1015 			    np = newnode(type);
1016 			    if (state == P_LEAF_PAREN ||
1017 			        curr->type == L_NAME || curr->type == L_NUMBER ||
1018 				curr->type == L_AVG || curr->type == L_COUNT ||
1019 				curr->type == L_DELTA || curr->type == L_MAX ||
1020 				curr->type == L_MIN || curr->type == L_SUM ||
1021 				curr->type == L_ANON ||
1022 			        type == L_PLUS || type == L_MINUS) {
1023 				/*
1024 				 * first operator or equal or lower precedence
1025 				 * make new root of tree and push previous
1026 				 * expr down left descendent branch
1027 				 */
1028 				np->left = curr;
1029 				expr = curr = np;
1030 			    }
1031 			    else {
1032 				/*
1033 				 * push previous right branch down one level
1034 				 */
1035 				np->left = curr->right;
1036 				curr->right = np;
1037 				curr = np;
1038 			    }
1039 			    state = P_BINOP;
1040 			}
1041 			else {
1042 			    free_expr(expr);
1043 			    return NULL;
1044 			}
1045 			break;
1046 	
1047 		    case P_BINOP:
1048 			if (type == L_NAME || type == L_NUMBER) {
1049 			    np = newnode(type);
1050 			    if ((np->value = strdup(tokbuf)) == NULL) {
1051 				__pmNoMem("pmRegisterDerived: leaf node", strlen(tokbuf)+1, PM_FATAL_ERR);
1052 				/*NOTREACHED*/
1053 			    }
1054 			    if (type == L_NUMBER) {
1055 				np->desc.pmid = PM_ID_NULL;
1056 				np->desc.type = PM_TYPE_U32;
1057 				np->desc.indom = PM_INDOM_NULL;
1058 				np->desc.sem = PM_SEM_DISCRETE;
1059 				memset(&np->desc.units, 0, sizeof(pmUnits));
1060 			    }
1061 			    curr->right = np;
1062 			    curr = expr;
1063 			    state = P_LEAF;
1064 			}
1065 			else if (type == L_LPAREN) {
1066 			    np = parse(level+1);
1067 			    if (np == NULL)
1068 				return NULL;
1069 			    curr->right = np;
1070 			    state = P_LEAF_PAREN;
1071 			}
1072 			else if (type == L_AVG || type == L_COUNT || type == L_DELTA
1073 			         || type == L_MAX || type == L_MIN || type == L_SUM
1074 				 || type == L_ANON) {
1075 			    np = newnode(type);
1076 			    curr->right = np;
1077 			    curr = np;
1078 			    state = P_FUNC_OP;
1079 			}
1080 			else {
1081 			    free_expr(expr);
1082 			    return NULL;
1083 			}
1084 			break;
1085 	
1086 		    case P_FUNC_OP:
1087 			if (type == L_NAME) {
1088 			    np = newnode(type);
1089 			    if ((np->value = strdup(tokbuf)) == NULL) {
1090 				__pmNoMem("pmRegisterDerived: func op node", strlen(tokbuf)+1, PM_FATAL_ERR);
1091 				/*NOTREACHED*/
1092 			    }
1093 			    np->save_last = 1;
1094 			    if (curr->type == L_ANON) {
1095 				/*
1096 				 * anon(PM_TYPE_...) is a special case ... the
1097 				 * argument defines the metric type, the remainder
1098 				 * of the metadata is fixed and there are never any
1099 				 * values available. So we build the pmDesc and then
1100 				 * clobber the "left" node and prevent any attempt to
1101 				 * contact a PMDA for metadata or values
1102 				 */
1103 				if (strcmp(np->value, "PM_TYPE_32") == 0)
1104 				    np->desc.type = PM_TYPE_32;
1105 				else if (strcmp(np->value, "PM_TYPE_U32") == 0)
1106 				    np->desc.type = PM_TYPE_U32;
1107 				else if (strcmp(np->value, "PM_TYPE_64") == 0)
1108 				    np->desc.type = PM_TYPE_64;
1109 				else if (strcmp(np->value, "PM_TYPE_U64") == 0)
1110 				    np->desc.type = PM_TYPE_U64;
1111 				else if (strcmp(np->value, "PM_TYPE_FLOAT") == 0)
1112 				    np->desc.type = PM_TYPE_FLOAT;
1113 				else if (strcmp(np->value, "PM_TYPE_DOUBLE") == 0)
1114 				    np->desc.type = PM_TYPE_DOUBLE;
1115 				else {
1116 				    fprintf(stderr, "Error: type=%s not allowed for anon()\n", np->value);
1117 				    free_expr(np);
1118 				    return NULL;
1119 				}
1120 				np->desc.pmid = PM_ID_NULL;
1121 				np->desc.indom = PM_INDOM_NULL;
1122 				np->desc.sem = PM_SEM_DISCRETE;
1123 				memset((void *)&np->desc.units, 0, sizeof(np->desc.units));
1124 				np->type = L_NUMBER;
1125 			    }
1126 			    curr->left = np;
1127 			    curr = expr;
1128 			    state = P_FUNC_END;
1129 			}
1130 			else {
1131 			    free_expr(expr);
1132 			    return NULL;
1133 			}
1134 			break;
1135 	
1136 		    default:
1137 			free_expr(expr);
1138 			return NULL;
1139 		}
1140 	    }
1141 	}
1142 	
1143 	static int
1144 	checkname(char *p)
1145 	{
1146 	    int	firstch = 1;
1147 	
1148 	    for ( ; *p; p++) {
1149 		if (firstch) {
1150 		    firstch = 0;
1151 		    if (isalpha(*p)) continue;
1152 		    return -1;
1153 		}
1154 		else {
1155 		    if (isalpha(*p) || isdigit(*p) || *p == '_') continue;
1156 		    if (*p == '.') {
1157 			firstch = 1;
1158 			continue;
1159 		    }
1160 		    return -1;
1161 		}
1162 	    }
1163 	    return 0;
1164 	}
1165 	
1166 	char *
1167 	pmRegisterDerived(char *name, char *expr)
1168 	{
1169 	    node_t		*np;
1170 	    static __pmID_int	pmid;
1171 	    int			i;
1172 	
1173 	#ifdef PCP_DEBUG
1174 	    if ((pmDebug & DBG_TRACE_DERIVE) && (pmDebug & DBG_TRACE_APPL0)) {
1175 		fprintf(stderr, "pmRegisterDerived: name=\"%s\" expr=\"%s\"\n", name, expr);
1176 	    }
1177 	#endif
1178 	
1179 	    for (i = 0; i < registered.nmetric; i++) {
1180 		if (strcmp(name, registered.mlist[i].name) == 0) {
1181 		    /* oops, duplicate name ... */
1182 		    errmsg = "Duplicate derived metric name";
1183 		    return expr;
1184 		}
1185 	    }
1186 	
1187 	    errmsg = NULL;
1188 	    string = expr;
1189 	    np = parse(1);
1190 	    if (np == NULL) {
1191 		/* parser error */
1192 		return this;
1193 	    }
1194 	
1195 	    registered.nmetric++;
1196 	    registered.mlist = (dm_t *)realloc(registered.mlist, registered.nmetric*sizeof(dm_t));
1197 	    if (registered.mlist == NULL) {
1198 		__pmNoMem("pmRegisterDerived: registered mlist", registered.nmetric*sizeof(dm_t), PM_FATAL_ERR);
1199 		/*NOTREACHED*/
1200 	    }
1201 	    if (registered.nmetric == 1) {
1202 		pmid.flag = 0;
1203 		pmid.domain = DYNAMIC_PMID;
1204 		pmid.cluster = 0;
1205 	    }
1206 	    registered.mlist[registered.nmetric-1].name = strdup(name);
1207 	    pmid.item = registered.nmetric;
1208 	    registered.mlist[registered.nmetric-1].pmid = *((pmID *)&pmid);
1209 	    registered.mlist[registered.nmetric-1].expr = np;
1210 	
1211 	#ifdef PCP_DEBUG
1212 	    if (pmDebug & DBG_TRACE_DERIVE) {
1213 		fprintf(stderr, "pmRegisterDerived: register metric[%d] %s = %s\n", registered.nmetric-1, name, expr);
1214 		if (pmDebug & DBG_TRACE_APPL0)
1215 		    __dmdumpexpr(np, 0);
1216 	    }
1217 	#endif
1218 	
1219 	    return NULL;
1220 	}
1221 	
1222 	int
1223 	pmLoadDerivedConfig(char *fname)
1224 	{
1225 	    FILE	*fp;
1226 	    int		buflen;
1227 	    char	*buf;
1228 	    char	*p;
1229 	    int		c;
1230 	    int		sts = 0;
1231 	    int		eq = -1;
1232 	    int		lineno = 1;
1233 	
1234 	#ifdef PCP_DEBUG
1235 	    if (pmDebug & DBG_TRACE_DERIVE) {
1236 		fprintf(stderr, "pmLoadDerivedConfig(\"%s\")\n", fname);
1237 	    }
1238 	#endif
1239 	
1240 	    if ((fp = fopen(fname, "r")) == NULL) {
1241 		return -oserror();
1242 	    }
1243 	    buflen = 128;
1244 	    if ((buf = (char *)malloc(buflen)) == NULL) {
1245 		__pmNoMem("pmLoadDerivedConfig: alloc buf", buflen, PM_FATAL_ERR);
1246 		/*NOTREACHED*/
1247 	    }
1248 	    p = buf;
1249 	    while ((c = fgetc(fp)) != EOF) {
1250 		if (p == &buf[buflen]) {
1251 		    if ((buf = (char *)realloc(buf, 2*buflen)) == NULL) {
1252 			__pmNoMem("pmLoadDerivedConfig: expand buf", 2*buflen, PM_FATAL_ERR);
1253 			/*NOTREACHED*/
1254 		    }
1255 		    p = &buf[buflen];
1256 		    buflen *= 2;
1257 		}
1258 		if (c == '=' && eq == -1) {
1259 		    /*
1260 		     * mark first = in line ... metric name to the left and
1261 		     * expression to the right
1262 		     */
1263 		    eq = p - buf;
1264 		}
1265 		if (c == '\n') {
1266 		    if (p == buf || buf[0] == '#') {
1267 			/* comment or empty line, skip it ... */
1268 			goto next_line;
1269 		    }
1270 		    *p = '\0';
1271 		    if (eq != -1) {
1272 			char	*np;	/* copy of name */
1273 			char	*ep;	/* start of expression */
1274 			char	*q;
1275 			char	*errp;
1276 			buf[eq] = '\0';
1277 			if ((np = strdup(buf)) == NULL) {
1278 			    __pmNoMem("pmLoadDerivedConfig: dupname", strlen(buf), PM_FATAL_ERR);
1279 			    /*NOTREACHED*/
1280 			}
1281 			/* trim white space from tail of metric name */
1282 			q = &np[eq-1];
1283 			while (q >= np && isspace(*q))
1284 			    *q-- = '\0';
1285 			/* trim white space from head of metric name */
1286 			q = np;
1287 			while (*q && isspace(*q))
1288 			    q++;
1289 			if (*q == '\0') {
1290 			    buf[eq] = '=';
1291 			    pmprintf("[%s:%d] Error: pmLoadDerivedConfig: derived metric name missing\n%s\n", fname, lineno, buf);
1292 			    pmflush();
1293 			    free(np);
1294 			    goto next_line;
1295 			}
1296 			if (checkname(q) < 0) {
1297 			    pmprintf("[%s:%d] Error: pmLoadDerivedConfig: illegal derived metric name (%s)\n", fname, lineno, q);
1298 			    pmflush();
1299 			    free(np);
1300 			    goto next_line;
1301 			}
1302 			ep = &buf[eq+1];
1303 			while (*ep != '\0' && isspace(*ep))
1304 			    ep++;
1305 			if (*ep == '\0') {
1306 			    buf[eq] = '=';
1307 			    pmprintf("[%s:%d] Error: pmLoadDerivedConfig: expression missing\n%s\n", fname, lineno, buf);
1308 			    pmflush();
1309 			    free(np);
1310 			    goto next_line;
1311 			}
1312 			errp = pmRegisterDerived(q, ep);
1313 			if (errp != NULL) {
1314 			    pmprintf("[%s:%d] Error: pmRegisterDerived(%s, ...) syntax error\n", fname, lineno, q);
1315 			    pmprintf("%s\n", &buf[eq+1]);
1316 			    for (q = &buf[eq+1]; *q; q++) {
1317 				if (q == errp) *q = '^';
1318 				else if (!isspace(*q)) *q = ' ';
1319 			    }
1320 			    pmprintf("%s\n", &buf[eq+1]);
1321 			    q = pmDerivedErrStr();
1322 			    if (q != NULL) pmprintf("%s\n", q);
1323 			    pmflush();
1324 			}
1325 			else
1326 			    sts++;
1327 			free(np);
1328 		    }
1329 		    else {
1330 			/*
1331 			 * error ... no = in the line, so no derived metric name
1332 			 */
1333 			pmprintf("[%s:%d] Error: pmLoadDerivedConfig: missing ``='' after derived metric name\n%s\n", fname, lineno, buf);
1334 			pmflush();
1335 		    }
1336 	next_line:
1337 		    lineno++;
1338 		    p = buf;
1339 		    eq = -1;
1340 		}
1341 		else
1342 		    *p++ = c;
1343 	    }
1344 	    free(buf);
1345 	    return sts;
1346 	}
1347 	
1348 	char *
1349 	pmDerivedErrStr(void)
1350 	{
1351 	    return errmsg;
1352 	}
1353 	
1354 	/*
1355 	 * callbacks
1356 	 */
1357 	
1358 	int
1359 	__dmtraverse(const char *name, char ***namelist)
1360 	{
1361 	    int		sts = 0;
1362 	    int		i;
1363 	    char	**list = NULL;
1364 	    int		matchlen = strlen(name);
1365 	
1366 	    if (need_init) init();
1367 	
1368 	    for (i = 0; i < registered.nmetric; i++) {
1369 		/*
1370 		 * prefix match ... if name is "", then all names match
1371 		 */
1372 		if (matchlen == 0 ||
1373 		    (strncmp(name, registered.mlist[i].name, matchlen) == 0 &&
1374 		     (registered.mlist[i].name[matchlen] == '.' ||
1375 		      registered.mlist[i].name[matchlen] == '\0'))) {
1376 		    sts++;
1377 		    if ((list = (char **)realloc(list, sts*sizeof(list[0]))) == NULL) {
1378 			__pmNoMem("__dmtraverse: list", sts*sizeof(list[0]), PM_FATAL_ERR);
1379 			/*NOTREACHED*/
1380 		    }
1381 		    list[sts-1] = registered.mlist[i].name;
1382 		}
1383 	    }
1384 	    *namelist = list;
1385 	
1386 	    return sts;
1387 	}
1388 	
1389 	int
1390 	__dmchildren(const char *name, char ***offspring, int **statuslist)
1391 	{
1392 	    int		sts = 0;
1393 	    int		i;
1394 	    int		j;
1395 	    char	**children = NULL;
1396 	    int		*status = NULL;
1397 	    int		matchlen = strlen(name);
1398 	    int		start;
1399 	    int		len;
1400 	
1401 	    if (need_init) init();
1402 	
1403 	    for (i = 0; i < registered.nmetric; i++) {
1404 		/*
1405 		 * prefix match ... pick off the unique next level names on match
1406 		 */
1407 		if (name[0] == '\0' ||
1408 		    (strncmp(name, registered.mlist[i].name, matchlen) == 0 &&
1409 		     (registered.mlist[i].name[matchlen] == '.' ||
1410 		      registered.mlist[i].name[matchlen] == '\0'))) {
1411 		    if (registered.mlist[i].name[matchlen] == '\0') {
1412 			/* leaf node */
1413 			return 0;
1414 		    }
1415 		    start = matchlen > 0 ? matchlen + 1 : 0;
1416 		    for (j = 0; j < sts; j++) {
1417 			len = strlen(children[j]);
1418 			if (strncmp(&registered.mlist[i].name[start], children[j], len) == 0 &&
1419 			    registered.mlist[i].name[start+len] == '.')
1420 			    break;
1421 		    }
1422 		    if (j == sts) {
1423 			/* first time for this one */
1424 			sts++;
1425 			if ((children = (char **)realloc(children, sts*sizeof(children[0]))) == NULL) {
1426 			    __pmNoMem("__dmchildren: children", sts*sizeof(children[0]), PM_FATAL_ERR);
1427 			    /*NOTREACHED*/
1428 			}
1429 			for (len = 0; registered.mlist[i].name[start+len] != '\0' && registered.mlist[i].name[start+len] != '.'; len++)
1430 			    ;
1431 			if ((children[sts-1] = (char *)malloc(len+1)) == NULL) {
1432 			    __pmNoMem("__dmchildren: name", len+1, PM_FATAL_ERR);
1433 			    /*NOTREACHED*/
1434 			}
1435 			strncpy(children[sts-1], &registered.mlist[i].name[start], len);
1436 			children[sts-1][len] = '\0';
1437 	#ifdef PCP_DEBUG
1438 			if ((pmDebug & DBG_TRACE_DERIVE) && (pmDebug & DBG_TRACE_APPL1)) {
1439 			    fprintf(stderr, "__dmchildren: offspring[%d] %s", sts-1, children[sts-1]);
1440 			}
1441 	#endif
1442 	
1443 			if (statuslist != NULL) {
1444 			    if ((status = (int *)realloc(status, sts*sizeof(status[0]))) == NULL) {
1445 				__pmNoMem("__dmchildren: statrus", sts*sizeof(status[0]), PM_FATAL_ERR);
1446 				/*NOTREACHED*/
1447 			    }
1448 			    status[sts-1] = registered.mlist[i].name[start+len] == '\0' ? PMNS_LEAF_STATUS : PMNS_NONLEAF_STATUS;
1449 	#ifdef PCP_DEBUG
1450 			    if ((pmDebug & DBG_TRACE_DERIVE) && (pmDebug & DBG_TRACE_APPL1)) {
1451 				fprintf(stderr, " (status=%d)", status[sts-1]);
1452 			}
1453 	#endif
1454 			}
1455 	#ifdef PCP_DEBUG
1456 			if ((pmDebug & DBG_TRACE_DERIVE) && (pmDebug & DBG_TRACE_APPL1)) {
1457 			    fputc('\n', stderr);
1458 			}
1459 	#endif
1460 		    }
1461 		}
1462 	    }
1463 	
1464 	    if (sts == 0)
1465 		return PM_ERR_NAME;
1466 	
1467 	    *offspring = children;
1468 	    if (statuslist != NULL)
1469 		*statuslist = status;
1470 	
1471 	    return sts;
1472 	}
1473 	
1474 	int
1475 	__dmgetpmid(const char *name, pmID *dp)
1476 	{
1477 	    int		i;
1478 	
1479 	    if (need_init) init();
1480 	
1481 	    for (i = 0; i < registered.nmetric; i++) {
1482 		if (strcmp(name, registered.mlist[i].name) == 0) {
1483 		    *dp = registered.mlist[i].pmid;
1484 		    return 0;
1485 		}
1486 	    }
1487 	    return PM_ERR_NAME;
1488 	}
1489 	
1490 	int
1491 	__dmgetname(pmID pmid, char ** name)
1492 	{
1493 	    int		i;
1494 	
1495 	    if (need_init) init();
1496 	
1497 	    for (i = 0; i < registered.nmetric; i++) {
1498 		if (pmid == registered.mlist[i].pmid) {
1499 		    *name = strdup(registered.mlist[i].name);
1500 		    if (*name == NULL)
1501 			return -oserror();
1502 		    else
1503 			return 0;
1504 		}
1505 	    }
1506 	    return PM_ERR_PMID;
1507 	}
1508 	
1509 	void
1510 	__dmopencontext(__pmContext *ctxp)
1511 	{
1512 	    int		i;
1513 	    int		sts;
1514 	    ctl_t	*cp;
1515 	
1516 	    if (need_init) init();
1517 	
1518 	#ifdef PCP_DEBUG
1519 	    if ((pmDebug & DBG_TRACE_DERIVE) && (pmDebug & DBG_TRACE_APPL1)) {
1520 		fprintf(stderr, "__dmopencontext(->ctx %d) called\n", __pmPtrToHandle(ctxp));
1521 	    }
1522 	#endif
1523 	    if (registered.nmetric == 0) {
1524 		ctxp->c_dm = NULL;
1525 		return;
1526 	    }
1527 	    if ((cp = (void *)malloc(sizeof(ctl_t))) == NULL) {
1528 		__pmNoMem("pmNewContext: derived metrics (ctl)", sizeof(ctl_t), PM_FATAL_ERR);
1529 		/* NOTREACHED */
1530 	    }
1531 	    ctxp->c_dm = (void *)cp;
1532 	    cp->nmetric = registered.nmetric;
1533 	    if ((cp->mlist = (dm_t *)malloc(cp->nmetric*sizeof(dm_t))) == NULL) {
1534 		__pmNoMem("pmNewContext: derived metrics (mlist)", cp->nmetric*sizeof(dm_t), PM_FATAL_ERR);
1535 		/* NOTREACHED */
1536 	    }
1537 	    for (i = 0; i < cp->nmetric; i++) {
1538 		cp->mlist[i].name = registered.mlist[i].name;
1539 		cp->mlist[i].pmid = registered.mlist[i].pmid;
1540 		assert(registered.mlist[i].expr != NULL);
1541 		/* failures must be reported in bind_expr() or below */
1542 		cp->mlist[i].expr = bind_expr(i, registered.mlist[i].expr);
1543 		if (cp->mlist[i].expr != NULL) {
1544 		    /* failures must be reported in check_expr() or below */
1545 		    sts = check_expr(i, cp->mlist[i].expr);
1546 		    if (sts < 0) {
1547 			free_expr(cp->mlist[i].expr);
1548 			cp->mlist[i].expr = NULL;
1549 		    }
1550 		    else {
1551 			/* set correct PMID in pmDesc at the top level */
1552 			cp->mlist[i].expr->desc.pmid = cp->mlist[i].pmid;
1553 		    }
1554 		}
1555 	#ifdef PCP_DEBUG
1556 		if ((pmDebug & DBG_TRACE_DERIVE) && cp->mlist[i].expr != NULL) {
1557 		    fprintf(stderr, "__dmopencontext: bind metric[%d] %s\n", i, registered.mlist[i].name);
1558 		    if (pmDebug & DBG_TRACE_APPL1)
1559 			__dmdumpexpr(cp->mlist[i].expr, 0);
1560 		}
1561 	#endif
1562 	    }
1563 	}
1564 	
1565 	void
1566 	__dmclosecontext(__pmContext *ctxp)
1567 	{
1568 	    int		i;
1569 	    ctl_t	*cp = (ctl_t *)ctxp->c_dm;
1570 	
1571 	    /* if needed, init() called in __dmopencontext beforehand */
1572 	
1573 	#ifdef PCP_DEBUG
1574 	    if (pmDebug & DBG_TRACE_DERIVE) {
1575 		fprintf(stderr, "__dmclosecontext(->ctx %d) called dm->" PRINTF_P_PFX "%p %d metrics\n", __pmPtrToHandle(ctxp), cp, cp == NULL ? -1 : cp->nmetric);
1576 	    }
1577 	#endif
1578 	    if (cp == NULL) return;
1579 	    for (i = 0; i < cp->nmetric; i++) {
1580 		free_expr(cp->mlist[i].expr); 
1581 	    }
1582 	    free(cp->mlist);
1583 	    free(cp);
1584 	    ctxp->c_dm = NULL;
1585 	}
1586 	
1587 	int
1588 	__dmdesc(__pmContext *ctxp, pmID pmid, pmDesc *desc)
1589 	{
1590 	    int		i;
1591 	    ctl_t	*cp = (ctl_t *)ctxp->c_dm;
1592 	
1593 	    /* if needed, init() called in __dmopencontext beforehand */
1594 	
1595 	    if (cp == NULL) return PM_ERR_PMID;
1596 	
1597 	    for (i = 0; i < cp->nmetric; i++) {
1598 		if (cp->mlist[i].pmid == pmid) {
1599 		    if (cp->mlist[i].expr == NULL)
1600 			/* bind failed for some reason, reported earlier */
1601 			return PM_ERR_NAME;
1602 		    *desc = cp->mlist[i].expr->desc;
1603 		    return 0;
1604 		}
1605 	    }
1606 	    return PM_ERR_PMID;
1607 	}