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 5package trace 6 7import "fmt" 8 9// ThreadID is the runtime-internal M structure's ID. This is unique 10// for each OS thread. 11type ThreadID int64 12 13// NoThread indicates that the relevant events don't correspond to any 14// thread in particular. 15const NoThread = ThreadID(-1) 16 17// ProcID is the runtime-internal G structure's id field. This is unique 18// for each P. 19type ProcID int64 20 21// NoProc indicates that the relevant events don't correspond to any 22// P in particular. 23const NoProc = ProcID(-1) 24 25// GoID is the runtime-internal G structure's goid field. This is unique 26// for each goroutine. 27type GoID int64 28 29// NoGoroutine indicates that the relevant events don't correspond to any 30// goroutine in particular. 31const 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. 37type GoState uint8 38 39const ( 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. 50func (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. 57func (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. 79type ProcState uint8 80 81const ( 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. 90func (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. 97func (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. 115type ResourceKind uint8 116 117const ( 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. 127func (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. 142type 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. 149func 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. 167func (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. 177func (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. 187func (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. 197func (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. 205type 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 232func 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 240func 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. 255func (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. 269func (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