Source to osfmk/ddb/tr.c
/*
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* @OSF_COPYRIGHT@
*/
/*
* File: ddb/tr.c
* Authors: Alan Langerman, Jeffrey Heller
* Date: 1992
*
* Internal trace routines. Like old-style XPRs but
* less formatting.
*/
#include <ddb/tr.h>
#if TRACE_BUFFER
#include <string.h>
#include <ddb/db_command.h>
#include <mach_kdb.h>
#include <kern/lock.h>
#include <kern/spl.h>
extern void fc_get(int *);
/*
* Primitive event tracing facility for kernel debugging. Yes,
* this has some resemblance to XPRs. However, it is primarily
* intended for post-mortem analysis through ddb.
*/
#define TRACE_MAX (4 * 1024)
#define TRACE_WINDOW 40
typedef struct trace_event {
char *funcname;
char *file;
char *fmt;
#if NCPUS > 1
char cpu_number;
#endif /* NCPUS > 1 */
unsigned int lineno;
unsigned int tag1;
unsigned int tag2;
unsigned int tag3;
unsigned int tag4;
int indent;
int timestamp[2]; /* largest needed by any clock */
} trace_event;
trace_event trace_buffer[TRACE_MAX];
unsigned long trace_index;
#if NCPUS == 1
int tr_indent = 0;
#else /* NCPUS == 1 */
int tr_indent[NCPUS];
int tr_limit = -1;
#endif /* NCPUS == 1 */
decl_simple_lock_data(,trace_lock)
void
tr_init(void)
{
#if NCPUS > 1
int i;
for(i=0;i<NCPUS;i++)
tr_indent[i]=0;
#endif /* NCPUS > 1 */
simple_lock_init(&trace_lock, ETAP_DIPC_TRACE);
}
void
tr(
char *funcname,
char *file,
unsigned int lineno,
char *fmt,
unsigned int tag1,
unsigned int tag2,
unsigned int tag3,
unsigned int tag4)
{
int s;
register unsigned long ti, tn;
#if NCPUS > 1
char cpu;
#endif /* NCPUS > 1 */
#if PARAGON860
/*
* The following loop replaces the spl_and_lock sequence that
* would normally be here, as they are too heavy weight. The
* cmpsw (compare-and-swap) call returns -1 if unsuccessful.
*/
do {
ti = trace_index;
tn = ti + 1;
if (tn >= TRACE_MAX - 1)
tn = 0;
} while (cmpsw(ti, tn, &trace_index) == -1);
fc_get(trace_buffer[ti].timestamp);
#else /* PARAGON860 */
/*
* Until someone does a cmpsw for other platforms, do it
* the slow way
*/
s = splimp();
simple_lock(&trace_lock);
ti = trace_index++;
if (trace_index >= TRACE_MAX - 1)
trace_index = 0;
simple_unlock(&trace_lock);
splx(s);
fc_get(trace_buffer[ti].timestamp);
/* get_uniq_timestamp(trace_buffer[ti].timestamp);*/
#endif /* PARAGON860 */
trace_buffer[ti].funcname = funcname;
trace_buffer[ti].file = file;
trace_buffer[ti].lineno = lineno;
trace_buffer[ti].fmt = fmt;
trace_buffer[ti].tag1 = tag1;
trace_buffer[ti].tag2 = tag2;
trace_buffer[ti].tag3 = tag3;
trace_buffer[ti].tag4 = tag4;
#if NCPUS == 1
trace_buffer[ti].indent = tr_indent;
#else /* NCPUS == 1 */
mp_disable_preemption();
cpu = cpu_number();
trace_buffer[ti].indent = tr_indent[cpu];
trace_buffer[ti].cpu_number = cpu;
mp_enable_preemption();
#endif /* NCPUS == 1 */
}
#if MACH_KDB
#include <ddb/db_output.h>
/*
* Forward.
*/
void show_tr(
unsigned long index,
unsigned long range,
unsigned long show_extra);
int matches(
char *pattern,
char *target);
void parse_tr(
unsigned long index,
unsigned long range);
/*
* The blank array must be a bit bigger than
* MAX_BLANKS to leave room for a terminating NULL.
*/
#define MAX_BLANKS 16
char blanks[MAX_BLANKS+4];
void
show_tr(
unsigned long index,
unsigned long range,
unsigned long show_extra)
{
char *filename, *cp;
#if PARAGON860
trace_event *last_trace;
#endif /* PARAGON860 */
unsigned int level;
int old_history;
int i;
if (index == -1) {
index = trace_index - (TRACE_WINDOW-4);
range = TRACE_WINDOW;
} else if (index == 0) {
index = trace_index - (TRACE_WINDOW-4);
range = TRACE_WINDOW;
show_extra = 0;
}
if (index + range > TRACE_MAX)
range = TRACE_MAX - index;
#if PARAGON860
last_trace = &trace_buffer[index-1];
#endif /* PARAGON860 */
level = trace_buffer[index-1].indent;
/*
* Set up the indentation buffer
*/
memset(blanks, ' ', trace_buffer[index].indent);
blanks[trace_buffer[index].indent] = '\0';
for (i = index; i < index + range; ++i) {
#if NCPUS > 1
if ((tr_limit != -1) &&
(trace_buffer[i].cpu_number != tr_limit))
continue;
#endif /* NCPUS > 1 */
if (trace_buffer[i].file == (char *) 0 ||
trace_buffer[i].funcname == (char *) 0 ||
trace_buffer[i].lineno == 0 ||
trace_buffer[i].fmt == 0) {
db_printf("[%04x%s]\n", i,
i >= trace_index ? "*" : "");
continue;
}
old_history = (i >= trace_index);
/*
* Adjust the blank count if necessary
*/
if (level != trace_buffer[i].indent) {
level = trace_buffer[i].indent;
if (level >= MAX_BLANKS)
level = MAX_BLANKS;
memset(blanks, ' ', level);
blanks[level] = '\0';
}
for (cp = trace_buffer[i].file; *cp; ++cp)
if (*cp == '/')
filename = cp + 1;
#if NCPUS > 1
db_printf("{%02d}",trace_buffer[i].cpu_number);
#endif /* NCPUS > 1 */
db_printf("[%04x%s] %s%-16s", i, old_history ? "*" : "",
blanks, trace_buffer[i].funcname);
if (show_extra) {
if (show_extra > 0) {
db_printf(" (%x/%8x)",
trace_buffer[i].timestamp[0],
trace_buffer[i].timestamp[1]);
#if PARAGON860
/*
* For Paragon only, we compute and
* print out deltas on the timestamps
* accumulated in the tr buffer. One
* interesting case: it is meaningless
* to compute this delta for the last
* current entry in the log.
*/
if (old_history &&
((last_trace - trace_buffer)
< trace_index))
db_printf("(N/A)");
else
db_printf("(%d)",
timer_subtime(
trace_buffer[i].timestamp,
last_trace->timestamp));
#endif /*PARAGON860*/
db_printf(" ");
}
if (show_extra > 1) {
db_printf("(%s:%05d):\n\t",
filename, trace_buffer[i].lineno);
}
} else
db_printf(": ");
db_printf(trace_buffer[i].fmt, trace_buffer[i].tag1,
trace_buffer[i].tag2, trace_buffer[i].tag3,
trace_buffer[i].tag4);
db_printf("\n");
#if PARAGON860
last_trace = &trace_buffer[i];
#endif /* PARAGON860 */
}
}
int
matches(
char *pattern,
char *target)
{
char *cp, *cp1, *cp2;
for (cp = target; *cp; ++cp) {
for (cp2 = pattern, cp1 = cp; *cp2 && *cp1; ++cp2, ++cp1)
if (*cp2 != *cp1)
break;
if (!*cp2)
return 1;
}
return 0;
}
char parse_tr_buffer[100] = "KMSG";
void
parse_tr(
unsigned long index,
unsigned long range)
{
int i;
char *filename, *cp;
char *string = parse_tr_buffer;
if (index == 0) {
index = trace_index - (TRACE_WINDOW-4);
range = TRACE_WINDOW;
}
if (index + range > TRACE_MAX)
range = TRACE_MAX - index;
for (i = index; i < index + range; ++i) {
#if NCPUS > 1
if ((tr_limit != -1) &&
(trace_buffer[i].cpu_number != tr_limit))
continue;
#endif /* NCPUS > 1 */
if (trace_buffer[i].file == (char *) 0 ||
trace_buffer[i].funcname == (char *) 0 ||
trace_buffer[i].lineno == 0 ||
trace_buffer[i].fmt == 0) {
db_printf("[%04x%s]\n", i,
i >= trace_index ? "*" : "");
continue;
}
if (!matches(string, trace_buffer[i].fmt))
continue;
for (cp = trace_buffer[i].file; *cp; ++cp)
if (*cp == '/')
filename = cp + 1;
#if NCPUS > 1
db_printf("{%02d}",trace_buffer[i].cpu_number);
#endif /* NCPUS > 1 */
db_printf("[%04x%s] %s", i, i >= trace_index ? "*" : "",
trace_buffer[i].funcname);
db_printf(": ");
db_printf(trace_buffer[i].fmt, trace_buffer[i].tag1,
trace_buffer[i].tag2, trace_buffer[i].tag3,
trace_buffer[i].tag4);
db_printf("\n");
}
}
void
db_show_tr(
db_expr_t addr,
boolean_t have_addr,
db_expr_t count,
char * modif)
{
int flag, level;
flag = 0, level = 0;
if (db_option(modif, 'l')) {
flag = 1;
level = -1;
}
if (db_option(modif, 'a')) {
flag = 2;
level = -1;
}
TR_SHOW(level, 0, flag);
}
#endif /* MACH_KDB */
#endif /* TRACE_BUFFER */