Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[202012] Fix crash when retrieving cpu utilization #71

Merged
merged 3 commits into from
Mar 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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