github.com/containerd/containerd@v22.0.0-20200918172823-438c87b8e050+incompatible/cmd/ctr/commands/tasks/metrics.go (about)

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