Source file src/internal/trace/resources.go

     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  

View as plain text