From owner-p4-projects@FreeBSD.ORG Tue Feb 19 23:19:57 2008 Return-Path: Delivered-To: p4-projects@freebsd.org Received: by hub.freebsd.org (Postfix, from userid 32767) id 4161C16A41A; Tue, 19 Feb 2008 23:19:57 +0000 (UTC) Delivered-To: perforce@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id C88E316A46C for ; Tue, 19 Feb 2008 23:19:56 +0000 (UTC) (envelope-from jb@freebsd.org) Received: from repoman.freebsd.org (repoman.freebsd.org [IPv6:2001:4f8:fff6::29]) by mx1.freebsd.org (Postfix) with ESMTP id B95FA13C47E for ; Tue, 19 Feb 2008 23:19:56 +0000 (UTC) (envelope-from jb@freebsd.org) Received: from repoman.freebsd.org (localhost [127.0.0.1]) by repoman.freebsd.org (8.14.1/8.14.1) with ESMTP id m1JNJuAI036716 for ; Tue, 19 Feb 2008 23:19:56 GMT (envelope-from jb@freebsd.org) Received: (from perforce@localhost) by repoman.freebsd.org (8.14.1/8.14.1/Submit) id m1JNJuSk036713 for perforce@freebsd.org; Tue, 19 Feb 2008 23:19:56 GMT (envelope-from jb@freebsd.org) Date: Tue, 19 Feb 2008 23:19:56 GMT Message-Id: <200802192319.m1JNJuSk036713@repoman.freebsd.org> X-Authentication-Warning: repoman.freebsd.org: perforce set sender to jb@freebsd.org using -f From: John Birrell To: Perforce Change Reviews Cc: Subject: PERFORCE change 135757 for review X-BeenThere: p4-projects@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: p4 projects tree changes List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 19 Feb 2008 23:19:57 -0000 http://perforce.freebsd.org/chv.cgi?CH=135757 Change 135757 by jb@jb_freebsd1 on 2008/02/19 23:19:51 Add code to implement probe argument descriptions by interpreting the CTF data. Affected files ... .. //depot/projects/dtrace/src/sys/cddl/dev/fbt/fbt.c#8 edit Differences ... ==== //depot/projects/dtrace/src/sys/cddl/dev/fbt/fbt.c#8 (text+ko) ==== @@ -60,7 +60,6 @@ #include -MALLOC_DECLARE(M_FBT); MALLOC_DEFINE(M_FBT, "fbt", "Function Boundary Tracing"); #define FBT_PUSHL_EBP 0x55 @@ -83,6 +82,7 @@ static d_open_t fbt_open; static int fbt_unload(void); +static void fbt_getargdesc(void *, dtrace_id_t, void *, dtrace_argdesc_t *); static void fbt_provide_module(void *, modctl_t *); static void fbt_destroy(void *, dtrace_id_t, void *); static void fbt_enable(void *, dtrace_id_t, void *); @@ -117,7 +117,7 @@ fbt_disable, fbt_suspend, fbt_resume, - NULL, + fbt_getargdesc, NULL, NULL, fbt_destroy @@ -136,6 +136,7 @@ int fbtp_loadcnt; int fbtp_primary; int fbtp_invop_cnt; + int fbtp_symindx; struct fbt_probe *fbtp_next; } fbt_probe_t; @@ -206,7 +207,8 @@ } static int -fbt_provide_module_function(linker_file_t lf, linker_symval_t *symval, void *opaque) +fbt_provide_module_function(linker_file_t lf, int symindx, + linker_symval_t *symval, void *opaque) { char *modname = opaque; const char *name = symval->name; @@ -271,6 +273,7 @@ fbt->fbtp_rval = DTRACE_INVOP_PUSHL_EBP; fbt->fbtp_savedval = *instr; fbt->fbtp_patchval = FBT_PATCHVAL; + fbt->fbtp_symindx = symindx; fbt->fbtp_hashnext = fbt_probetab[FBT_ADDR2NDX(instr)]; fbt_probetab[FBT_ADDR2NDX(instr)] = fbt; @@ -357,6 +360,7 @@ fbt->fbtp_patchpoint = instr; fbt->fbtp_ctl = lf; fbt->fbtp_loadcnt = lf->loadcnt; + fbt->fbtp_symindx = symindx; #ifndef __amd64__ if (*instr == FBT_POPL_EBP) { @@ -386,7 +390,6 @@ goto again; } -/*ARGSUSED*/ static void fbt_provide_module(void *arg, modctl_t *lf) { @@ -437,7 +440,6 @@ (void) linker_file_function_listall(lf, fbt_provide_module_function, modname); } -/* ARGSUSED */ static void fbt_destroy(void *arg, dtrace_id_t id, void *parg) { @@ -476,7 +478,6 @@ } while (fbt != NULL); } -/* ARGSUSED */ static void fbt_enable(void *arg, dtrace_id_t id, void *parg) { @@ -505,7 +506,6 @@ } } -/* ARGSUSED */ static void fbt_disable(void *arg, dtrace_id_t id, void *parg) { @@ -522,7 +522,6 @@ *fbt->fbtp_patchpoint = fbt->fbtp_savedval; } -/*ARGSUSED*/ static void fbt_suspend(void *arg, dtrace_id_t id, void *parg) { @@ -538,7 +537,6 @@ *fbt->fbtp_patchpoint = fbt->fbtp_savedval; } -/*ARGSUSED*/ static void fbt_resume(void *arg, dtrace_id_t id, void *parg) { @@ -554,7 +552,758 @@ *fbt->fbtp_patchpoint = fbt->fbtp_patchval; } +static int +fbt_ctfoff_init(modctl_t *lf, linker_ctf_t *lc) +{ + const Elf_Sym *symp = lc->symtab;; + const char *name; + const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab; + const uint8_t *ctfdata = lc->ctftab + sizeof(ctf_header_t); + int i; + uint32_t *ctfoff; + uint32_t objtoff = hp->cth_objtoff; + uint32_t funcoff = hp->cth_funcoff; + ushort_t info; + ushort_t vlen; + + /* Sanity check. */ + if (hp->cth_magic != CTF_MAGIC) { + printf("Bad magic value in CTF data of '%s'\n",lf->pathname); + return (EINVAL); + } + + if (lc->symtab == NULL) { + printf("No symbol table in '%s'\n",lf->pathname); + return (EINVAL); + } + + if ((ctfoff = malloc(sizeof(uint32_t) * lc->nsym, M_LINKER, M_WAITOK)) == NULL) + return (ENOMEM); + + *lc->ctfoffp = ctfoff; + + for (i = 0; i < lc->nsym; i++, ctfoff++, symp++) { + if (symp->st_name == 0 || symp->st_shndx == SHN_UNDEF) { + *ctfoff = 0xffffffff; + continue; + } + + if (symp->st_name < lc->strcnt) + name = lc->strtab + symp->st_name; + else + name = "(?)"; + + switch (ELF_ST_TYPE(symp->st_info)) { + case STT_OBJECT: + if (objtoff >= hp->cth_funcoff || + (symp->st_shndx == SHN_ABS && symp->st_value == 0)) { + *ctfoff = 0xffffffff; + break; + } + + *ctfoff = objtoff; + objtoff += sizeof (ushort_t); + break; + + case STT_FUNC: + if (funcoff >= hp->cth_typeoff) { + *ctfoff = 0xffffffff; + break; + } + + *ctfoff = funcoff; + + info = *((const ushort_t *)(ctfdata + funcoff)); + vlen = CTF_INFO_VLEN(info); + + /* + * If we encounter a zero pad at the end, just skip it. + * Otherwise skip over the function and its return type + * (+2) and the argument list (vlen). + */ + if (CTF_INFO_KIND(info) == CTF_K_UNKNOWN && vlen == 0) + funcoff += sizeof (ushort_t); /* skip pad */ + else + funcoff += sizeof (ushort_t) * (vlen + 2); + break; + + default: + *ctfoff = 0xffffffff; + break; + } + } + + return (0); +} + +static ssize_t +fbt_get_ctt_size(uint8_t version, const ctf_type_t *tp, ssize_t *sizep, + ssize_t *incrementp) +{ + ssize_t size, increment; + + if (version > CTF_VERSION_1 && + tp->ctt_size == CTF_LSIZE_SENT) { + size = CTF_TYPE_LSIZE(tp); + increment = sizeof (ctf_type_t); + } else { + size = tp->ctt_size; + increment = sizeof (ctf_stype_t); + } + + if (sizep) + *sizep = size; + if (incrementp) + *incrementp = increment; + + return (size); +} + +static int +fbt_typoff_init(linker_ctf_t *lc) +{ + const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab; + const ctf_type_t *tbuf; + const ctf_type_t *tend; + const ctf_type_t *tp; + const uint8_t *ctfdata = lc->ctftab + sizeof(ctf_header_t); + int ctf_typemax = 0; + uint32_t *xp; + ulong_t pop[CTF_K_MAX + 1] = { 0 }; + + + /* Sanity check. */ + if (hp->cth_magic != CTF_MAGIC) + return (EINVAL); + + tbuf = (const ctf_type_t *) (ctfdata + hp->cth_typeoff); + tend = (const ctf_type_t *) (ctfdata + hp->cth_stroff); + + int child = hp->cth_parname != 0; + + /* + * We make two passes through the entire type section. In this first + * pass, we count the number of each type and the total number of types. + */ + for (tp = tbuf; tp < tend; ctf_typemax++) { + ushort_t kind = CTF_INFO_KIND(tp->ctt_info); + ulong_t vlen = CTF_INFO_VLEN(tp->ctt_info); + ssize_t size, increment; + + size_t vbytes; + uint_t n; + + (void) fbt_get_ctt_size(hp->cth_version, tp, &size, &increment); + + switch (kind) { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + vbytes = sizeof (uint_t); + break; + case CTF_K_ARRAY: + vbytes = sizeof (ctf_array_t); + break; + case CTF_K_FUNCTION: + vbytes = sizeof (ushort_t) * (vlen + (vlen & 1)); + break; + case CTF_K_STRUCT: + case CTF_K_UNION: + if (size < CTF_LSTRUCT_THRESH) { + ctf_member_t *mp = (ctf_member_t *) + ((uintptr_t)tp + increment); + + vbytes = sizeof (ctf_member_t) * vlen; + for (n = vlen; n != 0; n--, mp++) + child |= CTF_TYPE_ISCHILD(mp->ctm_type); + } else { + ctf_lmember_t *lmp = (ctf_lmember_t *) + ((uintptr_t)tp + increment); + + vbytes = sizeof (ctf_lmember_t) * vlen; + for (n = vlen; n != 0; n--, lmp++) + child |= + CTF_TYPE_ISCHILD(lmp->ctlm_type); + } + break; + case CTF_K_ENUM: + vbytes = sizeof (ctf_enum_t) * vlen; + break; + case CTF_K_FORWARD: + /* + * For forward declarations, ctt_type is the CTF_K_* + * kind for the tag, so bump that population count too. + * If ctt_type is unknown, treat the tag as a struct. + */ + if (tp->ctt_type == CTF_K_UNKNOWN || + tp->ctt_type >= CTF_K_MAX) + pop[CTF_K_STRUCT]++; + else + pop[tp->ctt_type]++; + /*FALLTHRU*/ + case CTF_K_UNKNOWN: + vbytes = 0; + break; + case CTF_K_POINTER: + case CTF_K_TYPEDEF: + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + child |= CTF_TYPE_ISCHILD(tp->ctt_type); + vbytes = 0; + break; + default: + printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind); + return (EIO); + } + tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes); + pop[kind]++; + } + + *lc->typlenp = ctf_typemax; + + if ((xp = malloc(sizeof(uint32_t) * ctf_typemax, M_LINKER, M_ZERO | M_WAITOK)) == NULL) + return (ENOMEM); + + *lc->typoffp = xp; + + /* type id 0 is used as a sentinel value */ + *xp++ = 0; + + /* + * In the second pass, fill in the type offset. + */ + for (tp = tbuf; tp < tend; xp++) { + ushort_t kind = CTF_INFO_KIND(tp->ctt_info); + ulong_t vlen = CTF_INFO_VLEN(tp->ctt_info); + ssize_t size, increment; + + size_t vbytes; + uint_t n; + + (void) fbt_get_ctt_size(hp->cth_version, tp, &size, &increment); + + switch (kind) { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + vbytes = sizeof (uint_t); + break; + case CTF_K_ARRAY: + vbytes = sizeof (ctf_array_t); + break; + case CTF_K_FUNCTION: + vbytes = sizeof (ushort_t) * (vlen + (vlen & 1)); + break; + case CTF_K_STRUCT: + case CTF_K_UNION: + if (size < CTF_LSTRUCT_THRESH) { + ctf_member_t *mp = (ctf_member_t *) + ((uintptr_t)tp + increment); + + vbytes = sizeof (ctf_member_t) * vlen; + for (n = vlen; n != 0; n--, mp++) + child |= CTF_TYPE_ISCHILD(mp->ctm_type); + } else { + ctf_lmember_t *lmp = (ctf_lmember_t *) + ((uintptr_t)tp + increment); + + vbytes = sizeof (ctf_lmember_t) * vlen; + for (n = vlen; n != 0; n--, lmp++) + child |= + CTF_TYPE_ISCHILD(lmp->ctlm_type); + } + break; + case CTF_K_ENUM: + vbytes = sizeof (ctf_enum_t) * vlen; + break; + case CTF_K_FORWARD: + case CTF_K_UNKNOWN: + vbytes = 0; + break; + case CTF_K_POINTER: + case CTF_K_TYPEDEF: + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + vbytes = 0; + break; + default: + printf("%s(%d): detected invalid CTF kind -- %u\n", __func__, __LINE__, kind); + return (EIO); + } + *xp = (uint32_t)((uintptr_t) tp - (uintptr_t) ctfdata); + tp = (ctf_type_t *)((uintptr_t)tp + increment + vbytes); + } + + return (0); +} + +/* + * CTF Declaration Stack + * + * In order to implement ctf_type_name(), we must convert a type graph back + * into a C type declaration. Unfortunately, a type graph represents a storage + * class ordering of the type whereas a type declaration must obey the C rules + * for operator precedence, and the two orderings are frequently in conflict. + * For example, consider these CTF type graphs and their C declarations: + * + * CTF_K_POINTER -> CTF_K_FUNCTION -> CTF_K_INTEGER : int (*)() + * CTF_K_POINTER -> CTF_K_ARRAY -> CTF_K_INTEGER : int (*)[] + * + * In each case, parentheses are used to raise operator * to higher lexical + * precedence, so the string form of the C declaration cannot be constructed by + * walking the type graph links and forming the string from left to right. + * + * The functions in this file build a set of stacks from the type graph nodes + * corresponding to the C operator precedence levels in the appropriate order. + * The code in ctf_type_name() can then iterate over the levels and nodes in + * lexical precedence order and construct the final C declaration string. + */ +typedef struct ctf_list { + struct ctf_list *l_prev; /* previous pointer or tail pointer */ + struct ctf_list *l_next; /* next pointer or head pointer */ +} ctf_list_t; + +#define ctf_list_prev(elem) ((void *)(((ctf_list_t *)(elem))->l_prev)) +#define ctf_list_next(elem) ((void *)(((ctf_list_t *)(elem))->l_next)) + +typedef enum { + CTF_PREC_BASE, + CTF_PREC_POINTER, + CTF_PREC_ARRAY, + CTF_PREC_FUNCTION, + CTF_PREC_MAX +} ctf_decl_prec_t; + +typedef struct ctf_decl_node { + ctf_list_t cd_list; /* linked list pointers */ + ctf_id_t cd_type; /* type identifier */ + uint_t cd_kind; /* type kind */ + uint_t cd_n; /* type dimension if array */ +} ctf_decl_node_t; + +typedef struct ctf_decl { + ctf_list_t cd_nodes[CTF_PREC_MAX]; /* declaration node stacks */ + int cd_order[CTF_PREC_MAX]; /* storage order of decls */ + ctf_decl_prec_t cd_qualp; /* qualifier precision */ + ctf_decl_prec_t cd_ordp; /* ordered precision */ + char *cd_buf; /* buffer for output */ + char *cd_ptr; /* buffer location */ + char *cd_end; /* buffer limit */ + size_t cd_len; /* buffer space required */ + int cd_err; /* saved error value */ +} ctf_decl_t; + +/* + * Simple doubly-linked list append routine. This implementation assumes that + * each list element contains an embedded ctf_list_t as the first member. + * An additional ctf_list_t is used to store the head (l_next) and tail + * (l_prev) pointers. The current head and tail list elements have their + * previous and next pointers set to NULL, respectively. + */ +static void +ctf_list_append(ctf_list_t *lp, void *new) +{ + ctf_list_t *p = lp->l_prev; /* p = tail list element */ + ctf_list_t *q = new; /* q = new list element */ + + lp->l_prev = q; + q->l_prev = p; + q->l_next = NULL; + + if (p != NULL) + p->l_next = q; + else + lp->l_next = q; +} + +/* + * Prepend the specified existing element to the given ctf_list_t. The + * existing pointer should be pointing at a struct with embedded ctf_list_t. + */ static void +ctf_list_prepend(ctf_list_t *lp, void *new) +{ + ctf_list_t *p = new; /* p = new list element */ + ctf_list_t *q = lp->l_next; /* q = head list element */ + + lp->l_next = p; + p->l_prev = NULL; + p->l_next = q; + + if (q != NULL) + q->l_prev = p; + else + lp->l_prev = p; +} + +static void +ctf_decl_init(ctf_decl_t *cd, char *buf, size_t len) +{ + int i; + + bzero(cd, sizeof (ctf_decl_t)); + + for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++) + cd->cd_order[i] = CTF_PREC_BASE - 1; + + cd->cd_qualp = CTF_PREC_BASE; + cd->cd_ordp = CTF_PREC_BASE; + + cd->cd_buf = buf; + cd->cd_ptr = buf; + cd->cd_end = buf + len; +} + +static void +ctf_decl_fini(ctf_decl_t *cd) +{ + ctf_decl_node_t *cdp, *ndp; + int i; + + for (i = CTF_PREC_BASE; i < CTF_PREC_MAX; i++) { + for (cdp = ctf_list_next(&cd->cd_nodes[i]); + cdp != NULL; cdp = ndp) { + ndp = ctf_list_next(cdp); + free(cdp, M_FBT); + } + } +} + +static const ctf_type_t * +ctf_lookup_by_id(linker_ctf_t *lc, ctf_id_t type) +{ + const ctf_type_t *tp; + uint32_t offset; + uint32_t *typoff = *lc->typoffp; + + if (type >= *lc->typlenp) { + printf("%s(%d): type %d exceeds max %ld\n",__func__,__LINE__,(int) type,*lc->typlenp); + return(NULL); + } + + /* Check if the type isn't cross-referenced. */ + if ((offset = typoff[type]) == 0) { + printf("%s(%d): type %d isn't cross referenced\n",__func__,__LINE__, (int) type); + return(NULL); + } + + tp = (const ctf_type_t *)(lc->ctftab + offset + sizeof(ctf_header_t)); + + return (tp); +} + +static void +fbt_array_info(linker_ctf_t *lc, ctf_id_t type, ctf_arinfo_t *arp) +{ + const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab; + const ctf_type_t *tp; + const ctf_array_t *ap; + ssize_t increment; + + bzero(arp, sizeof(*arp)); + + if ((tp = ctf_lookup_by_id(lc, type)) == NULL) + return; + + if (CTF_INFO_KIND(tp->ctt_info) != CTF_K_ARRAY) + return; + + (void) fbt_get_ctt_size(hp->cth_version, tp, NULL, &increment); + + ap = (const ctf_array_t *)((uintptr_t)tp + increment); + arp->ctr_contents = ap->cta_contents; + arp->ctr_index = ap->cta_index; + arp->ctr_nelems = ap->cta_nelems; +} + +static const char * +ctf_strptr(linker_ctf_t *lc, int name) +{ + const ctf_header_t *hp = (const ctf_header_t *) lc->ctftab;; + const char *strp = ""; + + if (name < 0 || name >= hp->cth_strlen) + return(strp); + + strp = (const char *)(lc->ctftab + hp->cth_stroff + name + sizeof(ctf_header_t)); + + return (strp); +} + +static void +ctf_decl_push(ctf_decl_t *cd, linker_ctf_t *lc, ctf_id_t type) +{ + ctf_decl_node_t *cdp; + ctf_decl_prec_t prec; + uint_t kind, n = 1; + int is_qual = 0; + + const ctf_type_t *tp; + ctf_arinfo_t ar; + + if ((tp = ctf_lookup_by_id(lc, type)) == NULL) { + cd->cd_err = ENOENT; + return; + } + + switch (kind = CTF_INFO_KIND(tp->ctt_info)) { + case CTF_K_ARRAY: + fbt_array_info(lc, type, &ar); + ctf_decl_push(cd, lc, ar.ctr_contents); + n = ar.ctr_nelems; + prec = CTF_PREC_ARRAY; + break; + + case CTF_K_TYPEDEF: + if (ctf_strptr(lc, tp->ctt_name)[0] == '\0') { + ctf_decl_push(cd, lc, tp->ctt_type); + return; + } + prec = CTF_PREC_BASE; + break; + + case CTF_K_FUNCTION: + ctf_decl_push(cd, lc, tp->ctt_type); + prec = CTF_PREC_FUNCTION; + break; + + case CTF_K_POINTER: + ctf_decl_push(cd, lc, tp->ctt_type); + prec = CTF_PREC_POINTER; + break; + + case CTF_K_VOLATILE: + case CTF_K_CONST: + case CTF_K_RESTRICT: + ctf_decl_push(cd, lc, tp->ctt_type); + prec = cd->cd_qualp; + is_qual++; + break; + + default: + prec = CTF_PREC_BASE; + } + + if ((cdp = malloc(sizeof (ctf_decl_node_t), M_FBT, M_WAITOK)) == NULL) { + cd->cd_err = EAGAIN; + return; + } + + cdp->cd_type = type; + cdp->cd_kind = kind; + cdp->cd_n = n; + + if (ctf_list_next(&cd->cd_nodes[prec]) == NULL) + cd->cd_order[prec] = cd->cd_ordp++; + + /* + * Reset cd_qualp to the highest precedence level that we've seen so + * far that can be qualified (CTF_PREC_BASE or CTF_PREC_POINTER). + */ + if (prec > cd->cd_qualp && prec < CTF_PREC_ARRAY) + cd->cd_qualp = prec; + + /* + * C array declarators are ordered inside out so prepend them. Also by + * convention qualifiers of base types precede the type specifier (e.g. + * const int vs. int const) even though the two forms are equivalent. + */ + if (kind == CTF_K_ARRAY || (is_qual && prec == CTF_PREC_BASE)) + ctf_list_prepend(&cd->cd_nodes[prec], cdp); + else + ctf_list_append(&cd->cd_nodes[prec], cdp); +} + +static void +ctf_decl_sprintf(ctf_decl_t *cd, const char *format, ...) +{ + size_t len = (size_t)(cd->cd_end - cd->cd_ptr); + va_list ap; + size_t n; + + va_start(ap, format); + n = vsnprintf(cd->cd_ptr, len, format, ap); + va_end(ap); + + cd->cd_ptr += MIN(n, len); + cd->cd_len += n; +} + +static ssize_t +fbt_type_name(linker_ctf_t *lc, ctf_id_t type, char *buf, size_t len) +{ + ctf_decl_t cd; + ctf_decl_node_t *cdp; + ctf_decl_prec_t prec, lp, rp; + int ptr, arr; + uint_t k; + + if (lc == NULL && type == CTF_ERR) + return (-1); /* simplify caller code by permitting CTF_ERR */ + + ctf_decl_init(&cd, buf, len); + ctf_decl_push(&cd, lc, type); + + if (cd.cd_err != 0) { + ctf_decl_fini(&cd); + return (-1); + } + + /* + * If the type graph's order conflicts with lexical precedence order + * for pointers or arrays, then we need to surround the declarations at + * the corresponding lexical precedence with parentheses. This can + * result in either a parenthesized pointer (*) as in int (*)() or + * int (*)[], or in a parenthesized pointer and array as in int (*[])(). + */ + ptr = cd.cd_order[CTF_PREC_POINTER] > CTF_PREC_POINTER; + arr = cd.cd_order[CTF_PREC_ARRAY] > CTF_PREC_ARRAY; + + rp = arr ? CTF_PREC_ARRAY : ptr ? CTF_PREC_POINTER : -1; + lp = ptr ? CTF_PREC_POINTER : arr ? CTF_PREC_ARRAY : -1; + + k = CTF_K_POINTER; /* avoid leading whitespace (see below) */ + + for (prec = CTF_PREC_BASE; prec < CTF_PREC_MAX; prec++) { + for (cdp = ctf_list_next(&cd.cd_nodes[prec]); + cdp != NULL; cdp = ctf_list_next(cdp)) { + + const ctf_type_t *tp = + ctf_lookup_by_id(lc, cdp->cd_type); + const char *name = ctf_strptr(lc, tp->ctt_name); + + if (k != CTF_K_POINTER && k != CTF_K_ARRAY) + ctf_decl_sprintf(&cd, " "); + + if (lp == prec) { + ctf_decl_sprintf(&cd, "("); + lp = -1; + } + + switch (cdp->cd_kind) { + case CTF_K_INTEGER: + case CTF_K_FLOAT: + case CTF_K_TYPEDEF: + ctf_decl_sprintf(&cd, "%s", name); + break; + case CTF_K_POINTER: + ctf_decl_sprintf(&cd, "*"); + break; + case CTF_K_ARRAY: + ctf_decl_sprintf(&cd, "[%u]", cdp->cd_n); + break; + case CTF_K_FUNCTION: + ctf_decl_sprintf(&cd, "()"); + break; + case CTF_K_STRUCT: + case CTF_K_FORWARD: + ctf_decl_sprintf(&cd, "struct %s", name); + break; + case CTF_K_UNION: + ctf_decl_sprintf(&cd, "union %s", name); + break; + case CTF_K_ENUM: + ctf_decl_sprintf(&cd, "enum %s", name); + break; + case CTF_K_VOLATILE: + ctf_decl_sprintf(&cd, "volatile"); + break; + case CTF_K_CONST: + ctf_decl_sprintf(&cd, "const"); + break; + case CTF_K_RESTRICT: + ctf_decl_sprintf(&cd, "restrict"); + break; + } + + k = cdp->cd_kind; + } + + if (rp == prec) + ctf_decl_sprintf(&cd, ")"); + } + + ctf_decl_fini(&cd); + return (cd.cd_len); +} + +static void +fbt_getargdesc(void *arg __unused, dtrace_id_t id __unused, void *parg, dtrace_argdesc_t *desc) +{ + const ushort_t *dp; + fbt_probe_t *fbt = parg; + linker_ctf_t lc; + modctl_t *ctl = fbt->fbtp_ctl; + int ndx = desc->dtargd_ndx; + int symindx = fbt->fbtp_symindx; + uint32_t *ctfoff; + uint32_t offset; + ushort_t info, kind, n; + + desc->dtargd_ndx = DTRACE_ARGNONE; + + /* Get a pointer to the CTF data and it's length. */ + if (linker_ctf_get(ctl, &lc) != 0) + /* No CTF data? Something wrong? *shrug* */ + return; + + /* Check if this module hasn't been initialised yet. */ + if (*lc.ctfoffp == NULL) { + /* + * Initialise the CTF object and function symindx to + * byte offset array. + */ + if (fbt_ctfoff_init(ctl, &lc) != 0) + return; + + /* Initialise the CTF type to byte offset array. */ + if (fbt_typoff_init(&lc) != 0) + return; + } + + ctfoff = *lc.ctfoffp; + + if (ctfoff == NULL || *lc.typoffp == NULL) + return; + + /* Check if the symbol index is out of range. */ + if (symindx >= lc.nsym) + return; + + /* Check if the symbol isn't cross-referenced. */ + if ((offset = ctfoff[symindx]) == 0xffffffff) + return; + + dp = (const ushort_t *)(lc.ctftab + offset + sizeof(ctf_header_t)); + + info = *dp++; + kind = CTF_INFO_KIND(info); + n = CTF_INFO_VLEN(info); + + if (kind == CTF_K_UNKNOWN && n == 0) { + printf("%s(%d): Unknown function!\n",__func__,__LINE__); + return; + } + + if (kind != CTF_K_FUNCTION) { + printf("%s(%d): Expected a function!\n",__func__,__LINE__); + return; + } + + /* Check if the requested argument doesn't exist. */ + if (ndx >= n) + return; + + /* Skip the return type and arguments up to the one requested. */ + dp += ndx + 1; + + if (fbt_type_name(&lc, *dp, desc->dtargd_native, sizeof(desc->dtargd_native)) > 0) + desc->dtargd_ndx = ndx; + + return; +} + +static void fbt_load(void *dummy) { /* Create the /dev/dtrace/fbt entry. */ @@ -602,7 +1351,6 @@ return (error); } -/* ARGSUSED */ static int fbt_modevent(module_t mod __unused, int type, void *data __unused) { @@ -627,7 +1375,6 @@ return (error); } -/* ARGSUSED */ static int fbt_open(struct cdev *dev __unused, int oflags __unused, int devtype __unused, struct thread *td __unused) {