From 809b69ea4a90455445c34bbad7d8e5fea5cf3462 Mon Sep 17 00:00:00 2001 From: Ondrej Fabry Date: Tue, 17 Sep 2019 12:41:47 +0200 Subject: Optimizations for statsclient - this dramatically improves performance for stats data collection - memory allocation is now done only when stat dirs change - updating prepared stat dir does not need to allocate memory - created integration test for testing stats client - added NumWorkerThreads and VectorRatePerWorker to SystemStats - added ReduceSimpleCounterStatIndex, ReduceCombinedCounterStatIndex for aggregating specific index Change-Id: I702731a69024ab5dd0832bb5cfe2773a987359e5 Signed-off-by: Ondrej Fabry --- core/connection.go | 6 +- core/stats.go | 493 ++++++++++++++++++++++++++--------------------------- 2 files changed, 242 insertions(+), 257 deletions(-) (limited to 'core') diff --git a/core/connection.go b/core/connection.go index 8b8c7b1..6f82616 100644 --- a/core/connection.go +++ b/core/connection.go @@ -89,7 +89,6 @@ type ConnectionEvent struct { // Connection represents a shared memory connection to VPP via vppAdapter. type Connection struct { vppClient adapter.VppAPI // VPP binary API client - //statsClient adapter.StatsAPI // VPP stats API client maxAttempts int // interval for reconnect attempts recInterval time.Duration // maximum number of reconnect attempts @@ -177,7 +176,9 @@ func (c *Connection) connectVPP() error { log.Debugf("Connected to VPP") if err := c.retrieveMessageIDs(); err != nil { - c.vppClient.Disconnect() + if err := c.vppClient.Disconnect(); err != nil { + log.Debugf("disconnecting vpp client failed: %v", err) + } return fmt.Errorf("VPP is incompatible: %v", err) } @@ -192,7 +193,6 @@ func (c *Connection) Disconnect() { if c == nil { return } - if c.vppClient != nil { c.disconnectVPP() } diff --git a/core/stats.go b/core/stats.go index e935888..23b3848 100644 --- a/core/stats.go +++ b/core/stats.go @@ -1,22 +1,29 @@ package core import ( - "fmt" "path" "strings" "sync/atomic" + "time" "git.fd.io/govpp.git/adapter" "git.fd.io/govpp.git/api" ) +var ( + RetryUpdateCount = 10 + RetryUpdateDelay = time.Millisecond * 10 +) + const ( - SystemStatsPrefix = "/sys/" - SystemStats_VectorRate = SystemStatsPrefix + "vector_rate" - SystemStats_InputRate = SystemStatsPrefix + "input_rate" - SystemStats_LastUpdate = SystemStatsPrefix + "last_update" - SystemStats_LastStatsClear = SystemStatsPrefix + "last_stats_clear" - SystemStats_Heartbeat = SystemStatsPrefix + "heartbeat" + SystemStatsPrefix = "/sys/" + SystemStats_VectorRate = SystemStatsPrefix + "vector_rate" + SystemStats_NumWorkerThreads = SystemStatsPrefix + "num_worker_threads" + SystemStats_VectorRatePerWorker = SystemStatsPrefix + "vector_rate_per_worker" + SystemStats_InputRate = SystemStatsPrefix + "input_rate" + SystemStats_LastUpdate = SystemStatsPrefix + "last_update" + SystemStats_LastStatsClear = SystemStatsPrefix + "last_stats_clear" + SystemStats_Heartbeat = SystemStatsPrefix + "heartbeat" NodeStatsPrefix = "/sys/node/" NodeStats_Names = NodeStatsPrefix + "names" @@ -42,11 +49,13 @@ const ( InterfaceStats_RxMiss = InterfaceStatsPrefix + "rx-miss" InterfaceStats_RxError = InterfaceStatsPrefix + "rx-error" InterfaceStats_TxError = InterfaceStatsPrefix + "tx-error" + InterfaceStats_Mpls = InterfaceStatsPrefix + "mpls" InterfaceStats_Rx = InterfaceStatsPrefix + "rx" InterfaceStats_RxUnicast = InterfaceStatsPrefix + "rx-unicast" InterfaceStats_RxMulticast = InterfaceStatsPrefix + "rx-multicast" InterfaceStats_RxBroadcast = InterfaceStatsPrefix + "rx-broadcast" InterfaceStats_Tx = InterfaceStatsPrefix + "tx" + InterfaceStats_TxUnicast = InterfaceStatsPrefix + "tx-unicast" InterfaceStats_TxUnicastMiss = InterfaceStatsPrefix + "tx-unicast-miss" InterfaceStats_TxMulticast = InterfaceStatsPrefix + "tx-multicast" InterfaceStats_TxBroadcast = InterfaceStatsPrefix + "tx-broadcast" @@ -63,7 +72,14 @@ const ( type StatsConnection struct { statsClient adapter.StatsAPI - connected uint32 // non-zero if the adapter is connected to VPP + // connected is true if the adapter is connected to VPP + connected uint32 + + errorStatsData *adapter.StatDir + nodeStatsData *adapter.StatDir + ifaceStatsData *adapter.StatDir + sysStatsData *adapter.StatDir + bufStatsData *adapter.StatDir } func newStatsConnection(stats adapter.StatsAPI) *StatsConnection { @@ -105,7 +121,6 @@ func (c *StatsConnection) Disconnect() { if c == nil { return } - if c.statsClient != nil { c.disconnectClient() } @@ -113,301 +128,314 @@ func (c *StatsConnection) Disconnect() { func (c *StatsConnection) disconnectClient() { if atomic.CompareAndSwapUint32(&c.connected, 1, 0) { - c.statsClient.Disconnect() + if err := c.statsClient.Disconnect(); err != nil { + log.Debugf("disconnecting stats client failed: %v", err) + } } } -// GetSystemStats retrieves VPP system stats. -func (c *StatsConnection) GetSystemStats() (*api.SystemStats, error) { - stats, err := c.statsClient.DumpStats(SystemStatsPrefix) - if err != nil { - return nil, err +func (c *StatsConnection) updateStats(statDir **adapter.StatDir, patterns ...string) error { + if statDir == nil { + panic("statDir must not nil") } + try := func() error { + if (*statDir) == nil { + dir, err := c.statsClient.PrepareDir(patterns...) + if err != nil { + log.Debugln("preparing dir failed:", err) + return err + } + *statDir = dir + } else { + if err := c.statsClient.UpdateDir(*statDir); err != nil { + log.Debugln("updating dir failed:", err) + *statDir = nil + return err + } + } - sysStats := &api.SystemStats{} + return nil + } + var err error + for r := 0; r < RetryUpdateCount; r++ { + if err = try(); err == nil { + if r > 0 { + log.Debugf("retry successfull (r=%d)", r) + } + return nil + } else if err == adapter.ErrStatsDirStale || err == adapter.ErrStatsDataBusy { + // retrying + if r > 1 { + log.Debugln("sleeping for %v before next try", RetryUpdateDelay) + time.Sleep(RetryUpdateDelay) + } + } else { + // error is not retryable + break + } + } + return err +} - for _, stat := range stats { - switch stat.Name { +// UpdateSystemStats retrieves VPP system stats. +func (c *StatsConnection) GetSystemStats(sysStats *api.SystemStats) (err error) { + if err := c.updateStats(&c.sysStatsData, SystemStatsPrefix); err != nil { + return err + } + + for _, stat := range c.sysStatsData.Entries { + var val uint64 + if s, ok := stat.Data.(adapter.ScalarStat); ok { + val = uint64(s) + } + switch string(stat.Name) { case SystemStats_VectorRate: - sysStats.VectorRate = scalarStatToFloat64(stat.Data) + sysStats.VectorRate = val + case SystemStats_NumWorkerThreads: + sysStats.NumWorkerThreads = val + case SystemStats_VectorRatePerWorker: + var vals []uint64 + if ss, ok := stat.Data.(adapter.SimpleCounterStat); ok { + vals = make([]uint64, len(ss)) + for w := range ss { + vals[w] = uint64(ss[w][0]) + } + } + sysStats.VectorRatePerWorker = vals case SystemStats_InputRate: - sysStats.InputRate = scalarStatToFloat64(stat.Data) + sysStats.InputRate = val case SystemStats_LastUpdate: - sysStats.LastUpdate = scalarStatToFloat64(stat.Data) + sysStats.LastUpdate = val case SystemStats_LastStatsClear: - sysStats.LastStatsClear = scalarStatToFloat64(stat.Data) + sysStats.LastStatsClear = val case SystemStats_Heartbeat: - sysStats.Heartbeat = scalarStatToFloat64(stat.Data) + sysStats.Heartbeat = val } } - return sysStats, nil + return nil } // GetErrorStats retrieves VPP error stats. -func (c *StatsConnection) GetErrorStats(names ...string) (*api.ErrorStats, error) { - var patterns []string - if len(names) > 0 { - patterns = make([]string, len(names)) - for i, name := range names { - patterns[i] = CounterStatsPrefix + name - } - } else { - // retrieve all error counters by default - patterns = []string{CounterStatsPrefix} +func (c *StatsConnection) GetErrorStats(errorStats *api.ErrorStats) (err error) { + if err := c.updateStats(&c.errorStatsData, CounterStatsPrefix); err != nil { + return err } - stats, err := c.statsClient.DumpStats(patterns...) - if err != nil { - return nil, err + + if errorStats.Errors == nil || len(errorStats.Errors) != len(c.errorStatsData.Entries) { + errorStats.Errors = make([]api.ErrorCounter, len(c.errorStatsData.Entries)) + for i := 0; i < len(c.errorStatsData.Entries); i++ { + errorStats.Errors[i].CounterName = string(c.errorStatsData.Entries[i].Name) + } } - var errorStats = &api.ErrorStats{} - - for _, stat := range stats { - statName := strings.TrimPrefix(stat.Name, CounterStatsPrefix) - - /* TODO: deal with stats that contain '/' in node/counter name - parts := strings.Split(statName, "/") - var nodeName, counterName string - switch len(parts) { - case 2: - nodeName = parts[0] - counterName = parts[1] - case 3: - nodeName = parts[0] + parts[1] - counterName = parts[2] - }*/ - - errorStats.Errors = append(errorStats.Errors, api.ErrorCounter{ - CounterName: statName, - Value: errorStatToUint64(stat.Data), - }) + for i, stat := range c.errorStatsData.Entries { + if stat.Type != adapter.ErrorIndex { + continue + } + if errStat, ok := stat.Data.(adapter.ErrorStat); ok { + errorStats.Errors[i].Value = uint64(errStat) + } } - return errorStats, nil + return nil } -// GetNodeStats retrieves VPP per node stats. -func (c *StatsConnection) GetNodeStats() (*api.NodeStats, error) { - stats, err := c.statsClient.DumpStats(NodeStatsPrefix) - if err != nil { - return nil, err +func (c *StatsConnection) GetNodeStats(nodeStats *api.NodeStats) (err error) { + if err := c.updateStats(&c.nodeStatsData, NodeStatsPrefix); err != nil { + return err } - nodeStats := &api.NodeStats{} - - var setPerNode = func(perNode []uint64, fn func(c *api.NodeCounters, v uint64)) { - if nodeStats.Nodes == nil { - nodeStats.Nodes = make([]api.NodeCounters, len(perNode)) - for i := range perNode { + prepNodes := func(l int) { + if nodeStats.Nodes == nil || len(nodeStats.Nodes) != l { + nodeStats.Nodes = make([]api.NodeCounters, l) + for i := 0; i < l; i++ { nodeStats.Nodes[i].NodeIndex = uint32(i) } } - for i, v := range perNode { - if len(nodeStats.Nodes) <= i { - break - } - nodeCounters := nodeStats.Nodes[i] - fn(&nodeCounters, v) - nodeStats.Nodes[i] = nodeCounters + } + perNode := func(stat adapter.StatEntry, fn func(*api.NodeCounters, uint64)) { + s := stat.Data.(adapter.SimpleCounterStat) + prepNodes(len(s[0])) + for i := range nodeStats.Nodes { + val := adapter.ReduceSimpleCounterStatIndex(s, i) + fn(&nodeStats.Nodes[i], val) } } - for _, stat := range stats { - switch stat.Name { + for _, stat := range c.nodeStatsData.Entries { + switch string(stat.Name) { case NodeStats_Names: - if names, ok := stat.Data.(adapter.NameStat); !ok { - return nil, fmt.Errorf("invalid stat type for %s", stat.Name) - } else { - if nodeStats.Nodes == nil { - nodeStats.Nodes = make([]api.NodeCounters, len(names)) - for i := range names { - nodeStats.Nodes[i].NodeIndex = uint32(i) - } - } - for i, name := range names { - nodeStats.Nodes[i].NodeName = string(name) + stat := stat.Data.(adapter.NameStat) + prepNodes(len(stat)) + for i, nc := range nodeStats.Nodes { + if nc.NodeName != string(stat[i]) { + nc.NodeName = string(stat[i]) + nodeStats.Nodes[i] = nc } } case NodeStats_Clocks: - setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) { - c.Clocks = v + perNode(stat, func(node *api.NodeCounters, val uint64) { + node.Clocks = val }) case NodeStats_Vectors: - setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) { - c.Vectors = v + perNode(stat, func(node *api.NodeCounters, val uint64) { + node.Vectors = val }) case NodeStats_Calls: - setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) { - c.Calls = v + perNode(stat, func(node *api.NodeCounters, val uint64) { + node.Calls = val }) case NodeStats_Suspends: - setPerNode(reduceSimpleCounterStat(stat.Data), func(c *api.NodeCounters, v uint64) { - c.Suspends = v + perNode(stat, func(node *api.NodeCounters, val uint64) { + node.Suspends = val }) } } - return nodeStats, nil + return nil } // GetInterfaceStats retrieves VPP per interface stats. -func (c *StatsConnection) GetInterfaceStats() (*api.InterfaceStats, error) { - stats, err := c.statsClient.DumpStats(InterfaceStatsPrefix) - if err != nil { - return nil, err +func (c *StatsConnection) GetInterfaceStats(ifaceStats *api.InterfaceStats) (err error) { + if err := c.updateStats(&c.ifaceStatsData, InterfaceStatsPrefix); err != nil { + return err } - ifStats := &api.InterfaceStats{} - - var setPerIf = func(perIf []uint64, fn func(c *api.InterfaceCounters, v uint64)) { - if ifStats.Interfaces == nil { - ifStats.Interfaces = make([]api.InterfaceCounters, len(perIf)) - for i := range perIf { - ifStats.Interfaces[i].InterfaceIndex = uint32(i) + prep := func(l int) { + if ifaceStats.Interfaces == nil || len(ifaceStats.Interfaces) != l { + ifaceStats.Interfaces = make([]api.InterfaceCounters, l) + for i := 0; i < l; i++ { + ifaceStats.Interfaces[i].InterfaceIndex = uint32(i) } } - for i, v := range perIf { - if len(ifStats.Interfaces) <= i { - break - } - ifCounters := ifStats.Interfaces[i] - fn(&ifCounters, v) - ifStats.Interfaces[i] = ifCounters + } + perNode := func(stat adapter.StatEntry, fn func(*api.InterfaceCounters, uint64)) { + s := stat.Data.(adapter.SimpleCounterStat) + prep(len(s[0])) + for i := range ifaceStats.Interfaces { + val := adapter.ReduceSimpleCounterStatIndex(s, i) + fn(&ifaceStats.Interfaces[i], val) + } + } + perNodeComb := func(stat adapter.StatEntry, fn func(*api.InterfaceCounters, [2]uint64)) { + s := stat.Data.(adapter.CombinedCounterStat) + prep(len(s[0])) + for i := range ifaceStats.Interfaces { + val := adapter.ReduceCombinedCounterStatIndex(s, i) + fn(&ifaceStats.Interfaces[i], val) } } - for _, stat := range stats { - switch stat.Name { + for _, stat := range c.ifaceStatsData.Entries { + switch string(stat.Name) { case InterfaceStats_Names: - if names, ok := stat.Data.(adapter.NameStat); !ok { - return nil, fmt.Errorf("invalid stat type for %s", stat.Name) - } else { - if ifStats.Interfaces == nil { - ifStats.Interfaces = make([]api.InterfaceCounters, len(names)) - for i := range names { - ifStats.Interfaces[i].InterfaceIndex = uint32(i) - } - } - for i, name := range names { - ifStats.Interfaces[i].InterfaceName = string(name) + stat := stat.Data.(adapter.NameStat) + prep(len(stat)) + for i, nc := range ifaceStats.Interfaces { + if nc.InterfaceName != string(stat[i]) { + nc.InterfaceName = string(stat[i]) + ifaceStats.Interfaces[i] = nc } } case InterfaceStats_Drops: - setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) { - c.Drops = v + perNode(stat, func(iface *api.InterfaceCounters, val uint64) { + iface.Drops = val }) case InterfaceStats_Punt: - setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) { - c.Punts = v + perNode(stat, func(iface *api.InterfaceCounters, val uint64) { + iface.Punts = val }) case InterfaceStats_IP4: - setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) { - c.IP4 = v + perNode(stat, func(iface *api.InterfaceCounters, val uint64) { + iface.IP4 = val }) case InterfaceStats_IP6: - setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) { - c.IP6 = v + perNode(stat, func(iface *api.InterfaceCounters, val uint64) { + iface.IP6 = val }) case InterfaceStats_RxNoBuf: - setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) { - c.RxNoBuf = v + perNode(stat, func(iface *api.InterfaceCounters, val uint64) { + iface.RxNoBuf = val }) case InterfaceStats_RxMiss: - setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) { - c.RxMiss = v + perNode(stat, func(iface *api.InterfaceCounters, val uint64) { + iface.RxMiss = val }) case InterfaceStats_RxError: - setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) { - c.RxErrors = v + perNode(stat, func(iface *api.InterfaceCounters, val uint64) { + iface.RxErrors = val }) case InterfaceStats_TxError: - setPerIf(reduceSimpleCounterStat(stat.Data), func(c *api.InterfaceCounters, v uint64) { - c.TxErrors = v + perNode(stat, func(iface *api.InterfaceCounters, val uint64) { + iface.TxErrors = val }) - case InterfaceStats_Rx: - per := reduceCombinedCounterStat(stat.Data) - setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) { - c.RxPackets = v + case InterfaceStats_Mpls: + perNode(stat, func(iface *api.InterfaceCounters, val uint64) { + iface.Mpls = val }) - setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) { - c.RxBytes = v + case InterfaceStats_Rx: + perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) { + iface.Rx.Packets = val[0] + iface.Rx.Bytes = val[1] }) case InterfaceStats_RxUnicast: - per := reduceCombinedCounterStat(stat.Data) - setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) { - c.RxUnicast[0] = v - }) - setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) { - c.RxUnicast[1] = v + perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) { + iface.RxUnicast.Packets = val[0] + iface.RxUnicast.Bytes = val[1] }) case InterfaceStats_RxMulticast: - per := reduceCombinedCounterStat(stat.Data) - setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) { - c.RxMulticast[0] = v - }) - setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) { - c.RxMulticast[1] = v + perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) { + iface.RxMulticast.Packets = val[0] + iface.RxMulticast.Bytes = val[1] }) case InterfaceStats_RxBroadcast: - per := reduceCombinedCounterStat(stat.Data) - setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) { - c.RxBroadcast[0] = v - }) - setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) { - c.RxBroadcast[1] = v + perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) { + iface.RxBroadcast.Packets = val[0] + iface.RxBroadcast.Bytes = val[1] }) case InterfaceStats_Tx: - per := reduceCombinedCounterStat(stat.Data) - setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) { - c.TxPackets = v - }) - setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) { - c.TxBytes = v + perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) { + iface.Tx.Packets = val[0] + iface.Tx.Bytes = val[1] }) case InterfaceStats_TxUnicastMiss: - per := reduceCombinedCounterStat(stat.Data) - setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) { - c.TxUnicastMiss[0] = v - }) - setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) { - c.TxUnicastMiss[1] = v + // tx-unicast-miss was a spelling mistake in older versions + // + fallthrough + case InterfaceStats_TxUnicast: + perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) { + iface.TxUnicast.Packets = val[0] + iface.TxUnicast.Bytes = val[1] }) case InterfaceStats_TxMulticast: - per := reduceCombinedCounterStat(stat.Data) - setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) { - c.TxMulticast[0] = v - }) - setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) { - c.TxMulticast[1] = v + perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) { + iface.TxMulticast.Packets = val[0] + iface.TxMulticast.Bytes = val[1] }) case InterfaceStats_TxBroadcast: - per := reduceCombinedCounterStat(stat.Data) - setPerIf(per[0], func(c *api.InterfaceCounters, v uint64) { - c.TxBroadcast[0] = v - }) - setPerIf(per[1], func(c *api.InterfaceCounters, v uint64) { - c.TxBroadcast[1] = v + perNodeComb(stat, func(iface *api.InterfaceCounters, val [2]uint64) { + iface.TxBroadcast.Packets = val[0] + iface.TxBroadcast.Bytes = val[1] }) } } - return ifStats, nil + return nil } // GetBufferStats retrieves VPP buffer pools stats. -func (c *StatsConnection) GetBufferStats() (*api.BufferStats, error) { - stats, err := c.statsClient.DumpStats(BufferStatsPrefix) - if err != nil { - return nil, err +func (c *StatsConnection) GetBufferStats(bufStats *api.BufferStats) (err error) { + if err := c.updateStats(&c.bufStatsData, BufferStatsPrefix); err != nil { + return err } - bufStats := &api.BufferStats{ - Buffer: map[string]api.BufferPool{}, + if bufStats.Buffer == nil { + bufStats.Buffer = make(map[string]api.BufferPool) } - for _, stat := range stats { - d, f := path.Split(stat.Name) + for _, stat := range c.bufStatsData.Entries { + d, f := path.Split(string(stat.Name)) d = strings.TrimSuffix(d, "/") name := strings.TrimPrefix(d, BufferStatsPrefix) @@ -416,65 +444,22 @@ func (c *StatsConnection) GetBufferStats() (*api.BufferStats, error) { b.PoolName = name } + var val float64 + s, ok := stat.Data.(adapter.ScalarStat) + if ok { + val = float64(s) + } switch f { case BufferStats_Cached: - b.Cached = scalarStatToFloat64(stat.Data) + b.Cached = val case BufferStats_Used: - b.Used = scalarStatToFloat64(stat.Data) + b.Used = val case BufferStats_Available: - b.Available = scalarStatToFloat64(stat.Data) + b.Available = val } bufStats.Buffer[name] = b } - return bufStats, nil -} - -func scalarStatToFloat64(stat adapter.Stat) float64 { - if s, ok := stat.(adapter.ScalarStat); ok { - return float64(s) - } - return 0 -} - -func errorStatToUint64(stat adapter.Stat) uint64 { - if s, ok := stat.(adapter.ErrorStat); ok { - return uint64(s) - } - return 0 -} - -func reduceSimpleCounterStat(stat adapter.Stat) []uint64 { - if s, ok := stat.(adapter.SimpleCounterStat); ok { - if len(s) == 0 { - return []uint64{} - } - var per = make([]uint64, len(s[0])) - for _, w := range s { - for i, n := range w { - per[i] += uint64(n) - } - } - return per - } return nil } - -func reduceCombinedCounterStat(stat adapter.Stat) [2][]uint64 { - if s, ok := stat.(adapter.CombinedCounterStat); ok { - if len(s) == 0 { - return [2][]uint64{{}, {}} - } - var perPackets = make([]uint64, len(s[0])) - var perBytes = make([]uint64, len(s[0])) - for _, w := range s { - for i, n := range w { - perPackets[i] += uint64(n.Packets) - perBytes[i] += uint64(n.Bytes) - } - } - return [2][]uint64{perPackets, perBytes} - } - return [2][]uint64{} -} -- cgit 1.2.3-korg