PERFORCE change 135757 for review

John Birrell jb at FreeBSD.org
Tue Feb 19 23:19:57 UTC 2008


http://perforce.freebsd.org/chv.cgi?CH=135757

Change 135757 by jb at 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 <sys/dtrace.h>
 
-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)
 {


More information about the p4-projects mailing list