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