]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/heapdump.c
[dev.garbage] all: merge default (f38460037b72) into dev.garbage
[gostls13.git] / src / runtime / heapdump.c
1 // Copyright 2014 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 // Implementation of runtime/debug.WriteHeapDump.  Writes all
6 // objects in the heap plus additional info (roots, threads,
7 // finalizers, etc.) to a file.
8
9 // The format of the dumped file is described at
10 // http://code.google.com/p/go-wiki/wiki/heapdump14
11
12 #include "runtime.h"
13 #include "arch_GOARCH.h"
14 #include "malloc.h"
15 #include "mgc0.h"
16 #include "type.h"
17 #include "typekind.h"
18 #include "funcdata.h"
19 #include "zaexperiment.h"
20 #include "textflag.h"
21
22 extern byte runtime·data[];
23 extern byte runtime·edata[];
24 extern byte runtime·bss[];
25 extern byte runtime·ebss[];
26
27 enum {
28         FieldKindEol = 0,
29         FieldKindPtr = 1,
30         FieldKindIface = 2,
31         FieldKindEface = 3,
32
33         TagEOF = 0,
34         TagObject = 1,
35         TagOtherRoot = 2,
36         TagType = 3,
37         TagGoRoutine = 4,
38         TagStackFrame = 5,
39         TagParams = 6,
40         TagFinalizer = 7,
41         TagItab = 8,
42         TagOSThread = 9,
43         TagMemStats = 10,
44         TagQueuedFinalizer = 11,
45         TagData = 12,
46         TagBss = 13,
47         TagDefer = 14,
48         TagPanic = 15,
49         TagMemProf = 16,
50         TagAllocSample = 17,
51 };
52
53 static uintptr* playgcprog(uintptr offset, uintptr *prog, void (*callback)(void*,uintptr,uintptr), void *arg);
54 static void dumpfields(BitVector bv);
55 static void dumpbvtypes(BitVector *bv, byte *base);
56 static BitVector makeheapobjbv(byte *p, uintptr size);
57
58 // fd to write the dump to.
59 static uintptr  dumpfd;
60
61 #pragma dataflag NOPTR /* tmpbuf not a heap pointer at least */
62 static byte     *tmpbuf;
63 static uintptr  tmpbufsize;
64
65 // buffer of pending write data
66 enum {
67         BufSize = 4096,
68 };
69 #pragma dataflag NOPTR
70 static byte buf[BufSize];
71 static uintptr nbuf;
72
73 static void
74 write(byte *data, uintptr len)
75 {
76         if(len + nbuf <= BufSize) {
77                 runtime·memmove(buf + nbuf, data, len);
78                 nbuf += len;
79                 return;
80         }
81         runtime·write(dumpfd, buf, nbuf);
82         if(len >= BufSize) {
83                 runtime·write(dumpfd, data, len);
84                 nbuf = 0;
85         } else {
86                 runtime·memmove(buf, data, len);
87                 nbuf = len;
88         }
89 }
90
91 static void
92 flush(void)
93 {
94         runtime·write(dumpfd, buf, nbuf);
95         nbuf = 0;
96 }
97
98 // Cache of types that have been serialized already.
99 // We use a type's hash field to pick a bucket.
100 // Inside a bucket, we keep a list of types that
101 // have been serialized so far, most recently used first.
102 // Note: when a bucket overflows we may end up
103 // serializing a type more than once.  That's ok.
104 enum {
105         TypeCacheBuckets = 256, // must be a power of 2
106         TypeCacheAssoc = 4,
107 };
108 typedef struct TypeCacheBucket TypeCacheBucket;
109 struct TypeCacheBucket {
110         Type *t[TypeCacheAssoc];
111 };
112 #pragma dataflag NOPTR /* only initialized and used while world is stopped */
113 static TypeCacheBucket typecache[TypeCacheBuckets];
114
115 // dump a uint64 in a varint format parseable by encoding/binary
116 static void
117 dumpint(uint64 v)
118 {
119         byte buf[10];
120         int32 n;
121         n = 0;
122         while(v >= 0x80) {
123                 buf[n++] = v | 0x80;
124                 v >>= 7;
125         }
126         buf[n++] = v;
127         write(buf, n);
128 }
129
130 static void
131 dumpbool(bool b)
132 {
133         dumpint(b ? 1 : 0);
134 }
135
136 // dump varint uint64 length followed by memory contents
137 static void
138 dumpmemrange(byte *data, uintptr len)
139 {
140         dumpint(len);
141         write(data, len);
142 }
143
144 static void
145 dumpstr(String s)
146 {
147         dumpmemrange(s.str, s.len);
148 }
149
150 static void
151 dumpcstr(int8 *c)
152 {
153         dumpmemrange((byte*)c, runtime·findnull((byte*)c));
154 }
155
156 // dump information for a type
157 static void
158 dumptype(Type *t)
159 {
160         TypeCacheBucket *b;
161         int32 i, j;
162
163         if(t == nil) {
164                 return;
165         }
166
167         // If we've definitely serialized the type before,
168         // no need to do it again.
169         b = &typecache[t->hash & (TypeCacheBuckets-1)];
170         if(t == b->t[0]) return;
171         for(i = 1; i < TypeCacheAssoc; i++) {
172                 if(t == b->t[i]) {
173                         // Move-to-front
174                         for(j = i; j > 0; j--) {
175                                 b->t[j] = b->t[j-1];
176                         }
177                         b->t[0] = t;
178                         return;
179                 }
180         }
181         // Might not have been dumped yet.  Dump it and
182         // remember we did so.
183         for(j = TypeCacheAssoc-1; j > 0; j--) {
184                 b->t[j] = b->t[j-1];
185         }
186         b->t[0] = t;
187         
188         // dump the type
189         dumpint(TagType);
190         dumpint((uintptr)t);
191         dumpint(t->size);
192         if(t->x == nil || t->x->pkgPath == nil || t->x->name == nil) {
193                 dumpstr(*t->string);
194         } else {
195                 dumpint(t->x->pkgPath->len + 1 + t->x->name->len);
196                 write(t->x->pkgPath->str, t->x->pkgPath->len);
197                 write((byte*)".", 1);
198                 write(t->x->name->str, t->x->name->len);
199         }
200         dumpbool((t->kind & KindDirectIface) == 0 || (t->kind & KindNoPointers) == 0);
201 }
202
203 // dump an object
204 static void
205 dumpobj(byte *obj, uintptr size, BitVector bv)
206 {
207         dumpbvtypes(&bv, obj);
208         dumpint(TagObject);
209         dumpint((uintptr)obj);
210         dumpmemrange(obj, size);
211         dumpfields(bv);
212 }
213
214 static void
215 dumpotherroot(int8 *description, byte *to)
216 {
217         dumpint(TagOtherRoot);
218         dumpcstr(description);
219         dumpint((uintptr)to);
220 }
221
222 static void
223 dumpfinalizer(byte *obj, FuncVal *fn, Type* fint, PtrType *ot)
224 {
225         dumpint(TagFinalizer);
226         dumpint((uintptr)obj);
227         dumpint((uintptr)fn);
228         dumpint((uintptr)fn->fn);
229         dumpint((uintptr)fint);
230         dumpint((uintptr)ot);
231 }
232
233 typedef struct ChildInfo ChildInfo;
234 struct ChildInfo {
235         // Information passed up from the callee frame about
236         // the layout of the outargs region.
237         uintptr argoff;     // where the arguments start in the frame
238         uintptr arglen;     // size of args region
239         BitVector args;    // if args.n >= 0, pointer map of args region
240
241         byte *sp;           // callee sp
242         uintptr depth;      // depth in call stack (0 == most recent)
243 };
244
245 // dump kinds & offsets of interesting fields in bv
246 static void
247 dumpbv(BitVector *bv, uintptr offset)
248 {
249         uintptr i;
250
251         for(i = 0; i < bv->n; i += BitsPerPointer) {
252                 switch(bv->bytedata[i/8] >> i%8 & 3) {
253                 case BitsDead:
254                         // BitsDead has already been processed in makeheapobjbv.
255                         // We should only see it in stack maps, in which case we should continue processing.
256                         break;
257                 case BitsScalar:
258                         break;
259                 case BitsPointer:
260                         dumpint(FieldKindPtr);
261                         dumpint(offset + i / BitsPerPointer * PtrSize);
262                         break;
263                 case BitsMultiWord:
264                         runtime·throw("bumpbv unexpected garbage collection bits");
265                 }
266         }
267 }
268
269 static bool
270 dumpframe(Stkframe *s, void *arg)
271 {
272         Func *f;
273         ChildInfo *child;
274         uintptr pc, off, size;
275         int32 pcdata;
276         StackMap *stackmap;
277         int8 *name;
278         BitVector bv;
279
280         child = (ChildInfo*)arg;
281         f = s->fn;
282
283         // Figure out what we can about our stack map
284         pc = s->pc;
285         if(pc != f->entry)
286                 pc--;
287         pcdata = runtime·pcdatavalue(f, PCDATA_StackMapIndex, pc);
288         if(pcdata == -1) {
289                 // We do not have a valid pcdata value but there might be a
290                 // stackmap for this function.  It is likely that we are looking
291                 // at the function prologue, assume so and hope for the best.
292                 pcdata = 0;
293         }
294         stackmap = runtime·funcdata(f, FUNCDATA_LocalsPointerMaps);
295
296         // Dump any types we will need to resolve Efaces.
297         if(child->args.n >= 0)
298                 dumpbvtypes(&child->args, (byte*)s->sp + child->argoff);
299         if(stackmap != nil && stackmap->n > 0) {
300                 bv = runtime·stackmapdata(stackmap, pcdata);
301                 dumpbvtypes(&bv, (byte*)(s->varp - bv.n / BitsPerPointer * PtrSize));
302         } else {
303                 bv.n = -1;
304         }
305
306         // Dump main body of stack frame.
307         dumpint(TagStackFrame);
308         dumpint(s->sp); // lowest address in frame
309         dumpint(child->depth); // # of frames deep on the stack
310         dumpint((uintptr)child->sp); // sp of child, or 0 if bottom of stack
311         dumpmemrange((byte*)s->sp, s->fp - s->sp);  // frame contents
312         dumpint(f->entry);
313         dumpint(s->pc);
314         dumpint(s->continpc);
315         name = runtime·funcname(f);
316         if(name == nil)
317                 name = "unknown function";
318         dumpcstr(name);
319
320         // Dump fields in the outargs section
321         if(child->args.n >= 0) {
322                 dumpbv(&child->args, child->argoff);
323         } else {
324                 // conservative - everything might be a pointer
325                 for(off = child->argoff; off < child->argoff + child->arglen; off += PtrSize) {
326                         dumpint(FieldKindPtr);
327                         dumpint(off);
328                 }
329         }
330
331         // Dump fields in the local vars section
332         if(stackmap == nil) {
333                 // No locals information, dump everything.
334                 for(off = child->arglen; off < s->varp - s->sp; off += PtrSize) {
335                         dumpint(FieldKindPtr);
336                         dumpint(off);
337                 }
338         } else if(stackmap->n < 0) {
339                 // Locals size information, dump just the locals.
340                 size = -stackmap->n;
341                 for(off = s->varp - size - s->sp; off <  s->varp - s->sp; off += PtrSize) {
342                         dumpint(FieldKindPtr);
343                         dumpint(off);
344                 }
345         } else if(stackmap->n > 0) {
346                 // Locals bitmap information, scan just the pointers in
347                 // locals.
348                 dumpbv(&bv, s->varp - bv.n / BitsPerPointer * PtrSize - s->sp);
349         }
350         dumpint(FieldKindEol);
351
352         // Record arg info for parent.
353         child->argoff = s->argp - s->fp;
354         child->arglen = s->arglen;
355         child->sp = (byte*)s->sp;
356         child->depth++;
357         stackmap = runtime·funcdata(f, FUNCDATA_ArgsPointerMaps);
358         if(stackmap != nil)
359                 child->args = runtime·stackmapdata(stackmap, pcdata);
360         else
361                 child->args.n = -1;
362         return true;
363 }
364
365 static void
366 dumpgoroutine(G *gp)
367 {
368         uintptr sp, pc, lr;
369         ChildInfo child;
370         Defer *d;
371         Panic *p;
372         bool (*fn)(Stkframe*, void*);
373
374         if(gp->syscallsp != (uintptr)nil) {
375                 sp = gp->syscallsp;
376                 pc = gp->syscallpc;
377                 lr = 0;
378         } else {
379                 sp = gp->sched.sp;
380                 pc = gp->sched.pc;
381                 lr = gp->sched.lr;
382         }
383
384         dumpint(TagGoRoutine);
385         dumpint((uintptr)gp);
386         dumpint((uintptr)sp);
387         dumpint(gp->goid);
388         dumpint(gp->gopc);
389         dumpint(runtime·readgstatus(gp));
390         dumpbool(gp->issystem);
391         dumpbool(false);  // isbackground
392         dumpint(gp->waitsince);
393         dumpstr(gp->waitreason);
394         dumpint((uintptr)gp->sched.ctxt);
395         dumpint((uintptr)gp->m);
396         dumpint((uintptr)gp->defer);
397         dumpint((uintptr)gp->panic);
398
399         // dump stack
400         child.args.n = -1;
401         child.arglen = 0;
402         child.sp = nil;
403         child.depth = 0;
404         fn = dumpframe;
405         runtime·gentraceback(pc, sp, lr, gp, 0, nil, 0x7fffffff, &fn, &child, 0);
406
407         // dump defer & panic records
408         for(d = gp->defer; d != nil; d = d->link) {
409                 dumpint(TagDefer);
410                 dumpint((uintptr)d);
411                 dumpint((uintptr)gp);
412                 dumpint((uintptr)d->argp);
413                 dumpint((uintptr)d->pc);
414                 dumpint((uintptr)d->fn);
415                 dumpint((uintptr)d->fn->fn);
416                 dumpint((uintptr)d->link);
417         }
418         for (p = gp->panic; p != nil; p = p->link) {
419                 dumpint(TagPanic);
420                 dumpint((uintptr)p);
421                 dumpint((uintptr)gp);
422                 dumpint((uintptr)p->arg.type);
423                 dumpint((uintptr)p->arg.data);
424                 dumpint(0); // was p->defer, no longer recorded
425                 dumpint((uintptr)p->link);
426         }
427 }
428
429 static void
430 dumpgs(void)
431 {
432         G *gp;
433         uint32 i;
434         uint32 status;
435
436         // goroutines & stacks
437         for(i = 0; i < runtime·allglen; i++) {
438                 gp = runtime·allg[i];
439                 status = runtime·readgstatus(gp); // The world is stopped so gp will not be in a scan state.
440                 switch(status){
441                 default:
442                         runtime·printf("runtime: unexpected G.status %d\n", status);
443                         runtime·throw("dumpgs in STW - bad status");
444                 case Gdead:
445                         break;
446                 case Grunnable:
447                 case Gsyscall:
448                 case Gwaiting:
449                         dumpgoroutine(gp);
450                         break;
451                 }
452         }
453 }
454
455 static void
456 finq_callback(FuncVal *fn, byte *obj, uintptr nret, Type *fint, PtrType *ot)
457 {
458         dumpint(TagQueuedFinalizer);
459         dumpint((uintptr)obj);
460         dumpint((uintptr)fn);
461         dumpint((uintptr)fn->fn);
462         dumpint((uintptr)fint);
463         dumpint((uintptr)ot);
464         USED(&nret);
465 }
466
467
468 static void
469 dumproots(void)
470 {
471         MSpan *s, **allspans;
472         uint32 spanidx;
473         Special *sp;
474         SpecialFinalizer *spf;
475         byte *p;
476
477         // data segment
478         dumpbvtypes(&runtime·gcdatamask, runtime·data);
479         dumpint(TagData);
480         dumpint((uintptr)runtime·data);
481         dumpmemrange(runtime·data, runtime·edata - runtime·data);
482         dumpfields(runtime·gcdatamask);
483
484         // bss segment
485         dumpbvtypes(&runtime·gcbssmask, runtime·bss);
486         dumpint(TagBss);
487         dumpint((uintptr)runtime·bss);
488         dumpmemrange(runtime·bss, runtime·ebss - runtime·bss);
489         dumpfields(runtime·gcbssmask);
490
491         // MSpan.types
492         allspans = runtime·mheap.allspans;
493         for(spanidx=0; spanidx<runtime·mheap.nspan; spanidx++) {
494                 s = allspans[spanidx];
495                 if(s->state == MSpanInUse) {
496                         // Finalizers
497                         for(sp = s->specials; sp != nil; sp = sp->next) {
498                                 if(sp->kind != KindSpecialFinalizer)
499                                         continue;
500                                 spf = (SpecialFinalizer*)sp;
501                                 p = (byte*)((s->start << PageShift) + spf->special.offset);
502                                 dumpfinalizer(p, spf->fn, spf->fint, spf->ot);
503                         }
504                 }
505         }
506
507         // Finalizer queue
508         runtime·iterate_finq(finq_callback);
509 }
510
511 // Bit vector of free marks.    
512 // Needs to be as big as the largest number of objects per span.        
513 #pragma dataflag NOPTR
514 static byte free[PageSize/8];   
515
516 static void
517 dumpobjs(void)
518 {
519         uintptr i, j, size, n;
520         MSpan *s;
521         MLink *l;
522         byte *p;
523
524         for(i = 0; i < runtime·mheap.nspan; i++) {
525                 s = runtime·mheap.allspans[i];
526                 if(s->state != MSpanInUse)
527                         continue;
528                 p = (byte*)(s->start << PageShift);
529                 size = s->elemsize;
530                 n = (s->npages << PageShift) / size;
531                 if(n > nelem(free))     
532                         runtime·throw("free array doesn't have enough entries");       
533                 for(l = s->freelist; l != nil; l = l->next)
534                         free[((byte*)l - p) / size] = true;     
535                 for(j = 0; j < n; j++, p += size) {
536                         if(free[j]) {   
537                                 free[j] = false;        
538                                 continue;       
539                         }
540                         dumpobj(p, size, makeheapobjbv(p, size));
541                 }
542         }
543 }
544
545 static void
546 dumpparams(void)
547 {
548         byte *x;
549
550         dumpint(TagParams);
551         x = (byte*)1;
552         if(*(byte*)&x == 1)
553                 dumpbool(false); // little-endian ptrs
554         else
555                 dumpbool(true); // big-endian ptrs
556         dumpint(PtrSize);
557         dumpint((uintptr)runtime·mheap.arena_start);
558         dumpint((uintptr)runtime·mheap.arena_used);
559         dumpint(thechar);
560         dumpcstr(GOEXPERIMENT);
561         dumpint(runtime·ncpu);
562 }
563
564 static void
565 itab_callback(Itab *tab)
566 {
567         Type *t;
568
569         t = tab->type;
570         // Dump a map from itab* to the type of its data field.
571         // We want this map so we can deduce types of interface referents.
572         if((t->kind & KindDirectIface) == 0) {
573                 // indirect - data slot is a pointer to t.
574                 dumptype(t->ptrto);
575                 dumpint(TagItab);
576                 dumpint((uintptr)tab);
577                 dumpint((uintptr)t->ptrto);
578         } else if((t->kind & KindNoPointers) == 0) {
579                 // t is pointer-like - data slot is a t.
580                 dumptype(t);
581                 dumpint(TagItab);
582                 dumpint((uintptr)tab);
583                 dumpint((uintptr)t);
584         } else {
585                 // Data slot is a scalar.  Dump type just for fun.
586                 // With pointer-only interfaces, this shouldn't happen.
587                 dumptype(t);
588                 dumpint(TagItab);
589                 dumpint((uintptr)tab);
590                 dumpint((uintptr)t);
591         }
592 }
593
594 static void
595 dumpitabs(void)
596 {
597         void (*fn)(Itab*);
598         
599         fn = itab_callback;
600         runtime·iterate_itabs(&fn);
601 }
602
603 static void
604 dumpms(void)
605 {
606         M *mp;
607
608         for(mp = runtime·allm; mp != nil; mp = mp->alllink) {
609                 dumpint(TagOSThread);
610                 dumpint((uintptr)mp);
611                 dumpint(mp->id);
612                 dumpint(mp->procid);
613         }
614 }
615
616 static void
617 dumpmemstats(void)
618 {
619         int32 i;
620
621         dumpint(TagMemStats);
622         dumpint(mstats.alloc);
623         dumpint(mstats.total_alloc);
624         dumpint(mstats.sys);
625         dumpint(mstats.nlookup);
626         dumpint(mstats.nmalloc);
627         dumpint(mstats.nfree);
628         dumpint(mstats.heap_alloc);
629         dumpint(mstats.heap_sys);
630         dumpint(mstats.heap_idle);
631         dumpint(mstats.heap_inuse);
632         dumpint(mstats.heap_released);
633         dumpint(mstats.heap_objects);
634         dumpint(mstats.stacks_inuse);
635         dumpint(mstats.stacks_sys);
636         dumpint(mstats.mspan_inuse);
637         dumpint(mstats.mspan_sys);
638         dumpint(mstats.mcache_inuse);
639         dumpint(mstats.mcache_sys);
640         dumpint(mstats.buckhash_sys);
641         dumpint(mstats.gc_sys);
642         dumpint(mstats.other_sys);
643         dumpint(mstats.next_gc);
644         dumpint(mstats.last_gc);
645         dumpint(mstats.pause_total_ns);
646         for(i = 0; i < 256; i++)
647                 dumpint(mstats.pause_ns[i]);
648         dumpint(mstats.numgc);
649 }
650
651 static void
652 dumpmemprof_callback(Bucket *b, uintptr nstk, uintptr *stk, uintptr size, uintptr allocs, uintptr frees)
653 {
654         uintptr i, pc;
655         Func *f;
656         byte buf[20];
657         String file;
658         int32 line;
659
660         dumpint(TagMemProf);
661         dumpint((uintptr)b);
662         dumpint(size);
663         dumpint(nstk);
664         for(i = 0; i < nstk; i++) {
665                 pc = stk[i];
666                 f = runtime·findfunc(pc);
667                 if(f == nil) {
668                         runtime·snprintf(buf, sizeof(buf), "%X", (uint64)pc);
669                         dumpcstr((int8*)buf);
670                         dumpcstr("?");
671                         dumpint(0);
672                 } else {
673                         dumpcstr(runtime·funcname(f));
674                         // TODO: Why do we need to back up to a call instruction here?
675                         // Maybe profiler should do this.
676                         if(i > 0 && pc > f->entry) {
677                                 if(thechar == '6' || thechar == '8')
678                                         pc--;
679                                 else
680                                         pc -= 4; // arm, etc
681                         }
682                         line = runtime·funcline(f, pc, &file);
683                         dumpstr(file);
684                         dumpint(line);
685                 }
686         }
687         dumpint(allocs);
688         dumpint(frees);
689 }
690
691 static void
692 dumpmemprof(void)
693 {
694         MSpan *s, **allspans;
695         uint32 spanidx;
696         Special *sp;
697         SpecialProfile *spp;
698         byte *p;
699         void (*fn)(Bucket*, uintptr, uintptr*, uintptr, uintptr, uintptr);
700         
701         fn = dumpmemprof_callback;
702         runtime·iterate_memprof(&fn);
703
704         allspans = runtime·mheap.allspans;
705         for(spanidx=0; spanidx<runtime·mheap.nspan; spanidx++) {
706                 s = allspans[spanidx];
707                 if(s->state != MSpanInUse)
708                         continue;
709                 for(sp = s->specials; sp != nil; sp = sp->next) {
710                         if(sp->kind != KindSpecialProfile)
711                                 continue;
712                         spp = (SpecialProfile*)sp;
713                         p = (byte*)((s->start << PageShift) + spp->special.offset);
714                         dumpint(TagAllocSample);
715                         dumpint((uintptr)p);
716                         dumpint((uintptr)spp->b);
717                 }
718         }
719 }
720
721 static void
722 mdump(void)
723 {
724         byte *hdr;
725         uintptr i;
726         MSpan *s;
727
728         // make sure we're done sweeping
729         for(i = 0; i < runtime·mheap.nspan; i++) {
730                 s = runtime·mheap.allspans[i];
731                 if(s->state == MSpanInUse)
732                         runtime·MSpan_EnsureSwept(s);
733         }
734
735         runtime·memclr((byte*)&typecache[0], sizeof(typecache));
736         hdr = (byte*)"go1.4 heap dump\n";
737         write(hdr, runtime·findnull(hdr));
738         dumpparams();
739         dumpitabs();
740         dumpobjs();
741         dumpgs();
742         dumpms();
743         dumproots();
744         dumpmemstats();
745         dumpmemprof();
746         dumpint(TagEOF);
747         flush();
748 }
749
750 void
751 runtime·writeheapdump_m(void)
752 {
753         uintptr fd;
754         
755         fd = g->m->scalararg[0];
756         g->m->scalararg[0] = 0;
757
758         runtime·casgstatus(g->m->curg, Grunning, Gwaiting);
759         g->waitreason = runtime·gostringnocopy((byte*)"dumping heap");
760
761         // Update stats so we can dump them.
762         // As a side effect, flushes all the MCaches so the MSpan.freelist
763         // lists contain all the free objects.
764         runtime·updatememstats(nil);
765
766         // Set dump file.
767         dumpfd = fd;
768
769         // Call dump routine.
770         mdump();
771
772         // Reset dump file.
773         dumpfd = 0;
774         if(tmpbuf != nil) {
775                 runtime·SysFree(tmpbuf, tmpbufsize, &mstats.other_sys);
776                 tmpbuf = nil;
777                 tmpbufsize = 0;
778         }
779
780         runtime·casgstatus(g->m->curg, Gwaiting, Grunning);
781 }
782
783 // dumpint() the kind & offset of each field in an object.
784 static void
785 dumpfields(BitVector bv)
786 {
787         dumpbv(&bv, 0);
788         dumpint(FieldKindEol);
789 }
790
791 // The heap dump reader needs to be able to disambiguate
792 // Eface entries.  So it needs to know every type that might
793 // appear in such an entry.  The following routine accomplishes that.
794
795 // Dump all the types that appear in the type field of
796 // any Eface described by this bit vector.
797 static void
798 dumpbvtypes(BitVector *bv, byte *base)
799 {
800         uintptr i;
801
802         for(i = 0; i < bv->n; i += BitsPerPointer) {
803                 if((bv->bytedata[i/8] >> i%8 & 3) != BitsMultiWord)
804                         continue;
805                 switch(bv->bytedata[(i+BitsPerPointer)/8] >> (i+BitsPerPointer)%8 & 3) {
806                 default:
807                         runtime·throw("unexpected garbage collection bits");
808                 case BitsIface:
809                         i += BitsPerPointer;
810                         break;
811                 case BitsEface:
812                         dumptype(*(Type**)(base + i / BitsPerPointer * PtrSize));
813                         i += BitsPerPointer;
814                         break;
815                 }
816         }
817 }
818
819 static BitVector
820 makeheapobjbv(byte *p, uintptr size)
821 {
822         uintptr off, nptr, i;
823         byte shift, *bitp, bits;
824         bool mw;
825
826         // Extend the temp buffer if necessary.
827         nptr = size/PtrSize;
828         if(tmpbufsize < nptr*BitsPerPointer/8+1) {
829                 if(tmpbuf != nil)
830                         runtime·SysFree(tmpbuf, tmpbufsize, &mstats.other_sys);
831                 tmpbufsize = nptr*BitsPerPointer/8+1;
832                 tmpbuf = runtime·sysAlloc(tmpbufsize, &mstats.other_sys);
833                 if(tmpbuf == nil)
834                         runtime·throw("heapdump: out of memory");
835         }
836
837         // Copy and compact the bitmap.
838         mw = false;
839         for(i = 0; i < nptr; i++) {
840                 off = (uintptr*)(p + i*PtrSize) - (uintptr*)runtime·mheap.arena_start;
841                 bitp = runtime·mheap.arena_start - off/wordsPerBitmapByte - 1;
842                 shift = (off % wordsPerBitmapByte) * gcBits;
843                 bits = (*bitp >> (shift + 2)) & BitsMask;
844                 if(!mw && bits == BitsDead)
845                         break;  // end of heap object
846                 mw = !mw && bits == BitsMultiWord;
847                 tmpbuf[i*BitsPerPointer/8] &= ~(BitsMask<<((i*BitsPerPointer)%8));
848                 tmpbuf[i*BitsPerPointer/8] |= bits<<((i*BitsPerPointer)%8);
849         }
850         return (BitVector){i*BitsPerPointer, tmpbuf};
851 }