Merge pull request #164 from bilibili/cpu

fix cpu painc bug
pull/180/head
Tony 6 years ago committed by GitHub
commit bed7ffde87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      go.mod
  2. 2
      go.sum
  3. 2
      pkg/stat/sys/cpu/cgroup.go
  4. 102
      pkg/stat/sys/cpu/cgroupCPU.go
  5. 88
      pkg/stat/sys/cpu/cpu.go
  6. 20
      pkg/stat/sys/cpu/cpu_darwin.go
  7. 11
      pkg/stat/sys/cpu/cpu_other.go
  8. 22
      pkg/stat/sys/cpu/cpu_test.go
  9. 45
      pkg/stat/sys/cpu/psutilCPU.go
  10. 14
      pkg/stat/sys/cpu/sysconfig_notcgo.go

@ -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
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726
github.com/sirupsen/logrus v1.4.1
github.com/stretchr/testify v1.3.0

@ -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=

@ -1,5 +1,3 @@
// +build linux
package cpu
import (

@ -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
}

@ -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()
}

@ -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 }

@ -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 }

@ -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 * 1000)
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)
}
}

@ -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
}

@ -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
}
Loading…
Cancel
Save