1 // Copyright 2023 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 trace 6 7 import "fmt" 8 9 // ThreadID is the runtime-internal M structure's ID. This is unique 10 // for each OS thread. 11 type ThreadID int64 12 13 // NoThread indicates that the relevant events don't correspond to any 14 // thread in particular. 15 const NoThread = ThreadID(-1) 16 17 // ProcID is the runtime-internal G structure's id field. This is unique 18 // for each P. 19 type ProcID int64 20 21 // NoProc indicates that the relevant events don't correspond to any 22 // P in particular. 23 const NoProc = ProcID(-1) 24 25 // GoID is the runtime-internal G structure's goid field. This is unique 26 // for each goroutine. 27 type GoID int64 28 29 // NoGoroutine indicates that the relevant events don't correspond to any 30 // goroutine in particular. 31 const NoGoroutine = GoID(-1) 32 33 // GoState represents the state of a goroutine. 34 // 35 // New GoStates may be added in the future. Users of this type must be robust 36 // to that possibility. 37 type GoState uint8 38 39 const ( 40 GoUndetermined GoState = iota // No information is known about the goroutine. 41 GoNotExist // Goroutine does not exist. 42 GoRunnable // Goroutine is runnable but not running. 43 GoRunning // Goroutine is running. 44 GoWaiting // Goroutine is waiting on something to happen. 45 GoSyscall // Goroutine is in a system call. 46 ) 47 48 // Executing returns true if the state indicates that the goroutine is executing 49 // and bound to its thread. 50 func (s GoState) Executing() bool { 51 return s == GoRunning || s == GoSyscall 52 } 53 54 // String returns a human-readable representation of a GoState. 55 // 56 // The format of the returned string is for debugging purposes and is subject to change. 57 func (s GoState) String() string { 58 switch s { 59 case GoUndetermined: 60 return "Undetermined" 61 case GoNotExist: 62 return "NotExist" 63 case GoRunnable: 64 return "Runnable" 65 case GoRunning: 66 return "Running" 67 case GoWaiting: 68 return "Waiting" 69 case GoSyscall: 70 return "Syscall" 71 } 72 return "Bad" 73 } 74 75 // ProcState represents the state of a proc. 76 // 77 // New ProcStates may be added in the future. Users of this type must be robust 78 // to that possibility. 79 type ProcState uint8 80 81 const ( 82 ProcUndetermined ProcState = iota // No information is known about the proc. 83 ProcNotExist // Proc does not exist. 84 ProcRunning // Proc is running. 85 ProcIdle // Proc is idle. 86 ) 87 88 // Executing returns true if the state indicates that the proc is executing 89 // and bound to its thread. 90 func (s ProcState) Executing() bool { 91 return s == ProcRunning 92 } 93 94 // String returns a human-readable representation of a ProcState. 95 // 96 // The format of the returned string is for debugging purposes and is subject to change. 97 func (s ProcState) String() string { 98 switch s { 99 case ProcUndetermined: 100 return "Undetermined" 101 case ProcNotExist: 102 return "NotExist" 103 case ProcRunning: 104 return "Running" 105 case ProcIdle: 106 return "Idle" 107 } 108 return "Bad" 109 } 110 111 // ResourceKind indicates a kind of resource that has a state machine. 112 // 113 // New ResourceKinds may be added in the future. Users of this type must be robust 114 // to that possibility. 115 type ResourceKind uint8 116 117 const ( 118 ResourceNone ResourceKind = iota // No resource. 119 ResourceGoroutine // Goroutine. 120 ResourceProc // Proc. 121 ResourceThread // Thread. 122 ) 123 124 // String returns a human-readable representation of a ResourceKind. 125 // 126 // The format of the returned string is for debugging purposes and is subject to change. 127 func (r ResourceKind) String() string { 128 switch r { 129 case ResourceNone: 130 return "None" 131 case ResourceGoroutine: 132 return "Goroutine" 133 case ResourceProc: 134 return "Proc" 135 case ResourceThread: 136 return "Thread" 137 } 138 return "Bad" 139 } 140 141 // ResourceID represents a generic resource ID. 142 type ResourceID struct { 143 // Kind is the kind of resource this ID is for. 144 Kind ResourceKind 145 id int64 146 } 147 148 // MakeResourceID creates a general resource ID from a specific resource's ID. 149 func MakeResourceID[T interface{ GoID | ProcID | ThreadID }](id T) ResourceID { 150 var rd ResourceID 151 var a any = id 152 switch a.(type) { 153 case GoID: 154 rd.Kind = ResourceGoroutine 155 case ProcID: 156 rd.Kind = ResourceProc 157 case ThreadID: 158 rd.Kind = ResourceThread 159 } 160 rd.id = int64(id) 161 return rd 162 } 163 164 // Goroutine obtains a GoID from the resource ID. 165 // 166 // r.Kind must be ResourceGoroutine or this function will panic. 167 func (r ResourceID) Goroutine() GoID { 168 if r.Kind != ResourceGoroutine { 169 panic(fmt.Sprintf("attempted to get GoID from %s resource ID", r.Kind)) 170 } 171 return GoID(r.id) 172 } 173 174 // Proc obtains a ProcID from the resource ID. 175 // 176 // r.Kind must be ResourceProc or this function will panic. 177 func (r ResourceID) Proc() ProcID { 178 if r.Kind != ResourceProc { 179 panic(fmt.Sprintf("attempted to get ProcID from %s resource ID", r.Kind)) 180 } 181 return ProcID(r.id) 182 } 183 184 // Thread obtains a ThreadID from the resource ID. 185 // 186 // r.Kind must be ResourceThread or this function will panic. 187 func (r ResourceID) Thread() ThreadID { 188 if r.Kind != ResourceThread { 189 panic(fmt.Sprintf("attempted to get ThreadID from %s resource ID", r.Kind)) 190 } 191 return ThreadID(r.id) 192 } 193 194 // String returns a human-readable string representation of the ResourceID. 195 // 196 // This representation is subject to change and is intended primarily for debugging. 197 func (r ResourceID) String() string { 198 if r.Kind == ResourceNone { 199 return r.Kind.String() 200 } 201 return fmt.Sprintf("%s(%d)", r.Kind, r.id) 202 } 203 204 // StateTransition provides details about a StateTransition event. 205 type StateTransition struct { 206 // Resource is the resource this state transition is for. 207 Resource ResourceID 208 209 // Reason is a human-readable reason for the state transition. 210 Reason string 211 212 // Stack is the stack trace of the resource making the state transition. 213 // 214 // This is distinct from the result (Event).Stack because it pertains to 215 // the transitioning resource, not any of the ones executing the event 216 // this StateTransition came from. 217 // 218 // An example of this difference is the NotExist -> Runnable transition for 219 // goroutines, which indicates goroutine creation. In this particular case, 220 // a Stack here would refer to the starting stack of the new goroutine, and 221 // an (Event).Stack would refer to the stack trace of whoever created the 222 // goroutine. 223 Stack Stack 224 225 // The actual transition data. Stored in a neutral form so that 226 // we don't need fields for every kind of resource. 227 id int64 228 oldState uint8 229 newState uint8 230 } 231 232 func goStateTransition(id GoID, from, to GoState) StateTransition { 233 return StateTransition{ 234 Resource: ResourceID{Kind: ResourceGoroutine, id: int64(id)}, 235 oldState: uint8(from), 236 newState: uint8(to), 237 } 238 } 239 240 func procStateTransition(id ProcID, from, to ProcState) StateTransition { 241 return StateTransition{ 242 Resource: ResourceID{Kind: ResourceProc, id: int64(id)}, 243 oldState: uint8(from), 244 newState: uint8(to), 245 } 246 } 247 248 // Goroutine returns the state transition for a goroutine. 249 // 250 // Transitions to and from states that are Executing are special in that 251 // they change the future execution context. In other words, future events 252 // on the same thread will feature the same goroutine until it stops running. 253 // 254 // Panics if d.Resource.Kind is not ResourceGoroutine. 255 func (d StateTransition) Goroutine() (from, to GoState) { 256 if d.Resource.Kind != ResourceGoroutine { 257 panic("Goroutine called on non-Goroutine state transition") 258 } 259 return GoState(d.oldState), GoState(d.newState) 260 } 261 262 // Proc returns the state transition for a proc. 263 // 264 // Transitions to and from states that are Executing are special in that 265 // they change the future execution context. In other words, future events 266 // on the same thread will feature the same goroutine until it stops running. 267 // 268 // Panics if d.Resource.Kind is not ResourceProc. 269 func (d StateTransition) Proc() (from, to ProcState) { 270 if d.Resource.Kind != ResourceProc { 271 panic("Proc called on non-Proc state transition") 272 } 273 return ProcState(d.oldState), ProcState(d.newState) 274 } 275