1/*
2 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28/*
29 * @OSF_COPYRIGHT@
30 */
31/*
32 *	File:		ddb/tr.c
33 *	Authors:	Alan Langerman, Jeffrey Heller
34 *	Date:		1992
35 *
36 *	Internal trace routines.  Like old-style XPRs but
37 *	less formatting.
38 */
39
40#include <ddb/tr.h>
41
42#if	TRACE_BUFFER
43#include <string.h>
44#include <ddb/db_command.h>
45#include <mach_kdb.h>
46#include <kern/lock.h>
47#include <kern/spl.h>
48
49extern void fc_get(int *);
50
51/*
52 *	Primitive event tracing facility for kernel debugging.  Yes,
53 *	this has some resemblance to XPRs.  However, it is primarily
54 *	intended for post-mortem analysis through ddb.
55 */
56
57#define	TRACE_MAX	(4 * 1024)
58#define	TRACE_WINDOW	40
59
60typedef struct trace_event {
61	char		*funcname;
62	char		*file;
63	char		*fmt;
64#if	NCPUS > 1
65	char		cpu_number;
66#endif	/* NCPUS > 1 */
67	unsigned int	lineno;
68	unsigned int	tag1;
69	unsigned int	tag2;
70	unsigned int	tag3;
71	unsigned int	tag4;
72	int		indent;
73	int		timestamp[2]; /* largest needed by any clock */
74} trace_event;
75
76trace_event	trace_buffer[TRACE_MAX];
77unsigned long	trace_index;
78#if	NCPUS == 1
79int 		tr_indent = 0;
80#else	/* NCPUS == 1 */
81int 		tr_indent[NCPUS];
82int		tr_limit = -1;
83#endif	/* NCPUS == 1 */
84
85decl_simple_lock_data(,trace_lock)
86
87void
88tr_init(void)
89{
90#if	NCPUS > 1
91	int i;
92
93	for(i=0;i<NCPUS;i++)
94	    tr_indent[i]=0;
95#endif	/* NCPUS > 1 */
96
97	simple_lock_init(&trace_lock, 0);
98}
99
100void
101tr(
102	char		*funcname,
103	char		*file,
104	unsigned int	lineno,
105	char		*fmt,
106	unsigned int	tag1,
107	unsigned int	tag2,
108	unsigned int	tag3,
109	unsigned int	tag4)
110{
111	int	s;
112	register unsigned long ti, tn;
113#if	NCPUS > 1
114	char cpu;
115#endif	/* NCPUS > 1 */
116
117#if	PARAGON860
118	/*
119	 * The following loop replaces the spl_and_lock sequence that
120	 * would normally be here, as they are too heavy weight.  The
121	 * cmpsw (compare-and-swap) call returns -1 if unsuccessful.
122	 */
123	do {
124		ti = trace_index;
125		tn = ti + 1;
126		if (tn >= TRACE_MAX - 1)
127			tn = 0;
128	} while (cmpsw(ti, tn, &trace_index) == -1);
129	fc_get(trace_buffer[ti].timestamp);
130#else	/* PARAGON860 */
131	/*
132	 * Until someone does a cmpsw for other platforms, do it
133	 * the slow way
134	 */
135	s = splimp();
136	simple_lock(&trace_lock);
137
138	ti = trace_index++;
139	if (trace_index >= TRACE_MAX - 1)
140		trace_index = 0;
141
142	simple_unlock(&trace_lock);
143	splx(s);
144
145	fc_get(trace_buffer[ti].timestamp);
146/*	get_uniq_timestamp(trace_buffer[ti].timestamp);*/
147#endif	/* PARAGON860 */
148
149	trace_buffer[ti].funcname = funcname;
150	trace_buffer[ti].file = file;
151	trace_buffer[ti].lineno = lineno;
152	trace_buffer[ti].fmt = fmt;
153	trace_buffer[ti].tag1 = tag1;
154	trace_buffer[ti].tag2 = tag2;
155	trace_buffer[ti].tag3 = tag3;
156	trace_buffer[ti].tag4 = tag4;
157#if	NCPUS == 1
158	trace_buffer[ti].indent = tr_indent;
159#else	/* NCPUS == 1 */
160	mp_disable_preemption();
161	cpu = cpu_number();
162	trace_buffer[ti].indent = tr_indent[cpu];
163	trace_buffer[ti].cpu_number = cpu;
164	mp_enable_preemption();
165#endif	/* NCPUS == 1 */
166}
167
168#if	MACH_KDB
169#include <ddb/db_output.h>
170
171/*
172 * Forward.
173 */
174void	show_tr(
175		unsigned long	index,
176		unsigned long	range,
177		unsigned long	show_extra);
178
179int	matches(
180		char	*pattern,
181		char	*target);
182
183void	parse_tr(
184		unsigned long	index,
185		unsigned long	range);
186
187/*
188 *	The blank array must be a bit bigger than
189 *	MAX_BLANKS to leave room for a terminating NULL.
190 */
191#define	MAX_BLANKS	16
192char			blanks[MAX_BLANKS+4];
193
194void
195show_tr(
196	unsigned long	index,
197	unsigned long	range,
198	unsigned long	show_extra)
199{
200	char		*filename, *cp;
201#if	PARAGON860
202	trace_event	*last_trace;
203#endif	/* PARAGON860 */
204	unsigned int	level;
205	int		old_history;
206	int		i;
207
208	if (index == -1) {
209		index = trace_index - (TRACE_WINDOW-4);
210		range = TRACE_WINDOW;
211	} else if (index == 0) {
212		index = trace_index - (TRACE_WINDOW-4);
213		range = TRACE_WINDOW;
214		show_extra = 0;
215	}
216	if (index + range > TRACE_MAX)
217		range = TRACE_MAX - index;
218#if	PARAGON860
219	last_trace = &trace_buffer[index-1];
220#endif	/* PARAGON860 */
221	level = trace_buffer[index-1].indent;
222	/*
223	 * Set up the indentation buffer
224	 */
225	memset(blanks, ' ', trace_buffer[index].indent);
226	blanks[trace_buffer[index].indent] = '\0';
227	for (i = index; i < index + range; ++i) {
228#if	NCPUS > 1
229		if ((tr_limit != -1) &&
230		    (trace_buffer[i].cpu_number != tr_limit))
231		    continue;
232#endif	/* NCPUS > 1 */
233		if (trace_buffer[i].file == (char *) 0 ||
234		    trace_buffer[i].funcname == (char *) 0 ||
235		    trace_buffer[i].lineno == 0 ||
236		    trace_buffer[i].fmt == 0) {
237			db_printf("[%04x%s]\n", i,
238				  i >= trace_index ? "*" : "");
239			continue;
240		}
241
242		old_history = (i >= trace_index);
243
244		/*
245		 * Adjust the blank count if necessary
246		 */
247		if (level != trace_buffer[i].indent) {
248			level = trace_buffer[i].indent;
249			if (level >= MAX_BLANKS)
250				level = MAX_BLANKS;
251			memset(blanks, ' ', level);
252			blanks[level] = '\0';
253		}
254
255		for (cp = trace_buffer[i].file; *cp; ++cp)
256			if (*cp == '/')
257				filename = cp + 1;
258#if	NCPUS > 1
259		db_printf("{%02d}",trace_buffer[i].cpu_number);
260#endif	/* NCPUS > 1 */
261		db_printf("[%04x%s] %s%-16s", i, old_history ? "*" : "",
262			  blanks, trace_buffer[i].funcname);
263
264		if (show_extra) {
265			if (show_extra > 0) {
266				db_printf(" (%x/%8x)",
267					  trace_buffer[i].timestamp[0],
268					  trace_buffer[i].timestamp[1]);
269#if	PARAGON860
270				/*
271				 *	For Paragon only, we compute and
272				 *	print out deltas on the timestamps
273				 *	accumulated in the tr buffer.  One
274				 *	interesting case:  it is meaningless
275				 *	to compute this delta for the last
276				 *	current entry in the log.
277				 */
278				if (old_history &&
279				    ((last_trace - trace_buffer)
280				     < trace_index))
281					db_printf("(N/A)");
282				else
283					db_printf("(%d)",
284						  timer_subtime(
285						     trace_buffer[i].timestamp,
286						     last_trace->timestamp));
287#endif	/*PARAGON860*/
288				db_printf(" ");
289			}
290			if (show_extra > 1) {
291				db_printf("(%s:%05d):\n\t",
292					  filename, trace_buffer[i].lineno);
293			}
294		} else
295			db_printf(":  ");
296		db_printf(trace_buffer[i].fmt, trace_buffer[i].tag1,
297			  trace_buffer[i].tag2, trace_buffer[i].tag3,
298			  trace_buffer[i].tag4);
299		db_printf("\n");
300#if	PARAGON860
301		last_trace = &trace_buffer[i];
302#endif	/* PARAGON860 */
303	}
304}
305
306
307int
308matches(
309	char	*pattern,
310	char	*target)
311{
312	char	*cp, *cp1, *cp2;
313
314	for (cp = target; *cp; ++cp) {
315		for (cp2 = pattern, cp1 = cp; *cp2 && *cp1; ++cp2, ++cp1)
316			if (*cp2 != *cp1)
317				break;
318		if (!*cp2)
319			return 1;
320	}
321	return 0;
322}
323
324
325char	parse_tr_buffer[100] = "KMSG";
326
327void
328parse_tr(
329	unsigned long	index,
330	unsigned long	range)
331{
332	int		i;
333	char		*filename, *cp;
334	char		*string = parse_tr_buffer;
335
336	if (index == 0) {
337		index = trace_index - (TRACE_WINDOW-4);
338		range = TRACE_WINDOW;
339	}
340	if (index + range > TRACE_MAX)
341		range = TRACE_MAX - index;
342	for (i = index; i < index + range; ++i) {
343#if	NCPUS > 1
344		if ((tr_limit != -1) &&
345		    (trace_buffer[i].cpu_number != tr_limit))
346		    continue;
347#endif	/* NCPUS > 1 */
348		if (trace_buffer[i].file == (char *) 0 ||
349		    trace_buffer[i].funcname == (char *) 0 ||
350		    trace_buffer[i].lineno == 0 ||
351		    trace_buffer[i].fmt == 0) {
352			db_printf("[%04x%s]\n", i,
353				  i >= trace_index ? "*" : "");
354			continue;
355		}
356		if (!matches(string, trace_buffer[i].fmt))
357			continue;
358		for (cp = trace_buffer[i].file; *cp; ++cp)
359			if (*cp == '/')
360				filename = cp + 1;
361#if	NCPUS > 1
362		db_printf("{%02d}",trace_buffer[i].cpu_number);
363#endif	/* NCPUS > 1 */
364		db_printf("[%04x%s] %s", i, i >= trace_index ? "*" : "",
365		       trace_buffer[i].funcname);
366		db_printf(":  ");
367		db_printf(trace_buffer[i].fmt, trace_buffer[i].tag1,
368			  trace_buffer[i].tag2, trace_buffer[i].tag3,
369			  trace_buffer[i].tag4);
370		db_printf("\n");
371	}
372}
373
374
375void
376db_show_tr(
377	db_expr_t	addr,
378	boolean_t	have_addr,
379	db_expr_t	count,
380	char *		modif)
381{
382	int		flag, level;
383
384	flag = 0, level = 0;
385	if (db_option(modif, 'l')) {
386		flag = 1;
387		level = -1;
388	}
389	if (db_option(modif, 'a')) {
390		flag = 2;
391		level = -1;
392	}
393
394	TR_SHOW(level, 0, flag);
395}
396
397#endif	/* MACH_KDB */
398
399#endif	/* TRACE_BUFFER */
400