From 1d29bb0408506480e13afeca2ec6b8611e6a0ce7 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Fri, 24 Aug 2018 18:15:04 +0000 Subject: runtime: remove the dummy arg of getcallersp This is a port of https://golang.org/cl/109596 to the gofrontend, in preparation for updating libgo to 1.11. Original CL description: getcallersp is intrinsified, and so the dummy arg is no longer needed. Remove it, as well as a few dummy args that are solely to feed getcallersp. Reviewed-on: https://go-review.googlesource.com/131116 From-SVN: r263840 --- libgo/go/runtime/proc.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'libgo/go/runtime/proc.go') diff --git a/libgo/go/runtime/proc.go b/libgo/go/runtime/proc.go index 5826958..4c217cc 100644 --- a/libgo/go/runtime/proc.go +++ b/libgo/go/runtime/proc.go @@ -1168,7 +1168,7 @@ func kickoff() { goexit1() } -func mstart1(dummy int32) { +func mstart1() { _g_ := getg() if _g_ != _g_.m.g0 { @@ -2774,7 +2774,7 @@ func entersyscallblock_handoff() { // //go:nosplit //go:nowritebarrierrec -func exitsyscall(dummy int32) { +func exitsyscall() { _g_ := getg() _g_.m.locks++ // see comment in entersyscall @@ -2984,13 +2984,13 @@ func exitsyscallclear(gp *g) { //go:linkname syscall_entersyscall syscall.Entersyscall //go:nosplit func syscall_entersyscall() { - entersyscall(0) + entersyscall() } //go:linkname syscall_exitsyscall syscall.Exitsyscall //go:nosplit func syscall_exitsyscall() { - exitsyscall(0) + exitsyscall() } func beforefork() { -- cgit v1.1 From 2919ad1ee3bf475c8f3aae44c2aec694a9843c4d Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 13 Sep 2018 16:44:43 +0000 Subject: libgo: build roots index to speed up bulkBarrierPreWrite To reduce the amount of time spent in write barrier processing (specifically runtime.bulkBarrierPreWrite), add support for building a 'GC roots index', basically a sorted list of all roots, so as to allow more efficient lookups of gcdata structures for globals. The previous implementation worked on the raw (unsorted) roots list itself, which did not scale well. Reviewed-on: https://go-review.googlesource.com/132595 From-SVN: r264276 --- libgo/go/runtime/proc.go | 1 + 1 file changed, 1 insertion(+) (limited to 'libgo/go/runtime/proc.go') diff --git a/libgo/go/runtime/proc.go b/libgo/go/runtime/proc.go index 4c217cc..74325e3 100644 --- a/libgo/go/runtime/proc.go +++ b/libgo/go/runtime/proc.go @@ -207,6 +207,7 @@ func main() { fn := main_init // make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime fn() + createGcRootsIndex() close(main_init_done) needUnlock = false -- cgit v1.1 From f0d89c7759e7be18895868e0c4e7f9e120f7890f Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 13 Sep 2018 17:30:00 +0000 Subject: runtime: avoid write barriers with traceback info Unlike the gc runtime, libgo stores traceback information in location structs, which contain strings. Therefore, copying location structs around appears to require write barriers, although in fact write barriers are never important because the strings are never allocated in Go memory. They come from libbacktrace. Some of the generated write barriers come at times when write barriers are not permitted. For example, exitsyscall, marked nowritebarrierrec, calls exitsyscallfast which calls traceGoSysExit which calls traceEvent which calls traceStackID which calls trace.stackTab.put which copies location values into memory allocated by tab.newStack. This write barrier can be invoked when there is no p, causing a crash. This change fixes the problem by ensuring that location values are copied around in the tracing code with no write barriers. This was found by fixing the compiler to fully implement //go:nowritebarrierrec; CL to follow. Reviewed-on: https://go-review.googlesource.com/134226 From-SVN: r264282 --- libgo/go/runtime/proc.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'libgo/go/runtime/proc.go') diff --git a/libgo/go/runtime/proc.go b/libgo/go/runtime/proc.go index 74325e3..12d7071 100644 --- a/libgo/go/runtime/proc.go +++ b/libgo/go/runtime/proc.go @@ -1140,7 +1140,7 @@ func startTheWorldWithSema(emitTraceEvent bool) int64 { func kickoff() { gp := getg() - if gp.traceback != nil { + if gp.traceback != 0 { gtraceback(gp) } @@ -3097,7 +3097,7 @@ func newproc(fn uintptr, arg unsafe.Pointer) *g { } else { resetNewG(newg, &sp, &spsize) } - newg.traceback = nil + newg.traceback = 0 if readgstatus(newg) != _Gdead { throw("newproc1: new g is not Gdead") -- cgit v1.1 From cec07c4759e8af44ca77a2beb9312e4e30d1cc7a Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Thu, 13 Sep 2018 22:25:58 +0000 Subject: compiler, runtime: call gcWriteBarrier instead of writebarrierptr In 1.11 writebarrierptr is going away, so change the compiler to call gcWriteBarrier instead. We weren't using gcWriteBarrier before; adjust the implementation to use the putFast method. This revealed a problem in the kickoff function. When using cgo, kickoff can be called on the g0 of an m allocated by newExtraM. In that case the m will generally have a p, but systemstack may be called by wbBufFlush as part of flushing the write barrier buffer. At that point the buffer is full, so we can not do a write barrier. So adjust the existing code in kickoff so that in the case where we are g0, don't do any write barrier at all. Reviewed-on: https://go-review.googlesource.com/131395 From-SVN: r264295 --- libgo/go/runtime/proc.go | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) (limited to 'libgo/go/runtime/proc.go') diff --git a/libgo/go/runtime/proc.go b/libgo/go/runtime/proc.go index 12d7071..4fc45dd 100644 --- a/libgo/go/runtime/proc.go +++ b/libgo/go/runtime/proc.go @@ -1146,24 +1146,29 @@ func kickoff() { fv := gp.entry param := gp.param - gp.entry = nil // When running on the g0 stack we can wind up here without a p, - // for example from mcall(exitsyscall0) in exitsyscall. - // Setting gp.param = nil will call a write barrier, and if - // there is no p that write barrier will crash. When called from - // mcall the gp.param value will be a *g, which we don't need to - // shade since we know it will be kept alive elsewhere. In that - // case clear the field using uintptr so that the write barrier - // does nothing. - if gp.m.p == 0 { - if gp == gp.m.g0 && gp.param == unsafe.Pointer(gp.m.curg) { - *(*uintptr)(unsafe.Pointer(&gp.param)) = 0 - } else { - throw("no p in kickoff") - } + // for example from mcall(exitsyscall0) in exitsyscall, in + // which case we can not run a write barrier. + // It is also possible for us to get here from the systemstack + // call in wbBufFlush, at which point the write barrier buffer + // is full and we can not run a write barrier. + // Setting gp.entry = nil or gp.param = nil will try to run a + // write barrier, so if we are on the g0 stack due to mcall + // (systemstack calls mcall) then clear the field using uintptr. + // This is OK when gp.param is gp.m.curg, as curg will be kept + // alive elsewhere, and gp.entry always points into g, or + // to a statically allocated value, or (in the case of mcall) + // to the stack. + if gp == gp.m.g0 && gp.param == unsafe.Pointer(gp.m.curg) { + *(*uintptr)(unsafe.Pointer(&gp.entry)) = 0 + *(*uintptr)(unsafe.Pointer(&gp.param)) = 0 + } else if gp.m.p == 0 { + throw("no p in kickoff") + } else { + gp.entry = nil + gp.param = nil } - gp.param = nil fv(param) goexit1() -- cgit v1.1 From dd931d9b48647e898dc80927c532ae93cc09e192 Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Mon, 24 Sep 2018 21:46:21 +0000 Subject: libgo: update to Go 1.11 Reviewed-on: https://go-review.googlesource.com/136435 gotools/: * Makefile.am (mostlyclean-local): Run chmod on check-go-dir to make sure it is writable. (check-go-tools): Likewise. (check-vet): Copy internal/objabi to check-vet-dir. * Makefile.in: Rebuild. From-SVN: r264546 --- libgo/go/runtime/proc.go | 176 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 147 insertions(+), 29 deletions(-) (limited to 'libgo/go/runtime/proc.go') diff --git a/libgo/go/runtime/proc.go b/libgo/go/runtime/proc.go index 4fc45dd..77d379b 100644 --- a/libgo/go/runtime/proc.go +++ b/libgo/go/runtime/proc.go @@ -5,6 +5,7 @@ package runtime import ( + "internal/cpu" "runtime/internal/atomic" "runtime/internal/sys" "unsafe" @@ -169,9 +170,11 @@ func main() { // Allow newproc to start new Ms. mainStarted = true - systemstack(func() { - newm(sysmon, nil) - }) + if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon + systemstack(func() { + newm(sysmon, nil) + }) + } // Lock the main goroutine onto this, the main OS thread, // during initialization. Most programs won't care, but a few @@ -242,7 +245,7 @@ func main() { } } if atomic.Load(&panicking) != 0 { - gopark(nil, nil, "panicwait", traceEvGoStop, 1) + gopark(nil, nil, waitReasonPanicWait, traceEvGoStop, 1) } exit(0) @@ -276,7 +279,7 @@ func forcegchelper() { throw("forcegc: phase error") } atomic.Store(&forcegc.idle, 1) - goparkunlock(&forcegc.lock, "force gc (idle)", traceEvGoBlock, 1) + goparkunlock(&forcegc.lock, waitReasonForceGGIdle, traceEvGoBlock, 1) // this goroutine is explicitly resumed by sysmon if debug.gctrace > 0 { println("GC forced") @@ -291,6 +294,7 @@ func forcegchelper() { // Gosched yields the processor, allowing other goroutines to run. It does not // suspend the current goroutine, so execution resumes automatically. func Gosched() { + checkTimeouts() mcall(gosched_m) } @@ -305,7 +309,14 @@ func goschedguarded() { // If unlockf returns false, the goroutine is resumed. // unlockf must not access this G's stack, as it may be moved between // the call to gopark and the call to unlockf. -func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason string, traceEv byte, traceskip int) { +// Reason explains why the goroutine has been parked. +// It is displayed in stack traces and heap dumps. +// Reasons should be unique and descriptive. +// Do not re-use reasons, add new ones. +func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason waitReason, traceEv byte, traceskip int) { + if reason != waitReasonSleep { + checkTimeouts() // timeouts may expire while two goroutines keep the scheduler busy + } mp := acquirem() gp := mp.curg status := readgstatus(gp) @@ -324,7 +335,7 @@ func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason s // Puts the current goroutine into a waiting state and unlocks the lock. // The goroutine can be made runnable again by calling goready(gp). -func goparkunlock(lock *mutex, reason string, traceEv byte, traceskip int) { +func goparkunlock(lock *mutex, reason waitReason, traceEv byte, traceskip int) { gopark(parkunlock_c, unsafe.Pointer(lock), reason, traceEv, traceskip) } @@ -468,6 +479,37 @@ const ( _GoidCacheBatch = 16 ) +// cpuinit extracts the environment variable GODEBUGCPU from the environment on +// Linux and Darwin if the GOEXPERIMENT debugcpu was set and calls internal/cpu.Initialize. +func cpuinit() { + const prefix = "GODEBUGCPU=" + var env string + + if haveexperiment("debugcpu") && (GOOS == "linux" || GOOS == "darwin") { + cpu.DebugOptions = true + + // Similar to goenv_unix but extracts the environment value for + // GODEBUGCPU directly. + // TODO(moehrmann): remove when general goenvs() can be called before cpuinit() + n := int32(0) + for argv_index(argv, argc+1+n) != nil { + n++ + } + + for i := int32(0); i < n; i++ { + p := argv_index(argv, argc+1+i) + s := *(*string)(unsafe.Pointer(&stringStruct{unsafe.Pointer(p), findnull(p)})) + + if hasprefix(s, prefix) { + env = gostring(p)[len(prefix):] + break + } + } + } + + cpu.Initialize(env) +} + // The bootstrap sequence is: // // call osinit @@ -488,6 +530,7 @@ func schedinit() { mallocinit() mcommoninit(_g_.m) + cpuinit() // must run before alginit alginit() // maps must not be used before this call msigsave(_g_.m) @@ -778,7 +821,7 @@ func casgstatus(gp *g, oldval, newval uint32) { }) } - // See http://golang.org/cl/21503 for justification of the yield delay. + // See https://golang.org/cl/21503 for justification of the yield delay. const yieldDelay = 5 * 1000 var nextYield int64 @@ -786,9 +829,7 @@ func casgstatus(gp *g, oldval, newval uint32) { // GC time to finish and change the state to oldval. for i := 0; !atomic.Cas(&gp.atomicstatus, oldval, newval); i++ { if oldval == _Gwaiting && gp.atomicstatus == _Grunnable { - systemstack(func() { - throw("casgstatus: waiting for Gwaiting but is Grunnable") - }) + throw("casgstatus: waiting for Gwaiting but is Grunnable") } // Help GC if needed. // if gp.preemptscan && !gp.gcworkdone && (oldval == _Grunning || oldval == _Gsyscall) { @@ -826,7 +867,7 @@ func scang(gp *g, gcw *gcWork) { gp.gcscandone = false - // See http://golang.org/cl/21503 for justification of the yield delay. + // See https://golang.org/cl/21503 for justification of the yield delay. const yieldDelay = 10 * 1000 var nextYield int64 @@ -1212,7 +1253,9 @@ func mstart1() { //go:yeswritebarrierrec func mstartm0() { // Create an extra M for callbacks on threads not created by Go. - if iscgo && !cgoHasExtraM { + // An extra M is also needed on Windows for callbacks created by + // syscall.NewCallback. See issue #6751 for details. + if (iscgo || GOOS == "windows") && !cgoHasExtraM { cgoHasExtraM = true newextram() } @@ -1517,8 +1560,12 @@ func allocm(_p_ *p, fn func(), allocatestack bool) (mp *m, g0Stack unsafe.Pointe // put the m back on the list. //go:nosplit func needm(x byte) { - if iscgo && !cgoHasExtraM { + if (iscgo || GOOS == "windows") && !cgoHasExtraM { // Can happen if C/C++ code calls Go from a global ctor. + // Can also happen on Windows if a global ctor uses a + // callback created by syscall.NewCallback. See issue #6751 + // for details. + // // Can not throw, because scheduler is not initialized yet. write(2, unsafe.Pointer(&earlycgocallback[0]), int32(len(earlycgocallback))) exit(1) @@ -1814,13 +1861,16 @@ func newm1(mp *m) { // // The calling thread must itself be in a known-good state. func startTemplateThread() { + if GOARCH == "wasm" { // no threads on wasm yet + return + } if !atomic.Cas(&newmHandoff.haveTemplateThread, 0, 1) { return } newm(templateThread, nil) } -// tmeplateThread is a thread in a known-good state that exists solely +// templateThread is a thread in a known-good state that exists solely // to start new threads in known-good states when the calling thread // may not be a a good state. // @@ -2232,6 +2282,14 @@ stop: return gp, false } + // wasm only: + // Check if a goroutine is waiting for a callback from the WebAssembly host. + // If yes, pause the execution until a callback was triggered. + if pauseSchedulerUntilCallback() { + // A callback was triggered and caused at least one goroutine to wake up. + goto top + } + // Before we drop our P, make a snapshot of the allp slice, // which can change underfoot once we no longer block // safe-points. We don't need to snapshot the contents because @@ -2616,7 +2674,7 @@ func goexit0(gp *g) { gp._defer = nil // should be true already but just in case. gp._panic = nil // non-nil for Goexit during panic. points at stack-allocated data. gp.writebuf = nil - gp.waitreason = "" + gp.waitreason = 0 gp.param = nil gp.labels = nil gp.timer = nil @@ -2635,6 +2693,11 @@ func goexit0(gp *g) { gp.gcscanvalid = true dropg() + if GOARCH == "wasm" { // no threads yet on wasm + gfput(_g_.m.p.ptr(), gp) + schedule() // never returns + } + if _g_.m.lockedInt != 0 { print("invalid m->lockedInt = ", _g_.m.lockedInt, "\n") throw("internal lockOSThread error") @@ -2743,8 +2806,6 @@ func entersyscall_gcwait() { unlock(&sched.lock) } -// The same as reentersyscall(), but with a hint that the syscall is blocking. -//go:nosplit func reentersyscallblock(pc, sp uintptr) { _g_ := getg() @@ -2789,9 +2850,7 @@ func exitsyscall() { oldp := _g_.m.p.ptr() if exitsyscallfast() { if _g_.m.mcache == nil { - systemstack(func() { - throw("lost mcache") - }) + throw("lost mcache") } if trace.enabled { if oldp != _g_.m.p.ptr() || _g_.m.syscalltick != _g_.m.p.ptr().syscalltick { @@ -2836,9 +2895,7 @@ func exitsyscall() { mcall(exitsyscall0) if _g_.m.mcache == nil { - systemstack(func() { - throw("lost mcache") - }) + throw("lost mcache") } // Scheduler returned, so we're allowed to run now. @@ -3188,6 +3245,42 @@ func setSystemGoroutine() { atomic.Xadd(&expectedSystemGoroutines, -1) } +// saveAncestors copies previous ancestors of the given caller g and +// includes infor for the current caller into a new set of tracebacks for +// a g being created. +func saveAncestors(callergp *g) *[]ancestorInfo { + // Copy all prior info, except for the root goroutine (goid 0). + if debug.tracebackancestors <= 0 || callergp.goid == 0 { + return nil + } + var callerAncestors []ancestorInfo + if callergp.ancestors != nil { + callerAncestors = *callergp.ancestors + } + n := int32(len(callerAncestors)) + 1 + if n > debug.tracebackancestors { + n = debug.tracebackancestors + } + ancestors := make([]ancestorInfo, n) + copy(ancestors[1:], callerAncestors) + + var pcs [_TracebackMaxFrames]uintptr + // FIXME: This should get a traceback of callergp. + // npcs := gcallers(callergp, 0, pcs[:]) + npcs := 0 + ipcs := make([]uintptr, npcs) + copy(ipcs, pcs[:]) + ancestors[0] = ancestorInfo{ + pcs: ipcs, + goid: callergp.goid, + gopc: callergp.gopc, + } + + ancestorsp := new([]ancestorInfo) + *ancestorsp = ancestors + return ancestorsp +} + // Put on gfree list. // If local list is too long, transfer a batch to the global list. func gfput(_p_ *p, gp *g) { @@ -3265,6 +3358,9 @@ func Breakpoint() { // or else the m might be different in this function than in the caller. //go:nosplit func dolockOSThread() { + if GOARCH == "wasm" { + return // no threads on wasm yet + } _g_ := getg() _g_.m.lockedg.set(_g_) _g_.lockedm.set(_g_.m) @@ -3280,6 +3376,10 @@ func dolockOSThread() { // If the calling goroutine exits without unlocking the thread, // the thread will be terminated. // +// All init functions are run on the startup thread. Calling LockOSThread +// from an init function will cause the main function to be invoked on +// that thread. +// // A goroutine should call LockOSThread before calling OS services or // non-Go library functions that depend on per-thread state. func LockOSThread() { @@ -3309,6 +3409,9 @@ func lockOSThread() { // or else the m might be in different in this function than in the caller. //go:nosplit func dounlockOSThread() { + if GOARCH == "wasm" { + return // no threads on wasm yet + } _g_ := getg() if _g_.m.lockedInt != 0 || _g_.m.lockedExt != 0 { return @@ -3382,6 +3485,7 @@ func _ExternalCode() { _ExternalCode() } func _LostExternalCode() { _LostExternalCode() } func _GC() { _GC() } func _LostSIGPROFDuringAtomic64() { _LostSIGPROFDuringAtomic64() } +func _VDSO() { _VDSO() } // Counts SIGPROFs received while in atomic64 critical section, on mips{,le} var lostAtomic64Count uint64 @@ -3470,7 +3574,7 @@ func sigprof(pc uintptr, gp *g, mp *m) { } if prof.hz != 0 { - if (GOARCH == "mips" || GOARCH == "mipsle") && lostAtomic64Count > 0 { + if (GOARCH == "mips" || GOARCH == "mipsle" || GOARCH == "arm") && lostAtomic64Count > 0 { cpuprof.addLostAtomic64(lostAtomic64Count) lostAtomic64Count = 0 } @@ -3818,8 +3922,17 @@ func checkdead() { return } + // If we are not running under cgo, but we have an extra M then account + // for it. (It is possible to have an extra M on Windows without cgo to + // accommodate callbacks created by syscall.NewCallback. See issue #6751 + // for details.) + var run0 int32 + if !iscgo && cgoHasExtraM { + run0 = 1 + } + run := mcount() - sched.nmidle - sched.nmidlelocked - sched.nmsys - if run > 0 { + if run > run0 { return } if run < 0 { @@ -4215,7 +4328,7 @@ func schedtrace(detailed bool) { if lockedm != nil { id2 = lockedm.id } - print(" G", gp.goid, ": status=", readgstatus(gp), "(", gp.waitreason, ") m=", id1, " lockedm=", id2, "\n") + print(" G", gp.goid, ": status=", readgstatus(gp), "(", gp.waitreason.String(), ") m=", id1, " lockedm=", id2, "\n") } unlock(&allglock) unlock(&sched.lock) @@ -4375,7 +4488,7 @@ func runqempty(_p_ *p) bool { const randomizeScheduler = raceenabled // runqput tries to put g on the local runnable queue. -// If next if false, runqput adds g to the tail of the runnable queue. +// If next is false, runqput adds g to the tail of the runnable queue. // If next is true, runqput puts g in the _p_.runnext slot. // If the run queue is full, runnext puts g on the global queue. // Executed only by the owner P. @@ -4571,6 +4684,11 @@ func setMaxThreads(in int) (out int) { return } +func haveexperiment(name string) bool { + // The gofrontend does not support experiments. + return false +} + //go:nosplit func procPin() int { _g_ := getg() @@ -4618,7 +4736,7 @@ func sync_runtime_canSpin(i int) bool { // Spin only few times and only if running on a multicore machine and // GOMAXPROCS>1 and there is at least one other running P and local runq is empty. // As opposed to runtime mutex we don't do passive spinning here, - // because there can be work on global runq on on other Ps. + // because there can be work on global runq or on other Ps. if i >= active_spin || ncpu <= 1 || gomaxprocs <= int32(sched.npidle+sched.nmspinning)+1 { return false } -- cgit v1.1