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

Add function to expose /proc/softnet_stat data #291

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
82 changes: 82 additions & 0 deletions networking.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,13 @@ package veneur

import (
"crypto/tls"
"encoding/csv"
"fmt"
"io"
"net"
"os"
"path/filepath"
"strconv"
"sync"

"github.com/sirupsen/logrus"
Expand Down Expand Up @@ -191,3 +195,81 @@ func startSSFUnix(s *Server, addr *net.UnixAddr) <-chan struct{} {
}()
return done
}

type SoftnetData struct {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes logical sense here, but perhaps it's better placed in something os metric centric package-wise?

Processors []SoftnetDataProcessor
}

type SoftnetDataProcessor struct {
Processed int64
Dropped int64
TimeSqueeze int64
CPUCollision int64
ReceivedRPS int64
FlowLimitCount int64
}

// SoftnetStat provides information about UDP traffic, as provided
// on Linux machines by /proc/net/softnet_stat. Its behavior on other
// operating systems is undefined.
func SoftnetStat() (SoftnetData, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This likely should check runtime for Linux and squawk? (Although the caller could do that too.

f, err := os.Open(filepath.Join("/proc", "net", "softnet_stat"))
if err != nil {
return SoftnetData{}, err
}
return parseSoftnet(f)
}

func parseSoftnet(r io.Reader) (SoftnetData, error) {
// softnet_stat output consists of 11 columns.
// The first two refer to the number of packets processed and dropped.
// The third is the number of times "squeezing" occurred (work was remaining
// when netdev_budget or the time slice expired).
// The next five fields are always 0 for current kernel versions (they previously
// were used for fastroute, which is no longer supported).
// The last three columns provide the number of cpu collisions (when two CPUs attempted
// to acquire the device queue lock simultaneously), the number of times the CPU was woken
// up by an inter-processor interrupt, and the number of times the flow limit has been reached.
const columns = 11
cr := csv.NewReader(r)
cr.Comma = ' '
cr.FieldsPerRecord = columns

records, err := cr.ReadAll()
if err != nil {
return SoftnetData{}, err
}

sd := SoftnetData{}
sd.Processors = make([]SoftnetDataProcessor, len(records))
for i, processor := range records {
fields, err := parseSoftnetRecord(processor)
if err != nil {
return SoftnetData{}, err
}
sdp := SoftnetDataProcessor{
Processed: fields[0],
Dropped: fields[1],
TimeSqueeze: fields[2],
CPUCollision: fields[8],
ReceivedRPS: fields[9],
FlowLimitCount: fields[10],
}
sd.Processors[i] = sdp
}
return sd, nil
}

// parseSoftnetRecord parses a single row
func parseSoftnetRecord(processorRecord []string) ([]int64, error) {
const base = 16
r := make([]int64, len(processorRecord))
for i, c := range processorRecord {
n, err := strconv.ParseInt(c, base, 64)
if err != nil {
return nil, err
}
r[i] = n
}
return r, nil
}
53 changes: 53 additions & 0 deletions networking_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package veneur
import (
"fmt"
"net"
"strings"
"testing"
"time"

Expand Down Expand Up @@ -92,3 +93,55 @@ func TestConnectUNIX(t *testing.T) {
}
close(srv.shutdown)
}

func TestParseSoftnet(t *testing.T) {
const input = `
00058bcf 00000001 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0004c22d 00000000 00000004 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00042d39 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00052ffe 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00015a54 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
0001897c 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000c2 00000000 00000000
00233be1 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 000000d0 00000000
0025ec50 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000f00`

expected := SoftnetData{
Processors: []SoftnetDataProcessor{
{
Processed: 0x00058bcf,
Dropped: 0x1,
},
{
Processed: 0x0004c22d,
TimeSqueeze: 0x4,
},
{
Processed: 0x00042d39,
},
{
Processed: 0x00052ffe,
},
{
Processed: 0x00015a54,
},
{
Processed: 0x0001897c,
CPUCollision: 0xc2,
},
{
Processed: 0x00233be1,
ReceivedRPS: 0xd0,
},
{
Processed: 0x0025ec50,
FlowLimitCount: 0xf00,
},
},
}

r := strings.NewReader(input)
sd, err := parseSoftnet(r)
assert.NoError(t, err, "Could not parse valid softnet_stat data")

assert.Equal(t, sd, expected)
}