xref: /illumos-gate/usr/src/cmd/fm/modules/common/eversholt/itree.c (revision 1d9df23bbf4124f12ca7832d6eaaa600e0aa8eda)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  * itree.c -- instance tree creation and manipulation
27  *
28  * this module provides the instance tree
29  */
30 
31 #pragma ident	"%Z%%M%	%I%	%E% SMI"
32 
33 #include <stdio.h>
34 #include <ctype.h>
35 #include <string.h>
36 #include <strings.h>
37 #include "alloc.h"
38 #include "out.h"
39 #include "stable.h"
40 #include "literals.h"
41 #include "lut.h"
42 #include "tree.h"
43 #include "ptree.h"
44 #include "itree.h"
45 #include "ipath.h"
46 #include "iexpr.h"
47 #include "eval.h"
48 #include "config.h"
49 
50 /*
51  * struct info contains the state we keep when expanding a prop statement
52  * as part of constructing the instance tree.  state kept in struct info
53  * is the non-recursive stuff -- the stuff that doesn't need to be on
54  * the stack.  the rest of the state that is passed between all the
55  * mutually recursive functions, is required to be on the stack since
56  * we need to backtrack and recurse as we do the instance tree construction.
57  */
58 struct info {
59 	struct lut *lut;
60 	struct node *anp;	/* arrow np */
61 	struct lut *ex;		/* dictionary of explicit iterators */
62 	struct config *croot;
63 } Ninfo;
64 
65 /*
66  * struct wildcardinfo is used to track wildcarded portions of paths.
67  *
68  * for example, if the epname of an event is "c/d" and the path "a/b/c/d"
69  * exists, the wildcard path ewname is filled in with the path "a/b".  when
70  * matching is done, epname is temporarily replaced with the concatenation
71  * of ewname and epname.
72  *
73  * a linked list of these structs is used to track the expansion of each
74  * event node as it is processed in vmatch() --> vmatch_event() calls.
75  */
76 struct wildcardinfo {
77 	struct node *nptop;		/* event node fed to vmatch */
78 	struct node *oldepname;		/* epname without the wildcard part */
79 	struct node *ewname;		/* wildcard path */
80 	struct wildcardinfo *next;
81 };
82 
83 static struct wildcardinfo *wcproot = NULL;
84 
85 static void vmatch(struct info *infop, struct node *np, struct node *lnp,
86     struct node *anp);
87 static void hmatch(struct info *infop, struct node *np, struct node *nextnp);
88 static void itree_pbubble(int flags, struct bubble *bp);
89 static void itree_pruner(void *left, void *right, void *arg);
90 static void itree_destructor(void *left, void *right, void *arg);
91 static int itree_set_arrow_traits(struct arrow *ap, struct node *fromev,
92     struct node *toev, struct lut *ex);
93 static void itree_free_arrowlists(struct bubble *bubp, int arrows_too);
94 static void itree_prune_arrowlists(struct bubble *bubp);
95 static void arrow_add_within(struct arrow *ap, struct node *xpr);
96 static struct arrow *itree_add_arrow(struct node *apnode,
97     struct node *fromevent, struct node *toevent, struct lut *ex);
98 static struct event *find_or_add_event(struct info *infop, struct node *np);
99 static void add_arrow(struct bubble *bp, struct arrow *ap);
100 static struct constraintlist *itree_add_constraint(struct arrow *arrowp,
101     struct node *c);
102 static struct bubble *itree_add_bubble(struct event *eventp,
103     enum bubbletype btype, int nork, int gen);
104 static void itree_free_bubble(struct bubble *freeme);
105 static void itree_free_constraints(struct arrow *ap);
106 
107 /*
108  * the following struct contains the state we build up during
109  * vertical and horizontal expansion so that generate()
110  * has everything it needs to construct the appropriate arrows.
111  * after setting up the state by calling:
112  *	generate_arrownp()
113  *	generate_nork()
114  *	generate_new()
115  *	generate_from()
116  *	generate_to()
117  * the actual arrow generation is done by calling:
118  *	generate()
119  */
120 static struct {
121 	int generation;		/* generation number of arrow set */
122 	struct node *arrownp;	/* top-level parse tree for arrow */
123 	int n;			/* n value associated with arrow */
124 	int k;			/* k value associated with arrow */
125 	struct node *fromnp;	/* left-hand-side event in parse tree */
126 	struct node *tonp;	/* right-hand-side event in parse tree */
127 	struct event *frome;	/* left-hand-side event in instance tree */
128 	struct event *toe;	/* right-hand-side event in instance tree */
129 	struct bubble *frombp;	/* bubble arrow comes from */
130 	struct bubble *tobp;	/* bubble arrow goes to */
131 } G;
132 
133 static void
134 generate_arrownp(struct node *arrownp)
135 {
136 	G.arrownp = arrownp;
137 }
138 
139 static void
140 generate_nork(int n, int k)
141 {
142 	G.n = n;
143 	G.k = k;
144 }
145 
146 static void
147 generate_new(void)
148 {
149 	G.generation++;
150 }
151 
152 static void
153 generate_from(struct node *fromeventnp)
154 {
155 	G.frombp = NULL;
156 	G.fromnp = fromeventnp;
157 }
158 
159 static void
160 generate_to(struct node *toeventnp)
161 {
162 	G.tonp = toeventnp;
163 }
164 
165 static void
166 generate(struct info *infop)
167 {
168 	struct arrow *arrowp;
169 
170 	ASSERT(G.arrownp != NULL);
171 	ASSERT(G.fromnp != NULL);
172 	ASSERT(G.tonp != NULL);
173 
174 	out(O_ALTFP|O_VERB3|O_NONL, "        Arrow \"");
175 	ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.fromnp);
176 	out(O_ALTFP|O_VERB3|O_NONL, "\" -> \"");
177 	ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.tonp);
178 	out(O_ALTFP|O_VERB3|O_NONL, "\" ");
179 
180 	arrowp = itree_add_arrow(G.arrownp, G.fromnp, G.tonp, infop->ex);
181 	if (arrowp == NULL) {
182 		out(O_ALTFP|O_VERB3, "(prevented by constraints)");
183 	} else {
184 		out(O_ALTFP|O_VERB3, "");
185 		if (!G.frombp) {
186 			G.frome = find_or_add_event(infop, G.fromnp);
187 			G.frombp = itree_add_bubble(G.frome, B_FROM, G.n, 0);
188 		}
189 		G.toe = find_or_add_event(infop, G.tonp);
190 		G.tobp = itree_add_bubble(G.toe, B_TO, G.k, G.generation);
191 		arrowp->tail = G.frombp;
192 		arrowp->head = G.tobp;
193 		add_arrow(G.frombp, arrowp);
194 		add_arrow(G.tobp, arrowp);
195 	}
196 }
197 
198 enum childnode_action {
199 	CN_NONE,
200 	CN_DUP
201 };
202 
203 static struct node *
204 tname_dup(struct node *namep, enum childnode_action act)
205 {
206 	struct node *retp = NULL;
207 	const char *file;
208 	int line;
209 
210 	if (namep == NULL)
211 		return (NULL);
212 
213 	file = namep->file;
214 	line = namep->line;
215 
216 	for (; namep != NULL; namep = namep->u.name.next) {
217 		struct node *newnp = newnode(T_NAME, file, line);
218 
219 		newnp->u.name.t = namep->u.name.t;
220 		newnp->u.name.s = namep->u.name.s;
221 		newnp->u.name.last = newnp;
222 		newnp->u.name.it = namep->u.name.it;
223 		newnp->u.name.cp = namep->u.name.cp;
224 
225 		if (act == CN_DUP) {
226 			struct node *npc;
227 
228 			npc = namep->u.name.child;
229 			if (npc != NULL) {
230 				switch (npc->t) {
231 				case T_NUM:
232 					newnp->u.name.child =
233 					    newnode(T_NUM, file, line);
234 					newnp->u.name.child->u.ull =
235 					    npc->u.ull;
236 					break;
237 				case T_NAME:
238 					newnp->u.name.child =
239 					    tree_name(npc->u.name.s,
240 					    npc->u.name.it, file, line);
241 					break;
242 				default:
243 					out(O_DIE, "tname_dup: "
244 					    "invalid child type %s",
245 					    ptree_nodetype2str(npc->t));
246 				}
247 			}
248 		}
249 
250 		if (retp == NULL) {
251 			retp = newnp;
252 		} else {
253 			retp->u.name.last->u.name.next = newnp;
254 			retp->u.name.last = newnp;
255 		}
256 	}
257 
258 	return (retp);
259 }
260 
261 struct prop_wlk_data {
262 	struct lut *props;
263 	struct node *epname;
264 };
265 
266 static struct lut *props2instance(struct node *, struct node *);
267 
268 /*
269  * let oldepname be a subset of epname.  return the subsection of epname
270  * that ends with oldepname.  make each component in the path explicitly
271  * instanced (i.e., with a T_NUM child).
272  */
273 static struct node *
274 tname_dup_to_epname(struct node *oldepname, struct node *epname)
275 {
276 	struct node *npref, *npend, *np1, *np2;
277 	struct node *ret = NULL;
278 	int foundmatch = 0;
279 
280 	if (epname == NULL)
281 		return (NULL);
282 
283 	/*
284 	 * search for the longest path in epname which contains
285 	 * oldnode->u.event.epname.  set npend to point to just past the
286 	 * end of this path.
287 	 */
288 	npend = NULL;
289 	for (npref = epname; npref; npref = npref->u.name.next) {
290 		if (npref->u.name.s == oldepname->u.name.s) {
291 			for (np1 = npref, np2 = oldepname;
292 			    np1 != NULL && np2 != NULL;
293 			    np1 = np1->u.name.next, np2 = np2->u.name.next) {
294 				if (np1->u.name.s != np2->u.name.s)
295 					break;
296 			}
297 			if (np2 == NULL) {
298 				foundmatch = 1;
299 				npend = np1;
300 				if (np1 == NULL) {
301 					/* oldepname matched npref up to end */
302 					break;
303 				}
304 			}
305 		}
306 	}
307 
308 	if (foundmatch == 0) {
309 		/*
310 		 * if oldepname could not be found in epname, return a
311 		 * duplicate of the former.  do not try to instantize
312 		 * oldepname since it might not be a path.
313 		 */
314 		return (tname_dup(oldepname, CN_DUP));
315 	}
316 
317 	/*
318 	 * dup (epname -- npend).  all children should be T_NUMs.
319 	 */
320 	for (npref = epname;
321 	    ! (npref == NULL || npref == npend);
322 	    npref = npref->u.name.next) {
323 		struct node *newnp = newnode(T_NAME, oldepname->file,
324 		    oldepname->line);
325 
326 		newnp->u.name.t = npref->u.name.t;
327 		newnp->u.name.s = npref->u.name.s;
328 		newnp->u.name.last = newnp;
329 		newnp->u.name.it = npref->u.name.it;
330 		newnp->u.name.cp = npref->u.name.cp;
331 
332 		newnp->u.name.child = newnode(T_NUM, oldepname->file,
333 		    oldepname->line);
334 
335 		if (npref->u.name.child == NULL ||
336 		    npref->u.name.child->t != T_NUM) {
337 			int childnum;
338 
339 			ASSERT(npref->u.name.cp != NULL);
340 			config_getcompname(npref->u.name.cp, NULL, &childnum);
341 			newnp->u.name.child->u.ull = childnum;
342 		} else {
343 			newnp->u.name.child->u.ull =
344 			    npref->u.name.child->u.ull;
345 		}
346 
347 		if (ret == NULL) {
348 			ret = newnp;
349 		} else {
350 			ret->u.name.last->u.name.next = newnp;
351 			ret->u.name.last = newnp;
352 		}
353 	}
354 
355 	return (ret);
356 }
357 
358 /*
359  * restriction: oldnode->u.event.epname has to be equivalent to or a subset
360  * of epname
361  */
362 static struct node *
363 tevent_dup_to_epname(struct node *oldnode, struct node *epname)
364 {
365 	struct node *ret;
366 
367 	ret = newnode(T_EVENT, oldnode->file, oldnode->line);
368 	ret->u.event.ename = tname_dup(oldnode->u.event.ename, CN_NONE);
369 	ret->u.event.epname = tname_dup_to_epname(oldnode->u.event.epname,
370 	    epname);
371 	return (ret);
372 }
373 
374 static void
375 nv_instantiate(void *name, void *val, void *arg)
376 {
377 	struct prop_wlk_data *pd = (struct prop_wlk_data *)arg;
378 	struct node *orhs = (struct node *)val;
379 	struct node *nrhs;
380 
381 	/* handle engines by instantizing the entire engine */
382 	if (name == L_engine) {
383 		ASSERT(orhs->t == T_EVENT);
384 		ASSERT(orhs->u.event.ename->u.name.t == N_SERD);
385 
386 		/* there are only SERD engines for now */
387 
388 		nrhs = newnode(T_SERD, orhs->file, orhs->line);
389 		nrhs->u.stmt.np = tevent_dup_to_epname(orhs, pd->epname);
390 		nrhs->u.stmt.lutp = props2instance(orhs, pd->epname);
391 		pd->props = lut_add(pd->props, name, nrhs, NULL);
392 		return;
393 	}
394 
395 	switch (orhs->t) {
396 	case T_NUM:
397 		nrhs = newnode(T_NUM, orhs->file, orhs->line);
398 		nrhs->u.ull = orhs->u.ull;
399 		pd->props = lut_add(pd->props, name, nrhs, NULL);
400 		break;
401 	case T_TIMEVAL:
402 		nrhs = newnode(T_TIMEVAL, orhs->file, orhs->line);
403 		nrhs->u.ull = orhs->u.ull;
404 		pd->props = lut_add(pd->props, name, nrhs, NULL);
405 		break;
406 	case T_NAME:
407 		nrhs = tname_dup_to_epname(orhs, pd->epname);
408 		pd->props = lut_add(pd->props, name, nrhs, NULL);
409 		break;
410 	case T_EVENT:
411 		nrhs = tevent_dup_to_epname(orhs, pd->epname);
412 		pd->props = lut_add(pd->props, name, nrhs, NULL);
413 		break;
414 	case T_GLOBID:
415 		nrhs = newnode(T_GLOBID, orhs->file, orhs->line);
416 		nrhs->u.globid.s = orhs->u.globid.s;
417 		pd->props = lut_add(pd->props, name, nrhs, NULL);
418 		break;
419 	case T_FUNC:
420 		/* for T_FUNC, we don't duplicate it, just point to node */
421 		pd->props = lut_add(pd->props, name, orhs, NULL);
422 		break;
423 	default:
424 		out(O_DIE, "unexpected nvpair value type %s",
425 		    ptree_nodetype2str(((struct node *)val)->t));
426 	}
427 }
428 
429 static struct lut *
430 props2instance(struct node *eventnp, struct node *epname)
431 {
432 	struct prop_wlk_data pd;
433 
434 	pd.props = NULL;
435 	pd.epname = epname;
436 
437 	ASSERT(eventnp->u.event.declp != NULL);
438 	lut_walk(eventnp->u.event.declp->u.stmt.lutp, nv_instantiate, &pd);
439 	return (pd.props);
440 }
441 
442 /*ARGSUSED*/
443 static void
444 instances_destructor(void *left, void *right, void *arg)
445 {
446 	struct node *dn = (struct node *)right;
447 
448 	if (dn->t == T_SERD) {
449 		/* we allocated the lut during itree_create(), so free it */
450 		lut_free(dn->u.stmt.lutp, instances_destructor, NULL);
451 		dn->u.stmt.lutp = NULL;
452 	}
453 	if (dn->t != T_FUNC)	/* T_FUNC pointed to original node */
454 		tree_free(dn);
455 }
456 
457 /*ARGSUSED*/
458 static void
459 payloadprops_destructor(void *left, void *right, void *arg)
460 {
461 	FREE(right);
462 }
463 
464 /*
465  * event_cmp -- used via lut_lookup/lut_add on instance tree lut
466  */
467 static int
468 event_cmp(struct event *ep1, struct event *ep2)
469 {
470 	int diff;
471 
472 	if ((diff = ep2->enode->u.event.ename->u.name.s -
473 	    ep1->enode->u.event.ename->u.name.s) != 0)
474 		return (diff);
475 	if ((diff = (char *)ep2->ipp - (char *)ep1->ipp) != 0)
476 		return (diff);
477 	return (0);
478 
479 }
480 
481 struct event *
482 itree_lookup(struct lut *itp, const char *ename, const struct ipath *ipp)
483 {
484 	struct event searchevent;	/* just used for searching */
485 	struct node searcheventnode;
486 	struct node searchenamenode;
487 
488 	searchevent.enode = &searcheventnode;
489 	searcheventnode.t = T_EVENT;
490 	searcheventnode.u.event.ename = &searchenamenode;
491 	searchenamenode.t = T_NAME;
492 	searchenamenode.u.name.s = ename;
493 	searchevent.ipp = ipp;
494 	return (lut_lookup(itp, (void *)&searchevent, (lut_cmp)event_cmp));
495 }
496 
497 static struct event *
498 find_or_add_event(struct info *infop, struct node *np)
499 {
500 	struct event *ret;
501 	struct event searchevent;	/* just used for searching */
502 
503 	ASSERTeq(np->t, T_EVENT, ptree_nodetype2str);
504 
505 	searchevent.enode = np;
506 	searchevent.ipp = ipath(np->u.event.epname);
507 	if ((ret = lut_lookup(infop->lut, (void *)&searchevent,
508 	    (lut_cmp)event_cmp)) != NULL)
509 		return (ret);
510 
511 	/* wasn't already in tree, allocate it */
512 	ret = alloc_xmalloc(sizeof (*ret));
513 	bzero(ret, sizeof (*ret));
514 
515 	ret->t = np->u.event.ename->u.name.t;
516 	ret->enode = np;
517 	ret->ipp = searchevent.ipp;
518 	ret->props = props2instance(np, np->u.event.epname);
519 
520 	infop->lut = lut_add(infop->lut, (void *)ret, (void *)ret,
521 	    (lut_cmp)event_cmp);
522 
523 	return (ret);
524 }
525 
526 /*
527  * hmatch_event -- perform any appropriate horizontal expansion on an event
528  *
529  * this routine is used to perform horizontal expansion on both the
530  * left-hand-side events in a prop, and the right-hand-side events.
531  * when called to handle a left-side event, nextnp point to the right
532  * side of the prop that should be passed to hmatch() for each match
533  * found by horizontal expansion.   when no horizontal expansion exists,
534  * we will still "match" one event for every event found in the list on
535  * the left-hand-side of the prop because vmatch() already found that
536  * there's at least one match during vertical expansion.
537  */
538 static void
539 hmatch_event(struct info *infop, struct node *eventnp, struct node *epname,
540     struct config *ncp, struct node *nextnp, int rematch)
541 {
542 	if (epname == NULL) {
543 		/*
544 		 * end of pathname recursion, either we just located
545 		 * a left-hand-side event and we're ready to move on
546 		 * to the expanding the right-hand-side events, or
547 		 * we're further down the recursion and we just located
548 		 * a right-hand-side event.  the passed-in parameter
549 		 * "nextnp" tells us whether we're working on the left
550 		 * side and need to move on to nextnp, or if nextnp is
551 		 * NULL, we're working on the right side.
552 		 */
553 		if (nextnp) {
554 			/*
555 			 * finished a left side expansion, move on to right.
556 			 * tell generate() what event we just matched so
557 			 * it can be used at the source of any arrows
558 			 * we generate as we match events on the right side.
559 			 */
560 			generate_from(eventnp);
561 			hmatch(infop, nextnp, NULL);
562 		} else {
563 			/*
564 			 * finished a right side expansion.  tell generate
565 			 * the information about the destination and let
566 			 * it construct the arrows as appropriate.
567 			 */
568 			generate_to(eventnp);
569 			generate(infop);
570 		}
571 
572 		return;
573 	}
574 
575 	ASSERTeq(epname->t, T_NAME, ptree_nodetype2str);
576 
577 	/*
578 	 * we only get here when eventnp already has a completely
579 	 * instanced epname in it already.  so we first recurse
580 	 * down to the end of the name and as the recursion pops
581 	 * up, we look for opportunities to advance horizontal
582 	 * expansions on to the next match.
583 	 */
584 	if (epname->u.name.it == IT_HORIZONTAL || rematch) {
585 		struct config *cp;
586 		struct config *ocp = epname->u.name.cp;
587 		char *cp_s;
588 		int cp_num;
589 		int ocp_num;
590 		struct iterinfo *iterinfop = NULL;
591 		const char *iters;
592 		int hexpand = 0;
593 
594 		if (epname->u.name.it != IT_HORIZONTAL) {
595 			/*
596 			 * Ancestor was horizontal though, so must rematch
597 			 * against the name/num found in vmatch.
598 			 */
599 			config_getcompname(ocp, NULL, &ocp_num);
600 		} else {
601 			iters = epname->u.name.child->u.name.s;
602 			if ((iterinfop = lut_lookup(infop->ex,
603 			    (void *)iters, NULL)) == NULL) {
604 				/*
605 				 * do horizontal expansion on this node
606 				 */
607 				hexpand = 1;
608 				iterinfop = alloc_xmalloc(
609 				    sizeof (struct iterinfo));
610 				iterinfop->num = -1;
611 				iterinfop->np = epname;
612 				infop->ex = lut_add(infop->ex, (void *)iters,
613 				    iterinfop, NULL);
614 			} else if (iterinfop->num == -1) {
615 				hexpand = 1;
616 			} else {
617 				/*
618 				 * This name has already been used in a
619 				 * horizontal expansion. This time just match it
620 				 */
621 				ocp_num = iterinfop->num;
622 			}
623 		}
624 		/*
625 		 * Run through siblings looking for any that match the name.
626 		 * If hexpand not set then num must also match ocp_num.
627 		 */
628 		for (cp = rematch ? ncp : ocp; cp; cp = config_next(cp)) {
629 			config_getcompname(cp, &cp_s, &cp_num);
630 			if (cp_s == epname->u.name.s) {
631 				if (hexpand)
632 					iterinfop->num = cp_num;
633 				else if (ocp_num != cp_num)
634 					continue;
635 				epname->u.name.cp = cp;
636 				hmatch_event(infop, eventnp,
637 				    epname->u.name.next, config_child(cp),
638 				    nextnp, 1);
639 			}
640 		}
641 		epname->u.name.cp = ocp;
642 		if (hexpand)
643 			iterinfop->num = -1;
644 	} else {
645 		hmatch_event(infop, eventnp, epname->u.name.next,
646 		    NULL, nextnp, 0);
647 	}
648 }
649 
650 /*
651  * hmatch -- check for horizontal expansion matches
652  *
653  * np points to the things we're matching (like a T_LIST or a T_EVENT)
654  * and if we're working on a left-side of a prop, nextnp points to
655  * the other side of the prop that we'll tackle next when this recursion
656  * bottoms out.  when all the events in the entire prop arrow have been
657  * horizontally expanded, generate() will be called to generate the
658  * actualy arrow.
659  */
660 static void
661 hmatch(struct info *infop, struct node *np, struct node *nextnp)
662 {
663 	if (np == NULL)
664 		return;		/* all done */
665 
666 	/*
667 	 * for each item in the list of events (which could just
668 	 * be a single event, or it could get larger in the loop
669 	 * below due to horizontal expansion), call hmatch on
670 	 * the right side and create arrows to each element.
671 	 */
672 
673 	switch (np->t) {
674 	case T_LIST:
675 		/* loop through the list */
676 		if (np->u.expr.left)
677 			hmatch(infop, np->u.expr.left, nextnp);
678 		if (np->u.expr.right)
679 			hmatch(infop, np->u.expr.right, nextnp);
680 		break;
681 
682 	case T_EVENT:
683 		hmatch_event(infop, np, np->u.event.epname,
684 		    NULL, nextnp, 0);
685 		break;
686 
687 	default:
688 		outfl(O_DIE, np->file, np->line,
689 		    "hmatch: unexpected type: %s",
690 		    ptree_nodetype2str(np->t));
691 	}
692 }
693 
694 static int
695 itree_np2nork(struct node *norknp)
696 {
697 	if (norknp == NULL)
698 		return (1);
699 	else if (norknp->t == T_NAME && norknp->u.name.s == L_A)
700 		return (-1);	/* our internal version of "all" */
701 	else if (norknp->t == T_NUM)
702 		return ((int)norknp->u.ull);
703 	else
704 		outfl(O_DIE, norknp->file, norknp->line,
705 		    "itree_np2nork: internal error type %s",
706 		    ptree_nodetype2str(norknp->t));
707 	/*NOTREACHED*/
708 	return (1);
709 }
710 
711 static struct iterinfo *
712 newiterinfo(int num, struct node *np)
713 {
714 	struct iterinfo *ret = alloc_xmalloc(sizeof (*ret));
715 
716 	ret->num = num;
717 	ret->np = np;
718 	return (ret);
719 }
720 
721 /*ARGSUSED*/
722 static void
723 iterinfo_destructor(void *left, void *right, void *arg)
724 {
725 	struct iterinfo *iterinfop = (struct iterinfo *)right;
726 
727 	alloc_xfree(iterinfop, sizeof (*iterinfop));
728 }
729 
730 static void
731 vmatch_event(struct info *infop, struct config *cp, struct node *np,
732 	    struct node *lnp, struct node *anp, struct wildcardinfo *wcp)
733 {
734 	char *cp_s;
735 	int cp_num;
736 	struct node *ewlp, *ewfp;
737 	struct config *pcp;
738 	struct node *cpnode;
739 	int newewname = 0;
740 
741 	if (np == NULL) {
742 		/*
743 		 * Reached the end of the name. u.name.cp pointers should be set
744 		 * up for each part of name. From this we can use config tree
745 		 * to build up the wildcard part of the name (if any).
746 		 */
747 		pcp = config_parent(wcp->nptop->u.event.epname->u.name.cp);
748 		if (pcp == infop->croot) {
749 			/*
750 			 * no wildcarding done - move on to next entry
751 			 */
752 			wcp->nptop->u.event.ewname = wcp->ewname;
753 			wcp->nptop->u.event.oldepname = wcp->oldepname;
754 			vmatch(infop, np, lnp, anp);
755 			return;
756 		}
757 		if (wcp->ewname == NULL) {
758 			/*
759 			 * ewname not yet set up - do it now
760 			 */
761 			newewname = 1;
762 			for (; pcp != infop->croot; pcp = config_parent(pcp)) {
763 				config_getcompname(pcp, &cp_s, &cp_num);
764 				cpnode = tree_name(cp_s, IT_NONE, NULL, 0);
765 				cpnode->u.name.child = newnode(T_NUM, NULL, 0);
766 				cpnode->u.name.child->u.ull = cp_num;
767 				cpnode->u.name.cp = pcp;
768 				if (wcp->ewname != NULL) {
769 					cpnode->u.name.next = wcp->ewname;
770 					cpnode->u.name.last =
771 					    wcp->ewname->u.name.last;
772 				}
773 				wcp->ewname = cpnode;
774 			}
775 		}
776 
777 		/*
778 		 * dup ewname and append oldepname
779 		 */
780 		ewfp = tname_dup(wcp->ewname, CN_DUP);
781 		ewlp = ewfp->u.name.last;
782 		ewfp->u.name.last = wcp->oldepname->u.name.last;
783 		ewlp->u.name.next = wcp->oldepname;
784 
785 		wcp->nptop->u.event.epname = ewfp;
786 		wcp->nptop->u.event.ewname = wcp->ewname;
787 		wcp->nptop->u.event.oldepname = wcp->oldepname;
788 		vmatch(infop, np, lnp, anp);
789 		wcp->nptop->u.event.epname = wcp->oldepname;
790 
791 		/*
792 		 * reduce duped ewname to original then free
793 		 */
794 		ewlp->u.name.next = NULL;
795 		ewfp->u.name.last = ewlp;
796 		tree_free(ewfp);
797 
798 		if (newewname) {
799 			/*
800 			 * free ewname if allocated above
801 			 */
802 			tree_free(wcp->ewname);
803 			wcp->ewname = NULL;
804 		}
805 		return;
806 	}
807 
808 	/*
809 	 * We have an np. See if we can match it in this section of
810 	 * the config tree.
811 	 */
812 	if (cp == NULL)
813 		return;	/* no more config to match against */
814 
815 	for (; cp; cp = config_next(cp)) {
816 		config_getcompname(cp, &cp_s, &cp_num);
817 
818 		if (cp_s == np->u.name.s) {
819 			/* found a matching component name */
820 			if (np->u.name.child &&
821 			    np->u.name.child->t == T_NUM) {
822 				/*
823 				 * an explicit instance number was given
824 				 * in the source.  so only consider this
825 				 * a configuration match if the number
826 				 * also matches.
827 				 */
828 				if (cp_num != np->u.name.child->u.ull)
829 					continue;
830 
831 			} else if (np->u.name.it != IT_HORIZONTAL) {
832 				struct iterinfo *iterinfop;
833 				const char *iters;
834 
835 				/*
836 				 * vertical iterator.  look it up in
837 				 * the appropriate lut and if we get
838 				 * back a value it is either one that we
839 				 * set earlier, in which case we record
840 				 * the new value for this iteration and
841 				 * keep matching, or it is one that was
842 				 * set by an earlier reference to the
843 				 * iterator, in which case we only consider
844 				 * this a configuration match if the number
845 				 * matches cp_num.
846 				 */
847 
848 				ASSERT(np->u.name.child != NULL);
849 				ASSERT(np->u.name.child->t == T_NAME);
850 				iters = np->u.name.child->u.name.s;
851 
852 				if ((iterinfop = lut_lookup(infop->ex,
853 				    (void *)iters, NULL)) == NULL) {
854 					/* we're the first use, record our np */
855 					infop->ex = lut_add(infop->ex,
856 					    (void *)iters,
857 					    newiterinfo(cp_num, np), NULL);
858 				} else if (np == iterinfop->np) {
859 					/*
860 					 * we're the first use, back again
861 					 * for another iteration.  so update
862 					 * the num bound to this iterator in
863 					 * the lut.
864 					 */
865 					iterinfop->num = cp_num;
866 				} else if (cp_num != iterinfop->num) {
867 					/*
868 					 * an earlier reference to this
869 					 * iterator bound it to a different
870 					 * instance number, so there's no
871 					 * match here after all.
872 					 *
873 					 * however, it's possible that this
874 					 * component should really be part of
875 					 * the wildcard.  we explore this by
876 					 * forcing this component into the
877 					 * wildcarded section.
878 					 *
879 					 * for an more details of what's
880 					 * going to happen now, see
881 					 * comments block below entitled
882 					 * "forcing components into
883 					 * wildcard path".
884 					 */
885 					if (np == wcp->nptop->u.event.epname)
886 						vmatch_event(infop,
887 						    config_child(cp), np, lnp,
888 						    anp, wcp);
889 					continue;
890 				}
891 			}
892 
893 			/*
894 			 * if this was an IT_HORIZONTAL name, hmatch() will
895 			 * expand all matches horizontally into a list.
896 			 * we know the list will contain at least
897 			 * one element (the one we just matched),
898 			 * so we just let hmatch_event() do the rest.
899 			 *
900 			 * recurse on to next component. Note that
901 			 * wildcarding is now turned off.
902 			 */
903 			np->u.name.cp = cp;
904 			vmatch_event(infop, config_child(cp), np->u.name.next,
905 			    lnp, anp, wcp);
906 			np->u.name.cp = NULL;
907 
908 			/*
909 			 * forcing components into wildcard path:
910 			 *
911 			 * if this component is the first match, force it
912 			 * to be part of the wildcarded path and see if we
913 			 * can get additional matches.
914 			 *
915 			 * here's an example.  suppose we have the
916 			 * definition
917 			 *	event foo@x/y
918 			 * and configuration
919 			 *	a0/x0/y0/a1/x1/y1
920 			 *
921 			 * the code up to this point will treat "a0" as the
922 			 * wildcarded part of the path and "x0/y0" as the
923 			 * nonwildcarded part, resulting in the instanced
924 			 * event
925 			 *	foo@a0/x0/y0
926 			 *
927 			 * in order to discover the next match (.../x1/y1)
928 			 * in the configuration we have to force "x0" into
929 			 * the wildcarded part of the path.
930 			 * by doing so, we discover the wildcarded part
931 			 * "a0/x0/y0/a1" and the nonwildcarded part "x1/y1"
932 			 *
933 			 * the following call to vmatch_event() is also
934 			 * needed to properly handle the configuration
935 			 *	b0/x0/b1/x1/y1
936 			 *
937 			 * the recursions into vmatch_event() will start
938 			 * off uncovering "b0" as the wildcarded part and
939 			 * "x0" as the start of the nonwildcarded path.
940 			 * however, the next recursion will not result in a
941 			 * match since there is no "y" following "x0".  the
942 			 * subsequent match of (wildcard = "b0/x0/b1" and
943 			 * nonwildcard = "x1/y1") will be discovered only
944 			 * if "x0" is forced to be a part of the wildcarded
945 			 * path.
946 			 */
947 			if (np == wcp->nptop->u.event.epname)
948 				vmatch_event(infop, config_child(cp), np, lnp,
949 				    anp, wcp);
950 
951 			if (np->u.name.it == IT_HORIZONTAL) {
952 				/*
953 				 * hmatch() finished iterating through
954 				 * the configuration as described above, so
955 				 * don't continue iterating here.
956 				 */
957 				return;
958 			}
959 		} else if (np == wcp->nptop->u.event.epname) {
960 			/*
961 			 * no match - carry on down the tree looking for
962 			 * wildcarding
963 			 */
964 			vmatch_event(infop, config_child(cp), np, lnp, anp,
965 			    wcp);
966 		}
967 	}
968 }
969 
970 /*
971  * vmatch -- find the next vertical expansion match in the config database
972  *
973  * this routine is called with three node pointers:
974  *	 np -- the parse we're matching
975  *	lnp -- the rest of the list we're currently working on
976  *	anp -- the rest of the arrow we're currently working on
977  *
978  * the expansion matching happens via three types of recursion:
979  *
980  *	- when given an arrow, handle the left-side and then recursively
981  *	  handle the right side (which might be another cascaded arrow).
982  *
983  *	- when handling one side of an arrow, recurse through the T_LIST
984  *	  to get to each event (or just move on to the event if there
985  *	  is a single event instead of a list)  since the arrow parse
986  *	  trees recurse left, we actually start with the right-most
987  *	  event list in the prop statement and work our way towards
988  *	  the left-most event list.
989  *
990  *	- when handling an event, recurse down each component of the
991  *	  pathname, matching in the config database and recording the
992  *	  matches in the explicit iterator dictionary as we go.
993  *
994  * when the bottom of this matching recursion is met, meaning we have
995  * set the "cp" pointers on all the names in the entire statement,
996  * we call hmatch() which does it's own recursion to handle horizontal
997  * expandsion and then call generate() to generate nodes, bubbles, and
998  * arrows in the instance tree.  generate() looks at the cp pointers to
999  * see what instance numbers were matched in the configuration database.
1000  *
1001  * when horizontal expansion appears, vmatch() finds only the first match
1002  * and hmatch() then takes the horizontal expansion through all the other
1003  * matches when generating the arrows in the instance tree.
1004  *
1005  * the "infop" passed down through the recursion contains a dictionary
1006  * of the explicit iterators (all the implicit iterators have been converted
1007  * to explicit iterators when the parse tree was created by tree.c), which
1008  * allows things like this to work correctly:
1009  *
1010  *	prop error.a@x[n]/y/z -> error.b@x/y[n]/z -> error.c@x/y/z[n];
1011  *
1012  * during the top level call, the explicit iterator "n" will match an
1013  * instance number in the config database, and the result will be recorded
1014  * in the explicit iterator dictionary and passed down via "infop".  so
1015  * when the recursive call tries to match y[n] in the config database, it
1016  * will only match the same instance number as x[n] did since the dictionary
1017  * is consulted to see if "n" took on a value already.
1018  *
1019  * at any point during the recursion, match*() can return to indicate
1020  * a match was not found in the config database and that the caller should
1021  * move on to the next potential match, if any.
1022  *
1023  * constraints are completely ignored by match(), so the statement:
1024  *
1025  *	prop error.a@x[n] -> error.b@x[n] {n != 0};
1026  *
1027  * might very well match x[0] if it appears in the config database.  it
1028  * is the generate() routine that takes that match and then decides what
1029  * arrow, if any, should be generated in the instance tree.  generate()
1030  * looks at the explicit iterator dictionary to get values like "n" in
1031  * the above example so that it can evaluate constraints.
1032  *
1033  */
1034 static void
1035 vmatch(struct info *infop, struct node *np, struct node *lnp, struct node *anp)
1036 {
1037 	struct node *np1, *np2, *oldepname, *oldnptop;
1038 	int epmatches;
1039 	struct config *cp;
1040 	struct wildcardinfo *wcp;
1041 
1042 	if (np == NULL) {
1043 		if (lnp)
1044 			vmatch(infop, lnp, NULL, anp);
1045 		else if (anp)
1046 			vmatch(infop, anp, NULL, NULL);
1047 		else {
1048 			struct node *src;
1049 			struct node *dst;
1050 
1051 			/* end of vertical match recursion */
1052 			outfl(O_ALTFP|O_VERB3|O_NONL,
1053 			    infop->anp->file, infop->anp->line, "vmatch: ");
1054 			ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, infop->anp);
1055 			out(O_ALTFP|O_VERB3, NULL);
1056 
1057 			generate_nork(
1058 			    itree_np2nork(infop->anp->u.arrow.nnp),
1059 			    itree_np2nork(infop->anp->u.arrow.knp));
1060 			dst = infop->anp->u.arrow.rhs;
1061 			src = infop->anp->u.arrow.lhs;
1062 			for (;;) {
1063 				generate_new();	/* new set of arrows */
1064 				if (src->t == T_ARROW) {
1065 					hmatch(infop, src->u.arrow.rhs, dst);
1066 					generate_nork(
1067 					    itree_np2nork(src->u.arrow.nnp),
1068 					    itree_np2nork(src->u.arrow.knp));
1069 					dst = src->u.arrow.rhs;
1070 					src = src->u.arrow.lhs;
1071 				} else {
1072 					hmatch(infop, src, dst);
1073 					break;
1074 				}
1075 			}
1076 		}
1077 		return;
1078 	}
1079 
1080 	switch (np->t) {
1081 	case T_EVENT: {
1082 		epmatches = 0;
1083 		/*
1084 		 * see if we already have a match in the wcps
1085 		 */
1086 		for (wcp = wcproot; wcp; wcp = wcp->next) {
1087 			oldepname = wcp->oldepname;
1088 			oldnptop = wcp->nptop;
1089 			for (np1 = oldepname, np2 = np->u.event.epname;
1090 			    np1 != NULL && np2 != NULL; np1 = np1->u.name.next,
1091 			    np2 = np2->u.name.next) {
1092 				if (strcmp(np1->u.name.s, np2->u.name.s) != 0)
1093 					break;
1094 				if (np1->u.name.child->t !=
1095 				    np2->u.name.child->t)
1096 					break;
1097 				if (np1->u.name.child->t == T_NUM &&
1098 				    np1->u.name.child->u.ull !=
1099 				    np2->u.name.child->u.ull)
1100 					break;
1101 				if (np1->u.name.child->t == T_NAME &&
1102 				    strcmp(np1->u.name.child->u.name.s,
1103 				    np2->u.name.child->u.name.s) != 0)
1104 					break;
1105 				epmatches++;
1106 			}
1107 			if (epmatches)
1108 				break;
1109 		}
1110 		if (epmatches && np1 == NULL && np2 == NULL) {
1111 			/*
1112 			 * complete names match, can just borrow the fields
1113 			 */
1114 			oldepname = np->u.event.epname;
1115 			np->u.event.epname = oldnptop->u.event.epname;
1116 			np->u.event.oldepname = wcp->oldepname;
1117 			np->u.event.ewname = wcp->ewname;
1118 			vmatch(infop, NULL, lnp, anp);
1119 			np->u.event.epname = oldepname;
1120 			return;
1121 		}
1122 		if (epmatches) {
1123 			/*
1124 			 * just first part of names match - do wildcarding
1125 			 * by using existing wcp including ewname and also
1126 			 * copying as much of pwname as is valid, then start
1127 			 * vmatch_event() at start of non-matching section
1128 			 */
1129 			for (np1 = oldepname, np2 = np->u.event.epname;
1130 			    epmatches != 0; epmatches--) {
1131 				cp = np1->u.name.cp;
1132 				np2->u.name.cp = cp;
1133 				np1 = np1->u.name.next;
1134 				np2 = np2->u.name.next;
1135 			}
1136 			wcp->oldepname = np->u.event.epname;
1137 			wcp->nptop = np;
1138 			vmatch_event(infop, config_child(cp), np2, lnp,
1139 			    anp, wcp);
1140 			wcp->oldepname = oldepname;
1141 			wcp->nptop = oldnptop;
1142 			return;
1143 		}
1144 		/*
1145 		 * names do not match - allocate a new wcp
1146 		 */
1147 		wcp = MALLOC(sizeof (struct wildcardinfo));
1148 		wcp->next = wcproot;
1149 		wcproot = wcp;
1150 		wcp->nptop = np;
1151 		wcp->oldepname = np->u.event.epname;
1152 		wcp->ewname = NULL;
1153 
1154 		vmatch_event(infop, config_child(infop->croot),
1155 		    np->u.event.epname, lnp, anp, wcp);
1156 
1157 		wcproot = wcp->next;
1158 		FREE(wcp);
1159 		break;
1160 	}
1161 	case T_LIST:
1162 		ASSERT(lnp == NULL);
1163 		vmatch(infop, np->u.expr.right, np->u.expr.left, anp);
1164 		break;
1165 
1166 	case T_ARROW:
1167 		ASSERT(lnp == NULL && anp == NULL);
1168 		vmatch(infop, np->u.arrow.rhs, NULL, np->u.arrow.lhs);
1169 		break;
1170 
1171 	default:
1172 		outfl(O_DIE, np->file, np->line,
1173 		    "vmatch: unexpected type: %s",
1174 		    ptree_nodetype2str(np->t));
1175 	}
1176 }
1177 
1178 static void
1179 cp_reset(struct node *np)
1180 {
1181 	if (np == NULL)
1182 		return;
1183 	switch (np->t) {
1184 	case T_NAME:
1185 		np->u.name.cp = NULL;
1186 		cp_reset(np->u.name.next);
1187 		break;
1188 
1189 	case T_LIST:
1190 		cp_reset(np->u.expr.left);
1191 		cp_reset(np->u.expr.right);
1192 		break;
1193 
1194 	case T_ARROW:
1195 		cp_reset(np->u.arrow.lhs);
1196 		cp_reset(np->u.arrow.rhs);
1197 		break;
1198 
1199 	case T_EVENT:
1200 		cp_reset(np->u.event.epname);
1201 		break;
1202 	}
1203 }
1204 
1205 /*
1206  * itree_create -- apply the current config to the current parse tree
1207  *
1208  * returns a lut mapping fully-instance-qualified names to struct events.
1209  *
1210  */
1211 struct lut *
1212 itree_create(struct config *croot)
1213 {
1214 	struct lut *retval;
1215 	struct node *propnp;
1216 	extern int alloc_total();
1217 	int init_size;
1218 
1219 	Ninfo.lut = NULL;
1220 	Ninfo.croot = croot;
1221 	init_size = alloc_total();
1222 	out(O_ALTFP|O_STAMP, "start itree_create using %d bytes", init_size);
1223 	for (propnp = Props; propnp; propnp = propnp->u.stmt.next) {
1224 		struct node *anp = propnp->u.stmt.np;
1225 
1226 		ASSERTeq(anp->t, T_ARROW, ptree_nodetype2str);
1227 
1228 		if (!anp->u.arrow.needed)
1229 			continue;
1230 		Ninfo.anp = anp;
1231 		Ninfo.ex = NULL;
1232 
1233 		generate_arrownp(anp);
1234 		vmatch(&Ninfo, anp, NULL, NULL);
1235 
1236 		if (Ninfo.ex) {
1237 			lut_free(Ninfo.ex, iterinfo_destructor, NULL);
1238 			Ninfo.ex = NULL;
1239 		}
1240 		cp_reset(anp);
1241 	}
1242 
1243 	out(O_ALTFP|O_STAMP, "itree_create added %d bytes",
1244 	    alloc_total() - init_size);
1245 	retval = Ninfo.lut;
1246 	Ninfo.lut = NULL;
1247 	return (retval);
1248 }
1249 
1250 /*
1251  * initial first pass of the rules.
1252  * We don't use the config at all. Just check the last part of the pathname
1253  * in the rules. If this matches the last part of the pathname in the first
1254  * ereport, then set pathname to the pathname in the ereport. If not then
1255  * set pathname to just the last part of pathname with instance number 0.
1256  * Constraints are ignored and all nork values are set to 0. If after all that
1257  * any rules can still not be associated with the ereport, then they are set
1258  * to not needed in prune_propagations() and ignored in the real itree_create()
1259  * which follows.
1260  */
1261 
1262 static struct event *
1263 add_event_dummy(struct node *np, const struct ipath *ipp)
1264 {
1265 	struct event *ret;
1266 	struct event searchevent;	/* just used for searching */
1267 	extern struct ipath *ipath_dummy(struct node *, struct ipath *);
1268 
1269 	searchevent.enode = np;
1270 	searchevent.ipp = ipath_dummy(np->u.event.epname, (struct ipath *)ipp);
1271 	if ((ret = lut_lookup(Ninfo.lut, (void *)&searchevent,
1272 	    (lut_cmp)event_cmp)) != NULL)
1273 		return (ret);
1274 
1275 	ret = alloc_xmalloc(sizeof (*ret));
1276 	bzero(ret, sizeof (*ret));
1277 	ret->t = np->u.event.ename->u.name.t;
1278 	ret->enode = np;
1279 	ret->ipp = searchevent.ipp;
1280 	Ninfo.lut = lut_add(Ninfo.lut, (void *)ret, (void *)ret,
1281 	    (lut_cmp)event_cmp);
1282 	return (ret);
1283 }
1284 
1285 /*ARGSUSED*/
1286 struct lut *
1287 itree_create_dummy(const char *e0class, const struct ipath *e0ipp)
1288 {
1289 	struct node *propnp;
1290 	struct event *frome, *toe;
1291 	struct bubble *frombp, *tobp;
1292 	struct arrow *arrowp;
1293 	struct node *src, *dst, *slst, *dlst, *arrownp, *oldarrownp;
1294 	int gen = 0;
1295 	extern int alloc_total();
1296 	int init_size;
1297 
1298 	Ninfo.lut = NULL;
1299 	init_size = alloc_total();
1300 	out(O_ALTFP|O_STAMP, "start itree_create using %d bytes", init_size);
1301 	for (propnp = Props; propnp; propnp = propnp->u.stmt.next) {
1302 		arrownp = propnp->u.stmt.np;
1303 		while (arrownp) {
1304 			gen++;
1305 			dlst = arrownp->u.arrow.rhs;
1306 			slst = arrownp->u.arrow.lhs;
1307 			oldarrownp = arrownp;
1308 			if (slst->t == T_ARROW) {
1309 				arrownp = slst;
1310 				slst = slst->u.arrow.rhs;
1311 			} else {
1312 				arrownp = NULL;
1313 			}
1314 			while (slst) {
1315 				if (slst->t == T_LIST) {
1316 					src = slst->u.expr.right;
1317 					slst = slst->u.expr.left;
1318 				} else {
1319 					src = slst;
1320 					slst = NULL;
1321 				}
1322 				frome = add_event_dummy(src, e0ipp);
1323 				frombp = itree_add_bubble(frome, B_FROM, 0, 0);
1324 				while (dlst) {
1325 					if (dlst->t == T_LIST) {
1326 						dst = dlst->u.expr.right;
1327 						dlst = dlst->u.expr.left;
1328 					} else {
1329 						dst = dlst;
1330 						dlst = NULL;
1331 					}
1332 					arrowp = alloc_xmalloc(
1333 					    sizeof (struct arrow));
1334 					bzero(arrowp, sizeof (struct arrow));
1335 					arrowp->pnode = oldarrownp;
1336 					toe = add_event_dummy(dst, e0ipp);
1337 					tobp = itree_add_bubble(toe, B_TO, 0,
1338 					    gen);
1339 					arrowp->tail = frombp;
1340 					arrowp->head = tobp;
1341 					add_arrow(frombp, arrowp);
1342 					add_arrow(tobp, arrowp);
1343 					arrow_add_within(arrowp,
1344 					    dst->u.event.declp->u.stmt.np->
1345 					    u.event.eexprlist);
1346 					arrow_add_within(arrowp,
1347 					    dst->u.event.eexprlist);
1348 				}
1349 			}
1350 		}
1351 	}
1352 	out(O_ALTFP|O_STAMP, "itree_create added %d bytes",
1353 	    alloc_total() - init_size);
1354 	return (Ninfo.lut);
1355 }
1356 
1357 void
1358 itree_free(struct lut *lutp)
1359 {
1360 	int init_size;
1361 
1362 	init_size = alloc_total();
1363 	out(O_ALTFP|O_STAMP, "start itree_free");
1364 	lut_free(lutp, itree_destructor, NULL);
1365 	out(O_ALTFP|O_STAMP, "itree_free freed %d bytes",
1366 	    init_size - alloc_total());
1367 }
1368 
1369 void
1370 itree_prune(struct lut *lutp)
1371 {
1372 	int init_size;
1373 
1374 	init_size = alloc_total();
1375 	out(O_ALTFP|O_STAMP, "start itree_prune");
1376 	lut_walk(lutp, itree_pruner, NULL);
1377 	out(O_ALTFP|O_STAMP, "itree_prune freed %d bytes",
1378 	    init_size - alloc_total());
1379 }
1380 
1381 void
1382 itree_pevent_brief(int flags, struct event *ep)
1383 {
1384 	ASSERT(ep != NULL);
1385 	ASSERT(ep->enode != NULL);
1386 	ASSERT(ep->ipp != NULL);
1387 
1388 	ipath_print(flags, ep->enode->u.event.ename->u.name.s, ep->ipp);
1389 }
1390 
1391 /*ARGSUSED*/
1392 static void
1393 itree_pevent(struct event *lhs, struct event *ep, void *arg)
1394 {
1395 	struct plut_wlk_data propd;
1396 	struct bubble *bp;
1397 	int flags = (int)(intptr_t)arg;
1398 
1399 	itree_pevent_brief(flags, ep);
1400 	if (ep->t == N_EREPORT)
1401 		out(flags, " (count %d)", ep->count);
1402 	else
1403 		out(flags, NULL);
1404 
1405 	if (ep->props) {
1406 		propd.flags = flags;
1407 		propd.first = 1;
1408 		out(flags, "Properties:");
1409 		lut_walk(ep->props, ptree_plut, (void *)&propd);
1410 	}
1411 
1412 	for (bp = itree_next_bubble(ep, NULL); bp;
1413 	    bp = itree_next_bubble(ep, bp)) {
1414 		/* Print only TO bubbles in this loop */
1415 		if (bp->t != B_TO)
1416 			continue;
1417 		itree_pbubble(flags, bp);
1418 	}
1419 
1420 	for (bp = itree_next_bubble(ep, NULL); bp;
1421 	    bp = itree_next_bubble(ep, bp)) {
1422 		/* Print only INHIBIT bubbles in this loop */
1423 		if (bp->t != B_INHIBIT)
1424 			continue;
1425 		itree_pbubble(flags, bp);
1426 	}
1427 
1428 	for (bp = itree_next_bubble(ep, NULL); bp;
1429 	    bp = itree_next_bubble(ep, bp)) {
1430 		/* Print only FROM bubbles in this loop */
1431 		if (bp->t != B_FROM)
1432 			continue;
1433 		itree_pbubble(flags, bp);
1434 	}
1435 }
1436 
1437 static void
1438 itree_pbubble(int flags, struct bubble *bp)
1439 {
1440 	struct constraintlist *cp;
1441 	struct arrowlist *ap;
1442 
1443 	ASSERT(bp != NULL);
1444 
1445 	out(flags|O_NONL, "   ");
1446 	if (bp->mark)
1447 		out(flags|O_NONL, "*");
1448 	else
1449 		out(flags|O_NONL, " ");
1450 	if (bp->t == B_FROM)
1451 		out(flags|O_NONL, "N=%d to:", bp->nork);
1452 	else if (bp->t == B_TO)
1453 		out(flags|O_NONL, "K=%d from:", bp->nork);
1454 	else
1455 		out(flags|O_NONL, "K=%d masked from:", bp->nork);
1456 
1457 	if (bp->t == B_TO || bp->t == B_INHIBIT) {
1458 		for (ap = itree_next_arrow(bp, NULL); ap;
1459 		    ap = itree_next_arrow(bp, ap)) {
1460 			ASSERT(ap->arrowp->head == bp);
1461 			ASSERT(ap->arrowp->tail != NULL);
1462 			ASSERT(ap->arrowp->tail->myevent != NULL);
1463 			out(flags|O_NONL, " ");
1464 			itree_pevent_brief(flags, ap->arrowp->tail->myevent);
1465 		}
1466 		out(flags, NULL);
1467 		return;
1468 	}
1469 
1470 	for (ap = itree_next_arrow(bp, NULL); ap;
1471 	    ap = itree_next_arrow(bp, ap)) {
1472 		ASSERT(ap->arrowp->tail == bp);
1473 		ASSERT(ap->arrowp->head != NULL);
1474 		ASSERT(ap->arrowp->head->myevent != NULL);
1475 
1476 		out(flags|O_NONL, " ");
1477 		itree_pevent_brief(flags, ap->arrowp->head->myevent);
1478 
1479 		out(flags|O_NONL, " ");
1480 		ptree_timeval(flags, &ap->arrowp->mindelay);
1481 		out(flags|O_NONL, ",");
1482 		ptree_timeval(flags, &ap->arrowp->maxdelay);
1483 
1484 		/* Display anything from the propogation node? */
1485 		out(O_VERB3|O_NONL, " <%s:%d>",
1486 		    ap->arrowp->pnode->file, ap->arrowp->pnode->line);
1487 
1488 		if (itree_next_constraint(ap->arrowp, NULL))
1489 			out(flags|O_NONL, " {");
1490 
1491 		for (cp = itree_next_constraint(ap->arrowp, NULL); cp;
1492 		    cp = itree_next_constraint(ap->arrowp, cp)) {
1493 			ptree(flags, cp->cnode, 1, 0);
1494 			if (itree_next_constraint(ap->arrowp, cp))
1495 				out(flags|O_NONL, ", ");
1496 		}
1497 
1498 		if (itree_next_constraint(ap->arrowp, NULL))
1499 			out(flags|O_NONL, "}");
1500 	}
1501 	out(flags, NULL);
1502 }
1503 
1504 void
1505 itree_ptree(int flags, struct lut *itp)
1506 {
1507 	lut_walk(itp, (lut_cb)itree_pevent, (void *)(intptr_t)flags);
1508 }
1509 
1510 /*ARGSUSED*/
1511 static void
1512 itree_destructor(void *left, void *right, void *arg)
1513 {
1514 	struct event *ep = (struct event *)right;
1515 	struct bubble *nextbub, *bub;
1516 
1517 	/* Free the properties */
1518 	if (ep->props)
1519 		lut_free(ep->props, instances_destructor, NULL);
1520 
1521 	/* Free the payload properties */
1522 	if (ep->payloadprops)
1523 		lut_free(ep->payloadprops, payloadprops_destructor, NULL);
1524 
1525 	/* Free my bubbles */
1526 	for (bub = ep->bubbles; bub != NULL; ) {
1527 		nextbub = bub->next;
1528 		/*
1529 		 * Free arrows if they are FROM me.  Free arrowlists on
1530 		 * other types of bubbles (but not the attached arrows,
1531 		 * which will be freed when we free the originating
1532 		 * bubble.
1533 		 */
1534 		if (bub->t == B_FROM)
1535 			itree_free_arrowlists(bub, 1);
1536 		else
1537 			itree_free_arrowlists(bub, 0);
1538 		itree_free_bubble(bub);
1539 		bub = nextbub;
1540 	}
1541 
1542 	if (ep->nvp != NULL)
1543 		nvlist_free(ep->nvp);
1544 	alloc_xfree(ep, sizeof (*ep));
1545 }
1546 
1547 /*ARGSUSED*/
1548 static void
1549 itree_pruner(void *left, void *right, void *arg)
1550 {
1551 	struct event *ep = (struct event *)right;
1552 	struct bubble *nextbub, *bub;
1553 
1554 	if (ep->keep_in_tree)
1555 		return;
1556 
1557 	/* Free the properties */
1558 	lut_free(ep->props, instances_destructor, NULL);
1559 
1560 	/* Free the payload properties */
1561 	lut_free(ep->payloadprops, payloadprops_destructor, NULL);
1562 
1563 	/* Free my bubbles */
1564 	for (bub = ep->bubbles; bub != NULL; ) {
1565 		nextbub = bub->next;
1566 		itree_prune_arrowlists(bub);
1567 		itree_free_bubble(bub);
1568 		bub = nextbub;
1569 	}
1570 
1571 	if (ep->nvp != NULL)
1572 		nvlist_free(ep->nvp);
1573 	ep->props = NULL;
1574 	ep->payloadprops = NULL;
1575 	ep->bubbles = NULL;
1576 	ep->nvp = NULL;
1577 }
1578 
1579 static void
1580 itree_free_bubble(struct bubble *freeme)
1581 {
1582 	alloc_xfree(freeme, sizeof (*freeme));
1583 }
1584 
1585 static struct bubble *
1586 itree_add_bubble(struct event *eventp, enum bubbletype btype, int nork, int gen)
1587 {
1588 	struct bubble *prev = NULL;
1589 	struct bubble *curr;
1590 	struct bubble *newb;
1591 
1592 	/* Use existing bubbles as appropriate when possible */
1593 	for (curr = eventp->bubbles;
1594 	    curr != NULL;
1595 	    prev = curr, curr = curr->next) {
1596 		if (btype == B_TO && curr->t == B_TO) {
1597 			/* see if an existing "to" bubble works for us */
1598 			if (gen == curr->gen)
1599 				return (curr);	/* matched gen number */
1600 			else if (nork == 1 && curr->nork == 1) {
1601 				curr->gen = gen;
1602 				return (curr);	/* coalesce K==1 bubbles */
1603 			}
1604 		} else if (btype == B_FROM && curr->t == B_FROM) {
1605 			/* see if an existing "from" bubble works for us */
1606 			if ((nork == N_IS_ALL && curr->nork == N_IS_ALL) ||
1607 			    (nork == 0 && curr->nork == 0))
1608 				return (curr);
1609 		}
1610 	}
1611 
1612 	newb = alloc_xmalloc(sizeof (struct bubble));
1613 	newb->next = NULL;
1614 	newb->t = btype;
1615 	newb->myevent = eventp;
1616 	newb->nork = nork;
1617 	newb->mark = 0;
1618 	newb->gen = gen;
1619 	newb->arrows = NULL;
1620 
1621 	if (prev == NULL)
1622 		eventp->bubbles = newb;
1623 	else
1624 		prev->next = newb;
1625 
1626 	return (newb);
1627 }
1628 
1629 struct bubble *
1630 itree_next_bubble(struct event *eventp, struct bubble *last)
1631 {
1632 	struct bubble *next;
1633 
1634 	for (;;) {
1635 		if (last != NULL)
1636 			next = last->next;
1637 		else
1638 			next = eventp->bubbles;
1639 
1640 		if (next == NULL || next->arrows != NULL)
1641 			return (next);
1642 
1643 		/* bubble was empty, skip it */
1644 		last = next;
1645 	}
1646 }
1647 
1648 static void
1649 add_arrow(struct bubble *bp, struct arrow *ap)
1650 {
1651 	struct arrowlist *prev = NULL;
1652 	struct arrowlist *curr;
1653 	struct arrowlist *newal;
1654 
1655 	newal = alloc_xmalloc(sizeof (struct arrowlist));
1656 	bzero(newal, sizeof (struct arrowlist));
1657 	newal->arrowp = ap;
1658 
1659 	curr = itree_next_arrow(bp, NULL);
1660 	while (curr != NULL) {
1661 		prev = curr;
1662 		curr = itree_next_arrow(bp, curr);
1663 	}
1664 
1665 	if (prev == NULL)
1666 		bp->arrows = newal;
1667 	else
1668 		prev->next = newal;
1669 }
1670 
1671 static struct arrow *
1672 itree_add_arrow(struct node *apnode, struct node *fromevent,
1673     struct node *toevent, struct lut *ex)
1674 {
1675 	struct arrow *newa;
1676 
1677 	newa = alloc_xmalloc(sizeof (struct arrow));
1678 	bzero(newa, sizeof (struct arrow));
1679 	newa->pnode = apnode;
1680 	newa->constraints = NULL;
1681 
1682 	/*
1683 	 *  Set default delays, then try to re-set them from
1684 	 *  any within() constraints.
1685 	 */
1686 	newa->mindelay = newa->maxdelay = 0ULL;
1687 	if (itree_set_arrow_traits(newa, fromevent, toevent, ex) == 0) {
1688 		alloc_xfree(newa, sizeof (struct arrow));
1689 		return (NULL);
1690 	}
1691 
1692 	return (newa);
1693 }
1694 
1695 /* returns false if traits show that arrow should not be added after all */
1696 static int
1697 itree_set_arrow_traits(struct arrow *ap, struct node *fromev,
1698     struct node *toev, struct lut *ex)
1699 {
1700 	struct node *events[] = { NULL, NULL, NULL };
1701 	struct node *newc = NULL;
1702 
1703 	ASSERTeq(fromev->t, T_EVENT, ptree_nodetype2str);
1704 	ASSERTeq(toev->t, T_EVENT, ptree_nodetype2str);
1705 
1706 	/*
1707 	 * search for the within values first on the declaration of
1708 	 * the destination event, and then on the prop.  this allows
1709 	 * one to specify a "default" within by putting it on the
1710 	 * declaration,  but then allow overriding on the prop statement.
1711 	 */
1712 	arrow_add_within(ap, toev->u.event.declp->u.stmt.np->u.event.eexprlist);
1713 	arrow_add_within(ap, toev->u.event.eexprlist);
1714 
1715 	/*
1716 	 * handle any global constraints inherited from the
1717 	 * "fromev" event's declaration
1718 	 */
1719 	ASSERT(fromev->u.event.declp != NULL);
1720 	ASSERT(fromev->u.event.declp->u.stmt.np != NULL);
1721 
1722 #ifdef	notdef
1723 	/* XXX not quite ready to evaluate constraints from decls yet */
1724 	if (fromev->u.event.declp->u.stmt.np->u.event.eexprlist)
1725 		(void) itree_add_constraint(ap,
1726 		    fromev->u.event.declp->u.stmt.np->u.event.eexprlist);
1727 #endif	/* notdef */
1728 
1729 	/* handle constraints on the from event in the prop statement */
1730 	events[0] = fromev;
1731 	events[1] = toev;
1732 	if (eval_potential(fromev->u.event.eexprlist, ex, events, &newc,
1733 	    Ninfo.croot) == 0)
1734 		return (0);		/* constraint disallows arrow */
1735 
1736 	/*
1737 	 * handle any global constraints inherited from the
1738 	 * "toev" event's declaration
1739 	 */
1740 	ASSERT(toev->u.event.declp != NULL);
1741 	ASSERT(toev->u.event.declp->u.stmt.np != NULL);
1742 
1743 #ifdef	notdef
1744 	/* XXX not quite ready to evaluate constraints from decls yet */
1745 	if (toev->u.event.declp->u.stmt.np->u.event.eexprlist)
1746 		(void) itree_add_constraint(ap,
1747 		    toev->u.event.declp->u.stmt.np->u.event.eexprlist);
1748 #endif	/* notdef */
1749 
1750 	/* handle constraints on the to event in the prop statement */
1751 	events[0] = toev;
1752 	events[1] = fromev;
1753 	if (eval_potential(toev->u.event.eexprlist, ex, events, &newc,
1754 	    Ninfo.croot) == 0) {
1755 		if (newc != NULL)
1756 			tree_free(newc);
1757 		return (0);		/* constraint disallows arrow */
1758 	}
1759 
1760 	/* if we came up with any deferred constraints, add them to arrow */
1761 	if (newc != NULL) {
1762 		out(O_ALTFP|O_VERB3, "(deferred constraints)");
1763 		(void) itree_add_constraint(ap, iexpr(newc));
1764 	}
1765 
1766 	return (1);	/* constraints allow arrow */
1767 }
1768 
1769 /*
1770  * Set within() constraint.  If the constraint were set multiple times,
1771  * the last one would "win".
1772  */
1773 static void
1774 arrow_add_within(struct arrow *ap, struct node *xpr)
1775 {
1776 	struct node *arglist;
1777 
1778 	/* end of expressions list */
1779 	if (xpr == NULL)
1780 		return;
1781 
1782 	switch (xpr->t) {
1783 	case T_LIST:
1784 		arrow_add_within(ap, xpr->u.expr.left);
1785 		arrow_add_within(ap, xpr->u.expr.right);
1786 		return;
1787 	case T_FUNC:
1788 		if (xpr->u.func.s != L_within)
1789 			return;
1790 		arglist = xpr->u.func.arglist;
1791 		switch (arglist->t) {
1792 		case T_TIMEVAL:
1793 			ap->mindelay = 0;
1794 			ap->maxdelay = arglist->u.ull;
1795 			break;
1796 		case T_NAME:
1797 			ASSERT(arglist->u.name.s == L_infinity);
1798 			ap->mindelay = 0;
1799 			ap->maxdelay = TIMEVAL_EVENTUALLY;
1800 			break;
1801 		case T_LIST:
1802 			ASSERT(arglist->u.expr.left->t == T_TIMEVAL);
1803 			ap->mindelay = arglist->u.expr.left->u.ull;
1804 			switch (arglist->u.expr.right->t) {
1805 			case T_TIMEVAL:
1806 				ap->maxdelay = arglist->u.ull;
1807 				break;
1808 			case T_NAME:
1809 				ASSERT(arglist->u.expr.right->u.name.s ==
1810 				    L_infinity);
1811 				ap->maxdelay = TIMEVAL_EVENTUALLY;
1812 				break;
1813 			default:
1814 				out(O_DIE, "within: unexpected 2nd arg type");
1815 			}
1816 			break;
1817 		default:
1818 			out(O_DIE, "within: unexpected 1st arg type");
1819 		}
1820 		break;
1821 	default:
1822 		return;
1823 	}
1824 }
1825 
1826 static void
1827 itree_free_arrowlists(struct bubble *bubp, int arrows_too)
1828 {
1829 	struct arrowlist *al, *nal;
1830 
1831 	al = bubp->arrows;
1832 	while (al != NULL) {
1833 		nal = al->next;
1834 		if (arrows_too) {
1835 			itree_free_constraints(al->arrowp);
1836 			alloc_xfree(al->arrowp, sizeof (struct arrow));
1837 		}
1838 		alloc_xfree(al, sizeof (*al));
1839 		al = nal;
1840 	}
1841 }
1842 
1843 static void
1844 itree_delete_arrow(struct bubble *bubp, struct arrow *arrow)
1845 {
1846 	struct arrowlist *al, *oal;
1847 
1848 	al = bubp->arrows;
1849 	if (al->arrowp == arrow) {
1850 		bubp->arrows = al->next;
1851 		alloc_xfree(al, sizeof (*al));
1852 		return;
1853 	}
1854 	while (al != NULL) {
1855 		oal = al;
1856 		al = al->next;
1857 		ASSERT(al != NULL);
1858 		if (al->arrowp == arrow) {
1859 			oal->next = al->next;
1860 			alloc_xfree(al, sizeof (*al));
1861 			return;
1862 		}
1863 	}
1864 }
1865 
1866 static void
1867 itree_prune_arrowlists(struct bubble *bubp)
1868 {
1869 	struct arrowlist *al, *nal;
1870 
1871 	al = bubp->arrows;
1872 	while (al != NULL) {
1873 		nal = al->next;
1874 		if (bubp->t == B_FROM)
1875 			itree_delete_arrow(al->arrowp->head, al->arrowp);
1876 		else
1877 			itree_delete_arrow(al->arrowp->tail, al->arrowp);
1878 		itree_free_constraints(al->arrowp);
1879 		alloc_xfree(al->arrowp, sizeof (struct arrow));
1880 		alloc_xfree(al, sizeof (*al));
1881 		al = nal;
1882 	}
1883 }
1884 
1885 struct arrowlist *
1886 itree_next_arrow(struct bubble *bubble, struct arrowlist *last)
1887 {
1888 	struct arrowlist *next;
1889 
1890 	if (last != NULL)
1891 		next = last->next;
1892 	else
1893 		next = bubble->arrows;
1894 	return (next);
1895 }
1896 
1897 static struct constraintlist *
1898 itree_add_constraint(struct arrow *arrowp, struct node *c)
1899 {
1900 	struct constraintlist *prev = NULL;
1901 	struct constraintlist *curr;
1902 	struct constraintlist *newc;
1903 
1904 	for (curr = arrowp->constraints;
1905 	    curr != NULL;
1906 	    prev = curr, curr = curr->next)
1907 		;
1908 
1909 	newc = alloc_xmalloc(sizeof (struct constraintlist));
1910 	newc->next = NULL;
1911 	newc->cnode = c;
1912 
1913 	if (prev == NULL)
1914 		arrowp->constraints = newc;
1915 	else
1916 		prev->next = newc;
1917 
1918 	return (newc);
1919 }
1920 
1921 struct constraintlist *
1922 itree_next_constraint(struct arrow *arrowp, struct constraintlist *last)
1923 {
1924 	struct constraintlist *next;
1925 
1926 	if (last != NULL)
1927 		next = last->next;
1928 	else
1929 		next = arrowp->constraints;
1930 	return (next);
1931 }
1932 
1933 static void
1934 itree_free_constraints(struct arrow *ap)
1935 {
1936 	struct constraintlist *cl, *ncl;
1937 
1938 	cl = ap->constraints;
1939 	while (cl != NULL) {
1940 		ncl = cl->next;
1941 		ASSERT(cl->cnode != NULL);
1942 		if (!iexpr_cached(cl->cnode))
1943 			tree_free(cl->cnode);
1944 		else
1945 			iexpr_free(cl->cnode);
1946 		alloc_xfree(cl, sizeof (*cl));
1947 		cl = ncl;
1948 	}
1949 }
1950 
1951 const char *
1952 itree_bubbletype2str(enum bubbletype t)
1953 {
1954 	static char buf[100];
1955 
1956 	switch (t) {
1957 	case B_FROM:	return L_from;
1958 	case B_TO:	return L_to;
1959 	case B_INHIBIT:	return L_inhibit;
1960 	default:
1961 		(void) sprintf(buf, "[unexpected bubbletype: %d]", t);
1962 		return (buf);
1963 	}
1964 }
1965 
1966 /*
1967  * itree_fini -- clean up any half-built itrees
1968  */
1969 void
1970 itree_fini(void)
1971 {
1972 	if (Ninfo.lut != NULL) {
1973 		itree_free(Ninfo.lut);
1974 		Ninfo.lut = NULL;
1975 	}
1976 	if (Ninfo.ex) {
1977 		lut_free(Ninfo.ex, iterinfo_destructor, NULL);
1978 		Ninfo.ex = NULL;
1979 	}
1980 }
1981