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