xref: /inferno-os/emu/port/devmem.c (revision 7ef44d652ae9e5e1f5b3465d73684e4a54de73c0)
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