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