/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * calls - print a paragraphed list of who calls whom within a body of C source * * Author: M.M. Taylor, DCIEM, Toronto, Canada. * Modified by Alexis Kwan (HCR at DCIEM), * Kevin Szabo (watmath!wateng!ksbszabo, Elec Eng, U of Waterloo), * Tony Hansen, AT&T-IS, pegasus!hansen. */ #include <u.h>#include <libc.h>#include <ctype.h>#include <bio.h>#include <String.h> #define CPP "cpp -+"#define RINSTERR ((Rinst *)-1) /* ugly; error but keep going */ #define STREQ(a, b) (*(a) == *(b) && strcmp(a, b) == 0)#define OTHER(rdwr) ((rdwr) == Rd? Wr: Rd)/* per 8c, all multibyte runes are considered alphabetic */#define ISIDENT(r) (isascii(r) && isalnum(r) || (r) == '_' || (r) >= Runeself) /* safe macros */#define checksys(atom) strbsearch(atom, sysword, nelem(sysword)) enum { Printstats = 0, /* flag */ Maxseen = 4000, /* # of instances w/in a function */ Maxdepth = 300, /* max func call tree depth */ Hashsize = 2048, Maxid = 256 + UTFmax, /* max size of name */ Tabwidth = 8, Maxwidth = 132, /* limits tabbing */ Defwidth = 80, /* limits tabbing */ Backslash = '\\', Quote = '\'', Rd = 0, /* pipe indices */ Wr, Stdin = 0, Stdout, Stderr, Defn = 0, Decl, Call, Nomore = -1, Added, Found,}; typedef struct Pushstate Pushstate;typedef struct Rinst Rinst;typedef struct Root Root;typedef struct Rname Rname;typedef struct Rnamehash Rnamehash; struct Pushstate { int kid; int fd; /* original fd */ int rfd; /* replacement fd */ int input; int open;}; struct Rname { Rinst *dlistp; int rnameout; char rnamecalled; char rnamedefined; char *namer; Rname *next; /* next in hash chain */}; struct Rnamehash { Rname *head;}; /* list of calling instances of those names */struct Rinst { Rname *namep; Rinst *calls; Rinst *calledby;}; struct Root { char *func; Root *next;}; char *aseen[Maxseen]; /* names being gathered within a function */Rnamehash nameshash[Hashsize]; /* names being tracked */Rname *activelist[Maxdepth]; /* names being output */String *cppopt;Root *roots; static struct stats { long highestseen; /* aseen high water mark */ long highestname; /* namelist high water mark */ long highestact; /* activelist high water mark */ long highgetfree; /* getfrees high water mark */} stats; static long getfrees = 0; int bracket = 0; /* curly brace count in input */int linect = 0; /* line number in output */int activep = 0; /* current function being output */ char *infile;int lineno = 1; /* line number of input */int prevc = '\n', thisc = '\n'; /* options */int terse = 1; /* track functions only once */int ntabs = (Maxwidth - 20) / Tabwidth; /* how wide to go */ char *dashes; /* separators for deep nestings */ /* * These are C tokens after which a parenthesis is valid which would * otherwise be tagged as function names. The reserved words which are not * listed are break, const, continue, default, goto and volatile. */char *sysword[] = { "auto", "case", "char", "do", "double", "else", "enum", "extern", "float", "for", "if", "int", "long", "register", "return", "short", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "while",}; /* * warning - print best error message possible */voidwarning(char *s1, char *s2){ fprint(2, "%s: ", argv0); fprint(2, s1, s2); fprint(2, "\n");} /* * safe malloc() code. Does the checking for nil returns from malloc() */void *emalloc(int size){ void *f = mallocz(size, 1); if (f == nil) sysfatal("cannot allocate memory"); return f;} unsignedhash(char *s){ unsigned h; unsigned char *cp; h = 0; for(cp = (unsigned char *)s; *cp; h += *cp++) h *= 1119; return h;} intnextc(Biobuf *in){ prevc = thisc; thisc = Bgetrune(in); return thisc;} intungetc(Biobuf *in){ prevc = thisc; return Bungetrune(in);} intnewatom(Biobuf *in, char *atom){ atom[0] = '\0'; return nextc(in);} /* * lookup (name) accepts a pointer to a name and sees if the name is on the * namelist. If so, it returns a pointer to the nameblock. Otherwise it * returns nil. */Rname *lookfor(char *name){ int i; unsigned buck; Rname *np; Rnamehash *hp; buck = hash(name) % Hashsize; hp = &nameshash[buck]; i = 0; for (np = hp->head; np != nil; np = np->next, i++) if (STREQ(name, np->namer)) return np; if (i > stats.highestname) stats.highestname = i; return nil;} /* * place() returns a pointer to the name on the namelist. If the name was * not in the namelist, place adds it. */Rname *place(char name[]){ unsigned buck; Rname *np; Rnamehash *hp; np = lookfor(name); if (np != nil) return np; buck = hash(name) % Hashsize; hp = &nameshash[buck]; np = emalloc(sizeof *np); np->namer = strdup(name); np->next = hp->head; hp->head = np; return np;} /* * getfree returns a pointer to the next free instance block on the list */Rinst *getfree(void){ Rinst *ret, *new; static Rinst *prev; ++getfrees; if (getfrees > stats.highgetfree) stats.highgetfree = getfrees; if (prev == nil) prev = emalloc(sizeof *prev); new = emalloc(sizeof *new); prev->calls = new; /* also serves as next pointer */ new->calledby = prev; ret = prev; prev = new; return ret;} /* * install(np, rp) puts a new instance of a function into the linked list. * It puts a pointer (np) to its own name (returned by place) into its * namepointer, a pointer to the calling routine (rp) into its called-by * pointer, and zero into the calls pointer. It then puts a pointer to * itself into the last function in the chain. */Rinst *install(Rname *np, Rinst *rp){ Rinst *newp; if (np == nil) return RINSTERR; if ((newp = getfree()) == nil) return nil; newp->namep = np; newp->calls = 0; if (rp) { while (rp->calls) rp = rp->calls; newp->calledby = rp->calledby; rp->calls = newp; } else { newp->calledby = (Rinst *)np; np->dlistp = newp; } return newp;} /* * When scanning the text, each function instance is inserted into a * linear list of names, using the Rname structure, when it is first * encountered. It is also inserted into the linked list using the Rinst * structure. The entry into the name list has a pointer to the defining * instance in the linked list, and each entry in the linked list has a * pointer back to the relevant name. Newproc makes an entry in the * defining list, which is distinguished from the called list only * because it has no calledby link (value=0). Add2proc enters into the * called list, by inserting a link to the new instance in the calls * pointer of the last entry (may be a defining instance, or a function * called by that defining instance), and points back to the defining * instance of the caller in its called-by pointer. */Rinst *newproc(char *name){ int i; Rname *rp; for (i = 0; i < Maxseen; i++) if (aseen[i] != nil) { free(aseen[i]); aseen[i] = nil; } rp = place(name); if (rp == nil) return RINSTERR; /* declaration in a header file is enough to cause this. */ if (0 && rp->rnamedefined) warning("function `%s' redeclared", name); rp->rnamedefined = 1; return install(rp, nil);} /* * add the function name to the calling stack of the current function. */intadd2call(char name[], Rinst *curp){ Rname *p = place(name); Rinst *ip = install(p, curp); if (p != nil && curp != nil && curp->namep != nil && !STREQ(p->namer, curp->namep->namer)) p->rnamecalled = 1; return ip != nil;} /* * backup removes an item from the active stack */voidbackup(void){ if (activep > 0) activelist[--activep] = nil;} /* * makeactive simply puts a pointer to the nameblock into a stack with * maximum depth Maxdepth. the error return only happens for stack * overflow. */intmakeactive(Rname *func){ if (activep < Maxdepth) { if (activep > stats.highestact) stats.highestact = activep; activelist[activep++] = func; return 1; } return 0;} /* * active checks whether the pointer which is its argument has already * occurred on the active list, and returns 1 if so. */intactive(Rname *func){ int i; for (i = 0; i < activep - 1; i++) if (func == activelist[i]) return 1; return 0;} /* * output is a recursive routine to print one tab for each level of * recursion, then the name of the function called, followed by the next * function called by the same higher level routine. In doing this, it * calls itself to output the name of the first function called by the * function whose name it is printing. It maintains an active list of * functions currently being printed by the different levels of * recursion, and if it finds itself asked to print one which is already * active, it terminates, marking that call with a '*'. */voidoutput(Rname *func, int tabc){ int i, tabd, tabstar, tflag; Rinst *nextp; ++linect; print("\n%d", linect); if (!makeactive(func)) { print(" * nesting is too deep"); return; } tabstar = 0; tabd = tabc; for (; tabd > ntabs; tabstar++) tabd -= ntabs; if (tabstar > 0) { print(" "); for (i = 0; i < tabstar; i++) print("<"); } if (tabd == 0) print(" "); else for (i = 0; i < tabd; i++) print("\t"); if (active(func)) print("<<< %s", func->namer); /* recursive call */ else if (func->dlistp == nil) print("%s [external]", func->namer); else { print("%s", func->namer); nextp = func->dlistp->calls; if (!terse || !func->rnameout) { ++tabc; if (!func->rnameout) func->rnameout = linect; if (tabc > ntabs && tabc%ntabs == 1 && nextp) { print("\n%s", dashes); tflag = 1; } else tflag = 0; for (; nextp; nextp = nextp->calls) output(nextp->namep, tabc); if (tflag) { print("\n%s", dashes); tflag = 0; USED(tflag); } } else if (nextp != nil) /* not a leaf */ print(" ... [see line %d]", func->rnameout); } backup();} /* * Dumptree() lists out the calling stacks. All names will be listed out * unless some function names are specified in -f options. */voiddumptree(void){ unsigned buck; Root *rp; Rname *np; if (roots != nil) for (rp = roots; rp != nil; rp = rp->next) if ((np = lookfor(rp->func)) != nil) { output(np, 0); print("\n\n"); } else fprint(2, "%s: function '%s' not found\n", argv0, rp->func); else /* print everything */ for (buck = 0; buck < Hashsize; buck++) for (np = nameshash[buck].head; np != nil; np = np->next) if (!np->rnamecalled) { output(np, 0); print("\n\n"); }} /* * Skipcomments() skips past any blanks and comments in the input stream. */intskipcomments(Biobuf *in, int firstc){ int c;Lines 1–500 of 941. Load more
/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * calls - print a paragraphed list of who calls whom within a body of C source * * Author: M.M. Taylor, DCIEM, Toronto, Canada. * Modified by Alexis Kwan (HCR at DCIEM), * Kevin Szabo (watmath!wateng!ksbszabo, Elec Eng, U of Waterloo), * Tony Hansen, AT&T-IS, pegasus!hansen. */ #include <u.h> #include <libc.h> #include <ctype.h> #include <bio.h> #include <String.h> #define CPP "cpp -+" #define RINSTERR ((Rinst *)-1) /* ugly; error but keep going */ #define STREQ(a, b) (*(a) == *(b) && strcmp(a, b) == 0) #define OTHER(rdwr) ((rdwr) == Rd? Wr: Rd) /* per 8c, all multibyte runes are considered alphabetic */ #define ISIDENT(r) (isascii(r) && isalnum(r) || (r) == '_' || (r) >= Runeself) /* safe macros */ #define checksys(atom) strbsearch(atom, sysword, nelem(sysword)) enum { Printstats = 0, /* flag */ Maxseen = 4000, /* # of instances w/in a function */ Maxdepth = 300, /* max func call tree depth */ Hashsize = 2048, Maxid = 256 + UTFmax, /* max size of name */ Tabwidth = 8, Maxwidth = 132, /* limits tabbing */ Defwidth = 80, /* limits tabbing */ Backslash = '\\', Quote = '\'', Rd = 0, /* pipe indices */ Wr, Stdin = 0, Stdout, Stderr, Defn = 0, Decl, Call, Nomore = -1, Added, Found, }; typedef struct Pushstate Pushstate; typedef struct Rinst Rinst; typedef struct Root Root; typedef struct Rname Rname; typedef struct Rnamehash Rnamehash; struct Pushstate { int kid; int fd; /* original fd */ int rfd; /* replacement fd */ int input; int open; }; struct Rname { Rinst *dlistp; int rnameout; char rnamecalled; char rnamedefined; char *namer; Rname *next; /* next in hash chain */ }; struct Rnamehash { Rname *head; }; /* list of calling instances of those names */ struct Rinst { Rname *namep; Rinst *calls; Rinst *calledby; }; struct Root { char *func; Root *next; }; char *aseen[Maxseen]; /* names being gathered within a function */ Rnamehash nameshash[Hashsize]; /* names being tracked */ Rname *activelist[Maxdepth]; /* names being output */ String *cppopt; Root *roots; static struct stats { long highestseen; /* aseen high water mark */ long highestname; /* namelist high water mark */ long highestact; /* activelist high water mark */ long highgetfree; /* getfrees high water mark */ } stats; static long getfrees = 0; int bracket = 0; /* curly brace count in input */ int linect = 0; /* line number in output */ int activep = 0; /* current function being output */ char *infile; int lineno = 1; /* line number of input */ int prevc = '\n', thisc = '\n'; /* options */ int terse = 1; /* track functions only once */ int ntabs = (Maxwidth - 20) / Tabwidth; /* how wide to go */ char *dashes; /* separators for deep nestings */ /* * These are C tokens after which a parenthesis is valid which would * otherwise be tagged as function names. The reserved words which are not * listed are break, const, continue, default, goto and volatile. */ char *sysword[] = { "auto", "case", "char", "do", "double", "else", "enum", "extern", "float", "for", "if", "int", "long", "register", "return", "short", "sizeof", "static", "struct", "switch", "typedef", "union", "unsigned", "void", "while", }; /* * warning - print best error message possible */ void warning(char *s1, char *s2) { fprint(2, "%s: ", argv0); fprint(2, s1, s2); fprint(2, "\n"); } /* * safe malloc() code. Does the checking for nil returns from malloc() */ void * emalloc(int size) { void *f = mallocz(size, 1); if (f == nil) sysfatal("cannot allocate memory"); return f; } unsigned hash(char *s) { unsigned h; unsigned char *cp; h = 0; for(cp = (unsigned char *)s; *cp; h += *cp++) h *= 1119; return h; } int nextc(Biobuf *in) { prevc = thisc; thisc = Bgetrune(in); return thisc; } int ungetc(Biobuf *in) { prevc = thisc; return Bungetrune(in); } int newatom(Biobuf *in, char *atom) { atom[0] = '\0'; return nextc(in); } /* * lookup (name) accepts a pointer to a name and sees if the name is on the * namelist. If so, it returns a pointer to the nameblock. Otherwise it * returns nil. */ Rname * lookfor(char *name) { int i; unsigned buck; Rname *np; Rnamehash *hp; buck = hash(name) % Hashsize; hp = &nameshash[buck]; i = 0; for (np = hp->head; np != nil; np = np->next, i++) if (STREQ(name, np->namer)) return np; if (i > stats.highestname) stats.highestname = i; return nil; } /* * place() returns a pointer to the name on the namelist. If the name was * not in the namelist, place adds it. */ Rname * place(char name[]) { unsigned buck; Rname *np; Rnamehash *hp; np = lookfor(name); if (np != nil) return np; buck = hash(name) % Hashsize; hp = &nameshash[buck]; np = emalloc(sizeof *np); np->namer = strdup(name); np->next = hp->head; hp->head = np; return np; } /* * getfree returns a pointer to the next free instance block on the list */ Rinst * getfree(void) { Rinst *ret, *new; static Rinst *prev; ++getfrees; if (getfrees > stats.highgetfree) stats.highgetfree = getfrees; if (prev == nil) prev = emalloc(sizeof *prev); new = emalloc(sizeof *new); prev->calls = new; /* also serves as next pointer */ new->calledby = prev; ret = prev; prev = new; return ret; } /* * install(np, rp) puts a new instance of a function into the linked list. * It puts a pointer (np) to its own name (returned by place) into its * namepointer, a pointer to the calling routine (rp) into its called-by * pointer, and zero into the calls pointer. It then puts a pointer to * itself into the last function in the chain. */ Rinst * install(Rname *np, Rinst *rp) { Rinst *newp; if (np == nil) return RINSTERR; if ((newp = getfree()) == nil) return nil; newp->namep = np; newp->calls = 0; if (rp) { while (rp->calls) rp = rp->calls; newp->calledby = rp->calledby; rp->calls = newp; } else { newp->calledby = (Rinst *)np; np->dlistp = newp; } return newp; } /* * When scanning the text, each function instance is inserted into a * linear list of names, using the Rname structure, when it is first * encountered. It is also inserted into the linked list using the Rinst * structure. The entry into the name list has a pointer to the defining * instance in the linked list, and each entry in the linked list has a * pointer back to the relevant name. Newproc makes an entry in the * defining list, which is distinguished from the called list only * because it has no calledby link (value=0). Add2proc enters into the * called list, by inserting a link to the new instance in the calls * pointer of the last entry (may be a defining instance, or a function * called by that defining instance), and points back to the defining * instance of the caller in its called-by pointer. */ Rinst * newproc(char *name) { int i; Rname *rp; for (i = 0; i < Maxseen; i++) if (aseen[i] != nil) { free(aseen[i]); aseen[i] = nil; } rp = place(name); if (rp == nil) return RINSTERR; /* declaration in a header file is enough to cause this. */ if (0 && rp->rnamedefined) warning("function `%s' redeclared", name); rp->rnamedefined = 1; return install(rp, nil); } /* * add the function name to the calling stack of the current function. */ int add2call(char name[], Rinst *curp) { Rname *p = place(name); Rinst *ip = install(p, curp); if (p != nil && curp != nil && curp->namep != nil && !STREQ(p->namer, curp->namep->namer)) p->rnamecalled = 1; return ip != nil; } /* * backup removes an item from the active stack */ void backup(void) { if (activep > 0) activelist[--activep] = nil; } /* * makeactive simply puts a pointer to the nameblock into a stack with * maximum depth Maxdepth. the error return only happens for stack * overflow. */ int makeactive(Rname *func) { if (activep < Maxdepth) { if (activep > stats.highestact) stats.highestact = activep; activelist[activep++] = func; return 1; } return 0; } /* * active checks whether the pointer which is its argument has already * occurred on the active list, and returns 1 if so. */ int active(Rname *func) { int i; for (i = 0; i < activep - 1; i++) if (func == activelist[i]) return 1; return 0; } /* * output is a recursive routine to print one tab for each level of * recursion, then the name of the function called, followed by the next * function called by the same higher level routine. In doing this, it * calls itself to output the name of the first function called by the * function whose name it is printing. It maintains an active list of * functions currently being printed by the different levels of * recursion, and if it finds itself asked to print one which is already * active, it terminates, marking that call with a '*'. */ void output(Rname *func, int tabc) { int i, tabd, tabstar, tflag; Rinst *nextp; ++linect; print("\n%d", linect); if (!makeactive(func)) { print(" * nesting is too deep"); return; } tabstar = 0; tabd = tabc; for (; tabd > ntabs; tabstar++) tabd -= ntabs; if (tabstar > 0) { print(" "); for (i = 0; i < tabstar; i++) print("<"); } if (tabd == 0) print(" "); else for (i = 0; i < tabd; i++) print("\t"); if (active(func)) print("<<< %s", func->namer); /* recursive call */ else if (func->dlistp == nil) print("%s [external]", func->namer); else { print("%s", func->namer); nextp = func->dlistp->calls; if (!terse || !func->rnameout) { ++tabc; if (!func->rnameout) func->rnameout = linect; if (tabc > ntabs && tabc%ntabs == 1 && nextp) { print("\n%s", dashes); tflag = 1; } else tflag = 0; for (; nextp; nextp = nextp->calls) output(nextp->namep, tabc); if (tflag) { print("\n%s", dashes); tflag = 0; USED(tflag); } } else if (nextp != nil) /* not a leaf */ print(" ... [see line %d]", func->rnameout); } backup(); } /* * Dumptree() lists out the calling stacks. All names will be listed out * unless some function names are specified in -f options. */ void dumptree(void) { unsigned buck; Root *rp; Rname *np; if (roots != nil) for (rp = roots; rp != nil; rp = rp->next) if ((np = lookfor(rp->func)) != nil) { output(np, 0); print("\n\n"); } else fprint(2, "%s: function '%s' not found\n", argv0, rp->func); else /* print everything */ for (buck = 0; buck < Hashsize; buck++) for (np = nameshash[buck].head; np != nil; np = np->next) if (!np->rnamecalled) { output(np, 0); print("\n\n"); } } /* * Skipcomments() skips past any blanks and comments in the input stream. */ int skipcomments(Biobuf *in, int firstc) { int c;