github.com/MerlinKodo/gvisor@v0.0.0-20231110090155-957f62ecf90e/runsc/boot/events.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package boot
    16  
    17  import (
    18  	"errors"
    19  
    20  	"github.com/MerlinKodo/gvisor/pkg/sentry/control"
    21  	"github.com/MerlinKodo/gvisor/pkg/sentry/usage"
    22  )
    23  
    24  // EventOut is the return type of the Event command.
    25  type EventOut struct {
    26  	Event Event `json:"event"`
    27  
    28  	// ContainerUsage maps each container ID to its total CPU usage.
    29  	ContainerUsage map[string]uint64 `json:"containerUsage"`
    30  }
    31  
    32  // Event struct for encoding the event data to JSON. Corresponds to runc's
    33  // main.event struct.
    34  type Event struct {
    35  	Type string `json:"type"`
    36  	ID   string `json:"id"`
    37  	Data Stats  `json:"data"`
    38  }
    39  
    40  // Stats is the runc specific stats structure for stability when encoding and
    41  // decoding stats.
    42  type Stats struct {
    43  	CPU    CPU    `json:"cpu"`
    44  	Memory Memory `json:"memory"`
    45  	Pids   Pids   `json:"pids"`
    46  }
    47  
    48  // Pids contains stats on processes.
    49  type Pids struct {
    50  	Current uint64 `json:"current,omitempty"`
    51  	Limit   uint64 `json:"limit,omitempty"`
    52  }
    53  
    54  // MemoryEntry contains stats on a kind of memory.
    55  type MemoryEntry struct {
    56  	Limit   uint64 `json:"limit"`
    57  	Usage   uint64 `json:"usage,omitempty"`
    58  	Max     uint64 `json:"max,omitempty"`
    59  	Failcnt uint64 `json:"failcnt"`
    60  }
    61  
    62  // Memory contains stats on memory.
    63  type Memory struct {
    64  	Cache     uint64            `json:"cache,omitempty"`
    65  	Usage     MemoryEntry       `json:"usage,omitempty"`
    66  	Swap      MemoryEntry       `json:"swap,omitempty"`
    67  	Kernel    MemoryEntry       `json:"kernel,omitempty"`
    68  	KernelTCP MemoryEntry       `json:"kernelTCP,omitempty"`
    69  	Raw       map[string]uint64 `json:"raw,omitempty"`
    70  }
    71  
    72  // CPU contains stats on the CPU.
    73  type CPU struct {
    74  	Usage CPUUsage `json:"usage"`
    75  }
    76  
    77  // CPUUsage contains stats on CPU usage.
    78  type CPUUsage struct {
    79  	Kernel uint64   `json:"kernel,omitempty"`
    80  	User   uint64   `json:"user,omitempty"`
    81  	Total  uint64   `json:"total,omitempty"`
    82  	PerCPU []uint64 `json:"percpu,omitempty"`
    83  }
    84  
    85  // Event gets the events from the container.
    86  func (cm *containerManager) Event(cid *string, out *EventOut) error {
    87  	*out = EventOut{
    88  		Event: Event{
    89  			ID:   *cid,
    90  			Type: "stats",
    91  		},
    92  	}
    93  
    94  	// PIDs and check that container exists before going further.
    95  	pids, err := cm.l.pidsCount(*cid)
    96  	if err != nil {
    97  		return err
    98  	}
    99  	out.Event.Data.Pids.Current = uint64(pids)
   100  
   101  	// Memory usage.
   102  	mem := cm.l.k.MemoryFile()
   103  	_ = mem.UpdateUsage(0) // best effort to update.
   104  	_, totalUsage := usage.MemoryAccounting.Copy()
   105  	switch containers := cm.l.containerCount(); containers {
   106  	case 0:
   107  		return errors.New("no container was found")
   108  
   109  	case 1:
   110  		// There is a single container, so total usage can only come from it.
   111  
   112  	default:
   113  		// In the multi-container case, reports 0 for the root (pause) container,
   114  		// since it's small and idle. Then equally split the usage to the other
   115  		// containers. At least the sum of all containers will correctly account
   116  		// for the memory used by the sandbox.
   117  		//
   118  		// TODO(gvisor.dev/issue/172): Proper per-container accounting.
   119  		if *cid == cm.l.sandboxID {
   120  			totalUsage = 0
   121  		} else {
   122  			totalUsage /= uint64(containers - 1)
   123  		}
   124  	}
   125  
   126  	out.Event.Data.Memory.Usage.Usage = totalUsage
   127  
   128  	// CPU usage by container.
   129  	out.ContainerUsage = control.ContainerUsage(cm.l.k)
   130  
   131  	return nil
   132  }