Source File
traceruntime.go
Belonging Package
runtime
// Copyright 2023 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.// Runtime -> tracer API.package runtimeimport (_ // for go:linkname)// gTraceState is per-G state for the tracer.type gTraceState struct {traceSchedResourceState}// reset resets the gTraceState for a new goroutine.func ( *gTraceState) () {.seq = [2]uint64{}// N.B. s.statusTraced is managed and cleared separately.}// mTraceState is per-M state for the tracer.type mTraceState struct {seqlock atomic.Uintptr // seqlock indicating that this M is writing to a trace buffer.buf [2][tracev2.NumExperiments]*traceBuf // Per-M traceBuf for writing. Indexed by trace.gen%2.link *m // Snapshot of alllink or freelink.reentered uint32 // Whether we've reentered tracing from within tracing.oldthrowsplit bool // gp.throwsplit upon calling traceLocker.writer. For debugging.}// pTraceState is per-P state for the tracer.type pTraceState struct {traceSchedResourceState// mSyscallID is the ID of the M this was bound to before entering a syscall.mSyscallID int64// maySweep indicates the sweep events should be traced.// This is used to defer the sweep start event until a span// has actually been swept.maySweep bool// inSweep indicates that at least one sweep event has been traced.inSweep bool// swept and reclaimed track the number of bytes swept and reclaimed// by sweeping in the current sweep loop (while maySweep was true).swept, reclaimed uintptr}// traceLockInit initializes global trace locks.func traceLockInit() {// Sharing a lock rank here is fine because they should never be accessed// together. If they are, we want to find out immediately.lockInit(&trace.stringTab[0].lock, lockRankTraceStrings)lockInit(&trace.stringTab[0].tab.mem.lock, lockRankTraceStrings)lockInit(&trace.stringTab[1].lock, lockRankTraceStrings)lockInit(&trace.stringTab[1].tab.mem.lock, lockRankTraceStrings)lockInit(&trace.stackTab[0].tab.mem.lock, lockRankTraceStackTab)lockInit(&trace.stackTab[1].tab.mem.lock, lockRankTraceStackTab)lockInit(&trace.typeTab[0].tab.mem.lock, lockRankTraceTypeTab)lockInit(&trace.typeTab[1].tab.mem.lock, lockRankTraceTypeTab)lockInit(&trace.lock, lockRankTrace)}// lockRankMayTraceFlush records the lock ranking effects of a// potential call to traceFlush.//// nosplit because traceAcquire is nosplit.////go:nosplitfunc lockRankMayTraceFlush() {lockWithRankMayAcquire(&trace.lock, getLockRank(&trace.lock))}// traceBlockReason is an enumeration of reasons a goroutine might block.// This is the interface the rest of the runtime uses to tell the// tracer why a goroutine blocked. The tracer then propagates this information// into the trace however it sees fit.//// Note that traceBlockReasons should not be compared, since reasons that are// distinct by name may *not* be distinct by value.type traceBlockReason uint8const (traceBlockGeneric traceBlockReason = iotatraceBlockForevertraceBlockNettraceBlockSelecttraceBlockCondWaittraceBlockSynctraceBlockChanSendtraceBlockChanRecvtraceBlockGCMarkAssisttraceBlockGCSweeptraceBlockSystemGoroutinetraceBlockPreemptedtraceBlockDebugCalltraceBlockUntilGCEndstraceBlockSleeptraceBlockGCWeakToStrongWaittraceBlockSynctest)var traceBlockReasonStrings = [...]string{traceBlockGeneric: "unspecified",traceBlockForever: "forever",traceBlockNet: "network",traceBlockSelect: "select",traceBlockCondWait: "sync.(*Cond).Wait",traceBlockSync: "sync",traceBlockChanSend: "chan send",traceBlockChanRecv: "chan receive",traceBlockGCMarkAssist: "GC mark assist wait for work",traceBlockGCSweep: "GC background sweeper wait",traceBlockSystemGoroutine: "system goroutine wait",traceBlockPreempted: "preempted",traceBlockDebugCall: "wait for debug call",traceBlockUntilGCEnds: "wait until GC ends",traceBlockSleep: "sleep",traceBlockGCWeakToStrongWait: "GC weak to strong wait",traceBlockSynctest: "synctest",}// traceGoStopReason is an enumeration of reasons a goroutine might yield.//// Note that traceGoStopReasons should not be compared, since reasons that are// distinct by name may *not* be distinct by value.type traceGoStopReason uint8const (traceGoStopGeneric traceGoStopReason = iotatraceGoStopGoSchedtraceGoStopPreempted)var traceGoStopReasonStrings = [...]string{traceGoStopGeneric: "unspecified",traceGoStopGoSched: "runtime.Gosched",traceGoStopPreempted: "preempted",}// traceEnabled returns true if the trace is currently enabled.////go:nosplitfunc traceEnabled() bool {return trace.enabled}// traceAllocFreeEnabled returns true if the trace is currently enabled// and alloc/free events are also enabled.////go:nosplitfunc traceAllocFreeEnabled() bool {return trace.enabledWithAllocFree}// traceShuttingDown returns true if the trace is currently shutting down.func traceShuttingDown() bool {return trace.shutdown.Load()}// traceLocker represents an M writing trace events. While a traceLocker value// is valid, the tracer observes all operations on the G/M/P or trace events being// written as happening atomically.type traceLocker struct {mp *mgen uintptr}// debugTraceReentrancy checks if the trace is reentrant.//// This is optional because throwing in a function makes it instantly// not inlineable, and we want traceAcquire to be inlineable for// low overhead when the trace is disabled.const debugTraceReentrancy = false// traceAcquire prepares this M for writing one or more trace events.//// nosplit because it's called on the syscall path when stack movement is forbidden.////go:nosplitfunc traceAcquire() traceLocker {if !traceEnabled() {return traceLocker{}}return traceAcquireEnabled()}// traceAcquireEnabled is the traceEnabled path for traceAcquire. It's explicitly// broken out to make traceAcquire inlineable to keep the overhead of the tracer// when it's disabled low.//// nosplit because it's called by traceAcquire, which is nosplit.////go:nosplitfunc traceAcquireEnabled() traceLocker {// Any time we acquire a traceLocker, we may flush a trace buffer. But// buffer flushes are rare. Record the lock edge even if it doesn't happen// this time.lockRankMayTraceFlush()// Prevent preemption.:= acquirem()// Check if we're already tracing. It's safe to be reentrant in general,// because this function (and the invariants of traceLocker.writer) ensure// that it is.if .trace.seqlock.Load()%2 == 1 {.trace.reentered++return traceLocker{, trace.gen.Load()}}// Acquire the trace seqlock. This prevents traceAdvance from moving forward// until all Ms are observed to be outside of their seqlock critical section.//// Note: The seqlock is mutated here and also in traceCPUSample. If you update// usage of the seqlock here, make sure to also look at what traceCPUSample is// doing.:= .trace.seqlock.Add(1)if debugTraceReentrancy && %2 != 1 {throw("bad use of trace.seqlock")}// N.B. This load of gen appears redundant with the one in traceEnabled.// However, it's very important that the gen we use for writing to the trace// is acquired under a traceLocker so traceAdvance can make sure no stale// gen values are being used.//// Because we're doing this load again, it also means that the trace// might end up being disabled when we load it. In that case we need to undo// what we did and bail.:= trace.gen.Load()if == 0 {.trace.seqlock.Add(1)releasem()return traceLocker{}}return traceLocker{, }}// ok returns true if the traceLocker is valid (i.e. tracing is enabled).//// nosplit because it's called on the syscall path when stack movement is forbidden.////go:nosplitfunc ( traceLocker) () bool {return .gen != 0}// traceRelease indicates that this M is done writing trace events.//// nosplit because it's called on the syscall path when stack movement is forbidden.////go:nosplitfunc traceRelease( traceLocker) {if .mp.trace.reentered > 0 {.mp.trace.reentered--} else {:= .mp.trace.seqlock.Add(1)if debugTraceReentrancy && %2 != 0 {print("runtime: seq=", , "\n")throw("bad use of trace.seqlock")}}releasem(.mp)}// traceExitingSyscall marks a goroutine as exiting the syscall slow path.//// Must be paired with a traceExitedSyscall call.func traceExitingSyscall() {trace.exitingSyscall.Add(1)}// traceExitedSyscall marks a goroutine as having exited the syscall slow path.func traceExitedSyscall() {trace.exitingSyscall.Add(-1)}// Gomaxprocs emits a ProcsChange event.func ( traceLocker) ( int32) {.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvProcsChange, traceArg(), .stack(1))}// ProcStart traces a ProcStart event.//// Must be called with a valid P.func ( traceLocker) () {:= .mp.p.ptr()// Procs are typically started within the scheduler when there is no user goroutine. If there is a user goroutine,// it must be in _Gsyscall because the only time a goroutine is allowed to have its Proc moved around from under it// is during a syscall..eventWriter(tracev2.GoSyscall, tracev2.ProcIdle).event(tracev2.EvProcStart, traceArg(.id), .trace.nextSeq(.gen))}// ProcStop traces a ProcStop event.func ( traceLocker) ( *p) {// The only time a goroutine is allowed to have its Proc moved around// from under it is during a syscall..eventWriter(tracev2.GoSyscall, tracev2.ProcRunning).event(tracev2.EvProcStop)}// GCActive traces a GCActive event.//// Must be emitted by an actively running goroutine on an active P. This restriction can be changed// easily and only depends on where it's currently called.func ( traceLocker) () {.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCActive, traceArg(trace.seqGC))// N.B. Only one GC can be running at a time, so this is naturally// serialized by the caller.trace.seqGC++}// GCStart traces a GCBegin event.//// Must be emitted by an actively running goroutine on an active P. This restriction can be changed// easily and only depends on where it's currently called.func ( traceLocker) () {.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCBegin, traceArg(trace.seqGC), .stack(3))// N.B. Only one GC can be running at a time, so this is naturally// serialized by the caller.trace.seqGC++}// GCDone traces a GCEnd event.//// Must be emitted by an actively running goroutine on an active P. This restriction can be changed// easily and only depends on where it's currently called.func ( traceLocker) () {.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCEnd, traceArg(trace.seqGC))// N.B. Only one GC can be running at a time, so this is naturally// serialized by the caller.trace.seqGC++}// STWStart traces a STWBegin event.func ( traceLocker) ( stwReason) {// Although the current P may be in _Pgcstop here, we model the P as running during the STW. This deviates from the// runtime's state tracking, but it's more accurate and doesn't result in any loss of information..eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvSTWBegin, .string(.String()), .stack(2))}// STWDone traces a STWEnd event.func ( traceLocker) () {// Although the current P may be in _Pgcstop here, we model the P as running during the STW. This deviates from the// runtime's state tracking, but it's more accurate and doesn't result in any loss of information..eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvSTWEnd)}// GCSweepStart prepares to trace a sweep loop. This does not// emit any events until traceGCSweepSpan is called.//// GCSweepStart must be paired with traceGCSweepDone and there// must be no preemption points between these two calls.//// Must be called with a valid P.func ( traceLocker) () {// Delay the actual GCSweepBegin event until the first span// sweep. If we don't sweep anything, don't emit any events.:= .mp.p.ptr()if .trace.maySweep {throw("double traceGCSweepStart")}.trace.maySweep, .trace.swept, .trace.reclaimed = true, 0, 0}// GCSweepSpan traces the sweep of a single span. If this is// the first span swept since traceGCSweepStart was called, this// will emit a GCSweepBegin event.//// This may be called outside a traceGCSweepStart/traceGCSweepDone// pair; however, it will not emit any trace events in this case.//// Must be called with a valid P.func ( traceLocker) ( uintptr) {:= .mp.p.ptr()if .trace.maySweep {if .trace.swept == 0 {.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCSweepBegin, .stack(1)).trace.inSweep = true}.trace.swept +=}}// GCSweepDone finishes tracing a sweep loop. If any memory was// swept (i.e. traceGCSweepSpan emitted an event) then this will emit// a GCSweepEnd event.//// Must be called with a valid P.func ( traceLocker) () {:= .mp.p.ptr()if !.trace.maySweep {throw("missing traceGCSweepStart")}if .trace.inSweep {.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCSweepEnd, traceArg(.trace.swept), traceArg(.trace.reclaimed)).trace.inSweep = false}.trace.maySweep = false}// GCMarkAssistStart emits a MarkAssistBegin event.func ( traceLocker) () {.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCMarkAssistBegin, .stack(1))}// GCMarkAssistDone emits a MarkAssistEnd event.func ( traceLocker) () {.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGCMarkAssistEnd)}// GoCreate emits a GoCreate event.func ( traceLocker) ( *g, uintptr, bool) {.trace.setStatusTraced(.gen):= tracev2.EvGoCreateif {= tracev2.EvGoCreateBlocked}.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(, traceArg(.goid), .startPC(), .stack(2))}// GoStart emits a GoStart event.//// Must be called with a valid P.func ( traceLocker) () {:= getg().m.curg:= .m.p:= .eventWriter(tracev2.GoRunnable, tracev2.ProcRunning).event(tracev2.EvGoStart, traceArg(.goid), .trace.nextSeq(.gen))if .ptr().gcMarkWorkerMode != gcMarkWorkerNotWorker {.event(tracev2.EvGoLabel, trace.markWorkerLabels[.gen%2][.ptr().gcMarkWorkerMode])}}// GoEnd emits a GoDestroy event.//// TODO(mknyszek): Rename this to GoDestroy.func ( traceLocker) () {.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoDestroy)}// GoSched emits a GoStop event with a GoSched reason.func ( traceLocker) () {.GoStop(traceGoStopGoSched)}// GoPreempt emits a GoStop event with a GoPreempted reason.func ( traceLocker) () {.GoStop(traceGoStopPreempted)}// GoStop emits a GoStop event with the provided reason.func ( traceLocker) ( traceGoStopReason) {.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoStop, traceArg(trace.goStopReasons[.gen%2][]), .stack(0))}// GoPark emits a GoBlock event with the provided reason.//// TODO(mknyszek): Replace traceBlockReason with waitReason. It's silly// that we have both, and waitReason is way more descriptive.func ( traceLocker) ( traceBlockReason, int) {.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoBlock, traceArg(trace.goBlockReasons[.gen%2][]), .stack())}// GoUnpark emits a GoUnblock event.func ( traceLocker) ( *g, int) {// Emit a GoWaiting status if necessary for the unblocked goroutine..emitUnblockStatus(, .gen).eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoUnblock, traceArg(.goid), .trace.nextSeq(.gen), .stack())}// GoSwitch emits a GoSwitch event. If destroy is true, the calling goroutine// is simultaneously being destroyed.func ( traceLocker) ( *g, bool) {// Emit a GoWaiting status if necessary for the unblocked goroutine..emitUnblockStatus(, .gen):= .eventWriter(tracev2.GoRunning, tracev2.ProcRunning):= tracev2.EvGoSwitchif {= tracev2.EvGoSwitchDestroy}.event(, traceArg(.goid), .trace.nextSeq(.gen))}// emitUnblockStatus emits a GoStatus GoWaiting event for a goroutine about to be// unblocked to the trace writer.func ( traceLocker) ( *g, uintptr) {if !.trace.statusWasTraced() && .trace.acquireStatus() {// TODO(go.dev/issue/65634): Although it would be nice to add a stack trace here of gp,// we cannot safely do so. gp is in _Gwaiting and so we don't have ownership of its stack.// We can fix this by acquiring the goroutine's scan bit..writer().writeGoStatus(.goid, -1, tracev2.GoWaiting, .inMarkAssist, 0).end()}}// GoSysCall emits a GoSyscallBegin event.//// Must be called with a valid P.func ( traceLocker) () {// Scribble down the M that the P is currently attached to.:= .mp.p.ptr().trace.mSyscallID = int64(.mp.procid).eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvGoSyscallBegin, .trace.nextSeq(.gen), .stack(1))}// GoSysExit emits a GoSyscallEnd event, possibly along with a GoSyscallBlocked event// if lostP is true.//// lostP must be true in all cases that a goroutine loses its P during a syscall.// This means it's not sufficient to check if it has no P. In particular, it needs to be// true in the following cases:// - The goroutine lost its P, it ran some other code, and then got it back. It's now running with that P.// - The goroutine lost its P and was unable to reacquire it, and is now running without a P.// - The goroutine lost its P and acquired a different one, and is now running with that P.func ( traceLocker) ( bool) {:= tracev2.EvGoSyscallEnd:= tracev2.ProcSyscall // Procs implicitly enter tracev2.ProcSyscall on GoSyscallBegin.if {= tracev2.EvGoSyscallEndBlocked= tracev2.ProcRunning // If a G has a P when emitting this event, it reacquired a P and is indeed running.} else {.mp.p.ptr().trace.mSyscallID = -1}.eventWriter(tracev2.GoSyscall, ).event()}// ProcSteal indicates that our current M stole a P from another M.//// inSyscall indicates that we're stealing the P from a syscall context.//// The caller must have ownership of pp.func ( traceLocker) ( *p, bool) {// Grab the M ID we stole from.:= .trace.mSyscallID.trace.mSyscallID = -1// Emit the status of the P we're stealing. We may be just about to do this when creating the event// writer but it's not guaranteed, even if inSyscall is true. Although it might seem like from a// syscall context we're always stealing a P for ourselves, we may have not wired it up yet (so// it wouldn't be visible to eventWriter) or we may not even intend to wire it up to ourselves// at all (e.g. entersyscall_gcwait).if !.trace.statusWasTraced(.gen) && .trace.acquireStatus(.gen) {// Careful: don't use the event writer. We never want status or in-progress events// to trigger more in-progress events..writer().writeProcStatus(uint64(.id), tracev2.ProcSyscallAbandoned, .trace.inSweep).end()}// The status of the proc and goroutine, if we need to emit one here, is not evident from the// context of just emitting this event alone. There are two cases. Either we're trying to steal// the P just to get its attention (e.g. STW or sysmon retake) or we're trying to steal a P for// ourselves specifically to keep running. The two contexts look different, but can be summarized// fairly succinctly. In the former, we're a regular running goroutine and proc, if we have either.// In the latter, we're a goroutine in a syscall.:= tracev2.GoRunning:= tracev2.ProcRunningif {= tracev2.GoSyscall= tracev2.ProcSyscallAbandoned}.eventWriter(, ).event(tracev2.EvProcSteal, traceArg(.id), .trace.nextSeq(.gen), traceArg())}// HeapAlloc emits a HeapAlloc event.func ( traceLocker) ( uint64) {.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvHeapAlloc, traceArg())}// HeapGoal reads the current heap goal and emits a HeapGoal event.func ( traceLocker) () {:= gcController.heapGoal()// The heapGoal calculations will result in strange numbers if the GC if off. See go.dev/issue/63864.// Check gcPercent before using the heapGoal in the trace.if == ^uint64(0) || gcController.gcPercent.Load() < 0 {// Heap-based triggering is disabled.= 0}.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvHeapGoal, traceArg())}// GoCreateSyscall indicates that a goroutine has transitioned from dead to GoSyscall.//// Unlike GoCreate, the caller must be running on gp.//// This occurs when C code calls into Go. On pthread platforms it occurs only when// a C thread calls into Go code for the first time.func ( traceLocker) ( *g) {// N.B. We should never trace a status for this goroutine (which we're currently running on),// since we want this to appear like goroutine creation..trace.setStatusTraced(.gen).eventWriter(tracev2.GoBad, tracev2.ProcBad).event(tracev2.EvGoCreateSyscall, traceArg(.goid))}// GoDestroySyscall indicates that a goroutine has transitioned from GoSyscall to dead.//// Must not have a P.//// This occurs when Go code returns back to C. On pthread platforms it occurs only when// the C thread is destroyed.func ( traceLocker) () {// N.B. If we trace a status here, we must never have a P, and we must be on a goroutine// that is in the syscall state..eventWriter(tracev2.GoSyscall, tracev2.ProcBad).event(tracev2.EvGoDestroySyscall)}// To access runtime functions from runtime/trace.// See runtime/trace/annotation.go// trace_userTaskCreate emits a UserTaskCreate event.////go:linkname trace_userTaskCreate runtime/trace.userTaskCreatefunc trace_userTaskCreate(, uint64, string) {:= traceAcquire()if !.ok() {// Need to do this check because the caller won't have it.return}.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvUserTaskBegin, traceArg(), traceArg(), .string(), .stack(3))traceRelease()}// trace_userTaskEnd emits a UserTaskEnd event.////go:linkname trace_userTaskEnd runtime/trace.userTaskEndfunc trace_userTaskEnd( uint64) {:= traceAcquire()if !.ok() {// Need to do this check because the caller won't have it.return}.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvUserTaskEnd, traceArg(), .stack(2))traceRelease()}// trace_userRegion emits a UserRegionBegin or UserRegionEnd event,// depending on mode (0 == Begin, 1 == End).//// TODO(mknyszek): Just make this two functions.////go:linkname trace_userRegion runtime/trace.userRegionfunc trace_userRegion(, uint64, string) {:= traceAcquire()if !.ok() {// Need to do this check because the caller won't have it.return}var tracev2.EventTypeswitch {case 0:= tracev2.EvUserRegionBegincase 1:= tracev2.EvUserRegionEnddefault:return}.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(, traceArg(), .string(), .stack(3))traceRelease()}// trace_userLog emits a UserRegionBegin or UserRegionEnd event.////go:linkname trace_userLog runtime/trace.userLogfunc trace_userLog( uint64, , string) {:= traceAcquire()if !.ok() {// Need to do this check because the caller won't have it.return}.eventWriter(tracev2.GoRunning, tracev2.ProcRunning).event(tracev2.EvUserLog, traceArg(), .string(), .uniqueString(), .stack(3))traceRelease()}// traceThreadDestroy is called when a thread is removed from// sched.freem.//// mp must not be able to emit trace events anymore.//// sched.lock must be held to synchronize with traceAdvance.func traceThreadDestroy( *m) {assertLockHeld(&sched.lock)// Flush all outstanding buffers to maintain the invariant// that an M only has active buffers while on sched.freem// or allm.//// Perform a traceAcquire/traceRelease on behalf of mp to// synchronize with the tracer trying to flush our buffer// as well.:= .trace.seqlock.Add(1)if debugTraceReentrancy && %2 != 1 {throw("bad use of trace.seqlock")}systemstack(func() {lock(&trace.lock)for := range .trace.buf {for , := range .trace.buf[] {if != nil {// N.B. traceBufFlush accepts a generation, but it// really just cares about gen%2.traceBufFlush(, uintptr()).trace.buf[][] = nil}}}unlock(&trace.lock)}):= .trace.seqlock.Add(1)if != +1 {print("runtime: seq1=", , "\n")throw("bad use of trace.seqlock")}}
![]() |
The pages are generated with Golds v0.7.9-preview. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |