1 /* This file implements kernel debugging functionality that is not included 2 * in the standard kernel. Available functionality includes timing of lock 3 * functions and sanity checking of the scheduling queues. 4 */ 5 6 #include "kernel/kernel.h" 7 8 #include <minix/callnr.h> 9 #include <minix/u64.h> 10 #include <limits.h> 11 #include <string.h> 12 #include <assert.h> 13 14 #define MAX_LOOP (NR_PROCS + NR_TASKS) 15 16 int runqueues_ok_cpu(unsigned cpu) 17 { 18 int q, l = 0; 19 register struct proc *xp; 20 struct proc **rdy_head, **rdy_tail; 21 22 rdy_head = get_cpu_var(cpu, run_q_head); 23 rdy_tail = get_cpu_var(cpu, run_q_tail); 24 25 for (xp = BEG_PROC_ADDR; xp < END_PROC_ADDR; ++xp) { 26 xp->p_found = 0; 27 if (l++ > MAX_LOOP) panic("check error"); 28 } 29 30 for (q=l=0; q < NR_SCHED_QUEUES; q++) { 31 if (rdy_head[q] && !rdy_tail[q]) { 32 printf("head but no tail in %d\n", q); 33 return 0; 34 } 35 if (!rdy_head[q] && rdy_tail[q]) { 36 printf("tail but no head in %d\n", q); 37 return 0; 38 } 39 if (rdy_tail[q] && rdy_tail[q]->p_nextready) { 40 printf("tail and tail->next not null in %d\n", q); 41 return 0; 42 } 43 for(xp = rdy_head[q]; xp; xp = xp->p_nextready) { 44 const vir_bytes vxp = (vir_bytes) xp; 45 vir_bytes dxp; 46 if(vxp < (vir_bytes) BEG_PROC_ADDR || vxp >= (vir_bytes) END_PROC_ADDR) { 47 printf("xp out of range\n"); 48 return 0; 49 } 50 dxp = vxp - (vir_bytes) BEG_PROC_ADDR; 51 if(dxp % sizeof(struct proc)) { 52 printf("xp not a real pointer"); 53 return 0; 54 } 55 if(!proc_ptr_ok(xp)) { 56 printf("xp bogus pointer"); 57 return 0; 58 } 59 if (RTS_ISSET(xp, RTS_SLOT_FREE)) { 60 printf("scheduling error: dead proc q %d %d\n", 61 q, xp->p_endpoint); 62 return 0; 63 } 64 if (!proc_is_runnable(xp)) { 65 printf("scheduling error: unready on runq %d proc %d\n", 66 q, xp->p_nr); 67 return 0; 68 } 69 if (xp->p_priority != q) { 70 printf("scheduling error: wrong priority q %d proc %d ep %d name %s\n", 71 q, xp->p_nr, xp->p_endpoint, xp->p_name); 72 return 0; 73 } 74 if (xp->p_found) { 75 printf("scheduling error: double sched q %d proc %d\n", 76 q, xp->p_nr); 77 return 0; 78 } 79 xp->p_found = 1; 80 if (!xp->p_nextready && rdy_tail[q] != xp) { 81 printf("sched err: last element not tail q %d proc %d\n", 82 q, xp->p_nr); 83 return 0; 84 } 85 if (l++ > MAX_LOOP) { 86 printf("loop in schedule queue?"); 87 return 0; 88 } 89 } 90 } 91 92 for (xp = BEG_PROC_ADDR; xp < END_PROC_ADDR; ++xp) { 93 if(!proc_ptr_ok(xp)) { 94 printf("xp bogus pointer in proc table\n"); 95 return 0; 96 } 97 if (isemptyp(xp)) 98 continue; 99 if(proc_is_runnable(xp) && !xp->p_found) { 100 printf("sched error: ready proc %d not on queue\n", xp->p_nr); 101 return 0; 102 } 103 } 104 105 /* All is ok. */ 106 return 1; 107 } 108 109 #ifdef CONFIG_SMP 110 static int runqueues_ok_all(void) 111 { 112 unsigned c; 113 114 for (c = 0 ; c < ncpus; c++) { 115 if (!runqueues_ok_cpu(c)) 116 return 0; 117 } 118 return 1; 119 } 120 121 int runqueues_ok(void) 122 { 123 return runqueues_ok_all(); 124 } 125 126 #else 127 128 int runqueues_ok(void) 129 { 130 return runqueues_ok_cpu(0); 131 } 132 133 134 #endif 135 136 char * 137 rtsflagstr(const u32_t flags) 138 { 139 static char str[100]; 140 str[0] = '\0'; 141 142 #define FLAG(n) if(flags & n) { strlcat(str, #n " ", sizeof(str)); } 143 144 FLAG(RTS_SLOT_FREE); 145 FLAG(RTS_PROC_STOP); 146 FLAG(RTS_SENDING); 147 FLAG(RTS_RECEIVING); 148 FLAG(RTS_SIGNALED); 149 FLAG(RTS_SIG_PENDING); 150 FLAG(RTS_P_STOP); 151 FLAG(RTS_NO_PRIV); 152 FLAG(RTS_NO_ENDPOINT); 153 FLAG(RTS_VMINHIBIT); 154 FLAG(RTS_PAGEFAULT); 155 FLAG(RTS_VMREQUEST); 156 FLAG(RTS_VMREQTARGET); 157 FLAG(RTS_PREEMPTED); 158 FLAG(RTS_NO_QUANTUM); 159 160 return str; 161 } 162 163 char * 164 miscflagstr(const u32_t flags) 165 { 166 static char str[100]; 167 str[0] = '\0'; 168 169 FLAG(MF_REPLY_PEND); 170 FLAG(MF_DELIVERMSG); 171 FLAG(MF_KCALL_RESUME); 172 173 return str; 174 } 175 176 char * 177 schedulerstr(struct proc *scheduler) 178 { 179 if (scheduler != NULL) 180 { 181 return scheduler->p_name; 182 } 183 184 return "KERNEL"; 185 } 186 187 static void 188 print_proc_name(struct proc *pp) 189 { 190 char *name = pp->p_name; 191 endpoint_t ep = pp->p_endpoint; 192 193 if(name) { 194 printf("%s(%d)", name, ep); 195 } 196 else { 197 printf("%d", ep); 198 } 199 } 200 201 static void 202 print_endpoint(endpoint_t ep) 203 { 204 int proc_nr; 205 struct proc *pp = NULL; 206 207 switch(ep) { 208 case ANY: 209 printf("ANY"); 210 break; 211 case SELF: 212 printf("SELF"); 213 break; 214 case NONE: 215 printf("NONE"); 216 break; 217 default: 218 if(!isokendpt(ep, &proc_nr)) { 219 printf("??? %d\n", ep); 220 } 221 else { 222 pp = proc_addr(proc_nr); 223 if(isemptyp(pp)) { 224 printf("??? empty slot %d\n", proc_nr); 225 } 226 else { 227 print_proc_name(pp); 228 } 229 } 230 break; 231 } 232 } 233 234 static void 235 print_sigmgr(struct proc *pp) 236 { 237 endpoint_t sig_mgr, bak_sig_mgr; 238 sig_mgr = priv(pp) ? priv(pp)->s_sig_mgr : NONE; 239 bak_sig_mgr = priv(pp) ? priv(pp)->s_bak_sig_mgr : NONE; 240 if(sig_mgr == NONE) { printf("no sigmgr"); return; } 241 printf("sigmgr "); 242 print_endpoint(sig_mgr); 243 if(bak_sig_mgr != NONE) { 244 printf(" / "); 245 print_endpoint(bak_sig_mgr); 246 } 247 } 248 249 void print_proc(struct proc *pp) 250 { 251 endpoint_t dep; 252 253 printf("%d: %s %d prio %d time %d/%d cycles 0x%x%08x cpu %2d " 254 "pdbr 0x%lx rts %s misc %s sched %s ", 255 proc_nr(pp), pp->p_name, pp->p_endpoint, 256 pp->p_priority, pp->p_user_time, 257 pp->p_sys_time, ex64hi(pp->p_cycles), 258 ex64lo(pp->p_cycles), pp->p_cpu, 259 #if defined(__i386__) 260 pp->p_seg.p_cr3, 261 #elif defined(__arm__) 262 pp->p_seg.p_ttbr, 263 #endif 264 rtsflagstr(pp->p_rts_flags), miscflagstr(pp->p_misc_flags), 265 schedulerstr(pp->p_scheduler)); 266 267 print_sigmgr(pp); 268 269 dep = P_BLOCKEDON(pp); 270 if(dep != NONE) { 271 printf(" blocked on: "); 272 print_endpoint(dep); 273 } 274 printf("\n"); 275 } 276 277 static void print_proc_depends(struct proc *pp, const int level) 278 { 279 struct proc *depproc = NULL; 280 endpoint_t dep; 281 #define COL { int i; for(i = 0; i < level; i++) printf("> "); } 282 283 if(level >= NR_PROCS) { 284 printf("loop??\n"); 285 return; 286 } 287 288 COL 289 290 print_proc(pp); 291 292 COL 293 proc_stacktrace(pp); 294 295 296 dep = P_BLOCKEDON(pp); 297 if(dep != NONE && dep != ANY) { 298 int procno; 299 if(isokendpt(dep, &procno)) { 300 depproc = proc_addr(procno); 301 if(isemptyp(depproc)) 302 depproc = NULL; 303 } 304 if (depproc) 305 print_proc_depends(depproc, level+1); 306 } 307 } 308 309 void print_proc_recursive(struct proc *pp) 310 { 311 print_proc_depends(pp, 0); 312 } 313 314 #if DEBUG_DUMPIPC || DEBUG_DUMPIPCF 315 static const char *mtypename(int mtype, int *possible_callname) 316 { 317 char *callname = NULL, *errname = NULL; 318 /* use generated file to recognize message types 319 * 320 * we try to match both error numbers and call numbers, as the 321 * reader can probably decide from context what's going on. 322 * 323 * whenever it might be a call number we tell the caller so the 324 * call message fields can be decoded if known. 325 */ 326 switch(mtype) { 327 #define IDENT(x) case x: callname = #x; *possible_callname = 1; break; 328 #include "kernel/extracted-mtype.h" 329 #undef IDENT 330 } 331 switch(mtype) { 332 #define IDENT(x) case x: errname = #x; break; 333 #include "kernel/extracted-errno.h" 334 #undef IDENT 335 } 336 337 /* no match */ 338 if(!errname && !callname) 339 return NULL; 340 341 /* 2 matches */ 342 if(errname && callname) { 343 static char typename[100]; 344 strcpy(typename, errname); 345 strcat(typename, " / "); 346 strcat(typename, callname); 347 return typename; 348 } 349 350 if(errname) return errname; 351 352 assert(callname); 353 return callname; 354 } 355 356 static void printproc(struct proc *rp) 357 { 358 if (rp) 359 printf(" %s(%d)", rp->p_name, rp - proc); 360 else 361 printf(" kernel"); 362 } 363 364 static void printparam(const char *name, const void *data, size_t size) 365 { 366 printf(" %s=", name); 367 switch (size) { 368 case sizeof(char): printf("%d", *(char *) data); break; 369 case sizeof(short): printf("%d", *(short *) data); break; 370 case sizeof(int): printf("%d", *(int *) data); break; 371 default: printf("(%u bytes)", size); break; 372 } 373 } 374 375 #ifdef DEBUG_DUMPIPC_NAMES 376 static int namematch(char **names, int nnames, char *name) 377 { 378 int i; 379 for(i = 0; i < nnames; i++) 380 if(!strcmp(names[i], name)) 381 return 1; 382 return 0; 383 } 384 #endif 385 386 void printmsg(message *msg, struct proc *src, struct proc *dst, 387 char operation, int printparams) 388 { 389 const char *name; 390 int mtype = msg->m_type, mightbecall = 0; 391 392 #ifdef DEBUG_DUMPIPC_NAMES 393 { 394 char *names[] = DEBUG_DUMPIPC_NAMES; 395 int nnames = sizeof(names)/sizeof(names[0]); 396 397 /* skip printing messages for messages neither to 398 * or from DEBUG_DUMPIPC_EP if it is defined; either 399 * can be NULL to indicate kernel 400 */ 401 if(!(src && namematch(names, nnames, src->p_name)) && 402 !(dst && namematch(names, nnames, dst->p_name))) { 403 return; 404 } 405 } 406 #endif 407 408 /* source, destination and message type */ 409 printf("%c", operation); 410 printproc(src); 411 printproc(dst); 412 name = mtypename(mtype, &mightbecall); 413 if (name) { 414 printf(" %s(%d/0x%x)", name, mtype, mtype); 415 } else { 416 printf(" %d/0x%x", mtype, mtype); 417 } 418 419 if (mightbecall && printparams) { 420 #define IDENT(x, y) if (mtype == x) printparam(#y, &msg->y, sizeof(msg->y)); 421 #include "kernel/extracted-mfield.h" 422 #undef IDENT 423 } 424 printf("\n"); 425 } 426 #endif 427 428 #if DEBUG_IPCSTATS 429 #define IPCPROCS (NR_PROCS+1) /* number of slots we need */ 430 #define KERNELIPC NR_PROCS /* slot number to use for kernel calls */ 431 static int messages[IPCPROCS][IPCPROCS]; 432 433 #define PRINTSLOTS 20 434 static struct { 435 int src, dst, messages; 436 } winners[PRINTSLOTS]; 437 static int total, goodslots; 438 439 static void printstats(int ticks) 440 { 441 int i; 442 for(i = 0; i < goodslots; i++) { 443 #define name(s) (s == KERNELIPC ? "kernel" : proc_addr(s)->p_name) 444 #define persec(n) (system_hz*(n)/ticks) 445 char *n1 = name(winners[i].src), 446 *n2 = name(winners[i].dst); 447 printf("%2d. %8s -> %8s %9d/s\n", 448 i, n1, n2, persec(winners[i].messages)); 449 } 450 printf("total %d/s\n", persec(total)); 451 } 452 453 static void sortstats(void) 454 { 455 /* Print top message senders/receivers. */ 456 int src_slot, dst_slot; 457 total = goodslots = 0; 458 for(src_slot = 0; src_slot < IPCPROCS; src_slot++) { 459 for(dst_slot = 0; dst_slot < IPCPROCS; dst_slot++) { 460 int w = PRINTSLOTS, rem, 461 n = messages[src_slot][dst_slot]; 462 total += n; 463 while(w > 0 && n > winners[w-1].messages) 464 w--; 465 if(w >= PRINTSLOTS) continue; 466 467 /* This combination has beaten the current winners 468 * and should be inserted at position 'w.' 469 */ 470 rem = PRINTSLOTS-w-1; 471 assert(rem >= 0); 472 assert(rem < PRINTSLOTS); 473 if(rem > 0) { 474 assert(w+1 <= PRINTSLOTS-1); 475 assert(w >= 0); 476 memmove(&winners[w+1], &winners[w], 477 rem*sizeof(winners[0])); 478 } 479 winners[w].src = src_slot; 480 winners[w].dst = dst_slot; 481 winners[w].messages = n; 482 if(goodslots < PRINTSLOTS) goodslots++; 483 } 484 } 485 } 486 487 #define proc2slot(p, s) { \ 488 if(p) { s = p->p_nr; } \ 489 else { s = KERNELIPC; } \ 490 assert(s >= 0 && s < IPCPROCS); \ 491 } 492 493 static void statmsg(message *msg, struct proc *srcp, struct proc *dstp) 494 { 495 int src, dst, now, secs, dt; 496 static int lastprint; 497 498 /* Stat message. */ 499 assert(src); 500 proc2slot(srcp, src); 501 proc2slot(dstp, dst); 502 messages[src][dst]++; 503 504 /* Print something? */ 505 now = get_monotonic(); 506 dt = now - lastprint; 507 secs = dt/system_hz; 508 if(secs >= 30) { 509 memset(winners, 0, sizeof(winners)); 510 sortstats(); 511 printstats(dt); 512 memset(messages, 0, sizeof(messages)); 513 lastprint = now; 514 } 515 } 516 #endif 517 518 #if DEBUG_IPC_HOOK 519 void hook_ipc_msgkcall(message *msg, struct proc *proc) 520 { 521 #if DEBUG_DUMPIPC 522 printmsg(msg, proc, NULL, 'k', 1); 523 #endif 524 } 525 526 void hook_ipc_msgkresult(message *msg, struct proc *proc) 527 { 528 #if DEBUG_DUMPIPC 529 printmsg(msg, NULL, proc, 'k', 0); 530 #endif 531 #if DEBUG_IPCSTATS 532 statmsg(msg, proc, NULL); 533 #endif 534 } 535 536 void hook_ipc_msgrecv(message *msg, struct proc *src, struct proc *dst) 537 { 538 #if DEBUG_DUMPIPC 539 printmsg(msg, src, dst, 'r', 0); 540 #endif 541 #if DEBUG_IPCSTATS 542 statmsg(msg, src, dst); 543 #endif 544 } 545 546 void hook_ipc_msgsend(message *msg, struct proc *src, struct proc *dst) 547 { 548 #if DEBUG_DUMPIPC 549 printmsg(msg, src, dst, 's', 1); 550 #endif 551 } 552 553 void hook_ipc_clear(struct proc *p) 554 { 555 #if DEBUG_IPCSTATS 556 int slot, i; 557 assert(p); 558 proc2slot(p, slot); 559 for(i = 0; i < IPCPROCS; i++) 560 messages[slot][i] = messages[i][slot] = 0; 561 #endif 562 } 563 #endif 564