aboutsummaryrefslogtreecommitdiffstats
path: root/src/vpp-api
diff options
context:
space:
mode:
authorArthur de Kerhor <arthurdekerhor@gmail.com>2021-03-11 10:26:54 -0800
committerOle Tr�an <otroan@employees.org>2021-05-04 16:29:29 +0000
commitdb023809043f1dc64ed8c30dd5a575763df6045b (patch)
treeeace5cdd59301a33872e312dd59bb9d2a24b8820 /src/vpp-api
parent78b0a6e3f6e8644ebd06273f53b2440748ba34f4 (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.c55
-rwxr-xr-xsrc/vpp-api/python/vpp_papi/vpp_stats.py128
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