/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * itree.c -- instance tree creation and manipulation * * this module provides the instance tree */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include #include "alloc.h" #include "out.h" #include "stable.h" #include "literals.h" #include "lut.h" #include "tree.h" #include "ptree.h" #include "itree.h" #include "ipath.h" #include "iexpr.h" #include "eval.h" #include "config.h" /* * struct info contains the state we keep when expanding a prop statement * as part of constructing the instance tree. state kept in struct info * is the non-recursive stuff -- the stuff that doesn't need to be on * the stack. the rest of the state that is passed between all the * mutually recursive functions, is required to be on the stack since * we need to backtrack and recurse as we do the instance tree construction. */ struct info { struct lut *lut; struct node *anp; /* arrow np */ struct lut *ex; /* dictionary of explicit iterators */ struct config *croot; } Ninfo; /* * struct wildcardinfo is used to track wildcarded portions of paths. * * for example, if the epname of an event is "c/d" and the path "a/b/c/d" * exists, the wildcard path ewname is filled in with the path "a/b". when * matching is done, epname is temporarily replaced with the concatenation * of ewname and epname. * * a linked list of these structs is used to track the expansion of each * event node as it is processed in vmatch() --> vmatch_event() calls. */ struct wildcardinfo { struct node *nptop; /* event node fed to vmatch */ struct node *oldepname; /* epname without the wildcard part */ struct node *ewname; /* wildcard path */ struct wildcardinfo *next; }; static struct wildcardinfo *wcproot = NULL; static void vmatch(struct info *infop, struct node *np, struct node *lnp, struct node *anp); static void hmatch(struct info *infop, struct node *np, struct node *nextnp); static void itree_pbubble(int flags, struct bubble *bp); static void itree_pruner(void *left, void *right, void *arg); static void itree_destructor(void *left, void *right, void *arg); static int itree_set_arrow_traits(struct arrow *ap, struct node *fromev, struct node *toev, struct lut *ex); static void itree_free_arrowlists(struct bubble *bubp, int arrows_too); static void itree_prune_arrowlists(struct bubble *bubp); static void arrow_add_within(struct arrow *ap, struct node *xpr); static struct arrow *itree_add_arrow(struct node *apnode, struct node *fromevent, struct node *toevent, struct lut *ex); static struct event *find_or_add_event(struct info *infop, struct node *np); static void add_arrow(struct bubble *bp, struct arrow *ap); static struct constraintlist *itree_add_constraint(struct arrow *arrowp, struct node *c); static struct bubble *itree_add_bubble(struct event *eventp, enum bubbletype btype, int nork, int gen); static void itree_free_bubble(struct bubble *freeme); static void itree_free_constraints(struct arrow *ap); /* * the following struct contains the state we build up during * vertical and horizontal expansion so that generate() * has everything it needs to construct the appropriate arrows. * after setting up the state by calling: * generate_arrownp() * generate_nork() * generate_new() * generate_from() * generate_to() * the actual arrow generation is done by calling: * generate() */ static struct { int generation; /* generation number of arrow set */ struct node *arrownp; /* top-level parse tree for arrow */ int n; /* n value associated with arrow */ int k; /* k value associated with arrow */ struct node *fromnp; /* left-hand-side event in parse tree */ struct node *tonp; /* right-hand-side event in parse tree */ struct event *frome; /* left-hand-side event in instance tree */ struct event *toe; /* right-hand-side event in instance tree */ struct bubble *frombp; /* bubble arrow comes from */ struct bubble *tobp; /* bubble arrow goes to */ } G; static void generate_arrownp(struct node *arrownp) { G.arrownp = arrownp; } static void generate_nork(int n, int k) { G.n = n; G.k = k; } static void generate_new(void) { G.generation++; } static void generate_from(struct node *fromeventnp) { G.frombp = NULL; G.fromnp = fromeventnp; } static void generate_to(struct node *toeventnp) { G.tonp = toeventnp; } static void generate(struct info *infop) { struct arrow *arrowp; ASSERT(G.arrownp != NULL); ASSERT(G.fromnp != NULL); ASSERT(G.tonp != NULL); out(O_ALTFP|O_VERB3|O_NONL, " Arrow \""); ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.fromnp); out(O_ALTFP|O_VERB3|O_NONL, "\" -> \""); ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, G.tonp); out(O_ALTFP|O_VERB3|O_NONL, "\" "); arrowp = itree_add_arrow(G.arrownp, G.fromnp, G.tonp, infop->ex); if (arrowp == NULL) { out(O_ALTFP|O_VERB3, "(prevented by constraints)"); } else { out(O_ALTFP|O_VERB3, ""); if (!G.frombp) { G.frome = find_or_add_event(infop, G.fromnp); G.frombp = itree_add_bubble(G.frome, B_FROM, G.n, 0); } G.toe = find_or_add_event(infop, G.tonp); G.tobp = itree_add_bubble(G.toe, B_TO, G.k, G.generation); arrowp->tail = G.frombp; arrowp->head = G.tobp; add_arrow(G.frombp, arrowp); add_arrow(G.tobp, arrowp); } } enum childnode_action { CN_NONE, CN_DUP }; static struct node * tname_dup(struct node *namep, enum childnode_action act) { struct node *retp = NULL; const char *file; int line; if (namep == NULL) return (NULL); file = namep->file; line = namep->line; for (; namep != NULL; namep = namep->u.name.next) { struct node *newnp = newnode(T_NAME, file, line); newnp->u.name.t = namep->u.name.t; newnp->u.name.s = namep->u.name.s; newnp->u.name.last = newnp; newnp->u.name.it = namep->u.name.it; newnp->u.name.cp = namep->u.name.cp; if (act == CN_DUP) { struct node *npc; npc = namep->u.name.child; if (npc != NULL) { switch (npc->t) { case T_NUM: newnp->u.name.child = newnode(T_NUM, file, line); newnp->u.name.child->u.ull = npc->u.ull; break; case T_NAME: newnp->u.name.child = tree_name(npc->u.name.s, npc->u.name.it, file, line); break; default: out(O_DIE, "tname_dup: " "invalid child type %s", ptree_nodetype2str(npc->t)); } } } if (retp == NULL) { retp = newnp; } else { retp->u.name.last->u.name.next = newnp; retp->u.name.last = newnp; } } return (retp); } struct prop_wlk_data { struct lut *props; struct node *epname; }; static struct lut *props2instance(struct node *, struct node *); /* * let oldepname be a subset of epname. return the subsection of epname * that ends with oldepname. make each component in the path explicitly * instanced (i.e., with a T_NUM child). */ static struct node * tname_dup_to_epname(struct node *oldepname, struct node *epname) { struct node *npref, *npend, *np1, *np2; struct node *ret = NULL; int foundmatch = 0; if (epname == NULL) return (NULL); /* * search for the longest path in epname which contains * oldnode->u.event.epname. set npend to point to just past the * end of this path. */ npend = NULL; for (npref = epname; npref; npref = npref->u.name.next) { if (npref->u.name.s == oldepname->u.name.s) { for (np1 = npref, np2 = oldepname; np1 != NULL && np2 != NULL; np1 = np1->u.name.next, np2 = np2->u.name.next) { if (np1->u.name.s != np2->u.name.s) break; } if (np2 == NULL) { foundmatch = 1; npend = np1; if (np1 == NULL) { /* oldepname matched npref up to end */ break; } } } } if (foundmatch == 0) { /* * if oldepname could not be found in epname, return a * duplicate of the former. do not try to instantize * oldepname since it might not be a path. */ return (tname_dup(oldepname, CN_DUP)); } /* * dup (epname -- npend). all children should be T_NUMs. */ for (npref = epname; ! (npref == NULL || npref == npend); npref = npref->u.name.next) { struct node *newnp = newnode(T_NAME, oldepname->file, oldepname->line); newnp->u.name.t = npref->u.name.t; newnp->u.name.s = npref->u.name.s; newnp->u.name.last = newnp; newnp->u.name.it = npref->u.name.it; newnp->u.name.cp = npref->u.name.cp; newnp->u.name.child = newnode(T_NUM, oldepname->file, oldepname->line); if (npref->u.name.child == NULL || npref->u.name.child->t != T_NUM) { int childnum; ASSERT(npref->u.name.cp != NULL); config_getcompname(npref->u.name.cp, NULL, &childnum); newnp->u.name.child->u.ull = childnum; } else { newnp->u.name.child->u.ull = npref->u.name.child->u.ull; } if (ret == NULL) { ret = newnp; } else { ret->u.name.last->u.name.next = newnp; ret->u.name.last = newnp; } } return (ret); } /* * restriction: oldnode->u.event.epname has to be equivalent to or a subset * of epname */ static struct node * tevent_dup_to_epname(struct node *oldnode, struct node *epname) { struct node *ret; ret = newnode(T_EVENT, oldnode->file, oldnode->line); ret->u.event.ename = tname_dup(oldnode->u.event.ename, CN_NONE); ret->u.event.epname = tname_dup_to_epname(oldnode->u.event.epname, epname); return (ret); } static void nv_instantiate(void *name, void *val, void *arg) { struct prop_wlk_data *pd = (struct prop_wlk_data *)arg; struct node *orhs = (struct node *)val; struct node *nrhs; /* handle engines by instantizing the entire engine */ if (name == L_engine) { ASSERT(orhs->t == T_EVENT); ASSERT(orhs->u.event.ename->u.name.t == N_SERD); /* there are only SERD engines for now */ nrhs = newnode(T_SERD, orhs->file, orhs->line); nrhs->u.stmt.np = tevent_dup_to_epname(orhs, pd->epname); nrhs->u.stmt.lutp = props2instance(orhs, pd->epname); pd->props = lut_add(pd->props, name, nrhs, NULL); return; } switch (orhs->t) { case T_NUM: nrhs = newnode(T_NUM, orhs->file, orhs->line); nrhs->u.ull = orhs->u.ull; pd->props = lut_add(pd->props, name, nrhs, NULL); break; case T_TIMEVAL: nrhs = newnode(T_TIMEVAL, orhs->file, orhs->line); nrhs->u.ull = orhs->u.ull; pd->props = lut_add(pd->props, name, nrhs, NULL); break; case T_NAME: nrhs = tname_dup_to_epname(orhs, pd->epname); pd->props = lut_add(pd->props, name, nrhs, NULL); break; case T_EVENT: nrhs = tevent_dup_to_epname(orhs, pd->epname); pd->props = lut_add(pd->props, name, nrhs, NULL); break; case T_GLOBID: nrhs = newnode(T_GLOBID, orhs->file, orhs->line); nrhs->u.globid.s = orhs->u.globid.s; pd->props = lut_add(pd->props, name, nrhs, NULL); break; case T_FUNC: /* for T_FUNC, we don't duplicate it, just point to node */ pd->props = lut_add(pd->props, name, orhs, NULL); break; default: out(O_DIE, "unexpected nvpair value type %s", ptree_nodetype2str(((struct node *)val)->t)); } } static struct lut * props2instance(struct node *eventnp, struct node *epname) { struct prop_wlk_data pd; pd.props = NULL; pd.epname = epname; ASSERT(eventnp->u.event.declp != NULL); lut_walk(eventnp->u.event.declp->u.stmt.lutp, nv_instantiate, &pd); return (pd.props); } /*ARGSUSED*/ static void instances_destructor(void *left, void *right, void *arg) { struct node *dn = (struct node *)right; if (dn->t == T_SERD) { /* we allocated the lut during itree_create(), so free it */ lut_free(dn->u.stmt.lutp, instances_destructor, NULL); dn->u.stmt.lutp = NULL; } if (dn->t != T_FUNC) /* T_FUNC pointed to original node */ tree_free(dn); } /*ARGSUSED*/ static void payloadprops_destructor(void *left, void *right, void *arg) { FREE(right); } /* * event_cmp -- used via lut_lookup/lut_add on instance tree lut */ static int event_cmp(struct event *ep1, struct event *ep2) { int diff; if ((diff = ep2->enode->u.event.ename->u.name.s - ep1->enode->u.event.ename->u.name.s) != 0) return (diff); if ((diff = (char *)ep2->ipp - (char *)ep1->ipp) != 0) return (diff); return (0); } struct event * itree_lookup(struct lut *itp, const char *ename, const struct ipath *ipp) { struct event searchevent; /* just used for searching */ struct node searcheventnode; struct node searchenamenode; searchevent.enode = &searcheventnode; searcheventnode.t = T_EVENT; searcheventnode.u.event.ename = &searchenamenode; searchenamenode.t = T_NAME; searchenamenode.u.name.s = ename; searchevent.ipp = ipp; return (lut_lookup(itp, (void *)&searchevent, (lut_cmp)event_cmp)); } static struct event * find_or_add_event(struct info *infop, struct node *np) { struct event *ret; struct event searchevent; /* just used for searching */ ASSERTeq(np->t, T_EVENT, ptree_nodetype2str); searchevent.enode = np; searchevent.ipp = ipath(np->u.event.epname); if ((ret = lut_lookup(infop->lut, (void *)&searchevent, (lut_cmp)event_cmp)) != NULL) return (ret); /* wasn't already in tree, allocate it */ ret = alloc_xmalloc(sizeof (*ret)); bzero(ret, sizeof (*ret)); ret->t = np->u.event.ename->u.name.t; ret->enode = np; ret->ipp = searchevent.ipp; ret->props = props2instance(np, np->u.event.epname); infop->lut = lut_add(infop->lut, (void *)ret, (void *)ret, (lut_cmp)event_cmp); return (ret); } /* * hmatch_event -- perform any appropriate horizontal expansion on an event * * this routine is used to perform horizontal expansion on both the * left-hand-side events in a prop, and the right-hand-side events. * when called to handle a left-side event, nextnp point to the right * side of the prop that should be passed to hmatch() for each match * found by horizontal expansion. when no horizontal expansion exists, * we will still "match" one event for every event found in the list on * the left-hand-side of the prop because vmatch() already found that * there's at least one match during vertical expansion. */ static void hmatch_event(struct info *infop, struct node *eventnp, struct node *epname, struct config *ncp, struct node *nextnp, int rematch) { if (epname == NULL) { /* * end of pathname recursion, either we just located * a left-hand-side event and we're ready to move on * to the expanding the right-hand-side events, or * we're further down the recursion and we just located * a right-hand-side event. the passed-in parameter * "nextnp" tells us whether we're working on the left * side and need to move on to nextnp, or if nextnp is * NULL, we're working on the right side. */ if (nextnp) { /* * finished a left side expansion, move on to right. * tell generate() what event we just matched so * it can be used at the source of any arrows * we generate as we match events on the right side. */ generate_from(eventnp); hmatch(infop, nextnp, NULL); } else { /* * finished a right side expansion. tell generate * the information about the destination and let * it construct the arrows as appropriate. */ generate_to(eventnp); generate(infop); } return; } ASSERTeq(epname->t, T_NAME, ptree_nodetype2str); /* * we only get here when eventnp already has a completely * instanced epname in it already. so we first recurse * down to the end of the name and as the recursion pops * up, we look for opportunities to advance horizontal * expansions on to the next match. */ if (epname->u.name.it == IT_HORIZONTAL || rematch) { struct config *cp; struct config *ocp = epname->u.name.cp; char *cp_s; int cp_num; int ocp_num; struct iterinfo *iterinfop = NULL; const char *iters; int hexpand = 0; if (epname->u.name.it != IT_HORIZONTAL) { /* * Ancestor was horizontal though, so must rematch * against the name/num found in vmatch. */ config_getcompname(ocp, NULL, &ocp_num); } else { iters = epname->u.name.child->u.name.s; if ((iterinfop = lut_lookup(infop->ex, (void *)iters, NULL)) == NULL) { /* * do horizontal expansion on this node */ hexpand = 1; iterinfop = alloc_xmalloc( sizeof (struct iterinfo)); iterinfop->num = -1; iterinfop->np = epname; infop->ex = lut_add(infop->ex, (void *)iters, iterinfop, NULL); } else if (iterinfop->num == -1) { hexpand = 1; } else { /* * This name has already been used in a * horizontal expansion. This time just match it */ ocp_num = iterinfop->num; } } /* * Run through siblings looking for any that match the name. * If hexpand not set then num must also match ocp_num. */ for (cp = rematch ? ncp : ocp; cp; cp = config_next(cp)) { config_getcompname(cp, &cp_s, &cp_num); if (cp_s == epname->u.name.s) { if (hexpand) iterinfop->num = cp_num; else if (ocp_num != cp_num) continue; epname->u.name.cp = cp; hmatch_event(infop, eventnp, epname->u.name.next, config_child(cp), nextnp, 1); } } epname->u.name.cp = ocp; if (hexpand) iterinfop->num = -1; } else { hmatch_event(infop, eventnp, epname->u.name.next, NULL, nextnp, 0); } } /* * hmatch -- check for horizontal expansion matches * * np points to the things we're matching (like a T_LIST or a T_EVENT) * and if we're working on a left-side of a prop, nextnp points to * the other side of the prop that we'll tackle next when this recursion * bottoms out. when all the events in the entire prop arrow have been * horizontally expanded, generate() will be called to generate the * actualy arrow. */ static void hmatch(struct info *infop, struct node *np, struct node *nextnp) { if (np == NULL) return; /* all done */ /* * for each item in the list of events (which could just * be a single event, or it could get larger in the loop * below due to horizontal expansion), call hmatch on * the right side and create arrows to each element. */ switch (np->t) { case T_LIST: /* loop through the list */ if (np->u.expr.left) hmatch(infop, np->u.expr.left, nextnp); if (np->u.expr.right) hmatch(infop, np->u.expr.right, nextnp); break; case T_EVENT: hmatch_event(infop, np, np->u.event.epname, NULL, nextnp, 0); break; default: outfl(O_DIE, np->file, np->line, "hmatch: unexpected type: %s", ptree_nodetype2str(np->t)); } } static int itree_np2nork(struct node *norknp) { if (norknp == NULL) return (1); else if (norknp->t == T_NAME && norknp->u.name.s == L_A) return (-1); /* our internal version of "all" */ else if (norknp->t == T_NUM) return ((int)norknp->u.ull); else outfl(O_DIE, norknp->file, norknp->line, "itree_np2nork: internal error type %s", ptree_nodetype2str(norknp->t)); /*NOTREACHED*/ return (1); } static struct iterinfo * newiterinfo(int num, struct node *np) { struct iterinfo *ret = alloc_xmalloc(sizeof (*ret)); ret->num = num; ret->np = np; return (ret); } /*ARGSUSED*/ static void iterinfo_destructor(void *left, void *right, void *arg) { struct iterinfo *iterinfop = (struct iterinfo *)right; alloc_xfree(iterinfop, sizeof (*iterinfop)); } static void vmatch_event(struct info *infop, struct config *cp, struct node *np, struct node *lnp, struct node *anp, struct wildcardinfo *wcp) { char *cp_s; int cp_num; struct node *ewlp, *ewfp; struct config *pcp; struct node *cpnode; int newewname = 0; if (np == NULL) { /* * Reached the end of the name. u.name.cp pointers should be set * up for each part of name. From this we can use config tree * to build up the wildcard part of the name (if any). */ pcp = config_parent(wcp->nptop->u.event.epname->u.name.cp); if (pcp == infop->croot) { /* * no wildcarding done - move on to next entry */ wcp->nptop->u.event.ewname = wcp->ewname; wcp->nptop->u.event.oldepname = wcp->oldepname; vmatch(infop, np, lnp, anp); return; } if (wcp->ewname == NULL) { /* * ewname not yet set up - do it now */ newewname = 1; for (; pcp != infop->croot; pcp = config_parent(pcp)) { config_getcompname(pcp, &cp_s, &cp_num); cpnode = tree_name(cp_s, IT_NONE, NULL, 0); cpnode->u.name.child = newnode(T_NUM, NULL, 0); cpnode->u.name.child->u.ull = cp_num; cpnode->u.name.cp = pcp; if (wcp->ewname != NULL) { cpnode->u.name.next = wcp->ewname; cpnode->u.name.last = wcp->ewname->u.name.last; } wcp->ewname = cpnode; } } /* * dup ewname and append oldepname */ ewfp = tname_dup(wcp->ewname, CN_DUP); ewlp = ewfp->u.name.last; ewfp->u.name.last = wcp->oldepname->u.name.last; ewlp->u.name.next = wcp->oldepname; wcp->nptop->u.event.epname = ewfp; wcp->nptop->u.event.ewname = wcp->ewname; wcp->nptop->u.event.oldepname = wcp->oldepname; vmatch(infop, np, lnp, anp); wcp->nptop->u.event.epname = wcp->oldepname; /* * reduce duped ewname to original then free */ ewlp->u.name.next = NULL; ewfp->u.name.last = ewlp; tree_free(ewfp); if (newewname) { /* * free ewname if allocated above */ tree_free(wcp->ewname); wcp->ewname = NULL; } return; } /* * We have an np. See if we can match it in this section of * the config tree. */ if (cp == NULL) return; /* no more config to match against */ for (; cp; cp = config_next(cp)) { config_getcompname(cp, &cp_s, &cp_num); if (cp_s == np->u.name.s) { /* found a matching component name */ if (np->u.name.child && np->u.name.child->t == T_NUM) { /* * an explicit instance number was given * in the source. so only consider this * a configuration match if the number * also matches. */ if (cp_num != np->u.name.child->u.ull) continue; } else if (np->u.name.it != IT_HORIZONTAL) { struct iterinfo *iterinfop; const char *iters; /* * vertical iterator. look it up in * the appropriate lut and if we get * back a value it is either one that we * set earlier, in which case we record * the new value for this iteration and * keep matching, or it is one that was * set by an earlier reference to the * iterator, in which case we only consider * this a configuration match if the number * matches cp_num. */ ASSERT(np->u.name.child != NULL); ASSERT(np->u.name.child->t == T_NAME); iters = np->u.name.child->u.name.s; if ((iterinfop = lut_lookup(infop->ex, (void *)iters, NULL)) == NULL) { /* we're the first use, record our np */ infop->ex = lut_add(infop->ex, (void *)iters, newiterinfo(cp_num, np), NULL); } else if (np == iterinfop->np) { /* * we're the first use, back again * for another iteration. so update * the num bound to this iterator in * the lut. */ iterinfop->num = cp_num; } else if (cp_num != iterinfop->num) { /* * an earlier reference to this * iterator bound it to a different * instance number, so there's no * match here after all. * * however, it's possible that this * component should really be part of * the wildcard. we explore this by * forcing this component into the * wildcarded section. * * for an more details of what's * going to happen now, see * comments block below entitled * "forcing components into * wildcard path". */ if (np == wcp->nptop->u.event.epname) vmatch_event(infop, config_child(cp), np, lnp, anp, wcp); continue; } } /* * if this was an IT_HORIZONTAL name, hmatch() will * expand all matches horizontally into a list. * we know the list will contain at least * one element (the one we just matched), * so we just let hmatch_event() do the rest. * * recurse on to next component. Note that * wildcarding is now turned off. */ np->u.name.cp = cp; vmatch_event(infop, config_child(cp), np->u.name.next, lnp, anp, wcp); np->u.name.cp = NULL; /* * forcing components into wildcard path: * * if this component is the first match, force it * to be part of the wildcarded path and see if we * can get additional matches. * * here's an example. suppose we have the * definition * event foo@x/y * and configuration * a0/x0/y0/a1/x1/y1 * * the code up to this point will treat "a0" as the * wildcarded part of the path and "x0/y0" as the * nonwildcarded part, resulting in the instanced * event * foo@a0/x0/y0 * * in order to discover the next match (.../x1/y1) * in the configuration we have to force "x0" into * the wildcarded part of the path. * by doing so, we discover the wildcarded part * "a0/x0/y0/a1" and the nonwildcarded part "x1/y1" * * the following call to vmatch_event() is also * needed to properly handle the configuration * b0/x0/b1/x1/y1 * * the recursions into vmatch_event() will start * off uncovering "b0" as the wildcarded part and * "x0" as the start of the nonwildcarded path. * however, the next recursion will not result in a * match since there is no "y" following "x0". the * subsequent match of (wildcard = "b0/x0/b1" and * nonwildcard = "x1/y1") will be discovered only * if "x0" is forced to be a part of the wildcarded * path. */ if (np == wcp->nptop->u.event.epname) vmatch_event(infop, config_child(cp), np, lnp, anp, wcp); if (np->u.name.it == IT_HORIZONTAL) { /* * hmatch() finished iterating through * the configuration as described above, so * don't continue iterating here. */ return; } } else if (np == wcp->nptop->u.event.epname) { /* * no match - carry on down the tree looking for * wildcarding */ vmatch_event(infop, config_child(cp), np, lnp, anp, wcp); } } } /* * vmatch -- find the next vertical expansion match in the config database * * this routine is called with three node pointers: * np -- the parse we're matching * lnp -- the rest of the list we're currently working on * anp -- the rest of the arrow we're currently working on * * the expansion matching happens via three types of recursion: * * - when given an arrow, handle the left-side and then recursively * handle the right side (which might be another cascaded arrow). * * - when handling one side of an arrow, recurse through the T_LIST * to get to each event (or just move on to the event if there * is a single event instead of a list) since the arrow parse * trees recurse left, we actually start with the right-most * event list in the prop statement and work our way towards * the left-most event list. * * - when handling an event, recurse down each component of the * pathname, matching in the config database and recording the * matches in the explicit iterator dictionary as we go. * * when the bottom of this matching recursion is met, meaning we have * set the "cp" pointers on all the names in the entire statement, * we call hmatch() which does it's own recursion to handle horizontal * expandsion and then call generate() to generate nodes, bubbles, and * arrows in the instance tree. generate() looks at the cp pointers to * see what instance numbers were matched in the configuration database. * * when horizontal expansion appears, vmatch() finds only the first match * and hmatch() then takes the horizontal expansion through all the other * matches when generating the arrows in the instance tree. * * the "infop" passed down through the recursion contains a dictionary * of the explicit iterators (all the implicit iterators have been converted * to explicit iterators when the parse tree was created by tree.c), which * allows things like this to work correctly: * * prop error.a@x[n]/y/z -> error.b@x/y[n]/z -> error.c@x/y/z[n]; * * during the top level call, the explicit iterator "n" will match an * instance number in the config database, and the result will be recorded * in the explicit iterator dictionary and passed down via "infop". so * when the recursive call tries to match y[n] in the config database, it * will only match the same instance number as x[n] did since the dictionary * is consulted to see if "n" took on a value already. * * at any point during the recursion, match*() can return to indicate * a match was not found in the config database and that the caller should * move on to the next potential match, if any. * * constraints are completely ignored by match(), so the statement: * * prop error.a@x[n] -> error.b@x[n] {n != 0}; * * might very well match x[0] if it appears in the config database. it * is the generate() routine that takes that match and then decides what * arrow, if any, should be generated in the instance tree. generate() * looks at the explicit iterator dictionary to get values like "n" in * the above example so that it can evaluate constraints. * */ static void vmatch(struct info *infop, struct node *np, struct node *lnp, struct node *anp) { struct node *np1, *np2, *oldepname, *oldnptop; int epmatches; struct config *cp; struct wildcardinfo *wcp; if (np == NULL) { if (lnp) vmatch(infop, lnp, NULL, anp); else if (anp) vmatch(infop, anp, NULL, NULL); else { struct node *src; struct node *dst; /* end of vertical match recursion */ outfl(O_ALTFP|O_VERB3|O_NONL, infop->anp->file, infop->anp->line, "vmatch: "); ptree_name_iter(O_ALTFP|O_VERB3|O_NONL, infop->anp); out(O_ALTFP|O_VERB3, NULL); generate_nork( itree_np2nork(infop->anp->u.arrow.nnp), itree_np2nork(infop->anp->u.arrow.knp)); dst = infop->anp->u.arrow.rhs; src = infop->anp->u.arrow.lhs; for (;;) { generate_new(); /* new set of arrows */ if (src->t == T_ARROW) { hmatch(infop, src->u.arrow.rhs, dst); generate_nork( itree_np2nork(src->u.arrow.nnp), itree_np2nork(src->u.arrow.knp)); dst = src->u.arrow.rhs; src = src->u.arrow.lhs; } else { hmatch(infop, src, dst); break; } } } return; } switch (np->t) { case T_EVENT: { epmatches = 0; /* * see if we already have a match in the wcps */ for (wcp = wcproot; wcp; wcp = wcp->next) { oldepname = wcp->oldepname; oldnptop = wcp->nptop; for (np1 = oldepname, np2 = np->u.event.epname; np1 != NULL && np2 != NULL; np1 = np1->u.name.next, np2 = np2->u.name.next) { if (strcmp(np1->u.name.s, np2->u.name.s) != 0) break; if (np1->u.name.child->t != np2->u.name.child->t) break; if (np1->u.name.child->t == T_NUM && np1->u.name.child->u.ull != np2->u.name.child->u.ull) break; if (np1->u.name.child->t == T_NAME && strcmp(np1->u.name.child->u.name.s, np2->u.name.child->u.name.s) != 0) break; epmatches++; } if (epmatches) break; } if (epmatches && np1 == NULL && np2 == NULL) { /* * complete names match, can just borrow the fields */ oldepname = np->u.event.epname; np->u.event.epname = oldnptop->u.event.epname; np->u.event.oldepname = wcp->oldepname; np->u.event.ewname = wcp->ewname; vmatch(infop, NULL, lnp, anp); np->u.event.epname = oldepname; return; } if (epmatches) { /* * just first part of names match - do wildcarding * by using existing wcp including ewname and also * copying as much of pwname as is valid, then start * vmatch_event() at start of non-matching section */ for (np1 = oldepname, np2 = np->u.event.epname; epmatches != 0; epmatches--) { cp = np1->u.name.cp; np2->u.name.cp = cp; np1 = np1->u.name.next; np2 = np2->u.name.next; } wcp->oldepname = np->u.event.epname; wcp->nptop = np; vmatch_event(infop, config_child(cp), np2, lnp, anp, wcp); wcp->oldepname = oldepname; wcp->nptop = oldnptop; return; } /* * names do not match - allocate a new wcp */ wcp = MALLOC(sizeof (struct wildcardinfo)); wcp->next = wcproot; wcproot = wcp; wcp->nptop = np; wcp->oldepname = np->u.event.epname; wcp->ewname = NULL; vmatch_event(infop, config_child(infop->croot), np->u.event.epname, lnp, anp, wcp); wcproot = wcp->next; FREE(wcp); break; } case T_LIST: ASSERT(lnp == NULL); vmatch(infop, np->u.expr.right, np->u.expr.left, anp); break; case T_ARROW: ASSERT(lnp == NULL && anp == NULL); vmatch(infop, np->u.arrow.rhs, NULL, np->u.arrow.lhs); break; default: outfl(O_DIE, np->file, np->line, "vmatch: unexpected type: %s", ptree_nodetype2str(np->t)); } } static void cp_reset(struct node *np) { if (np == NULL) return; switch (np->t) { case T_NAME: np->u.name.cp = NULL; cp_reset(np->u.name.next); break; case T_LIST: cp_reset(np->u.expr.left); cp_reset(np->u.expr.right); break; case T_ARROW: cp_reset(np->u.arrow.lhs); cp_reset(np->u.arrow.rhs); break; case T_EVENT: cp_reset(np->u.event.epname); break; } } /* * itree_create -- apply the current config to the current parse tree * * returns a lut mapping fully-instance-qualified names to struct events. * */ struct lut * itree_create(struct config *croot) { struct lut *retval; struct node *propnp; extern int alloc_total(); int init_size; Ninfo.lut = NULL; Ninfo.croot = croot; init_size = alloc_total(); out(O_ALTFP|O_STAMP, "start itree_create using %d bytes", init_size); for (propnp = Props; propnp; propnp = propnp->u.stmt.next) { struct node *anp = propnp->u.stmt.np; ASSERTeq(anp->t, T_ARROW, ptree_nodetype2str); if (!anp->u.arrow.needed) continue; Ninfo.anp = anp; Ninfo.ex = NULL; generate_arrownp(anp); vmatch(&Ninfo, anp, NULL, NULL); if (Ninfo.ex) { lut_free(Ninfo.ex, iterinfo_destructor, NULL); Ninfo.ex = NULL; } cp_reset(anp); } out(O_ALTFP|O_STAMP, "itree_create added %d bytes", alloc_total() - init_size); retval = Ninfo.lut; Ninfo.lut = NULL; return (retval); } /* * initial first pass of the rules. * We don't use the config at all. Just check the last part of the pathname * in the rules. If this matches the last part of the pathname in the first * ereport, then set pathname to the pathname in the ereport. If not then * set pathname to just the last part of pathname with instance number 0. * Constraints are ignored and all nork values are set to 0. If after all that * any rules can still not be associated with the ereport, then they are set * to not needed in prune_propagations() and ignored in the real itree_create() * which follows. */ static struct event * add_event_dummy(struct node *np, const struct ipath *ipp) { struct event *ret; struct event searchevent; /* just used for searching */ extern struct ipath *ipath_dummy(struct node *, struct ipath *); searchevent.enode = np; searchevent.ipp = ipath_dummy(np->u.event.epname, (struct ipath *)ipp); if ((ret = lut_lookup(Ninfo.lut, (void *)&searchevent, (lut_cmp)event_cmp)) != NULL) return (ret); ret = alloc_xmalloc(sizeof (*ret)); bzero(ret, sizeof (*ret)); ret->t = np->u.event.ename->u.name.t; ret->enode = np; ret->ipp = searchevent.ipp; Ninfo.lut = lut_add(Ninfo.lut, (void *)ret, (void *)ret, (lut_cmp)event_cmp); return (ret); } /*ARGSUSED*/ struct lut * itree_create_dummy(const char *e0class, const struct ipath *e0ipp) { struct node *propnp; struct event *frome, *toe; struct bubble *frombp, *tobp; struct arrow *arrowp; struct node *src, *dst, *slst, *dlst, *arrownp, *oldarrownp; int gen = 0; extern int alloc_total(); int init_size; Ninfo.lut = NULL; init_size = alloc_total(); out(O_ALTFP|O_STAMP, "start itree_create using %d bytes", init_size); for (propnp = Props; propnp; propnp = propnp->u.stmt.next) { arrownp = propnp->u.stmt.np; while (arrownp) { gen++; dlst = arrownp->u.arrow.rhs; slst = arrownp->u.arrow.lhs; oldarrownp = arrownp; if (slst->t == T_ARROW) { arrownp = slst; slst = slst->u.arrow.rhs; } else { arrownp = NULL; } while (slst) { if (slst->t == T_LIST) { src = slst->u.expr.right; slst = slst->u.expr.left; } else { src = slst; slst = NULL; } frome = add_event_dummy(src, e0ipp); frombp = itree_add_bubble(frome, B_FROM, 0, 0); while (dlst) { if (dlst->t == T_LIST) { dst = dlst->u.expr.right; dlst = dlst->u.expr.left; } else { dst = dlst; dlst = NULL; } arrowp = alloc_xmalloc( sizeof (struct arrow)); bzero(arrowp, sizeof (struct arrow)); arrowp->pnode = oldarrownp; toe = add_event_dummy(dst, e0ipp); tobp = itree_add_bubble(toe, B_TO, 0, gen); arrowp->tail = frombp; arrowp->head = tobp; add_arrow(frombp, arrowp); add_arrow(tobp, arrowp); arrow_add_within(arrowp, dst->u.event.declp->u.stmt.np-> u.event.eexprlist); arrow_add_within(arrowp, dst->u.event.eexprlist); } } } } out(O_ALTFP|O_STAMP, "itree_create added %d bytes", alloc_total() - init_size); return (Ninfo.lut); } void itree_free(struct lut *lutp) { int init_size; init_size = alloc_total(); out(O_ALTFP|O_STAMP, "start itree_free"); lut_free(lutp, itree_destructor, NULL); out(O_ALTFP|O_STAMP, "itree_free freed %d bytes", init_size - alloc_total()); } void itree_prune(struct lut *lutp) { int init_size; init_size = alloc_total(); out(O_ALTFP|O_STAMP, "start itree_prune"); lut_walk(lutp, itree_pruner, NULL); out(O_ALTFP|O_STAMP, "itree_prune freed %d bytes", init_size - alloc_total()); } void itree_pevent_brief(int flags, struct event *ep) { ASSERT(ep != NULL); ASSERT(ep->enode != NULL); ASSERT(ep->ipp != NULL); ipath_print(flags, ep->enode->u.event.ename->u.name.s, ep->ipp); } /*ARGSUSED*/ static void itree_pevent(struct event *lhs, struct event *ep, void *arg) { struct plut_wlk_data propd; struct bubble *bp; int flags = (int)(intptr_t)arg; itree_pevent_brief(flags, ep); if (ep->t == N_EREPORT) out(flags, " (count %d)", ep->count); else out(flags, NULL); if (ep->props) { propd.flags = flags; propd.first = 1; out(flags, "Properties:"); lut_walk(ep->props, ptree_plut, (void *)&propd); } for (bp = itree_next_bubble(ep, NULL); bp; bp = itree_next_bubble(ep, bp)) { /* Print only TO bubbles in this loop */ if (bp->t != B_TO) continue; itree_pbubble(flags, bp); } for (bp = itree_next_bubble(ep, NULL); bp; bp = itree_next_bubble(ep, bp)) { /* Print only INHIBIT bubbles in this loop */ if (bp->t != B_INHIBIT) continue; itree_pbubble(flags, bp); } for (bp = itree_next_bubble(ep, NULL); bp; bp = itree_next_bubble(ep, bp)) { /* Print only FROM bubbles in this loop */ if (bp->t != B_FROM) continue; itree_pbubble(flags, bp); } } static void itree_pbubble(int flags, struct bubble *bp) { struct constraintlist *cp; struct arrowlist *ap; ASSERT(bp != NULL); out(flags|O_NONL, " "); if (bp->mark) out(flags|O_NONL, "*"); else out(flags|O_NONL, " "); if (bp->t == B_FROM) out(flags|O_NONL, "N=%d to:", bp->nork); else if (bp->t == B_TO) out(flags|O_NONL, "K=%d from:", bp->nork); else out(flags|O_NONL, "K=%d masked from:", bp->nork); if (bp->t == B_TO || bp->t == B_INHIBIT) { for (ap = itree_next_arrow(bp, NULL); ap; ap = itree_next_arrow(bp, ap)) { ASSERT(ap->arrowp->head == bp); ASSERT(ap->arrowp->tail != NULL); ASSERT(ap->arrowp->tail->myevent != NULL); out(flags|O_NONL, " "); itree_pevent_brief(flags, ap->arrowp->tail->myevent); } out(flags, NULL); return; } for (ap = itree_next_arrow(bp, NULL); ap; ap = itree_next_arrow(bp, ap)) { ASSERT(ap->arrowp->tail == bp); ASSERT(ap->arrowp->head != NULL); ASSERT(ap->arrowp->head->myevent != NULL); out(flags|O_NONL, " "); itree_pevent_brief(flags, ap->arrowp->head->myevent); out(flags|O_NONL, " "); ptree_timeval(flags, &ap->arrowp->mindelay); out(flags|O_NONL, ","); ptree_timeval(flags, &ap->arrowp->maxdelay); /* Display anything from the propogation node? */ out(O_VERB3|O_NONL, " <%s:%d>", ap->arrowp->pnode->file, ap->arrowp->pnode->line); if (itree_next_constraint(ap->arrowp, NULL)) out(flags|O_NONL, " {"); for (cp = itree_next_constraint(ap->arrowp, NULL); cp; cp = itree_next_constraint(ap->arrowp, cp)) { ptree(flags, cp->cnode, 1, 0); if (itree_next_constraint(ap->arrowp, cp)) out(flags|O_NONL, ", "); } if (itree_next_constraint(ap->arrowp, NULL)) out(flags|O_NONL, "}"); } out(flags, NULL); } void itree_ptree(int flags, struct lut *itp) { lut_walk(itp, (lut_cb)itree_pevent, (void *)(intptr_t)flags); } /*ARGSUSED*/ static void itree_destructor(void *left, void *right, void *arg) { struct event *ep = (struct event *)right; struct bubble *nextbub, *bub; /* Free the properties */ if (ep->props) lut_free(ep->props, instances_destructor, NULL); /* Free the payload properties */ if (ep->payloadprops) lut_free(ep->payloadprops, payloadprops_destructor, NULL); /* Free my bubbles */ for (bub = ep->bubbles; bub != NULL; ) { nextbub = bub->next; /* * Free arrows if they are FROM me. Free arrowlists on * other types of bubbles (but not the attached arrows, * which will be freed when we free the originating * bubble. */ if (bub->t == B_FROM) itree_free_arrowlists(bub, 1); else itree_free_arrowlists(bub, 0); itree_free_bubble(bub); bub = nextbub; } if (ep->nvp != NULL) nvlist_free(ep->nvp); alloc_xfree(ep, sizeof (*ep)); } /*ARGSUSED*/ static void itree_pruner(void *left, void *right, void *arg) { struct event *ep = (struct event *)right; struct bubble *nextbub, *bub; if (ep->keep_in_tree) return; /* Free the properties */ lut_free(ep->props, instances_destructor, NULL); /* Free the payload properties */ lut_free(ep->payloadprops, payloadprops_destructor, NULL); /* Free my bubbles */ for (bub = ep->bubbles; bub != NULL; ) { nextbub = bub->next; itree_prune_arrowlists(bub); itree_free_bubble(bub); bub = nextbub; } if (ep->nvp != NULL) nvlist_free(ep->nvp); ep->props = NULL; ep->payloadprops = NULL; ep->bubbles = NULL; ep->nvp = NULL; } static void itree_free_bubble(struct bubble *freeme) { alloc_xfree(freeme, sizeof (*freeme)); } static struct bubble * itree_add_bubble(struct event *eventp, enum bubbletype btype, int nork, int gen) { struct bubble *prev = NULL; struct bubble *curr; struct bubble *newb; /* Use existing bubbles as appropriate when possible */ for (curr = eventp->bubbles; curr != NULL; prev = curr, curr = curr->next) { if (btype == B_TO && curr->t == B_TO) { /* see if an existing "to" bubble works for us */ if (gen == curr->gen) return (curr); /* matched gen number */ else if (nork == 1 && curr->nork == 1) { curr->gen = gen; return (curr); /* coalesce K==1 bubbles */ } } else if (btype == B_FROM && curr->t == B_FROM) { /* see if an existing "from" bubble works for us */ if ((nork == N_IS_ALL && curr->nork == N_IS_ALL) || (nork == 0 && curr->nork == 0)) return (curr); } } newb = alloc_xmalloc(sizeof (struct bubble)); newb->next = NULL; newb->t = btype; newb->myevent = eventp; newb->nork = nork; newb->mark = 0; newb->gen = gen; newb->arrows = NULL; if (prev == NULL) eventp->bubbles = newb; else prev->next = newb; return (newb); } struct bubble * itree_next_bubble(struct event *eventp, struct bubble *last) { struct bubble *next; for (;;) { if (last != NULL) next = last->next; else next = eventp->bubbles; if (next == NULL || next->arrows != NULL) return (next); /* bubble was empty, skip it */ last = next; } } static void add_arrow(struct bubble *bp, struct arrow *ap) { struct arrowlist *prev = NULL; struct arrowlist *curr; struct arrowlist *newal; newal = alloc_xmalloc(sizeof (struct arrowlist)); bzero(newal, sizeof (struct arrowlist)); newal->arrowp = ap; curr = itree_next_arrow(bp, NULL); while (curr != NULL) { prev = curr; curr = itree_next_arrow(bp, curr); } if (prev == NULL) bp->arrows = newal; else prev->next = newal; } static struct arrow * itree_add_arrow(struct node *apnode, struct node *fromevent, struct node *toevent, struct lut *ex) { struct arrow *newa; newa = alloc_xmalloc(sizeof (struct arrow)); bzero(newa, sizeof (struct arrow)); newa->pnode = apnode; newa->constraints = NULL; /* * Set default delays, then try to re-set them from * any within() constraints. */ newa->mindelay = newa->maxdelay = 0ULL; if (itree_set_arrow_traits(newa, fromevent, toevent, ex) == 0) { alloc_xfree(newa, sizeof (struct arrow)); return (NULL); } return (newa); } /* returns false if traits show that arrow should not be added after all */ static int itree_set_arrow_traits(struct arrow *ap, struct node *fromev, struct node *toev, struct lut *ex) { struct node *events[] = { NULL, NULL, NULL }; struct node *newc = NULL; ASSERTeq(fromev->t, T_EVENT, ptree_nodetype2str); ASSERTeq(toev->t, T_EVENT, ptree_nodetype2str); /* * search for the within values first on the declaration of * the destination event, and then on the prop. this allows * one to specify a "default" within by putting it on the * declaration, but then allow overriding on the prop statement. */ arrow_add_within(ap, toev->u.event.declp->u.stmt.np->u.event.eexprlist); arrow_add_within(ap, toev->u.event.eexprlist); /* * handle any global constraints inherited from the * "fromev" event's declaration */ ASSERT(fromev->u.event.declp != NULL); ASSERT(fromev->u.event.declp->u.stmt.np != NULL); #ifdef notdef /* XXX not quite ready to evaluate constraints from decls yet */ if (fromev->u.event.declp->u.stmt.np->u.event.eexprlist) (void) itree_add_constraint(ap, fromev->u.event.declp->u.stmt.np->u.event.eexprlist); #endif /* notdef */ /* handle constraints on the from event in the prop statement */ events[0] = fromev; events[1] = toev; if (eval_potential(fromev->u.event.eexprlist, ex, events, &newc, Ninfo.croot) == 0) return (0); /* constraint disallows arrow */ /* * handle any global constraints inherited from the * "toev" event's declaration */ ASSERT(toev->u.event.declp != NULL); ASSERT(toev->u.event.declp->u.stmt.np != NULL); #ifdef notdef /* XXX not quite ready to evaluate constraints from decls yet */ if (toev->u.event.declp->u.stmt.np->u.event.eexprlist) (void) itree_add_constraint(ap, toev->u.event.declp->u.stmt.np->u.event.eexprlist); #endif /* notdef */ /* handle constraints on the to event in the prop statement */ events[0] = toev; events[1] = fromev; if (eval_potential(toev->u.event.eexprlist, ex, events, &newc, Ninfo.croot) == 0) { if (newc != NULL) tree_free(newc); return (0); /* constraint disallows arrow */ } /* if we came up with any deferred constraints, add them to arrow */ if (newc != NULL) { out(O_ALTFP|O_VERB3, "(deferred constraints)"); (void) itree_add_constraint(ap, iexpr(newc)); } return (1); /* constraints allow arrow */ } /* * Set within() constraint. If the constraint were set multiple times, * the last one would "win". */ static void arrow_add_within(struct arrow *ap, struct node *xpr) { struct node *arglist; /* end of expressions list */ if (xpr == NULL) return; switch (xpr->t) { case T_LIST: arrow_add_within(ap, xpr->u.expr.left); arrow_add_within(ap, xpr->u.expr.right); return; case T_FUNC: if (xpr->u.func.s != L_within) return; arglist = xpr->u.func.arglist; switch (arglist->t) { case T_TIMEVAL: ap->mindelay = 0; ap->maxdelay = arglist->u.ull; break; case T_NAME: ASSERT(arglist->u.name.s == L_infinity); ap->mindelay = 0; ap->maxdelay = TIMEVAL_EVENTUALLY; break; case T_LIST: ASSERT(arglist->u.expr.left->t == T_TIMEVAL); ap->mindelay = arglist->u.expr.left->u.ull; switch (arglist->u.expr.right->t) { case T_TIMEVAL: ap->maxdelay = arglist->u.ull; break; case T_NAME: ASSERT(arglist->u.expr.right->u.name.s == L_infinity); ap->maxdelay = TIMEVAL_EVENTUALLY; break; default: out(O_DIE, "within: unexpected 2nd arg type"); } break; default: out(O_DIE, "within: unexpected 1st arg type"); } break; default: return; } } static void itree_free_arrowlists(struct bubble *bubp, int arrows_too) { struct arrowlist *al, *nal; al = bubp->arrows; while (al != NULL) { nal = al->next; if (arrows_too) { itree_free_constraints(al->arrowp); alloc_xfree(al->arrowp, sizeof (struct arrow)); } alloc_xfree(al, sizeof (*al)); al = nal; } } static void itree_delete_arrow(struct bubble *bubp, struct arrow *arrow) { struct arrowlist *al, *oal; al = bubp->arrows; if (al->arrowp == arrow) { bubp->arrows = al->next; alloc_xfree(al, sizeof (*al)); return; } while (al != NULL) { oal = al; al = al->next; ASSERT(al != NULL); if (al->arrowp == arrow) { oal->next = al->next; alloc_xfree(al, sizeof (*al)); return; } } } static void itree_prune_arrowlists(struct bubble *bubp) { struct arrowlist *al, *nal; al = bubp->arrows; while (al != NULL) { nal = al->next; if (bubp->t == B_FROM) itree_delete_arrow(al->arrowp->head, al->arrowp); else itree_delete_arrow(al->arrowp->tail, al->arrowp); itree_free_constraints(al->arrowp); alloc_xfree(al->arrowp, sizeof (struct arrow)); alloc_xfree(al, sizeof (*al)); al = nal; } } struct arrowlist * itree_next_arrow(struct bubble *bubble, struct arrowlist *last) { struct arrowlist *next; if (last != NULL) next = last->next; else next = bubble->arrows; return (next); } static struct constraintlist * itree_add_constraint(struct arrow *arrowp, struct node *c) { struct constraintlist *prev = NULL; struct constraintlist *curr; struct constraintlist *newc; for (curr = arrowp->constraints; curr != NULL; prev = curr, curr = curr->next) ; newc = alloc_xmalloc(sizeof (struct constraintlist)); newc->next = NULL; newc->cnode = c; if (prev == NULL) arrowp->constraints = newc; else prev->next = newc; return (newc); } struct constraintlist * itree_next_constraint(struct arrow *arrowp, struct constraintlist *last) { struct constraintlist *next; if (last != NULL) next = last->next; else next = arrowp->constraints; return (next); } static void itree_free_constraints(struct arrow *ap) { struct constraintlist *cl, *ncl; cl = ap->constraints; while (cl != NULL) { ncl = cl->next; ASSERT(cl->cnode != NULL); if (!iexpr_cached(cl->cnode)) tree_free(cl->cnode); else iexpr_free(cl->cnode); alloc_xfree(cl, sizeof (*cl)); cl = ncl; } } const char * itree_bubbletype2str(enum bubbletype t) { static char buf[100]; switch (t) { case B_FROM: return L_from; case B_TO: return L_to; case B_INHIBIT: return L_inhibit; default: (void) sprintf(buf, "[unexpected bubbletype: %d]", t); return (buf); } } /* * itree_fini -- clean up any half-built itrees */ void itree_fini(void) { if (Ninfo.lut != NULL) { itree_free(Ninfo.lut); Ninfo.lut = NULL; } if (Ninfo.ex) { lut_free(Ninfo.ex, iterinfo_destructor, NULL); Ninfo.ex = NULL; } }