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
mprofreset(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
mprofmonitor(int pool,ulong pc,ulong base,ulong size)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
_memmonitor(int pool,ulong pc,ulong base,ulong size)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
ismemdata(void * v)201 ismemdata(void *v)
202 {
203 USED(v);
204 return poolevents.full || poolevents.rd != poolevents.wr;
205 }
206
207 static char*
memaudit(int pno,Bhdr * b)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
mput4(uchar * m,ulong v)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*
memattach(char * spec)256 memattach(char *spec)
257 {
258 return devattach('%', spec);
259 }
260
261 static Walkqid*
memwalk(Chan * c,Chan * nc,char ** name,int nname)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
memstat(Chan * c,uchar * db,int n)268 memstat(Chan *c, uchar *db, int n)
269 {
270 return devstat(c, db, n, memdir, nelem(memdir), devgen);
271 }
272
273 static Chan*
memopen(Chan * c,int omode)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
memclose(Chan * c)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
memread(Chan * c,void * va,long count,vlong offset)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
memwrite(Chan * c,void * va,long count,vlong offset)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