1 #include "dat.h" 2 #include "fns.h" 3 #include "error.h" 4 #include "interp.h" 5 6 enum 7 { 8 Qdir, 9 Qctl, 10 Qstate, 11 Qsum, 12 Qevent, 13 Qprof, 14 Qheap, 15 Qgc 16 }; 17 18 static 19 Dirtab memdir[] = 20 { 21 ".", {Qdir, 0, QTDIR}, 0, DMDIR|0555, 22 "memctl", {Qctl}, 0, 0666, 23 "memstate", {Qstate}, 0, 0444, 24 "memsum", {Qsum}, 0, 0444, 25 "memevent", {Qevent}, 0, 0444, 26 "memprof", {Qprof}, 0, 0444, 27 "memheap", {Qheap}, 0, 0444, 28 "memgc", {Qgc}, 0, 0444, 29 }; 30 31 enum 32 { 33 /* these are the top two bits of size */ 34 Pflags= 3<<30, 35 Pfree= 0<<30, 36 Palloc= 1<<30, 37 Paend= 2<<30, 38 Pimmutable= 3<<30, 39 40 Npool = 3, 41 Nevent = 10000, 42 Nstate = 12000 43 }; 44 45 /* 46 * pool snapshots 47 */ 48 typedef struct Pstate Pstate; 49 struct Pstate 50 { 51 ulong base; 52 ulong size; 53 }; 54 55 static struct 56 { 57 Pstate state[3+Nstate]; 58 Pstate* lim; 59 Pstate* ptr; 60 int summary; 61 } poolstate[Npool]; 62 static Ref stateopen; 63 64 /* 65 * pool/heap allocation events 66 */ 67 typedef struct Pevent Pevent; 68 struct Pevent 69 { 70 int pool; 71 ulong pc; 72 ulong base; 73 ulong size; 74 }; 75 76 static struct 77 { 78 Lock l; 79 Ref inuse; 80 Rendez r; 81 int open; 82 Pevent events[Nevent]; 83 int rd; 84 int wr; 85 int full; 86 int want; 87 ulong lost; 88 } poolevents; 89 90 /* 91 * allocation profiles 92 */ 93 typedef struct Pprof Pprof; 94 typedef struct Pbucket Pbucket; 95 96 struct Pbucket 97 { 98 ulong val; 99 ulong pool; 100 ulong count; 101 ulong size; 102 Pbucket* next; 103 }; 104 105 static struct { 106 Ref inuse; /* only one of these things */ 107 Lock l; 108 Pbucket buckets[1000]; 109 Pbucket snap[1000]; 110 int used; 111 int snapped; 112 Pbucket* hash[128]; 113 ulong lost; 114 } memprof; 115 116 extern void (*memmonitor)(int, ulong, ulong, ulong); 117 extern ulong gcnruns; 118 extern ulong gcsweeps; 119 extern ulong gcbroken; 120 extern ulong gchalted; 121 extern ulong gcepochs; 122 extern uvlong gcdestroys; 123 extern uvlong gcinspects; 124 extern uvlong gcbusy; 125 extern uvlong gcidle; 126 extern uvlong gcidlepass; 127 extern uvlong gcpartial; 128 129 130 static void 131 mprofreset(void) 132 { 133 lock(&memprof.l); /* need ilock in kernel */ 134 memset(memprof.hash, 0, sizeof(memprof.hash)); 135 memprof.used = 0; 136 memprof.lost = 0; 137 unlock(&memprof.l); 138 } 139 140 static void 141 mprofmonitor(int pool, ulong pc, ulong base, ulong size) 142 { 143 Pbucket **h0, **h, *p; 144 145 if((pool&7) == 1) 146 return; /* ignore heap */ 147 USED(base); 148 h0 = &memprof.hash[((pc>>16)^(pc>>4))&(nelem(memprof.hash)-1)]; 149 lock(&memprof.l); 150 for(h = h0; (p = *h) != nil; h = &p->next) 151 if(p->val == pc && p->pool == pool){ 152 p->count++; 153 p->size += size; 154 *h = p->next; 155 p->next = *h0; 156 *h0 = p; 157 unlock(&memprof.l); 158 return; 159 } 160 if(memprof.used >= nelem(memprof.buckets)){ 161 memprof.lost++; 162 unlock(&memprof.l); 163 return; 164 } 165 p = &memprof.buckets[memprof.used++]; 166 p->val = pc; 167 p->pool = pool; 168 p->count = 1; 169 p->size = size; 170 p->next = *h0; 171 *h0 = p; 172 unlock(&memprof.l); 173 } 174 175 static void 176 _memmonitor(int pool, ulong pc, ulong base, ulong size) 177 { 178 Pevent e; 179 180 e.pool = pool; 181 e.pc = pc; 182 e.base = base; 183 e.size = size; 184 lock(&poolevents.l); 185 if(!poolevents.full){ 186 poolevents.events[poolevents.wr] = e; 187 if(++poolevents.wr == nelem(poolevents.events)) 188 poolevents.wr = 0; 189 if(poolevents.wr == poolevents.rd) 190 poolevents.full = 1; 191 }else 192 poolevents.lost++; 193 if(poolevents.want){ 194 poolevents.want = 0; 195 Wakeup(&poolevents.r); 196 } 197 unlock(&poolevents.l); 198 } 199 200 static int 201 ismemdata(void *v) 202 { 203 USED(v); 204 return poolevents.full || poolevents.rd != poolevents.wr; 205 } 206 207 static char* 208 memaudit(int pno, Bhdr *b) 209 { 210 Pstate *p; 211 212 if(pno >= Npool) 213 return "too many pools for memaudit"; 214 if((p = poolstate[pno].ptr) == poolstate[pno].lim){ 215 if(b->magic == MAGIC_E) 216 return nil; 217 p = &poolstate[pno].state[1]; 218 if(b->magic == MAGIC_F) 219 p++; 220 p->base++; 221 p->size += b->size; 222 return nil; 223 } 224 poolstate[pno].ptr++; 225 p->base = (ulong)b; 226 p->size = b->size; 227 switch(b->magic){ 228 case MAGIC_A: 229 p->size |= Palloc; 230 break; 231 case MAGIC_F: 232 p->size |= Pfree; 233 break; 234 case MAGIC_E: 235 p->size = b->csize | Paend; 236 break; 237 case MAGIC_I: 238 p->size |= Pimmutable; 239 break; 240 default: 241 return "bad magic number in block"; 242 } 243 return nil; 244 } 245 246 static void 247 mput4(uchar *m, ulong v) 248 { 249 m[0] = v>>24; 250 m[1] = v>>16; 251 m[2] = v>>8; 252 m[3] = v; 253 } 254 255 static Chan* 256 memattach(char *spec) 257 { 258 return devattach('%', spec); 259 } 260 261 static Walkqid* 262 memwalk(Chan *c, Chan *nc, char **name, int nname) 263 { 264 return devwalk(c, nc, name, nname, memdir, nelem(memdir), devgen); 265 } 266 267 static int 268 memstat(Chan *c, uchar *db, int n) 269 { 270 return devstat(c, db, n, memdir, nelem(memdir), devgen); 271 } 272 273 static Chan* 274 memopen(Chan *c, int omode) 275 { 276 if(memmonitor != nil && c->qid.path != Qgc) 277 error(Einuse); 278 c = devopen(c, omode, memdir, nelem(memdir), devgen); 279 switch((ulong)c->qid.path){ 280 case Qevent: 281 if(incref(&poolevents.inuse) != 1){ 282 decref(&poolevents.inuse); 283 c->flag &= ~COPEN; 284 error(Einuse); 285 } 286 poolevents.rd = poolevents.wr = 0; 287 poolevents.full = 0; 288 poolevents.want = 0; 289 poolevents.lost = 0; 290 memmonitor = _memmonitor; 291 poolevents.open = 1; 292 break; 293 case Qstate: 294 if(incref(&stateopen) != 1){ 295 decref(&stateopen); 296 c->flag &= ~COPEN; 297 error(Einuse); 298 } 299 break; 300 case Qprof: 301 if(incref(&memprof.inuse) != 1){ 302 decref(&memprof.inuse); 303 c->flag &= ~COPEN; 304 error(Einuse); 305 } 306 memmonitor = mprofmonitor; 307 break; 308 } 309 return c; 310 } 311 312 static void 313 memclose(Chan *c) 314 { 315 if((c->flag & COPEN) == 0) 316 return; 317 switch((ulong)c->qid.path) { 318 case Qevent: 319 memmonitor = nil; 320 poolevents.open = 0; 321 decref(&poolevents.inuse); 322 break; 323 case Qstate: 324 decref(&stateopen); 325 break; 326 case Qprof: 327 decref(&memprof.inuse); 328 memmonitor = nil; 329 break; 330 } 331 332 } 333 334 static long 335 memread(Chan *c, void *va, long count, vlong offset) 336 { 337 uchar *m; 338 char *e, *s; 339 int i, summary; 340 long n, nr; 341 Pstate *p; 342 Pevent pe; 343 Pbucket *b; 344 345 if(c->qid.type & QTDIR) 346 return devdirread(c, va, count, memdir, nelem(memdir), devgen); 347 348 summary = 0; 349 switch((ulong)c->qid.path) { 350 default: 351 error(Egreg); 352 case Qctl: 353 return 0; 354 case Qsum: 355 summary = 1; 356 /* fall through */ 357 case Qstate: 358 if(offset == 0){ 359 for(i=0; i<Npool; i++){ 360 poolstate[i].ptr = &poolstate[i].state[3]; 361 poolstate[i].lim = poolstate[i].ptr + Nstate; 362 memset(poolstate[i].state, 0, sizeof(poolstate[i].state)); 363 poolstate[i].summary = summary; 364 } 365 e = poolaudit(memaudit); 366 if(e != nil){ 367 print("mem: %s\n", e); 368 error(e); 369 } 370 } 371 m = va; 372 nr = offset/8; 373 for(i=0; i<Npool && count >= 8; i++){ 374 n = poolstate[i].ptr - poolstate[i].state; 375 poolstate[i].state[0].base = i; 376 poolstate[i].state[0].size = n; 377 if(nr >= n){ 378 nr -= n; 379 continue; 380 } 381 n -= nr; 382 p = &poolstate[i].state[nr]; 383 for(; --n >= 0 && (count -= 8) >= 0; m += 8, p++){ 384 mput4(m, p->base); 385 mput4(m+4, p->size); 386 } 387 } 388 return m-(uchar*)va; 389 case Qevent: 390 while(!ismemdata(nil)){ 391 poolevents.want = 1; 392 Sleep(&poolevents.r, ismemdata, nil); 393 } 394 m = va; 395 do{ 396 if((count -= 4*4) < 0) 397 return m-(uchar*)va; 398 pe = poolevents.events[poolevents.rd]; 399 mput4(m, pe.pool); 400 mput4(m+4, pe.pc); 401 mput4(m+8, pe.base); 402 mput4(m+12, pe.size); 403 m += 4*4; 404 if(++poolevents.rd >= nelem(poolevents.events)) 405 poolevents.rd = 0; 406 }while(poolevents.rd != poolevents.wr); 407 poolevents.full = 0; 408 return m-(uchar*)va; 409 case Qprof: 410 if(offset == 0){ 411 lock(&memprof.l); 412 memmove(memprof.snap, memprof.buckets, memprof.used*sizeof(memprof.buckets[0])); 413 memprof.snapped = memprof.used; 414 unlock(&memprof.l); 415 } 416 m = va; 417 for(i = offset/(4*4); i < memprof.snapped && (count -= 4*4) >= 0; i++){ 418 b = &memprof.snap[i]; 419 mput4(m, b->pool); 420 mput4(m+4, b->val); 421 mput4(m+8, b->count); 422 mput4(m+12, b->size); 423 m += 4*4; 424 } 425 return m-(uchar*)va; 426 case Qgc: 427 s = malloc(READSTR); 428 if(s == nil) 429 error(Enomem); 430 if(waserror()){ 431 free(s); 432 nexterror(); 433 } 434 snprint(s, READSTR, "runs: %lud\nsweeps: %lud\nbchain: %lud\nhalted: %lud\nepochs: %lud\ndestroy: %llud\ninspects: %llud\nbusy: %llud\nidle: %llud\nidlepass: %llud\npartial: %llud\n", 435 gcnruns, gcsweeps, gcbroken, gchalted, gcepochs, gcdestroys, gcinspects, gcbusy, gcidle, gcidlepass, gcpartial); 436 count = readstr(offset, va, count, s); 437 poperror(); 438 free(s); 439 return count; 440 } 441 } 442 443 static long 444 memwrite(Chan *c, void *va, long count, vlong offset) 445 { 446 USED(offset); 447 USED(count); 448 USED(va); 449 450 if(c->qid.type & QTDIR) 451 error(Eperm); 452 453 switch((ulong)c->qid.path) { 454 default: 455 error(Egreg); 456 case Qctl: 457 error(Ebadarg); 458 case Qstate: 459 error(Eperm); 460 case Qprof: 461 mprofreset(); 462 break; 463 } 464 return 0; 465 } 466 467 Dev memdevtab = { 468 '%', 469 "mem", 470 471 devinit, 472 memattach, 473 memwalk, 474 memstat, 475 memopen, 476 devcreate, 477 memclose, 478 memread, 479 devbread, 480 memwrite, 481 devbwrite, 482 devremove, 483 devwstat 484 }; 485