commit
5d93ece758
@ -19,6 +19,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
_ "github.com/containerd/aufs"
|
_ "github.com/containerd/aufs"
|
||||||
_ "github.com/containerd/containerd/metrics/cgroups"
|
_ "github.com/containerd/containerd/metrics/cgroups"
|
||||||
|
_ "github.com/containerd/containerd/metrics/cgroups/v2"
|
||||||
_ "github.com/containerd/containerd/runtime/v1/linux"
|
_ "github.com/containerd/containerd/runtime/v1/linux"
|
||||||
_ "github.com/containerd/containerd/runtime/v2"
|
_ "github.com/containerd/containerd/runtime/v2"
|
||||||
_ "github.com/containerd/containerd/runtime/v2/runc/options"
|
_ "github.com/containerd/containerd/runtime/v2/runc/options"
|
||||||
|
@ -27,6 +27,7 @@ import (
|
|||||||
|
|
||||||
wstats "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/stats"
|
wstats "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/stats"
|
||||||
v1 "github.com/containerd/cgroups/stats/v1"
|
v1 "github.com/containerd/cgroups/stats/v1"
|
||||||
|
v2 "github.com/containerd/cgroups/v2/stats"
|
||||||
"github.com/containerd/containerd/cmd/ctr/commands"
|
"github.com/containerd/containerd/cmd/ctr/commands"
|
||||||
"github.com/containerd/typeurl"
|
"github.com/containerd/typeurl"
|
||||||
"github.com/urfave/cli"
|
"github.com/urfave/cli"
|
||||||
@ -80,11 +81,14 @@ var metricsCommand = cli.Command{
|
|||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
data *v1.Metrics
|
data *v1.Metrics
|
||||||
|
data2 *v2.Metrics
|
||||||
windowsStats *wstats.Statistics
|
windowsStats *wstats.Statistics
|
||||||
)
|
)
|
||||||
switch v := anydata.(type) {
|
switch v := anydata.(type) {
|
||||||
case *v1.Metrics:
|
case *v1.Metrics:
|
||||||
data = v
|
data = v
|
||||||
|
case *v2.Metrics:
|
||||||
|
data2 = v
|
||||||
case *wstats.Statistics:
|
case *wstats.Statistics:
|
||||||
windowsStats = v
|
windowsStats = v
|
||||||
default:
|
default:
|
||||||
@ -98,6 +102,8 @@ var metricsCommand = cli.Command{
|
|||||||
fmt.Fprintf(w, "%s\t%s\t\n\n", metric.ID, metric.Timestamp)
|
fmt.Fprintf(w, "%s\t%s\t\n\n", metric.ID, metric.Timestamp)
|
||||||
if data != nil {
|
if data != nil {
|
||||||
printCgroupMetricsTable(w, data)
|
printCgroupMetricsTable(w, data)
|
||||||
|
} else if data2 != nil {
|
||||||
|
printCgroup2MetricsTable(w, data2)
|
||||||
} else {
|
} else {
|
||||||
if windowsStats.GetLinux() != nil {
|
if windowsStats.GetLinux() != nil {
|
||||||
printCgroupMetricsTable(w, windowsStats.GetLinux())
|
printCgroupMetricsTable(w, windowsStats.GetLinux())
|
||||||
@ -111,7 +117,7 @@ var metricsCommand = cli.Command{
|
|||||||
}
|
}
|
||||||
return w.Flush()
|
return w.Flush()
|
||||||
case formatJSON:
|
case formatJSON:
|
||||||
marshaledJSON, err := json.MarshalIndent(data, "", " ")
|
marshaledJSON, err := json.MarshalIndent(anydata, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -140,6 +146,14 @@ func printCgroupMetricsTable(w *tabwriter.Writer, data *v1.Metrics) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func printCgroup2MetricsTable(w *tabwriter.Writer, data *v2.Metrics) {
|
||||||
|
fmt.Fprintf(w, "METRIC\tVALUE\t\n")
|
||||||
|
if data.Pids != nil {
|
||||||
|
fmt.Fprintf(w, "pids.current\t%v\t\n", data.Pids.Current)
|
||||||
|
fmt.Fprintf(w, "pids.limit\t%v\t\n", data.Pids.Limit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func printWindowsContainerStatistics(w *tabwriter.Writer, stats *wstats.WindowsContainerStatistics) {
|
func printWindowsContainerStatistics(w *tabwriter.Writer, stats *wstats.WindowsContainerStatistics) {
|
||||||
fmt.Fprintf(w, "METRIC\tVALUE\t\n")
|
fmt.Fprintf(w, "METRIC\tVALUE\t\n")
|
||||||
fmt.Fprintf(w, "timestamp\t%s\t\n", stats.Timestamp)
|
fmt.Fprintf(w, "timestamp\t%s\t\n", stats.Timestamp)
|
||||||
|
95
metrics/cgroups/v2/cgroups.go
Normal file
95
metrics/cgroups/v2/cgroups.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/errdefs"
|
||||||
|
"github.com/containerd/containerd/events"
|
||||||
|
"github.com/containerd/containerd/platforms"
|
||||||
|
"github.com/containerd/containerd/plugin"
|
||||||
|
"github.com/containerd/containerd/runtime"
|
||||||
|
"github.com/containerd/containerd/runtime/v1/linux"
|
||||||
|
metrics "github.com/docker/go-metrics"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Config for the cgroups monitor
|
||||||
|
type Config struct {
|
||||||
|
NoPrometheus bool `toml:"no_prometheus"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
plugin.Register(&plugin.Registration{
|
||||||
|
Type: plugin.TaskMonitorPlugin,
|
||||||
|
ID: "cgroups-v2",
|
||||||
|
InitFn: New,
|
||||||
|
Config: &Config{},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// New returns a new cgroups monitor
|
||||||
|
func New(ic *plugin.InitContext) (interface{}, error) {
|
||||||
|
var ns *metrics.Namespace
|
||||||
|
config := ic.Config.(*Config)
|
||||||
|
if !config.NoPrometheus {
|
||||||
|
ns = metrics.NewNamespace("container", "", nil)
|
||||||
|
}
|
||||||
|
collector := newCollector(ns)
|
||||||
|
if ns != nil {
|
||||||
|
metrics.Register(ns)
|
||||||
|
}
|
||||||
|
ic.Meta.Platforms = append(ic.Meta.Platforms, platforms.DefaultSpec())
|
||||||
|
return &cgroupsMonitor{
|
||||||
|
collector: collector,
|
||||||
|
context: ic.Context,
|
||||||
|
publisher: ic.Events,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type cgroupsMonitor struct {
|
||||||
|
collector *collector
|
||||||
|
context context.Context
|
||||||
|
publisher events.Publisher
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *cgroupsMonitor) Monitor(c runtime.Task) error {
|
||||||
|
if err := m.collector.Add(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
t, ok := c.(*linux.Task)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cg, err := t.Cgroup()
|
||||||
|
if err != nil {
|
||||||
|
if errdefs.IsNotFound(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// OOM handler is not implemented yet
|
||||||
|
_ = cg
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *cgroupsMonitor) Stop(c runtime.Task) error {
|
||||||
|
m.collector.Remove(c)
|
||||||
|
return nil
|
||||||
|
}
|
61
metrics/cgroups/v2/metric.go
Normal file
61
metrics/cgroups/v2/metric.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
v2 "github.com/containerd/containerd/metrics/types/v2"
|
||||||
|
metrics "github.com/docker/go-metrics"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type value struct {
|
||||||
|
v float64
|
||||||
|
l []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type metric struct {
|
||||||
|
name string
|
||||||
|
help string
|
||||||
|
unit metrics.Unit
|
||||||
|
vt prometheus.ValueType
|
||||||
|
labels []string
|
||||||
|
// getValues returns the value and labels for the data
|
||||||
|
getValues func(stats *v2.Metrics) []value
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metric) desc(ns *metrics.Namespace) *prometheus.Desc {
|
||||||
|
// the namespace label is for containerd namespaces
|
||||||
|
return ns.NewDesc(m.name, m.help, m.unit, append([]string{"container_id", "namespace"}, m.labels...)...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *metric) collect(id, namespace string, stats *v2.Metrics, ns *metrics.Namespace, ch chan<- prometheus.Metric, block bool) {
|
||||||
|
values := m.getValues(stats)
|
||||||
|
for _, v := range values {
|
||||||
|
// block signals to block on the sending the metrics so none are missed
|
||||||
|
if block {
|
||||||
|
ch <- prometheus.MustNewConstMetric(m.desc(ns), m.vt, v.v, append([]string{id, namespace}, v.l...)...)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// non-blocking metrics can be dropped if the chan is full
|
||||||
|
select {
|
||||||
|
case ch <- prometheus.MustNewConstMetric(m.desc(ns), m.vt, v.v, append([]string{id, namespace}, v.l...)...):
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
141
metrics/cgroups/v2/metrics.go
Normal file
141
metrics/cgroups/v2/metrics.go
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/containerd/containerd/log"
|
||||||
|
v2 "github.com/containerd/containerd/metrics/types/v2"
|
||||||
|
"github.com/containerd/containerd/namespaces"
|
||||||
|
"github.com/containerd/containerd/runtime"
|
||||||
|
"github.com/containerd/typeurl"
|
||||||
|
metrics "github.com/docker/go-metrics"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// newCollector registers the collector with the provided namespace and returns it so
|
||||||
|
// that cgroups can be added for collection
|
||||||
|
func newCollector(ns *metrics.Namespace) *collector {
|
||||||
|
if ns == nil {
|
||||||
|
return &collector{}
|
||||||
|
}
|
||||||
|
c := &collector{
|
||||||
|
ns: ns,
|
||||||
|
tasks: make(map[string]runtime.Task),
|
||||||
|
}
|
||||||
|
c.metrics = append(c.metrics, pidMetrics...)
|
||||||
|
c.storedMetrics = make(chan prometheus.Metric, 100*len(c.metrics))
|
||||||
|
ns.Add(c)
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func taskID(id, namespace string) string {
|
||||||
|
return fmt.Sprintf("%s-%s", id, namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
// collector provides the ability to collect container stats and export
|
||||||
|
// them in the prometheus format
|
||||||
|
type collector struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
|
||||||
|
tasks map[string]runtime.Task
|
||||||
|
ns *metrics.Namespace
|
||||||
|
metrics []*metric
|
||||||
|
storedMetrics chan prometheus.Metric
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collector) Describe(ch chan<- *prometheus.Desc) {
|
||||||
|
for _, m := range c.metrics {
|
||||||
|
ch <- m.desc(c.ns)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collector) Collect(ch chan<- prometheus.Metric) {
|
||||||
|
c.mu.RLock()
|
||||||
|
wg := &sync.WaitGroup{}
|
||||||
|
for _, t := range c.tasks {
|
||||||
|
wg.Add(1)
|
||||||
|
go c.collect(t, ch, true, wg)
|
||||||
|
}
|
||||||
|
storedLoop:
|
||||||
|
for {
|
||||||
|
// read stored metrics until the channel is flushed
|
||||||
|
select {
|
||||||
|
case m := <-c.storedMetrics:
|
||||||
|
ch <- m
|
||||||
|
default:
|
||||||
|
break storedLoop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.mu.RUnlock()
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *collector) collect(t runtime.Task, ch chan<- prometheus.Metric, block bool, wg *sync.WaitGroup) {
|
||||||
|
if wg != nil {
|
||||||
|
defer wg.Done()
|
||||||
|
}
|
||||||
|
ctx := namespaces.WithNamespace(context.Background(), t.Namespace())
|
||||||
|
stats, err := t.Stats(ctx)
|
||||||
|
if err != nil {
|
||||||
|
log.L.WithError(err).Errorf("stat task %s", t.ID())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data, err := typeurl.UnmarshalAny(stats)
|
||||||
|
if err != nil {
|
||||||
|
log.L.WithError(err).Errorf("unmarshal stats for %s", t.ID())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s, ok := data.(*v2.Metrics)
|
||||||
|
if !ok {
|
||||||
|
log.L.WithError(err).Errorf("invalid metric type for %s", t.ID())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, m := range c.metrics {
|
||||||
|
m.collect(t.ID(), t.Namespace(), s, c.ns, ch, block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add adds the provided cgroup and id so that metrics are collected and exported
|
||||||
|
func (c *collector) Add(t runtime.Task) error {
|
||||||
|
if c.ns == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
id := taskID(t.ID(), t.Namespace())
|
||||||
|
if _, ok := c.tasks[id]; ok {
|
||||||
|
return nil // requests to collect metrics should be idempotent
|
||||||
|
}
|
||||||
|
c.tasks[id] = t
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove removes the provided cgroup by id from the collector
|
||||||
|
func (c *collector) Remove(t runtime.Task) {
|
||||||
|
if c.ns == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.mu.Lock()
|
||||||
|
defer c.mu.Unlock()
|
||||||
|
delete(c.tasks, taskID(t.ID(), t.Namespace()))
|
||||||
|
}
|
60
metrics/cgroups/v2/pids.go
Normal file
60
metrics/cgroups/v2/pids.go
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
v2 "github.com/containerd/containerd/metrics/types/v2"
|
||||||
|
metrics "github.com/docker/go-metrics"
|
||||||
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pidMetrics = []*metric{
|
||||||
|
{
|
||||||
|
name: "pids_v2",
|
||||||
|
help: "The limit to the number of pids allowed",
|
||||||
|
unit: metrics.Unit("limit"),
|
||||||
|
vt: prometheus.GaugeValue,
|
||||||
|
getValues: func(stats *v2.Metrics) []value {
|
||||||
|
if stats.Pids == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []value{
|
||||||
|
{
|
||||||
|
v: float64(stats.Pids.Limit),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "pids_v2",
|
||||||
|
help: "The current number of pids",
|
||||||
|
unit: metrics.Unit("current"),
|
||||||
|
vt: prometheus.GaugeValue,
|
||||||
|
getValues: func(stats *v2.Metrics) []value {
|
||||||
|
if stats.Pids == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return []value{
|
||||||
|
{
|
||||||
|
v: float64(stats.Pids.Current),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
34
metrics/types/v2/types.go
Normal file
34
metrics/types/v2/types.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
v2 "github.com/containerd/cgroups/v2/stats"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
// Metrics alias
|
||||||
|
Metrics = v2.Metrics
|
||||||
|
// MemoryStat alias
|
||||||
|
MemoryStat = v2.MemoryStat
|
||||||
|
// CPUStat alias
|
||||||
|
CPUStat = v2.CPUStat
|
||||||
|
// PidsStat alias
|
||||||
|
PidsStat = v2.PidsStat
|
||||||
|
)
|
@ -20,11 +20,22 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/containerd/cgroups"
|
"github.com/containerd/cgroups"
|
||||||
|
cgroupsv2 "github.com/containerd/cgroups/v2"
|
||||||
"github.com/containerd/containerd/namespaces"
|
"github.com/containerd/containerd/namespaces"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WithNamespaceCgroupDeletion removes the cgroup directory that was created for the namespace
|
// WithNamespaceCgroupDeletion removes the cgroup directory that was created for the namespace
|
||||||
func WithNamespaceCgroupDeletion(ctx context.Context, i *namespaces.DeleteInfo) error {
|
func WithNamespaceCgroupDeletion(ctx context.Context, i *namespaces.DeleteInfo) error {
|
||||||
|
if cgroups.Mode() == cgroups.Unified {
|
||||||
|
cg, err := cgroupsv2.LoadManager("/sys/fs/cgroup", i.Name)
|
||||||
|
if err != nil {
|
||||||
|
if err == cgroupsv2.ErrCgroupDeleted {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return cg.Delete()
|
||||||
|
}
|
||||||
cg, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(i.Name))
|
cg, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(i.Name))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err == cgroups.ErrCgroupDeleted {
|
if err == cgroups.ErrCgroupDeleted {
|
||||||
|
@ -26,6 +26,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/containerd/cgroups"
|
"github.com/containerd/cgroups"
|
||||||
|
cgroupsv2 "github.com/containerd/cgroups/v2"
|
||||||
"github.com/containerd/console"
|
"github.com/containerd/console"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
"github.com/containerd/containerd/mount"
|
"github.com/containerd/containerd/mount"
|
||||||
@ -133,10 +134,23 @@ func NewContainer(ctx context.Context, platform stdio.Platform, r *task.CreateTa
|
|||||||
}
|
}
|
||||||
pid := p.Pid()
|
pid := p.Pid()
|
||||||
if pid > 0 {
|
if pid > 0 {
|
||||||
cg, err := cgroups.Load(cgroups.V1, cgroups.PidPath(pid))
|
var cg interface{}
|
||||||
|
if cgroups.Mode() == cgroups.Unified {
|
||||||
|
g, err := cgroupsv2.PidGroupPath(pid)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Errorf("loading cgroup2 for %d", pid)
|
||||||
|
return container, nil
|
||||||
|
}
|
||||||
|
cg, err = cgroupsv2.LoadManager("/sys/fs/cgroup", g)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Errorf("loading cgroup2 for %d", pid)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cg, err = cgroups.Load(cgroups.V1, cgroups.PidPath(pid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithError(err).Errorf("loading cgroup for %d", pid)
|
logrus.WithError(err).Errorf("loading cgroup for %d", pid)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
container.cgroup = cg
|
container.cgroup = cg
|
||||||
}
|
}
|
||||||
return container, nil
|
return container, nil
|
||||||
@ -190,7 +204,8 @@ type Container struct {
|
|||||||
// Bundle path
|
// Bundle path
|
||||||
Bundle string
|
Bundle string
|
||||||
|
|
||||||
cgroup cgroups.Cgroup
|
// cgroup is either cgroups.Cgroup or *cgroupsv2.Manager
|
||||||
|
cgroup interface{}
|
||||||
process process.Process
|
process process.Process
|
||||||
processes map[string]process.Process
|
processes map[string]process.Process
|
||||||
reservedProcess map[string]struct{}
|
reservedProcess map[string]struct{}
|
||||||
@ -228,14 +243,14 @@ func (c *Container) Pid() int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Cgroup of the container
|
// Cgroup of the container
|
||||||
func (c *Container) Cgroup() cgroups.Cgroup {
|
func (c *Container) Cgroup() interface{} {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.Unlock()
|
||||||
return c.cgroup
|
return c.cgroup
|
||||||
}
|
}
|
||||||
|
|
||||||
// CgroupSet sets the cgroup to the container
|
// CgroupSet sets the cgroup to the container
|
||||||
func (c *Container) CgroupSet(cg cgroups.Cgroup) {
|
func (c *Container) CgroupSet(cg interface{}) {
|
||||||
c.mu.Lock()
|
c.mu.Lock()
|
||||||
c.cgroup = cg
|
c.cgroup = cg
|
||||||
c.mu.Unlock()
|
c.mu.Unlock()
|
||||||
@ -307,10 +322,22 @@ func (c *Container) Start(ctx context.Context, r *task.StartRequest) (process.Pr
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if c.Cgroup() == nil && p.Pid() > 0 {
|
if c.Cgroup() == nil && p.Pid() > 0 {
|
||||||
cg, err := cgroups.Load(cgroups.V1, cgroups.PidPath(p.Pid()))
|
var cg interface{}
|
||||||
|
if cgroups.Mode() == cgroups.Unified {
|
||||||
|
g, err := cgroupsv2.PidGroupPath(p.Pid())
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Errorf("loading cgroup2 for %d", p.Pid())
|
||||||
|
}
|
||||||
|
cg, err = cgroupsv2.LoadManager("/sys/fs/cgroup", g)
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Errorf("loading cgroup2 for %d", p.Pid())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cg, err = cgroups.Load(cgroups.V1, cgroups.PidPath(p.Pid()))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logrus.WithError(err).Errorf("loading cgroup for %d", p.Pid())
|
logrus.WithError(err).Errorf("loading cgroup for %d", p.Pid())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
c.cgroup = cg
|
c.cgroup = cg
|
||||||
}
|
}
|
||||||
return p, nil
|
return p, nil
|
||||||
|
@ -271,9 +271,13 @@ func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI.
|
|||||||
s.eventSendMu.Unlock()
|
s.eventSendMu.Unlock()
|
||||||
return nil, errdefs.ToGRPC(err)
|
return nil, errdefs.ToGRPC(err)
|
||||||
}
|
}
|
||||||
if err := s.ep.Add(container.ID, container.Cgroup()); err != nil {
|
if cg, ok := container.Cgroup().(cgroups.Cgroup); ok {
|
||||||
|
if err := s.ep.Add(container.ID, cg); err != nil {
|
||||||
logrus.WithError(err).Error("add cg to OOM monitor")
|
logrus.WithError(err).Error("add cg to OOM monitor")
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
logrus.WithError(errdefs.ErrNotImplemented).Error("add cg to OOM monitor")
|
||||||
|
}
|
||||||
switch r.ExecID {
|
switch r.ExecID {
|
||||||
case "":
|
case "":
|
||||||
s.send(&eventstypes.TaskStart{
|
s.send(&eventstypes.TaskStart{
|
||||||
@ -545,7 +549,14 @@ func (s *service) Shutdown(ctx context.Context, r *taskAPI.ShutdownRequest) (*pt
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *service) Stats(ctx context.Context, r *taskAPI.StatsRequest) (*taskAPI.StatsResponse, error) {
|
func (s *service) Stats(ctx context.Context, r *taskAPI.StatsRequest) (*taskAPI.StatsResponse, error) {
|
||||||
cg := s.container.Cgroup()
|
cgx := s.container.Cgroup()
|
||||||
|
if cgx == nil {
|
||||||
|
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "cgroup does not exist")
|
||||||
|
}
|
||||||
|
cg, ok := cgx.(cgroups.Cgroup)
|
||||||
|
if !ok {
|
||||||
|
return nil, errdefs.ToGRPCf(errdefs.ErrNotImplemented, "cgroup v2 not implemented for Stats")
|
||||||
|
}
|
||||||
if cg == nil {
|
if cg == nil {
|
||||||
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "cgroup does not exist")
|
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "cgroup does not exist")
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/containerd/cgroups"
|
"github.com/containerd/cgroups"
|
||||||
|
cgroupsv2 "github.com/containerd/cgroups/v2"
|
||||||
eventstypes "github.com/containerd/containerd/api/events"
|
eventstypes "github.com/containerd/containerd/api/events"
|
||||||
"github.com/containerd/containerd/api/types/task"
|
"github.com/containerd/containerd/api/types/task"
|
||||||
"github.com/containerd/containerd/errdefs"
|
"github.com/containerd/containerd/errdefs"
|
||||||
@ -222,6 +223,18 @@ func (s *service) StartShim(ctx context.Context, id, containerdBinary, container
|
|||||||
}
|
}
|
||||||
if opts, ok := v.(*options.Options); ok {
|
if opts, ok := v.(*options.Options); ok {
|
||||||
if opts.ShimCgroup != "" {
|
if opts.ShimCgroup != "" {
|
||||||
|
if cgroups.Mode() == cgroups.Unified {
|
||||||
|
if err := cgroupsv2.VerifyGroupPath(opts.ShimCgroup); err != nil {
|
||||||
|
return "", errors.Wrapf(err, "failed to verify cgroup path %q", opts.ShimCgroup)
|
||||||
|
}
|
||||||
|
cg, err := cgroupsv2.LoadManager("/sys/fs/cgroup", opts.ShimCgroup)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "failed to load cgroup %s", opts.ShimCgroup)
|
||||||
|
}
|
||||||
|
if err := cg.AddProc(uint64(cmd.Process.Pid)); err != nil {
|
||||||
|
return "", errors.Wrapf(err, "failed to join cgroup %s", opts.ShimCgroup)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
cg, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(opts.ShimCgroup))
|
cg, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(opts.ShimCgroup))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrapf(err, "failed to load cgroup %s", opts.ShimCgroup)
|
return "", errors.Wrapf(err, "failed to load cgroup %s", opts.ShimCgroup)
|
||||||
@ -235,6 +248,7 @@ func (s *service) StartShim(ctx context.Context, id, containerdBinary, container
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if err := shim.AdjustOOMScore(cmd.Process.Pid); err != nil {
|
if err := shim.AdjustOOMScore(cmd.Process.Pid); err != nil {
|
||||||
return "", errors.Wrap(err, "failed to adjust OOM score for shim")
|
return "", errors.Wrap(err, "failed to adjust OOM score for shim")
|
||||||
}
|
}
|
||||||
@ -316,9 +330,25 @@ func (s *service) Start(ctx context.Context, r *taskAPI.StartRequest) (*taskAPI.
|
|||||||
s.eventSendMu.Unlock()
|
s.eventSendMu.Unlock()
|
||||||
return nil, errdefs.ToGRPC(err)
|
return nil, errdefs.ToGRPC(err)
|
||||||
}
|
}
|
||||||
if err := s.ep.Add(container.ID, container.Cgroup()); err != nil {
|
switch cg := container.Cgroup().(type) {
|
||||||
|
case cgroups.Cgroup:
|
||||||
|
if err := s.ep.Add(container.ID, cg); err != nil {
|
||||||
logrus.WithError(err).Error("add cg to OOM monitor")
|
logrus.WithError(err).Error("add cg to OOM monitor")
|
||||||
}
|
}
|
||||||
|
case *cgroupsv2.Manager:
|
||||||
|
allControllers, err := cg.RootControllers()
|
||||||
|
if err != nil {
|
||||||
|
logrus.WithError(err).Error("failed to get root controllers")
|
||||||
|
} else {
|
||||||
|
if err := cg.ToggleControllers(allControllers, cgroupsv2.Enable); err != nil {
|
||||||
|
logrus.WithError(err).Errorf("failed to enable controllers (%v)", allControllers)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OOM monitor is not implemented yet
|
||||||
|
logrus.WithError(errdefs.ErrNotImplemented).Warn("add cg to OOM monitor")
|
||||||
|
}
|
||||||
|
|
||||||
switch r.ExecID {
|
switch r.ExecID {
|
||||||
case "":
|
case "":
|
||||||
s.send(&eventstypes.TaskStart{
|
s.send(&eventstypes.TaskStart{
|
||||||
@ -608,15 +638,28 @@ func (s *service) Stats(ctx context.Context, r *taskAPI.StatsRequest) (*taskAPI.
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
cg := container.Cgroup()
|
cgx := container.Cgroup()
|
||||||
if cg == nil {
|
if cgx == nil {
|
||||||
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "cgroup does not exist")
|
return nil, errdefs.ToGRPCf(errdefs.ErrNotFound, "cgroup does not exist")
|
||||||
}
|
}
|
||||||
|
var statsx interface{}
|
||||||
|
switch cg := cgx.(type) {
|
||||||
|
case cgroups.Cgroup:
|
||||||
stats, err := cg.Stat(cgroups.IgnoreNotExist)
|
stats, err := cg.Stat(cgroups.IgnoreNotExist)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
data, err := typeurl.MarshalAny(stats)
|
statsx = stats
|
||||||
|
case *cgroupsv2.Manager:
|
||||||
|
stats, err := cg.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
statsx = stats
|
||||||
|
default:
|
||||||
|
return nil, errdefs.ToGRPCf(errdefs.ErrNotImplemented, "unsupported cgroup type %T", cg)
|
||||||
|
}
|
||||||
|
data, err := typeurl.MarshalAny(statsx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containerd/cgroups"
|
"github.com/containerd/cgroups"
|
||||||
|
cgroupsv2 "github.com/containerd/cgroups/v2"
|
||||||
"github.com/containerd/containerd/log"
|
"github.com/containerd/containerd/log"
|
||||||
srvconfig "github.com/containerd/containerd/services/server/config"
|
srvconfig "github.com/containerd/containerd/services/server/config"
|
||||||
"github.com/containerd/containerd/sys"
|
"github.com/containerd/containerd/sys"
|
||||||
@ -37,6 +38,20 @@ func apply(ctx context.Context, config *srvconfig.Config) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if config.Cgroup.Path != "" {
|
if config.Cgroup.Path != "" {
|
||||||
|
if cgroups.Mode() == cgroups.Unified {
|
||||||
|
cg, err := cgroupsv2.LoadManager("/sys/fs/cgroup", config.Cgroup.Path)
|
||||||
|
if err != nil {
|
||||||
|
if err != cgroupsv2.ErrCgroupDeleted {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if cg, err = cgroupsv2.NewManager("/sys/fs/cgroup", config.Cgroup.Path, nil); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := cg.AddProc(uint64(os.Getpid())); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
cg, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(config.Cgroup.Path))
|
cg, err := cgroups.Load(cgroups.V1, cgroups.StaticPath(config.Cgroup.Path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != cgroups.ErrCgroupDeleted {
|
if err != cgroups.ErrCgroupDeleted {
|
||||||
@ -52,6 +67,7 @@ func apply(ctx context.Context, config *srvconfig.Config) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
|
github.com/beorn7/perks 4c0e84591b9aa9e6dcfdf3e020114cd81f89d5f9
|
||||||
github.com/BurntSushi/toml 3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005 # v0.3.1
|
github.com/BurntSushi/toml 3012a1dbe2e4bd1391d42b32f0577cb7bbc7f005 # v0.3.1
|
||||||
github.com/containerd/btrfs af5082808c833de0e79c1e72eea9fea239364877
|
github.com/containerd/btrfs af5082808c833de0e79c1e72eea9fea239364877
|
||||||
github.com/containerd/cgroups abd0b19954a6b05e0963f48427062d1481b7faad
|
github.com/containerd/cgroups fada802a7909d430bd17126fd6e4bbf5716f2bcd
|
||||||
github.com/containerd/console 02ecf6a7291e65f4a361525245c2bea023dc2e0b
|
github.com/containerd/console 02ecf6a7291e65f4a361525245c2bea023dc2e0b
|
||||||
github.com/containerd/continuity f2a389ac0a02ce21c09edd7344677a601970f41c
|
github.com/containerd/continuity f2a389ac0a02ce21c09edd7344677a601970f41c
|
||||||
github.com/containerd/fifo bda0ff6ed73c67bfb5e62bc9c697f146b7fd7f13
|
github.com/containerd/fifo bda0ff6ed73c67bfb5e62bc9c697f146b7fd7f13
|
||||||
@ -89,3 +89,6 @@ github.com/mistifyio/go-zfs f784269be439d704d3dfa1906f45
|
|||||||
|
|
||||||
# aufs dependencies
|
# aufs dependencies
|
||||||
github.com/containerd/aufs 371312c1e31c210a21e49bf3dfd3f31729ed9f2f
|
github.com/containerd/aufs 371312c1e31c210a21e49bf3dfd3f31729ed9f2f
|
||||||
|
|
||||||
|
# cgroups dependencies
|
||||||
|
github.com/cilium/ebpf 60c3aa43f488292fe2ee50fb8b833b383ca8ebbb
|
||||||
|
23
vendor/github.com/cilium/ebpf/LICENSE
generated
vendored
Normal file
23
vendor/github.com/cilium/ebpf/LICENSE
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2017 Nathan Sweet
|
||||||
|
Copyright (c) 2018, 2019 Cloudflare
|
||||||
|
Copyright (c) 2019 Authors of Cilium
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
203
vendor/github.com/cilium/ebpf/abi.go
generated
vendored
Normal file
203
vendor/github.com/cilium/ebpf/abi.go
generated
vendored
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/cilium/ebpf/internal"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MapABI are the attributes of a Map which are available across all supported kernels.
|
||||||
|
type MapABI struct {
|
||||||
|
Type MapType
|
||||||
|
KeySize uint32
|
||||||
|
ValueSize uint32
|
||||||
|
MaxEntries uint32
|
||||||
|
Flags uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMapABIFromSpec(spec *MapSpec) *MapABI {
|
||||||
|
return &MapABI{
|
||||||
|
spec.Type,
|
||||||
|
spec.KeySize,
|
||||||
|
spec.ValueSize,
|
||||||
|
spec.MaxEntries,
|
||||||
|
spec.Flags,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMapABIFromFd(fd *bpfFD) (string, *MapABI, error) {
|
||||||
|
info, err := bpfGetMapInfoByFD(fd)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Cause(err) == syscall.EINVAL {
|
||||||
|
abi, err := newMapABIFromProc(fd)
|
||||||
|
return "", abi, err
|
||||||
|
}
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", &MapABI{
|
||||||
|
MapType(info.mapType),
|
||||||
|
info.keySize,
|
||||||
|
info.valueSize,
|
||||||
|
info.maxEntries,
|
||||||
|
info.flags,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMapABIFromProc(fd *bpfFD) (*MapABI, error) {
|
||||||
|
var abi MapABI
|
||||||
|
err := scanFdInfo(fd, map[string]interface{}{
|
||||||
|
"map_type": &abi.Type,
|
||||||
|
"key_size": &abi.KeySize,
|
||||||
|
"value_size": &abi.ValueSize,
|
||||||
|
"max_entries": &abi.MaxEntries,
|
||||||
|
"map_flags": &abi.Flags,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &abi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if two ABIs have the same values.
|
||||||
|
func (abi *MapABI) Equal(other *MapABI) bool {
|
||||||
|
switch {
|
||||||
|
case abi.Type != other.Type:
|
||||||
|
return false
|
||||||
|
case abi.KeySize != other.KeySize:
|
||||||
|
return false
|
||||||
|
case abi.ValueSize != other.ValueSize:
|
||||||
|
return false
|
||||||
|
case abi.MaxEntries != other.MaxEntries:
|
||||||
|
return false
|
||||||
|
case abi.Flags != other.Flags:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProgramABI are the attributes of a Program which are available across all supported kernels.
|
||||||
|
type ProgramABI struct {
|
||||||
|
Type ProgramType
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProgramABIFromSpec(spec *ProgramSpec) *ProgramABI {
|
||||||
|
return &ProgramABI{
|
||||||
|
spec.Type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProgramABIFromFd(fd *bpfFD) (string, *ProgramABI, error) {
|
||||||
|
info, err := bpfGetProgInfoByFD(fd)
|
||||||
|
if err != nil {
|
||||||
|
if errors.Cause(err) == syscall.EINVAL {
|
||||||
|
return newProgramABIFromProc(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var name string
|
||||||
|
if bpfName := convertCString(info.name[:]); bpfName != "" {
|
||||||
|
name = bpfName
|
||||||
|
} else {
|
||||||
|
name = convertCString(info.tag[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
return name, &ProgramABI{
|
||||||
|
Type: ProgramType(info.progType),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProgramABIFromProc(fd *bpfFD) (string, *ProgramABI, error) {
|
||||||
|
var (
|
||||||
|
abi ProgramABI
|
||||||
|
name string
|
||||||
|
)
|
||||||
|
|
||||||
|
err := scanFdInfo(fd, map[string]interface{}{
|
||||||
|
"prog_type": &abi.Type,
|
||||||
|
"prog_tag": &name,
|
||||||
|
})
|
||||||
|
if errors.Cause(err) == errMissingFields {
|
||||||
|
return "", nil, &internal.UnsupportedFeatureError{
|
||||||
|
Name: "reading ABI from /proc/self/fdinfo",
|
||||||
|
MinimumVersion: internal.Version{4, 11, 0},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return name, &abi, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func scanFdInfo(fd *bpfFD, fields map[string]interface{}) error {
|
||||||
|
raw, err := fd.value()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fh, err := os.Open(fmt.Sprintf("/proc/self/fdinfo/%d", raw))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer fh.Close()
|
||||||
|
|
||||||
|
return errors.Wrap(scanFdInfoReader(fh, fields), fh.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
var errMissingFields = errors.New("missing fields")
|
||||||
|
|
||||||
|
func scanFdInfoReader(r io.Reader, fields map[string]interface{}) error {
|
||||||
|
var (
|
||||||
|
scanner = bufio.NewScanner(r)
|
||||||
|
scanned int
|
||||||
|
)
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
parts := bytes.SplitN(scanner.Bytes(), []byte("\t"), 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
name := bytes.TrimSuffix(parts[0], []byte(":"))
|
||||||
|
field, ok := fields[string(name)]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if n, err := fmt.Fscanln(bytes.NewReader(parts[1]), field); err != nil || n != 1 {
|
||||||
|
return errors.Wrapf(err, "can't parse field %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
scanned++
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if scanned != len(fields) {
|
||||||
|
return errMissingFields
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if two ABIs have the same values.
|
||||||
|
func (abi *ProgramABI) Equal(other *ProgramABI) bool {
|
||||||
|
switch {
|
||||||
|
case abi.Type != other.Type:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
149
vendor/github.com/cilium/ebpf/asm/alu.go
generated
vendored
Normal file
149
vendor/github.com/cilium/ebpf/asm/alu.go
generated
vendored
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
package asm
|
||||||
|
|
||||||
|
//go:generate stringer -output alu_string.go -type=Source,Endianness,ALUOp
|
||||||
|
|
||||||
|
// Source of ALU / ALU64 / Branch operations
|
||||||
|
//
|
||||||
|
// msb lsb
|
||||||
|
// +----+-+---+
|
||||||
|
// |op |S|cls|
|
||||||
|
// +----+-+---+
|
||||||
|
type Source uint8
|
||||||
|
|
||||||
|
const sourceMask OpCode = 0x08
|
||||||
|
|
||||||
|
// Source bitmask
|
||||||
|
const (
|
||||||
|
// InvalidSource is returned by getters when invoked
|
||||||
|
// on non ALU / branch OpCodes.
|
||||||
|
InvalidSource Source = 0xff
|
||||||
|
// ImmSource src is from constant
|
||||||
|
ImmSource Source = 0x00
|
||||||
|
// RegSource src is from register
|
||||||
|
RegSource Source = 0x08
|
||||||
|
)
|
||||||
|
|
||||||
|
// The Endianness of a byte swap instruction.
|
||||||
|
type Endianness uint8
|
||||||
|
|
||||||
|
const endianMask = sourceMask
|
||||||
|
|
||||||
|
// Endian flags
|
||||||
|
const (
|
||||||
|
InvalidEndian Endianness = 0xff
|
||||||
|
// Convert to little endian
|
||||||
|
LE Endianness = 0x00
|
||||||
|
// Convert to big endian
|
||||||
|
BE Endianness = 0x08
|
||||||
|
)
|
||||||
|
|
||||||
|
// ALUOp are ALU / ALU64 operations
|
||||||
|
//
|
||||||
|
// msb lsb
|
||||||
|
// +----+-+---+
|
||||||
|
// |OP |s|cls|
|
||||||
|
// +----+-+---+
|
||||||
|
type ALUOp uint8
|
||||||
|
|
||||||
|
const aluMask OpCode = 0xf0
|
||||||
|
|
||||||
|
const (
|
||||||
|
// InvalidALUOp is returned by getters when invoked
|
||||||
|
// on non ALU OpCodes
|
||||||
|
InvalidALUOp ALUOp = 0xff
|
||||||
|
// Add - addition
|
||||||
|
Add ALUOp = 0x00
|
||||||
|
// Sub - subtraction
|
||||||
|
Sub ALUOp = 0x10
|
||||||
|
// Mul - multiplication
|
||||||
|
Mul ALUOp = 0x20
|
||||||
|
// Div - division
|
||||||
|
Div ALUOp = 0x30
|
||||||
|
// Or - bitwise or
|
||||||
|
Or ALUOp = 0x40
|
||||||
|
// And - bitwise and
|
||||||
|
And ALUOp = 0x50
|
||||||
|
// LSh - bitwise shift left
|
||||||
|
LSh ALUOp = 0x60
|
||||||
|
// RSh - bitwise shift right
|
||||||
|
RSh ALUOp = 0x70
|
||||||
|
// Neg - sign/unsign signing bit
|
||||||
|
Neg ALUOp = 0x80
|
||||||
|
// Mod - modulo
|
||||||
|
Mod ALUOp = 0x90
|
||||||
|
// Xor - bitwise xor
|
||||||
|
Xor ALUOp = 0xa0
|
||||||
|
// Mov - move value from one place to another
|
||||||
|
Mov ALUOp = 0xb0
|
||||||
|
// ArSh - arithmatic shift
|
||||||
|
ArSh ALUOp = 0xc0
|
||||||
|
// Swap - endian conversions
|
||||||
|
Swap ALUOp = 0xd0
|
||||||
|
)
|
||||||
|
|
||||||
|
// HostTo converts from host to another endianness.
|
||||||
|
func HostTo(endian Endianness, dst Register, size Size) Instruction {
|
||||||
|
var imm int64
|
||||||
|
switch size {
|
||||||
|
case Half:
|
||||||
|
imm = 16
|
||||||
|
case Word:
|
||||||
|
imm = 32
|
||||||
|
case DWord:
|
||||||
|
imm = 64
|
||||||
|
default:
|
||||||
|
return Instruction{OpCode: InvalidOpCode}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Instruction{
|
||||||
|
OpCode: OpCode(ALUClass).SetALUOp(Swap).SetSource(Source(endian)),
|
||||||
|
Dst: dst,
|
||||||
|
Constant: imm,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Op returns the OpCode for an ALU operation with a given source.
|
||||||
|
func (op ALUOp) Op(source Source) OpCode {
|
||||||
|
return OpCode(ALU64Class).SetALUOp(op).SetSource(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reg emits `dst (op) src`.
|
||||||
|
func (op ALUOp) Reg(dst, src Register) Instruction {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: op.Op(RegSource),
|
||||||
|
Dst: dst,
|
||||||
|
Src: src,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imm emits `dst (op) value`.
|
||||||
|
func (op ALUOp) Imm(dst Register, value int32) Instruction {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: op.Op(ImmSource),
|
||||||
|
Dst: dst,
|
||||||
|
Constant: int64(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Op32 returns the OpCode for a 32-bit ALU operation with a given source.
|
||||||
|
func (op ALUOp) Op32(source Source) OpCode {
|
||||||
|
return OpCode(ALUClass).SetALUOp(op).SetSource(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reg32 emits `dst (op) src`, zeroing the upper 32 bit of dst.
|
||||||
|
func (op ALUOp) Reg32(dst, src Register) Instruction {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: op.Op32(RegSource),
|
||||||
|
Dst: dst,
|
||||||
|
Src: src,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imm32 emits `dst (op) value`, zeroing the upper 32 bit of dst.
|
||||||
|
func (op ALUOp) Imm32(dst Register, value int32) Instruction {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: op.Op32(ImmSource),
|
||||||
|
Dst: dst,
|
||||||
|
Constant: int64(value),
|
||||||
|
}
|
||||||
|
}
|
107
vendor/github.com/cilium/ebpf/asm/alu_string.go
generated
vendored
Normal file
107
vendor/github.com/cilium/ebpf/asm/alu_string.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
// Code generated by "stringer -output alu_string.go -type=Source,Endianness,ALUOp"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package asm
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[InvalidSource-255]
|
||||||
|
_ = x[ImmSource-0]
|
||||||
|
_ = x[RegSource-8]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_Source_name_0 = "ImmSource"
|
||||||
|
_Source_name_1 = "RegSource"
|
||||||
|
_Source_name_2 = "InvalidSource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i Source) String() string {
|
||||||
|
switch {
|
||||||
|
case i == 0:
|
||||||
|
return _Source_name_0
|
||||||
|
case i == 8:
|
||||||
|
return _Source_name_1
|
||||||
|
case i == 255:
|
||||||
|
return _Source_name_2
|
||||||
|
default:
|
||||||
|
return "Source(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[InvalidEndian-255]
|
||||||
|
_ = x[LE-0]
|
||||||
|
_ = x[BE-8]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_Endianness_name_0 = "LE"
|
||||||
|
_Endianness_name_1 = "BE"
|
||||||
|
_Endianness_name_2 = "InvalidEndian"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i Endianness) String() string {
|
||||||
|
switch {
|
||||||
|
case i == 0:
|
||||||
|
return _Endianness_name_0
|
||||||
|
case i == 8:
|
||||||
|
return _Endianness_name_1
|
||||||
|
case i == 255:
|
||||||
|
return _Endianness_name_2
|
||||||
|
default:
|
||||||
|
return "Endianness(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[InvalidALUOp-255]
|
||||||
|
_ = x[Add-0]
|
||||||
|
_ = x[Sub-16]
|
||||||
|
_ = x[Mul-32]
|
||||||
|
_ = x[Div-48]
|
||||||
|
_ = x[Or-64]
|
||||||
|
_ = x[And-80]
|
||||||
|
_ = x[LSh-96]
|
||||||
|
_ = x[RSh-112]
|
||||||
|
_ = x[Neg-128]
|
||||||
|
_ = x[Mod-144]
|
||||||
|
_ = x[Xor-160]
|
||||||
|
_ = x[Mov-176]
|
||||||
|
_ = x[ArSh-192]
|
||||||
|
_ = x[Swap-208]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _ALUOp_name = "AddSubMulDivOrAndLShRShNegModXorMovArShSwapInvalidALUOp"
|
||||||
|
|
||||||
|
var _ALUOp_map = map[ALUOp]string{
|
||||||
|
0: _ALUOp_name[0:3],
|
||||||
|
16: _ALUOp_name[3:6],
|
||||||
|
32: _ALUOp_name[6:9],
|
||||||
|
48: _ALUOp_name[9:12],
|
||||||
|
64: _ALUOp_name[12:14],
|
||||||
|
80: _ALUOp_name[14:17],
|
||||||
|
96: _ALUOp_name[17:20],
|
||||||
|
112: _ALUOp_name[20:23],
|
||||||
|
128: _ALUOp_name[23:26],
|
||||||
|
144: _ALUOp_name[26:29],
|
||||||
|
160: _ALUOp_name[29:32],
|
||||||
|
176: _ALUOp_name[32:35],
|
||||||
|
192: _ALUOp_name[35:39],
|
||||||
|
208: _ALUOp_name[39:43],
|
||||||
|
255: _ALUOp_name[43:55],
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i ALUOp) String() string {
|
||||||
|
if str, ok := _ALUOp_map[i]; ok {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
return "ALUOp(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
2
vendor/github.com/cilium/ebpf/asm/doc.go
generated
vendored
Normal file
2
vendor/github.com/cilium/ebpf/asm/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// Package asm is an assembler for eBPF bytecode.
|
||||||
|
package asm
|
143
vendor/github.com/cilium/ebpf/asm/func.go
generated
vendored
Normal file
143
vendor/github.com/cilium/ebpf/asm/func.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package asm
|
||||||
|
|
||||||
|
//go:generate stringer -output func_string.go -type=BuiltinFunc
|
||||||
|
|
||||||
|
// BuiltinFunc is a built-in eBPF function.
|
||||||
|
type BuiltinFunc int32
|
||||||
|
|
||||||
|
// eBPF built-in functions
|
||||||
|
//
|
||||||
|
// You can renegerate this list using the following gawk script:
|
||||||
|
//
|
||||||
|
// /FN\(.+\),/ {
|
||||||
|
// match($1, /\((.+)\)/, r)
|
||||||
|
// split(r[1], p, "_")
|
||||||
|
// printf "Fn"
|
||||||
|
// for (i in p) {
|
||||||
|
// printf "%s%s", toupper(substr(p[i], 1, 1)), substr(p[i], 2)
|
||||||
|
// }
|
||||||
|
// print ""
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The script expects include/uapi/linux/bpf.h as it's input.
|
||||||
|
const (
|
||||||
|
FnUnspec BuiltinFunc = iota
|
||||||
|
FnMapLookupElem
|
||||||
|
FnMapUpdateElem
|
||||||
|
FnMapDeleteElem
|
||||||
|
FnProbeRead
|
||||||
|
FnKtimeGetNs
|
||||||
|
FnTracePrintk
|
||||||
|
FnGetPrandomU32
|
||||||
|
FnGetSmpProcessorId
|
||||||
|
FnSkbStoreBytes
|
||||||
|
FnL3CsumReplace
|
||||||
|
FnL4CsumReplace
|
||||||
|
FnTailCall
|
||||||
|
FnCloneRedirect
|
||||||
|
FnGetCurrentPidTgid
|
||||||
|
FnGetCurrentUidGid
|
||||||
|
FnGetCurrentComm
|
||||||
|
FnGetCgroupClassid
|
||||||
|
FnSkbVlanPush
|
||||||
|
FnSkbVlanPop
|
||||||
|
FnSkbGetTunnelKey
|
||||||
|
FnSkbSetTunnelKey
|
||||||
|
FnPerfEventRead
|
||||||
|
FnRedirect
|
||||||
|
FnGetRouteRealm
|
||||||
|
FnPerfEventOutput
|
||||||
|
FnSkbLoadBytes
|
||||||
|
FnGetStackid
|
||||||
|
FnCsumDiff
|
||||||
|
FnSkbGetTunnelOpt
|
||||||
|
FnSkbSetTunnelOpt
|
||||||
|
FnSkbChangeProto
|
||||||
|
FnSkbChangeType
|
||||||
|
FnSkbUnderCgroup
|
||||||
|
FnGetHashRecalc
|
||||||
|
FnGetCurrentTask
|
||||||
|
FnProbeWriteUser
|
||||||
|
FnCurrentTaskUnderCgroup
|
||||||
|
FnSkbChangeTail
|
||||||
|
FnSkbPullData
|
||||||
|
FnCsumUpdate
|
||||||
|
FnSetHashInvalid
|
||||||
|
FnGetNumaNodeId
|
||||||
|
FnSkbChangeHead
|
||||||
|
FnXdpAdjustHead
|
||||||
|
FnProbeReadStr
|
||||||
|
FnGetSocketCookie
|
||||||
|
FnGetSocketUid
|
||||||
|
FnSetHash
|
||||||
|
FnSetsockopt
|
||||||
|
FnSkbAdjustRoom
|
||||||
|
FnRedirectMap
|
||||||
|
FnSkRedirectMap
|
||||||
|
FnSockMapUpdate
|
||||||
|
FnXdpAdjustMeta
|
||||||
|
FnPerfEventReadValue
|
||||||
|
FnPerfProgReadValue
|
||||||
|
FnGetsockopt
|
||||||
|
FnOverrideReturn
|
||||||
|
FnSockOpsCbFlagsSet
|
||||||
|
FnMsgRedirectMap
|
||||||
|
FnMsgApplyBytes
|
||||||
|
FnMsgCorkBytes
|
||||||
|
FnMsgPullData
|
||||||
|
FnBind
|
||||||
|
FnXdpAdjustTail
|
||||||
|
FnSkbGetXfrmState
|
||||||
|
FnGetStack
|
||||||
|
FnSkbLoadBytesRelative
|
||||||
|
FnFibLookup
|
||||||
|
FnSockHashUpdate
|
||||||
|
FnMsgRedirectHash
|
||||||
|
FnSkRedirectHash
|
||||||
|
FnLwtPushEncap
|
||||||
|
FnLwtSeg6StoreBytes
|
||||||
|
FnLwtSeg6AdjustSrh
|
||||||
|
FnLwtSeg6Action
|
||||||
|
FnRcRepeat
|
||||||
|
FnRcKeydown
|
||||||
|
FnSkbCgroupId
|
||||||
|
FnGetCurrentCgroupId
|
||||||
|
FnGetLocalStorage
|
||||||
|
FnSkSelectReuseport
|
||||||
|
FnSkbAncestorCgroupId
|
||||||
|
FnSkLookupTcp
|
||||||
|
FnSkLookupUdp
|
||||||
|
FnSkRelease
|
||||||
|
FnMapPushElem
|
||||||
|
FnMapPopElem
|
||||||
|
FnMapPeekElem
|
||||||
|
FnMsgPushData
|
||||||
|
FnMsgPopData
|
||||||
|
FnRcPointerRel
|
||||||
|
FnSpinLock
|
||||||
|
FnSpinUnlock
|
||||||
|
FnSkFullsock
|
||||||
|
FnTcpSock
|
||||||
|
FnSkbEcnSetCe
|
||||||
|
FnGetListenerSock
|
||||||
|
FnSkcLookupTcp
|
||||||
|
FnTcpCheckSyncookie
|
||||||
|
FnSysctlGetName
|
||||||
|
FnSysctlGetCurrentValue
|
||||||
|
FnSysctlGetNewValue
|
||||||
|
FnSysctlSetNewValue
|
||||||
|
FnStrtol
|
||||||
|
FnStrtoul
|
||||||
|
FnSkStorageGet
|
||||||
|
FnSkStorageDelete
|
||||||
|
FnSendSignal
|
||||||
|
FnTcpGenSyncookie
|
||||||
|
)
|
||||||
|
|
||||||
|
// Call emits a function call.
|
||||||
|
func (fn BuiltinFunc) Call() Instruction {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: OpCode(JumpClass).SetJumpOp(Call),
|
||||||
|
Constant: int64(fn),
|
||||||
|
}
|
||||||
|
}
|
133
vendor/github.com/cilium/ebpf/asm/func_string.go
generated
vendored
Normal file
133
vendor/github.com/cilium/ebpf/asm/func_string.go
generated
vendored
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
// Code generated by "stringer -output func_string.go -type=BuiltinFunc"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package asm
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[FnUnspec-0]
|
||||||
|
_ = x[FnMapLookupElem-1]
|
||||||
|
_ = x[FnMapUpdateElem-2]
|
||||||
|
_ = x[FnMapDeleteElem-3]
|
||||||
|
_ = x[FnProbeRead-4]
|
||||||
|
_ = x[FnKtimeGetNs-5]
|
||||||
|
_ = x[FnTracePrintk-6]
|
||||||
|
_ = x[FnGetPrandomU32-7]
|
||||||
|
_ = x[FnGetSmpProcessorId-8]
|
||||||
|
_ = x[FnSkbStoreBytes-9]
|
||||||
|
_ = x[FnL3CsumReplace-10]
|
||||||
|
_ = x[FnL4CsumReplace-11]
|
||||||
|
_ = x[FnTailCall-12]
|
||||||
|
_ = x[FnCloneRedirect-13]
|
||||||
|
_ = x[FnGetCurrentPidTgid-14]
|
||||||
|
_ = x[FnGetCurrentUidGid-15]
|
||||||
|
_ = x[FnGetCurrentComm-16]
|
||||||
|
_ = x[FnGetCgroupClassid-17]
|
||||||
|
_ = x[FnSkbVlanPush-18]
|
||||||
|
_ = x[FnSkbVlanPop-19]
|
||||||
|
_ = x[FnSkbGetTunnelKey-20]
|
||||||
|
_ = x[FnSkbSetTunnelKey-21]
|
||||||
|
_ = x[FnPerfEventRead-22]
|
||||||
|
_ = x[FnRedirect-23]
|
||||||
|
_ = x[FnGetRouteRealm-24]
|
||||||
|
_ = x[FnPerfEventOutput-25]
|
||||||
|
_ = x[FnSkbLoadBytes-26]
|
||||||
|
_ = x[FnGetStackid-27]
|
||||||
|
_ = x[FnCsumDiff-28]
|
||||||
|
_ = x[FnSkbGetTunnelOpt-29]
|
||||||
|
_ = x[FnSkbSetTunnelOpt-30]
|
||||||
|
_ = x[FnSkbChangeProto-31]
|
||||||
|
_ = x[FnSkbChangeType-32]
|
||||||
|
_ = x[FnSkbUnderCgroup-33]
|
||||||
|
_ = x[FnGetHashRecalc-34]
|
||||||
|
_ = x[FnGetCurrentTask-35]
|
||||||
|
_ = x[FnProbeWriteUser-36]
|
||||||
|
_ = x[FnCurrentTaskUnderCgroup-37]
|
||||||
|
_ = x[FnSkbChangeTail-38]
|
||||||
|
_ = x[FnSkbPullData-39]
|
||||||
|
_ = x[FnCsumUpdate-40]
|
||||||
|
_ = x[FnSetHashInvalid-41]
|
||||||
|
_ = x[FnGetNumaNodeId-42]
|
||||||
|
_ = x[FnSkbChangeHead-43]
|
||||||
|
_ = x[FnXdpAdjustHead-44]
|
||||||
|
_ = x[FnProbeReadStr-45]
|
||||||
|
_ = x[FnGetSocketCookie-46]
|
||||||
|
_ = x[FnGetSocketUid-47]
|
||||||
|
_ = x[FnSetHash-48]
|
||||||
|
_ = x[FnSetsockopt-49]
|
||||||
|
_ = x[FnSkbAdjustRoom-50]
|
||||||
|
_ = x[FnRedirectMap-51]
|
||||||
|
_ = x[FnSkRedirectMap-52]
|
||||||
|
_ = x[FnSockMapUpdate-53]
|
||||||
|
_ = x[FnXdpAdjustMeta-54]
|
||||||
|
_ = x[FnPerfEventReadValue-55]
|
||||||
|
_ = x[FnPerfProgReadValue-56]
|
||||||
|
_ = x[FnGetsockopt-57]
|
||||||
|
_ = x[FnOverrideReturn-58]
|
||||||
|
_ = x[FnSockOpsCbFlagsSet-59]
|
||||||
|
_ = x[FnMsgRedirectMap-60]
|
||||||
|
_ = x[FnMsgApplyBytes-61]
|
||||||
|
_ = x[FnMsgCorkBytes-62]
|
||||||
|
_ = x[FnMsgPullData-63]
|
||||||
|
_ = x[FnBind-64]
|
||||||
|
_ = x[FnXdpAdjustTail-65]
|
||||||
|
_ = x[FnSkbGetXfrmState-66]
|
||||||
|
_ = x[FnGetStack-67]
|
||||||
|
_ = x[FnSkbLoadBytesRelative-68]
|
||||||
|
_ = x[FnFibLookup-69]
|
||||||
|
_ = x[FnSockHashUpdate-70]
|
||||||
|
_ = x[FnMsgRedirectHash-71]
|
||||||
|
_ = x[FnSkRedirectHash-72]
|
||||||
|
_ = x[FnLwtPushEncap-73]
|
||||||
|
_ = x[FnLwtSeg6StoreBytes-74]
|
||||||
|
_ = x[FnLwtSeg6AdjustSrh-75]
|
||||||
|
_ = x[FnLwtSeg6Action-76]
|
||||||
|
_ = x[FnRcRepeat-77]
|
||||||
|
_ = x[FnRcKeydown-78]
|
||||||
|
_ = x[FnSkbCgroupId-79]
|
||||||
|
_ = x[FnGetCurrentCgroupId-80]
|
||||||
|
_ = x[FnGetLocalStorage-81]
|
||||||
|
_ = x[FnSkSelectReuseport-82]
|
||||||
|
_ = x[FnSkbAncestorCgroupId-83]
|
||||||
|
_ = x[FnSkLookupTcp-84]
|
||||||
|
_ = x[FnSkLookupUdp-85]
|
||||||
|
_ = x[FnSkRelease-86]
|
||||||
|
_ = x[FnMapPushElem-87]
|
||||||
|
_ = x[FnMapPopElem-88]
|
||||||
|
_ = x[FnMapPeekElem-89]
|
||||||
|
_ = x[FnMsgPushData-90]
|
||||||
|
_ = x[FnMsgPopData-91]
|
||||||
|
_ = x[FnRcPointerRel-92]
|
||||||
|
_ = x[FnSpinLock-93]
|
||||||
|
_ = x[FnSpinUnlock-94]
|
||||||
|
_ = x[FnSkFullsock-95]
|
||||||
|
_ = x[FnTcpSock-96]
|
||||||
|
_ = x[FnSkbEcnSetCe-97]
|
||||||
|
_ = x[FnGetListenerSock-98]
|
||||||
|
_ = x[FnSkcLookupTcp-99]
|
||||||
|
_ = x[FnTcpCheckSyncookie-100]
|
||||||
|
_ = x[FnSysctlGetName-101]
|
||||||
|
_ = x[FnSysctlGetCurrentValue-102]
|
||||||
|
_ = x[FnSysctlGetNewValue-103]
|
||||||
|
_ = x[FnSysctlSetNewValue-104]
|
||||||
|
_ = x[FnStrtol-105]
|
||||||
|
_ = x[FnStrtoul-106]
|
||||||
|
_ = x[FnSkStorageGet-107]
|
||||||
|
_ = x[FnSkStorageDelete-108]
|
||||||
|
_ = x[FnSendSignal-109]
|
||||||
|
_ = x[FnTcpGenSyncookie-110]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookie"
|
||||||
|
|
||||||
|
var _BuiltinFunc_index = [...]uint16{0, 8, 23, 38, 53, 64, 76, 89, 104, 123, 138, 153, 168, 178, 193, 212, 230, 246, 264, 277, 289, 306, 323, 338, 348, 363, 380, 394, 406, 416, 433, 450, 466, 481, 497, 512, 528, 544, 568, 583, 596, 608, 624, 639, 654, 669, 683, 700, 714, 723, 735, 750, 763, 778, 793, 808, 828, 847, 859, 875, 894, 910, 925, 939, 952, 958, 973, 990, 1000, 1022, 1033, 1049, 1066, 1082, 1096, 1115, 1133, 1148, 1158, 1169, 1182, 1202, 1219, 1238, 1259, 1272, 1285, 1296, 1309, 1321, 1334, 1347, 1359, 1373, 1383, 1395, 1407, 1416, 1429, 1446, 1460, 1479, 1494, 1517, 1536, 1555, 1563, 1572, 1586, 1603, 1615, 1632}
|
||||||
|
|
||||||
|
func (i BuiltinFunc) String() string {
|
||||||
|
if i < 0 || i >= BuiltinFunc(len(_BuiltinFunc_index)-1) {
|
||||||
|
return "BuiltinFunc(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _BuiltinFunc_name[_BuiltinFunc_index[i]:_BuiltinFunc_index[i+1]]
|
||||||
|
}
|
416
vendor/github.com/cilium/ebpf/asm/instruction.go
generated
vendored
Normal file
416
vendor/github.com/cilium/ebpf/asm/instruction.go
generated
vendored
Normal file
@ -0,0 +1,416 @@
|
|||||||
|
package asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// InstructionSize is the size of a BPF instruction in bytes
|
||||||
|
const InstructionSize = 8
|
||||||
|
|
||||||
|
// Instruction is a single eBPF instruction.
|
||||||
|
type Instruction struct {
|
||||||
|
OpCode OpCode
|
||||||
|
Dst Register
|
||||||
|
Src Register
|
||||||
|
Offset int16
|
||||||
|
Constant int64
|
||||||
|
Reference string
|
||||||
|
Symbol string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sym creates a symbol.
|
||||||
|
func (ins Instruction) Sym(name string) Instruction {
|
||||||
|
ins.Symbol = name
|
||||||
|
return ins
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal decodes a BPF instruction.
|
||||||
|
func (ins *Instruction) Unmarshal(r io.Reader, bo binary.ByteOrder) (uint64, error) {
|
||||||
|
var bi bpfInstruction
|
||||||
|
err := binary.Read(r, bo, &bi)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ins.OpCode = bi.OpCode
|
||||||
|
ins.Dst = bi.Registers.Dst()
|
||||||
|
ins.Src = bi.Registers.Src()
|
||||||
|
ins.Offset = bi.Offset
|
||||||
|
ins.Constant = int64(bi.Constant)
|
||||||
|
|
||||||
|
if !bi.OpCode.isDWordLoad() {
|
||||||
|
return InstructionSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var bi2 bpfInstruction
|
||||||
|
if err := binary.Read(r, bo, &bi2); err != nil {
|
||||||
|
// No Wrap, to avoid io.EOF clash
|
||||||
|
return 0, errors.New("64bit immediate is missing second half")
|
||||||
|
}
|
||||||
|
if bi2.OpCode != 0 || bi2.Offset != 0 || bi2.Registers != 0 {
|
||||||
|
return 0, errors.New("64bit immediate has non-zero fields")
|
||||||
|
}
|
||||||
|
ins.Constant = int64(uint64(uint32(bi2.Constant))<<32 | uint64(uint32(bi.Constant)))
|
||||||
|
|
||||||
|
return 2 * InstructionSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal encodes a BPF instruction.
|
||||||
|
func (ins Instruction) Marshal(w io.Writer, bo binary.ByteOrder) (uint64, error) {
|
||||||
|
if ins.OpCode == InvalidOpCode {
|
||||||
|
return 0, errors.New("invalid opcode")
|
||||||
|
}
|
||||||
|
|
||||||
|
isDWordLoad := ins.OpCode.isDWordLoad()
|
||||||
|
|
||||||
|
cons := int32(ins.Constant)
|
||||||
|
if isDWordLoad {
|
||||||
|
// Encode least significant 32bit first for 64bit operations.
|
||||||
|
cons = int32(uint32(ins.Constant))
|
||||||
|
}
|
||||||
|
|
||||||
|
bpfi := bpfInstruction{
|
||||||
|
ins.OpCode,
|
||||||
|
newBPFRegisters(ins.Dst, ins.Src),
|
||||||
|
ins.Offset,
|
||||||
|
cons,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(w, bo, &bpfi); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isDWordLoad {
|
||||||
|
return InstructionSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bpfi = bpfInstruction{
|
||||||
|
Constant: int32(ins.Constant >> 32),
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := binary.Write(w, bo, &bpfi); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return 2 * InstructionSize, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RewriteMapPtr changes an instruction to use a new map fd.
|
||||||
|
//
|
||||||
|
// Returns an error if the fd is invalid, or the instruction
|
||||||
|
// is incorrect.
|
||||||
|
func (ins *Instruction) RewriteMapPtr(fd int) error {
|
||||||
|
if !ins.OpCode.isDWordLoad() {
|
||||||
|
return errors.Errorf("%s is not a 64 bit load", ins.OpCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fd < 0 {
|
||||||
|
return errors.New("invalid fd")
|
||||||
|
}
|
||||||
|
|
||||||
|
ins.Src = R1
|
||||||
|
ins.Constant = int64(fd)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format implements fmt.Formatter.
|
||||||
|
func (ins Instruction) Format(f fmt.State, c rune) {
|
||||||
|
if c != 'v' {
|
||||||
|
fmt.Fprintf(f, "{UNRECOGNIZED: %c}", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
op := ins.OpCode
|
||||||
|
|
||||||
|
if op == InvalidOpCode {
|
||||||
|
fmt.Fprint(f, "INVALID")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Omit trailing space for Exit
|
||||||
|
if op.JumpOp() == Exit {
|
||||||
|
fmt.Fprint(f, op)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(f, "%v ", op)
|
||||||
|
switch cls := op.Class(); cls {
|
||||||
|
case LdClass, LdXClass, StClass, StXClass:
|
||||||
|
switch op.Mode() {
|
||||||
|
case ImmMode:
|
||||||
|
fmt.Fprintf(f, "dst: %s imm: %d", ins.Dst, ins.Constant)
|
||||||
|
case AbsMode:
|
||||||
|
fmt.Fprintf(f, "imm: %d", ins.Constant)
|
||||||
|
case IndMode:
|
||||||
|
fmt.Fprintf(f, "dst: %s src: %s imm: %d", ins.Dst, ins.Src, ins.Constant)
|
||||||
|
case MemMode:
|
||||||
|
fmt.Fprintf(f, "dst: %s src: %s off: %d imm: %d", ins.Dst, ins.Src, ins.Offset, ins.Constant)
|
||||||
|
case XAddMode:
|
||||||
|
fmt.Fprintf(f, "dst: %s src: %s", ins.Dst, ins.Src)
|
||||||
|
}
|
||||||
|
|
||||||
|
case ALU64Class, ALUClass:
|
||||||
|
fmt.Fprintf(f, "dst: %s ", ins.Dst)
|
||||||
|
if op.ALUOp() == Swap || op.Source() == ImmSource {
|
||||||
|
fmt.Fprintf(f, "imm: %d", ins.Constant)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f, "src: %s", ins.Src)
|
||||||
|
}
|
||||||
|
|
||||||
|
case JumpClass:
|
||||||
|
switch jop := op.JumpOp(); jop {
|
||||||
|
case Call:
|
||||||
|
if ins.Src == R1 {
|
||||||
|
// bpf-to-bpf call
|
||||||
|
fmt.Fprint(f, ins.Constant)
|
||||||
|
} else {
|
||||||
|
fmt.Fprint(f, BuiltinFunc(ins.Constant))
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(f, "dst: %s off: %d ", ins.Dst, ins.Offset)
|
||||||
|
if op.Source() == ImmSource {
|
||||||
|
fmt.Fprintf(f, "imm: %d", ins.Constant)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(f, "src: %s", ins.Src)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ins.Reference != "" {
|
||||||
|
fmt.Fprintf(f, " <%s>", ins.Reference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instructions is an eBPF program.
|
||||||
|
type Instructions []Instruction
|
||||||
|
|
||||||
|
func (insns Instructions) String() string {
|
||||||
|
return fmt.Sprint(insns)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RewriteMapPtr rewrites all loads of a specific map pointer to a new fd.
|
||||||
|
//
|
||||||
|
// Returns an error if the symbol isn't used, see IsUnreferencedSymbol.
|
||||||
|
func (insns Instructions) RewriteMapPtr(symbol string, fd int) error {
|
||||||
|
if symbol == "" {
|
||||||
|
return errors.New("empty symbol")
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for i := range insns {
|
||||||
|
ins := &insns[i]
|
||||||
|
if ins.Reference != symbol {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ins.RewriteMapPtr(fd); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return &unreferencedSymbolError{symbol}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SymbolOffsets returns the set of symbols and their offset in
|
||||||
|
// the instructions.
|
||||||
|
func (insns Instructions) SymbolOffsets() (map[string]int, error) {
|
||||||
|
offsets := make(map[string]int)
|
||||||
|
|
||||||
|
for i, ins := range insns {
|
||||||
|
if ins.Symbol == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := offsets[ins.Symbol]; ok {
|
||||||
|
return nil, errors.Errorf("duplicate symbol %s", ins.Symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
offsets[ins.Symbol] = i
|
||||||
|
}
|
||||||
|
|
||||||
|
return offsets, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReferenceOffsets returns the set of references and their offset in
|
||||||
|
// the instructions.
|
||||||
|
func (insns Instructions) ReferenceOffsets() map[string][]int {
|
||||||
|
offsets := make(map[string][]int)
|
||||||
|
|
||||||
|
for i, ins := range insns {
|
||||||
|
if ins.Reference == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
offsets[ins.Reference] = append(offsets[ins.Reference], i)
|
||||||
|
}
|
||||||
|
|
||||||
|
return offsets
|
||||||
|
}
|
||||||
|
|
||||||
|
func (insns Instructions) marshalledOffsets() (map[string]int, error) {
|
||||||
|
symbols := make(map[string]int)
|
||||||
|
|
||||||
|
marshalledPos := 0
|
||||||
|
for _, ins := range insns {
|
||||||
|
currentPos := marshalledPos
|
||||||
|
marshalledPos += ins.OpCode.marshalledInstructions()
|
||||||
|
|
||||||
|
if ins.Symbol == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := symbols[ins.Symbol]; ok {
|
||||||
|
return nil, errors.Errorf("duplicate symbol %s", ins.Symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
symbols[ins.Symbol] = currentPos
|
||||||
|
}
|
||||||
|
|
||||||
|
return symbols, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format implements fmt.Formatter.
|
||||||
|
//
|
||||||
|
// You can control indentation of symbols by
|
||||||
|
// specifying a width. Setting a precision controls the indentation of
|
||||||
|
// instructions.
|
||||||
|
// The default character is a tab, which can be overriden by specifying
|
||||||
|
// the ' ' space flag.
|
||||||
|
func (insns Instructions) Format(f fmt.State, c rune) {
|
||||||
|
if c != 's' && c != 'v' {
|
||||||
|
fmt.Fprintf(f, "{UNKNOWN FORMAT '%c'}", c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Precision is better in this case, because it allows
|
||||||
|
// specifying 0 padding easily.
|
||||||
|
padding, ok := f.Precision()
|
||||||
|
if !ok {
|
||||||
|
padding = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
indent := strings.Repeat("\t", padding)
|
||||||
|
if f.Flag(' ') {
|
||||||
|
indent = strings.Repeat(" ", padding)
|
||||||
|
}
|
||||||
|
|
||||||
|
symPadding, ok := f.Width()
|
||||||
|
if !ok {
|
||||||
|
symPadding = padding - 1
|
||||||
|
}
|
||||||
|
if symPadding < 0 {
|
||||||
|
symPadding = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
symIndent := strings.Repeat("\t", symPadding)
|
||||||
|
if f.Flag(' ') {
|
||||||
|
symIndent = strings.Repeat(" ", symPadding)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out how many digits we need to represent the highest
|
||||||
|
// offset.
|
||||||
|
highestOffset := 0
|
||||||
|
for _, ins := range insns {
|
||||||
|
highestOffset += ins.OpCode.marshalledInstructions()
|
||||||
|
}
|
||||||
|
offsetWidth := int(math.Ceil(math.Log10(float64(highestOffset))))
|
||||||
|
|
||||||
|
offset := 0
|
||||||
|
for _, ins := range insns {
|
||||||
|
if ins.Symbol != "" {
|
||||||
|
fmt.Fprintf(f, "%s%s:\n", symIndent, ins.Symbol)
|
||||||
|
}
|
||||||
|
fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, offset, ins)
|
||||||
|
offset += ins.OpCode.marshalledInstructions()
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal encodes a BPF program into the kernel format.
|
||||||
|
func (insns Instructions) Marshal(w io.Writer, bo binary.ByteOrder) error {
|
||||||
|
absoluteOffsets, err := insns.marshalledOffsets()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
num := 0
|
||||||
|
for i, ins := range insns {
|
||||||
|
switch {
|
||||||
|
case ins.OpCode.JumpOp() == Call && ins.Constant == -1:
|
||||||
|
// Rewrite bpf to bpf call
|
||||||
|
offset, ok := absoluteOffsets[ins.Reference]
|
||||||
|
if !ok {
|
||||||
|
return errors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
|
||||||
|
}
|
||||||
|
|
||||||
|
ins.Constant = int64(offset - num - 1)
|
||||||
|
|
||||||
|
case ins.OpCode.Class() == JumpClass && ins.Offset == -1:
|
||||||
|
// Rewrite jump to label
|
||||||
|
offset, ok := absoluteOffsets[ins.Reference]
|
||||||
|
if !ok {
|
||||||
|
return errors.Errorf("instruction %d: reference to missing symbol %s", i, ins.Reference)
|
||||||
|
}
|
||||||
|
|
||||||
|
ins.Offset = int16(offset - num - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
n, err := ins.Marshal(w, bo)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "instruction %d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
num += int(n / InstructionSize)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type bpfInstruction struct {
|
||||||
|
OpCode OpCode
|
||||||
|
Registers bpfRegisters
|
||||||
|
Offset int16
|
||||||
|
Constant int32
|
||||||
|
}
|
||||||
|
|
||||||
|
type bpfRegisters uint8
|
||||||
|
|
||||||
|
func newBPFRegisters(dst, src Register) bpfRegisters {
|
||||||
|
return bpfRegisters((src << 4) | (dst & 0xF))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r bpfRegisters) Dst() Register {
|
||||||
|
return Register(r & 0xF)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r bpfRegisters) Src() Register {
|
||||||
|
return Register(r >> 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
type unreferencedSymbolError struct {
|
||||||
|
symbol string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (use *unreferencedSymbolError) Error() string {
|
||||||
|
return fmt.Sprintf("unreferenced symbol %s", use.symbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsUnreferencedSymbol returns true if err was caused by
|
||||||
|
// an unreferenced symbol.
|
||||||
|
func IsUnreferencedSymbol(err error) bool {
|
||||||
|
_, ok := err.(*unreferencedSymbolError)
|
||||||
|
return ok
|
||||||
|
}
|
109
vendor/github.com/cilium/ebpf/asm/jump.go
generated
vendored
Normal file
109
vendor/github.com/cilium/ebpf/asm/jump.go
generated
vendored
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package asm
|
||||||
|
|
||||||
|
//go:generate stringer -output jump_string.go -type=JumpOp
|
||||||
|
|
||||||
|
// JumpOp affect control flow.
|
||||||
|
//
|
||||||
|
// msb lsb
|
||||||
|
// +----+-+---+
|
||||||
|
// |OP |s|cls|
|
||||||
|
// +----+-+---+
|
||||||
|
type JumpOp uint8
|
||||||
|
|
||||||
|
const jumpMask OpCode = aluMask
|
||||||
|
|
||||||
|
const (
|
||||||
|
// InvalidJumpOp is returned by getters when invoked
|
||||||
|
// on non branch OpCodes
|
||||||
|
InvalidJumpOp JumpOp = 0xff
|
||||||
|
// Ja jumps by offset unconditionally
|
||||||
|
Ja JumpOp = 0x00
|
||||||
|
// JEq jumps by offset if r == imm
|
||||||
|
JEq JumpOp = 0x10
|
||||||
|
// JGT jumps by offset if r > imm
|
||||||
|
JGT JumpOp = 0x20
|
||||||
|
// JGE jumps by offset if r >= imm
|
||||||
|
JGE JumpOp = 0x30
|
||||||
|
// JSet jumps by offset if r & imm
|
||||||
|
JSet JumpOp = 0x40
|
||||||
|
// JNE jumps by offset if r != imm
|
||||||
|
JNE JumpOp = 0x50
|
||||||
|
// JSGT jumps by offset if signed r > signed imm
|
||||||
|
JSGT JumpOp = 0x60
|
||||||
|
// JSGE jumps by offset if signed r >= signed imm
|
||||||
|
JSGE JumpOp = 0x70
|
||||||
|
// Call builtin or user defined function from imm
|
||||||
|
Call JumpOp = 0x80
|
||||||
|
// Exit ends execution, with value in r0
|
||||||
|
Exit JumpOp = 0x90
|
||||||
|
// JLT jumps by offset if r < imm
|
||||||
|
JLT JumpOp = 0xa0
|
||||||
|
// JLE jumps by offset if r <= imm
|
||||||
|
JLE JumpOp = 0xb0
|
||||||
|
// JSLT jumps by offset if signed r < signed imm
|
||||||
|
JSLT JumpOp = 0xc0
|
||||||
|
// JSLE jumps by offset if signed r <= signed imm
|
||||||
|
JSLE JumpOp = 0xd0
|
||||||
|
)
|
||||||
|
|
||||||
|
// Return emits an exit instruction.
|
||||||
|
//
|
||||||
|
// Requires a return value in R0.
|
||||||
|
func Return() Instruction {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: OpCode(JumpClass).SetJumpOp(Exit),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Op returns the OpCode for a given jump source.
|
||||||
|
func (op JumpOp) Op(source Source) OpCode {
|
||||||
|
return OpCode(JumpClass).SetJumpOp(op).SetSource(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Imm compares dst to value, and adjusts PC by offset if the condition is fulfilled.
|
||||||
|
func (op JumpOp) Imm(dst Register, value int32, label string) Instruction {
|
||||||
|
if op == Exit || op == Call || op == Ja {
|
||||||
|
return Instruction{OpCode: InvalidOpCode}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Instruction{
|
||||||
|
OpCode: OpCode(JumpClass).SetJumpOp(op).SetSource(ImmSource),
|
||||||
|
Dst: dst,
|
||||||
|
Offset: -1,
|
||||||
|
Constant: int64(value),
|
||||||
|
Reference: label,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reg compares dst to src, and adjusts PC by offset if the condition is fulfilled.
|
||||||
|
func (op JumpOp) Reg(dst, src Register, label string) Instruction {
|
||||||
|
if op == Exit || op == Call || op == Ja {
|
||||||
|
return Instruction{OpCode: InvalidOpCode}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Instruction{
|
||||||
|
OpCode: OpCode(JumpClass).SetJumpOp(op).SetSource(RegSource),
|
||||||
|
Dst: dst,
|
||||||
|
Src: src,
|
||||||
|
Offset: -1,
|
||||||
|
Reference: label,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Label adjusts PC to the address of the label.
|
||||||
|
func (op JumpOp) Label(label string) Instruction {
|
||||||
|
if op == Call {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: OpCode(JumpClass).SetJumpOp(Call),
|
||||||
|
Src: R1,
|
||||||
|
Constant: -1,
|
||||||
|
Reference: label,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Instruction{
|
||||||
|
OpCode: OpCode(JumpClass).SetJumpOp(op),
|
||||||
|
Offset: -1,
|
||||||
|
Reference: label,
|
||||||
|
}
|
||||||
|
}
|
53
vendor/github.com/cilium/ebpf/asm/jump_string.go
generated
vendored
Normal file
53
vendor/github.com/cilium/ebpf/asm/jump_string.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Code generated by "stringer -output jump_string.go -type=JumpOp"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package asm
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[InvalidJumpOp-255]
|
||||||
|
_ = x[Ja-0]
|
||||||
|
_ = x[JEq-16]
|
||||||
|
_ = x[JGT-32]
|
||||||
|
_ = x[JGE-48]
|
||||||
|
_ = x[JSet-64]
|
||||||
|
_ = x[JNE-80]
|
||||||
|
_ = x[JSGT-96]
|
||||||
|
_ = x[JSGE-112]
|
||||||
|
_ = x[Call-128]
|
||||||
|
_ = x[Exit-144]
|
||||||
|
_ = x[JLT-160]
|
||||||
|
_ = x[JLE-176]
|
||||||
|
_ = x[JSLT-192]
|
||||||
|
_ = x[JSLE-208]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _JumpOp_name = "JaJEqJGTJGEJSetJNEJSGTJSGECallExitJLTJLEJSLTJSLEInvalidJumpOp"
|
||||||
|
|
||||||
|
var _JumpOp_map = map[JumpOp]string{
|
||||||
|
0: _JumpOp_name[0:2],
|
||||||
|
16: _JumpOp_name[2:5],
|
||||||
|
32: _JumpOp_name[5:8],
|
||||||
|
48: _JumpOp_name[8:11],
|
||||||
|
64: _JumpOp_name[11:15],
|
||||||
|
80: _JumpOp_name[15:18],
|
||||||
|
96: _JumpOp_name[18:22],
|
||||||
|
112: _JumpOp_name[22:26],
|
||||||
|
128: _JumpOp_name[26:30],
|
||||||
|
144: _JumpOp_name[30:34],
|
||||||
|
160: _JumpOp_name[34:37],
|
||||||
|
176: _JumpOp_name[37:40],
|
||||||
|
192: _JumpOp_name[40:44],
|
||||||
|
208: _JumpOp_name[44:48],
|
||||||
|
255: _JumpOp_name[48:61],
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i JumpOp) String() string {
|
||||||
|
if str, ok := _JumpOp_map[i]; ok {
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
return "JumpOp(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
189
vendor/github.com/cilium/ebpf/asm/load_store.go
generated
vendored
Normal file
189
vendor/github.com/cilium/ebpf/asm/load_store.go
generated
vendored
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
package asm
|
||||||
|
|
||||||
|
//go:generate stringer -output load_store_string.go -type=Mode,Size
|
||||||
|
|
||||||
|
// Mode for load and store operations
|
||||||
|
//
|
||||||
|
// msb lsb
|
||||||
|
// +---+--+---+
|
||||||
|
// |MDE|sz|cls|
|
||||||
|
// +---+--+---+
|
||||||
|
type Mode uint8
|
||||||
|
|
||||||
|
const modeMask OpCode = 0xe0
|
||||||
|
|
||||||
|
const (
|
||||||
|
// InvalidMode is returned by getters when invoked
|
||||||
|
// on non load / store OpCodes
|
||||||
|
InvalidMode Mode = 0xff
|
||||||
|
// ImmMode - immediate value
|
||||||
|
ImmMode Mode = 0x00
|
||||||
|
// AbsMode - immediate value + offset
|
||||||
|
AbsMode Mode = 0x20
|
||||||
|
// IndMode - indirect (imm+src)
|
||||||
|
IndMode Mode = 0x40
|
||||||
|
// MemMode - load from memory
|
||||||
|
MemMode Mode = 0x60
|
||||||
|
// XAddMode - add atomically across processors.
|
||||||
|
XAddMode Mode = 0xc0
|
||||||
|
)
|
||||||
|
|
||||||
|
// Size of load and store operations
|
||||||
|
//
|
||||||
|
// msb lsb
|
||||||
|
// +---+--+---+
|
||||||
|
// |mde|SZ|cls|
|
||||||
|
// +---+--+---+
|
||||||
|
type Size uint8
|
||||||
|
|
||||||
|
const sizeMask OpCode = 0x18
|
||||||
|
|
||||||
|
const (
|
||||||
|
// InvalidSize is returned by getters when invoked
|
||||||
|
// on non load / store OpCodes
|
||||||
|
InvalidSize Size = 0xff
|
||||||
|
// DWord - double word; 64 bits
|
||||||
|
DWord Size = 0x18
|
||||||
|
// Word - word; 32 bits
|
||||||
|
Word Size = 0x00
|
||||||
|
// Half - half-word; 16 bits
|
||||||
|
Half Size = 0x08
|
||||||
|
// Byte - byte; 8 bits
|
||||||
|
Byte Size = 0x10
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sizeof returns the size in bytes.
|
||||||
|
func (s Size) Sizeof() int {
|
||||||
|
switch s {
|
||||||
|
case DWord:
|
||||||
|
return 8
|
||||||
|
case Word:
|
||||||
|
return 4
|
||||||
|
case Half:
|
||||||
|
return 2
|
||||||
|
case Byte:
|
||||||
|
return 1
|
||||||
|
default:
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadMemOp returns the OpCode to load a value of given size from memory.
|
||||||
|
func LoadMemOp(size Size) OpCode {
|
||||||
|
return OpCode(LdXClass).SetMode(MemMode).SetSize(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadMem emits `dst = *(size *)(src + offset)`.
|
||||||
|
func LoadMem(dst, src Register, offset int16, size Size) Instruction {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: LoadMemOp(size),
|
||||||
|
Dst: dst,
|
||||||
|
Src: src,
|
||||||
|
Offset: offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadImmOp returns the OpCode to load an immediate of given size.
|
||||||
|
//
|
||||||
|
// As of kernel 4.20, only DWord size is accepted.
|
||||||
|
func LoadImmOp(size Size) OpCode {
|
||||||
|
return OpCode(LdClass).SetMode(ImmMode).SetSize(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadImm emits `dst = (size)value`.
|
||||||
|
//
|
||||||
|
// As of kernel 4.20, only DWord size is accepted.
|
||||||
|
func LoadImm(dst Register, value int64, size Size) Instruction {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: LoadImmOp(size),
|
||||||
|
Dst: dst,
|
||||||
|
Constant: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadMapPtr stores a pointer to a map in dst.
|
||||||
|
func LoadMapPtr(dst Register, fd int) Instruction {
|
||||||
|
if fd < 0 {
|
||||||
|
return Instruction{OpCode: InvalidOpCode}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Instruction{
|
||||||
|
OpCode: LoadImmOp(DWord),
|
||||||
|
Dst: dst,
|
||||||
|
Src: R1,
|
||||||
|
Constant: int64(fd),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadIndOp returns the OpCode for loading a value of given size from an sk_buff.
|
||||||
|
func LoadIndOp(size Size) OpCode {
|
||||||
|
return OpCode(LdClass).SetMode(IndMode).SetSize(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadInd emits `dst = ntoh(*(size *)(((sk_buff *)R6)->data + src + offset))`.
|
||||||
|
func LoadInd(dst, src Register, offset int32, size Size) Instruction {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: LoadIndOp(size),
|
||||||
|
Dst: dst,
|
||||||
|
Src: src,
|
||||||
|
Constant: int64(offset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadAbsOp returns the OpCode for loading a value of given size from an sk_buff.
|
||||||
|
func LoadAbsOp(size Size) OpCode {
|
||||||
|
return OpCode(LdClass).SetMode(AbsMode).SetSize(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadAbs emits `r0 = ntoh(*(size *)(((sk_buff *)R6)->data + offset))`.
|
||||||
|
func LoadAbs(offset int32, size Size) Instruction {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: LoadAbsOp(size),
|
||||||
|
Dst: R0,
|
||||||
|
Constant: int64(offset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreMemOp returns the OpCode for storing a register of given size in memory.
|
||||||
|
func StoreMemOp(size Size) OpCode {
|
||||||
|
return OpCode(StXClass).SetMode(MemMode).SetSize(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreMem emits `*(size *)(dst + offset) = src`
|
||||||
|
func StoreMem(dst Register, offset int16, src Register, size Size) Instruction {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: StoreMemOp(size),
|
||||||
|
Dst: dst,
|
||||||
|
Src: src,
|
||||||
|
Offset: offset,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreImmOp returns the OpCode for storing an immediate of given size in memory.
|
||||||
|
func StoreImmOp(size Size) OpCode {
|
||||||
|
return OpCode(StClass).SetMode(MemMode).SetSize(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreImm emits `*(size *)(dst + offset) = value`.
|
||||||
|
func StoreImm(dst Register, offset int16, value int64, size Size) Instruction {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: StoreImmOp(size),
|
||||||
|
Dst: dst,
|
||||||
|
Offset: offset,
|
||||||
|
Constant: value,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreXAddOp returns the OpCode to atomically add a register to a value in memory.
|
||||||
|
func StoreXAddOp(size Size) OpCode {
|
||||||
|
return OpCode(StXClass).SetMode(XAddMode).SetSize(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// StoreXAdd atomically adds src to *dst.
|
||||||
|
func StoreXAdd(dst, src Register, size Size) Instruction {
|
||||||
|
return Instruction{
|
||||||
|
OpCode: StoreXAddOp(size),
|
||||||
|
Dst: dst,
|
||||||
|
Src: src,
|
||||||
|
}
|
||||||
|
}
|
80
vendor/github.com/cilium/ebpf/asm/load_store_string.go
generated
vendored
Normal file
80
vendor/github.com/cilium/ebpf/asm/load_store_string.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// Code generated by "stringer -output load_store_string.go -type=Mode,Size"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package asm
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[InvalidMode-255]
|
||||||
|
_ = x[ImmMode-0]
|
||||||
|
_ = x[AbsMode-32]
|
||||||
|
_ = x[IndMode-64]
|
||||||
|
_ = x[MemMode-96]
|
||||||
|
_ = x[XAddMode-192]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_Mode_name_0 = "ImmMode"
|
||||||
|
_Mode_name_1 = "AbsMode"
|
||||||
|
_Mode_name_2 = "IndMode"
|
||||||
|
_Mode_name_3 = "MemMode"
|
||||||
|
_Mode_name_4 = "XAddMode"
|
||||||
|
_Mode_name_5 = "InvalidMode"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i Mode) String() string {
|
||||||
|
switch {
|
||||||
|
case i == 0:
|
||||||
|
return _Mode_name_0
|
||||||
|
case i == 32:
|
||||||
|
return _Mode_name_1
|
||||||
|
case i == 64:
|
||||||
|
return _Mode_name_2
|
||||||
|
case i == 96:
|
||||||
|
return _Mode_name_3
|
||||||
|
case i == 192:
|
||||||
|
return _Mode_name_4
|
||||||
|
case i == 255:
|
||||||
|
return _Mode_name_5
|
||||||
|
default:
|
||||||
|
return "Mode(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[InvalidSize-255]
|
||||||
|
_ = x[DWord-24]
|
||||||
|
_ = x[Word-0]
|
||||||
|
_ = x[Half-8]
|
||||||
|
_ = x[Byte-16]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_Size_name_0 = "Word"
|
||||||
|
_Size_name_1 = "Half"
|
||||||
|
_Size_name_2 = "Byte"
|
||||||
|
_Size_name_3 = "DWord"
|
||||||
|
_Size_name_4 = "InvalidSize"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i Size) String() string {
|
||||||
|
switch {
|
||||||
|
case i == 0:
|
||||||
|
return _Size_name_0
|
||||||
|
case i == 8:
|
||||||
|
return _Size_name_1
|
||||||
|
case i == 16:
|
||||||
|
return _Size_name_2
|
||||||
|
case i == 24:
|
||||||
|
return _Size_name_3
|
||||||
|
case i == 255:
|
||||||
|
return _Size_name_4
|
||||||
|
default:
|
||||||
|
return "Size(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
}
|
237
vendor/github.com/cilium/ebpf/asm/opcode.go
generated
vendored
Normal file
237
vendor/github.com/cilium/ebpf/asm/opcode.go
generated
vendored
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
package asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:generate stringer -output opcode_string.go -type=Class
|
||||||
|
|
||||||
|
type encoding int
|
||||||
|
|
||||||
|
const (
|
||||||
|
unknownEncoding encoding = iota
|
||||||
|
loadOrStore
|
||||||
|
jumpOrALU
|
||||||
|
)
|
||||||
|
|
||||||
|
// Class of operations
|
||||||
|
//
|
||||||
|
// msb lsb
|
||||||
|
// +---+--+---+
|
||||||
|
// | ?? |CLS|
|
||||||
|
// +---+--+---+
|
||||||
|
type Class uint8
|
||||||
|
|
||||||
|
const classMask OpCode = 0x07
|
||||||
|
|
||||||
|
const (
|
||||||
|
// LdClass load memory
|
||||||
|
LdClass Class = 0x00
|
||||||
|
// LdXClass load memory from constant
|
||||||
|
LdXClass Class = 0x01
|
||||||
|
// StClass load register from memory
|
||||||
|
StClass Class = 0x02
|
||||||
|
// StXClass load register from constant
|
||||||
|
StXClass Class = 0x03
|
||||||
|
// ALUClass arithmetic operators
|
||||||
|
ALUClass Class = 0x04
|
||||||
|
// JumpClass jump operators
|
||||||
|
JumpClass Class = 0x05
|
||||||
|
// ALU64Class arithmetic in 64 bit mode
|
||||||
|
ALU64Class Class = 0x07
|
||||||
|
)
|
||||||
|
|
||||||
|
func (cls Class) encoding() encoding {
|
||||||
|
switch cls {
|
||||||
|
case LdClass, LdXClass, StClass, StXClass:
|
||||||
|
return loadOrStore
|
||||||
|
case ALU64Class, ALUClass, JumpClass:
|
||||||
|
return jumpOrALU
|
||||||
|
default:
|
||||||
|
return unknownEncoding
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OpCode is a packed eBPF opcode.
|
||||||
|
//
|
||||||
|
// Its encoding is defined by a Class value:
|
||||||
|
//
|
||||||
|
// msb lsb
|
||||||
|
// +----+-+---+
|
||||||
|
// | ???? |CLS|
|
||||||
|
// +----+-+---+
|
||||||
|
type OpCode uint8
|
||||||
|
|
||||||
|
// InvalidOpCode is returned by setters on OpCode
|
||||||
|
const InvalidOpCode OpCode = 0xff
|
||||||
|
|
||||||
|
// marshalledInstructions returns the number of BPF instructions required
|
||||||
|
// to encode this opcode.
|
||||||
|
func (op OpCode) marshalledInstructions() int {
|
||||||
|
if op == LoadImmOp(DWord) {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op OpCode) isDWordLoad() bool {
|
||||||
|
return op == LoadImmOp(DWord)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Class returns the class of operation.
|
||||||
|
func (op OpCode) Class() Class {
|
||||||
|
return Class(op & classMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mode returns the mode for load and store operations.
|
||||||
|
func (op OpCode) Mode() Mode {
|
||||||
|
if op.Class().encoding() != loadOrStore {
|
||||||
|
return InvalidMode
|
||||||
|
}
|
||||||
|
return Mode(op & modeMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Size returns the size for load and store operations.
|
||||||
|
func (op OpCode) Size() Size {
|
||||||
|
if op.Class().encoding() != loadOrStore {
|
||||||
|
return InvalidSize
|
||||||
|
}
|
||||||
|
return Size(op & sizeMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Source returns the source for branch and ALU operations.
|
||||||
|
func (op OpCode) Source() Source {
|
||||||
|
if op.Class().encoding() != jumpOrALU || op.ALUOp() == Swap {
|
||||||
|
return InvalidSource
|
||||||
|
}
|
||||||
|
return Source(op & sourceMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ALUOp returns the ALUOp.
|
||||||
|
func (op OpCode) ALUOp() ALUOp {
|
||||||
|
if op.Class().encoding() != jumpOrALU {
|
||||||
|
return InvalidALUOp
|
||||||
|
}
|
||||||
|
return ALUOp(op & aluMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Endianness returns the Endianness for a byte swap instruction.
|
||||||
|
func (op OpCode) Endianness() Endianness {
|
||||||
|
if op.ALUOp() != Swap {
|
||||||
|
return InvalidEndian
|
||||||
|
}
|
||||||
|
return Endianness(op & endianMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JumpOp returns the JumpOp.
|
||||||
|
func (op OpCode) JumpOp() JumpOp {
|
||||||
|
if op.Class().encoding() != jumpOrALU {
|
||||||
|
return InvalidJumpOp
|
||||||
|
}
|
||||||
|
return JumpOp(op & jumpMask)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetMode sets the mode on load and store operations.
|
||||||
|
//
|
||||||
|
// Returns InvalidOpCode if op is of the wrong class.
|
||||||
|
func (op OpCode) SetMode(mode Mode) OpCode {
|
||||||
|
if op.Class().encoding() != loadOrStore || !valid(OpCode(mode), modeMask) {
|
||||||
|
return InvalidOpCode
|
||||||
|
}
|
||||||
|
return (op & ^modeMask) | OpCode(mode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSize sets the size on load and store operations.
|
||||||
|
//
|
||||||
|
// Returns InvalidOpCode if op is of the wrong class.
|
||||||
|
func (op OpCode) SetSize(size Size) OpCode {
|
||||||
|
if op.Class().encoding() != loadOrStore || !valid(OpCode(size), sizeMask) {
|
||||||
|
return InvalidOpCode
|
||||||
|
}
|
||||||
|
return (op & ^sizeMask) | OpCode(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetSource sets the source on jump and ALU operations.
|
||||||
|
//
|
||||||
|
// Returns InvalidOpCode if op is of the wrong class.
|
||||||
|
func (op OpCode) SetSource(source Source) OpCode {
|
||||||
|
if op.Class().encoding() != jumpOrALU || !valid(OpCode(source), sourceMask) {
|
||||||
|
return InvalidOpCode
|
||||||
|
}
|
||||||
|
return (op & ^sourceMask) | OpCode(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetALUOp sets the ALUOp on ALU operations.
|
||||||
|
//
|
||||||
|
// Returns InvalidOpCode if op is of the wrong class.
|
||||||
|
func (op OpCode) SetALUOp(alu ALUOp) OpCode {
|
||||||
|
class := op.Class()
|
||||||
|
if (class != ALUClass && class != ALU64Class) || !valid(OpCode(alu), aluMask) {
|
||||||
|
return InvalidOpCode
|
||||||
|
}
|
||||||
|
return (op & ^aluMask) | OpCode(alu)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetJumpOp sets the JumpOp on jump operations.
|
||||||
|
//
|
||||||
|
// Returns InvalidOpCode if op is of the wrong class.
|
||||||
|
func (op OpCode) SetJumpOp(jump JumpOp) OpCode {
|
||||||
|
if op.Class() != JumpClass || !valid(OpCode(jump), jumpMask) {
|
||||||
|
return InvalidOpCode
|
||||||
|
}
|
||||||
|
return (op & ^jumpMask) | OpCode(jump)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op OpCode) String() string {
|
||||||
|
var f strings.Builder
|
||||||
|
|
||||||
|
switch class := op.Class(); class {
|
||||||
|
case LdClass, LdXClass, StClass, StXClass:
|
||||||
|
f.WriteString(strings.TrimSuffix(class.String(), "Class"))
|
||||||
|
|
||||||
|
mode := op.Mode()
|
||||||
|
f.WriteString(strings.TrimSuffix(mode.String(), "Mode"))
|
||||||
|
|
||||||
|
switch op.Size() {
|
||||||
|
case DWord:
|
||||||
|
f.WriteString("DW")
|
||||||
|
case Word:
|
||||||
|
f.WriteString("W")
|
||||||
|
case Half:
|
||||||
|
f.WriteString("H")
|
||||||
|
case Byte:
|
||||||
|
f.WriteString("B")
|
||||||
|
}
|
||||||
|
|
||||||
|
case ALU64Class, ALUClass:
|
||||||
|
f.WriteString(op.ALUOp().String())
|
||||||
|
|
||||||
|
if op.ALUOp() == Swap {
|
||||||
|
// Width for Endian is controlled by Constant
|
||||||
|
f.WriteString(op.Endianness().String())
|
||||||
|
} else {
|
||||||
|
if class == ALUClass {
|
||||||
|
f.WriteString("32")
|
||||||
|
}
|
||||||
|
|
||||||
|
f.WriteString(strings.TrimSuffix(op.Source().String(), "Source"))
|
||||||
|
}
|
||||||
|
|
||||||
|
case JumpClass:
|
||||||
|
f.WriteString(op.JumpOp().String())
|
||||||
|
if jop := op.JumpOp(); jop != Exit && jop != Call {
|
||||||
|
f.WriteString(strings.TrimSuffix(op.Source().String(), "Source"))
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(&f, "%#x", op)
|
||||||
|
}
|
||||||
|
|
||||||
|
return f.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// valid returns true if all bits in value are covered by mask.
|
||||||
|
func valid(value, mask OpCode) bool {
|
||||||
|
return value & ^mask == 0
|
||||||
|
}
|
38
vendor/github.com/cilium/ebpf/asm/opcode_string.go
generated
vendored
Normal file
38
vendor/github.com/cilium/ebpf/asm/opcode_string.go
generated
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// Code generated by "stringer -output opcode_string.go -type=Class"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package asm
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[LdClass-0]
|
||||||
|
_ = x[LdXClass-1]
|
||||||
|
_ = x[StClass-2]
|
||||||
|
_ = x[StXClass-3]
|
||||||
|
_ = x[ALUClass-4]
|
||||||
|
_ = x[JumpClass-5]
|
||||||
|
_ = x[ALU64Class-7]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_Class_name_0 = "LdClassLdXClassStClassStXClassALUClassJumpClass"
|
||||||
|
_Class_name_1 = "ALU64Class"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_Class_index_0 = [...]uint8{0, 7, 15, 22, 30, 38, 47}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i Class) String() string {
|
||||||
|
switch {
|
||||||
|
case 0 <= i && i <= 5:
|
||||||
|
return _Class_name_0[_Class_index_0[i]:_Class_index_0[i+1]]
|
||||||
|
case i == 7:
|
||||||
|
return _Class_name_1
|
||||||
|
default:
|
||||||
|
return "Class(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
}
|
42
vendor/github.com/cilium/ebpf/asm/register.go
generated
vendored
Normal file
42
vendor/github.com/cilium/ebpf/asm/register.go
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package asm
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Register is the source or destination of most operations.
|
||||||
|
type Register uint8
|
||||||
|
|
||||||
|
// R0 contains return values.
|
||||||
|
const R0 Register = 0
|
||||||
|
|
||||||
|
// Registers for function arguments.
|
||||||
|
const (
|
||||||
|
R1 Register = R0 + 1 + iota
|
||||||
|
R2
|
||||||
|
R3
|
||||||
|
R4
|
||||||
|
R5
|
||||||
|
)
|
||||||
|
|
||||||
|
// Callee saved registers preserved by function calls.
|
||||||
|
const (
|
||||||
|
R6 Register = R5 + 1 + iota
|
||||||
|
R7
|
||||||
|
R8
|
||||||
|
R9
|
||||||
|
)
|
||||||
|
|
||||||
|
// Read-only frame pointer to access stack.
|
||||||
|
const (
|
||||||
|
R10 Register = R9 + 1
|
||||||
|
RFP = R10
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r Register) String() string {
|
||||||
|
v := uint8(r)
|
||||||
|
if v == 10 {
|
||||||
|
return "rfp"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("r%d", v)
|
||||||
|
}
|
148
vendor/github.com/cilium/ebpf/collection.go
generated
vendored
Normal file
148
vendor/github.com/cilium/ebpf/collection.go
generated
vendored
Normal file
@ -0,0 +1,148 @@
|
|||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cilium/ebpf/asm"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CollectionOptions control loading a collection into the kernel.
|
||||||
|
type CollectionOptions struct {
|
||||||
|
Programs ProgramOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
// CollectionSpec describes a collection.
|
||||||
|
type CollectionSpec struct {
|
||||||
|
Maps map[string]*MapSpec
|
||||||
|
Programs map[string]*ProgramSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a recursive copy of the spec.
|
||||||
|
func (cs *CollectionSpec) Copy() *CollectionSpec {
|
||||||
|
if cs == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cpy := CollectionSpec{
|
||||||
|
Maps: make(map[string]*MapSpec, len(cs.Maps)),
|
||||||
|
Programs: make(map[string]*ProgramSpec, len(cs.Programs)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, spec := range cs.Maps {
|
||||||
|
cpy.Maps[name] = spec.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, spec := range cs.Programs {
|
||||||
|
cpy.Programs[name] = spec.Copy()
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cpy
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collection is a collection of Programs and Maps associated
|
||||||
|
// with their symbols
|
||||||
|
type Collection struct {
|
||||||
|
Programs map[string]*Program
|
||||||
|
Maps map[string]*Map
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCollection creates a Collection from a specification.
|
||||||
|
//
|
||||||
|
// Only maps referenced by at least one of the programs are initialized.
|
||||||
|
func NewCollection(spec *CollectionSpec) (*Collection, error) {
|
||||||
|
return NewCollectionWithOptions(spec, CollectionOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewCollectionWithOptions creates a Collection from a specification.
|
||||||
|
//
|
||||||
|
// Only maps referenced by at least one of the programs are initialized.
|
||||||
|
func NewCollectionWithOptions(spec *CollectionSpec, opts CollectionOptions) (*Collection, error) {
|
||||||
|
maps := make(map[string]*Map)
|
||||||
|
for mapName, mapSpec := range spec.Maps {
|
||||||
|
m, err := NewMap(mapSpec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "map %s", mapName)
|
||||||
|
}
|
||||||
|
maps[mapName] = m
|
||||||
|
}
|
||||||
|
|
||||||
|
progs := make(map[string]*Program)
|
||||||
|
for progName, origProgSpec := range spec.Programs {
|
||||||
|
progSpec := origProgSpec.Copy()
|
||||||
|
|
||||||
|
// Rewrite any reference to a valid map.
|
||||||
|
for i := range progSpec.Instructions {
|
||||||
|
var (
|
||||||
|
ins = &progSpec.Instructions[i]
|
||||||
|
m = maps[ins.Reference]
|
||||||
|
)
|
||||||
|
|
||||||
|
if ins.Reference == "" || m == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ins.Src == asm.R1 {
|
||||||
|
// Don't overwrite maps already rewritten, users can
|
||||||
|
// rewrite programs in the spec themselves
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := ins.RewriteMapPtr(m.FD()); err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "progam %s: map %s", progName, ins.Reference)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
prog, err := NewProgramWithOptions(progSpec, opts.Programs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "program %s", progName)
|
||||||
|
}
|
||||||
|
progs[progName] = prog
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Collection{
|
||||||
|
progs,
|
||||||
|
maps,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadCollection parses an object file and converts it to a collection.
|
||||||
|
func LoadCollection(file string) (*Collection, error) {
|
||||||
|
spec, err := LoadCollectionSpec(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return NewCollection(spec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close frees all maps and programs associated with the collection.
|
||||||
|
//
|
||||||
|
// The collection mustn't be used afterwards.
|
||||||
|
func (coll *Collection) Close() {
|
||||||
|
for _, prog := range coll.Programs {
|
||||||
|
prog.Close()
|
||||||
|
}
|
||||||
|
for _, m := range coll.Maps {
|
||||||
|
m.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetachMap removes the named map from the Collection.
|
||||||
|
//
|
||||||
|
// This means that a later call to Close() will not affect this map.
|
||||||
|
//
|
||||||
|
// Returns nil if no map of that name exists.
|
||||||
|
func (coll *Collection) DetachMap(name string) *Map {
|
||||||
|
m := coll.Maps[name]
|
||||||
|
delete(coll.Maps, name)
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetachProgram removes the named program from the Collection.
|
||||||
|
//
|
||||||
|
// This means that a later call to Close() will not affect this program.
|
||||||
|
//
|
||||||
|
// Returns nil if no program of that name exists.
|
||||||
|
func (coll *Collection) DetachProgram(name string) *Program {
|
||||||
|
p := coll.Programs[name]
|
||||||
|
delete(coll.Programs, name)
|
||||||
|
return p
|
||||||
|
}
|
17
vendor/github.com/cilium/ebpf/doc.go
generated
vendored
Normal file
17
vendor/github.com/cilium/ebpf/doc.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Package ebpf is a toolkit for working with eBPF programs.
|
||||||
|
//
|
||||||
|
// eBPF programs are small snippets of code which are executed directly
|
||||||
|
// in a VM in the Linux kernel, which makes them very fast and flexible.
|
||||||
|
// Many Linux subsystems now accept eBPF programs. This makes it possible
|
||||||
|
// to implement highly application specific logic inside the kernel,
|
||||||
|
// without having to modify the actual kernel itself.
|
||||||
|
//
|
||||||
|
// This package is designed for long-running processes which
|
||||||
|
// want to use eBPF to implement part of their application logic. It has no
|
||||||
|
// run-time dependencies outside of the library and the Linux kernel itself.
|
||||||
|
// eBPF code should be compiled ahead of time using clang, and shipped with
|
||||||
|
// your application as any other resource.
|
||||||
|
//
|
||||||
|
// This package doesn't include code required to attach eBPF to Linux
|
||||||
|
// subsystems, since this varies per subsystem.
|
||||||
|
package ebpf
|
392
vendor/github.com/cilium/ebpf/elf_reader.go
generated
vendored
Normal file
392
vendor/github.com/cilium/ebpf/elf_reader.go
generated
vendored
Normal file
@ -0,0 +1,392 @@
|
|||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"debug/elf"
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/cilium/ebpf/asm"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type elfCode struct {
|
||||||
|
*elf.File
|
||||||
|
symbols []elf.Symbol
|
||||||
|
symbolsPerSection map[elf.SectionIndex]map[uint64]string
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadCollectionSpec parses an ELF file into a CollectionSpec.
|
||||||
|
func LoadCollectionSpec(file string) (*CollectionSpec, error) {
|
||||||
|
f, err := os.Open(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
spec, err := LoadCollectionSpecFromReader(f)
|
||||||
|
return spec, errors.Wrapf(err, "file %s", file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadCollectionSpecFromReader parses an ELF file into a CollectionSpec.
|
||||||
|
func LoadCollectionSpecFromReader(code io.ReaderAt) (*CollectionSpec, error) {
|
||||||
|
f, err := elf.NewFile(code)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
symbols, err := f.Symbols()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "load symbols")
|
||||||
|
}
|
||||||
|
|
||||||
|
ec := &elfCode{f, symbols, symbolsPerSection(symbols)}
|
||||||
|
|
||||||
|
var licenseSection, versionSection *elf.Section
|
||||||
|
progSections := make(map[elf.SectionIndex]*elf.Section)
|
||||||
|
relSections := make(map[elf.SectionIndex]*elf.Section)
|
||||||
|
mapSections := make(map[elf.SectionIndex]*elf.Section)
|
||||||
|
for i, sec := range ec.Sections {
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(sec.Name, "license"):
|
||||||
|
licenseSection = sec
|
||||||
|
case strings.HasPrefix(sec.Name, "version"):
|
||||||
|
versionSection = sec
|
||||||
|
case strings.HasPrefix(sec.Name, "maps"):
|
||||||
|
mapSections[elf.SectionIndex(i)] = sec
|
||||||
|
case sec.Type == elf.SHT_REL:
|
||||||
|
if int(sec.Info) >= len(ec.Sections) {
|
||||||
|
return nil, errors.Errorf("found relocation section %v for missing section %v", i, sec.Info)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store relocations under the section index of the target
|
||||||
|
idx := elf.SectionIndex(sec.Info)
|
||||||
|
if relSections[idx] != nil {
|
||||||
|
return nil, errors.Errorf("section %d has multiple relocation sections", idx)
|
||||||
|
}
|
||||||
|
relSections[idx] = sec
|
||||||
|
case sec.Type == elf.SHT_PROGBITS && (sec.Flags&elf.SHF_EXECINSTR) != 0 && sec.Size > 0:
|
||||||
|
progSections[elf.SectionIndex(i)] = sec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
license, err := loadLicense(licenseSection)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "load license")
|
||||||
|
}
|
||||||
|
|
||||||
|
version, err := loadVersion(versionSection, ec.ByteOrder)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "load version")
|
||||||
|
}
|
||||||
|
|
||||||
|
maps, err := ec.loadMaps(mapSections)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "load maps")
|
||||||
|
}
|
||||||
|
|
||||||
|
progs, libs, err := ec.loadPrograms(progSections, relSections, license, version)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "load programs")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(libs) > 0 {
|
||||||
|
for name, prog := range progs {
|
||||||
|
prog.Instructions, err = link(prog.Instructions, libs...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "program %s", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &CollectionSpec{maps, progs}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadLicense(sec *elf.Section) (string, error) {
|
||||||
|
if sec == nil {
|
||||||
|
return "", errors.Errorf("missing license section")
|
||||||
|
}
|
||||||
|
data, err := sec.Data()
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrapf(err, "section %s", sec.Name)
|
||||||
|
}
|
||||||
|
return string(bytes.TrimRight(data, "\000")), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadVersion(sec *elf.Section, bo binary.ByteOrder) (uint32, error) {
|
||||||
|
if sec == nil {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var version uint32
|
||||||
|
err := binary.Read(sec.Open(), bo, &version)
|
||||||
|
return version, errors.Wrapf(err, "section %s", sec.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *elfCode) loadPrograms(progSections, relSections map[elf.SectionIndex]*elf.Section, license string, version uint32) (map[string]*ProgramSpec, []asm.Instructions, error) {
|
||||||
|
var (
|
||||||
|
progs = make(map[string]*ProgramSpec)
|
||||||
|
libs []asm.Instructions
|
||||||
|
)
|
||||||
|
for idx, prog := range progSections {
|
||||||
|
syms := ec.symbolsPerSection[idx]
|
||||||
|
if len(syms) == 0 {
|
||||||
|
return nil, nil, errors.Errorf("section %v: missing symbols", prog.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
funcSym := syms[0]
|
||||||
|
if funcSym == "" {
|
||||||
|
return nil, nil, errors.Errorf("section %v: no label at start", prog.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
rels, err := ec.loadRelocations(relSections[idx])
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrapf(err, "program %s: can't load relocations", funcSym)
|
||||||
|
}
|
||||||
|
|
||||||
|
insns, err := ec.loadInstructions(prog, syms, rels)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, errors.Wrapf(err, "program %s: can't unmarshal instructions", funcSym)
|
||||||
|
}
|
||||||
|
|
||||||
|
if progType, attachType := getProgType(prog.Name); progType == UnspecifiedProgram {
|
||||||
|
// There is no single name we can use for "library" sections,
|
||||||
|
// since they may contain multiple functions. We'll decode the
|
||||||
|
// labels they contain later on, and then link sections that way.
|
||||||
|
libs = append(libs, insns)
|
||||||
|
} else {
|
||||||
|
progs[funcSym] = &ProgramSpec{
|
||||||
|
Name: funcSym,
|
||||||
|
Type: progType,
|
||||||
|
AttachType: attachType,
|
||||||
|
License: license,
|
||||||
|
KernelVersion: version,
|
||||||
|
Instructions: insns,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return progs, libs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *elfCode) loadInstructions(section *elf.Section, symbols, relocations map[uint64]string) (asm.Instructions, error) {
|
||||||
|
var (
|
||||||
|
r = section.Open()
|
||||||
|
insns asm.Instructions
|
||||||
|
ins asm.Instruction
|
||||||
|
offset uint64
|
||||||
|
)
|
||||||
|
for {
|
||||||
|
n, err := ins.Unmarshal(r, ec.ByteOrder)
|
||||||
|
if err == io.EOF {
|
||||||
|
return insns, nil
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "offset %d", offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
ins.Symbol = symbols[offset]
|
||||||
|
ins.Reference = relocations[offset]
|
||||||
|
|
||||||
|
insns = append(insns, ins)
|
||||||
|
offset += n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *elfCode) loadMaps(mapSections map[elf.SectionIndex]*elf.Section) (map[string]*MapSpec, error) {
|
||||||
|
var (
|
||||||
|
maps = make(map[string]*MapSpec)
|
||||||
|
b = make([]byte, 1)
|
||||||
|
)
|
||||||
|
for idx, sec := range mapSections {
|
||||||
|
syms := ec.symbolsPerSection[idx]
|
||||||
|
if len(syms) == 0 {
|
||||||
|
return nil, errors.Errorf("section %v: no symbols", sec.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sec.Size%uint64(len(syms)) != 0 {
|
||||||
|
return nil, errors.Errorf("section %v: map descriptors are not of equal size", sec.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
r = sec.Open()
|
||||||
|
size = sec.Size / uint64(len(syms))
|
||||||
|
)
|
||||||
|
for i, offset := 0, uint64(0); i < len(syms); i, offset = i+1, offset+size {
|
||||||
|
mapSym := syms[offset]
|
||||||
|
if mapSym == "" {
|
||||||
|
fmt.Println(syms)
|
||||||
|
return nil, errors.Errorf("section %s: missing symbol for map at offset %d", sec.Name, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if maps[mapSym] != nil {
|
||||||
|
return nil, errors.Errorf("section %v: map %v already exists", sec.Name, mapSym)
|
||||||
|
}
|
||||||
|
|
||||||
|
lr := io.LimitReader(r, int64(size))
|
||||||
|
|
||||||
|
var spec MapSpec
|
||||||
|
switch {
|
||||||
|
case binary.Read(lr, ec.ByteOrder, &spec.Type) != nil:
|
||||||
|
return nil, errors.Errorf("map %v: missing type", mapSym)
|
||||||
|
case binary.Read(lr, ec.ByteOrder, &spec.KeySize) != nil:
|
||||||
|
return nil, errors.Errorf("map %v: missing key size", mapSym)
|
||||||
|
case binary.Read(lr, ec.ByteOrder, &spec.ValueSize) != nil:
|
||||||
|
return nil, errors.Errorf("map %v: missing value size", mapSym)
|
||||||
|
case binary.Read(lr, ec.ByteOrder, &spec.MaxEntries) != nil:
|
||||||
|
return nil, errors.Errorf("map %v: missing max entries", mapSym)
|
||||||
|
case binary.Read(lr, ec.ByteOrder, &spec.Flags) != nil:
|
||||||
|
return nil, errors.Errorf("map %v: missing flags", mapSym)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, err := lr.Read(b)
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if b[0] != 0 {
|
||||||
|
return nil, errors.Errorf("map %v: unknown and non-zero fields in definition", mapSym)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maps[mapSym] = &spec
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return maps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProgType(v string) (ProgramType, AttachType) {
|
||||||
|
types := map[string]ProgramType{
|
||||||
|
// From https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/lib/bpf/libbpf.c#n3568
|
||||||
|
"socket": SocketFilter,
|
||||||
|
"seccomp": SocketFilter,
|
||||||
|
"kprobe/": Kprobe,
|
||||||
|
"kretprobe/": Kprobe,
|
||||||
|
"tracepoint/": TracePoint,
|
||||||
|
"xdp": XDP,
|
||||||
|
"perf_event": PerfEvent,
|
||||||
|
"sockops": SockOps,
|
||||||
|
"sk_skb": SkSKB,
|
||||||
|
"sk_msg": SkMsg,
|
||||||
|
"lirc_mode2": LircMode2,
|
||||||
|
"flow_dissector": FlowDissector,
|
||||||
|
|
||||||
|
"cgroup_skb/": CGroupSKB,
|
||||||
|
"cgroup/dev": CGroupDevice,
|
||||||
|
"cgroup/skb": CGroupSKB,
|
||||||
|
"cgroup/sock": CGroupSock,
|
||||||
|
"cgroup/post_bind": CGroupSock,
|
||||||
|
"cgroup/bind": CGroupSockAddr,
|
||||||
|
"cgroup/connect": CGroupSockAddr,
|
||||||
|
"cgroup/sendmsg": CGroupSockAddr,
|
||||||
|
"cgroup/recvmsg": CGroupSockAddr,
|
||||||
|
"cgroup/sysctl": CGroupSysctl,
|
||||||
|
"cgroup/getsockopt": CGroupSockopt,
|
||||||
|
"cgroup/setsockopt": CGroupSockopt,
|
||||||
|
"classifier": SchedCLS,
|
||||||
|
"action": SchedACT,
|
||||||
|
}
|
||||||
|
attachTypes := map[string]AttachType{
|
||||||
|
"cgroup_skb/ingress": AttachCGroupInetIngress,
|
||||||
|
"cgroup_skb/egress": AttachCGroupInetEgress,
|
||||||
|
"cgroup/sock": AttachCGroupInetSockCreate,
|
||||||
|
"cgroup/post_bind4": AttachCGroupInet4PostBind,
|
||||||
|
"cgroup/post_bind6": AttachCGroupInet6PostBind,
|
||||||
|
"cgroup/dev": AttachCGroupDevice,
|
||||||
|
"sockops": AttachCGroupSockOps,
|
||||||
|
"sk_skb/stream_parser": AttachSkSKBStreamParser,
|
||||||
|
"sk_skb/stream_verdict": AttachSkSKBStreamVerdict,
|
||||||
|
"sk_msg": AttachSkSKBStreamVerdict,
|
||||||
|
"lirc_mode2": AttachLircMode2,
|
||||||
|
"flow_dissector": AttachFlowDissector,
|
||||||
|
"cgroup/bind4": AttachCGroupInet4Bind,
|
||||||
|
"cgroup/bind6": AttachCGroupInet6Bind,
|
||||||
|
"cgroup/connect4": AttachCGroupInet4Connect,
|
||||||
|
"cgroup/connect6": AttachCGroupInet6Connect,
|
||||||
|
"cgroup/sendmsg4": AttachCGroupUDP4Sendmsg,
|
||||||
|
"cgroup/sendmsg6": AttachCGroupUDP6Sendmsg,
|
||||||
|
"cgroup/recvmsg4": AttachCGroupUDP4Recvmsg,
|
||||||
|
"cgroup/recvmsg6": AttachCGroupUDP6Recvmsg,
|
||||||
|
"cgroup/sysctl": AttachCGroupSysctl,
|
||||||
|
"cgroup/getsockopt": AttachCGroupGetsockopt,
|
||||||
|
"cgroup/setsockopt": AttachCGroupSetsockopt,
|
||||||
|
}
|
||||||
|
attachType := AttachNone
|
||||||
|
for k, t := range attachTypes {
|
||||||
|
if strings.HasPrefix(v, k) {
|
||||||
|
attachType = t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, t := range types {
|
||||||
|
if strings.HasPrefix(v, k) {
|
||||||
|
return t, attachType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return UnspecifiedProgram, AttachNone
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ec *elfCode) loadRelocations(sec *elf.Section) (map[uint64]string, error) {
|
||||||
|
rels := make(map[uint64]string)
|
||||||
|
if sec == nil {
|
||||||
|
return rels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if sec.Entsize < 16 {
|
||||||
|
return nil, errors.New("rels are less than 16 bytes")
|
||||||
|
}
|
||||||
|
|
||||||
|
r := sec.Open()
|
||||||
|
for off := uint64(0); off < sec.Size; off += sec.Entsize {
|
||||||
|
ent := io.LimitReader(r, int64(sec.Entsize))
|
||||||
|
|
||||||
|
var rel elf.Rel64
|
||||||
|
if binary.Read(ent, ec.ByteOrder, &rel) != nil {
|
||||||
|
return nil, errors.Errorf("can't parse relocation at offset %v", off)
|
||||||
|
}
|
||||||
|
|
||||||
|
symNo := int(elf.R_SYM64(rel.Info) - 1)
|
||||||
|
if symNo >= len(ec.symbols) {
|
||||||
|
return nil, errors.Errorf("relocation at offset %d: symbol %v doesnt exist", off, symNo)
|
||||||
|
}
|
||||||
|
|
||||||
|
rels[rel.Off] = ec.symbols[symNo].Name
|
||||||
|
}
|
||||||
|
return rels, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func symbolsPerSection(symbols []elf.Symbol) map[elf.SectionIndex]map[uint64]string {
|
||||||
|
result := make(map[elf.SectionIndex]map[uint64]string)
|
||||||
|
for i, sym := range symbols {
|
||||||
|
switch elf.ST_TYPE(sym.Info) {
|
||||||
|
case elf.STT_NOTYPE:
|
||||||
|
// Older versions of LLVM doesn't tag
|
||||||
|
// symbols correctly.
|
||||||
|
break
|
||||||
|
case elf.STT_OBJECT:
|
||||||
|
break
|
||||||
|
case elf.STT_FUNC:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if sym.Name == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
idx := sym.Section
|
||||||
|
if _, ok := result[idx]; !ok {
|
||||||
|
result[idx] = make(map[uint64]string)
|
||||||
|
}
|
||||||
|
result[idx][sym.Value] = symbols[i].Name
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
8
vendor/github.com/cilium/ebpf/go.mod
generated
vendored
Normal file
8
vendor/github.com/cilium/ebpf/go.mod
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
module github.com/cilium/ebpf
|
||||||
|
|
||||||
|
go 1.12
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/pkg/errors v0.8.1
|
||||||
|
golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7
|
||||||
|
)
|
64
vendor/github.com/cilium/ebpf/internal/cpu.go
generated
vendored
Normal file
64
vendor/github.com/cilium/ebpf/internal/cpu.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var sysCPU struct {
|
||||||
|
once sync.Once
|
||||||
|
err error
|
||||||
|
num int
|
||||||
|
}
|
||||||
|
|
||||||
|
// PossibleCPUs returns the max number of CPUs a system may possibly have
|
||||||
|
// Logical CPU numbers must be of the form 0-n
|
||||||
|
func PossibleCPUs() (int, error) {
|
||||||
|
sysCPU.once.Do(func() {
|
||||||
|
sysCPU.num, sysCPU.err = parseCPUs("/sys/devices/system/cpu/possible")
|
||||||
|
})
|
||||||
|
|
||||||
|
return sysCPU.num, sysCPU.err
|
||||||
|
}
|
||||||
|
|
||||||
|
var onlineCPU struct {
|
||||||
|
once sync.Once
|
||||||
|
err error
|
||||||
|
num int
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnlineCPUs returns the number of currently online CPUs
|
||||||
|
// Logical CPU numbers must be of the form 0-n
|
||||||
|
func OnlineCPUs() (int, error) {
|
||||||
|
onlineCPU.once.Do(func() {
|
||||||
|
onlineCPU.num, onlineCPU.err = parseCPUs("/sys/devices/system/cpu/online")
|
||||||
|
})
|
||||||
|
|
||||||
|
return onlineCPU.num, onlineCPU.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCPUs parses the number of cpus from sysfs,
|
||||||
|
// in the format of "/sys/devices/system/cpu/{possible,online,..}.
|
||||||
|
// Logical CPU numbers must be of the form 0-n
|
||||||
|
func parseCPUs(path string) (int, error) {
|
||||||
|
file, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var low, high int
|
||||||
|
n, _ := fmt.Fscanf(file, "%d-%d", &low, &high)
|
||||||
|
if n < 1 || low != 0 {
|
||||||
|
return 0, errors.Wrapf(err, "%s has unknown format", path)
|
||||||
|
}
|
||||||
|
if n == 1 {
|
||||||
|
high = low
|
||||||
|
}
|
||||||
|
|
||||||
|
// cpus is 0 indexed
|
||||||
|
return high + 1, nil
|
||||||
|
}
|
24
vendor/github.com/cilium/ebpf/internal/endian.go
generated
vendored
Normal file
24
vendor/github.com/cilium/ebpf/internal/endian.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NativeEndian is set to either binary.BigEndian or binary.LittleEndian,
|
||||||
|
// depending on the host's endianness.
|
||||||
|
var NativeEndian binary.ByteOrder
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if isBigEndian() {
|
||||||
|
NativeEndian = binary.BigEndian
|
||||||
|
} else {
|
||||||
|
NativeEndian = binary.LittleEndian
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func isBigEndian() (ret bool) {
|
||||||
|
i := int(0x1)
|
||||||
|
bs := (*[int(unsafe.Sizeof(i))]byte)(unsafe.Pointer(&i))
|
||||||
|
return bs[0] == 0
|
||||||
|
}
|
85
vendor/github.com/cilium/ebpf/internal/feature.go
generated
vendored
Normal file
85
vendor/github.com/cilium/ebpf/internal/feature.go
generated
vendored
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
package internal
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UnsupportedFeatureError is returned by FeatureTest() functions.
|
||||||
|
type UnsupportedFeatureError struct {
|
||||||
|
// The minimum Linux mainline version required for this feature.
|
||||||
|
// Used for the error string, and for sanity checking during testing.
|
||||||
|
MinimumVersion Version
|
||||||
|
|
||||||
|
// The name of the feature that isn't supported.
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ufe *UnsupportedFeatureError) Error() string {
|
||||||
|
return fmt.Sprintf("%s not supported (requires >= %s)", ufe.Name, ufe.MinimumVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FeatureTest wraps a function so that it is run at most once.
|
||||||
|
//
|
||||||
|
// name should identify the tested feature, while version must be in the
|
||||||
|
// form Major.Minor[.Patch].
|
||||||
|
//
|
||||||
|
// Returns a descriptive UnsupportedFeatureError if the feature is not available.
|
||||||
|
func FeatureTest(name, version string, fn func() bool) func() error {
|
||||||
|
v, err := NewVersion(version)
|
||||||
|
if err != nil {
|
||||||
|
return func() error { return err }
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
once sync.Once
|
||||||
|
result error
|
||||||
|
)
|
||||||
|
|
||||||
|
return func() error {
|
||||||
|
once.Do(func() {
|
||||||
|
if !fn() {
|
||||||
|
result = &UnsupportedFeatureError{
|
||||||
|
MinimumVersion: v,
|
||||||
|
Name: name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Version in the form Major.Minor.Patch.
|
||||||
|
type Version [3]uint16
|
||||||
|
|
||||||
|
// NewVersion creates a version from a string like "Major.Minor.Patch".
|
||||||
|
//
|
||||||
|
// Patch is optional.
|
||||||
|
func NewVersion(ver string) (Version, error) {
|
||||||
|
var major, minor, patch uint16
|
||||||
|
n, _ := fmt.Sscanf(ver, "%d.%d.%d", &major, &minor, &patch)
|
||||||
|
if n < 2 {
|
||||||
|
return Version{}, errors.Errorf("invalid version: %s", ver)
|
||||||
|
}
|
||||||
|
return Version{major, minor, patch}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Version) String() string {
|
||||||
|
if v[2] == 0 {
|
||||||
|
return fmt.Sprintf("v%d.%d", v[0], v[1])
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("v%d.%d.%d", v[0], v[1], v[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Less returns true if the version is less than another version.
|
||||||
|
func (v Version) Less(other Version) bool {
|
||||||
|
for i, a := range v {
|
||||||
|
if a == other[i] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return a < other[i]
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
127
vendor/github.com/cilium/ebpf/internal/unix/types_linux.go
generated
vendored
Normal file
127
vendor/github.com/cilium/ebpf/internal/unix/types_linux.go
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
// +build linux
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
linux "golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ENOENT = linux.ENOENT
|
||||||
|
EAGAIN = linux.EAGAIN
|
||||||
|
ENOSPC = linux.ENOSPC
|
||||||
|
EINVAL = linux.EINVAL
|
||||||
|
EPOLLIN = linux.EPOLLIN
|
||||||
|
BPF_OBJ_NAME_LEN = linux.BPF_OBJ_NAME_LEN
|
||||||
|
BPF_TAG_SIZE = linux.BPF_TAG_SIZE
|
||||||
|
SYS_BPF = linux.SYS_BPF
|
||||||
|
F_DUPFD_CLOEXEC = linux.F_DUPFD_CLOEXEC
|
||||||
|
EPOLL_CTL_ADD = linux.EPOLL_CTL_ADD
|
||||||
|
EPOLL_CLOEXEC = linux.EPOLL_CLOEXEC
|
||||||
|
O_CLOEXEC = linux.O_CLOEXEC
|
||||||
|
O_NONBLOCK = linux.O_NONBLOCK
|
||||||
|
PROT_READ = linux.PROT_READ
|
||||||
|
PROT_WRITE = linux.PROT_WRITE
|
||||||
|
MAP_SHARED = linux.MAP_SHARED
|
||||||
|
PERF_TYPE_SOFTWARE = linux.PERF_TYPE_SOFTWARE
|
||||||
|
PERF_COUNT_SW_BPF_OUTPUT = linux.PERF_COUNT_SW_BPF_OUTPUT
|
||||||
|
PerfBitWatermark = linux.PerfBitWatermark
|
||||||
|
PERF_SAMPLE_RAW = linux.PERF_SAMPLE_RAW
|
||||||
|
PERF_FLAG_FD_CLOEXEC = linux.PERF_FLAG_FD_CLOEXEC
|
||||||
|
RLIM_INFINITY = linux.RLIM_INFINITY
|
||||||
|
)
|
||||||
|
|
||||||
|
// Statfs_t is a wrapper
|
||||||
|
type Statfs_t = linux.Statfs_t
|
||||||
|
|
||||||
|
// Rlimit is a wrapper
|
||||||
|
type Rlimit = linux.Rlimit
|
||||||
|
|
||||||
|
// Setrlimit is a wrapper
|
||||||
|
func Setrlimit(resource int, rlim *Rlimit) (err error) {
|
||||||
|
return linux.Setrlimit(resource, rlim)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Syscall is a wrapper
|
||||||
|
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||||
|
return linux.Syscall(trap, a1, a2, a3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FcntlInt is a wrapper
|
||||||
|
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
||||||
|
return linux.FcntlInt(fd, cmd, arg)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statfs is a wrapper
|
||||||
|
func Statfs(path string, buf *Statfs_t) (err error) {
|
||||||
|
return linux.Statfs(path, buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close is a wrapper
|
||||||
|
func Close(fd int) (err error) {
|
||||||
|
return linux.Close(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EpollEvent is a wrapper
|
||||||
|
type EpollEvent = linux.EpollEvent
|
||||||
|
|
||||||
|
// EpollWait is a wrapper
|
||||||
|
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||||
|
return linux.EpollWait(epfd, events, msec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EpollCtl is a wrapper
|
||||||
|
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
|
||||||
|
return linux.EpollCtl(epfd, op, fd, event)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eventfd is a wrapper
|
||||||
|
func Eventfd(initval uint, flags int) (fd int, err error) {
|
||||||
|
return linux.Eventfd(initval, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write is a wrapper
|
||||||
|
func Write(fd int, p []byte) (n int, err error) {
|
||||||
|
return linux.Write(fd, p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// EpollCreate1 is a wrapper
|
||||||
|
func EpollCreate1(flag int) (fd int, err error) {
|
||||||
|
return linux.EpollCreate1(flag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PerfEventMmapPage is a wrapper
|
||||||
|
type PerfEventMmapPage linux.PerfEventMmapPage
|
||||||
|
|
||||||
|
// SetNonblock is a wrapper
|
||||||
|
func SetNonblock(fd int, nonblocking bool) (err error) {
|
||||||
|
return linux.SetNonblock(fd, nonblocking)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mmap is a wrapper
|
||||||
|
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
|
||||||
|
return linux.Mmap(fd, offset, length, prot, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Munmap is a wrapper
|
||||||
|
func Munmap(b []byte) (err error) {
|
||||||
|
return linux.Munmap(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PerfEventAttr is a wrapper
|
||||||
|
type PerfEventAttr = linux.PerfEventAttr
|
||||||
|
|
||||||
|
// PerfEventOpen is a wrapper
|
||||||
|
func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {
|
||||||
|
return linux.PerfEventOpen(attr, pid, cpu, groupFd, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utsname is a wrapper
|
||||||
|
type Utsname = linux.Utsname
|
||||||
|
|
||||||
|
// Uname is a wrapper
|
||||||
|
func Uname(buf *Utsname) (err error) {
|
||||||
|
return linux.Uname(buf)
|
||||||
|
}
|
193
vendor/github.com/cilium/ebpf/internal/unix/types_other.go
generated
vendored
Normal file
193
vendor/github.com/cilium/ebpf/internal/unix/types_other.go
generated
vendored
Normal file
@ -0,0 +1,193 @@
|
|||||||
|
// +build !linux
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"syscall"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ENOENT = syscall.ENOENT
|
||||||
|
EAGAIN = syscall.EAGAIN
|
||||||
|
ENOSPC = syscall.ENOSPC
|
||||||
|
EINVAL = syscall.EINVAL
|
||||||
|
BPF_OBJ_NAME_LEN = 0x10
|
||||||
|
BPF_TAG_SIZE = 0x8
|
||||||
|
SYS_BPF = 321
|
||||||
|
F_DUPFD_CLOEXEC = 0x406
|
||||||
|
EPOLLIN = 0x1
|
||||||
|
EPOLL_CTL_ADD = 0x1
|
||||||
|
EPOLL_CLOEXEC = 0x80000
|
||||||
|
O_CLOEXEC = 0x80000
|
||||||
|
O_NONBLOCK = 0x800
|
||||||
|
PROT_READ = 0x1
|
||||||
|
PROT_WRITE = 0x2
|
||||||
|
MAP_SHARED = 0x1
|
||||||
|
PERF_TYPE_SOFTWARE = 0x1
|
||||||
|
PERF_COUNT_SW_BPF_OUTPUT = 0xa
|
||||||
|
PerfBitWatermark = 0x4000
|
||||||
|
PERF_SAMPLE_RAW = 0x400
|
||||||
|
PERF_FLAG_FD_CLOEXEC = 0x8
|
||||||
|
)
|
||||||
|
|
||||||
|
// Statfs_t is a wrapper
|
||||||
|
type Statfs_t struct {
|
||||||
|
Type int64
|
||||||
|
Bsize int64
|
||||||
|
Blocks uint64
|
||||||
|
Bfree uint64
|
||||||
|
Bavail uint64
|
||||||
|
Files uint64
|
||||||
|
Ffree uint64
|
||||||
|
Fsid [2]int32
|
||||||
|
Namelen int64
|
||||||
|
Frsize int64
|
||||||
|
Flags int64
|
||||||
|
Spare [4]int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rlimit is a wrapper
|
||||||
|
type Rlimit struct {
|
||||||
|
Cur uint64
|
||||||
|
Max uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setrlimit is a wrapper
|
||||||
|
func Setrlimit(resource int, rlim *Rlimit) (err error) {
|
||||||
|
return errNonLinux
|
||||||
|
}
|
||||||
|
|
||||||
|
// Syscall is a wrapper
|
||||||
|
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) {
|
||||||
|
return 0, 0, syscall.Errno(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FcntlInt is a wrapper
|
||||||
|
func FcntlInt(fd uintptr, cmd, arg int) (int, error) {
|
||||||
|
return -1, errNonLinux
|
||||||
|
}
|
||||||
|
|
||||||
|
// Statfs is a wrapper
|
||||||
|
func Statfs(path string, buf *Statfs_t) error {
|
||||||
|
return errNonLinux
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close is a wrapper
|
||||||
|
func Close(fd int) (err error) {
|
||||||
|
return errNonLinux
|
||||||
|
}
|
||||||
|
|
||||||
|
// EpollEvent is a wrapper
|
||||||
|
type EpollEvent struct {
|
||||||
|
Events uint32
|
||||||
|
Fd int32
|
||||||
|
Pad int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// EpollWait is a wrapper
|
||||||
|
func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) {
|
||||||
|
return 0, errNonLinux
|
||||||
|
}
|
||||||
|
|
||||||
|
// EpollCtl is a wrapper
|
||||||
|
func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) {
|
||||||
|
return errNonLinux
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eventfd is a wrapper
|
||||||
|
func Eventfd(initval uint, flags int) (fd int, err error) {
|
||||||
|
return 0, errNonLinux
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write is a wrapper
|
||||||
|
func Write(fd int, p []byte) (n int, err error) {
|
||||||
|
return 0, errNonLinux
|
||||||
|
}
|
||||||
|
|
||||||
|
// EpollCreate1 is a wrapper
|
||||||
|
func EpollCreate1(flag int) (fd int, err error) {
|
||||||
|
return 0, errNonLinux
|
||||||
|
}
|
||||||
|
|
||||||
|
// PerfEventMmapPage is a wrapper
|
||||||
|
type PerfEventMmapPage struct {
|
||||||
|
Version uint32
|
||||||
|
Compat_version uint32
|
||||||
|
Lock uint32
|
||||||
|
Index uint32
|
||||||
|
Offset int64
|
||||||
|
Time_enabled uint64
|
||||||
|
Time_running uint64
|
||||||
|
Capabilities uint64
|
||||||
|
Pmc_width uint16
|
||||||
|
Time_shift uint16
|
||||||
|
Time_mult uint32
|
||||||
|
Time_offset uint64
|
||||||
|
Time_zero uint64
|
||||||
|
Size uint32
|
||||||
|
|
||||||
|
Data_head uint64
|
||||||
|
Data_tail uint64
|
||||||
|
Data_offset uint64
|
||||||
|
Data_size uint64
|
||||||
|
Aux_head uint64
|
||||||
|
Aux_tail uint64
|
||||||
|
Aux_offset uint64
|
||||||
|
Aux_size uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNonblock is a wrapper
|
||||||
|
func SetNonblock(fd int, nonblocking bool) (err error) {
|
||||||
|
return errNonLinux
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mmap is a wrapper
|
||||||
|
func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) {
|
||||||
|
return []byte{}, errNonLinux
|
||||||
|
}
|
||||||
|
|
||||||
|
// Munmap is a wrapper
|
||||||
|
func Munmap(b []byte) (err error) {
|
||||||
|
return errNonLinux
|
||||||
|
}
|
||||||
|
|
||||||
|
// PerfEventAttr is a wrapper
|
||||||
|
type PerfEventAttr struct {
|
||||||
|
Type uint32
|
||||||
|
Size uint32
|
||||||
|
Config uint64
|
||||||
|
Sample uint64
|
||||||
|
Sample_type uint64
|
||||||
|
Read_format uint64
|
||||||
|
Bits uint64
|
||||||
|
Wakeup uint32
|
||||||
|
Bp_type uint32
|
||||||
|
Ext1 uint64
|
||||||
|
Ext2 uint64
|
||||||
|
Branch_sample_type uint64
|
||||||
|
Sample_regs_user uint64
|
||||||
|
Sample_stack_user uint32
|
||||||
|
Clockid int32
|
||||||
|
Sample_regs_intr uint64
|
||||||
|
Aux_watermark uint32
|
||||||
|
Sample_max_stack uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
// PerfEventOpen is a wrapper
|
||||||
|
func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) {
|
||||||
|
return 0, errNonLinux
|
||||||
|
}
|
||||||
|
|
||||||
|
// Utsname is a wrapper
|
||||||
|
type Utsname struct {
|
||||||
|
Release [65]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uname is a wrapper
|
||||||
|
func Uname(buf *Utsname) (err error) {
|
||||||
|
return errNonLinux
|
||||||
|
}
|
58
vendor/github.com/cilium/ebpf/linker.go
generated
vendored
Normal file
58
vendor/github.com/cilium/ebpf/linker.go
generated
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cilium/ebpf/asm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// link resolves bpf-to-bpf calls.
|
||||||
|
//
|
||||||
|
// Each section may contain multiple functions / labels, and is only linked
|
||||||
|
// if the program being edited references one of these functions.
|
||||||
|
//
|
||||||
|
// Sections must not require linking themselves.
|
||||||
|
func link(insns asm.Instructions, sections ...asm.Instructions) (asm.Instructions, error) {
|
||||||
|
for _, section := range sections {
|
||||||
|
var err error
|
||||||
|
insns, err = linkSection(insns, section)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return insns, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func linkSection(insns, section asm.Instructions) (asm.Instructions, error) {
|
||||||
|
// A map of symbols to the libraries which contain them.
|
||||||
|
symbols, err := section.SymbolOffsets()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, ins := range insns {
|
||||||
|
if ins.Reference == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ins.OpCode.JumpOp() != asm.Call || ins.Src != asm.R1 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if ins.Constant != -1 {
|
||||||
|
// This is already a valid call, no need to link again.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := symbols[ins.Reference]; !ok {
|
||||||
|
// Symbol isn't available in this section
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point we know that at least one function in the
|
||||||
|
// library is called from insns. Merge the two sections.
|
||||||
|
// The rewrite of ins.Constant happens in asm.Instruction.Marshal.
|
||||||
|
return append(insns, section...), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// None of the functions in the section are called. Do nothing.
|
||||||
|
return insns, nil
|
||||||
|
}
|
604
vendor/github.com/cilium/ebpf/map.go
generated
vendored
Normal file
604
vendor/github.com/cilium/ebpf/map.go
generated
vendored
Normal file
@ -0,0 +1,604 @@
|
|||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/cilium/ebpf/internal"
|
||||||
|
"github.com/cilium/ebpf/internal/unix"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// MapSpec defines a Map.
|
||||||
|
type MapSpec struct {
|
||||||
|
// Name is passed to the kernel as a debug aid. Must only contain
|
||||||
|
// alpha numeric and '_' characters.
|
||||||
|
Name string
|
||||||
|
Type MapType
|
||||||
|
KeySize uint32
|
||||||
|
ValueSize uint32
|
||||||
|
MaxEntries uint32
|
||||||
|
Flags uint32
|
||||||
|
// InnerMap is used as a template for ArrayOfMaps and HashOfMaps
|
||||||
|
InnerMap *MapSpec
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ms *MapSpec) String() string {
|
||||||
|
return fmt.Sprintf("%s(keySize=%d, valueSize=%d, maxEntries=%d, flags=%d)", ms.Type, ms.KeySize, ms.ValueSize, ms.MaxEntries, ms.Flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the spec.
|
||||||
|
func (ms *MapSpec) Copy() *MapSpec {
|
||||||
|
if ms == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cpy := *ms
|
||||||
|
cpy.InnerMap = ms.InnerMap.Copy()
|
||||||
|
return &cpy
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map represents a Map file descriptor.
|
||||||
|
//
|
||||||
|
// It is not safe to close a map which is used by other goroutines.
|
||||||
|
//
|
||||||
|
// Methods which take interface{} arguments by default encode
|
||||||
|
// them using binary.Read/Write in the machine's native endianness.
|
||||||
|
//
|
||||||
|
// Implement encoding.BinaryMarshaler or encoding.BinaryUnmarshaler
|
||||||
|
// if you require custom encoding.
|
||||||
|
type Map struct {
|
||||||
|
name string
|
||||||
|
fd *bpfFD
|
||||||
|
abi MapABI
|
||||||
|
// Per CPU maps return values larger than the size in the spec
|
||||||
|
fullValueSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMapFromFD creates a map from a raw fd.
|
||||||
|
//
|
||||||
|
// You should not use fd after calling this function.
|
||||||
|
func NewMapFromFD(fd int) (*Map, error) {
|
||||||
|
if fd < 0 {
|
||||||
|
return nil, errors.New("invalid fd")
|
||||||
|
}
|
||||||
|
bpfFd := newBPFFD(uint32(fd))
|
||||||
|
|
||||||
|
name, abi, err := newMapABIFromFd(bpfFd)
|
||||||
|
if err != nil {
|
||||||
|
bpfFd.forget()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newMap(bpfFd, name, abi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMap creates a new Map.
|
||||||
|
//
|
||||||
|
// Creating a map for the first time will perform feature detection
|
||||||
|
// by creating small, temporary maps.
|
||||||
|
func NewMap(spec *MapSpec) (*Map, error) {
|
||||||
|
if spec.Type != ArrayOfMaps && spec.Type != HashOfMaps {
|
||||||
|
return createMap(spec, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
if spec.InnerMap == nil {
|
||||||
|
return nil, errors.Errorf("%s requires InnerMap", spec.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
template, err := createMap(spec.InnerMap, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer template.Close()
|
||||||
|
|
||||||
|
return createMap(spec, template.fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMap(spec *MapSpec, inner *bpfFD) (*Map, error) {
|
||||||
|
spec = spec.Copy()
|
||||||
|
|
||||||
|
switch spec.Type {
|
||||||
|
case ArrayOfMaps:
|
||||||
|
fallthrough
|
||||||
|
case HashOfMaps:
|
||||||
|
if err := haveNestedMaps(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if spec.ValueSize != 0 && spec.ValueSize != 4 {
|
||||||
|
return nil, errors.Errorf("ValueSize must be zero or four for map of map")
|
||||||
|
}
|
||||||
|
spec.ValueSize = 4
|
||||||
|
|
||||||
|
case PerfEventArray:
|
||||||
|
if spec.KeySize != 0 {
|
||||||
|
return nil, errors.Errorf("KeySize must be zero for perf event array")
|
||||||
|
}
|
||||||
|
if spec.ValueSize != 0 {
|
||||||
|
return nil, errors.Errorf("ValueSize must be zero for perf event array")
|
||||||
|
}
|
||||||
|
if spec.MaxEntries == 0 {
|
||||||
|
n, err := internal.OnlineCPUs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "perf event array")
|
||||||
|
}
|
||||||
|
spec.MaxEntries = uint32(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
spec.KeySize = 4
|
||||||
|
spec.ValueSize = 4
|
||||||
|
}
|
||||||
|
|
||||||
|
attr := bpfMapCreateAttr{
|
||||||
|
mapType: spec.Type,
|
||||||
|
keySize: spec.KeySize,
|
||||||
|
valueSize: spec.ValueSize,
|
||||||
|
maxEntries: spec.MaxEntries,
|
||||||
|
flags: spec.Flags,
|
||||||
|
}
|
||||||
|
|
||||||
|
if inner != nil {
|
||||||
|
var err error
|
||||||
|
attr.innerMapFd, err = inner.value()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "map create")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
name, err := newBPFObjName(spec.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "map create")
|
||||||
|
}
|
||||||
|
|
||||||
|
if haveObjName() == nil {
|
||||||
|
attr.mapName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := bpfMapCreate(&attr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "map create")
|
||||||
|
}
|
||||||
|
|
||||||
|
return newMap(fd, spec.Name, newMapABIFromSpec(spec))
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMap(fd *bpfFD, name string, abi *MapABI) (*Map, error) {
|
||||||
|
m := &Map{
|
||||||
|
name,
|
||||||
|
fd,
|
||||||
|
*abi,
|
||||||
|
int(abi.ValueSize),
|
||||||
|
}
|
||||||
|
|
||||||
|
if !abi.Type.hasPerCPUValue() {
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
possibleCPUs, err := internal.PossibleCPUs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
m.fullValueSize = align(int(abi.ValueSize), 8) * possibleCPUs
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map) String() string {
|
||||||
|
if m.name != "" {
|
||||||
|
return fmt.Sprintf("%s(%s)#%v", m.abi.Type, m.name, m.fd)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s#%v", m.abi.Type, m.fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ABI gets the ABI of the Map
|
||||||
|
func (m *Map) ABI() MapABI {
|
||||||
|
return m.abi
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lookup retrieves a value from a Map.
|
||||||
|
//
|
||||||
|
// Calls Close() on valueOut if it is of type **Map or **Program,
|
||||||
|
// and *valueOut is not nil.
|
||||||
|
//
|
||||||
|
// Returns an error if the key doesn't exist, see IsNotExist.
|
||||||
|
func (m *Map) Lookup(key, valueOut interface{}) error {
|
||||||
|
valuePtr, valueBytes := makeBuffer(valueOut, m.fullValueSize)
|
||||||
|
|
||||||
|
if err := m.lookup(key, valuePtr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if valueBytes == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.abi.Type.hasPerCPUValue() {
|
||||||
|
return unmarshalPerCPUValue(valueOut, int(m.abi.ValueSize), valueBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch value := valueOut.(type) {
|
||||||
|
case **Map:
|
||||||
|
m, err := unmarshalMap(valueBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
(*value).Close()
|
||||||
|
*value = m
|
||||||
|
return nil
|
||||||
|
case *Map:
|
||||||
|
return errors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil))
|
||||||
|
case Map:
|
||||||
|
return errors.Errorf("can't unmarshal into %T, need %T", value, (**Map)(nil))
|
||||||
|
|
||||||
|
case **Program:
|
||||||
|
p, err := unmarshalProgram(valueBytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
(*value).Close()
|
||||||
|
*value = p
|
||||||
|
return nil
|
||||||
|
case *Program:
|
||||||
|
return errors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil))
|
||||||
|
case Program:
|
||||||
|
return errors.Errorf("can't unmarshal into %T, need %T", value, (**Program)(nil))
|
||||||
|
|
||||||
|
default:
|
||||||
|
return unmarshalBytes(valueOut, valueBytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LookupBytes gets a value from Map.
|
||||||
|
//
|
||||||
|
// Returns a nil value if a key doesn't exist.
|
||||||
|
func (m *Map) LookupBytes(key interface{}) ([]byte, error) {
|
||||||
|
valueBytes := make([]byte, m.fullValueSize)
|
||||||
|
valuePtr := newPtr(unsafe.Pointer(&valueBytes[0]))
|
||||||
|
|
||||||
|
err := m.lookup(key, valuePtr)
|
||||||
|
if IsNotExist(err) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return valueBytes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map) lookup(key interface{}, valueOut syscallPtr) error {
|
||||||
|
keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithMessage(err, "can't marshal key")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bpfMapLookupElem(m.fd, keyPtr, valueOut)
|
||||||
|
return errors.WithMessage(err, "lookup failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapUpdateFlags controls the behaviour of the Map.Update call.
|
||||||
|
//
|
||||||
|
// The exact semantics depend on the specific MapType.
|
||||||
|
type MapUpdateFlags uint64
|
||||||
|
|
||||||
|
const (
|
||||||
|
// UpdateAny creates a new element or update an existing one.
|
||||||
|
UpdateAny MapUpdateFlags = iota
|
||||||
|
// UpdateNoExist creates a new element.
|
||||||
|
UpdateNoExist MapUpdateFlags = 1 << (iota - 1)
|
||||||
|
// UpdateExist updates an existing element.
|
||||||
|
UpdateExist
|
||||||
|
)
|
||||||
|
|
||||||
|
// Put replaces or creates a value in map.
|
||||||
|
//
|
||||||
|
// It is equivalent to calling Update with UpdateAny.
|
||||||
|
func (m *Map) Put(key, value interface{}) error {
|
||||||
|
return m.Update(key, value, UpdateAny)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update changes the value of a key.
|
||||||
|
func (m *Map) Update(key, value interface{}, flags MapUpdateFlags) error {
|
||||||
|
keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithMessage(err, "can't marshal key")
|
||||||
|
}
|
||||||
|
|
||||||
|
var valuePtr syscallPtr
|
||||||
|
if m.abi.Type.hasPerCPUValue() {
|
||||||
|
valuePtr, err = marshalPerCPUValue(value, int(m.abi.ValueSize))
|
||||||
|
} else {
|
||||||
|
valuePtr, err = marshalPtr(value, int(m.abi.ValueSize))
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithMessage(err, "can't marshal value")
|
||||||
|
}
|
||||||
|
|
||||||
|
return bpfMapUpdateElem(m.fd, keyPtr, valuePtr, uint64(flags))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete removes a value.
|
||||||
|
//
|
||||||
|
// Returns an error if the key does not exist, see IsNotExist.
|
||||||
|
func (m *Map) Delete(key interface{}) error {
|
||||||
|
keyPtr, err := marshalPtr(key, int(m.abi.KeySize))
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithMessage(err, "can't marshal key")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bpfMapDeleteElem(m.fd, keyPtr)
|
||||||
|
return errors.WithMessage(err, "can't delete key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextKey finds the key following an initial key.
|
||||||
|
//
|
||||||
|
// See NextKeyBytes for details.
|
||||||
|
func (m *Map) NextKey(key, nextKeyOut interface{}) error {
|
||||||
|
nextKeyPtr, nextKeyBytes := makeBuffer(nextKeyOut, int(m.abi.KeySize))
|
||||||
|
|
||||||
|
if err := m.nextKey(key, nextKeyPtr); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if nextKeyBytes == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := unmarshalBytes(nextKeyOut, nextKeyBytes)
|
||||||
|
return errors.WithMessage(err, "can't unmarshal next key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// NextKeyBytes returns the key following an initial key as a byte slice.
|
||||||
|
//
|
||||||
|
// Passing nil will return the first key.
|
||||||
|
//
|
||||||
|
// Use Iterate if you want to traverse all entries in the map.
|
||||||
|
func (m *Map) NextKeyBytes(key interface{}) ([]byte, error) {
|
||||||
|
nextKey := make([]byte, m.abi.KeySize)
|
||||||
|
nextKeyPtr := newPtr(unsafe.Pointer(&nextKey[0]))
|
||||||
|
|
||||||
|
err := m.nextKey(key, nextKeyPtr)
|
||||||
|
if IsNotExist(err) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nextKey, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Map) nextKey(key interface{}, nextKeyOut syscallPtr) error {
|
||||||
|
var (
|
||||||
|
keyPtr syscallPtr
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
if key != nil {
|
||||||
|
keyPtr, err = marshalPtr(key, int(m.abi.KeySize))
|
||||||
|
if err != nil {
|
||||||
|
return errors.WithMessage(err, "can't marshal key")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = bpfMapGetNextKey(m.fd, keyPtr, nextKeyOut)
|
||||||
|
return errors.WithMessage(err, "can't get next key")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate traverses a map.
|
||||||
|
//
|
||||||
|
// It's safe to create multiple iterators at the same time.
|
||||||
|
//
|
||||||
|
// It's not possible to guarantee that all keys in a map will be
|
||||||
|
// returned if there are concurrent modifications to the map.
|
||||||
|
func (m *Map) Iterate() *MapIterator {
|
||||||
|
return newMapIterator(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close removes a Map
|
||||||
|
func (m *Map) Close() error {
|
||||||
|
if m == nil {
|
||||||
|
// This makes it easier to clean up when iterating maps
|
||||||
|
// of maps / programs.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.fd.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FD gets the file descriptor of the Map.
|
||||||
|
//
|
||||||
|
// Calling this function is invalid after Close has been called.
|
||||||
|
func (m *Map) FD() int {
|
||||||
|
fd, err := m.fd.value()
|
||||||
|
if err != nil {
|
||||||
|
// Best effort: -1 is the number most likely to be an
|
||||||
|
// invalid file descriptor.
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone creates a duplicate of the Map.
|
||||||
|
//
|
||||||
|
// Closing the duplicate does not affect the original, and vice versa.
|
||||||
|
// Changes made to the map are reflected by both instances however.
|
||||||
|
//
|
||||||
|
// Cloning a nil Map returns nil.
|
||||||
|
func (m *Map) Clone() (*Map, error) {
|
||||||
|
if m == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dup, err := m.fd.dup()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "can't clone map")
|
||||||
|
}
|
||||||
|
|
||||||
|
return newMap(dup, m.name, &m.abi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pin persists the map past the lifetime of the process that created it.
|
||||||
|
//
|
||||||
|
// This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional
|
||||||
|
func (m *Map) Pin(fileName string) error {
|
||||||
|
return bpfPinObject(fileName, m.fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadPinnedMap load a Map from a BPF file.
|
||||||
|
//
|
||||||
|
// The function is not compatible with nested maps.
|
||||||
|
// Use LoadPinnedMapExplicit in these situations.
|
||||||
|
func LoadPinnedMap(fileName string) (*Map, error) {
|
||||||
|
fd, err := bpfGetObject(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
name, abi, err := newMapABIFromFd(fd)
|
||||||
|
if err != nil {
|
||||||
|
_ = fd.close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newMap(fd, name, abi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadPinnedMapExplicit loads a map with explicit parameters.
|
||||||
|
func LoadPinnedMapExplicit(fileName string, abi *MapABI) (*Map, error) {
|
||||||
|
fd, err := bpfGetObject(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return newMap(fd, "", abi)
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalMap(buf []byte) (*Map, error) {
|
||||||
|
if len(buf) != 4 {
|
||||||
|
return nil, errors.New("map id requires 4 byte value")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looking up an entry in a nested map or prog array returns an id,
|
||||||
|
// not an fd.
|
||||||
|
id := internal.NativeEndian.Uint32(buf)
|
||||||
|
fd, err := bpfGetMapFDByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
name, abi, err := newMapABIFromFd(fd)
|
||||||
|
if err != nil {
|
||||||
|
_ = fd.close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newMap(fd, name, abi)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements BinaryMarshaler.
|
||||||
|
func (m *Map) MarshalBinary() ([]byte, error) {
|
||||||
|
fd, err := m.fd.value()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 4)
|
||||||
|
internal.NativeEndian.PutUint32(buf, fd)
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapIterator iterates a Map.
|
||||||
|
//
|
||||||
|
// See Map.Iterate.
|
||||||
|
type MapIterator struct {
|
||||||
|
target *Map
|
||||||
|
prevKey interface{}
|
||||||
|
prevBytes []byte
|
||||||
|
count, maxEntries uint32
|
||||||
|
done bool
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMapIterator(target *Map) *MapIterator {
|
||||||
|
return &MapIterator{
|
||||||
|
target: target,
|
||||||
|
maxEntries: target.abi.MaxEntries,
|
||||||
|
prevBytes: make([]byte, int(target.abi.KeySize)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var errIterationAborted = errors.New("iteration aborted")
|
||||||
|
|
||||||
|
// Next decodes the next key and value.
|
||||||
|
//
|
||||||
|
// Iterating a hash map from which keys are being deleted is not
|
||||||
|
// safe. You may see the same key multiple times. Iteration may
|
||||||
|
// also abort with an error, see IsIterationAborted.
|
||||||
|
//
|
||||||
|
// Returns false if there are no more entries. You must check
|
||||||
|
// the result of Err afterwards.
|
||||||
|
//
|
||||||
|
// See Map.Get for further caveats around valueOut.
|
||||||
|
func (mi *MapIterator) Next(keyOut, valueOut interface{}) bool {
|
||||||
|
if mi.err != nil || mi.done {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
for ; mi.count < mi.maxEntries; mi.count++ {
|
||||||
|
var nextBytes []byte
|
||||||
|
nextBytes, mi.err = mi.target.NextKeyBytes(mi.prevKey)
|
||||||
|
if mi.err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if nextBytes == nil {
|
||||||
|
mi.done = true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// The user can get access to nextBytes since unmarshalBytes
|
||||||
|
// does not copy when unmarshaling into a []byte.
|
||||||
|
// Make a copy to prevent accidental corruption of
|
||||||
|
// iterator state.
|
||||||
|
copy(mi.prevBytes, nextBytes)
|
||||||
|
mi.prevKey = mi.prevBytes
|
||||||
|
|
||||||
|
mi.err = mi.target.Lookup(nextBytes, valueOut)
|
||||||
|
if IsNotExist(mi.err) {
|
||||||
|
// Even though the key should be valid, we couldn't look up
|
||||||
|
// its value. If we're iterating a hash map this is probably
|
||||||
|
// because a concurrent delete removed the value before we
|
||||||
|
// could get it. This means that the next call to NextKeyBytes
|
||||||
|
// is very likely to restart iteration.
|
||||||
|
// If we're iterating one of the fd maps like
|
||||||
|
// ProgramArray it means that a given slot doesn't have
|
||||||
|
// a valid fd associated. It's OK to continue to the next slot.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if mi.err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
mi.err = unmarshalBytes(keyOut, nextBytes)
|
||||||
|
return mi.err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mi.err = errIterationAborted
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Err returns any encountered error.
|
||||||
|
//
|
||||||
|
// The method must be called after Next returns nil.
|
||||||
|
func (mi *MapIterator) Err() error {
|
||||||
|
return mi.err
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotExist returns true if the error indicates that a
|
||||||
|
// key doesn't exist.
|
||||||
|
func IsNotExist(err error) bool {
|
||||||
|
return errors.Cause(err) == unix.ENOENT
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsIterationAborted returns true if the iteration was aborted.
|
||||||
|
//
|
||||||
|
// This occurs when keys are deleted from a hash map during iteration.
|
||||||
|
func IsIterationAborted(err error) bool {
|
||||||
|
return errors.Cause(err) == errIterationAborted
|
||||||
|
}
|
192
vendor/github.com/cilium/ebpf/marshalers.go
generated
vendored
Normal file
192
vendor/github.com/cilium/ebpf/marshalers.go
generated
vendored
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding"
|
||||||
|
"encoding/binary"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/cilium/ebpf/internal"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
func marshalPtr(data interface{}, length int) (syscallPtr, error) {
|
||||||
|
if ptr, ok := data.(unsafe.Pointer); ok {
|
||||||
|
return newPtr(ptr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf, err := marshalBytes(data, length)
|
||||||
|
if err != nil {
|
||||||
|
return syscallPtr{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPtr(unsafe.Pointer(&buf[0])), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func marshalBytes(data interface{}, length int) (buf []byte, err error) {
|
||||||
|
switch value := data.(type) {
|
||||||
|
case encoding.BinaryMarshaler:
|
||||||
|
buf, err = value.MarshalBinary()
|
||||||
|
case string:
|
||||||
|
buf = []byte(value)
|
||||||
|
case []byte:
|
||||||
|
buf = value
|
||||||
|
case unsafe.Pointer:
|
||||||
|
err = errors.New("can't marshal from unsafe.Pointer")
|
||||||
|
default:
|
||||||
|
var wr bytes.Buffer
|
||||||
|
err = binary.Write(&wr, internal.NativeEndian, value)
|
||||||
|
err = errors.Wrapf(err, "encoding %T", value)
|
||||||
|
buf = wr.Bytes()
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(buf) != length {
|
||||||
|
return nil, errors.Errorf("%T doesn't marshal to %d bytes", data, length)
|
||||||
|
}
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeBuffer(dst interface{}, length int) (syscallPtr, []byte) {
|
||||||
|
if ptr, ok := dst.(unsafe.Pointer); ok {
|
||||||
|
return newPtr(ptr), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, length)
|
||||||
|
return newPtr(unsafe.Pointer(&buf[0])), buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalBytes(data interface{}, buf []byte) error {
|
||||||
|
switch value := data.(type) {
|
||||||
|
case unsafe.Pointer:
|
||||||
|
sh := &reflect.SliceHeader{
|
||||||
|
Data: uintptr(value),
|
||||||
|
Len: len(buf),
|
||||||
|
Cap: len(buf),
|
||||||
|
}
|
||||||
|
|
||||||
|
dst := *(*[]byte)(unsafe.Pointer(sh))
|
||||||
|
copy(dst, buf)
|
||||||
|
runtime.KeepAlive(value)
|
||||||
|
return nil
|
||||||
|
case encoding.BinaryUnmarshaler:
|
||||||
|
return value.UnmarshalBinary(buf)
|
||||||
|
case *string:
|
||||||
|
*value = string(buf)
|
||||||
|
return nil
|
||||||
|
case *[]byte:
|
||||||
|
*value = buf
|
||||||
|
return nil
|
||||||
|
case string:
|
||||||
|
return errors.New("require pointer to string")
|
||||||
|
case []byte:
|
||||||
|
return errors.New("require pointer to []byte")
|
||||||
|
default:
|
||||||
|
rd := bytes.NewReader(buf)
|
||||||
|
err := binary.Read(rd, internal.NativeEndian, value)
|
||||||
|
return errors.Wrapf(err, "decoding %T", value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshalPerCPUValue encodes a slice containing one value per
|
||||||
|
// possible CPU into a buffer of bytes.
|
||||||
|
//
|
||||||
|
// Values are initialized to zero if the slice has less elements than CPUs.
|
||||||
|
//
|
||||||
|
// slice must have a type like []elementType.
|
||||||
|
func marshalPerCPUValue(slice interface{}, elemLength int) (syscallPtr, error) {
|
||||||
|
sliceType := reflect.TypeOf(slice)
|
||||||
|
if sliceType.Kind() != reflect.Slice {
|
||||||
|
return syscallPtr{}, errors.New("per-CPU value requires slice")
|
||||||
|
}
|
||||||
|
|
||||||
|
possibleCPUs, err := internal.PossibleCPUs()
|
||||||
|
if err != nil {
|
||||||
|
return syscallPtr{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
sliceValue := reflect.ValueOf(slice)
|
||||||
|
sliceLen := sliceValue.Len()
|
||||||
|
if sliceLen > possibleCPUs {
|
||||||
|
return syscallPtr{}, errors.Errorf("per-CPU value exceeds number of CPUs")
|
||||||
|
}
|
||||||
|
|
||||||
|
alignedElemLength := align(elemLength, 8)
|
||||||
|
buf := make([]byte, alignedElemLength*possibleCPUs)
|
||||||
|
|
||||||
|
for i := 0; i < sliceLen; i++ {
|
||||||
|
elem := sliceValue.Index(i).Interface()
|
||||||
|
elemBytes, err := marshalBytes(elem, elemLength)
|
||||||
|
if err != nil {
|
||||||
|
return syscallPtr{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := i * alignedElemLength
|
||||||
|
copy(buf[offset:offset+elemLength], elemBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newPtr(unsafe.Pointer(&buf[0])), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmarshalPerCPUValue decodes a buffer into a slice containing one value per
|
||||||
|
// possible CPU.
|
||||||
|
//
|
||||||
|
// valueOut must have a type like *[]elementType
|
||||||
|
func unmarshalPerCPUValue(slicePtr interface{}, elemLength int, buf []byte) error {
|
||||||
|
slicePtrType := reflect.TypeOf(slicePtr)
|
||||||
|
if slicePtrType.Kind() != reflect.Ptr || slicePtrType.Elem().Kind() != reflect.Slice {
|
||||||
|
return errors.Errorf("per-cpu value requires pointer to slice")
|
||||||
|
}
|
||||||
|
|
||||||
|
possibleCPUs, err := internal.PossibleCPUs()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
sliceType := slicePtrType.Elem()
|
||||||
|
slice := reflect.MakeSlice(sliceType, possibleCPUs, possibleCPUs)
|
||||||
|
|
||||||
|
sliceElemType := sliceType.Elem()
|
||||||
|
sliceElemIsPointer := sliceElemType.Kind() == reflect.Ptr
|
||||||
|
if sliceElemIsPointer {
|
||||||
|
sliceElemType = sliceElemType.Elem()
|
||||||
|
}
|
||||||
|
|
||||||
|
step := len(buf) / possibleCPUs
|
||||||
|
if step < elemLength {
|
||||||
|
return errors.Errorf("per-cpu element length is larger than available data")
|
||||||
|
}
|
||||||
|
for i := 0; i < possibleCPUs; i++ {
|
||||||
|
var elem interface{}
|
||||||
|
if sliceElemIsPointer {
|
||||||
|
newElem := reflect.New(sliceElemType)
|
||||||
|
slice.Index(i).Set(newElem)
|
||||||
|
elem = newElem.Interface()
|
||||||
|
} else {
|
||||||
|
elem = slice.Index(i).Addr().Interface()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make a copy, since unmarshal can hold on to itemBytes
|
||||||
|
elemBytes := make([]byte, elemLength)
|
||||||
|
copy(elemBytes, buf[:elemLength])
|
||||||
|
|
||||||
|
err := unmarshalBytes(elem, elemBytes)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrapf(err, "cpu %d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = buf[step:]
|
||||||
|
}
|
||||||
|
|
||||||
|
reflect.ValueOf(slicePtr).Elem().Set(slice)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func align(n, alignment int) int {
|
||||||
|
return (int(n) + alignment - 1) / alignment * alignment
|
||||||
|
}
|
504
vendor/github.com/cilium/ebpf/prog.go
generated
vendored
Normal file
504
vendor/github.com/cilium/ebpf/prog.go
generated
vendored
Normal file
@ -0,0 +1,504 @@
|
|||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/cilium/ebpf/asm"
|
||||||
|
"github.com/cilium/ebpf/internal"
|
||||||
|
"github.com/cilium/ebpf/internal/unix"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Number of bytes to pad the output buffer for BPF_PROG_TEST_RUN.
|
||||||
|
// This is currently the maximum of spare space allocated for SKB
|
||||||
|
// and XDP programs, and equal to XDP_PACKET_HEADROOM + NET_IP_ALIGN.
|
||||||
|
outputPad = 256 + 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// DefaultVerifierLogSize is the default number of bytes allocated for the
|
||||||
|
// verifier log.
|
||||||
|
const DefaultVerifierLogSize = 64 * 1024
|
||||||
|
|
||||||
|
// ProgramOptions control loading a program into the kernel.
|
||||||
|
type ProgramOptions struct {
|
||||||
|
// Controls the detail emitted by the kernel verifier. Set to non-zero
|
||||||
|
// to enable logging.
|
||||||
|
LogLevel uint32
|
||||||
|
// Controls the output buffer size for the verifier. Defaults to
|
||||||
|
// DefaultVerifierLogSize.
|
||||||
|
LogSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProgramSpec defines a Program
|
||||||
|
type ProgramSpec struct {
|
||||||
|
// Name is passed to the kernel as a debug aid. Must only contain
|
||||||
|
// alpha numeric and '_' characters.
|
||||||
|
Name string
|
||||||
|
Type ProgramType
|
||||||
|
AttachType AttachType
|
||||||
|
Instructions asm.Instructions
|
||||||
|
License string
|
||||||
|
KernelVersion uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy returns a copy of the spec.
|
||||||
|
func (ps *ProgramSpec) Copy() *ProgramSpec {
|
||||||
|
if ps == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cpy := *ps
|
||||||
|
cpy.Instructions = make(asm.Instructions, len(ps.Instructions))
|
||||||
|
copy(cpy.Instructions, ps.Instructions)
|
||||||
|
return &cpy
|
||||||
|
}
|
||||||
|
|
||||||
|
// Program represents BPF program loaded into the kernel.
|
||||||
|
//
|
||||||
|
// It is not safe to close a Program which is used by other goroutines.
|
||||||
|
type Program struct {
|
||||||
|
// Contains the output of the kernel verifier if enabled,
|
||||||
|
// otherwise it is empty.
|
||||||
|
VerifierLog string
|
||||||
|
|
||||||
|
fd *bpfFD
|
||||||
|
name string
|
||||||
|
abi ProgramABI
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProgram creates a new Program.
|
||||||
|
//
|
||||||
|
// Loading a program for the first time will perform
|
||||||
|
// feature detection by loading small, temporary programs.
|
||||||
|
func NewProgram(spec *ProgramSpec) (*Program, error) {
|
||||||
|
return NewProgramWithOptions(spec, ProgramOptions{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProgramWithOptions creates a new Program.
|
||||||
|
//
|
||||||
|
// Loading a program for the first time will perform
|
||||||
|
// feature detection by loading small, temporary programs.
|
||||||
|
func NewProgramWithOptions(spec *ProgramSpec, opts ProgramOptions) (*Program, error) {
|
||||||
|
attr, err := convertProgramSpec(spec)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
logSize := DefaultVerifierLogSize
|
||||||
|
if opts.LogSize > 0 {
|
||||||
|
logSize = opts.LogSize
|
||||||
|
}
|
||||||
|
|
||||||
|
var logBuf []byte
|
||||||
|
if opts.LogLevel > 0 {
|
||||||
|
logBuf = make([]byte, logSize)
|
||||||
|
attr.logLevel = opts.LogLevel
|
||||||
|
attr.logSize = uint32(len(logBuf))
|
||||||
|
attr.logBuf = newPtr(unsafe.Pointer(&logBuf[0]))
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := bpfProgLoad(attr)
|
||||||
|
if err == nil {
|
||||||
|
prog := newProgram(fd, spec.Name, &ProgramABI{spec.Type})
|
||||||
|
prog.VerifierLog = convertCString(logBuf)
|
||||||
|
return prog, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
truncated := errors.Cause(err) == unix.ENOSPC
|
||||||
|
if opts.LogLevel == 0 {
|
||||||
|
// Re-run with the verifier enabled to get better error messages.
|
||||||
|
logBuf = make([]byte, logSize)
|
||||||
|
attr.logLevel = 1
|
||||||
|
attr.logSize = uint32(len(logBuf))
|
||||||
|
attr.logBuf = newPtr(unsafe.Pointer(&logBuf[0]))
|
||||||
|
|
||||||
|
_, nerr := bpfProgLoad(attr)
|
||||||
|
truncated = errors.Cause(nerr) == unix.ENOSPC
|
||||||
|
}
|
||||||
|
|
||||||
|
logs := convertCString(logBuf)
|
||||||
|
if truncated {
|
||||||
|
logs += "\n(truncated...)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, &loadError{err, logs}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewProgramFromFD creates a program from a raw fd.
|
||||||
|
//
|
||||||
|
// You should not use fd after calling this function.
|
||||||
|
//
|
||||||
|
// Requires at least Linux 4.11.
|
||||||
|
func NewProgramFromFD(fd int) (*Program, error) {
|
||||||
|
if fd < 0 {
|
||||||
|
return nil, errors.New("invalid fd")
|
||||||
|
}
|
||||||
|
bpfFd := newBPFFD(uint32(fd))
|
||||||
|
|
||||||
|
name, abi, err := newProgramABIFromFd(bpfFd)
|
||||||
|
if err != nil {
|
||||||
|
bpfFd.forget()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newProgram(bpfFd, name, abi), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newProgram(fd *bpfFD, name string, abi *ProgramABI) *Program {
|
||||||
|
return &Program{
|
||||||
|
name: name,
|
||||||
|
fd: fd,
|
||||||
|
abi: *abi,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertProgramSpec(spec *ProgramSpec) (*bpfProgLoadAttr, error) {
|
||||||
|
if len(spec.Instructions) == 0 {
|
||||||
|
return nil, errors.New("Instructions cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(spec.License) == 0 {
|
||||||
|
return nil, errors.New("License cannot be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := bytes.NewBuffer(make([]byte, 0, len(spec.Instructions)*asm.InstructionSize))
|
||||||
|
err := spec.Instructions.Marshal(buf, internal.NativeEndian)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bytecode := buf.Bytes()
|
||||||
|
insCount := uint32(len(bytecode) / asm.InstructionSize)
|
||||||
|
lic := []byte(spec.License)
|
||||||
|
attr := &bpfProgLoadAttr{
|
||||||
|
progType: spec.Type,
|
||||||
|
expectedAttachType: spec.AttachType,
|
||||||
|
insCount: insCount,
|
||||||
|
instructions: newPtr(unsafe.Pointer(&bytecode[0])),
|
||||||
|
license: newPtr(unsafe.Pointer(&lic[0])),
|
||||||
|
}
|
||||||
|
|
||||||
|
name, err := newBPFObjName(spec.Name)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if haveObjName() == nil {
|
||||||
|
attr.progName = name
|
||||||
|
}
|
||||||
|
|
||||||
|
return attr, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Program) String() string {
|
||||||
|
if p.name != "" {
|
||||||
|
return fmt.Sprintf("%s(%s)#%v", p.abi.Type, p.name, p.fd)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s#%v", p.abi.Type, p.fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ABI gets the ABI of the Program
|
||||||
|
func (p *Program) ABI() ProgramABI {
|
||||||
|
return p.abi
|
||||||
|
}
|
||||||
|
|
||||||
|
// FD gets the file descriptor of the Program.
|
||||||
|
//
|
||||||
|
// It is invalid to call this function after Close has been called.
|
||||||
|
func (p *Program) FD() int {
|
||||||
|
fd, err := p.fd.value()
|
||||||
|
if err != nil {
|
||||||
|
// Best effort: -1 is the number most likely to be an
|
||||||
|
// invalid file descriptor.
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
return int(fd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clone creates a duplicate of the Program.
|
||||||
|
//
|
||||||
|
// Closing the duplicate does not affect the original, and vice versa.
|
||||||
|
//
|
||||||
|
// Cloning a nil Program returns nil.
|
||||||
|
func (p *Program) Clone() (*Program, error) {
|
||||||
|
if p == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
dup, err := p.fd.dup()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "can't clone program")
|
||||||
|
}
|
||||||
|
|
||||||
|
return newProgram(dup, p.name, &p.abi), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pin persists the Program past the lifetime of the process that created it
|
||||||
|
//
|
||||||
|
// This requires bpffs to be mounted above fileName. See http://cilium.readthedocs.io/en/doc-1.0/kubernetes/install/#mounting-the-bpf-fs-optional
|
||||||
|
func (p *Program) Pin(fileName string) error {
|
||||||
|
return errors.Wrap(bpfPinObject(fileName, p.fd), "can't pin program")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close unloads the program from the kernel.
|
||||||
|
func (p *Program) Close() error {
|
||||||
|
if p == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return p.fd.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test runs the Program in the kernel with the given input and returns the
|
||||||
|
// value returned by the eBPF program. outLen may be zero.
|
||||||
|
//
|
||||||
|
// Note: the kernel expects at least 14 bytes input for an ethernet header for
|
||||||
|
// XDP and SKB programs.
|
||||||
|
//
|
||||||
|
// This function requires at least Linux 4.12.
|
||||||
|
func (p *Program) Test(in []byte) (uint32, []byte, error) {
|
||||||
|
ret, out, _, err := p.testRun(in, 1)
|
||||||
|
return ret, out, errors.Wrap(err, "can't test program")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Benchmark runs the Program with the given input for a number of times
|
||||||
|
// and returns the time taken per iteration.
|
||||||
|
//
|
||||||
|
// The returned value is the return value of the last execution of
|
||||||
|
// the program.
|
||||||
|
//
|
||||||
|
// This function requires at least Linux 4.12.
|
||||||
|
func (p *Program) Benchmark(in []byte, repeat int) (uint32, time.Duration, error) {
|
||||||
|
ret, _, total, err := p.testRun(in, repeat)
|
||||||
|
return ret, total, errors.Wrap(err, "can't benchmark program")
|
||||||
|
}
|
||||||
|
|
||||||
|
var haveProgTestRun = internal.FeatureTest("BPF_PROG_TEST_RUN", "4.12", func() bool {
|
||||||
|
prog, err := NewProgram(&ProgramSpec{
|
||||||
|
Type: SocketFilter,
|
||||||
|
Instructions: asm.Instructions{
|
||||||
|
asm.LoadImm(asm.R0, 0, asm.DWord),
|
||||||
|
asm.Return(),
|
||||||
|
},
|
||||||
|
License: "MIT",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
// This may be because we lack sufficient permissions, etc.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer prog.Close()
|
||||||
|
|
||||||
|
fd, err := prog.fd.value()
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Programs require at least 14 bytes input
|
||||||
|
in := make([]byte, 14)
|
||||||
|
attr := bpfProgTestRunAttr{
|
||||||
|
fd: fd,
|
||||||
|
dataSizeIn: uint32(len(in)),
|
||||||
|
dataIn: newPtr(unsafe.Pointer(&in[0])),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = bpfCall(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
||||||
|
|
||||||
|
// Check for EINVAL specifically, rather than err != nil since we
|
||||||
|
// otherwise misdetect due to insufficient permissions.
|
||||||
|
return errors.Cause(err) != unix.EINVAL
|
||||||
|
})
|
||||||
|
|
||||||
|
func (p *Program) testRun(in []byte, repeat int) (uint32, []byte, time.Duration, error) {
|
||||||
|
if uint(repeat) > math.MaxUint32 {
|
||||||
|
return 0, nil, 0, fmt.Errorf("repeat is too high")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(in) == 0 {
|
||||||
|
return 0, nil, 0, fmt.Errorf("missing input")
|
||||||
|
}
|
||||||
|
|
||||||
|
if uint(len(in)) > math.MaxUint32 {
|
||||||
|
return 0, nil, 0, fmt.Errorf("input is too long")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := haveProgTestRun(); err != nil {
|
||||||
|
return 0, nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Older kernels ignore the dataSizeOut argument when copying to user space.
|
||||||
|
// Combined with things like bpf_xdp_adjust_head() we don't really know what the final
|
||||||
|
// size will be. Hence we allocate an output buffer which we hope will always be large
|
||||||
|
// enough, and panic if the kernel wrote past the end of the allocation.
|
||||||
|
// See https://patchwork.ozlabs.org/cover/1006822/
|
||||||
|
out := make([]byte, len(in)+outputPad)
|
||||||
|
|
||||||
|
fd, err := p.fd.value()
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
attr := bpfProgTestRunAttr{
|
||||||
|
fd: fd,
|
||||||
|
dataSizeIn: uint32(len(in)),
|
||||||
|
dataSizeOut: uint32(len(out)),
|
||||||
|
dataIn: newPtr(unsafe.Pointer(&in[0])),
|
||||||
|
dataOut: newPtr(unsafe.Pointer(&out[0])),
|
||||||
|
repeat: uint32(repeat),
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = bpfCall(_ProgTestRun, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, 0, errors.Wrap(err, "can't run test")
|
||||||
|
}
|
||||||
|
|
||||||
|
if int(attr.dataSizeOut) > cap(out) {
|
||||||
|
// Houston, we have a problem. The program created more data than we allocated,
|
||||||
|
// and the kernel wrote past the end of our buffer.
|
||||||
|
panic("kernel wrote past end of output buffer")
|
||||||
|
}
|
||||||
|
out = out[:int(attr.dataSizeOut)]
|
||||||
|
|
||||||
|
total := time.Duration(attr.duration) * time.Nanosecond
|
||||||
|
return attr.retval, out, total, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func unmarshalProgram(buf []byte) (*Program, error) {
|
||||||
|
if len(buf) != 4 {
|
||||||
|
return nil, errors.New("program id requires 4 byte value")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Looking up an entry in a nested map or prog array returns an id,
|
||||||
|
// not an fd.
|
||||||
|
id := internal.NativeEndian.Uint32(buf)
|
||||||
|
fd, err := bpfGetProgramFDByID(id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
name, abi, err := newProgramABIFromFd(fd)
|
||||||
|
if err != nil {
|
||||||
|
_ = fd.close()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newProgram(fd, name, abi), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements BinaryMarshaler.
|
||||||
|
func (p *Program) MarshalBinary() ([]byte, error) {
|
||||||
|
value, err := p.fd.value()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buf := make([]byte, 4)
|
||||||
|
internal.NativeEndian.PutUint32(buf, value)
|
||||||
|
return buf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attach a Program to a container object fd
|
||||||
|
func (p *Program) Attach(fd int, typ AttachType, flags AttachFlags) error {
|
||||||
|
if fd < 0 {
|
||||||
|
return errors.New("invalid fd")
|
||||||
|
}
|
||||||
|
|
||||||
|
pfd, err := p.fd.value()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
attr := bpfProgAlterAttr{
|
||||||
|
targetFd: uint32(fd),
|
||||||
|
attachBpfFd: pfd,
|
||||||
|
attachType: uint32(typ),
|
||||||
|
attachFlags: uint32(flags),
|
||||||
|
}
|
||||||
|
|
||||||
|
return bpfProgAlter(_ProgAttach, &attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detach a Program from a container object fd
|
||||||
|
func (p *Program) Detach(fd int, typ AttachType, flags AttachFlags) error {
|
||||||
|
if fd < 0 {
|
||||||
|
return errors.New("invalid fd")
|
||||||
|
}
|
||||||
|
|
||||||
|
pfd, err := p.fd.value()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
attr := bpfProgAlterAttr{
|
||||||
|
targetFd: uint32(fd),
|
||||||
|
attachBpfFd: pfd,
|
||||||
|
attachType: uint32(typ),
|
||||||
|
attachFlags: uint32(flags),
|
||||||
|
}
|
||||||
|
|
||||||
|
return bpfProgAlter(_ProgDetach, &attr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadPinnedProgram loads a Program from a BPF file.
|
||||||
|
//
|
||||||
|
// Requires at least Linux 4.11.
|
||||||
|
func LoadPinnedProgram(fileName string) (*Program, error) {
|
||||||
|
fd, err := bpfGetObject(fileName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
name, abi, err := newProgramABIFromFd(fd)
|
||||||
|
if err != nil {
|
||||||
|
_ = fd.close()
|
||||||
|
return nil, errors.Wrapf(err, "can't get ABI for %s", fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
return newProgram(fd, name, abi), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SanitizeName replaces all invalid characters in name.
|
||||||
|
//
|
||||||
|
// Use this to automatically generate valid names for maps and
|
||||||
|
// programs at run time.
|
||||||
|
//
|
||||||
|
// Passing a negative value for replacement will delete characters
|
||||||
|
// instead of replacing them.
|
||||||
|
func SanitizeName(name string, replacement rune) string {
|
||||||
|
return strings.Map(func(char rune) rune {
|
||||||
|
if invalidBPFObjNameChar(char) {
|
||||||
|
return replacement
|
||||||
|
}
|
||||||
|
return char
|
||||||
|
}, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
type loadError struct {
|
||||||
|
cause error
|
||||||
|
verifierLog string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (le *loadError) Error() string {
|
||||||
|
if le.verifierLog == "" {
|
||||||
|
return fmt.Sprintf("failed to load program: %s", le.cause)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("failed to load program: %s: %s", le.cause, le.verifierLog)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (le *loadError) Cause() error {
|
||||||
|
return le.cause
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotSupported returns true if an error occurred because
|
||||||
|
// the kernel does not have support for a specific feature.
|
||||||
|
func IsNotSupported(err error) bool {
|
||||||
|
_, notSupported := errors.Cause(err).(*internal.UnsupportedFeatureError)
|
||||||
|
return notSupported
|
||||||
|
}
|
14
vendor/github.com/cilium/ebpf/ptr_32_be.go
generated
vendored
Normal file
14
vendor/github.com/cilium/ebpf/ptr_32_be.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// +build armbe mips mips64p32
|
||||||
|
|
||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ptr wraps an unsafe.Pointer to be 64bit to
|
||||||
|
// conform to the syscall specification.
|
||||||
|
type syscallPtr struct {
|
||||||
|
pad uint32
|
||||||
|
ptr unsafe.Pointer
|
||||||
|
}
|
14
vendor/github.com/cilium/ebpf/ptr_32_le.go
generated
vendored
Normal file
14
vendor/github.com/cilium/ebpf/ptr_32_le.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// +build 386 amd64p32 arm mipsle mips64p32le
|
||||||
|
|
||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ptr wraps an unsafe.Pointer to be 64bit to
|
||||||
|
// conform to the syscall specification.
|
||||||
|
type syscallPtr struct {
|
||||||
|
ptr unsafe.Pointer
|
||||||
|
pad uint32
|
||||||
|
}
|
14
vendor/github.com/cilium/ebpf/ptr_64.go
generated
vendored
Normal file
14
vendor/github.com/cilium/ebpf/ptr_64.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// +build !386,!amd64p32,!arm,!mipsle,!mips64p32le
|
||||||
|
// +build !armbe,!mips,!mips64p32
|
||||||
|
|
||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ptr wraps an unsafe.Pointer to be 64bit to
|
||||||
|
// conform to the syscall specification.
|
||||||
|
type syscallPtr struct {
|
||||||
|
ptr unsafe.Pointer
|
||||||
|
}
|
20
vendor/github.com/cilium/ebpf/readme.md
generated
vendored
Normal file
20
vendor/github.com/cilium/ebpf/readme.md
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
eBPF
|
||||||
|
-------
|
||||||
|
[](https://godoc.org/github.com/cilium/ebpf)
|
||||||
|
|
||||||
|
eBPF is a pure Go library that provides utilities for loading, compiling, and debugging eBPF programs. It has minimal external dependencies and is intended to be used in long running processes.
|
||||||
|
|
||||||
|
[ebpf/asm](https://godoc.org/github.com/cilium/ebpf/asm) contains a basic assembler.
|
||||||
|
|
||||||
|
The library is maintained by [Cloudflare](https://www.cloudflare.com) and [Cilium](https://www.cilium.io). Feel free to [join](https://cilium.herokuapp.com/) the [libbpf-go](https://cilium.slack.com/messages/libbpf-go) channel on Slack.
|
||||||
|
|
||||||
|
## Current status
|
||||||
|
|
||||||
|
The package is production ready, but **the API is explicitly unstable
|
||||||
|
right now**. Expect to update your code if you want to follow along.
|
||||||
|
|
||||||
|
## Useful resources
|
||||||
|
|
||||||
|
* [Cilium eBPF documentation](https://cilium.readthedocs.io/en/latest/bpf/#bpf-guide) (recommended)
|
||||||
|
* [Linux documentation on BPF](http://elixir.free-electrons.com/linux/latest/source/Documentation/networking/filter.txt)
|
||||||
|
* [eBPF features by Linux version](https://github.com/iovisor/bcc/blob/master/docs/kernel-versions.md)
|
447
vendor/github.com/cilium/ebpf/syscalls.go
generated
vendored
Normal file
447
vendor/github.com/cilium/ebpf/syscalls.go
generated
vendored
Normal file
@ -0,0 +1,447 @@
|
|||||||
|
package ebpf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"unsafe"
|
||||||
|
|
||||||
|
"github.com/cilium/ebpf/internal"
|
||||||
|
"github.com/cilium/ebpf/internal/unix"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var errClosedFd = errors.New("use of closed file descriptor")
|
||||||
|
|
||||||
|
type bpfFD struct {
|
||||||
|
raw int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBPFFD(value uint32) *bpfFD {
|
||||||
|
fd := &bpfFD{int64(value)}
|
||||||
|
runtime.SetFinalizer(fd, (*bpfFD).close)
|
||||||
|
return fd
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fd *bpfFD) String() string {
|
||||||
|
return strconv.FormatInt(fd.raw, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fd *bpfFD) value() (uint32, error) {
|
||||||
|
if fd.raw < 0 {
|
||||||
|
return 0, errClosedFd
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint32(fd.raw), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fd *bpfFD) close() error {
|
||||||
|
if fd.raw < 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
value := int(fd.raw)
|
||||||
|
fd.raw = -1
|
||||||
|
|
||||||
|
fd.forget()
|
||||||
|
return unix.Close(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fd *bpfFD) forget() {
|
||||||
|
runtime.SetFinalizer(fd, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fd *bpfFD) dup() (*bpfFD, error) {
|
||||||
|
if fd.raw < 0 {
|
||||||
|
return nil, errClosedFd
|
||||||
|
}
|
||||||
|
|
||||||
|
dup, err := unix.FcntlInt(uintptr(fd.raw), unix.F_DUPFD_CLOEXEC, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "can't dup fd")
|
||||||
|
}
|
||||||
|
|
||||||
|
return newBPFFD(uint32(dup)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// bpfObjName is a null-terminated string made up of
|
||||||
|
// 'A-Za-z0-9_' characters.
|
||||||
|
type bpfObjName [unix.BPF_OBJ_NAME_LEN]byte
|
||||||
|
|
||||||
|
// newBPFObjName truncates the result if it is too long.
|
||||||
|
func newBPFObjName(name string) (bpfObjName, error) {
|
||||||
|
idx := strings.IndexFunc(name, invalidBPFObjNameChar)
|
||||||
|
if idx != -1 {
|
||||||
|
return bpfObjName{}, errors.Errorf("invalid character '%c' in name '%s'", name[idx], name)
|
||||||
|
}
|
||||||
|
|
||||||
|
var result bpfObjName
|
||||||
|
copy(result[:unix.BPF_OBJ_NAME_LEN-1], name)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func invalidBPFObjNameChar(char rune) bool {
|
||||||
|
switch {
|
||||||
|
case char >= 'A' && char <= 'Z':
|
||||||
|
fallthrough
|
||||||
|
case char >= 'a' && char <= 'z':
|
||||||
|
fallthrough
|
||||||
|
case char >= '0' && char <= '9':
|
||||||
|
fallthrough
|
||||||
|
case char == '_':
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type bpfMapCreateAttr struct {
|
||||||
|
mapType MapType
|
||||||
|
keySize uint32
|
||||||
|
valueSize uint32
|
||||||
|
maxEntries uint32
|
||||||
|
flags uint32
|
||||||
|
innerMapFd uint32 // since 4.12 56f668dfe00d
|
||||||
|
numaNode uint32 // since 4.14 96eabe7a40aa
|
||||||
|
mapName bpfObjName // since 4.15 ad5b177bd73f
|
||||||
|
}
|
||||||
|
|
||||||
|
type bpfMapOpAttr struct {
|
||||||
|
mapFd uint32
|
||||||
|
padding uint32
|
||||||
|
key syscallPtr
|
||||||
|
value syscallPtr
|
||||||
|
flags uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
type bpfMapInfo struct {
|
||||||
|
mapType uint32
|
||||||
|
id uint32
|
||||||
|
keySize uint32
|
||||||
|
valueSize uint32
|
||||||
|
maxEntries uint32
|
||||||
|
flags uint32
|
||||||
|
mapName bpfObjName // since 4.15 ad5b177bd73f
|
||||||
|
}
|
||||||
|
|
||||||
|
type bpfPinObjAttr struct {
|
||||||
|
fileName syscallPtr
|
||||||
|
fd uint32
|
||||||
|
padding uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type bpfProgLoadAttr struct {
|
||||||
|
progType ProgramType
|
||||||
|
insCount uint32
|
||||||
|
instructions syscallPtr
|
||||||
|
license syscallPtr
|
||||||
|
logLevel uint32
|
||||||
|
logSize uint32
|
||||||
|
logBuf syscallPtr
|
||||||
|
kernelVersion uint32 // since 4.1 2541517c32be
|
||||||
|
progFlags uint32 // since 4.11 e07b98d9bffe
|
||||||
|
progName bpfObjName // since 4.15 067cae47771c
|
||||||
|
progIfIndex uint32 // since 4.15 1f6f4cb7ba21
|
||||||
|
expectedAttachType AttachType // since 4.17 5e43f899b03a
|
||||||
|
}
|
||||||
|
|
||||||
|
type bpfProgInfo struct {
|
||||||
|
progType uint32
|
||||||
|
id uint32
|
||||||
|
tag [unix.BPF_TAG_SIZE]byte
|
||||||
|
jitedLen uint32
|
||||||
|
xlatedLen uint32
|
||||||
|
jited syscallPtr
|
||||||
|
xlated syscallPtr
|
||||||
|
loadTime uint64 // since 4.15 cb4d2b3f03d8
|
||||||
|
createdByUID uint32
|
||||||
|
nrMapIDs uint32
|
||||||
|
mapIds syscallPtr
|
||||||
|
name bpfObjName
|
||||||
|
}
|
||||||
|
|
||||||
|
type bpfProgTestRunAttr struct {
|
||||||
|
fd uint32
|
||||||
|
retval uint32
|
||||||
|
dataSizeIn uint32
|
||||||
|
dataSizeOut uint32
|
||||||
|
dataIn syscallPtr
|
||||||
|
dataOut syscallPtr
|
||||||
|
repeat uint32
|
||||||
|
duration uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type bpfProgAlterAttr struct {
|
||||||
|
targetFd uint32
|
||||||
|
attachBpfFd uint32
|
||||||
|
attachType uint32
|
||||||
|
attachFlags uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
type bpfObjGetInfoByFDAttr struct {
|
||||||
|
fd uint32
|
||||||
|
infoLen uint32
|
||||||
|
info syscallPtr // May be either bpfMapInfo or bpfProgInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type bpfGetFDByIDAttr struct {
|
||||||
|
id uint32
|
||||||
|
next uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func newPtr(ptr unsafe.Pointer) syscallPtr {
|
||||||
|
return syscallPtr{ptr: ptr}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bpfProgLoad(attr *bpfProgLoadAttr) (*bpfFD, error) {
|
||||||
|
for {
|
||||||
|
fd, err := bpfCall(_ProgLoad, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
|
||||||
|
// As of ~4.20 the verifier can be interrupted by a signal,
|
||||||
|
// and returns EAGAIN in that case.
|
||||||
|
if err == unix.EAGAIN {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newBPFFD(uint32(fd)), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func bpfProgAlter(cmd int, attr *bpfProgAlterAttr) error {
|
||||||
|
_, err := bpfCall(cmd, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func bpfMapCreate(attr *bpfMapCreateAttr) (*bpfFD, error) {
|
||||||
|
fd, err := bpfCall(_MapCreate, unsafe.Pointer(attr), unsafe.Sizeof(*attr))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return newBPFFD(uint32(fd)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var haveNestedMaps = internal.FeatureTest("nested maps", "4.12", func() bool {
|
||||||
|
inner, err := bpfMapCreate(&bpfMapCreateAttr{
|
||||||
|
mapType: Array,
|
||||||
|
keySize: 4,
|
||||||
|
valueSize: 4,
|
||||||
|
maxEntries: 1,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
defer inner.close()
|
||||||
|
|
||||||
|
innerFd, _ := inner.value()
|
||||||
|
nested, err := bpfMapCreate(&bpfMapCreateAttr{
|
||||||
|
mapType: ArrayOfMaps,
|
||||||
|
keySize: 4,
|
||||||
|
valueSize: 4,
|
||||||
|
maxEntries: 1,
|
||||||
|
innerMapFd: innerFd,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = nested.close()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
func bpfMapLookupElem(m *bpfFD, key, valueOut syscallPtr) error {
|
||||||
|
fd, err := m.value()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
attr := bpfMapOpAttr{
|
||||||
|
mapFd: fd,
|
||||||
|
key: key,
|
||||||
|
value: valueOut,
|
||||||
|
}
|
||||||
|
_, err = bpfCall(_MapLookupElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func bpfMapUpdateElem(m *bpfFD, key, valueOut syscallPtr, flags uint64) error {
|
||||||
|
fd, err := m.value()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
attr := bpfMapOpAttr{
|
||||||
|
mapFd: fd,
|
||||||
|
key: key,
|
||||||
|
value: valueOut,
|
||||||
|
flags: flags,
|
||||||
|
}
|
||||||
|
_, err = bpfCall(_MapUpdateElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func bpfMapDeleteElem(m *bpfFD, key syscallPtr) error {
|
||||||
|
fd, err := m.value()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
attr := bpfMapOpAttr{
|
||||||
|
mapFd: fd,
|
||||||
|
key: key,
|
||||||
|
}
|
||||||
|
_, err = bpfCall(_MapDeleteElem, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func bpfMapGetNextKey(m *bpfFD, key, nextKeyOut syscallPtr) error {
|
||||||
|
fd, err := m.value()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
attr := bpfMapOpAttr{
|
||||||
|
mapFd: fd,
|
||||||
|
key: key,
|
||||||
|
value: nextKeyOut,
|
||||||
|
}
|
||||||
|
_, err = bpfCall(_MapGetNextKey, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
const bpfFSType = 0xcafe4a11
|
||||||
|
|
||||||
|
func bpfPinObject(fileName string, fd *bpfFD) error {
|
||||||
|
dirName := filepath.Dir(fileName)
|
||||||
|
var statfs unix.Statfs_t
|
||||||
|
if err := unix.Statfs(dirName, &statfs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if uint64(statfs.Type) != bpfFSType {
|
||||||
|
return errors.Errorf("%s is not on a bpf filesystem", fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := fd.value()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = bpfCall(_ObjPin, unsafe.Pointer(&bpfPinObjAttr{
|
||||||
|
fileName: newPtr(unsafe.Pointer(&[]byte(fileName)[0])),
|
||||||
|
fd: value,
|
||||||
|
}), 16)
|
||||||
|
return errors.Wrapf(err, "pin object %s", fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
func bpfGetObject(fileName string) (*bpfFD, error) {
|
||||||
|
ptr, err := bpfCall(_ObjGet, unsafe.Pointer(&bpfPinObjAttr{
|
||||||
|
fileName: newPtr(unsafe.Pointer(&[]byte(fileName)[0])),
|
||||||
|
}), 16)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "get object %s", fileName)
|
||||||
|
}
|
||||||
|
return newBPFFD(uint32(ptr)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func bpfGetObjectInfoByFD(fd *bpfFD, info unsafe.Pointer, size uintptr) error {
|
||||||
|
value, err := fd.value()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// available from 4.13
|
||||||
|
attr := bpfObjGetInfoByFDAttr{
|
||||||
|
fd: value,
|
||||||
|
infoLen: uint32(size),
|
||||||
|
info: newPtr(info),
|
||||||
|
}
|
||||||
|
_, err = bpfCall(_ObjGetInfoByFD, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
||||||
|
return errors.Wrapf(err, "fd %d", value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func bpfGetProgInfoByFD(fd *bpfFD) (*bpfProgInfo, error) {
|
||||||
|
var info bpfProgInfo
|
||||||
|
err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info))
|
||||||
|
return &info, errors.Wrap(err, "can't get program info")
|
||||||
|
}
|
||||||
|
|
||||||
|
func bpfGetMapInfoByFD(fd *bpfFD) (*bpfMapInfo, error) {
|
||||||
|
var info bpfMapInfo
|
||||||
|
err := bpfGetObjectInfoByFD(fd, unsafe.Pointer(&info), unsafe.Sizeof(info))
|
||||||
|
return &info, errors.Wrap(err, "can't get map info")
|
||||||
|
}
|
||||||
|
|
||||||
|
var haveObjName = internal.FeatureTest("object names", "4.15", func() bool {
|
||||||
|
name, err := newBPFObjName("feature_test")
|
||||||
|
if err != nil {
|
||||||
|
// This really is a fatal error, but it should be caught
|
||||||
|
// by the unit tests not working.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
attr := bpfMapCreateAttr{
|
||||||
|
mapType: Array,
|
||||||
|
keySize: 4,
|
||||||
|
valueSize: 4,
|
||||||
|
maxEntries: 1,
|
||||||
|
mapName: name,
|
||||||
|
}
|
||||||
|
|
||||||
|
fd, err := bpfMapCreate(&attr)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = fd.close()
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
|
||||||
|
func bpfGetMapFDByID(id uint32) (*bpfFD, error) {
|
||||||
|
// available from 4.13
|
||||||
|
attr := bpfGetFDByIDAttr{
|
||||||
|
id: id,
|
||||||
|
}
|
||||||
|
ptr, err := bpfCall(_MapGetFDByID, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "can't get fd for map id %d", id)
|
||||||
|
}
|
||||||
|
return newBPFFD(uint32(ptr)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func bpfGetProgramFDByID(id uint32) (*bpfFD, error) {
|
||||||
|
// available from 4.13
|
||||||
|
attr := bpfGetFDByIDAttr{
|
||||||
|
id: id,
|
||||||
|
}
|
||||||
|
ptr, err := bpfCall(_ProgGetFDByID, unsafe.Pointer(&attr), unsafe.Sizeof(attr))
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrapf(err, "can't get fd for program id %d", id)
|
||||||
|
}
|
||||||
|
return newBPFFD(uint32(ptr)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func bpfCall(cmd int, attr unsafe.Pointer, size uintptr) (uintptr, error) {
|
||||||
|
r1, _, errNo := unix.Syscall(unix.SYS_BPF, uintptr(cmd), uintptr(attr), size)
|
||||||
|
runtime.KeepAlive(attr)
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if errNo != 0 {
|
||||||
|
err = errNo
|
||||||
|
}
|
||||||
|
|
||||||
|
return r1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func convertCString(in []byte) string {
|
||||||
|
inLen := bytes.IndexByte(in, 0)
|
||||||
|
if inLen == -1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return string(in[:inLen])
|
||||||
|
}
|
189
vendor/github.com/cilium/ebpf/types.go
generated
vendored
Normal file
189
vendor/github.com/cilium/ebpf/types.go
generated
vendored
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
package ebpf
|
||||||
|
|
||||||
|
//go:generate stringer -output types_string.go -type=MapType,ProgramType
|
||||||
|
|
||||||
|
// MapType indicates the type map structure
|
||||||
|
// that will be initialized in the kernel.
|
||||||
|
type MapType uint32
|
||||||
|
|
||||||
|
// All the various map types that can be created
|
||||||
|
const (
|
||||||
|
UnspecifiedMap MapType = iota
|
||||||
|
// Hash is a hash map
|
||||||
|
Hash
|
||||||
|
// Array is an array map
|
||||||
|
Array
|
||||||
|
// ProgramArray - A program array map is a special kind of array map whose map
|
||||||
|
// values contain only file descriptors referring to other eBPF
|
||||||
|
// programs. Thus, both the key_size and value_size must be
|
||||||
|
// exactly four bytes. This map is used in conjunction with the
|
||||||
|
// TailCall helper.
|
||||||
|
ProgramArray
|
||||||
|
// PerfEventArray - A perf event array is used in conjunction with PerfEventRead
|
||||||
|
// and PerfEventOutput calls, to read the raw bpf_perf_data from the registers.
|
||||||
|
PerfEventArray
|
||||||
|
// PerCPUHash - This data structure is useful for people who have high performance
|
||||||
|
// network needs and can reconcile adds at the end of some cycle, so that
|
||||||
|
// hashes can be lock free without the use of XAdd, which can be costly.
|
||||||
|
PerCPUHash
|
||||||
|
// PerCPUArray - This data structure is useful for people who have high performance
|
||||||
|
// network needs and can reconcile adds at the end of some cycle, so that
|
||||||
|
// hashes can be lock free without the use of XAdd, which can be costly.
|
||||||
|
// Each CPU gets a copy of this hash, the contents of all of which can be reconciled
|
||||||
|
// later.
|
||||||
|
PerCPUArray
|
||||||
|
// StackTrace - This holds whole user and kernel stack traces, it can be retrieved with
|
||||||
|
// GetStackID
|
||||||
|
StackTrace
|
||||||
|
// CGroupArray - This is a very niche structure used to help SKBInCGroup determine
|
||||||
|
// if an skb is from a socket belonging to a specific cgroup
|
||||||
|
CGroupArray
|
||||||
|
// LRUHash - This allows you to create a small hash structure that will purge the
|
||||||
|
// least recently used items rather than thow an error when you run out of memory
|
||||||
|
LRUHash
|
||||||
|
// LRUCPUHash - This is NOT like PerCPUHash, this structure is shared among the CPUs,
|
||||||
|
// it has more to do with including the CPU id with the LRU calculation so that if a
|
||||||
|
// particular CPU is using a value over-and-over again, then it will be saved, but if
|
||||||
|
// a value is being retrieved a lot but sparsely across CPUs it is not as important, basically
|
||||||
|
// giving weight to CPU locality over overall usage.
|
||||||
|
LRUCPUHash
|
||||||
|
// LPMTrie - This is an implementation of Longest-Prefix-Match Trie structure. It is useful,
|
||||||
|
// for storing things like IP addresses which can be bit masked allowing for keys of differing
|
||||||
|
// values to refer to the same reference based on their masks. See wikipedia for more details.
|
||||||
|
LPMTrie
|
||||||
|
// ArrayOfMaps - Each item in the array is another map. The inner map mustn't be a map of maps
|
||||||
|
// itself.
|
||||||
|
ArrayOfMaps
|
||||||
|
// HashOfMaps - Each item in the hash map is another map. The inner map mustn't be a map of maps
|
||||||
|
// itself.
|
||||||
|
HashOfMaps
|
||||||
|
)
|
||||||
|
|
||||||
|
// hasPerCPUValue returns true if the Map stores a value per CPU.
|
||||||
|
func (mt MapType) hasPerCPUValue() bool {
|
||||||
|
if mt == PerCPUHash || mt == PerCPUArray {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_MapCreate = iota
|
||||||
|
_MapLookupElem
|
||||||
|
_MapUpdateElem
|
||||||
|
_MapDeleteElem
|
||||||
|
_MapGetNextKey
|
||||||
|
_ProgLoad
|
||||||
|
_ObjPin
|
||||||
|
_ObjGet
|
||||||
|
_ProgAttach
|
||||||
|
_ProgDetach
|
||||||
|
_ProgTestRun
|
||||||
|
_ProgGetNextID
|
||||||
|
_MapGetNextID
|
||||||
|
_ProgGetFDByID
|
||||||
|
_MapGetFDByID
|
||||||
|
_ObjGetInfoByFD
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
_Any = iota
|
||||||
|
_NoExist
|
||||||
|
_Exist
|
||||||
|
)
|
||||||
|
|
||||||
|
// ProgramType of the eBPF program
|
||||||
|
type ProgramType uint32
|
||||||
|
|
||||||
|
// eBPF program types
|
||||||
|
const (
|
||||||
|
// Unrecognized program type
|
||||||
|
UnspecifiedProgram ProgramType = iota
|
||||||
|
// SocketFilter socket or seccomp filter
|
||||||
|
SocketFilter
|
||||||
|
// Kprobe program
|
||||||
|
Kprobe
|
||||||
|
// SchedCLS traffic control shaper
|
||||||
|
SchedCLS
|
||||||
|
// SchedACT routing control shaper
|
||||||
|
SchedACT
|
||||||
|
// TracePoint program
|
||||||
|
TracePoint
|
||||||
|
// XDP program
|
||||||
|
XDP
|
||||||
|
// PerfEvent program
|
||||||
|
PerfEvent
|
||||||
|
// CGroupSKB program
|
||||||
|
CGroupSKB
|
||||||
|
// CGroupSock program
|
||||||
|
CGroupSock
|
||||||
|
// LWTIn program
|
||||||
|
LWTIn
|
||||||
|
// LWTOut program
|
||||||
|
LWTOut
|
||||||
|
// LWTXmit program
|
||||||
|
LWTXmit
|
||||||
|
// SockOps program
|
||||||
|
SockOps
|
||||||
|
// SkSKB program
|
||||||
|
SkSKB
|
||||||
|
// CGroupDevice program
|
||||||
|
CGroupDevice
|
||||||
|
// SkMsg program
|
||||||
|
SkMsg
|
||||||
|
// RawTracepoint program
|
||||||
|
RawTracepoint
|
||||||
|
// CGroupSockAddr program
|
||||||
|
CGroupSockAddr
|
||||||
|
// LWTSeg6Local program
|
||||||
|
LWTSeg6Local
|
||||||
|
// LircMode2 program
|
||||||
|
LircMode2
|
||||||
|
// SkReuseport program
|
||||||
|
SkReuseport
|
||||||
|
// FlowDissector program
|
||||||
|
FlowDissector
|
||||||
|
// CGroupSysctl program
|
||||||
|
CGroupSysctl
|
||||||
|
// RawTracepointWritable program
|
||||||
|
RawTracepointWritable
|
||||||
|
// CGroupSockopt program
|
||||||
|
CGroupSockopt
|
||||||
|
)
|
||||||
|
|
||||||
|
// AttachType of the eBPF program, needed to differentiate allowed context accesses in
|
||||||
|
// some newer program types like CGroupSockAddr. Should be set to AttachNone if not required.
|
||||||
|
// Will cause invalid argument (EINVAL) at program load time if set incorrectly.
|
||||||
|
type AttachType uint32
|
||||||
|
|
||||||
|
// AttachNone is an alias for AttachCGroupInetIngress for readability reasons
|
||||||
|
const AttachNone AttachType = 0
|
||||||
|
|
||||||
|
const (
|
||||||
|
AttachCGroupInetIngress AttachType = iota
|
||||||
|
AttachCGroupInetEgress
|
||||||
|
AttachCGroupInetSockCreate
|
||||||
|
AttachCGroupSockOps
|
||||||
|
AttachSkSKBStreamParser
|
||||||
|
AttachSkSKBStreamVerdict
|
||||||
|
AttachCGroupDevice
|
||||||
|
AttachSkMsgVerdict
|
||||||
|
AttachCGroupInet4Bind
|
||||||
|
AttachCGroupInet6Bind
|
||||||
|
AttachCGroupInet4Connect
|
||||||
|
AttachCGroupInet6Connect
|
||||||
|
AttachCGroupInet4PostBind
|
||||||
|
AttachCGroupInet6PostBind
|
||||||
|
AttachCGroupUDP4Sendmsg
|
||||||
|
AttachCGroupUDP6Sendmsg
|
||||||
|
AttachLircMode2
|
||||||
|
AttachFlowDissector
|
||||||
|
AttachCGroupSysctl
|
||||||
|
AttachCGroupUDP4Recvmsg
|
||||||
|
AttachCGroupUDP6Recvmsg
|
||||||
|
AttachCGroupGetsockopt
|
||||||
|
AttachCGroupSetsockopt
|
||||||
|
)
|
||||||
|
|
||||||
|
// AttachFlags of the eBPF program used in BPF_PROG_ATTACH command
|
||||||
|
type AttachFlags uint32
|
78
vendor/github.com/cilium/ebpf/types_string.go
generated
vendored
Normal file
78
vendor/github.com/cilium/ebpf/types_string.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
// Code generated by "stringer -output types_string.go -type=MapType,ProgramType"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package ebpf
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[UnspecifiedMap-0]
|
||||||
|
_ = x[Hash-1]
|
||||||
|
_ = x[Array-2]
|
||||||
|
_ = x[ProgramArray-3]
|
||||||
|
_ = x[PerfEventArray-4]
|
||||||
|
_ = x[PerCPUHash-5]
|
||||||
|
_ = x[PerCPUArray-6]
|
||||||
|
_ = x[StackTrace-7]
|
||||||
|
_ = x[CGroupArray-8]
|
||||||
|
_ = x[LRUHash-9]
|
||||||
|
_ = x[LRUCPUHash-10]
|
||||||
|
_ = x[LPMTrie-11]
|
||||||
|
_ = x[ArrayOfMaps-12]
|
||||||
|
_ = x[HashOfMaps-13]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _MapType_name = "UnspecifiedMapHashArrayProgramArrayPerfEventArrayPerCPUHashPerCPUArrayStackTraceCGroupArrayLRUHashLRUCPUHashLPMTrieArrayOfMapsHashOfMaps"
|
||||||
|
|
||||||
|
var _MapType_index = [...]uint8{0, 14, 18, 23, 35, 49, 59, 70, 80, 91, 98, 108, 115, 126, 136}
|
||||||
|
|
||||||
|
func (i MapType) String() string {
|
||||||
|
if i >= MapType(len(_MapType_index)-1) {
|
||||||
|
return "MapType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _MapType_name[_MapType_index[i]:_MapType_index[i+1]]
|
||||||
|
}
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[UnspecifiedProgram-0]
|
||||||
|
_ = x[SocketFilter-1]
|
||||||
|
_ = x[Kprobe-2]
|
||||||
|
_ = x[SchedCLS-3]
|
||||||
|
_ = x[SchedACT-4]
|
||||||
|
_ = x[TracePoint-5]
|
||||||
|
_ = x[XDP-6]
|
||||||
|
_ = x[PerfEvent-7]
|
||||||
|
_ = x[CGroupSKB-8]
|
||||||
|
_ = x[CGroupSock-9]
|
||||||
|
_ = x[LWTIn-10]
|
||||||
|
_ = x[LWTOut-11]
|
||||||
|
_ = x[LWTXmit-12]
|
||||||
|
_ = x[SockOps-13]
|
||||||
|
_ = x[SkSKB-14]
|
||||||
|
_ = x[CGroupDevice-15]
|
||||||
|
_ = x[SkMsg-16]
|
||||||
|
_ = x[RawTracepoint-17]
|
||||||
|
_ = x[CGroupSockAddr-18]
|
||||||
|
_ = x[LWTSeg6Local-19]
|
||||||
|
_ = x[LircMode2-20]
|
||||||
|
_ = x[SkReuseport-21]
|
||||||
|
_ = x[FlowDissector-22]
|
||||||
|
_ = x[CGroupSysctl-23]
|
||||||
|
_ = x[RawTracepointWritable-24]
|
||||||
|
_ = x[CGroupSockopt-25]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _ProgramType_name = "UnspecifiedProgramSocketFilterKprobeSchedCLSSchedACTTracePointXDPPerfEventCGroupSKBCGroupSockLWTInLWTOutLWTXmitSockOpsSkSKBCGroupDeviceSkMsgRawTracepointCGroupSockAddrLWTSeg6LocalLircMode2SkReuseportFlowDissectorCGroupSysctlRawTracepointWritableCGroupSockopt"
|
||||||
|
|
||||||
|
var _ProgramType_index = [...]uint16{0, 18, 30, 36, 44, 52, 62, 65, 74, 83, 93, 98, 104, 111, 118, 123, 135, 140, 153, 167, 179, 188, 199, 212, 224, 245, 258}
|
||||||
|
|
||||||
|
func (i ProgramType) String() string {
|
||||||
|
if i >= ProgramType(len(_ProgramType_index)-1) {
|
||||||
|
return "ProgramType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _ProgramType_name[_ProgramType_index[i]:_ProgramType_index[i+1]]
|
||||||
|
}
|
4
vendor/github.com/containerd/cgroups/README.md
generated
vendored
4
vendor/github.com/containerd/cgroups/README.md
generated
vendored
@ -112,6 +112,10 @@ err := control.MoveTo(destination)
|
|||||||
subCgroup, err := control.New("child", resources)
|
subCgroup, err := control.New("child", resources)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Attention
|
||||||
|
|
||||||
|
All static path should not include `/sys/fs/cgroup/` prefix, it should start with your own cgroups name
|
||||||
|
|
||||||
## Project details
|
## Project details
|
||||||
|
|
||||||
Cgroups is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
|
Cgroups is a containerd sub-project, licensed under the [Apache 2.0 license](./LICENSE).
|
||||||
|
1
vendor/github.com/containerd/cgroups/cgroup.go
generated
vendored
1
vendor/github.com/containerd/cgroups/cgroup.go
generated
vendored
@ -67,6 +67,7 @@ func New(hierarchy Hierarchy, path Path, resources *specs.LinuxResources, opts .
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Load will load an existing cgroup and allow it to be controlled
|
// Load will load an existing cgroup and allow it to be controlled
|
||||||
|
// All static path should not include `/sys/fs/cgroup/` prefix, it should start with your own cgroups name
|
||||||
func Load(hierarchy Hierarchy, path Path, opts ...InitOpts) (Cgroup, error) {
|
func Load(hierarchy Hierarchy, path Path, opts ...InitOpts) (Cgroup, error) {
|
||||||
config := newInitConfig()
|
config := newInitConfig()
|
||||||
for _, o := range opts {
|
for _, o := range opts {
|
||||||
|
5
vendor/github.com/containerd/cgroups/go.mod
generated
vendored
5
vendor/github.com/containerd/cgroups/go.mod
generated
vendored
@ -3,11 +3,14 @@ module github.com/containerd/cgroups
|
|||||||
go 1.12
|
go 1.12
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/cilium/ebpf v0.0.0-20191113100448-d9fb101ca1fb
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
|
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e
|
||||||
github.com/docker/go-units v0.4.0
|
github.com/docker/go-units v0.4.0
|
||||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e
|
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e
|
||||||
github.com/gogo/protobuf v1.2.1
|
github.com/gogo/protobuf v1.2.1
|
||||||
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700
|
github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700
|
||||||
github.com/pkg/errors v0.8.1
|
github.com/pkg/errors v0.8.1
|
||||||
golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f
|
github.com/sirupsen/logrus v1.4.2
|
||||||
|
github.com/urfave/cli v1.22.1
|
||||||
|
golang.org/x/sys v0.0.0-20191112214154-59a1497f0cea
|
||||||
)
|
)
|
||||||
|
584
vendor/github.com/containerd/cgroups/stats/v1/metrics.pb.go
generated
vendored
584
vendor/github.com/containerd/cgroups/stats/v1/metrics.pb.go
generated
vendored
@ -32,6 +32,7 @@ type Metrics struct {
|
|||||||
Blkio *BlkIOStat `protobuf:"bytes,5,opt,name=blkio,proto3" json:"blkio,omitempty"`
|
Blkio *BlkIOStat `protobuf:"bytes,5,opt,name=blkio,proto3" json:"blkio,omitempty"`
|
||||||
Rdma *RdmaStat `protobuf:"bytes,6,opt,name=rdma,proto3" json:"rdma,omitempty"`
|
Rdma *RdmaStat `protobuf:"bytes,6,opt,name=rdma,proto3" json:"rdma,omitempty"`
|
||||||
Network []*NetworkStat `protobuf:"bytes,7,rep,name=network,proto3" json:"network,omitempty"`
|
Network []*NetworkStat `protobuf:"bytes,7,rep,name=network,proto3" json:"network,omitempty"`
|
||||||
|
CgroupStats *CgroupStats `protobuf:"bytes,8,opt,name=cgroup_stats,json=cgroupStats,proto3" json:"cgroup_stats,omitempty"`
|
||||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
XXX_unrecognized []byte `json:"-"`
|
XXX_unrecognized []byte `json:"-"`
|
||||||
XXX_sizecache int32 `json:"-"`
|
XXX_sizecache int32 `json:"-"`
|
||||||
@ -608,6 +609,55 @@ func (m *NetworkStat) XXX_DiscardUnknown() {
|
|||||||
|
|
||||||
var xxx_messageInfo_NetworkStat proto.InternalMessageInfo
|
var xxx_messageInfo_NetworkStat proto.InternalMessageInfo
|
||||||
|
|
||||||
|
// CgroupStats exports per-cgroup statistics.
|
||||||
|
type CgroupStats struct {
|
||||||
|
// number of tasks sleeping
|
||||||
|
NrSleeping uint64 `protobuf:"varint,1,opt,name=nr_sleeping,json=nrSleeping,proto3" json:"nr_sleeping,omitempty"`
|
||||||
|
// number of tasks running
|
||||||
|
NrRunning uint64 `protobuf:"varint,2,opt,name=nr_running,json=nrRunning,proto3" json:"nr_running,omitempty"`
|
||||||
|
// number of tasks in stopped state
|
||||||
|
NrStopped uint64 `protobuf:"varint,3,opt,name=nr_stopped,json=nrStopped,proto3" json:"nr_stopped,omitempty"`
|
||||||
|
// number of tasks in uninterruptible state
|
||||||
|
NrUninterruptible uint64 `protobuf:"varint,4,opt,name=nr_uninterruptible,json=nrUninterruptible,proto3" json:"nr_uninterruptible,omitempty"`
|
||||||
|
// number of tasks waiting on IO
|
||||||
|
NrIoWait uint64 `protobuf:"varint,5,opt,name=nr_io_wait,json=nrIoWait,proto3" json:"nr_io_wait,omitempty"`
|
||||||
|
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||||
|
XXX_unrecognized []byte `json:"-"`
|
||||||
|
XXX_sizecache int32 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CgroupStats) Reset() { *m = CgroupStats{} }
|
||||||
|
func (*CgroupStats) ProtoMessage() {}
|
||||||
|
func (*CgroupStats) Descriptor() ([]byte, []int) {
|
||||||
|
return fileDescriptor_a17b2d87c332bfaa, []int{13}
|
||||||
|
}
|
||||||
|
func (m *CgroupStats) XXX_Unmarshal(b []byte) error {
|
||||||
|
return m.Unmarshal(b)
|
||||||
|
}
|
||||||
|
func (m *CgroupStats) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||||
|
if deterministic {
|
||||||
|
return xxx_messageInfo_CgroupStats.Marshal(b, m, deterministic)
|
||||||
|
} else {
|
||||||
|
b = b[:cap(b)]
|
||||||
|
n, err := m.MarshalTo(b)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return b[:n], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (m *CgroupStats) XXX_Merge(src proto.Message) {
|
||||||
|
xxx_messageInfo_CgroupStats.Merge(m, src)
|
||||||
|
}
|
||||||
|
func (m *CgroupStats) XXX_Size() int {
|
||||||
|
return m.Size()
|
||||||
|
}
|
||||||
|
func (m *CgroupStats) XXX_DiscardUnknown() {
|
||||||
|
xxx_messageInfo_CgroupStats.DiscardUnknown(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
var xxx_messageInfo_CgroupStats proto.InternalMessageInfo
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
proto.RegisterType((*Metrics)(nil), "io.containerd.cgroups.v1.Metrics")
|
proto.RegisterType((*Metrics)(nil), "io.containerd.cgroups.v1.Metrics")
|
||||||
proto.RegisterType((*HugetlbStat)(nil), "io.containerd.cgroups.v1.HugetlbStat")
|
proto.RegisterType((*HugetlbStat)(nil), "io.containerd.cgroups.v1.HugetlbStat")
|
||||||
@ -622,6 +672,7 @@ func init() {
|
|||||||
proto.RegisterType((*RdmaStat)(nil), "io.containerd.cgroups.v1.RdmaStat")
|
proto.RegisterType((*RdmaStat)(nil), "io.containerd.cgroups.v1.RdmaStat")
|
||||||
proto.RegisterType((*RdmaEntry)(nil), "io.containerd.cgroups.v1.RdmaEntry")
|
proto.RegisterType((*RdmaEntry)(nil), "io.containerd.cgroups.v1.RdmaEntry")
|
||||||
proto.RegisterType((*NetworkStat)(nil), "io.containerd.cgroups.v1.NetworkStat")
|
proto.RegisterType((*NetworkStat)(nil), "io.containerd.cgroups.v1.NetworkStat")
|
||||||
|
proto.RegisterType((*CgroupStats)(nil), "io.containerd.cgroups.v1.CgroupStats")
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@ -629,105 +680,112 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var fileDescriptor_a17b2d87c332bfaa = []byte{
|
var fileDescriptor_a17b2d87c332bfaa = []byte{
|
||||||
// 1558 bytes of a gzipped FileDescriptorProto
|
// 1669 bytes of a gzipped FileDescriptorProto
|
||||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x57, 0xcf, 0x73, 0x13, 0x39,
|
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x58, 0x4f, 0x73, 0x1b, 0xb7,
|
||||||
0x16, 0xc6, 0xb1, 0x13, 0xbb, 0x9f, 0x93, 0x90, 0x28, 0x10, 0x3a, 0x01, 0xe2, 0xe0, 0x24, 0xbb,
|
0x15, 0x0f, 0xc5, 0x95, 0xc8, 0x7d, 0x94, 0x6c, 0x09, 0xfe, 0xb7, 0x52, 0x1c, 0x51, 0xa1, 0xec,
|
||||||
0xd9, 0xa5, 0xca, 0x29, 0xd8, 0x2d, 0x6a, 0x61, 0xa1, 0xb6, 0x70, 0x80, 0x82, 0xda, 0xcd, 0x62,
|
0xd6, 0xad, 0xa7, 0xd2, 0x24, 0xed, 0x78, 0xea, 0x34, 0x99, 0x4e, 0xa4, 0x24, 0x63, 0x4f, 0xab,
|
||||||
0xda, 0x49, 0xb1, 0x7b, 0xea, 0x92, 0xdb, 0xa2, 0xad, 0xc4, 0x6e, 0x35, 0x6a, 0xb5, 0xe3, 0xcc,
|
0x9a, 0x59, 0x4a, 0x93, 0xf6, 0xb4, 0x03, 0x2e, 0xe1, 0x25, 0xac, 0xe5, 0x62, 0x83, 0xc5, 0x52,
|
||||||
0x69, 0x0e, 0x53, 0x35, 0xa7, 0xf9, 0x67, 0xe6, 0xaf, 0xe0, 0x38, 0x97, 0xa9, 0x9a, 0xb9, 0xa4,
|
0x74, 0x4f, 0x3d, 0x74, 0xa6, 0xa7, 0x7e, 0xa0, 0x7e, 0x83, 0x1c, 0x7b, 0xe9, 0x4c, 0x7b, 0xd1,
|
||||||
0x06, 0xff, 0x25, 0x53, 0x92, 0xfa, 0x87, 0x0c, 0x84, 0x8c, 0x6f, 0x2d, 0xe9, 0xfb, 0xbe, 0xf7,
|
0x34, 0xfc, 0x1c, 0x3d, 0x74, 0x80, 0x87, 0xfd, 0x43, 0xc7, 0xb2, 0xc2, 0xdb, 0xe2, 0xe1, 0xf7,
|
||||||
0xf4, 0xfa, 0x53, 0xeb, 0x35, 0xfc, 0xdd, 0xa7, 0xa2, 0x17, 0x77, 0x1a, 0x1e, 0x1b, 0xec, 0x79,
|
0x7e, 0xef, 0xe1, 0xe1, 0x07, 0xe0, 0x91, 0xf0, 0xab, 0x88, 0xab, 0x71, 0x3e, 0x3c, 0x08, 0xc5,
|
||||||
0x2c, 0x10, 0x98, 0x06, 0x84, 0x77, 0xf7, 0x3c, 0x9f, 0xb3, 0x38, 0x8c, 0xf6, 0x22, 0x81, 0x45,
|
0xe4, 0x30, 0x14, 0x89, 0xa2, 0x3c, 0x61, 0x72, 0x74, 0x18, 0x46, 0x52, 0xe4, 0x69, 0x76, 0x98,
|
||||||
0xb4, 0x37, 0xbc, 0xb7, 0x37, 0x20, 0x82, 0x53, 0x2f, 0x6a, 0x84, 0x9c, 0x09, 0x86, 0x6c, 0xca,
|
0x29, 0xaa, 0xb2, 0xc3, 0xe9, 0x47, 0x87, 0x13, 0xa6, 0x24, 0x0f, 0xb3, 0x83, 0x54, 0x0a, 0x25,
|
||||||
0x1a, 0x39, 0xba, 0x91, 0xa0, 0x1b, 0xc3, 0x7b, 0xeb, 0xd7, 0x7c, 0xe6, 0x33, 0x05, 0xda, 0x93,
|
0x88, 0xc7, 0xc5, 0x41, 0x85, 0x3e, 0xb0, 0xe8, 0x83, 0xe9, 0x47, 0x3b, 0xb7, 0x23, 0x11, 0x09,
|
||||||
0x4f, 0x1a, 0x5f, 0xff, 0xb1, 0x08, 0xe5, 0x03, 0xad, 0x80, 0xfe, 0x05, 0xe5, 0x5e, 0xec, 0x13,
|
0x03, 0x3a, 0xd4, 0x5f, 0x88, 0xef, 0xfd, 0xaf, 0x09, 0xad, 0x13, 0x64, 0x20, 0xbf, 0x85, 0xd6,
|
||||||
0xd1, 0xef, 0xd8, 0x85, 0xcd, 0xe2, 0x6e, 0xf5, 0xfe, 0x4e, 0xe3, 0x22, 0xb5, 0xc6, 0x4b, 0x0d,
|
0x38, 0x8f, 0x98, 0x8a, 0x87, 0x5e, 0x63, 0xaf, 0xf9, 0xa8, 0xf3, 0xf1, 0xc3, 0x83, 0xab, 0xd8,
|
||||||
0x6c, 0x0b, 0x2c, 0x9c, 0x94, 0x85, 0x1e, 0x40, 0x29, 0xa4, 0xdd, 0xc8, 0x9e, 0xd9, 0x2c, 0xec,
|
0x0e, 0x9e, 0x21, 0x70, 0xa0, 0xa8, 0xf2, 0x0b, 0x2f, 0xf2, 0x04, 0x9c, 0x94, 0x8f, 0x32, 0x6f,
|
||||||
0x56, 0xef, 0xd7, 0x2f, 0x66, 0xb7, 0x68, 0x37, 0x52, 0x54, 0x85, 0x47, 0x8f, 0xa1, 0xe8, 0x85,
|
0x65, 0xaf, 0xf1, 0xa8, 0xf3, 0x71, 0xef, 0x6a, 0xef, 0x3e, 0x1f, 0x65, 0xc6, 0xd5, 0xe0, 0xc9,
|
||||||
0xb1, 0x5d, 0x54, 0xb4, 0x3b, 0x17, 0xd3, 0xf6, 0x5b, 0x47, 0x92, 0xd5, 0x2c, 0x8f, 0xcf, 0x6b,
|
0xa7, 0xd0, 0x0c, 0xd3, 0xdc, 0x6b, 0x1a, 0xb7, 0x0f, 0xaf, 0x76, 0x3b, 0xee, 0x9f, 0x69, 0xaf,
|
||||||
0xc5, 0xfd, 0xd6, 0x91, 0x23, 0x69, 0xe8, 0x31, 0xcc, 0x0d, 0xc8, 0x80, 0xf1, 0x33, 0xbb, 0xa4,
|
0xa3, 0xd6, 0xfc, 0xb2, 0xdb, 0x3c, 0xee, 0x9f, 0xf9, 0xda, 0x8d, 0x7c, 0x0a, 0x6b, 0x13, 0x36,
|
||||||
0x04, 0xb6, 0x2f, 0x16, 0x38, 0x50, 0x38, 0x15, 0x39, 0xe1, 0xa0, 0x87, 0x30, 0xdb, 0xe9, 0x9f,
|
0x11, 0xf2, 0xb5, 0xe7, 0x18, 0x82, 0x07, 0x57, 0x13, 0x9c, 0x18, 0x9c, 0x89, 0x6c, 0x7d, 0xc8,
|
||||||
0x50, 0x66, 0xcf, 0x2a, 0xf2, 0xd6, 0xc5, 0xe4, 0x66, 0xff, 0xe4, 0xd5, 0x6b, 0xc5, 0xd5, 0x0c,
|
0x53, 0x58, 0x1d, 0xc6, 0xe7, 0x5c, 0x78, 0xab, 0xc6, 0x79, 0xff, 0x6a, 0xe7, 0xa3, 0xf8, 0xfc,
|
||||||
0xb9, 0x5d, 0xde, 0x1d, 0x60, 0x7b, 0xee, 0xb2, 0xed, 0x3a, 0xdd, 0x01, 0xd6, 0xdb, 0x95, 0x78,
|
0xf9, 0x0b, 0xe3, 0x8b, 0x1e, 0x7a, 0xb9, 0x72, 0x34, 0xa1, 0xde, 0xda, 0x75, 0xcb, 0xf5, 0x47,
|
||||||
0x59, 0xe7, 0x80, 0x88, 0x53, 0xc6, 0x4f, 0xec, 0xf2, 0x65, 0x75, 0xfe, 0xaf, 0x06, 0xea, 0x3a,
|
0x13, 0x8a, 0xcb, 0xd5, 0x78, 0x5d, 0xe7, 0x84, 0xa9, 0x0b, 0x21, 0xcf, 0xbd, 0xd6, 0x75, 0x75,
|
||||||
0x27, 0xac, 0xfa, 0x09, 0x54, 0x8d, 0xfa, 0xa3, 0x6b, 0x30, 0x1b, 0x47, 0xd8, 0x27, 0x76, 0x61,
|
0xfe, 0x03, 0x02, 0xb1, 0xce, 0xd6, 0x8b, 0x3c, 0x83, 0x75, 0x84, 0x04, 0x46, 0x05, 0x5e, 0xdb,
|
||||||
0xb3, 0xb0, 0x5b, 0x72, 0xf4, 0x00, 0x2d, 0x41, 0x71, 0x80, 0x47, 0xea, 0x5d, 0x94, 0x1c, 0xf9,
|
0x24, 0xf0, 0x0e, 0x96, 0x63, 0xf3, 0xa9, 0x49, 0x32, 0xbf, 0x13, 0x56, 0x83, 0xde, 0x39, 0x74,
|
||||||
0x88, 0x6c, 0x28, 0xbf, 0xc3, 0xb4, 0xef, 0x05, 0x42, 0x95, 0xba, 0xe4, 0xa4, 0x43, 0xb4, 0x0e,
|
0x6a, 0x3b, 0x49, 0x6e, 0xc3, 0x6a, 0x9e, 0xd1, 0x88, 0x79, 0x8d, 0xbd, 0xc6, 0x23, 0xc7, 0xc7,
|
||||||
0x95, 0x10, 0xfb, 0x24, 0xa2, 0xdf, 0x10, 0x55, 0x44, 0xcb, 0xc9, 0xc6, 0xf5, 0x47, 0x50, 0x49,
|
0x01, 0xd9, 0x84, 0xe6, 0x84, 0xce, 0xcc, 0xae, 0x3a, 0xbe, 0xfe, 0x24, 0x1e, 0xb4, 0x5e, 0x52,
|
||||||
0x5f, 0x97, 0x54, 0xf0, 0x62, 0xce, 0x49, 0x20, 0x92, 0x58, 0xe9, 0x50, 0xe6, 0xd0, 0xa7, 0x03,
|
0x1e, 0x87, 0x89, 0x32, 0x9b, 0xe6, 0xf8, 0xc5, 0x90, 0xec, 0x40, 0x3b, 0xa5, 0x11, 0xcb, 0xf8,
|
||||||
0x2a, 0x92, 0x78, 0x7a, 0x50, 0xff, 0xbe, 0x00, 0xe5, 0xe4, 0xa5, 0xa1, 0x7f, 0x98, 0x59, 0x7e,
|
0x9f, 0x99, 0xd9, 0x0e, 0xd7, 0x2f, 0xc7, 0xbd, 0x4f, 0xa0, 0x5d, 0x6c, 0xbc, 0x66, 0x08, 0x73,
|
||||||
0xb5, 0x5c, 0xfb, 0xad, 0xa3, 0x23, 0x89, 0x4c, 0x77, 0xd2, 0x04, 0x10, 0x3d, 0xce, 0x84, 0xe8,
|
0x29, 0x59, 0xa2, 0x6c, 0xac, 0x62, 0xa8, 0x73, 0x88, 0xf9, 0x84, 0x2b, 0x1b, 0x0f, 0x07, 0xbd,
|
||||||
0xd3, 0xc0, 0xbf, 0xdc, 0x5c, 0x87, 0x1a, 0x4b, 0x1c, 0x83, 0x55, 0x7f, 0x0f, 0x95, 0x54, 0x56,
|
0xbf, 0x35, 0xa0, 0x65, 0xb7, 0x9f, 0xfc, 0xba, 0x9e, 0xe5, 0x3b, 0x0b, 0x7f, 0xdc, 0x3f, 0x3b,
|
||||||
0xe6, 0x2a, 0x98, 0xc0, 0xfd, 0xb4, 0x5e, 0x6a, 0x80, 0x56, 0x61, 0xee, 0x84, 0xf0, 0x80, 0xf4,
|
0xd3, 0xc8, 0x62, 0x25, 0x47, 0x00, 0x6a, 0x2c, 0x85, 0x52, 0x31, 0x4f, 0xa2, 0xeb, 0x65, 0x7a,
|
||||||
0x93, 0x2d, 0x24, 0x23, 0x84, 0xa0, 0x14, 0x47, 0x84, 0x27, 0x25, 0x53, 0xcf, 0x68, 0x0b, 0xca,
|
0x8a, 0x58, 0xe6, 0xd7, 0xbc, 0x7a, 0xdf, 0x42, 0xbb, 0xa0, 0xd5, 0xb9, 0x2a, 0xa1, 0x68, 0x5c,
|
||||||
0x21, 0xe1, 0xae, 0x34, 0x6d, 0x69, 0xb3, 0xb8, 0x5b, 0x6a, 0xc2, 0xf8, 0xbc, 0x36, 0xd7, 0x22,
|
0xd4, 0xcb, 0x0c, 0xc8, 0x5d, 0x58, 0x3b, 0x67, 0x32, 0x61, 0xb1, 0x5d, 0x82, 0x1d, 0x11, 0x02,
|
||||||
0x5c, 0x9a, 0x72, 0x2e, 0x24, 0x7c, 0x3f, 0x8c, 0xeb, 0x23, 0xa8, 0xa4, 0xa9, 0xc8, 0xc2, 0x85,
|
0x4e, 0x9e, 0x31, 0x69, 0x4b, 0x66, 0xbe, 0xc9, 0x3e, 0xb4, 0x52, 0x26, 0x03, 0x2d, 0x7f, 0x67,
|
||||||
0x84, 0x53, 0xd6, 0x8d, 0xd2, 0xc2, 0x25, 0x43, 0x74, 0x17, 0x96, 0x93, 0x34, 0x49, 0xd7, 0x4d,
|
0xaf, 0xf9, 0xc8, 0x39, 0x82, 0xf9, 0x65, 0x77, 0xad, 0xcf, 0xa4, 0x96, 0xf7, 0x5a, 0xca, 0xe4,
|
||||||
0x31, 0x3a, 0x83, 0xa5, 0x6c, 0xa1, 0x95, 0x80, 0x77, 0x60, 0x31, 0x07, 0x0b, 0x3a, 0x20, 0x49,
|
0x71, 0x9a, 0xf7, 0x66, 0xd0, 0x2e, 0x52, 0xd1, 0x85, 0x4b, 0x99, 0xe4, 0x62, 0x94, 0x15, 0x85,
|
||||||
0x56, 0x0b, 0xd9, 0xec, 0x21, 0x1d, 0x90, 0xfa, 0xaf, 0x55, 0x80, 0xdc, 0xea, 0x72, 0xbf, 0x1e,
|
0xb3, 0x43, 0xf2, 0x18, 0xb6, 0x6c, 0x9a, 0x6c, 0x14, 0x14, 0x18, 0xcc, 0x60, 0xb3, 0x9c, 0xe8,
|
||||||
0xf6, 0x7a, 0x99, 0x3f, 0xd4, 0x00, 0xad, 0x41, 0x91, 0x47, 0x49, 0x28, 0x7d, 0xa2, 0x9c, 0x76,
|
0x5b, 0xf0, 0x43, 0xb8, 0x51, 0x81, 0x15, 0x9f, 0x30, 0x9b, 0xd5, 0x46, 0x69, 0x3d, 0xe5, 0x13,
|
||||||
0xdb, 0x91, 0x73, 0xe8, 0x4f, 0x50, 0xe1, 0x51, 0xe4, 0xca, 0x63, 0xad, 0x03, 0x34, 0xab, 0xe3,
|
0xd6, 0xfb, 0x4f, 0x07, 0xa0, 0x3a, 0x34, 0x7a, 0xbd, 0x21, 0x0d, 0xc7, 0xa5, 0x3e, 0xcc, 0x80,
|
||||||
0xf3, 0x5a, 0xd9, 0x69, 0xb7, 0xa5, 0xed, 0x9c, 0x32, 0x8f, 0x22, 0xf9, 0x80, 0x6a, 0x50, 0x1d,
|
0x6c, 0x43, 0x53, 0x66, 0x36, 0x14, 0x9e, 0x4d, 0x7f, 0x30, 0xf0, 0xb5, 0x8d, 0xfc, 0x04, 0xda,
|
||||||
0xe0, 0x30, 0x24, 0x5d, 0xf7, 0x1d, 0xed, 0x6b, 0xe7, 0x94, 0x1c, 0xd0, 0x53, 0x2f, 0x68, 0x5f,
|
0x32, 0xcb, 0x02, 0x7d, 0x41, 0x60, 0x80, 0xa3, 0xce, 0xfc, 0xb2, 0xdb, 0xf2, 0x07, 0x03, 0x2d,
|
||||||
0x55, 0xba, 0x4b, 0xb9, 0x38, 0x53, 0x87, 0xab, 0xe4, 0xe8, 0x01, 0xba, 0x05, 0xd6, 0x29, 0xa7,
|
0x3b, 0xbf, 0x25, 0xb3, 0x4c, 0x7f, 0x90, 0x2e, 0x74, 0x26, 0x34, 0x4d, 0xd9, 0x28, 0x78, 0xc9,
|
||||||
0x82, 0x74, 0xb0, 0x77, 0xa2, 0x0e, 0x4f, 0xc9, 0xc9, 0x27, 0x90, 0x0d, 0x95, 0xd0, 0x77, 0x43,
|
0x63, 0x54, 0x8e, 0xe3, 0x03, 0x9a, 0xbe, 0xe2, 0xb1, 0xa9, 0xf4, 0x88, 0x4b, 0xf5, 0xda, 0x1c,
|
||||||
0xdf, 0xa5, 0x81, 0x5d, 0xd6, 0x6f, 0x22, 0xf4, 0x5b, 0xfe, 0xab, 0x00, 0xad, 0x83, 0xa5, 0x57,
|
0x53, 0xc7, 0xc7, 0x01, 0xb9, 0x0f, 0xee, 0x85, 0xe4, 0x8a, 0x0d, 0x69, 0x78, 0x6e, 0x8e, 0xa1,
|
||||||
0x58, 0x2c, 0xec, 0x4a, 0x52, 0x46, 0xbf, 0xe5, 0xbf, 0x8e, 0x05, 0x5a, 0x53, 0xac, 0x77, 0x38,
|
0xe3, 0x57, 0x06, 0xe2, 0x41, 0x3b, 0x8d, 0x82, 0x34, 0x0a, 0x78, 0xe2, 0xb5, 0x70, 0x27, 0xd2,
|
||||||
0xee, 0x0b, 0xdb, 0x4a, 0x97, 0x5e, 0xc8, 0x21, 0xda, 0x84, 0xf9, 0xd0, 0x77, 0x07, 0xf8, 0x38,
|
0xa8, 0x1f, 0x3d, 0x4f, 0xc8, 0x0e, 0xb8, 0x38, 0x23, 0x72, 0x65, 0x4e, 0x8f, 0x2e, 0x63, 0xd4,
|
||||||
0x59, 0x06, 0x9d, 0x66, 0xe8, 0x1f, 0xe0, 0x63, 0x8d, 0xd8, 0x82, 0x05, 0x1a, 0x60, 0x4f, 0xd0,
|
0x8f, 0x5e, 0xe4, 0x8a, 0x6c, 0x1b, 0xaf, 0x97, 0x34, 0x8f, 0x95, 0xe7, 0x16, 0x53, 0x5f, 0xe9,
|
||||||
0x21, 0x71, 0x71, 0xc0, 0x02, 0xbb, 0xaa, 0x20, 0xf3, 0xe9, 0xe4, 0xd3, 0x80, 0x05, 0x72, 0xb3,
|
0x21, 0xd9, 0x83, 0xf5, 0x34, 0x0a, 0x26, 0xf4, 0x95, 0x9d, 0x06, 0x4c, 0x33, 0x8d, 0x4e, 0xe8,
|
||||||
0x26, 0x64, 0x5e, 0xab, 0x18, 0x00, 0x53, 0x45, 0xd5, 0x63, 0x61, 0x52, 0x45, 0x55, 0x24, 0x57,
|
0x2b, 0x44, 0xec, 0xc3, 0x06, 0x4f, 0x68, 0xa8, 0xf8, 0x94, 0x05, 0x34, 0x11, 0x89, 0xd7, 0x31,
|
||||||
0x51, 0x90, 0x45, 0x53, 0x45, 0x01, 0x36, 0xa1, 0x1a, 0x07, 0x64, 0x48, 0x3d, 0x81, 0x3b, 0x7d,
|
0x90, 0xf5, 0xc2, 0xf8, 0x79, 0x22, 0x12, 0xbd, 0xd8, 0x3a, 0x64, 0x1d, 0x59, 0x6a, 0x80, 0x3a,
|
||||||
0x62, 0x5f, 0x55, 0x00, 0x73, 0x0a, 0x3d, 0x82, 0xb5, 0x1e, 0x25, 0x1c, 0x73, 0xaf, 0x47, 0x3d,
|
0x8b, 0xa9, 0xc7, 0xc6, 0x22, 0x8b, 0xa9, 0x48, 0xc5, 0x62, 0x20, 0x37, 0xea, 0x2c, 0x06, 0xb0,
|
||||||
0xdc, 0x77, 0xf5, 0x87, 0xcc, 0xd5, 0xc7, 0x6f, 0x49, 0xe1, 0x6f, 0x98, 0x00, 0xed, 0x84, 0xff,
|
0x07, 0x9d, 0x3c, 0x61, 0x53, 0x1e, 0x2a, 0x3a, 0x8c, 0x99, 0x77, 0xd3, 0x00, 0xea, 0x26, 0xf2,
|
||||||
0xc8, 0x65, 0xf4, 0x00, 0x26, 0x96, 0xdc, 0xe8, 0x14, 0x87, 0x09, 0x73, 0x59, 0x31, 0xaf, 0x9b,
|
0x09, 0x6c, 0x8f, 0x39, 0x93, 0x54, 0x86, 0x63, 0x1e, 0xd2, 0x38, 0xc0, 0x2b, 0x31, 0xc0, 0xe3,
|
||||||
0xcb, 0xed, 0x53, 0x1c, 0x6a, 0x5e, 0x0d, 0xaa, 0xea, 0x94, 0xb8, 0xda, 0x48, 0x48, 0xa7, 0xad,
|
0xb7, 0x69, 0xf0, 0xf7, 0xea, 0x00, 0x54, 0xc2, 0xef, 0xf5, 0x34, 0x79, 0x02, 0x0b, 0x53, 0x41,
|
||||||
0xa6, 0xf6, 0x95, 0x9b, 0xfe, 0x02, 0x96, 0x06, 0x48, 0x4f, 0xad, 0x28, 0xcf, 0xcc, 0x8f, 0xcf,
|
0x76, 0x41, 0x53, 0xeb, 0xb9, 0x65, 0x3c, 0xef, 0xd4, 0xa7, 0x07, 0x17, 0x34, 0x45, 0xbf, 0x2e,
|
||||||
0x6b, 0x95, 0x43, 0x39, 0x29, 0x8d, 0x55, 0x51, 0xcb, 0x4e, 0x14, 0xa1, 0x07, 0xb0, 0x98, 0x41,
|
0x74, 0xcc, 0x29, 0x09, 0x50, 0x48, 0x04, 0xd3, 0x36, 0xa6, 0x63, 0xa3, 0xa6, 0x9f, 0x81, 0x8b,
|
||||||
0xb5, 0xc7, 0xae, 0x29, 0xfc, 0xd2, 0xf8, 0xbc, 0x36, 0x9f, 0xe2, 0x95, 0xd1, 0xe6, 0x53, 0x8e,
|
0x00, 0xad, 0xa9, 0x5b, 0x46, 0x33, 0xeb, 0xf3, 0xcb, 0x6e, 0xfb, 0x54, 0x1b, 0xb5, 0xb0, 0xda,
|
||||||
0x72, 0xdb, 0x5f, 0x61, 0x59, 0xf3, 0x4c, 0xcf, 0x5d, 0x57, 0x99, 0x5c, 0x55, 0x0b, 0x07, 0xb9,
|
0x66, 0xda, 0xcf, 0x32, 0xf2, 0x04, 0x6e, 0x94, 0x50, 0xd4, 0xd8, 0x6d, 0x83, 0xdf, 0x9c, 0x5f,
|
||||||
0xf1, 0xb2, 0x7c, 0xb5, 0xfd, 0x56, 0x8d, 0x7c, 0x9f, 0x29, 0x0f, 0xfe, 0x19, 0x34, 0xc7, 0xcd,
|
0x76, 0xd7, 0x0b, 0xbc, 0x11, 0xda, 0x7a, 0xe1, 0x63, 0xd4, 0xf6, 0x73, 0xd8, 0x42, 0xbf, 0xba,
|
||||||
0x9d, 0x78, 0x43, 0x81, 0x74, 0x6e, 0x6f, 0x33, 0x3b, 0x6e, 0xa5, 0xd9, 0x66, 0xa6, 0xb4, 0xf5,
|
0xe6, 0xee, 0x98, 0x4c, 0x6e, 0x9a, 0x89, 0x93, 0x4a, 0x78, 0x65, 0xbe, 0x28, 0xbf, 0xbb, 0xb5,
|
||||||
0x2b, 0x51, 0xb3, 0x2d, 0xed, 0xcc, 0x9d, 0x54, 0x2d, 0xf7, 0xe7, 0x9a, 0x7e, 0xf9, 0x19, 0x4a,
|
0x7c, 0xbf, 0x30, 0x1a, 0xfc, 0x29, 0xa0, 0x4f, 0x50, 0x29, 0xf1, 0x9e, 0x01, 0x61, 0x6e, 0xdf,
|
||||||
0x9a, 0x74, 0xdb, 0xd0, 0xd2, 0x5e, 0x5c, 0x9f, 0x40, 0x69, 0x37, 0xde, 0x05, 0x94, 0xa1, 0x72,
|
0x94, 0x72, 0xdc, 0x2f, 0xb2, 0x2d, 0x45, 0xe9, 0xe1, 0x96, 0x18, 0x6b, 0x1f, 0x95, 0xf9, 0xb0,
|
||||||
0xd7, 0xde, 0x34, 0x36, 0xda, 0xca, 0xad, 0xdb, 0x80, 0x15, 0x0d, 0x9e, 0x34, 0xf0, 0x2d, 0x85,
|
0x60, 0xab, 0xf4, 0xb9, 0x8d, 0x9b, 0x5f, 0xa2, 0xb4, 0x48, 0x1f, 0xd4, 0xb8, 0x50, 0x8b, 0x3b,
|
||||||
0xd6, 0xf5, 0x7a, 0x65, 0xba, 0x38, 0x2b, 0xa2, 0x89, 0xbe, 0x6d, 0x68, 0x3f, 0xcd, 0xb1, 0x9f,
|
0x0b, 0x28, 0x54, 0xe3, 0x63, 0x20, 0x25, 0xaa, 0x52, 0xed, 0xfb, 0xb5, 0x85, 0xf6, 0x2b, 0xe9,
|
||||||
0x6b, 0xab, 0x92, 0x6f, 0x7c, 0x41, 0x5b, 0x15, 0xfd, 0x53, 0x6d, 0x85, 0xae, 0x7d, 0xa6, 0xad,
|
0x1e, 0xc0, 0x2d, 0x04, 0x2f, 0x0a, 0xf8, 0xbe, 0x41, 0x63, 0xbd, 0x9e, 0xd7, 0x55, 0x5c, 0x16,
|
||||||
0xb0, 0x77, 0x53, 0xac, 0x69, 0xf6, 0xcd, 0xe4, 0xb3, 0x27, 0x17, 0x8e, 0x0c, 0xc7, 0xff, 0x33,
|
0xb1, 0x8e, 0xfe, 0xa0, 0xc6, 0xfd, 0x79, 0x85, 0xfd, 0x21, 0xb7, 0x29, 0xf9, 0xee, 0x5b, 0xb8,
|
||||||
0xbd, 0x3a, 0xee, 0xa8, 0x6f, 0xff, 0xce, 0x65, 0x17, 0xfc, 0xf3, 0x40, 0xf0, 0xb3, 0xf4, 0xf6,
|
0x4d, 0xd1, 0xdf, 0xe4, 0x36, 0xe8, 0xee, 0x0f, 0xb8, 0x0d, 0xf6, 0x71, 0x81, 0xad, 0x8b, 0x7d,
|
||||||
0x78, 0x08, 0x25, 0xe9, 0x72, 0xbb, 0x3e, 0x0d, 0x57, 0x51, 0xd0, 0x93, 0xec, 0x4a, 0xd8, 0x9a,
|
0xcf, 0x5e, 0x7b, 0x7a, 0xe2, 0xac, 0xa6, 0xf8, 0xdf, 0x14, 0x4f, 0xc7, 0x87, 0xd7, 0x3d, 0x99,
|
||||||
0x86, 0x9c, 0xde, 0x1c, 0x6d, 0x00, 0xfd, 0xe4, 0x0a, 0x2f, 0xb4, 0xb7, 0xa7, 0x90, 0x68, 0x2e,
|
0xa8, 0xf5, 0x2f, 0x13, 0x25, 0x5f, 0x17, 0xaf, 0xc7, 0x53, 0x70, 0xb4, 0xca, 0xbd, 0xde, 0x32,
|
||||||
0x8c, 0xcf, 0x6b, 0xd6, 0xbf, 0x15, 0xf9, 0x70, 0xbf, 0xe5, 0x58, 0x5a, 0xe7, 0xd0, 0x0b, 0xeb,
|
0xbe, 0xc6, 0x85, 0x7c, 0x56, 0x3e, 0x09, 0xfb, 0xcb, 0x38, 0x17, 0x2f, 0xc7, 0x00, 0x00, 0xbf,
|
||||||
0x04, 0xaa, 0x06, 0x30, 0xbf, 0x77, 0x0b, 0xc6, 0xbd, 0x9b, 0x77, 0x04, 0x33, 0x5f, 0xe8, 0x08,
|
0x02, 0x15, 0xa6, 0xde, 0x83, 0x25, 0x28, 0x8e, 0x36, 0xe6, 0x97, 0x5d, 0xf7, 0x77, 0xc6, 0xf9,
|
||||||
0x8a, 0x5f, 0xec, 0x08, 0x4a, 0x13, 0x1d, 0x41, 0xfd, 0xe7, 0x59, 0xb0, 0xb2, 0x86, 0x07, 0x61,
|
0xf4, 0xb8, 0xef, 0xbb, 0xc8, 0x73, 0x1a, 0xa6, 0x3d, 0x06, 0x9d, 0x1a, 0xb0, 0x7a, 0x77, 0x1b,
|
||||||
0x58, 0xa7, 0xcc, 0x8d, 0x08, 0x1f, 0x52, 0x8f, 0xb8, 0x9d, 0x33, 0x41, 0x22, 0x97, 0x13, 0x2f,
|
0xb5, 0x77, 0xb7, 0xea, 0x08, 0x56, 0xde, 0xd2, 0x11, 0x34, 0xdf, 0xda, 0x11, 0x38, 0x0b, 0x1d,
|
||||||
0xe6, 0x11, 0x1d, 0x92, 0xa4, 0x59, 0xdc, 0xbe, 0xa4, 0x73, 0xd2, 0xb5, 0xb9, 0x41, 0x59, 0x5b,
|
0x41, 0xef, 0x5f, 0xab, 0xe0, 0x96, 0xad, 0x13, 0xa1, 0xb0, 0xc3, 0x45, 0x90, 0x31, 0x39, 0xe5,
|
||||||
0xcb, 0x34, 0xa5, 0x8a, 0x93, 0x8a, 0xa0, 0xff, 0xc1, 0xf5, 0x3c, 0x44, 0xd7, 0x50, 0x9f, 0x99,
|
0x21, 0x0b, 0x86, 0xaf, 0x15, 0xcb, 0x02, 0xc9, 0xc2, 0x5c, 0x66, 0x7c, 0xca, 0x6c, 0xdb, 0xf9,
|
||||||
0x42, 0x7d, 0x25, 0x53, 0xef, 0xe6, 0xca, 0x87, 0xb0, 0x42, 0x99, 0xfb, 0x3e, 0x26, 0xf1, 0x84,
|
0xe0, 0x9a, 0x1e, 0x0c, 0x6b, 0x73, 0x8f, 0x8b, 0x01, 0xd2, 0x1c, 0x69, 0x16, 0xbf, 0x20, 0x21,
|
||||||
0x6e, 0x71, 0x0a, 0xdd, 0x65, 0xca, 0xde, 0x28, 0x7e, 0xae, 0xea, 0xc2, 0x9a, 0x51, 0x12, 0x79,
|
0x7f, 0x84, 0x3b, 0x55, 0x88, 0x51, 0x8d, 0x7d, 0x65, 0x09, 0xf6, 0x5b, 0x25, 0xfb, 0xa8, 0x62,
|
||||||
0x17, 0x1b, 0xda, 0xa5, 0x29, 0xb4, 0x57, 0xb3, 0x9c, 0xe5, 0xdd, 0x9d, 0x07, 0xf8, 0x3f, 0xac,
|
0x3e, 0x85, 0x5b, 0x5c, 0x04, 0xdf, 0xe6, 0x2c, 0x5f, 0xe0, 0x6d, 0x2e, 0xc1, 0xbb, 0xc5, 0xc5,
|
||||||
0x52, 0xe6, 0x9e, 0x62, 0x2a, 0x3e, 0x55, 0x9f, 0x9d, 0xae, 0x22, 0x6f, 0x31, 0x15, 0x93, 0xd2,
|
0xd7, 0xc6, 0xbf, 0x62, 0x0d, 0x60, 0xbb, 0x56, 0x12, 0xfd, 0x16, 0xd7, 0xb8, 0x9d, 0x25, 0xb8,
|
||||||
0xba, 0x22, 0x03, 0xc2, 0xfd, 0x89, 0x8a, 0xcc, 0x4d, 0x57, 0x91, 0x03, 0xc5, 0xcf, 0x55, 0x5b,
|
0xef, 0x96, 0x39, 0xeb, 0xb7, 0xbb, 0x0a, 0xf0, 0x27, 0xb8, 0xcb, 0x45, 0x70, 0x41, 0xb9, 0x7a,
|
||||||
0xb0, 0x4c, 0xd9, 0xa7, 0xb9, 0x96, 0xa7, 0xd0, 0xbc, 0x4a, 0xd9, 0x64, 0x9e, 0x6f, 0x60, 0x39,
|
0x93, 0x7d, 0x75, 0xb9, 0x8a, 0x7c, 0x43, 0xb9, 0x5a, 0xa4, 0xc6, 0x8a, 0x4c, 0x98, 0x8c, 0x16,
|
||||||
0x22, 0x9e, 0x60, 0xdc, 0x74, 0x5b, 0x65, 0x0a, 0xc5, 0xa5, 0x84, 0x9e, 0x49, 0xd6, 0x87, 0x00,
|
0x2a, 0xb2, 0xb6, 0x5c, 0x45, 0x4e, 0x8c, 0x7f, 0xc5, 0xda, 0x87, 0x2d, 0x2e, 0xde, 0xcc, 0xb5,
|
||||||
0xf9, 0x3a, 0x5a, 0x84, 0x19, 0x16, 0xaa, 0xa3, 0x63, 0x39, 0x33, 0x2c, 0x94, 0x3d, 0x60, 0x57,
|
0xb5, 0x04, 0xe7, 0x4d, 0x2e, 0x16, 0xf3, 0xfc, 0x1a, 0xb6, 0x32, 0x16, 0x2a, 0x21, 0xeb, 0x6a,
|
||||||
0x7e, 0x76, 0xf4, 0xc1, 0xb1, 0x9c, 0x64, 0x24, 0xcf, 0xd3, 0x00, 0x1f, 0xb3, 0xb4, 0x09, 0xd4,
|
0x6b, 0x2f, 0xc1, 0xb8, 0x69, 0xdd, 0x4b, 0xca, 0xde, 0x14, 0xa0, 0x9a, 0x27, 0x37, 0x60, 0x45,
|
||||||
0x03, 0x35, 0x4b, 0x03, 0xc6, 0x93, 0xb3, 0xa3, 0x07, 0x72, 0x76, 0x88, 0xfb, 0x31, 0x49, 0x7b,
|
0xa4, 0xe6, 0xe8, 0xb8, 0xfe, 0x8a, 0x48, 0x75, 0x0f, 0x38, 0xd2, 0xd7, 0x0e, 0x1e, 0x1c, 0xd7,
|
||||||
0x1e, 0x35, 0xa8, 0x7f, 0x57, 0x80, 0x4a, 0xfa, 0x1b, 0x80, 0x9e, 0x98, 0x6d, 0x74, 0xf1, 0xeb,
|
0xb7, 0x23, 0x7d, 0x9e, 0x26, 0xf4, 0x95, 0x28, 0x9a, 0x40, 0x1c, 0x18, 0x2b, 0x4f, 0x84, 0xb4,
|
||||||
0x7f, 0x1d, 0x92, 0xa4, 0x37, 0x93, 0xf5, 0xda, 0x0f, 0xf3, 0x5e, 0xfb, 0x0f, 0x93, 0x93, 0x86,
|
0x67, 0x07, 0x07, 0xda, 0x3a, 0xa5, 0x71, 0xce, 0x8a, 0x9e, 0xc7, 0x0c, 0x7a, 0x7f, 0x6d, 0x40,
|
||||||
0x9c, 0x80, 0x95, 0xcd, 0x19, 0xbb, 0x2d, 0x4c, 0xec, 0xb6, 0x06, 0xd5, 0x9e, 0x87, 0xdd, 0x1e,
|
0xbb, 0xf8, 0x41, 0x41, 0x3e, 0xab, 0xb7, 0xd1, 0xcd, 0x77, 0xff, 0x7e, 0xd1, 0x4e, 0xb8, 0x98,
|
||||||
0x0e, 0xba, 0x7d, 0xa2, 0x3b, 0xc4, 0x05, 0x07, 0x7a, 0x1e, 0x7e, 0xa9, 0x67, 0x52, 0x00, 0xeb,
|
0xb2, 0xd7, 0x7e, 0x5a, 0xf5, 0xda, 0x3f, 0xda, 0xd9, 0x36, 0xe4, 0x0c, 0xdc, 0xd2, 0x56, 0x5b,
|
||||||
0x1c, 0x13, 0x4f, 0x44, 0xaa, 0x28, 0x1a, 0xf0, 0x5a, 0xcf, 0xd4, 0x7f, 0x98, 0x81, 0xaa, 0xf1,
|
0x6d, 0x63, 0x61, 0xb5, 0x5d, 0xe8, 0x8c, 0x43, 0x1a, 0x8c, 0x69, 0x32, 0x8a, 0x19, 0x76, 0x88,
|
||||||
0xe7, 0x22, 0x7b, 0xe8, 0x00, 0x0f, 0xd2, 0x38, 0xea, 0x59, 0x76, 0x6c, 0x7c, 0xa4, 0xbf, 0x25,
|
0x1b, 0x3e, 0x8c, 0x43, 0xfa, 0x0c, 0x2d, 0x05, 0x40, 0x0c, 0x5f, 0xb1, 0x50, 0x65, 0xa6, 0x28,
|
||||||
0xc9, 0x67, 0xaa, 0xcc, 0x47, 0xea, 0xa3, 0x80, 0x6e, 0x03, 0xf0, 0x91, 0x1b, 0x62, 0xef, 0x84,
|
0x08, 0x78, 0x81, 0x96, 0xde, 0xdf, 0x57, 0xa0, 0x53, 0xfb, 0x0d, 0xa4, 0x7b, 0xe8, 0x84, 0x4e,
|
||||||
0x24, 0xf2, 0x25, 0xc7, 0xe2, 0xa3, 0x96, 0x9e, 0x40, 0x37, 0xc1, 0xe2, 0x23, 0x97, 0x70, 0xce,
|
0x8a, 0x38, 0xe6, 0x5b, 0x77, 0x6c, 0x72, 0x86, 0x77, 0x89, 0xbd, 0xa6, 0x5a, 0x72, 0x66, 0x2e,
|
||||||
0x78, 0x94, 0xd4, 0xbe, 0xc2, 0x47, 0xcf, 0xd5, 0x38, 0xe1, 0x76, 0x39, 0x93, 0xbd, 0x40, 0xf2,
|
0x05, 0xf2, 0x01, 0x80, 0x9c, 0x05, 0x29, 0x0d, 0xcf, 0x99, 0xa5, 0x77, 0x7c, 0x57, 0xce, 0xfa,
|
||||||
0x0e, 0x2c, 0x3e, 0x7a, 0xa6, 0x27, 0x64, 0x54, 0x91, 0x46, 0xd5, 0xad, 0x67, 0x59, 0xe4, 0x51,
|
0x68, 0x20, 0xef, 0x83, 0x2b, 0x67, 0x01, 0x93, 0x52, 0xc8, 0xcc, 0xd6, 0xbe, 0x2d, 0x67, 0x5f,
|
||||||
0x45, 0x1e, 0x55, 0xb7, 0x9e, 0x96, 0x30, 0xa3, 0x8a, 0x2c, 0xaa, 0xee, 0x3e, 0x2b, 0xc2, 0x88,
|
0x9a, 0xb1, 0xf5, 0x1d, 0x49, 0xa1, 0x7b, 0x01, 0xbb, 0x07, 0xae, 0x9c, 0x7d, 0x81, 0x06, 0x1d,
|
||||||
0x2a, 0xf2, 0xa8, 0x56, 0xca, 0x4d, 0xa2, 0x36, 0xed, 0x0f, 0x1f, 0x37, 0xae, 0xfc, 0xf2, 0x71,
|
0x55, 0x15, 0x51, 0xb1, 0xf5, 0x6c, 0xa9, 0x2a, 0xaa, 0xaa, 0xa2, 0x62, 0xeb, 0xe9, 0xaa, 0x7a,
|
||||||
0xe3, 0xca, 0xb7, 0xe3, 0x8d, 0xc2, 0x87, 0xf1, 0x46, 0xe1, 0xa7, 0xf1, 0x46, 0xe1, 0xb7, 0xf1,
|
0x54, 0x55, 0x46, 0xc5, 0xee, 0xb3, 0xad, 0x6a, 0x51, 0x55, 0x15, 0xd5, 0x2d, 0x7c, 0x6d, 0xd4,
|
||||||
0x46, 0xa1, 0x33, 0xa7, 0x7e, 0xc3, 0xff, 0xf6, 0x7b, 0x00, 0x00, 0x00, 0xff, 0xff, 0x2f, 0xc0,
|
0xde, 0x3f, 0x1a, 0xd0, 0xa9, 0xfd, 0x9a, 0xd3, 0x05, 0x4c, 0x64, 0x90, 0xc5, 0x8c, 0xa5, 0xfa,
|
||||||
0x49, 0x92, 0xee, 0x0f, 0x00, 0x00,
|
0x27, 0x0d, 0xde, 0xdd, 0x90, 0xc8, 0x81, 0xb5, 0x68, 0xbe, 0x44, 0x06, 0x32, 0x4f, 0x92, 0xe2,
|
||||||
|
0x27, 0x8f, 0xe3, 0xbb, 0x89, 0xf4, 0xd1, 0x60, 0xa7, 0x33, 0x85, 0xe1, 0x9a, 0xc5, 0xf4, 0x00,
|
||||||
|
0x0d, 0xe4, 0x17, 0x40, 0x12, 0x19, 0xe4, 0x09, 0x4f, 0x14, 0x93, 0x32, 0x4f, 0x15, 0x1f, 0x96,
|
||||||
|
0xed, 0xf9, 0x56, 0x22, 0xcf, 0x16, 0x27, 0xc8, 0x7d, 0xc3, 0x66, 0x2f, 0x1b, 0x5b, 0xb2, 0x76,
|
||||||
|
0x22, 0x9f, 0x9b, 0x9b, 0xe3, 0xc8, 0xfb, 0xee, 0xfb, 0xdd, 0xf7, 0xfe, 0xfd, 0xfd, 0xee, 0x7b,
|
||||||
|
0x7f, 0x99, 0xef, 0x36, 0xbe, 0x9b, 0xef, 0x36, 0xfe, 0x39, 0xdf, 0x6d, 0xfc, 0x77, 0xbe, 0xdb,
|
||||||
|
0x18, 0xae, 0x99, 0x3f, 0x23, 0x7e, 0xf9, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb9, 0x78, 0x66,
|
||||||
|
0x06, 0xf4, 0x10, 0x00, 0x00,
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Metrics) Marshal() (dAtA []byte, err error) {
|
func (m *Metrics) Marshal() (dAtA []byte, err error) {
|
||||||
@ -819,6 +877,16 @@ func (m *Metrics) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
i += n
|
i += n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if m.CgroupStats != nil {
|
||||||
|
dAtA[i] = 0x42
|
||||||
|
i++
|
||||||
|
i = encodeVarintMetrics(dAtA, i, uint64(m.CgroupStats.Size()))
|
||||||
|
n6, err := m.CgroupStats.MarshalTo(dAtA[i:])
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
i += n6
|
||||||
|
}
|
||||||
if m.XXX_unrecognized != nil {
|
if m.XXX_unrecognized != nil {
|
||||||
i += copy(dAtA[i:], m.XXX_unrecognized)
|
i += copy(dAtA[i:], m.XXX_unrecognized)
|
||||||
}
|
}
|
||||||
@ -917,21 +985,21 @@ func (m *CPUStat) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0xa
|
dAtA[i] = 0xa
|
||||||
i++
|
i++
|
||||||
i = encodeVarintMetrics(dAtA, i, uint64(m.Usage.Size()))
|
i = encodeVarintMetrics(dAtA, i, uint64(m.Usage.Size()))
|
||||||
n6, err := m.Usage.MarshalTo(dAtA[i:])
|
n7, err := m.Usage.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n6
|
i += n7
|
||||||
}
|
}
|
||||||
if m.Throttling != nil {
|
if m.Throttling != nil {
|
||||||
dAtA[i] = 0x12
|
dAtA[i] = 0x12
|
||||||
i++
|
i++
|
||||||
i = encodeVarintMetrics(dAtA, i, uint64(m.Throttling.Size()))
|
i = encodeVarintMetrics(dAtA, i, uint64(m.Throttling.Size()))
|
||||||
n7, err := m.Throttling.MarshalTo(dAtA[i:])
|
n8, err := m.Throttling.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n7
|
i += n8
|
||||||
}
|
}
|
||||||
if m.XXX_unrecognized != nil {
|
if m.XXX_unrecognized != nil {
|
||||||
i += copy(dAtA[i:], m.XXX_unrecognized)
|
i += copy(dAtA[i:], m.XXX_unrecognized)
|
||||||
@ -970,21 +1038,21 @@ func (m *CPUUsage) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
i = encodeVarintMetrics(dAtA, i, uint64(m.User))
|
i = encodeVarintMetrics(dAtA, i, uint64(m.User))
|
||||||
}
|
}
|
||||||
if len(m.PerCPU) > 0 {
|
if len(m.PerCPU) > 0 {
|
||||||
dAtA9 := make([]byte, len(m.PerCPU)*10)
|
dAtA10 := make([]byte, len(m.PerCPU)*10)
|
||||||
var j8 int
|
var j9 int
|
||||||
for _, num := range m.PerCPU {
|
for _, num := range m.PerCPU {
|
||||||
for num >= 1<<7 {
|
for num >= 1<<7 {
|
||||||
dAtA9[j8] = uint8(uint64(num)&0x7f | 0x80)
|
dAtA10[j9] = uint8(uint64(num)&0x7f | 0x80)
|
||||||
num >>= 7
|
num >>= 7
|
||||||
j8++
|
j9++
|
||||||
}
|
}
|
||||||
dAtA9[j8] = uint8(num)
|
dAtA10[j9] = uint8(num)
|
||||||
j8++
|
j9++
|
||||||
}
|
}
|
||||||
dAtA[i] = 0x22
|
dAtA[i] = 0x22
|
||||||
i++
|
i++
|
||||||
i = encodeVarintMetrics(dAtA, i, uint64(j8))
|
i = encodeVarintMetrics(dAtA, i, uint64(j9))
|
||||||
i += copy(dAtA[i:], dAtA9[:j8])
|
i += copy(dAtA[i:], dAtA10[:j9])
|
||||||
}
|
}
|
||||||
if m.XXX_unrecognized != nil {
|
if m.XXX_unrecognized != nil {
|
||||||
i += copy(dAtA[i:], m.XXX_unrecognized)
|
i += copy(dAtA[i:], m.XXX_unrecognized)
|
||||||
@ -1243,11 +1311,11 @@ func (m *MemoryStat) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x2
|
dAtA[i] = 0x2
|
||||||
i++
|
i++
|
||||||
i = encodeVarintMetrics(dAtA, i, uint64(m.Usage.Size()))
|
i = encodeVarintMetrics(dAtA, i, uint64(m.Usage.Size()))
|
||||||
n10, err := m.Usage.MarshalTo(dAtA[i:])
|
n11, err := m.Usage.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n10
|
i += n11
|
||||||
}
|
}
|
||||||
if m.Swap != nil {
|
if m.Swap != nil {
|
||||||
dAtA[i] = 0x92
|
dAtA[i] = 0x92
|
||||||
@ -1255,11 +1323,11 @@ func (m *MemoryStat) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x2
|
dAtA[i] = 0x2
|
||||||
i++
|
i++
|
||||||
i = encodeVarintMetrics(dAtA, i, uint64(m.Swap.Size()))
|
i = encodeVarintMetrics(dAtA, i, uint64(m.Swap.Size()))
|
||||||
n11, err := m.Swap.MarshalTo(dAtA[i:])
|
n12, err := m.Swap.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n11
|
i += n12
|
||||||
}
|
}
|
||||||
if m.Kernel != nil {
|
if m.Kernel != nil {
|
||||||
dAtA[i] = 0x9a
|
dAtA[i] = 0x9a
|
||||||
@ -1267,11 +1335,11 @@ func (m *MemoryStat) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x2
|
dAtA[i] = 0x2
|
||||||
i++
|
i++
|
||||||
i = encodeVarintMetrics(dAtA, i, uint64(m.Kernel.Size()))
|
i = encodeVarintMetrics(dAtA, i, uint64(m.Kernel.Size()))
|
||||||
n12, err := m.Kernel.MarshalTo(dAtA[i:])
|
n13, err := m.Kernel.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n12
|
i += n13
|
||||||
}
|
}
|
||||||
if m.KernelTCP != nil {
|
if m.KernelTCP != nil {
|
||||||
dAtA[i] = 0xa2
|
dAtA[i] = 0xa2
|
||||||
@ -1279,11 +1347,11 @@ func (m *MemoryStat) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
dAtA[i] = 0x2
|
dAtA[i] = 0x2
|
||||||
i++
|
i++
|
||||||
i = encodeVarintMetrics(dAtA, i, uint64(m.KernelTCP.Size()))
|
i = encodeVarintMetrics(dAtA, i, uint64(m.KernelTCP.Size()))
|
||||||
n13, err := m.KernelTCP.MarshalTo(dAtA[i:])
|
n14, err := m.KernelTCP.MarshalTo(dAtA[i:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
i += n13
|
i += n14
|
||||||
}
|
}
|
||||||
if m.XXX_unrecognized != nil {
|
if m.XXX_unrecognized != nil {
|
||||||
i += copy(dAtA[i:], m.XXX_unrecognized)
|
i += copy(dAtA[i:], m.XXX_unrecognized)
|
||||||
@ -1646,6 +1714,52 @@ func (m *NetworkStat) MarshalTo(dAtA []byte) (int, error) {
|
|||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *CgroupStats) Marshal() (dAtA []byte, err error) {
|
||||||
|
size := m.Size()
|
||||||
|
dAtA = make([]byte, size)
|
||||||
|
n, err := m.MarshalTo(dAtA)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return dAtA[:n], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *CgroupStats) MarshalTo(dAtA []byte) (int, error) {
|
||||||
|
var i int
|
||||||
|
_ = i
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if m.NrSleeping != 0 {
|
||||||
|
dAtA[i] = 0x8
|
||||||
|
i++
|
||||||
|
i = encodeVarintMetrics(dAtA, i, uint64(m.NrSleeping))
|
||||||
|
}
|
||||||
|
if m.NrRunning != 0 {
|
||||||
|
dAtA[i] = 0x10
|
||||||
|
i++
|
||||||
|
i = encodeVarintMetrics(dAtA, i, uint64(m.NrRunning))
|
||||||
|
}
|
||||||
|
if m.NrStopped != 0 {
|
||||||
|
dAtA[i] = 0x18
|
||||||
|
i++
|
||||||
|
i = encodeVarintMetrics(dAtA, i, uint64(m.NrStopped))
|
||||||
|
}
|
||||||
|
if m.NrUninterruptible != 0 {
|
||||||
|
dAtA[i] = 0x20
|
||||||
|
i++
|
||||||
|
i = encodeVarintMetrics(dAtA, i, uint64(m.NrUninterruptible))
|
||||||
|
}
|
||||||
|
if m.NrIoWait != 0 {
|
||||||
|
dAtA[i] = 0x28
|
||||||
|
i++
|
||||||
|
i = encodeVarintMetrics(dAtA, i, uint64(m.NrIoWait))
|
||||||
|
}
|
||||||
|
if m.XXX_unrecognized != nil {
|
||||||
|
i += copy(dAtA[i:], m.XXX_unrecognized)
|
||||||
|
}
|
||||||
|
return i, nil
|
||||||
|
}
|
||||||
|
|
||||||
func encodeVarintMetrics(dAtA []byte, offset int, v uint64) int {
|
func encodeVarintMetrics(dAtA []byte, offset int, v uint64) int {
|
||||||
for v >= 1<<7 {
|
for v >= 1<<7 {
|
||||||
dAtA[offset] = uint8(v&0x7f | 0x80)
|
dAtA[offset] = uint8(v&0x7f | 0x80)
|
||||||
@ -1693,6 +1807,10 @@ func (m *Metrics) Size() (n int) {
|
|||||||
n += 1 + l + sovMetrics(uint64(l))
|
n += 1 + l + sovMetrics(uint64(l))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if m.CgroupStats != nil {
|
||||||
|
l = m.CgroupStats.Size()
|
||||||
|
n += 1 + l + sovMetrics(uint64(l))
|
||||||
|
}
|
||||||
if m.XXX_unrecognized != nil {
|
if m.XXX_unrecognized != nil {
|
||||||
n += len(m.XXX_unrecognized)
|
n += len(m.XXX_unrecognized)
|
||||||
}
|
}
|
||||||
@ -2134,6 +2252,33 @@ func (m *NetworkStat) Size() (n int) {
|
|||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *CgroupStats) Size() (n int) {
|
||||||
|
if m == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
var l int
|
||||||
|
_ = l
|
||||||
|
if m.NrSleeping != 0 {
|
||||||
|
n += 1 + sovMetrics(uint64(m.NrSleeping))
|
||||||
|
}
|
||||||
|
if m.NrRunning != 0 {
|
||||||
|
n += 1 + sovMetrics(uint64(m.NrRunning))
|
||||||
|
}
|
||||||
|
if m.NrStopped != 0 {
|
||||||
|
n += 1 + sovMetrics(uint64(m.NrStopped))
|
||||||
|
}
|
||||||
|
if m.NrUninterruptible != 0 {
|
||||||
|
n += 1 + sovMetrics(uint64(m.NrUninterruptible))
|
||||||
|
}
|
||||||
|
if m.NrIoWait != 0 {
|
||||||
|
n += 1 + sovMetrics(uint64(m.NrIoWait))
|
||||||
|
}
|
||||||
|
if m.XXX_unrecognized != nil {
|
||||||
|
n += len(m.XXX_unrecognized)
|
||||||
|
}
|
||||||
|
return n
|
||||||
|
}
|
||||||
|
|
||||||
func sovMetrics(x uint64) (n int) {
|
func sovMetrics(x uint64) (n int) {
|
||||||
for {
|
for {
|
||||||
n++
|
n++
|
||||||
@ -2159,6 +2304,7 @@ func (this *Metrics) String() string {
|
|||||||
`Blkio:` + strings.Replace(fmt.Sprintf("%v", this.Blkio), "BlkIOStat", "BlkIOStat", 1) + `,`,
|
`Blkio:` + strings.Replace(fmt.Sprintf("%v", this.Blkio), "BlkIOStat", "BlkIOStat", 1) + `,`,
|
||||||
`Rdma:` + strings.Replace(fmt.Sprintf("%v", this.Rdma), "RdmaStat", "RdmaStat", 1) + `,`,
|
`Rdma:` + strings.Replace(fmt.Sprintf("%v", this.Rdma), "RdmaStat", "RdmaStat", 1) + `,`,
|
||||||
`Network:` + strings.Replace(fmt.Sprintf("%v", this.Network), "NetworkStat", "NetworkStat", 1) + `,`,
|
`Network:` + strings.Replace(fmt.Sprintf("%v", this.Network), "NetworkStat", "NetworkStat", 1) + `,`,
|
||||||
|
`CgroupStats:` + strings.Replace(fmt.Sprintf("%v", this.CgroupStats), "CgroupStats", "CgroupStats", 1) + `,`,
|
||||||
`XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`,
|
`XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`,
|
||||||
`}`,
|
`}`,
|
||||||
}, "")
|
}, "")
|
||||||
@ -2366,6 +2512,21 @@ func (this *NetworkStat) String() string {
|
|||||||
}, "")
|
}, "")
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
func (this *CgroupStats) String() string {
|
||||||
|
if this == nil {
|
||||||
|
return "nil"
|
||||||
|
}
|
||||||
|
s := strings.Join([]string{`&CgroupStats{`,
|
||||||
|
`NrSleeping:` + fmt.Sprintf("%v", this.NrSleeping) + `,`,
|
||||||
|
`NrRunning:` + fmt.Sprintf("%v", this.NrRunning) + `,`,
|
||||||
|
`NrStopped:` + fmt.Sprintf("%v", this.NrStopped) + `,`,
|
||||||
|
`NrUninterruptible:` + fmt.Sprintf("%v", this.NrUninterruptible) + `,`,
|
||||||
|
`NrIoWait:` + fmt.Sprintf("%v", this.NrIoWait) + `,`,
|
||||||
|
`XXX_unrecognized:` + fmt.Sprintf("%v", this.XXX_unrecognized) + `,`,
|
||||||
|
`}`,
|
||||||
|
}, "")
|
||||||
|
return s
|
||||||
|
}
|
||||||
func valueToStringMetrics(v interface{}) string {
|
func valueToStringMetrics(v interface{}) string {
|
||||||
rv := reflect.ValueOf(v)
|
rv := reflect.ValueOf(v)
|
||||||
if rv.IsNil() {
|
if rv.IsNil() {
|
||||||
@ -2651,6 +2812,42 @@ func (m *Metrics) Unmarshal(dAtA []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
iNdEx = postIndex
|
iNdEx = postIndex
|
||||||
|
case 8:
|
||||||
|
if wireType != 2 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field CgroupStats", wireType)
|
||||||
|
}
|
||||||
|
var msglen int
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
msglen |= int(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if msglen < 0 {
|
||||||
|
return ErrInvalidLengthMetrics
|
||||||
|
}
|
||||||
|
postIndex := iNdEx + msglen
|
||||||
|
if postIndex < 0 {
|
||||||
|
return ErrInvalidLengthMetrics
|
||||||
|
}
|
||||||
|
if postIndex > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
if m.CgroupStats == nil {
|
||||||
|
m.CgroupStats = &CgroupStats{}
|
||||||
|
}
|
||||||
|
if err := m.CgroupStats.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
iNdEx = postIndex
|
||||||
default:
|
default:
|
||||||
iNdEx = preIndex
|
iNdEx = preIndex
|
||||||
skippy, err := skipMetrics(dAtA[iNdEx:])
|
skippy, err := skipMetrics(dAtA[iNdEx:])
|
||||||
@ -5256,6 +5453,155 @@ func (m *NetworkStat) Unmarshal(dAtA []byte) error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (m *CgroupStats) Unmarshal(dAtA []byte) error {
|
||||||
|
l := len(dAtA)
|
||||||
|
iNdEx := 0
|
||||||
|
for iNdEx < l {
|
||||||
|
preIndex := iNdEx
|
||||||
|
var wire uint64
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
wire |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldNum := int32(wire >> 3)
|
||||||
|
wireType := int(wire & 0x7)
|
||||||
|
if wireType == 4 {
|
||||||
|
return fmt.Errorf("proto: CgroupStats: wiretype end group for non-group")
|
||||||
|
}
|
||||||
|
if fieldNum <= 0 {
|
||||||
|
return fmt.Errorf("proto: CgroupStats: illegal tag %d (wire type %d)", fieldNum, wire)
|
||||||
|
}
|
||||||
|
switch fieldNum {
|
||||||
|
case 1:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field NrSleeping", wireType)
|
||||||
|
}
|
||||||
|
m.NrSleeping = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.NrSleeping |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 2:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field NrRunning", wireType)
|
||||||
|
}
|
||||||
|
m.NrRunning = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.NrRunning |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field NrStopped", wireType)
|
||||||
|
}
|
||||||
|
m.NrStopped = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.NrStopped |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 4:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field NrUninterruptible", wireType)
|
||||||
|
}
|
||||||
|
m.NrUninterruptible = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.NrUninterruptible |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 5:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field NrIoWait", wireType)
|
||||||
|
}
|
||||||
|
m.NrIoWait = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if shift >= 64 {
|
||||||
|
return ErrIntOverflowMetrics
|
||||||
|
}
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := dAtA[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.NrIoWait |= uint64(b&0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
iNdEx = preIndex
|
||||||
|
skippy, err := skipMetrics(dAtA[iNdEx:])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if skippy < 0 {
|
||||||
|
return ErrInvalidLengthMetrics
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) < 0 {
|
||||||
|
return ErrInvalidLengthMetrics
|
||||||
|
}
|
||||||
|
if (iNdEx + skippy) > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
m.XXX_unrecognized = append(m.XXX_unrecognized, dAtA[iNdEx:iNdEx+skippy]...)
|
||||||
|
iNdEx += skippy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if iNdEx > l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func skipMetrics(dAtA []byte) (n int, err error) {
|
func skipMetrics(dAtA []byte) (n int, err error) {
|
||||||
l := len(dAtA)
|
l := len(dAtA)
|
||||||
iNdEx := 0
|
iNdEx := 0
|
||||||
|
15
vendor/github.com/containerd/cgroups/stats/v1/metrics.proto
generated
vendored
15
vendor/github.com/containerd/cgroups/stats/v1/metrics.proto
generated
vendored
@ -12,6 +12,7 @@ message Metrics {
|
|||||||
BlkIOStat blkio = 5;
|
BlkIOStat blkio = 5;
|
||||||
RdmaStat rdma = 6;
|
RdmaStat rdma = 6;
|
||||||
repeated NetworkStat network = 7;
|
repeated NetworkStat network = 7;
|
||||||
|
CgroupStats cgroup_stats = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message HugetlbStat {
|
message HugetlbStat {
|
||||||
@ -134,3 +135,17 @@ message NetworkStat {
|
|||||||
uint64 tx_errors = 8;
|
uint64 tx_errors = 8;
|
||||||
uint64 tx_dropped = 9;
|
uint64 tx_dropped = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CgroupStats exports per-cgroup statistics.
|
||||||
|
message CgroupStats {
|
||||||
|
// number of tasks sleeping
|
||||||
|
uint64 nr_sleeping = 1;
|
||||||
|
// number of tasks running
|
||||||
|
uint64 nr_running = 2;
|
||||||
|
// number of tasks in stopped state
|
||||||
|
uint64 nr_stopped = 3;
|
||||||
|
// number of tasks in uninterruptible state
|
||||||
|
uint64 nr_uninterruptible = 4;
|
||||||
|
// number of tasks waiting on IO
|
||||||
|
uint64 nr_io_wait = 5;
|
||||||
|
}
|
||||||
|
48
vendor/github.com/containerd/cgroups/utils.go
generated
vendored
48
vendor/github.com/containerd/cgroups/utils.go
generated
vendored
@ -25,13 +25,59 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
units "github.com/docker/go-units"
|
units "github.com/docker/go-units"
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
var isUserNS = runningInUserNS()
|
var (
|
||||||
|
isUserNS = runningInUserNS()
|
||||||
|
checkMode sync.Once
|
||||||
|
cgMode CGMode
|
||||||
|
)
|
||||||
|
|
||||||
|
const unifiedMountpoint = "/sys/fs/cgroup"
|
||||||
|
|
||||||
|
// CGMode is the cgroups mode of the host system
|
||||||
|
type CGMode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Unavailable cgroup mountpoint
|
||||||
|
Unavailable CGMode = iota
|
||||||
|
// Legacy cgroups v1
|
||||||
|
Legacy
|
||||||
|
// Hybrid with cgroups v1 and v2 controllers mounted
|
||||||
|
Hybrid
|
||||||
|
// Unified with only cgroups v2 mounted
|
||||||
|
Unified
|
||||||
|
)
|
||||||
|
|
||||||
|
// Mode returns the cgroups mode running on the host
|
||||||
|
func Mode() CGMode {
|
||||||
|
checkMode.Do(func() {
|
||||||
|
var st unix.Statfs_t
|
||||||
|
if err := unix.Statfs(unifiedMountpoint, &st); err != nil {
|
||||||
|
cgMode = Unavailable
|
||||||
|
return
|
||||||
|
}
|
||||||
|
switch st.Type {
|
||||||
|
case unix.CGROUP2_SUPER_MAGIC:
|
||||||
|
cgMode = Unified
|
||||||
|
default:
|
||||||
|
cgMode = Legacy
|
||||||
|
if err := unix.Statfs(filepath.Join(unifiedMountpoint, "unified"), &st); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if st.Type == unix.CGROUP2_SUPER_MAGIC {
|
||||||
|
cgMode = Hybrid
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return cgMode
|
||||||
|
}
|
||||||
|
|
||||||
// runningInUserNS detects whether we are currently running in a user namespace.
|
// runningInUserNS detects whether we are currently running in a user namespace.
|
||||||
// Copied from github.com/lxc/lxd/shared/util.go
|
// Copied from github.com/lxc/lxd/shared/util.go
|
||||||
|
52
vendor/github.com/containerd/cgroups/v2/cpu.go
generated
vendored
Normal file
52
vendor/github.com/containerd/cgroups/v2/cpu.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
type CPU struct {
|
||||||
|
Weight *uint64
|
||||||
|
Max *uint64
|
||||||
|
Cpus string
|
||||||
|
Mems string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *CPU) Values() (o []Value) {
|
||||||
|
if r.Weight != nil {
|
||||||
|
o = append(o, Value{
|
||||||
|
filename: "cpu.weight",
|
||||||
|
value: *r.Weight,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if r.Max != nil {
|
||||||
|
o = append(o, Value{
|
||||||
|
filename: "cpu.max",
|
||||||
|
value: *r.Max,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if r.Cpus != "" {
|
||||||
|
o = append(o, Value{
|
||||||
|
filename: "cpuset.cpus",
|
||||||
|
value: r.Cpus,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if r.Mems != "" {
|
||||||
|
o = append(o, Value{
|
||||||
|
filename: "cpuset.mems",
|
||||||
|
value: r.Mems,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
199
vendor/github.com/containerd/cgroups/v2/devicefilter.go
generated
vendored
Normal file
199
vendor/github.com/containerd/cgroups/v2/devicefilter.go
generated
vendored
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Devicefilter containes eBPF device filter program
|
||||||
|
//
|
||||||
|
// The implementation is based on https://github.com/containers/crun/blob/0.10.2/src/libcrun/ebpf.c
|
||||||
|
//
|
||||||
|
// Although ebpf.c is originally licensed under LGPL-3.0-or-later, the author (Giuseppe Scrivano)
|
||||||
|
// agreed to relicense the file in Apache License 2.0: https://github.com/opencontainers/runc/issues/2144#issuecomment-543116397
|
||||||
|
//
|
||||||
|
// This particular Go implementation based on runc version
|
||||||
|
// https://github.com/opencontainers/runc/blob/master/libcontainer/cgroups/ebpf/devicefilter/devicefilter.go
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
|
||||||
|
"github.com/cilium/ebpf/asm"
|
||||||
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// license string format is same as kernel MODULE_LICENSE macro
|
||||||
|
license = "Apache"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeviceFilter returns eBPF device filter program and its license string
|
||||||
|
func DeviceFilter(devices []specs.LinuxDeviceCgroup) (asm.Instructions, string, error) {
|
||||||
|
p := &program{}
|
||||||
|
p.init()
|
||||||
|
for i := len(devices) - 1; i >= 0; i-- {
|
||||||
|
if err := p.appendDevice(devices[i]); err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insts, err := p.finalize()
|
||||||
|
return insts, license, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type program struct {
|
||||||
|
insts asm.Instructions
|
||||||
|
hasWildCard bool
|
||||||
|
blockID int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *program) init() {
|
||||||
|
// struct bpf_cgroup_dev_ctx: https://elixir.bootlin.com/linux/v5.3.6/source/include/uapi/linux/bpf.h#L3423
|
||||||
|
/*
|
||||||
|
u32 access_type
|
||||||
|
u32 major
|
||||||
|
u32 minor
|
||||||
|
*/
|
||||||
|
// R2 <- type (lower 16 bit of u32 access_type at R1[0])
|
||||||
|
p.insts = append(p.insts,
|
||||||
|
asm.LoadMem(asm.R2, asm.R1, 0, asm.Half))
|
||||||
|
|
||||||
|
// R3 <- access (upper 16 bit of u32 access_type at R1[0])
|
||||||
|
p.insts = append(p.insts,
|
||||||
|
asm.LoadMem(asm.R3, asm.R1, 0, asm.Word),
|
||||||
|
// RSh: bitwise shift right
|
||||||
|
asm.RSh.Imm32(asm.R3, 16))
|
||||||
|
|
||||||
|
// R4 <- major (u32 major at R1[4])
|
||||||
|
p.insts = append(p.insts,
|
||||||
|
asm.LoadMem(asm.R4, asm.R1, 4, asm.Word))
|
||||||
|
|
||||||
|
// R5 <- minor (u32 minor at R1[8])
|
||||||
|
p.insts = append(p.insts,
|
||||||
|
asm.LoadMem(asm.R5, asm.R1, 8, asm.Word))
|
||||||
|
}
|
||||||
|
|
||||||
|
// appendDevice needs to be called from the last element of OCI linux.resources.devices to the head element.
|
||||||
|
func (p *program) appendDevice(dev specs.LinuxDeviceCgroup) error {
|
||||||
|
if p.blockID < 0 {
|
||||||
|
return errors.New("the program is finalized")
|
||||||
|
}
|
||||||
|
if p.hasWildCard {
|
||||||
|
// All entries after wildcard entry are ignored
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
bpfType := int32(-1)
|
||||||
|
hasType := true
|
||||||
|
switch dev.Type {
|
||||||
|
case string('c'):
|
||||||
|
bpfType = int32(unix.BPF_DEVCG_DEV_CHAR)
|
||||||
|
case string('b'):
|
||||||
|
bpfType = int32(unix.BPF_DEVCG_DEV_BLOCK)
|
||||||
|
case string('a'):
|
||||||
|
hasType = false
|
||||||
|
default:
|
||||||
|
// if not specified in OCI json, typ is set to DeviceTypeAll
|
||||||
|
return errors.Errorf("invalid DeviceType %q", dev.Type)
|
||||||
|
}
|
||||||
|
if *dev.Major > math.MaxUint32 {
|
||||||
|
return errors.Errorf("invalid major %d", *dev.Major)
|
||||||
|
}
|
||||||
|
if *dev.Minor > math.MaxUint32 {
|
||||||
|
return errors.Errorf("invalid minor %d", *dev.Major)
|
||||||
|
}
|
||||||
|
hasMajor := *dev.Major >= 0 // if not specified in OCI json, major is set to -1
|
||||||
|
hasMinor := *dev.Minor >= 0
|
||||||
|
bpfAccess := int32(0)
|
||||||
|
for _, r := range dev.Access {
|
||||||
|
switch r {
|
||||||
|
case 'r':
|
||||||
|
bpfAccess |= unix.BPF_DEVCG_ACC_READ
|
||||||
|
case 'w':
|
||||||
|
bpfAccess |= unix.BPF_DEVCG_ACC_WRITE
|
||||||
|
case 'm':
|
||||||
|
bpfAccess |= unix.BPF_DEVCG_ACC_MKNOD
|
||||||
|
default:
|
||||||
|
return errors.Errorf("unknown device access %v", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If the access is rwm, skip the check.
|
||||||
|
hasAccess := bpfAccess != (unix.BPF_DEVCG_ACC_READ | unix.BPF_DEVCG_ACC_WRITE | unix.BPF_DEVCG_ACC_MKNOD)
|
||||||
|
|
||||||
|
blockSym := fmt.Sprintf("block-%d", p.blockID)
|
||||||
|
nextBlockSym := fmt.Sprintf("block-%d", p.blockID+1)
|
||||||
|
prevBlockLastIdx := len(p.insts) - 1
|
||||||
|
if hasType {
|
||||||
|
p.insts = append(p.insts,
|
||||||
|
// if (R2 != bpfType) goto next
|
||||||
|
asm.JNE.Imm(asm.R2, bpfType, nextBlockSym),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if hasAccess {
|
||||||
|
p.insts = append(p.insts,
|
||||||
|
// if (R3 & bpfAccess == 0 /* use R1 as a temp var */) goto next
|
||||||
|
asm.Mov.Reg32(asm.R1, asm.R3),
|
||||||
|
asm.And.Imm32(asm.R1, bpfAccess),
|
||||||
|
asm.JEq.Imm(asm.R1, 0, nextBlockSym),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if hasMajor {
|
||||||
|
p.insts = append(p.insts,
|
||||||
|
// if (R4 != major) goto next
|
||||||
|
asm.JNE.Imm(asm.R4, int32(*dev.Major), nextBlockSym),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if hasMinor {
|
||||||
|
p.insts = append(p.insts,
|
||||||
|
// if (R5 != minor) goto next
|
||||||
|
asm.JNE.Imm(asm.R5, int32(*dev.Minor), nextBlockSym),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if !hasType && !hasAccess && !hasMajor && !hasMinor {
|
||||||
|
p.hasWildCard = true
|
||||||
|
}
|
||||||
|
p.insts = append(p.insts, acceptBlock(dev.Allow)...)
|
||||||
|
// set blockSym to the first instruction we added in this iteration
|
||||||
|
p.insts[prevBlockLastIdx+1] = p.insts[prevBlockLastIdx+1].Sym(blockSym)
|
||||||
|
p.blockID++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *program) finalize() (asm.Instructions, error) {
|
||||||
|
if p.hasWildCard {
|
||||||
|
// acceptBlock with asm.Return() is already inserted
|
||||||
|
return p.insts, nil
|
||||||
|
}
|
||||||
|
blockSym := fmt.Sprintf("block-%d", p.blockID)
|
||||||
|
p.insts = append(p.insts,
|
||||||
|
// R0 <- 0
|
||||||
|
asm.Mov.Imm32(asm.R0, 0).Sym(blockSym),
|
||||||
|
asm.Return(),
|
||||||
|
)
|
||||||
|
p.blockID = -1
|
||||||
|
return p.insts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func acceptBlock(accept bool) asm.Instructions {
|
||||||
|
v := int32(0)
|
||||||
|
if accept {
|
||||||
|
v = 1
|
||||||
|
}
|
||||||
|
return []asm.Instruction{
|
||||||
|
// R0 <- v
|
||||||
|
asm.Mov.Imm32(asm.R0, v),
|
||||||
|
asm.Return(),
|
||||||
|
}
|
||||||
|
}
|
83
vendor/github.com/containerd/cgroups/v2/ebpf.go
generated
vendored
Normal file
83
vendor/github.com/containerd/cgroups/v2/ebpf.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/cilium/ebpf"
|
||||||
|
"github.com/cilium/ebpf/asm"
|
||||||
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoadAttachCgroupDeviceFilter installs eBPF device filter program to /sys/fs/cgroup/<foo> directory.
|
||||||
|
//
|
||||||
|
// Requires the system to be running in cgroup2 unified-mode with kernel >= 4.15 .
|
||||||
|
//
|
||||||
|
// https://github.com/torvalds/linux/commit/ebc614f687369f9df99828572b1d85a7c2de3d92
|
||||||
|
func LoadAttachCgroupDeviceFilter(insts asm.Instructions, license string, dirFD int) (func() error, error) {
|
||||||
|
nilCloser := func() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
spec := &ebpf.ProgramSpec{
|
||||||
|
Type: ebpf.CGroupDevice,
|
||||||
|
Instructions: insts,
|
||||||
|
License: license,
|
||||||
|
}
|
||||||
|
prog, err := ebpf.NewProgram(spec)
|
||||||
|
if err != nil {
|
||||||
|
return nilCloser, err
|
||||||
|
}
|
||||||
|
if err := prog.Attach(dirFD, ebpf.AttachCGroupDevice, unix.BPF_F_ALLOW_MULTI); err != nil {
|
||||||
|
return nilCloser, errors.Wrap(err, "failed to call BPF_PROG_ATTACH (BPF_CGROUP_DEVICE, BPF_F_ALLOW_MULTI)")
|
||||||
|
}
|
||||||
|
closer := func() error {
|
||||||
|
if err := prog.Detach(dirFD, ebpf.AttachCGroupDevice, unix.BPF_F_ALLOW_MULTI); err != nil {
|
||||||
|
return errors.Wrap(err, "failed to call BPF_PROG_DETACH (BPF_CGROUP_DEVICE, BPF_F_ALLOW_MULTI)")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return closer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isRWM(cgroupPermissions string) bool {
|
||||||
|
r := false
|
||||||
|
w := false
|
||||||
|
m := false
|
||||||
|
for _, rn := range cgroupPermissions {
|
||||||
|
switch rn {
|
||||||
|
case 'r':
|
||||||
|
r = true
|
||||||
|
case 'w':
|
||||||
|
w = true
|
||||||
|
case 'm':
|
||||||
|
m = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r && w && m
|
||||||
|
}
|
||||||
|
|
||||||
|
// the logic is from runc
|
||||||
|
// https://github.com/opencontainers/runc/blob/master/libcontainer/cgroups/fs/devices_v2.go#L44
|
||||||
|
func canSkipEBPFError(devices []specs.LinuxDeviceCgroup) bool {
|
||||||
|
for _, dev := range devices {
|
||||||
|
if dev.Allow || !isRWM(dev.Access) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
50
vendor/github.com/containerd/cgroups/v2/errors.go
generated
vendored
Normal file
50
vendor/github.com/containerd/cgroups/v2/errors.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidPid = errors.New("cgroups: pid must be greater than 0")
|
||||||
|
ErrMountPointNotExist = errors.New("cgroups: cgroup mountpoint does not exist")
|
||||||
|
ErrInvalidFormat = errors.New("cgroups: parsing file with invalid format failed")
|
||||||
|
ErrFreezerNotSupported = errors.New("cgroups: freezer cgroup (v2) not supported on this system")
|
||||||
|
ErrMemoryNotSupported = errors.New("cgroups: memory cgroup (v2) not supported on this system")
|
||||||
|
ErrPidsNotSupported = errors.New("cgroups: pids cgroup (v2) not supported on this system")
|
||||||
|
ErrCPUNotSupported = errors.New("cgroups: cpu cgroup (v2) not supported on this system")
|
||||||
|
ErrCgroupDeleted = errors.New("cgroups: cgroup deleted")
|
||||||
|
ErrNoCgroupMountDestination = errors.New("cgroups: cannot find cgroup mount destination")
|
||||||
|
ErrInvalidGroupPath = errors.New("cgroups: invalid group path")
|
||||||
|
)
|
||||||
|
|
||||||
|
// ErrorHandler is a function that handles and acts on errors
|
||||||
|
type ErrorHandler func(err error) error
|
||||||
|
|
||||||
|
// IgnoreNotExist ignores any errors that are for not existing files
|
||||||
|
func IgnoreNotExist(err error) error {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func errPassthrough(err error) error {
|
||||||
|
return err
|
||||||
|
}
|
64
vendor/github.com/containerd/cgroups/v2/io.go
generated
vendored
Normal file
64
vendor/github.com/containerd/cgroups/v2/io.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type IOType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ReadBPS IOType = "rbps"
|
||||||
|
WriteBPS IOType = "wbps"
|
||||||
|
ReadIOPS IOType = "riops"
|
||||||
|
WriteIOPS IOType = "wiops"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BFQ struct {
|
||||||
|
Weight uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
type Entry struct {
|
||||||
|
Type IOType
|
||||||
|
Major int64
|
||||||
|
Minor int64
|
||||||
|
Rate uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e Entry) String() string {
|
||||||
|
return fmt.Sprintf("%d:%d %s=%d", e.Major, e.Minor, e.Type, e.Rate)
|
||||||
|
}
|
||||||
|
|
||||||
|
type IO struct {
|
||||||
|
BFQ BFQ
|
||||||
|
Max []Entry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *IO) Values() (o []Value) {
|
||||||
|
if i.BFQ.Weight != 0 {
|
||||||
|
o = append(o, Value{
|
||||||
|
filename: "io.bfq.weight",
|
||||||
|
value: i.BFQ.Weight,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for _, e := range i.Max {
|
||||||
|
o = append(o, Value{
|
||||||
|
filename: "io.max",
|
||||||
|
value: e.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
552
vendor/github.com/containerd/cgroups/v2/manager.go
generated
vendored
Normal file
552
vendor/github.com/containerd/cgroups/v2/manager.go
generated
vendored
Normal file
@ -0,0 +1,552 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
"github.com/containerd/cgroups/v2/stats"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
subtreeControl = "cgroup.subtree_control"
|
||||||
|
controllersFile = "cgroup.controllers"
|
||||||
|
)
|
||||||
|
|
||||||
|
type cgValuer interface {
|
||||||
|
Values() []Value
|
||||||
|
}
|
||||||
|
|
||||||
|
type Event struct {
|
||||||
|
Low uint64
|
||||||
|
High uint64
|
||||||
|
Max uint64
|
||||||
|
OOM uint64
|
||||||
|
OOMKill uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resources for a cgroups v2 unified hierarchy
|
||||||
|
type Resources struct {
|
||||||
|
CPU *CPU
|
||||||
|
Memory *Memory
|
||||||
|
Pids *Pids
|
||||||
|
IO *IO
|
||||||
|
RDMA *RDMA
|
||||||
|
// When len(Devices) is zero, devices are not controlled
|
||||||
|
Devices []specs.LinuxDeviceCgroup
|
||||||
|
}
|
||||||
|
|
||||||
|
// Values returns the raw filenames and values that
|
||||||
|
// can be written to the unified hierarchy
|
||||||
|
func (r *Resources) Values() (o []Value) {
|
||||||
|
values := []cgValuer{
|
||||||
|
r.CPU,
|
||||||
|
r.Memory,
|
||||||
|
r.Pids,
|
||||||
|
r.IO,
|
||||||
|
r.RDMA,
|
||||||
|
}
|
||||||
|
for _, v := range values {
|
||||||
|
if v == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
o = append(o, v.Values()...)
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value of a cgroup setting
|
||||||
|
type Value struct {
|
||||||
|
filename string
|
||||||
|
value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the value to the full, absolute path, of a unified hierarchy
|
||||||
|
func (c *Value) write(path string, perm os.FileMode) error {
|
||||||
|
var data []byte
|
||||||
|
switch t := c.value.(type) {
|
||||||
|
case uint64:
|
||||||
|
data = []byte(strconv.FormatUint(t, 10))
|
||||||
|
case uint16:
|
||||||
|
data = []byte(strconv.FormatUint(uint64(t), 10))
|
||||||
|
case int64:
|
||||||
|
data = []byte(strconv.FormatInt(t, 10))
|
||||||
|
case []byte:
|
||||||
|
data = t
|
||||||
|
case string:
|
||||||
|
data = []byte(t)
|
||||||
|
default:
|
||||||
|
return ErrInvalidFormat
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(
|
||||||
|
filepath.Join(path, c.filename),
|
||||||
|
data,
|
||||||
|
perm,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeValues(path string, values []Value) error {
|
||||||
|
for _, o := range values {
|
||||||
|
if err := o.write(path, defaultFilePerm); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewManager(mountpoint string, group string, resources *Resources) (*Manager, error) {
|
||||||
|
if err := VerifyGroupPath(group); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
path := filepath.Join(mountpoint, group)
|
||||||
|
if err := os.MkdirAll(path, defaultDirPerm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := setResources(path, resources); err != nil {
|
||||||
|
// clean up cgroup dir on failure
|
||||||
|
os.Remove(path)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Manager{
|
||||||
|
unifiedMountpoint: mountpoint,
|
||||||
|
path: path,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadManager(mountpoint string, group string) (*Manager, error) {
|
||||||
|
if err := VerifyGroupPath(group); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
path := filepath.Join(mountpoint, group)
|
||||||
|
return &Manager{
|
||||||
|
unifiedMountpoint: mountpoint,
|
||||||
|
path: path,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Manager struct {
|
||||||
|
unifiedMountpoint string
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
func setResources(path string, resources *Resources) error {
|
||||||
|
if resources != nil {
|
||||||
|
if err := writeValues(path, resources.Values()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := setDevices(path, resources.Devices); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) RootControllers() ([]string, error) {
|
||||||
|
b, err := ioutil.ReadFile(filepath.Join(c.unifiedMountpoint, controllersFile))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return strings.Fields(string(b)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) Controllers() ([]string, error) {
|
||||||
|
b, err := ioutil.ReadFile(filepath.Join(c.path, controllersFile))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return strings.Fields(string(b)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ControllerToggle int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Enable ControllerToggle = iota + 1
|
||||||
|
Disable
|
||||||
|
)
|
||||||
|
|
||||||
|
func toggleFunc(controllers []string, prefix string) []string {
|
||||||
|
out := make([]string, len(controllers))
|
||||||
|
for i, c := range controllers {
|
||||||
|
out[i] = prefix + c
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) ToggleControllers(controllers []string, t ControllerToggle) error {
|
||||||
|
// when c.path is like /foo/bar/baz, the following files need to be written:
|
||||||
|
// * /sys/fs/cgroup/cgroup.subtree_control
|
||||||
|
// * /sys/fs/cgroup/foo/cgroup.subtree_control
|
||||||
|
// * /sys/fs/cgroup/foo/bar/cgroup.subtree_control
|
||||||
|
// Note that /sys/fs/cgroup/foo/bar/baz/cgroup.subtree_control does not need to be written.
|
||||||
|
split := strings.Split(c.path, "/")
|
||||||
|
var lastErr error
|
||||||
|
for i, _ := range split {
|
||||||
|
f := strings.Join(split[:i], "/")
|
||||||
|
if !strings.HasPrefix(f, c.unifiedMountpoint) || f == c.path {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filePath := filepath.Join(f, subtreeControl)
|
||||||
|
if err := c.writeSubtreeControl(filePath, controllers, t); err != nil {
|
||||||
|
// When running as rootless, the user may face EPERM on parent groups, but it is neglible when the
|
||||||
|
// controller is already written.
|
||||||
|
// So we only return the last error.
|
||||||
|
lastErr = errors.Wrapf(err, "failed to write subtree controllers %+v to %q", controllers, filePath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lastErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) writeSubtreeControl(filePath string, controllers []string, t ControllerToggle) error {
|
||||||
|
f, err := os.OpenFile(filePath, os.O_WRONLY, 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
switch t {
|
||||||
|
case Enable:
|
||||||
|
controllers = toggleFunc(controllers, "+")
|
||||||
|
case Disable:
|
||||||
|
controllers = toggleFunc(controllers, "-")
|
||||||
|
}
|
||||||
|
_, err = f.WriteString(strings.Join(controllers, " "))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) NewChild(name string, resources *Resources) (*Manager, error) {
|
||||||
|
if strings.HasPrefix(name, "/") {
|
||||||
|
return nil, errors.New("name must be relative")
|
||||||
|
}
|
||||||
|
path := filepath.Join(c.path, name)
|
||||||
|
if err := os.MkdirAll(path, defaultDirPerm); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := setResources(path, resources); err != nil {
|
||||||
|
// clean up cgroup dir on failure
|
||||||
|
os.Remove(path)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &Manager{
|
||||||
|
unifiedMountpoint: c.unifiedMountpoint,
|
||||||
|
path: path,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) AddProc(pid uint64) error {
|
||||||
|
v := Value{
|
||||||
|
filename: cgroupProcs,
|
||||||
|
value: pid,
|
||||||
|
}
|
||||||
|
return writeValues(c.path, []Value{v})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) Delete() error {
|
||||||
|
return remove(c.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) Procs(recursive bool) ([]uint64, error) {
|
||||||
|
var processes []uint64
|
||||||
|
err := filepath.Walk(c.path, func(p string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !recursive && info.IsDir() {
|
||||||
|
if p == c.path {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
_, name := filepath.Split(p)
|
||||||
|
if name != cgroupProcs {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
procs, err := parseCgroupProcsFile(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
processes = append(processes, procs...)
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return processes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var singleValueFiles = []string{
|
||||||
|
"pids.current",
|
||||||
|
"pids.max",
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) Stat() (*stats.Metrics, error) {
|
||||||
|
controllers, err := c.Controllers()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out := make(map[string]interface{})
|
||||||
|
for _, controller := range controllers {
|
||||||
|
filename := fmt.Sprintf("%s.stat", controller)
|
||||||
|
if err := readStatsFile(c.path, filename, out); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, name := range singleValueFiles {
|
||||||
|
if err := readSingleFile(c.path, name, out); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var metrics stats.Metrics
|
||||||
|
|
||||||
|
metrics.Pids = &stats.PidsStat{
|
||||||
|
Current: getPidValue("pids.current", out),
|
||||||
|
Limit: getPidValue("pids.max", out),
|
||||||
|
}
|
||||||
|
metrics.CPU = &stats.CPUStat{
|
||||||
|
UsageUsec: getUint64Value("usage_usec", out),
|
||||||
|
UserUsec: getUint64Value("user_usec", out),
|
||||||
|
SystemUsec: getUint64Value("system_usec", out),
|
||||||
|
NrPeriods: getUint64Value("nr_periods", out),
|
||||||
|
NrThrottled: getUint64Value("nr_throttled", out),
|
||||||
|
ThrottledUsec: getUint64Value("throttled_usec", out),
|
||||||
|
}
|
||||||
|
metrics.Memory = &stats.MemoryStat{
|
||||||
|
Anon: getUint64Value("anon", out),
|
||||||
|
File: getUint64Value("file", out),
|
||||||
|
KernelStack: getUint64Value("kernel_stack", out),
|
||||||
|
Slab: getUint64Value("slab", out),
|
||||||
|
Sock: getUint64Value("sock", out),
|
||||||
|
Shmem: getUint64Value("shmem", out),
|
||||||
|
FileMapped: getUint64Value("file_mapped", out),
|
||||||
|
FileDirty: getUint64Value("file_dirty", out),
|
||||||
|
FileWriteback: getUint64Value("file_writeback", out),
|
||||||
|
AnonThp: getUint64Value("anon_thp", out),
|
||||||
|
InactiveAnon: getUint64Value("inactive_anon", out),
|
||||||
|
ActiveAnon: getUint64Value("active_anon", out),
|
||||||
|
InactiveFile: getUint64Value("inactive_file", out),
|
||||||
|
ActiveFile: getUint64Value("active_file", out),
|
||||||
|
Unevictable: getUint64Value("unevictable", out),
|
||||||
|
SlabReclaimable: getUint64Value("slab_reclaimable", out),
|
||||||
|
SlabUnreclaimable: getUint64Value("slab_unreclaimable", out),
|
||||||
|
Pgfault: getUint64Value("pgfault", out),
|
||||||
|
Pgmajfault: getUint64Value("pgmajfault", out),
|
||||||
|
WorkingsetRefault: getUint64Value("workingset_refault", out),
|
||||||
|
WorkingsetActivate: getUint64Value("workingset_activate", out),
|
||||||
|
WorkingsetNodereclaim: getUint64Value("workingset_nodereclaim", out),
|
||||||
|
Pgrefill: getUint64Value("pgrefill", out),
|
||||||
|
Pgscan: getUint64Value("pgscan", out),
|
||||||
|
Pgsteal: getUint64Value("pgsteal", out),
|
||||||
|
Pgactivate: getUint64Value("pgactivate", out),
|
||||||
|
Pgdeactivate: getUint64Value("pgdeactivate", out),
|
||||||
|
Pglazyfree: getUint64Value("pglazyfree", out),
|
||||||
|
Pglazyfreed: getUint64Value("pglazyfreed", out),
|
||||||
|
ThpFaultAlloc: getUint64Value("thp_fault_alloc", out),
|
||||||
|
ThpCollapseAlloc: getUint64Value("thp_collapse_alloc", out),
|
||||||
|
Usage: getStatFileContentUint64(filepath.Join(c.path, "memory.current")),
|
||||||
|
UsageLimit: getStatFileContentUint64(filepath.Join(c.path, "memory.max")),
|
||||||
|
SwapUsage: getStatFileContentUint64(filepath.Join(c.path, "memory.swap.current")),
|
||||||
|
SwapLimit: getStatFileContentUint64(filepath.Join(c.path, "memory.swap.max")),
|
||||||
|
}
|
||||||
|
|
||||||
|
metrics.Rdma = &stats.RdmaStat{
|
||||||
|
Current: rdmaStats(filepath.Join(c.path, "rdma.current")),
|
||||||
|
Limit: rdmaStats(filepath.Join(c.path, "rdma.max")),
|
||||||
|
}
|
||||||
|
|
||||||
|
return &metrics, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getUint64Value(key string, out map[string]interface{}) uint64 {
|
||||||
|
v, ok := out[key]
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
switch t := v.(type) {
|
||||||
|
case uint64:
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPidValue(key string, out map[string]interface{}) uint64 {
|
||||||
|
v, ok := out[key]
|
||||||
|
if !ok {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
switch t := v.(type) {
|
||||||
|
case uint64:
|
||||||
|
return t
|
||||||
|
case string:
|
||||||
|
if t == "max" {
|
||||||
|
return math.MaxUint64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func readSingleFile(path string, file string, out map[string]interface{}) error {
|
||||||
|
f, err := os.Open(filepath.Join(path, file))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
data, err := ioutil.ReadAll(f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
s := strings.TrimSpace(string(data))
|
||||||
|
v, err := parseUint(s, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
// if we cannot parse as a uint, parse as a string
|
||||||
|
out[file] = s
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
out[file] = v
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readStatsFile(path string, file string, out map[string]interface{}) error {
|
||||||
|
f, err := os.Open(filepath.Join(path, file))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
s := bufio.NewScanner(f)
|
||||||
|
for s.Scan() {
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
name, value, err := parseKV(s.Text())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out[name] = value
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) Freeze() error {
|
||||||
|
return c.freeze(c.path, Frozen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) Thaw() error {
|
||||||
|
return c.freeze(c.path, Thawed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) freeze(path string, state State) error {
|
||||||
|
values := state.Values()
|
||||||
|
for {
|
||||||
|
if err := writeValues(path, values); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
current, err := fetchState(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if current == state {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
time.Sleep(1 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) MemoryEventFD() (uintptr, error) {
|
||||||
|
fpath := filepath.Join(c.path, "memory.events")
|
||||||
|
fd, err := syscall.InotifyInit()
|
||||||
|
if err != nil {
|
||||||
|
return 0, errors.Errorf("Failed to create inotify fd")
|
||||||
|
}
|
||||||
|
defer syscall.Close(fd)
|
||||||
|
wd, err := syscall.InotifyAddWatch(fd, fpath, unix.IN_MODIFY)
|
||||||
|
if wd < 0 {
|
||||||
|
return 0, errors.Errorf("Failed to add inotify watch for %q", fpath)
|
||||||
|
}
|
||||||
|
defer syscall.InotifyRmWatch(fd, uint32(wd))
|
||||||
|
|
||||||
|
return uintptr(fd), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) EventChan() (<-chan Event, <-chan error) {
|
||||||
|
ec := make(chan Event)
|
||||||
|
errCh := make(chan error)
|
||||||
|
go c.waitForEvents(ec, errCh)
|
||||||
|
|
||||||
|
return ec, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Manager) waitForEvents(ec chan<- Event, errCh chan<- error) {
|
||||||
|
fd, err := c.MemoryEventFD()
|
||||||
|
if err != nil {
|
||||||
|
errCh <- errors.Errorf("Failed to create memory event fd")
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
buffer := make([]byte, syscall.SizeofInotifyEvent*10)
|
||||||
|
bytesRead, err := syscall.Read(int(fd), buffer)
|
||||||
|
if err != nil {
|
||||||
|
errCh <- err
|
||||||
|
}
|
||||||
|
var out map[string]interface{}
|
||||||
|
if bytesRead >= syscall.SizeofInotifyEvent {
|
||||||
|
if err := readStatsFile(c.path, "memory.events", out); err != nil {
|
||||||
|
e := Event{
|
||||||
|
High: out["high"].(uint64),
|
||||||
|
Low: out["low"].(uint64),
|
||||||
|
Max: out["max"].(uint64),
|
||||||
|
OOM: out["oom"].(uint64),
|
||||||
|
OOMKill: out["oom_kill"].(uint64),
|
||||||
|
}
|
||||||
|
ec <- e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDevices(path string, devices []specs.LinuxDeviceCgroup) error {
|
||||||
|
if len(devices) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
insts, license, err := DeviceFilter(devices)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dirFD, err := unix.Open(path, unix.O_DIRECTORY|unix.O_RDONLY, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return errors.Errorf("cannot get dir FD for %s", path)
|
||||||
|
}
|
||||||
|
defer unix.Close(dirFD)
|
||||||
|
if _, err := LoadAttachCgroupDeviceFilter(insts, license, dirFD); err != nil {
|
||||||
|
if !canSkipEBPFError(devices) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
52
vendor/github.com/containerd/cgroups/v2/memory.go
generated
vendored
Normal file
52
vendor/github.com/containerd/cgroups/v2/memory.go
generated
vendored
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
type Memory struct {
|
||||||
|
Swap *int64
|
||||||
|
Max *int64
|
||||||
|
Low *int64
|
||||||
|
High *int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Memory) Values() (o []Value) {
|
||||||
|
if r.Swap != nil {
|
||||||
|
o = append(o, Value{
|
||||||
|
filename: "memory.swap.max",
|
||||||
|
value: *r.Swap,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if r.Max != nil {
|
||||||
|
o = append(o, Value{
|
||||||
|
filename: "memory.max",
|
||||||
|
value: *r.Max,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if r.Low != nil {
|
||||||
|
o = append(o, Value{
|
||||||
|
filename: "memory.low",
|
||||||
|
value: *r.Low,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if r.High != nil {
|
||||||
|
o = append(o, Value{
|
||||||
|
filename: "memory.high",
|
||||||
|
value: *r.High,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
60
vendor/github.com/containerd/cgroups/v2/paths.go
generated
vendored
Normal file
60
vendor/github.com/containerd/cgroups/v2/paths.go
generated
vendored
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NestedGroupPath will nest the cgroups based on the calling processes cgroup
|
||||||
|
// placing its child processes inside its own path
|
||||||
|
func NestedGroupPath(suffix string) (string, error) {
|
||||||
|
path, err := parseCgroupFile("/proc/self/cgroup")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Join(string(path), suffix), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PidGroupPath will return the correct cgroup paths for an existing process running inside a cgroup
|
||||||
|
// This is commonly used for the Load function to restore an existing container
|
||||||
|
func PidGroupPath(pid int) (string, error) {
|
||||||
|
p := fmt.Sprintf("/proc/%d/cgroup", pid)
|
||||||
|
return parseCgroupFile(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyGroupPath verifies the format of group path string g.
|
||||||
|
// The format is same as the third field in /proc/PID/cgroup.
|
||||||
|
// e.g. "/user.slice/user-1001.slice/session-1.scope"
|
||||||
|
//
|
||||||
|
// g must be a "clean" absolute path starts with "/", and must not contain "/sys/fs/cgroup" prefix.
|
||||||
|
//
|
||||||
|
// VerifyGroupPath doesn't verify whether g actually exists on the system.
|
||||||
|
func VerifyGroupPath(g string) error {
|
||||||
|
if !strings.HasPrefix(g, "/") {
|
||||||
|
return ErrInvalidGroupPath
|
||||||
|
}
|
||||||
|
if filepath.Clean(g) != g {
|
||||||
|
return ErrInvalidGroupPath
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(g, "/sys/fs/cgroup") {
|
||||||
|
return ErrInvalidGroupPath
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
37
vendor/github.com/containerd/cgroups/v2/pids.go
generated
vendored
Normal file
37
vendor/github.com/containerd/cgroups/v2/pids.go
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
type Pids struct {
|
||||||
|
Max int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Pids) Values() (o []Value) {
|
||||||
|
if r.Max != 0 {
|
||||||
|
limit := "max"
|
||||||
|
if r.Max > 0 {
|
||||||
|
limit = strconv.FormatInt(r.Max, 10)
|
||||||
|
}
|
||||||
|
o = append(o, Value{
|
||||||
|
filename: "pids.max",
|
||||||
|
value: limit,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return o
|
||||||
|
}
|
46
vendor/github.com/containerd/cgroups/v2/rdma.go
generated
vendored
Normal file
46
vendor/github.com/containerd/cgroups/v2/rdma.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RDMA struct {
|
||||||
|
Limit []RDMAEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
type RDMAEntry struct {
|
||||||
|
Device string
|
||||||
|
HcaHandles uint32
|
||||||
|
HcaObjects uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r RDMAEntry) String() string {
|
||||||
|
return fmt.Sprintf("%s hca_handle=%d hca_object=%d", r.Device, r.HcaHandles, r.HcaObjects)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RDMA) Values() (o []Value) {
|
||||||
|
for _, e := range r.Limit {
|
||||||
|
o = append(o, Value{
|
||||||
|
filename: "rdma.max",
|
||||||
|
value: e.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return o
|
||||||
|
}
|
65
vendor/github.com/containerd/cgroups/v2/state.go
generated
vendored
Normal file
65
vendor/github.com/containerd/cgroups/v2/state.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// State is a type that represents the state of the current cgroup
|
||||||
|
type State string
|
||||||
|
|
||||||
|
const (
|
||||||
|
Unknown State = ""
|
||||||
|
Thawed State = "thawed"
|
||||||
|
Frozen State = "frozen"
|
||||||
|
Deleted State = "deleted"
|
||||||
|
|
||||||
|
cgroupFreeze = "cgroup.freeze"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s State) Values() []Value {
|
||||||
|
v := Value{
|
||||||
|
filename: cgroupFreeze,
|
||||||
|
}
|
||||||
|
switch s {
|
||||||
|
case Frozen:
|
||||||
|
v.value = "1"
|
||||||
|
case Thawed:
|
||||||
|
v.value = "0"
|
||||||
|
}
|
||||||
|
return []Value{
|
||||||
|
v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchState(path string) (State, error) {
|
||||||
|
current, err := ioutil.ReadFile(filepath.Join(path, cgroupFreeze))
|
||||||
|
if err != nil {
|
||||||
|
return Unknown, err
|
||||||
|
}
|
||||||
|
switch strings.TrimSpace(string(current)) {
|
||||||
|
case "1":
|
||||||
|
return Frozen, nil
|
||||||
|
case "0":
|
||||||
|
return Thawed, nil
|
||||||
|
default:
|
||||||
|
return Unknown, nil
|
||||||
|
}
|
||||||
|
}
|
17
vendor/github.com/containerd/cgroups/v2/stats/doc.go
generated
vendored
Normal file
17
vendor/github.com/containerd/cgroups/v2/stats/doc.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package stats
|
2759
vendor/github.com/containerd/cgroups/v2/stats/metrics.pb.go
generated
vendored
Normal file
2759
vendor/github.com/containerd/cgroups/v2/stats/metrics.pb.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
78
vendor/github.com/containerd/cgroups/v2/stats/metrics.proto
generated
vendored
Normal file
78
vendor/github.com/containerd/cgroups/v2/stats/metrics.proto
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package io.containerd.cgroups.v2;
|
||||||
|
|
||||||
|
import "gogoproto/gogo.proto";
|
||||||
|
|
||||||
|
message Metrics {
|
||||||
|
PidsStat pids = 1;
|
||||||
|
CPUStat cpu = 2 [(gogoproto.customname) = "CPU"];
|
||||||
|
MemoryStat memory = 4;
|
||||||
|
RdmaStat rdma = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
message PidsStat {
|
||||||
|
uint64 current = 1;
|
||||||
|
uint64 limit = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message CPUStat {
|
||||||
|
uint64 usage_usec = 1;
|
||||||
|
uint64 user_usec = 2;
|
||||||
|
uint64 system_usec = 3;
|
||||||
|
uint64 nr_periods = 4;
|
||||||
|
uint64 nr_throttled = 5;
|
||||||
|
uint64 throttled_usec = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MemoryStat {
|
||||||
|
uint64 anon = 1;
|
||||||
|
uint64 file = 2;
|
||||||
|
uint64 kernel_stack = 3;
|
||||||
|
uint64 slab = 4;
|
||||||
|
uint64 sock = 5;
|
||||||
|
uint64 shmem = 6;
|
||||||
|
uint64 file_mapped = 7;
|
||||||
|
uint64 file_dirty = 8;
|
||||||
|
uint64 file_writeback = 9;
|
||||||
|
uint64 anon_thp = 10;
|
||||||
|
uint64 inactive_anon = 11;
|
||||||
|
uint64 active_anon = 12;
|
||||||
|
uint64 inactive_file = 13;
|
||||||
|
uint64 active_file = 14;
|
||||||
|
uint64 unevictable = 15;
|
||||||
|
uint64 slab_reclaimable = 16;
|
||||||
|
uint64 slab_unreclaimable = 17;
|
||||||
|
uint64 pgfault = 18;
|
||||||
|
uint64 pgmajfault = 19;
|
||||||
|
uint64 workingset_refault = 20;
|
||||||
|
uint64 workingset_activate = 21;
|
||||||
|
uint64 workingset_nodereclaim = 22;
|
||||||
|
uint64 pgrefill = 23;
|
||||||
|
uint64 pgscan = 24;
|
||||||
|
uint64 pgsteal = 25;
|
||||||
|
uint64 pgactivate = 26;
|
||||||
|
uint64 pgdeactivate = 27;
|
||||||
|
uint64 pglazyfree = 28;
|
||||||
|
uint64 pglazyfreed = 29;
|
||||||
|
uint64 thp_fault_alloc = 30;
|
||||||
|
uint64 thp_collapse_alloc = 31;
|
||||||
|
uint64 usage = 32;
|
||||||
|
uint64 usage_limit = 33;
|
||||||
|
uint64 swap_usage = 34;
|
||||||
|
uint64 swap_limit = 35;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RdmaStat {
|
||||||
|
repeated RdmaEntry current = 1;
|
||||||
|
repeated RdmaEntry limit = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RdmaEntry {
|
||||||
|
string device = 1;
|
||||||
|
uint32 hca_handles = 2;
|
||||||
|
uint32 hca_objects = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iostat
|
||||||
|
// fmt: 230:0 rbytes=394211328 wbytes=65044480 rios=16313 wios=2006 dbytes=0 dios=0
|
304
vendor/github.com/containerd/cgroups/v2/utils.go
generated
vendored
Normal file
304
vendor/github.com/containerd/cgroups/v2/utils.go
generated
vendored
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
/*
|
||||||
|
Copyright The containerd Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package v2
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"math"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/containerd/cgroups/v2/stats"
|
||||||
|
"github.com/opencontainers/runtime-spec/specs-go"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
cgroupProcs = "cgroup.procs"
|
||||||
|
defaultDirPerm = 0755
|
||||||
|
)
|
||||||
|
|
||||||
|
// defaultFilePerm is a var so that the test framework can change the filemode
|
||||||
|
// of all files created when the tests are running. The difference between the
|
||||||
|
// tests and real world use is that files like "cgroup.procs" will exist when writing
|
||||||
|
// to a read cgroup filesystem and do not exist prior when running in the tests.
|
||||||
|
// this is set to a non 0 value in the test code
|
||||||
|
var defaultFilePerm = os.FileMode(0)
|
||||||
|
|
||||||
|
// remove will remove a cgroup path handling EAGAIN and EBUSY errors and
|
||||||
|
// retrying the remove after a exp timeout
|
||||||
|
func remove(path string) error {
|
||||||
|
var err error
|
||||||
|
delay := 10 * time.Millisecond
|
||||||
|
for i := 0; i < 5; i++ {
|
||||||
|
if i != 0 {
|
||||||
|
time.Sleep(delay)
|
||||||
|
delay *= 2
|
||||||
|
}
|
||||||
|
if err = os.RemoveAll(path); err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.Wrapf(err, "cgroups: unable to remove path %q", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCgroupProcsFile parses /sys/fs/cgroup/$GROUPPATH/cgroup.procs
|
||||||
|
func parseCgroupProcsFile(path string) ([]uint64, error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
var (
|
||||||
|
out []uint64
|
||||||
|
s = bufio.NewScanner(f)
|
||||||
|
)
|
||||||
|
for s.Scan() {
|
||||||
|
if t := s.Text(); t != "" {
|
||||||
|
pid, err := strconv.ParseUint(t, 10, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out = append(out, pid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseKV(raw string) (string, interface{}, error) {
|
||||||
|
parts := strings.Fields(raw)
|
||||||
|
switch len(parts) {
|
||||||
|
case 2:
|
||||||
|
v, err := parseUint(parts[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
// if we cannot parse as a uint, parse as a string
|
||||||
|
return parts[0], parts[1], nil
|
||||||
|
}
|
||||||
|
return parts[0], v, nil
|
||||||
|
default:
|
||||||
|
return "", 0, ErrInvalidFormat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func readUint(path string) (uint64, error) {
|
||||||
|
v, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return parseUint(strings.TrimSpace(string(v)), 10, 64)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseUint(s string, base, bitSize int) (uint64, error) {
|
||||||
|
v, err := strconv.ParseUint(s, base, bitSize)
|
||||||
|
if err != nil {
|
||||||
|
intValue, intErr := strconv.ParseInt(s, base, bitSize)
|
||||||
|
// 1. Handle negative values greater than MinInt64 (and)
|
||||||
|
// 2. Handle negative values lesser than MinInt64
|
||||||
|
if intErr == nil && intValue < 0 {
|
||||||
|
return 0, nil
|
||||||
|
} else if intErr != nil &&
|
||||||
|
intErr.(*strconv.NumError).Err == strconv.ErrRange &&
|
||||||
|
intValue < 0 {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return v, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// parseCgroupFile parses /proc/PID/cgroup file and return string
|
||||||
|
func parseCgroupFile(path string) (string, error) {
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
return parseCgroupFromReader(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseCgroupFromReader(r io.Reader) (string, error) {
|
||||||
|
var (
|
||||||
|
s = bufio.NewScanner(r)
|
||||||
|
)
|
||||||
|
for s.Scan() {
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
text = s.Text()
|
||||||
|
parts = strings.SplitN(text, ":", 3)
|
||||||
|
)
|
||||||
|
if len(parts) < 3 {
|
||||||
|
return "", fmt.Errorf("invalid cgroup entry: %q", text)
|
||||||
|
}
|
||||||
|
// text is like "0::/user.slice/user-1001.slice/session-1.scope"
|
||||||
|
if parts[0] == "0" && parts[1] == "" {
|
||||||
|
return parts[2], nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("cgroup path not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToResources converts the oci LinuxResources struct into a
|
||||||
|
// v2 Resources type for use with this package.
|
||||||
|
//
|
||||||
|
// converting cgroups configuration from v1 to v2
|
||||||
|
// ref: https://github.com/containers/crun/blob/master/crun.1.md#cgroup-v2
|
||||||
|
func ToResources(spec *specs.LinuxResources) *Resources {
|
||||||
|
var resources Resources
|
||||||
|
if cpu := spec.CPU; cpu != nil {
|
||||||
|
resources.CPU = &CPU{
|
||||||
|
Cpus: cpu.Cpus,
|
||||||
|
Mems: cpu.Mems,
|
||||||
|
}
|
||||||
|
if shares := cpu.Shares; shares != nil {
|
||||||
|
convertedWeight := (1 + ((*shares-2)*9999)/262142)
|
||||||
|
resources.CPU.Weight = &convertedWeight
|
||||||
|
}
|
||||||
|
if period := cpu.Period; period != nil {
|
||||||
|
resources.CPU.Max = period
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mem := spec.Memory; mem != nil {
|
||||||
|
resources.Memory = &Memory{}
|
||||||
|
if swap := mem.Swap; swap != nil {
|
||||||
|
resources.Memory.Swap = swap
|
||||||
|
}
|
||||||
|
if l := mem.Limit; l != nil {
|
||||||
|
resources.Memory.Max = l
|
||||||
|
}
|
||||||
|
if l := mem.Reservation; l != nil {
|
||||||
|
resources.Memory.Low = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if pids := spec.Pids; pids != nil {
|
||||||
|
resources.Pids = &Pids{
|
||||||
|
Max: pids.Limit,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i := spec.BlockIO; i != nil {
|
||||||
|
resources.IO = &IO{}
|
||||||
|
if i.Weight != nil {
|
||||||
|
resources.IO.BFQ.Weight = *i.Weight
|
||||||
|
}
|
||||||
|
for t, devices := range map[IOType][]specs.LinuxThrottleDevice{
|
||||||
|
ReadBPS: i.ThrottleReadBpsDevice,
|
||||||
|
WriteBPS: i.ThrottleWriteBpsDevice,
|
||||||
|
ReadIOPS: i.ThrottleReadIOPSDevice,
|
||||||
|
WriteIOPS: i.ThrottleWriteIOPSDevice,
|
||||||
|
} {
|
||||||
|
for _, d := range devices {
|
||||||
|
resources.IO.Max = append(resources.IO.Max, Entry{
|
||||||
|
Type: t,
|
||||||
|
Major: d.Major,
|
||||||
|
Minor: d.Minor,
|
||||||
|
Rate: d.Rate,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if i := spec.Rdma; i != nil {
|
||||||
|
resources.RDMA = &RDMA{}
|
||||||
|
for device, value := range spec.Rdma {
|
||||||
|
if device != "" && (value.HcaHandles != nil || value.HcaObjects != nil) {
|
||||||
|
resources.RDMA.Limit = append(resources.RDMA.Limit, RDMAEntry{
|
||||||
|
Device: device,
|
||||||
|
HcaHandles: *value.HcaHandles,
|
||||||
|
HcaObjects: *value.HcaObjects,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &resources
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets uint64 parsed content of single value cgroup stat file
|
||||||
|
func getStatFileContentUint64(filePath string) uint64 {
|
||||||
|
contents, err := ioutil.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error(err)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
trimmed := strings.TrimSpace(string(contents))
|
||||||
|
if trimmed == "max" {
|
||||||
|
return math.MaxUint64
|
||||||
|
}
|
||||||
|
|
||||||
|
res, err := parseUint(trimmed, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
logrus.Errorf("unable to parse %q as a uint from Cgroup file %q", string(contents), filePath)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
func rdmaStats(filepath string) []*stats.RdmaEntry {
|
||||||
|
currentData, err := ioutil.ReadFile(filepath)
|
||||||
|
if err != nil {
|
||||||
|
return []*stats.RdmaEntry{}
|
||||||
|
}
|
||||||
|
return toRdmaEntry(strings.Split(string(currentData), "\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRdmaKV(raw string, entry *stats.RdmaEntry) {
|
||||||
|
var value uint64
|
||||||
|
var err error
|
||||||
|
|
||||||
|
parts := strings.Split(raw, "=")
|
||||||
|
switch len(parts) {
|
||||||
|
case 2:
|
||||||
|
if parts[1] == "max" {
|
||||||
|
value = math.MaxUint32
|
||||||
|
} else {
|
||||||
|
value, err = parseUint(parts[1], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if parts[0] == "hca_handle" {
|
||||||
|
entry.HcaHandles = uint32(value)
|
||||||
|
} else if parts[0] == "hca_object" {
|
||||||
|
entry.HcaObjects = uint32(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func toRdmaEntry(strEntries []string) []*stats.RdmaEntry {
|
||||||
|
var rdmaEntries []*stats.RdmaEntry
|
||||||
|
for i := range strEntries {
|
||||||
|
parts := strings.Fields(strEntries[i])
|
||||||
|
switch len(parts) {
|
||||||
|
case 3:
|
||||||
|
entry := new(stats.RdmaEntry)
|
||||||
|
entry.Device = parts[0]
|
||||||
|
parseRdmaKV(parts[1], entry)
|
||||||
|
parseRdmaKV(parts[2], entry)
|
||||||
|
|
||||||
|
rdmaEntries = append(rdmaEntries, entry)
|
||||||
|
default:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rdmaEntries
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user