1
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
160 linux := windowsStats.GetLinux()
161
162
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
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