Commit 548d0805 authored by Russ Cox's avatar Russ Cox

runtime: convert mprof.goc to mprof.go

The exported Go definitions appearing in mprof.go are
copied verbatim from debug.go.

The unexported Go funcs and types are new.
The C Bucket type used a union and was not a line-for-line translation.

LGTM=remyoudompheng
R=golang-codereviews, remyoudompheng
CC=dvyukov, golang-codereviews, iant, khr, r
https://golang.org/cl/137040043
parent 60be9621
......@@ -382,7 +382,7 @@ func (w *Walker) parseFile(dir, file string) (*ast.File, error) {
" maptype struct{}; _type struct{}; alg struct{};" +
" mspan struct{}; m struct{}; mutex struct{}; slicetype struct{};" +
" iface struct{}; eface struct{}; interfacetype struct{}; itab struct{};" +
" mcache struct{}; bucket struct{}; sudog struct{}; g struct{};" +
" mcache struct{}; sudog struct{}; g struct{};" +
" hchan struct{}; chantype struct{}; waitq struct{};" +
" note struct{}; wincallbackcontext struct{};" +
" gobuf struct{}; funcval struct{}; _func struct{};" +
......
......@@ -346,10 +346,11 @@ mkzruntimedefs(char *dir, char *file)
"\n"
);
// Do not emit constant definitions for these.
// Do not emit definitions for these.
vadd(&seen, "true");
vadd(&seen, "false");
vadd(&seen, "raceenabled");
vadd(&seen, "allgs");
// Run 6c -D GOOS_goos -D GOARCH_goarch -I workdir -q -n -o workdir/runtimedefs
// on each of the runtimedefs C files.
......
......@@ -239,6 +239,7 @@ selectgo(Select **selp)
G *gp;
byte *as;
void *pc;
extern uint64 runtime·blockprofilerate;
sel = *selp;
......
......@@ -50,73 +50,6 @@ func NumGoroutine() int {
func gcount() int32
// MemProfileRate controls the fraction of memory allocations
// that are recorded and reported in the memory profile.
// The profiler aims to sample an average of
// one allocation per MemProfileRate bytes allocated.
//
// To include every allocated block in the profile, set MemProfileRate to 1.
// To turn off profiling entirely, set MemProfileRate to 0.
//
// The tools that process the memory profiles assume that the
// profile rate is constant across the lifetime of the program
// and equal to the current value. Programs that change the
// memory profiling rate should do so just once, as early as
// possible in the execution of the program (for example,
// at the beginning of main).
var MemProfileRate int = 512 * 1024
// A MemProfileRecord describes the live objects allocated
// by a particular call sequence (stack trace).
type MemProfileRecord struct {
AllocBytes, FreeBytes int64 // number of bytes allocated, freed
AllocObjects, FreeObjects int64 // number of objects allocated, freed
Stack0 [32]uintptr // stack trace for this record; ends at first 0 entry
}
// InUseBytes returns the number of bytes in use (AllocBytes - FreeBytes).
func (r *MemProfileRecord) InUseBytes() int64 { return r.AllocBytes - r.FreeBytes }
// InUseObjects returns the number of objects in use (AllocObjects - FreeObjects).
func (r *MemProfileRecord) InUseObjects() int64 {
return r.AllocObjects - r.FreeObjects
}
// Stack returns the stack trace associated with the record,
// a prefix of r.Stack0.
func (r *MemProfileRecord) Stack() []uintptr {
for i, v := range r.Stack0 {
if v == 0 {
return r.Stack0[0:i]
}
}
return r.Stack0[0:]
}
// A StackRecord describes a single execution stack.
type StackRecord struct {
Stack0 [32]uintptr // stack trace for this record; ends at first 0 entry
}
// Stack returns the stack trace associated with the record,
// a prefix of r.Stack0.
func (r *StackRecord) Stack() []uintptr {
for i, v := range r.Stack0 {
if v == 0 {
return r.Stack0[0:i]
}
}
return r.Stack0[0:]
}
// GoroutineProfile returns n, the number of records in the active goroutine stack profile.
// If len(p) >= n, GoroutineProfile copies the profile into p and returns n, true.
// If len(p) < n, GoroutineProfile does not change p and returns n, false.
//
// Most clients should use the runtime/pprof package instead
// of calling GoroutineProfile directly.
func GoroutineProfile(p []StackRecord) (n int, ok bool)
// CPUProfile returns the next chunk of binary CPU profiling stack trace data,
// blocking until data is available. If profiling is turned off and all the profile
// data accumulated while it was on has been returned, CPUProfile returns nil.
......@@ -135,19 +68,3 @@ func CPUProfile() []byte
// the testing package's -test.cpuprofile flag instead of calling
// SetCPUProfileRate directly.
func SetCPUProfileRate(hz int)
// SetBlockProfileRate controls the fraction of goroutine blocking events
// that are reported in the blocking profile. The profiler aims to sample
// an average of one blocking event per rate nanoseconds spent blocked.
//
// To include every blocking event in the profile, pass rate = 1.
// To turn off profiling entirely, pass rate <= 0.
func SetBlockProfileRate(rate int)
// BlockProfileRecord describes blocking events originated
// at a particular call sequence (stack trace).
type BlockProfileRecord struct {
Count int64
Cycles int64
StackRecord
}
......@@ -11,6 +11,5 @@
#include "type.h"
#include "race.h"
#include "chan.h"
#include "mprof.h"
#include "defs_GOOS_GOARCH.h"
#include "os_GOOS.h"
......@@ -148,6 +148,12 @@ func Callers(skip int, pc []uintptr) int {
//go:noescape
func callers(int32, *uintptr, int32) int32
//go:noescape
func gcallers(*g, int32, *uintptr, int32) int32
//go:noescape
func gentraceback(uintptr, uintptr, uintptr, *g, int32, *uintptr, int32, unsafe.Pointer, unsafe.Pointer, bool) int32
func getgoroot() string
// GOROOT returns the root of the Go tree.
......
......@@ -686,8 +686,10 @@ dumpmemprof(void)
Special *sp;
SpecialProfile *spp;
byte *p;
runtime·iterate_memprof(dumpmemprof_callback);
void (*fn)(Bucket*, uintptr, uintptr*, uintptr, uintptr, uintptr);
fn = dumpmemprof_callback;
runtime·iterate_memprof(&fn);
allspans = runtime·mheap.allspans;
for(spanidx=0; spanidx<runtime·mheap.nspan; spanidx++) {
......
......@@ -397,9 +397,8 @@ func profilealloc(mp *m, x unsafe.Pointer, size uintptr) {
}
c.next_sample = next
}
mp.scalararg[0] = uintptr(size)
mp.ptrarg[0] = x
onM(&mprofMalloc_m)
mProf_Malloc(x, size)
}
// force = 1 - do GC regardless of current heap usage
......
......@@ -540,10 +540,10 @@ enum
FlagNoZero = 1<<1, // don't zero memory
};
void runtime·MProf_Malloc(void*, uintptr);
void runtime·MProf_Free(Bucket*, uintptr, bool);
void runtime·MProf_GC(void);
void runtime·iterate_memprof(void (*callback)(Bucket*, uintptr, uintptr*, uintptr, uintptr, uintptr));
void runtime·mProf_Malloc(void*, uintptr);
void runtime·mProf_Free(Bucket*, uintptr, bool);
void runtime·mProf_GC(void);
void runtime·iterate_memprof(void (**callback)(Bucket*, uintptr, uintptr*, uintptr, uintptr, uintptr));
int32 runtime·gcprocs(void);
void runtime·helpgc(int32 nproc);
void runtime·gchelper(void);
......
......@@ -1438,7 +1438,7 @@ gc(struct gc_args *args)
sweep.npausesweep++;
}
runtime·MProf_GC();
runtime·mProf_GC();
g->m->traceback = 0;
}
......
......@@ -861,7 +861,7 @@ runtime·freespecial(Special *s, void *p, uintptr size, bool freed)
return false; // don't free p until finalizer is done
case KindSpecialProfile:
sp = (SpecialProfile*)s;
runtime·MProf_Free(sp->b, size, freed);
runtime·mProf_Free(sp->b, size, freed);
runtime·lock(&runtime·mheap.speciallock);
runtime·FixAlloc_Free(&runtime·mheap.specialprofilealloc, sp);
runtime·unlock(&runtime·mheap.speciallock);
......
This diff is collapsed.
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Malloc profiling.
// Patterned after tcmalloc's algorithms; shorter code.
package runtime
#include "runtime.h"
#include "arch_GOARCH.h"
#include "malloc.h"
#include "mprof.h"
#include "defs_GOOS_GOARCH.h"
#include "type.h"
// NOTE(rsc): Everything here could use cas if contention became an issue.
extern Mutex runtime·proflock;
// All memory allocations are local and do not escape outside of the profiler.
// The profiler is forbidden from referring to garbage-collected memory.
enum { MProf, BProf }; // profile types
enum {
BuckHashSize = 179999,
};
static Bucket **buckhash;
extern Bucket *runtime·mbuckets; // memory profile buckets
extern Bucket *runtime·bbuckets; // blocking profile buckets
static uintptr bucketmem;
// Return the bucket for stk[0:nstk], allocating new bucket if needed.
static Bucket*
stkbucket(int32 typ, uintptr size, uintptr *stk, int32 nstk, bool alloc)
{
int32 i;
uintptr h;
Bucket *b;
if(buckhash == nil) {
buckhash = runtime·sysAlloc(BuckHashSize*sizeof buckhash[0], &mstats.buckhash_sys);
if(buckhash == nil)
runtime·throw("runtime: cannot allocate memory");
}
// Hash stack.
h = 0;
for(i=0; i<nstk; i++) {
h += stk[i];
h += h<<10;
h ^= h>>6;
}
// hash in size
h += size;
h += h<<10;
h ^= h>>6;
// finalize
h += h<<3;
h ^= h>>11;
i = h%BuckHashSize;
for(b = buckhash[i]; b; b=b->next)
if(b->typ == typ && b->hash == h && b->size == size && b->nstk == nstk &&
runtime·mcmp((byte*)b->stk, (byte*)stk, nstk*sizeof stk[0]) == 0)
return b;
if(!alloc)
return nil;
b = runtime·persistentalloc(sizeof *b + nstk*sizeof stk[0], 0, &mstats.buckhash_sys);
bucketmem += sizeof *b + nstk*sizeof stk[0];
runtime·memmove(b->stk, stk, nstk*sizeof stk[0]);
b->typ = typ;
b->hash = h;
b->size = size;
b->nstk = nstk;
b->next = buckhash[i];
buckhash[i] = b;
if(typ == MProf) {
b->allnext = runtime·mbuckets;
runtime·mbuckets = b;
} else {
b->allnext = runtime·bbuckets;
runtime·bbuckets = b;
}
return b;
}
static void
MProf_GC(void)
{
Bucket *b;
for(b=runtime·mbuckets; b; b=b->allnext) {
b->data.mp.allocs += b->data.mp.prev_allocs;
b->data.mp.frees += b->data.mp.prev_frees;
b->data.mp.alloc_bytes += b->data.mp.prev_alloc_bytes;
b->data.mp.free_bytes += b->data.mp.prev_free_bytes;
b->data.mp.prev_allocs = b->data.mp.recent_allocs;
b->data.mp.prev_frees = b->data.mp.recent_frees;
b->data.mp.prev_alloc_bytes = b->data.mp.recent_alloc_bytes;
b->data.mp.prev_free_bytes = b->data.mp.recent_free_bytes;
b->data.mp.recent_allocs = 0;
b->data.mp.recent_frees = 0;
b->data.mp.recent_alloc_bytes = 0;
b->data.mp.recent_free_bytes = 0;
}
}
// Record that a gc just happened: all the 'recent' statistics are now real.
void
runtime·MProf_GC(void)
{
runtime·lock(&runtime·proflock);
MProf_GC();
runtime·unlock(&runtime·proflock);
}
// Called by malloc to record a profiled block.
void
runtime·MProf_Malloc(void *p, uintptr size)
{
uintptr stk[32];
Bucket *b;
int32 nstk;
nstk = runtime·callers(1, stk, nelem(stk));
runtime·lock(&runtime·proflock);
b = stkbucket(MProf, size, stk, nstk, true);
b->data.mp.recent_allocs++;
b->data.mp.recent_alloc_bytes += size;
runtime·unlock(&runtime·proflock);
// Setprofilebucket locks a bunch of other mutexes, so we call it outside of proflock.
// This reduces potential contention and chances of deadlocks.
// Since the object must be alive during call to MProf_Malloc,
// it's fine to do this non-atomically.
runtime·setprofilebucket(p, b);
}
// Called by malloc to record a profiled block.
void
runtime·mprofMalloc_m(void)
{
uintptr stk[32];
Bucket *b;
int32 nstk;
uintptr size;
void *p;
size = g->m->scalararg[0];
p = g->m->ptrarg[0];
g->m->ptrarg[0] = nil;
if(g->m->curg == nil)
nstk = runtime·callers(1, stk, nelem(stk));
else
nstk = runtime·gcallers(g->m->curg, 1, stk, nelem(stk));
runtime·lock(&runtime·proflock);
b = stkbucket(MProf, size, stk, nstk, true);
b->data.mp.recent_allocs++;
b->data.mp.recent_alloc_bytes += size;
runtime·unlock(&runtime·proflock);
// Setprofilebucket locks a bunch of other mutexes, so we call it outside of proflock.
// This reduces potential contention and chances of deadlocks.
// Since the object must be alive during call to MProf_Malloc,
// it's fine to do this non-atomically.
runtime·setprofilebucket(p, b);
}
// Called when freeing a profiled block.
void
runtime·MProf_Free(Bucket *b, uintptr size, bool freed)
{
runtime·lock(&runtime·proflock);
if(freed) {
b->data.mp.recent_frees++;
b->data.mp.recent_free_bytes += size;
} else {
b->data.mp.prev_frees++;
b->data.mp.prev_free_bytes += size;
}
runtime·unlock(&runtime·proflock);
}
int64 runtime·blockprofilerate; // in CPU ticks
void
runtime·SetBlockProfileRate(intgo rate)
{
int64 r;
if(rate <= 0)
r = 0; // disable profiling
else {
// convert ns to cycles, use float64 to prevent overflow during multiplication
r = (float64)rate*runtime·tickspersecond()/(1000*1000*1000);
if(r == 0)
r = 1;
}
runtime·atomicstore64((uint64*)&runtime·blockprofilerate, r);
}
void
runtime·blockevent(int64 cycles, int32 skip)
{
int32 nstk;
int64 rate;
uintptr stk[32];
Bucket *b;
if(cycles <= 0)
return;
rate = runtime·atomicload64((uint64*)&runtime·blockprofilerate);
if(rate <= 0 || (rate > cycles && runtime·fastrand1()%rate > cycles))
return;
if(g->m->curg == nil || g->m->curg == g)
nstk = runtime·callers(skip, stk, nelem(stk));
else
nstk = runtime·gcallers(g->m->curg, skip, stk, nelem(stk));
runtime·lock(&runtime·proflock);
b = stkbucket(BProf, 0, stk, nstk, true);
b->data.bp.count++;
b->data.bp.cycles += cycles;
runtime·unlock(&runtime·proflock);
}
void
runtime·iterate_memprof(void (*callback)(Bucket*, uintptr, uintptr*, uintptr, uintptr, uintptr))
{
Bucket *b;
runtime·lock(&runtime·proflock);
for(b=runtime·mbuckets; b; b=b->allnext) {
callback(b, b->nstk, b->stk, b->size, b->data.mp.allocs, b->data.mp.frees);
}
runtime·unlock(&runtime·proflock);
}
// Go interface to profile data. (Declared in debug.go)
// Must match StackRecord in debug.go.
typedef struct TRecord TRecord;
struct TRecord {
uintptr stk[32];
};
static void
saveg(uintptr pc, uintptr sp, G *gp, TRecord *r)
{
int32 n;
n = runtime·gentraceback(pc, sp, 0, gp, 0, r->stk, nelem(r->stk), nil, nil, false);
if(n < nelem(r->stk))
r->stk[n] = 0;
}
func GoroutineProfile(b Slice) (n int, ok bool) {
uintptr pc, sp, i;
TRecord *r;
G *gp;
sp = runtime·getcallersp(&b);
pc = (uintptr)runtime·getcallerpc(&b);
ok = false;
n = runtime·gcount();
if(n <= b.len) {
runtime·semacquire(&runtime·worldsema, false);
g->m->gcing = 1;
runtime·stoptheworld();
n = runtime·gcount();
if(n <= b.len) {
ok = true;
r = (TRecord*)b.array;
saveg(pc, sp, g, r++);
for(i = 0; i < runtime·allglen; i++) {
gp = runtime·allg[i];
if(gp == g || runtime·readgstatus(gp) == Gdead)
continue;
saveg(~(uintptr)0, ~(uintptr)0, gp, r++);
}
}
g->m->gcing = 0;
runtime·semrelease(&runtime·worldsema);
runtime·starttheworld();
}
}
// Tracing of alloc/free/gc.
static Mutex tracelock;
void
runtime·tracealloc(void *p, uintptr size, Type *type)
{
runtime·lock(&tracelock);
g->m->traceback = 2;
if(type == nil)
runtime·printf("tracealloc(%p, %p)\n", p, size);
else
runtime·printf("tracealloc(%p, %p, %S)\n", p, size, *type->string);
if(g->m->curg == nil || g == g->m->curg) {
runtime·goroutineheader(g);
runtime·traceback((uintptr)runtime·getcallerpc(&p), (uintptr)runtime·getcallersp(&p), 0, g);
} else {
runtime·goroutineheader(g->m->curg);
runtime·traceback(~(uintptr)0, ~(uintptr)0, 0, g->m->curg);
}
runtime·printf("\n");
g->m->traceback = 0;
runtime·unlock(&tracelock);
}
void
runtime·tracefree(void *p, uintptr size)
{
runtime·lock(&tracelock);
g->m->traceback = 2;
runtime·printf("tracefree(%p, %p)\n", p, size);
runtime·goroutineheader(g);
runtime·traceback((uintptr)runtime·getcallerpc(&p), (uintptr)runtime·getcallersp(&p), 0, g);
runtime·printf("\n");
g->m->traceback = 0;
runtime·unlock(&tracelock);
}
void
runtime·tracegc(void)
{
runtime·lock(&tracelock);
g->m->traceback = 2;
runtime·printf("tracegc()\n");
// running on m->g0 stack; show all non-g0 goroutines
runtime·tracebackothers(g);
runtime·printf("end tracegc\n");
runtime·printf("\n");
g->m->traceback = 0;
runtime·unlock(&tracelock);
}
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Per-call-stack profiling information.
// Lookup by hashing call stack into a linked-list hash table.
struct Bucket
{
Bucket *next; // next in hash list
Bucket *allnext; // next in list of all mbuckets/bbuckets
int32 typ;
// Generally unions can break precise GC,
// this one is fine because it does not contain pointers.
union
{
struct MProfRecord // typ == MProf
{
// The following complex 3-stage scheme of stats accumulation
// is required to obtain a consistent picture of mallocs and frees
// for some point in time.
// The problem is that mallocs come in real time, while frees
// come only after a GC during concurrent sweeping. So if we would
// naively count them, we would get a skew toward mallocs.
//
// Mallocs are accounted in recent stats.
// Explicit frees are accounted in recent stats.
// GC frees are accounted in prev stats.
// After GC prev stats are added to final stats and
// recent stats are moved into prev stats.
uintptr allocs;
uintptr frees;
uintptr alloc_bytes;
uintptr free_bytes;
uintptr prev_allocs; // since last but one till last gc
uintptr prev_frees;
uintptr prev_alloc_bytes;
uintptr prev_free_bytes;
uintptr recent_allocs; // since last gc till now
uintptr recent_frees;
uintptr recent_alloc_bytes;
uintptr recent_free_bytes;
} mp;
struct BProfRecord // typ == BProf
{
int64 count;
int64 cycles;
} bp;
} data;
uintptr hash; // hash of size + stk
uintptr size;
uintptr nstk;
uintptr stk[1];
};
......@@ -83,6 +83,7 @@ static int32 newprocs;
static Mutex allglock; // the following vars are protected by this lock or by stoptheworld
G** runtime·allg;
Slice runtime·allgs;
uintptr runtime·allglen;
static uintptr allgcap;
ForceGCState runtime·forcegc;
......@@ -2131,9 +2132,12 @@ allgadd(G *gp)
if(runtime·allg != nil)
runtime·memmove(new, runtime·allg, runtime·allglen*sizeof(new[0]));
runtime·allg = new;
runtime·allgs.array = (void*)runtime·allg;
allgcap = cap;
runtime·allgs.cap = allgcap;
}
runtime·allg[runtime·allglen++] = gp;
runtime·allgs.len = runtime·allglen;
runtime·unlock(&allglock);
}
......
......@@ -683,6 +683,7 @@ enum
extern String runtime·emptystring;
extern uintptr runtime·zerobase;
extern G** runtime·allg;
extern Slice runtime·allgs; // []*G
extern uintptr runtime·allglen;
extern G* runtime·lastg;
extern M* runtime·allm;
......@@ -868,7 +869,6 @@ void runtime·usleep(uint32);
int64 runtime·cputicks(void);
int64 runtime·tickspersecond(void);
void runtime·blockevent(int64, int32);
extern int64 runtime·blockprofilerate;
G* runtime·netpoll(bool);
void runtime·netpollinit(void);
int32 runtime·netpollopen(uintptr, PollDesc*);
......
......@@ -74,7 +74,6 @@ func onM(fn *mFunction)
var (
mcacheRefill_m,
largeAlloc_m,
mprofMalloc_m,
gc_m,
scavenge_m,
setFinalizer_m,
......@@ -89,15 +88,12 @@ var (
park_m mFunction
)
func blockevent(int64, int32)
// memclr clears n bytes starting at ptr.
// in memclr_*.s
//go:noescape
func memclr(ptr unsafe.Pointer, n uintptr)
func racemalloc(p unsafe.Pointer, size uintptr)
func tracealloc(p unsafe.Pointer, size uintptr, typ *_type)
// memmove copies n bytes from "from" to "to".
// in memmove_*.s
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment