From 0c4a93bb8c1d315e3becfe4a04b9e183d0b5ecb9 Mon Sep 17 00:00:00 2001 From: Sagar Sagar Date: Mon, 2 Mar 2026 10:45:05 +0000 Subject: [PATCH] system-tools: restore backward compatibility with older pmapi versions Commit 0a37ed0ec8e93a30b3ee349b78c23ba708caef45 introduced support for pmapi version 4, but upgrading to the latest pmapi is not currently desired. This change updates pcp-iostat, mpstat, pidstat, and ps to fall back to tv_usec when tv_nsec is unavailable during timestamp delta calculation, ensuring compatibility with older pmapi versions and collectors that only expose tv_usec. --- src/pcp/iostat/pcp-iostat.py | 15 ++++++++++++--- src/pcp/mpstat/pcp-mpstat.py | 13 +++++++++++-- src/pcp/pidstat/pcp-pidstat.py | 13 +++++++++++-- src/pcp/ps/pcp-ps.py | 13 +++++++++++-- src/pcp/tapestat/pcp-tapestat.py | 15 ++++++++++++--- 5 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/pcp/iostat/pcp-iostat.py b/src/pcp/iostat/pcp-iostat.py index fd049a6f5a7..ccd1265aea0 100755 --- a/src/pcp/iostat/pcp-iostat.py +++ b/src/pcp/iostat/pcp-iostat.py @@ -56,9 +56,18 @@ class IostatReport(pmcc.MetricGroupPrinter): Hcount = 0 def timeStampDelta(self, group): s = group.timestamp.tv_sec - group.prevTimestamp.tv_sec - n = group.timestamp.tv_nsec - group.prevTimestamp.tv_nsec - # n may be negative here, calculation is still correct. - return s + n / 1000000000.0 + # pmapi timestamps may provide sub-second resolution via tv_nsec (nanoseconds) + # or tv_usec (microseconds) depending on the collector. Prefer nanoseconds + # when available, but gracefully fall back to microseconds to avoid + if hasattr(group.timestamp, 'tv_nsec') and hasattr(group.prevTimestamp, 'tv_nsec'): + n = group.timestamp.tv_nsec - group.prevTimestamp.tv_nsec + # n may be negative here, calculation is still correct. + return s + n / 1_000_000_000.0 + elif hasattr(group.timestamp, 'tv_usec') and hasattr(group.prevTimestamp, 'tv_usec'): + u = group.timestamp.tv_usec - group.prevTimestamp.tv_usec + return s + u / 1_000_000.0 + # it should not reach here + return s def instlist(self, group, name): return dict(map(lambda x: (x[1], x[2]), group[name].netValues)).keys() diff --git a/src/pcp/mpstat/pcp-mpstat.py b/src/pcp/mpstat/pcp-mpstat.py index 2b0bd59dd4f..85bf2828cc4 100755 --- a/src/pcp/mpstat/pcp-mpstat.py +++ b/src/pcp/mpstat/pcp-mpstat.py @@ -499,8 +499,17 @@ def __init__(self, cpu_util_reporter, total_interrupt_usage_reporter, soft_inter def timeStampDelta(self, group): s = group.timestamp.tv_sec - group.prevTimestamp.tv_sec - n = group.timestamp.tv_nsec - group.prevTimestamp.tv_nsec - return s + n / 1000000000.0 + # pmapi timestamps may provide sub-second resolution via tv_nsec (nanoseconds) + # or tv_usec (microseconds) depending on the collector. Prefer nanoseconds + # when available, but gracefully fall back to microseconds to avoid + if hasattr(group.timestamp, 'tv_nsec') and hasattr(group.prevTimestamp, 'tv_nsec'): + n = group.timestamp.tv_nsec - group.prevTimestamp.tv_nsec + return s + n / 1_000_000_000.0 + elif hasattr(group.timestamp, 'tv_usec') and hasattr(group.prevTimestamp, 'tv_usec'): + u = group.timestamp.tv_usec - group.prevTimestamp.tv_usec + return s + u / 1_000_000.0 + # it should not reach here + return s def print_machine_info(self,group, context): self.get_summary_metrics(group) diff --git a/src/pcp/pidstat/pcp-pidstat.py b/src/pcp/pidstat/pcp-pidstat.py index 32bf9256211..813bf912dbc 100755 --- a/src/pcp/pidstat/pcp-pidstat.py +++ b/src/pcp/pidstat/pcp-pidstat.py @@ -925,8 +925,17 @@ class PidstatReport(pmcc.MetricGroupPrinter): def timeStampDelta(self, group): s = group.timestamp.tv_sec - group.prevTimestamp.tv_sec - n = group.timestamp.tv_nsec - group.prevTimestamp.tv_nsec - return s + n / 1000000000.0 + # pmapi timestamps may provide sub-second resolution via tv_nsec (nanoseconds) + # or tv_usec (microseconds) depending on the collector. Prefer nanoseconds + # when available, but gracefully fall back to microseconds to avoid + if hasattr(group.timestamp, 'tv_nsec') and hasattr(group.prevTimestamp, 'tv_nsec'): + n = group.timestamp.tv_nsec - group.prevTimestamp.tv_nsec + return s + n / 1_000_000_000.0 + elif hasattr(group.timestamp, 'tv_usec') and hasattr(group.prevTimestamp, 'tv_usec'): + u = group.timestamp.tv_usec - group.prevTimestamp.tv_usec + return s + u / 1_000_000.0 + # it should not reach here + return s def print_machine_info(self,group, context): timestamp = context.pmLocaltime(group.timestamp.tv_sec) diff --git a/src/pcp/ps/pcp-ps.py b/src/pcp/ps/pcp-ps.py index c8f2e9d972b..254be7d4aa2 100755 --- a/src/pcp/ps/pcp-ps.py +++ b/src/pcp/ps/pcp-ps.py @@ -585,8 +585,17 @@ def __init__(self, group=None, options = None): def timeStampDelta(self): s = self.group.timestamp.tv_sec - self.group.prevTimestamp.tv_sec - n = self.group.timestamp.tv_nsec - self.group.prevTimestamp.tv_nsec - return s + n / 1000000000.0 + # pmapi timestamps may provide sub-second resolution via tv_nsec (nanoseconds) + # or tv_usec (microseconds) depending on the collector. Prefer nanoseconds + # when available, but gracefully fall back to microseconds to avoid + if hasattr(self.group.timestamp, 'tv_nsec') and hasattr(self.group.prevTimestamp, 'tv_nsec'): + n = self.group.timestamp.tv_nsec - self.group.prevTimestamp.tv_nsec + return s + n / 1_000_000_000.0 + elif hasattr(self.group.timestamp, 'tv_usec') and hasattr(self.group.prevTimestamp, 'tv_usec'): + u = self.group.timestamp.tv_usec - self.group.prevTimestamp.tv_usec + return s + u / 1_000_000.0 + # it should not reach here + return s def print_machine_info(self,context): timestamp = context.pmLocaltime(self.group.timestamp.tv_sec) diff --git a/src/pcp/tapestat/pcp-tapestat.py b/src/pcp/tapestat/pcp-tapestat.py index dd54c701607..99d0fbcae09 100755 --- a/src/pcp/tapestat/pcp-tapestat.py +++ b/src/pcp/tapestat/pcp-tapestat.py @@ -68,9 +68,18 @@ class TapestatReport(pmcc.MetricGroupPrinter): Hcount = 0 def timeStampDelta(self, group): s = group.timestamp.tv_sec - group.prevTimestamp.tv_sec - n = group.timestamp.tv_nsec - group.prevTimestamp.tv_nsec - # n may be negative here, calculation is still correct. - return s + n / 1000000000.0 + # pmapi timestamps may provide sub-second resolution via tv_nsec (nanoseconds) + # or tv_usec (microseconds) depending on the collector. Prefer nanoseconds + # when available, but gracefully fall back to microseconds to avoid + if hasattr(group.timestamp, 'tv_nsec') and hasattr(group.prevTimestamp, 'tv_nsec'): + n = group.timestamp.tv_nsec - group.prevTimestamp.tv_nsec + # n may be negative here, calculation is still correct. + return s + n / 1_000_000_000.0 + elif hasattr(group.timestamp, 'tv_usec') and hasattr(group.prevTimestamp, 'tv_usec'): + u = group.timestamp.tv_usec - group.prevTimestamp.tv_usec + return s + u / 1_000_000.0 + # it should not reach here + return s def instlist(self, group, name): return dict(map(lambda x: (x[1], x[2]), group[name].netValues)).keys()