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 }
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;
|
Event alloc_fn: |
Storage is returned from allocation function "malloc". |
|
Event var_assign: |
Assigning: "np" = "malloc(sizeof (node_t) /*64*/)". |
| Also see events: |
[return_alloc] |
289 np = (node_t *)malloc(sizeof(node_t));
|
At conditional (1): "np == NULL": Taking false branch.
|
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;
|
Event return_alloc: |
Returning allocated memory "np". |
| Also see events: |
[alloc_fn][var_assign] |
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
|
At conditional (3): "true": Taking true branch.
|
|
At conditional (12): "true": Taking true branch.
|
937 for ( ; ; ) {
938 type = lex();
939 #ifdef PCP_DEBUG
|
At conditional (4): "pmDebug & 0x40000": Taking true branch.
|
|
At conditional (5): "pmDebug & 0x800": Taking false branch.
|
|
At conditional (13): "pmDebug & 0x40000": Taking true branch.
|
|
At conditional (14): "pmDebug & 0x800": Taking false branch.
|
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 */
|
At conditional (6): {CovLStr{v1}{switch default}}: Taking true branch.
|
|
At conditional (15): {CovLStr{v1}{switch default}}: Taking true branch.
|
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) {
|
At conditional (16): {CovLStr{v1}{switch case value {0}}{"0"}}: Taking true branch.
|
972 case P_INIT:
|
At conditional (17): "type == 2": Taking false branch.
|
|
At conditional (18): "type == 1": Taking false branch.
|
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 }
|
At conditional (19): "type == 7": Taking false branch.
|
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 }
|
At conditional (20): "type == 9": Taking false branch.
|
|
At conditional (21): "type == 10": Taking false branch.
|
|
At conditional (22): "type == 11": Taking false branch.
|
|
At conditional (23): "type == 12": Taking false branch.
|
|
At conditional (24): "type == 13": Taking false branch.
|
|
At conditional (25): "type == 14": Taking false branch.
|
|
At conditional (26): "type == 15": Taking false branch.
|
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);
|
At conditional (1): "state == 2": Taking false branch.
|
|
At conditional (2): "curr->type == 2": Taking true branch.
|
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
|
At conditional (7): {CovLStr{v1}{switch case value {0}}{"3"}}: Taking true branch.
|
1047 case P_BINOP:
|
At conditional (8): "type == 2": Taking false branch.
|
|
At conditional (9): "type == 1": Taking false branch.
|
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 }
|
At conditional (10): "type == 7": Taking true branch.
|
1065 else if (type == L_LPAREN) {
1066 np = parse(level+1);
|
At conditional (11): "np == NULL": Taking false branch.
|
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(®istered.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], ®istered.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 }