Files
Arquivotheca.SunOS-4.1.3/sys/os/kern_trace.c
seta75D 2e8a93c394 Init
2021-10-11 18:20:23 -03:00

417 lines
8.8 KiB
C

/* @(#)kern_trace.c 1.1 92/07/30 SMI; from UCB 7.1 6/5/86 */
/*
* Copyright (c) 1982, 1986 Regents of the University of California.
* All rights reserved. The Berkeley software License Agreement
* specifies the terms and conditions for redistribution.
*/
/*
* Copyright (c) 1987 by Sun Microsystems, Inc.
*/
/*
* kern_trace.c
*
* Code to support the vtrace system call and the
* kernel-internal trace macro.
*/
#ifdef TRACE
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/user.h>
#include <sys/trace.h>
#include <sys/kernel.h>
#include <sys/errno.h>
#include "hrc.h"
#if NHRC == 0
/* No HRC device is configured */
/* This is because stubs.c is only for Object distributions */
#define hrc_time() 0
#define hrc_acquire(x) 0
#else
extern u_long hrc_time();
#endif NHRC == 0
/*
* Size, in entries, of tracebuffer. Forced into
* the data segment, so it can be patched.
*/
u_long tracetime();
void tracetime_reset();
void initialtraceevents();
int traceval; /* Temp storage for returned macro values */
/* (see, e.g., sys/vnode.h) */
u_int tracebufents = 3000;
fd_set tracedevents = { /* FD_ISSET(ev, &tracedevents) ==> tracing event ev */
0, 0, 0, 0, 0, 0, 0, 0
};
u_int eventstraced; /* cumulative number of events traced */
struct trace_rec *tracebuffer; /* a pointer to the trace buffer */
struct trace_rec *tracebufp; /* the next trace buffer entry to be filled */
/*
* Initialize the event tracing subsystem, allocating storage,
* and so on. Called at kernel startup time.
*/
void
inittrace()
{
register u_int entries = tracebufents;
/*
* If tracebufents is nonzero (having been patched to
* some specific value), then allocate that much space
* at startup time. This gives us a chance to get
* the space before the kernel memory pool has been
* fragmented from lots of allocation and deallocation.
*/
if (entries == 0)
return;
(void) realloctrace(entries);
}
/*
* Reallocate the trace buffer, first freeing its previous
* incarnation and then carving out the new area. Reset
* eventstraced to zero. Return 0 on success, errno value
* otherwise.
*
* We use kmem_alloc to obtain space. We use KMEM_NOSLEEP so that
* kmem_alloc fails if we ask for more than it can satisfy immediately,
* since the system is likely to hang otherwise.
*/
int
realloctrace(entries)
u_int entries;
{
/*
* Give back what we had.
*/
if (tracebuffer != NULL)
kmem_free((caddr_t)tracebuffer,
tracebufents * sizeof (struct trace_rec));
tracebuffer = NULL;
eventstraced = 0;
if (entries == 0)
return (0);
/*
* Grab what we need.
*/
tracebuffer = (struct trace_rec *)
new_kmem_alloc(entries * sizeof (struct trace_rec), KMEM_NOSLEEP);
if (tracebuffer == NULL)
return (ENOMEM);
tracebufents = entries;
resettracebuf();
return (0);
}
/*
* Reset the trace buffer, discarding all events in it and
* setting eventstraced to zero.
*/
void
resettracebuf()
{
register int s = splclock();
eventstraced = 0;
tracebufp = tracebuffer;
initialtraceevents();
(void) splx(s);
}
/*
* Called when tracing is reset to record one-time-only initial trace
* events.
*/
void
initialtraceevents()
{
initialtrace_page();
initialtrace_dnlc();
}
/*
* Record the event specified by the arguments, stashing away as well
* a timestamp and the currently active process. Assumes that ev has
* already been validity-checked.
*/
int
traceit(ev, datum0, datum1, datum2, datum3, datum4, datum5)
int ev;
u_long datum0, datum1, datum2, datum3, datum4, datum5;
{
extern struct proc *masterprocp;
register struct trace_rec *tbp;
register int s;
/*
* Sanity check.
*/
if (tracebuffer == NULL)
return (0);
s = splclock();
/*
* If there's a high-resolution timer available, it would
* be better to use it instead of the system time variable.
* HRC -- High Resolution Clock returns ticks which are
* .5 microseconds each.
* tracetime() is defined below, gives the HRC value if
* it is available, otherwise a timeval time value.
*/
tbp = tracebufp;
tbp->tr_time = tracetime();
tbp->tr_tag = ev;
tbp->tr_pid = masterprocp ? masterprocp->p_pid : 0;
tbp->tr_datum0 = datum0;
tbp->tr_datum1 = datum1;
tbp->tr_datum2 = datum2;
tbp->tr_datum3 = datum3;
tbp->tr_datum4 = datum4;
tbp->tr_datum5 = datum5;
if (++tracebufp >= tracebuffer + tracebufents)
tracebufp = tracebuffer;
eventstraced++;
(void) splx(s);
return (0);
}
/*
* Vtrace system call interface.
*
* XXX: Add request to (re)allocate the trace buffer.
*/
vtrace ()
{
register struct a {
int request;
int nbits_or_value;
fd_set *bits;
} *uap = (struct a *)u.u_ap;
fd_set bits;
register int ni, request;
register int s;
/*
* Super-user only...
*
* XXX: Liberalize?
*/
if (!suser()) {
u.u_error = EPERM;
return;
}
/*
* Do some preliminary checking and setup. Note that using
* fd_sets for this interface requires that all event tags
* be less than FD_SETSIZE in value.
*/
request = uap->request;
switch (request) {
case VTR_DISABLE:
case VTR_ENABLE:
case VTR_VALUE:
case VTR_EXEC:
if (uap->nbits_or_value > FD_SETSIZE)
uap->nbits_or_value = FD_SETSIZE;
ni = howmany(uap->nbits_or_value, NFDBITS);
break;
case VTR_STAMP:
case VTR_RESET:
break;
default:
u.u_error = EINVAL;
return;
}
/*
* Handle the request.
*/
switch (request) {
/*
* Enable or disable a set of trace events.
*/
case VTR_DISABLE:
tracetime_reset(); /* free up HRC device, if exists */
case VTR_ENABLE:
if (u.u_error = copyin((caddr_t)uap->bits, (caddr_t)&bits,
(u_int)(ni * sizeof (fd_mask))))
return;
/*
* Lock out new trace events while changing the
* set of enabled events.
*/
s = splclock();
while (--ni >= 0) {
if (request == VTR_DISABLE)
tracedevents.fds_bits[ni] &= ~bits.fds_bits[ni];
else
tracedevents.fds_bits[ni] |= bits.fds_bits[ni];
}
(void) splx(s);
break;
/*
* Report the currently enabled set of trace events.
*/
case VTR_VALUE:
u.u_error = copyout((caddr_t)&tracedevents, (caddr_t)uap->bits,
(u_int)(ni * sizeof (fd_mask)));
break;
/*
* Cause a TR_STAMP trace event (subject to its being enabled).
*/
case VTR_STAMP:
trace2(TR_STAMP, uap->nbits_or_value, u.u_procp->p_pid);
break;
/*
* Reset the trace buffer.
*/
case VTR_RESET:
resettracebuf();
tracetime_reset();
break;
case VTR_EXEC:
if (u.u_error = copyin((caddr_t)uap->bits, (caddr_t)&bits,
(u_int)(ni * sizeof (fd_mask))))
return;
vtrace_exec(&bits);
break;
}
}
int
vtrace_exec(bits)
fd_set *bits;
{
fd_set savebits;
int s, ni;
s = splclock();
savebits = tracedevents;
ni = howmany(FD_SETSIZE, NFDBITS);
/* Turn on the bits designated to enable the requested trace points */
while (--ni >= 0)
tracedevents.fds_bits[ni] |= bits->fds_bits[ni];
(void) splx(s);
/* Force the designated trace points to be executed */
#ifdef UFS
if (FD_ISSET(TR_UFS_INODE, bits))
trace_inode();
if (FD_ISSET(TR_UFS_INSTATS, bits))
trace_instats();
if (FD_ISSET(TR_UFS_INSTATS_RESET, bits))
trace_instats_reset();
#endif UFS
s = splclock();
tracedevents = savebits;
(void) splx(s);
}
/*
* tracetime()
* returns the highest resolution realtime clock value
* available. Presenty this is HRC, when it is installed and working.
* if HRC is not available, the system time value
* is scaled to milliseconds and returned.
* XXX Other Clocks may be used in the future for this routine.
* XXX This routine could be made more generally available
* for now, it is just for event tracing within the kernel
*/
#define BT_UNUSED 0
#define BT_HRC 1
#define BT_SYSTIME 2
/* Only intereted in Milliseconds of system time variable XXXX */
#define CEILING 2000000
#define SYSTIME_MSEC ((time.tv_sec % CEILING) * 1000 + time.tv_usec / 1000)
int tracetime_source = BT_UNUSED;
int tracetime_ticks_sec = 0;
u_long
tracetime()
{
register u_long result_ticks = 0;
/* XXX: should we provide unique timestamps, similar to uniqtime() */
switch (tracetime_source) {
case BT_HRC:
result_ticks = hrc_time();
break;
case BT_SYSTIME:
result_ticks = SYSTIME_MSEC;
break;
case BT_UNUSED:
if (hrc_acquire(1)) {
/* Captured the HRC access privilege */
tracetime_source = BT_HRC;
tracetime_ticks_sec = 2000000; /* 2 MHz ticks */
result_ticks = hrc_time();
} else {
tracetime_source = BT_SYSTIME;
tracetime_ticks_sec = 1000; /* msec */
result_ticks = SYSTIME_MSEC;
}
break;
default:
printf("Bad tracetime_source: %d", tracetime_source);
result_ticks = 0;
break;
}
return (result_ticks);
}
/*
* tracetime_reset()
* called to relinquish HRC access (if it had been obtained)
* allows later calls to tracetime() to acquire best clock device
*/
void
tracetime_reset()
{
if (tracetime_source == BT_HRC) {
hrc_acquire(0); /* release hrc */
}
tracetime_source = BT_UNUSED;
tracetime_ticks_sec = 0;
}
#endif TRACE