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 and semantic analysis
18 * DERIVE & APPL2 - fetch handling
19 */
20
21 #include <inttypes.h>
22 #include "derive.h"
23
24 static void
25 get_pmids(node_t *np, int *cnt, pmID **list)
26 {
27 assert(np != NULL);
28 if (np->left != NULL) get_pmids(np->left, cnt, list);
29 if (np->right != NULL) get_pmids(np->right, cnt, list);
30 if (np->type == L_NAME) {
31 (*cnt)++;
32 if ((*list = (pmID *)realloc(*list, (*cnt)*sizeof(pmID))) == NULL) {
33 __pmNoMem("__dmprefetch: realloc xtralist", (*cnt)*sizeof(pmID), PM_FATAL_ERR);
34 /*NOTREACHED*/
35 }
36 (*list)[*cnt-1] = np->info->pmid;
37 }
38 }
39
40 /*
41 * Walk the pmidlist[] from pmFetch.
42 * For each derived metric found in the list add all the operand metrics,
43 * and build a combined pmID list (newlist).
44 *
45 * Return 0 if no derived metrics in the list, else the number of pmIDs
46 * in the combined list.
47 *
48 * The derived metric pmIDs are left in the combined list (they will
49 * return PM_ERR_NOAGENT from the fetch) to simplify the post-processing
50 * of the pmResult in __dmpostfetch()
51 */
52 int
53 __dmprefetch(__pmContext *ctxp, int numpmid, pmID *pmidlist, pmID **newlist)
54 {
55 int i;
56 int j;
57 int m;
58 int xtracnt = 0;
59 pmID *xtralist = NULL;
60 pmID *list;
61 ctl_t *cp = (ctl_t *)ctxp->c_dm;
62
63 /* if needed, init() called in __dmopencontext beforehand */
64
65 if (cp == NULL) return 0;
66
67 /*
68 * save numpmid to be used in __dmpostfetch() ... works because calls
69 * to pmFetch cannot be nested (at all, but certainly for the same
70 * context).
71 * Ditto for the fast path flag (fetch_has_dm).
72 */
73 cp->numpmid = numpmid;
74 cp->fetch_has_dm = 0;
75
76 for (m = 0; m < numpmid; m++) {
77 if (pmid_domain(pmidlist[m]) != DYNAMIC_PMID ||
78 pmid_item(pmidlist[m]) == 0)
79 continue;
80 for (i = 0; i < cp->nmetric; i++) {
81 if (pmidlist[m] == cp->mlist[i].pmid) {
82 if (cp->mlist[i].expr != NULL) {
83 get_pmids(cp->mlist[i].expr, &xtracnt, &xtralist);
84 cp->fetch_has_dm = 1;
85 }
86 break;
87 }
88 }
89 }
90 if (xtracnt == 0) {
91 if (cp->fetch_has_dm)
92 return numpmid;
93 else
94 return 0;
95 }
96
97 /*
98 * Some of the "extra" ones, may already be in the caller's pmFetch
99 * list, or repeated in xtralist[] (if the same metric operand appears
100 * more than once as a leaf node in the expression tree.
101 * Remove these duplicates
102 */
103 j = 0;
104 for (i = 0; i < xtracnt; i++) {
105 for (m = 0; m < numpmid; m++) {
106 if (xtralist[i] == pmidlist[m])
107 /* already in pmFetch list */
108 break;
109 }
110 if (m < numpmid) continue;
111 for (m = 0; m < j; m++) {
112 if (xtralist[i] == xtralist[m])
113 /* already in xtralist[] */
114 break;
115 }
116 if (m == j)
117 xtralist[j++] = xtralist[i];
118 }
119 xtracnt = j;
120 if (xtracnt == 0) {
121 free(xtralist);
122 return numpmid;
123 }
124
125 #ifdef PCP_DEBUG
126 if ((pmDebug & DBG_TRACE_DERIVE) && (pmDebug & DBG_TRACE_APPL2)) {
127 fprintf(stderr, "derived metrics prefetch added %d metrics:", xtracnt);
128 for (i = 0; i < xtracnt; i++)
129 fprintf(stderr, " %s", pmIDStr(xtralist[i]));
130 fputc('\n', stderr);
131 }
132 #endif
133 if ((list = (pmID *)malloc((numpmid+xtracnt)*sizeof(pmID))) == NULL) {
134 __pmNoMem("__dmprefetch: alloc list", (numpmid+xtracnt)*sizeof(pmID), PM_FATAL_ERR);
135 /*NOTREACHED*/
136 }
137 for (m = 0; m < numpmid; m++) {
138 list[m] = pmidlist[m];
139 }
140 for (i = 0; i < xtracnt; i++) {
141 list[m++] = xtralist[i];
142 }
143 free(xtralist);
144 *newlist = list;
145
146 return m;
147 }
148
149 /*
150 * Free the old ivlist[] (if any) ... may need to walk the list because
151 * the pmAtomValues may have buffers attached in the type STRING,
152 * type AGGREGATE* and type EVENT cases.
153 * Includes logic to save one history sample (for delta()).
154 */
155 static void
156 free_ivlist(node_t *np)
157 {
158 int i;
159
160 assert(np->info != NULL);
161
162 if (np->save_last) {
163 if (np->info->last_ivlist != NULL) {
164 /*
165 * no STRING, AGGREGATE or EVENT types for delta(),
166 * so simple free()
167 */
168 free(np->info->last_ivlist);
169 }
170 np->info->last_numval = np->info->numval;
171 np->info->last_ivlist = np->info->ivlist;
172 }
173 else {
174 /* no history */
175 if (np->info->ivlist != NULL) {
176 if (np->desc.type == PM_TYPE_STRING) {
177 for (i = 0; i < np->info->numval; i++) {
178 if (np->info->ivlist[i].value.cp != NULL)
179 free(np->info->ivlist[i].value.cp);
180 }
181 }
182 else if (np->desc.type == PM_TYPE_AGGREGATE ||
183 np->desc.type == PM_TYPE_AGGREGATE_STATIC ||
184 np->desc.type == PM_TYPE_EVENT) {
185 for (i = 0; i < np->info->numval; i++) {
186 if (np->info->ivlist[i].value.vbp != NULL)
187 free(np->info->ivlist[i].value.vbp);
188 }
189 }
190 }
191 free(np->info->ivlist);
192 np->info->ivlist = NULL;
193 np->info->numval = 0;
194 }
195 }
196
197 /*
198 * Binary arithmetic.
199 *
200 * result = <a> <op> <b>
201 * ltype, rtype and type are the types of <a>, <b> and the result
202 * respectively
203 *
204 * If type is PM_TYPE_DOUBLE then lmul, ldiv, rmul and rdiv are
205 * the scale factors for units scale conversion of <a> and <b>
206 * respectively, so lmul*<a>/ldiv ... all are 1 in the common cases.
207 */
208 static pmAtomValue
209 bin_op(int type, int op, pmAtomValue a, int ltype, int lmul, int ldiv, pmAtomValue b, int rtype, int rmul, int rdiv)
210 {
211 static pmAtomValue res;
212 pmAtomValue l;
213 pmAtomValue r;
214
215 l = a; /* struct assignments */
216 r = b;
217
218 /*
219 * Promote each operand to the type of the result ... there are limited
220 * cases to be considered here, see promote[][] and map_desc().
221 */
222 switch (type) {
223 case PM_TYPE_32:
224 case PM_TYPE_U32:
225 /* do nothing */
226 break;
227 case PM_TYPE_64:
228 switch (ltype) {
229 case PM_TYPE_32:
230 l.ll = a.l;
231 break;
232 case PM_TYPE_U32:
233 l.ll = a.ul;
234 break;
235 case PM_TYPE_64:
236 case PM_TYPE_U64:
237 /* do nothing */
238 break;
239 }
240 switch (rtype) {
241 case PM_TYPE_32:
242 r.ll = b.l;
243 break;
244 case PM_TYPE_U32:
245 r.ll = b.ul;
246 break;
247 case PM_TYPE_64:
248 case PM_TYPE_U64:
249 /* do nothing */
250 break;
251 }
252 break;
253 case PM_TYPE_U64:
254 switch (ltype) {
255 case PM_TYPE_32:
256 l.ull = a.l;
257 break;
258 case PM_TYPE_U32:
259 l.ull = a.ul;
260 break;
261 case PM_TYPE_64:
262 case PM_TYPE_U64:
263 /* do nothing */
264 break;
265 }
266 switch (rtype) {
267 case PM_TYPE_32:
268 r.ull = b.l;
269 break;
270 case PM_TYPE_U32:
271 r.ull = b.ul;
272 break;
273 case PM_TYPE_64:
274 case PM_TYPE_U64:
275 /* do nothing */
276 break;
277 }
278 break;
279 case PM_TYPE_FLOAT:
280 switch (ltype) {
281 case PM_TYPE_32:
282 l.f = a.l;
283 break;
284 case PM_TYPE_U32:
285 l.f = a.ul;
286 break;
287 case PM_TYPE_64:
288 l.f = a.ll;
289 break;
290 case PM_TYPE_U64:
291 l.f = a.ull;
292 break;
293 case PM_TYPE_FLOAT:
294 /* do nothing */
295 break;
296 }
297 switch (rtype) {
298 case PM_TYPE_32:
299 r.f = b.l;
300 break;
301 case PM_TYPE_U32:
302 r.f = b.ul;
303 break;
304 case PM_TYPE_64:
305 r.f = b.ll;
306 break;
307 case PM_TYPE_U64:
308 r.f = b.ull;
309 break;
310 case PM_TYPE_FLOAT:
311 /* do nothing */
312 break;
313 }
314 break;
315 case PM_TYPE_DOUBLE:
316 switch (ltype) {
317 case PM_TYPE_32:
318 l.d = a.l;
319 break;
320 case PM_TYPE_U32:
321 l.d = a.ul;
322 break;
323 case PM_TYPE_64:
324 l.d = a.ll;
325 break;
326 case PM_TYPE_U64:
327 l.d = a.ull;
328 break;
329 case PM_TYPE_FLOAT:
330 l.d = a.f;
331 break;
332 case PM_TYPE_DOUBLE:
333 /* do nothing */
334 break;
335 }
336 l.d = (l.d / ldiv) * lmul;
337 switch (rtype) {
338 case PM_TYPE_32:
339 r.d = b.l;
340 break;
341 case PM_TYPE_U32:
342 r.d = b.ul;
343 break;
344 case PM_TYPE_64:
345 r.d = b.ll;
346 break;
347 case PM_TYPE_U64:
348 r.d = b.ull;
349 break;
350 case PM_TYPE_FLOAT:
351 r.d = b.f;
352 break;
353 case PM_TYPE_DOUBLE:
354 /* do nothing */
355 break;
356 }
357 r.d = (r.d / rdiv) * rmul;
358 break;
359 }
360
361 /*
362 * Do the aritmetic ... messy!
363 */
364 switch (type) {
365 case PM_TYPE_32:
366 switch (op) {
367 case L_PLUS:
368 res.l = l.l + r.l;
369 break;
370 case L_MINUS:
371 res.l = l.l - r.l;
372 break;
373 case L_STAR:
374 res.l = l.l * r.l;
375 break;
376 /* semantics enforce no L_SLASH for integer results */
377 }
378 break;
379 case PM_TYPE_U32:
380 switch (op) {
381 case L_PLUS:
382 res.ul = l.ul + r.ul;
383 break;
384 case L_MINUS:
385 res.ul = l.ul - r.ul;
386 break;
387 case L_STAR:
388 res.ul = l.ul * r.ul;
389 break;
390 /* semantics enforce no L_SLASH for integer results */
391 }
392 break;
393 case PM_TYPE_64:
394 switch (op) {
395 case L_PLUS:
396 res.ll = l.ll + r.ll;
397 break;
398 case L_MINUS:
399 res.ll = l.ll - r.ll;
400 break;
401 case L_STAR:
402 res.ll = l.ll * r.ll;
403 break;
404 /* semantics enforce no L_SLASH for integer results */
405 }
406 break;
407 case PM_TYPE_U64:
408 switch (op) {
409 case L_PLUS:
410 res.ull = l.ull + r.ull;
411 break;
412 case L_MINUS:
413 res.ull = l.ull - r.ull;
414 break;
415 case L_STAR:
416 res.ull = l.ull * r.ull;
417 break;
418 /* semantics enforce no L_SLASH for integer results */
419 }
420 break;
421 case PM_TYPE_FLOAT:
422 switch (op) {
423 case L_PLUS:
424 res.f = l.f + r.f;
425 break;
426 case L_MINUS:
427 res.f = l.f - r.f;
428 break;
429 case L_STAR:
430 res.f = l.f * r.f;
431 break;
432 /* semantics enforce no L_SLASH for float results */
433 }
434 break;
435 case PM_TYPE_DOUBLE:
436 switch (op) {
437 case L_PLUS:
438 res.d = l.d + r.d;
439 break;
440 case L_MINUS:
441 res.d = l.d - r.d;
442 break;
443 case L_STAR:
444 res.d = l.d * r.d;
445 break;
446 case L_SLASH:
447 if (l.d == 0)
448 res.d = 0;
449 else
450 res.d = l.d / r.d;
451 break;
452 }
453 break;
454 }
455
456 return res;
457 }
458
459
460 /*
461 * Walk an expression tree, filling in operand values from the
462 * pmResult at the leaf nodes and propagating the computed values
463 * towards the root node of the tree.
464 */
465 static int
466 eval_expr(node_t *np, pmResult *rp, int level)
467 {
468 int sts;
469 int i;
470 int j;
471 int k;
472 size_t need;
473
474 assert(np != NULL);
|
Event var_compare_op: |
Comparing "np->left" to null implies that "np->left" might be null. |
| Also see events: |
[var_deref_op][var_deref_op] |
|
At conditional (1): "np->left != NULL": Taking false branch.
|
475 if (np->left != NULL) {
476 sts = eval_expr(np->left, rp, level+1);
477 if (sts < 0) return sts;
478 }
|
At conditional (2): "np->right != NULL": Taking true branch.
|
479 if (np->right != NULL) {
480 sts = eval_expr(np->right, rp, level+1);
|
At conditional (3): "sts < 0": Taking false branch.
|
481 if (sts < 0) return sts;
482 }
483
484 switch (np->type) {
485
486 case L_NUMBER:
487 if (np->info->numval == 0) {
488 /* initialize ivlist[] for singular instance first time through */
489 np->info->numval = 1;
490 if ((np->info->ivlist = (val_t *)malloc(sizeof(val_t))) == NULL) {
491 __pmNoMem("eval_expr: number ivlist", sizeof(val_t), PM_FATAL_ERR);
492 /*NOTREACHED*/
493 }
494 np->info->ivlist[0].inst = PM_INDOM_NULL;
495 /* don't need error checking, done in the lexical scanner */
496 np->info->ivlist[0].value.l = atoi(np->value);
497 }
498 return 1;
499 break;
500
|
At conditional (4): {CovLStr{v1}{switch case value {0}}{"11"}}: Taking true branch.
|
501 case L_DELTA:
502 /*
503 * this and the last values are in the left expr
504 */
505 free_ivlist(np);
506 np->info->numval = np->left->info->numval <= np->left->info->last_numval ? np->left->info->numval : np->left->info->last_numval;
507 if (np->info->numval <= 0)
508 return np->info->numval;
509 if ((np->info->ivlist = (val_t *)malloc(np->info->numval*sizeof(val_t))) == NULL) {
510 __pmNoMem("eval_expr: delta() ivlist", np->info->numval*sizeof(val_t), PM_FATAL_ERR);
511 /*NOTREACHED*/
512 }
513 /*
514 * ivlist[k] = left-ivlist[i] - left-last-ivlist[j]
515 */
516 for (i = k = 0; i < np->left->info->numval; i++) {
517 j = i;
518 if (j >= np->left->info->last_numval)
519 j = 0;
520 if (np->left->info->ivlist[i].inst != np->left->info->last_ivlist[j].inst) {
521 /* current ith inst != last jth inst ... search in last */
522 #ifdef PCP_DEBUG
523 if ((pmDebug & DBG_TRACE_DERIVE) && (pmDebug & DBG_TRACE_APPL2)) {
524 fprintf(stderr, "eval_expr: inst[%d] mismatch left [%d]=%d last [%d]=%d\n", k, i, np->left->info->ivlist[i].inst, j, np->left->info->last_ivlist[j].inst);
525 }
526 #endif
527 for (j = 0; j < np->left->info->last_numval; j++) {
528 if (np->left->info->ivlist[i].inst == np->left->info->last_ivlist[j].inst)
529 break;
530 }
531 if (j == np->left->info->last_numval) {
532 /* no match, skip this instance from this result */
533 continue;
534 }
535 #ifdef PCP_DEBUG
536 else {
537 if ((pmDebug & DBG_TRACE_DERIVE) && (pmDebug & DBG_TRACE_APPL2)) {
538 fprintf(stderr, "eval_expr: recover @ last [%d]=%d\n", j, np->left->info->last_ivlist[j].inst);
539 }
540 }
541 #endif
542 }
543 np->info->ivlist[k].inst = np->left->info->ivlist[i].inst;
544 switch (np->desc.type) {
545 case PM_TYPE_32:
546 np->info->ivlist[k].value.l = np->left->info->ivlist[i].value.l - np->left->info->last_ivlist[j].value.l;
547 break;
548 case PM_TYPE_U32:
549 np->info->ivlist[k].value.ul = np->left->info->ivlist[i].value.ul - np->left->info->last_ivlist[j].value.ul;
550 break;
551 case PM_TYPE_64:
552 np->info->ivlist[k].value.ll = np->left->info->ivlist[i].value.ll - np->left->info->last_ivlist[j].value.ll;
553 break;
554 case PM_TYPE_U64:
555 np->info->ivlist[k].value.ull = np->left->info->ivlist[i].value.ull - np->left->info->last_ivlist[j].value.ull;
556 break;
557 case PM_TYPE_FLOAT:
558 np->info->ivlist[k].value.f = np->left->info->ivlist[i].value.f - np->left->info->last_ivlist[j].value.f;
559 break;
560 case PM_TYPE_DOUBLE:
561 np->info->ivlist[k].value.d = np->left->info->ivlist[i].value.d - np->left->info->last_ivlist[j].value.d;
562 break;
563 default:
564 /*
565 * Nothing should end up here as check_expr() checks
566 * for numeric data type at bind time
567 */
568 return PM_ERR_CONV;
569 }
570 k++;
571 }
572 np->info->numval = k;
573 return np->info->numval;
574 break;
575
576 case L_AVG:
577 case L_COUNT:
578 case L_SUM:
579 case L_MAX:
580 case L_MIN:
581 if (np->info->ivlist == NULL) {
582 /* initialize ivlist[] for singular instance first time through */
583 if ((np->info->ivlist = (val_t *)malloc(sizeof(val_t))) == NULL) {
584 __pmNoMem("eval_expr: aggr ivlist", sizeof(val_t), PM_FATAL_ERR);
585 /*NOTREACHED*/
586 }
587 np->info->ivlist[0].inst = PM_IN_NULL;
588 }
589 /*
590 * values are in the left expr
591 */
592 if (np->type == L_COUNT) {
593 np->info->numval = 1;
594 np->info->ivlist[0].value.l = np->left->info->numval;
595 }
596 else {
597 np->info->numval = 1;
598 if (np->type == L_AVG)
599 np->info->ivlist[0].value.f = 0;
600 else if (np->type == L_SUM) {
601 switch (np->desc.type) {
602 case PM_TYPE_32:
603 np->info->ivlist[0].value.l = 0;
604 break;
605 case PM_TYPE_U32:
606 np->info->ivlist[0].value.ul = 0;
607 break;
608 case PM_TYPE_64:
609 np->info->ivlist[0].value.ll = 0;
610 break;
611 case PM_TYPE_U64:
612 np->info->ivlist[0].value.ull = 0;
613 break;
614 case PM_TYPE_FLOAT:
615 np->info->ivlist[0].value.f = 0;
616 break;
617 case PM_TYPE_DOUBLE:
618 np->info->ivlist[0].value.d = 0;
619 break;
620 }
621 }
622 for (i = 0; i < np->left->info->numval; i++) {
623 switch (np->type) {
624
625 case L_AVG:
626 switch (np->left->desc.type) {
627 case PM_TYPE_32:
628 np->info->ivlist[0].value.f += (float)np->left->info->ivlist[i].value.l / np->left->info->numval;
629 break;
630 case PM_TYPE_U32:
631 np->info->ivlist[0].value.f += (float)np->left->info->ivlist[i].value.ul / np->left->info->numval;
632 break;
633 case PM_TYPE_64:
634 np->info->ivlist[0].value.f += (float)np->left->info->ivlist[i].value.ll / np->left->info->numval;
635 break;
636 case PM_TYPE_U64:
637 np->info->ivlist[0].value.f += (float)np->left->info->ivlist[i].value.ull / np->left->info->numval;
638 break;
639 case PM_TYPE_FLOAT:
640 np->info->ivlist[0].value.f += (float)np->left->info->ivlist[i].value.f / np->left->info->numval;
641 break;
642 case PM_TYPE_DOUBLE:
643 np->info->ivlist[0].value.f += (float)np->left->info->ivlist[i].value.d / np->left->info->numval;
644 break;
645 default:
646 /*
647 * check_expr() checks for numeric data
648 * type at bind time ... if here, botch!
649 */
650 return PM_ERR_CONV;
651 }
652 break;
653
654 case L_MAX:
655 switch (np->desc.type) {
656 case PM_TYPE_32:
657 if (i == 0 ||
658 np->info->ivlist[0].value.l < np->left->info->ivlist[i].value.l)
659 np->info->ivlist[0].value.l = np->left->info->ivlist[i].value.l;
660 break;
661 case PM_TYPE_U32:
662 if (i == 0 ||
663 np->info->ivlist[0].value.ul < np->left->info->ivlist[i].value.ul)
664 np->info->ivlist[0].value.ul = np->left->info->ivlist[i].value.ul;
665 break;
666 case PM_TYPE_64:
667 if (i == 0 ||
668 np->info->ivlist[0].value.ll < np->left->info->ivlist[i].value.ll)
669 np->info->ivlist[0].value.ll = np->left->info->ivlist[i].value.ll;
670 break;
671 case PM_TYPE_U64:
672 if (i == 0 ||
673 np->info->ivlist[0].value.ull < np->left->info->ivlist[i].value.ull)
674 np->info->ivlist[0].value.ull = np->left->info->ivlist[i].value.ull;
675 break;
676 case PM_TYPE_FLOAT:
677 if (i == 0 ||
678 np->info->ivlist[0].value.f < np->left->info->ivlist[i].value.f)
679 np->info->ivlist[0].value.f = np->left->info->ivlist[i].value.f;
680 break;
681 case PM_TYPE_DOUBLE:
682 if (i == 0 ||
683 np->info->ivlist[0].value.d < np->left->info->ivlist[i].value.d)
684 np->info->ivlist[0].value.d = np->left->info->ivlist[i].value.d;
685 break;
686 default:
687 /*
688 * check_expr() checks for numeric data
689 * type at bind time ... if here, botch!
690 */
691 return PM_ERR_CONV;
692 }
693 break;
694
695 case L_MIN:
696 switch (np->desc.type) {
697 case PM_TYPE_32:
698 if (i == 0 ||
699 np->info->ivlist[0].value.l > np->left->info->ivlist[i].value.l)
700 np->info->ivlist[0].value.l = np->left->info->ivlist[i].value.l;
701 break;
702 case PM_TYPE_U32:
703 if (i == 0 ||
704 np->info->ivlist[0].value.ul > np->left->info->ivlist[i].value.ul)
705 np->info->ivlist[0].value.ul = np->left->info->ivlist[i].value.ul;
706 break;
707 case PM_TYPE_64:
708 if (i == 0 ||
709 np->info->ivlist[0].value.ll > np->left->info->ivlist[i].value.ll)
710 np->info->ivlist[0].value.ll = np->left->info->ivlist[i].value.ll;
711 break;
712 case PM_TYPE_U64:
713 if (i == 0 ||
714 np->info->ivlist[0].value.ull > np->left->info->ivlist[i].value.ull)
715 np->info->ivlist[0].value.ull = np->left->info->ivlist[i].value.ull;
716 break;
717 case PM_TYPE_FLOAT:
718 if (i == 0 ||
719 np->info->ivlist[0].value.f > np->left->info->ivlist[i].value.f)
720 np->info->ivlist[0].value.f = np->left->info->ivlist[i].value.f;
721 break;
722 case PM_TYPE_DOUBLE:
723 if (i == 0 ||
724 np->info->ivlist[0].value.d > np->left->info->ivlist[i].value.d)
725 np->info->ivlist[0].value.d = np->left->info->ivlist[i].value.d;
726 break;
727 default:
728 /*
729 * check_expr() checks for numeric data
730 * type at bind time ... if here, botch!
731 */
732 return PM_ERR_CONV;
733 }
734 break;
735
736 case L_SUM:
737 switch (np->desc.type) {
738 case PM_TYPE_32:
739 np->info->ivlist[0].value.l += np->left->info->ivlist[i].value.l;
740 break;
741 case PM_TYPE_U32:
742 np->info->ivlist[0].value.ul += np->left->info->ivlist[i].value.ul;
743 break;
744 case PM_TYPE_64:
745 np->info->ivlist[0].value.ll += np->left->info->ivlist[i].value.ll;
746 break;
747 case PM_TYPE_U64:
748 np->info->ivlist[0].value.ull += np->left->info->ivlist[i].value.ull;
749 break;
750 case PM_TYPE_FLOAT:
751 np->info->ivlist[0].value.f += np->left->info->ivlist[i].value.f;
752 break;
753 case PM_TYPE_DOUBLE:
754 np->info->ivlist[0].value.d += np->left->info->ivlist[i].value.d;
755 break;
756 default:
757 /*
758 * check_expr() checks for numeric data
759 * type at bind time ... if here, botch!
760 */
761 return PM_ERR_CONV;
762 }
763 break;
764
765 }
766 }
767 }
768 return np->info->numval;
769 break;
770
771 case L_NAME:
772 /*
773 * Extract instance-values from pmResult and store them in
774 * ivlist[] as <int, pmAtomValue> pairs
775 */
776 for (j = 0; j < rp->numpmid; j++) {
777 if (np->info->pmid == rp->vset[j]->pmid) {
778 free_ivlist(np);
779 np->info->numval = rp->vset[j]->numval;
780 if (np->info->numval <= 0)
781 return np->info->numval;
782 if ((np->info->ivlist = (val_t *)malloc(np->info->numval*sizeof(val_t))) == NULL) {
783 __pmNoMem("eval_expr: metric ivlist", np->info->numval*sizeof(val_t), PM_FATAL_ERR);
784 /*NOTREACHED*/
785 }
786 for (i = 0; i < np->info->numval; i++) {
787 np->info->ivlist[i].inst = rp->vset[j]->vlist[i].inst;
788 switch (np->desc.type) {
789 case PM_TYPE_32:
790 case PM_TYPE_U32:
791 np->info->ivlist[i].value.l = rp->vset[j]->vlist[i].value.lval;
792 break;
793
794 case PM_TYPE_64:
795 case PM_TYPE_U64:
796 memcpy((void *)&np->info->ivlist[i].value.ll, (void *)rp->vset[j]->vlist[i].value.pval->vbuf, sizeof(__int64_t));
797 break;
798
799 case PM_TYPE_FLOAT:
800 if (rp->vset[j]->valfmt == PM_VAL_INSITU) {
801 /* old style insitu float */
802 np->info->ivlist[i].value.l = rp->vset[j]->vlist[i].value.lval;
803 }
804 else {
805 assert(rp->vset[j]->vlist[i].value.pval->vtype == PM_TYPE_FLOAT);
806 memcpy((void *)&np->info->ivlist[i].value.f, (void *)rp->vset[j]->vlist[i].value.pval->vbuf, sizeof(float));
807 }
808 break;
809
810 case PM_TYPE_DOUBLE:
811 memcpy((void *)&np->info->ivlist[i].value.d, (void *)rp->vset[j]->vlist[i].value.pval->vbuf, sizeof(double));
812 break;
813
814 case PM_TYPE_STRING:
815 need = rp->vset[j]->vlist[i].value.pval->vlen-PM_VAL_HDR_SIZE;
816 if ((np->info->ivlist[i].value.cp = (char *)malloc(need)) == NULL) {
817 __pmNoMem("eval_expr: string value", rp->vset[j]->vlist[i].value.pval->vlen, PM_FATAL_ERR);
818 /*NOTREACHED*/
819 }
820 memcpy((void *)np->info->ivlist[i].value.cp, (void *)rp->vset[j]->vlist[i].value.pval->vbuf, need);
821 np->info->ivlist[i].vlen = need;
822 break;
823
824 case PM_TYPE_AGGREGATE:
825 case PM_TYPE_AGGREGATE_STATIC:
826 case PM_TYPE_EVENT:
827 if ((np->info->ivlist[i].value.vbp = (pmValueBlock *)malloc(rp->vset[j]->vlist[i].value.pval->vlen)) == NULL) {
828 __pmNoMem("eval_expr: aggregate value", rp->vset[j]->vlist[i].value.pval->vlen, PM_FATAL_ERR);
829 /*NOTREACHED*/
830 }
831 memcpy(np->info->ivlist[i].value.vbp, (void *)rp->vset[j]->vlist[i].value.pval, rp->vset[j]->vlist[i].value.pval->vlen);
832 np->info->ivlist[i].vlen = rp->vset[j]->vlist[i].value.pval->vlen;
833 break;
834
835 default:
836 /*
837 * really only PM_TYPE_NOSUPPORT should
838 * end up here
839 */
840 return PM_ERR_TYPE;
841 }
842 }
843 return np->info->numval;
844 }
845 }
846 #ifdef PCP_DEBUG
847 if (pmDebug & DBG_TRACE_DERIVE) {
848 fprintf(stderr, "eval_expr: botch: operand %s not in the extended pmResult\n", pmIDStr(np->info->pmid));
849 __pmDumpResult(stderr, rp);
850 }
851 #endif
852 return PM_ERR_PMID;
853
854 case L_ANON:
855 /* no values available for anonymous metrics */
856 return 0;
857
858 default:
859 /*
860 * binary operator cases ... always have a left and right
861 * operand and no errors (these are caught earlier when the
862 * recursive call on each of the operands would may have
863 * returned an error
864 */
865 assert(np->left != NULL);
866 assert(np->right != NULL);
867
868 free_ivlist(np);
869 /*
870 * empty result cases first
871 */
872 if (np->left->info->numval == 0) {
873 np->info->numval = 0;
874 return np->info->numval;
875 }
876 if (np->right->info->numval == 0) {
877 np->info->numval = 0;
878 return np->info->numval;
879 }
880 /*
881 * really got some work to do ...
882 */
883 if (np->left->desc.indom == PM_INDOM_NULL)
884 np->info->numval = np->right->info->numval;
885 else if (np->right->desc.indom == PM_INDOM_NULL)
886 np->info->numval = np->left->info->numval;
887 else {
888 /*
889 * Generally have the same number of instances because
890 * both operands are over the same instance domain,
891 * fetched with the same profile. When not the case,
892 * the result can contain no more instances than in
893 * the smaller of the operands.
894 */
895 if (np->left->info->numval <= np->right->info->numval)
896 np->info->numval = np->left->info->numval;
897 else
898 np->info->numval = np->right->info->numval;
899 }
900 if ((np->info->ivlist = (val_t *)malloc(np->info->numval*sizeof(val_t))) == NULL) {
901 __pmNoMem("eval_expr: expr ivlist", np->info->numval*sizeof(val_t), PM_FATAL_ERR);
902 /*NOTREACHED*/
903 }
904 /*
905 * ivlist[k] = left-ivlist[i] <op> right-ivlist[j]
906 */
907 for (i = j = k = 0; k < np->info->numval; ) {
908 if (i >= np->left->info->numval || j >= np->right->info->numval) {
909 /* run out of operand instances, quit */
910 np->info->numval = k;
911 break;
912 }
913 if (np->left->desc.indom != PM_INDOM_NULL &&
914 np->right->desc.indom != PM_INDOM_NULL) {
915 if (np->left->info->ivlist[i].inst != np->right->info->ivlist[j].inst) {
916 /* left ith inst != right jth inst ... search in right */
917 #ifdef PCP_DEBUG
918 if ((pmDebug & DBG_TRACE_DERIVE) && (pmDebug & DBG_TRACE_APPL2)) {
919 fprintf(stderr, "eval_expr: inst[%d] mismatch left [%d]=%d right [%d]=%d\n", k, i, np->left->info->ivlist[i].inst, j, np->right->info->ivlist[j].inst);
920 }
921 #endif
922 for (j = 0; j < np->right->info->numval; j++) {
923 if (np->left->info->ivlist[i].inst == np->right->info->ivlist[j].inst)
924 break;
925 }
926 if (j == np->right->info->numval) {
927 /*
928 * no match, so next instance on left operand,
929 * and reset to start from first instance of
930 * right operand
931 */
932 i++;
933 j = 0;
934 continue;
935 }
936 #ifdef PCP_DEBUG
937 else {
938 if ((pmDebug & DBG_TRACE_DERIVE) && (pmDebug & DBG_TRACE_APPL2)) {
939 fprintf(stderr, "eval_expr: recover @ right [%d]=%d\n", j, np->right->info->ivlist[j].inst);
940 }
941 }
942 #endif
943 }
944 }
945 np->info->ivlist[k].value =
946 bin_op(np->desc.type, np->type,
947 np->left->info->ivlist[i].value, np->left->desc.type, np->left->info->mul_scale, np->left->info->div_scale,
948 np->right->info->ivlist[j].value, np->right->desc.type, np->right->info->mul_scale, np->right->info->div_scale);
949 if (np->left->desc.indom != PM_INDOM_NULL)
950 np->info->ivlist[k].inst = np->left->info->ivlist[i].inst;
951 else
952 np->info->ivlist[k].inst = np->right->info->ivlist[j].inst;
953 k++;
954 if (np->left->desc.indom != PM_INDOM_NULL) {
955 i++;
956 if (np->right->desc.indom != PM_INDOM_NULL) {
957 j++;
958 if (j >= np->right->info->numval) {
959 /* rescan if need be */
960 j = 0;
961 }
962 }
963 }
964 else if (np->right->desc.indom != PM_INDOM_NULL) {
965 j++;
966 }
967 }
968 return np->info->numval;
969 }
970 /*NOTREACHED*/
971 }
972
973 /*
974 * Algorithm here is complicated by trying to re-write the pmResult.
975 *
976 * On entry the pmResult is likely to be built over a pinned PDU buffer,
977 * which means individual pmValueSets cannot be selectively replaced
978 * (this would come to tears badly in pmFreeResult() where as soon as
979 * one pmValueSet is found to be in a pinned PDU buffer it is assumed
980 * they are all so ... leaving a memory leak for any ones we'd modified
981 * here).
982 *
983 * So the only option is to COPY the pmResult, selectively replacing
984 * the pmValueSets for the derived metrics, and then calling
985 * pmFreeResult() to free the input structure and return the new one.
986 *
987 * In making the COPY it is critical that we reverse the algorithm
988 * used in pmFreeResult() so that a later call to pmFreeResult() will
989 * not cause a memory leak.
990 * This means ...
991 * - malloc() the pmResult (padded out to the right number of vset[]
992 * entries)
993 * - if valfmt is not PM_VAL_INSITU use PM_VAL_DPTR (not PM_VAL_SPTR),
994 * so anything we point to is going to be released when our caller
995 * calls pmFreeResult()
996 * - if numval == 1, use __pmPoolAlloc() for the pmValueSet;
997 * otherwise use one malloc() for each pmValueSet with vlist[] sized
998 * to be 0 if numval < 0 else numval
999 * - pmValueBlocks for 64-bit integers, doubles or anything with a
1000 * length equal to the size of a 64-bit integer are from
1001 * __pmPoolAlloc(); otherwise pmValueBlocks are from malloc()
1002 *
1003 * For reference, the same logic appears in __pmLogFetchInterp() to
1004 * sythesize a pmResult there.
1005 */
1006 void
1007 __dmpostfetch(__pmContext *ctxp, pmResult **result)
1008 {
1009 int i;
1010 int j;
1011 int m;
1012 int numval;
1013 int valfmt;
1014 size_t need;
1015 int rewrite;
1016 ctl_t *cp = (ctl_t *)ctxp->c_dm;
1017 pmResult *rp = *result;
1018 pmResult *newrp;
1019
1020 /* if needed, init() called in __dmopencontext beforehand */
1021
1022 if (cp == NULL || cp->fetch_has_dm == 0) return;
1023
1024 newrp = (pmResult *)malloc(sizeof(pmResult)+(cp->numpmid-1)*sizeof(pmValueSet *));
1025 if (newrp == NULL) {
1026 __pmNoMem("__dmpostfetch: newrp", sizeof(pmResult)+(cp->numpmid-1)*sizeof(pmValueSet *), PM_FATAL_ERR);
1027 /*NOTREACHED*/
1028 }
1029 newrp->timestamp = rp->timestamp;
1030 newrp->numpmid = cp->numpmid;
1031
1032 for (j = 0; j < newrp->numpmid; j++) {
1033 numval = rp->vset[j]->numval;
1034 valfmt = rp->vset[j]->valfmt;
1035 rewrite = 0;
1036 /*
1037 * pandering to gcc ... m is not used unless rewrite == 1 in
1038 * which case m is well-defined
1039 */
1040 m = 0;
1041 if (pmid_domain(rp->vset[j]->pmid) == DYNAMIC_PMID &&
1042 pmid_item(rp->vset[j]->pmid) != 0) {
1043 for (m = 0; m < cp->nmetric; m++) {
1044 if (rp->vset[j]->pmid == cp->mlist[m].pmid) {
1045 if (cp->mlist[m].expr == NULL) {
1046 numval = PM_ERR_PMID;
1047 }
1048 else {
1049 rewrite = 1;
1050 if (cp->mlist[m].expr->desc.type == PM_TYPE_32 ||
1051 cp->mlist[m].expr->desc.type == PM_TYPE_U32)
1052 valfmt = PM_VAL_INSITU;
1053 else
1054 valfmt = PM_VAL_DPTR;
1055 numval = eval_expr(cp->mlist[m].expr, rp, 1);
1056 #ifdef PCP_DEBUG
1057 if ((pmDebug & DBG_TRACE_DERIVE) && (pmDebug & DBG_TRACE_APPL2)) {
1058 int k;
1059
1060 fprintf(stderr, "__dmpostfetch: [%d] root node %s: numval=%d", j, pmIDStr(rp->vset[j]->pmid), numval);
1061 for (k = 0; k < numval; k++) {
1062 fprintf(stderr, " vset[%d]: inst=%d", k, cp->mlist[m].expr->info->ivlist[k].inst);
1063 if (cp->mlist[m].expr->desc.type == PM_TYPE_32)
1064 fprintf(stderr, " l=%d", cp->mlist[m].expr->info->ivlist[k].value.l);
1065 else if (cp->mlist[m].expr->desc.type == PM_TYPE_U32)
1066 fprintf(stderr, " u=%u", cp->mlist[m].expr->info->ivlist[k].value.ul);
1067 else if (cp->mlist[m].expr->desc.type == PM_TYPE_64)
1068 fprintf(stderr, " ll=%"PRIi64, cp->mlist[m].expr->info->ivlist[k].value.ll);
1069 else if (cp->mlist[m].expr->desc.type == PM_TYPE_U64)
1070 fprintf(stderr, " ul=%"PRIu64, cp->mlist[m].expr->info->ivlist[k].value.ull);
1071 else if (cp->mlist[m].expr->desc.type == PM_TYPE_FLOAT)
1072 fprintf(stderr, " f=%f", (double)cp->mlist[m].expr->info->ivlist[k].value.f);
1073 else if (cp->mlist[m].expr->desc.type == PM_TYPE_DOUBLE)
1074 fprintf(stderr, " d=%f", cp->mlist[m].expr->info->ivlist[k].value.d);
1075 else if (cp->mlist[m].expr->desc.type == PM_TYPE_STRING) {
1076 fprintf(stderr, " cp=%s (len=%d)", cp->mlist[m].expr->info->ivlist[k].value.cp, cp->mlist[m].expr->info->ivlist[k].vlen);
1077 }
1078 else {
1079 fprintf(stderr, " vbp=" PRINTF_P_PFX "%p (len=%d)", cp->mlist[m].expr->info->ivlist[k].value.vbp, cp->mlist[m].expr->info->ivlist[k].vlen);
1080 }
1081 }
1082 fputc('\n', stderr);
1083 if (cp->mlist[m].expr->info != NULL)
1084 __dmdumpexpr(cp->mlist[m].expr, 1);
1085 }
1086 #endif
1087 }
1088 break;
1089 }
1090 }
1091 }
1092
1093 if (numval < 0) {
1094 /* only need pmid and numval */
1095 need = sizeof(pmValueSet) - sizeof(pmValue);
1096 }
1097 else if (numval == 1) {
1098 /* special case for single value */
1099 newrp->vset[j] = (pmValueSet *)__pmPoolAlloc(sizeof(pmValueSet));
1100 need = 0;
1101 }
1102 else {
1103 /* already one pmValue in a pmValueSet */
1104 need = sizeof(pmValueSet) + (numval - 1)*sizeof(pmValue);
1105 }
1106 if (need > 0) {
1107 if ((newrp->vset[j] = (pmValueSet *)malloc(need)) == NULL) {
1108 __pmNoMem("__dmpostfetch: vset", need, PM_FATAL_ERR);
1109 /*NOTREACHED*/
1110 }
1111 }
1112 newrp->vset[j]->pmid = rp->vset[j]->pmid;
1113 newrp->vset[j]->numval = numval;
1114 newrp->vset[j]->valfmt = valfmt;
1115 if (numval < 0)
1116 continue;
1117
1118 for (i = 0; i < numval; i++) {
1119 pmValueBlock *vp;
1120
1121 if (!rewrite) {
1122 newrp->vset[j]->vlist[i].inst = rp->vset[j]->vlist[i].inst;
1123 if (newrp->vset[j]->valfmt == PM_VAL_INSITU) {
1124 newrp->vset[j]->vlist[i].value.lval = rp->vset[j]->vlist[i].value.lval;
1125 }
1126 else {
1127 need = rp->vset[j]->vlist[i].value.pval->vlen;
1128 if (need == PM_VAL_HDR_SIZE + sizeof(__int64_t))
1129 vp = (pmValueBlock *)__pmPoolAlloc(need);
1130 else
1131 vp = (pmValueBlock *)malloc(need);
1132 if (vp == NULL) {
1133 __pmNoMem("__dmpostfetch: copy value", need, PM_FATAL_ERR);
1134 /*NOTREACHED*/
1135 }
1136 memcpy((void *)vp, (void *)rp->vset[j]->vlist[i].value.pval, need);
1137 newrp->vset[j]->vlist[i].value.pval = vp;
1138 }
1139 continue;
1140 }
1141
1142 /*
1143 * the rewrite case ...
1144 */
1145 newrp->vset[j]->vlist[i].inst = cp->mlist[m].expr->info->ivlist[i].inst;
1146 switch (cp->mlist[m].expr->desc.type) {
1147 case PM_TYPE_32:
1148 case PM_TYPE_U32:
1149 newrp->vset[j]->vlist[i].value.lval = cp->mlist[m].expr->info->ivlist[i].value.l;
1150 break;
1151
1152 case PM_TYPE_64:
1153 case PM_TYPE_U64:
1154 need = PM_VAL_HDR_SIZE + sizeof(__int64_t);
1155 if ((vp = (pmValueBlock *)__pmPoolAlloc(need)) == NULL) {
1156 __pmNoMem("__dmpostfetch: 64-bit int value", need, PM_FATAL_ERR);
1157 /*NOTREACHED*/
1158 }
1159 vp->vlen = need;
1160 vp->vtype = cp->mlist[m].expr->desc.type;
1161 memcpy((void *)vp->vbuf, (void *)&cp->mlist[m].expr->info->ivlist[i].value.ll, sizeof(__int64_t));
1162 newrp->vset[j]->vlist[i].value.pval = vp;
1163 break;
1164
1165 case PM_TYPE_FLOAT:
1166 need = PM_VAL_HDR_SIZE + sizeof(float);
1167 if ((vp = (pmValueBlock *)malloc(need)) == NULL) {
1168 __pmNoMem("__dmpostfetch: float value", need, PM_FATAL_ERR);
1169 /*NOTREACHED*/
1170 }
1171 vp->vlen = need;
1172 vp->vtype = PM_TYPE_FLOAT;
1173 memcpy((void *)vp->vbuf, (void *)&cp->mlist[m].expr->info->ivlist[i].value.f, sizeof(float));
1174 newrp->vset[j]->vlist[i].value.pval = vp;
1175 break;
1176
1177 case PM_TYPE_DOUBLE:
1178 need = PM_VAL_HDR_SIZE + sizeof(double);
1179 if ((vp = (pmValueBlock *)__pmPoolAlloc(need)) == NULL) {
1180 __pmNoMem("__dmpostfetch: double value", need, PM_FATAL_ERR);
1181 /*NOTREACHED*/
1182 }
1183 vp->vlen = need;
1184 vp->vtype = PM_TYPE_DOUBLE;
1185 memcpy((void *)vp->vbuf, (void *)&cp->mlist[m].expr->info->ivlist[i].value.f, sizeof(double));
1186 newrp->vset[j]->vlist[i].value.pval = vp;
1187 break;
1188
1189 case PM_TYPE_STRING:
1190 need = PM_VAL_HDR_SIZE + cp->mlist[m].expr->info->ivlist[i].vlen;
1191 if (need == PM_VAL_HDR_SIZE + sizeof(__int64_t))
1192 vp = (pmValueBlock *)__pmPoolAlloc(need);
1193 else
1194 vp = (pmValueBlock *)malloc(need);
1195 if (vp == NULL) {
1196 __pmNoMem("__dmpostfetch: string value", need, PM_FATAL_ERR);
1197 /*NOTREACHED*/
1198 }
1199 vp->vlen = need;
1200 vp->vtype = cp->mlist[m].expr->desc.type;
1201 memcpy((void *)vp->vbuf, cp->mlist[m].expr->info->ivlist[i].value.cp, cp->mlist[m].expr->info->ivlist[i].vlen);
1202 newrp->vset[j]->vlist[i].value.pval = vp;
1203 break;
1204
1205 case PM_TYPE_AGGREGATE:
1206 case PM_TYPE_AGGREGATE_STATIC:
1207 case PM_TYPE_EVENT:
1208 need = cp->mlist[m].expr->info->ivlist[i].vlen;
1209 if (need == PM_VAL_HDR_SIZE + sizeof(__int64_t))
1210 vp = (pmValueBlock *)__pmPoolAlloc(need);
1211 else
1212 vp = (pmValueBlock *)malloc(need);
1213 if (vp == NULL) {
1214 __pmNoMem("__dmpostfetch: aggregate or event value", need, PM_FATAL_ERR);
1215 /*NOTREACHED*/
1216 }
1217 memcpy((void *)vp, cp->mlist[m].expr->info->ivlist[i].value.vbp, cp->mlist[m].expr->info->ivlist[i].vlen);
1218 newrp->vset[j]->vlist[i].value.pval = vp;
1219 break;
1220
1221 default:
1222 /*
1223 * really nothing should end up here ...
1224 * do nothing as numval should have been < 0
1225 */
1226 #ifdef PCP_DEBUG
1227 if (pmDebug & DBG_TRACE_DERIVE) {
1228 fprintf(stderr, "__dmpostfetch: botch: drived metric[%d]: operand %s has odd type (%d)\n", m, pmIDStr(rp->vset[j]->pmid), cp->mlist[m].expr->desc.type);
1229 }
1230 #endif
1231 break;
1232 }
1233 }
1234 }
1235
1236 /*
1237 * cull the original pmResult and return the rewritten one
1238 */
1239 pmFreeResult(rp);
1240 *result = newrp;
1241
1242 return;
1243 }