...

Source file src/github.com/containerd/containerd/cmd/ctr/commands/tasks/metrics.go

Documentation: github.com/containerd/containerd/cmd/ctr/commands/tasks

     1  /*
     2     Copyright The containerd Authors.
     3  
     4     Licensed under the Apache License, Version 2.0 (the "License");
     5     you may not use this file except in compliance with the License.
     6     You may obtain a copy of the License at
     7  
     8         http://www.apache.org/licenses/LICENSE-2.0
     9  
    10     Unless required by applicable law or agreed to in writing, software
    11     distributed under the License is distributed on an "AS IS" BASIS,
    12     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13     See the License for the specific language governing permissions and
    14     limitations under the License.
    15  */
    16  
    17  package tasks
    18  
    19  import (
    20  	"encoding/json"
    21  	"errors"
    22  	"fmt"
    23  	"os"
    24  	"text/tabwriter"
    25  
    26  	wstats "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/stats"
    27  	v1 "github.com/containerd/cgroups/v3/cgroup1/stats"
    28  	v2 "github.com/containerd/cgroups/v3/cgroup2/stats"
    29  	"github.com/containerd/containerd/cmd/ctr/commands"
    30  	"github.com/containerd/containerd/protobuf/proto"
    31  	"github.com/containerd/typeurl/v2"
    32  	"github.com/urfave/cli"
    33  )
    34  
    35  const (
    36  	formatFlag  = "format"
    37  	formatTable = "table"
    38  	formatJSON  = "json"
    39  )
    40  
    41  var metricsCommand = cli.Command{
    42  	Name:      "metrics",
    43  	Usage:     "Get a single data point of metrics for a task with the built-in Linux runtime",
    44  	ArgsUsage: "CONTAINER",
    45  	Aliases:   []string{"metric"},
    46  	Flags: []cli.Flag{
    47  		cli.StringFlag{
    48  			Name:  formatFlag,
    49  			Usage: `"table" or "json"`,
    50  			Value: formatTable,
    51  		},
    52  	},
    53  	Action: func(context *cli.Context) error {
    54  		client, ctx, cancel, err := commands.NewClient(context)
    55  		if err != nil {
    56  			return err
    57  		}
    58  		defer cancel()
    59  		container, err := client.LoadContainer(ctx, context.Args().First())
    60  		if err != nil {
    61  			return err
    62  		}
    63  		task, err := container.Task(ctx, nil)
    64  		if err != nil {
    65  			return err
    66  		}
    67  		metric, err := task.Metrics(ctx)
    68  		if err != nil {
    69  			return err
    70  		}
    71  
    72  		var data interface{}
    73  		switch {
    74  		case typeurl.Is(metric.Data, (*v1.Metrics)(nil)):
    75  			data = &v1.Metrics{}
    76  		case typeurl.Is(metric.Data, (*v2.Metrics)(nil)):
    77  			data = &v2.Metrics{}
    78  		case typeurl.Is(metric.Data, (*wstats.Statistics)(nil)):
    79  			data = &wstats.Statistics{}
    80  		default:
    81  			return errors.New("cannot convert metric data to cgroups.Metrics or windows.Statistics")
    82  		}
    83  		if err := typeurl.UnmarshalTo(metric.Data, data); err != nil {
    84  			return err
    85  		}
    86  
    87  		switch context.String(formatFlag) {
    88  		case formatTable:
    89  			w := tabwriter.NewWriter(os.Stdout, 1, 8, 4, ' ', 0)
    90  			fmt.Fprintf(w, "ID\tTIMESTAMP\t\n")
    91  			fmt.Fprintf(w, "%s\t%s\t\n\n", metric.ID, metric.Timestamp)
    92  			switch v := data.(type) {
    93  			case *v1.Metrics:
    94  				printCgroupMetricsTable(w, v)
    95  			case *v2.Metrics:
    96  				printCgroup2MetricsTable(w, v)
    97  			case *wstats.Statistics:
    98  				if err := printWindowsStats(w, v); err != nil {
    99  					return fmt.Errorf("cannot convert metrics data from windows.Statistics: %w", err)
   100  				}
   101  			}
   102  			return w.Flush()
   103  		case formatJSON:
   104  			marshaledJSON, err := json.MarshalIndent(data, "", "  ")
   105  			if err != nil {
   106  				return err
   107  			}
   108  			fmt.Println(string(marshaledJSON))
   109  			return nil
   110  		default:
   111  			return errors.New("format must be table or json")
   112  		}
   113  	},
   114  }
   115  
   116  func printCgroupMetricsTable(w *tabwriter.Writer, data *v1.Metrics) {
   117  	fmt.Fprintf(w, "METRIC\tVALUE\t\n")
   118  	if data.Memory != nil {
   119  		fmt.Fprintf(w, "memory.usage_in_bytes\t%d\t\n", data.Memory.Usage.Usage)
   120  		fmt.Fprintf(w, "memory.limit_in_bytes\t%d\t\n", data.Memory.Usage.Limit)
   121  		fmt.Fprintf(w, "memory.stat.cache\t%d\t\n", data.Memory.TotalCache)
   122  	}
   123  	if data.CPU != nil {
   124  		fmt.Fprintf(w, "cpuacct.usage\t%d\t\n", data.CPU.Usage.Total)
   125  		fmt.Fprintf(w, "cpuacct.usage_percpu\t%v\t\n", data.CPU.Usage.PerCPU)
   126  	}
   127  	if data.Pids != nil {
   128  		fmt.Fprintf(w, "pids.current\t%v\t\n", data.Pids.Current)
   129  		fmt.Fprintf(w, "pids.limit\t%v\t\n", data.Pids.Limit)
   130  	}
   131  }
   132  
   133  func printCgroup2MetricsTable(w *tabwriter.Writer, data *v2.Metrics) {
   134  	fmt.Fprintf(w, "METRIC\tVALUE\t\n")
   135  	if data.Pids != nil {
   136  		fmt.Fprintf(w, "pids.current\t%v\t\n", data.Pids.Current)
   137  		fmt.Fprintf(w, "pids.limit\t%v\t\n", data.Pids.Limit)
   138  	}
   139  	if data.CPU != nil {
   140  		fmt.Fprintf(w, "cpu.usage_usec\t%v\t\n", data.CPU.UsageUsec)
   141  		fmt.Fprintf(w, "cpu.user_usec\t%v\t\n", data.CPU.UserUsec)
   142  		fmt.Fprintf(w, "cpu.system_usec\t%v\t\n", data.CPU.SystemUsec)
   143  		fmt.Fprintf(w, "cpu.nr_periods\t%v\t\n", data.CPU.NrPeriods)
   144  		fmt.Fprintf(w, "cpu.nr_throttled\t%v\t\n", data.CPU.NrThrottled)
   145  		fmt.Fprintf(w, "cpu.throttled_usec\t%v\t\n", data.CPU.ThrottledUsec)
   146  	}
   147  	if data.Memory != nil {
   148  		fmt.Fprintf(w, "memory.usage\t%v\t\n", data.Memory.Usage)
   149  		fmt.Fprintf(w, "memory.usage_limit\t%v\t\n", data.Memory.UsageLimit)
   150  		fmt.Fprintf(w, "memory.swap_usage\t%v\t\n", data.Memory.SwapUsage)
   151  		fmt.Fprintf(w, "memory.swap_limit\t%v\t\n", data.Memory.SwapLimit)
   152  	}
   153  }
   154  
   155  func printWindowsStats(w *tabwriter.Writer, windowsStats *wstats.Statistics) error {
   156  	if windowsStats.GetLinux() != nil {
   157  		var stats v1.Metrics
   158  
   159  		// It cannot be casted to v1.Metrics since windowsStats is still generated by gogo/protobuf.
   160  		linux := windowsStats.GetLinux()
   161  
   162  		// But Marshal/Unmarshal works because the underlying protobuf message is compatible.
   163  		data, err := linux.Marshal()
   164  		if err != nil {
   165  			return err
   166  		}
   167  		err = proto.Unmarshal(data, &stats)
   168  		if err != nil {
   169  			return err
   170  		}
   171  
   172  		printCgroupMetricsTable(w, &stats)
   173  	} else if windowsStats.GetWindows() != nil {
   174  		printWindowsContainerStatistics(w, windowsStats.GetWindows())
   175  	}
   176  	// Print VM stats if its isolated
   177  	if windowsStats.VM != nil {
   178  		printWindowsVMStatistics(w, windowsStats.VM)
   179  	}
   180  	return nil
   181  }
   182  
   183  func printWindowsContainerStatistics(w *tabwriter.Writer, stats *wstats.WindowsContainerStatistics) {
   184  	fmt.Fprintf(w, "METRIC\tVALUE\t\n")
   185  	fmt.Fprintf(w, "timestamp\t%s\t\n", stats.Timestamp)
   186  	fmt.Fprintf(w, "start_time\t%s\t\n", stats.ContainerStartTime)
   187  	fmt.Fprintf(w, "uptime_ns\t%d\t\n", stats.UptimeNS)
   188  	if stats.Processor != nil {
   189  		fmt.Fprintf(w, "cpu.total_runtime_ns\t%d\t\n", stats.Processor.TotalRuntimeNS)
   190  		fmt.Fprintf(w, "cpu.runtime_user_ns\t%d\t\n", stats.Processor.RuntimeUserNS)
   191  		fmt.Fprintf(w, "cpu.runtime_kernel_ns\t%d\t\n", stats.Processor.RuntimeKernelNS)
   192  	}
   193  	if stats.Memory != nil {
   194  		fmt.Fprintf(w, "memory.commit_bytes\t%d\t\n", stats.Memory.MemoryUsageCommitBytes)
   195  		fmt.Fprintf(w, "memory.commit_peak_bytes\t%d\t\n", stats.Memory.MemoryUsageCommitPeakBytes)
   196  		fmt.Fprintf(w, "memory.private_working_set_bytes\t%d\t\n", stats.Memory.MemoryUsagePrivateWorkingSetBytes)
   197  	}
   198  	if stats.Storage != nil {
   199  		fmt.Fprintf(w, "storage.read_count_normalized\t%d\t\n", stats.Storage.ReadCountNormalized)
   200  		fmt.Fprintf(w, "storage.read_size_bytes\t%d\t\n", stats.Storage.ReadSizeBytes)
   201  		fmt.Fprintf(w, "storage.write_count_normalized\t%d\t\n", stats.Storage.WriteCountNormalized)
   202  		fmt.Fprintf(w, "storage.write_size_bytes\t%d\t\n", stats.Storage.WriteSizeBytes)
   203  	}
   204  }
   205  
   206  func printWindowsVMStatistics(w *tabwriter.Writer, stats *wstats.VirtualMachineStatistics) {
   207  	fmt.Fprintf(w, "METRIC\tVALUE\t\n")
   208  	if stats.Processor != nil {
   209  		fmt.Fprintf(w, "vm.cpu.total_runtime_ns\t%d\t\n", stats.Processor.TotalRuntimeNS)
   210  	}
   211  	if stats.Memory != nil {
   212  		fmt.Fprintf(w, "vm.memory.working_set_bytes\t%d\t\n", stats.Memory.WorkingSetBytes)
   213  		fmt.Fprintf(w, "vm.memory.virtual_node_count\t%d\t\n", stats.Memory.VirtualNodeCount)
   214  		fmt.Fprintf(w, "vm.memory.available\t%d\t\n", stats.Memory.VmMemory.AvailableMemory)
   215  		fmt.Fprintf(w, "vm.memory.available_buffer\t%d\t\n", stats.Memory.VmMemory.AvailableMemoryBuffer)
   216  		fmt.Fprintf(w, "vm.memory.reserved\t%d\t\n", stats.Memory.VmMemory.ReservedMemory)
   217  		fmt.Fprintf(w, "vm.memory.assigned\t%d\t\n", stats.Memory.VmMemory.AssignedMemory)
   218  		fmt.Fprintf(w, "vm.memory.slp_active\t%t\t\n", stats.Memory.VmMemory.SlpActive)
   219  		fmt.Fprintf(w, "vm.memory.balancing_enabled\t%t\t\n", stats.Memory.VmMemory.BalancingEnabled)
   220  		fmt.Fprintf(w, "vm.memory.dm_operation_in_progress\t%t\t\n", stats.Memory.VmMemory.DmOperationInProgress)
   221  	}
   222  }
   223  

View as plain text