mirror of
https://github.com/prometheus/node_exporter.git
synced 2024-12-25 21:54:18 -08:00
Add perf exporter (#1274)
Signed-off-by: Daniel Hodges <hodges.daniel.scott@gmail.com>
This commit is contained in:
parent
0c6b90be4e
commit
7882009870
|
@ -32,6 +32,7 @@
|
|||
* [FEATURE] Add diskstats collector for OpenBSD #1250
|
||||
* [CHANGE] Bonding state uses mii_status #1124
|
||||
* [FEATURE] Add pressure collector exposing pressure stall information for Linux #1174
|
||||
* [FEATURE] Add perf exporter for Linux #1274
|
||||
|
||||
## 0.17.0 / 2018-11-30
|
||||
|
||||
|
|
18
README.md
18
README.md
|
@ -63,6 +63,23 @@ zfs | Exposes [ZFS](http://open-zfs.org/) performance statistics. | [Linux](http
|
|||
|
||||
### Disabled by default
|
||||
|
||||
The perf collector may not work by default on all Linux systems due to kernel
|
||||
configuration and security settings. To allow access, set the following sysctl
|
||||
parameter:
|
||||
|
||||
```
|
||||
sysctl -w kernel.perf_event_paranoid=X
|
||||
```
|
||||
|
||||
- 2 allow only user-space measurements (default since Linux 4.6).
|
||||
- 1 allow both kernel and user measurements (default before Linux 4.6).
|
||||
- 0 allow access to CPU-specific data but not raw tracepoint samples.
|
||||
- -1 no restrictions.
|
||||
|
||||
Depending on the configured value different metrics will be available, for most
|
||||
cases `0` will provide the most complete set. For more information see [`man 2
|
||||
perf_event_open`](http://man7.org/linux/man-pages/man2/perf_event_open.2.html).
|
||||
|
||||
Name | Description | OS
|
||||
---------|-------------|----
|
||||
buddyinfo | Exposes statistics of memory fragments as reported by /proc/buddyinfo. | Linux
|
||||
|
@ -81,6 +98,7 @@ supervisord | Exposes service status from [supervisord](http://supervisord.org/)
|
|||
systemd | Exposes service and system status from [systemd](http://www.freedesktop.org/wiki/Software/systemd/). | Linux
|
||||
tcpstat | Exposes TCP connection status information from `/proc/net/tcp` and `/proc/net/tcp6`. (Warning: the current version has potential performance issues in high load situations.) | Linux
|
||||
wifi | Exposes WiFi device and station statistics. | Linux
|
||||
perf | Exposes perf based metrics (Warning: Metrics are dependent on kernel configuration and settings). | Linux
|
||||
|
||||
### Textfile Collector
|
||||
|
||||
|
|
567
collector/perf_linux.go
Normal file
567
collector/perf_linux.go
Normal file
|
@ -0,0 +1,567 @@
|
|||
// Copyright 2019 The Prometheus 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 collector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
perf "github.com/hodgesds/perf-utils"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
const (
|
||||
perfSubsystem = "perf"
|
||||
)
|
||||
|
||||
func init() {
|
||||
registerCollector(perfSubsystem, defaultDisabled, NewPerfCollector)
|
||||
}
|
||||
|
||||
// perfCollector is a Collecter that uses the perf subsystem to collect
|
||||
// metrics. It uses perf_event_open an ioctls for profiling. Due to the fact
|
||||
// that the perf subsystem is highly dependent on kernel configuration and
|
||||
// settings not all profiler values may be exposed on the target system at any
|
||||
// given time.
|
||||
type perfCollector struct {
|
||||
perfHwProfilers map[int]perf.HardwareProfiler
|
||||
perfSwProfilers map[int]perf.SoftwareProfiler
|
||||
perfCacheProfilers map[int]perf.CacheProfiler
|
||||
desc map[string]*prometheus.Desc
|
||||
}
|
||||
|
||||
// NewPerfCollector returns a new perf based collector, it creates a profiler
|
||||
// per CPU.
|
||||
func NewPerfCollector() (Collector, error) {
|
||||
collector := &perfCollector{
|
||||
perfHwProfilers: map[int]perf.HardwareProfiler{},
|
||||
perfSwProfilers: map[int]perf.SoftwareProfiler{},
|
||||
perfCacheProfilers: map[int]perf.CacheProfiler{},
|
||||
}
|
||||
ncpus := runtime.NumCPU()
|
||||
for i := 0; i < ncpus; i++ {
|
||||
// Use -1 to profile all processes on the CPU, see:
|
||||
// man perf_event_open
|
||||
collector.perfHwProfilers[i] = perf.NewHardwareProfiler(-1, i)
|
||||
if err := collector.perfHwProfilers[i].Start(); err != nil {
|
||||
return collector, err
|
||||
}
|
||||
collector.perfSwProfilers[i] = perf.NewSoftwareProfiler(-1, i)
|
||||
if err := collector.perfSwProfilers[i].Start(); err != nil {
|
||||
return collector, err
|
||||
}
|
||||
collector.perfCacheProfilers[i] = perf.NewCacheProfiler(-1, i)
|
||||
if err := collector.perfCacheProfilers[i].Start(); err != nil {
|
||||
return collector, err
|
||||
}
|
||||
}
|
||||
collector.desc = map[string]*prometheus.Desc{
|
||||
"cpucycles_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cpucycles_total",
|
||||
),
|
||||
"Number of CPU cycles (frequency scaled)",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"instructions_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"instructions_total",
|
||||
),
|
||||
"Number of CPU instructions",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"branch_instructions_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"branch_instructions_total",
|
||||
),
|
||||
"Number of CPU branch instructions",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"branch_misses_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"branch_misses_total",
|
||||
),
|
||||
"Number of CPU branch misses",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_refs_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_refs_total",
|
||||
),
|
||||
"Number of cache references (non frequency scaled)",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_misses_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_misses_total",
|
||||
),
|
||||
"Number of cache misses",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"ref_cpucycles_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"ref_cpucycles_total",
|
||||
),
|
||||
"Number of CPU cycles",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"page_faults_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"page_faults_total",
|
||||
),
|
||||
"Number of page faults",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"context_switches_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"context_switches_total",
|
||||
),
|
||||
"Number of context switches",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cpu_migrations_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cpu_migrations_total",
|
||||
),
|
||||
"Number of CPU process migrations",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"minor_faults_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"minor_faults_total",
|
||||
),
|
||||
"Number of minor page faults",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"major_faults_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"major_faults_total",
|
||||
),
|
||||
"Number of major page faults",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_l1d_read_hits_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_l1d_read_hits_total",
|
||||
),
|
||||
"Number L1 data cache read hits",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_l1d_read_misses_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_l1d_read_misses_total",
|
||||
),
|
||||
"Number L1 data cache read misses",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_l1d_write_hits_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_l1d_write_hits_total",
|
||||
),
|
||||
"Number L1 data cache write hits",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_l1_instr_read_misses_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_l1_instr_read_misses_total",
|
||||
),
|
||||
"Number instruction L1 instruction read misses",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_tlb_instr_read_hits_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_tlb_instr_read_hits_total",
|
||||
),
|
||||
"Number instruction TLB read hits",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_tlb_instr_read_misses_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_tlb_instr_read_misses_total",
|
||||
),
|
||||
"Number instruction TLB read misses",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_ll_read_hits_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_ll_read_hits_total",
|
||||
),
|
||||
"Number last level read hits",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_ll_read_misses_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_ll_read_misses_total",
|
||||
),
|
||||
"Number last level read misses",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_ll_write_hits_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_ll_write_hits_total",
|
||||
),
|
||||
"Number last level write hits",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_ll_write_misses_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_ll_write_misses_total",
|
||||
),
|
||||
"Number last level write misses",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_bpu_read_hits_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_bpu_read_hits_total",
|
||||
),
|
||||
"Number BPU read hits",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
"cache_bpu_read_misses_total": prometheus.NewDesc(
|
||||
prometheus.BuildFQName(
|
||||
namespace,
|
||||
perfSubsystem,
|
||||
"cache_bpu_read_misses_total",
|
||||
),
|
||||
"Number BPU read misses",
|
||||
[]string{"cpu"},
|
||||
nil,
|
||||
),
|
||||
}
|
||||
|
||||
return collector, nil
|
||||
}
|
||||
|
||||
// Update implements the Collector interface and will collect metrics per CPU.
|
||||
func (c *perfCollector) Update(ch chan<- prometheus.Metric) error {
|
||||
if err := c.updateHardwareStats(ch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.updateSoftwareStats(ch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.updateCacheStats(ch); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *perfCollector) updateHardwareStats(ch chan<- prometheus.Metric) error {
|
||||
for cpu, profiler := range c.perfHwProfilers {
|
||||
cpuStr := fmt.Sprintf("%d", cpu)
|
||||
hwProfile, err := profiler.Profile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if hwProfile == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if hwProfile.CPUCycles != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cpucycles_total"],
|
||||
prometheus.CounterValue, float64(*hwProfile.CPUCycles),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if hwProfile.Instructions != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["instructions_total"],
|
||||
prometheus.CounterValue, float64(*hwProfile.Instructions),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if hwProfile.BranchInstr != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["branch_instructions_total"],
|
||||
prometheus.CounterValue, float64(*hwProfile.BranchInstr),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if hwProfile.BranchMisses != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["branch_misses_total"],
|
||||
prometheus.CounterValue, float64(*hwProfile.BranchMisses),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if hwProfile.CacheRefs != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_refs_total"],
|
||||
prometheus.CounterValue, float64(*hwProfile.CacheRefs),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if hwProfile.CacheMisses != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_misses_total"],
|
||||
prometheus.CounterValue, float64(*hwProfile.CacheMisses),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if hwProfile.RefCPUCycles != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["ref_cpucycles_total"],
|
||||
prometheus.CounterValue, float64(*hwProfile.RefCPUCycles),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *perfCollector) updateSoftwareStats(ch chan<- prometheus.Metric) error {
|
||||
for cpu, profiler := range c.perfSwProfilers {
|
||||
cpuStr := fmt.Sprintf("%d", cpu)
|
||||
swProfile, err := profiler.Profile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if swProfile == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if swProfile.PageFaults != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["page_faults_total"],
|
||||
prometheus.CounterValue, float64(*swProfile.PageFaults),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if swProfile.ContextSwitches != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["context_switches_total"],
|
||||
prometheus.CounterValue, float64(*swProfile.ContextSwitches),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if swProfile.CPUMigrations != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cpu_migrations_total"],
|
||||
prometheus.CounterValue, float64(*swProfile.CPUMigrations),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if swProfile.MinorPageFaults != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["minor_faults_total"],
|
||||
prometheus.CounterValue, float64(*swProfile.MinorPageFaults),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if swProfile.MajorPageFaults != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["major_faults_total"],
|
||||
prometheus.CounterValue, float64(*swProfile.MajorPageFaults),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *perfCollector) updateCacheStats(ch chan<- prometheus.Metric) error {
|
||||
for cpu, profiler := range c.perfCacheProfilers {
|
||||
cpuStr := fmt.Sprintf("%d", cpu)
|
||||
cacheProfile, err := profiler.Profile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cacheProfile == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if cacheProfile.L1DataReadHit != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_l1d_read_hits_total"],
|
||||
prometheus.CounterValue, float64(*cacheProfile.L1DataReadHit),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if cacheProfile.L1DataReadMiss != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_l1d_read_misses_total"],
|
||||
prometheus.CounterValue, float64(*cacheProfile.L1DataReadMiss),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if cacheProfile.L1DataWriteHit != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_l1d_write_hits_total"],
|
||||
prometheus.CounterValue, float64(*cacheProfile.L1DataWriteHit),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if cacheProfile.L1InstrReadMiss != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_l1_instr_read_misses_total"],
|
||||
prometheus.CounterValue, float64(*cacheProfile.L1InstrReadMiss),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if cacheProfile.InstrTLBReadHit != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_tlb_instr_read_hits_total"],
|
||||
prometheus.CounterValue, float64(*cacheProfile.InstrTLBReadHit),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if cacheProfile.InstrTLBReadMiss != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_tlb_instr_read_misses_total"],
|
||||
prometheus.CounterValue, float64(*cacheProfile.InstrTLBReadMiss),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if cacheProfile.LastLevelReadHit != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_ll_read_hits_total"],
|
||||
prometheus.CounterValue, float64(*cacheProfile.LastLevelReadHit),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if cacheProfile.LastLevelReadMiss != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_ll_read_misses_total"],
|
||||
prometheus.CounterValue, float64(*cacheProfile.LastLevelReadMiss),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if cacheProfile.LastLevelWriteHit != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_ll_write_hits_total"],
|
||||
prometheus.CounterValue, float64(*cacheProfile.LastLevelWriteHit),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if cacheProfile.LastLevelWriteMiss != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_ll_write_misses_total"],
|
||||
prometheus.CounterValue, float64(*cacheProfile.LastLevelWriteMiss),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if cacheProfile.BPUReadHit != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_bpu_read_hits_total"],
|
||||
prometheus.CounterValue, float64(*cacheProfile.BPUReadHit),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
|
||||
if cacheProfile.BPUReadMiss != nil {
|
||||
ch <- prometheus.MustNewConstMetric(
|
||||
c.desc["cache_bpu_read_misses_total"],
|
||||
prometheus.CounterValue, float64(*cacheProfile.BPUReadMiss),
|
||||
cpuStr,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
55
collector/perf_linux_test.go
Normal file
55
collector/perf_linux_test.go
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2019 The Prometheus 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.
|
||||
|
||||
// +build !noprocesses
|
||||
|
||||
package collector
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
)
|
||||
|
||||
func TestPerfCollector(t *testing.T) {
|
||||
paranoidBytes, err := ioutil.ReadFile("/proc/sys/kernel/perf_event_paranoid")
|
||||
if err != nil {
|
||||
t.Skip("Procfs not mounted, skipping perf tests")
|
||||
}
|
||||
paranoidStr := strings.Replace(string(paranoidBytes), "\n", "", -1)
|
||||
paranoid, err := strconv.Atoi(paranoidStr)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected perf_event_paranoid to be an int, got: %s", paranoidStr)
|
||||
}
|
||||
if paranoid >= 1 {
|
||||
t.Skip("Skipping perf tests, set perf_event_paranoid to 0")
|
||||
}
|
||||
collector, err := NewPerfCollector()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Setup background goroutine to capture metrics.
|
||||
metrics := make(chan prometheus.Metric)
|
||||
defer close(metrics)
|
||||
go func() {
|
||||
for range metrics {
|
||||
}
|
||||
}()
|
||||
if err := collector.Update(metrics); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
3
go.mod
3
go.mod
|
@ -7,6 +7,7 @@ require (
|
|||
github.com/godbus/dbus v0.0.0-20190402143921-271e53dc4968
|
||||
github.com/golang/protobuf v1.3.1 // indirect
|
||||
github.com/google/go-cmp v0.2.0 // indirect
|
||||
github.com/hodgesds/perf-utils v0.0.6
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
|
||||
github.com/lufia/iostat v0.0.0-20170605150913-9f7362b77ad3
|
||||
github.com/mattn/go-xmlrpc v0.0.1
|
||||
|
@ -21,6 +22,8 @@ require (
|
|||
github.com/sirupsen/logrus v1.4.1 // indirect
|
||||
github.com/soundcloud/go-runit v0.0.0-20150630195641-06ad41a06c4a
|
||||
github.com/stretchr/testify v1.3.0 // indirect
|
||||
go.uber.org/atomic v1.3.2 // indirect
|
||||
go.uber.org/multierr v1.1.0 // indirect
|
||||
golang.org/x/net v0.0.0-20190328230028-74de082e2cca // indirect
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 // indirect
|
||||
golang.org/x/sys v0.0.0-20190402142545-baf5eb976a8c
|
||||
|
|
6
go.sum
6
go.sum
|
@ -26,6 +26,8 @@ github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg
|
|||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/hodgesds/perf-utils v0.0.6 h1:qtHULYRGc+LEIADV2+XI1tJrb9d4PrWl5bwdA94WV3c=
|
||||
github.com/hodgesds/perf-utils v0.0.6/go.mod h1:F6TfvsbtrF88i++hou29dTXlI2sfsJv+gRZDtmTJkAs=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
|
@ -76,6 +78,10 @@ github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1
|
|||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
go.uber.org/atomic v1.3.2 h1:2Oa65PReHzfn29GpvgsYwloV9AVFHPDk8tYxt2c2tr4=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
|
2
vendor/github.com/hodgesds/perf-utils/.gitignore
generated
vendored
Normal file
2
vendor/github.com/hodgesds/perf-utils/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*.swp
|
||||
vendor
|
15
vendor/github.com/hodgesds/perf-utils/Gopkg.lock
generated
vendored
Normal file
15
vendor/github.com/hodgesds/perf-utils/Gopkg.lock
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
|
||||
|
||||
|
||||
[[projects]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
packages = ["unix"]
|
||||
revision = "90b0e4468f9980bf79a2290394adaf7f045c5d24"
|
||||
|
||||
[solve-meta]
|
||||
analyzer-name = "dep"
|
||||
analyzer-version = 1
|
||||
inputs-digest = "c188619af29e454f9af8a4b24b5d13720a55a70615395ba2ded3a628fa51776a"
|
||||
solver-name = "gps-cdcl"
|
||||
solver-version = 1
|
34
vendor/github.com/hodgesds/perf-utils/Gopkg.toml
generated
vendored
Normal file
34
vendor/github.com/hodgesds/perf-utils/Gopkg.toml
generated
vendored
Normal file
|
@ -0,0 +1,34 @@
|
|||
# Gopkg.toml example
|
||||
#
|
||||
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
|
||||
# for detailed Gopkg.toml documentation.
|
||||
#
|
||||
# required = ["github.com/user/thing/cmd/thing"]
|
||||
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project"
|
||||
# version = "1.0.0"
|
||||
#
|
||||
# [[constraint]]
|
||||
# name = "github.com/user/project2"
|
||||
# branch = "dev"
|
||||
# source = "github.com/myfork/project2"
|
||||
#
|
||||
# [[override]]
|
||||
# name = "github.com/x/y"
|
||||
# version = "2.4.0"
|
||||
#
|
||||
# [prune]
|
||||
# non-go = false
|
||||
# go-tests = true
|
||||
# unused-packages = true
|
||||
|
||||
|
||||
[[constraint]]
|
||||
branch = "master"
|
||||
name = "golang.org/x/sys"
|
||||
|
||||
[prune]
|
||||
go-tests = true
|
||||
unused-packages = true
|
22
vendor/github.com/hodgesds/perf-utils/LICENSE
generated
vendored
Normal file
22
vendor/github.com/hodgesds/perf-utils/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Daniel Hodges
|
||||
|
||||
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.
|
||||
|
120
vendor/github.com/hodgesds/perf-utils/README.md
generated
vendored
Normal file
120
vendor/github.com/hodgesds/perf-utils/README.md
generated
vendored
Normal file
|
@ -0,0 +1,120 @@
|
|||
# Perf
|
||||
[![GoDoc](https://godoc.org/github.com/hodgesds/perf-utils?status.svg)](https://godoc.org/github.com/hodgesds/perf-utils)
|
||||
|
||||
This package is a go library for interacting with the `perf` subsystem in
|
||||
Linux. It allows you to do things like see how many CPU instructions a function
|
||||
takes, profile a process for various hardware events, and other interesting
|
||||
things. The library is by no means finalized and should be considered pre-alpha
|
||||
at best.
|
||||
|
||||
# Use Cases
|
||||
A majority of the utility methods in this package should only be used for
|
||||
testing and/or debugging performance issues. Due to the nature of the go
|
||||
runtime profiling on the goroutine level is extremely tricky, with the
|
||||
exception of a long running worker goroutine locked to an OS thread. Eventually
|
||||
this library could be used to implement many of the features of `perf` but in
|
||||
accessible via Go directly.
|
||||
|
||||
## Caveats
|
||||
* Some utility functions will call
|
||||
[`runtime.LockOSThread`](https://golang.org/pkg/runtime/#LockOSThread) for
|
||||
you, they will also unlock the thread after profiling. ***Note*** using these
|
||||
utility functions will incur significant overhead.
|
||||
* Overflow handling is not implemented.
|
||||
|
||||
# Setup
|
||||
Most likely you will need to tweak some system settings unless you are running as root. From `man perf_event_open`:
|
||||
|
||||
```
|
||||
perf_event related configuration files
|
||||
Files in /proc/sys/kernel/
|
||||
|
||||
/proc/sys/kernel/perf_event_paranoid
|
||||
The perf_event_paranoid file can be set to restrict access to the performance counters.
|
||||
|
||||
2 allow only user-space measurements (default since Linux 4.6).
|
||||
1 allow both kernel and user measurements (default before Linux 4.6).
|
||||
0 allow access to CPU-specific data but not raw tracepoint samples.
|
||||
-1 no restrictions.
|
||||
|
||||
The existence of the perf_event_paranoid file is the official method for determining if a kernel supports perf_event_open().
|
||||
|
||||
/proc/sys/kernel/perf_event_max_sample_rate
|
||||
This sets the maximum sample rate. Setting this too high can allow users to sample at a rate that impacts overall machine performance and potentially lock up the machine. The default value is 100000 (samples per
|
||||
second).
|
||||
|
||||
/proc/sys/kernel/perf_event_max_stack
|
||||
This file sets the maximum depth of stack frame entries reported when generating a call trace.
|
||||
|
||||
/proc/sys/kernel/perf_event_mlock_kb
|
||||
Maximum number of pages an unprivileged user can mlock(2). The default is 516 (kB).
|
||||
|
||||
```
|
||||
|
||||
# Example
|
||||
Say you wanted to see how many CPU instructions a particular function took:
|
||||
|
||||
```
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"github.com/hodgesds/perf-utils"
|
||||
)
|
||||
|
||||
func foo() error {
|
||||
var total int
|
||||
for i:=0;i<1000;i++ {
|
||||
total++
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
profileValue, err := perf.CPUInstructions(foo)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fmt.Printf("CPU instructions: %+v\n", profileValue)
|
||||
}
|
||||
```
|
||||
|
||||
# Benchmarks
|
||||
To profile a single function call there is an overhead of ~0.4ms.
|
||||
|
||||
```
|
||||
$ go test -bench=BenchmarkCPUCycles .
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/hodgesds/perf-utils
|
||||
BenchmarkCPUCycles-8 3000 397924 ns/op 32 B/op 1 allocs/op
|
||||
PASS
|
||||
ok github.com/hodgesds/perf-utils 1.255s
|
||||
```
|
||||
|
||||
The `Profiler` interface has low overhead and suitable for many use cases:
|
||||
|
||||
```
|
||||
$ go test -bench=BenchmarkProfiler .
|
||||
goos: linux
|
||||
goarch: amd64
|
||||
pkg: github.com/hodgesds/perf-utils
|
||||
BenchmarkProfiler-8 3000000 488 ns/op 32 B/op 1 allocs/op
|
||||
PASS
|
||||
ok github.com/hodgesds/perf-utils 1.981s
|
||||
```
|
||||
|
||||
# BPF Support
|
||||
BPF is supported by using the `BPFProfiler` which is available via the
|
||||
`ProfileTracepoint` function. To use BPF you need to create the BPF program and
|
||||
then call `AttachBPF` with the file descriptor of the BPF program. This is not
|
||||
well tested so use at your own peril.
|
||||
|
||||
# Misc
|
||||
Originally I set out to use `go generate` to build Go structs that were
|
||||
compatible with perf, I found a really good
|
||||
[article](https://utcc.utoronto.ca/~cks/space/blog/programming/GoCGoCompatibleStructs)
|
||||
on how to do so. Eventually, after digging through some of the `/x/sys/unix`
|
||||
code I found pretty much what I was needed. However, I think if you are
|
||||
interested in interacting with the kernel it is a worthwhile read.
|
22
vendor/github.com/hodgesds/perf-utils/bpf.go
generated
vendored
Normal file
22
vendor/github.com/hodgesds/perf-utils/bpf.go
generated
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
// +build linux
|
||||
|
||||
package perf
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// BPFProfiler is a Profiler that allows attaching a Berkeley
|
||||
// Packet Filter (BPF) program to an existing kprobe tracepoint event.
|
||||
// You need CAP_SYS_ADMIN privileges to use this interface. See:
|
||||
// https://lwn.net/Articles/683504/
|
||||
type BPFProfiler interface {
|
||||
Profiler
|
||||
AttachBPF(int) error
|
||||
}
|
||||
|
||||
// AttachBPF is used to attach a BPF program to a profiler by using the file
|
||||
// descriptor of the BPF program.
|
||||
func (p *profiler) AttachBPF(fd int) error {
|
||||
return unix.IoctlSetInt(p.fd, unix.PERF_EVENT_IOC_SET_BPF, fd)
|
||||
}
|
336
vendor/github.com/hodgesds/perf-utils/cache_profiler.go
generated
vendored
Normal file
336
vendor/github.com/hodgesds/perf-utils/cache_profiler.go
generated
vendored
Normal file
|
@ -0,0 +1,336 @@
|
|||
// +build linux
|
||||
|
||||
package perf
|
||||
|
||||
import (
|
||||
"go.uber.org/multierr"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// L1DataReadHit is a constant...
|
||||
L1DataReadHit = (unix.PERF_COUNT_HW_CACHE_L1D) | (unix.PERF_COUNT_HW_CACHE_OP_READ << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
// L1DataReadMiss is a constant...
|
||||
L1DataReadMiss = (unix.PERF_COUNT_HW_CACHE_L1D) | (unix.PERF_COUNT_HW_CACHE_OP_READ << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
// L1DataWriteHit is a constant...
|
||||
L1DataWriteHit = (unix.PERF_COUNT_HW_CACHE_L1D) | (unix.PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
// L1InstrReadMiss is a constant...
|
||||
L1InstrReadMiss = (unix.PERF_COUNT_HW_CACHE_L1I) | (unix.PERF_COUNT_HW_CACHE_OP_READ << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
|
||||
// LLReadHit is a constant...
|
||||
LLReadHit = (unix.PERF_COUNT_HW_CACHE_LL) | (unix.PERF_COUNT_HW_CACHE_OP_READ << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
// LLReadMiss is a constant...
|
||||
LLReadMiss = (unix.PERF_COUNT_HW_CACHE_LL) | (unix.PERF_COUNT_HW_CACHE_OP_READ << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
// LLWriteHit is a constant...
|
||||
LLWriteHit = (unix.PERF_COUNT_HW_CACHE_LL) | (unix.PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
// LLWriteMiss is a constant...
|
||||
LLWriteMiss = (unix.PERF_COUNT_HW_CACHE_LL) | (unix.PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
|
||||
// DataTLBReadHit is a constant...
|
||||
DataTLBReadHit = (unix.PERF_COUNT_HW_CACHE_DTLB) | (unix.PERF_COUNT_HW_CACHE_OP_READ << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
// DataTLBReadMiss is a constant...
|
||||
DataTLBReadMiss = (unix.PERF_COUNT_HW_CACHE_DTLB) | (unix.PERF_COUNT_HW_CACHE_OP_READ << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
// DataTLBWriteHit is a constant...
|
||||
DataTLBWriteHit = (unix.PERF_COUNT_HW_CACHE_DTLB) | (unix.PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
// DataTLBWriteMiss is a constant...
|
||||
DataTLBWriteMiss = (unix.PERF_COUNT_HW_CACHE_DTLB) | (unix.PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
|
||||
// InstrTLBReadHit is a constant...
|
||||
InstrTLBReadHit = (unix.PERF_COUNT_HW_CACHE_ITLB) | (unix.PERF_COUNT_HW_CACHE_OP_READ << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
// InstrTLBReadMiss is a constant...
|
||||
InstrTLBReadMiss = (unix.PERF_COUNT_HW_CACHE_ITLB) | (unix.PERF_COUNT_HW_CACHE_OP_READ << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
|
||||
// BPUReadHit is a constant...
|
||||
BPUReadHit = (unix.PERF_COUNT_HW_CACHE_BPU) | (unix.PERF_COUNT_HW_CACHE_OP_READ << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
// BPUReadMiss is a constant...
|
||||
BPUReadMiss = (unix.PERF_COUNT_HW_CACHE_BPU) | (unix.PERF_COUNT_HW_CACHE_OP_READ << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
|
||||
// NodeCacheReadHit is a constant...
|
||||
NodeCacheReadHit = (unix.PERF_COUNT_HW_CACHE_NODE) | (unix.PERF_COUNT_HW_CACHE_OP_READ << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
// NodeCacheReadMiss is a constant...
|
||||
NodeCacheReadMiss = (unix.PERF_COUNT_HW_CACHE_NODE) | (unix.PERF_COUNT_HW_CACHE_OP_READ << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
// NodeCacheWriteHit is a constant...
|
||||
NodeCacheWriteHit = (unix.PERF_COUNT_HW_CACHE_NODE) | (unix.PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16)
|
||||
// NodeCacheWriteMiss is a constant...
|
||||
NodeCacheWriteMiss = (unix.PERF_COUNT_HW_CACHE_NODE) | (unix.PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (unix.PERF_COUNT_HW_CACHE_RESULT_MISS << 16)
|
||||
)
|
||||
|
||||
type cacheProfiler struct {
|
||||
// map of perf counter type to file descriptor
|
||||
profilers map[int]Profiler
|
||||
}
|
||||
|
||||
// NewCacheProfiler returns a new cache profiler.
|
||||
func NewCacheProfiler(pid, cpu int, opts ...int) CacheProfiler {
|
||||
profilers := map[int]Profiler{}
|
||||
|
||||
// L1 data
|
||||
op := unix.PERF_COUNT_HW_CACHE_OP_READ
|
||||
result := unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS
|
||||
l1dataReadHit, err := NewL1DataProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[L1DataReadHit] = l1dataReadHit
|
||||
}
|
||||
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_READ
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_MISS
|
||||
l1dataReadMiss, err := NewL1DataProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[L1DataReadMiss] = l1dataReadMiss
|
||||
}
|
||||
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_WRITE
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS
|
||||
l1dataWriteHit, err := NewL1DataProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[L1DataWriteHit] = l1dataWriteHit
|
||||
}
|
||||
|
||||
// L1 instruction
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_READ
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_MISS
|
||||
l1InstrReadMiss, err := NewL1InstrProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[L1InstrReadMiss] = l1InstrReadMiss
|
||||
}
|
||||
|
||||
// Last Level
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_READ
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS
|
||||
llReadHit, err := NewLLCacheProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[LLReadHit] = llReadHit
|
||||
}
|
||||
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_READ
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_MISS
|
||||
llReadMiss, err := NewLLCacheProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[LLReadMiss] = llReadMiss
|
||||
}
|
||||
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_WRITE
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS
|
||||
llWriteHit, err := NewLLCacheProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[LLWriteHit] = llWriteHit
|
||||
}
|
||||
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_WRITE
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_MISS
|
||||
llWriteMiss, err := NewLLCacheProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[LLWriteMiss] = llWriteMiss
|
||||
}
|
||||
|
||||
// dTLB
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_READ
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS
|
||||
dTLBReadHit, err := NewDataTLBProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[DataTLBReadHit] = dTLBReadHit
|
||||
}
|
||||
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_READ
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_MISS
|
||||
dTLBReadMiss, err := NewDataTLBProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[DataTLBReadMiss] = dTLBReadMiss
|
||||
}
|
||||
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_WRITE
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS
|
||||
dTLBWriteHit, err := NewDataTLBProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[DataTLBWriteHit] = dTLBWriteHit
|
||||
}
|
||||
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_WRITE
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_MISS
|
||||
dTLBWriteMiss, err := NewDataTLBProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[DataTLBWriteMiss] = dTLBWriteMiss
|
||||
}
|
||||
|
||||
// iTLB
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_READ
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS
|
||||
iTLBReadHit, err := NewInstrTLBProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[InstrTLBReadHit] = iTLBReadHit
|
||||
}
|
||||
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_READ
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_MISS
|
||||
iTLBReadMiss, err := NewInstrTLBProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[InstrTLBReadMiss] = iTLBReadMiss
|
||||
}
|
||||
|
||||
// BPU
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_READ
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS
|
||||
bpuReadHit, err := NewBPUProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[BPUReadHit] = bpuReadHit
|
||||
}
|
||||
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_READ
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_MISS
|
||||
bpuReadMiss, err := NewBPUProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[BPUReadMiss] = bpuReadMiss
|
||||
}
|
||||
|
||||
// Node
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_READ
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS
|
||||
nodeReadHit, err := NewNodeCacheProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[NodeCacheReadHit] = nodeReadHit
|
||||
}
|
||||
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_READ
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_MISS
|
||||
nodeReadMiss, err := NewNodeCacheProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[NodeCacheReadMiss] = nodeReadMiss
|
||||
}
|
||||
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_WRITE
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_ACCESS
|
||||
nodeWriteHit, err := NewNodeCacheProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[NodeCacheWriteHit] = nodeWriteHit
|
||||
}
|
||||
|
||||
op = unix.PERF_COUNT_HW_CACHE_OP_WRITE
|
||||
result = unix.PERF_COUNT_HW_CACHE_RESULT_MISS
|
||||
nodeWriteMiss, err := NewNodeCacheProfiler(pid, cpu, op, result, opts...)
|
||||
if err == nil {
|
||||
profilers[NodeCacheWriteMiss] = nodeWriteMiss
|
||||
}
|
||||
|
||||
return &cacheProfiler{
|
||||
profilers: profilers,
|
||||
}
|
||||
}
|
||||
|
||||
// Start is used to start the CacheProfiler, it will return an error if no
|
||||
// profilers are configured.
|
||||
func (p *cacheProfiler) Start() error {
|
||||
if len(p.profilers) == 0 {
|
||||
return ErrNoProfiler
|
||||
}
|
||||
var err error
|
||||
for _, profiler := range p.profilers {
|
||||
err = multierr.Append(err, profiler.Start())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Reset is used to reset the CacheProfiler.
|
||||
func (p *cacheProfiler) Reset() error {
|
||||
var err error
|
||||
for _, profiler := range p.profilers {
|
||||
err = multierr.Append(err, profiler.Reset())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Stop is used to reset the CacheProfiler.
|
||||
func (p *cacheProfiler) Stop() error {
|
||||
var err error
|
||||
for _, profiler := range p.profilers {
|
||||
err = multierr.Append(err, profiler.Stop())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Close is used to reset the CacheProfiler.
|
||||
func (p *cacheProfiler) Close() error {
|
||||
var err error
|
||||
for _, profiler := range p.profilers {
|
||||
err = multierr.Append(err, profiler.Close())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Profile is used to read the CacheProfiler CacheProfile it returns an
|
||||
// error only if all profiles fail.
|
||||
func (p *cacheProfiler) Profile() (*CacheProfile, error) {
|
||||
var err error
|
||||
cacheProfile := &CacheProfile{}
|
||||
for profilerType, profiler := range p.profilers {
|
||||
profileVal, err2 := profiler.Profile()
|
||||
err = multierr.Append(err, err2)
|
||||
if err2 == nil {
|
||||
if cacheProfile.TimeEnabled == nil {
|
||||
cacheProfile.TimeEnabled = &profileVal.TimeEnabled
|
||||
}
|
||||
if cacheProfile.TimeRunning == nil {
|
||||
cacheProfile.TimeRunning = &profileVal.TimeRunning
|
||||
}
|
||||
switch {
|
||||
// L1 data
|
||||
case (profilerType ^ L1DataReadHit) == 0:
|
||||
cacheProfile.L1DataReadHit = &profileVal.Value
|
||||
case (profilerType ^ L1DataReadMiss) == 0:
|
||||
cacheProfile.L1DataReadMiss = &profileVal.Value
|
||||
case (profilerType ^ L1DataWriteHit) == 0:
|
||||
cacheProfile.L1DataWriteHit = &profileVal.Value
|
||||
|
||||
// L1 instruction
|
||||
case (profilerType ^ L1InstrReadMiss) == 0:
|
||||
cacheProfile.L1InstrReadMiss = &profileVal.Value
|
||||
|
||||
// Last Level
|
||||
case (profilerType ^ LLReadHit) == 0:
|
||||
cacheProfile.LastLevelReadHit = &profileVal.Value
|
||||
case (profilerType ^ LLReadMiss) == 0:
|
||||
cacheProfile.LastLevelReadMiss = &profileVal.Value
|
||||
case (profilerType ^ LLWriteHit) == 0:
|
||||
cacheProfile.LastLevelWriteHit = &profileVal.Value
|
||||
case (profilerType ^ LLWriteMiss) == 0:
|
||||
cacheProfile.LastLevelWriteMiss = &profileVal.Value
|
||||
|
||||
// dTLB
|
||||
case (profilerType ^ DataTLBReadHit) == 0:
|
||||
cacheProfile.DataTLBReadHit = &profileVal.Value
|
||||
case (profilerType ^ DataTLBReadMiss) == 0:
|
||||
cacheProfile.DataTLBReadMiss = &profileVal.Value
|
||||
case (profilerType ^ DataTLBWriteHit) == 0:
|
||||
cacheProfile.DataTLBWriteHit = &profileVal.Value
|
||||
case (profilerType ^ DataTLBWriteMiss) == 0:
|
||||
cacheProfile.DataTLBWriteMiss = &profileVal.Value
|
||||
|
||||
// iTLB
|
||||
case (profilerType ^ InstrTLBReadHit) == 0:
|
||||
cacheProfile.InstrTLBReadHit = &profileVal.Value
|
||||
case (profilerType ^ InstrTLBReadMiss) == 0:
|
||||
cacheProfile.InstrTLBReadMiss = &profileVal.Value
|
||||
|
||||
// BPU
|
||||
case (profilerType ^ BPUReadHit) == 0:
|
||||
cacheProfile.BPUReadHit = &profileVal.Value
|
||||
case (profilerType ^ BPUReadMiss) == 0:
|
||||
cacheProfile.BPUReadMiss = &profileVal.Value
|
||||
|
||||
// node
|
||||
case (profilerType ^ NodeCacheReadHit) == 0:
|
||||
cacheProfile.NodeReadHit = &profileVal.Value
|
||||
case (profilerType ^ NodeCacheReadMiss) == 0:
|
||||
cacheProfile.NodeReadMiss = &profileVal.Value
|
||||
case (profilerType ^ NodeCacheWriteHit) == 0:
|
||||
cacheProfile.NodeWriteHit = &profileVal.Value
|
||||
case (profilerType ^ NodeCacheWriteMiss) == 0:
|
||||
cacheProfile.NodeWriteMiss = &profileVal.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(multierr.Errors(err)) == len(p.profilers) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return cacheProfile, nil
|
||||
}
|
98
vendor/github.com/hodgesds/perf-utils/events.go
generated
vendored
Normal file
98
vendor/github.com/hodgesds/perf-utils/events.go
generated
vendored
Normal file
|
@ -0,0 +1,98 @@
|
|||
// +build linux
|
||||
|
||||
package perf
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// PERF_TYPE_TRACEPOINT is a kernel tracepoint.
|
||||
PERF_TYPE_TRACEPOINT = 2
|
||||
)
|
||||
|
||||
// AvailableEvents returns the list of available events.
|
||||
func AvailableEvents() (map[string][]string, error) {
|
||||
events := map[string][]string{}
|
||||
rawEvents, err := fileToStrings(TracingDir + "/available_events")
|
||||
// Events are colon delimited by type so parse the type and add sub
|
||||
// events appropriately.
|
||||
if err != nil {
|
||||
return events, err
|
||||
}
|
||||
for _, rawEvent := range rawEvents {
|
||||
splits := strings.Split(rawEvent, ":")
|
||||
if len(splits) <= 1 {
|
||||
continue
|
||||
}
|
||||
eventTypeEvents, found := events[splits[0]]
|
||||
if found {
|
||||
events[splits[0]] = append(eventTypeEvents, splits[1])
|
||||
continue
|
||||
}
|
||||
events[splits[0]] = []string{splits[1]}
|
||||
}
|
||||
return events, err
|
||||
}
|
||||
|
||||
// AvailableTracers returns the list of available tracers.
|
||||
func AvailableTracers() ([]string, error) {
|
||||
return fileToStrings(TracingDir + "/available_tracers")
|
||||
}
|
||||
|
||||
// CurrentTracer returns the current tracer.
|
||||
func CurrentTracer() (string, error) {
|
||||
res, err := fileToStrings(TracingDir + "/current_tracer")
|
||||
return res[0], err
|
||||
}
|
||||
|
||||
// getTracepointConfig is used to get the configuration for a trace event.
|
||||
func getTracepointConfig(kind, event string) (uint64, error) {
|
||||
res, err := fileToStrings(TracingDir + fmt.Sprintf("/events/%s/%s/id", kind, event))
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return strconv.ParseUint(res[0], 10, 64)
|
||||
}
|
||||
|
||||
// ProfileTracepoint is used to profile a kernel tracepoint event. Events can
|
||||
// be listed with `perf list` for Tracepoint Events or in the
|
||||
// /sys/kernel/debug/tracing/events directory with the kind being the directory
|
||||
// and the event being the subdirectory.
|
||||
func ProfileTracepoint(kind, event string, pid, cpu int, opts ...int) (BPFProfiler, error) {
|
||||
config, err := getTracepointConfig(kind, event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: PERF_TYPE_TRACEPOINT,
|
||||
Config: config,
|
||||
Size: uint32(unsafe.Sizeof(unix.PerfEventAttr{})),
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
Sample_type: PERF_SAMPLE_IDENTIFIER,
|
||||
}
|
||||
var eventOps int
|
||||
if len(opts) > 0 {
|
||||
eventOps = opts[0]
|
||||
}
|
||||
fd, err := unix.PerfEventOpen(
|
||||
eventAttr,
|
||||
pid,
|
||||
cpu,
|
||||
-1,
|
||||
eventOps,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &profiler{
|
||||
fd: fd,
|
||||
}, nil
|
||||
}
|
102
vendor/github.com/hodgesds/perf-utils/fs_utils.go
generated
vendored
Normal file
102
vendor/github.com/hodgesds/perf-utils/fs_utils.go
generated
vendored
Normal file
|
@ -0,0 +1,102 @@
|
|||
// +build linux
|
||||
|
||||
package perf
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// DebugFS is the filesystem type for debugfs.
|
||||
DebugFS = "debugfs"
|
||||
|
||||
// TraceFS is the filesystem type for tracefs.
|
||||
TraceFS = "tracefs"
|
||||
|
||||
// ProcMounts is the mount point for file systems in procfs.
|
||||
ProcMounts = "/proc/mounts"
|
||||
|
||||
// PerfMaxStack is the mount point for the max perf event size.
|
||||
PerfMaxStack = "/proc/sys/kernel/perf_event_max_stack"
|
||||
|
||||
// PerfMaxContexts is a sysfs mount that contains the max perf contexts.
|
||||
PerfMaxContexts = "/proc/sys/kernel/perf_event_max_contexts_per_stack"
|
||||
|
||||
// SyscallsDir is a constant of the default tracing event syscalls directory.
|
||||
SyscallsDir = "/sys/kernel/debug/tracing/events/syscalls/"
|
||||
|
||||
// TracingDir is a constant of the default tracing directory.
|
||||
TracingDir = "/sys/kernel/debug/tracing"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoMount is when there is no such mount.
|
||||
ErrNoMount = fmt.Errorf("no such mount")
|
||||
)
|
||||
|
||||
// TraceFSMount returns the first found mount point of a tracefs file system.
|
||||
func TraceFSMount() (string, error) {
|
||||
mounts, err := GetFSMount(TraceFS)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(mounts) == 0 {
|
||||
return "", ErrNoMount
|
||||
}
|
||||
return mounts[0], nil
|
||||
}
|
||||
|
||||
// DebugFSMount returns the first found mount point of a debugfs file system.
|
||||
func DebugFSMount() (string, error) {
|
||||
mounts, err := GetFSMount(DebugFS)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(mounts) == 0 {
|
||||
return "", ErrNoMount
|
||||
}
|
||||
return mounts[0], nil
|
||||
}
|
||||
|
||||
// GetFSMount is a helper function to get a mount file system type.
|
||||
func GetFSMount(mountType string) ([]string, error) {
|
||||
mounts := []string{}
|
||||
file, err := os.Open(ProcMounts)
|
||||
if err != nil {
|
||||
return mounts, err
|
||||
}
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
mountInfo := strings.Split(scanner.Text(), " ")
|
||||
if len(mountInfo) > 3 && mountInfo[2] == mountType {
|
||||
mounts = append(mounts, mountInfo[1])
|
||||
}
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return mounts, err
|
||||
}
|
||||
|
||||
return mounts, file.Close()
|
||||
}
|
||||
|
||||
// fileToStrings is a helper method that reads a line line by line and returns
|
||||
// a slice of strings.
|
||||
func fileToStrings(path string) ([]string, error) {
|
||||
res := []string{}
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
res = append(res, scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
170
vendor/github.com/hodgesds/perf-utils/group_profiler.go
generated
vendored
Normal file
170
vendor/github.com/hodgesds/perf-utils/group_profiler.go
generated
vendored
Normal file
|
@ -0,0 +1,170 @@
|
|||
// +build linux
|
||||
|
||||
package perf
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"syscall"
|
||||
|
||||
"go.uber.org/multierr"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// ErrNoLeader is returned when a leader of a GroupProfiler is not defined.
|
||||
var ErrNoLeader = fmt.Errorf("No leader defined")
|
||||
|
||||
// GroupProfileValue is returned from a GroupProfiler.
|
||||
type GroupProfileValue struct {
|
||||
Events uint64
|
||||
TimeEnabled uint64
|
||||
TimeRunning uint64
|
||||
Values []uint64
|
||||
}
|
||||
|
||||
// GroupProfiler is used to setup a group profiler.
|
||||
type GroupProfiler interface {
|
||||
Start() error
|
||||
Reset() error
|
||||
Stop() error
|
||||
Close() error
|
||||
Profile() (*GroupProfileValue, error)
|
||||
}
|
||||
|
||||
// groupProfiler implements the GroupProfiler interface.
|
||||
type groupProfiler struct {
|
||||
fds []int // leader is always element 0
|
||||
}
|
||||
|
||||
// NewGroupProfiler returns a GroupProfiler.
|
||||
func NewGroupProfiler(pid, cpu, opts int, eventAttrs ...unix.PerfEventAttr) (GroupProfiler, error) {
|
||||
fds := make([]int, len(eventAttrs))
|
||||
|
||||
for i, eventAttr := range eventAttrs {
|
||||
// common configs
|
||||
eventAttr.Size = EventAttrSize
|
||||
eventAttr.Sample_type = PERF_SAMPLE_IDENTIFIER
|
||||
|
||||
// Leader fd must be opened first
|
||||
if i == 0 {
|
||||
// leader specific configs
|
||||
eventAttr.Bits = unix.PerfBitDisabled | unix.PerfBitExcludeHv
|
||||
eventAttr.Read_format = unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED | unix.PERF_FORMAT_GROUP
|
||||
|
||||
fd, err := unix.PerfEventOpen(
|
||||
&eventAttr,
|
||||
pid,
|
||||
cpu,
|
||||
-1,
|
||||
opts,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
fds[i] = fd
|
||||
continue
|
||||
}
|
||||
|
||||
// non leader configs
|
||||
eventAttr.Read_format = unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED | unix.PERF_FORMAT_GROUP
|
||||
eventAttr.Bits = unix.PerfBitExcludeHv
|
||||
|
||||
fd, err := unix.PerfEventOpen(
|
||||
&eventAttr,
|
||||
pid,
|
||||
cpu,
|
||||
fds[0],
|
||||
opts,
|
||||
)
|
||||
if err != nil {
|
||||
// cleanup any old Fds
|
||||
for ii, fd2 := range fds {
|
||||
if ii == i {
|
||||
break
|
||||
}
|
||||
err = multierr.Append(err, unix.Close(fd2))
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
fds[i] = fd
|
||||
}
|
||||
|
||||
return &groupProfiler{
|
||||
fds: fds,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Start is used to start the GroupProfiler.
|
||||
func (p *groupProfiler) Start() error {
|
||||
if len(p.fds) == 0 {
|
||||
return ErrNoLeader
|
||||
}
|
||||
return unix.IoctlSetInt(p.fds[0], unix.PERF_EVENT_IOC_ENABLE, 0)
|
||||
}
|
||||
|
||||
// Reset is used to reset the GroupProfiler.
|
||||
func (p *groupProfiler) Reset() error {
|
||||
if len(p.fds) == 0 {
|
||||
return ErrNoLeader
|
||||
}
|
||||
return unix.IoctlSetInt(p.fds[0], unix.PERF_EVENT_IOC_RESET, 0)
|
||||
}
|
||||
|
||||
// Stop is used to stop the GroupProfiler.
|
||||
func (p *groupProfiler) Stop() error {
|
||||
if len(p.fds) == 0 {
|
||||
return ErrNoLeader
|
||||
}
|
||||
return unix.IoctlSetInt(p.fds[0], unix.PERF_EVENT_IOC_DISABLE, 0)
|
||||
}
|
||||
|
||||
// Close is used to close the GroupProfiler.
|
||||
func (p *groupProfiler) Close() error {
|
||||
var err error
|
||||
for _, fd := range p.fds {
|
||||
err = multierr.Append(err, unix.Close(fd))
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Profile is used to return the GroupProfileValue of the GroupProfiler.
|
||||
func (p *groupProfiler) Profile() (*GroupProfileValue, error) {
|
||||
nEvents := len(p.fds)
|
||||
if nEvents == 0 {
|
||||
return nil, ErrNoLeader
|
||||
}
|
||||
|
||||
// read format of the raw event looks like this:
|
||||
/*
|
||||
struct read_format {
|
||||
u64 nr; // The number of events /
|
||||
u64 time_enabled; // if PERF_FORMAT_TOTAL_TIME_ENABLED
|
||||
u64 time_running; // if PERF_FORMAT_TOTAL_TIME_RUNNING
|
||||
struct {
|
||||
u64 value; // The value of the event
|
||||
u64 id; // if PERF_FORMAT_ID
|
||||
} values[nr];
|
||||
};
|
||||
*/
|
||||
|
||||
buf := make([]byte, 24+8*nEvents)
|
||||
_, err := syscall.Read(p.fds[0], buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
val := &GroupProfileValue{
|
||||
Events: binary.LittleEndian.Uint64(buf[0:8]),
|
||||
TimeEnabled: binary.LittleEndian.Uint64(buf[8:16]),
|
||||
TimeRunning: binary.LittleEndian.Uint64(buf[16:24]),
|
||||
Values: make([]uint64, len(p.fds)),
|
||||
}
|
||||
|
||||
offset := 24
|
||||
for i := range p.fds {
|
||||
val.Values[i] = binary.LittleEndian.Uint64(buf[offset : offset+8])
|
||||
offset += 8
|
||||
}
|
||||
|
||||
return val, nil
|
||||
}
|
157
vendor/github.com/hodgesds/perf-utils/hardware_profiler.go
generated
vendored
Normal file
157
vendor/github.com/hodgesds/perf-utils/hardware_profiler.go
generated
vendored
Normal file
|
@ -0,0 +1,157 @@
|
|||
// +build linux
|
||||
|
||||
package perf
|
||||
|
||||
import (
|
||||
"go.uber.org/multierr"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type hardwareProfiler struct {
|
||||
// map of perf counter type to file descriptor
|
||||
profilers map[int]Profiler
|
||||
}
|
||||
|
||||
// NewHardwareProfiler returns a new hardware profiler.
|
||||
func NewHardwareProfiler(pid, cpu int, opts ...int) HardwareProfiler {
|
||||
profilers := map[int]Profiler{}
|
||||
|
||||
cpuCycleProfiler, err := NewCPUCycleProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_HW_CPU_CYCLES] = cpuCycleProfiler
|
||||
}
|
||||
|
||||
instrProfiler, err := NewInstrProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_HW_INSTRUCTIONS] = instrProfiler
|
||||
}
|
||||
|
||||
cacheRefProfiler, err := NewCacheRefProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_HW_CACHE_REFERENCES] = cacheRefProfiler
|
||||
}
|
||||
|
||||
cacheMissesProfiler, err := NewCacheMissesProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_HW_CACHE_MISSES] = cacheMissesProfiler
|
||||
}
|
||||
|
||||
branchInstrProfiler, err := NewBranchInstrProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = branchInstrProfiler
|
||||
}
|
||||
|
||||
branchMissesProfiler, err := NewBranchMissesProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_HW_BRANCH_MISSES] = branchMissesProfiler
|
||||
}
|
||||
|
||||
busCyclesProfiler, err := NewBusCyclesProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_HW_BUS_CYCLES] = busCyclesProfiler
|
||||
}
|
||||
|
||||
stalledCyclesFrontProfiler, err := NewStalledCyclesFrontProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_HW_STALLED_CYCLES_FRONTEND] = stalledCyclesFrontProfiler
|
||||
}
|
||||
|
||||
stalledCyclesBackProfiler, err := NewStalledCyclesBackProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_HW_STALLED_CYCLES_BACKEND] = stalledCyclesBackProfiler
|
||||
}
|
||||
|
||||
refCPUCyclesProfiler, err := NewRefCPUCyclesProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_HW_REF_CPU_CYCLES] = refCPUCyclesProfiler
|
||||
}
|
||||
|
||||
return &hardwareProfiler{
|
||||
profilers: profilers,
|
||||
}
|
||||
}
|
||||
|
||||
// Start is used to start the HardwareProfiler.
|
||||
func (p *hardwareProfiler) Start() error {
|
||||
if len(p.profilers) == 0 {
|
||||
return ErrNoProfiler
|
||||
}
|
||||
var err error
|
||||
for _, profiler := range p.profilers {
|
||||
err = multierr.Append(err, profiler.Start())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Reset is used to reset the HardwareProfiler.
|
||||
func (p *hardwareProfiler) Reset() error {
|
||||
var err error
|
||||
for _, profiler := range p.profilers {
|
||||
err = multierr.Append(err, profiler.Reset())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Stop is used to reset the HardwareProfiler.
|
||||
func (p *hardwareProfiler) Stop() error {
|
||||
var err error
|
||||
for _, profiler := range p.profilers {
|
||||
err = multierr.Append(err, profiler.Stop())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Close is used to reset the HardwareProfiler.
|
||||
func (p *hardwareProfiler) Close() error {
|
||||
var err error
|
||||
for _, profiler := range p.profilers {
|
||||
err = multierr.Append(err, profiler.Close())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Profile is used to read the HardwareProfiler HardwareProfile it returns an
|
||||
// error only if all profiles fail.
|
||||
func (p *hardwareProfiler) Profile() (*HardwareProfile, error) {
|
||||
var err error
|
||||
hwProfile := &HardwareProfile{}
|
||||
for profilerType, profiler := range p.profilers {
|
||||
profileVal, err2 := profiler.Profile()
|
||||
err = multierr.Append(err, err2)
|
||||
if err2 == nil {
|
||||
if hwProfile.TimeEnabled == nil {
|
||||
hwProfile.TimeEnabled = &profileVal.TimeEnabled
|
||||
}
|
||||
if hwProfile.TimeRunning == nil {
|
||||
hwProfile.TimeRunning = &profileVal.TimeRunning
|
||||
}
|
||||
switch profilerType {
|
||||
case unix.PERF_COUNT_HW_CPU_CYCLES:
|
||||
hwProfile.CPUCycles = &profileVal.Value
|
||||
case unix.PERF_COUNT_HW_INSTRUCTIONS:
|
||||
hwProfile.Instructions = &profileVal.Value
|
||||
case unix.PERF_COUNT_HW_CACHE_REFERENCES:
|
||||
hwProfile.CacheRefs = &profileVal.Value
|
||||
case unix.PERF_COUNT_HW_CACHE_MISSES:
|
||||
hwProfile.CacheMisses = &profileVal.Value
|
||||
case unix.PERF_COUNT_HW_BRANCH_INSTRUCTIONS:
|
||||
hwProfile.BranchInstr = &profileVal.Value
|
||||
case unix.PERF_COUNT_HW_BRANCH_MISSES:
|
||||
hwProfile.BranchMisses = &profileVal.Value
|
||||
case unix.PERF_COUNT_HW_BUS_CYCLES:
|
||||
hwProfile.BusCycles = &profileVal.Value
|
||||
case unix.PERF_COUNT_HW_STALLED_CYCLES_FRONTEND:
|
||||
hwProfile.StalledCyclesFrontend = &profileVal.Value
|
||||
case unix.PERF_COUNT_HW_STALLED_CYCLES_BACKEND:
|
||||
hwProfile.StalledCyclesBackend = &profileVal.Value
|
||||
case unix.PERF_COUNT_HW_REF_CPU_CYCLES:
|
||||
hwProfile.RefCPUCycles = &profileVal.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(multierr.Errors(err)) == len(p.profilers) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return hwProfile, nil
|
||||
}
|
507
vendor/github.com/hodgesds/perf-utils/process_profile.go
generated
vendored
Normal file
507
vendor/github.com/hodgesds/perf-utils/process_profile.go
generated
vendored
Normal file
|
@ -0,0 +1,507 @@
|
|||
// +build linux
|
||||
|
||||
package perf
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
const (
|
||||
// PERF_SAMPLE_IDENTIFIER is not defined in x/sys/unix.
|
||||
PERF_SAMPLE_IDENTIFIER = 1 << 16
|
||||
|
||||
// PERF_IOC_FLAG_GROUP is not defined in x/sys/unix.
|
||||
PERF_IOC_FLAG_GROUP = 1 << 0
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrNoProfiler is returned when no profiler is available for profiling.
|
||||
ErrNoProfiler = fmt.Errorf("No profiler available")
|
||||
)
|
||||
|
||||
// Profiler is a profiler.
|
||||
type Profiler interface {
|
||||
Start() error
|
||||
Reset() error
|
||||
Stop() error
|
||||
Close() error
|
||||
Profile() (*ProfileValue, error)
|
||||
}
|
||||
|
||||
// HardwareProfiler is a hardware profiler.
|
||||
type HardwareProfiler interface {
|
||||
Start() error
|
||||
Reset() error
|
||||
Stop() error
|
||||
Close() error
|
||||
Profile() (*HardwareProfile, error)
|
||||
}
|
||||
|
||||
// HardwareProfile is returned by a HardwareProfiler. Depending on kernel
|
||||
// configuration some fields may return nil.
|
||||
type HardwareProfile struct {
|
||||
CPUCycles *uint64 `json:"cpu_cycles,omitempty"`
|
||||
Instructions *uint64 `json:"instructions,omitempty"`
|
||||
CacheRefs *uint64 `json:"cache_refs,omitempty"`
|
||||
CacheMisses *uint64 `json:"cache_misses,omitempty"`
|
||||
BranchInstr *uint64 `json:"branch_instr,omitempty"`
|
||||
BranchMisses *uint64 `json:"branch_misses,omitempty"`
|
||||
BusCycles *uint64 `json:"bus_cycles,omitempty"`
|
||||
StalledCyclesFrontend *uint64 `json:"stalled_cycles_frontend,omitempty"`
|
||||
StalledCyclesBackend *uint64 `json:"stalled_cycles_backend,omitempty"`
|
||||
RefCPUCycles *uint64 `json:"ref_cpu_cycles,omitempty"`
|
||||
TimeEnabled *uint64 `json:"time_enabled,omitempty"`
|
||||
TimeRunning *uint64 `json:"time_running,omitempty"`
|
||||
}
|
||||
|
||||
// SoftwareProfiler is a software profiler.
|
||||
type SoftwareProfiler interface {
|
||||
Start() error
|
||||
Reset() error
|
||||
Stop() error
|
||||
Close() error
|
||||
Profile() (*SoftwareProfile, error)
|
||||
}
|
||||
|
||||
// SoftwareProfile is returned by a SoftwareProfiler.
|
||||
type SoftwareProfile struct {
|
||||
CPUClock *uint64 `json:"cpu_clock,omitempty"`
|
||||
TaskClock *uint64 `json:"task_clock,omitempty"`
|
||||
PageFaults *uint64 `json:"page_faults,omitempty"`
|
||||
ContextSwitches *uint64 `json:"context_switches,omitempty"`
|
||||
CPUMigrations *uint64 `json:"cpu_migrations,omitempty"`
|
||||
MinorPageFaults *uint64 `json:"minor_page_faults,omitempty"`
|
||||
MajorPageFaults *uint64 `json:"major_page_faults,omitempty"`
|
||||
AlignmentFaults *uint64 `json:"alignment_faults,omitempty"`
|
||||
EmulationFaults *uint64 `json:"emulation_faults,omitempty"`
|
||||
TimeEnabled *uint64 `json:"time_enabled,omitempty"`
|
||||
TimeRunning *uint64 `json:"time_running,omitempty"`
|
||||
}
|
||||
|
||||
// CacheProfiler is a cache profiler.
|
||||
type CacheProfiler interface {
|
||||
Start() error
|
||||
Reset() error
|
||||
Stop() error
|
||||
Close() error
|
||||
Profile() (*CacheProfile, error)
|
||||
}
|
||||
|
||||
// CacheProfile is returned by a CacheProfiler.
|
||||
type CacheProfile struct {
|
||||
L1DataReadHit *uint64 `json:"l1_data_read_hit,omitempty"`
|
||||
L1DataReadMiss *uint64 `json:"l1_data_read_miss,omitempty"`
|
||||
L1DataWriteHit *uint64 `json:"l1_data_write_hit,omitempty"`
|
||||
L1InstrReadMiss *uint64 `json:"l1_instr_read_miss,omitempty"`
|
||||
LastLevelReadHit *uint64 `json:"last_level_read_hit,omitempty"`
|
||||
LastLevelReadMiss *uint64 `json:"last_level_read_miss,omitempty"`
|
||||
LastLevelWriteHit *uint64 `json:"last_level_write_hit,omitempty"`
|
||||
LastLevelWriteMiss *uint64 `json:"last_level_write_miss,omitempty"`
|
||||
DataTLBReadHit *uint64 `json:"data_tlb_read_hit,omitempty"`
|
||||
DataTLBReadMiss *uint64 `json:"data_tlb_read_miss,omitempty"`
|
||||
DataTLBWriteHit *uint64 `json:"data_tlb_write_hit,omitempty"`
|
||||
DataTLBWriteMiss *uint64 `json:"data_tlb_write_miss,omitempty"`
|
||||
InstrTLBReadHit *uint64 `json:"instr_tlb_read_hit,omitempty"`
|
||||
InstrTLBReadMiss *uint64 `json:"instr_tlb_read_miss,omitempty"`
|
||||
BPUReadHit *uint64 `json:"bpu_read_hit,omitempty"`
|
||||
BPUReadMiss *uint64 `json:"bpu_read_miss,omitempty"`
|
||||
NodeReadHit *uint64 `json:"node_read_hit,omitempty"`
|
||||
NodeReadMiss *uint64 `json:"node_read_miss,omitempty"`
|
||||
NodeWriteHit *uint64 `json:"node_write_hit,omitempty"`
|
||||
NodeWriteMiss *uint64 `json:"node_write_miss,omitempty"`
|
||||
TimeEnabled *uint64 `json:"time_enabled,omitempty"`
|
||||
TimeRunning *uint64 `json:"time_running,omitempty"`
|
||||
}
|
||||
|
||||
// ProfileValue is a value returned by a profiler.
|
||||
type ProfileValue struct {
|
||||
Value uint64
|
||||
TimeEnabled uint64
|
||||
TimeRunning uint64
|
||||
}
|
||||
|
||||
// profiler is used to profile a process.
|
||||
type profiler struct {
|
||||
fd int
|
||||
}
|
||||
|
||||
// NewProfiler creates a new hardware profiler. It does not support grouping.
|
||||
func NewProfiler(profilerType uint32, config uint64, pid, cpu int, opts ...int) (Profiler, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: profilerType,
|
||||
Config: config,
|
||||
Size: uint32(unsafe.Sizeof(unix.PerfEventAttr{})),
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeHv | unix.PerfBitInherit,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
Sample_type: PERF_SAMPLE_IDENTIFIER,
|
||||
}
|
||||
var eventOps int
|
||||
if len(opts) > 0 {
|
||||
eventOps = opts[0]
|
||||
}
|
||||
fd, err := unix.PerfEventOpen(
|
||||
eventAttr,
|
||||
pid,
|
||||
cpu,
|
||||
-1,
|
||||
eventOps,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &profiler{
|
||||
fd: fd,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewCPUCycleProfiler returns a Profiler that profiles CPU cycles.
|
||||
func NewCPUCycleProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HARDWARE,
|
||||
unix.PERF_COUNT_HW_CPU_CYCLES,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewInstrProfiler returns a Profiler that profiles CPU instructions.
|
||||
func NewInstrProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HARDWARE,
|
||||
unix.PERF_COUNT_HW_INSTRUCTIONS,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewCacheRefProfiler returns a Profiler that profiles cache references.
|
||||
func NewCacheRefProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HARDWARE,
|
||||
unix.PERF_COUNT_HW_CACHE_REFERENCES,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewCacheMissesProfiler returns a Profiler that profiles cache misses.
|
||||
func NewCacheMissesProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HARDWARE,
|
||||
unix.PERF_COUNT_HW_CACHE_MISSES,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewBranchInstrProfiler returns a Profiler that profiles branch instructions.
|
||||
func NewBranchInstrProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HARDWARE,
|
||||
unix.PERF_COUNT_HW_BRANCH_INSTRUCTIONS,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewBranchMissesProfiler returns a Profiler that profiles branch misses.
|
||||
func NewBranchMissesProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HARDWARE,
|
||||
unix.PERF_COUNT_HW_BRANCH_MISSES,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewBusCyclesProfiler returns a Profiler that profiles bus cycles.
|
||||
func NewBusCyclesProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HARDWARE,
|
||||
unix.PERF_COUNT_HW_BUS_CYCLES,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewStalledCyclesFrontProfiler returns a Profiler that profiles stalled
|
||||
// frontend cycles.
|
||||
func NewStalledCyclesFrontProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HARDWARE,
|
||||
unix.PERF_COUNT_HW_STALLED_CYCLES_FRONTEND,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewStalledCyclesBackProfiler returns a Profiler that profiles stalled
|
||||
// backend cycles.
|
||||
func NewStalledCyclesBackProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HARDWARE,
|
||||
unix.PERF_COUNT_HW_STALLED_CYCLES_BACKEND,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewRefCPUCyclesProfiler returns a Profiler that profiles CPU cycles, it
|
||||
// is not affected by frequency scaling.
|
||||
func NewRefCPUCyclesProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HARDWARE,
|
||||
unix.PERF_COUNT_HW_REF_CPU_CYCLES,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewCPUClockProfiler returns a Profiler that profiles CPU clock speed.
|
||||
func NewCPUClockProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_SOFTWARE,
|
||||
unix.PERF_COUNT_SW_CPU_CLOCK,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewTaskClockProfiler returns a Profiler that profiles clock count of the
|
||||
// running task.
|
||||
func NewTaskClockProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_SOFTWARE,
|
||||
unix.PERF_COUNT_SW_TASK_CLOCK,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewPageFaultProfiler returns a Profiler that profiles the number of page
|
||||
// faults.
|
||||
func NewPageFaultProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_SOFTWARE,
|
||||
unix.PERF_COUNT_SW_PAGE_FAULTS,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewCtxSwitchesProfiler returns a Profiler that profiles the number of context
|
||||
// switches.
|
||||
func NewCtxSwitchesProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_SOFTWARE,
|
||||
unix.PERF_COUNT_SW_CONTEXT_SWITCHES,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewCPUMigrationsProfiler returns a Profiler that profiles the number of times
|
||||
// the process has migrated to a new CPU.
|
||||
func NewCPUMigrationsProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_SOFTWARE,
|
||||
unix.PERF_COUNT_SW_CPU_MIGRATIONS,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewMinorFaultsProfiler returns a Profiler that profiles the number of minor
|
||||
// page faults.
|
||||
func NewMinorFaultsProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_SOFTWARE,
|
||||
unix.PERF_COUNT_SW_PAGE_FAULTS_MIN,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewMajorFaultsProfiler returns a Profiler that profiles the number of major
|
||||
// page faults.
|
||||
func NewMajorFaultsProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_SOFTWARE,
|
||||
unix.PERF_COUNT_SW_PAGE_FAULTS_MAJ,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewAlignFaultsProfiler returns a Profiler that profiles the number of
|
||||
// alignment faults.
|
||||
func NewAlignFaultsProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_SOFTWARE,
|
||||
unix.PERF_COUNT_SW_ALIGNMENT_FAULTS,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewEmulationFaultsProfiler returns a Profiler that profiles the number of
|
||||
// alignment faults.
|
||||
func NewEmulationFaultsProfiler(pid, cpu int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_SOFTWARE,
|
||||
unix.PERF_COUNT_SW_EMULATION_FAULTS,
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewL1DataProfiler returns a Profiler that profiles L1 cache data.
|
||||
func NewL1DataProfiler(pid, cpu, op, result int, opts ...int) (Profiler, error) {
|
||||
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HW_CACHE,
|
||||
uint64((unix.PERF_COUNT_HW_CACHE_L1D)|(op<<8)|(result<<16)),
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewL1InstrProfiler returns a Profiler that profiles L1 instruction data.
|
||||
func NewL1InstrProfiler(pid, cpu, op, result int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HW_CACHE,
|
||||
uint64((unix.PERF_COUNT_HW_CACHE_L1I)|(op<<8)|(result<<16)),
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewLLCacheProfiler returns a Profiler that profiles last level cache.
|
||||
func NewLLCacheProfiler(pid, cpu, op, result int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HW_CACHE,
|
||||
uint64((unix.PERF_COUNT_HW_CACHE_LL)|(op<<8)|(result<<16)),
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewDataTLBProfiler returns a Profiler that profiles the data TLB.
|
||||
func NewDataTLBProfiler(pid, cpu, op, result int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HW_CACHE,
|
||||
uint64((unix.PERF_COUNT_HW_CACHE_DTLB)|(op<<8)|(result<<16)),
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewInstrTLBProfiler returns a Profiler that profiles the instruction TLB.
|
||||
func NewInstrTLBProfiler(pid, cpu, op, result int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HW_CACHE,
|
||||
uint64((unix.PERF_COUNT_HW_CACHE_ITLB)|(op<<8)|(result<<16)),
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewBPUProfiler returns a Profiler that profiles the BPU (branch prediction unit).
|
||||
func NewBPUProfiler(pid, cpu, op, result int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HW_CACHE,
|
||||
uint64((unix.PERF_COUNT_HW_CACHE_BPU)|(op<<8)|(result<<16)),
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// NewNodeCacheProfiler returns a Profiler that profiles the node cache accesses.
|
||||
func NewNodeCacheProfiler(pid, cpu, op, result int, opts ...int) (Profiler, error) {
|
||||
return NewProfiler(
|
||||
unix.PERF_TYPE_HW_CACHE,
|
||||
uint64((unix.PERF_COUNT_HW_CACHE_NODE)|(op<<8)|(result<<16)),
|
||||
pid,
|
||||
cpu,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
// Reset is used to reset the counters of the profiler.
|
||||
func (p *profiler) Reset() error {
|
||||
return unix.IoctlSetInt(p.fd, unix.PERF_EVENT_IOC_RESET, 0)
|
||||
}
|
||||
|
||||
// Start is used to Start the profiler.
|
||||
func (p *profiler) Start() error {
|
||||
return unix.IoctlSetInt(p.fd, unix.PERF_EVENT_IOC_ENABLE, 0)
|
||||
}
|
||||
|
||||
// Stop is used to stop the profiler.
|
||||
func (p *profiler) Stop() error {
|
||||
return unix.IoctlSetInt(p.fd, unix.PERF_EVENT_IOC_DISABLE, 0)
|
||||
}
|
||||
|
||||
// Profile returns the current Profile.
|
||||
func (p *profiler) Profile() (*ProfileValue, error) {
|
||||
// The underlying struct that gets read from the profiler looks like:
|
||||
/*
|
||||
struct read_format {
|
||||
u64 value; // The value of the event
|
||||
u64 time_enabled; // if PERF_FORMAT_TOTAL_TIME_ENABLED
|
||||
u64 time_running; // if PERF_FORMAT_TOTAL_TIME_RUNNING
|
||||
u64 id; // if PERF_FORMAT_ID
|
||||
};
|
||||
*/
|
||||
|
||||
// read 24 bytes since PERF_FORMAT_TOTAL_TIME_ENABLED and
|
||||
// PERF_FORMAT_TOTAL_TIME_RUNNING are always set.
|
||||
// XXX: allow profile ids?
|
||||
buf := make([]byte, 24, 24)
|
||||
_, err := syscall.Read(p.fd, buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ProfileValue{
|
||||
Value: binary.LittleEndian.Uint64(buf[0:8]),
|
||||
TimeEnabled: binary.LittleEndian.Uint64(buf[8:16]),
|
||||
TimeRunning: binary.LittleEndian.Uint64(buf[16:24]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Close is used to close the perf context.
|
||||
func (p *profiler) Close() error {
|
||||
return unix.Close(p.fd)
|
||||
}
|
151
vendor/github.com/hodgesds/perf-utils/software_profiler.go
generated
vendored
Normal file
151
vendor/github.com/hodgesds/perf-utils/software_profiler.go
generated
vendored
Normal file
|
@ -0,0 +1,151 @@
|
|||
// +build linux
|
||||
|
||||
package perf
|
||||
|
||||
import (
|
||||
"go.uber.org/multierr"
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type softwareProfiler struct {
|
||||
// map of perf counter type to file descriptor
|
||||
profilers map[int]Profiler
|
||||
}
|
||||
|
||||
// NewSoftwareProfiler returns a new software profiler.
|
||||
func NewSoftwareProfiler(pid, cpu int, opts ...int) SoftwareProfiler {
|
||||
profilers := map[int]Profiler{}
|
||||
|
||||
cpuClockProfiler, err := NewCPUClockProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_SW_CPU_CLOCK] = cpuClockProfiler
|
||||
}
|
||||
|
||||
taskClockProfiler, err := NewTaskClockProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_SW_TASK_CLOCK] = taskClockProfiler
|
||||
}
|
||||
|
||||
pageFaultProfiler, err := NewPageFaultProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_SW_PAGE_FAULTS] = pageFaultProfiler
|
||||
}
|
||||
|
||||
ctxSwitchesProfiler, err := NewCtxSwitchesProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_SW_CONTEXT_SWITCHES] = ctxSwitchesProfiler
|
||||
}
|
||||
|
||||
cpuMigrationsProfiler, err := NewCPUMigrationsProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_SW_CPU_MIGRATIONS] = cpuMigrationsProfiler
|
||||
}
|
||||
|
||||
minorFaultProfiler, err := NewMinorFaultsProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_SW_PAGE_FAULTS_MIN] = minorFaultProfiler
|
||||
}
|
||||
|
||||
majorFaultProfiler, err := NewMajorFaultsProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_SW_PAGE_FAULTS_MAJ] = majorFaultProfiler
|
||||
}
|
||||
|
||||
alignFaultsFrontProfiler, err := NewAlignFaultsProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_SW_ALIGNMENT_FAULTS] = alignFaultsFrontProfiler
|
||||
}
|
||||
|
||||
emuFaultProfiler, err := NewEmulationFaultsProfiler(pid, cpu, opts...)
|
||||
if err == nil {
|
||||
profilers[unix.PERF_COUNT_SW_EMULATION_FAULTS] = emuFaultProfiler
|
||||
}
|
||||
|
||||
return &softwareProfiler{
|
||||
profilers: profilers,
|
||||
}
|
||||
}
|
||||
|
||||
// Start is used to start the SoftwareProfiler.
|
||||
func (p *softwareProfiler) Start() error {
|
||||
if len(p.profilers) == 0 {
|
||||
return ErrNoProfiler
|
||||
}
|
||||
var err error
|
||||
for _, profiler := range p.profilers {
|
||||
err = multierr.Append(err, profiler.Start())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Reset is used to reset the SoftwareProfiler.
|
||||
func (p *softwareProfiler) Reset() error {
|
||||
var err error
|
||||
for _, profiler := range p.profilers {
|
||||
err = multierr.Append(err, profiler.Reset())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Stop is used to reset the SoftwareProfiler.
|
||||
func (p *softwareProfiler) Stop() error {
|
||||
var err error
|
||||
for _, profiler := range p.profilers {
|
||||
err = multierr.Append(err, profiler.Stop())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Close is used to reset the SoftwareProfiler.
|
||||
func (p *softwareProfiler) Close() error {
|
||||
var err error
|
||||
for _, profiler := range p.profilers {
|
||||
err = multierr.Append(err, profiler.Close())
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Profile is used to read the SoftwareProfiler SoftwareProfile it returns an
|
||||
// error only if all profiles fail.
|
||||
func (p *softwareProfiler) Profile() (*SoftwareProfile, error) {
|
||||
var err error
|
||||
swProfile := &SoftwareProfile{}
|
||||
for profilerType, profiler := range p.profilers {
|
||||
profileVal, err2 := profiler.Profile()
|
||||
err = multierr.Append(err, err2)
|
||||
if err2 == nil {
|
||||
if swProfile.TimeEnabled == nil {
|
||||
swProfile.TimeEnabled = &profileVal.TimeEnabled
|
||||
}
|
||||
if swProfile.TimeRunning == nil {
|
||||
swProfile.TimeRunning = &profileVal.TimeRunning
|
||||
}
|
||||
switch profilerType {
|
||||
case unix.PERF_COUNT_SW_CPU_CLOCK:
|
||||
swProfile.CPUClock = &profileVal.Value
|
||||
case unix.PERF_COUNT_SW_TASK_CLOCK:
|
||||
swProfile.TaskClock = &profileVal.Value
|
||||
case unix.PERF_COUNT_SW_PAGE_FAULTS:
|
||||
swProfile.PageFaults = &profileVal.Value
|
||||
case unix.PERF_COUNT_SW_CONTEXT_SWITCHES:
|
||||
swProfile.ContextSwitches = &profileVal.Value
|
||||
case unix.PERF_COUNT_SW_CPU_MIGRATIONS:
|
||||
swProfile.CPUMigrations = &profileVal.Value
|
||||
case unix.PERF_COUNT_SW_PAGE_FAULTS_MIN:
|
||||
swProfile.MinorPageFaults = &profileVal.Value
|
||||
case unix.PERF_COUNT_SW_PAGE_FAULTS_MAJ:
|
||||
swProfile.MajorPageFaults = &profileVal.Value
|
||||
case unix.PERF_COUNT_SW_ALIGNMENT_FAULTS:
|
||||
swProfile.AlignmentFaults = &profileVal.Value
|
||||
case unix.PERF_COUNT_SW_EMULATION_FAULTS:
|
||||
swProfile.EmulationFaults = &profileVal.Value
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(multierr.Errors(err)) == len(p.profilers) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return swProfile, nil
|
||||
}
|
681
vendor/github.com/hodgesds/perf-utils/utils.go
generated
vendored
Normal file
681
vendor/github.com/hodgesds/perf-utils/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,681 @@
|
|||
// +build linux
|
||||
|
||||
package perf
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"runtime"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
var (
|
||||
// EventAttrSize is the size of a PerfEventAttr
|
||||
EventAttrSize = uint32(unsafe.Sizeof(unix.PerfEventAttr{}))
|
||||
)
|
||||
|
||||
// profileFn is a helper function to profile a function.
|
||||
func profileFn(eventAttr *unix.PerfEventAttr, f func() error) (*ProfileValue, error) {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
fd, err := unix.PerfEventOpen(
|
||||
eventAttr,
|
||||
unix.Gettid(),
|
||||
-1,
|
||||
-1,
|
||||
0,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := unix.IoctlSetInt(fd, unix.PERF_EVENT_IOC_RESET, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := unix.IoctlSetInt(fd, unix.PERF_EVENT_IOC_ENABLE, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := f(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := unix.IoctlSetInt(fd, unix.PERF_EVENT_IOC_DISABLE, 0); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf := make([]byte, 24)
|
||||
if _, err := syscall.Read(fd, buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ProfileValue{
|
||||
Value: binary.LittleEndian.Uint64(buf[0:8]),
|
||||
TimeEnabled: binary.LittleEndian.Uint64(buf[8:16]),
|
||||
TimeRunning: binary.LittleEndian.Uint64(buf[16:24]),
|
||||
}, unix.Close(fd)
|
||||
}
|
||||
|
||||
// CPUInstructions is used to profile a function and return the number of CPU instructions.
|
||||
// Note that it will call runtime.LockOSThread to ensure accurate profilng.
|
||||
func CPUInstructions(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_INSTRUCTIONS,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// CPUInstructionsEventAttr returns a unix.PerfEventAttr configured for CPUInstructions.
|
||||
func CPUInstructionsEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_INSTRUCTIONS,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// CPUCycles is used to profile a function and return the number of CPU cycles.
|
||||
// Note that it will call runtime.LockOSThread to ensure accurate profilng.
|
||||
func CPUCycles(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_CPU_CYCLES,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// CPUCyclesEventAttr returns a unix.PerfEventAttr configured for CPUCycles.
|
||||
func CPUCyclesEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_CPU_CYCLES,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// CacheRef is used to profile a function and return the number of cache
|
||||
// references. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func CacheRef(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_CACHE_REFERENCES,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// CacheRefEventAttr returns a unix.PerfEventAttr configured for CacheRef.
|
||||
func CacheRefEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_CACHE_REFERENCES,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// CacheMiss is used to profile a function and return the number of cache
|
||||
// misses. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func CacheMiss(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_CACHE_MISSES,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// CacheMissEventAttr returns a unix.PerfEventAttr configured for CacheMisses.
|
||||
func CacheMissEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_CACHE_MISSES,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// BusCycles is used to profile a function and return the number of bus
|
||||
// cycles. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func BusCycles(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_BUS_CYCLES,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// BusCyclesEventAttr returns a unix.PerfEventAttr configured for BusCycles.
|
||||
func BusCyclesEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_BUS_CYCLES,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// StalledFrontendCycles is used to profile a function and return the number of
|
||||
// stalled frontend cycles. Note that it will call runtime.LockOSThread to
|
||||
// ensure accurate profilng.
|
||||
func StalledFrontendCycles(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_STALLED_CYCLES_FRONTEND,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// StalledFrontendCyclesEventAttr returns a unix.PerfEventAttr configured for StalledFrontendCycles.
|
||||
func StalledFrontendCyclesEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_STALLED_CYCLES_FRONTEND,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// StalledBackendCycles is used to profile a function and return the number of
|
||||
// stalled backend cycles. Note that it will call runtime.LockOSThread to
|
||||
// ensure accurate profilng.
|
||||
func StalledBackendCycles(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_STALLED_CYCLES_BACKEND,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// StalledBackendCyclesEventAttr returns a unix.PerfEventAttr configured for StalledBackendCycles.
|
||||
func StalledBackendCyclesEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_STALLED_CYCLES_BACKEND,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// CPURefCycles is used to profile a function and return the number of CPU
|
||||
// references cycles which are not affected by frequency scaling. Note that it
|
||||
// will call runtime.LockOSThread to ensure accurate profilng.
|
||||
func CPURefCycles(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_REF_CPU_CYCLES,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// CPURefCyclesEventAttr returns a unix.PerfEventAttr configured for CPURefCycles.
|
||||
func CPURefCyclesEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HARDWARE,
|
||||
Config: unix.PERF_COUNT_HW_REF_CPU_CYCLES,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// CPUClock is used to profile a function and return the CPU clock timer. Note
|
||||
// that it will call runtime.LockOSThread to ensure accurate profilng.
|
||||
func CPUClock(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_CPU_CLOCK,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// CPUClockEventAttr returns a unix.PerfEventAttr configured for CPUClock.
|
||||
func CPUClockEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_CPU_CLOCK,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
// CPUTaskClock is used to profile a function and return the CPU clock timer
|
||||
// for the running task. Note that it will call runtime.LockOSThread to ensure
|
||||
// accurate profilng.
|
||||
func CPUTaskClock(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_TASK_CLOCK,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// CPUTaskClockEventAttr returns a unix.PerfEventAttr configured for CPUTaskClock.
|
||||
func CPUTaskClockEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_TASK_CLOCK,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// PageFaults is used to profile a function and return the number of page
|
||||
// faults. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func PageFaults(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_PAGE_FAULTS,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// PageFaultsEventAttr returns a unix.PerfEventAttr configured for PageFaults.
|
||||
func PageFaultsEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_PAGE_FAULTS,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
// ContextSwitches is used to profile a function and return the number of
|
||||
// context switches. Note that it will call runtime.LockOSThread to ensure
|
||||
// accurate profilng.
|
||||
func ContextSwitches(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_CONTEXT_SWITCHES,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// ContextSwitchesEventAttr returns a unix.PerfEventAttr configured for ContextSwitches.
|
||||
func ContextSwitchesEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_CONTEXT_SWITCHES,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
// CPUMigrations is used to profile a function and return the number of times
|
||||
// the thread has been migrated to a new CPU. Note that it will call
|
||||
// runtime.LockOSThread to ensure accurate profilng.
|
||||
func CPUMigrations(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_CPU_MIGRATIONS,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// CPUMigrationsEventAttr returns a unix.PerfEventAttr configured for CPUMigrations.
|
||||
func CPUMigrationsEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_CPU_MIGRATIONS,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
// MinorPageFaults is used to profile a function and return the number of minor
|
||||
// page faults. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func MinorPageFaults(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_PAGE_FAULTS_MIN,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// MinorPageFaultsEventAttr returns a unix.PerfEventAttr configured for MinorPageFaults.
|
||||
func MinorPageFaultsEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_PAGE_FAULTS_MIN,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
// MajorPageFaults is used to profile a function and return the number of major
|
||||
// page faults. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func MajorPageFaults(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_PAGE_FAULTS_MAJ,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// MajorPageFaultsEventAttr returns a unix.PerfEventAttr configured for MajorPageFaults.
|
||||
func MajorPageFaultsEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_PAGE_FAULTS_MAJ,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
// AlignmentFaults is used to profile a function and return the number of alignment
|
||||
// faults. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func AlignmentFaults(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_ALIGNMENT_FAULTS,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// AlignmentFaultsEventAttr returns a unix.PerfEventAttr configured for AlignmentFaults.
|
||||
func AlignmentFaultsEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_ALIGNMENT_FAULTS,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
// EmulationFaults is used to profile a function and return the number of emulation
|
||||
// faults. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func EmulationFaults(f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_EMULATION_FAULTS,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// EmulationFaultsEventAttr returns a unix.PerfEventAttr configured for EmulationFaults.
|
||||
func EmulationFaultsEventAttr() unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_SOFTWARE,
|
||||
Config: unix.PERF_COUNT_SW_EMULATION_FAULTS,
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
// L1Data is used to profile a function and the L1 data cache faults. Use
|
||||
// PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_OP_WRITE, or
|
||||
// PERF_COUNT_HW_CACHE_OP_PREFETCH for the opt and
|
||||
// PERF_COUNT_HW_CACHE_RESULT_ACCESS or PERF_COUNT_HW_CACHE_RESULT_MISS for the
|
||||
// result. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func L1Data(op, result int, f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_L1D) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// L1DataEventAttr returns a unix.PerfEventAttr configured for L1Data.
|
||||
func L1DataEventAttr(op, result int) unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_L1D) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
// L1Instructions is used to profile a function for the instruction level L1
|
||||
// cache. Use PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_OP_WRITE, or
|
||||
// PERF_COUNT_HW_CACHE_OP_PREFETCH for the opt and
|
||||
// PERF_COUNT_HW_CACHE_RESULT_ACCESS or PERF_COUNT_HW_CACHE_RESULT_MISS for the
|
||||
// result. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func L1Instructions(op, result int, f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_L1I) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// L1InstructionsEventAttr returns a unix.PerfEventAttr configured for L1Instructions.
|
||||
func L1InstructionsEventAttr(op, result int) unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_L1I) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
// LLCache is used to profile a function and return the number of emulation
|
||||
// PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_OP_WRITE, or
|
||||
// PERF_COUNT_HW_CACHE_OP_PREFETCH for the opt and
|
||||
// PERF_COUNT_HW_CACHE_RESULT_ACCESS or PERF_COUNT_HW_CACHE_RESULT_MISS for the
|
||||
// result. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func LLCache(op, result int, f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_LL) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// LLCacheEventAttr returns a unix.PerfEventAttr configured for LLCache.
|
||||
func LLCacheEventAttr(op, result int) unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_LL) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
// DataTLB is used to profile the data TLB. Use PERF_COUNT_HW_CACHE_OP_READ,
|
||||
// PERF_COUNT_HW_CACHE_OP_WRITE, or PERF_COUNT_HW_CACHE_OP_PREFETCH for the opt
|
||||
// and PERF_COUNT_HW_CACHE_RESULT_ACCESS or PERF_COUNT_HW_CACHE_RESULT_MISS for
|
||||
// the result. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func DataTLB(op, result int, f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_DTLB) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// DataTLBEventAttr returns a unix.PerfEventAttr configured for DataTLB.
|
||||
func DataTLBEventAttr(op, result int) unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_DTLB) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
// InstructionTLB is used to profile the instruction TLB. Use
|
||||
// PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_OP_WRITE, or
|
||||
// PERF_COUNT_HW_CACHE_OP_PREFETCH for the opt and
|
||||
// PERF_COUNT_HW_CACHE_RESULT_ACCESS or PERF_COUNT_HW_CACHE_RESULT_MISS for the
|
||||
// result. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func InstructionTLB(op, result int, f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_ITLB) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// InstructionTLBEventAttr returns a unix.PerfEventAttr configured for InstructionTLB.
|
||||
func InstructionTLBEventAttr(op, result int) unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_ITLB) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// BPU is used to profile a function for the Branch Predictor Unit.
|
||||
// Use PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_OP_WRITE, or
|
||||
// PERF_COUNT_HW_CACHE_OP_PREFETCH for the opt and
|
||||
// PERF_COUNT_HW_CACHE_RESULT_ACCESS or PERF_COUNT_HW_CACHE_RESULT_MISS for the
|
||||
// result. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func BPU(op, result int, f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_BPU) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// BPUEventAttr returns a unix.PerfEventAttr configured for BPU events.
|
||||
func BPUEventAttr(op, result int) unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_BPU) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
||||
|
||||
// NodeCache is used to profile a function for NUMA operations. Use Use
|
||||
// PERF_COUNT_HW_CACHE_OP_READ, PERF_COUNT_HW_CACHE_OP_WRITE, or
|
||||
// PERF_COUNT_HW_CACHE_OP_PREFETCH for the opt and
|
||||
// PERF_COUNT_HW_CACHE_RESULT_ACCESS or PERF_COUNT_HW_CACHE_RESULT_MISS for the
|
||||
// result. Note that it will call runtime.LockOSThread to ensure accurate
|
||||
// profilng.
|
||||
func NodeCache(op, result int, f func() error) (*ProfileValue, error) {
|
||||
eventAttr := &unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_NODE) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitDisabled | unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
return profileFn(eventAttr, f)
|
||||
}
|
||||
|
||||
// NodeCacheEventAttr returns a unix.PerfEventAttr configured for NUMA cache operations.
|
||||
func NodeCacheEventAttr(op, result int) unix.PerfEventAttr {
|
||||
return unix.PerfEventAttr{
|
||||
Type: unix.PERF_TYPE_HW_CACHE,
|
||||
Config: uint64((unix.PERF_COUNT_HW_CACHE_NODE) | (op << 8) | (result << 16)),
|
||||
Size: EventAttrSize,
|
||||
Bits: unix.PerfBitExcludeKernel | unix.PerfBitExcludeHv,
|
||||
Read_format: unix.PERF_FORMAT_TOTAL_TIME_RUNNING | unix.PERF_FORMAT_TOTAL_TIME_ENABLED,
|
||||
}
|
||||
}
|
15
vendor/go.uber.org/atomic/.codecov.yml
generated
vendored
Normal file
15
vendor/go.uber.org/atomic/.codecov.yml
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
coverage:
|
||||
range: 80..100
|
||||
round: down
|
||||
precision: 2
|
||||
|
||||
status:
|
||||
project: # measuring the overall project coverage
|
||||
default: # context, you can create multiple ones with custom titles
|
||||
enabled: yes # must be yes|true to enable this status
|
||||
target: 100 # specify the target coverage for each commit status
|
||||
# option: "auto" (must increase from parent commit or pull request base)
|
||||
# option: "X%" a static target percentage to hit
|
||||
if_not_found: success # if parent is not found report status as success, error, or failure
|
||||
if_ci_failed: error # if ci fails report status as success, error, or failure
|
||||
|
11
vendor/go.uber.org/atomic/.gitignore
generated
vendored
Normal file
11
vendor/go.uber.org/atomic/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
.DS_Store
|
||||
/vendor
|
||||
/cover
|
||||
cover.out
|
||||
lint.log
|
||||
|
||||
# Binaries
|
||||
*.test
|
||||
|
||||
# Profiling output
|
||||
*.prof
|
23
vendor/go.uber.org/atomic/.travis.yml
generated
vendored
Normal file
23
vendor/go.uber.org/atomic/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
sudo: false
|
||||
language: go
|
||||
go_import_path: go.uber.org/atomic
|
||||
|
||||
go:
|
||||
- 1.7
|
||||
- 1.8
|
||||
- 1.9
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
|
||||
install:
|
||||
- make install_ci
|
||||
|
||||
script:
|
||||
- make test_ci
|
||||
- scripts/test-ubergo.sh
|
||||
- make lint
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
19
vendor/go.uber.org/atomic/LICENSE.txt
generated
vendored
Normal file
19
vendor/go.uber.org/atomic/LICENSE.txt
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2016 Uber Technologies, Inc.
|
||||
|
||||
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.
|
64
vendor/go.uber.org/atomic/Makefile
generated
vendored
Normal file
64
vendor/go.uber.org/atomic/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,64 @@
|
|||
PACKAGES := $(shell glide nv)
|
||||
# Many Go tools take file globs or directories as arguments instead of packages.
|
||||
PACKAGE_FILES ?= *.go
|
||||
|
||||
|
||||
# The linting tools evolve with each Go version, so run them only on the latest
|
||||
# stable release.
|
||||
GO_VERSION := $(shell go version | cut -d " " -f 3)
|
||||
GO_MINOR_VERSION := $(word 2,$(subst ., ,$(GO_VERSION)))
|
||||
LINTABLE_MINOR_VERSIONS := 7 8
|
||||
ifneq ($(filter $(LINTABLE_MINOR_VERSIONS),$(GO_MINOR_VERSION)),)
|
||||
SHOULD_LINT := true
|
||||
endif
|
||||
|
||||
|
||||
export GO15VENDOREXPERIMENT=1
|
||||
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build -i $(PACKAGES)
|
||||
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
glide --version || go get github.com/Masterminds/glide
|
||||
glide install
|
||||
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -cover -race $(PACKAGES)
|
||||
|
||||
|
||||
.PHONY: install_ci
|
||||
install_ci: install
|
||||
go get github.com/wadey/gocovmerge
|
||||
go get github.com/mattn/goveralls
|
||||
go get golang.org/x/tools/cmd/cover
|
||||
ifdef SHOULD_LINT
|
||||
go get github.com/golang/lint/golint
|
||||
endif
|
||||
|
||||
.PHONY: lint
|
||||
lint:
|
||||
ifdef SHOULD_LINT
|
||||
@rm -rf lint.log
|
||||
@echo "Checking formatting..."
|
||||
@gofmt -d -s $(PACKAGE_FILES) 2>&1 | tee lint.log
|
||||
@echo "Checking vet..."
|
||||
@$(foreach dir,$(PACKAGE_FILES),go tool vet $(dir) 2>&1 | tee -a lint.log;)
|
||||
@echo "Checking lint..."
|
||||
@$(foreach dir,$(PKGS),golint $(dir) 2>&1 | tee -a lint.log;)
|
||||
@echo "Checking for unresolved FIXMEs..."
|
||||
@git grep -i fixme | grep -v -e vendor -e Makefile | tee -a lint.log
|
||||
@[ ! -s lint.log ]
|
||||
else
|
||||
@echo "Skipping linters on" $(GO_VERSION)
|
||||
endif
|
||||
|
||||
|
||||
.PHONY: test_ci
|
||||
test_ci: install_ci build
|
||||
./scripts/cover.sh $(shell go list $(PACKAGES))
|
36
vendor/go.uber.org/atomic/README.md
generated
vendored
Normal file
36
vendor/go.uber.org/atomic/README.md
generated
vendored
Normal file
|
@ -0,0 +1,36 @@
|
|||
# atomic [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov] [![Go Report Card][reportcard-img]][reportcard]
|
||||
|
||||
Simple wrappers for primitive types to enforce atomic access.
|
||||
|
||||
## Installation
|
||||
`go get -u go.uber.org/atomic`
|
||||
|
||||
## Usage
|
||||
The standard library's `sync/atomic` is powerful, but it's easy to forget which
|
||||
variables must be accessed atomically. `go.uber.org/atomic` preserves all the
|
||||
functionality of the standard library, but wraps the primitive types to
|
||||
provide a safer, more convenient API.
|
||||
|
||||
```go
|
||||
var atom atomic.Uint32
|
||||
atom.Store(42)
|
||||
atom.Sub(2)
|
||||
atom.CAS(40, 11)
|
||||
```
|
||||
|
||||
See the [documentation][doc] for a complete API specification.
|
||||
|
||||
## Development Status
|
||||
Stable.
|
||||
|
||||
<hr>
|
||||
Released under the [MIT License](LICENSE.txt).
|
||||
|
||||
[doc-img]: https://godoc.org/github.com/uber-go/atomic?status.svg
|
||||
[doc]: https://godoc.org/go.uber.org/atomic
|
||||
[ci-img]: https://travis-ci.org/uber-go/atomic.svg?branch=master
|
||||
[ci]: https://travis-ci.org/uber-go/atomic
|
||||
[cov-img]: https://codecov.io/gh/uber-go/atomic/branch/master/graph/badge.svg
|
||||
[cov]: https://codecov.io/gh/uber-go/atomic
|
||||
[reportcard-img]: https://goreportcard.com/badge/go.uber.org/atomic
|
||||
[reportcard]: https://goreportcard.com/report/go.uber.org/atomic
|
351
vendor/go.uber.org/atomic/atomic.go
generated
vendored
Normal file
351
vendor/go.uber.org/atomic/atomic.go
generated
vendored
Normal file
|
@ -0,0 +1,351 @@
|
|||
// Copyright (c) 2016 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Package atomic provides simple wrappers around numerics to enforce atomic
|
||||
// access.
|
||||
package atomic
|
||||
|
||||
import (
|
||||
"math"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Int32 is an atomic wrapper around an int32.
|
||||
type Int32 struct{ v int32 }
|
||||
|
||||
// NewInt32 creates an Int32.
|
||||
func NewInt32(i int32) *Int32 {
|
||||
return &Int32{i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Int32) Load() int32 {
|
||||
return atomic.LoadInt32(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Add(n int32) int32 {
|
||||
return atomic.AddInt32(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Sub(n int32) int32 {
|
||||
return atomic.AddInt32(&i.v, -n)
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Inc() int32 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped int32 and returns the new value.
|
||||
func (i *Int32) Dec() int32 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Int32) CAS(old, new int32) bool {
|
||||
return atomic.CompareAndSwapInt32(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Int32) Store(n int32) {
|
||||
atomic.StoreInt32(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped int32 and returns the old value.
|
||||
func (i *Int32) Swap(n int32) int32 {
|
||||
return atomic.SwapInt32(&i.v, n)
|
||||
}
|
||||
|
||||
// Int64 is an atomic wrapper around an int64.
|
||||
type Int64 struct{ v int64 }
|
||||
|
||||
// NewInt64 creates an Int64.
|
||||
func NewInt64(i int64) *Int64 {
|
||||
return &Int64{i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Int64) Load() int64 {
|
||||
return atomic.LoadInt64(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Add(n int64) int64 {
|
||||
return atomic.AddInt64(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Sub(n int64) int64 {
|
||||
return atomic.AddInt64(&i.v, -n)
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Inc() int64 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped int64 and returns the new value.
|
||||
func (i *Int64) Dec() int64 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Int64) CAS(old, new int64) bool {
|
||||
return atomic.CompareAndSwapInt64(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Int64) Store(n int64) {
|
||||
atomic.StoreInt64(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped int64 and returns the old value.
|
||||
func (i *Int64) Swap(n int64) int64 {
|
||||
return atomic.SwapInt64(&i.v, n)
|
||||
}
|
||||
|
||||
// Uint32 is an atomic wrapper around an uint32.
|
||||
type Uint32 struct{ v uint32 }
|
||||
|
||||
// NewUint32 creates a Uint32.
|
||||
func NewUint32(i uint32) *Uint32 {
|
||||
return &Uint32{i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Uint32) Load() uint32 {
|
||||
return atomic.LoadUint32(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Add(n uint32) uint32 {
|
||||
return atomic.AddUint32(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Sub(n uint32) uint32 {
|
||||
return atomic.AddUint32(&i.v, ^(n - 1))
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped uint32 and returns the new value.
|
||||
func (i *Uint32) Inc() uint32 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped int32 and returns the new value.
|
||||
func (i *Uint32) Dec() uint32 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Uint32) CAS(old, new uint32) bool {
|
||||
return atomic.CompareAndSwapUint32(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Uint32) Store(n uint32) {
|
||||
atomic.StoreUint32(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped uint32 and returns the old value.
|
||||
func (i *Uint32) Swap(n uint32) uint32 {
|
||||
return atomic.SwapUint32(&i.v, n)
|
||||
}
|
||||
|
||||
// Uint64 is an atomic wrapper around a uint64.
|
||||
type Uint64 struct{ v uint64 }
|
||||
|
||||
// NewUint64 creates a Uint64.
|
||||
func NewUint64(i uint64) *Uint64 {
|
||||
return &Uint64{i}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (i *Uint64) Load() uint64 {
|
||||
return atomic.LoadUint64(&i.v)
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Add(n uint64) uint64 {
|
||||
return atomic.AddUint64(&i.v, n)
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Sub(n uint64) uint64 {
|
||||
return atomic.AddUint64(&i.v, ^(n - 1))
|
||||
}
|
||||
|
||||
// Inc atomically increments the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Inc() uint64 {
|
||||
return i.Add(1)
|
||||
}
|
||||
|
||||
// Dec atomically decrements the wrapped uint64 and returns the new value.
|
||||
func (i *Uint64) Dec() uint64 {
|
||||
return i.Sub(1)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (i *Uint64) CAS(old, new uint64) bool {
|
||||
return atomic.CompareAndSwapUint64(&i.v, old, new)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (i *Uint64) Store(n uint64) {
|
||||
atomic.StoreUint64(&i.v, n)
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped uint64 and returns the old value.
|
||||
func (i *Uint64) Swap(n uint64) uint64 {
|
||||
return atomic.SwapUint64(&i.v, n)
|
||||
}
|
||||
|
||||
// Bool is an atomic Boolean.
|
||||
type Bool struct{ v uint32 }
|
||||
|
||||
// NewBool creates a Bool.
|
||||
func NewBool(initial bool) *Bool {
|
||||
return &Bool{boolToInt(initial)}
|
||||
}
|
||||
|
||||
// Load atomically loads the Boolean.
|
||||
func (b *Bool) Load() bool {
|
||||
return truthy(atomic.LoadUint32(&b.v))
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (b *Bool) CAS(old, new bool) bool {
|
||||
return atomic.CompareAndSwapUint32(&b.v, boolToInt(old), boolToInt(new))
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (b *Bool) Store(new bool) {
|
||||
atomic.StoreUint32(&b.v, boolToInt(new))
|
||||
}
|
||||
|
||||
// Swap sets the given value and returns the previous value.
|
||||
func (b *Bool) Swap(new bool) bool {
|
||||
return truthy(atomic.SwapUint32(&b.v, boolToInt(new)))
|
||||
}
|
||||
|
||||
// Toggle atomically negates the Boolean and returns the previous value.
|
||||
func (b *Bool) Toggle() bool {
|
||||
return truthy(atomic.AddUint32(&b.v, 1) - 1)
|
||||
}
|
||||
|
||||
func truthy(n uint32) bool {
|
||||
return n&1 == 1
|
||||
}
|
||||
|
||||
func boolToInt(b bool) uint32 {
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Float64 is an atomic wrapper around float64.
|
||||
type Float64 struct {
|
||||
v uint64
|
||||
}
|
||||
|
||||
// NewFloat64 creates a Float64.
|
||||
func NewFloat64(f float64) *Float64 {
|
||||
return &Float64{math.Float64bits(f)}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (f *Float64) Load() float64 {
|
||||
return math.Float64frombits(atomic.LoadUint64(&f.v))
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (f *Float64) Store(s float64) {
|
||||
atomic.StoreUint64(&f.v, math.Float64bits(s))
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped float64 and returns the new value.
|
||||
func (f *Float64) Add(s float64) float64 {
|
||||
for {
|
||||
old := f.Load()
|
||||
new := old + s
|
||||
if f.CAS(old, new) {
|
||||
return new
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped float64 and returns the new value.
|
||||
func (f *Float64) Sub(s float64) float64 {
|
||||
return f.Add(-s)
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (f *Float64) CAS(old, new float64) bool {
|
||||
return atomic.CompareAndSwapUint64(&f.v, math.Float64bits(old), math.Float64bits(new))
|
||||
}
|
||||
|
||||
// Duration is an atomic wrapper around time.Duration
|
||||
// https://godoc.org/time#Duration
|
||||
type Duration struct {
|
||||
v Int64
|
||||
}
|
||||
|
||||
// NewDuration creates a Duration.
|
||||
func NewDuration(d time.Duration) *Duration {
|
||||
return &Duration{v: *NewInt64(int64(d))}
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped value.
|
||||
func (d *Duration) Load() time.Duration {
|
||||
return time.Duration(d.v.Load())
|
||||
}
|
||||
|
||||
// Store atomically stores the passed value.
|
||||
func (d *Duration) Store(n time.Duration) {
|
||||
d.v.Store(int64(n))
|
||||
}
|
||||
|
||||
// Add atomically adds to the wrapped time.Duration and returns the new value.
|
||||
func (d *Duration) Add(n time.Duration) time.Duration {
|
||||
return time.Duration(d.v.Add(int64(n)))
|
||||
}
|
||||
|
||||
// Sub atomically subtracts from the wrapped time.Duration and returns the new value.
|
||||
func (d *Duration) Sub(n time.Duration) time.Duration {
|
||||
return time.Duration(d.v.Sub(int64(n)))
|
||||
}
|
||||
|
||||
// Swap atomically swaps the wrapped time.Duration and returns the old value.
|
||||
func (d *Duration) Swap(n time.Duration) time.Duration {
|
||||
return time.Duration(d.v.Swap(int64(n)))
|
||||
}
|
||||
|
||||
// CAS is an atomic compare-and-swap.
|
||||
func (d *Duration) CAS(old, new time.Duration) bool {
|
||||
return d.v.CAS(int64(old), int64(new))
|
||||
}
|
||||
|
||||
// Value shadows the type of the same name from sync/atomic
|
||||
// https://godoc.org/sync/atomic#Value
|
||||
type Value struct{ atomic.Value }
|
17
vendor/go.uber.org/atomic/glide.lock
generated
vendored
Normal file
17
vendor/go.uber.org/atomic/glide.lock
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
hash: f14d51408e3e0e4f73b34e4039484c78059cd7fc5f4996fdd73db20dc8d24f53
|
||||
updated: 2016-10-27T00:10:51.16960137-07:00
|
||||
imports: []
|
||||
testImports:
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: 5215b55f46b2b919f50a1df0eaa5886afe4e3b3d
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/pmezard/go-difflib
|
||||
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
|
||||
subpackages:
|
||||
- difflib
|
||||
- name: github.com/stretchr/testify
|
||||
version: d77da356e56a7428ad25149ca77381849a6a5232
|
||||
subpackages:
|
||||
- assert
|
||||
- require
|
6
vendor/go.uber.org/atomic/glide.yaml
generated
vendored
Normal file
6
vendor/go.uber.org/atomic/glide.yaml
generated
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
package: go.uber.org/atomic
|
||||
testImport:
|
||||
- package: github.com/stretchr/testify
|
||||
subpackages:
|
||||
- assert
|
||||
- require
|
49
vendor/go.uber.org/atomic/string.go
generated
vendored
Normal file
49
vendor/go.uber.org/atomic/string.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
// Copyright (c) 2016 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
package atomic
|
||||
|
||||
// String is an atomic type-safe wrapper around Value for strings.
|
||||
type String struct{ v Value }
|
||||
|
||||
// NewString creates a String.
|
||||
func NewString(str string) *String {
|
||||
s := &String{}
|
||||
if str != "" {
|
||||
s.Store(str)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Load atomically loads the wrapped string.
|
||||
func (s *String) Load() string {
|
||||
v := s.v.Load()
|
||||
if v == nil {
|
||||
return ""
|
||||
}
|
||||
return v.(string)
|
||||
}
|
||||
|
||||
// Store atomically stores the passed string.
|
||||
// Note: Converting the string to an interface{} to store in the Value
|
||||
// requires an allocation.
|
||||
func (s *String) Store(str string) {
|
||||
s.v.Store(str)
|
||||
}
|
15
vendor/go.uber.org/multierr/.codecov.yml
generated
vendored
Normal file
15
vendor/go.uber.org/multierr/.codecov.yml
generated
vendored
Normal file
|
@ -0,0 +1,15 @@
|
|||
coverage:
|
||||
range: 80..100
|
||||
round: down
|
||||
precision: 2
|
||||
|
||||
status:
|
||||
project: # measuring the overall project coverage
|
||||
default: # context, you can create multiple ones with custom titles
|
||||
enabled: yes # must be yes|true to enable this status
|
||||
target: 100 # specify the target coverage for each commit status
|
||||
# option: "auto" (must increase from parent commit or pull request base)
|
||||
# option: "X%" a static target percentage to hit
|
||||
if_not_found: success # if parent is not found report status as success, error, or failure
|
||||
if_ci_failed: error # if ci fails report status as success, error, or failure
|
||||
|
1
vendor/go.uber.org/multierr/.gitignore
generated
vendored
Normal file
1
vendor/go.uber.org/multierr/.gitignore
generated
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/vendor
|
33
vendor/go.uber.org/multierr/.travis.yml
generated
vendored
Normal file
33
vendor/go.uber.org/multierr/.travis.yml
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
sudo: false
|
||||
language: go
|
||||
go_import_path: go.uber.org/multierr
|
||||
|
||||
env:
|
||||
global:
|
||||
- GO15VENDOREXPERIMENT=1
|
||||
|
||||
go:
|
||||
- 1.7
|
||||
- 1.8
|
||||
- tip
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- vendor
|
||||
|
||||
before_install:
|
||||
- go version
|
||||
|
||||
install:
|
||||
- |
|
||||
set -e
|
||||
make install_ci
|
||||
|
||||
script:
|
||||
- |
|
||||
set -e
|
||||
make lint
|
||||
make test_ci
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
28
vendor/go.uber.org/multierr/CHANGELOG.md
generated
vendored
Normal file
28
vendor/go.uber.org/multierr/CHANGELOG.md
generated
vendored
Normal file
|
@ -0,0 +1,28 @@
|
|||
Releases
|
||||
========
|
||||
|
||||
v1.1.0 (2017-06-30)
|
||||
===================
|
||||
|
||||
- Added an `Errors(error) []error` function to extract the underlying list of
|
||||
errors for a multierr error.
|
||||
|
||||
|
||||
v1.0.0 (2017-05-31)
|
||||
===================
|
||||
|
||||
No changes since v0.2.0. This release is committing to making no breaking
|
||||
changes to the current API in the 1.X series.
|
||||
|
||||
|
||||
v0.2.0 (2017-04-11)
|
||||
===================
|
||||
|
||||
- Repeatedly appending to the same error is now faster due to fewer
|
||||
allocations.
|
||||
|
||||
|
||||
v0.1.0 (2017-31-03)
|
||||
===================
|
||||
|
||||
- Initial release
|
19
vendor/go.uber.org/multierr/LICENSE.txt
generated
vendored
Normal file
19
vendor/go.uber.org/multierr/LICENSE.txt
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2017 Uber Technologies, Inc.
|
||||
|
||||
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.
|
74
vendor/go.uber.org/multierr/Makefile
generated
vendored
Normal file
74
vendor/go.uber.org/multierr/Makefile
generated
vendored
Normal file
|
@ -0,0 +1,74 @@
|
|||
export GO15VENDOREXPERIMENT=1
|
||||
|
||||
PACKAGES := $(shell glide nv)
|
||||
|
||||
GO_FILES := $(shell \
|
||||
find . '(' -path '*/.*' -o -path './vendor' ')' -prune \
|
||||
-o -name '*.go' -print | cut -b3-)
|
||||
|
||||
.PHONY: install
|
||||
install:
|
||||
glide --version || go get github.com/Masterminds/glide
|
||||
glide install
|
||||
|
||||
.PHONY: build
|
||||
build:
|
||||
go build -i $(PACKAGES)
|
||||
|
||||
.PHONY: test
|
||||
test:
|
||||
go test -cover -race $(PACKAGES)
|
||||
|
||||
.PHONY: gofmt
|
||||
gofmt:
|
||||
$(eval FMT_LOG := $(shell mktemp -t gofmt.XXXXX))
|
||||
@gofmt -e -s -l $(GO_FILES) > $(FMT_LOG) || true
|
||||
@[ ! -s "$(FMT_LOG)" ] || (echo "gofmt failed:" | cat - $(FMT_LOG) && false)
|
||||
|
||||
.PHONY: govet
|
||||
govet:
|
||||
$(eval VET_LOG := $(shell mktemp -t govet.XXXXX))
|
||||
@go vet $(PACKAGES) 2>&1 \
|
||||
| grep -v '^exit status' > $(VET_LOG) || true
|
||||
@[ ! -s "$(VET_LOG)" ] || (echo "govet failed:" | cat - $(VET_LOG) && false)
|
||||
|
||||
.PHONY: golint
|
||||
golint:
|
||||
@go get github.com/golang/lint/golint
|
||||
$(eval LINT_LOG := $(shell mktemp -t golint.XXXXX))
|
||||
@cat /dev/null > $(LINT_LOG)
|
||||
@$(foreach pkg, $(PACKAGES), golint $(pkg) >> $(LINT_LOG) || true;)
|
||||
@[ ! -s "$(LINT_LOG)" ] || (echo "golint failed:" | cat - $(LINT_LOG) && false)
|
||||
|
||||
.PHONY: staticcheck
|
||||
staticcheck:
|
||||
@go get honnef.co/go/tools/cmd/staticcheck
|
||||
$(eval STATICCHECK_LOG := $(shell mktemp -t staticcheck.XXXXX))
|
||||
@staticcheck $(PACKAGES) 2>&1 > $(STATICCHECK_LOG) || true
|
||||
@[ ! -s "$(STATICCHECK_LOG)" ] || (echo "staticcheck failed:" | cat - $(STATICCHECK_LOG) && false)
|
||||
|
||||
.PHONY: lint
|
||||
lint: gofmt govet golint staticcheck
|
||||
|
||||
.PHONY: cover
|
||||
cover:
|
||||
./scripts/cover.sh $(shell go list $(PACKAGES))
|
||||
go tool cover -html=cover.out -o cover.html
|
||||
|
||||
update-license:
|
||||
@go get go.uber.org/tools/update-license
|
||||
@update-license \
|
||||
$(shell go list -json $(PACKAGES) | \
|
||||
jq -r '.Dir + "/" + (.GoFiles | .[])')
|
||||
|
||||
##############################################################################
|
||||
|
||||
.PHONY: install_ci
|
||||
install_ci: install
|
||||
go get github.com/wadey/gocovmerge
|
||||
go get github.com/mattn/goveralls
|
||||
go get golang.org/x/tools/cmd/cover
|
||||
|
||||
.PHONY: test_ci
|
||||
test_ci: install_ci
|
||||
./scripts/cover.sh $(shell go list $(PACKAGES))
|
23
vendor/go.uber.org/multierr/README.md
generated
vendored
Normal file
23
vendor/go.uber.org/multierr/README.md
generated
vendored
Normal file
|
@ -0,0 +1,23 @@
|
|||
# multierr [![GoDoc][doc-img]][doc] [![Build Status][ci-img]][ci] [![Coverage Status][cov-img]][cov]
|
||||
|
||||
`multierr` allows combining one or more Go `error`s together.
|
||||
|
||||
## Installation
|
||||
|
||||
go get -u go.uber.org/multierr
|
||||
|
||||
## Status
|
||||
|
||||
Stable: No breaking changes will be made before 2.0.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Released under the [MIT License].
|
||||
|
||||
[MIT License]: LICENSE.txt
|
||||
[doc-img]: https://godoc.org/go.uber.org/multierr?status.svg
|
||||
[doc]: https://godoc.org/go.uber.org/multierr
|
||||
[ci-img]: https://travis-ci.org/uber-go/multierr.svg?branch=master
|
||||
[cov-img]: https://codecov.io/gh/uber-go/multierr/branch/master/graph/badge.svg
|
||||
[ci]: https://travis-ci.org/uber-go/multierr
|
||||
[cov]: https://codecov.io/gh/uber-go/multierr
|
401
vendor/go.uber.org/multierr/error.go
generated
vendored
Normal file
401
vendor/go.uber.org/multierr/error.go
generated
vendored
Normal file
|
@ -0,0 +1,401 @@
|
|||
// Copyright (c) 2017 Uber Technologies, Inc.
|
||||
//
|
||||
// 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.
|
||||
|
||||
// Package multierr allows combining one or more errors together.
|
||||
//
|
||||
// Overview
|
||||
//
|
||||
// Errors can be combined with the use of the Combine function.
|
||||
//
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// conn.Close(),
|
||||
// )
|
||||
//
|
||||
// If only two errors are being combined, the Append function may be used
|
||||
// instead.
|
||||
//
|
||||
// err = multierr.Combine(reader.Close(), writer.Close())
|
||||
//
|
||||
// This makes it possible to record resource cleanup failures from deferred
|
||||
// blocks with the help of named return values.
|
||||
//
|
||||
// func sendRequest(req Request) (err error) {
|
||||
// conn, err := openConnection()
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, conn.Close())
|
||||
// }()
|
||||
// // ...
|
||||
// }
|
||||
//
|
||||
// The underlying list of errors for a returned error object may be retrieved
|
||||
// with the Errors function.
|
||||
//
|
||||
// errors := multierr.Errors(err)
|
||||
// if len(errors) > 0 {
|
||||
// fmt.Println("The following errors occurred:")
|
||||
// }
|
||||
//
|
||||
// Advanced Usage
|
||||
//
|
||||
// Errors returned by Combine and Append MAY implement the following
|
||||
// interface.
|
||||
//
|
||||
// type errorGroup interface {
|
||||
// // Returns a slice containing the underlying list of errors.
|
||||
// //
|
||||
// // This slice MUST NOT be modified by the caller.
|
||||
// Errors() []error
|
||||
// }
|
||||
//
|
||||
// Note that if you need access to list of errors behind a multierr error, you
|
||||
// should prefer using the Errors function. That said, if you need cheap
|
||||
// read-only access to the underlying errors slice, you can attempt to cast
|
||||
// the error to this interface. You MUST handle the failure case gracefully
|
||||
// because errors returned by Combine and Append are not guaranteed to
|
||||
// implement this interface.
|
||||
//
|
||||
// var errors []error
|
||||
// group, ok := err.(errorGroup)
|
||||
// if ok {
|
||||
// errors = group.Errors()
|
||||
// } else {
|
||||
// errors = []error{err}
|
||||
// }
|
||||
package multierr // import "go.uber.org/multierr"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
var (
|
||||
// Separator for single-line error messages.
|
||||
_singlelineSeparator = []byte("; ")
|
||||
|
||||
_newline = []byte("\n")
|
||||
|
||||
// Prefix for multi-line messages
|
||||
_multilinePrefix = []byte("the following errors occurred:")
|
||||
|
||||
// Prefix for the first and following lines of an item in a list of
|
||||
// multi-line error messages.
|
||||
//
|
||||
// For example, if a single item is:
|
||||
//
|
||||
// foo
|
||||
// bar
|
||||
//
|
||||
// It will become,
|
||||
//
|
||||
// - foo
|
||||
// bar
|
||||
_multilineSeparator = []byte("\n - ")
|
||||
_multilineIndent = []byte(" ")
|
||||
)
|
||||
|
||||
// _bufferPool is a pool of bytes.Buffers.
|
||||
var _bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &bytes.Buffer{}
|
||||
},
|
||||
}
|
||||
|
||||
type errorGroup interface {
|
||||
Errors() []error
|
||||
}
|
||||
|
||||
// Errors returns a slice containing zero or more errors that the supplied
|
||||
// error is composed of. If the error is nil, the returned slice is empty.
|
||||
//
|
||||
// err := multierr.Append(r.Close(), w.Close())
|
||||
// errors := multierr.Errors(err)
|
||||
//
|
||||
// If the error is not composed of other errors, the returned slice contains
|
||||
// just the error that was passed in.
|
||||
//
|
||||
// Callers of this function are free to modify the returned slice.
|
||||
func Errors(err error) []error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Note that we're casting to multiError, not errorGroup. Our contract is
|
||||
// that returned errors MAY implement errorGroup. Errors, however, only
|
||||
// has special behavior for multierr-specific error objects.
|
||||
//
|
||||
// This behavior can be expanded in the future but I think it's prudent to
|
||||
// start with as little as possible in terms of contract and possibility
|
||||
// of misuse.
|
||||
eg, ok := err.(*multiError)
|
||||
if !ok {
|
||||
return []error{err}
|
||||
}
|
||||
|
||||
errors := eg.Errors()
|
||||
result := make([]error, len(errors))
|
||||
copy(result, errors)
|
||||
return result
|
||||
}
|
||||
|
||||
// multiError is an error that holds one or more errors.
|
||||
//
|
||||
// An instance of this is guaranteed to be non-empty and flattened. That is,
|
||||
// none of the errors inside multiError are other multiErrors.
|
||||
//
|
||||
// multiError formats to a semi-colon delimited list of error messages with
|
||||
// %v and with a more readable multi-line format with %+v.
|
||||
type multiError struct {
|
||||
copyNeeded atomic.Bool
|
||||
errors []error
|
||||
}
|
||||
|
||||
var _ errorGroup = (*multiError)(nil)
|
||||
|
||||
// Errors returns the list of underlying errors.
|
||||
//
|
||||
// This slice MUST NOT be modified.
|
||||
func (merr *multiError) Errors() []error {
|
||||
if merr == nil {
|
||||
return nil
|
||||
}
|
||||
return merr.errors
|
||||
}
|
||||
|
||||
func (merr *multiError) Error() string {
|
||||
if merr == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
buff := _bufferPool.Get().(*bytes.Buffer)
|
||||
buff.Reset()
|
||||
|
||||
merr.writeSingleline(buff)
|
||||
|
||||
result := buff.String()
|
||||
_bufferPool.Put(buff)
|
||||
return result
|
||||
}
|
||||
|
||||
func (merr *multiError) Format(f fmt.State, c rune) {
|
||||
if c == 'v' && f.Flag('+') {
|
||||
merr.writeMultiline(f)
|
||||
} else {
|
||||
merr.writeSingleline(f)
|
||||
}
|
||||
}
|
||||
|
||||
func (merr *multiError) writeSingleline(w io.Writer) {
|
||||
first := true
|
||||
for _, item := range merr.errors {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
w.Write(_singlelineSeparator)
|
||||
}
|
||||
io.WriteString(w, item.Error())
|
||||
}
|
||||
}
|
||||
|
||||
func (merr *multiError) writeMultiline(w io.Writer) {
|
||||
w.Write(_multilinePrefix)
|
||||
for _, item := range merr.errors {
|
||||
w.Write(_multilineSeparator)
|
||||
writePrefixLine(w, _multilineIndent, fmt.Sprintf("%+v", item))
|
||||
}
|
||||
}
|
||||
|
||||
// Writes s to the writer with the given prefix added before each line after
|
||||
// the first.
|
||||
func writePrefixLine(w io.Writer, prefix []byte, s string) {
|
||||
first := true
|
||||
for len(s) > 0 {
|
||||
if first {
|
||||
first = false
|
||||
} else {
|
||||
w.Write(prefix)
|
||||
}
|
||||
|
||||
idx := strings.IndexByte(s, '\n')
|
||||
if idx < 0 {
|
||||
idx = len(s) - 1
|
||||
}
|
||||
|
||||
io.WriteString(w, s[:idx+1])
|
||||
s = s[idx+1:]
|
||||
}
|
||||
}
|
||||
|
||||
type inspectResult struct {
|
||||
// Number of top-level non-nil errors
|
||||
Count int
|
||||
|
||||
// Total number of errors including multiErrors
|
||||
Capacity int
|
||||
|
||||
// Index of the first non-nil error in the list. Value is meaningless if
|
||||
// Count is zero.
|
||||
FirstErrorIdx int
|
||||
|
||||
// Whether the list contains at least one multiError
|
||||
ContainsMultiError bool
|
||||
}
|
||||
|
||||
// Inspects the given slice of errors so that we can efficiently allocate
|
||||
// space for it.
|
||||
func inspect(errors []error) (res inspectResult) {
|
||||
first := true
|
||||
for i, err := range errors {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
res.Count++
|
||||
if first {
|
||||
first = false
|
||||
res.FirstErrorIdx = i
|
||||
}
|
||||
|
||||
if merr, ok := err.(*multiError); ok {
|
||||
res.Capacity += len(merr.errors)
|
||||
res.ContainsMultiError = true
|
||||
} else {
|
||||
res.Capacity++
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// fromSlice converts the given list of errors into a single error.
|
||||
func fromSlice(errors []error) error {
|
||||
res := inspect(errors)
|
||||
switch res.Count {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
// only one non-nil entry
|
||||
return errors[res.FirstErrorIdx]
|
||||
case len(errors):
|
||||
if !res.ContainsMultiError {
|
||||
// already flat
|
||||
return &multiError{errors: errors}
|
||||
}
|
||||
}
|
||||
|
||||
nonNilErrs := make([]error, 0, res.Capacity)
|
||||
for _, err := range errors[res.FirstErrorIdx:] {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if nested, ok := err.(*multiError); ok {
|
||||
nonNilErrs = append(nonNilErrs, nested.errors...)
|
||||
} else {
|
||||
nonNilErrs = append(nonNilErrs, err)
|
||||
}
|
||||
}
|
||||
|
||||
return &multiError{errors: nonNilErrs}
|
||||
}
|
||||
|
||||
// Combine combines the passed errors into a single error.
|
||||
//
|
||||
// If zero arguments were passed or if all items are nil, a nil error is
|
||||
// returned.
|
||||
//
|
||||
// Combine(nil, nil) // == nil
|
||||
//
|
||||
// If only a single error was passed, it is returned as-is.
|
||||
//
|
||||
// Combine(err) // == err
|
||||
//
|
||||
// Combine skips over nil arguments so this function may be used to combine
|
||||
// together errors from operations that fail independently of each other.
|
||||
//
|
||||
// multierr.Combine(
|
||||
// reader.Close(),
|
||||
// writer.Close(),
|
||||
// pipe.Close(),
|
||||
// )
|
||||
//
|
||||
// If any of the passed errors is a multierr error, it will be flattened along
|
||||
// with the other errors.
|
||||
//
|
||||
// multierr.Combine(multierr.Combine(err1, err2), err3)
|
||||
// // is the same as
|
||||
// multierr.Combine(err1, err2, err3)
|
||||
//
|
||||
// The returned error formats into a readable multi-line error message if
|
||||
// formatted with %+v.
|
||||
//
|
||||
// fmt.Sprintf("%+v", multierr.Combine(err1, err2))
|
||||
func Combine(errors ...error) error {
|
||||
return fromSlice(errors)
|
||||
}
|
||||
|
||||
// Append appends the given errors together. Either value may be nil.
|
||||
//
|
||||
// This function is a specialization of Combine for the common case where
|
||||
// there are only two errors.
|
||||
//
|
||||
// err = multierr.Append(reader.Close(), writer.Close())
|
||||
//
|
||||
// The following pattern may also be used to record failure of deferred
|
||||
// operations without losing information about the original error.
|
||||
//
|
||||
// func doSomething(..) (err error) {
|
||||
// f := acquireResource()
|
||||
// defer func() {
|
||||
// err = multierr.Append(err, f.Close())
|
||||
// }()
|
||||
func Append(left error, right error) error {
|
||||
switch {
|
||||
case left == nil:
|
||||
return right
|
||||
case right == nil:
|
||||
return left
|
||||
}
|
||||
|
||||
if _, ok := right.(*multiError); !ok {
|
||||
if l, ok := left.(*multiError); ok && !l.copyNeeded.Swap(true) {
|
||||
// Common case where the error on the left is constantly being
|
||||
// appended to.
|
||||
errs := append(l.errors, right)
|
||||
return &multiError{errors: errs}
|
||||
} else if !ok {
|
||||
// Both errors are single errors.
|
||||
return &multiError{errors: []error{left, right}}
|
||||
}
|
||||
}
|
||||
|
||||
// Either right or both, left and right, are multiErrors. Rely on usual
|
||||
// expensive logic.
|
||||
errors := [2]error{left, right}
|
||||
return fromSlice(errors[0:])
|
||||
}
|
19
vendor/go.uber.org/multierr/glide.lock
generated
vendored
Normal file
19
vendor/go.uber.org/multierr/glide.lock
generated
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
hash: b53b5e9a84b9cb3cc4b2d0499e23da2feca1eec318ce9bb717ecf35bf24bf221
|
||||
updated: 2017-04-10T13:34:45.671678062-07:00
|
||||
imports:
|
||||
- name: go.uber.org/atomic
|
||||
version: 3b8db5e93c4c02efbc313e17b2e796b0914a01fb
|
||||
testImports:
|
||||
- name: github.com/davecgh/go-spew
|
||||
version: 6d212800a42e8ab5c146b8ace3490ee17e5225f9
|
||||
subpackages:
|
||||
- spew
|
||||
- name: github.com/pmezard/go-difflib
|
||||
version: d8ed2627bdf02c080bf22230dbb337003b7aba2d
|
||||
subpackages:
|
||||
- difflib
|
||||
- name: github.com/stretchr/testify
|
||||
version: 69483b4bd14f5845b5a1e55bca19e954e827f1d0
|
||||
subpackages:
|
||||
- assert
|
||||
- require
|
8
vendor/go.uber.org/multierr/glide.yaml
generated
vendored
Normal file
8
vendor/go.uber.org/multierr/glide.yaml
generated
vendored
Normal file
|
@ -0,0 +1,8 @@
|
|||
package: go.uber.org/multierr
|
||||
import:
|
||||
- package: go.uber.org/atomic
|
||||
version: ^1
|
||||
testImport:
|
||||
- package: github.com/stretchr/testify
|
||||
subpackages:
|
||||
- assert
|
6
vendor/modules.txt
vendored
6
vendor/modules.txt
vendored
|
@ -15,6 +15,8 @@ github.com/ema/qdisc
|
|||
github.com/godbus/dbus
|
||||
# github.com/golang/protobuf v1.3.1
|
||||
github.com/golang/protobuf/proto
|
||||
# github.com/hodgesds/perf-utils v0.0.6
|
||||
github.com/hodgesds/perf-utils
|
||||
# github.com/konsorten/go-windows-terminal-sequences v1.0.2
|
||||
github.com/konsorten/go-windows-terminal-sequences
|
||||
# github.com/lufia/iostat v0.0.0-20170605150913-9f7362b77ad3
|
||||
|
@ -57,6 +59,10 @@ github.com/siebenmann/go-kstat
|
|||
github.com/sirupsen/logrus
|
||||
# github.com/soundcloud/go-runit v0.0.0-20150630195641-06ad41a06c4a
|
||||
github.com/soundcloud/go-runit/runit
|
||||
# go.uber.org/atomic v1.3.2
|
||||
go.uber.org/atomic
|
||||
# go.uber.org/multierr v1.1.0
|
||||
go.uber.org/multierr
|
||||
# golang.org/x/net v0.0.0-20190328230028-74de082e2cca
|
||||
golang.org/x/net/ipv4
|
||||
golang.org/x/net/bpf
|
||||
|
|
Loading…
Reference in a new issue