diff --git a/AUTHORS.md b/AUTHORS.md index 0c802dd87..34c92ef5e 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -18,3 +18,4 @@ The following individuals have contributed code to this repository * Nicky Gerritsen * Rémi Audebert * Tobias Schmidt +* Abhi Yerra diff --git a/fixtures/meminfo b/fixtures/meminfo new file mode 100644 index 000000000..5dec80e81 --- /dev/null +++ b/fixtures/meminfo @@ -0,0 +1,42 @@ +MemTotal: 15666184 kB +MemFree: 440324 kB +Buffers: 1020128 kB +Cached: 12007640 kB +SwapCached: 0 kB +Active: 6761276 kB +Inactive: 6532708 kB +Active(anon): 267256 kB +Inactive(anon): 268 kB +Active(file): 6494020 kB +Inactive(file): 6532440 kB +Unevictable: 0 kB +Mlocked: 0 kB +SwapTotal: 0 kB +SwapFree: 0 kB +Dirty: 768 kB +Writeback: 0 kB +AnonPages: 266216 kB +Mapped: 44204 kB +Shmem: 1308 kB +Slab: 1807264 kB +SReclaimable: 1738124 kB +SUnreclaim: 69140 kB +KernelStack: 1616 kB +PageTables: 5288 kB +NFS_Unstable: 0 kB +Bounce: 0 kB +WritebackTmp: 0 kB +CommitLimit: 7833092 kB +Committed_AS: 530844 kB +VmallocTotal: 34359738367 kB +VmallocUsed: 36596 kB +VmallocChunk: 34359637840 kB +HardwareCorrupted: 0 kB +AnonHugePages: 12288 kB +HugePages_Total: 0 +HugePages_Free: 0 +HugePages_Rsvd: 0 +HugePages_Surp: 0 +Hugepagesize: 2048 kB +DirectMap4k: 91136 kB +DirectMap2M: 16039936 kB diff --git a/fixtures/net/sockstat b/fixtures/net/sockstat new file mode 100644 index 000000000..47e51f731 --- /dev/null +++ b/fixtures/net/sockstat @@ -0,0 +1,6 @@ +sockets: used 3014 +TCP: inuse 3168 orphan 612 tw 13701 alloc 3169 mem 2734 +UDP: inuse 9 mem 2 +UDPLITE: inuse 0 +RAW: inuse 1 +FRAG: inuse 2 memory 2 \ No newline at end of file diff --git a/fixtures/net/tcp b/fixtures/net/tcp new file mode 100644 index 000000000..aecc80ccb --- /dev/null +++ b/fixtures/net/tcp @@ -0,0 +1,20 @@ + sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode + 0: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 16071 1 0000000000000000 100 0 0 10 0 + 1: 00000000:0056 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 16481 1 0000000000000000 100 0 0 10 0 + 2: 00000000:0057 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 16482 1 0000000000000000 100 0 0 10 0 + 3: 00000000:0BB8 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 516865617 1 0000000000000000 100 0 0 10 0 + 4: 00000000:0058 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 16476 1 0000000000000000 100 0 0 10 0 + 5: 00000000:0059 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 16480 1 0000000000000000 100 0 0 10 0 + 6: 00000000:005A 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 16479 1 0000000000000000 100 0 0 10 0 + 7: 00000000:01BB 00000000:0000 0A 00000000:00000000 02:00000002 00000000 0 0 3994549147 2 0000000000000000 100 0 1 10 0 + 8: 260FC90A:01BB AE6857C8:C747 03 00000000:00000000 01:000000DC 00000005 0 0 0 2 0000000000000000 + 9: 260FC90A:01BB 0E7F51DE:7A79 03 00000000:00000000 01:0000084A 00000005 0 0 0 2 0000000000000000 + 10: 260FC90A:01BB 89E2E3C9:749B 03 00000000:00000000 01:00000A16 00000005 0 0 0 2 0000000000000000 + 11: 260FC90A:01BB AE6857C8:C752 03 00000000:00000000 01:0000008E 00000002 0 0 0 2 0000000000000000 + 12: 260FC90A:01BB D81B38AC:64F9 03 00000000:00000000 01:00000063 00000000 0 0 0 2 0000000000000000 + 13: 260FC90A:01BB 14288875:155E 03 00000000:00000000 01:00000A16 00000005 0 0 0 2 0000000000000000 + 14: 260FC90A:01BB 53202464:C9D6 03 00000000:00000000 01:00000051 00000000 0 0 0 2 0000000000000000 + 15: 260FC90A:01BB 5C49E8DD:F022 03 00000000:00000000 01:0000021F 00000004 0 0 0 2 0000000000000000 + 16: 260FC90A:01BB 9A99935B:AFF3 03 00000000:00000000 01:00000246 00000004 0 0 0 2 0000000000000000 + 17: 260FC90A:01BB 231D4B4B:EF00 03 00000000:00000000 01:000001BA 00000004 0 0 0 2 0000000000000000 + 18: 260FC90A:01BB 162491A5:C524 03 00000000:00000000 01:00000272 00000004 0 0 0 2 0000000000000000 diff --git a/meminfo.go b/meminfo.go new file mode 100644 index 000000000..3d86de50f --- /dev/null +++ b/meminfo.go @@ -0,0 +1,174 @@ +package procfs + +import ( + "bufio" + "os" + "reflect" + "regexp" + "strconv" +) + +// Meminfo represents memory statistics. +type Meminfo struct { + // Total usable ram (i.e. physical ram minus a few reserved + // bits and the kernel binary code) + MemTotal int64 `meminfo:"MemTotal"` + // The sum of LowFree+HighFree + MemFree int64 `meminfo:"MemFree"` + // An estimate of how much memory is available for starting + // new applications, without swapping. Calculated from + // MemFree, SReclaimable, the size of the file LRU lists, and + // the low watermarks in each zone. The estimate takes into + // account that the system needs some page cache to function + // well, and that not all reclaimable slab will be + // reclaimable, due to items being in use. The impact of those + // factors will vary from system to system. + MemAvailable int64 `meminfo:"MemAvailable"` + // Relatively temporary storage for raw disk blocks shouldn't + // get tremendously large (20MB or so) + Buffers int64 `meminfo:"Buffers"` + Cached int64 `meminfo:"Cached"` + // Memory that once was swapped out, is swapped back in but + // still also is in the swapfile (if memory is needed it + // doesn't need to be swapped out AGAIN because it is already + // in the swapfile. This saves I/O) + SwapCached int64 `meminfo:"SwapCached"` + // Memory that has been used more recently and usually not + // reclaimed unless absolutely necessary. + Active int64 `meminfo:"Active"` + // Memory which has been less recently used. It is more + // eligible to be reclaimed for other purposes + Inactive int64 `meminfo:"Inactive"` + ActiveAnon int64 `meminfo:"Active(anon)"` + InactiveAnon int64 `meminfo:"Inactive(anon)"` + ActiveFile int64 `meminfo:"Active(file)"` + InactiveFile int64 `meminfo:"Inactive(file)"` + Unevictable int64 `meminfo:"Unevictable"` + Mlocked int64 `meminfo:"Mlocked"` + // total amount of swap space available + SwapTotal int64 `meminfo:"SwapTotal"` + // Memory which has been evicted from RAM, and is temporarily + // on the disk + SwapFree int64 `meminfo:"SwapFree"` + // Memory which is waiting to get written back to the disk + Dirty int64 `meminfo:"Dirty"` + // Memory which is actively being written back to the disk + Writeback int64 `meminfo:"Writeback"` + // Non-file backed pages mapped into userspace page tables + AnonPages int64 `meminfo:"AnonPages"` + // files which have been mmaped, such as libraries + Mapped int64 `meminfo:"Mapped"` + Shmem int64 `meminfo:"Shmem"` + // in-kernel data structures cache + Slab int64 `meminfo:"Slab"` + // Part of Slab, that might be reclaimed, such as caches + SReclaimable int64 `meminfo:"SReclaimable"` + // Part of Slab, that cannot be reclaimed on memory pressure + SUnreclaim int64 `meminfo:"SUnreclaim"` + KernelStack int64 `meminfo:"KernelStack"` + // amount of memory dedicated to the lowest level of page + // tables. + PageTables int64 `meminfo:"PageTables"` + // NFS pages sent to the server, but not yet committed to + // stable storage + NFSUnstable int64 `meminfo:"NFS_Unstable"` + // Memory used for block device "bounce buffers" + Bounce int64 `meminfo:"Bounce"` + // Memory used by FUSE for temporary writeback buffers + WritebackTmp int64 `meminfo:"WritebackTmp"` + // Based on the overcommit ratio ('vm.overcommit_ratio'), + // this is the total amount of memory currently available to + // be allocated on the system. This limit is only adhered to + // if strict overcommit accounting is enabled (mode 2 in + // 'vm.overcommit_memory'). + // The CommitLimit is calculated with the following formula: + // CommitLimit = ([total RAM pages] - [total huge TLB pages]) * + // overcommit_ratio / 100 + [total swap pages] + // For example, on a system with 1G of physical RAM and 7G + // of swap with a `vm.overcommit_ratio` of 30 it would + // yield a CommitLimit of 7.3G. + // For more details, see the memory overcommit documentation + // in vm/overcommit-accounting. + CommitLimit int64 `meminfo:"CommitLimit"` + // The amount of memory presently allocated on the system. + // The committed memory is a sum of all of the memory which + // has been allocated by processes, even if it has not been + // "used" by them as of yet. A process which malloc()'s 1G + // of memory, but only touches 300M of it will show up as + // using 1G. This 1G is memory which has been "committed" to + // by the VM and can be used at any time by the allocating + // application. With strict overcommit enabled on the system + // (mode 2 in 'vm.overcommit_memory'),allocations which would + // exceed the CommitLimit (detailed above) will not be permitted. + // This is useful if one needs to guarantee that processes will + // not fail due to lack of memory once that memory has been + // successfully allocated. + CommittedAS int64 `meminfo:"Committed_AS"` + // total size of vmalloc memory area + VmallocTotal int64 `meminfo:"VmallocTotal"` + // amount of vmalloc area which is used + VmallocUsed int64 `meminfo:"VmallocUsed"` + // largest contiguous block of vmalloc area which is free + VmallocChunk int64 `meminfo:"VmallocChunk"` + HardwareCorrupted int64 `meminfo:"HardwareCorrupted"` + AnonHugePages int64 `meminfo:"AnonHugePages"` + HugePagesTotal int64 `meminfo:"HugePages_Total"` + HugePagesFree int64 `meminfo:"HugePages_Free"` + HugePagesRsvd int64 `meminfo:"HugePages_Rsvd"` + HugePagesSurp int64 `meminfo:"HugePages_Surp"` + Hugepagesize int64 `meminfo:"Hugepagesize"` + DirectMap4k int64 `meminfo:"DirectMap4k"` + DirectMap2M int64 `meminfo:"DirectMap2M"` +} + +// NewMeminfo returns kernel/system statistics read from /proc/stat. +func NewMeminfo() (Meminfo, error) { + fs, err := NewFS(DefaultMountPoint) + if err != nil { + return Meminfo{}, err + } + + return fs.NewMeminfo() +} + +// NewMeminfo returns an information about current kernel/system statistics. +func (fs FS) NewMeminfo() (m Meminfo, err error) { + f, err := os.Open(fs.Path("meminfo")) + if err != nil { + return Meminfo{}, err + } + defer f.Close() + + st := reflect.TypeOf(m) + re := regexp.MustCompile(m.regex()) + s := bufio.NewScanner(f) + + for s.Scan() { + line := s.Text() + + submatch := re.FindAllStringSubmatch(line, 1) + if submatch == nil { + continue + } + + key := submatch[0][1] + val := submatch[0][2] + + for i := 0; i < st.NumField(); i++ { + field := st.Field(i) + if field.Tag.Get("meminfo") == key { + v, err := strconv.ParseInt(val, 10, 64) + if err != nil { + // no op + } + reflect.ValueOf(&m).Elem().Field(i).SetInt(v) + } + } + } + + return m, nil +} + +func (m Meminfo) regex() string { + return "([A-Za-z0-9()_]*): *([0-9]*).*$" +} diff --git a/meminfo_test.go b/meminfo_test.go new file mode 100644 index 000000000..0e8f9dd3d --- /dev/null +++ b/meminfo_test.go @@ -0,0 +1,118 @@ +package procfs + +import ( + "reflect" + "regexp" + "testing" +) + +func TestMeminfo_regex(t *testing.T) { + + var expected = []struct { + Line string + Key string + Value string + }{ + { + Line: "Inactive(file): 6532440 kB", + Key: "Inactive(file)", + Value: "6532440", + }, + { + Line: "Writeback: 0 kB", + + Key: "Writeback", + Value: "0", + }, + { + Line: "Writeback: 123", + Key: "Writeback", + Value: "123", + }, + { + Line: "HugePages_Total: 0", + Key: "HugePages_Total", + Value: "0", + }, + { + Line: "DirectMap2M: 16039936 kB", + Key: "DirectMap2M", + Value: "16039936", + }, + } + + m := Meminfo{} + re := regexp.MustCompile(m.regex()) + + for _, i := range expected { + submatch := re.FindAllStringSubmatch(i.Line, 1) + if submatch == nil { + t.Errorf("regex fail") + } + + if submatch[0][1] != i.Key { + t.Errorf("Key failed for %s", i.Key) + } + if submatch[0][2] != i.Value { + t.Errorf("Value failed for %s", i.Value) + } + + } + +} + +func TestMeminfo(t *testing.T) { + expected := Meminfo{ + MemTotal: 15666184, + MemFree: 440324, + Buffers: 1020128, + Cached: 12007640, + SwapCached: 0, + Active: 6761276, + Inactive: 6532708, + ActiveAnon: 267256, + InactiveAnon: 268, + ActiveFile: 6494020, + InactiveFile: 6532440, + Unevictable: 0, + Mlocked: 0, + SwapTotal: 0, + SwapFree: 0, + Dirty: 768, + Writeback: 0, + AnonPages: 266216, + Mapped: 44204, + Shmem: 1308, + Slab: 1807264, + SReclaimable: 1738124, + SUnreclaim: 69140, + KernelStack: 1616, + PageTables: 5288, + NFSUnstable: 0, + Bounce: 0, + WritebackTmp: 0, + CommitLimit: 7833092, + CommittedAS: 530844, + VmallocTotal: 34359738367, + VmallocUsed: 36596, + VmallocChunk: 34359637840, + HardwareCorrupted: 0, + AnonHugePages: 12288, + HugePagesTotal: 0, + HugePagesFree: 0, + HugePagesRsvd: 0, + HugePagesSurp: 0, + Hugepagesize: 2048, + DirectMap4k: 91136, + DirectMap2M: 16039936, + } + + have, err := FS("fixtures").NewMeminfo() + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(have, expected) { + t.Errorf("structs are not equal") + } +} diff --git a/net_sockstat.go b/net_sockstat.go new file mode 100644 index 000000000..ba6a23991 --- /dev/null +++ b/net_sockstat.go @@ -0,0 +1,139 @@ +package procfs + +import ( + "bufio" + "os" + "strconv" + "strings" +) + +// NetSockstat stats on /proc/net/sockstat +type NetSockstat struct { + Sockets struct { + Used int64 + } + + TCP struct { + InUse int64 + Orphan int64 + Tw int64 + Alloc int64 + Mem int64 + } + + UDP struct { + InUse int64 + Mem int64 + } + + UDPLite struct { + InUse int64 + } + + RAW struct { + InUse int64 + } + + FRAG struct { + InUse int64 + Memory int64 + } +} + +// NewNetSockstat returns kernel/system statistics read from /proc/net/sockstat. +func NewNetSockstat() (NetSockstat, error) { + fs, err := NewFS(DefaultMountPoint) + if err != nil { + return NetSockstat{}, err + } + + return fs.NewNetSockstat() +} + +// NewNetSockstat returns an information about current kernel/system statistics. +func (fs FS) NewNetSockstat() (m NetSockstat, err error) { + f, err := os.Open(fs.Path("net/sockstat")) + if err != nil { + return NetSockstat{}, err + } + defer f.Close() + + s := bufio.NewScanner(f) + for s.Scan() { + line := s.Text() + + switch { + case strings.HasPrefix(line, "sockets:"): + parsedLines := strings.Fields(line) + for i := range parsedLines { + switch parsedLines[i] { + case "used": + m.Sockets.Used = parseInt64(parsedLines[i+1]) + } + } + case strings.HasPrefix(line, "TCP:"): + parsedLines := strings.Fields(line) + for i := range parsedLines { + switch parsedLines[i] { + case "inuse": + m.TCP.InUse = parseInt64(parsedLines[i+1]) + case "orphan": + m.TCP.Orphan = parseInt64(parsedLines[i+1]) + case "tw": + m.TCP.Tw = parseInt64(parsedLines[i+1]) + case "alloc": + m.TCP.Alloc = parseInt64(parsedLines[i+1]) + case "mem": + m.TCP.Mem = parseInt64(parsedLines[i+1]) + } + } + case strings.HasPrefix(line, "UDP:"): + parsedLines := strings.Fields(line) + for i := range parsedLines { + switch parsedLines[i] { + case "inuse": + m.UDP.InUse = parseInt64(parsedLines[i+1]) + case "mem": + m.UDP.Mem = parseInt64(parsedLines[i+1]) + } + } + case strings.HasPrefix(line, "UDPLITE:"): + parsedLines := strings.Fields(line) + for i := range parsedLines { + switch parsedLines[i] { + case "inuse": + m.UDPLite.InUse = parseInt64(parsedLines[i+1]) + } + } + case strings.HasPrefix(line, "RAW:"): + parsedLines := strings.Fields(line) + for i := range parsedLines { + switch parsedLines[i] { + case "inuse": + m.RAW.InUse = parseInt64(parsedLines[i+1]) + } + } + case strings.HasPrefix(line, "FRAG:"): + parsedLines := strings.Fields(line) + for i := range parsedLines { + switch parsedLines[i] { + case "inuse": + m.FRAG.InUse = parseInt64(parsedLines[i+1]) + case "memory": + m.FRAG.Memory = parseInt64(parsedLines[i+1]) + } + } + } + } + + return m, nil +} + +func parseInt64(val string) int64 { + v, err := strconv.ParseInt(val, 10, 64) + if err != nil { + return 0 + } + + return v +} diff --git a/net_sockstat_test.go b/net_sockstat_test.go new file mode 100644 index 000000000..47fe7233a --- /dev/null +++ b/net_sockstat_test.go @@ -0,0 +1,31 @@ +package procfs + +import ( + "reflect" + "testing" +) + +func TestNetSockstat(t *testing.T) { + expected := NetSockstat{} + expected.Sockets.Used = 3014 + expected.TCP.InUse = 3168 + expected.TCP.Orphan = 612 + expected.TCP.Tw = 13701 + expected.TCP.Alloc = 3169 + expected.TCP.Mem = 2734 + expected.UDP.InUse = 9 + expected.UDP.Mem = 2 + expected.UDPLite.InUse = 0 + expected.RAW.InUse = 1 + expected.FRAG.InUse = 2 + expected.FRAG.Memory = 2 + + have, err := FS("fixtures").NewNetSockstat() + if err != nil { + t.Fatal(err) + } + + if !reflect.DeepEqual(expected, have) { + t.Errorf("structs are not equal") + } +} diff --git a/net_tcp.go b/net_tcp.go new file mode 100644 index 000000000..ea1ecdf29 --- /dev/null +++ b/net_tcp.go @@ -0,0 +1,126 @@ +package procfs + +import ( + "bufio" + "fmt" + "os" + "regexp" +) + +// NetTCPLine is a parsed line of /proc/net/tcp +type NetTCPLine struct { + Sl string `net_tcp:"sl"` + LocalAddress string `net_tcp:"local_address"` + RemoteAddress string `net_tcp:"rem_address"` + St string `net_tcp:"st"` + TxQueue string `net_tcp:"tx_queue"` + RxQueue string `net_tcp:"rx_queue"` + Tr string `net_tcp:"tr"` + TmWhen string `net_tcp:"tm->when"` + Retrnsmt string `net_tcp:"retrnsmt"` + UID string `net_tcp:"uid"` + Timeout string `net_tcp:"timeout"` + Inode string `net_tcp:"inode"` + RefCount string `net_tcp:""` + MemoryAddress string `net_tcp:""` + + // There are optional attributes which I am not capturing + // including: Retransmit Timeout, Predicted Tick, Ack.quick, + // Sending Congestion Window, Slow Start Size Threshold. +} + +// NetTCP stats on /proc/net/tcp +type NetTCP []NetTCPLine + +// NewNetTCP returns kernel/system statistics read from /proc/net/tcp. +func NewNetTCP() (NetTCP, error) { + fs, err := NewFS(DefaultMountPoint) + if err != nil { + return NetTCP{}, err + } + + return fs.NewNetTCP() +} + +// NewNetTCP returns an information about current kernel/system statistics. +func (fs FS) NewNetTCP() (m NetTCP, err error) { + f, err := os.Open(fs.Path("net/tcp")) + if err != nil { + return NetTCP{}, err + } + defer f.Close() + + re := regexp.MustCompile(m.regex()) + s := bufio.NewScanner(f) + + for s.Scan() { + line := s.Text() + + l, err := m.netTCPLine(re, line) + if err == nil { + m = append(m, l) + } + } + + return m, nil +} + +func (n NetTCP) netTCPLine(re *regexp.Regexp, line string) (l NetTCPLine, err error) { + matches := re.FindAllStringSubmatch(line, 21) + if matches == nil { + return NetTCPLine{}, fmt.Errorf("Invalid NetTCP Line") + } + r := matches[0] + + l = NetTCPLine{ + Sl: r[1], + LocalAddress: r[2] + ":" + r[3], + RemoteAddress: r[4] + ":" + r[5], + St: r[6], + TxQueue: r[7], + RxQueue: r[8], + Tr: r[9], + TmWhen: r[10], + Retrnsmt: r[11], + UID: r[12], + Timeout: r[13], + Inode: r[14], + RefCount: r[15], + MemoryAddress: r[16], + } + + return +} + +/* +https://metacpan.org/source/SALVA/Linux-Proc-Net-TCP-0.07/README + +qr/^\s* + (\d+):\s # sl - 0 + ([\dA-F]{8}(?:[\dA-F]{24})?):([\dA-F]{4})\s # local address and port - 1 & 2 + ([\dA-F]{8}(?:[\dA-F]{24})?):([\dA-F]{4})\s # remote address and port - 3 & 4 + ([\dA-F]{2})\s # st - 5 + ([\dA-F]{8}):([\dA-F]{8})\s # tx_queue and rx_queue - 6 & 7 + (\d\d):([\dA-F]{8}|F{9,}|1AD7F[\dA-F]{6})\s # tr and tm->when - 8 & 9 + ([\dA-F]{8})\s+ # retrnsmt - 10 + (\d+)\s+ # uid - 11 + (\d+)\s+ # timeout - 12 + (\d+)\s+ # inode - 13 + (\d+)\s+ # ref count - 14 + ((?:[\dA-F]{8}){1,2}) # memory address - 15 + (?: + \s+ + (\d+)\s+ # retransmit timeout - 16 + (\d+)\s+ # predicted tick - 17 + (\d+)\s+ # ack.quick - 18 + (\d+)\s+ # sending congestion window - 19 + (-?\d+) # slow start size threshold - 20 + )? + \s* + (.*) # more - 21 + $ +/xi, +*/ +func (n NetTCP) regex() string { + return `^\s*(\d+):\s([\dA-F]{8}(?:[\dA-F]{24})?):([\dA-F]{4})\s([\dA-F]{8}(?:[\dA-F]{24})?):([\dA-F]{4})\s([\dA-F]{2})\s([\dA-F]{8}):([\dA-F]{8})\s(\d\d):([\dA-F]{8}|F{9,}|1AD7F[\dA-F]{6\})\s([\dA-F]{8})\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+((?:[\dA-F]{8}){1,2})(?:\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(-?\d+))?\s*(.*)$` +} diff --git a/net_tcp_test.go b/net_tcp_test.go new file mode 100644 index 000000000..a71ff0cb8 --- /dev/null +++ b/net_tcp_test.go @@ -0,0 +1,77 @@ +package procfs + +import ( + "reflect" + "regexp" + "testing" +) + +func TestNetTCP_regex(t *testing.T) { + + var expected = []struct { + Line string + Expected NetTCPLine + }{ + { + Line: " 0: 00000000:0016 00000000:0000 0A 00000000:00000000 00:00000000 00000000 0 0 16071 1 0000000000000000 100 0 0 10 0", + Expected: NetTCPLine{ + Sl: "0", + LocalAddress: "00000000:0016", + RemoteAddress: "00000000:0000", + St: "0A", + TxQueue: "00000000", + RxQueue: "00000000", + Tr: "00", + TmWhen: "00000000", + Retrnsmt: "00000000", + UID: "0", + Timeout: "0", + Inode: "16071", + RefCount: "1", + MemoryAddress: "0000000000000000", + }, + }, + { + Line: " 8: 260FC90A:01BB AE6857C8:C747 03 00000000:00000000 01:000000DC 00000005 0 0 0 2 0000000000000000", + Expected: NetTCPLine{ + Sl: "8", + LocalAddress: "260FC90A:01BB", + RemoteAddress: "AE6857C8:C747", + St: "03", + TxQueue: "00000000", + RxQueue: "00000000", + Tr: "01", + TmWhen: "000000DC", + Retrnsmt: "00000005", + UID: "0", + Timeout: "0", + Inode: "0", + RefCount: "2", + MemoryAddress: "0000000000000000", + }, + }, + } + + m := NetTCP{} + re := regexp.MustCompile(m.regex()) + + for _, i := range expected { + have, _ := m.netTCPLine(re, i.Line) + + if !reflect.DeepEqual(i.Expected, have) { + t.Errorf("NetTCP Regex is invalid") + } + } + +} + +func TestNetTCP(t *testing.T) { + have, err := FS("fixtures").NewNetTCP() + if err != nil { + t.Fatal(err) + } + + if len(have) != 19 { + t.Errorf("incorrect number of NetTCPLine items.") + } +}