diff options
| author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
|---|---|---|
| committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
| commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
| tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/tnf | |
| download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz | |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/tnf')
43 files changed, 7742 insertions, 0 deletions
diff --git a/usr/src/cmd/tnf/Makefile b/usr/src/cmd/tnf/Makefile new file mode 100644 index 0000000000..82c77e7fed --- /dev/null +++ b/usr/src/cmd/tnf/Makefile @@ -0,0 +1,49 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright (c) 1995, by Sun Microsystems, Inc. +# All rights reserved. +# +# cmd/tnf/Makefile +# + +SUBDIRS= prex \ + tnfdump \ + tnfxtract + +all := TARGET= all +install := TARGET= install +clean := TARGET= clean +clobber := TARGET= clobber +lint := TARGET= lint +_msg := TARGET= _msg + +.KEEP_STATE: + +all install clean clobber lint _msg: $(SUBDIRS) + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: diff --git a/usr/src/cmd/tnf/prex/Makefile b/usr/src/cmd/tnf/prex/Makefile new file mode 100644 index 0000000000..89cc856f06 --- /dev/null +++ b/usr/src/cmd/tnf/prex/Makefile @@ -0,0 +1,67 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +# ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# cmd/tnf/prex/Makefile +# + +PROG= prex + +include ../../Makefile.cmd + +POFILE=prex.po +POFILES=cmd.po fcn.po list.po main.po new.po prbk.po source.po spec.po util.po + +$(64ONLY)SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +install := TARGET = install +clean := TARGET = clean +clobber := TARGET = clobber +lint := TARGET = lint + +.KEEP_STATE: + +all: $(SUBDIRS) + +clean clobber lint: $(SUBDIRS) + +install: $(SUBDIRS) + -$(RM) $(ROOTPROG) + -$(LN) $(ISAEXEC) $(ROOTPROG) + + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +$(POFILE): $(POFILES) + rm -f $@ + cat $(POFILES) > $@ + +FRC: + +include ../../Makefile.targ diff --git a/usr/src/cmd/tnf/prex/Makefile.com b/usr/src/cmd/tnf/prex/Makefile.com new file mode 100644 index 0000000000..b6ea70d2dc --- /dev/null +++ b/usr/src/cmd/tnf/prex/Makefile.com @@ -0,0 +1,111 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright 1989,2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# cmd/tnf/prex/Makefile.com +# + +PROG= prex + +OBJS.c= source.o \ + main.o \ + util.o \ + expr.o \ + spec.o \ + set.o \ + queue.o \ + cmd.o \ + new.o \ + list.o \ + fcn.o \ + prbk.o \ + help.o + +OBJS.yl= prexgram.o \ + prexlex.o + +OBJS= $(OBJS.yl) $(OBJS.c) + +SRCS= $(OBJS.c:%.o=../%.c) $(OBJS.yl:%.o=%.c) + +SRCS.yl = $(OBJS.yl:%.o=%.c) +CLEANFILES = $(SRCS.yl) y.tab.h + +include ../../../Makefile.cmd + +POFILE= prex.po +POFILES= $(OBJS.c:%.o=%.po) + +#YFLAGS= -d -t -v +YFLAGS= -d +LFLAGS= -v +# FOR normal makefile, uncomment the next line +LDLIBS += -lgen -ltnfctl -lelf -lc +# Uncomment the following line for a debug build +# COPTFLAG = -g -DDEBUG -v + +.KEEP_STATE: + +.PARALLEL: $(OBJS) + +all: $(PROG) + +#OBJS can be built in parallel after all .c (and y.tab.h) are properly built +$(PROG): $(SRCS.yl) .WAIT $(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +#This also builds y.tab.h +prexgram.c: ../prexgram.y + $(YACC.y) ../prexgram.y + mv y.tab.c $@ + +prexlex.c: ../prexlex.l + $(RM) $@ + $(LEX.l) ../prexlex.l > $@ + +#Use %.c in priority to ../%.c for prexgram.c and prexlec.c +%.o: %.c + $(COMPILE.c) $< + +%.o: ../%.c + $(COMPILE.c) $< + + +$(ROOTBIN): + $(INS.dir) + +$(POFILE): $(POFILES) + $(RM) $@ + cat $(POFILES) > $@ + +clean: + $(RM) $(OBJS) $(CLEANFILES) + +lint: $(OBJS) + $(LINT.c) $(SRCS) + +include ../../../Makefile.targ diff --git a/usr/src/cmd/tnf/prex/amd64/Makefile b/usr/src/cmd/tnf/prex/amd64/Makefile new file mode 100644 index 0000000000..f5c1bee5a7 --- /dev/null +++ b/usr/src/cmd/tnf/prex/amd64/Makefile @@ -0,0 +1,35 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.cmd.64 +CFLAGS64 += -I.. -I. +LINTFLAGS64 += -I.. -I. +prexgram.o prexlex.o := CCVERBOSE= + +install: all $(ROOTPROG64) diff --git a/usr/src/cmd/tnf/prex/cmd.c b/usr/src/cmd/tnf/prex/cmd.c new file mode 100644 index 0000000000..9cc6519dde --- /dev/null +++ b/usr/src/cmd/tnf/prex/cmd.c @@ -0,0 +1,296 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#ifndef DEBUG +#define NDEBUG 1 +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <libintl.h> +#include "cmd.h" +#include "set.h" +#include "fcn.h" +#include "new.h" +#include "source.h" + + +/* + * Globals + */ + +static queue_node_t g_cmdlist = { + &g_cmdlist, +&g_cmdlist}; + + +/* + * cmd_set() - creates a cmd using a named set and adds it to the global list + */ + +cmd_t * +cmd_set(char *setname_p, cmd_kind_t kind, char *fcnname_p) +{ + cmd_t *new_p; + set_t *set_p; + + set_p = set_find(setname_p); + if (!set_p) { + semantic_err(gettext("no set named \"$%s\""), setname_p); + return (NULL); + } + if (kind == CMD_CONNECT && !fcn_find(fcnname_p)) { + semantic_err(gettext("no function named \"&%s\""), fcnname_p); + return (NULL); + } + new_p = new(cmd_t); + queue_init(&new_p->qn); +#ifdef LATEBINDSETS + new_p->isnamed = B_TRUE; + new_p->expr.setname_p = setname_p; +#else + new_p->isnamed = B_FALSE; + new_p->expr.expr_p = expr_dup(set_p->exprlist_p); +#endif + new_p->isnew = B_TRUE; + new_p->kind = kind; + new_p->fcnname_p = fcnname_p; + + (void) queue_append(&g_cmdlist, &new_p->qn); + return (new_p); + +} /* end cmd_set */ + + +/* + * cmd_expr() - creates a cmd using a set and adds it to the global list + */ + +cmd_t * +cmd_expr(expr_t * expr_p, cmd_kind_t kind, char *fcnname_p) +{ + cmd_t *new_p; + + if (kind == CMD_CONNECT && !fcn_find(fcnname_p)) { + semantic_err(gettext("no function named \"&%s\""), fcnname_p); + return (NULL); + } + new_p = new(cmd_t); + queue_init(&new_p->qn); + new_p->isnamed = B_FALSE; + new_p->expr.expr_p = expr_p; + new_p->isnew = B_TRUE; + new_p->kind = kind; + new_p->fcnname_p = fcnname_p; + + (void) queue_append(&g_cmdlist, &new_p->qn); + return (new_p); + +} /* end cmd */ + + +#if 0 +/* + * cmd_destroy() + */ + +static void +cmd_destroy(cmd_t * cmd_p) +{ + if (!cmd_p) + return; + + if (!queue_isempty(&cmd_p->qn)) + (void) queue_remove(&cmd_p->qn); + + if (!cmd_p->isnamed) + expr_destroy(cmd_p->expr.expr_p); + + free(cmd_p); + +} /* end cmd_destroy */ +#endif + + +/* + * cmd_list() - pretty prints the global cmdlist + */ + +void +cmd_list(void) +{ + cmd_t *cmd_p; + int i = 0; + char *str_p; + + cmd_p = (cmd_t *) & g_cmdlist; + while ((cmd_p = (cmd_t *) queue_next(&g_cmdlist, &cmd_p->qn))) { + switch (cmd_p->kind) { + case CMD_ENABLE: + str_p = "enable "; + break; + case CMD_DISABLE: + str_p = "disable"; + break; + case CMD_CONNECT: + str_p = "connect"; + break; + case CMD_CLEAR: + str_p = "clear "; + break; + case CMD_TRACE: + str_p = "trace "; + break; + case CMD_UNTRACE: + str_p = "untrace"; + break; + default: + str_p = "???????"; + break; + } + (void) printf("[%d] %s ", i++, str_p); + + if (cmd_p->kind == CMD_CONNECT) { + (void) printf("&%s ", cmd_p->fcnname_p); + } + if (!cmd_p->isnamed) { + expr_print(stdout, cmd_p->expr.expr_p); + } + + (void) printf("\n"); + } + +} /* end cmd_list */ + + +/* + * cmd_traverse() - calls the suppied traversal function on each command. + */ + +tnfctl_errcode_t +cmd_traverse(cmd_traverse_func_t percmdfunc, void *calldata_p) +{ + cmd_t *cmd_p; + tnfctl_errcode_t err = TNFCTL_ERR_NONE; + + cmd_p = (cmd_t *) & g_cmdlist; + while ((cmd_p = (cmd_t *) queue_next(&g_cmdlist, &cmd_p->qn))) { + expr_t *expr_p; + fcn_t *fcn_p; + + if (!cmd_p->isnamed) { + expr_p = cmd_p->expr.expr_p; + } + + if (cmd_p->kind == CMD_CONNECT) { + fcn_p = fcn_find(cmd_p->fcnname_p); + assert(fcn_p); + } + else + fcn_p = NULL; + + err = (*percmdfunc) (expr_p, + cmd_p->kind, + fcn_p, cmd_p->isnew, calldata_p); + if (err) + return (err); + } + return (err); +} /* end cmd_traverse */ + + +/* + * cmd_traverse() - calls the suppied traversal function on each command. + */ + +tnfctl_errcode_t +cmd_callback(cmd_t *cmd_p, cmd_traverse_func_t percmdfunc, void *calldata_p) +{ + tnfctl_errcode_t err = TNFCTL_ERR_NONE; + expr_t *expr_p; + fcn_t *fcn_p; + + if (!cmd_p->isnamed) { + expr_p = cmd_p->expr.expr_p; + } + + if (cmd_p->kind == CMD_CONNECT) { + fcn_p = fcn_find(cmd_p->fcnname_p); + assert(fcn_p); + } + else + fcn_p = NULL; + + err = (*percmdfunc) (expr_p, cmd_p->kind, fcn_p, cmd_p->isnew, + calldata_p); + + return (err); +} + +#ifdef NOTNEEDED +/* + * cmd_mark() - mark all of the commands in the global list as old + */ + +void +cmd_mark(void) +{ + cmd_t *cmd_p; + + cmd_p = (cmd_t *) & g_cmdlist; + while ((cmd_p = (cmd_t *) queue_next(&g_cmdlist, &cmd_p->qn))) { + cmd_p->isnew = B_FALSE; + } + +} /* end cmd_mark */ + +/* + * cmd_delete() - + */ + +void +cmd_delete(int cmdnum) +{ + cmd_t *cmd_p; + int i = 0; + + cmd_p = (cmd_t *) & g_cmdlist; + while ((cmd_p = (cmd_t *) queue_next(&g_cmdlist, &cmd_p->qn))) { + if (cmdnum == i) { + cmd_destroy(cmd_p); + return; + } + i++; + } + +} /* end cmd_delete */ +#endif diff --git a/usr/src/cmd/tnf/prex/cmd.h b/usr/src/cmd/tnf/prex/cmd.h new file mode 100644 index 0000000000..8e67544b93 --- /dev/null +++ b/usr/src/cmd/tnf/prex/cmd.h @@ -0,0 +1,102 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#ifndef _CMD_H +#define _CMD_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#include <sys/types.h> +#include "queue.h" +#include "expr.h" +#include "set.h" +#include "fcn.h" + +#include <tnf/tnfctl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Typedefs + */ + +typedef enum cmd_kind { + CMD_ENABLE, + CMD_DISABLE, + CMD_CONNECT, + CMD_CLEAR, + CMD_TRACE, + CMD_UNTRACE +} cmd_kind_t; + +typedef struct cmd { + queue_node_t qn; + boolean_t isnamed; + boolean_t isnew; + union { +#ifdef LATEBINDSETS + char *setname_p; +#endif + expr_t *expr_p; + } expr; + char *fcnname_p; + cmd_kind_t kind; +} cmd_t; + +typedef +tnfctl_errcode_t (*cmd_traverse_func_t) ( + expr_t * expr_p, + cmd_kind_t kind, + fcn_t * fcn_p, + boolean_t isnew, + void *calldata_p); + + +/* + * Declarations + */ + +cmd_t *cmd_set(char *setname_p, cmd_kind_t kind, char *fcnname_p); +cmd_t *cmd_expr(expr_t * expr_p, cmd_kind_t kind, char *fcnname_p); +void cmd_list(void); +#if 0 +void cmd_mark(void); +void cmd_delete(int cmdnum); +#endif +tnfctl_errcode_t cmd_traverse(cmd_traverse_func_t percmdfunc, void *calldata_p); +tnfctl_errcode_t cmd_callback(cmd_t *cmd, cmd_traverse_func_t percmdfunc, + void *calldata_p); + +#ifdef __cplusplus +} +#endif + +#endif /* _CMD_H */ diff --git a/usr/src/cmd/tnf/prex/dbg.h b/usr/src/cmd/tnf/prex/dbg.h new file mode 100644 index 0000000000..ae9f6831db --- /dev/null +++ b/usr/src/cmd/tnf/prex/dbg.h @@ -0,0 +1,50 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#ifndef _DBG_H +#define _DBG_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef DEBUG +#define DBG(x) (x) +#else +#define DBG(x) +#endif + +#if defined(DEBUG) || defined(lint) +#include <stdio.h> +extern int __prb_verbose; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _DBG_H */ diff --git a/usr/src/cmd/tnf/prex/expr.c b/usr/src/cmd/tnf/prex/expr.c new file mode 100644 index 0000000000..46c14f621f --- /dev/null +++ b/usr/src/cmd/tnf/prex/expr.c @@ -0,0 +1,262 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +/* we need to define this to get strtok_r from string.h */ +/* SEEMS LIKE A BUG TO ME */ +#define _REENTRANT + +#ifndef DEBUG +#define NDEBUG 1 +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libgen.h> +#include <assert.h> +#include "spec.h" +#include "expr.h" +#include "new.h" + + +/* + * Typedefs + */ + +typedef enum { + MATCH_NONE = 0, + MATCH_FALSE, + MATCH_TRUE + + +} match_t; + + +/* + * Declarations + */ + +static boolean_t matchattrs(expr_t * expr_p, const char *attrs); +static void matchvals(spec_t * spec_p, char *attrstr, + char *valstr, void *calldatap); +static void matched(spec_t * spec_p, char *valstr, void *calldatap); + + +/* ---------------------------------------------------------------- */ +/* ----------------------- Public Functions ----------------------- */ +/* ---------------------------------------------------------------- */ + +/* + * expr() - builds an expr + */ + +expr_t * +expr(spec_t * left_p, + spec_t * right_p) +{ + expr_t *new_p; + + new_p = new(expr_t); + queue_init(&new_p->qn); + new_p->left_p = left_p; + new_p->right_p = right_p; + + return (new_p); + +} /* end expr */ + + +/* + * expr_dup() - duplicates an expression list + */ + +expr_t * +expr_dup(expr_t * list_p) +{ + expr_t *expr_p; + expr_t *head_p; + + if (!list_p) + return (NULL); + + /* copy the first node */ + head_p = expr(spec_dup(list_p->left_p), + spec_dup(list_p->right_p)); + + /* append each additional node */ + expr_p = list_p; + while (expr_p = (expr_t *) queue_next(&list_p->qn, &expr_p->qn)) { + expr_t *new_p; + + new_p = expr(spec_dup(expr_p->left_p), + spec_dup(expr_p->right_p)); + (void) queue_append(&head_p->qn, &new_p->qn); + } + + return (head_p); + +} /* end expr_dup */ + + +/* + * expr_destroy() - destroys an expression list + */ + +void +expr_destroy(expr_t * list_p) +{ + expr_t *expr_p; + + while (expr_p = (expr_t *) queue_next(&list_p->qn, &list_p->qn)) { + (void) queue_remove(&expr_p->qn); + + if (expr_p->left_p) + spec_destroy(expr_p->left_p); + if (expr_p->right_p) + spec_destroy(expr_p->right_p); + free(expr_p); + } + + if (list_p->left_p) + spec_destroy(list_p->left_p); + if (list_p->right_p) + spec_destroy(list_p->right_p); + free(list_p); + +} /* end expr_destroy */ + + +/* + * expr_list() - append a expr_t to a list + */ + +expr_t * +expr_list(expr_t * h, + expr_t * f) +{ + /* queue append handles the NULL cases OK */ + return ((expr_t *) queue_append(&h->qn, &f->qn)); + +} /* end expr_list */ + + +/* + * expr_print() - pretty prints an expr list + */ + +void +expr_print(FILE * stream, + expr_t * list_p) +{ + expr_t *expr_p = NULL; + + while ((expr_p = (expr_t *) queue_next(&list_p->qn, &expr_p->qn))) { + spec_print(stream, expr_p->left_p); + (void) fprintf(stream, "="); + spec_print(stream, expr_p->right_p); + (void) fprintf(stream, " "); + } + +} /* end expr_print */ + + +/* + * expr_match() - figures out whether a probe matches in an expression list + */ + +boolean_t +expr_match(expr_t * list_p, + const char *attrs) +{ + expr_t *expr_p = NULL; + + while ((expr_p = (expr_t *) queue_next(&list_p->qn, &expr_p->qn))) { + if (matchattrs(expr_p, attrs)) + return (B_TRUE); + } + + return (B_FALSE); + +} /* end expr_match */ + + +/* ---------------------------------------------------------------- */ +/* ----------------------- Private Functions ---------------------- */ +/* ---------------------------------------------------------------- */ + +typedef struct matchargs { + spec_t *spec_p; + boolean_t match; + +} matchargs_t; + +static boolean_t +matchattrs(expr_t * expr_p, + const char *attrs) +{ + matchargs_t args; + + args.spec_p = expr_p->right_p; + args.match = B_FALSE; + + spec_attrtrav(expr_p->left_p, + (char *) attrs, matchvals, (void *) &args); + + return (args.match); + +} /* end matchattrs */ + + +/*ARGSUSED*/ +static void +matchvals(spec_t * spec_p, + char *attrstr, + char *valstr, + void *calldatap) +{ + matchargs_t *args_p = (matchargs_t *) calldatap; + + spec_valtrav(args_p->spec_p, valstr, matched, calldatap); + +} /* matchvals */ + + +/*ARGSUSED*/ +static void +matched(spec_t * spec_p, + char *valstr, + void *calldatap) +{ + matchargs_t *args_p = (matchargs_t *) calldatap; + + args_p->match = B_TRUE; + +} /* end matched */ diff --git a/usr/src/cmd/tnf/prex/expr.h b/usr/src/cmd/tnf/prex/expr.h new file mode 100644 index 0000000000..96f84af525 --- /dev/null +++ b/usr/src/cmd/tnf/prex/expr.h @@ -0,0 +1,71 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#ifndef _EXPR_H +#define _EXPR_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#include <stdio.h> + +#include "queue.h" +#include "spec.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Typedefs + */ + +typedef struct expr { + queue_node_t qn; + spec_t *left_p; + spec_t *right_p; + +} expr_t; + + +/* + * Declarations + */ + +expr_t * expr(spec_t * left_p, spec_t * right_p); +void expr_destroy(expr_t * list_p); +expr_t * expr_list(expr_t * list_p, expr_t * item_p); +void expr_print(FILE * stream, expr_t * list_p); +boolean_t expr_match(expr_t * expr_p, const char *attrs); +expr_t * expr_dup(expr_t * list_p); + +#ifdef __cplusplus +} +#endif + +#endif /* _EXPR_H */ diff --git a/usr/src/cmd/tnf/prex/fcn.c b/usr/src/cmd/tnf/prex/fcn.c new file mode 100644 index 0000000000..527bc5fc20 --- /dev/null +++ b/usr/src/cmd/tnf/prex/fcn.c @@ -0,0 +1,213 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#include <stdlib.h> +#include <string.h> +#include <libintl.h> +#include "queue.h" +#include "set.h" +#include "fcn.h" +#include "new.h" +#include "source.h" + + +/* + * Globals + */ + +static queue_node_t g_fcnlist = { + &g_fcnlist, +&g_fcnlist}; + +/* + * Forward Declarations + */ + +static void fcn_destroy(fcn_t * fcn_p); +static void fcn_print(FILE * stream, fcn_t * fcn_p); + + +/* + * fcn() - builds a function block and inserts it on the global list. + */ + +#define NSYMS 1 + +void +fcn(char *name_p, char *entry_name_p) +{ + fcn_t *new_p; + fcn_t *old_p; + + /* does this setname exist already? */ + old_p = fcn_find(name_p); + if (old_p) + fcn_destroy(old_p); + + /* create a new set */ + new_p = new(fcn_t); + queue_init(&new_p->qn); + new_p->name_p = name_p; + new_p->entry_name_p = entry_name_p; + +#ifdef OLD + /* + * allocate a target function block, and stuff the init and fini + * addrs + */ + prbstat = prb_targmem_alloc(g_procfd, sizeof (probe_funcs_t), + &new_p->funcs_p); + if (prbstat) { + semantic_err(gettext("problem allocating target memory")); + goto Error; + } + prbstat = prb_proc_write(g_procfd, new_p->funcs_p, + &new_p->funcs, sizeof (probe_funcs_t)); + if (prbstat) { + semantic_err(gettext( + "setup problem, initial/final " + "funcs in target memory")); + goto Error; + } +#endif + + /* append the new set to the global list */ + (void) queue_append(&g_fcnlist, &new_p->qn); + + return; + +Error: + if (new_p) + free(new_p); + return; + +} /* end fcn */ + + +/* + * fcn_destroy() - destroys a fcn and related resources + */ + +static void +fcn_destroy(fcn_t * fcn_p) +{ + if (!fcn_p) + return; + + /* remove ourselves from any list */ + if (!queue_isempty(&fcn_p->qn)) + (void) queue_remove(&fcn_p->qn); + + if (fcn_p->name_p) + free(fcn_p->name_p); + if (fcn_p->entry_name_p) + free(fcn_p->entry_name_p); + + free(fcn_p); + +} /* end fcn_destroy */ + + +/* + * fcn_list() - pretty prints the global fcnlist + */ + +void +fcn_list(void) +{ + fcn_t *fcn_p; + + fcn_p = (fcn_t *) & g_fcnlist; + while ((fcn_p = (fcn_t *) queue_next(&g_fcnlist, &fcn_p->qn))) { + fcn_print(stdout, fcn_p); + } + +} /* end fcn_list */ + + +/* + * fcn_print() - pretty prints a fcn + */ + +static void +fcn_print(FILE * stream, fcn_t * fcn_p) +{ + if (!fcn_p) + return; + + (void) fprintf(stream, "&%-8s %-24s\n", + fcn_p->name_p, fcn_p->entry_name_p); + +} /* end fcn_print */ + + +/* + * fcn_findname() - find the created name, given an entry name + */ + +char * +fcn_findname(const char * const entry_p) +{ + fcn_t *fcn_p; + + if (!entry_p) + return (NULL); + + fcn_p = (fcn_t *) & g_fcnlist; + while ((fcn_p = (fcn_t *) queue_next(&g_fcnlist, &fcn_p->qn))) + if (strcmp(entry_p, fcn_p->entry_name_p) == 0) + return (fcn_p->name_p); + + return (NULL); + +} /* end fcn_findname */ + + +/* + * fcn_find() - finds a fcn by name + */ + +fcn_t * +fcn_find(char *fcnname_p) +{ + fcn_t *fcn_p; + + if (!fcnname_p) + return (NULL); + + fcn_p = (fcn_t *) & g_fcnlist; + while ((fcn_p = (fcn_t *) queue_next(&g_fcnlist, &fcn_p->qn))) + if (strcmp(fcnname_p, fcn_p->name_p) == 0) + return (fcn_p); + + return (NULL); + +} /* end set_find */ diff --git a/usr/src/cmd/tnf/prex/fcn.h b/usr/src/cmd/tnf/prex/fcn.h new file mode 100644 index 0000000000..dcf4136ec3 --- /dev/null +++ b/usr/src/cmd/tnf/prex/fcn.h @@ -0,0 +1,65 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#ifndef _FCN_H +#define _FCN_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#include "queue.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Typedefs + */ + +typedef struct fcn { + queue_node_t qn; + char *name_p; + char *entry_name_p; +} fcn_t; + + +/* + * Declarations + */ + +void fcn(char *name_p, char *func_entry_p); +void fcn_list(void); +fcn_t *fcn_find(char *name_p); +char *fcn_findname(const char * const entry_p); + +#ifdef __cplusplus +} +#endif + +#endif /* _FCN_H */ diff --git a/usr/src/cmd/tnf/prex/help.c b/usr/src/cmd/tnf/prex/help.c new file mode 100644 index 0000000000..4af6ee3317 --- /dev/null +++ b/usr/src/cmd/tnf/prex/help.c @@ -0,0 +1,626 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <strings.h> +#include "expr.h" +#include "y.tab.h" + +#define NUMHELPTOPICS 6 +static char *helptopics[NUMHELPTOPICS] = { + "intro", "functions", "kernel_mode", "probe_spec", "processes", + "set_spec" }; +static char *helptopicstrings[NUMHELPTOPICS] = { +/* help intro */ +"Introduction to prex\n" +"\n" +"prex is used to control probes in a target process, or in the kernel.\n" +"If you are reading this help text, you have sucessfully invoked prex,\n" +"by either connecting to an existing process (prex -p), a new\n" +"process (prex myprogram), or to the kernel (prex -k).\n" +"\n" +"Most often, the user will want to enable some probes in the target,\n" +"continue the target - either to completion, or until the target is\n" +"interrupted - and then exit from prex to perform analysis on the resulting\n" +"tracefile. An ascii dump of the tracefile can be obtained using the\n" +"tnfdump(1) command.\n" +"\n" +"The tracefile can be found in /tmp/trace-<pid> by default, or in a location\n" +"of your choice, if you specify the -o option when you invoke prex.\n" +"You can query the name of the current trace file by using the command.\n" +"list tracefile.\n" +"\n" +"Upon invocation, prex reads commands from the files ~/.prexrc and\n" +"./.prexrc (in that order). The \"source\" command may be used to take\n" +"commands from an arbitrary file or set of files.\n" +"\n" +"Help is available for a variety of topics, and for each prex command.\n" +"Type help with no arguments for a list of available help topics.\n" +"\n" +"end of help for topic intro\n", +/* help functions */ +"Probe Functions\n" +"\n" +"Note - probe functions are not available from kernel mode\n" +"\n" +"It is possible to use prex to connect functions to probe points, such that\n" +"the function is invoked each time a given probe point is hit. Currently,\n" +"the only function available from prex is the &debug function, which prints\n" +"out the arguments sent in to the probe, as well as the value (if any)\n" +"associated with the sunw%debug attribute in the detail field.\n" +"\n" +"Relevant commands:\n" +" list fcns # list the defined probe functions\n" +" connect &debug name=myprobe # attach probe &debug to probe myprobe\n" +" connect &debug $myset # attach probe &debug to probes in $myset\n" +" clear name=myprobe # disconnect probe functions from myprobe\n" +" clear $myset # disconnect probe functions from $myset\n" +"\n" +"end of help for topic functions\n", +/* help kernel_mode */ +"Controlling Kernel Probes\n" +"\n" +"The Solaris kernel is instrumented with a small number of strategically\n" +"placed probes, documented in tnf_kernel_probes(4). The superuser can\n" +"control these probes by running prex with the \"-k\" option.\n" +"\n" +"In kernel mode, trace output is written to an in-core buffer, rather\n" +"than to a file on disk. This buffer can be extracted with the tnfxtract(1)\n" +"commmand. This buffer must be set up before tracing can begin, through the\n" +"use of the \"buffer alloc\" command. After kernel tracing is complete (and\n" +"after the buffer has been extracted), the buffer can be deallocated from\n" +"prex using the \"buffer dealloc\" command.\n" +"\n" +"As in user mode, kernel probe control is accomplished using the commands\n" +"\"trace\", \"untrace\", \"enable\", and \"disable\". Additionally, in\n" +"kernel mode, a \"master switch\" is provided to turn all tracing activity\n" +"on or off. This switch is toggled using the commands \"ktrace on\" and\n" +"\"ktrace off\". Unlike user mode, where the target is stopped while\n" +"tracing paramaters are manipulated from prex, the kernel does not stop\n" +"running during a tracing session. Using the \"ktrace\" command, one can\n" +"set up all tracing parameters in advance of a session, without actually\n" +"writing trace records until a \"ktrace on\" command is given.\n" +"\n" +"Kernel mode also provides the ability to limit tracing to those kernel\n" +"probes hit on behalf of a specific user process. The pfilter command\n" +"is provided to toggle process filtering on or off, and to specify the\n" +"set of processes that comprise the filter. If pid 0 is a member of the\n" +"filter list, then any threads not associated with a process are included.\n" +"\n" +"Note that after a kernel tracing session, all tracing parameters are left\n" +"as-is. One should re-enter prex to disable and untrace probes, turn off\n" +"process filtering, and deallocate the in-core trace buffer.\n" +"\n" +"Relevant Commands:\n" +" buffer alloc 2m # allocate a 2M in-core buffer\n" +" enable $all # enable all kernel probes\n" +" trace $all # trace all kernel probes\n" +" ktrace on # turn on kernel tracing\n" +" pfilter add 1234 # add pid 1234 to the filter list\n" +" pfilter on # turn on process filtering\n" +" ktrace off # turn off kernel tracing\n" +"Also see tnfxtract(1), which is used to extract the in-core trace buffer\n" +"to an on-disk tracefile.\n" +"\n" +"end of help for topic kernel_mode\n", +/* help probe_spec */ +"Probe Specification\n" +"\n" +"Many prex commands operate on probes or sets of probes. Probes are\n" +"specified by a list of space-separated selectors of the form:\n" +" <attribute>=<value>\n" +"If the \"" +"<attribute>=\" is omitted, the attribute defaults to \"keys=\".\n" +"The \"" +"<value>\" can be either a string or an ed(1) regular expression\n" +"enclosed in slashes. Regular expressions in prex are unanchored, meaning\n" +"that any value that contains the given regex as a substring is a valid\n" +"match, regardless of position. To anchor a regular expression, use \"^\"\n" +"to match the beginning of a line, or \"$\" to match the end of a line.\n" + +"If a list of selectors is specified, an OR operation is applied - the\n" +"resulting probe_spec includes probes that match any of the selectors.\n" +"See the prex(1) man page for a complete specification of the accepted\n" +"grammar.\n" +"\n" +"The \"list\" command is used to view available probes in the target,\n" +"and to display their attributes. The \"trace\" and \"untrace\" commands\n" +"determine whether a probe will write a trace record when hit. The\n" +"\"enable\" and \"disable\" commands indicate whether a probe will perform\n" +"any action (such as a calling a connected function or creating a trace\n" +"record) when hit. Normally, a probe is enabled and traced for tracing,\n" +"and disabled and untraced otherwise. It is possible to enable a probe\n" +"with tracing off to get debug output without writing trace records.\n" +"\n" +"Relevant Commands:\n" +" list probes $all # list probes in set $all (all probes)\n" +" list probes file=test.c # list probes with a specific attribute\n" +" list 'file' probes $all # list the file attribute in all probes\n" +" list probes name=/^thr/ # list probes whose name attribute matches\n" +" # the given regular expression\n" +" list probes name=/^thr/ keys=vm # list probes matching either selector\n" +" enable name=/^thr/ # enable probes whose name matches this regex\n" +" trace $all # trace all probes\n" +" untrace $myset # untrace probes in set $myset\n" +"\n" +"end of help for topic probe_spec\n", +/* help processes */ +"Controlling Processes with prex\n" +"\n" +"Prex is used to control probes in a given process, or in the kernel.\n" +"The process which prex is to control is identified as an argument when\n" +"prex is invoked. If the \"-p" +" <pid>\" switch is used, prex connects to the\n" +"specified process. Otherwise prex exec's the command supplied at the\n" +"end of its argument list. In either case, prex stops the target process\n" +"immediately so that the user may set up probe control.\n" +"\n" +"Once probe control is set up (typically using the \"enable\" and \"trace\"\n" +"commands), the process is continued using the \"continue\" command. Prex\n" +"remains attached to the target, and the user can force the target to\n" +"stop again by typing control-C, at which time additional probe control\n" +"directives may be given.\n" +"\n" +"Upon quitting from prex, the target process is normally resumed if prex\n" +"attached to it, or killed if prex invoked it. An optional argument may\n" +"be given with the \"quit\" command to explicitly specify whether to kill\n" +"the target, continue it, or leave it suspended.\n" +"\n" +"If the target forks, any probe that the child encounters will be logged to\n" +"the same trace file as the parent. If the child calls exec, it will no\n" +"longer be traced.\n" +"\n" +"In kernel mode (prex -k), process filtering may be enabled, to limit\n" +"tracing to those kernel probes hit on behalf of a specific process or\n" +"set of processes. Kernel-mode process filtering is controlled using\n" +"the \"pfilter\" command.\n" +"\n" +"\n" +"Relevant Commands:\n" +" continue # continue target (user mode only)\n" +" Control-C # stop target (user mode only)\n" +" quit resume # quit prex, continue target\n" +" quit suspend # quit prex, suspend target\n" +" quit kill # quit prex, kill target\n" +" quit # quit prex, default action\n" +"# Note: pfilter commands apply only to kernel mode\n" +" pfilter # show pfilter status\n" +" pfilter on # turn on process filter mode\n" +" pfilter off # turn off process filter mode\n" +" pfilter add 1234 # add to process filter pid list\n" +" pfilter delete 1234 # delete from process filter pid list\n" +"\n" +"end of help for topic processes\n", +/* help set_spec */ +"Specifying Probe Sets\n" +"\n" +"Prex provides the ability to define named sets of probes to simplify\n" +"commands operating on multiple probes. The set \"$all\" is predefined,\n" +"as the set of all probes in the target. A set is defined using the\n" +"\"create\" command, and can be used as an argument to the \"list\",\n" +"\"enable\", \"disable\", \"trace\", \"untrace\", \"connect\" and\n" +"\"clear\" commands.\n" +"\n" +"Relevant Commands:\n" +" create $myset name=/^thr/ # create a set\n" +" list probes $myset # list probes in a set\n" +" list sets # list defined sets\n" +" enable $myset # enable a set of probes\n" +" trace $myset # trace a set of probes\n" +"\n" +"end of help for topic set_spec\n" +}; + +static char *helpstr_continue = +"\n" +"Usage: continue\n" +"\n" +"\"continue\" is used to resume execution of the target process. This\n" +"command is not available in kernel mode, since the kernel is never stopped.\n" +"\n" +"end of help for cmd continue\n"; +static char *helpstr_disable = +"\n" +"Usage: disable <probe_spec>|<set_spec>\n" +"\n" +"\"disable\" is used to to turn off all tracing activity associated with a\n" +"probe or a set of probes.\n" +"\n" +"End of help for cmd disable\n"; +static char *helpstr_enable= +"\n" +"Usage: enable <probe_spec>|<set_spec>\n" +"\n" +"\"enable\" is used to specify that any activity associated with the probe\n" +"or probes will be performed when the probe is hit. This includes connected\n" +"probe functions as well as the generation of trace records. Note that in\n" +"order for a probe to generate a trace record, it must be \"traced\" as well\n" +"as enabled.\n" +"\n" +"End of help for cmd enable\n"; +static char *helpstr_help = +"\n" +"Usage: help [<cmd>|<topic>]\n" +"\n" +"\"help\" lists all available help topics when run without any arguments.\n" +"If a valid topic or command-name is supplied as an argument, help text for\n" +"the topic or command is displayed.\n" +"\n" +"End of help for cmd help\n"; +static char *helpstr_list = +"\n" +"Usage: list probes <probe_spec>|<set_spec> \n" +" list <attrs> probes <probe_spec>|<set_spec>\n" +" list sets\n" +" list fcns\n" +" list history\n" +" list tracefile\n" +"\n" +"\"list\" displays information about currently defined probes, sets, and\n" +"probe functions. When listing probes, one can limit the output to a\n" +"desired set of attributes by specifying an attribute list as a set of\n" +"strings. If an attribute is also a reserved word (such as \"trace\", it\n" +"must be enclosed in single quotes. For example:\n" +"\n" +" list file 'trace' probes $all\n" +"\n" +"\"list\" history lists the probe control commands history, and\n" +"\"list\" tracefile displays the current trace file name.\n" +"\n" +"End of help for cmd list\n"; +static char *helpstr_quit = +"\n" +"Usage: quit\n" +" quit kill\n" +" quit resume\n" +" quit suspend\n" +"\n" +"The \"quit\" command exits prex, leaving the target in a state specified\n" +"by the user, or taking a default action if no instructions are specified.\n" +"An optional argument may be used to indicated that the target should be\n" +"killed, resumed, or left suspended. If no argument is supplied, then\n" +"prex's default behavior is to resume a process to which it had attached,\n" +"and to kill a process which it had invoked.\n" +"\n" +"End of help for cmd quit\n"; +static char *helpstr_source = +"\n" +"Usage: source <filename>\n" +"\n" +"The \"source\" command is used to invoke a set of prex commands stored in\n" +"a file. A sourced file may in turn source other files. The files\n" +"~/.prexrc and ./.prexrc are sourced automatically (in that order) when prex\n" +"is invoked, and may be used to store commonly used probe and set\n" +"specifications, or probe control directives. Commands in sourced files\n" +"may override the effects of commands in previously sourced files.\n" +"\n" +"End of help for cmd source\n"; +static char *helpstr_trace = +"\n" +"Usage: trace <probe_spec>|<set_spec>\n" +"\n" +"\"trace\" is used to turn on tracing for the specified probe or probes.\n" +"A \"traced\" probe that is also \"enabled\" will generate a trace record\n" +"when it is hit.\n" +"\n" +"End of help for cmd trace\n"; +static char *helpstr_untrace = +"\n" +"Usage: untrace <probe_spec>|<set_spec>\n" +"\n" +"\"untrace\" turns tracing off for the specified probe or probes. A probe\n" +"will not generate a trace record when it is not traced, although connected\n" +"probe functions will still be invoked as long as a probe is \"enabled\".\n" +"\n" +"End of help for cmd untrace\n"; +static char *helpstr_buffer = +"\n" +"Usage: buffer alloc <size>\n" +" buffer dealloc\n" +"\n" +"Note: Kernel Mode Only\n" +"\n" +"\"buffer\" allocates or deallocates the in-core buffer used to hold\n" +"kernel trace records. Size can be specified in kilobytes or megabytes,\n" +"by appending the character 'k' or 'm' to a numeric value (e.g. \"2m\").\n" +"A buffer must be allocated prior to a kernel tracing session. Once\n" +"allocated, the buffer remains usable until deallocated, even through\n" +"multiple invocations of prex.\n" +"\n" +"Before the buffer is deallocated, data may be extracted to an on-disk\n" +"tracefile using tnfxtract(1).\n" +"\n" +"End of help for cmd buffer\n"; +static char *helpstr_ktrace = +"\n" +"Usage: ktrace on\n" +" ktrace off\n" +"\n" +"Note: Kernel Mode Only\n" +"\n" +"\"ktrace\" toggles the master switch that indicates whether kernel\n" +"tracing is active or inactive. Since the kernel cannot be stopped while\n" +"a tracing experiment is set up, \"ktrace\" is provided so that tracing\n" +"can be set up as desired before any trace records are generated\n" +"\n" +"End of help for cmd ktrace\n"; +static char *helpstr_pfilter = +"\n" +"Usage: pfilter\n" +" pfilter on\n" +" pfilter off\n" +" pfilter add <pidlist>\n" +" pfilter delete <pidlist>\n" +"\n" +"Note: Kernel Mode Only\n" +"\n" +"\"pfilter\" controls process filtering by toggling process-filter mode,\n" +"and maintaining a list of process id's on which to filter. When process\n" +"filtering mode is on, tracing is limited to the kernel events hit on behalf\n" +"of the processes in the pid list. Process id 0 is used to represent all\n" +"threads not associated with a process.\n" + +"When run without arguments, \"pfilter\" displays the current process-filter\n" +"mode and pid list. A process filter pid list can be maintained whether\n" +"or not process filter mode is currently active.\n" +"\n" +"A process must exist in order to be added to the pid list, and the pid list\n" +"is automatically updated to delete any processes that no longer exist. If\n" +"the pid list becomes empty while process filtering is on, prex issues a\n" +"warning. Process filtering mode is persistent between invocations of prex\n" +"so it should be turned off manually when a tracing experiment is complete.\n" +"\n" +"End of help for cmd pfilter\n"; +static char *helpstr_clear = +"\n" +"Usage: clear <probe_spec>|<set_spec>\n" +"\n" +"Note: Not available in Kernel Mode\n" +"\n" +"\"clear\" disconnects any probe functions that have previously been\n" +"connected to a probe or group of probes using the \"connect\" command.\n" +"The \"clear\" command cannot be used in kernel mode, since probe functions\n" +"are not available for kernel probes.\n" +"\n" +"End of help for cmd clear\n"; +static char *helpstr_connect = +"\n" +"Usage: connect <function> <probe_spec>|<set_spec>\n" +"\n" +"Note: Not available in Kernel Mode\n" +"\n" +"\"connect\" connects a probe function to a probe or group of probes.\n" +"Currently, the only probe function available from prex is \"&debug\", which\n" +"prints out the arguments sent in to the probe, as well as the value (if\n" +"any) associated with the sunw%debug attribute in the detail field.\n" +"In order for a probe function to be invoked, the probe to which the\n" +"function is attached must be \"enabled\", but need not be \"traced\".\n" +"\n" +"The \"clear\" command is available to disconnect a probe function from\n" +"a probe or group of probes.\n" +"\n" +"End of help for cmd connect\n"; + +static char *helpstr = +"\n" +"Usage: help [topic|command]\n" +"\n" +"Topics\n" +"\tintro functions kernel_mode\n" +"\tprobe_spec processes set_spec\n" +"\n" +"User and Kernel Mode Commands\n" +"\tdisable enable help list\n" +"\tquit source trace untrace\n" +"\n" +"Additional user-mode-only commands\n" +"\tclear connect continue\n" +"\n" +"Additional kernel-mode-only (prex -k) commands\n" +"\tbuffer ktrace pfilter\n" +; + + +static char *oldhelpstr = +"grammar for non-terminals:\n" +"__________________________\n" +"\n" +"filename ::= QUOTED_STR\n" +"\n" +"selector_list ::= /* empty */ |\n" +" <selector_list> <selector>\n" +"\n" +"spec_list ::= /*empty */ |\n" +" <spec_list> <spec>\n" +"\n" +"selector ::= <spec>=<spec> | /* whitespace around '=' optional */\n" +" <spec> /* keys attribute is default */\n" +"\n" +"spec ::= IDENT |\n" +" QUOTED_STR |\n" +" REGEXP\n" +"\n" +"pidlist ::= <pid> |\n" +" <pid> ',' <pidlist>\n" +"pid ::= INT\n" +"\n" +"Reg-exps to match terminals:\n" +"____________________________\n" +"\n" +"IDENT = [a-zA-Z_\\.%]{[a-zA-Z0-9_\\.%]}+ \n" +"QUOTED_STR = '[^\\n']*' /* any string in single quotes */\n" +"REGEXP = /[^\\n/]/ /* reg-exp's have to be in / / */\n" +"INT = [0-9]+\n" +"\n" +"Commands:\n" +"_________\n" +"\n" +"# set creation and set listing\n" +"create $<set_name> <selector_list>\n" +"list sets # list the defined sets\n" +"\n" +"# function listing\n" +"list fcns # list the defined functions.\n" +"\n" +"# commands to connect and disconnect probe functions\n" +"# (not available in kernel mode)\n" +"connect &<fcn_handle> $<set_name> # eg. connect &debug $all\n" +"connect &<fcn_handle> <selector_list>\n" +"\n" +"# command to disconnect all connected probe functions\n" +"# (not available in kernel mode)\n" +"clear $<set_name>\n" +"clear <selector_list>\n" +"\n" +"# commands to toggle the tracing mode\n" +"trace $<set_name>\n" +"trace <selector_list>\n" +"untrace $<set_name>\n" +"untrace <selector_list>\n" +"\n" +"# commands to enable and disable probes\n" +"enable $<set_name>\n" +"enable <selector_list>\n" +"disable $<set_name>\n" +"disable <selector_list>\n" +"list history # lists probe control commands issued\n" +"list tracefile # lists the current trace file name\n" +"\n" +"# commands to list probes or to list values\n" +"list <spec_list> probes $<set_name> #eg. list probes $all\n" +"list <spec_list> probes <selector_list> #eg. list name probes file=test.c\n" +"list values <spec_list> # eg. list values keys\n" +"\n" +"# help command\n" +"help\n" +"\n" +"# source a file of prex commands\n" +"source <filename>\n" +"\n" +"# process control - ^C stops target and returns to 'prex>' prompt\n" +"# (In kernel mode, `continue' is a no-op, and 'quit' detaches prex\n" +"# from the kernel.)\n" +"continue # continues target\n" +"quit kill # quit prex, kill target\n" +"quit resume # quit prex, continue target\n" +"quit suspend # quit prex, leave target suspended\n" +"quit # quit prex (continue or kill target)\n" +"\n" + +"\n" +"# Kernel mode commands\n" +"# \"master switch\" enabling/disabling all tracing\n" +"ktrace on # Enabled probes will generate trace output\n" +"ktrace off # All trace output suppressed\n" +"# Create, destroy, or show the size of the kernel trace buffer\n" +"buffer [ alloc [ size ] | dealloc ]\n" +"# Control per-process kernel trace filtering\n" +"pfilter off # Filtering off: trace all processes\n" +"pfilter on # Filtering on: trace only processes in filter set\n" +"pfilter add <pidlist> # Add specified process ids to the filter set\n" +"pfilter delete <pidlist> # Drop specified pids from the filter set\n" +"\n" +; + +void +help(void) +{ + (void) fputs(helpstr, stdout); + +} /* end help */ + +void +help_on_topic(char *topic) +{ + int i; + + if (topic && strlen(topic)) { + for (i = 0; i < NUMHELPTOPICS; i++) + if (strcmp(topic, helptopics[i]) == 0) + break; + if (i < NUMHELPTOPICS) + fputs(helptopicstrings[i], stdout); + else { + printf("No help for %s\n", topic); + help(); + } + } +} + + +void +help_on_command(int cmd) +{ + switch (cmd) { + case CONTINUE: + fputs(helpstr_continue, stdout); + break; + case DISABLE: + fputs(helpstr_disable, stdout); + break; + case ENABLE: + fputs(helpstr_enable, stdout); + break; + case HELP: + fputs(helpstr_help, stdout); + break; + case LIST: + fputs(helpstr_list, stdout); + break; + case QUIT: + fputs(helpstr_quit, stdout); + break; + case SOURCE: + fputs(helpstr_source, stdout); + break; + case TRACE: + fputs(helpstr_trace, stdout); + break; + case UNTRACE: + fputs(helpstr_untrace, stdout); + break; + case BUFFER: + fputs(helpstr_buffer, stdout); + break; + case KTRACE: + fputs(helpstr_ktrace, stdout); + break; + case PFILTER: + fputs(helpstr_pfilter, stdout); + break; + case CLEAR: + fputs(helpstr_clear, stdout); + break; + case CONNECT: + fputs(helpstr_connect, stdout); + break; + default: + fputs("No help for this command\n", stdout); + break; + } + +} /* end help */ diff --git a/usr/src/cmd/tnf/prex/i386/Makefile b/usr/src/cmd/tnf/prex/i386/Makefile new file mode 100644 index 0000000000..b57ca8205f --- /dev/null +++ b/usr/src/cmd/tnf/prex/i386/Makefile @@ -0,0 +1,34 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright (c) 1997, by Sun Microsystems, Inc. +# All rights reserved. +# +# cmd/tnf/prex/sparc/Makefile + +include ../Makefile.com +CFLAGS += -I.. -I. +LINTFLAGS += -I.. -I. + +install: all $(ROOTPROG32) diff --git a/usr/src/cmd/tnf/prex/list.c b/usr/src/cmd/tnf/prex/list.c new file mode 100644 index 0000000000..cfecaeef9b --- /dev/null +++ b/usr/src/cmd/tnf/prex/list.c @@ -0,0 +1,571 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#ifndef DEBUG +#define NDEBUG 1 +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <libintl.h> +#include <search.h> + +#include "source.h" +#include "queue.h" +#include "list.h" +#include "spec.h" +#include "new.h" +#include "fcn.h" + +extern caddr_t g_commitfunc; + + +/* + * Typedefs + */ + +typedef struct list_probe_args { + spec_t *speclist_p; + expr_t *exprlist_p; +} list_probe_args_t; + +typedef struct list_attrs_args { + spec_t *speclist_p; + void *attrroot_p; +} list_attrs_args_t; + +typedef struct attr_node { + char *name; + void *valsroot_p; +} attr_node_t; + +typedef struct vals_node { + char *name; +} vals_node_t; + + +/* + * Globals + */ + + +/* + * Declarations + */ + +static tnfctl_errcode_t listprobe(tnfctl_handle_t *hndl, + tnfctl_probe_t *ref_p, void *calldata_p); +static tnfctl_errcode_t probescan(tnfctl_handle_t *hndl, + tnfctl_probe_t *ref_p, void *calldata_p); +static void printattrval(spec_t * spec_p, char *attr, char *value, + void *pdata); +static void attrscan(spec_t * spec_p, char *attr, char *values, void *pdata); +static int attrcompare(const void *node1, const void *node2); +static int valscompare(const void *node1, const void *node2); +static void printattrs(const void *node, VISIT order, int level); +static void printvals(const void *node, VISIT order, int level); + +#if 0 +static void attrnodedel(attr_node_t * an_p); +#endif + +static void valadd(spec_t * spec_p, char *val, void *calldata_p); + + +/* ---------------------------------------------------------------- */ +/* ----------------------- Public Functions ----------------------- */ +/* ---------------------------------------------------------------- */ + +extern tnfctl_handle_t *g_hndl; + +/* + * list_set() - lists all of the current probes in a target process + */ + +void +list_set(spec_t * speclist_p, char *setname_p) +{ + set_t *set_p; + list_probe_args_t args; + tnfctl_errcode_t err; + + set_p = set_find(setname_p); + if (!set_p) { + semantic_err(gettext("missing or invalid set")); + return; + } + args.speclist_p = speclist_p; + args.exprlist_p = set_p->exprlist_p; + err = tnfctl_probe_apply(g_hndl, listprobe, &args); + if (err) { + semantic_err(gettext("listing error : %s"), + tnfctl_strerror(err)); + } +} + + +/* + * list_expr() - lists all of the current probes in an expression list + */ + +void +list_expr(spec_t * speclist_p, expr_t * expr_p) +{ + list_probe_args_t args; + tnfctl_errcode_t err; + + args.speclist_p = speclist_p; + args.exprlist_p = expr_p; + err = tnfctl_probe_apply(g_hndl, listprobe, &args); + if (err) { + semantic_err(gettext("listing error : %s"), + tnfctl_strerror(err)); + } +} + + +/* + * list_values() - list all the values for a supplied spec + */ + +void +list_values(spec_t * speclist_p) +{ + list_attrs_args_t args; + tnfctl_errcode_t err; + + /* setup argument block */ + args.speclist_p = speclist_p; + args.attrroot_p = NULL; + + /* traverse the probes, recording attributes that match */ + err = tnfctl_probe_apply(g_hndl, probescan, &args); + if (err) { + semantic_err(gettext("probe traversal error : %s"), + tnfctl_strerror(err)); + } + + /* pretty print the results */ + twalk(args.attrroot_p, printattrs); + + /* destroy the attribute tree */ + while (args.attrroot_p) { + attr_node_t **aptr; + char *anameptr; + + aptr = (attr_node_t **) args.attrroot_p; + + /* destroy the value tree */ + while ((*aptr)->valsroot_p) { + vals_node_t **vptr; + char *vnameptr; + + vptr = (vals_node_t **) (*aptr)->valsroot_p; + vnameptr = (*vptr)->name; +#ifdef LEAKCHK + (void) fprintf(stderr, "freeing value \"%s\"\n", + vnameptr); +#endif + (void) tdelete((void *) *vptr, &(*aptr)->valsroot_p, + valscompare); + if (vnameptr) free(vnameptr); + } + + anameptr = (*aptr)->name; +#ifdef LEAKCHK + (void) fprintf(stderr, "freeing attr \"%s\"\n", anameptr); +#endif + (void) tdelete((void *) *aptr, &args.attrroot_p, attrcompare); + if (anameptr) free(anameptr); + } + +} /* end list_values */ + + +/* + * list_getattrs() - build an attribute string for this probe. + */ + + +#define BUF_LIMIT 2048 + +char * +list_getattrs(tnfctl_probe_t *probe_p) +{ + tnfctl_errcode_t err; + tnfctl_probe_state_t p_state; + char *attrs; + char buffer[BUF_LIMIT]; + char *buf_p; + char *buf_end; + int str_len; + size_t len; + + err = tnfctl_probe_state_get(g_hndl, probe_p, &p_state); + if (err) { + attrs = malloc(2); + if (attrs) + attrs[0] = '\0'; + return (attrs); + } + + buf_p = buffer; + buf_end = buf_p + BUF_LIMIT; + str_len = sprintf(buf_p, "enable %s; trace %s; ", + (p_state.enabled) ? "on" : "off", + (p_state.traced) ? "on" : "off"); + buf_p += str_len; + if (p_state.obj_name) { + str_len = strlen(p_state.obj_name); + if (buf_p + str_len < buf_end) { + str_len = sprintf(buf_p, "object %s; ", + p_state.obj_name); + buf_p += str_len; + } + } + str_len = sprintf(buf_p, "funcs"); + buf_p += str_len; + + /* REMIND: add limit for string size */ + if (p_state.func_names) { + int i = 0; + char *fcnname; + + while (p_state.func_names[i]) { + (void) strcat(buffer, " "); + + fcnname = fcn_findname(p_state.func_names[i]); + if (fcnname) { + (void) strcat(buffer, "&"); + (void) strcat(buffer, fcnname); + } else + (void) strcat(buffer, p_state.func_names[i]); + i++; + } + } + + (void) strcat(buffer, ";"); + + len = strlen(buffer) + strlen(p_state.attr_string) + 1; + attrs = (char *) malloc(len); + + if (attrs) { + (void) strcpy(attrs, buffer); + (void) strcat(attrs, p_state.attr_string); + } + + return (attrs); +} + + +/* ---------------------------------------------------------------- */ +/* ----------------------- Private Functions ---------------------- */ +/* ---------------------------------------------------------------- */ + +/* + * probescan() - function used as a callback, gathers probe attributes and + * values + */ +/*ARGSUSED*/ +static tnfctl_errcode_t +probescan(tnfctl_handle_t *hndl, tnfctl_probe_t *ref_p, void *calldata_p) +{ + list_attrs_args_t *args_p = (list_attrs_args_t *) calldata_p; + spec_t *speclist_p; + spec_t *spec_p; + char *attrs; + + speclist_p = args_p->speclist_p; + spec_p = NULL; + + attrs = list_getattrs(ref_p); + + while (spec_p = (spec_t *) queue_next(&speclist_p->qn, &spec_p->qn)) { + spec_attrtrav(spec_p, attrs, attrscan, calldata_p); + } + + if (attrs) + free(attrs); + + return (TNFCTL_ERR_NONE); +} + + +/* + * attrscan() - called on each matching attr/values component + */ + +/*ARGSUSED*/ +static void +attrscan(spec_t * spec_p, + char *attr, + char *values, + void *pdata) +{ + list_attrs_args_t *args_p = (list_attrs_args_t *) pdata; + attr_node_t *an_p; + attr_node_t **ret_pp; + static spec_t *allspec = NULL; + + if (!allspec) + allspec = spec(".*", SPEC_REGEXP); + + an_p = new(attr_node_t); + +#ifdef LEAKCHK + (void) fprintf(stderr, "creating attr \"%s\"\n", attr); +#endif + an_p->name = strdup(attr); + an_p->valsroot_p = NULL; + + ret_pp = tfind((void *) an_p, &args_p->attrroot_p, attrcompare); + + if (ret_pp) { + /* + * we already had a node for this attribute; delete ours * + * and point at the original instead. + */ +#ifdef LEAKCHK + (void) fprintf(stderr, "attr already there \"%s\"\n", attr); +#endif + if (an_p->name) + free(an_p->name); + free(an_p); + + an_p = *ret_pp; + } else { + (void) tsearch((void *) an_p, &args_p->attrroot_p, attrcompare); + } + + spec_valtrav(allspec, values, valadd, (void *) an_p); + +} /* end attrscan */ + + +/* + * valadd() - add vals to an attributes tree + */ + +/*ARGSUSED*/ +static void +valadd(spec_t * spec_p, + char *val, + void *calldata_p) +{ + attr_node_t *an_p = (attr_node_t *) calldata_p; + + vals_node_t *vn_p; + vals_node_t **ret_pp; + + vn_p = new(vals_node_t); +#ifdef LEAKCHK + (void) fprintf(stderr, "creating value \"%s\"\n", val); +#endif + vn_p->name = strdup(val); + + ret_pp = tfind((void *) vn_p, &an_p->valsroot_p, valscompare); + + if (ret_pp) { + /* we already had a node for this value */ +#ifdef LEAKCHK + (void) fprintf(stderr, "value already there \"%s\"\n", val); +#endif + if (vn_p->name) + free(vn_p->name); + free(vn_p); + } else { + (void) tsearch((void *) vn_p, &an_p->valsroot_p, valscompare); + } + + +} /* end valadd */ + + +/* + * attrcompare() - compares attribute nodes, alphabetically + */ + +static int +attrcompare(const void *node1, + const void *node2) +{ + return strcmp(((attr_node_t *) node1)->name, + ((attr_node_t *) node2)->name); + +} /* end attrcompare */ + + +/* + * valscompare() - compares attribute nodes, alphabetically + */ + +static int +valscompare(const void *node1, + const void *node2) +{ + return strcmp(((vals_node_t *) node1)->name, + ((vals_node_t *) node2)->name); + +} /* end valscompare */ + + +/* + * printattrs() - prints attributes from the attr tree + */ + +/*ARGSUSED*/ +static void +printattrs(const void *node, + VISIT order, + int level) +{ + attr_node_t *an_p = (*(attr_node_t **) node); + + if (order == postorder || order == leaf) { + (void) printf("%s =\n", an_p->name); + twalk(an_p->valsroot_p, printvals); + } +} /* end printattrs */ + + +/* + * printvals() - prints values from a value tree + */ + +/*ARGSUSED*/ +static void +printvals(const void *node, + VISIT order, + int level) +{ + vals_node_t *vn_p = (*(vals_node_t **) node); + + if (order == postorder || order == leaf) + (void) printf(" %s\n", vn_p->name); + +} /* end printvals */ + + +#if 0 +/* + * attrnodedel() - deletes an attr_node_t after the action + */ + +static void +attrnodedel(attr_node_t * an_p) +{ + if (an_p->name) + free(an_p->name); + + /* destroy the value tree */ + while (an_p->valsroot_p) { + vals_node_t **ptr; + + ptr = (vals_node_t **) an_p->valsroot_p; + (void) tdelete((void *) *ptr, &an_p->valsroot_p, valscompare); + } + + /* We don't need to free this object, since tdelete() appears to */ + /* free(an_p); */ + +} /* end attrnodedel */ +#endif + + +/* + * listprobe() - function used as a callback, pretty prints a probe + */ +/*ARGSUSED*/ +static tnfctl_errcode_t +listprobe(tnfctl_handle_t *hndl, tnfctl_probe_t *ref_p, void *calldata_p) +{ + static spec_t *default_speclist = NULL; + list_probe_args_t *args_p = (list_probe_args_t *) calldata_p; + spec_t *speclist_p; + spec_t *spec_p; + boolean_t sawattr; + char *attrs; + + /* build a default speclist if there is not one built already */ + if (!default_speclist) { + default_speclist = spec_list( + spec_list( + spec_list( + spec_list( + spec_list( + spec("name", + SPEC_EXACT), + spec("enable", + SPEC_EXACT)), + spec("trace", SPEC_EXACT)), + spec("file", SPEC_EXACT)), + spec("line", SPEC_EXACT)), + spec("funcs", SPEC_EXACT)); + } + attrs = list_getattrs(ref_p); + + if (expr_match(args_p->exprlist_p, attrs)) { + speclist_p = args_p->speclist_p; + speclist_p = (speclist_p) ? speclist_p : default_speclist; + + spec_p = NULL; + while (spec_p = (spec_t *) + queue_next(&speclist_p->qn, &spec_p->qn)) { + sawattr = B_FALSE; + spec_attrtrav(spec_p, attrs, printattrval, &sawattr); + if (!sawattr) + (void) printf("<no attr> "); + } + (void) printf("\n"); + } + if (attrs) + free(attrs); + + return (TNFCTL_ERR_NONE); +} + + +/*ARGSUSED*/ +static void +printattrval(spec_t * spec_p, + char *attr, + char *value, + void *pdata) +{ + boolean_t *bptr = (boolean_t *) pdata; + + *bptr = B_TRUE; + + (void) printf("%s=%s ", attr, (value && *value) ? value : "<no value>"); + +} /* end printattrval */ diff --git a/usr/src/cmd/tnf/prex/list.h b/usr/src/cmd/tnf/prex/list.h new file mode 100644 index 0000000000..ba131db951 --- /dev/null +++ b/usr/src/cmd/tnf/prex/list.h @@ -0,0 +1,58 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#ifndef _LIST_H +#define _LIST_H + +#pragma ident "%Z%%M% %I% %E% SMI" + + +/* + * Includes + */ + +#include "expr.h" +#include "set.h" + +#include <tnf/tnfctl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Declarations + */ +void list_expr(spec_t *speclist_p, expr_t *expr_p); +void list_set(spec_t *speclist_p, char *setname_p); +void list_values(spec_t *speclist_p); + +char *list_getattrs(tnfctl_probe_t *ref_p); + +#ifdef __cplusplus +} +#endif + +#endif /* _LIST_H */ diff --git a/usr/src/cmd/tnf/prex/main.c b/usr/src/cmd/tnf/prex/main.c new file mode 100644 index 0000000000..fa7be93ab2 --- /dev/null +++ b/usr/src/cmd/tnf/prex/main.c @@ -0,0 +1,974 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <errno.h> +#include <locale.h> +#include <libintl.h> +#include <fcntl.h> +#include <limits.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/procfs.h> +#include <libelf.h> +#include <gelf.h> +#include <sys/systeminfo.h> + +#include <tnf/tnfctl.h> + +#include "set.h" +#include "cmd.h" +#include "spec.h" +#include "expr.h" +#include "source.h" +#include "list.h" +#include "prbk.h" + +/* + * Defines - Project private interfaces + */ + +#define DEBUG_ENTRY "tnf_probe_debug" +#ifdef TESTING +#define EMPTY_ENTRY "tnf_probe_empty" +#endif + +#define USER_OUTSIZE (4*1024*1024) +#define KERNEL_OUTSIZE (384*1024) + +#if defined(__sparc) +#define PREX32DIR "/sparcv7/" +#elif defined(__i386) || defined(__amd64) +#define PREX32DIR "/i86/" +#endif +#define PREX32EXEC "/usr/bin" PREX32DIR "prex" + +/* + * Globals + */ + +char **g_argv; /* copy of argv pointer */ +tnfctl_handle_t *g_hndl; /* handle on target or kernel */ + +static int g_verbose; /* debugging to stderr */ +static char *g_cmdname; /* target command name */ +static char **g_cmdargs; /* target command args */ +static pid_t g_targetpid; /* target process id */ +static volatile boolean_t g_getcmds; /* accept input flag */ +static boolean_t g_testflag; /* asserted in test mode */ +static char *g_preload; /* objects to preload */ +static char *g_outname; /* tracefile name */ +static char *tracefile; /* tracefile name used by list cmd */ +int g_outsize; /* tracefile size */ +boolean_t g_kernelmode; /* -k flag: kernel mode */ +static int prex_dmodel; /* prex data model */ +/* + * Local Declarations + */ + +static void usage(char **argv, const char *msg); +static void scanargs(int argc, char **argv); +static int set_signal(void); +static int get_data_model(pid_t pid); +static int get_elf_class(char *filename); +static int get_executable(char *); +static void prex_isaexec(char **argv, char **envp); +static void check_pid_model(char **argv, char **envp); +static void check_exec_model(char **argv, char **envp); + +/* #### - FIXME - need to put this in a private header file */ +extern void err_fatal(char *s, ...); + +extern int yyparse(void); + +static tnfctl_errcode_t check_trace_error(tnfctl_handle_t *hndl); +static void set_default_cmd(void); +static void get_commands(void); +static tnfctl_errcode_t set_tracefile(tnfctl_handle_t *hndl); +static tnfctl_errcode_t set_probe_discovery_callback(tnfctl_handle_t *hndl); +static void * perprobe(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_p); +static tnfctl_errcode_t perprobe2(tnfctl_handle_t *hndl, + tnfctl_probe_t *probe_p, void *ignored); +static tnfctl_errcode_t percmd(expr_t *expr_p, cmd_kind_t kind, fcn_t *fcn_p, + boolean_t isnew, void *calldata_p); +void quit(boolean_t killtarget, boolean_t runtarget); +void cmd_listtracefile(); + + +/* + * usage() - gives a description of the arguments, and exits + */ + +static void +usage(char *argv[], const char *msg) +{ + if (msg) + (void) fprintf(stderr, + gettext("%s: %s\n"), argv[0], msg); + + (void) fprintf(stderr, gettext( + "usage: %s [options] <cmd> [cmd-args...]\n"), argv[0]); + (void) fprintf(stderr, gettext( + "usage: %s [options] -p <pid>\n"), argv[0]); + (void) fprintf(stderr, gettext( + "usage: %s -s <kbytes-size> -k\n"), argv[0]); + (void) fprintf(stderr, gettext( + "options:\n")); + (void) fprintf(stderr, gettext( + " -o <outfilename> set trace output file name\n")); + (void) fprintf(stderr, gettext( + " -s <kbytes-size> set trace file size\n")); + (void) fprintf(stderr, gettext( + " -l <sharedobjs> shared objects to " + "be preloaded (cmd only)\n")); + + exit(1); +} + + +/* + * main() - + */ + +int +main(int argc, char **argv, char **envp) +{ + tnfctl_errcode_t err = TNFCTL_ERR_NONE; + int sys_err; + tnfctl_trace_attrs_t trace_attrs; + tnfctl_event_t event = TNFCTL_EVENT_EINTR; + pid_t prex_pid; + + /* internationalization stuff */ + (void) setlocale(LC_ALL, ""); +#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ +#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ +#endif + (void) textdomain(TEXT_DOMAIN); + + g_argv = argv; + + prex_pid = getpid(); +#if defined(DEBUG) + fprintf(stderr, "### prex_pid = %d ###\n", prex_pid); +#endif + prex_dmodel = get_data_model(prex_pid); +#if defined(DEBUG) + fprintf(stderr, "### prex_dmodel = %d ###\n", prex_dmodel); +#endif + scanargs(argc, argv); + + if (g_kernelmode) { + /* prexing the kernel */ + err = tnfctl_kernel_open(&g_hndl); + if (err) { + err_fatal(gettext( + "%s: trouble attaching to the kernel: %s\n"), + argv[0], tnfctl_strerror(err)); + } + } else { + /* prexing a user process */ + if (g_targetpid != 0) { + /* check data model */ + check_pid_model(argv, envp); + /* attach case */ + err = tnfctl_pid_open(g_targetpid, &g_hndl); + if (err == TNFCTL_ERR_NOLIBTNFPROBE) { + err_fatal(gettext( + "%s: missing symbols, is " + "libtnfprobe.so loaded in target?\n"), + argv[0], tnfctl_strerror(err)); + } else if (err) { + err_fatal(gettext( + "%s: trouble attaching to target " + "process: %s\n"), + argv[0], tnfctl_strerror(err)); + } + } else { + /* check elf class model */ + check_exec_model(argv, envp); + /* exec case */ + err = tnfctl_exec_open(g_cmdname, g_cmdargs, NULL, + g_preload, NULL, &g_hndl); + if (err == TNFCTL_ERR_NONE) + err = tnfctl_trace_attrs_get(g_hndl, + &trace_attrs); + if (err) { + err_fatal(gettext( + "%s: trouble creating target process: " + "%s\n"), + argv[0], tnfctl_strerror(err)); + } + g_targetpid = trace_attrs.targ_pid; + } + + sys_err = set_signal(); + if (sys_err) + err_fatal(gettext( + "%s: trouble setting up signal handler: %s\n"), + argv[0], strerror(err)); + } + + /* initialize the source stack for the parser */ + source_init(); + + if (!g_kernelmode) { + /* set the tracefile name and size */ + err = set_tracefile(g_hndl); + if (err) { + (void) fprintf(stderr, gettext( + "%s: trouble initializing tracefile: %s\n"), + argv[0], tnfctl_strerror(err)); + goto Cleanup; + } + err = check_trace_error(g_hndl); + if (err) { + (void) fprintf(stderr, gettext( + "%s: cannot read tracing status : %s\n"), + argv[0], tnfctl_strerror(err)); + goto Cleanup; + } + } + + /* accept commands from stdin the first time through */ + g_getcmds = B_TRUE; + + /* set up default aliases */ + set_default_cmd(); + + /* set up creator/destructor function to call for new probes */ + err = set_probe_discovery_callback(g_hndl); + if (err) { + (void) fprintf(stderr, gettext( + "%s: error in probe discovery : %s\n"), + argv[0], tnfctl_strerror(err)); + goto Cleanup; + } + + if (g_kernelmode) { + prbk_warn_pfilter_empty(); + } + + while (err == TNFCTL_ERR_NONE) { + + if (g_kernelmode || g_getcmds) { + g_getcmds = B_FALSE; + get_commands(); + } + + if (!g_kernelmode && (g_getcmds == B_FALSE)) { + err = tnfctl_continue(g_hndl, &event, NULL); + if (err) { + (void) fprintf(stderr, gettext( + "%s: cannot continue target : %s\n"), + argv[0], tnfctl_strerror(err)); + goto Cleanup; + } + } + err = check_trace_error(g_hndl); + if (err) { + (void) fprintf(stderr, gettext( + "%s: cannot read tracing status : %s\n"), + argv[0], tnfctl_strerror(err)); + goto Cleanup; + } + if (!g_kernelmode) { + if (event == TNFCTL_EVENT_EXEC) { + (void) printf(gettext( + "Target process exec'd\n")); + quit(B_FALSE, B_TRUE); /* quit resume */ + } else if (event == TNFCTL_EVENT_EXIT) { + /* target exited */ + (void) fprintf(stderr, gettext( + "%s: target process exited\n"), + g_argv[0]); + goto Cleanup; + } else if (event == TNFCTL_EVENT_TARGGONE) { + /* target terminated */ + (void) fprintf(stderr, + gettext("%s: target process disappeared (without calling exit)\n"), + g_argv[0]); + goto Cleanup; + } + } + } + +Cleanup: + err = tnfctl_close(g_hndl, TNFCTL_TARG_DEFAULT); + if (err) + (void) fprintf(stderr, gettext( + "%s: error on closing : %s\n"), + argv[0], tnfctl_strerror(err)); + + exit(0); + + return (0); + +} + +/* + * check_trace_error() - checks whether there was an error in tracing + */ +static tnfctl_errcode_t +check_trace_error(tnfctl_handle_t *hndl) +{ + tnfctl_trace_attrs_t trace_attrs; + tnfctl_errcode_t err; + + err = tnfctl_trace_attrs_get(hndl, &trace_attrs); + if (err) + return (err); + + if (trace_attrs.trace_buf_state == TNFCTL_BUF_BROKEN) { + (void) printf(gettext("Tracing shut down in target program " + "due to an internal error - Please restart prex " + "and target\n")); + } + + return (TNFCTL_ERR_NONE); +} + +/* + * set_default_cmd() - set the default debug entry and $all + */ +static void +set_default_cmd(void) +{ + if (!g_kernelmode) + fcn(strdup("debug"), DEBUG_ENTRY); +#ifdef TESTING + fcn(strdup("empty"), EMPTY_ENTRY); +#endif + (void) set(strdup("all"), expr(spec(strdup("keys"), SPEC_EXACT), + spec(strdup(".*"), SPEC_REGEXP))); + +} + +/* + * process() - enable and disable selected probes + */ + +typedef struct { + tnfctl_probe_t *probe_p; + tnfctl_handle_t *hndl; +} process_args_t; + +static tnfctl_errcode_t +percmd(expr_t *expr_p, cmd_kind_t kind, fcn_t *fcn_p, boolean_t isnew, + void *calldata_p) +{ + process_args_t *args_p = (process_args_t *)calldata_p; + tnfctl_handle_t *hndl = args_p->hndl; + tnfctl_probe_t *probe_p = args_p->probe_p; + tnfctl_errcode_t err = TNFCTL_ERR_NONE; + char *attrs; + + attrs = list_getattrs(probe_p); + + if (expr_match(expr_p, attrs)) { +#if defined(DEBUG) || defined(lint) + if (g_verbose) { + char *cmdstr[] = { + "enable", "disable", + "connect", "clear", + "trace", "untrace"}; + + (void) fprintf(stderr, ": %s command: %s ", + (isnew) ? "new" : "old", cmdstr[kind]); + expr_print(stderr, expr_p); + } +#endif + + switch (kind) { + case CMD_ENABLE: + err = tnfctl_probe_enable(hndl, probe_p, NULL); + break; + case CMD_DISABLE: + err = tnfctl_probe_disable(hndl, probe_p, NULL); + break; + case CMD_TRACE: + err = tnfctl_probe_trace(hndl, probe_p, NULL); + break; + case CMD_UNTRACE: + err = tnfctl_probe_untrace(hndl, probe_p, NULL); + break; + case CMD_CONNECT: + err = tnfctl_probe_connect(hndl, probe_p, NULL, + fcn_p->entry_name_p); + break; + case CMD_CLEAR: + err = tnfctl_probe_disconnect_all(hndl, probe_p, NULL); + break; + } + +#if defined(DEBUG) || defined(lint) + if (g_verbose) + (void) fprintf(stderr, "\n"); +#endif + + } + if (attrs) + free(attrs); + + return (err); + +} + +/*ARGSUSED*/ +static void * +perprobe(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_p) +{ + process_args_t args; + tnfctl_errcode_t err; + + args.probe_p = probe_p; + args.hndl = hndl; + err = cmd_traverse(percmd, &args); + if (err) { + (void) fprintf(stderr, gettext( + "%s: error on new (dlopened) probe : %s\n"), + g_argv[0], tnfctl_strerror(err)); + } + return (NULL); +} + +static tnfctl_errcode_t +set_probe_discovery_callback(tnfctl_handle_t *hndl) +{ + tnfctl_errcode_t err; + + err = tnfctl_register_funcs(hndl, perprobe, NULL); + if (err) + return (err); + + return (TNFCTL_ERR_NONE); +} + +static tnfctl_errcode_t +perprobe2(tnfctl_handle_t *hndl, tnfctl_probe_t *probe_p, void *cd) +{ + cmd_t *cmd = cd; + process_args_t args; + tnfctl_errcode_t err; + + args.probe_p = probe_p; + args.hndl = hndl; + err = cmd_callback(cmd, percmd, &args); + if (err) { + (void) fprintf(stderr, gettext( + "%s: error on probe operation: %s\n"), + g_argv[0], tnfctl_strerror(err)); + } + return (err); +} + +void +process_cmd(tnfctl_handle_t *hndl, cmd_t *cmd) +{ +#if defined(DEBUG) || defined(lint) + if (g_verbose) + (void) fprintf(stderr, "processing commands\n"); +#endif + (void) tnfctl_probe_apply(hndl, perprobe2, cmd); +} + +/* + * get_commands() - process commands from stdin + */ +static void +get_commands(void) +{ + /* Read commands from STDIN */ + if (g_kernelmode) { + (void) printf(gettext("Type \"help\" for help ...\n")); + } else { + if (g_testflag) + (void) printf("prex(%ld), target(%ld): ", + getpid(), g_targetpid); + (void) printf(gettext("Target process stopped\n")); + (void) printf(gettext( + "Type \"continue\" to resume the target, " + "\"help\" for help ...\n")); + } + + while (yyparse()); +} + + +/* + * quit() - called to quit the controlling process. The boolean argument + * specifies whether to terminate the target as well. + */ + +void +quit(boolean_t killtarget, boolean_t runtarget) +{ + tnfctl_errcode_t err; + + if (killtarget && runtarget) + err = tnfctl_close(g_hndl, TNFCTL_TARG_DEFAULT); + else if (killtarget && !runtarget) + err = tnfctl_close(g_hndl, TNFCTL_TARG_KILL); + else if (!killtarget && runtarget) + err = tnfctl_close(g_hndl, TNFCTL_TARG_RESUME); + else if (!killtarget && !runtarget) + err = tnfctl_close(g_hndl, TNFCTL_TARG_SUSPEND); + if (err) { + (void) fprintf(stderr, gettext( + "%s: trouble quitting : %s\n"), + g_argv[0], tnfctl_strerror(err)); + exit(1); + } + exit(0); +} + + +/* + * scanargs() - processes the command line arguments + */ + +#define strneq(s1, s2, n) (strncmp(s1, s2, n) == 0) + +static void +scanargs(int argc, + char **argv) +{ + int c; +#if defined(DEBUG) || defined(lint) + char *optstr = "l:o:p:s:tkv:"; /* debugging options */ +#else + char *optstr = "l:o:p:s:tk"; /* production options */ +#endif + + /* set up some defaults */ + g_targetpid = 0; + g_cmdname = NULL; + g_cmdargs = NULL; + g_preload = NULL; + g_outname = NULL; + g_outsize = -1; + + while ((c = getopt(argc, argv, optstr)) != EOF) { + switch (c) { + case 'l': /* preload objects */ + g_preload = optarg; + break; + case 'o': /* tracefile name */ + g_outname = optarg; + break; + case 'p': /* target pid (attach case) */ + g_targetpid = atoi(optarg); + break; + case 's': /* tracefile size */ + g_outsize = atoi(optarg) * 1024; + break; + case 't': /* test flag */ + g_testflag = B_TRUE; + (void) setvbuf(stdout, NULL, _IOLBF, 0); + break; + case 'k': /* kernel mode */ + g_kernelmode = B_TRUE; + break; +#if defined(DEBUG) || defined(lint) + case 'v': /* verbose flag */ + g_verbose = atoi(optarg); + break; +#endif + case '?': /* error case */ + usage(argv, gettext("unrecognized argument")); + } + } + + if (optind < argc) { + g_cmdname = strdup(argv[optind]); + g_cmdargs = &argv[optind]; + } + /* sanity clause */ + if (!g_kernelmode && (g_cmdname == NULL && g_targetpid == 0)) + usage(argv, gettext("need to specify cmd or pid")); + if (g_cmdname != NULL && g_targetpid != 0) + usage(argv, gettext("can't specify both cmd and pid")); + if (g_targetpid && g_preload) + usage(argv, gettext("can't use preload option with attach")); + if (g_kernelmode) { + if (g_outname) + usage(argv, "can't specify a filename in kernel mode"); + if (g_cmdname) + usage(argv, "can't specify a command in kernel mode"); + if (g_targetpid) + usage(argv, "can't specify pid in kernel mode"); + if (g_preload) + usage(argv, "can't use preload option in kernel mode"); + } + /* default output size */ + if (g_outsize == -1) + g_outsize = g_kernelmode ? KERNEL_OUTSIZE : USER_OUTSIZE; + +#ifdef OLD + int i; + + for (i = 1; i < argc; i++) { + if (strneq(argv[i], "-v", 2)) { + int vlevel; + + vlevel = (strlen(argv[i]) > 2)? atoi(&argv[i][2]) : 1; + g_verbose = B_TRUE; + prb_verbose_set(vlevel); + } else if (strneq(argv[i], "-pid", 2)) { + if (++i >= argc) + usage(argv, gettext("missing pid argument")); + g_targetpid = atoi(argv[i]); + } else if (strneq(argv[i], "-t", 2)) { + g_testflag = B_TRUE; + (void) setvbuf(stdout, NULL, _IOLBF, 0); + } else if (argv[i][0] != '-') { + g_cmdname = strdup(argv[i]); + if (!g_cmdname) { + err_fatal(gettext( + "%s: out of memory"), argv[0]); + } + if (g_verbose >= 2) { + (void) fprintf(stderr, + "cmdname=%s\n", g_cmdname); + } + /* + * rest of arguments are the args to the executable - + * by convention argv[0] should be name of + * executable, so we don't increment i + */ + g_cmdargs = &argv[i]; + break; + } else { + usage(argv, gettext("unrecognized argument")); + } + } +#endif + +} /* end scanargs */ + + +/* + * sig_handler() - cleans up if a signal is received + */ + +/*ARGSUSED*/ +static void +sig_handler(int signo) +{ + g_getcmds = B_TRUE; +} /* end sig_handler */ + + +/* + * set_signal() - sets up function to call for clean up + */ + +static int +set_signal(void) +{ + struct sigaction newact; + + newact.sa_handler = sig_handler; + (void) sigemptyset(&newact.sa_mask); + newact.sa_flags = 0; + if (sigaction(SIGINT, &newact, NULL) < 0) { + return (errno); + } + return (0); +} + + +/* + * set_tracefile() - initializes tracefile, sets the tracefile name and size + */ +static tnfctl_errcode_t +set_tracefile(tnfctl_handle_t *hndl) +{ + tnfctl_errcode_t err; + tnfctl_trace_attrs_t attrs; + size_t minoutsize; + char path[MAXPATHLEN]; + char *outfile_name; + char *tmpdir; + + /* Init tracefile name used by list cmd */ + tracefile = NULL; + err = tnfctl_trace_attrs_get(hndl, &attrs); + if (err) + return (err); + + if (attrs.trace_buf_state == TNFCTL_BUF_BROKEN) + return (TNFCTL_ERR_BUFBROKEN); + if (attrs.trace_buf_state == TNFCTL_BUF_OK) { + /* trace file set already - can't change it */ + return (TNFCTL_ERR_NONE); + } + + minoutsize = attrs.trace_min_size; + if (g_outsize < minoutsize) { + (void) fprintf(stderr, + gettext("specified tracefile size smaller then " + "minimum; setting to %d kbytes\n"), + minoutsize / 1024); + g_outsize = minoutsize; + } + + /* where is $TMPDIR? */ + tmpdir = getenv("TMPDIR"); + if (!tmpdir || *tmpdir == '\0') { + tmpdir = "/tmp"; + } + + /* do we have an absolute, relative or no pathname specified? */ + if (g_outname == NULL) { + /* default, no tracefile specified */ + if ((strlen(tmpdir) + 1 + 20) > (size_t)MAXPATHLEN) { + (void) fprintf(stderr, gettext( + "%s: $TMPDIR too long\n"), g_argv[0]); + exit(1); + } + (void) sprintf(path, "%s/trace-%ld", tmpdir, g_targetpid); + outfile_name = path; + } else { + /* filename specified */ + outfile_name = g_outname; + } + tracefile = strdup(outfile_name); + if (tracefile == NULL) { + if ((errno == ENOMEM) || (errno == EAGAIN)) { + return (TNFCTL_ERR_ALLOCFAIL); + } else { + return (TNFCTL_ERR_INTERNAL); + } + } + +#if defined(DEBUG) || defined(lint) + if (g_verbose) + (void) fprintf(stderr, + "setting tracefile name=\"%s\", size=%d\n", + path, g_outsize); +#endif + err = tnfctl_buffer_alloc(hndl, outfile_name, g_outsize); + return (err); +} +/* + * get_data_model() - get the process data model from psinfo + * structure. + */ +#define PROCFORMAT "/proc/%d" +static int +get_data_model(pid_t pid) +{ + char path[MAXPATHLEN]; + int fd, dmodel = -1; + prpsinfo_t psinfo; + + (void) sprintf(path, PROCFORMAT, (int)pid); + fd = open(path, O_RDONLY); + if (fd == -1) + return (dmodel); + if ((dmodel = ioctl(fd, PIOCPSINFO, &psinfo)) == -1) + return (dmodel); + return ((int)psinfo.pr_dmodel); +} +/* + * get_executable - return file descriptor for PATH-resolved + * target file. + * + */ +static int +get_executable(char *name) { + int fd = -1; + + if (name != NULL) { + char path[PATH_MAX + 1]; + char line[MAX_INPUT + 1]; + char *p = line; + char *fname = name; + int N = sizeof (line); + struct stat file_att; + + while (*fname == ' ') fname++; + if (fname[0] == '-' || strchr(fname, '/')) { + fd = open(fname, O_RDONLY); + } else { + int len = strlen(fname); + char *dirlist = getenv("PATH"); + char *dir = NULL; + + if (dirlist != NULL) { + dirlist = strdup(dirlist); + dir = strtok(dirlist, ":"); + } + while (fd < 0 && dir != NULL) { + if ((strlen(dir) + len + 1) < sizeof (path)) { + strcat(strcat(strcpy(path, dir), "/"), fname); + fd = open(path, O_RDONLY); + } + dir = strtok(NULL, ":"); + } + if (dirlist != NULL) free(dirlist); + } + if (fstat(fd, &file_att) || !S_ISREG(file_att.st_mode)) { + if (fd >= 0) + close(fd); + return (-1); + } + if (read(fd, p, 2) && p[0] == '#' && p[1] == '!') { + while (N-- > 1 && read(fd, p, 1) && *p != '\n') + p++; + *p = '\0'; + close(fd); + return (get_executable(line)); + } + if (fd >= 0) lseek(fd, 0, SEEK_SET); + } /* %$#@! cstyle complaint */ + return (fd); +} + +/* + * get_elf_class - get the target executable elf class + * i.e. ELFCLASS64 or ELFCLASS32. + */ +static int +get_elf_class(char *filename) +{ + int elfclass = -1; + int elffd = get_executable(filename); + Elf *elf; + size_t size; + char *ident; + GElf_Ehdr ehdr; + + if (elffd < 0) + return (elfclass); + if (elf_version(EV_CURRENT) == EV_NONE) { + (void) close(elffd); + return (elfclass); + } + elf = elf_begin(elffd, ELF_C_READ, (Elf *) 0); + /* + * verify information in file header + */ + if (gelf_getehdr(elf, &ehdr) == (GElf_Ehdr *) 0) { + close(elffd); + return (elfclass); + } + ident = elf_getident(elf, &size); + if (ident[EI_CLASS] == ELFCLASS32) + elfclass = ELFCLASS32; + if (ident[EI_CLASS] == ELFCLASS64) + elfclass = ELFCLASS64; + close(elffd); + return (elfclass); +} +/* + * check_exec_model() - check the consistency between prex data model + * and target elf class and act accordingly + */ +static void +check_exec_model(char **argv, char **envp) +{ + int elfclass; + + elfclass = get_elf_class(g_cmdname); + if (((elfclass == ELFCLASS32) && (prex_dmodel == PR_MODEL_ILP32)) || + ((elfclass == ELFCLASS64) && (prex_dmodel == PR_MODEL_LP64))) + return; + if ((prex_dmodel == PR_MODEL_ILP32) && + (elfclass == ELFCLASS64)) { + (void) fprintf(stderr, gettext( + "Error: 32 bit prex can not exec 64 bit target\n")); + exit(1); + } + if ((prex_dmodel == PR_MODEL_LP64) && + (elfclass == ELFCLASS32)) + prex_isaexec(argv, envp); +} + +/* + * check_pid_model() - check the consistency between prex data model + * and target data model and act accordingly + */ +static void +check_pid_model(char **argv, char **envp) +{ + int dmodel; + + dmodel = get_data_model(g_targetpid); + if (prex_dmodel == dmodel) + return; + if ((prex_dmodel == PR_MODEL_ILP32) && + (dmodel == PR_MODEL_LP64)) { + (void) fprintf(stderr, gettext( + "Error: 32 bit prex can not exec 64 bit target\n")); + exit(1); + } + if ((prex_dmodel == PR_MODEL_LP64) && + (dmodel == PR_MODEL_ILP32)) + prex_isaexec(argv, envp); +} +/* + * prex_isaexec() - there is only one case this function get called + * 64 bit prex, 32 bit target, need to exec 32 bit + * prex here. + */ +static void +prex_isaexec(char **argv, char **envp) +{ + char path[PATH_MAX + sizeof (PREX32DIR)]; + strcat(strcat(strcpy(path, dirname(dirname(argv[0]))), PREX32DIR), + basename(argv[0])); + if (get_elf_class(path) != ELFCLASS32) + strcpy(path, PREX32EXEC); + argv[0] = path; + (void) execve(path, argv, envp); + (void) fprintf(stderr, + gettext("%s: execve(\"%s\") failed\n"), + argv[0], path); +} +void +cmd_listtracefile() +{ + + if (g_kernelmode) { + (void) fprintf(stderr, + gettext("There is no trace file in kernel mode!\n")); + } else { + (void) printf(gettext("Current trace file is: %s\n"), tracefile); + } +} diff --git a/usr/src/cmd/tnf/prex/new.c b/usr/src/cmd/tnf/prex/new.c new file mode 100644 index 0000000000..e54276c7a3 --- /dev/null +++ b/usr/src/cmd/tnf/prex/new.c @@ -0,0 +1,57 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#include <stdio.h> +#include <stdlib.h> +#include <libintl.h> +#include "new.h" + + +/* + * new_alloc() - allocate and bail if neccessary + */ + +void * +new_alloc(size_t size) +{ + void *ptr; + + ptr = malloc(size); + + if (!ptr) { + (void) fprintf(stderr, + gettext("new; out of memory, aborting\n")); + abort(); + + } + return (ptr); + +} /* new_alloc */ diff --git a/usr/src/cmd/tnf/prex/new.h b/usr/src/cmd/tnf/prex/new.h new file mode 100644 index 0000000000..73bd6fcb66 --- /dev/null +++ b/usr/src/cmd/tnf/prex/new.h @@ -0,0 +1,58 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#ifndef _NEW_H +#define _NEW_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Defines + */ + +#define new(t) ((t *) (new_alloc(sizeof (t)))) + + +/* + * Declarations + */ + +void *new_alloc(size_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _NEW_H */ diff --git a/usr/src/cmd/tnf/prex/prbk.c b/usr/src/cmd/tnf/prex/prbk.c new file mode 100644 index 0000000000..5faf8a39bb --- /dev/null +++ b/usr/src/cmd/tnf/prex/prbk.c @@ -0,0 +1,387 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> /* for strerror() */ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/tnf.h> +#include <fcntl.h> +#include <errno.h> +#include <locale.h> + +#include "prbk.h" + +#include <tnf/tnfctl.h> + +extern tnfctl_handle_t *g_hndl; + +typedef struct _pidlist { + pid_t pid; + struct _pidlist *next; +} pidlist_t; + +static boolean_t check_kernelmode(tnfctl_trace_attrs_t *attrs_p); + +static boolean_t +check_kernelmode(tnfctl_trace_attrs_t *attrs_p) +{ + extern int g_kernelmode; + tnfctl_errcode_t err; + + if (!g_kernelmode) { + (void) fprintf(stderr, gettext( + "This command is only available " + "in kernel mode (prex invoked with the -k flag)\n")); + return (B_TRUE); + } + if (attrs_p) { + err = tnfctl_trace_attrs_get(g_hndl, attrs_p); + if (err) { + (void) fprintf(stderr, gettext( + "error on checking trace attributes : %s\n"), + tnfctl_strerror(err)); + return (B_TRUE); + } + } + return (B_FALSE); +} + +/* + * Print trace buffer status (is one allocated, and if so, how big is it. + */ +void +prbk_buffer_list() +{ + tnfctl_trace_attrs_t attrs; + + if (check_kernelmode(&attrs)) + return; + if (attrs.trace_buf_state == TNFCTL_BUF_NONE) { + (void) printf(gettext("No trace buffer allocated\n")); + } else { + (void) printf(gettext("Trace buffer size is %d bytes\n"), + attrs.trace_buf_size); + if (attrs.trace_buf_state == TNFCTL_BUF_BROKEN) { + (void) printf(gettext("Tracing system has failed -- " + "tracing suspended\n")); + } + } +} + + +/* + * Allocate a trace buffer. Check for reasonable size; reject if there's + * already a buffer. + */ +void +prbk_buffer_alloc(int size) +{ + tnfctl_errcode_t err; + tnfctl_trace_attrs_t attrs; + + if (check_kernelmode(&attrs)) + return; + + if (attrs.trace_buf_state != TNFCTL_BUF_NONE) { + (void) fprintf(stderr, + gettext("There is already a buffer allocated\n")); + return; + } + if (size < attrs.trace_min_size) { + (void) fprintf(stderr, gettext( + "Size %d is less than the minimum buffer size of %d -- " + "buffer size set to %d bytes\n"), + size, attrs.trace_min_size, attrs.trace_min_size); + size = attrs.trace_min_size; + } + + err = tnfctl_buffer_alloc(g_hndl, NULL, size); + if (err) { + (void) fprintf(stderr, + gettext("error in allocating buffer: %s\n"), + tnfctl_strerror(err)); + return; + } + + /* get the trace attributes again */ + if (check_kernelmode(&attrs)) + return; + (void) printf(gettext("Buffer of size %d bytes allocated\n"), + attrs.trace_buf_size); +} + + +/* + * Deallocate the kernel's trace buffer. + */ +void +prbk_buffer_dealloc() +{ + tnfctl_errcode_t err; + + if (check_kernelmode(NULL)) + return; + + err = tnfctl_buffer_dealloc(g_hndl); + switch (err) { + case (TNFCTL_ERR_NONE): + (void) printf(gettext("buffer deallocated\n")); + break; + case (TNFCTL_ERR_NOBUF): + (void) fprintf(stderr, + gettext("There is no buffer to deallocate\n")); + break; + case (TNFCTL_ERR_BADDEALLOC): + (void) fprintf(stderr, + gettext("Can't deallocate the buffer when " + "tracing is active\n")); + break; + default: + (void) fprintf(stderr, + gettext("error in deleting buffer: %s\n"), + tnfctl_strerror(err)); + break; + } +} + + +/* + * Process filter routines. + * + * Process id sets are encoded as "pidlists": a linked list of pids. + * In a feeble attempt at encapsulation, the pidlist_t type is private + * to this file; prexgram.y manipulates pidlists only as opaque handles. + */ + +/* + * Add the given pid (new) to the pidlist (pl). + */ +void * +prbk_pidlist_add(void *pl, int new) + +{ + pidlist_t *npl = (pidlist_t *) malloc(sizeof (*npl)); + + if (npl == NULL) { + (void) fprintf(stderr, + gettext("Out of memory -- can't process pid %d\n"), + new); + return (pl); + } + npl->next = pl; + npl->pid = new; + return (npl); +} + +/* + * Add the pids in the given pidlist to the process filter list. + * For each pid, check whether it's already in the filter list, + * and whether the process exists. + */ +void +prbk_pfilter_add(void *pl) +{ + pidlist_t *ppl = (pidlist_t *) pl; + pidlist_t *tmp; + tnfctl_errcode_t err; + + if (check_kernelmode(NULL)) + return; + while (ppl != NULL) { + err = tnfctl_filter_list_add(g_hndl, ppl->pid); + if (err) { + (void) fprintf(stderr, gettext("Process %ld: %s\n"), + ppl->pid, tnfctl_strerror(err)); + } + tmp = ppl; + ppl = ppl->next; + free(tmp); + } +} + +/* + * Drop the pids in the given pidlist from the process filter list. + * For each pid, complain if it's not in the process filter list; + * and if the process no longer exists (and hence has already implicitly + * been dropped from the process filter list), say so. + */ +void +prbk_pfilter_drop(void *pl) +{ + pidlist_t *ppl = (pidlist_t *) pl; + pidlist_t *tmp; + tnfctl_errcode_t err; + + if (check_kernelmode(NULL)) + return; + + while (ppl != NULL) { + tmp = ppl; + err = tnfctl_filter_list_delete(g_hndl, tmp->pid); + switch (err) { + case (TNFCTL_ERR_NONE): + break; + case (TNFCTL_ERR_BADARG): + (void) fprintf(stderr, + gettext("Process %ld is not being traced\n"), + tmp->pid); + break; + case (TNFCTL_ERR_NOPROCESS): + (void) printf(gettext("Process %ld has exited\n"), + tmp->pid); + break; + default: + (void) fprintf(stderr, gettext("Process %ld: %s\n"), + tmp->pid, tnfctl_strerror(err)); + break; + } + ppl = ppl->next; + free(tmp); + } +} + +/* + * Turn process filter mode on or off. The process filter is maintained + * even when process filtering is off, but has no effect: all processes + * are traced. + */ +void +prbk_set_pfilter_mode(boolean_t onoff) +{ + tnfctl_errcode_t err; + + if (check_kernelmode(NULL)) + return; + err = tnfctl_filter_state_set(g_hndl, onoff); + if (err) { + (void) fprintf(stderr, gettext("pfilter: %s\n"), + tnfctl_strerror(err)); + } +} + + +/* + * Report whether process filter mode is currently on or off, and + * dump the current process filter set. + */ +void +prbk_show_pfilter_mode() +{ + tnfctl_errcode_t err; + tnfctl_trace_attrs_t attrs; + pid_t *pids_p; + int i, pid_count; + pid_t *cur_pid; + + if (check_kernelmode(&attrs)) + return; + (void) printf(gettext("Process filtering is %s\n"), + attrs.filter_state ? "on" : "off"); + err = tnfctl_filter_list_get(g_hndl, &pids_p, &pid_count); + if (err) { + (void) fprintf(stderr, + gettext("error in getting process filter list: %s\n"), + tnfctl_strerror(err)); + return; + } + (void) printf(gettext("Process filter set is ")); + if (pid_count == 0) + (void) printf("empty.\n"); + else { + (void) printf("{"); + cur_pid = pids_p; + for (i = 0; i < pid_count; i++, cur_pid++) { + (void) printf("%ld%s", *cur_pid, + (i != (pid_count - 1)) ? ", " : "}\n"); + } + } +} + +/* + * Check for process filtering on with empty pid filter. + */ +void +prbk_warn_pfilter_empty(void) +{ + tnfctl_errcode_t err; + pid_t *pids_p; + int pid_count; + tnfctl_trace_attrs_t attrs; + + if (check_kernelmode(&attrs)) + return; + if (attrs.filter_state) { + err = tnfctl_filter_list_get(g_hndl, &pids_p, &pid_count); + if (err) { + (void) fprintf(stderr, + gettext("error in getting process filter list: %s\n"), + tnfctl_strerror(err)); + return; + } + if (!pid_count) + (void) fprintf(stderr, + gettext("Warning: Process filtering on, \ +but pid filter list is empty\n")); + } +} + + +/* + * Turn kernel tracing on or off. + */ +void +prbk_set_tracing(boolean_t onoff) +{ + tnfctl_errcode_t err; + + if (check_kernelmode(NULL)) + return; + + err = tnfctl_trace_state_set(g_hndl, onoff); + if (err) { + (void) fprintf(stderr, + gettext("error in setting tracing state: %s\n"), + tnfctl_strerror(err)); + } +} + +/* + * Show whether kernel tracing is currently on or off. + */ +void +prbk_show_tracing() +{ + tnfctl_trace_attrs_t attrs; + + if (check_kernelmode(&attrs)) + return; + (void) printf(gettext("Tracing is %s\n"), + attrs.trace_state ? "on" : "off"); +} diff --git a/usr/src/cmd/tnf/prex/prbk.h b/usr/src/cmd/tnf/prex/prbk.h new file mode 100644 index 0000000000..7b3e0fdb66 --- /dev/null +++ b/usr/src/cmd/tnf/prex/prbk.h @@ -0,0 +1,55 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#ifndef _PRBK_H +#define _PRBK_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Declarations + */ + +void prbk_buffer_list(void); +void prbk_buffer_alloc(int size); +void prbk_buffer_dealloc(void); +void *prbk_pidlist_add(void *, int); +void prbk_pfilter_add(void *); +void prbk_pfilter_drop(void *); +void prbk_set_pfilter_mode(boolean_t); +void prbk_show_pfilter_mode(void); +void prbk_set_tracing(boolean_t); +void prbk_show_tracing(void); +void prbk_warn_pfilter_empty(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _PRBK_H */ diff --git a/usr/src/cmd/tnf/prex/prexgram.y b/usr/src/cmd/tnf/prex/prexgram.y new file mode 100644 index 0000000000..3dbe9495fb --- /dev/null +++ b/usr/src/cmd/tnf/prex/prexgram.y @@ -0,0 +1,382 @@ +%{ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" +%} + +%token ADD +%token ALLOC +%token BUFFER +%token CLEAR +%token COMMA +%token CONNECT +%token DEALLOC +%token DELETE +%token FILTER +%token CONTINUE +%token CREATE +%token DISABLE +%token ENABLE +%token EQ +%token FCNNAME +%token FCNS +%token ON +%token OFF +%token HELP +%token KTRACE +%token HISTORY +%token IDENT +%token INVAL +%token KILL +%token LIST +%token NL +%token PFILTER +%token PROBES +%token QUIT +%token REGEXP +%token RESUME +%token SCALED_INT +%token SETNAME +%token SETS +%token SOURCE +%token SUSPEND +%token TRACE +%token TRACEFILE +%token UNTRACE +%token VALSTR +%token VALUES + +%{ +#include <stdio.h> +#include <string.h> +#include <sys/types.h> + +#include "set.h" +#include "cmd.h" +#include "fcn.h" +#include "list.h" +#include "expr.h" +#include "spec.h" +#include "source.h" +#include "prbk.h" + +extern int yylex(); + +extern void help_on_topic(char *topic); +extern void help_on_command(int cmd); +extern boolean_t g_kernelmode; +extern tnfctl_handle_t *g_hndl; + +void quit(boolean_t killtarget, boolean_t runtarget); +extern void help(void); +extern void process_cmd(tnfctl_handle_t *hndl, cmd_t *cmd); +extern void cmd_listtracefile(); +%} + +%union +{ + char * strval; + expr_t * exprval; + spec_t * specval; + void * pidlistval; + int intval; +} + +%type <strval> SETNAME FCNNAME IDENT VALSTR REGEXP +%type <intval> CONTINUE DISABLE ENABLE HELP LIST QUIT SOURCE TRACE UNTRACE BUFFER KTRACE PFILTER CLEAR CONNECT command +%type <exprval> expr exprlist +%type <specval> spec speclist +%type <pidlistval> pidlist +%type <intval> SCALED_INT singlepid + +%% + +file : statement_list + ; + +statement_list : /* empty */ { prompt(); } + | statement_list statement + { + if (g_kernelmode) + prbk_warn_pfilter_empty(); + prompt(); + } + ; + +statement : empty_statement + | help_statement + | continue_statement + | quit_statement + | enable_statement + | disable_statement + | trace_statement + | untrace_statement + | connect_statement + | clear_statement + | pfilter_statement + | ktrace_statement + | buffer_statement + | create_statement + | source_statement + | listsets_statement + | listhistory_statement + | listtracefile_statement + | listfcns_statement + | listprobes_statement + | listvalues_statement + | error NL { yyerrok; } + ; + +empty_statement : NL + ; + +command : CONTINUE { $$ = $1; } /* user&kernel */ + | DISABLE { $$ = $1; } + | ENABLE { $$ = $1; } + | HELP { $$ = $1; } + | LIST { $$ = $1; } + | QUIT { $$ = $1; } + | SOURCE { $$ = $1; } + | TRACE { $$ = $1; } + | UNTRACE { $$ = $1; } + | BUFFER { $$ = $1; } /* kernel only */ + | KTRACE { $$ = $1; } + | PFILTER { $$ = $1; } + | CLEAR { $$ = $1; } /* user only */ + | CONNECT { $$ = $1; } + ; + +help_statement : HELP NL { help(); } + | HELP command NL { help_on_command($2); } + | HELP IDENT NL { help_on_topic($2); } + ; + +continue_statement : CONTINUE NL + { + if (!g_kernelmode) YYACCEPT; + } + ; + +quit_statement : QUIT NL { quit(B_TRUE, B_TRUE); } + | QUIT KILL NL { quit(B_TRUE, B_FALSE); } + | QUIT RESUME NL { quit(B_FALSE, B_TRUE); } + | QUIT SUSPEND NL { quit(B_FALSE, B_FALSE); } + ; + +enable_statement : ENABLE SETNAME NL + { + cmd_t *cmd_p; + cmd_p = cmd_set($2, CMD_ENABLE, NULL); + if (cmd_p) + process_cmd(g_hndl, cmd_p); + } + | ENABLE exprlist NL + { + cmd_t *cmd_p; + cmd_p = cmd_expr($2, CMD_ENABLE, NULL); + if (cmd_p) + process_cmd(g_hndl, cmd_p); + } + ; + +disable_statement : DISABLE SETNAME NL + { + cmd_t *cmd_p; + cmd_p = cmd_set($2, CMD_DISABLE, NULL); + if (cmd_p) + process_cmd(g_hndl, cmd_p); + } + | DISABLE exprlist NL + { + cmd_t *cmd_p; + cmd_p = cmd_expr($2, CMD_DISABLE, NULL); + if (cmd_p) + process_cmd(g_hndl, cmd_p); + } + ; + +trace_statement : TRACE SETNAME NL + { + cmd_t *cmd_p; + cmd_p = cmd_set($2, CMD_TRACE, NULL); + if (cmd_p) + process_cmd(g_hndl, cmd_p); + } + | TRACE exprlist NL + { + cmd_t *cmd_p; + cmd_p = cmd_expr($2, CMD_TRACE, NULL); + if (cmd_p) + process_cmd(g_hndl, cmd_p); + } + ; + +untrace_statement : UNTRACE SETNAME NL + { + cmd_t *cmd_p; + cmd_p = cmd_set($2, CMD_UNTRACE, NULL); + if (cmd_p) + process_cmd(g_hndl, cmd_p); + } + | UNTRACE exprlist NL + { + cmd_t *cmd_p; + cmd_p = cmd_expr($2, CMD_UNTRACE, NULL); + if (cmd_p) + process_cmd(g_hndl, cmd_p); + } + ; + +connect_statement : CONNECT FCNNAME SETNAME NL + { + cmd_t *cmd_p; + cmd_p = cmd_set($3, CMD_CONNECT, $2); + if (cmd_p) + process_cmd(g_hndl, cmd_p); + } + | CONNECT FCNNAME exprlist NL + { + cmd_t *cmd_p; + cmd_p = cmd_expr($3, CMD_CONNECT, $2); + if (cmd_p) + process_cmd(g_hndl, cmd_p); + } + ; + +clear_statement : CLEAR SETNAME NL + { + cmd_t *cmd_p; + cmd_p = cmd_set($2, CMD_CLEAR, NULL); + if (cmd_p) + process_cmd(g_hndl, cmd_p); + } + | CLEAR exprlist NL + { + cmd_t *cmd_p; + cmd_p = cmd_expr($2, CMD_CLEAR, NULL); + if (cmd_p) + process_cmd(g_hndl, cmd_p); + } + ; + +create_statement : CREATE SETNAME exprlist NL { (void) set($2, $3); } + | CREATE FCNNAME IDENT NL { fcn($2, $3); } + ; + +source_statement : SOURCE VALSTR NL { source_file($2); } + | SOURCE IDENT NL { source_file($2); } + ; + +listsets_statement : LIST SETS NL { set_list(); } + ; + +listhistory_statement : LIST HISTORY NL { cmd_list(); } + ; + +listtracefile_statement : LIST TRACEFILE NL { cmd_listtracefile(); } + ; + +listfcns_statement : LIST FCNS NL { fcn_list(); } + ; + + ; + +pfilter_statement : PFILTER ON NL + { prbk_set_pfilter_mode(B_TRUE); } + | PFILTER OFF NL + { prbk_set_pfilter_mode(B_FALSE); } + | PFILTER ADD pidlist NL + { prbk_pfilter_add($3); } + | PFILTER DELETE pidlist NL + { prbk_pfilter_drop($3); } + | PFILTER NL + { prbk_show_pfilter_mode(); } + ; + +ktrace_statement : KTRACE ON NL + { prbk_set_tracing(B_TRUE); } + | KTRACE OFF NL + { prbk_set_tracing(B_FALSE); } + | KTRACE NL + { prbk_show_tracing(); } + ; + +listprobes_statement : LIST speclist PROBES SETNAME NL + { list_set($2, $4); } + | LIST speclist PROBES exprlist NL + { list_expr($2, $4); } + ; + +listvalues_statement : LIST VALUES speclist NL { list_values($3); } + ; + +exprlist : /* empty */ { $$ = NULL; } + | exprlist expr { $$ = expr_list($1, $2); } + ; + +speclist : /* empty */ { $$ = NULL; } + | speclist spec { $$ = spec_list($1, $2); } + ; + +expr : spec EQ spec { $$ = expr($1, $3); } + | spec { $$ = expr(spec(strdup("keys"), + SPEC_EXACT), $1); } + ; + +spec : IDENT { $$ = spec($1, SPEC_EXACT); } + | VALSTR { $$ = spec($1, SPEC_EXACT); } + | REGEXP { $$ = spec($1, SPEC_REGEXP); } + ; + +pidlist : pidlist COMMA singlepid + { $$ = prbk_pidlist_add($1, $3); } + | singlepid + { $$ = prbk_pidlist_add(NULL, $1); } + ; + +singlepid : SCALED_INT + ; + +buffer_statement : BUFFER NL + { + prbk_buffer_list(); + } + | BUFFER ALLOC NL + { + extern int g_outsize; + prbk_buffer_alloc(g_outsize); + } + | BUFFER ALLOC SCALED_INT NL + { + prbk_buffer_alloc($3); + } + | BUFFER DEALLOC NL + { + prbk_buffer_dealloc(); + } + ; + + +%% diff --git a/usr/src/cmd/tnf/prex/prexlex.l b/usr/src/cmd/tnf/prex/prexlex.l new file mode 100644 index 0000000000..c307c429a9 --- /dev/null +++ b/usr/src/cmd/tnf/prex/prexlex.l @@ -0,0 +1,169 @@ +%{ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" +%} + +%a 10000 +%o 10000 + +%{ +#include "spec.h" +#include "expr.h" +#include "y.tab.h" +#include <stdlib.h> +#include <string.h> + +char * qtstr (char * instr); +char * rgstr (char * instr); + +/* +** we substitute i/o routines defined in main.c for the +** standard fare. This allows us to support the "source" +** function by redirecting the input stream from different +** places +*/ +#include "source.h" +#undef input +#undef unput +#undef output +#define input() source_input() +#define unput(c) source_unput(c) +#define output(c) source_output(c) +%} + +IDFIRST [a-zA-Z_\.%] +IDCHAR ({IDFIRST}|[0-9]) +ID {IDFIRST}{IDCHAR}* + +%% + +#.* ; /* eat comments */ +[ \t]+ ; /* eats whitespace */ + +\n { source_nl(); return NL; } +\\\n { source_nl(); } /* escaped newline */ += return (EQ); +\, return (COMMA); + +add { yylval.intval = ADD; return (ADD); } +alloc { yylval.intval = ALLOC; return (ALLOC); } +buffer { yylval.intval = BUFFER; return (BUFFER); } +clear { yylval.intval = CLEAR; return (CLEAR); } +connect { yylval.intval = CONNECT; return (CONNECT); } +continue { yylval.intval = CONTINUE; return (CONTINUE); } +create { yylval.intval = CREATE; return (CREATE); } +dealloc { yylval.intval = DEALLOC; return (DEALLOC); } +delete { yylval.intval = DELETE; return (DELETE); } +disable { yylval.intval = DISABLE; return (DISABLE); } +enable { yylval.intval = ENABLE; return (ENABLE); } +fcns { yylval.intval = FCNS; return (FCNS); } +filter { yylval.intval = FILTER; return (FILTER); } +help { yylval.intval = HELP; return (HELP); } +history { yylval.intval = HISTORY; return (HISTORY); } +tracefile { yylval.intval = TRACEFILE; return (TRACEFILE); } +kill { yylval.intval = KILL; return (KILL); } +ktrace { yylval.intval = KTRACE; return (KTRACE); } +list { yylval.intval = LIST; return (LIST); } +off { yylval.intval = OFF; return (OFF); } +on { yylval.intval = ON; return (ON); } +pfilter { yylval.intval = PFILTER; return (PFILTER); } +probes { yylval.intval = PROBES; return (PROBES); } +quit { yylval.intval = QUIT; return (QUIT); } +resume { yylval.intval = RESUME; return (RESUME); } +sets { yylval.intval = SETS; return (SETS); } +source { yylval.intval = SOURCE; return (SOURCE); } +suspend { yylval.intval = SUSPEND; return (SUSPEND); } +trace { yylval.intval = TRACE; return (TRACE); } +untrace { yylval.intval = UNTRACE; return (UNTRACE); } +values { yylval.intval = VALUES; return (VALUES); } + +${ID} { yylval.strval = strdup(&yytext[1]); return SETNAME; } +&{ID} { yylval.strval = strdup(&yytext[1]); return FCNNAME; } +{ID} { yylval.strval = strdup(yytext); return IDENT; } +\'[^'\n]*\' { yylval.strval = qtstr(yytext); return VALSTR; } + +\/([^/\\\n]|\\.)*\/ { yylval.strval = rgstr(yytext); return REGEXP; } + +[0-9]+[KkMm]? { + char scale = yytext[yyleng - 1]; + yylval.intval = atoi(yytext); + if (scale == 'k' || scale == 'K') + yylval.intval *= 1024; + else if (scale == 'm' || scale == 'M') + yylval.intval *= 1024 * 1024; + return (SCALED_INT); + } + +. return (INVAL); /* barf on anything else */ + +%% + +/**************************************************************** +qtstr() - shucks a quoted str, and copies it into new memory +****************************************************************/ + +char * +qtstr (char * instr) +{ + char *ptr; + int indx; + + /* skip the leading quote in the copy */ + ptr = strdup(&instr[1]); + + /* null out the trailing quote */ + indx = strlen(ptr) - 1; + indx = (indx < 0) ? 0 : indx; + ptr[indx] = '\0'; + + return ptr; +} /* end qtstr */ + + +/**************************************************************** +rgstr() - shucks a decorated regular expression, and copies it +into new memory +****************************************************************/ + +char * +rgstr (char * instr) +{ + char *ptr; + int indx; + + /* skip the leading slash in the copy */ + ptr = strdup(&instr[1]); + + /* null out the trailing slash */ + indx = strlen(ptr) - 1; + indx = (indx < 0) ? 0 : indx; + ptr[indx] = '\0'; + + return (ptr); + +} /* end rgstr */ + + diff --git a/usr/src/cmd/tnf/prex/queue.c b/usr/src/cmd/tnf/prex/queue.c new file mode 100644 index 0000000000..c2e6867bce --- /dev/null +++ b/usr/src/cmd/tnf/prex/queue.c @@ -0,0 +1,154 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#include "queue.h" +#include "new.h" + + +/* + * queue_init() - initializes a queue_node to be unlinked. + */ + +void +queue_init(queue_node_t * q) +{ + q->next_p = q->prev_p = q; + +} /* end queue_init */ + + +/* + * queue_prepend() - prepends a queue_node to another in a list + */ + +queue_node_t * +queue_prepend(queue_node_t * h, queue_node_t * q) +{ + if (!h) + return ((q) ? q : NULL); + + if (q) { + queue_node_t *qtail_p = q->prev_p; + queue_node_t *hnode_p = h->next_p; + + hnode_p->prev_p = qtail_p; + h->next_p = q; + + q->prev_p = h; + qtail_p->next_p = hnode_p; + } + return (h); + +} /* end queue_prepend */ + + +/* + * queue_append() - appends a queue_node to another in a list + */ + +queue_node_t * +queue_append(queue_node_t * h, queue_node_t * q) +{ + if (!h) + return ((q) ? q : NULL); + + if (q) { + queue_node_t *htail_p = h->prev_p; + queue_node_t *qtail_p = q->prev_p; + + h->prev_p = qtail_p; + htail_p->next_p = q; + + q->prev_p = htail_p; + qtail_p->next_p = h; + } + return (h); + +} /* end queue_append */ + + +/* + * queue_remove() - removes a node from a list, returns a pointer to the next + * node in the list. + */ + +queue_node_t * +queue_remove(queue_node_t * q) +{ + queue_node_t *n; + + n = q->next_p; + + if (queue_isempty(q)) + return (NULL); + + q->next_p->prev_p = q->prev_p; + q->prev_p->next_p = q->next_p; + + q->next_p = q->prev_p = q; + + return (n); + +} /* end queue_remove */ + + +/* + * queue_isempty() + */ + +boolean_t +queue_isempty(queue_node_t * q) +{ + return ((q->next_p == q)); + +} /* queue_isempty */ + + +/* + * queue_next() - returns the next element in a queue, or NULL if the + * supplied previous item was the last. + */ + +queue_node_t * +queue_next(queue_node_t * h, queue_node_t * q) +{ + if (!h) + return (NULL); + + if (!q) + return (h); + + if (q->next_p == h) + return (NULL); + + return (q->next_p); + +} /* end queue_next */ diff --git a/usr/src/cmd/tnf/prex/queue.h b/usr/src/cmd/tnf/prex/queue.h new file mode 100644 index 0000000000..ee9529c0fa --- /dev/null +++ b/usr/src/cmd/tnf/prex/queue.h @@ -0,0 +1,67 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#ifndef _QUEUE_H +#define _QUEUE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#include <sys/types.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Typedefs + */ + +typedef struct queue_node queue_node_t; +struct queue_node { + queue_node_t *next_p; + queue_node_t *prev_p; +}; + + +/* + * Declarations + */ + +boolean_t queue_isempty(queue_node_t * q); +queue_node_t *queue_prepend(queue_node_t * h, queue_node_t * q); +queue_node_t *queue_append(queue_node_t * h, queue_node_t * q); +void queue_init(queue_node_t * q); +queue_node_t *queue_next(queue_node_t * h, queue_node_t * q); +queue_node_t *queue_remove(queue_node_t * q); + +#ifdef __cplusplus +} +#endif + +#endif /* _QUEUE_H */ diff --git a/usr/src/cmd/tnf/prex/set.c b/usr/src/cmd/tnf/prex/set.c new file mode 100644 index 0000000000..db7f4fb29a --- /dev/null +++ b/usr/src/cmd/tnf/prex/set.c @@ -0,0 +1,181 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "queue.h" +#include "set.h" +#include "new.h" + + +/* + * Globals + */ + +static queue_node_t g_setlist = { + &g_setlist, +&g_setlist}; + + +/* + * Forward Declarations + */ + +static void set_destroy(set_t * set_p); +static void set_print(FILE * stream, set_t * set_p); + + +/* + * set() - creates a set + */ + +set_t * +set(char *setname_p, expr_t * exprlist_p) +{ + set_t *new_p; + set_t *old_p; + + /* does this setname exist already? */ + old_p = set_find(setname_p); + if (old_p) + set_destroy(old_p); + + /* create a new set */ + new_p = new(set_t); + queue_init(&new_p->qn); + new_p->setname_p = setname_p; + new_p->exprlist_p = exprlist_p; + + /* append the new set to the global list */ + (void) queue_append(&g_setlist, &new_p->qn); + + return (new_p); + +} /* end set */ + + +/* + * set_destroy() - destroys a set and related resources + */ + +static void +set_destroy(set_t * set_p) +{ + if (!set_p) + return; + + /* remove ourselves from any list */ + if (!queue_isempty(&set_p->qn)) + (void) queue_remove(&set_p->qn); + + if (set_p->setname_p) + free(set_p->setname_p); + + /* destroy the exprlist */ + expr_destroy(set_p->exprlist_p); + + free(set_p); + +} /* end set_destroy */ + + +/* + * set_list() - pretty prints the global setlist + */ + +void +set_list(void) +{ + set_t *set_p; + + set_p = (set_t *) & g_setlist; + while ((set_p = (set_t *) queue_next(&g_setlist, &set_p->qn))) { + (void) printf("$%-8s ", set_p->setname_p); + set_print(stdout, set_p); + (void) printf("\n"); + } + +} /* end set_list */ + + +/* + * set_print() - pretty prints a set + */ + +static void +set_print(FILE * stream, set_t * set_p) +{ + if (!set_p) + return; + + expr_print(stream, set_p->exprlist_p); + +} /* end set_print */ + + +#ifdef OLD +/* + * set_match() - discerns whether a probe is in a set + */ + +boolean_t +set_match(set_t * set_p, const char *name, const char *keys) +{ + if (!set_p) + return (B_FALSE); + + return (expr_match(set_p->exprlist_p, name, keys)); + +} /* end set_match */ +#endif + + +/* + * set_find() - finds a set by name + */ + +set_t * +set_find(char *setname_p) +{ + set_t *set_p; + + if (!setname_p) + return (NULL); + + set_p = (set_t *) & g_setlist; + while ((set_p = (set_t *) queue_next(&g_setlist, &set_p->qn))) + if (strcmp(setname_p, set_p->setname_p) == 0) + return (set_p); + + return (NULL); + +} /* end set_find */ diff --git a/usr/src/cmd/tnf/prex/set.h b/usr/src/cmd/tnf/prex/set.h new file mode 100644 index 0000000000..a3bf53be9d --- /dev/null +++ b/usr/src/cmd/tnf/prex/set.h @@ -0,0 +1,70 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#ifndef _SET_H +#define _SET_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#include <stdio.h> +#include <sys/types.h> + +#include "queue.h" +#include "expr.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Typedefs + */ + +typedef struct set { + queue_node_t qn; + char *setname_p; + expr_t *exprlist_p; + +} set_t; + + +/* + * Declarations + */ + +set_t *set(char *name, expr_t * exprlist_p); +void set_list(void); +set_t *set_find(char *setname_p); +boolean_t set_match(set_t * set_p, const char *name, const char *keys); + +#ifdef __cplusplus +} +#endif + +#endif /* _SET_H */ diff --git a/usr/src/cmd/tnf/prex/source.c b/usr/src/cmd/tnf/prex/source.c new file mode 100644 index 0000000000..140c2bcecd --- /dev/null +++ b/usr/src/cmd/tnf/prex/source.c @@ -0,0 +1,307 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <libintl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/param.h> + +#include "new.h" +#include "queue.h" +#include "source.h" + + +/* + * Typedefs + */ + +typedef struct source { + queue_node_t qn; + char *path; + FILE *instream; + int linenum; + boolean_t isatty; + +} source_t; + + +/* + * Defines + */ + +#define HOME "HOME" +#define PREXRC ".prexrc" + + +/* + * Globals + */ + +static queue_node_t stack; +static source_t *top; + + +/* + * source_init() - initializes the source stack + */ + +void +source_init(void) +{ + source_t *new_p; + struct stat statbuf; + char *home; + int retval; + + + /* initialize the stack queue head */ + queue_init(&stack); + + /* stick the standard input on the bottom of the stack */ + new_p = new(source_t); + queue_init(&new_p->qn); + new_p->path = strdup("<STDIN>"); + new_p->instream = stdin; + new_p->linenum = 1; + new_p->isatty = isatty(fileno(new_p->instream)); + + (void) queue_prepend(&stack, &new_p->qn); + top = new_p; + + /* + * since we are pushing onto a stack, we invert the search order * + * and push the prexrc in the current directory on next. + */ + retval = stat(PREXRC, &statbuf); + if (retval != -1) { + source_file(PREXRC); + } + home = getenv(HOME); + if (home) { + char path[MAXPATHLEN]; + + if ((strlen(home) + strlen(PREXRC) + 2) < (size_t) MAXPATHLEN) { + (void) sprintf(path, "%s/%s", home, PREXRC); + retval = stat(path, &statbuf); + if (retval != -1) { + source_file(path); + } + } + } +} /* end source_init */ + + +/* + * source_file() - pushes a new source onto the stack + */ + +void +source_file(char *path) +{ + FILE *newfile; + source_t *new_p; + + newfile = fopen(path, "r"); + if (!newfile) { + semantic_err(gettext("cannot open \"%s\""), path); + return; + } + new_p = new(source_t); + queue_init(&new_p->qn); + new_p->path = strdup(path); + new_p->instream = newfile; + new_p->linenum = 1; + new_p->isatty = isatty(fileno(new_p->instream)); + + (void) queue_prepend(&stack, &new_p->qn); + top = new_p; + +} /* end source_file */ + + +/* + * source_input() - lexical analyzer input routine + */ + +extern void quit(boolean_t, boolean_t); + +int +source_input(void) +{ + int c; + + if (!top) + return (0); + + c = getc(top->instream); + + if (c == EOF) { + /* + * If we get an EOF at the top level, we quit if we are * + * non-interactive, pretend we saw a new-line if we are * + * interactive. + */ + if (top->instream == stdin) { + if (top->isatty) { + source_output('\n'); + return ('\n'); + } else + quit(B_TRUE, B_TRUE); + } + /* we've exhausted the current stream, pop it, delete it ... */ + if (top->path) + free(top->path); + (void) fclose(top->instream); + (void) queue_remove(&top->qn); + free(top); + + /* point to the new top level i/o stream */ + top = (source_t *) queue_next(&stack, &stack); + + if (!top) + return (0); + + /* trigger a prompt if neccessary */ + prompt(); + return (source_input()); + } + return (c); + +} /* end source_input */ + + +/* + * source_unput() - lexical analyzer unput routine + */ + +void +source_unput(int c) +{ + if (top) + (void) ungetc(c, top->instream); + +} /* end source_unput */ + + +/* + * source_output() - lexical analyzer output routine + */ + +void +source_output(int c) +{ + (void) putc(c, stdout); + +} /* end source_output */ + + +/* + * source_nl() - increment the line counter + */ + +void +source_nl(void) +{ + if (top) + top->linenum++; + +} /* end source_nl */ + + +/* + * yyerror() - + */ + +extern char yytext[]; +extern int g_linenum; + +void +yyerror(char *s) +{ + (void) fprintf(stderr, + gettext("\"%s\", line %d: %s on or before \"%s\"\n"), + top->path, top->linenum, s, yytext); + +} + + +/* + * yywrap() - + */ + +int +yywrap() +{ + return (1); + +} /* end yywrap */ + + +/* + * prompt() - + */ + +extern char **g_argv; + +void +prompt(void) +{ + if (top && top->isatty) + (void) printf("%s> ", g_argv[0]); + +} /* end g_prompt */ + + +/* + * semantic_err() - reports a semantic error + */ + +void +semantic_err(char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + if (!top) + return; + + (void) fprintf(stderr, gettext("\"%s\", line %d: semantic error: "), + top->path, top->linenum); + (void) vfprintf(stderr, format, ap); + (void) fprintf(stderr, gettext("\n")); + +} /* end semantic_err */ diff --git a/usr/src/cmd/tnf/prex/source.h b/usr/src/cmd/tnf/prex/source.h new file mode 100644 index 0000000000..6979bf9f6c --- /dev/null +++ b/usr/src/cmd/tnf/prex/source.h @@ -0,0 +1,55 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#ifndef _SOURCE_H +#define _SOURCE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Declarations + */ + +#ifdef __cplusplus +extern "C" { +#endif + +void source_init(void); +void source_file(char *path); +int source_input(void); +void source_unput(int c); +void source_output(int c); +void source_nl(void); + +void yyerror(char *s); +void prompt(void); +void +semantic_err(char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif /* _SOURCE_H */ diff --git a/usr/src/cmd/tnf/prex/sparcv9/Makefile b/usr/src/cmd/tnf/prex/sparcv9/Makefile new file mode 100644 index 0000000000..1b8392fdbe --- /dev/null +++ b/usr/src/cmd/tnf/prex/sparcv9/Makefile @@ -0,0 +1,36 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright (c) 1997, by Sun Microsystems, Inc. +# All rights reserved. +# +# cmd/tnf/prex/sparcv9/Makefile + +include ../Makefile.com +include ../../../Makefile.cmd.64 +CFLAGS64 += -I.. -I. +LINTFLAGS64 += -I.. -I. +prexgram.o prexlex.o := CCVERBOSE= + +install: all $(ROOTPROG64) diff --git a/usr/src/cmd/tnf/prex/spec.c b/usr/src/cmd/tnf/prex/spec.c new file mode 100644 index 0000000000..159212d8e6 --- /dev/null +++ b/usr/src/cmd/tnf/prex/spec.c @@ -0,0 +1,394 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +/* we need to define this to get strtok_r from string.h */ +/* SEEMS LIKE A BUG TO ME */ +#define _REENTRANT + +#ifndef DEBUG +#define NDEBUG 1 +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <libintl.h> +#include <regexpr.h> +#include <assert.h> +#include <sys/types.h> +#include "spec.h" +#include "new.h" +#include "source.h" + + +static boolean_t spec_match(spec_t * spec_p, char *str); + +/* + * Globals + */ + + + +/* + * spec() - builds a spec + */ + +spec_t * +spec(char *str_p, + spec_type_t type) +{ + spec_t *new_p; + + new_p = new(spec_t); + queue_init(&new_p->qn); + new_p->str = str_p; + new_p->type = type; + new_p->regexp_p = NULL; + + if (type == SPEC_REGEXP) { + new_p->regexp_p = compile(str_p, NULL, NULL); + if (!new_p->regexp_p) { + semantic_err(gettext("invalid regular expression")); + free(new_p); + return (NULL); + } + } + return (new_p); + +} /* end spec */ + + +/* + * spec_dup() - duplicates a spec, NOT A SPEC LIST! + */ + +spec_t * +spec_dup(spec_t * spec_p) +{ + spec_t *new_p; + + new_p = spec(strdup(spec_p->str), spec_p->type); + + return (new_p); + +} /* end spec_dup */ + + +/* + * spec_destroy() - destroys a spec list + */ + +void +spec_destroy(spec_t * list_p) +{ + spec_t *spec_p; + + while ((spec_p = (spec_t *) queue_next(&list_p->qn, &list_p->qn))) { + (void) queue_remove(&spec_p->qn); + + if (spec_p->str) + free(spec_p->str); + if (spec_p->regexp_p) + free(spec_p->regexp_p); + free(spec_p); + } + + if (list_p->str) + free(list_p->str); + if (list_p->regexp_p) + free(list_p->regexp_p); + free(list_p); + +} /* end spec_destroy */ + + +/* + * spec_list() - append a spec_t to a list + */ + +spec_t * +spec_list(spec_t * h, + spec_t * f) +{ + /* queue append handles the NULL cases OK */ + return ((spec_t *) queue_append(&h->qn, &f->qn)); + +} /* end spec_list */ + + +/* + * spec_print() - pretty prints a speclist + */ + +void +spec_print(FILE * stream, + spec_t * list_p) +{ + spec_t *spec_p = NULL; + + while ((spec_p = (spec_t *) queue_next(&list_p->qn, &spec_p->qn))) { + switch (spec_p->type) { + case SPEC_EXACT: + (void) fprintf(stream, "'%s'", spec_p->str); + break; + case SPEC_REGEXP: + (void) fprintf(stream, "/%s/", spec_p->str); + break; + } + } + +} /* end spec_print */ + + +/* + * spec_match() - called with a spec and a string, returns whether they + * match. + */ + +static boolean_t +spec_match(spec_t * spec_p, + char *str) +{ + if (!spec_p) + return (B_FALSE); + + switch (spec_p->type) { + case SPEC_EXACT: + return ((strcmp(spec_p->str, str) == 0)); + + case SPEC_REGEXP: + return ((step(str, spec_p->regexp_p) != NULL)); + } + + return (B_FALSE); + +} /* end spec_match */ + + +/* + * spec_attrtrav() - traverse an attribute list, calling the supplied + * function on each matching attribute. + */ + +void +spec_attrtrav(spec_t * spec_p, + char *attrs, + spec_attr_fun_t fun, + void *calldatap) +{ + char *lasts; + char *refptr = NULL; + char *escptr = NULL; + char *pair; + char *s; + boolean_t inquote = B_FALSE; + + /* + * * STRATEGY - we make two copies of the attr string. In one * + * string we escape (translate) all relevant quoted characters to * a + * non-significant character. We use this string to feed to * strtok + * to do the parsing. * Once strtok has parsed the string, we use the + * same fragement * positions from the unescaped string to pass to + * the next level. + */ + + /* make two copies of the string */ + refptr = strdup(attrs); + escptr = strdup(attrs); + + /* escape any quoted ';'s in the escptr string */ + for (s = escptr; *s; s++) { + switch (*s) { + case ';': + if (inquote) + *s = '#'; + break; + + case '\'': + inquote = (inquote) ? B_FALSE : B_TRUE; + break; + + default: + /* nothing on purpose */ + break; + } + } + + /* loop over each attribute section separated by ';' */ + for (pair = strtok_r(escptr, ";", &lasts); pair; + pair = strtok_r(NULL, ";", &lasts)) { + char *escattr; + char *escvals; + char *refattr; + char *refvals; + char emptystr[1]; + + escattr = strtok_r(pair, " \t", &escvals); + + /* + * setup the ref pointers to the same locations as the esc + * ptrs + */ + /* + * null the reference string in the same spots as the esc + * string + */ + refattr = (refptr + (escattr - escptr)); + refattr[strlen(escattr)] = '\0'; + + if (escvals && *escvals) { + refvals = (refptr + (escvals - escptr)); + refvals[strlen(escvals)] = '\0'; + } else { + refvals = NULL; + emptystr[0] = '\0'; + } + + if (spec_match(spec_p, refattr)) { + if (refvals) + (*fun) (spec_p, refattr, refvals, calldatap); + else + (*fun) (spec_p, refattr, emptystr, calldatap); + } + } + +alldone: + if (refptr) + free(refptr); + if (escptr) + free(escptr); + +} /* end spec_attrtrav */ + + +/* + * spec_valtrav() - traverse an value list, calling the supplied function on + * each matching value. + */ + +void +spec_valtrav(spec_t * spec_p, + char *valstr, + spec_val_fun_t fun, + void *calldatap) +{ + char *s0; + char *s; + boolean_t intoken = B_FALSE; + boolean_t inquote = B_FALSE; + + /* return immeadiatly on null pointers */ + if (!valstr) + return; + + /* special case, match once on empty string */ + if (!*valstr) { + if (spec_match(spec_p, valstr)) + (*fun) (spec_p, valstr, calldatap); + return; + } + for (s = s0 = valstr; ; s++) { + switch (*s) { + case NULL: + if (intoken) { + if (spec_match(spec_p, s0)) + (*fun) (spec_p, s0, calldatap); + } + return; /* ALL DONE */ + + case '\'': + if (inquote) { + /* end a quoted string */ + inquote = B_FALSE; + intoken = B_FALSE; + *s = '\0'; + if (spec_match(spec_p, s0)) + (*fun) (spec_p, s0, calldatap); + /* next string starts past the quote */ + s0 = s + 1; + } else { + /* start a quoted string */ + inquote = B_TRUE; + intoken = B_TRUE; + s0 = s + 1; /* point past the quote */ + } + break; + + case ' ': + case '\t': + /* ignore whitespace in quoted strings */ + if (inquote) + break; + + if (intoken) { + /* whitespace ended this token */ + intoken = B_FALSE; + *s = '\0'; + if (spec_match(spec_p, s0)) + (*fun) (spec_p, s0, calldatap); + /* next string starts past the whitespace */ + s0 = s + 1; + } + break; + + default: + /* characters all OK inside quoted string */ + if (inquote) + break; + + if (!intoken) { + /* start of unquoted token */ + intoken = B_TRUE; + s0 = s; /* token starts here */ + } + break; + } + } + + +#ifdef TOOSIMPLE + char *v; + char *ls; + + /* + * #### MISSING - need to handle quoted value strings * containing + * whitespace. + */ + + for (v = strtok_r(valstr, " \t", &ls); v; + v = strtok_r(NULL, " \t", &ls)) { + if (spec_match(spec_p, v)) { + (*fun) (spec_p, v, calldatap); + } + } +#endif + +} /* end spec_valtrav */ diff --git a/usr/src/cmd/tnf/prex/spec.h b/usr/src/cmd/tnf/prex/spec.h new file mode 100644 index 0000000000..5972b351e1 --- /dev/null +++ b/usr/src/cmd/tnf/prex/spec.h @@ -0,0 +1,94 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#ifndef _SPEC_H +#define _SPEC_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Includes + */ + +#include <stdio.h> +#include <libgen.h> +#include <sys/types.h> + +#include "queue.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * Typedefs + */ + +typedef enum spec_type { + SPEC_EXACT, + SPEC_REGEXP + + +} spec_type_t; + + +typedef struct spec { + queue_node_t qn; + char *str; + spec_type_t type; + char *regexp_p; + +} spec_t; + +typedef void +(*spec_attr_fun_t) (spec_t * spec, char *attr, char *value, void *calldatap); +typedef void +(*spec_val_fun_t) (spec_t * spec, char *value, void *calldatap); + + +/* + * Globals + */ + + +/* + * Declarations + */ + +spec_t * spec(char *str_p, spec_type_t type); +void spec_destroy(spec_t * list_p); +void spec_print(FILE * stream, spec_t * list_p); +spec_t * spec_list(spec_t * list_p, spec_t * item_p); +void spec_attrtrav(spec_t * spec_p, char *attrs, + spec_attr_fun_t fun, void *calldata_p); +void spec_valtrav(spec_t * spec_p, char *valstr, + spec_val_fun_t fun, void *calldata_p); +spec_t *spec_dup(spec_t * spec_p); + +#ifdef __cplusplus +} +#endif + +#endif /* _SPEC_H */ diff --git a/usr/src/cmd/tnf/prex/util.c b/usr/src/cmd/tnf/prex/util.c new file mode 100644 index 0000000000..bcb0fa8021 --- /dev/null +++ b/usr/src/cmd/tnf/prex/util.c @@ -0,0 +1,56 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <libintl.h> + +void +err_fatal(char *s, ...) +{ + va_list ap; + + va_start(ap, s); + (void) vfprintf(stderr, s, ap); + (void) fprintf(stderr, gettext("\n")); + va_end(ap); + exit(1); +} + +#if 0 +void +err_warning(char *s, ...) +{ + va_list ap; + + va_start(ap, s); + (void) vfprintf(stderr, s, ap); + (void) fprintf(stderr, gettext("\n")); + va_end(ap); +} +#endif diff --git a/usr/src/cmd/tnf/tnfdump/Makefile b/usr/src/cmd/tnf/tnfdump/Makefile new file mode 100644 index 0000000000..a4fb64a00c --- /dev/null +++ b/usr/src/cmd/tnf/tnfdump/Makefile @@ -0,0 +1,66 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright (c) 1989 by Sun Microsystems, Inc. +# +# cmd/tnf/tnfdump/Makefile +# + +PROG= tnfdump + +OBJS.c= main.o cooked.o table.o +OBJS= $(OBJS.c) + +SRCS= $(OBJS.c:%.o=%.c) + +include ../../Makefile.cmd + +POFILES= $(OBJS.c:%.o=%.po) + +CPPFLAGS += -I../../../lib/libtnfprobe +LDLIBS += -ltnf + +.KEEP_STATE: + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +install: all $(ROOTBIN) $(ROOTPROG) + +$(ROOTBIN): + $(INS.dir) + +$(POFILE): $(POFILES) + $(RM) $@ + cat $(POFILES) > $@ + +clean: + $(RM) $(OBJS) + +lint: $(SRCS) lint_SRCS + +include ../../Makefile.targ diff --git a/usr/src/cmd/tnf/tnfdump/cooked.c b/usr/src/cmd/tnf/tnfdump/cooked.c new file mode 100644 index 0000000000..ff63d27ba4 --- /dev/null +++ b/usr/src/cmd/tnf/tnfdump/cooked.c @@ -0,0 +1,337 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <tnf/tnf.h> +#include <errno.h> +#include <libintl.h> + +#include "state.h" + +#define STREQ(s1, s2, n) (strncmp(s1, s2, n) == 0) + +#define IS_64BIT(kind) ((1 << kind) & \ + ((1 << TNF_K_UINT64) | (1 << TNF_K_INT64))) + +#define PROBE_TYPE "tnf_probe_type" + +static void print_event (entry_t *ent); +static void insert_event (tnf_datum_t, tnf_datum_t); +static void describe_c_brief (tnf_datum_t); +static void describe_target (tnf_datum_t); +static void describe_c_struct (tnf_datum_t); +static void describe_probe_type (tnf_datum_t); +static void describe_event (tnf_datum_t, tnf_datum_t, hrtime_t); + +static hrtime_t base_time = 0; + +void +print_c_header(void) +{ + (void) printf("%16s %16s %5s %5s %10s %3s %-25s %s\n", + "----------------", "----------------", "-----", "-----", + "----------", "---", "-------------------------", + "------------------------"); + (void) printf("%16s %16s %5s %5s %10s %3s %-25s %s\n", + "Elapsed (ms)", "Delta (ms)", "PID", "LWPID", + " TID ", "CPU", "Probe Name", "Data / Description . . ."); + (void) printf("%16s %16s %5s %5s %10s %3s %-25s %s\n", + "----------------", "----------------", "-----", "-----", + "----------", "---", "-------------------------", + "------------------------"); +} + +static void +print_event(entry_t *ent) +{ + tnf_datum_t evt, sched; + hrtime_t normalized_time; + + evt = ent->record; + sched = tnf_get_tag_arg(evt); + if (sched == TNF_DATUM_NULL) { + /* + * should never happen because it had a schedule + * record earlier + */ + fail(0, gettext("event without a schedule record")); + } + normalized_time = ent->time - base_time; + describe_event(evt, sched, normalized_time); +} + +void +print_sorted_events(void) +{ + entry_t *ent; + + table_sort(); + ent = table_get_entry_indexed(0); + if (ent) { + base_time = ent->time; + } + table_print(&print_event); +} + +void +describe_c_record(tnf_datum_t datum) +{ + char *name_str; + tnf_datum_t schedule_rec; + + switch (tnf_get_kind(datum)) { + + case TNF_K_STRUCT: + /* print only event records */ + schedule_rec = tnf_get_tag_arg(datum); + if (schedule_rec != TNF_DATUM_NULL) { + /* event record */ + insert_event(datum, schedule_rec); + } + break; + case TNF_K_STRING: + case TNF_K_ARRAY: + /* Skip arrays at top level */ + break; + case TNF_K_TYPE: + name_str = tnf_get_type_name(datum); + /* REMIND: filter based on property */ + if (STREQ(name_str, PROBE_TYPE, strlen(name_str))) + describe_probe_type(datum); + break; + default: + fail(0, gettext("illegal record at %x (%d)"), + tnf_get_raw(datum), tnf_get_kind(datum)); + break; + } + +} + +static void +describe_probe_type(tnf_datum_t datum) +{ + unsigned n, i; + char *slotname; + size_t slot_len; + + n = tnf_get_slot_count(datum); +#if 0 + /* print the OUTPUT PAD */ + (void) printf("%16s %14s %5s %5s %8s %3s %-25s", + "-", "-", "-", "-", "-", "-", "-"); +#endif + (void) printf("probe\t"); + for (i = 0; i < n; i++) { + slotname = tnf_get_slot_name(datum, i); + slot_len = strlen(slotname); + + /* print all fields except ... */ + if ((!STREQ(slotname, TNF_N_TAG, slot_len)) && + (!STREQ(slotname, TNF_N_PROPERTIES, slot_len)) && + (!STREQ(slotname, TNF_N_SLOT_TYPES, slot_len)) && + (!STREQ(slotname, TNF_N_TYPE_SIZE, slot_len)) && + (!STREQ(slotname, TNF_N_SLOT_NAMES, slot_len))) { + (void) printf(" "); + (void) printf("%s: ", slotname); + describe_c_brief(tnf_get_slot_indexed(datum, + i)); + } + } + (void) printf("\n"); +} + +static void +insert_event(tnf_datum_t datum, tnf_datum_t schedule_rec) +{ + tnf_datum_t temp; + hrtime_t evt_time; + unsigned time_delta = 0; + entry_t element; + + temp = tnf_get_slot_named(schedule_rec, TNF_N_TIME_BASE); + evt_time = tnf_get_int64(temp); + temp = tnf_get_slot_named(datum, TNF_N_TIME_DELTA); + time_delta = (unsigned) tnf_get_int32(temp); + evt_time = evt_time + time_delta; + + element.time = evt_time; + element.record = datum; + table_insert(&element); +} + +#define K_TID "tnf_kthread_id" +#define CPUID "cpuid" + +static void +describe_event(tnf_datum_t datum, tnf_datum_t schedule_rec, hrtime_t evt_time) +{ + unsigned n, i; + char *slotname, *eventname, *tidtype; + tnf_datum_t temp; + int lwpid = 0, pid = 0; + int start_slots = 0; + static hrtime_t last_time = 0; + unsigned long long tid = 0; + + temp = tnf_get_slot_named(schedule_rec, TNF_N_TID); + if (IS_64BIT(tnf_get_kind(temp))) { + tid = tnf_get_int64(temp); + } else { + tid = (unsigned int)tnf_get_int32(temp); + } + tidtype = tnf_get_type_name(temp); + + temp = tnf_get_slot_named(schedule_rec, TNF_N_LWPID); + lwpid = tnf_get_int32(temp); + temp = tnf_get_slot_named(schedule_rec, TNF_N_PID); + pid = tnf_get_int32(temp); + + /* XXX should use TNF_N_KERNEL_SCHEDULE, TNF_N_USER_SCHEDULE */ + if (strcmp(tidtype, K_TID) == 0) { + int cpuid; + /* XXX Assumes cpuid always exists in kernel schedule */ + cpuid = tnf_get_int32(tnf_get_slot_named(schedule_rec, CPUID)); + /* print the OUTPUT schedule record for Kernel case */ + (void) printf("%16.6f %16.6f %5u %5u 0x%-8llx %3d", + evt_time / 1000000.0, + (evt_time - last_time)/1000000.0, + pid, lwpid, tid, cpuid); + } else { + /* print the OUTPUT schedule record */ + (void) printf("%16.6f %16.6f %5u %5u %10llu %3s", + evt_time / 1000000.0, + (evt_time - last_time)/1000000.0, + pid, lwpid, tid, "-"); + } + /* print the tag */ + eventname = tnf_type_get_name(tnf_get_slot_named(datum, TNF_N_TAG)); + (void) printf(" %-25s", eventname); + + /* heuristic - start of data is after TIME_DELTA field */ + start_slots = tnf_get_slot_index(datum, TNF_N_TIME_DELTA); + start_slots++; + + n = tnf_get_slot_count(datum); + + /* print the rest of the fields */ + for (i = start_slots; i < n; i++) { + (void) printf(" "); + slotname = tnf_get_slot_name(datum, i); + (void) printf("%s: ", slotname); + describe_target(tnf_get_slot_indexed(datum, i)); + } + (void) printf("\n"); + last_time = evt_time; +} + +static void +describe_c_struct(tnf_datum_t datum) +{ + unsigned n, i, tag_index; + char *slotname; + + n = tnf_get_slot_count(datum); + + /* print the tag */ + (void) printf(" "); + (void) printf("%s: ", "type"); + describe_c_brief(tnf_get_slot_named(datum, TNF_N_TAG)); + tag_index = tnf_get_slot_index(datum, TNF_N_TAG); + + for (i = 0; i < n; i++) { + /* print the rest of the members */ + if (i != tag_index) { + (void) printf(" "); + slotname = tnf_get_slot_name(datum, i); + (void) printf("%s: ", slotname); + describe_target(tnf_get_slot_indexed(datum, i)); + } + } +} + +static void +describe_c_brief(tnf_datum_t datum) +{ + if (datum == TNF_DATUM_NULL) /* allowed */ + (void) printf("0x%-8x <NULL>", 0); + + else if (tnf_is_scalar(datum)) + describe_scalar(datum); + + else if (tnf_is_record(datum)) { + + switch (tnf_get_kind(datum)) { + case TNF_K_TYPE: + (void) printf("%s", tnf_type_get_name(datum)); + break; + case TNF_K_STRING: + (void) printf("\"%s\"", tnf_get_chars(datum)); + break; + default: + (void) printf("<%s>", tnf_get_type_name(datum)); + } + } else + fail(0, gettext("inline aggregate slots/elements unhandled")); +} + +static void +describe_target(tnf_datum_t datum) +{ + if (datum == TNF_DATUM_NULL) /* allowed */ + (void) printf("0x%-8x <NULL>", 0); + + else if (tnf_is_scalar(datum)) + describe_scalar(datum); + + else if (tnf_is_record(datum)) { + + switch (tnf_get_kind(datum)) { + case TNF_K_STRUCT: + (void) printf("{"); + describe_c_struct(datum); + (void) printf(" }"); + break; + case TNF_K_TYPE: + (void) printf("%s", tnf_type_get_name(datum)); + break; + case TNF_K_STRING: + (void) printf("\"%s\"", tnf_get_chars(datum)); + break; + default: + (void) printf("<%s>", tnf_get_type_name(datum)); + } + } else + fail(0, gettext("inline aggregate slots/elements unhandled")); +} diff --git a/usr/src/cmd/tnf/tnfdump/main.c b/usr/src/cmd/tnf/tnfdump/main.c new file mode 100644 index 0000000000..4c3d76ad7c --- /dev/null +++ b/usr/src/cmd/tnf/tnfdump/main.c @@ -0,0 +1,406 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <unistd.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include <tnf/tnf.h> +#include <errno.h> +#include <stdlib.h> +#include <libintl.h> +#include <locale.h> + +#include "state.h" + +static caddr_t g_file_base; /* base address of file */ +static char *g_cmdname; /* name of this command */ +static int g_raw = B_FALSE; /* output format */ +static int g_status = EXIT_SUCCESS; /* exit status (from stdlib.h) */ +static const char *print_unsigned = "%u"; +static const char *print_unsigned64 = "%llu"; + +#define OFF(p) (p - g_file_base) + +static void describe_array (tnf_datum_t); +static void describe_brief (tnf_datum_t); +static void describe_record (tnf_datum_t); +static void describe_struct (tnf_datum_t); +static void describe_type (tnf_datum_t); +static void read_tnf_file (int, char *); +static void usage (void); +static void scanargs (int, char **, int *, char ***); + +int +main(int ac, char *av[]) +{ + int numfiles; /* number of files to be printed */ + char **filenames; /* start of file names list */ + int i; + + /* internationalization stuff */ + (void) setlocale(LC_ALL, ""); +#if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ +#define TEXT_DOMAIN "SYS_TEST" /* Use this only if it weren't */ +#endif + (void) textdomain(TEXT_DOMAIN); + + g_cmdname = av[0]; + scanargs(ac, av, &numfiles, &filenames); + for (i = 0; i < numfiles; i++) { + read_tnf_file(g_raw, filenames[i]); + } + + if (!g_raw) { + if (table_get_num_elements() > 0) { + print_c_header(); + print_sorted_events(); + } + } + + exit(g_status); + + return (0); +} + +static void +scanargs(int argc, char **argv, int *nfiles, char ***files) +{ + int c; + int errflg = B_FALSE; + char *optstr = "rx"; + + while ((c = getopt(argc, argv, optstr)) != EOF) { + switch (c) { + case 'r': + g_raw = B_TRUE; + break; + case 'x': + print_unsigned = "0x%x"; + print_unsigned64 = "0x%llx"; + break; + case '?': + errflg = B_TRUE; + break; + } + } + *files = &argv[optind]; + *nfiles = argc - optind; + if (*nfiles <= 0) { + errflg = B_TRUE; + } + if (errflg) { + usage(); + } +} + + +static void +read_tnf_file(int raw, char *path) +{ + int fd; + struct stat st; + caddr_t p, curr_p, end_p; + TNF *tnf; + tnf_errcode_t err; + tnf_datum_t record; + void (*desc_func)(tnf_datum_t) = describe_c_record; + + if ((fd = open(path, O_RDONLY, 0777)) == -1) { + (void) fprintf(stderr, gettext("%s: cannot open %s\n"), + g_cmdname, path); + g_status = EXIT_FAILURE; + return; + } + if (fstat(fd, &st) != 0) { + (void) fprintf(stderr, gettext("%s: fstat error on %s\n"), + g_cmdname, path); + (void) close(fd); + g_status = EXIT_FAILURE; + return; + } + if (st.st_size == 0) { + (void) fprintf(stderr, gettext("%s: %s is empty\n"), + g_cmdname, path); + (void) close(fd); + g_status = EXIT_FAILURE; + return; + } + if ((p = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0)) + == (caddr_t)-1) { + (void) fprintf(stderr, gettext("%s: mmap error on %s\n"), + g_cmdname, path); + (void) close(fd); + g_status = EXIT_FAILURE; + return; + } + + if (raw) + g_file_base = p; /* for OFF() */ + + if (*p == 0) { + /* + * magic word is 0 - catch the error if entire file is zero. + * tnf_reader_begin() will catch the "not a TNF file" error. + */ + curr_p = p; + end_p = p + st.st_size; + while ((curr_p < end_p) && (*curr_p == 0)) + curr_p++; + if (curr_p == end_p) { + (void) fprintf(stderr, + gettext("%s: %s is an empty TNF file\n"), + g_cmdname, path); + (void) munmap(p, st.st_size); + (void) close(fd); + return; + } + } + + if ((err = tnf_reader_begin(p, st.st_size, &tnf)) != TNF_ERR_NONE) { + (void) fprintf(stderr, gettext("%s: error in %s: %s\n"), + g_cmdname, path, tnf_error_message(err)); + (void) munmap(p, st.st_size); + (void) close(fd); + g_status = EXIT_FAILURE; + return; + } + + /* Describe file header */ + record = tnf_get_file_header(tnf); + if (raw) { + describe_record(record); + desc_func = describe_record; + } + + /* Describe all other records */ + while ((record = tnf_get_next_record(record)) != TNF_DATUM_NULL) + desc_func(record); + + /* Don't munmap for cooked output because we access records later */ + if (raw) + (void) munmap(p, st.st_size); + (void) close(fd); +} + +static void +describe_record(tnf_datum_t datum) +{ + (void) printf("0x%-8x: {\n", OFF(tnf_get_raw(datum))); + + switch (tnf_get_kind(datum)) { + + case TNF_K_STRUCT: + describe_struct(datum); + break; + case TNF_K_STRING: + case TNF_K_ARRAY: + describe_array(datum); + break; + case TNF_K_TYPE: + describe_type(datum); + break; + default: + fail(0, gettext("illegal record at %x (%d)"), + tnf_get_raw(datum), tnf_get_kind(datum)); + break; + } + + (void) printf("\t}\n"); +} + +void +describe_scalar(tnf_datum_t datum) +{ + switch (tnf_get_kind(datum)) { + + case TNF_K_CHAR: + (void) printf("%c", tnf_get_char(datum)); + break; + case TNF_K_INT8: + (void) printf("%d", tnf_get_int8(datum)); + break; + case TNF_K_UINT8: + (void) printf(print_unsigned, (tnf_uint8_t)tnf_get_int8(datum)); + break; + case TNF_K_INT16: + (void) printf("%d", tnf_get_int16(datum)); + break; + case TNF_K_UINT16: + (void) printf(print_unsigned, + (tnf_uint16_t)tnf_get_int16(datum)); + break; + case TNF_K_INT32: + (void) printf("%d", (int)tnf_get_int32(datum)); + break; + case TNF_K_UINT32: + if ((tnf_type_get_property(tnf_get_type(datum), TNF_N_OPAQUE)) + != TNF_DATUM_NULL) { + /* XXX */ + (void) printf("0x%x", + (tnf_uint32_t)tnf_get_int32(datum)); + } else { + (void) printf(print_unsigned, + (tnf_uint32_t)tnf_get_int32(datum)); + } + break; + case TNF_K_INT64: + /* lint not updated, it complains: malformed format string */ + (void) printf("%lld", tnf_get_int64(datum)); + break; + case TNF_K_UINT64: + if ((tnf_type_get_property(tnf_get_type(datum), TNF_N_OPAQUE)) + != TNF_DATUM_NULL) { + (void) printf("0x%llx", + (tnf_uint64_t)tnf_get_int64(datum)); + } else { + /* lint not updated, it complains: malformed format string */ + (void) printf(print_unsigned64, + (tnf_uint64_t)tnf_get_int64(datum)); + } + break; + case TNF_K_FLOAT32: + (void) printf("%f", tnf_get_float32(datum)); + break; + case TNF_K_FLOAT64: + (void) printf("%f", tnf_get_float64(datum)); + break; + case TNF_K_SCALAR: + (void) printf("unhandled scalar"); + break; + default: + fail(0, gettext("not a scalar")); + break; + } +} + +static void +describe_struct(tnf_datum_t datum) +{ + unsigned n, i; + char *slotname; + + n = tnf_get_slot_count(datum); + for (i = 0; i < n; i++) { + slotname = tnf_get_slot_name(datum, i); + (void) printf("%24s ", slotname); + describe_brief(tnf_get_slot_indexed(datum, i)); + (void) printf("\n"); + /* tag_arg heuristic */ + if ((i == 0) && tnf_is_record(datum)) { + tnf_datum_t tag_arg; + + if ((tag_arg = tnf_get_tag_arg(datum)) + != TNF_DATUM_NULL) { + (void) printf("%24s ", TNF_N_TAG_ARG); + describe_brief(tag_arg); + (void) printf("\n"); + } + } + } +} + +static void +describe_array(tnf_datum_t datum) +{ + unsigned n, i; + + describe_struct(datum); /* XXX */ + + if (tnf_is_string(datum)) + (void) printf("%24s \"%s\"\n", "chars", tnf_get_chars(datum)); + else { + n = tnf_get_element_count(datum); + for (i = 0; i < n; i++) { + (void) printf("%24d ", i); + describe_brief(tnf_get_element(datum, i)); + (void) printf("\n"); + } + } +} + +static void +describe_type(tnf_datum_t datum) +{ + describe_struct(datum); +} + +static void +describe_brief(tnf_datum_t datum) +{ + if (datum == TNF_DATUM_NULL) /* allowed */ + (void) printf("0x%-8x <NULL>", 0); + + else if (tnf_is_scalar(datum)) + describe_scalar(datum); + + else if (tnf_is_record(datum)) { + + (void) printf("0x%-8x ", + OFF(tnf_get_raw(datum))); /* common */ + + switch (tnf_get_kind(datum)) { + case TNF_K_TYPE: + (void) printf("%s", tnf_type_get_name(datum)); + break; + case TNF_K_STRING: + (void) printf("\"%s\"", tnf_get_chars(datum)); + break; + default: + (void) printf("<%s>", tnf_get_type_name(datum)); + } + } else + fail(0, gettext("inline aggregate slots/elements unhandled")); +} + +void +fail(int do_perror, char *message, ...) +{ + va_list args; + + va_start(args, message); + (void) fprintf(stderr, gettext("%s: "), g_cmdname); + (void) vfprintf(stderr, message, args); + va_end(args); + if (do_perror) + (void) fprintf(stderr, gettext(": %s"), strerror(errno)); + (void) fprintf(stderr, gettext("\n")); + exit(EXIT_FAILURE); +} + +static void +usage(void) +{ + (void) fprintf(stderr, + gettext("Usage: %s [-r] <tnf_file> [<tnf_file> ...]\n"), + g_cmdname); + exit(EXIT_FAILURE); +} diff --git a/usr/src/cmd/tnf/tnfdump/state.h b/usr/src/cmd/tnf/tnfdump/state.h new file mode 100644 index 0000000000..3b6a51af8d --- /dev/null +++ b/usr/src/cmd/tnf/tnfdump/state.h @@ -0,0 +1,67 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#ifndef _STATE_H +#define _STATE_H + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <sys/time.h> +#include <tnf/tnf.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * data structures for table of record pointers: + * time: timestamp of record + * record: handle on record + */ +typedef struct entry { + hrtime_t time; + tnf_datum_t record; +} entry_t; + +void print_c_header (void); +void describe_c_record (tnf_datum_t); +void print_sorted_events(void); + +void describe_scalar (tnf_datum_t); + +void fail (int, char *, ...); + +/* routines for manipulating table of records */ +void table_insert (entry_t *); +void table_sort (void); +void table_print (void (*print_elem)(entry_t *)); +int table_get_num_elements(void); +entry_t *table_get_entry_indexed(int); + +#ifdef __cplusplus +} +#endif + +#endif /* _STATE_H */ diff --git a/usr/src/cmd/tnf/tnfdump/table.c b/usr/src/cmd/tnf/tnfdump/table.c new file mode 100644 index 0000000000..581b2b7dd3 --- /dev/null +++ b/usr/src/cmd/tnf/tnfdump/table.c @@ -0,0 +1,122 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 (c) 1994, by Sun Microsytems, Inc. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <stdlib.h> +#include <locale.h> + +#include "state.h" + +/* + * This file defines routines on a table data structure. There is one + * static table that has the following operations: insert, sort, print, + * and get an element by index. + */ + +static entry_t *table_start = NULL; /* start of table */ +static int table_cur = 0; /* current size of table */ +static int table_size = 0; /* max number of elements */ + +#define GUESS_NUM_ELEM (16 * 1024) + +static void table_grow (int); +static int timecompare (const void *, const void *); + +static void +table_grow(int num_entries) +{ + entry_t *temp; + + if (table_start == NULL) { + table_size = num_entries; + table_start = malloc(table_size * sizeof (struct entry)); + if (table_start == NULL) + fail(1, gettext("malloc:")); + return; + } + table_size += num_entries; + temp = realloc(table_start, table_size * sizeof (struct entry)); + if (temp == NULL) + fail(1, gettext("realloc:")); + table_start = temp; +} + +static int +timecompare(const void *i, const void *j) +{ + hrtime_t result; + + result = ((entry_t *)i)->time - ((entry_t *)j)->time; + if (result < (longlong_t) 0) + return (-1); + else if (result == (longlong_t) 0) + return (0); + else + return (1); +} + +/* + * insert an entry into the table. Automatically grows it if needed + */ +void +table_insert(entry_t *element) +{ + if (table_cur >= table_size) { + table_grow(GUESS_NUM_ELEM); + } + /* copy the structure to the array, increment cur index */ + table_start[table_cur++] = *element; +} + +int +table_get_num_elements(void) +{ + return (table_size); +} + +void +table_sort(void) +{ + qsort(table_start, table_cur, sizeof (struct entry), &timecompare); +} + +void +table_print(void (*print_elem)(entry_t *)) +{ + int i; + + for (i = 0; i < table_cur; i++) { + print_elem(&(table_start[i])); + } +} + +entry_t * +table_get_entry_indexed(int n) +{ + if (n < table_cur) + return (&(table_start[n])); + return (NULL); +} diff --git a/usr/src/cmd/tnf/tnfxtract/Makefile b/usr/src/cmd/tnf/tnfxtract/Makefile new file mode 100644 index 0000000000..543ec63c9b --- /dev/null +++ b/usr/src/cmd/tnf/tnfxtract/Makefile @@ -0,0 +1,60 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +# ident "%Z%%M% %I% %E% SMI" +# +# Copyright 2003 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# cmd/tnf/tnfxtract/Makefile +# + +PROG= tnfxtract + +include ../../Makefile.cmd + +$(64ONLY)SUBDIRS = $(MACH) +$(BUILD64)SUBDIRS += $(MACH64) + +all := TARGET = all +install := TARGET = install +clean := TARGET = clean +clobber := TARGET = clobber +lint := TARGET = lint + +.KEEP_STATE: + +all: $(SUBDIRS) + +clean clobber lint: $(SUBDIRS) + +install: $(SUBDIRS) + -$(RM) $(ROOTPROG) + -$(LN) $(ISAEXEC) $(ROOTPROG) + + +$(SUBDIRS): FRC + @cd $@; pwd; $(MAKE) $(TARGET) + +FRC: + +include ../../Makefile.targ diff --git a/usr/src/cmd/tnf/tnfxtract/Makefile.com b/usr/src/cmd/tnf/tnfxtract/Makefile.com new file mode 100644 index 0000000000..747f5030f1 --- /dev/null +++ b/usr/src/cmd/tnf/tnfxtract/Makefile.com @@ -0,0 +1,58 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 1994, 2002 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +PROG= tnfxtract + +OBJS.c= tnfxtract.o + +OBJS= $(OBJS.c) + +SRCS= $(OBJS.c:%.o=../%.c) + +include ../../../Makefile.cmd + +LFLAGS= -v +LDLIBS += -lkvm + +.KEEP_STATE: + +all: $(PROG) + +$(PROG): $(OBJS) + $(LINK.c) $(OBJS) -o $@ $(LDLIBS) + $(POST_PROCESS) + +clean: + $(RM) $(OBJS) + +lint: lint_SRCS + +%.o: ../%.c + $(COMPILE.c) $< + +include ../../../Makefile.targ diff --git a/usr/src/cmd/tnf/tnfxtract/amd64/Makefile b/usr/src/cmd/tnf/tnfxtract/amd64/Makefile new file mode 100644 index 0000000000..074aa0891e --- /dev/null +++ b/usr/src/cmd/tnf/tnfxtract/amd64/Makefile @@ -0,0 +1,32 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 2004 Sun Microsystems, Inc. All rights reserved. +# Use is subject to license terms. +# +# ident "%Z%%M% %I% %E% SMI" +# + +include ../Makefile.com +include ../../../Makefile.cmd.64 + +install: all $(ROOTPROG64) diff --git a/usr/src/cmd/tnf/tnfxtract/i386/Makefile b/usr/src/cmd/tnf/tnfxtract/i386/Makefile new file mode 100644 index 0000000000..84309e70a3 --- /dev/null +++ b/usr/src/cmd/tnf/tnfxtract/i386/Makefile @@ -0,0 +1,32 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright (c) 1997, by Sun Microsystems, Inc. +# All rights reserved. +# +# cmd/tnf/tnfxtract/sparc/Makefile + +include ../Makefile.com + +install: all $(ROOTPROG32) diff --git a/usr/src/cmd/tnf/tnfxtract/sparcv9/Makefile b/usr/src/cmd/tnf/tnfxtract/sparcv9/Makefile new file mode 100644 index 0000000000..f77e6f4858 --- /dev/null +++ b/usr/src/cmd/tnf/tnfxtract/sparcv9/Makefile @@ -0,0 +1,33 @@ +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License, Version 1.0 only +# (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 +# +# +#ident "%Z%%M% %I% %E% SMI" +# +# Copyright (c) 1997, by Sun Microsystems, Inc. +# All rights reserved. +# +# cmd/tnf/tnfxtract/sparcv9/Makefile + +include ../Makefile.com +include ../../../Makefile.cmd.64 + +install: all $(ROOTPROG64) diff --git a/usr/src/cmd/tnf/tnfxtract/tnfxtract.c b/usr/src/cmd/tnf/tnfxtract/tnfxtract.c new file mode 100644 index 0000000000..5e48d6094d --- /dev/null +++ b/usr/src/cmd/tnf/tnfxtract/tnfxtract.c @@ -0,0 +1,423 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (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 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> +#include <libintl.h> +#include <unistd.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <kvm.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/tnf.h> +#include <sys/tnf_com.h> +#include <nlist.h> +#include <errno.h> + +#define TNFDEV "/dev/tnfmap" + +#define MAXFWTRY 5 + +typedef struct { + boolean_t ever_read; + tnf_uint32_t generation; + tnf_uint16_t bytes_valid; +} BLOCK_STATUS; + +static char *dumpfile; /* Dump file if extracting from crashdump */ +static char *namelist; /* Symbol tbl. if extracting from crashdump */ +static kvm_t *kvm_p; /* Handle for kvm_open, kvm_read, ... */ + +static struct nlist kvm_syms[] = { + { "tnf_buf" }, + { "tnf_trace_file_size" }, + { NULL } +}; + +static uintptr_t dump_bufaddr; +static size_t tnf_bufsize; +static char *program_name; +static int input_fd; +static int output_fd; +static tnf_file_header_t *tnf_header; + +/* + * usage() - gives a description of the arguments, and exits + */ + +static void +usage(char *argv[], const char *msg) +{ + if (msg) + (void) fprintf(stderr, + gettext("%s: %s\n"), argv[0], msg); + + (void) fprintf(stderr, gettext( + "usage: %s [-d <dumpfile> -n <symbolfile> ] " + "<output-filename>\n"), argv[0]); + exit(1); +} /* end usage */ + + +/* + * Write 'size' bytes at offset 'offset' from 'addr' + * to the output file. Bail out unceremoniously if anything goes wrong. + */ +static void +writeout(char *addr, int offset, int size) + +{ + if (lseek(output_fd, offset, SEEK_SET) < 0) { + perror("lseek"); + exit(1); + } + if (write(output_fd, addr, size) != size) { + perror("write"); + exit(1); + } +} + + +static void +dumpfile_init() + +{ + kvm_p = kvm_open(namelist, dumpfile, NULL, O_RDONLY, program_name); + if (kvm_p == NULL) { + /* kvm_open prints an error message */ + exit(1); + } + if (kvm_nlist(kvm_p, kvm_syms) != 0) { + (void) fprintf(stderr, gettext( + "Symbol lookup error in %s\n"), namelist); + exit(1); + } + if (kvm_read(kvm_p, kvm_syms[0].n_value, (char *) &dump_bufaddr, + sizeof (dump_bufaddr)) != sizeof (dump_bufaddr) || + kvm_read(kvm_p, kvm_syms[1].n_value, (char *) &tnf_bufsize, + sizeof (tnf_bufsize)) != sizeof (tnf_bufsize)) { + (void) fprintf(stderr, gettext( + "kvm_read error in %s\n"), dumpfile); + exit(1); + } + if (dump_bufaddr == NULL || tnf_bufsize == 0) { + (void) fprintf(stderr, gettext( + "No trace data available in the kernel.\n")); + exit(1); + } +} + +static void +live_kernel_init() + +{ + tifiocstate_t tstate; + + if ((input_fd = open(TNFDEV, O_RDWR)) < 0) { + perror(TNFDEV); + exit(1); + } + if (ioctl(input_fd, TIFIOCGSTATE, &tstate) < 0) { + perror(gettext("Error getting trace system state")); + exit(1); + } + if (tstate.buffer_state != TIFIOCBUF_OK) { + (void) fprintf(stderr, gettext( + "No trace data available in the kernel.\n")); + exit(1); + } + tnf_bufsize = tstate.buffer_size; +} + +static void +read_tnf_header(char *addr) + +{ + if (dumpfile != NULL) { + if (kvm_read(kvm_p, dump_bufaddr, addr, 512) != 512) { + (void) fprintf(stderr, gettext( + "Error reading tnf header from dump file.\n")); + exit(1); + } + } else { + if (ioctl(input_fd, TIFIOCGHEADER, addr) != 0) { + perror(gettext("Error reading tnf header from kernel")); + exit(1); + } + } +} + +static int +read_tnf_block(tnf_block_header_t *addr, int block_num) + +{ + int offset; + tifiocgblock_t ioctl_arg; + + if (dumpfile != NULL) { + offset = tnf_header->directory_size + + block_num * tnf_header->block_size; + if (kvm_read(kvm_p, dump_bufaddr + offset, (char *) addr, + tnf_header->block_size) != tnf_header->block_size) { + (void) fprintf(stderr, gettext( + "Error reading tnf block.\n")); + exit(1); + } + } else { + ioctl_arg.dst_addr = (char *) addr; + ioctl_arg.block_num = block_num; + if (ioctl(input_fd, TIFIOCGBLOCK, &ioctl_arg) < 0) { + if (errno == EBUSY) + return (EBUSY); + perror(gettext("Error reading tnf block")); + exit(1); + } + } + return (0); +} + +static void +read_tnf_fwzone(tnf_ref32_t *dest, int start, int slots) + +{ + int offset; + int len; + tifiocgfw_t ioctl_arg; + + if (dumpfile != NULL) { + /* LINTED assignment of 64-bit integer to 32-bit integer */ + offset = tnf_header->block_size + start * sizeof (tnf_ref32_t); + /* LINTED assignment of 64-bit integer to 32-bit integer */ + len = slots * sizeof (tnf_ref32_t); + if (kvm_read(kvm_p, dump_bufaddr + offset, (char *) dest, + len) != len) { + (void) fprintf(stderr, gettext( + "Error reading tnf forwarding zone.\n")); + exit(1); + } + } else { + /* LINTED pointer cast may result in improper alignment */ + ioctl_arg.dst_addr = (long *) dest; + ioctl_arg.start = start; + ioctl_arg.slots = slots; + if (ioctl(input_fd, TIFIOCGFWZONE, &ioctl_arg) < 0) { + perror(gettext("Error reading tnf block")); + exit(1); + } + } +} + +int +main(int argc, char *argv[]) +{ + const char *optstr = "d:n:"; + const char *outfile; + char *local_buf; + int c; + tnf_uint32_t *magicp; + tnf_block_header_t *block_base, *blockp; + BLOCK_STATUS *block_stat, *bsp; + int block_num; + boolean_t any_unread, any_different, retry; + tnf_ref32_t *fwzone; + int fwzonesize; + int i; + int fwtries; + int block_count; + + program_name = argv[0]; + while ((c = getopt(argc, argv, optstr)) != EOF) { + switch (c) { + case 'd': + dumpfile = optarg; + break; + case 'n': + namelist = optarg; + break; + case '?': + usage(argv, gettext("unrecognized argument")); + } + } + if (optind != argc - 1) { + usage(argv, gettext("too many or too few arguments")); + } else { + outfile = argv[optind]; + } + if ((dumpfile != NULL) ^ (namelist != NULL)) { + usage(argv, gettext("must specify both or neither of the " + "-d and -n options")); + } + + output_fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (output_fd < 0) { + perror(outfile); + exit(1); + } + if (dumpfile != NULL) + dumpfile_init(); + else + live_kernel_init(); + + if ((local_buf = malloc(tnf_bufsize)) == NULL) { + (void) fprintf(stderr, + gettext("tnfxtract memory allocation failure\n")); + exit(1); + } + + /* Read header, get block size, check for version mismatch */ + read_tnf_header(local_buf); + /*LINTED pointer cast may result in improper alignment*/ + magicp = (tnf_uint32_t *) local_buf; + /*LINTED pointer cast may result in improper alignment*/ + tnf_header = (tnf_file_header_t *)(local_buf + sizeof (*magicp)); + if (*magicp != TNF_MAGIC) { + (void) fprintf(stderr, gettext( + "Buffer is not in TNF format.\n")); + exit(1); + } + if (tnf_header->file_version != TNF_FILE_VERSION) { + (void) fprintf(stderr, + gettext("Version mismatch (tnfxtract: %d; buffer: %d)\n"), + TNF_FILE_VERSION, tnf_header->file_version); + exit(1); + } + writeout(local_buf, 0, tnf_header->block_size); + /* LINTED pointer cast may result in improper alignment */ + block_base = (tnf_block_header_t *) + (local_buf + tnf_header->directory_size); + block_count = tnf_header->block_count - + tnf_header->directory_size / tnf_header->block_size; + fwzonesize = tnf_header->directory_size - tnf_header->block_size; + + block_stat = (BLOCK_STATUS *) + calloc(block_count, sizeof (BLOCK_STATUS)); + if (block_stat == NULL) { + (void) fprintf(stderr, + gettext("tnfxtract memory allocation failure\n")); + exit(1); + } + + for (bsp = block_stat; bsp != block_stat + block_count; ++bsp) + bsp->ever_read = B_FALSE; + /* + * Make repeated passes until we've read every non-tag block. + */ + do { + any_unread = B_FALSE; + bsp = block_stat; + block_num = 0; + blockp = block_base; + while (block_num != block_count) { + if (!bsp->ever_read) { + if (read_tnf_block(blockp, block_num) != 0) + any_unread = B_TRUE; + else { + bsp->ever_read = B_TRUE; + bsp->generation = blockp->generation; + bsp->bytes_valid = blockp->bytes_valid; + writeout((char *) blockp, + /* LINTED cast 64 to 32 bit */ + (int)((char *) blockp - local_buf), + tnf_header->block_size); + } + } + ++bsp; + ++block_num; + /* LINTED pointer cast may result in improper alignment */ + blockp = (tnf_block_header_t *) + ((char *) blockp + tnf_header->block_size); + } + } while (any_unread); + + /* + * Then read tag blocks only, until we have two consecutive, + * consistent reads. + */ + do { + any_different = B_FALSE; + bsp = block_stat; + block_num = 0; + blockp = block_base; + while (block_num != block_count) { + if (read_tnf_block(blockp, block_num) == 0 && + blockp->generation == TNF_TAG_GENERATION_NUM && + (bsp->generation != TNF_TAG_GENERATION_NUM || + bsp->bytes_valid != blockp->bytes_valid)) { + bsp->generation = TNF_TAG_GENERATION_NUM; + bsp->bytes_valid = blockp->bytes_valid; + writeout((char *) blockp, + /* LINTED cast 64bit to 32 bit */ + (int)((char *) blockp - local_buf), + tnf_header->block_size); + any_different = B_TRUE; + } + ++bsp; + ++block_num; + /* LINTED pointer cast may result in improper alignment */ + blockp = (tnf_block_header_t *) + ((char *) blockp + tnf_header->block_size); + } + } while (any_different); + + /* + * Then read the forwarding pointers. If any are -1: + * sleep briefly, then make another pass. + */ + /*LINTED pointer cast may result in improper alignment*/ + fwzone = (tnf_ref32_t *)(local_buf + tnf_header->block_size); + + read_tnf_fwzone(fwzone, 0, + /* LINTED cast from 64-bit integer to 32-bit integer */ + (int)(fwzonesize / sizeof (fwzone[0]))); + fwtries = 0; + while (fwtries != MAXFWTRY) { + retry = B_FALSE; + for (i = 0; i != fwzonesize / sizeof (fwzone[0]); ++i) { + if (fwzone[i] == -1) { + read_tnf_fwzone(&fwzone[i], i, 1); + if (!retry) { + retry = B_TRUE; + ++fwtries; + } + } + } + if (!retry) + break; + sleep(2); + } + if (fwtries == MAXFWTRY) { + (void) fprintf(stderr, gettext( + "Warning: forwarding pointers may " + "be invalid.\n")); + } + writeout((char *) fwzone, tnf_header->block_size, fwzonesize); + return (0); +} |
