]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/runtime.go
runtime: improve tickspersecond
[gostls13.git] / src / runtime / runtime.go
1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package runtime
6
7 import (
8         "runtime/internal/atomic"
9         "unsafe"
10 )
11
12 //go:generate go run wincallback.go
13 //go:generate go run mkduff.go
14 //go:generate go run mkfastlog2table.go
15 //go:generate go run mklockrank.go -o lockrank.go
16
17 var ticks ticksType
18
19 type ticksType struct {
20         // lock protects access to start* and val.
21         lock       mutex
22         startTicks int64
23         startTime  int64
24         val        atomic.Int64
25 }
26
27 // init initializes ticks to maximize the chance that we have a good ticksPerSecond reference.
28 //
29 // Must not run concurrently with ticksPerSecond.
30 func (t *ticksType) init() {
31         lock(&ticks.lock)
32         t.startTime = nanotime()
33         t.startTicks = cputicks()
34         unlock(&ticks.lock)
35 }
36
37 // minTimeForTicksPerSecond is the minimum elapsed time we require to consider our ticksPerSecond
38 // measurement to be of decent enough quality for profiling.
39 //
40 // There's a linear relationship here between minimum time and error from the true value.
41 // The error from the true ticks-per-second in a linux/amd64 VM seems to be:
42 // -   1 ms -> ~0.02% error
43 // -   5 ms -> ~0.004% error
44 // -  10 ms -> ~0.002% error
45 // -  50 ms -> ~0.0003% error
46 // - 100 ms -> ~0.0001% error
47 //
48 // We're willing to take 0.004% error here, because ticksPerSecond is intended to be used for
49 // converting durations, not timestamps. Durations are usually going to be much larger, and so
50 // the tiny error doesn't matter. The error is definitely going to be a problem when trying to
51 // use this for timestamps, as it'll make those timestamps much less likely to line up.
52 const minTimeForTicksPerSecond = 5_000_000*(1-osHasLowResClockInt) + 100_000_000*osHasLowResClockInt
53
54 // ticksPerSecond returns a conversion rate between the cputicks clock and the nanotime clock.
55 //
56 // Note: Clocks are hard. Using this as an actual conversion rate for timestamps is ill-advised
57 // and should be avoided when possible. Use only for durations, where a tiny error term isn't going
58 // to make a meaningful difference in even a 1ms duration. If an accurate timestamp is needed,
59 // use nanotime instead. (The entire Windows platform is a broad exception to this rule, where nanotime
60 // produces timestamps on such a coarse granularity that the error from this conversion is actually
61 // preferable.)
62 //
63 // The strategy for computing the conversion rate is to write down nanotime and cputicks as
64 // early in process startup as possible. From then, we just need to wait until we get values
65 // from nanotime that we can use (some platforms have a really coarse system time granularity).
66 // We require some amount of time to pass to ensure that the conversion rate is fairly accurate
67 // in aggregate. But because we compute this rate lazily, there's a pretty good chance a decent
68 // amount of time has passed by the time we get here.
69 //
70 // Must be called from a normal goroutine context (running regular goroutine with a P).
71 //
72 // Called by runtime/pprof in addition to runtime code.
73 //
74 // TODO(mknyszek): This doesn't account for things like CPU frequency scaling. Consider
75 // a more sophisticated and general approach in the future.
76 func ticksPerSecond() int64 {
77         // Get the conversion rate if we've already computed it.
78         r := ticks.val.Load()
79         if r != 0 {
80                 return r
81         }
82
83         // Compute the conversion rate.
84         for {
85                 lock(&ticks.lock)
86                 r = ticks.val.Load()
87                 if r != 0 {
88                         unlock(&ticks.lock)
89                         return r
90                 }
91
92                 // Grab the current time in both clocks.
93                 nowTime := nanotime()
94                 nowTicks := cputicks()
95
96                 // See if we can use these times.
97                 if nowTicks > ticks.startTicks && nowTime-ticks.startTime > minTimeForTicksPerSecond {
98                         // Perform the calculation with floats. We don't want to risk overflow.
99                         r = int64(float64(nowTicks-ticks.startTicks) * 1e9 / float64(nowTime-ticks.startTime))
100                         if r == 0 {
101                                 // Zero is both a sentinel value and it would be bad if callers used this as
102                                 // a divisor. We tried out best, so just make it 1.
103                                 r++
104                         }
105                         ticks.val.Store(r)
106                         unlock(&ticks.lock)
107                         break
108                 }
109                 unlock(&ticks.lock)
110
111                 // Sleep in one millisecond increments until we have a reliable time.
112                 timeSleep(1_000_000)
113         }
114         return r
115 }
116
117 var envs []string
118 var argslice []string
119
120 //go:linkname syscall_runtime_envs syscall.runtime_envs
121 func syscall_runtime_envs() []string { return append([]string{}, envs...) }
122
123 //go:linkname syscall_Getpagesize syscall.Getpagesize
124 func syscall_Getpagesize() int { return int(physPageSize) }
125
126 //go:linkname os_runtime_args os.runtime_args
127 func os_runtime_args() []string { return append([]string{}, argslice...) }
128
129 //go:linkname syscall_Exit syscall.Exit
130 //go:nosplit
131 func syscall_Exit(code int) {
132         exit(int32(code))
133 }
134
135 var godebugDefault string
136 var godebugUpdate atomic.Pointer[func(string, string)]
137 var godebugEnv atomic.Pointer[string] // set by parsedebugvars
138 var godebugNewIncNonDefault atomic.Pointer[func(string) func()]
139
140 //go:linkname godebug_setUpdate internal/godebug.setUpdate
141 func godebug_setUpdate(update func(string, string)) {
142         p := new(func(string, string))
143         *p = update
144         godebugUpdate.Store(p)
145         godebugNotify(false)
146 }
147
148 //go:linkname godebug_setNewIncNonDefault internal/godebug.setNewIncNonDefault
149 func godebug_setNewIncNonDefault(newIncNonDefault func(string) func()) {
150         p := new(func(string) func())
151         *p = newIncNonDefault
152         godebugNewIncNonDefault.Store(p)
153 }
154
155 // A godebugInc provides access to internal/godebug's IncNonDefault function
156 // for a given GODEBUG setting.
157 // Calls before internal/godebug registers itself are dropped on the floor.
158 type godebugInc struct {
159         name string
160         inc  atomic.Pointer[func()]
161 }
162
163 func (g *godebugInc) IncNonDefault() {
164         inc := g.inc.Load()
165         if inc == nil {
166                 newInc := godebugNewIncNonDefault.Load()
167                 if newInc == nil {
168                         return
169                 }
170                 // If other goroutines are racing here, no big deal. One will win,
171                 // and all the inc functions will be using the same underlying
172                 // *godebug.Setting.
173                 inc = new(func())
174                 *inc = (*newInc)(g.name)
175                 g.inc.Store(inc)
176         }
177         (*inc)()
178 }
179
180 func godebugNotify(envChanged bool) {
181         update := godebugUpdate.Load()
182         var env string
183         if p := godebugEnv.Load(); p != nil {
184                 env = *p
185         }
186         if envChanged {
187                 reparsedebugvars(env)
188         }
189         if update != nil {
190                 (*update)(godebugDefault, env)
191         }
192 }
193
194 //go:linkname syscall_runtimeSetenv syscall.runtimeSetenv
195 func syscall_runtimeSetenv(key, value string) {
196         setenv_c(key, value)
197         if key == "GODEBUG" {
198                 p := new(string)
199                 *p = value
200                 godebugEnv.Store(p)
201                 godebugNotify(true)
202         }
203 }
204
205 //go:linkname syscall_runtimeUnsetenv syscall.runtimeUnsetenv
206 func syscall_runtimeUnsetenv(key string) {
207         unsetenv_c(key)
208         if key == "GODEBUG" {
209                 godebugEnv.Store(nil)
210                 godebugNotify(true)
211         }
212 }
213
214 // writeErrStr writes a string to descriptor 2.
215 //
216 //go:nosplit
217 func writeErrStr(s string) {
218         write(2, unsafe.Pointer(unsafe.StringData(s)), int32(len(s)))
219 }
220
221 // auxv is populated on relevant platforms but defined here for all platforms
222 // so x/sys/cpu can assume the getAuxv symbol exists without keeping its list
223 // of auxv-using GOOS build tags in sync.
224 //
225 // It contains an even number of elements, (tag, value) pairs.
226 var auxv []uintptr
227
228 func getAuxv() []uintptr { return auxv } // accessed from x/sys/cpu; see issue 57336