Skip to content

Commit

Permalink
Fix crash when retrieving cpu utilization (#70) (#71)
Browse files Browse the repository at this point in the history
Fix crash in which there was a divide by zero error inside retrieving cpu utilization
  • Loading branch information
zbud-msft authored Mar 1, 2023
1 parent af1ec19 commit b93c4ac
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 10 deletions.
81 changes: 81 additions & 0 deletions gnmi_server/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"encoding/json"
"flag"
"fmt"
"sync"
"strings"

testcert "github.com/Azure/sonic-telemetry/testdata/tls"
Expand Down Expand Up @@ -39,9 +40,11 @@ import (
sgpb "github.com/Azure/sonic-telemetry/proto/gnoi"
sdc "github.com/Azure/sonic-telemetry/sonic_data_client"
sdcfg "github.com/Azure/sonic-telemetry/sonic_db_config"
linuxproc "github.com/c9s/goprocinfo/linux"
gclient "github.com/jipanyang/gnmi/client/gnmi"
"github.com/jipanyang/gnxi/utils/xpath"
gnoi_system_pb "github.com/openconfig/gnoi/system"
"github.com/agiledragon/gomonkey/v2"
)

var clientTypes = []string{gclient.Type}
Expand Down Expand Up @@ -2303,6 +2306,84 @@ func TestGNOI(t *testing.T) {
}
}

func TestCPUUtilization(t *testing.T) {
mock := gomonkey.ApplyFunc(sdc.PollStats, func() {
var i uint64
for i = 0; i < 3000; i++ {
sdc.WriteStatsToBuffer(&linuxproc.Stat{})
}
})

defer mock.Reset()
s := createServer(t, 8081)
go runServer(t, s)
defer s.s.Stop()

tests := []struct {
desc string
q client.Query
want []client.Notification
poll int
}{
{
desc: "poll query for CPU Utilization",
poll: 10,
q: client.Query{
Target: "OTHERS",
Type: client.Poll,
Queries: []client.Path{{"platform", "cpu"}},
TLS: &tls.Config{InsecureSkipVerify: true},
},
want: []client.Notification{
client.Connected{},
client.Sync{},
},
},
}

for _, tt := range tests {
t.Run(tt.desc, func(t *testing.T) {
q := tt.q
q.Addrs = []string{"127.0.0.1:8081"}
c := client.New()
var gotNoti []client.Notification
q.NotificationHandler = func(n client.Notification) error {
if nn, ok := n.(client.Update); ok {
nn.TS = time.Unix(0, 200)
gotNoti = append(gotNoti, nn)
} else {
gotNoti = append(gotNoti, n)
}
return nil
}

wg := new(sync.WaitGroup)
wg.Add(1)

go func() {
defer wg.Done()
if err := c.Subscribe(context.Background(), q); err != nil {
t.Errorf("c.Subscribe(): got error %v, expected nil", err)
}
}()

wg.Wait()

for i := 0; i < tt.poll; i++ {
if err := c.Poll(); err != nil {
t.Errorf("c.Poll(): got error %v, expected nil", err)
}
}

if len(gotNoti) == 0 {
t.Errorf("expected non zero notifications")
}

c.Close()
})
}
}

func TestBundleVersion(t *testing.T) {
s := createServer(t, 8087)
go runServer(t, s)
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.12
require (
github.com/Azure/sonic-mgmt-common v0.0.0-00010101000000-000000000000
github.com/Workiva/go-datastructures v1.0.50
github.com/agiledragon/gomonkey/v2 v2.8.0
github.com/c9s/goprocinfo v0.0.0-20191125144613-4acdd056c72d
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/go-redis/redis v6.15.6+incompatible
Expand Down
26 changes: 16 additions & 10 deletions sonic_data_client/non_db_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,10 +132,13 @@ type cpuUtil struct {
}

func getCpuUtilPercents(cur, last *linuxproc.CPUStat) uint64 {
curTotal := (cur.User + cur.Nice + cur.System + cur.Idle + cur.IOWait + cur.IRQ + cur.SoftIRQ + cur.Steal + cur.Guest + cur.GuestNice)
lastTotal := (last.User + last.Nice + last.System + last.Idle + last.IOWait + last.IRQ + last.SoftIRQ + last.Steal + last.Guest + last.GuestNice)
curTotal := (cur.User + cur.Nice + cur.System + cur.Idle + cur.IOWait + cur.IRQ + cur.SoftIRQ + cur.Steal)
lastTotal := (last.User + last.Nice + last.System + last.Idle + last.IOWait + last.IRQ + last.SoftIRQ + last.Steal)
idleTicks := cur.Idle - last.Idle
totalTicks := curTotal - lastTotal
if totalTicks == 0 { // No change in CPU Utilization
return 0
}
return 100 * (totalTicks - idleTicks) / totalTicks
}

Expand Down Expand Up @@ -332,20 +335,23 @@ func getBuildVersion() ([]byte, error) {
return b, nil
}

func pollStats() {
func WriteStatsToBuffer(stat *linuxproc.Stat) {
statsR.mu.Lock()
statsR.buff[statsR.writeIdx] = stat
statsR.writeIdx++
statsR.writeIdx %= statsRingCap
statsR.mu.Unlock()
}

func PollStats() {
for {
stat, err := linuxproc.ReadStat("/proc/stat")
if err != nil {
log.V(2).Infof("stat read fail")
continue
}

statsR.mu.Lock()

statsR.buff[statsR.writeIdx] = stat
statsR.writeIdx++
statsR.writeIdx %= statsRingCap
statsR.mu.Unlock()
WriteStatsToBuffer(stat)
time.Sleep(time.Millisecond * 100)
}

Expand All @@ -355,7 +361,7 @@ func init() {
clientTrie = NewTrie()
clientTrie.clientTriePopulate()
statsR.buff = make([]*linuxproc.Stat, statsRingCap)
go pollStats()
go PollStats()
}

type NonDbClient struct {
Expand Down

0 comments on commit b93c4ac

Please sign in to comment.