/*
* 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");
}