From 69282c4c87f2dfec6b4e91f450a96128432b0c2d Mon Sep 17 00:00:00 2001 From: longXboy Date: Mon, 17 Jun 2019 15:43:35 +0800 Subject: [PATCH] fix cpu painc bug --- go.mod | 1 + go.sum | 2 + pkg/stat/sys/cpu/cgroup.go | 2 - .../sys/cpu/{cpu_linux.go => cgroupCPU.go} | 102 +++++++++++++++++- pkg/stat/sys/cpu/cpu.go | 88 +++++---------- pkg/stat/sys/cpu/cpu_darwin.go | 20 ---- pkg/stat/sys/cpu/cpu_other.go | 11 -- pkg/stat/sys/cpu/cpu_test.go | 22 ++++ pkg/stat/sys/cpu/psutilCPU.go | 45 ++++++++ pkg/stat/sys/cpu/psutilCPU_test.go | 22 ++++ pkg/stat/sys/cpu/sysconfig_notcgo.go | 14 --- 11 files changed, 215 insertions(+), 114 deletions(-) rename pkg/stat/sys/cpu/{cpu_linux.go => cgroupCPU.go} (58%) delete mode 100644 pkg/stat/sys/cpu/cpu_darwin.go delete mode 100644 pkg/stat/sys/cpu/cpu_other.go create mode 100644 pkg/stat/sys/cpu/cpu_test.go create mode 100644 pkg/stat/sys/cpu/psutilCPU.go create mode 100644 pkg/stat/sys/cpu/psutilCPU_test.go delete mode 100644 pkg/stat/sys/cpu/sysconfig_notcgo.go diff --git a/go.mod b/go.mod index 915ce23d0..8da4e4372 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,7 @@ require ( github.com/prometheus/client_golang v0.9.2 github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 // indirect github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec // indirect + github.com/shirou/gopsutil v2.18.12+incompatible // indirect github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 github.com/sirupsen/logrus v1.4.1 github.com/stretchr/testify v1.3.0 diff --git a/go.sum b/go.sum index b20ff58d8..8b34b7ceb 100644 --- a/go.sum +++ b/go.sum @@ -86,6 +86,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001 h1:YDeskXpkN github.com/remyoudompheng/bigfft v0.0.0-20190321074620-2f0d2b0e0001/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec h1:6ncX5ko6B9LntYM0YBRXkiSaZMmLYeZ/NWcmeB43mMY= github.com/samuel/go-zookeeper v0.0.0-20180130194729-c4fab1ac1bec/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= +github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM= +github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 h1:xT+JlYxNGqyT+XcU8iUrN18JYed2TvG9yN5ULG2jATM= github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw= github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= diff --git a/pkg/stat/sys/cpu/cgroup.go b/pkg/stat/sys/cpu/cgroup.go index 99e80bb98..a3edd52f4 100644 --- a/pkg/stat/sys/cpu/cgroup.go +++ b/pkg/stat/sys/cpu/cgroup.go @@ -1,5 +1,3 @@ -// +build linux - package cpu import ( diff --git a/pkg/stat/sys/cpu/cpu_linux.go b/pkg/stat/sys/cpu/cgroupCPU.go similarity index 58% rename from pkg/stat/sys/cpu/cpu_linux.go rename to pkg/stat/sys/cpu/cgroupCPU.go index 7f536eb4a..2d7402acd 100644 --- a/pkg/stat/sys/cpu/cpu_linux.go +++ b/pkg/stat/sys/cpu/cgroupCPU.go @@ -1,5 +1,3 @@ -// +build linux - package cpu import ( @@ -12,12 +10,97 @@ import ( "github.com/pkg/errors" ) +type cgroupCPU struct { + frequency uint64 + quota float64 + cores uint64 + + preSystem uint64 + preTotal uint64 + usage uint64 +} + +func newCgroupCPU() (cpu *cgroupCPU, err error) { + cpus, err := perCPUUsage() + if err != nil { + err = errors.Errorf("perCPUUsage() failed!err:=%v", err) + return + } + cores := uint64(len(cpus)) + + sets, err := cpuSets() + if err != nil { + err = errors.Errorf("cpuSets() failed!err:=%v", err) + return + } + quota := float64(len(sets)) + cq, err := cpuQuota() + if err == nil && cq != -1 { + var period uint64 + if period, err = cpuPeriod(); err != nil { + err = errors.Errorf("cpuPeriod() failed!err:=%v", err) + return + } + limit := float64(cq) / float64(period) + if limit < quota { + quota = limit + } + } + maxFreq := cpuMaxFreq() + + preSystem, err := systemCPUUsage() + if err != nil { + err = errors.Errorf("systemCPUUsage() failed!err:=%v", err) + return + } + preTotal, err := totalCPUUsage() + if err != nil { + err = errors.Errorf("totalCPUUsage() failed!err:=%v", err) + } + cpu = &cgroupCPU{ + frequency: maxFreq, + quota: quota, + cores: cores, + preSystem: preSystem, + preTotal: preTotal, + } + return +} + +func (cpu *cgroupCPU) Usage() (u uint64, err error) { + var ( + total uint64 + system uint64 + ) + total, err = totalCPUUsage() + if err != nil { + return + } + system, err = systemCPUUsage() + if err != nil { + return + } + if system != cpu.preSystem { + u = uint64(float64((total-cpu.preTotal)*cpu.cores*1e3) / (float64(system-cpu.preSystem) * cpu.quota)) + } + cpu.preSystem = system + cpu.preTotal = total + return +} + +func (cpu *cgroupCPU) Info() Info { + return Info{ + Frequency: cpu.frequency, + Quota: cpu.quota, + } +} + const nanoSecondsPerSecond = 1e9 // ErrNoCFSLimit is no quota limit var ErrNoCFSLimit = errors.Errorf("no quota limit") -var clockTicksPerSecond = uint64(GetClockTicks()) +var clockTicksPerSecond = uint64(getClockTicks()) // systemCPUUsage returns the host system's cpu usage in // nanoseconds. An error is returned if the format of the underlying @@ -145,3 +228,16 @@ func cpuMaxFreq() uint64 { } return feq } + +//GetClockTicks get the OS's ticks per second +func getClockTicks() int { + // TODO figure out a better alternative for platforms where we're missing cgo + // + // TODO Windows. This could be implemented using Win32 QueryPerformanceFrequency(). + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx + // + // An example of its usage can be found here. + // https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx + + return 100 +} diff --git a/pkg/stat/sys/cpu/cpu.go b/pkg/stat/sys/cpu/cpu.go index f9bf95461..2718536f3 100644 --- a/pkg/stat/sys/cpu/cpu.go +++ b/pkg/stat/sys/cpu/cpu.go @@ -6,82 +6,45 @@ import ( "time" ) -var ( - cores uint64 - maxFreq uint64 - quota float64 - usage uint64 +const ( + interval time.Duration = time.Millisecond * 500 +) - preSystem uint64 - preTotal uint64 +var ( + stats CPU + usage uint64 ) -func init() { - cpus, err := perCPUUsage() - if err != nil { - panic(fmt.Errorf("stat/sys/cpu: perCPUUsage() failed!err:=%v", err)) - } - cores = uint64(len(cpus)) +type CPU interface { + Usage() (u uint64, e error) + Info() Info +} - sets, err := cpuSets() +func init() { + var ( + err error + ) + stats, err = newCgroupCPU() if err != nil { - panic(fmt.Errorf("stat/sys/cpu: cpuSets() failed!err:=%v", err)) - } - quota = float64(len(sets)) - cq, err := cpuQuota() - if err == nil { - if cq != -1 { - var period uint64 - if period, err = cpuPeriod(); err != nil { - panic(fmt.Errorf("stat/sys/cpu: cpuPeriod() failed!err:=%v", err)) - } - limit := float64(cq) / float64(period) - if limit < quota { - quota = limit - } + fmt.Printf("cgroup cpu init failed(%v),switch to psutil cpu\n", err) + stats, err = newPsutilCPU(interval) + if err != nil { + panic(fmt.Sprintf("cgroup cpu init failed!err:=%v", err)) } } - maxFreq = cpuMaxFreq() - - preSystem, err = systemCPUUsage() - if err != nil { - panic(fmt.Errorf("sys/cpu: systemCPUUsage() failed!err:=%v", err)) - } - preTotal, err = totalCPUUsage() - if err != nil { - panic(fmt.Errorf("sys/cpu: totalCPUUsage() failed!err:=%v", err)) - } - go func() { - ticker := time.NewTicker(time.Millisecond * 250) + ticker := time.NewTicker(interval) defer ticker.Stop() for { <-ticker.C - cpu := refreshCPU() - if cpu != 0 { - atomic.StoreUint64(&usage, cpu) + u, err := stats.Usage() + if err == nil && u != 0 { + atomic.StoreUint64(&usage, u) } } }() } -func refreshCPU() (u uint64) { - total, err := totalCPUUsage() - if err != nil { - return - } - system, err := systemCPUUsage() - if err != nil { - return - } - if system != preSystem { - u = uint64(float64((total-preTotal)*cores*1e3) / (float64(system-preSystem) * quota)) - } - preSystem = system - preTotal = total - return u -} - // Stat cpu stat. type Stat struct { Usage uint64 // cpu use ratio. @@ -100,8 +63,5 @@ func ReadStat(stat *Stat) { // GetInfo get cpu info. func GetInfo() Info { - return Info{ - Frequency: maxFreq, - Quota: quota, - } + return stats.Info() } diff --git a/pkg/stat/sys/cpu/cpu_darwin.go b/pkg/stat/sys/cpu/cpu_darwin.go deleted file mode 100644 index 3081507b0..000000000 --- a/pkg/stat/sys/cpu/cpu_darwin.go +++ /dev/null @@ -1,20 +0,0 @@ -// +build darwin - -package cpu - -var su uint64 = 10 -var tu uint64 = 10 - -func systemCPUUsage() (usage uint64, err error) { - su += 1000 - return su, nil -} -func totalCPUUsage() (usage uint64, err error) { - tu += 500 - return tu, nil -} -func perCPUUsage() (usage []uint64, err error) { return []uint64{10, 10, 10, 10}, nil } -func cpuSets() (sets []uint64, err error) { return []uint64{0, 1, 2, 3}, nil } -func cpuQuota() (quota int64, err error) { return 100, nil } -func cpuPeriod() (peroid uint64, err error) { return 10, nil } -func cpuMaxFreq() (feq uint64) { return 10 } diff --git a/pkg/stat/sys/cpu/cpu_other.go b/pkg/stat/sys/cpu/cpu_other.go deleted file mode 100644 index 681976ab2..000000000 --- a/pkg/stat/sys/cpu/cpu_other.go +++ /dev/null @@ -1,11 +0,0 @@ -// +build windows - -package cpu - -func systemCPUUsage() (usage uint64, err error) { return 10, nil } -func totalCPUUsage() (usage uint64, err error) { return 10, nil } -func perCPUUsage() (usage []uint64, err error) { return []uint64{10, 10, 10, 10}, nil } -func cpuSets() (sets []uint64, err error) { return []uint64{0, 1, 2, 3}, nil } -func cpuQuota() (quota int64, err error) { return 100, nil } -func cpuPeriod() (peroid uint64, err error) { return 10, nil } -func cpuMaxFreq() (feq uint64) { return 10 } diff --git a/pkg/stat/sys/cpu/cpu_test.go b/pkg/stat/sys/cpu/cpu_test.go new file mode 100644 index 000000000..accad1deb --- /dev/null +++ b/pkg/stat/sys/cpu/cpu_test.go @@ -0,0 +1,22 @@ +package cpu + +import ( + "fmt" + "testing" + "time" +) + +func Test_CPUUsage(t *testing.T) { + var stat Stat + ReadStat(&stat) + fmt.Println(stat) + time.Sleep(time.Millisecond * 600) + for i := 0; i < 6; i++ { + time.Sleep(time.Millisecond * 500) + ReadStat(&stat) + if stat.Usage == 0 { + t.Fatalf("get cpu failed!cpu usage is zero!") + } + fmt.Println(stat) + } +} diff --git a/pkg/stat/sys/cpu/psutilCPU.go b/pkg/stat/sys/cpu/psutilCPU.go new file mode 100644 index 000000000..8d64f8dcc --- /dev/null +++ b/pkg/stat/sys/cpu/psutilCPU.go @@ -0,0 +1,45 @@ +package cpu + +import ( + "time" + + "github.com/shirou/gopsutil/cpu" +) + +type psutilCPU struct { + interval time.Duration +} + +func newPsutilCPU(interval time.Duration) (cpu *psutilCPU, err error) { + cpu = &psutilCPU{interval: interval} + _, err = cpu.Usage() + if err != nil { + return + } + return +} + +func (ps *psutilCPU) Usage() (u uint64, err error) { + var percents []float64 + percents, err = cpu.Percent(ps.interval, false) + if err == nil { + u = uint64(percents[0] * 10) + } + return +} + +func (ps *psutilCPU) Info() (info Info) { + stats, err := cpu.Info() + if err != nil { + return + } + cores, err := cpu.Counts(true) + if err != nil { + return + } + info = Info{ + Frequency: uint64(stats[0].Mhz), + Quota: float64(cores), + } + return +} diff --git a/pkg/stat/sys/cpu/psutilCPU_test.go b/pkg/stat/sys/cpu/psutilCPU_test.go new file mode 100644 index 000000000..3b14d5edc --- /dev/null +++ b/pkg/stat/sys/cpu/psutilCPU_test.go @@ -0,0 +1,22 @@ +package cpu + +import ( + "fmt" + "testing" + "time" +) + +func Test_PsutilCPU(t *testing.T) { + cpu, err := newPsutilCPU(time.Millisecond * 500) + if err != nil { + t.Fatalf("newPsutilCPU failed!err:=%v", err) + } + for i := 0; i < 6; i++ { + time.Sleep(time.Millisecond * 500) + u, err := cpu.Usage() + if u == 0 { + t.Fatalf("get cpu from psutil failed!cpu usage is zero!err:=%v", err) + } + fmt.Println(u) + } +} diff --git a/pkg/stat/sys/cpu/sysconfig_notcgo.go b/pkg/stat/sys/cpu/sysconfig_notcgo.go deleted file mode 100644 index 9edab7ef1..000000000 --- a/pkg/stat/sys/cpu/sysconfig_notcgo.go +++ /dev/null @@ -1,14 +0,0 @@ -package cpu - -//GetClockTicks get the OS's ticks per second -func GetClockTicks() int { - // TODO figure out a better alternative for platforms where we're missing cgo - // - // TODO Windows. This could be implemented using Win32 QueryPerformanceFrequency(). - // https://msdn.microsoft.com/en-us/library/windows/desktop/ms644905(v=vs.85).aspx - // - // An example of its usage can be found here. - // https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408(v=vs.85).aspx - - return 100 -}