//
uint32 runtime·worldsema = 1;
-// TODO: Make these per-M.
-static uint64 nhandoff;
-
static int32 gctrace;
typedef struct Workbuf Workbuf;
}
if(work.nwait == work.nproc)
return nil;
- if(i < 10)
+ if(i < 10) {
+ m->gcstats.nprocyield++;
runtime·procyield(20);
- else if(i < 20)
+ } else if(i < 20) {
+ m->gcstats.nosyield++;
runtime·osyield();
- else
+ } else {
+ m->gcstats.nsleep++;
runtime·usleep(100);
+ }
}
}
b->nobj -= n;
b1->nobj = n;
runtime·memmove(b1->obj, b->obj+b->nobj, n*sizeof b1->obj[0]);
- nhandoff += n;
+ m->gcstats.nhandoff++;
+ m->gcstats.nhandoffcnt += n;
// Put b on full list - let first half of b get stolen.
runtime·lock(&work.fmu);
}
static void
-cachestats(void)
+cachestats(GCStats *stats)
{
M *m;
MCache *c;
int32 i;
uint64 stacks_inuse;
uint64 stacks_sys;
+ uint64 *src, *dst;
+ if(stats)
+ runtime·memclr((byte*)stats, sizeof(*stats));
stacks_inuse = 0;
stacks_sys = 0;
for(m=runtime·allm; m; m=m->alllink) {
runtime·purgecachedstats(m);
stacks_inuse += m->stackalloc->inuse;
stacks_sys += m->stackalloc->sys;
+ if(stats) {
+ src = (uint64*)&m->gcstats;
+ dst = (uint64*)stats;
+ for(i=0; i<sizeof(*stats)/sizeof(uint64); i++)
+ dst[i] += src[i];
+ runtime·memclr((byte*)&m->gcstats, sizeof(m->gcstats));
+ }
c = m->mcache;
for(i=0; i<nelem(c->local_by_size); i++) {
mstats.by_size[i].nmalloc += c->local_by_size[i].nmalloc;
uint64 heap0, heap1, obj0, obj1;
byte *p;
bool extra;
+ GCStats stats;
// The gc is turned off (via enablegc) until
// the bootstrap has completed.
}
t0 = runtime·nanotime();
- nhandoff = 0;
m->gcing = 1;
runtime·stoptheworld();
- cachestats();
+ cachestats(nil);
heap0 = mstats.heap_alloc;
obj0 = mstats.nmalloc - mstats.nfree;
t2 = runtime·nanotime();
stealcache();
- cachestats();
+ cachestats(&stats);
mstats.next_gc = mstats.heap_alloc+mstats.heap_alloc*gcpercent/100;
m->gcing = 0;
- m->locks++; // disable gc during the mallocs in newproc
if(finq != nil) {
+ m->locks++; // disable gc during the mallocs in newproc
// kick off or wake up goroutine to run queued finalizers
if(fing == nil)
fing = runtime·newproc1((byte*)runfinq, nil, 0, 0, runtime·gc);
fingwait = 0;
runtime·ready(fing);
}
+ m->locks--;
}
- m->locks--;
- cachestats();
heap1 = mstats.heap_alloc;
obj1 = mstats.nmalloc - mstats.nfree;
runtime·printf("pause %D\n", t3-t0);
if(gctrace) {
- runtime·printf("gc%d(%d): %D+%D+%D ms %D -> %D MB %D -> %D (%D-%D) objects %D handoff\n",
+ runtime·printf("gc%d(%d): %D+%D+%D ms, %D -> %D MB %D -> %D (%D-%D) objects,"
+ " %D(%D) handoff, %D/%D/%D yields\n",
mstats.numgc, work.nproc, (t1-t0)/1000000, (t2-t1)/1000000, (t3-t2)/1000000,
heap0>>20, heap1>>20, obj0, obj1,
mstats.nmalloc, mstats.nfree,
- nhandoff);
+ stats.nhandoff, stats.nhandoffcnt,
+ stats.nprocyield, stats.nosyield, stats.nsleep);
}
runtime·MProf_GC();
runtime·semacquire(&runtime·worldsema);
m->gcing = 1;
runtime·stoptheworld();
- cachestats();
+ cachestats(nil);
*stats = mstats;
m->gcing = 0;
runtime·semrelease(&runtime·worldsema);
typedef struct WinCall WinCall;
typedef struct Timers Timers;
typedef struct Timer Timer;
+typedef struct GCStats GCStats;
/*
* per-cpu declaration.
byte* pc;
G* g;
};
+struct GCStats
+{
+ // the struct must consist of only uint64's,
+ // because it is casted to uint64[].
+ uint64 nhandoff;
+ uint64 nhandoffcnt;
+ uint64 nprocyield;
+ uint64 nosyield;
+ uint64 nsleep;
+};
struct G
{
byte* stackguard; // cannot move - also known to linker, libmach, runtime/cgo
uintptr waitsema; // semaphore for parking on locks
uint32 waitsemacount;
uint32 waitsemalock;
+ GCStats gcstats;
#ifdef GOOS_windows
void* thread; // thread handle