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