From c380ee6064379258768fdfe4e9d4ad9138980ec0 Mon Sep 17 00:00:00 2001 From: Vladimir Lavor Date: Wed, 17 Mar 2021 12:27:10 +0100 Subject: [PATCH] statsclient: allow index as pattern * ListStats() returns stats identifiers containing the stat name and index * New method PrepareDirOnIndex(indexes...). Instead of the name filter it does not browse through all available indexes * Stats example shows how to get the last epoch value (added "e" or "epoch" command) Change-Id: Ibb15090fb0dfdb7f9b0ecf8ac07a5eb9a9ace8f8 Signed-off-by: Vladimir Lavor --- adapter/mock/mock_stats_adapter.go | 13 +- adapter/stats_api.go | 23 +-- adapter/statsclient/stat_segment_api.go | 27 ++-- adapter/statsclient/statsclient.go | 237 +++++++++++++++++-------------- adapter/statsclient/statseg_v1.go | 68 ++++----- adapter/statsclient/statseg_v2.go | 38 ++--- adapter/vppapiclient/stat_client.go | 18 ++- adapter/vppapiclient/stat_client_stub.go | 6 +- examples/stats-client/stats_api.go | 75 ++++++++-- 9 files changed, 307 insertions(+), 198 deletions(-) diff --git a/adapter/mock/mock_stats_adapter.go b/adapter/mock/mock_stats_adapter.go index 55b1831..08d18d4 100644 --- a/adapter/mock/mock_stats_adapter.go +++ b/adapter/mock/mock_stats_adapter.go @@ -46,10 +46,13 @@ func (a *StatsAdapter) Disconnect() error { } // ListStats mocks name listing for all stats. -func (a *StatsAdapter) ListStats(patterns ...string) ([]string, error) { - var statNames []string +func (a *StatsAdapter) ListStats(patterns ...string) ([]adapter.StatIdentifier, error) { + var statNames []adapter.StatIdentifier for _, stat := range a.entries { - statNames = append(statNames, string(stat.Name)) + statNames = append(statNames, adapter.StatIdentifier{ + Name: stat.Name, + Index: stat.Index, + }) } return statNames, nil } @@ -63,6 +66,10 @@ func (a *StatsAdapter) PrepareDir(prefixes ...string) (*adapter.StatDir, error) return a.dir, nil } +func (a *StatsAdapter) PrepareDirOnIndex(indexes ...uint32) (*adapter.StatDir, error) { + return a.dir, nil +} + func (a *StatsAdapter) UpdateDir(dir *adapter.StatDir) error { *dir = *a.dir return nil diff --git a/adapter/stats_api.go b/adapter/stats_api.go index d15dee8..5b19173 100644 --- a/adapter/stats_api.go +++ b/adapter/stats_api.go @@ -38,13 +38,15 @@ type StatsAPI interface { // Disconnect terminates client connection. Disconnect() error - // ListStats lists names for stats matching patterns. - ListStats(patterns ...string) (names []string, err error) + // ListStats lists indexed names for stats matching patterns. + ListStats(patterns ...string) (indexes []StatIdentifier, err error) // DumpStats dumps all stat entries. DumpStats(patterns ...string) (entries []StatEntry, err error) // PrepareDir prepares new stat dir for entries that match any of prefixes. PrepareDir(patterns ...string) (*StatDir, error) + // PrepareDirOnIndex prepares new stat dir for entries that match any of indexes. + PrepareDirOnIndex(indexes ...uint32) (*StatDir, error) // UpdateDir updates stat dir and all of their entries. UpdateDir(dir *StatDir) error } @@ -84,14 +86,19 @@ func (d StatType) String() string { // StatDir defines directory of stats entries created by PrepareDir. type StatDir struct { Epoch int64 - Indexes []uint32 Entries []StatEntry } +// StatIdentifier holds a stat entry name and index +type StatIdentifier struct { + Index uint32 + Name []byte +} + // StatEntry represents single stat entry. The type of stat stored in Data // is defined by Type. type StatEntry struct { - Name []byte + StatIdentifier Type StatType Data Stat } @@ -103,11 +110,11 @@ type Counter uint64 type CombinedCounter [2]uint64 func (s CombinedCounter) Packets() uint64 { - return uint64(s[0]) + return s[0] } func (s CombinedCounter) Bytes() uint64 { - return uint64(s[1]) + return s[1] } // Name represents string value stored under name vector. @@ -228,8 +235,8 @@ func ReduceSimpleCounterStatIndex(s SimpleCounterStat, i int) uint64 { func ReduceCombinedCounterStatIndex(s CombinedCounterStat, i int) [2]uint64 { var val [2]uint64 for _, w := range s { - val[0] += uint64(w[i][0]) - val[1] += uint64(w[i][1]) + val[0] += w[i][0] + val[1] += w[i][1] } return val } diff --git a/adapter/statsclient/stat_segment_api.go b/adapter/statsclient/stat_segment_api.go index 0ca0c8b..23755a5 100644 --- a/adapter/statsclient/stat_segment_api.go +++ b/adapter/statsclient/stat_segment_api.go @@ -48,15 +48,18 @@ const ( statDirEmpty = 6 ) -type statDirectoryType int32 - -type statDirectoryName []byte +type ( + dirVector unsafe.Pointer + dirSegment unsafe.Pointer + dirName []byte + dirType int32 +) // statSegment represents common API for every stats API version type statSegment interface { // GetDirectoryVector returns pointer to memory where the beginning // of the data directory is located. - GetDirectoryVector() unsafe.Pointer + GetDirectoryVector() dirVector // GetStatDirOnIndex accepts directory vector and particular index. // Returns pointer to the beginning of the segment. Also the directory @@ -65,7 +68,7 @@ type statSegment interface { // // Note that if the index is equal to 0, the result pointer points to // the same memory address as the argument. - GetStatDirOnIndex(directory unsafe.Pointer, index uint32) (unsafe.Pointer, statDirectoryName, statDirectoryType) + GetStatDirOnIndex(v dirVector, index uint32) (dirSegment, dirName, dirType) // GetEpoch re-loads stats header and returns current epoch //and 'inProgress' value @@ -73,11 +76,11 @@ type statSegment interface { // CopyEntryData accepts pointer to a directory segment and returns adapter.Stat // based on directory type populated with data - CopyEntryData(segment unsafe.Pointer) adapter.Stat + CopyEntryData(segment dirSegment) adapter.Stat // UpdateEntryData accepts pointer to a directory segment with data, and stat // segment to update - UpdateEntryData(segment unsafe.Pointer, s *adapter.Stat) error + UpdateEntryData(segment dirSegment, s *adapter.Stat) error } // vecHeader represents a vector header @@ -86,7 +89,7 @@ type vecHeader struct { vectorData [0]uint8 } -func (t statDirectoryType) String() string { +func (t dirType) String() string { return adapter.StatType(t).String() } @@ -102,12 +105,12 @@ func getVersion(data []byte) uint64 { return version.value } -func vectorLen(v unsafe.Pointer) unsafe.Pointer { +func vectorLen(v dirVector) dirVector { vec := *(*vecHeader)(unsafe.Pointer(uintptr(v) - unsafe.Sizeof(uint64(0)))) - return unsafe.Pointer(&vec.length) + return dirVector(&vec.length) } //go:nosplit -func statSegPointer(p unsafe.Pointer, offset uintptr) unsafe.Pointer { - return unsafe.Pointer(uintptr(p) + offset) +func statSegPointer(v dirVector, offset uintptr) dirVector { + return dirVector(uintptr(v) + offset) } diff --git a/adapter/statsclient/statsclient.go b/adapter/statsclient/statsclient.go index b2d91db..16af16a 100644 --- a/adapter/statsclient/statsclient.go +++ b/adapter/statsclient/statsclient.go @@ -157,7 +157,7 @@ func (sc *StatsClient) Disconnect() error { return nil } -func (sc *StatsClient) ListStats(patterns ...string) ([]string, error) { +func (sc *StatsClient) ListStats(patterns ...string) (entries []adapter.StatIdentifier, err error) { if !sc.isConnected() { return nil, adapter.ErrStatsDisconnected } @@ -166,76 +166,35 @@ func (sc *StatsClient) ListStats(patterns ...string) ([]string, error) { return nil, adapter.ErrStatsAccessFailed } - indexes, err := sc.listIndexes(patterns...) + entries, err = sc.getIdentifierEntries(patterns...) if err != nil { return nil, err } - dirVector := sc.GetDirectoryVector() - if dirVector == nil { - return nil, fmt.Errorf("failed to list stats: %v", err) - } - vecLen := *(*uint32)(vectorLen(dirVector)) - - var names []string - for _, index := range indexes { - if index >= vecLen { - return nil, fmt.Errorf("stat entry index %d out of dir vector len (%d)", index, vecLen) - } - _, dirName, _ := sc.GetStatDirOnIndex(dirVector, index) - names = append(names, string(dirName)) - } - if !sc.accessEnd(accessEpoch) { return nil, adapter.ErrStatsDataBusy } - - return names, nil + return entries, nil } func (sc *StatsClient) DumpStats(patterns ...string) (entries []adapter.StatEntry, err error) { if !sc.isConnected() { return nil, adapter.ErrStatsDisconnected } + accessEpoch := sc.accessStart() if accessEpoch == 0 { return nil, adapter.ErrStatsAccessFailed } - indexes, err := sc.listIndexes(patterns...) + entries, err = sc.getStatEntries(patterns...) if err != nil { return nil, err } - dirVector := sc.GetDirectoryVector() - if dirVector == nil { - return nil, err - } - dirLen := *(*uint32)(vectorLen(dirVector)) - - debugf("dumping entries for %d indexes", len(indexes)) - - entries = make([]adapter.StatEntry, 0, len(indexes)) - for _, index := range indexes { - if index >= dirLen { - return nil, fmt.Errorf("stat entry index %d out of dir vector length (%d)", index, dirLen) - } - dirPtr, dirName, dirType := sc.GetStatDirOnIndex(dirVector, index) - if len(dirName) == 0 { - continue - } - entry := adapter.StatEntry{ - Name: append([]byte(nil), dirName...), - Type: adapter.StatType(dirType), - Data: sc.CopyEntryData(dirPtr), - } - entries = append(entries, entry) - } - if !sc.accessEnd(accessEpoch) { return nil, adapter.ErrStatsDataBusy } - return entries, nil } @@ -243,49 +202,55 @@ func (sc *StatsClient) PrepareDir(patterns ...string) (*adapter.StatDir, error) if !sc.isConnected() { return nil, adapter.ErrStatsDisconnected } - dir := new(adapter.StatDir) accessEpoch := sc.accessStart() if accessEpoch == 0 { return nil, adapter.ErrStatsAccessFailed } - indexes, err := sc.listIndexes(patterns...) + entries, err := sc.getStatEntries(patterns...) if err != nil { return nil, err } - dir.Indexes = indexes - dirVector := sc.GetDirectoryVector() - if dirVector == nil { - return nil, err + if !sc.accessEnd(accessEpoch) { + return nil, adapter.ErrStatsDataBusy } - dirLen := *(*uint32)(vectorLen(dirVector)) - debugf("dumping entries for %d indexes", len(indexes)) + dir := &adapter.StatDir{ + Epoch: accessEpoch, + Entries: entries, + } - entries := make([]adapter.StatEntry, 0, len(indexes)) - for _, index := range indexes { - if index >= dirLen { - return nil, fmt.Errorf("stat entry index %d out of dir vector length (%d)", index, dirLen) - } - dirPtr, dirName, dirType := sc.GetStatDirOnIndex(dirVector, index) - if len(dirName) == 0 { - continue - } - entry := adapter.StatEntry{ - Name: append([]byte(nil), dirName...), - Type: adapter.StatType(dirType), - Data: sc.CopyEntryData(dirPtr), - } - entries = append(entries, entry) + return dir, nil +} + +func (sc *StatsClient) PrepareDirOnIndex(indexes ...uint32) (*adapter.StatDir, error) { + if !sc.isConnected() { + return nil, adapter.ErrStatsDisconnected + } + + accessEpoch := sc.accessStart() + if accessEpoch == 0 { + return nil, adapter.ErrStatsAccessFailed + } + vector := sc.GetDirectoryVector() + if vector == nil { + return nil, fmt.Errorf("failed to prepare dir on index: directory vector is nil") + } + entries, err := sc.getStatEntriesOnIndex(vector, indexes...) + if err != nil { + return nil, err } - dir.Entries = entries if !sc.accessEnd(accessEpoch) { return nil, adapter.ErrStatsDataBusy } - dir.Epoch = accessEpoch + + dir := &adapter.StatDir{ + Epoch: accessEpoch, + Entries: entries, + } return dir, nil } @@ -304,34 +269,18 @@ func (sc *StatsClient) UpdateDir(dir *adapter.StatDir) (err error) { if accessEpoch == 0 { return adapter.ErrStatsAccessFailed } - dirVector := sc.GetDirectoryVector() if dirVector == nil { return err } - for i, index := range dir.Indexes { - statSegDir, dirName, dirType := sc.GetStatDirOnIndex(dirVector, index) - if len(dirName) == 0 { - continue - } - entry := &dir.Entries[i] - if !bytes.Equal(dirName, entry.Name) { - continue - } - if adapter.StatType(dirType) != entry.Type { - continue - } - if entry.Data == nil { - continue - } - if err := sc.UpdateEntryData(statSegDir, &entry.Data); err != nil { - return fmt.Errorf("updating stat data for entry %s failed: %v", dirName, err) + for i := 0; i < len(dir.Entries); i++ { + if err := sc.updateStatOnIndex(&dir.Entries[i], dirVector); err != nil { + return err } } if !sc.accessEnd(accessEpoch) { return adapter.ErrStatsDataBusy } - return nil } @@ -518,10 +467,79 @@ func (sc *StatsClient) accessEnd(accessEpoch int64) bool { return true } +// getStatEntries retrieves all stats matching desired patterns, or all stats if no pattern is provided. +func (sc *StatsClient) getStatEntries(patterns ...string) (entries []adapter.StatEntry, err error) { + vector := sc.GetDirectoryVector() + if vector == nil { + return nil, fmt.Errorf("failed to get stat entries: directory vector is nil") + } + indexes, err := sc.listIndexes(vector, patterns...) + if err != nil { + return nil, err + } + return sc.getStatEntriesOnIndex(vector, indexes...) +} + +// getIdentifierEntries retrieves all identifiers matching desired patterns, or all identifiers +// if no pattern is provided. +func (sc *StatsClient) getIdentifierEntries(patterns ...string) (identifiers []adapter.StatIdentifier, err error) { + vector := sc.GetDirectoryVector() + if vector == nil { + return nil, fmt.Errorf("failed to get identifier entries: directory vector is nil") + } + indexes, err := sc.listIndexes(vector, patterns...) + if err != nil { + return nil, err + } + return sc.getIdentifierEntriesOnIndex(vector, indexes...) +} + +// getStatEntriesOnIndex retrieves stats on indexes, or all stats if indexes are not defined. +func (sc *StatsClient) getStatEntriesOnIndex(vector dirVector, indexes ...uint32) (entries []adapter.StatEntry, err error) { + dirLen := *(*uint32)(vectorLen(vector)) + for _, index := range indexes { + if index >= dirLen { + return nil, fmt.Errorf("stat entry index %d out of dir vector length (%d)", index, dirLen) + } + dirPtr, dirName, dirType := sc.GetStatDirOnIndex(vector, index) + if len(dirName) == 0 { + return + } + entries = append(entries, adapter.StatEntry{ + StatIdentifier: adapter.StatIdentifier{ + Index: index, + Name: dirName, + }, + Type: adapter.StatType(dirType), + Data: sc.CopyEntryData(dirPtr), + }) + } + return entries, nil +} + +// getIdentifierEntriesOnIndex retrieves identifiers on indexes, or all identifiers if indexes are not defined. +func (sc *StatsClient) getIdentifierEntriesOnIndex(vector dirVector, indexes ...uint32) (identifiers []adapter.StatIdentifier, err error) { + dirLen := *(*uint32)(vectorLen(vector)) + for _, index := range indexes { + if index >= dirLen { + return nil, fmt.Errorf("stat entry index %d out of dir vector length (%d)", index, dirLen) + } + _, dirName, _ := sc.GetStatDirOnIndex(vector, index) + if len(dirName) == 0 { + return + } + identifiers = append(identifiers, adapter.StatIdentifier{ + Index: index, + Name: dirName, + }) + } + return identifiers, nil +} + // listIndexes lists indexes for all stat entries that match any of the regex patterns. -func (sc *StatsClient) listIndexes(patterns ...string) (indexes []uint32, err error) { +func (sc *StatsClient) listIndexes(vector dirVector, patterns ...string) (indexes []uint32, err error) { if len(patterns) == 0 { - return sc.listIndexesFunc(nil) + return sc.listIndexesFunc(vector, nil) } var regexes = make([]*regexp.Regexp, len(patterns)) for i, pattern := range patterns { @@ -531,7 +549,7 @@ func (sc *StatsClient) listIndexes(patterns ...string) (indexes []uint32, err er } regexes[i] = r } - nameMatches := func(name []byte) bool { + nameMatches := func(name dirName) bool { for _, r := range regexes { if r.Match(name) { return true @@ -539,26 +557,20 @@ func (sc *StatsClient) listIndexes(patterns ...string) (indexes []uint32, err er } return false } - return sc.listIndexesFunc(nameMatches) + return sc.listIndexesFunc(vector, nameMatches) } // listIndexesFunc lists stats indexes. The optional function // argument filters returned values or returns all if empty -func (sc *StatsClient) listIndexesFunc(f func(name []byte) bool) (indexes []uint32, err error) { +func (sc *StatsClient) listIndexesFunc(vector dirVector, f func(name dirName) bool) (indexes []uint32, err error) { if f == nil { // there is around ~3157 stats, so to avoid too many allocations // we set capacity to 3200 when listing all stats indexes = make([]uint32, 0, 3200) } - - dirVector := sc.GetDirectoryVector() - if dirVector == nil { - return nil, err - } - vecLen := *(*uint32)(vectorLen(dirVector)) - + vecLen := *(*uint32)(vectorLen(vector)) for i := uint32(0); i < vecLen; i++ { - _, dirName, _ := sc.GetStatDirOnIndex(dirVector, i) + _, dirName, _ := sc.GetStatDirOnIndex(vector, i) if f != nil { if len(dirName) == 0 || !f(dirName) { continue @@ -573,3 +585,22 @@ func (sc *StatsClient) listIndexesFunc(f func(name []byte) bool) (indexes []uint func (sc *StatsClient) isConnected() bool { return atomic.LoadUint32(&sc.connected) == 1 } + +// updateStatOnIndex refreshes the entry data. +func (sc *StatsClient) updateStatOnIndex(entry *adapter.StatEntry, vector dirVector) (err error) { + dirLen := *(*uint32)(vectorLen(vector)) + if entry.Index >= dirLen { + return fmt.Errorf("stat entry index %d out of dir vector length (%d)", entry.Index, dirLen) + } + dirPtr, dirName, dirType := sc.GetStatDirOnIndex(vector, entry.Index) + if len(dirName) == 0 || + !bytes.Equal(dirName, entry.Name) || + adapter.StatType(dirType) != entry.Type || + entry.Data == nil { + return nil + } + if err := sc.UpdateEntryData(dirPtr, &entry.Data); err != nil { + err = fmt.Errorf("updating stat data for entry %s failed: %v", dirName, err) + } + return +} diff --git a/adapter/statsclient/statseg_v1.go b/adapter/statsclient/statseg_v1.go index 38f51bd..e9e8331 100644 --- a/adapter/statsclient/statseg_v1.go +++ b/adapter/statsclient/statseg_v1.go @@ -37,7 +37,7 @@ type sharedHeaderV1 struct { } type statSegDirectoryEntryV1 struct { - directoryType statDirectoryType + directoryType dirType // unionData can represent: // - offset // - index @@ -66,17 +66,17 @@ func (ss *statSegmentV1) loadSharedHeader(b []byte) (header sharedHeaderV1) { } } -func (ss *statSegmentV1) GetDirectoryVector() unsafe.Pointer { +func (ss *statSegmentV1) GetDirectoryVector() dirVector { dirOffset, _, _ := ss.getOffsets() - return unsafe.Pointer(&ss.sharedHeader[dirOffset]) + return dirVector(&ss.sharedHeader[dirOffset]) } -func (ss *statSegmentV1) GetErrorVector() (unsafe.Pointer, error) { +func (ss *statSegmentV1) getErrorVector() (unsafe.Pointer, error) { return nil, fmt.Errorf("error vector is not defined for stats API v1") } -func (ss *statSegmentV1) GetStatDirOnIndex(p unsafe.Pointer, index uint32) (unsafe.Pointer, statDirectoryName, statDirectoryType) { - statSegDir := unsafe.Pointer(uintptr(p) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntryV1{})) +func (ss *statSegmentV1) GetStatDirOnIndex(v dirVector, index uint32) (dirSegment, dirName, dirType) { + statSegDir := dirSegment(uintptr(v) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntryV1{})) dir := (*statSegDirectoryEntryV1)(statSegDir) var name []byte for n := 0; n < len(dir.name); n++ { @@ -85,7 +85,7 @@ func (ss *statSegmentV1) GetStatDirOnIndex(p unsafe.Pointer, index uint32) (unsa break } } - return statSegDir, name, dir.directoryType + return statSegDir, dirName(name), dir.directoryType } func (ss *statSegmentV1) GetEpoch() (int64, bool) { @@ -93,8 +93,8 @@ func (ss *statSegmentV1) GetEpoch() (int64, bool) { return sh.epoch, sh.inProgress != 0 } -func (ss *statSegmentV1) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { - dirEntry := (*statSegDirectoryEntryV1)(statSegDir) +func (ss *statSegmentV1) CopyEntryData(segment dirSegment) adapter.Stat { + dirEntry := (*statSegDirectoryEntryV1)(segment) dirType := adapter.StatType(dirEntry.directoryType) switch dirType { @@ -111,7 +111,7 @@ func (ss *statSegmentV1) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { } _, errOffset, _ := ss.getOffsets() - offsetVector := unsafe.Pointer(&ss.sharedHeader[errOffset]) + offsetVector := dirVector(&ss.sharedHeader[errOffset]) var errData []adapter.Counter @@ -120,7 +120,7 @@ func (ss *statSegmentV1) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0)))) offset := uintptr(cb) + uintptr(dirEntry.unionData)*unsafe.Sizeof(adapter.Counter(0)) debugf("error index, cb: %d, offset: %d", cb, offset) - val := *(*adapter.Counter)(statSegPointer(unsafe.Pointer(&ss.sharedHeader[0]), offset)) + val := *(*adapter.Counter)(statSegPointer(dirVector(&ss.sharedHeader[0]), offset)) errData = append(errData, val) } return adapter.ErrorStat(errData) @@ -134,13 +134,13 @@ func (ss *statSegmentV1) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { break } - vecLen := *(*uint32)(vectorLen(unsafe.Pointer(&ss.sharedHeader[dirEntry.unionData]))) - offsetVector := statSegPointer(unsafe.Pointer(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector)) + vecLen := *(*uint32)(vectorLen(dirVector(&ss.sharedHeader[dirEntry.unionData]))) + offsetVector := statSegPointer(dirVector(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector)) data := make([][]adapter.Counter, vecLen) for i := uint32(0); i < vecLen; i++ { cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0)))) - counterVec := unsafe.Pointer(&ss.sharedHeader[uintptr(cb)]) + counterVec := dirVector(&ss.sharedHeader[uintptr(cb)]) vecLen2 := *(*uint32)(vectorLen(counterVec)) data[i] = make([]adapter.Counter, vecLen2) for j := uint32(0); j < vecLen2; j++ { @@ -160,13 +160,13 @@ func (ss *statSegmentV1) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { break } - vecLen := *(*uint32)(vectorLen(unsafe.Pointer(&ss.sharedHeader[dirEntry.unionData]))) - offsetVector := statSegPointer(unsafe.Pointer(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector)) + vecLen := *(*uint32)(vectorLen(dirVector(&ss.sharedHeader[dirEntry.unionData]))) + offsetVector := statSegPointer(dirVector(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector)) data := make([][]adapter.CombinedCounter, vecLen) for i := uint32(0); i < vecLen; i++ { cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0)))) - counterVec := unsafe.Pointer(&ss.sharedHeader[uintptr(cb)]) + counterVec := dirVector(&ss.sharedHeader[uintptr(cb)]) vecLen2 := *(*uint32)(vectorLen(counterVec)) data[i] = make([]adapter.CombinedCounter, vecLen2) for j := uint32(0); j < vecLen2; j++ { @@ -186,8 +186,8 @@ func (ss *statSegmentV1) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { break } - vecLen := *(*uint32)(vectorLen(unsafe.Pointer(&ss.sharedHeader[dirEntry.unionData]))) - offsetVector := statSegPointer(unsafe.Pointer(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector)) + vecLen := *(*uint32)(vectorLen(dirVector(&ss.sharedHeader[dirEntry.unionData]))) + offsetVector := statSegPointer(dirVector(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector)) data := make([]adapter.Name, vecLen) for i := uint32(0); i < vecLen; i++ { @@ -196,7 +196,7 @@ func (ss *statSegmentV1) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { debugf("name vector out of range for %s (%v)", dirEntry.name, i) continue } - nameVec := unsafe.Pointer(&ss.sharedHeader[cb]) + nameVec := dirVector(&ss.sharedHeader[cb]) vecLen2 := *(*uint32)(vectorLen(nameVec)) nameStr := make([]byte, 0, vecLen2) @@ -221,8 +221,8 @@ func (ss *statSegmentV1) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { return nil } -func (ss *statSegmentV1) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapter.Stat) error { - dirEntry := (*statSegDirectoryEntryV1)(statSegDir) +func (ss *statSegmentV1) UpdateEntryData(segment dirSegment, stat *adapter.Stat) error { + dirEntry := (*statSegDirectoryEntryV1)(segment) switch (*stat).(type) { case adapter.ScalarStat: *stat = adapter.ScalarStat(dirEntry.unionData) @@ -237,15 +237,15 @@ func (ss *statSegmentV1) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte } _, errOffset, _ := ss.getOffsets() - offsetVector := unsafe.Pointer(&ss.sharedHeader[errOffset]) + offsetVector := dirVector(&ss.sharedHeader[errOffset]) var errData []adapter.Counter - vecLen := *(*uint32)(vectorLen(unsafe.Pointer(&ss.sharedHeader[errOffset]))) + vecLen := *(*uint32)(vectorLen(dirVector(&ss.sharedHeader[errOffset]))) for i := uint32(0); i < vecLen; i++ { cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0)))) offset := uintptr(cb) + uintptr(dirEntry.unionData)*unsafe.Sizeof(adapter.Counter(0)) - val := *(*adapter.Counter)(statSegPointer(unsafe.Pointer(&ss.sharedHeader[0]), offset)) + val := *(*adapter.Counter)(statSegPointer(dirVector(&ss.sharedHeader[0]), offset)) errData = append(errData, val) } *stat = adapter.ErrorStat(errData) @@ -259,8 +259,8 @@ func (ss *statSegmentV1) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte break } - vecLen := *(*uint32)(vectorLen(unsafe.Pointer(&ss.sharedHeader[dirEntry.unionData]))) - offsetVector := statSegPointer(unsafe.Pointer(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector)) + vecLen := *(*uint32)(vectorLen(dirVector(&ss.sharedHeader[dirEntry.unionData]))) + offsetVector := statSegPointer(dirVector(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector)) data := (*stat).(adapter.SimpleCounterStat) if uint32(len(data)) != vecLen { @@ -268,7 +268,7 @@ func (ss *statSegmentV1) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte } for i := uint32(0); i < vecLen; i++ { cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0)))) - counterVec := unsafe.Pointer(&ss.sharedHeader[uintptr(cb)]) + counterVec := dirVector(&ss.sharedHeader[uintptr(cb)]) vecLen2 := *(*uint32)(vectorLen(counterVec)) simpleData := data[i] if uint32(len(simpleData)) != vecLen2 { @@ -290,8 +290,8 @@ func (ss *statSegmentV1) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte break } - vecLen := *(*uint32)(vectorLen(unsafe.Pointer(&ss.sharedHeader[dirEntry.unionData]))) - offsetVector := statSegPointer(unsafe.Pointer(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector)) + vecLen := *(*uint32)(vectorLen(dirVector(&ss.sharedHeader[dirEntry.unionData]))) + offsetVector := statSegPointer(dirVector(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector)) data := (*stat).(adapter.CombinedCounterStat) if uint32(len(data)) != vecLen { @@ -299,7 +299,7 @@ func (ss *statSegmentV1) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte } for i := uint32(0); i < vecLen; i++ { cb := *(*uint64)(statSegPointer(offsetVector, uintptr(i)*unsafe.Sizeof(uint64(0)))) - counterVec := unsafe.Pointer(&ss.sharedHeader[uintptr(cb)]) + counterVec := dirVector(&ss.sharedHeader[uintptr(cb)]) vecLen2 := *(*uint32)(vectorLen(counterVec)) combData := data[i] if uint32(len(combData)) != vecLen2 { @@ -321,8 +321,8 @@ func (ss *statSegmentV1) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte break } - vecLen := *(*uint32)(vectorLen(unsafe.Pointer(&ss.sharedHeader[dirEntry.unionData]))) - offsetVector := statSegPointer(unsafe.Pointer(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector)) + vecLen := *(*uint32)(vectorLen(dirVector(&ss.sharedHeader[dirEntry.unionData]))) + offsetVector := statSegPointer(dirVector(&ss.sharedHeader[0]), uintptr(dirEntry.offsetVector)) data := (*stat).(adapter.NameStat) if uint32(len(data)) != vecLen { @@ -333,7 +333,7 @@ func (ss *statSegmentV1) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte if cb == 0 { continue } - nameVec := unsafe.Pointer(&ss.sharedHeader[cb]) + nameVec := dirVector(&ss.sharedHeader[cb]) vecLen2 := *(*uint32)(vectorLen(nameVec)) nameData := data[i] diff --git a/adapter/statsclient/statseg_v2.go b/adapter/statsclient/statseg_v2.go index 08467c1..10bc5f5 100644 --- a/adapter/statsclient/statseg_v2.go +++ b/adapter/statsclient/statseg_v2.go @@ -36,7 +36,7 @@ type sharedHeaderV2 struct { } type statSegDirectoryEntryV2 struct { - directoryType statDirectoryType + directoryType dirType // unionData can represent: // - index // - value @@ -64,13 +64,13 @@ func (ss *statSegmentV2) loadSharedHeader(b []byte) (header sharedHeaderV2) { } } -func (ss *statSegmentV2) GetDirectoryVector() unsafe.Pointer { +func (ss *statSegmentV2) GetDirectoryVector() dirVector { header := ss.loadSharedHeader(ss.sharedHeader) - return ss.adjust(unsafe.Pointer(&header.dirVector)) + return ss.adjust(dirVector(&header.dirVector)) } -func (ss *statSegmentV2) GetStatDirOnIndex(p unsafe.Pointer, index uint32) (unsafe.Pointer, statDirectoryName, statDirectoryType) { - statSegDir := unsafe.Pointer(uintptr(p) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntryV2{})) +func (ss *statSegmentV2) GetStatDirOnIndex(v dirVector, index uint32) (dirSegment, dirName, dirType) { + statSegDir := dirSegment(uintptr(v) + uintptr(index)*unsafe.Sizeof(statSegDirectoryEntryV2{})) dir := (*statSegDirectoryEntryV2)(statSegDir) var name []byte for n := 0; n < len(dir.name); n++ { @@ -87,8 +87,8 @@ func (ss *statSegmentV2) GetEpoch() (int64, bool) { return sh.epoch, sh.inProgress != 0 } -func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { - dirEntry := (*statSegDirectoryEntryV2)(statSegDir) +func (ss *statSegmentV2) CopyEntryData(segment dirSegment) adapter.Stat { + dirEntry := (*statSegDirectoryEntryV2)(segment) if dirEntry.unionData == 0 { debugf("data value or pointer not defined for %s", dirEntry.name) return nil @@ -120,7 +120,7 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { return adapter.ErrorStat(errData) case statDirCounterVectorSimple: - dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData)) + dirVector := ss.adjust(dirVector(&dirEntry.unionData)) if dirVector == nil { debugf("data vector pointer is out of range for %s", dirEntry.name) return nil @@ -145,7 +145,7 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { return adapter.SimpleCounterStat(data) case statDirCounterVectorCombined: - dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData)) + dirVector := ss.adjust(dirVector(&dirEntry.unionData)) if dirVector == nil { debugf("data vector pointer is out of range for %s", dirEntry.name) return nil @@ -170,7 +170,7 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { return adapter.CombinedCounterStat(data) case statDirNameVector: - dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData)) + dirVector := ss.adjust(dirVector(&dirEntry.unionData)) if dirVector == nil { debugf("data vector pointer is out of range for %s", dirEntry.name) return nil @@ -212,8 +212,8 @@ func (ss *statSegmentV2) CopyEntryData(statSegDir unsafe.Pointer) adapter.Stat { return nil } -func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapter.Stat) error { - dirEntry := (*statSegDirectoryEntryV2)(statSegDir) +func (ss *statSegmentV2) UpdateEntryData(segment dirSegment, stat *adapter.Stat) error { + dirEntry := (*statSegDirectoryEntryV2)(segment) switch (*stat).(type) { case adapter.ScalarStat: *stat = adapter.ScalarStat(dirEntry.unionData) @@ -240,7 +240,7 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte *stat = adapter.ErrorStat(errData) case adapter.SimpleCounterStat: - dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData)) + dirVector := ss.adjust(dirVector(&dirEntry.unionData)) if dirVector == nil { debugf("data vector pointer is out of range for %s", dirEntry.name) return nil @@ -267,7 +267,7 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte } case adapter.CombinedCounterStat: - dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData)) + dirVector := ss.adjust(dirVector(&dirEntry.unionData)) if dirVector == nil { debugf("data vector pointer is out of range for %s", dirEntry.name) return nil @@ -291,7 +291,7 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte } case adapter.NameStat: - dirVector := ss.adjust(unsafe.Pointer(&dirEntry.unionData)) + dirVector := ss.adjust(dirVector(&dirEntry.unionData)) if dirVector == nil { debugf("data vector pointer is out of range for %s", dirEntry.name) return nil @@ -334,9 +334,9 @@ func (ss *statSegmentV2) UpdateEntryData(statSegDir unsafe.Pointer, stat *adapte // Adjust data pointer using shared header and base and return // the pointer to a data segment -func (ss *statSegmentV2) adjust(data unsafe.Pointer) unsafe.Pointer { +func (ss *statSegmentV2) adjust(data dirVector) dirVector { header := ss.loadSharedHeader(ss.sharedHeader) - adjusted := unsafe.Pointer(uintptr(unsafe.Pointer(&ss.sharedHeader[0])) + + adjusted := dirVector(uintptr(unsafe.Pointer(&ss.sharedHeader[0])) + uintptr(*(*uint64)(data)) - uintptr(*(*uint64)(unsafe.Pointer(&header.base)))) if uintptr(unsafe.Pointer(&ss.sharedHeader[len(ss.sharedHeader)-1])) <= uintptr(adjusted) || uintptr(unsafe.Pointer(&ss.sharedHeader[0])) >= uintptr(adjusted) { @@ -345,7 +345,7 @@ func (ss *statSegmentV2) adjust(data unsafe.Pointer) unsafe.Pointer { return adjusted } -func (ss *statSegmentV2) getErrorVector() unsafe.Pointer { +func (ss *statSegmentV2) getErrorVector() dirVector { header := ss.loadSharedHeader(ss.sharedHeader) - return ss.adjust(unsafe.Pointer(&header.errorVector)) + return ss.adjust(dirVector(&header.errorVector)) } diff --git a/adapter/vppapiclient/stat_client.go b/adapter/vppapiclient/stat_client.go index a124f59..734c158 100644 --- a/adapter/vppapiclient/stat_client.go +++ b/adapter/vppapiclient/stat_client.go @@ -83,7 +83,7 @@ func (c *statClient) Disconnect() error { return nil } -func (c *statClient) ListStats(patterns ...string) (stats []string, err error) { +func (c *statClient) ListStats(patterns ...string) (indexes []adapter.StatIdentifier, err error) { dir := C.govpp_stat_segment_ls(convertStringSlice(patterns)) if dir == nil { return nil, adapter.ErrStatsDataBusy @@ -93,11 +93,14 @@ func (c *statClient) ListStats(patterns ...string) (stats []string, err error) { l := C.govpp_stat_segment_vec_len(unsafe.Pointer(dir)) for i := 0; i < int(l); i++ { nameChar := C.govpp_stat_segment_dir_index_to_name(dir, C.uint32_t(i)) - stats = append(stats, C.GoString(nameChar)) + indexes = append(indexes, adapter.StatIdentifier{ + Name: []byte(C.GoString(nameChar)), + Index: uint32(i), + }) C.free(unsafe.Pointer(nameChar)) } - return stats, nil + return indexes, nil } func (c *statClient) DumpStats(patterns ...string) (stats []adapter.StatEntry, err error) { @@ -121,7 +124,10 @@ func (c *statClient) DumpStats(patterns ...string) (stats []adapter.StatEntry, e typ := adapter.StatType(C.govpp_stat_segment_data_type(&v)) stat := adapter.StatEntry{ - Name: []byte(name), + StatIdentifier: adapter.StatIdentifier{ + Name: []byte(name), + Index: uint32(i), + }, Type: typ, } @@ -184,6 +190,10 @@ func (c *statClient) PrepareDir(prefixes ...string) (*adapter.StatDir, error) { return nil, adapter.ErrNotImplemented } +func (c *statClient) PrepareDirOnIndex(indexes ...uint32) (*adapter.StatDir, error) { + return nil, adapter.ErrNotImplemented +} + func (c *statClient) UpdateDir(dir *adapter.StatDir) error { return adapter.ErrNotImplemented } diff --git a/adapter/vppapiclient/stat_client_stub.go b/adapter/vppapiclient/stat_client_stub.go index c764391..856a1e3 100644 --- a/adapter/vppapiclient/stat_client_stub.go +++ b/adapter/vppapiclient/stat_client_stub.go @@ -36,7 +36,7 @@ func (*stubStatClient) Disconnect() error { return nil } -func (*stubStatClient) ListStats(patterns ...string) (statNames []string, err error) { +func (*stubStatClient) ListStats(patterns ...string) (indexes []adapter.StatIdentifier, err error) { return nil, adapter.ErrNotImplemented } @@ -48,6 +48,10 @@ func (*stubStatClient) PrepareDir(prefixes ...string) (*adapter.StatDir, error) return nil, adapter.ErrNotImplemented } +func (*stubStatClient) PrepareDirOnIndex(indexes ...uint32) (*adapter.StatDir, error) { + return nil, adapter.ErrNotImplemented +} + func (*stubStatClient) UpdateDir(dir *adapter.StatDir) error { return adapter.ErrNotImplemented } diff --git a/examples/stats-client/stats_api.go b/examples/stats-client/stats_api.go index fa39b54..63ee55d 100644 --- a/examples/stats-client/stats_api.go +++ b/examples/stats-client/stats_api.go @@ -19,6 +19,7 @@ import ( "fmt" "log" "os" + "strconv" "strings" "time" @@ -44,7 +45,7 @@ var ( func init() { flag.Usage = func() { - fmt.Fprintf(os.Stderr, "%s: usage [ls|dump|poll|errors|interfaces|nodes|system|buffers|memory] ...\n", os.Args[0]) + fmt.Fprintf(os.Stderr, "%s: usage [ls|dump|poll|errors|interfaces|nodes|system|buffers|memory|epoch] ...\n", os.Args[0]) flag.PrintDefaults() os.Exit(1) } @@ -54,9 +55,16 @@ func main() { flag.Parse() skipZeros := !*dumpAll - var patterns []string + patterns := make([]string, 0) + indexes := make([]uint32, 0) if flag.NArg() > 0 { - patterns = flag.Args()[1:] + for _, arg := range flag.Args()[1:] { + if index, err := strconv.Atoi(arg); err == nil { + indexes = append(indexes, uint32(index)) + continue + } + patterns = append(patterns, arg) + } } var ( @@ -168,7 +176,7 @@ func main() { case "dump": fmt.Printf("Dumping stats.. %s\n", strings.Join(patterns, " ")) - dumpStats(client, patterns, skipZeros) + dumpStats(client, patterns, indexes, skipZeros) case "poll": fmt.Printf("Polling stats.. %s\n", strings.Join(patterns, " ")) @@ -178,30 +186,69 @@ func main() { case "list", "ls", "": fmt.Printf("Listing stats.. %s\n", strings.Join(patterns, " ")) - listStats(client, patterns) + listStats(client, patterns, indexes) + + case "epoch", "e": + fmt.Printf("Getting epoch..\n") + + getEpoch(client) default: fmt.Printf("invalid command: %q\n", cmd) } } -func listStats(client adapter.StatsAPI, patterns []string) { - list, err := client.ListStats(patterns...) - if err != nil { - log.Fatalln("listing stats failed:", err) +func listStats(client adapter.StatsAPI, patterns []string, indexes []uint32) { + var err error + list := make([]adapter.StatIdentifier, 0) + if (len(patterns) == 0 && len(indexes) == 0) || len(patterns) != 0 { + list, err = client.ListStats(patterns...) + if err != nil { + log.Fatalln("listing stats failed:", err) + } + } + if len(indexes) != 0 { + dir, err := client.PrepareDirOnIndex(indexes...) + if err != nil { + log.Fatalln("listing stats failed:", err) + } + for _, onIndexSi := range dir.Entries { + list = append(list, onIndexSi.StatIdentifier) + } } - for _, stat := range list { - fmt.Printf(" - %v\n", stat) + fmt.Printf(" - %d\t %v\n", stat.Index, string(stat.Name)) } fmt.Printf("Listed %d stats\n", len(list)) } -func dumpStats(client adapter.StatsAPI, patterns []string, skipZeros bool) { - stats, err := client.DumpStats(patterns...) +func getEpoch(client adapter.StatsAPI) { + dir, err := client.PrepareDir() if err != nil { - log.Fatalln("dumping stats failed:", err) + log.Fatalln("failed to prepare dir in order to read epoch:", err) + } + d := *dir + fmt.Printf("Epoch %d\n", d.Epoch) +} + +func dumpStats(client adapter.StatsAPI, patterns []string, indexes []uint32, skipZeros bool) { + var err error + stats := make([]adapter.StatEntry, 0) + if (len(patterns) == 0 && len(indexes) == 0) || len(patterns) != 0 { + stats, err = client.DumpStats(patterns...) + if err != nil { + log.Fatalln("dumping stats failed:", err) + } + } + if len(indexes) != 0 { + dir, err := client.PrepareDirOnIndex(indexes...) + if err != nil { + log.Fatalln("dumping stats failed:", err) + } + for _, onIndexSi := range dir.Entries { + stats = append(stats, onIndexSi) + } } n := 0 -- 2.16.6