diff options
author | Arthur de Kerhor <arthurdekerhor@gmail.com> | 2021-03-11 10:26:54 -0800 |
---|---|---|
committer | Ole Tr�an <otroan@employees.org> | 2021-05-04 16:29:29 +0000 |
commit | db023809043f1dc64ed8c30dd5a575763df6045b (patch) | |
tree | eace5cdd59301a33872e312dd59bb9d2a24b8820 /src/vpp-api | |
parent | 78b0a6e3f6e8644ebd06273f53b2440748ba34f4 (diff) |
stats: adding symlinks for nodes and interfaces in the stat segment
A given interface counter (e.g rx) can be accessed via
/interfaces/<interface_name>/<counter_name>.
Same goes with nodes: /nodes/<node_name>/<counter_name>
As interfaces may contain '/' in their names,
these are replaced by '_' in symlinks
Also added 2 tests for the stat segment
Type: feature
Signed-off-by: Arthur de Kerhor <arthurdekerhor@gmail.com>
Change-Id: I27da252e7b3dc177815616ca46b5c200a456bf0f
Signed-off-by: Ole Troan <ot@cisco.com>
Diffstat (limited to 'src/vpp-api')
-rw-r--r-- | src/vpp-api/client/stat_client.c | 55 | ||||
-rwxr-xr-x | src/vpp-api/python/vpp_papi/vpp_stats.py | 128 |
2 files changed, 136 insertions, 47 deletions
diff --git a/src/vpp-api/client/stat_client.c b/src/vpp-api/client/stat_client.c index 018cce34228..2c30be62326 100644 --- a/src/vpp-api/client/stat_client.c +++ b/src/vpp-api/client/stat_client.c @@ -174,7 +174,7 @@ stat_segment_heartbeat_r (stat_client_main_t * sm) stat_segment_access_t sa; stat_segment_directory_entry_t *ep; - /* Has directory been update? */ + /* Has directory been updated? */ if (sm->shared_header->epoch != sm->current_epoch) return 0; if (stat_segment_access_start (&sa, sm)) @@ -202,8 +202,29 @@ stat_segment_heartbeat (void) _v(v); \ }) +static counter_t * +stat_vec_simple_init (counter_t c) +{ + counter_t *v = 0; + vec_add1 (v, c); + return v; +} + +static vlib_counter_t * +stat_vec_combined_init (vlib_counter_t c) +{ + vlib_counter_t *v = 0; + vec_add1 (v, c); + return v; +} + +/* + * If index2 is specified copy out the column (the indexed value across all + * threads), otherwise copy out all values. + */ static stat_segment_data_t -copy_data (stat_segment_directory_entry_t * ep, stat_client_main_t * sm) +copy_data (stat_segment_directory_entry_t *ep, u32 index2, char *name, + stat_client_main_t *sm) { stat_segment_data_t result = { 0 }; int i; @@ -214,7 +235,8 @@ copy_data (stat_segment_directory_entry_t * ep, stat_client_main_t * sm) assert (sm->shared_header); result.type = ep->type; - result.name = strdup (ep->name); + result.name = strdup (name ? name : ep->name); + switch (ep->type) { case STAT_DIR_TYPE_SCALAR_INDEX: @@ -227,7 +249,10 @@ copy_data (stat_segment_directory_entry_t * ep, stat_client_main_t * sm) for (i = 0; i < vec_len (simple_c); i++) { counter_t *cb = stat_segment_adjust (sm, simple_c[i]); - result.simple_counter_vec[i] = stat_vec_dup (sm, cb); + if (index2 != ~0) + result.simple_counter_vec[i] = stat_vec_simple_init (cb[index2]); + else + result.simple_counter_vec[i] = stat_vec_dup (sm, cb); } break; @@ -237,7 +262,11 @@ copy_data (stat_segment_directory_entry_t * ep, stat_client_main_t * sm) for (i = 0; i < vec_len (combined_c); i++) { vlib_counter_t *cb = stat_segment_adjust (sm, combined_c[i]); - result.combined_counter_vec[i] = stat_vec_dup (sm, cb); + if (index2 != ~0) + result.combined_counter_vec[i] = + stat_vec_combined_init (cb[index2]); + else + result.combined_counter_vec[i] = stat_vec_dup (sm, cb); } break; @@ -265,6 +294,14 @@ copy_data (stat_segment_directory_entry_t * ep, stat_client_main_t * sm) } break; + case STAT_DIR_TYPE_SYMLINK: + /* Gather info from all threads into a vector */ + { + stat_segment_directory_entry_t *ep2; + ep2 = vec_elt_at_index (sm->directory_vector, ep->index1); + return copy_data (ep2, ep->index2, ep->name, sm); + } + case STAT_DIR_TYPE_EMPTY: break; @@ -390,7 +427,7 @@ stat_segment_dump_r (uint32_t * stats, stat_client_main_t * sm) { /* Collect counter */ ep = vec_elt_at_index (sm->directory_vector, stats[i]); - vec_add1 (res, copy_data (ep, sm)); + vec_add1 (res, copy_data (ep, ~0, 0, sm)); } if (stat_segment_access_end (&sa, sm)) @@ -440,12 +477,16 @@ stat_segment_dump_entry_r (uint32_t index, stat_client_main_t * sm) stat_segment_data_t *res = 0; stat_segment_access_t sa; + /* Has directory been update? */ + if (sm->shared_header->epoch != sm->current_epoch) + return 0; + if (stat_segment_access_start (&sa, sm)) return 0; /* Collect counter */ ep = vec_elt_at_index (sm->directory_vector, index); - vec_add1 (res, copy_data (ep, sm)); + vec_add1 (res, copy_data (ep, ~0, 0, sm)); if (stat_segment_access_end (&sa, sm)) return res; diff --git a/src/vpp-api/python/vpp_papi/vpp_stats.py b/src/vpp-api/python/vpp_papi/vpp_stats.py index 919df7e48b8..821a4131834 100755 --- a/src/vpp-api/python/vpp_papi/vpp_stats.py +++ b/src/vpp-api/python/vpp_papi/vpp_stats.py @@ -180,67 +180,97 @@ class VPPStats(): elementfmt = 'IQ128s' - def refresh(self): + def refresh(self, blocking=True): '''Refresh directory vector cache (epoch changed)''' directory = {} - with self.lock: - for direntry in StatsVector(self, self.directory_vector, self.elementfmt): - path_raw = direntry[2].find(b'\x00') - path = direntry[2][:path_raw].decode('ascii') - directory[path] = StatsEntry(direntry[0], direntry[1]) - self.last_epoch = self.epoch - self.directory = directory - - # Cache the error index vectors - self.error_vectors = [] - for threads in StatsVector(self, self.error_vector, 'P'): - self.error_vectors.append(StatsVector(self, threads[0], 'Q')) - - def __getitem__(self, item): + directory_by_idx = {} + while True: + try: + with self.lock: + for i, direntry in enumerate(StatsVector(self, self.directory_vector, self.elementfmt)): + path_raw = direntry[2].find(b'\x00') + path = direntry[2][:path_raw].decode('ascii') + directory[path] = StatsEntry(direntry[0], direntry[1]) + directory_by_idx[i] = path + self.last_epoch = self.epoch + self.directory = directory + self.directory_by_idx = directory_by_idx + + # Cache the error index vectors + self.error_vectors = [] + for threads in StatsVector(self, self.error_vector, 'P'): + self.error_vectors.append(StatsVector(self, threads[0], 'Q')) + return + except IOError: + if not blocking: + raise + + def __getitem__(self, item, blocking=True): if not self.connected: self.connect() - if self.last_epoch != self.epoch: - self.refresh() - with self.lock: - return self.directory[item].get_counter(self) + while True: + try: + if self.last_epoch != self.epoch: + self.refresh(blocking) + with self.lock: + return self.directory[item].get_counter(self) + except IOError: + if not blocking: + raise def __iter__(self): return iter(self.directory.items()) - def set_errors(self): + def set_errors(self, blocking=True): '''Return dictionary of error counters > 0''' if not self.connected: self.connect() errors = {k:v for k, v in self.directory.items() if k.startswith("/err/")} result = {} - with self.lock: - for k, entry in errors.items(): - total = 0 - i = entry.value - for per_thread in self.error_vectors: - total += per_thread[i] - if total: - result[k] = total - return result - - def set_errors_str(self): + while True: + try: + if self.last_epoch != self.epoch: + self.refresh(blocking) + with self.lock: + for k, entry in errors.items(): + total = 0 + i = entry.value + for per_thread in self.error_vectors: + total += per_thread[i] + if total: + result[k] = total + return result + except IOError: + if not blocking: + raise + + def set_errors_str(self, blocking=True): '''Return all errors counters > 0 pretty printed''' error_string = ['ERRORS:'] - error_counters = self.set_errors() + error_counters = self.set_errors(blocking) for k in sorted(error_counters): error_string.append('{:<60}{:>10}'.format(k, error_counters[k])) return '%s\n' % '\n'.join(error_string) - def get_counter(self, name): + def get_counter(self, name, blocking=True): '''Alternative call to __getitem__''' - return self.__getitem__(name) + return self.__getitem__(name, blocking) - def get_err_counter(self, name): + def get_err_counter(self, name, blocking=True): '''Return a single value (sum of all threads)''' if not self.connected: self.connect() - return sum(self.directory[name].get_counter(self)) + if name.startswith("/err/"): + while True: + try: + if self.last_epoch != self.epoch: + self.refresh(blocking) + with self.lock: + return sum(self.directory[name].get_counter(self)) + except IOError: + if not blocking: + raise def ls(self, patterns): '''Returns list of counters matching pattern''' @@ -253,13 +283,13 @@ class VPPStats(): return [k for k, v in self.directory.items() if any(re.match(pattern, k) for pattern in regex)] - def dump(self, counters): + def dump(self, counters, blocking=True): '''Given a list of counters return a dictionary of results''' if not self.connected: self.connect() result = {} for cnt in counters: - result[cnt] = self.__getitem__(cnt) + result[cnt] = self.__getitem__(cnt,blocking) return result class StatsLock(): @@ -377,6 +407,8 @@ class StatsEntry(): self.function = self.error elif stattype == 5: self.function = self.name + elif stattype == 7: + self.function = self.symlink else: self.function = self.illegal @@ -415,12 +447,23 @@ class StatsEntry(): '''Name counter''' counter = [] for name in StatsVector(stats, self.value, 'P'): - counter.append(get_string(stats, name[0])) + if name[0]: + counter.append(get_string(stats, name[0])) return counter + SYMLINK_FMT1 = Struct('II') + SYMLINK_FMT2 = Struct('Q') + def symlink(self, stats): + '''Symlink counter''' + b = self.SYMLINK_FMT2.pack(self.value) + index1, index2 = self.SYMLINK_FMT1.unpack(b) + name = stats.directory_by_idx[index1] + return stats[name][:,index2] + def get_counter(self, stats): '''Return a list of counters''' - return self.function(stats) + if stats: + return self.function(stats) class TestStats(unittest.TestCase): '''Basic statseg tests''' @@ -508,6 +551,11 @@ class TestStats(unittest.TestCase): print('/sys/node', self.stat.dump(counters)) print('/net/route/to', self.stat['/net/route/to']) + def test_symlink(self): + '''Symbolic links''' + print('/interface/local0/rx', self.stat['/interfaces/local0/rx']) + print('/sys/nodes/unix-epoll-input', self.stat['/nodes/unix-epoll-input/calls']) + if __name__ == '__main__': import cProfile from pstats import Stats |