]> Cypherpunks.ru repositories - gostls13.git/blob - src/runtime/mklockrank.go
c0d5a02146066eb626d773fd5195f747e567d338
[gostls13.git] / src / runtime / mklockrank.go
1 // Copyright 2022 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 //go:build ignore
6
7 // mklockrank records the static rank graph of the locks in the
8 // runtime and generates the rank checking structures in lockrank.go.
9 package main
10
11 import (
12         "bytes"
13         "flag"
14         "fmt"
15         "go/format"
16         "internal/dag"
17         "io"
18         "log"
19         "os"
20         "strings"
21 )
22
23 // ranks describes the lock rank graph. See "go doc internal/dag" for
24 // the syntax.
25 //
26 // "a < b" means a must be acquired before b if both are held
27 // (or, if b is held, a cannot be acquired).
28 //
29 // "NONE < a" means no locks may be held when a is acquired.
30 //
31 // If a lock is not given a rank, then it is assumed to be a leaf
32 // lock, which means no other lock can be acquired while it is held.
33 // Therefore, leaf locks do not need to be given an explicit rank.
34 //
35 // Ranks in all caps are pseudo-nodes that help define order, but do
36 // not actually define a rank.
37 //
38 // TODO: It's often hard to correlate rank names to locks. Change
39 // these to be more consistent with the locks they label.
40 const ranks = `
41 # Sysmon
42 NONE
43 < sysmon
44 < scavenge, forcegc;
45
46 # Defer
47 NONE < defer;
48
49 # GC
50 NONE <
51   sweepWaiters,
52   assistQueue,
53   sweep;
54
55 # Scheduler, timers, netpoll
56 NONE < pollDesc, cpuprof;
57 assistQueue,
58   cpuprof,
59   forcegc,
60   pollDesc, # pollDesc can interact with timers, which can lock sched.
61   scavenge,
62   sweep,
63   sweepWaiters
64 < sched;
65 sched < allg, allp;
66 allp < timers;
67 timers < netpollInit;
68
69 # Channels
70 scavenge, sweep < hchan;
71 NONE < notifyList;
72 hchan, notifyList < sudog;
73
74 # RWMutex
75 NONE < rwmutexW;
76 rwmutexW, sysmon < rwmutexR;
77
78 # Semaphores
79 NONE < root;
80
81 # Itabs
82 NONE
83 < itab
84 < reflectOffs;
85
86 # User arena state
87 NONE < userArenaState;
88
89 # Tracing without a P uses a global trace buffer.
90 scavenge
91 # Above TRACEGLOBAL can emit a trace event without a P.
92 < TRACEGLOBAL
93 # Below TRACEGLOBAL manages the global tracing buffer.
94 # Note that traceBuf eventually chains to MALLOC, but we never get that far
95 # in the situation where there's no P.
96 < traceBuf;
97 # Starting/stopping tracing traces strings.
98 traceBuf < traceStrings;
99
100 # Malloc
101 allg,
102   hchan,
103   notifyList,
104   reflectOffs,
105   timers,
106   traceStrings,
107   userArenaState
108 # Above MALLOC are things that can allocate memory.
109 < MALLOC
110 # Below MALLOC is the malloc implementation.
111 < fin,
112   spanSetSpine,
113   mspanSpecial,
114   MPROF;
115
116 # We can acquire gcBitsArenas for pinner bits, and
117 # it's guarded by mspanSpecial.
118 MALLOC, mspanSpecial < gcBitsArenas;
119
120 # Memory profiling
121 MPROF < profInsert, profBlock, profMemActive;
122 profMemActive < profMemFuture;
123
124 # Stack allocation and copying
125 gcBitsArenas,
126   netpollInit,
127   profBlock,
128   profInsert,
129   profMemFuture,
130   spanSetSpine,
131   fin,
132   root
133 # Anything that can grow the stack can acquire STACKGROW.
134 # (Most higher layers imply STACKGROW, like MALLOC.)
135 < STACKGROW
136 # Below STACKGROW is the stack allocator/copying implementation.
137 < gscan;
138 gscan, rwmutexR < stackpool;
139 gscan < stackLarge;
140 # Generally, hchan must be acquired before gscan. But in one case,
141 # where we suspend a G and then shrink its stack, syncadjustsudogs
142 # can acquire hchan locks while holding gscan. To allow this case,
143 # we use hchanLeaf instead of hchan.
144 gscan < hchanLeaf;
145
146 # Write barrier
147 defer,
148   gscan,
149   mspanSpecial,
150   sudog
151 # Anything that can have write barriers can acquire WB.
152 # Above WB, we can have write barriers.
153 < WB
154 # Below WB is the write barrier implementation.
155 < wbufSpans;
156
157 # Span allocator
158 stackLarge,
159   stackpool,
160   wbufSpans
161 # Above mheap is anything that can call the span allocator.
162 < mheap;
163 # Below mheap is the span allocator implementation.
164 #
165 # Specials: we're allowed to allocate a special while holding
166 # an mspanSpecial lock, and they're part of the malloc implementation.
167 # Pinner bits might be freed by the span allocator.
168 mheap, mspanSpecial < mheapSpecial;
169 mheap, mheapSpecial < globalAlloc;
170
171 # Execution tracer events (with a P)
172 hchan,
173   mheap,
174   root,
175   sched,
176   traceStrings,
177   notifyList,
178   fin
179 # Above TRACE is anything that can create a trace event
180 < TRACE
181 < trace
182 < traceStackTab;
183
184 # panic is handled specially. It is implicitly below all other locks.
185 NONE < panic;
186 # deadlock is not acquired while holding panic, but it also needs to be
187 # below all other locks.
188 panic < deadlock;
189 # raceFini is only held while exiting.
190 panic < raceFini;
191 `
192
193 // cyclicRanks lists lock ranks that allow multiple locks of the same
194 // rank to be acquired simultaneously. The runtime enforces ordering
195 // within these ranks using a separate mechanism.
196 var cyclicRanks = map[string]bool{
197         // Multiple timers are locked simultaneously in destroy().
198         "timers": true,
199         // Multiple hchans are acquired in hchan.sortkey() order in
200         // select.
201         "hchan": true,
202         // Multiple hchanLeafs are acquired in hchan.sortkey() order in
203         // syncadjustsudogs().
204         "hchanLeaf": true,
205         // The point of the deadlock lock is to deadlock.
206         "deadlock": true,
207 }
208
209 func main() {
210         flagO := flag.String("o", "", "write to `file` instead of stdout")
211         flagDot := flag.Bool("dot", false, "emit graphviz output instead of Go")
212         flag.Parse()
213         if flag.NArg() != 0 {
214                 fmt.Fprintf(os.Stderr, "too many arguments")
215                 os.Exit(2)
216         }
217
218         g, err := dag.Parse(ranks)
219         if err != nil {
220                 log.Fatal(err)
221         }
222
223         var out []byte
224         if *flagDot {
225                 var b bytes.Buffer
226                 g.TransitiveReduction()
227                 // Add cyclic edges for visualization.
228                 for k := range cyclicRanks {
229                         g.AddEdge(k, k)
230                 }
231                 // Reverse the graph. It's much easier to read this as
232                 // a "<" partial order than a ">" partial order. This
233                 // ways, locks are acquired from the top going down
234                 // and time moves forward over the edges instead of
235                 // backward.
236                 g.Transpose()
237                 generateDot(&b, g)
238                 out = b.Bytes()
239         } else {
240                 var b bytes.Buffer
241                 generateGo(&b, g)
242                 out, err = format.Source(b.Bytes())
243                 if err != nil {
244                         log.Fatal(err)
245                 }
246         }
247
248         if *flagO != "" {
249                 err = os.WriteFile(*flagO, out, 0666)
250         } else {
251                 _, err = os.Stdout.Write(out)
252         }
253         if err != nil {
254                 log.Fatal(err)
255         }
256 }
257
258 func generateGo(w io.Writer, g *dag.Graph) {
259         fmt.Fprintf(w, `// Code generated by mklockrank.go; DO NOT EDIT.
260
261 package runtime
262
263 type lockRank int
264
265 `)
266
267         // Create numeric ranks.
268         topo := g.Topo()
269         for i, j := 0, len(topo)-1; i < j; i, j = i+1, j-1 {
270                 topo[i], topo[j] = topo[j], topo[i]
271         }
272         fmt.Fprintf(w, `
273 // Constants representing the ranks of all non-leaf runtime locks, in rank order.
274 // Locks with lower rank must be taken before locks with higher rank,
275 // in addition to satisfying the partial order in lockPartialOrder.
276 // A few ranks allow self-cycles, which are specified in lockPartialOrder.
277 const (
278         lockRankUnknown lockRank = iota
279
280 `)
281         for _, rank := range topo {
282                 if isPseudo(rank) {
283                         fmt.Fprintf(w, "\t// %s\n", rank)
284                 } else {
285                         fmt.Fprintf(w, "\t%s\n", cname(rank))
286                 }
287         }
288         fmt.Fprintf(w, `)
289
290 // lockRankLeafRank is the rank of lock that does not have a declared rank,
291 // and hence is a leaf lock.
292 const lockRankLeafRank lockRank = 1000
293 `)
294
295         // Create string table.
296         fmt.Fprintf(w, `
297 // lockNames gives the names associated with each of the above ranks.
298 var lockNames = []string{
299 `)
300         for _, rank := range topo {
301                 if !isPseudo(rank) {
302                         fmt.Fprintf(w, "\t%s: %q,\n", cname(rank), rank)
303                 }
304         }
305         fmt.Fprintf(w, `}
306
307 func (rank lockRank) String() string {
308         if rank == 0 {
309                 return "UNKNOWN"
310         }
311         if rank == lockRankLeafRank {
312                 return "LEAF"
313         }
314         if rank < 0 || int(rank) >= len(lockNames) {
315                 return "BAD RANK"
316         }
317         return lockNames[rank]
318 }
319 `)
320
321         // Create partial order structure.
322         fmt.Fprintf(w, `
323 // lockPartialOrder is the transitive closure of the lock rank graph.
324 // An entry for rank X lists all of the ranks that can already be held
325 // when rank X is acquired.
326 //
327 // Lock ranks that allow self-cycles list themselves.
328 var lockPartialOrder [][]lockRank = [][]lockRank{
329 `)
330         for _, rank := range topo {
331                 if isPseudo(rank) {
332                         continue
333                 }
334                 list := []string{}
335                 for _, before := range g.Edges(rank) {
336                         if !isPseudo(before) {
337                                 list = append(list, cname(before))
338                         }
339                 }
340                 if cyclicRanks[rank] {
341                         list = append(list, cname(rank))
342                 }
343
344                 fmt.Fprintf(w, "\t%s: {%s},\n", cname(rank), strings.Join(list, ", "))
345         }
346         fmt.Fprintf(w, "}\n")
347 }
348
349 // cname returns the Go const name for the given lock rank label.
350 func cname(label string) string {
351         return "lockRank" + strings.ToUpper(label[:1]) + label[1:]
352 }
353
354 func isPseudo(label string) bool {
355         return strings.ToUpper(label) == label
356 }
357
358 // generateDot emits a Graphviz dot representation of g to w.
359 func generateDot(w io.Writer, g *dag.Graph) {
360         fmt.Fprintf(w, "digraph g {\n")
361
362         // Define all nodes.
363         for _, node := range g.Nodes {
364                 fmt.Fprintf(w, "%q;\n", node)
365         }
366
367         // Create edges.
368         for _, node := range g.Nodes {
369                 for _, to := range g.Edges(node) {
370                         fmt.Fprintf(w, "%q -> %q;\n", node, to)
371                 }
372         }
373
374         fmt.Fprintf(w, "}\n")
375 }