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