runtime: remove __go_alloc and __go_free
Move allocg and handling of allgs slice from C to Go. Reviewed-on: https://go-review.googlesource.com/34797 From-SVN: r244036
This commit is contained in:
parent
c65f76af36
commit
0f2a6e84c6
|
@ -1,4 +1,4 @@
|
||||||
eac28020ee4b2532d4cd43f448fe612e84e0a108
|
dfe446c5a54ca0febabb81b542cc4e634c6f5c30
|
||||||
|
|
||||||
The first line of this file holds the git revision number of the last
|
The first line of this file holds the git revision number of the last
|
||||||
merge done from the gofrontend repository.
|
merge done from the gofrontend repository.
|
||||||
|
|
|
@ -556,7 +556,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
|
||||||
stopTheWorld("profile")
|
stopTheWorld("profile")
|
||||||
|
|
||||||
n = 1
|
n = 1
|
||||||
for _, gp1 := range allgs() {
|
for _, gp1 := range allgs {
|
||||||
if isOK(gp1) {
|
if isOK(gp1) {
|
||||||
n++
|
n++
|
||||||
}
|
}
|
||||||
|
@ -571,7 +571,7 @@ func GoroutineProfile(p []StackRecord) (n int, ok bool) {
|
||||||
r = r[1:]
|
r = r[1:]
|
||||||
|
|
||||||
// Save other goroutines.
|
// Save other goroutines.
|
||||||
for _, gp1 := range allgs() {
|
for _, gp1 := range allgs {
|
||||||
if isOK(gp1) {
|
if isOK(gp1) {
|
||||||
if len(r) == 0 {
|
if len(r) == 0 {
|
||||||
// Should be impossible, but better to return a
|
// Should be impossible, but better to return a
|
||||||
|
|
|
@ -11,15 +11,18 @@ import (
|
||||||
|
|
||||||
// Functions temporarily called by C code.
|
// Functions temporarily called by C code.
|
||||||
//go:linkname newextram runtime.newextram
|
//go:linkname newextram runtime.newextram
|
||||||
|
//go:linkname checkdead runtime.checkdead
|
||||||
|
//go:linkname schedtrace runtime.schedtrace
|
||||||
|
//go:linkname allgadd runtime.allgadd
|
||||||
|
|
||||||
// Functions temporarily in C that have not yet been ported.
|
// Functions temporarily in C that have not yet been ported.
|
||||||
func allocm(*p, bool, *unsafe.Pointer, *uintptr) *m
|
func allocm(*p, bool, *unsafe.Pointer, *uintptr) *m
|
||||||
func malg(bool, bool, *unsafe.Pointer, *uintptr) *g
|
func malg(bool, bool, *unsafe.Pointer, *uintptr) *g
|
||||||
func allgadd(*g)
|
|
||||||
|
|
||||||
// C functions for ucontext management.
|
// C functions for ucontext management.
|
||||||
func setGContext()
|
func setGContext()
|
||||||
func makeGContext(*g, unsafe.Pointer, uintptr)
|
func makeGContext(*g, unsafe.Pointer, uintptr)
|
||||||
|
func getTraceback(me, gp *g)
|
||||||
|
|
||||||
// main_init_done is a signal used by cgocallbackg that initialization
|
// main_init_done is a signal used by cgocallbackg that initialization
|
||||||
// has been completed. It is made before _cgo_notify_runtime_init_done,
|
// has been completed. It is made before _cgo_notify_runtime_init_done,
|
||||||
|
@ -27,6 +30,39 @@ func makeGContext(*g, unsafe.Pointer, uintptr)
|
||||||
// it is closed, meaning cgocallbackg can reliably receive from it.
|
// it is closed, meaning cgocallbackg can reliably receive from it.
|
||||||
var main_init_done chan bool
|
var main_init_done chan bool
|
||||||
|
|
||||||
|
var (
|
||||||
|
allgs []*g
|
||||||
|
allglock mutex
|
||||||
|
)
|
||||||
|
|
||||||
|
func allgadd(gp *g) {
|
||||||
|
if readgstatus(gp) == _Gidle {
|
||||||
|
throw("allgadd: bad status Gidle")
|
||||||
|
}
|
||||||
|
|
||||||
|
lock(&allglock)
|
||||||
|
allgs = append(allgs, gp)
|
||||||
|
allglen = uintptr(len(allgs))
|
||||||
|
|
||||||
|
// Grow GC rescan list if necessary.
|
||||||
|
if len(allgs) > cap(work.rescan.list) {
|
||||||
|
lock(&work.rescan.lock)
|
||||||
|
l := work.rescan.list
|
||||||
|
// Let append do the heavy lifting, but keep the
|
||||||
|
// length the same.
|
||||||
|
work.rescan.list = append(l[:cap(l)], 0)[:len(l)]
|
||||||
|
unlock(&work.rescan.lock)
|
||||||
|
}
|
||||||
|
unlock(&allglock)
|
||||||
|
}
|
||||||
|
|
||||||
|
// All reads and writes of g's status go through readgstatus, casgstatus
|
||||||
|
// castogscanstatus, casfrom_Gscanstatus.
|
||||||
|
//go:nosplit
|
||||||
|
func readgstatus(gp *g) uint32 {
|
||||||
|
return atomic.Load(&gp.atomicstatus)
|
||||||
|
}
|
||||||
|
|
||||||
// If asked to move to or from a Gscanstatus this will throw. Use the castogscanstatus
|
// If asked to move to or from a Gscanstatus this will throw. Use the castogscanstatus
|
||||||
// and casfrom_Gscanstatus instead.
|
// and casfrom_Gscanstatus instead.
|
||||||
// casgstatus will loop if the g->atomicstatus is in a Gscan status until the routine that
|
// casgstatus will loop if the g->atomicstatus is in a Gscan status until the routine that
|
||||||
|
@ -328,3 +364,170 @@ func lockextra(nilokay bool) *m {
|
||||||
func unlockextra(mp *m) {
|
func unlockextra(mp *m) {
|
||||||
atomic.Storeuintptr(&extram, uintptr(unsafe.Pointer(mp)))
|
atomic.Storeuintptr(&extram, uintptr(unsafe.Pointer(mp)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check for deadlock situation.
|
||||||
|
// The check is based on number of running M's, if 0 -> deadlock.
|
||||||
|
func checkdead() {
|
||||||
|
// For -buildmode=c-shared or -buildmode=c-archive it's OK if
|
||||||
|
// there are no running goroutines. The calling program is
|
||||||
|
// assumed to be running.
|
||||||
|
if islibrary || isarchive {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are dying because of a signal caught on an already idle thread,
|
||||||
|
// freezetheworld will cause all running threads to block.
|
||||||
|
// And runtime will essentially enter into deadlock state,
|
||||||
|
// except that there is a thread that will call exit soon.
|
||||||
|
if panicking > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// -1 for sysmon
|
||||||
|
run := sched.mcount - sched.nmidle - sched.nmidlelocked - 1
|
||||||
|
if run > 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if run < 0 {
|
||||||
|
print("runtime: checkdead: nmidle=", sched.nmidle, " nmidlelocked=", sched.nmidlelocked, " mcount=", sched.mcount, "\n")
|
||||||
|
throw("checkdead: inconsistent counts")
|
||||||
|
}
|
||||||
|
|
||||||
|
grunning := 0
|
||||||
|
lock(&allglock)
|
||||||
|
for i := 0; i < len(allgs); i++ {
|
||||||
|
gp := allgs[i]
|
||||||
|
if isSystemGoroutine(gp) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
s := readgstatus(gp)
|
||||||
|
switch s &^ _Gscan {
|
||||||
|
case _Gwaiting:
|
||||||
|
grunning++
|
||||||
|
case _Grunnable,
|
||||||
|
_Grunning,
|
||||||
|
_Gsyscall:
|
||||||
|
unlock(&allglock)
|
||||||
|
print("runtime: checkdead: find g ", gp.goid, " in status ", s, "\n")
|
||||||
|
throw("checkdead: runnable g")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unlock(&allglock)
|
||||||
|
if grunning == 0 { // possible if main goroutine calls runtime·Goexit()
|
||||||
|
throw("no goroutines (main called runtime.Goexit) - deadlock!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Maybe jump time forward for playground.
|
||||||
|
gp := timejump()
|
||||||
|
if gp != nil {
|
||||||
|
// Temporarily commented out for gccgo.
|
||||||
|
// For gccgo this code will never run anyhow.
|
||||||
|
// casgstatus(gp, _Gwaiting, _Grunnable)
|
||||||
|
// globrunqput(gp)
|
||||||
|
// _p_ := pidleget()
|
||||||
|
// if _p_ == nil {
|
||||||
|
// throw("checkdead: no p for timer")
|
||||||
|
// }
|
||||||
|
// mp := mget()
|
||||||
|
// if mp == nil {
|
||||||
|
// // There should always be a free M since
|
||||||
|
// // nothing is running.
|
||||||
|
// throw("checkdead: no m for timer")
|
||||||
|
// }
|
||||||
|
// nmp.nextp.set(_p_)
|
||||||
|
// notewakeup(&mp.park)
|
||||||
|
// return
|
||||||
|
}
|
||||||
|
|
||||||
|
getg().m.throwing = -1 // do not dump full stacks
|
||||||
|
throw("all goroutines are asleep - deadlock!")
|
||||||
|
}
|
||||||
|
|
||||||
|
var starttime int64
|
||||||
|
|
||||||
|
func schedtrace(detailed bool) {
|
||||||
|
now := nanotime()
|
||||||
|
if starttime == 0 {
|
||||||
|
starttime = now
|
||||||
|
}
|
||||||
|
|
||||||
|
gomaxprocs := int32(GOMAXPROCS(0))
|
||||||
|
|
||||||
|
lock(&sched.lock)
|
||||||
|
print("SCHED ", (now-starttime)/1e6, "ms: gomaxprocs=", gomaxprocs, " idleprocs=", sched.npidle, " threads=", sched.mcount, " spinningthreads=", sched.nmspinning, " idlethreads=", sched.nmidle, " runqueue=", sched.runqsize)
|
||||||
|
if detailed {
|
||||||
|
print(" gcwaiting=", sched.gcwaiting, " nmidlelocked=", sched.nmidlelocked, " stopwait=", sched.stopwait, " sysmonwait=", sched.sysmonwait, "\n")
|
||||||
|
}
|
||||||
|
// We must be careful while reading data from P's, M's and G's.
|
||||||
|
// Even if we hold schedlock, most data can be changed concurrently.
|
||||||
|
// E.g. (p->m ? p->m->id : -1) can crash if p->m changes from non-nil to nil.
|
||||||
|
for i := int32(0); i < gomaxprocs; i++ {
|
||||||
|
_p_ := allp[i]
|
||||||
|
if _p_ == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
mp := _p_.m.ptr()
|
||||||
|
h := atomic.Load(&_p_.runqhead)
|
||||||
|
t := atomic.Load(&_p_.runqtail)
|
||||||
|
if detailed {
|
||||||
|
id := int32(-1)
|
||||||
|
if mp != nil {
|
||||||
|
id = mp.id
|
||||||
|
}
|
||||||
|
print(" P", i, ": status=", _p_.status, " schedtick=", _p_.schedtick, " syscalltick=", _p_.syscalltick, " m=", id, " runqsize=", t-h, " gfreecnt=", _p_.gfreecnt, "\n")
|
||||||
|
} else {
|
||||||
|
// In non-detailed mode format lengths of per-P run queues as:
|
||||||
|
// [len1 len2 len3 len4]
|
||||||
|
print(" ")
|
||||||
|
if i == 0 {
|
||||||
|
print("[")
|
||||||
|
}
|
||||||
|
print(t - h)
|
||||||
|
if i == gomaxprocs-1 {
|
||||||
|
print("]\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !detailed {
|
||||||
|
unlock(&sched.lock)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for mp := allm(); mp != nil; mp = mp.alllink {
|
||||||
|
_p_ := mp.p.ptr()
|
||||||
|
gp := mp.curg
|
||||||
|
lockedg := mp.lockedg
|
||||||
|
id1 := int32(-1)
|
||||||
|
if _p_ != nil {
|
||||||
|
id1 = _p_.id
|
||||||
|
}
|
||||||
|
id2 := int64(-1)
|
||||||
|
if gp != nil {
|
||||||
|
id2 = gp.goid
|
||||||
|
}
|
||||||
|
id3 := int64(-1)
|
||||||
|
if lockedg != nil {
|
||||||
|
id3 = lockedg.goid
|
||||||
|
}
|
||||||
|
print(" M", mp.id, ": p=", id1, " curg=", id2, " mallocing=", mp.mallocing, " throwing=", mp.throwing, " preemptoff=", mp.preemptoff, ""+" locks=", mp.locks, " dying=", mp.dying, " helpgc=", mp.helpgc, " spinning=", mp.spinning, " blocked=", mp.blocked, " lockedg=", id3, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
lock(&allglock)
|
||||||
|
for gi := 0; gi < len(allgs); gi++ {
|
||||||
|
gp := allgs[gi]
|
||||||
|
mp := gp.m
|
||||||
|
lockedm := gp.lockedm
|
||||||
|
id1 := int32(-1)
|
||||||
|
if mp != nil {
|
||||||
|
id1 = mp.id
|
||||||
|
}
|
||||||
|
id2 := int32(-1)
|
||||||
|
if lockedm != nil {
|
||||||
|
id2 = lockedm.id
|
||||||
|
}
|
||||||
|
print(" G", gp.goid, ": status=", readgstatus(gp), "(", gp.waitreason, ") m=", id1, " lockedm=", id2, "\n")
|
||||||
|
}
|
||||||
|
unlock(&allglock)
|
||||||
|
unlock(&sched.lock)
|
||||||
|
}
|
||||||
|
|
|
@ -755,9 +755,13 @@ const _TracebackMaxFrames = 100
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// emptystring string
|
// emptystring string
|
||||||
// allglen uintptr
|
|
||||||
|
allglen uintptr
|
||||||
|
|
||||||
// allm *m
|
// allm *m
|
||||||
// allp [_MaxGomaxprocs + 1]*p
|
|
||||||
|
allp [_MaxGomaxprocs + 1]*p
|
||||||
|
|
||||||
// gomaxprocs int32
|
// gomaxprocs int32
|
||||||
|
|
||||||
panicking uint32
|
panicking uint32
|
||||||
|
|
|
@ -476,12 +476,6 @@ func UnlockOSThread()
|
||||||
func lockOSThread()
|
func lockOSThread()
|
||||||
func unlockOSThread()
|
func unlockOSThread()
|
||||||
func allm() *m
|
func allm() *m
|
||||||
func allgs() []*g
|
|
||||||
|
|
||||||
//go:nosplit
|
|
||||||
func readgstatus(gp *g) uint32 {
|
|
||||||
return atomic.Load(&gp.atomicstatus)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporary for gccgo until we port malloc.go
|
// Temporary for gccgo until we port malloc.go
|
||||||
func persistentalloc(size, align uintptr, sysStat *uint64) unsafe.Pointer
|
func persistentalloc(size, align uintptr, sysStat *uint64) unsafe.Pointer
|
||||||
|
@ -489,9 +483,6 @@ func persistentalloc(size, align uintptr, sysStat *uint64) unsafe.Pointer
|
||||||
// Temporary for gccgo until we port mheap.go
|
// Temporary for gccgo until we port mheap.go
|
||||||
func setprofilebucket(p unsafe.Pointer, b *bucket)
|
func setprofilebucket(p unsafe.Pointer, b *bucket)
|
||||||
|
|
||||||
// Currently in proc.c.
|
|
||||||
func tracebackothers(*g)
|
|
||||||
|
|
||||||
// Temporary for gccgo until we port mgc.go.
|
// Temporary for gccgo until we port mgc.go.
|
||||||
func setgcpercent(int32) int32
|
func setgcpercent(int32) int32
|
||||||
|
|
||||||
|
@ -530,9 +521,7 @@ func getZerobase() *uintptr {
|
||||||
// Temporary for gccgo until we port proc.go.
|
// Temporary for gccgo until we port proc.go.
|
||||||
func sigprof()
|
func sigprof()
|
||||||
func mcount() int32
|
func mcount() int32
|
||||||
func gcount() int32
|
|
||||||
func goexit1()
|
func goexit1()
|
||||||
func schedtrace(bool)
|
|
||||||
func freezetheworld()
|
func freezetheworld()
|
||||||
|
|
||||||
// Get signal trampoline, written in C.
|
// Get signal trampoline, written in C.
|
||||||
|
@ -562,6 +551,30 @@ func getCgoHasExtraM() *bool {
|
||||||
return &cgoHasExtraM
|
return &cgoHasExtraM
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Temporary for gccgo until we port proc.go.
|
||||||
|
//go:linkname getAllP runtime.getAllP
|
||||||
|
func getAllP() **p {
|
||||||
|
return &allp[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary for gccgo until we port proc.go.
|
||||||
|
//go:linkname allocg runtime.allocg
|
||||||
|
func allocg() *g {
|
||||||
|
return new(g)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary for gccgo until we port the garbage collector.
|
||||||
|
//go:linkname getallglen runtime.getallglen
|
||||||
|
func getallglen() uintptr {
|
||||||
|
return allglen
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary for gccgo until we port the garbage collector.
|
||||||
|
//go:linkname getallg runtime.getallg
|
||||||
|
func getallg(i int) *g {
|
||||||
|
return allgs[i]
|
||||||
|
}
|
||||||
|
|
||||||
// Throw and rethrow an exception.
|
// Throw and rethrow an exception.
|
||||||
func throwException()
|
func throwException()
|
||||||
func rethrowException()
|
func rethrowException()
|
||||||
|
@ -579,3 +592,27 @@ func getPanicking() uint32 {
|
||||||
|
|
||||||
// Temporary for gccgo until we port mcache.go.
|
// Temporary for gccgo until we port mcache.go.
|
||||||
func allocmcache() *mcache
|
func allocmcache() *mcache
|
||||||
|
|
||||||
|
// Temporary for gccgo until we port mgc.go.
|
||||||
|
// This is just so that allgadd will compile.
|
||||||
|
var work struct {
|
||||||
|
rescan struct {
|
||||||
|
lock mutex
|
||||||
|
list []guintptr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// gcount is temporary for gccgo until more of proc.go is ported.
|
||||||
|
// This is a copy of the C function we used to use.
|
||||||
|
func gcount() int32 {
|
||||||
|
n := int32(0)
|
||||||
|
lock(&allglock)
|
||||||
|
for _, gp := range allgs {
|
||||||
|
s := readgstatus(gp)
|
||||||
|
if s == _Grunnable || s == _Grunning || s == _Gsyscall || s == _Gwaiting {
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unlock(&allglock)
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
|
@ -171,3 +171,58 @@ func isSystemGoroutine(gp *g) bool {
|
||||||
// FIXME.
|
// FIXME.
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func tracebackothers(me *g) {
|
||||||
|
var tb tracebackg
|
||||||
|
tb.gp = me
|
||||||
|
|
||||||
|
level, _, _ := gotraceback()
|
||||||
|
|
||||||
|
// Show the current goroutine first, if we haven't already.
|
||||||
|
g := getg()
|
||||||
|
gp := g.m.curg
|
||||||
|
if gp != nil && gp != me {
|
||||||
|
print("\n")
|
||||||
|
goroutineheader(gp)
|
||||||
|
gp.traceback = &tb
|
||||||
|
getTraceback(me, gp)
|
||||||
|
printtrace(tb.locbuf[:tb.c], nil)
|
||||||
|
printcreatedby(gp)
|
||||||
|
}
|
||||||
|
|
||||||
|
lock(&allglock)
|
||||||
|
for _, gp := range allgs {
|
||||||
|
if gp == me || gp == g.m.curg || readgstatus(gp) == _Gdead || isSystemGoroutine(gp) && level < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
print("\n")
|
||||||
|
goroutineheader(gp)
|
||||||
|
|
||||||
|
// gccgo's only mechanism for doing a stack trace is
|
||||||
|
// _Unwind_Backtrace. And that only works for the
|
||||||
|
// current thread, not for other random goroutines.
|
||||||
|
// So we need to switch context to the goroutine, get
|
||||||
|
// the backtrace, and then switch back.
|
||||||
|
//
|
||||||
|
// This means that if g is running or in a syscall, we
|
||||||
|
// can't reliably print a stack trace. FIXME.
|
||||||
|
|
||||||
|
// Note: gp.m == g.m occurs when tracebackothers is
|
||||||
|
// called from a signal handler initiated during a
|
||||||
|
// systemstack call. The original G is still in the
|
||||||
|
// running state, and we want to print its stack.
|
||||||
|
if gp.m != g.m && readgstatus(gp)&^_Gscan == _Grunning {
|
||||||
|
print("\tgoroutine running on other thread; stack unavailable\n")
|
||||||
|
printcreatedby(gp)
|
||||||
|
} else if readgstatus(gp)&^_Gscan == _Gsyscall {
|
||||||
|
print("\tgoroutine in C code; stack unavailable\n")
|
||||||
|
printcreatedby(gp)
|
||||||
|
} else {
|
||||||
|
gp.traceback = &tb
|
||||||
|
getTraceback(me, gp)
|
||||||
|
printtrace(tb.locbuf[:tb.c], nil)
|
||||||
|
printcreatedby(gp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unlock(&allglock)
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
#include "go-alloc.h"
|
|
||||||
#include "array.h"
|
#include "array.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "malloc.h"
|
#include "malloc.h"
|
||||||
|
|
|
@ -15,7 +15,6 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
#include "go-alloc.h"
|
|
||||||
#include "array.h"
|
#include "array.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "malloc.h"
|
#include "malloc.h"
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
Use of this source code is governed by a BSD-style
|
Use of this source code is governed by a BSD-style
|
||||||
license that can be found in the LICENSE file. */
|
license that can be found in the LICENSE file. */
|
||||||
|
|
||||||
#include "go-alloc.h"
|
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "malloc.h"
|
#include "malloc.h"
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
#include "go-alloc.h"
|
|
||||||
#include "go-assert.h"
|
#include "go-assert.h"
|
||||||
#include "go-type.h"
|
#include "go-type.h"
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,6 @@
|
||||||
#include "unwind-pe.h"
|
#include "unwind-pe.h"
|
||||||
|
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
#include "go-alloc.h"
|
|
||||||
|
|
||||||
/* The code for a Go exception. */
|
/* The code for a Go exception. */
|
||||||
|
|
||||||
|
|
|
@ -311,8 +311,8 @@ dumpgs(void)
|
||||||
uint32 i;
|
uint32 i;
|
||||||
|
|
||||||
// goroutines & stacks
|
// goroutines & stacks
|
||||||
for(i = 0; i < runtime_allglen; i++) {
|
for(i = 0; i < runtime_getallglen(); i++) {
|
||||||
gp = runtime_allg[i];
|
gp = runtime_getallg(i);
|
||||||
switch(gp->atomicstatus){
|
switch(gp->atomicstatus){
|
||||||
default:
|
default:
|
||||||
runtime_printf("unexpected G.status %d\n", gp->atomicstatus);
|
runtime_printf("unexpected G.status %d\n", gp->atomicstatus);
|
||||||
|
|
|
@ -10,7 +10,6 @@ package runtime
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "go-alloc.h"
|
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
#include "malloc.h"
|
#include "malloc.h"
|
||||||
|
@ -308,107 +307,6 @@ runtime_profilealloc(void *v, uintptr size)
|
||||||
runtime_MProf_Malloc(v, size);
|
runtime_MProf_Malloc(v, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void*
|
|
||||||
__go_alloc(uintptr size)
|
|
||||||
{
|
|
||||||
return runtime_mallocgc(size, 0, FlagNoInvokeGC);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Free the object whose base pointer is v.
|
|
||||||
void
|
|
||||||
__go_free(void *v)
|
|
||||||
{
|
|
||||||
M *m;
|
|
||||||
int32 sizeclass;
|
|
||||||
MSpan *s;
|
|
||||||
MCache *c;
|
|
||||||
uintptr size;
|
|
||||||
|
|
||||||
if(v == nil)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// If you change this also change mgc0.c:/^sweep,
|
|
||||||
// which has a copy of the guts of free.
|
|
||||||
|
|
||||||
m = runtime_m();
|
|
||||||
if(m->mallocing)
|
|
||||||
runtime_throw("malloc/free - deadlock");
|
|
||||||
m->mallocing = 1;
|
|
||||||
|
|
||||||
if(!runtime_mlookup(v, nil, nil, &s)) {
|
|
||||||
runtime_printf("free %p: not an allocated block\n", v);
|
|
||||||
runtime_throw("free runtime_mlookup");
|
|
||||||
}
|
|
||||||
size = s->elemsize;
|
|
||||||
sizeclass = s->sizeclass;
|
|
||||||
// Objects that are smaller than TinySize can be allocated using tiny alloc,
|
|
||||||
// if then such object is combined with an object with finalizer, we will crash.
|
|
||||||
if(size < TinySize)
|
|
||||||
runtime_throw("freeing too small block");
|
|
||||||
|
|
||||||
if(runtime_debug.allocfreetrace)
|
|
||||||
runtime_tracefree(v, size);
|
|
||||||
|
|
||||||
// Ensure that the span is swept.
|
|
||||||
// If we free into an unswept span, we will corrupt GC bitmaps.
|
|
||||||
runtime_MSpan_EnsureSwept(s);
|
|
||||||
|
|
||||||
if(s->specials != nil)
|
|
||||||
runtime_freeallspecials(s, v, size);
|
|
||||||
|
|
||||||
c = m->mcache;
|
|
||||||
if(sizeclass == 0) {
|
|
||||||
// Large object.
|
|
||||||
s->needzero = 1;
|
|
||||||
// Must mark v freed before calling unmarkspan and MHeap_Free:
|
|
||||||
// they might coalesce v into other spans and change the bitmap further.
|
|
||||||
runtime_markfreed(v);
|
|
||||||
runtime_unmarkspan(v, 1<<PageShift);
|
|
||||||
// NOTE(rsc,dvyukov): The original implementation of efence
|
|
||||||
// in CL 22060046 used SysFree instead of SysFault, so that
|
|
||||||
// the operating system would eventually give the memory
|
|
||||||
// back to us again, so that an efence program could run
|
|
||||||
// longer without running out of memory. Unfortunately,
|
|
||||||
// calling SysFree here without any kind of adjustment of the
|
|
||||||
// heap data structures means that when the memory does
|
|
||||||
// come back to us, we have the wrong metadata for it, either in
|
|
||||||
// the MSpan structures or in the garbage collection bitmap.
|
|
||||||
// Using SysFault here means that the program will run out of
|
|
||||||
// memory fairly quickly in efence mode, but at least it won't
|
|
||||||
// have mysterious crashes due to confused memory reuse.
|
|
||||||
// It should be possible to switch back to SysFree if we also
|
|
||||||
// implement and then call some kind of MHeap_DeleteSpan.
|
|
||||||
if(runtime_debug.efence)
|
|
||||||
runtime_SysFault((void*)(s->start<<PageShift), size);
|
|
||||||
else
|
|
||||||
runtime_MHeap_Free(&runtime_mheap, s, 1);
|
|
||||||
c->local_nlargefree++;
|
|
||||||
c->local_largefree += size;
|
|
||||||
} else {
|
|
||||||
// Small object.
|
|
||||||
if(size > 2*sizeof(uintptr))
|
|
||||||
((uintptr*)v)[1] = (uintptr)0xfeedfeedfeedfeedll; // mark as "needs to be zeroed"
|
|
||||||
else if(size > sizeof(uintptr))
|
|
||||||
((uintptr*)v)[1] = 0;
|
|
||||||
// Must mark v freed before calling MCache_Free:
|
|
||||||
// it might coalesce v and other blocks into a bigger span
|
|
||||||
// and change the bitmap further.
|
|
||||||
c->local_nsmallfree[sizeclass]++;
|
|
||||||
c->local_cachealloc -= size;
|
|
||||||
if(c->alloc[sizeclass] == s) {
|
|
||||||
// We own the span, so we can just add v to the freelist
|
|
||||||
runtime_markfreed(v);
|
|
||||||
((MLink*)v)->next = s->freelist;
|
|
||||||
s->freelist = v;
|
|
||||||
s->ref--;
|
|
||||||
} else {
|
|
||||||
// Someone else owns this span. Add to free queue.
|
|
||||||
runtime_MCache_Free(c, v, sizeclass, size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m->mallocing = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **sp)
|
runtime_mlookup(void *v, byte **base, uintptr *size, MSpan **sp)
|
||||||
{
|
{
|
||||||
|
@ -628,9 +526,6 @@ runtime_mallocinit(void)
|
||||||
// Initialize the rest of the allocator.
|
// Initialize the rest of the allocator.
|
||||||
runtime_MHeap_Init(&runtime_mheap);
|
runtime_MHeap_Init(&runtime_mheap);
|
||||||
runtime_m()->mcache = runtime_allocmcache();
|
runtime_m()->mcache = runtime_allocmcache();
|
||||||
|
|
||||||
// See if it works.
|
|
||||||
runtime_free(runtime_malloc(TinySize));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void*
|
void*
|
||||||
|
|
|
@ -1279,7 +1279,6 @@ markroot(ParFor *desc, uint32 i)
|
||||||
// For gccgo we use this for all the other global roots.
|
// For gccgo we use this for all the other global roots.
|
||||||
enqueue1(&wbuf, (Obj){(byte*)&runtime_m0, sizeof runtime_m0, 0});
|
enqueue1(&wbuf, (Obj){(byte*)&runtime_m0, sizeof runtime_m0, 0});
|
||||||
enqueue1(&wbuf, (Obj){(byte*)&runtime_g0, sizeof runtime_g0, 0});
|
enqueue1(&wbuf, (Obj){(byte*)&runtime_g0, sizeof runtime_g0, 0});
|
||||||
enqueue1(&wbuf, (Obj){(byte*)&runtime_allg, sizeof runtime_allg, 0});
|
|
||||||
enqueue1(&wbuf, (Obj){(byte*)&runtime_allm, sizeof runtime_allm, 0});
|
enqueue1(&wbuf, (Obj){(byte*)&runtime_allm, sizeof runtime_allm, 0});
|
||||||
enqueue1(&wbuf, (Obj){(byte*)&runtime_allp, sizeof runtime_allp, 0});
|
enqueue1(&wbuf, (Obj){(byte*)&runtime_allp, sizeof runtime_allp, 0});
|
||||||
enqueue1(&wbuf, (Obj){(byte*)&work, sizeof work, 0});
|
enqueue1(&wbuf, (Obj){(byte*)&work, sizeof work, 0});
|
||||||
|
@ -1334,9 +1333,9 @@ markroot(ParFor *desc, uint32 i)
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// the rest is scanning goroutine stacks
|
// the rest is scanning goroutine stacks
|
||||||
if(i - RootCount >= runtime_allglen)
|
if(i - RootCount >= runtime_getallglen())
|
||||||
runtime_throw("markroot: bad index");
|
runtime_throw("markroot: bad index");
|
||||||
gp = runtime_allg[i - RootCount];
|
gp = runtime_getallg(i - RootCount);
|
||||||
// remember when we've first observed the G blocked
|
// remember when we've first observed the G blocked
|
||||||
// needed only to output in traceback
|
// needed only to output in traceback
|
||||||
if((gp->atomicstatus == _Gwaiting || gp->atomicstatus == _Gsyscall) && gp->waitsince == 0)
|
if((gp->atomicstatus == _Gwaiting || gp->atomicstatus == _Gsyscall) && gp->waitsince == 0)
|
||||||
|
@ -2243,7 +2242,7 @@ gc(struct gc_args *args)
|
||||||
work.nwait = 0;
|
work.nwait = 0;
|
||||||
work.ndone = 0;
|
work.ndone = 0;
|
||||||
work.nproc = runtime_gcprocs();
|
work.nproc = runtime_gcprocs();
|
||||||
runtime_parforsetup(work.markfor, work.nproc, RootCount + runtime_allglen, false, &markroot_funcval);
|
runtime_parforsetup(work.markfor, work.nproc, RootCount + runtime_getallglen(), false, &markroot_funcval);
|
||||||
if(work.nproc > 1) {
|
if(work.nproc > 1) {
|
||||||
runtime_noteclear(&work.alldone);
|
runtime_noteclear(&work.alldone);
|
||||||
runtime_helpgc(work.nproc);
|
runtime_helpgc(work.nproc);
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
// Parallel for algorithm.
|
// Parallel for algorithm.
|
||||||
|
|
||||||
#include "runtime.h"
|
#include "runtime.h"
|
||||||
|
#include "malloc.h"
|
||||||
#include "arch.h"
|
#include "arch.h"
|
||||||
|
|
||||||
struct ParForThread
|
struct ParForThread
|
||||||
|
@ -27,7 +28,7 @@ runtime_parforalloc(uint32 nthrmax)
|
||||||
|
|
||||||
// The ParFor object is followed by CacheLineSize padding
|
// The ParFor object is followed by CacheLineSize padding
|
||||||
// and then nthrmax ParForThread.
|
// and then nthrmax ParForThread.
|
||||||
desc = (ParFor*)runtime_malloc(sizeof(ParFor) + CacheLineSize + nthrmax * sizeof(ParForThread));
|
desc = (ParFor*)runtime_mallocgc(sizeof(ParFor) + CacheLineSize + nthrmax * sizeof(ParForThread), 0, FlagNoInvokeGC);
|
||||||
desc->thr = (ParForThread*)((byte*)(desc+1) + CacheLineSize);
|
desc->thr = (ParForThread*)((byte*)(desc+1) + CacheLineSize);
|
||||||
desc->nthrmax = nthrmax;
|
desc->nthrmax = nthrmax;
|
||||||
return desc;
|
return desc;
|
||||||
|
|
|
@ -361,6 +361,10 @@ enum
|
||||||
extern Sched* runtime_getsched() __asm__ (GOSYM_PREFIX "runtime.getsched");
|
extern Sched* runtime_getsched() __asm__ (GOSYM_PREFIX "runtime.getsched");
|
||||||
extern bool* runtime_getCgoHasExtraM()
|
extern bool* runtime_getCgoHasExtraM()
|
||||||
__asm__ (GOSYM_PREFIX "runtime.getCgoHasExtraM");
|
__asm__ (GOSYM_PREFIX "runtime.getCgoHasExtraM");
|
||||||
|
extern P** runtime_getAllP()
|
||||||
|
__asm__ (GOSYM_PREFIX "runtime.getAllP");
|
||||||
|
extern G* allocg(void)
|
||||||
|
__asm__ (GOSYM_PREFIX "runtime.allocg");
|
||||||
|
|
||||||
Sched* runtime_sched;
|
Sched* runtime_sched;
|
||||||
int32 runtime_gomaxprocs;
|
int32 runtime_gomaxprocs;
|
||||||
|
@ -374,11 +378,6 @@ int32 runtime_ncpu;
|
||||||
bool runtime_precisestack;
|
bool runtime_precisestack;
|
||||||
static int32 newprocs;
|
static int32 newprocs;
|
||||||
|
|
||||||
static Lock allglock; // the following vars are protected by this lock or by stoptheworld
|
|
||||||
G** runtime_allg;
|
|
||||||
uintptr runtime_allglen;
|
|
||||||
static uintptr allgcap;
|
|
||||||
|
|
||||||
bool runtime_isarchive;
|
bool runtime_isarchive;
|
||||||
|
|
||||||
void* runtime_mstart(void*);
|
void* runtime_mstart(void*);
|
||||||
|
@ -403,7 +402,6 @@ static void startlockedm(G*);
|
||||||
static void sysmon(void);
|
static void sysmon(void);
|
||||||
static uint32 retake(int64);
|
static uint32 retake(int64);
|
||||||
static void incidlelocked(int32);
|
static void incidlelocked(int32);
|
||||||
static void checkdead(void);
|
|
||||||
static void exitsyscall0(G*);
|
static void exitsyscall0(G*);
|
||||||
static void park0(G*);
|
static void park0(G*);
|
||||||
static void goexit0(G*);
|
static void goexit0(G*);
|
||||||
|
@ -421,6 +419,8 @@ static bool exitsyscallfast(void);
|
||||||
|
|
||||||
void allgadd(G*)
|
void allgadd(G*)
|
||||||
__asm__(GOSYM_PREFIX "runtime.allgadd");
|
__asm__(GOSYM_PREFIX "runtime.allgadd");
|
||||||
|
void checkdead(void)
|
||||||
|
__asm__(GOSYM_PREFIX "runtime.checkdead");
|
||||||
|
|
||||||
bool runtime_isstarted;
|
bool runtime_isstarted;
|
||||||
|
|
||||||
|
@ -482,7 +482,7 @@ runtime_schedinit(void)
|
||||||
n = _MaxGomaxprocs;
|
n = _MaxGomaxprocs;
|
||||||
procs = n;
|
procs = n;
|
||||||
}
|
}
|
||||||
runtime_allp = runtime_malloc((_MaxGomaxprocs+1)*sizeof(runtime_allp[0]));
|
runtime_allp = runtime_getAllP();
|
||||||
procresize(procs);
|
procresize(procs);
|
||||||
|
|
||||||
// Can not enable GC until all roots are registered.
|
// Can not enable GC until all roots are registered.
|
||||||
|
@ -586,85 +586,25 @@ runtime_main(void* dummy __attribute__((unused)))
|
||||||
*(int32*)0 = 0;
|
*(int32*)0 = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void getTraceback(G*, G*) __asm__(GOSYM_PREFIX "runtime.getTraceback");
|
||||||
runtime_tracebackothers(G * volatile me)
|
|
||||||
|
// getTraceback stores a traceback of gp in the g's traceback field
|
||||||
|
// and then returns to me. We expect that gp's traceback is not nil.
|
||||||
|
// It works by saving me's current context, and checking gp's traceback field.
|
||||||
|
// If gp's traceback field is not nil, it starts running gp.
|
||||||
|
// In places where we call getcontext, we check the traceback field.
|
||||||
|
// If it is not nil, we collect a traceback, and then return to the
|
||||||
|
// goroutine stored in the traceback field, which is me.
|
||||||
|
void getTraceback(G* me, G* gp)
|
||||||
{
|
{
|
||||||
G * volatile gp;
|
|
||||||
Traceback tb;
|
|
||||||
int32 traceback;
|
|
||||||
Slice slice;
|
|
||||||
volatile uintptr i;
|
|
||||||
|
|
||||||
tb.gp = me;
|
|
||||||
traceback = runtime_gotraceback(nil);
|
|
||||||
|
|
||||||
// Show the current goroutine first, if we haven't already.
|
|
||||||
if((gp = g->m->curg) != nil && gp != me) {
|
|
||||||
runtime_printf("\n");
|
|
||||||
runtime_goroutineheader(gp);
|
|
||||||
gp->traceback = &tb;
|
|
||||||
|
|
||||||
#ifdef USING_SPLIT_STACK
|
#ifdef USING_SPLIT_STACK
|
||||||
__splitstack_getcontext(&me->stackcontext[0]);
|
__splitstack_getcontext(&me->stackcontext[0]);
|
||||||
#endif
|
#endif
|
||||||
getcontext(ucontext_arg(&me->context[0]));
|
getcontext(ucontext_arg(&me->stackcontext[0]));
|
||||||
|
|
||||||
if(gp->traceback != nil) {
|
if (gp->traceback != nil) {
|
||||||
runtime_gogo(gp);
|
runtime_gogo(gp);
|
||||||
}
|
|
||||||
|
|
||||||
slice.__values = &tb.locbuf[0];
|
|
||||||
slice.__count = tb.c;
|
|
||||||
slice.__capacity = tb.c;
|
|
||||||
runtime_printtrace(slice, nil);
|
|
||||||
runtime_printcreatedby(gp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime_lock(&allglock);
|
|
||||||
for(i = 0; i < runtime_allglen; i++) {
|
|
||||||
gp = runtime_allg[i];
|
|
||||||
if(gp == me || gp == g->m->curg || gp->atomicstatus == _Gdead)
|
|
||||||
continue;
|
|
||||||
if(gp->issystem && traceback < 2)
|
|
||||||
continue;
|
|
||||||
runtime_printf("\n");
|
|
||||||
runtime_goroutineheader(gp);
|
|
||||||
|
|
||||||
// Our only mechanism for doing a stack trace is
|
|
||||||
// _Unwind_Backtrace. And that only works for the
|
|
||||||
// current thread, not for other random goroutines.
|
|
||||||
// So we need to switch context to the goroutine, get
|
|
||||||
// the backtrace, and then switch back.
|
|
||||||
|
|
||||||
// This means that if g is running or in a syscall, we
|
|
||||||
// can't reliably print a stack trace. FIXME.
|
|
||||||
|
|
||||||
if(gp->atomicstatus == _Grunning) {
|
|
||||||
runtime_printf("\tgoroutine running on other thread; stack unavailable\n");
|
|
||||||
runtime_printcreatedby(gp);
|
|
||||||
} else if(gp->atomicstatus == _Gsyscall) {
|
|
||||||
runtime_printf("\tgoroutine in C code; stack unavailable\n");
|
|
||||||
runtime_printcreatedby(gp);
|
|
||||||
} else {
|
|
||||||
gp->traceback = &tb;
|
|
||||||
|
|
||||||
#ifdef USING_SPLIT_STACK
|
|
||||||
__splitstack_getcontext(&me->stackcontext[0]);
|
|
||||||
#endif
|
|
||||||
getcontext(ucontext_arg(&me->context[0]));
|
|
||||||
|
|
||||||
if(gp->traceback != nil) {
|
|
||||||
runtime_gogo(gp);
|
|
||||||
}
|
|
||||||
|
|
||||||
slice.__values = &tb.locbuf[0];
|
|
||||||
slice.__count = tb.c;
|
|
||||||
slice.__capacity = tb.c;
|
|
||||||
runtime_printtrace(slice, nil);
|
|
||||||
runtime_printcreatedby(gp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runtime_unlock(&allglock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -1067,22 +1007,6 @@ runtime_allocm(P *p, bool allocatestack, byte** ret_g0_stack, uintptr* ret_g0_st
|
||||||
return mp;
|
return mp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static G*
|
|
||||||
allocg(void)
|
|
||||||
{
|
|
||||||
G *gp;
|
|
||||||
// static Type *gtype;
|
|
||||||
|
|
||||||
// if(gtype == nil) {
|
|
||||||
// Eface e;
|
|
||||||
// runtime_gc_g_ptr(&e);
|
|
||||||
// gtype = ((PtrType*)e.__type_descriptor)->__element_type;
|
|
||||||
// }
|
|
||||||
// gp = runtime_cnew(gtype);
|
|
||||||
gp = runtime_malloc(sizeof(G));
|
|
||||||
return gp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setGContext(void) __asm__ (GOSYM_PREFIX "runtime.setGContext");
|
void setGContext(void) __asm__ (GOSYM_PREFIX "runtime.setGContext");
|
||||||
|
|
||||||
// setGContext sets up a new goroutine context for the current g.
|
// setGContext sets up a new goroutine context for the current g.
|
||||||
|
@ -2129,6 +2053,7 @@ __go_go(void (*fn)(void*), void* arg)
|
||||||
|
|
||||||
newg = runtime_malg(true, false, &sp, &malsize);
|
newg = runtime_malg(true, false, &sp, &malsize);
|
||||||
spsize = (size_t)malsize;
|
spsize = (size_t)malsize;
|
||||||
|
newg->atomicstatus = _Gdead;
|
||||||
allgadd(newg);
|
allgadd(newg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2152,31 +2077,6 @@ __go_go(void (*fn)(void*), void* arg)
|
||||||
return newg;
|
return newg;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
allgadd(G *gp)
|
|
||||||
{
|
|
||||||
G **new;
|
|
||||||
uintptr cap;
|
|
||||||
|
|
||||||
runtime_lock(&allglock);
|
|
||||||
if(runtime_allglen >= allgcap) {
|
|
||||||
cap = 4096/sizeof(new[0]);
|
|
||||||
if(cap < 2*allgcap)
|
|
||||||
cap = 2*allgcap;
|
|
||||||
new = runtime_malloc(cap*sizeof(new[0]));
|
|
||||||
if(new == nil)
|
|
||||||
runtime_throw("runtime: cannot allocate memory");
|
|
||||||
if(runtime_allg != nil) {
|
|
||||||
runtime_memmove(new, runtime_allg, runtime_allglen*sizeof(new[0]));
|
|
||||||
runtime_free(runtime_allg);
|
|
||||||
}
|
|
||||||
runtime_allg = new;
|
|
||||||
allgcap = cap;
|
|
||||||
}
|
|
||||||
runtime_allg[runtime_allglen++] = gp;
|
|
||||||
runtime_unlock(&allglock);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put on gfree list.
|
// Put on gfree list.
|
||||||
// If local list is too long, transfer a batch to the global list.
|
// If local list is too long, transfer a batch to the global list.
|
||||||
static void
|
static void
|
||||||
|
@ -2351,29 +2251,6 @@ runtime_lockedOSThread(void)
|
||||||
return g->lockedm != nil && g->m->lockedg != nil;
|
return g->lockedm != nil && g->m->lockedg != nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32
|
|
||||||
runtime_gcount(void)
|
|
||||||
{
|
|
||||||
G *gp;
|
|
||||||
int32 n, s;
|
|
||||||
uintptr i;
|
|
||||||
|
|
||||||
n = 0;
|
|
||||||
runtime_lock(&allglock);
|
|
||||||
// TODO(dvyukov): runtime.NumGoroutine() is O(N).
|
|
||||||
// We do not want to increment/decrement centralized counter in newproc/goexit,
|
|
||||||
// just to make runtime.NumGoroutine() faster.
|
|
||||||
// Compromise solution is to introduce per-P counters of active goroutines.
|
|
||||||
for(i = 0; i < runtime_allglen; i++) {
|
|
||||||
gp = runtime_allg[i];
|
|
||||||
s = gp->atomicstatus;
|
|
||||||
if(s == _Grunnable || s == _Grunning || s == _Gsyscall || s == _Gwaiting)
|
|
||||||
n++;
|
|
||||||
}
|
|
||||||
runtime_unlock(&allglock);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
int32
|
int32
|
||||||
runtime_mcount(void)
|
runtime_mcount(void)
|
||||||
{
|
{
|
||||||
|
@ -2638,59 +2515,6 @@ incidlelocked(int32 v)
|
||||||
runtime_unlock(&runtime_sched->lock);
|
runtime_unlock(&runtime_sched->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for deadlock situation.
|
|
||||||
// The check is based on number of running M's, if 0 -> deadlock.
|
|
||||||
static void
|
|
||||||
checkdead(void)
|
|
||||||
{
|
|
||||||
G *gp;
|
|
||||||
int32 run, grunning, s;
|
|
||||||
uintptr i;
|
|
||||||
|
|
||||||
// For -buildmode=c-shared or -buildmode=c-archive it's OK if
|
|
||||||
// there are no running goroutines. The calling program is
|
|
||||||
// assumed to be running.
|
|
||||||
if(runtime_isarchive) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// -1 for sysmon
|
|
||||||
run = runtime_sched->mcount - runtime_sched->nmidle - runtime_sched->nmidlelocked - 1;
|
|
||||||
if(run > 0)
|
|
||||||
return;
|
|
||||||
// If we are dying because of a signal caught on an already idle thread,
|
|
||||||
// freezetheworld will cause all running threads to block.
|
|
||||||
// And runtime will essentially enter into deadlock state,
|
|
||||||
// except that there is a thread that will call runtime_exit soon.
|
|
||||||
if(runtime_panicking() > 0)
|
|
||||||
return;
|
|
||||||
if(run < 0) {
|
|
||||||
runtime_printf("runtime: checkdead: nmidle=%d nmidlelocked=%d mcount=%d\n",
|
|
||||||
runtime_sched->nmidle, runtime_sched->nmidlelocked, runtime_sched->mcount);
|
|
||||||
runtime_throw("checkdead: inconsistent counts");
|
|
||||||
}
|
|
||||||
grunning = 0;
|
|
||||||
runtime_lock(&allglock);
|
|
||||||
for(i = 0; i < runtime_allglen; i++) {
|
|
||||||
gp = runtime_allg[i];
|
|
||||||
if(gp->isbackground)
|
|
||||||
continue;
|
|
||||||
s = gp->atomicstatus;
|
|
||||||
if(s == _Gwaiting)
|
|
||||||
grunning++;
|
|
||||||
else if(s == _Grunnable || s == _Grunning || s == _Gsyscall) {
|
|
||||||
runtime_unlock(&allglock);
|
|
||||||
runtime_printf("runtime: checkdead: find g %D in status %d\n", gp->goid, s);
|
|
||||||
runtime_throw("checkdead: runnable g");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runtime_unlock(&allglock);
|
|
||||||
if(grunning == 0) // possible if main goroutine calls runtime_Goexit()
|
|
||||||
runtime_throw("no goroutines (main called runtime.Goexit) - deadlock!");
|
|
||||||
g->m->throwing = -1; // do not dump full stacks
|
|
||||||
runtime_throw("all goroutines are asleep - deadlock!");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sysmon(void)
|
sysmon(void)
|
||||||
{
|
{
|
||||||
|
@ -2832,94 +2656,6 @@ preemptall(void)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
runtime_schedtrace(bool detailed)
|
|
||||||
{
|
|
||||||
static int64 starttime;
|
|
||||||
int64 now;
|
|
||||||
int64 id1, id2, id3;
|
|
||||||
int32 i, t, h;
|
|
||||||
uintptr gi;
|
|
||||||
const char *fmt;
|
|
||||||
M *mp, *lockedm;
|
|
||||||
G *gp, *lockedg;
|
|
||||||
P *p;
|
|
||||||
|
|
||||||
now = runtime_nanotime();
|
|
||||||
if(starttime == 0)
|
|
||||||
starttime = now;
|
|
||||||
|
|
||||||
runtime_lock(&runtime_sched->lock);
|
|
||||||
runtime_printf("SCHED %Dms: gomaxprocs=%d idleprocs=%d threads=%d idlethreads=%d runqueue=%d",
|
|
||||||
(now-starttime)/1000000, runtime_gomaxprocs, runtime_sched->npidle, runtime_sched->mcount,
|
|
||||||
runtime_sched->nmidle, runtime_sched->runqsize);
|
|
||||||
if(detailed) {
|
|
||||||
runtime_printf(" gcwaiting=%d nmidlelocked=%d nmspinning=%d stopwait=%d sysmonwait=%d\n",
|
|
||||||
runtime_sched->gcwaiting, runtime_sched->nmidlelocked, runtime_sched->nmspinning,
|
|
||||||
runtime_sched->stopwait, runtime_sched->sysmonwait);
|
|
||||||
}
|
|
||||||
// We must be careful while reading data from P's, M's and G's.
|
|
||||||
// Even if we hold schedlock, most data can be changed concurrently.
|
|
||||||
// E.g. (p->m ? p->m->id : -1) can crash if p->m changes from non-nil to nil.
|
|
||||||
for(i = 0; i < runtime_gomaxprocs; i++) {
|
|
||||||
p = runtime_allp[i];
|
|
||||||
if(p == nil)
|
|
||||||
continue;
|
|
||||||
mp = (M*)p->m;
|
|
||||||
h = runtime_atomicload(&p->runqhead);
|
|
||||||
t = runtime_atomicload(&p->runqtail);
|
|
||||||
if(detailed)
|
|
||||||
runtime_printf(" P%d: status=%d schedtick=%d syscalltick=%d m=%d runqsize=%d gfreecnt=%d\n",
|
|
||||||
i, p->status, p->schedtick, p->syscalltick, mp ? mp->id : -1, t-h, p->gfreecnt);
|
|
||||||
else {
|
|
||||||
// In non-detailed mode format lengths of per-P run queues as:
|
|
||||||
// [len1 len2 len3 len4]
|
|
||||||
fmt = " %d";
|
|
||||||
if(runtime_gomaxprocs == 1)
|
|
||||||
fmt = " [%d]\n";
|
|
||||||
else if(i == 0)
|
|
||||||
fmt = " [%d";
|
|
||||||
else if(i == runtime_gomaxprocs-1)
|
|
||||||
fmt = " %d]\n";
|
|
||||||
runtime_printf(fmt, t-h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!detailed) {
|
|
||||||
runtime_unlock(&runtime_sched->lock);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for(mp = runtime_allm; mp; mp = mp->alllink) {
|
|
||||||
p = (P*)mp->p;
|
|
||||||
gp = mp->curg;
|
|
||||||
lockedg = mp->lockedg;
|
|
||||||
id1 = -1;
|
|
||||||
if(p)
|
|
||||||
id1 = p->id;
|
|
||||||
id2 = -1;
|
|
||||||
if(gp)
|
|
||||||
id2 = gp->goid;
|
|
||||||
id3 = -1;
|
|
||||||
if(lockedg)
|
|
||||||
id3 = lockedg->goid;
|
|
||||||
runtime_printf(" M%d: p=%D curg=%D mallocing=%d throwing=%d gcing=%d"
|
|
||||||
" locks=%d dying=%d helpgc=%d spinning=%d blocked=%d lockedg=%D\n",
|
|
||||||
mp->id, id1, id2,
|
|
||||||
mp->mallocing, mp->throwing, mp->gcing, mp->locks, mp->dying, mp->helpgc,
|
|
||||||
mp->spinning, mp->blocked, id3);
|
|
||||||
}
|
|
||||||
runtime_lock(&allglock);
|
|
||||||
for(gi = 0; gi < runtime_allglen; gi++) {
|
|
||||||
gp = runtime_allg[gi];
|
|
||||||
mp = gp->m;
|
|
||||||
lockedm = gp->lockedm;
|
|
||||||
runtime_printf(" G%D: status=%d(%S) m=%d lockedm=%d\n",
|
|
||||||
gp->goid, gp->atomicstatus, gp->waitreason, mp ? mp->id : -1,
|
|
||||||
lockedm ? lockedm->id : -1);
|
|
||||||
}
|
|
||||||
runtime_unlock(&allglock);
|
|
||||||
runtime_unlock(&runtime_sched->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put mp on midle list.
|
// Put mp on midle list.
|
||||||
// Sched must be locked.
|
// Sched must be locked.
|
||||||
static void
|
static void
|
||||||
|
@ -3357,20 +3093,6 @@ runtime_go_allm()
|
||||||
return &runtime_allm;
|
return &runtime_allm;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern Slice runtime_go_allgs(void)
|
|
||||||
__asm__ (GOSYM_PREFIX "runtime.allgs");
|
|
||||||
|
|
||||||
Slice
|
|
||||||
runtime_go_allgs()
|
|
||||||
{
|
|
||||||
Slice s;
|
|
||||||
|
|
||||||
s.__values = runtime_allg;
|
|
||||||
s.__count = runtime_allglen;
|
|
||||||
s.__capacity = allgcap;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
intgo NumCPU(void) __asm__ (GOSYM_PREFIX "runtime.NumCPU");
|
intgo NumCPU(void) __asm__ (GOSYM_PREFIX "runtime.NumCPU");
|
||||||
|
|
||||||
intgo
|
intgo
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "go-assert.h"
|
#include "go-assert.h"
|
||||||
#include <complex.h>
|
#include <complex.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
@ -22,8 +23,6 @@
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "go-alloc.h"
|
|
||||||
|
|
||||||
#define _STRINGIFY2_(x) #x
|
#define _STRINGIFY2_(x) #x
|
||||||
#define _STRINGIFY_(x) _STRINGIFY2_(x)
|
#define _STRINGIFY_(x) _STRINGIFY2_(x)
|
||||||
#define GOSYM_PREFIX _STRINGIFY_(__USER_LABEL_PREFIX__)
|
#define GOSYM_PREFIX _STRINGIFY_(__USER_LABEL_PREFIX__)
|
||||||
|
@ -233,8 +232,10 @@ enum
|
||||||
*/
|
*/
|
||||||
extern uintptr* runtime_getZerobase(void)
|
extern uintptr* runtime_getZerobase(void)
|
||||||
__asm__(GOSYM_PREFIX "runtime.getZerobase");
|
__asm__(GOSYM_PREFIX "runtime.getZerobase");
|
||||||
extern G** runtime_allg;
|
extern G* runtime_getallg(intgo)
|
||||||
extern uintptr runtime_allglen;
|
__asm__(GOSYM_PREFIX "runtime.getallg");
|
||||||
|
extern uintptr runtime_getallglen(void)
|
||||||
|
__asm__(GOSYM_PREFIX "runtime.getallglen");
|
||||||
extern G* runtime_lastg;
|
extern G* runtime_lastg;
|
||||||
extern M* runtime_allm;
|
extern M* runtime_allm;
|
||||||
extern P** runtime_allp;
|
extern P** runtime_allp;
|
||||||
|
@ -309,13 +310,9 @@ MCache* runtime_allocmcache(void)
|
||||||
void runtime_freemcache(MCache*);
|
void runtime_freemcache(MCache*);
|
||||||
void runtime_mallocinit(void);
|
void runtime_mallocinit(void);
|
||||||
void runtime_mprofinit(void);
|
void runtime_mprofinit(void);
|
||||||
#define runtime_malloc(s) __go_alloc(s)
|
|
||||||
#define runtime_free(p) __go_free(p)
|
|
||||||
#define runtime_getcallersp(p) __builtin_frame_address(0)
|
#define runtime_getcallersp(p) __builtin_frame_address(0)
|
||||||
int32 runtime_mcount(void)
|
int32 runtime_mcount(void)
|
||||||
__asm__ (GOSYM_PREFIX "runtime.mcount");
|
__asm__ (GOSYM_PREFIX "runtime.mcount");
|
||||||
int32 runtime_gcount(void)
|
|
||||||
__asm__ (GOSYM_PREFIX "runtime.gcount");
|
|
||||||
void runtime_mcall(void(*)(G*));
|
void runtime_mcall(void(*)(G*));
|
||||||
uint32 runtime_fastrand1(void) __asm__ (GOSYM_PREFIX "runtime.fastrand1");
|
uint32 runtime_fastrand1(void) __asm__ (GOSYM_PREFIX "runtime.fastrand1");
|
||||||
int32 runtime_timediv(int64, int32, int32*)
|
int32 runtime_timediv(int64, int32, int32*)
|
||||||
|
|
|
@ -25,7 +25,8 @@ extern volatile intgo runtime_MemProfileRate
|
||||||
|
|
||||||
struct gotraceback_ret {
|
struct gotraceback_ret {
|
||||||
int32 level;
|
int32 level;
|
||||||
bool crash;
|
bool all;
|
||||||
|
bool crash;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern struct gotraceback_ret gotraceback(void)
|
extern struct gotraceback_ret gotraceback(void)
|
||||||
|
|
Loading…
Reference in New Issue