Revert OL modifications
This commit is contained in:
parent
7b4c9c7ecc
commit
70b12ef6e8
File diff suppressed because it is too large
Load Diff
@ -1,92 +0,0 @@
|
||||
From ff8b1172b43b850f2e858a621f7d79f34083d52e Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Thu, 17 Nov 2022 00:57:13 -0500
|
||||
Subject: [PATCH] pcp-ps: python2 print issue and -o option fix
|
||||
|
||||
For dynamic printing with -o option,end literal in print was being used to
|
||||
print the output continously but it's not supported in python2 so we have
|
||||
changed the method.Now we get all the data once in buffer then we print that buffer.
|
||||
|
||||
pcp-ps -o option was not working because number of arguments in the extra option
|
||||
function were incorrect so it was not able to override.
|
||||
|
||||
cherry-picked upstream commit:- a5632f0e72fda4ab1e81a971c5b1eda31ab22f34
|
||||
2641b1713252753a94c7e40cb59f4d3f45e828c1
|
||||
Signed-off-by: Sagar Sagar <sagar.sagar@oracle.com>
|
||||
Orabug: 34321683
|
||||
---
|
||||
src/pcp/ps/pcp-ps.py | 33 ++++++++++++---------------------
|
||||
1 file changed, 12 insertions(+), 21 deletions(-)
|
||||
|
||||
diff --git a/src/pcp/ps/pcp-ps.py b/src/pcp/ps/pcp-ps.py
|
||||
index b087951c4..65e4403ff 100755
|
||||
--- a/src/pcp/ps/pcp-ps.py
|
||||
+++ b/src/pcp/ps/pcp-ps.py
|
||||
@@ -38,25 +38,16 @@ SCHED_POLICY = ['NORMAL', 'FIFO', 'RR', 'BATCH', '', 'IDLE', 'DEADLINE']
|
||||
|
||||
|
||||
class StdoutPrinter:
|
||||
- def Print(self, args, continuation=None):
|
||||
- if continuation is not None:
|
||||
- print(args, end=continuation)
|
||||
- else:
|
||||
- print(args)
|
||||
-
|
||||
+ def Print(self, args):
|
||||
+ print(args)
|
||||
|
||||
class NoneHandlingPrinterDecorator:
|
||||
def __init__(self, printer):
|
||||
self.printer = printer
|
||||
|
||||
- def Print(self, args, continuation=None):
|
||||
- if continuation is None:
|
||||
- new_args = args.replace('None', '?')
|
||||
- elif type(args) not in [float, int] or args is not None:
|
||||
- new_args = str(args).replace('None', '?')
|
||||
- else:
|
||||
- new_args = args
|
||||
- self.printer.Print(new_args, continuation)
|
||||
+ def Print(self, args):
|
||||
+ new_args = args.replace('None', '?')
|
||||
+ self.printer.Print(new_args)
|
||||
|
||||
|
||||
# After fetching non singular metric values, create a mapping of instance id
|
||||
@@ -429,18 +420,18 @@ class DynamicProcessReporter:
|
||||
process.rss(), current_process_sname, process.age(), process.total_time(), wchan,
|
||||
process_name))
|
||||
elif self.processStatOptions.colum_list is not None:
|
||||
- print('Timestamp', end="\t")
|
||||
+ header = "Timestamp" + '\t'
|
||||
for key in self.processStatOptions.colum_list:
|
||||
if key in PIDINFO_PAIR:
|
||||
- self.printer(PIDINFO_PAIR[key][0], '\t\t')
|
||||
- print('')
|
||||
+ header += PIDINFO_PAIR[key][0] + '\t\t'
|
||||
+ print(header)
|
||||
processes = self.process_filter.filter_processes(self.process_report.get_processes(self.delta_time))
|
||||
for process in processes:
|
||||
- print(timestamp, end="\t")
|
||||
+ data_to_print = timestamp + '\t'
|
||||
for key in self.processStatOptions.colum_list:
|
||||
if key in PIDINFO_PAIR:
|
||||
- self.printer(PIDINFO_PAIR[key][1](process), '\t\t')
|
||||
- print('')
|
||||
+ data_to_print += str(PIDINFO_PAIR[key][1](process)) + '\t\t'
|
||||
+ print(data_to_print)
|
||||
|
||||
|
||||
class ProcessStatusReporter:
|
||||
@@ -729,7 +720,7 @@ class ProcessStatOptions(pmapi.pmOptions):
|
||||
# """Override standard Pcp-ps option to show all process """
|
||||
return bool(opts in ['p', 'c', 'o', 'P', 'U'])
|
||||
|
||||
- def extraOptions(self, opts, optarg):
|
||||
+ def extraOptions(self, opts, optarg,index):
|
||||
if opts == 'e':
|
||||
ProcessStatOptions.show_all_process = True
|
||||
elif opts == 'c':
|
||||
--
|
||||
2.31.1
|
||||
|
||||
@ -1,82 +0,0 @@
|
||||
From 9e13cbc088bb88660a124e4a6b7f333ad9350670 Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Tue, 17 Jan 2023 07:14:48 -0500
|
||||
Subject: [PATCH] pcp-ps:added capabilities to show n sample with archives
|
||||
|
||||
we are checking if our current context is archive then don't
|
||||
limit the output print to one instance of data.
|
||||
|
||||
cherry-pick upstream commit:- 5236b96127adde8b4aaf01ad6dc9d4265b102979
|
||||
|
||||
Signed-off-by: Sagar Sagar <sagar.sagar@oracle.com>
|
||||
Orabug: 34849959
|
||||
---
|
||||
src/pcp/ps/pcp-ps.py | 24 ++++++++++++++----------
|
||||
1 file changed, 14 insertions(+), 10 deletions(-)
|
||||
|
||||
diff --git a/src/pcp/ps/pcp-ps.py b/src/pcp/ps/pcp-ps.py
|
||||
index 65e4403ff..f05cbe755 100755
|
||||
--- a/src/pcp/ps/pcp-ps.py
|
||||
+++ b/src/pcp/ps/pcp-ps.py
|
||||
@@ -22,6 +22,8 @@ import sys
|
||||
import time
|
||||
from pcp import pmcc
|
||||
from pcp import pmapi
|
||||
+from cpmapi import PM_CONTEXT_ARCHIVE
|
||||
+
|
||||
import datetime
|
||||
|
||||
process_state_info = {}
|
||||
@@ -378,10 +380,11 @@ class DynamicProcessReporter:
|
||||
|
||||
# when the print count is exhausted exit the program gracefully
|
||||
# we can't use break here because it's being called by the run manager
|
||||
- if self.processStatOptions.print_count == 0:
|
||||
- sys.exit(0)
|
||||
- else:
|
||||
- self.processStatOptions.print_count -= 1
|
||||
+ if self.processStatOptions.context is not PM_CONTEXT_ARCHIVE:
|
||||
+ if self.processStatOptions.print_count == 0:
|
||||
+ sys.exit(0)
|
||||
+ else:
|
||||
+ self.processStatOptions.print_count -= 1
|
||||
|
||||
if self.processStatOptions.filterstate is not None:
|
||||
self.printer("Timestamp" + header_indentation +
|
||||
@@ -446,11 +449,11 @@ class ProcessStatusReporter:
|
||||
|
||||
# when the print count is exhausted exit the program gracefully
|
||||
# we can't use break here because it's being called by the run manager
|
||||
-
|
||||
- if self.processStatOptions.print_count == 0:
|
||||
- sys.exit(0)
|
||||
- else:
|
||||
- self.processStatOptions.print_count -= 1
|
||||
+ if self.processStatOptions.context is not PM_CONTEXT_ARCHIVE:
|
||||
+ if self.processStatOptions.print_count == 0:
|
||||
+ sys.exit(0)
|
||||
+ else:
|
||||
+ self.processStatOptions.print_count -= 1
|
||||
|
||||
if self.processStatOptions.show_all_process:
|
||||
self.printer("Timestamp" + header_indentation + "PID\t\t\tTTY\tTIME\t\tCMD")
|
||||
@@ -666,6 +669,7 @@ class ProcessStatOptions(pmapi.pmOptions):
|
||||
pid_list = []
|
||||
ppid_list = []
|
||||
filtered_process_user = None
|
||||
+ context = None
|
||||
|
||||
def __init__(self):
|
||||
pmapi.pmOptions.__init__(self, "t:c:e::p:ukVZ:z?:o:P:l:U:k")
|
||||
@@ -792,7 +796,7 @@ if __name__ == "__main__":
|
||||
try:
|
||||
opts = ProcessStatOptions()
|
||||
manager = pmcc.MetricGroupManager.builder(opts, sys.argv)
|
||||
-
|
||||
+ ProcessStatOptions.context = manager.type
|
||||
if not opts.checkOptions():
|
||||
raise pmapi.pmUsageErr
|
||||
missing = manager.checkMissingMetrics(PSSTAT_METRICS)
|
||||
--
|
||||
2.31.1
|
||||
|
||||
@ -1,687 +0,0 @@
|
||||
From d2aeae7b1954f4c12a59928d807b0f796bf3ad7a Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Wed, 18 Jan 2023 06:04:43 -0500
|
||||
Subject: [PATCH] Fixed multiple pcp-ps and pcp-mpstat issue
|
||||
|
||||
fixed broken pipe issue in pcp ps and pcp-pidstat utility
|
||||
fixed mpstat crash issue with archives by adding error handling in current values
|
||||
|
||||
cherry-pick upstream commit:-
|
||||
ac02934682e96a1bfd977c1b89b60d15878eef95
|
||||
fc82b0bc4a89722094a07ec1acdd37028ca710b6
|
||||
c8bdc0fa919b3534bd7184cc1aaaac1e3ca01188
|
||||
94e2e6da2ccea5df70253037ce1106e44c5e2a68
|
||||
1ce54e647bc8dfab4b313c39db005f16d10b87c4
|
||||
a0e2447506c1579d1a936d7b1b25c736b560bd07
|
||||
60d436e929b8964b1eea035a4d69fa17a5108d6c
|
||||
Signed-off-by: Sagar Sagar <sagar.sagar@oracle.com>
|
||||
Orabug: 34830203
|
||||
Orabug: 34891338
|
||||
Orabug: 34869451
|
||||
---
|
||||
src/pcp/mpstat/pcp-mpstat.py | 45 +++-
|
||||
src/pcp/pidstat/pcp-pidstat.py | 3 +
|
||||
src/pcp/ps/pcp-ps.py | 238 +++++++++---------
|
||||
.../test/process_state_util_reporter_test.py | 5 +-
|
||||
src/pcp/ps/test/process_statusutil_test.py | 50 ++--
|
||||
5 files changed, 188 insertions(+), 153 deletions(-)
|
||||
|
||||
diff --git a/src/pcp/mpstat/pcp-mpstat.py b/src/pcp/mpstat/pcp-mpstat.py
|
||||
index fefb6aae7..ad73fd6c8 100755
|
||||
--- a/src/pcp/mpstat/pcp-mpstat.py
|
||||
+++ b/src/pcp/mpstat/pcp-mpstat.py
|
||||
@@ -108,7 +108,10 @@ class MetricRepository:
|
||||
if instance is not None:
|
||||
return dict(map(lambda x: (x[0].inst, x[2]), self.group[metric].netValues))
|
||||
else:
|
||||
- return self.group[metric].netValues[0][2]
|
||||
+ if self.group[metric].netValues == []:
|
||||
+ return None
|
||||
+ else:
|
||||
+ return self.group[metric].netValues[0][2]
|
||||
|
||||
def __fetch_previous_values(self,metric,instance):
|
||||
if instance is not None:
|
||||
@@ -140,9 +143,11 @@ class CoreCpuUtil:
|
||||
c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
if p_time is not None and c_time is not None:
|
||||
value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None:
|
||||
+ if self.instance is None and self.total_cpus() is not None:
|
||||
return float("%.2f"%(value/self.total_cpus()))
|
||||
else:
|
||||
+ if self.total_cpus() is None:
|
||||
+ return None
|
||||
return float("%.2f"%(value))
|
||||
|
||||
else:
|
||||
@@ -154,9 +159,11 @@ class CoreCpuUtil:
|
||||
c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
if p_time is not None and c_time is not None:
|
||||
value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None:
|
||||
+ if self.instance is None and self.total_cpus() is not None:
|
||||
return float("%.2f"%(value/self.total_cpus()))
|
||||
else:
|
||||
+ if self.total_cpus() is None:
|
||||
+ return None
|
||||
return float("%.2f"%(value))
|
||||
else:
|
||||
return None
|
||||
@@ -167,9 +174,11 @@ class CoreCpuUtil:
|
||||
c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
if p_time is not None and c_time is not None:
|
||||
value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None:
|
||||
+ if self.instance is None and self.total_cpus() is not None:
|
||||
return float("%.2f"%(value/self.total_cpus()))
|
||||
else:
|
||||
+ if self.total_cpus() is None:
|
||||
+ return None
|
||||
return float("%.2f"%(value))
|
||||
else:
|
||||
return None
|
||||
@@ -180,9 +189,11 @@ class CoreCpuUtil:
|
||||
c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
if p_time is not None and c_time is not None:
|
||||
value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None:
|
||||
+ if self.instance is None and self.total_cpus() is not None:
|
||||
return float("%.2f"%(value/self.total_cpus()))
|
||||
else:
|
||||
+ if self.total_cpus() is None:
|
||||
+ return None
|
||||
return float("%.2f"%(value))
|
||||
else:
|
||||
return None
|
||||
@@ -193,9 +204,11 @@ class CoreCpuUtil:
|
||||
c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
if p_time is not None and c_time is not None:
|
||||
value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None:
|
||||
+ if self.instance is None and self.total_cpus() is not None:
|
||||
return float("%.2f"%(value/self.total_cpus()))
|
||||
else:
|
||||
+ if self.total_cpus() is None:
|
||||
+ return None
|
||||
return float("%.2f"%(value))
|
||||
else:
|
||||
return None
|
||||
@@ -206,9 +219,11 @@ class CoreCpuUtil:
|
||||
c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
if p_time is not None and c_time is not None:
|
||||
value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None:
|
||||
+ if self.instance is None and self.total_cpus() is not None:
|
||||
return float("%.2f"%(value/self.total_cpus()))
|
||||
else:
|
||||
+ if self.total_cpus() is None:
|
||||
+ return None
|
||||
return float("%.2f"%(value))
|
||||
else:
|
||||
return None
|
||||
@@ -219,9 +234,11 @@ class CoreCpuUtil:
|
||||
c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
if p_time is not None and c_time is not None:
|
||||
value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None:
|
||||
+ if self.instance is None and self.total_cpus() is not None:
|
||||
return float("%.2f"%(value/self.total_cpus()))
|
||||
else:
|
||||
+ if self.total_cpus() is None:
|
||||
+ return None
|
||||
return float("%.2f"%(value))
|
||||
else:
|
||||
return None
|
||||
@@ -232,9 +249,11 @@ class CoreCpuUtil:
|
||||
c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
if p_time is not None and c_time is not None:
|
||||
value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None:
|
||||
+ if self.instance is None and self.total_cpus() is not None:
|
||||
return float("%.2f"%(value/self.total_cpus()))
|
||||
else:
|
||||
+ if self.total_cpus() is None:
|
||||
+ return None
|
||||
return float("%.2f"%(value))
|
||||
else:
|
||||
return None
|
||||
@@ -245,9 +264,11 @@ class CoreCpuUtil:
|
||||
c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
if p_time is not None and c_time is not None:
|
||||
value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None:
|
||||
+ if self.instance is None and self.total_cpus() is not None:
|
||||
return float("%.2f"%(value/self.total_cpus()))
|
||||
else:
|
||||
+ if self.total_cpus() is None:
|
||||
+ return None
|
||||
return float("%.2f"%(value))
|
||||
else:
|
||||
return None
|
||||
@@ -258,9 +279,11 @@ class CoreCpuUtil:
|
||||
c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
if p_time is not None and c_time is not None:
|
||||
value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None:
|
||||
+ if self.instance is None and self.total_cpus() is not None:
|
||||
return float("%.2f"%(value/self.total_cpus()))
|
||||
else:
|
||||
+ if self.total_cpus() is None:
|
||||
+ return None
|
||||
return float("%.2f"%(value))
|
||||
else:
|
||||
return None
|
||||
diff --git a/src/pcp/pidstat/pcp-pidstat.py b/src/pcp/pidstat/pcp-pidstat.py
|
||||
index 888527ca8..a086e0f88 100755
|
||||
--- a/src/pcp/pidstat/pcp-pidstat.py
|
||||
+++ b/src/pcp/pidstat/pcp-pidstat.py
|
||||
@@ -21,6 +21,7 @@
|
||||
import sys
|
||||
import re
|
||||
import time
|
||||
+import signal
|
||||
from pcp import pmcc
|
||||
from pcp import pmapi
|
||||
|
||||
@@ -1033,5 +1034,7 @@ if __name__ == "__main__":
|
||||
except pmapi.pmUsageErr as usage:
|
||||
usage.message()
|
||||
sys.exit(1)
|
||||
+ except IOError:
|
||||
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
diff --git a/src/pcp/ps/pcp-ps.py b/src/pcp/ps/pcp-ps.py
|
||||
index f05cbe755..6e2e9f4ac 100755
|
||||
--- a/src/pcp/ps/pcp-ps.py
|
||||
+++ b/src/pcp/ps/pcp-ps.py
|
||||
@@ -20,18 +20,17 @@
|
||||
|
||||
import sys
|
||||
import time
|
||||
+import signal
|
||||
from pcp import pmcc
|
||||
from pcp import pmapi
|
||||
from cpmapi import PM_CONTEXT_ARCHIVE
|
||||
|
||||
-import datetime
|
||||
-
|
||||
process_state_info = {}
|
||||
|
||||
PSSTAT_METRICS = ['kernel.uname.nodename', 'kernel.uname.release', 'kernel.uname.sysname',
|
||||
'kernel.uname.machine', 'hinv.ncpu', 'proc.psinfo.pid', 'proc.psinfo.guest_time',
|
||||
'proc.psinfo.utime', 'proc.psinfo.ppid', 'proc.psinfo.rt_priority', 'proc.psinfo.rss',
|
||||
- 'proc.id.uid_nm', 'proc.psinfo.stime', 'kernel.all.uptime', 'proc.psinfo.sname',
|
||||
+ 'proc.id.uid_nm', 'proc.psinfo.stime', 'kernel.all.boottime', 'proc.psinfo.sname',
|
||||
'proc.psinfo.start_time', 'proc.psinfo.vsize', 'proc.psinfo.priority',
|
||||
'proc.psinfo.nice', 'proc.psinfo.wchan_s', 'proc.psinfo.psargs', 'proc.psinfo.cmd',
|
||||
'proc.psinfo.ttyname', 'mem.physmem', 'proc.psinfo.policy']
|
||||
@@ -43,6 +42,7 @@ class StdoutPrinter:
|
||||
def Print(self, args):
|
||||
print(args)
|
||||
|
||||
+
|
||||
class NoneHandlingPrinterDecorator:
|
||||
def __init__(self, printer):
|
||||
self.printer = printer
|
||||
@@ -288,17 +288,18 @@ class ProcessStatusUtil:
|
||||
else:
|
||||
return c_systime - p_systime
|
||||
|
||||
- def age(self):
|
||||
+ def start(self):
|
||||
s_time = self.__metric_repository.current_value('proc.psinfo.start_time', self.instance)
|
||||
group = manager['psstat']
|
||||
- kernel_uptime = group['kernel.all.uptime'].netValues[0][2]
|
||||
- timefmt = "%H:%M:%S"
|
||||
- age = kernel_uptime - (s_time / 1000)
|
||||
- res = datetime.datetime.now() - datetime.timedelta(seconds=age)
|
||||
- if res.date() - datetime.datetime.now().date():
|
||||
- return res.date()
|
||||
+ kernel_boottime = group['kernel.all.boottime'].netValues[0][2]
|
||||
+ ts = group.contextCache.pmLocaltime(int(0.5 + kernel_boottime + (s_time / 1000)))
|
||||
+ if group.timestamp.tv_sec - int(0.5 + kernel_boottime + s_time / 1000) >= 24*60*60:
|
||||
+ # started one day or more ago, use MmmDD HH:MM
|
||||
+ return time.strftime("%b%d %H:%M", ts.struct_time())
|
||||
else:
|
||||
- return res.strftime(timefmt)
|
||||
+ # started less than one day ago, use HH:MM:SS
|
||||
+ return time.strftime("%H:%M:%S", ts.struct_time())
|
||||
+
|
||||
|
||||
def total_time(self):
|
||||
c_usertime = self.__metric_repository.current_value('proc.psinfo.stime', self.instance)
|
||||
@@ -338,7 +339,7 @@ class ProcessStatusUtil:
|
||||
|
||||
PIDINFO_PAIR = {"%cpu": ('%CPU', ProcessStatusUtil.system_percent),
|
||||
"%mem": ('%MEM', ProcessStatusUtil.mem),
|
||||
- "start": ("START\t", ProcessStatusUtil.age),
|
||||
+ "start": ("START\t", ProcessStatusUtil.start),
|
||||
"time": ("TIME\t", ProcessStatusUtil.total_time),
|
||||
"cls": ("CLS", ProcessStatusUtil.policy),
|
||||
"cmd": ("Command\t\t\t", ProcessStatusUtil.process_name_with_args),
|
||||
@@ -408,19 +409,19 @@ class DynamicProcessReporter:
|
||||
self.printer("%s%s%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\t%s" %
|
||||
(timestamp, value_indentation, process.user_name(), process.pid(), process.ppid(),
|
||||
process.priority(), total_percent, process.system_percent(), process.vsize(),
|
||||
- process.rss(), current_process_sname, process.age(), process.total_time(), wchan,
|
||||
+ process.rss(), current_process_sname, process.start(), process.total_time(), wchan,
|
||||
process_name))
|
||||
elif len(wchan) >= 15:
|
||||
self.printer("%s%s%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" %
|
||||
(timestamp, value_indentation, process.user_name(), process.pid(), process.ppid(),
|
||||
process.priority(), total_percent, process.system_percent(), process.vsize(),
|
||||
- process.rss(), current_process_sname, process.age(), process.total_time(), wchan,
|
||||
+ process.rss(), current_process_sname, process.start(), process.total_time(), wchan,
|
||||
process_name))
|
||||
else:
|
||||
self.printer("%s%s%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t\t\t%s" %
|
||||
(timestamp, value_indentation, process.user_name(), process.pid(), process.ppid(),
|
||||
process.priority(), total_percent, process.system_percent(), process.vsize(),
|
||||
- process.rss(), current_process_sname, process.age(), process.total_time(), wchan,
|
||||
+ process.rss(), current_process_sname, process.start(), process.total_time(), wchan,
|
||||
process_name))
|
||||
elif self.processStatOptions.colum_list is not None:
|
||||
header = "Timestamp" + '\t'
|
||||
@@ -497,7 +498,7 @@ class ProcessStatusReporter:
|
||||
self.printer("%s%s%s\t\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" % (
|
||||
timestamp, value_indentation, process.user_name(), process.pid(),
|
||||
process.system_percent(), process.total_percent(), process.vsize(), process.rss(),
|
||||
- process.tty_name(), process.ppid(), process.total_time(), process.age(),
|
||||
+ process.tty_name(), process.ppid(), process.total_time(), process.start(),
|
||||
process.process_name()))
|
||||
|
||||
elif self.processStatOptions.user_oriented_format:
|
||||
@@ -508,7 +509,7 @@ class ProcessStatusReporter:
|
||||
self.printer("%s%s%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" % (
|
||||
timestamp, value_indentation, process.user_name(), process.pid(),
|
||||
process.system_percent(), process.total_percent(), process.vsize(), process.rss(),
|
||||
- process.tty_name(), process.s_name(), process.total_time(), process.age(),
|
||||
+ process.tty_name(), process.s_name(), process.total_time(), process.start(),
|
||||
process.process_name()))
|
||||
|
||||
elif self.processStatOptions.command_filter_flag:
|
||||
@@ -549,106 +550,109 @@ class ProcessstatReport(pmcc.MetricGroupPrinter):
|
||||
return group['hinv.ncpu'].netValues[0][2]
|
||||
|
||||
def report(self, manager):
|
||||
- group = manager['psstat']
|
||||
- if group['proc.psinfo.utime'].netPrevValues is None:
|
||||
- # need two fetches to report rate converted counter metrics
|
||||
- return
|
||||
+ try:
|
||||
+ group = manager['psstat']
|
||||
+ if group['proc.psinfo.utime'].netPrevValues is None:
|
||||
+ # need two fetches to report rate converted counter metrics
|
||||
+ return
|
||||
|
||||
- if not group['hinv.ncpu'].netValues or not group['kernel.uname.sysname'].netValues:
|
||||
- return
|
||||
+ if not group['hinv.ncpu'].netValues or not group['kernel.uname.sysname'].netValues:
|
||||
+ return
|
||||
|
||||
- try:
|
||||
- if not self.Machine_info_count:
|
||||
- self.print_machine_info(group, manager)
|
||||
- self.Machine_info_count = 1
|
||||
- except IndexError:
|
||||
- # missing some metrics
|
||||
- return
|
||||
-
|
||||
- ts = group.contextCache.pmLocaltime(int(group.timestamp))
|
||||
- timestamp = time.strftime(ProcessStatOptions.timefmt, ts.struct_time())
|
||||
- interval_in_seconds = self.timeStampDelta(group)
|
||||
- header_indentation = " " if len(timestamp) < 9 else (len(timestamp) - 7) * " "
|
||||
- value_indentation = ((len(header_indentation) + 9) - len(timestamp)) * " "
|
||||
-
|
||||
- metric_repository = ReportingMetricRepository(group)
|
||||
-
|
||||
- # Doing this for one single print instance in case there is no count specified
|
||||
- if ProcessStatOptions.print_count is None:
|
||||
- ProcessStatOptions.print_count = 1
|
||||
-
|
||||
- # =============================================================================================================
|
||||
- if ProcessStatOptions.show_all_process:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds, printdecorator.Print,
|
||||
- ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
-
|
||||
- if ProcessStatOptions.empty_arg_flag:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds, printdecorator.Print,
|
||||
- ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
-
|
||||
- if ProcessStatOptions.pid_filter_flag:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds, printdecorator.Print,
|
||||
- ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
- if ProcessStatOptions.ppid_filter_flag:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds, printdecorator.Print,
|
||||
- ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
-
|
||||
- if ProcessStatOptions.command_filter_flag:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds, printdecorator.Print,
|
||||
- ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
-
|
||||
- if ProcessStatOptions.user_oriented_format:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds, printdecorator.Print,
|
||||
- ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
-
|
||||
- if ProcessStatOptions.username_filter_flag:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds, printdecorator.Print,
|
||||
- ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
-
|
||||
- # ================================================================
|
||||
- if ProcessStatOptions.selective_colum_flag:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = DynamicProcessReporter(process_report, process_filter, interval_in_seconds, printdecorator.Print,
|
||||
- ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
+ try:
|
||||
+ if not self.Machine_info_count:
|
||||
+ self.print_machine_info(group, manager)
|
||||
+ self.Machine_info_count = 1
|
||||
+ except IndexError:
|
||||
+ # missing some metrics
|
||||
+ return
|
||||
+
|
||||
+ ts = group.contextCache.pmLocaltime(int(group.timestamp))
|
||||
+ timestamp = time.strftime(ProcessStatOptions.timefmt, ts.struct_time())
|
||||
+ interval_in_seconds = self.timeStampDelta(group)
|
||||
+ header_indentation = " " if len(timestamp) < 9 else (len(timestamp) - 7) * " "
|
||||
+ value_indentation = ((len(header_indentation) + 9) - len(timestamp)) * " "
|
||||
+
|
||||
+ metric_repository = ReportingMetricRepository(group)
|
||||
+
|
||||
+ # Doing this for one single print instance in case there is no count specified
|
||||
+ if ProcessStatOptions.print_count is None:
|
||||
+ ProcessStatOptions.print_count = 1
|
||||
+
|
||||
+ # ================================================================
|
||||
+ if ProcessStatOptions.show_all_process:
|
||||
+ process_report = ProcessStatus(metric_repository)
|
||||
+ process_filter = ProcessFilter(ProcessStatOptions)
|
||||
+ stdout = StdoutPrinter()
|
||||
+ printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
+ report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
+ printdecorator.Print, ProcessStatOptions)
|
||||
+ report.print_report(timestamp, header_indentation, value_indentation)
|
||||
+
|
||||
+ if ProcessStatOptions.empty_arg_flag:
|
||||
+ process_report = ProcessStatus(metric_repository)
|
||||
+ process_filter = ProcessFilter(ProcessStatOptions)
|
||||
+ stdout = StdoutPrinter()
|
||||
+ printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
+ report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
+ printdecorator.Print, ProcessStatOptions)
|
||||
+ report.print_report(timestamp, header_indentation, value_indentation)
|
||||
+
|
||||
+ if ProcessStatOptions.pid_filter_flag:
|
||||
+ process_report = ProcessStatus(metric_repository)
|
||||
+ process_filter = ProcessFilter(ProcessStatOptions)
|
||||
+ stdout = StdoutPrinter()
|
||||
+ printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
+ report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
+ printdecorator.Print, ProcessStatOptions)
|
||||
+ report.print_report(timestamp, header_indentation, value_indentation)
|
||||
+ if ProcessStatOptions.ppid_filter_flag:
|
||||
+ process_report = ProcessStatus(metric_repository)
|
||||
+ process_filter = ProcessFilter(ProcessStatOptions)
|
||||
+ stdout = StdoutPrinter()
|
||||
+ printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
+ report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
+ printdecorator.Print, ProcessStatOptions)
|
||||
+ report.print_report(timestamp, header_indentation, value_indentation)
|
||||
+
|
||||
+ if ProcessStatOptions.command_filter_flag:
|
||||
+ process_report = ProcessStatus(metric_repository)
|
||||
+ process_filter = ProcessFilter(ProcessStatOptions)
|
||||
+ stdout = StdoutPrinter()
|
||||
+ printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
+ report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
+ printdecorator.Print, ProcessStatOptions)
|
||||
+ report.print_report(timestamp, header_indentation, value_indentation)
|
||||
+
|
||||
+ if ProcessStatOptions.user_oriented_format:
|
||||
+ process_report = ProcessStatus(metric_repository)
|
||||
+ process_filter = ProcessFilter(ProcessStatOptions)
|
||||
+ stdout = StdoutPrinter()
|
||||
+ printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
+ report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
+ printdecorator.Print, ProcessStatOptions)
|
||||
+ report.print_report(timestamp, header_indentation, value_indentation)
|
||||
+
|
||||
+ if ProcessStatOptions.username_filter_flag:
|
||||
+ process_report = ProcessStatus(metric_repository)
|
||||
+ process_filter = ProcessFilter(ProcessStatOptions)
|
||||
+ stdout = StdoutPrinter()
|
||||
+ printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
+ report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
+ printdecorator.Print, ProcessStatOptions)
|
||||
+ report.print_report(timestamp, header_indentation, value_indentation)
|
||||
+
|
||||
+ # ================================================================
|
||||
+ if ProcessStatOptions.selective_colum_flag:
|
||||
+ process_report = ProcessStatus(metric_repository)
|
||||
+ process_filter = ProcessFilter(ProcessStatOptions)
|
||||
+ stdout = StdoutPrinter()
|
||||
+ printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
+ report = DynamicProcessReporter(process_report, process_filter, interval_in_seconds,
|
||||
+ printdecorator.Print, ProcessStatOptions)
|
||||
+ report.print_report(timestamp, header_indentation, value_indentation)
|
||||
+ finally:
|
||||
+ sys.stdout.flush()
|
||||
|
||||
|
||||
class ProcessStatOptions(pmapi.pmOptions):
|
||||
@@ -724,7 +728,7 @@ class ProcessStatOptions(pmapi.pmOptions):
|
||||
# """Override standard Pcp-ps option to show all process """
|
||||
return bool(opts in ['p', 'c', 'o', 'P', 'U'])
|
||||
|
||||
- def extraOptions(self, opts, optarg,index):
|
||||
+ def extraOptions(self, opts, optarg, index):
|
||||
if opts == 'e':
|
||||
ProcessStatOptions.show_all_process = True
|
||||
elif opts == 'c':
|
||||
@@ -812,6 +816,8 @@ if __name__ == "__main__":
|
||||
except pmapi.pmUsageErr as usage:
|
||||
usage.message()
|
||||
sys.exit(1)
|
||||
+ except IOError:
|
||||
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except KeyboardInterrupt:
|
||||
print("Interrupted")
|
||||
sys.exit(0)
|
||||
diff --git a/src/pcp/ps/test/process_state_util_reporter_test.py b/src/pcp/ps/test/process_state_util_reporter_test.py
|
||||
index f473351b5..e7a30364d 100755
|
||||
--- a/src/pcp/ps/test/process_state_util_reporter_test.py
|
||||
+++ b/src/pcp/ps/test/process_state_util_reporter_test.py
|
||||
@@ -15,7 +15,7 @@
|
||||
#
|
||||
from mock import Mock
|
||||
import unittest
|
||||
-from pcp_ps import ProcessStateReporter
|
||||
+from pcp_ps import ProcessStatusReporter
|
||||
|
||||
|
||||
class TestProcessStateReporter(unittest.TestCase):
|
||||
@@ -35,11 +35,12 @@ class TestProcessStateReporter(unittest.TestCase):
|
||||
|
||||
def test_print_report_with_user_name(self):
|
||||
self.options.show_all_process = True
|
||||
+ self.options.print_count=1
|
||||
process_stack_util = Mock()
|
||||
process_filter = Mock()
|
||||
printer = Mock()
|
||||
process_filter.filter_processes = Mock(return_value=self.processes)
|
||||
- reporter = ProcessStateReporter(process_stack_util, process_filter, 1.34, printer, self.options)
|
||||
+ reporter = ProcessStatusReporter(process_stack_util, process_filter, 1.34, printer, self.options)
|
||||
|
||||
reporter.print_report(123, " ", " ")
|
||||
|
||||
diff --git a/src/pcp/ps/test/process_statusutil_test.py b/src/pcp/ps/test/process_statusutil_test.py
|
||||
index 8ef45b451..7d2c9797e 100755
|
||||
--- a/src/pcp/ps/test/process_statusutil_test.py
|
||||
+++ b/src/pcp/ps/test/process_statusutil_test.py
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
import mock
|
||||
import unittest
|
||||
-from pcp_ps import ProcessStatus
|
||||
+from pcp_ps import ProcessStatusUtil
|
||||
|
||||
|
||||
class TestProcessStackUtil(unittest.TestCase):
|
||||
@@ -71,88 +71,90 @@ class TestProcessStackUtil(unittest.TestCase):
|
||||
def test_stack_referenced_size(self):
|
||||
self.skipTest(reason="Implement when suitable metric is found")
|
||||
|
||||
+ #These are blank spaces in assert case been addded
|
||||
+ #to match the format of function ouput.please don't remove
|
||||
def test_username(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
name = process_status_usage.user_name()
|
||||
- self.assertEqual(name, "test")
|
||||
+ self.assertEqual(name, "test ")
|
||||
|
||||
def test_Processname(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
name = process_status_usage.process_name()
|
||||
- self.assertEqual(name, "test")
|
||||
+ self.assertEqual(name, "test ")
|
||||
|
||||
def test_process_name_with_args(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
name = process_status_usage.process_name_with_args()
|
||||
- self.assertEqual(name, "test")
|
||||
+ self.assertEqual(name, "test ")
|
||||
|
||||
def test_vszie(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
vsize = process_status_usage.vsize()
|
||||
self.assertEqual(vsize, 1)
|
||||
|
||||
def test_rss(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
vsize = process_status_usage.rss()
|
||||
self.assertEqual(vsize, 1)
|
||||
|
||||
def test_mem(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
vsize = process_status_usage.mem()
|
||||
self.assertEqual(vsize, 100)
|
||||
|
||||
def test_pid(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
pid = process_status_usage.pid()
|
||||
- self.assertEqual(pid, 1)
|
||||
+ self.assertEqual(pid,'1 ')
|
||||
|
||||
def test_process_name(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
name = process_status_usage.process_name()
|
||||
- self.assertEqual(name, 'test')
|
||||
+ self.assertEqual(name, 'test ')
|
||||
|
||||
def test_user_id(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
user_id = process_status_usage.user_id()
|
||||
self.assertEqual(user_id, 1)
|
||||
|
||||
def test_s_name(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
name = process_status_usage.s_name()
|
||||
self.assertEqual(name, 'R')
|
||||
|
||||
def test_cpu_number(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
name = process_status_usage.cpu_number()
|
||||
self.assertEqual(name, 1)
|
||||
|
||||
def test_wchan_s(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
name = process_status_usage.wchan_s()
|
||||
- self.assertEqual(name, 'test')
|
||||
+ self.assertEqual(name, 'test ')
|
||||
|
||||
def test_priority(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
name = process_status_usage.priority()
|
||||
self.assertEqual(name, 1)
|
||||
|
||||
def test_tty_name(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
name = process_status_usage.tty_name()
|
||||
self.assertEqual(name, 'tty')
|
||||
|
||||
def test_start_time(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
name = process_status_usage.start_time()
|
||||
self.assertEqual(name, 1)
|
||||
|
||||
def test_func_state(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
name = process_status_usage.func_state()
|
||||
self.assertEqual(name, 'N/A')
|
||||
|
||||
def test_policy(self):
|
||||
- process_status_usage = ProcessStatus(1, 1.34, self.__metric_repository)
|
||||
+ process_status_usage = ProcessStatusUtil(1, 1.34, self.__metric_repository)
|
||||
name = process_status_usage.policy()
|
||||
self.assertEqual(name, 'FIFO')
|
||||
|
||||
--
|
||||
2.31.1
|
||||
|
||||
@ -1,315 +0,0 @@
|
||||
From 752bfc9fc16c1e432c3c7534416d2b2b0e3f0713 Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Mon, 29 May 2023 12:48:41 +0530
|
||||
Subject: [PATCH] Fixed PCP python utility issues
|
||||
|
||||
Fixed issue where initially created files by pmie failed to read
|
||||
Fixed broken pipe issue in pcp-mpstat utility
|
||||
Fixed exception in pcp-ps utility for process name
|
||||
|
||||
Orabug: 35434363
|
||||
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
|
||||
---
|
||||
qa/883 | 4 ++
|
||||
qa/883.out | 7 ++
|
||||
qa/915 | 6 +-
|
||||
qa/archives/GNUmakefile | 20 ++++++
|
||||
src/libpcp_web/src/query_parser.y | 21 ++++--
|
||||
src/pcp/mpstat/pcp-mpstat.py | 106 ++++++++++++++++++++----------
|
||||
src/pcp/ps/pcp-ps.py | 16 +++--
|
||||
7 files changed, 133 insertions(+), 47 deletions(-)
|
||||
|
||||
diff --git a/qa/883 b/qa/883
|
||||
index 4c798c352..fdc124fcb 100755
|
||||
--- a/qa/883
|
||||
+++ b/qa/883
|
||||
@@ -23,6 +23,7 @@ test -x $pcp_mpstat || _notrun "No pcp-mpstat(1) installed"
|
||||
pcp_mpstat="$python $pcp_mpstat"
|
||||
pcp_archive="-z -a archives/pcp-mpstat"
|
||||
pcp_archive2="-z -a archives/pcp-mpstat2"
|
||||
+pcp_archive3="-z -a archives/pcp-mpstat3"
|
||||
|
||||
# real QA test starts here
|
||||
export LC_TIME=POSIX
|
||||
@@ -66,6 +67,9 @@ pcp $pcp_archive2 mpstat -I SCPU -s 2
|
||||
echo && echo === pcp-mpstat hard interrupt usage without metrics
|
||||
pcp $pcp_archive2 mpstat -I CPU -s 2
|
||||
|
||||
+echo && echo === pcp-mpstat with log-once summary metric archive
|
||||
+pcp $pcp_archive3 mpstat
|
||||
+
|
||||
# success, all done
|
||||
status=0
|
||||
exit
|
||||
diff --git a/qa/883.out b/qa/883.out
|
||||
index 2a975cd29..be7088cb8 100644
|
||||
--- a/qa/883.out
|
||||
+++ b/qa/883.out
|
||||
@@ -198,3 +198,10 @@ Timestamp CPU
|
||||
23:52:55 1
|
||||
23:52:55 2
|
||||
23:52:55 3
|
||||
+
|
||||
+=== pcp-mpstat with log-once summary metric archive
|
||||
+Linux 4.4.0-31-generic (ram-Lenovo) 08/02/16 x86_64 (4 CPU)
|
||||
+
|
||||
+Timestamp CPU %usr %nice %sys %iowait %irq %soft %steal %guest %nice %idle
|
||||
+23:52:55 all 4.58 0.0 26.38 4.58 0.0 0.0 0.0 0.0 0.0 63.12
|
||||
+23:52:56 all 4.75 0.0 27.0 4.5 0.0 0.0 0.0 0.0 0.0 63.0
|
||||
diff --git a/qa/915 b/qa/915
|
||||
index 3217f9b62..e4bab5fcd 100755
|
||||
--- a/qa/915
|
||||
+++ b/qa/915
|
||||
@@ -131,11 +131,13 @@ echo quit | pmlc -h localhost -P 2>&1 | _filter
|
||||
echo; echo "== checking bad pmlogger access, expect no control"
|
||||
echo quit | pmlc -h $interface -P 2>&1 | _filter
|
||||
|
||||
-echo; echo "== checking bad pmproxy access, expect no values"
|
||||
+echo; echo "== checking bad pmproxy access, expect no values" | tee -a $here/$seq.full
|
||||
pminfo -f -h localhost@$interface pmcd.feature.local 2>&1 | _filter
|
||||
+cat $PCP_LOG_DIR/pmproxy/pmproxy.log >> $here/$seq.full
|
||||
|
||||
-echo; echo "== checking loop pmproxy access, expecting success"
|
||||
+echo; echo "== checking loop pmproxy access, expecting success" | tee -a $here/$seq.full
|
||||
pminfo -f -h localhost@localhost pmcd.feature.local 2>&1 | _filter
|
||||
+cat $PCP_LOG_DIR/pmproxy/pmproxy.log >> $here/$seq.full
|
||||
|
||||
# success, all done
|
||||
status=0
|
||||
diff --git a/qa/archives/GNUmakefile b/qa/archives/GNUmakefile
|
||||
index 31d51dd45..005ca231f 100644
|
||||
--- a/qa/archives/GNUmakefile
|
||||
+++ b/qa/archives/GNUmakefile
|
||||
@@ -150,3 +150,23 @@ pcp-mpstat2.0:
|
||||
pmlogextract -c tmp/config pcp-mpstat pcp-mpstat2
|
||||
rm -rf tmp
|
||||
|
||||
+# variant of the mpstat2 archive with log-once summary metrics
|
||||
+#
|
||||
+pcp-mpstat3.0: pcp-mpstat2.0
|
||||
+ rm -rf tmp
|
||||
+ mkdir tmp
|
||||
+ pminfo -a pcp-mpstat2 \
|
||||
+ | sed \
|
||||
+ -e '/^kernel.uname./d' \
|
||||
+ -e '/^hinv.ncpu/d' \
|
||||
+ > tmp/config-body
|
||||
+ pminfo -a pcp-mpstat2 \
|
||||
+ | grep -E '^kernel.uname.|^hinv.ncpu' \
|
||||
+ > tmp/config-head
|
||||
+ pmlogextract -c tmp/config-head -s 1 pcp-mpstat2 pcp-mpstat3-head
|
||||
+ pmlogextract -c tmp/config-body pcp-mpstat2 pcp-mpstat3-body
|
||||
+ pmlogextract pcp-mpstat3-head pcp-mpstat3-body pcp-mpstat3
|
||||
+ mv pcp-mpstat3.0 tmp
|
||||
+ ../src/stripmark tmp/pcp-mpstat3.0 pcp-mpstat3.0
|
||||
+ rm -rf tmp pcp-mpstat3-head.* pcp-mpstat3-body.*
|
||||
+
|
||||
diff --git a/src/libpcp_web/src/query_parser.y b/src/libpcp_web/src/query_parser.y
|
||||
index 20b92489c..7badd8711 100644
|
||||
--- a/src/libpcp_web/src/query_parser.y
|
||||
+++ b/src/libpcp_web/src/query_parser.y
|
||||
@@ -1118,10 +1118,23 @@ newmetric(char *name)
|
||||
else
|
||||
node = newnode(N_EQ);
|
||||
|
||||
- node->left = newnode(N_NAME);
|
||||
- node->left->value = sdsnew("metric.name");
|
||||
- node->right = newnode(N_STRING);
|
||||
- node->right->value = sdsnew(name);
|
||||
+ if (node) {
|
||||
+ if ((node->left = newnode(N_NAME)) == NULL) {
|
||||
+ sdsfree(name);
|
||||
+ free(node);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ if ((node->right = newnode(N_STRING)) == NULL) {
|
||||
+ sdsfree(name);
|
||||
+ free(node->left);
|
||||
+ free(node);
|
||||
+ return NULL;
|
||||
+ }
|
||||
+ node->left->value = sdsnew("metric.name");
|
||||
+ node->right->value = name;
|
||||
+ } else {
|
||||
+ sdsfree(name);
|
||||
+ }
|
||||
return node;
|
||||
}
|
||||
|
||||
diff --git a/src/pcp/mpstat/pcp-mpstat.py b/src/pcp/mpstat/pcp-mpstat.py
|
||||
index ad73fd6c8..5b761a5a7 100755
|
||||
--- a/src/pcp/mpstat/pcp-mpstat.py
|
||||
+++ b/src/pcp/mpstat/pcp-mpstat.py
|
||||
@@ -15,6 +15,7 @@
|
||||
# pylint: disable=bad-whitespace,line-too-long
|
||||
# pylint: disable=redefined-outer-name,unnecessary-lambda
|
||||
#
|
||||
+import signal
|
||||
from pcp import pmapi
|
||||
from pcp import pmcc
|
||||
import sys
|
||||
@@ -588,6 +589,11 @@ class DisplayOptions:
|
||||
|
||||
class MpstatReport(pmcc.MetricGroupPrinter):
|
||||
Machine_info_count = 0
|
||||
+ ncpu = 1
|
||||
+ machine = ''
|
||||
+ release = ''
|
||||
+ sysname = ''
|
||||
+ nodename = ''
|
||||
|
||||
def __init__(self, cpu_util_reporter, total_interrupt_usage_reporter, soft_interrupt_usage_reporter, hard_interrupt_usage_reporter):
|
||||
self.cpu_util_reporter = cpu_util_reporter
|
||||
@@ -601,6 +607,7 @@ class MpstatReport(pmcc.MetricGroupPrinter):
|
||||
return s + u / 1000000.0
|
||||
|
||||
def print_machine_info(self,group, context):
|
||||
+ self.get_summary_metrics(group)
|
||||
timestamp = context.pmLocaltime(group.timestamp.tv_sec)
|
||||
# Please check strftime(3) for different formatting options.
|
||||
# Also check TZ and LC_TIME environment variables for more information
|
||||
@@ -608,44 +615,71 @@ class MpstatReport(pmcc.MetricGroupPrinter):
|
||||
time_string = time.strftime("%x", timestamp.struct_time())
|
||||
|
||||
header_string = ''
|
||||
- header_string += group['kernel.uname.sysname'].netValues[0][2] + ' '
|
||||
- header_string += group['kernel.uname.release'].netValues[0][2] + ' '
|
||||
- header_string += '(' + group['kernel.uname.nodename'].netValues[0][2] + ') '
|
||||
+ header_string += self.sysname + ' '
|
||||
+ header_string += self.release + ' '
|
||||
+ header_string += '(' + self.nodename + ') '
|
||||
header_string += time_string + ' '
|
||||
- header_string += group['kernel.uname.machine'].netValues[0][2] + ' '
|
||||
- no_cpu = self.get_ncpu(group)
|
||||
- print("%s (%s CPU)" % (header_string,no_cpu))
|
||||
-
|
||||
- def get_ncpu(self,group):
|
||||
- return group['hinv.ncpu'].netValues[0][2]
|
||||
+ header_string += self.machine + ' '
|
||||
+ header_string += '(' + str(self.ncpu) + ' CPU)'
|
||||
+ print(header_string)
|
||||
+
|
||||
+ def get_summary_metrics(self,group):
|
||||
+ # extract metrics safely which may have 'logged-once' semantics;
|
||||
+ # we checked earlier so know the metrics exist once at least, so
|
||||
+ # fallback to previous observed value if not in latest sample -
|
||||
+ # always try though in case a later sample finds updated values.
|
||||
+ try:
|
||||
+ self.ncpu = group['hinv.ncpu'].netValues[0][2]
|
||||
+ except IndexError:
|
||||
+ pass
|
||||
+ try:
|
||||
+ self.sysname = group['kernel.uname.sysname'].netValues[0][2]
|
||||
+ except IndexError:
|
||||
+ pass
|
||||
+ try:
|
||||
+ self.machine = group['kernel.uname.machine'].netValues[0][2]
|
||||
+ except IndexError:
|
||||
+ pass
|
||||
+ try:
|
||||
+ self.release = group['kernel.uname.release'].netValues[0][2]
|
||||
+ except IndexError:
|
||||
+ pass
|
||||
+ try:
|
||||
+ self.nodename = group['kernel.uname.nodename'].netValues[0][2]
|
||||
+ except IndexError:
|
||||
+ pass
|
||||
|
||||
def report(self,manager):
|
||||
- group = manager['mpstat']
|
||||
- if group['kernel.all.cpu.user'].netPrevValues is None:
|
||||
- # need two fetches to report rate converted counter metrics
|
||||
- return
|
||||
-
|
||||
- if self.Machine_info_count == 0:
|
||||
- self.print_machine_info(group, manager)
|
||||
- self.Machine_info_count = 1
|
||||
-
|
||||
- timestamp = group.contextCache.pmCtime(int(group.timestamp)).rstrip().split()
|
||||
- interval_in_seconds = self.timeStampDelta(group)
|
||||
- metric_repository = MetricRepository(group)
|
||||
- display_options = DisplayOptions(MpstatOptions)
|
||||
-
|
||||
- if display_options.display_cpu_usage_summary():
|
||||
- cpu_util = CpuUtil(interval_in_seconds, metric_repository)
|
||||
- self.cpu_util_reporter.print_report(cpu_util, timestamp[3])
|
||||
- if display_options.display_total_cpu_usage():
|
||||
- total_interrupt_usage = TotalInterruptUsage(interval_in_seconds, metric_repository)
|
||||
- self.total_interrupt_usage_reporter.print_report(total_interrupt_usage, timestamp[3])
|
||||
- if display_options.display_hard_interrupt_usage():
|
||||
- hard_interrupt_usage = HardInterruptUsage(interval_in_seconds, metric_repository, interrupts_list)
|
||||
- self.hard_interrupt_usage_reporter.print_report(hard_interrupt_usage,timestamp[3])
|
||||
- if display_options.display_soft_interrupt_usage():
|
||||
- soft_interrupt_usage = SoftInterruptUsage(interval_in_seconds, metric_repository, soft_interrupts_list)
|
||||
- self.soft_interrupt_usage_reporter.print_report(soft_interrupt_usage, timestamp[3])
|
||||
+ try:
|
||||
+ group = manager['mpstat']
|
||||
+ if group['kernel.all.cpu.user'].netPrevValues is None:
|
||||
+ # need two fetches to report rate converted counter metrics
|
||||
+ self.get_summary_metrics(group)
|
||||
+ return
|
||||
+
|
||||
+ if self.Machine_info_count == 0:
|
||||
+ self.print_machine_info(group, manager)
|
||||
+ self.Machine_info_count = 1
|
||||
+
|
||||
+ timestamp = group.contextCache.pmCtime(int(group.timestamp)).rstrip().split()
|
||||
+ interval_in_seconds = self.timeStampDelta(group)
|
||||
+ metric_repository = MetricRepository(group)
|
||||
+ display_options = DisplayOptions(MpstatOptions)
|
||||
+
|
||||
+ if display_options.display_cpu_usage_summary():
|
||||
+ cpu_util = CpuUtil(interval_in_seconds, metric_repository)
|
||||
+ self.cpu_util_reporter.print_report(cpu_util, timestamp[3])
|
||||
+ if display_options.display_total_cpu_usage():
|
||||
+ total_interrupt_usage = TotalInterruptUsage(interval_in_seconds, metric_repository)
|
||||
+ self.total_interrupt_usage_reporter.print_report(total_interrupt_usage, timestamp[3])
|
||||
+ if display_options.display_hard_interrupt_usage():
|
||||
+ hard_interrupt_usage = HardInterruptUsage(interval_in_seconds, metric_repository, interrupts_list)
|
||||
+ self.hard_interrupt_usage_reporter.print_report(hard_interrupt_usage,timestamp[3])
|
||||
+ if display_options.display_soft_interrupt_usage():
|
||||
+ soft_interrupt_usage = SoftInterruptUsage(interval_in_seconds, metric_repository, soft_interrupts_list)
|
||||
+ self.soft_interrupt_usage_reporter.print_report(soft_interrupt_usage, timestamp[3])
|
||||
+ finally:
|
||||
+ sys.stdout.flush()
|
||||
|
||||
|
||||
|
||||
@@ -680,5 +714,7 @@ if __name__ == '__main__':
|
||||
except pmapi.pmUsageErr as usage:
|
||||
usage.message()
|
||||
sys.exit(1)
|
||||
+ except IOError:
|
||||
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
diff --git a/src/pcp/ps/pcp-ps.py b/src/pcp/ps/pcp-ps.py
|
||||
index 6e2e9f4ac..dbd556556 100755
|
||||
--- a/src/pcp/ps/pcp-ps.py
|
||||
+++ b/src/pcp/ps/pcp-ps.py
|
||||
@@ -195,12 +195,16 @@ class ProcessStatusUtil:
|
||||
return data
|
||||
|
||||
def process_name(self):
|
||||
- data = self.__metric_repository.current_value('proc.psinfo.cmd', self.instance)[:20]
|
||||
- if len(data) < 20:
|
||||
- whitespace = 20 - len(data)
|
||||
- res = data.ljust(whitespace + len(data), ' ')
|
||||
- return res
|
||||
- else:
|
||||
+ try:
|
||||
+ data = self.__metric_repository.current_value('proc.psinfo.cmd', self.instance)[:20]
|
||||
+ if len(data) < 20:
|
||||
+ whitespace = 20 - len(data)
|
||||
+ res = data.ljust(whitespace + len(data), ' ')
|
||||
+ return res
|
||||
+ else:
|
||||
+ return data
|
||||
+ except TypeError:
|
||||
+ data = '-'
|
||||
return data
|
||||
|
||||
def process_name_with_args(self):
|
||||
--
|
||||
2.39.3
|
||||
|
||||
@ -1,562 +0,0 @@
|
||||
From e6522f0be49a214eb9aacfbc0e948b2c523457ab Mon Sep 17 00:00:00 2001
|
||||
From: Sagar Sagar <sagar.sagar@oracle.com>
|
||||
Date: Mon, 4 Sep 2023 11:19:19 +0000
|
||||
Subject: [PATCH] pcp-meminfo : initial PCP implementation of meminfo
|
||||
|
||||
This patch introduces the pcp-meminfo utility to the pcp.
|
||||
It gives the output like /proc/meminfo
|
||||
|
||||
Signed-off-by: Mohith Kumar Thummaluru <mohith.k.kumar.thummaluru@oracle.com>
|
||||
upstream commit-links:- 4ec9bbbb264d2766e73512ffafdaf606f17b79a0
|
||||
125bde660464b40a06a3b0db3fa7acf99bd2b4d4
|
||||
a5cc36434c767069d2a8e6466d68fd472e56586e
|
||||
38d06784b7c6d883ce74e9bbc61601304c728791
|
||||
5d249b060d3f961b242dadd28302497313652ddd
|
||||
Orabug: 35759707
|
||||
Signed-off-by: Sagar Sagar <sagar.sagar@oracle.com>
|
||||
---
|
||||
build/rpm/pcp.spec.in | 2 +-
|
||||
build/rpm/redhat.spec | 2 +-
|
||||
qa/1988 | 39 +++++++
|
||||
qa/archives/GNUmakefile | 2 +-
|
||||
qa/archives/mk.meminfo | 68 ++++++++++++
|
||||
qa/group | 2 +
|
||||
src/pcp/GNUmakefile | 1 +
|
||||
src/pcp/meminfo/GNUmakefile | 44 ++++++++
|
||||
src/pcp/meminfo/pcp-meminfo.1 | 77 +++++++++++++
|
||||
src/pcp/meminfo/pcp-meminfo.py | 195 +++++++++++++++++++++++++++++++++
|
||||
10 files changed, 429 insertions(+), 3 deletions(-)
|
||||
create mode 100755 qa/1988
|
||||
create mode 100755 qa/archives/mk.meminfo
|
||||
create mode 100755 src/pcp/meminfo/GNUmakefile
|
||||
create mode 100755 src/pcp/meminfo/pcp-meminfo.1
|
||||
create mode 100755 src/pcp/meminfo/pcp-meminfo.py
|
||||
|
||||
diff --git a/build/rpm/pcp.spec.in b/build/rpm/pcp.spec.in
|
||||
index 9e27061d3..95459942c 100755
|
||||
--- a/build/rpm/pcp.spec.in
|
||||
+++ b/build/rpm/pcp.spec.in
|
||||
@@ -2262,7 +2262,7 @@ basic_manifest | grep -E -e 'pmiostat|pmrep|dstat|htop|pcp2csv' \
|
||||
-e 'pcp-atop|pcp-dmcache|pcp-dstat|pcp-free|pcp-htop' \
|
||||
-e 'pcp-ipcs|pcp-iostat|pcp-lvmcache|pcp-mpstat' \
|
||||
-e 'pcp-numastat|pcp-pidstat|pcp-shping|pcp-tapestat' \
|
||||
- -e 'pcp-uptime|pcp-verify|pcp-ss|pcp-ps' | \
|
||||
+ -e 'pcp-uptime|pcp-verify|pcp-ss|pcp-ps|pcp-meminfo' | \
|
||||
cull 'selinux|pmlogconf|pmieconf|pmrepconf' >pcp-system-tools-files
|
||||
basic_manifest | keep 'sar2pcp' >pcp-import-sar2pcp-files
|
||||
basic_manifest | keep 'iostat2pcp' >pcp-import-iostat2pcp-files
|
||||
diff --git a/build/rpm/redhat.spec b/build/rpm/redhat.spec
|
||||
index adc3a6f7f..5f54a5232 100644
|
||||
--- a/build/rpm/redhat.spec
|
||||
+++ b/build/rpm/redhat.spec
|
||||
@@ -2434,7 +2434,7 @@ basic_manifest | grep -E -e 'pmiostat|pmrep|dstat|htop|pcp2csv' \
|
||||
-e 'pcp-atop|pcp-dmcache|pcp-dstat|pcp-free|pcp-htop' \
|
||||
-e 'pcp-ipcs|pcp-iostat|pcp-lvmcache|pcp-mpstat' \
|
||||
-e 'pcp-numastat|pcp-pidstat|pcp-shping|pcp-tapestat' \
|
||||
- -e 'pcp-uptime|pcp-verify|pcp-ss|pcp-ps' | \
|
||||
+ -e 'pcp-uptime|pcp-verify|pcp-ss|pcp-ps|pcp-meminfo' | \
|
||||
cull 'selinux|pmlogconf|pmieconf|pmrepconf' >pcp-system-tools-files
|
||||
|
||||
basic_manifest | keep 'sar2pcp' >pcp-import-sar2pcp-files
|
||||
diff --git a/qa/1988 b/qa/1988
|
||||
new file mode 100755
|
||||
index 000000000..8dd249134
|
||||
--- /dev/null
|
||||
+++ b/qa/1988
|
||||
@@ -0,0 +1,39 @@
|
||||
+#!/bin/sh
|
||||
+# PCP QA Test No. 1987
|
||||
+# Exercise various pcp-meminfo(1) command options.
|
||||
+#
|
||||
+# Copyright (c) 2023 Oracle and/or its affiliates.
|
||||
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
+#
|
||||
+
|
||||
+seq=`basename $0`
|
||||
+echo "QA output created by $seq"
|
||||
+
|
||||
+. ./common.python
|
||||
+
|
||||
+__version=`$python --version 2>&1 | sed -e 's/Python //'`
|
||||
+case "$__version"
|
||||
+in
|
||||
+ 2.7.5) _notrun "Python $__version reports start seconds off-by one for some processes"
|
||||
+ ;;
|
||||
+esac
|
||||
+
|
||||
+status=1 # failure is the default!
|
||||
+$sudo rm -rf $tmp.* $seq.full
|
||||
+trap "cd $here; rm -rf $tmp.*; exit \$status" 0 1 2 3 15
|
||||
+
|
||||
+pcp_meminfo="$PCP_BINADM_DIR/pcp-meminfo"
|
||||
+test -x $pcp_meminfo || _notrun "No pcp-meminfo(1) installed"
|
||||
+pcp_meminfo="$python $pcp_meminfo"
|
||||
+
|
||||
+# real QA test starts here
|
||||
+echo && echo pcp-meminfo output : Display default output
|
||||
+PCP_ARCHIVE="archives/pcp-meminfo" PCP_HOSTZONE=1 PCP_ORIGIN=1 $pcp_meminfo
|
||||
+
|
||||
+archive_first="-a archives/pcp-meminfo -z -O +1"
|
||||
+
|
||||
+echo && echo pcp-meminfo output : Display output when given specified number of samples
|
||||
+pcp $archive_first meminfo -s 3
|
||||
+
|
||||
+status=0
|
||||
+exit
|
||||
diff --git a/qa/archives/GNUmakefile b/qa/archives/GNUmakefile
|
||||
index 005ca231f..2abf6425f 100644
|
||||
--- a/qa/archives/GNUmakefile
|
||||
+++ b/qa/archives/GNUmakefile
|
||||
@@ -65,7 +65,7 @@ SCRIPTS = mk.diff mk.gap mk.mysql mk.schizo mk.foo+ \
|
||||
mk.sample_expr mk.tzchange mk.arch-all \
|
||||
mk.atop mk.hotatop mk.atop-boot mk.atop-nvidia mk.atop-threads \
|
||||
mk.log-derived mk.vmstat mk.rep mk.procsched \
|
||||
- mk.ipcs mk.value-test mk.rank-pred mk.cputime \
|
||||
+ mk.ipcs mk.value-test mk.rank-pred mk.cputime mk.meminfo \
|
||||
mk.sample-labels mk.pmproxy mk.mmv.help
|
||||
|
||||
CONFIGS = config.verify config.shping YWhcCi.toium.config \
|
||||
diff --git a/qa/archives/mk.meminfo b/qa/archives/mk.meminfo
|
||||
new file mode 100755
|
||||
index 000000000..9b50717e8
|
||||
--- /dev/null
|
||||
+++ b/qa/archives/mk.meminfo
|
||||
@@ -0,0 +1,68 @@
|
||||
+#!/bin/sh
|
||||
+#
|
||||
+# remake the free archive ...
|
||||
+# this archive is intended to be checked in and not remade, this script is
|
||||
+# simply a record of how it was created
|
||||
+#
|
||||
+
|
||||
+. /etc/pcp.env
|
||||
+
|
||||
+tmp=/var/tmp/$$
|
||||
+rm -f $tmp.*
|
||||
+trap "rm -f $tmp.*; exit 0" 0 1 2 3 15
|
||||
+
|
||||
+cat <<End-of-File >>$tmp.config
|
||||
+log mandatory on 1 sec {
|
||||
+ mem.physmem
|
||||
+ mem.util.free
|
||||
+ mem.util.available
|
||||
+ mem.util.bufmem
|
||||
+ mem.util.cached
|
||||
+ mem.util.swapCached
|
||||
+ mem.util.active
|
||||
+ mem.util.inactive
|
||||
+ mem.util.active_anon
|
||||
+ mem.util.inactive_anon
|
||||
+ mem.util.active_file
|
||||
+ mem.util.inactive_file
|
||||
+ mem.util.unevictable
|
||||
+ mem.util.mlocked
|
||||
+ mem.util.swapTotal
|
||||
+ mem.util.swapFree
|
||||
+ mem.util.dirty
|
||||
+ mem.util.writeback
|
||||
+ mem.util.anonpages
|
||||
+ mem.util.mapped
|
||||
+ mem.util.shared
|
||||
+ mem.util.slab
|
||||
+ mem.util.slabReclaimable
|
||||
+ mem.util.slabUnreclaimable
|
||||
+ mem.util.kernelStack
|
||||
+ mem.util.pageTables
|
||||
+ mem.util.NFS_Unstable
|
||||
+ mem.util.bounce
|
||||
+ mem.vmstat.nr_writeback_temp
|
||||
+ mem.util.commitLimit
|
||||
+ mem.util.committed_AS
|
||||
+ mem.util.vmallocTotal
|
||||
+ mem.util.vmallocUsed
|
||||
+ mem.util.vmallocChunk
|
||||
+ mem.util.corrupthardware
|
||||
+ mem.util.anonhugepages
|
||||
+ mem.vmstat.nr_shmem_hugepages
|
||||
+ mem.vmstat.nr_shmem_pmdmapped
|
||||
+ mem.zoneinfo.nr_free_cma
|
||||
+ mem.util.hugepagesTotal
|
||||
+ mem.util.hugepagesFree
|
||||
+ mem.util.hugepagesRsvd
|
||||
+ mem.util.hugepagesSurp
|
||||
+ hinv.hugepagesize
|
||||
+ mem.util.directMap4k
|
||||
+ mem.util.directMap2M
|
||||
+ mem.util.directMap1G
|
||||
+}
|
||||
+End-of-File
|
||||
+
|
||||
+rm -f pcp-meminfo.0 pcp-meminfo.meta pcp-meminfo.index
|
||||
+
|
||||
+pmlogger -s 10 -c $tmp.config pcp-meminfo
|
||||
diff --git a/qa/group b/qa/group
|
||||
index 63c63b7f7..96b901278 100644
|
||||
--- a/qa/group
|
||||
+++ b/qa/group
|
||||
@@ -367,6 +367,7 @@ pidstat
|
||||
iostat
|
||||
tapestat
|
||||
ipcs
|
||||
+meminfo
|
||||
mpstat
|
||||
|
||||
# full test search
|
||||
@@ -1976,8 +1977,9 @@ x11
|
||||
1984 pmlogconf pmda.redis local
|
||||
1985 pmfind local valgrind
|
||||
1986 pmfind local
|
||||
+1988 pcp meminfo python local
|
||||
2101 pmda.sockets local security
|
||||
2104 libpcp local security
|
||||
2100 pmproxy local security
|
||||
2105 libpcp pmcd local security pmcd.pdu
|
||||
4751 libpcp threads valgrind local pcp helgrind
|
||||
diff --git a/src/pcp/GNUmakefile b/src/pcp/GNUmakefile
|
||||
index bd70c1395..8eee01260 100644
|
||||
--- a/src/pcp/GNUmakefile
|
||||
+++ b/src/pcp/GNUmakefile
|
||||
@@ -24,6 +24,7 @@ SUBDIRS = \
|
||||
htop \
|
||||
iostat \
|
||||
ipcs \
|
||||
+ meminfo \
|
||||
mpstat \
|
||||
numastat \
|
||||
pidstat \
|
||||
diff --git a/src/pcp/meminfo/GNUmakefile b/src/pcp/meminfo/GNUmakefile
|
||||
new file mode 100755
|
||||
index 000000000..98411a7dc
|
||||
--- /dev/null
|
||||
+++ b/src/pcp/meminfo/GNUmakefile
|
||||
@@ -0,0 +1,44 @@
|
||||
+#
|
||||
+# Copyright (c) 2023 Oracle and/or its affiliates.
|
||||
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
+#
|
||||
+# This program is free software; you can redistribute it and/or modify it
|
||||
+# under the terms of the GNU General Public License as published by the
|
||||
+# Free Software Foundation; either version 2 of the License, or (at your
|
||||
+# option) any later version.
|
||||
+#
|
||||
+# This program is distributed in the hope that it will be useful, but
|
||||
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
+# for more details.
|
||||
+#
|
||||
+
|
||||
+TOPDIR = ../../..
|
||||
+include $(TOPDIR)/src/include/builddefs
|
||||
+
|
||||
+TARGET = pcp-meminfo
|
||||
+SCRIPT = $(TARGET).py
|
||||
+MAN_SECTION = 1
|
||||
+MAN_PAGES = $(TARGET).$(MAN_SECTION)
|
||||
+MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION)
|
||||
+
|
||||
+default: $(SCRIPT) $(MAN_PAGES)
|
||||
+
|
||||
+include $(BUILDRULES)
|
||||
+
|
||||
+install: default
|
||||
+ifeq "$(HAVE_PYTHON)" "true"
|
||||
+ $(INSTALL) -m 755 $(SCRIPT) $(PCP_BINADM_DIR)/$(TARGET)
|
||||
+ @$(INSTALL_MAN)
|
||||
+endif
|
||||
+
|
||||
+default_pcp : default
|
||||
+
|
||||
+install_pcp : install
|
||||
+
|
||||
+check:: $(SCRIPT)
|
||||
+ $(PYLINT) $^
|
||||
+
|
||||
+check :: $(MAN_PAGES)
|
||||
+ $(MANLINT) $^
|
||||
+
|
||||
diff --git a/src/pcp/meminfo/pcp-meminfo.1 b/src/pcp/meminfo/pcp-meminfo.1
|
||||
new file mode 100755
|
||||
index 000000000..462dd8dbc
|
||||
--- /dev/null
|
||||
+++ b/src/pcp/meminfo/pcp-meminfo.1
|
||||
@@ -0,0 +1,77 @@
|
||||
+'\"! tbl | mmdoc
|
||||
+'\"macro stdmacro
|
||||
+.\"
|
||||
+.\" Copyright (c) 2023 Oracle and/or its affiliates.
|
||||
+.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
+.\"
|
||||
+.\" This program is free software; you can redistribute it and/or modify it
|
||||
+.\" under the terms of the GNU General Public License as published by the
|
||||
+.\" Free Software Foundation; either version 2 of the License, or (at your
|
||||
+.\" option) any later version.
|
||||
+.\"
|
||||
+.\" This program is distributed in the hope that it will be useful, but
|
||||
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
+.\" or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
+.\" for more details.
|
||||
+.\"
|
||||
+.TH PCP-MEMINFO 1 "PCP" "Performance Co-Pilot"
|
||||
+.SH NAME
|
||||
+\f3pcp-meminfo\f1 \- Report statistics for System Memory.
|
||||
+.SH SYNOPSIS
|
||||
+\f3pcp\f1
|
||||
+[\f2pcp\ options\f1]
|
||||
+\f3meminfo\f1
|
||||
+[\f3\-s\f1 \f2samples\f1]
|
||||
+[\f3\-a\f1 \f2archive\f1]
|
||||
+[\f3\-S\f1 \f2start_time\f1]
|
||||
+[\f3\-T\f1 \f2end_time\f1]
|
||||
+.SH DESCRIPTION
|
||||
+The
|
||||
+.B pcp-meminfo
|
||||
+command is used for viewing the different kinds of stats related to memory.
|
||||
+Using various options it helps a user to analyze useful information related to
|
||||
+the memory availability.
|
||||
+This information includes total memory, memory available, shared memory, etc.
|
||||
+By default
|
||||
+.B pcp-meminfo
|
||||
+reports live data for the local host.
|
||||
+.SH OPTIONS
|
||||
+.TP
|
||||
+\fB\-a\fP, \fB\-\-archive\fP
|
||||
+Fetch /proc/meminfo for a specified archive file
|
||||
+.TP
|
||||
+\fB\-s\fP, \fB\-\-samples\fP
|
||||
+Get the meminfo for specified number of samples count
|
||||
+.TP
|
||||
+\fB\-S\fP, \fB\-\-START TIME\fP
|
||||
+Filter the samples from the archive from the given time
|
||||
+.TP
|
||||
+\fB\-T\fP, \fB\-\-END TIME\fP
|
||||
+Filter the samples from the archive till the given time
|
||||
+.TP
|
||||
+\fB\-V\fR, \fB\-\-version\fR
|
||||
+Display version number and exit.
|
||||
+.TP
|
||||
+\fB\-?\fR, \fB\-\-help\fR
|
||||
+Display usage message and exit.
|
||||
+.SH NOTES
|
||||
+.B pcp-meminfo
|
||||
+collects information from
|
||||
+.BR /proc/meminfo
|
||||
+and aims to be command line and output compatible with it.
|
||||
+.SH PCP ENVIRONMENT
|
||||
+Environment variables with the prefix \fBPCP_\fP are used to parameterize
|
||||
+the file and directory names used by PCP.
|
||||
+On each installation, the
|
||||
+file \fI/etc/pcp.conf\fP contains the local values for these variables.
|
||||
+The \fB$PCP_CONF\fP variable may be used to specify an alternative
|
||||
+configuration file, as described in \fBpcp.conf\fP(5).
|
||||
+.PP
|
||||
+For environment variables affecting PCP tools, see \fBpmGetOptions\fP(3).
|
||||
+.SH SEE ALSO
|
||||
+.BR PCPIntro (1),
|
||||
+.BR pcp (1),
|
||||
+.BR pmParseInterval (3)
|
||||
+and
|
||||
+.BR environ (7).
|
||||
+
|
||||
diff --git a/src/pcp/meminfo/pcp-meminfo.py b/src/pcp/meminfo/pcp-meminfo.py
|
||||
new file mode 100755
|
||||
index 000000000..57ac05e34
|
||||
--- /dev/null
|
||||
+++ b/src/pcp/meminfo/pcp-meminfo.py
|
||||
@@ -0,0 +1,195 @@
|
||||
+#!/usr/bin/env pmpython
|
||||
+#
|
||||
+# Copyright (c) 2023 Oracle and/or its affiliates.
|
||||
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
+#
|
||||
+# This program is free software; you can redistribute it and/or modify it
|
||||
+# under the terms of the GNU General Public License as published by the
|
||||
+# Free Software Foundation; either version 2 of the License, or (at your
|
||||
+# option) any later version.
|
||||
+#
|
||||
+# This program is distributed in the hope that it will be useful, but
|
||||
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
+# for more details.
|
||||
+#
|
||||
+# pylint: disable=bad-whitespace,too-many-arguments,too-many-lines, bad-continuation
|
||||
+# pylint: disable=redefined-outer-name,unnecessary-lambda
|
||||
+#
|
||||
+
|
||||
+import sys
|
||||
+import time
|
||||
+from pcp import pmapi, pmcc
|
||||
+
|
||||
+METRICS = ["mem.physmem",
|
||||
+ "mem.util.free",
|
||||
+ "mem.util.available",
|
||||
+ "mem.util.bufmem",
|
||||
+ "mem.util.cached",
|
||||
+ "mem.util.swapCached",
|
||||
+ "mem.util.active",
|
||||
+ "mem.util.inactive",
|
||||
+ "mem.util.active_anon",
|
||||
+ "mem.util.inactive_anon",
|
||||
+ "mem.util.active_file",
|
||||
+ "mem.util.inactive_file",
|
||||
+ "mem.util.unevictable",
|
||||
+ "mem.util.mlocked",
|
||||
+ "mem.util.swapTotal",
|
||||
+ "mem.util.swapFree",
|
||||
+ "mem.util.dirty",
|
||||
+ "mem.util.writeback",
|
||||
+ "mem.util.anonpages",
|
||||
+ "mem.util.mapped",
|
||||
+ "mem.util.shared",
|
||||
+ "mem.util.slab",
|
||||
+ "mem.util.slabReclaimable",
|
||||
+ "mem.util.slabUnreclaimable",
|
||||
+ "mem.util.kernelStack",
|
||||
+ "mem.util.pageTables",
|
||||
+ "mem.util.NFS_Unstable",
|
||||
+ "mem.util.bounce",
|
||||
+ "mem.vmstat.nr_writeback_temp",
|
||||
+ "mem.util.commitLimit",
|
||||
+ "mem.util.committed_AS",
|
||||
+ "mem.util.vmallocTotal",
|
||||
+ "mem.util.vmallocUsed",
|
||||
+ "mem.util.vmallocChunk",
|
||||
+ "mem.util.corrupthardware",
|
||||
+ "mem.util.anonhugepages",
|
||||
+ "mem.vmstat.nr_shmem_hugepages",
|
||||
+ "mem.vmstat.nr_shmem_pmdmapped",
|
||||
+ "mem.zoneinfo.nr_free_cma",
|
||||
+ "mem.util.hugepagesTotal",
|
||||
+ "mem.util.hugepagesFree",
|
||||
+ "mem.util.hugepagesRsvd",
|
||||
+ "mem.util.hugepagesSurp",
|
||||
+ "hinv.hugepagesize",
|
||||
+ "mem.util.directMap4k",
|
||||
+ "mem.util.directMap2M",
|
||||
+ "mem.util.directMap1G"]
|
||||
+
|
||||
+METRICS_DESC = ["MemTotal",
|
||||
+ "MemFree",
|
||||
+ "MemAvailable",
|
||||
+ "Buffers",
|
||||
+ "Cached",
|
||||
+ "SwapCached",
|
||||
+ "Active",
|
||||
+ "Inactive",
|
||||
+ "Active(anon)",
|
||||
+ "Inactive(anon)",
|
||||
+ "Active(file)",
|
||||
+ "Inactive(file)",
|
||||
+ "Unevictable",
|
||||
+ "Mlocked",
|
||||
+ "SwapTotal",
|
||||
+ "SwapFree",
|
||||
+ "Dirty",
|
||||
+ "Writeback",
|
||||
+ "AnonPages",
|
||||
+ "Mapped",
|
||||
+ "Shmem",
|
||||
+ "Slab",
|
||||
+ "SReclaimable",
|
||||
+ "SUnreclaim",
|
||||
+ "KernelStack",
|
||||
+ "PageTables",
|
||||
+ "NFS_Unstable",
|
||||
+ "Bounce",
|
||||
+ "WritebackTmp",
|
||||
+ "CommitLimit",
|
||||
+ "Committed_AS",
|
||||
+ "VmallocTotal",
|
||||
+ "VmallocUsed",
|
||||
+ "VmallocChunk",
|
||||
+ "HardwareCorrupted",
|
||||
+ "AnonHugePages",
|
||||
+ "ShmemHugePages",
|
||||
+ "ShmemPmdMapped",
|
||||
+ "CmaFree",
|
||||
+ "HugePages_Total_NO_kb",
|
||||
+ "HugePages_Free_NO_kb",
|
||||
+ "HugePages_Rsvd_NO_kb",
|
||||
+ "HugePages_Surp_NO_kb",
|
||||
+ "Hugepagesize",
|
||||
+ "DirectMap4k",
|
||||
+ "DirectMap2M",
|
||||
+ "DirectMap1G"]
|
||||
+
|
||||
+class MeminfoReport(pmcc.MetricGroupPrinter):
|
||||
+ samples = 0
|
||||
+
|
||||
+ def __init__(self, samples):
|
||||
+ self.samples = samples
|
||||
+
|
||||
+ def getMetricName(self, idx):
|
||||
+ metric_name = ""
|
||||
+ units = ""
|
||||
+ if METRICS_DESC[idx][-6:] == "_NO_kb":
|
||||
+ metric_name = METRICS_DESC[idx][:-6]
|
||||
+ else:
|
||||
+ metric_name = METRICS_DESC[idx]
|
||||
+ units = "kB"
|
||||
+ return metric_name, units
|
||||
+
|
||||
+ def report(self, manager):
|
||||
+ group = manager["meminfo"]
|
||||
+
|
||||
+ opts.pmGetOptionSamples()
|
||||
+
|
||||
+ t_s = group.contextCache.pmLocaltime(int(group.timestamp))
|
||||
+ time_string = time.strftime(MeminfoOptions.timefmt, t_s.struct_time())
|
||||
+ print(time_string)
|
||||
+
|
||||
+ idx = 0
|
||||
+ for metric in METRICS:
|
||||
+ try:
|
||||
+ val = group[metric].netValues[0][2]
|
||||
+ except IndexError:
|
||||
+ metric_name, units = self.getMetricName(idx)
|
||||
+ print(F"{metric_name:17} : NA")
|
||||
+
|
||||
+ idx += 1
|
||||
+ continue
|
||||
+
|
||||
+ metric_name, units = self.getMetricName(idx)
|
||||
+ print(F"{metric_name:17} : {val} {units}")
|
||||
+
|
||||
+ idx += 1
|
||||
+ print()
|
||||
+
|
||||
+class MeminfoOptions(pmapi.pmOptions):
|
||||
+ context = None
|
||||
+ timefmt = "%H:%M:%S"
|
||||
+ samples = 0
|
||||
+
|
||||
+ def __init__(self):
|
||||
+ pmapi.pmOptions.__init__(self, "a:s:S:T:z:A:t:")
|
||||
+ self.pmSetLongOptionStart()
|
||||
+ self.pmSetLongOptionFinish()
|
||||
+ self.pmSetLongOptionHelp()
|
||||
+
|
||||
+if __name__ == '__main__':
|
||||
+ try:
|
||||
+ opts = MeminfoOptions()
|
||||
+ mngr = pmcc.MetricGroupManager.builder(opts,sys.argv)
|
||||
+ MeminfoOptions.context = mngr.type
|
||||
+
|
||||
+ missing = mngr.checkMissingMetrics(METRICS)
|
||||
+ if missing is not None:
|
||||
+ sys.stderr.write(F"Error:Metric is {missing} missing\n")
|
||||
+ sys.exit(1)
|
||||
+
|
||||
+ mngr["meminfo"] = METRICS
|
||||
+ mngr.printer = MeminfoReport(opts.samples)
|
||||
+ sts = mngr.run()
|
||||
+ sys.exit(sts)
|
||||
+
|
||||
+ except pmapi.pmErr as error:
|
||||
+ sys.stderr.write(F"{error.progname()} {error.message()}")
|
||||
+ except pmapi.pmUsageErr as usage:
|
||||
+ usage.message()
|
||||
+ sys.exit(1)
|
||||
+ except KeyboardInterrupt:
|
||||
+ pass
|
||||
--
|
||||
2.39.3
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,623 +0,0 @@
|
||||
From 80c92de1ac7e589956d974cb6f8c00dfcb999764 Mon Sep 17 00:00:00 2001
|
||||
From: Sagar Sagar <sagar.sagar@oracle.com>
|
||||
Date: Mon, 4 Sep 2023 13:52:49 +0000
|
||||
Subject: [PATCH] pcp-buddyinfo: initial commit of pcp implementation of
|
||||
buddyinfo
|
||||
|
||||
(cherry picked from commit ebf62cada9887eb521d8269ff9bb9b84345d326a)
|
||||
Orabug: 35660932
|
||||
Signed-off-by: Sagar Sagar <sagar.sagar@oracle.com>
|
||||
---
|
||||
qa/1990 | 42 ++++++
|
||||
qa/1990.out | 117 +++++++++++++++++
|
||||
qa/archives/mk.buddyinfo | 39 ++++++
|
||||
qa/group | 1 +
|
||||
src/pcp/GNUmakefile | 1 +
|
||||
src/pcp/buddyinfo/GNUmakefile | 44 +++++++
|
||||
src/pcp/buddyinfo/pcp-buddyinfo.1 | 94 ++++++++++++++
|
||||
src/pcp/buddyinfo/pcp-buddyinfo.py | 197 +++++++++++++++++++++++++++++
|
||||
8 files changed, 535 insertions(+)
|
||||
create mode 100755 qa/1990
|
||||
create mode 100644 qa/1990.out
|
||||
create mode 100755 qa/archives/mk.buddyinfo
|
||||
create mode 100644 src/pcp/buddyinfo/GNUmakefile
|
||||
create mode 100644 src/pcp/buddyinfo/pcp-buddyinfo.1
|
||||
create mode 100644 src/pcp/buddyinfo/pcp-buddyinfo.py
|
||||
|
||||
diff --git a/qa/1990 b/qa/1990
|
||||
new file mode 100755
|
||||
index 000000000..47a5f70f9
|
||||
--- /dev/null
|
||||
+++ b/qa/1990
|
||||
@@ -0,0 +1,42 @@
|
||||
+#!/bin/sh
|
||||
+# PCP QA Test No. 1990
|
||||
+# Exercise various pcp-buddyinfo(1) command options.
|
||||
+#
|
||||
+# Copyright (c) 2023 Oracle and/or its affiliates.
|
||||
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
+#
|
||||
+
|
||||
+seq=`basename $0`
|
||||
+echo "QA output created by $seq"
|
||||
+
|
||||
+. ./common.python
|
||||
+
|
||||
+__version=`$python --version 2>&1 | sed -e 's/Python //'`
|
||||
+case "$__version"
|
||||
+in
|
||||
+ 2.7.5) _notrun "Python $__version reports start seconds off-by one for some processes"
|
||||
+ ;;
|
||||
+esac
|
||||
+
|
||||
+status=1 # failure is the default!
|
||||
+$sudo rm -rf $tmp.* $seq.full
|
||||
+trap "cd $here; rm -rf $tmp.*; exit \$status" 0 1 2 3 15
|
||||
+
|
||||
+pcp_buddyinfo="$PCP_BINADM_DIR/pcp-buddyinfo"
|
||||
+test -x $pcp_buddyinfo || _notrun "No pcp-buddyinfo(1) installed"
|
||||
+pcp_buddyinfo="$python $pcp_buddyinfo"
|
||||
+
|
||||
+# real QA test starts here
|
||||
+echo && echo pcp-buddyinfo output : Display default output
|
||||
+PCP_ARCHIVE="archives/pcp-buddyinfo" PCP_HOSTZONE=1 $pcp_buddyinfo
|
||||
+
|
||||
+archive_first="-a archives/pcp-buddyinfo -z -O +1"
|
||||
+
|
||||
+echo && echo pcp-buddyinfo output : Display output
|
||||
+pcp $archive_first buddyinfo
|
||||
+
|
||||
+echo && echo pcp-buddyinfo output : Display output when given specified number of samples
|
||||
+pcp $archive_first buddyinfo -s 3
|
||||
+
|
||||
+status=0
|
||||
+exit
|
||||
diff --git a/qa/1990.out b/qa/1990.out
|
||||
new file mode 100644
|
||||
index 000000000..adafa9287
|
||||
--- /dev/null
|
||||
+++ b/qa/1990.out
|
||||
@@ -0,0 +1,117 @@
|
||||
+QA output created by 1990
|
||||
+
|
||||
+pcp-buddyinfo output : Display default output
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:21 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:21 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:21 Normal node0 1720 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:24 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:24 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:24 Normal node0 1720 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:27 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:27 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:27 Normal node0 1720 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:30 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:30 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:30 Normal node0 1657 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:33 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:33 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:33 Normal node0 1657 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:36 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:36 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:36 Normal node0 1657 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:39 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:39 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:39 Normal node0 1657 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:42 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:42 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:42 Normal node0 1657 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:45 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:45 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:45 Normal node0 1657 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+
|
||||
+pcp-buddyinfo output : Display output
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:19 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:19 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:19 Normal node0 1720 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:22 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:22 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:22 Normal node0 1720 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:25 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:25 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:25 Normal node0 1720 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:28 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:28 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:28 Normal node0 1720 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:31 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:31 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:31 Normal node0 1657 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:34 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:34 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:34 Normal node0 1657 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:37 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:37 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:37 Normal node0 1657 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:40 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:40 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:40 Normal node0 1657 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:43 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:43 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:43 Normal node0 1657 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:46 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:46 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:46 Normal node0 1657 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+
|
||||
+pcp-buddyinfo output : Display output when given specified number of samples
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:19 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:19 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:19 Normal node0 1720 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:22 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:22 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:22 Normal node0 1720 4118 1862 671 220 120 66 29 21 26 3040
|
||||
+Linux 5.4.17-2136.317.5.3.el8uek.x86_64 (localhost.localdomain) 08/02/23 x86_64 (4 CPU)
|
||||
+TimeStamp Normal Nodes Order0 Order1 Order2 Order3 Order4 Order5 Order6 Order7 Order8 Order9 Order10
|
||||
+15:53:25 DMA node0 0 0 0 0 0 0 0 0 1 1 3
|
||||
+15:53:25 DMA32 node0 3 1 1 1 2 3 2 3 3 2 803
|
||||
+15:53:25 Normal node0 1720 4118 1862 671 220 120 66 29 21 26 3040
|
||||
diff --git a/qa/archives/mk.buddyinfo b/qa/archives/mk.buddyinfo
|
||||
new file mode 100755
|
||||
index 000000000..c9a6707ca
|
||||
--- /dev/null
|
||||
+++ b/qa/archives/mk.buddyinfo
|
||||
@@ -0,0 +1,39 @@
|
||||
+#!/bin/sh
|
||||
+#
|
||||
+# remake the pcp-buddyinfo archive ...
|
||||
+# this archive is intended to be checked in and not remade, this script is
|
||||
+# simply a record of how it was created
|
||||
+#
|
||||
+
|
||||
+. /etc/pcp.env
|
||||
+
|
||||
+tmp=/var/tmp/$$
|
||||
+rm -f $tmp.*
|
||||
+trap "rm -f $tmp.*; exit 0" 0 1 2 3 15
|
||||
+
|
||||
+cat <<End-of-File >>$tmp.config
|
||||
+log advisory on once{
|
||||
+ kernel.uname.sysname
|
||||
+ kernel.uname.release
|
||||
+ kernel.uname.nodename
|
||||
+ kernel.uname.machine
|
||||
+ hinv.ncpu
|
||||
+}
|
||||
+log advisory on 10 seconds {
|
||||
+ mem.buddyinfo.pages
|
||||
+ mem.buddyinfo.total
|
||||
+}
|
||||
+End-of-File
|
||||
+
|
||||
+rm -f pcp-buddyinfo.0 pcp-buddyinfo.index pcp-buddyinfo.meta pcp-buddyinfo.*
|
||||
+
|
||||
+if pmlogger -s 5 -c $tmp.config pcp-buddyinfo; then
|
||||
+ xz pcp-buddyinfo.0
|
||||
+ xz pcp-buddyinfo.index
|
||||
+ xz pcp-buddyinfo.meta
|
||||
+else
|
||||
+ echo "Argh: pmlogger failed ..."
|
||||
+ cat pmlogger.log
|
||||
+fi
|
||||
+
|
||||
+
|
||||
diff --git a/qa/group b/qa/group
|
||||
index 254e6cb42..42863daa4 100644
|
||||
--- a/qa/group
|
||||
+++ b/qa/group
|
||||
@@ -1980,8 +1980,9 @@ x11
|
||||
1986 pmfind local
|
||||
1988 pcp meminfo python local
|
||||
1989 pcp slabinfo python local
|
||||
+1990 pcp buddyinfo python local
|
||||
2101 pmda.sockets local security
|
||||
2104 libpcp local security
|
||||
2100 pmproxy local security
|
||||
2105 libpcp pmcd local security pmcd.pdu
|
||||
4751 libpcp threads valgrind local pcp helgrind
|
||||
diff --git a/src/pcp/GNUmakefile b/src/pcp/GNUmakefile
|
||||
index d4675a8d9..4f656ce5b 100644
|
||||
--- a/src/pcp/GNUmakefile
|
||||
+++ b/src/pcp/GNUmakefile
|
||||
@@ -18,6 +18,7 @@ include $(TOPDIR)/src/include/builddefs
|
||||
|
||||
SUBDIRS = \
|
||||
atop \
|
||||
+ buddyinfo \
|
||||
dmcache \
|
||||
dstat \
|
||||
free \
|
||||
diff --git a/src/pcp/buddyinfo/GNUmakefile b/src/pcp/buddyinfo/GNUmakefile
|
||||
new file mode 100644
|
||||
index 000000000..121b358d3
|
||||
--- /dev/null
|
||||
+++ b/src/pcp/buddyinfo/GNUmakefile
|
||||
@@ -0,0 +1,44 @@
|
||||
+#
|
||||
+# Copyright (c) 2023 Oracle and/or its affiliates.
|
||||
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
+#
|
||||
+# This program is free software; you can redistribute it and/or modify it
|
||||
+# under the terms of the GNU General Public License as published by the
|
||||
+# Free Software Foundation; either version 2 of the License, or (at your
|
||||
+# option) any later version.
|
||||
+#
|
||||
+# This program is distributed in the hope that it will be useful, but
|
||||
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
+# for more details.
|
||||
+#
|
||||
+
|
||||
+TOPDIR = ../../..
|
||||
+include $(TOPDIR)/src/include/builddefs
|
||||
+
|
||||
+TARGET = pcp-buddyinfo
|
||||
+SCRIPT = $(TARGET).py
|
||||
+MAN_SECTION = 1
|
||||
+MAN_PAGES = $(TARGET).$(MAN_SECTION)
|
||||
+MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION)
|
||||
+
|
||||
+default: $(SCRIPT) $(MAN_PAGES)
|
||||
+
|
||||
+include $(BUILDRULES)
|
||||
+
|
||||
+install: default
|
||||
+ifeq "$(HAVE_PYTHON)" "true"
|
||||
+ $(INSTALL) -m 755 $(SCRIPT) $(PCP_BINADM_DIR)/$(TARGET)
|
||||
+ @$(INSTALL_MAN)
|
||||
+endif
|
||||
+
|
||||
+default_pcp : default
|
||||
+
|
||||
+install_pcp : install
|
||||
+
|
||||
+check:: $(SCRIPT)
|
||||
+ $(PYLINT) $^
|
||||
+
|
||||
+check :: $(MAN_PAGES)
|
||||
+ $(MANLINT) $^
|
||||
+
|
||||
diff --git a/src/pcp/buddyinfo/pcp-buddyinfo.1 b/src/pcp/buddyinfo/pcp-buddyinfo.1
|
||||
new file mode 100644
|
||||
index 000000000..26c06ba69
|
||||
--- /dev/null
|
||||
+++ b/src/pcp/buddyinfo/pcp-buddyinfo.1
|
||||
@@ -0,0 +1,94 @@
|
||||
+'\"! tbl | mmdoc
|
||||
+'\"macro stdmacro
|
||||
+.\"
|
||||
+.\" Man page for pcp-buddyinfo
|
||||
+.\" Copyright (c) 2023 Oracle and/or its affiliates.
|
||||
+.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
+.\"
|
||||
+.\" This program is free software; you can redistribute it and/or modify it
|
||||
+.\" under the terms of the GNU General Public License as published by the
|
||||
+.\" Free Software Foundation; either version 2 of the License, or (at your
|
||||
+.\" option) any later version.
|
||||
+.\"
|
||||
+.\" This program is distributed in the hope that it will be useful, but
|
||||
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
+.\" or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
+.\" for more details.
|
||||
+.\"
|
||||
+
|
||||
+.TH PCP-BUDDYINFO 1 "PCP" "Performance Co-Pilot"
|
||||
+
|
||||
+.SH NAME
|
||||
+\fBpcp-buddyinfo\fP \- Report statistics for buddy algorithm shown in cat /proc/buddyinfo
|
||||
+
|
||||
+.SH SYNOPSIS
|
||||
+\fBpcp\fP [\fBpcp options\fP] \fBbuddyinfo\fP [\fB-s\fP \fBsamples\fP] [\fB-a\fP \fBarchive\fP] [\fB-Z\fP \fB--timezone\fP] [\fB-z\fP \fB--hostzone\fP] [\fB-V\fP \fBVersion\fP]
|
||||
+
|
||||
+.SH DESCRIPTION
|
||||
+The \fBpcp-buddyinfo\fP command is used for viewing different stats related to buddyinfo. It helps users analyze useful information related to the buddy algorithm. The information includes the total number of zones that are currently active, order pages etc. By default, \fBpcp-buddyinfo\fP reports live data for the local host.
|
||||
+
|
||||
+The statistics shown are as follows:
|
||||
+
|
||||
+.TS
|
||||
+lfB lfB
|
||||
+l lx.
|
||||
+HEADER DESCRIPTION
|
||||
+_ _
|
||||
+Normal zones available
|
||||
+Nodes available nodes
|
||||
+Order0 available pages of order 0
|
||||
+Order1 available pages of order 1
|
||||
+Order2 available pages of order 2
|
||||
+Order3 available pages of order 3
|
||||
+Order4 available pages of order 4
|
||||
+Order5 available pages of order 5
|
||||
+Order6 available pages of order 6
|
||||
+Order7 available pages of order 7
|
||||
+Order8 available pages of order 8
|
||||
+Order9 available pages of order 9
|
||||
+Order10 available pages of order 10
|
||||
+.TE
|
||||
+
|
||||
+
|
||||
+Each column represents the number of pages of a certain order (a certain size) that are available at any given time. For example, for zone DMA (direct memory access), there are 90 of 2^(0*PAGE_SIZE) chunks of memory. Similarly, there are 6 of 2^(1*PAGE_SIZE) chunks, and 2 of 2^(2*PAGE_SIZE) chunks of memory available.
|
||||
+
|
||||
+The DMA row references the first 16 MB on a system, the HighMem row references all memory greater than 4 GB on a system, and the Normal row references all memory in between.
|
||||
+
|
||||
+.SH OPTIONS
|
||||
+.TP
|
||||
+\fB-a\fP, \fB\-\-archive\fP
|
||||
+Fetch /proc/buddyinfo for a specified archive file
|
||||
+
|
||||
+.TP
|
||||
+\fB-s\fP, \fB\-\-samples\fP
|
||||
+Get the buddyinfo for the specified number of samples count
|
||||
+
|
||||
+.TP
|
||||
+\fB-z\fP, \fB\-\-hostzone\fP
|
||||
+Set the reporting timezone to the local time of metrics source
|
||||
+
|
||||
+.TP
|
||||
+\fB-Z\fP, \fB\-\-timezone\fP
|
||||
+Set the reporting timezone
|
||||
+
|
||||
+.TP
|
||||
+\fB-V\fP, \fB\-\-version\fP
|
||||
+Display the version number and exit.
|
||||
+
|
||||
+.TP
|
||||
+\fB-?\fP, \fB\-\-help\fP
|
||||
+Display the usage message and exit.
|
||||
+
|
||||
+.SH NOTES
|
||||
+\fBpcp-buddyinfo\fP collects information from \fI/proc/buddyinfo\fP and aims to be command-line and output compatible with it.
|
||||
+
|
||||
+.SH PCP ENVIRONMENT
|
||||
+Environment variables with the prefix \fBPCP_\fP are used to parameterize the file and directory names used by PCP. On each installation, the file \fI/etc/pcp.conf\fP contains the local values for these variables. The \fB$PCP_CONF\fP variable may be used to specify an alternative configuration file, as described in \fBpcp.conf\fP(5).
|
||||
+
|
||||
+For environment variables affecting PCP tools, see \fBpmGetOptions\fP(3).
|
||||
+
|
||||
+.SH SEE ALSO
|
||||
+.BR PCPIntro(1),
|
||||
+.BR pcp(1),
|
||||
+.BR pmParseInterval(3),
|
||||
+.BR environ(7).
|
||||
diff --git a/src/pcp/buddyinfo/pcp-buddyinfo.py b/src/pcp/buddyinfo/pcp-buddyinfo.py
|
||||
new file mode 100644
|
||||
index 000000000..012fe7cec
|
||||
--- /dev/null
|
||||
+++ b/src/pcp/buddyinfo/pcp-buddyinfo.py
|
||||
@@ -0,0 +1,197 @@
|
||||
+#!/usr/bin/env pmpython
|
||||
+#
|
||||
+# Copyright (c) 2023 Oracle and/or its affiliates.
|
||||
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
+#
|
||||
+# This program is free software; you can redistribute it and/or modify it
|
||||
+# under the terms of the GNU General Public License as published by the
|
||||
+# Free Software Foundation; either version 2 of the License, or (at your
|
||||
+# option) any later version.
|
||||
+#
|
||||
+# This program is distributed in the hope that it will be useful, but
|
||||
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
+# for more details.
|
||||
+#
|
||||
+# pylint: disable=bad-whitespace,too-many-arguments,too-many-lines, bad-continuation
|
||||
+# pylint: disable=redefined-outer-name,unnecessary-lambda
|
||||
+#
|
||||
+import signal
|
||||
+import sys
|
||||
+import time
|
||||
+from pcp import pmapi, pmcc
|
||||
+from cpmapi import PM_CONTEXT_ARCHIVE
|
||||
+
|
||||
+SYS_MECTRICS= ["kernel.uname.sysname","kernel.uname.release",
|
||||
+ "kernel.uname.nodename","kernel.uname.machine","hinv.ncpu"]
|
||||
+BUDDYSTAT_METRICS = ["mem.buddyinfo.pages","mem.buddyinfo.total"]
|
||||
+
|
||||
+ALL_METRICS = BUDDYSTAT_METRICS + SYS_MECTRICS
|
||||
+
|
||||
+def adjust_length(name,size):
|
||||
+ return name.ljust(size)
|
||||
+
|
||||
+class ReportingMetricRepository:
|
||||
+
|
||||
+ def __init__(self,group):
|
||||
+ self.group=group
|
||||
+ self.current_cached_values = {}
|
||||
+
|
||||
+ def __fetch_current_value(self,metric):
|
||||
+ val=dict(map(lambda x: (x[1], x[2]), self.group[metric].netValues))
|
||||
+ return dict(val)
|
||||
+
|
||||
+ def current_value(self,metric):
|
||||
+ if metric not in self.group:
|
||||
+ return None
|
||||
+ if self.current_cached_values.get(metric) is None:
|
||||
+ first_value=self.__fetch_current_value(metric)
|
||||
+ self.current_cached_values[metric]=first_value
|
||||
+ return self.current_cached_values[metric]
|
||||
+
|
||||
+class BuddyStatUtil:
|
||||
+ def __init__(self,metrics_repository):
|
||||
+ self.__metric_repository=metrics_repository
|
||||
+ self.report=ReportingMetricRepository(self.__metric_repository)
|
||||
+
|
||||
+ def buddy_pages(self):
|
||||
+ return self.report.current_value('mem.buddyinfo.pages')
|
||||
+
|
||||
+ def buddy_total_pages(self):
|
||||
+ return self.report.current_value('mem.buddyinfo.total')
|
||||
+
|
||||
+ def names(self):
|
||||
+ data = self.report.current_value('mem.buddyinfo.pages')
|
||||
+ return data.keys()
|
||||
+
|
||||
+class BuddyinfoReport(pmcc.MetricGroupPrinter):
|
||||
+ def __init__(self,samples,group,context):
|
||||
+ self.samples = samples
|
||||
+ self.group=group
|
||||
+ self.context=context
|
||||
+
|
||||
+ def __get_ncpu(self, group):
|
||||
+ return group['hinv.ncpu'].netValues[0][2]
|
||||
+
|
||||
+ def __print_machine_info(self, context):
|
||||
+ timestamp = self.group.pmLocaltime(context.timestamp.tv_sec)
|
||||
+ # Please check strftime(3) for different formatting options.
|
||||
+ # Also check TZ and LC_TIME environment variables for more
|
||||
+ # information on how to override the default formatting of
|
||||
+ # the date display in the header
|
||||
+ time_string = time.strftime("%x", timestamp.struct_time())
|
||||
+ header_string = ''
|
||||
+ header_string += context['kernel.uname.sysname'].netValues[0][2] + ' '
|
||||
+ header_string += context['kernel.uname.release'].netValues[0][2] + ' '
|
||||
+ header_string += '(' + context['kernel.uname.nodename'].netValues[0][2] + ') '
|
||||
+ header_string += time_string + ' '
|
||||
+ header_string += context['kernel.uname.machine'].netValues[0][2] + ' '
|
||||
+ print("%s (%s CPU)" % (header_string, self.__get_ncpu(context)))
|
||||
+
|
||||
+ def __print_header(self,header_indentation,value_indentation):
|
||||
+ value_indentation+=" "*2
|
||||
+ print("TimeStamp"+ " "+header_indentation + "Normal" + header_indentation+value_indentation+" " +
|
||||
+ "Nodes" + header_indentation + "Order0" + value_indentation +
|
||||
+ "Order1" + value_indentation +"Order2" + value_indentation + "Order3" + value_indentation +
|
||||
+ "Order4" + value_indentation +"Order5" + value_indentation + "Order6" + value_indentation +
|
||||
+ "Order7" + value_indentation +"Order8" + value_indentation + "Order9" +
|
||||
+ value_indentation + "Order10")
|
||||
+
|
||||
+ def __print_values(self,timestamp,header_indentation,\
|
||||
+ value_indentation,buddystatus):
|
||||
+ names=buddystatus.names()
|
||||
+ pages=buddystatus.buddy_pages()
|
||||
+ order_set = set()
|
||||
+ nodes_set = set()
|
||||
+ no_of_nodes_set = set()
|
||||
+ for name in names:
|
||||
+ part=name.split('::')
|
||||
+ if len(part)==3:
|
||||
+ nodes_set.add(part[0])
|
||||
+ order_set.add(part[1])
|
||||
+ no_of_nodes_set.add(part[2])
|
||||
+ def __extract_numeric_part(element):
|
||||
+ return int(element[5:])
|
||||
+ for Normal in sorted(nodes_set):
|
||||
+ for node in no_of_nodes_set:
|
||||
+ value=""
|
||||
+ for order in sorted(order_set,key=__extract_numeric_part):
|
||||
+ nodename = adjust_length(Normal,9) if len(Normal) < 9 else Normal
|
||||
+ key = f"{Normal}::{order}::{node}"
|
||||
+ data = str(pages.get(key,0))
|
||||
+ value += adjust_length(data,8) if len(data) < 8 else data
|
||||
+ value += value_indentation
|
||||
+
|
||||
+ print("%s %s %s %s %s %s %s "%(timestamp,header_indentation,nodename,header_indentation,node,
|
||||
+ header_indentation,value))
|
||||
+
|
||||
+ def print_report(self,group,timestamp,header_indentation,value_indentation,manager_buddyinfo):
|
||||
+
|
||||
+ def __print_buddy_status():
|
||||
+ buddystatus = BuddyStatUtil(manager_buddyinfo)
|
||||
+ if buddystatus.names():
|
||||
+ try:
|
||||
+ self.__print_machine_info(group)
|
||||
+ self.__print_header(header_indentation, value_indentation)
|
||||
+ self.__print_values(timestamp, header_indentation, value_indentation, buddystatus)
|
||||
+ except IndexError:
|
||||
+ print("Incorrect machine info due to some missing metrics")
|
||||
+ return
|
||||
+ else:
|
||||
+ return
|
||||
+
|
||||
+ if self.context != PM_CONTEXT_ARCHIVE and self.samples is None:
|
||||
+ __print_buddy_status()
|
||||
+ sys.exit(0)
|
||||
+ elif self.context == PM_CONTEXT_ARCHIVE and self.samples is None:
|
||||
+ __print_buddy_status()
|
||||
+ elif self.samples >=1:
|
||||
+ __print_buddy_status()
|
||||
+ self.samples-=1
|
||||
+
|
||||
+ def report(self, manager):
|
||||
+ group = manager["sysinfo"]
|
||||
+ self.samples=opts.pmGetOptionSamples()
|
||||
+ t_s = group.contextCache.pmLocaltime(int(group.timestamp))
|
||||
+ timestamp = time.strftime(BuddyinfoOptions.timefmt, t_s.struct_time())
|
||||
+ header_indentation = " " if len(timestamp) < 9 else (len(timestamp) - 7) * " "
|
||||
+ value_indentation = ((len(header_indentation) + 2) - len(timestamp)) * " "
|
||||
+ self.print_report(group,timestamp,header_indentation,value_indentation,manager['buddyinfo'])
|
||||
+
|
||||
+class BuddyinfoOptions(pmapi.pmOptions):
|
||||
+ timefmt = "%H:%M:%S"
|
||||
+ def __init__(self):
|
||||
+ pmapi.pmOptions.__init__(self, "a:s:Z:zV?")
|
||||
+ self.pmSetLongOptionHeader("General options")
|
||||
+ self.pmSetLongOptionHostZone()
|
||||
+ self.pmSetLongOptionTimeZone()
|
||||
+ self.pmSetLongOptionHelp()
|
||||
+ self.pmSetLongOptionSamples()
|
||||
+ self.pmSetLongOptionVersion()
|
||||
+ self.samples=None
|
||||
+ self.context=None
|
||||
+
|
||||
+if __name__ == '__main__':
|
||||
+ try:
|
||||
+ opts = BuddyinfoOptions()
|
||||
+ mngr = pmcc.MetricGroupManager.builder(opts,sys.argv)
|
||||
+ opts.context=mngr.type
|
||||
+ missing = mngr.checkMissingMetrics(ALL_METRICS)
|
||||
+ if missing is not None:
|
||||
+ sys.stderr.write("Error:some metrics are unavailable",missing)
|
||||
+ sys.exit(1)
|
||||
+ mngr["buddyinfo"] = BUDDYSTAT_METRICS
|
||||
+ mngr["sysinfo"] = SYS_MECTRICS
|
||||
+ mngr["allinfo"]=ALL_METRICS
|
||||
+ mngr.printer = BuddyinfoReport(opts.samples,mngr,opts.context)
|
||||
+ sts = mngr.run()
|
||||
+ sys.exit(sts)
|
||||
+ except pmapi.pmErr as error:
|
||||
+ sys.stderr.write('%s\n' % (error.message()))
|
||||
+ except pmapi.pmUsageErr as usage:
|
||||
+ usage.message()
|
||||
+ sys.exit(1)
|
||||
+ except IOError:
|
||||
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
+ except KeyboardInterrupt:
|
||||
+ pass
|
||||
--
|
||||
2.39.3
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,455 +0,0 @@
|
||||
From 4a2cf79cd483bdb55feab3a2053294495525652d Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Thu, 12 Oct 2023 17:46:07 +0530
|
||||
Subject: [PATCH] Fixed pcp-zoneinfo to replay ol7 archive on ol8 machine For
|
||||
more info https://github.com/performancecopilot/pcp/pull/1814
|
||||
|
||||
Orabug:35903733
|
||||
|
||||
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
|
||||
---
|
||||
src/pcp/zoneinfo/pcp-zoneinfo.py | 315 +++++++++++++++++++------------
|
||||
1 file changed, 193 insertions(+), 122 deletions(-)
|
||||
|
||||
diff --git a/src/pcp/zoneinfo/pcp-zoneinfo.py b/src/pcp/zoneinfo/pcp-zoneinfo.py
|
||||
index 32fc5ee..e5eabbd 100755
|
||||
--- a/src/pcp/zoneinfo/pcp-zoneinfo.py
|
||||
+++ b/src/pcp/zoneinfo/pcp-zoneinfo.py
|
||||
@@ -21,38 +21,44 @@ import sys
|
||||
import time
|
||||
from pcp import pmapi, pmcc
|
||||
from cpmapi import PM_CONTEXT_ARCHIVE
|
||||
+from cpmapi import PM_CONTEXT_HOST
|
||||
|
||||
-SYS_MECTRICS = ["kernel.uname.sysname","kernel.uname.release",
|
||||
- "kernel.uname.nodename","kernel.uname.machine","hinv.ncpu"]
|
||||
-
|
||||
-ZONESTAT_METRICS = [ "mem.zoneinfo.free","mem.zoneinfo.min","mem.zoneinfo.low","mem.zoneinfo.high",
|
||||
- "mem.zoneinfo.scanned","mem.zoneinfo.spanned","mem.zoneinfo.present","mem.zoneinfo.managed",
|
||||
- "mem.zoneinfo.nr_free_pages","mem.zoneinfo.nr_alloc_batch","mem.zoneinfo.nr_inactive_anon",
|
||||
- "mem.zoneinfo.nr_active_anon","mem.zoneinfo.nr_inactive_file","mem.zoneinfo.nr_active_file",
|
||||
- "mem.zoneinfo.nr_unevictable","mem.zoneinfo.nr_mlock","mem.zoneinfo.nr_anon_pages",
|
||||
- "mem.zoneinfo.nr_mapped","mem.zoneinfo.nr_file_pages","mem.zoneinfo.nr_dirty",
|
||||
- "mem.zoneinfo.nr_writeback","mem.zoneinfo.nr_slab_reclaimable","mem.zoneinfo.nr_slab_unreclaimable",
|
||||
- "mem.zoneinfo.nr_page_table_pages","mem.zoneinfo.nr_kernel_stack","mem.zoneinfo.nr_unstable",
|
||||
- "mem.zoneinfo.nr_bounce","mem.zoneinfo.nr_vmscan_write","mem.zoneinfo.nr_vmscan_immediate_reclaim",
|
||||
- "mem.zoneinfo.nr_writeback_temp","mem.zoneinfo.nr_isolated_anon","mem.zoneinfo.nr_isolated_file",
|
||||
- "mem.zoneinfo.nr_shmem","mem.zoneinfo.nr_dirtied","mem.zoneinfo.nr_written","mem.zoneinfo.numa_hit",
|
||||
- "mem.zoneinfo.numa_miss","mem.zoneinfo.numa_foreign","mem.zoneinfo.numa_interleave",
|
||||
- "mem.zoneinfo.numa_local","mem.zoneinfo.numa_other","mem.zoneinfo.workingset_refault",
|
||||
- "mem.zoneinfo.workingset_activate","mem.zoneinfo.workingset_nodereclaim",
|
||||
- "mem.zoneinfo.nr_anon_transparent_hugepages","mem.zoneinfo.nr_free_cma","mem.zoneinfo.cma",
|
||||
- "mem.zoneinfo.nr_swapcached","mem.zoneinfo.nr_shmem_hugepages","mem.zoneinfo.nr_shmem_pmdmapped",
|
||||
- "mem.zoneinfo.nr_file_hugepages","mem.zoneinfo.nr_file_pmdmapped",
|
||||
- "mem.zoneinfo.nr_kernel_misc_reclaimable","mem.zoneinfo.nr_foll_pin_acquired",
|
||||
- "mem.zoneinfo.nr_foll_pin_released","mem.zoneinfo.workingset_refault_anon",
|
||||
- "mem.zoneinfo.workingset_refault_file","mem.zoneinfo.workingset_active_anon",
|
||||
- "mem.zoneinfo.workingset_active_file","mem.zoneinfo.workingset_restore_anon",
|
||||
- "mem.zoneinfo.workingset_restore_file","mem.zoneinfo.nr_zspages",
|
||||
- "mem.zoneinfo.nr_zone_inactive_file","mem.zoneinfo.nr_zone_active_file",
|
||||
- "mem.zoneinfo.nr_zone_inactive_anon","mem.zoneinfo.nr_zone_active_anon",
|
||||
- "mem.zoneinfo.nr_zone_unevictable","mem.zoneinfo.nr_zone_write_pending",
|
||||
- "mem.zoneinfo.protection" ]
|
||||
-
|
||||
-ALL_METRICS = ZONESTAT_METRICS + SYS_MECTRICS
|
||||
+# By default we are assuming we are running on the latest pcp version
|
||||
+# Later will figure out the version and will change the newVersionFlag
|
||||
+
|
||||
+newVersionFlag = True
|
||||
+
|
||||
+SYS_METRICS = ["kernel.uname.sysname", "kernel.uname.release",
|
||||
+ "kernel.uname.nodename", "kernel.uname.machine", "hinv.ncpu"]
|
||||
+
|
||||
+ZONESTAT_METRICS = ["mem.zoneinfo.managed", "mem.zoneinfo.nr_mlock", "mem.zoneinfo.present",
|
||||
+ "mem.zoneinfo.scanned", "mem.zoneinfo.nr_unstable", "mem.zoneinfo.nr_page_table_pages",
|
||||
+ "mem.zoneinfo.nr_shmem", "mem.zoneinfo.nr_free_pages", "mem.zoneinfo.nr_active_file",
|
||||
+ "mem.zoneinfo.nr_dirty", "mem.zoneinfo.nr_writeback", "mem.zoneinfo.free",
|
||||
+ "mem.zoneinfo.nr_unevictable", "mem.zoneinfo.nr_alloc_batch", "mem.zoneinfo.low",
|
||||
+ "mem.zoneinfo.nr_slab_reclaimable", "mem.zoneinfo.nr_kernel_stack", "mem.zoneinfo.numa_interleave",
|
||||
+ "mem.zoneinfo.workingset_nodereclaim", "mem.zoneinfo.nr_isolated_anon", "mem.zoneinfo.nr_bounce",
|
||||
+ "mem.zoneinfo.numa_other", "mem.zoneinfo.nr_file_pages", "mem.zoneinfo.numa_hit",
|
||||
+ "mem.zoneinfo.nr_isolated_file", "mem.zoneinfo.nr_anon_transparent_hugepages",
|
||||
+ "mem.zoneinfo.nr_inactive_file", "mem.zoneinfo.spanned", "mem.zoneinfo.nr_written",
|
||||
+ "mem.zoneinfo.numa_foreign", "mem.zoneinfo.nr_vmscan_write", "mem.zoneinfo.nr_free_cma",
|
||||
+ "mem.zoneinfo.nr_writeback_temp", "mem.zoneinfo.nr_slab_unreclaimable",
|
||||
+ "mem.zoneinfo.nr_vmscan_immediate_reclaim", "mem.zoneinfo.nr_mapped", "mem.zoneinfo.nr_anon_pages",
|
||||
+ "mem.zoneinfo.high", "mem.zoneinfo.protection", "mem.zoneinfo.numa_local", "mem.zoneinfo.numa_miss",
|
||||
+ "mem.zoneinfo.nr_active_anon", "mem.zoneinfo.workingset_refault", "mem.zoneinfo.nr_dirtied",
|
||||
+ "mem.zoneinfo.workingset_activate", "mem.zoneinfo.nr_inactive_anon", "mem.zoneinfo.min"]
|
||||
+
|
||||
+ZONESTAT_NEW_METRICS = ["mem.zoneinfo.nr_foll_pin_acquired", "mem.zoneinfo.nr_foll_pin_released",
|
||||
+ "mem.zoneinfo.nr_zspages", "mem.zoneinfo.workingset_restore_anon",
|
||||
+ "mem.zoneinfo.workingset_refault_anon", "mem.zoneinfo.nr_zone_active_anon",
|
||||
+ "mem.zoneinfo.nr_zone_inactive_file", "mem.zoneinfo.nr_swapcached",
|
||||
+ "mem.zoneinfo.nr_file_pmdmapped", "mem.zoneinfo.workingset_active_file",
|
||||
+ "mem.zoneinfo.workingset_refault_file", "mem.zoneinfo.cma",
|
||||
+ "mem.zoneinfo.workingset_active_anon", "mem.zoneinfo.nr_zone_write_pending",
|
||||
+ "mem.zoneinfo.nr_shmem_pmdmapped", "mem.zoneinfo.nr_shmem_hugepages",
|
||||
+ "mem.zoneinfo.nr_kernel_misc_reclaimable", "mem.zoneinfo.nr_zone_active_file",
|
||||
+ "mem.zoneinfo.nr_file_hugepages", "mem.zoneinfo.nr_zone_unevictable",
|
||||
+ "mem.zoneinfo.nr_zone_inactive_anon", "mem.zoneinfo.workingset_restore_file"]
|
||||
|
||||
ZONEINFO_PER_NODE = {
|
||||
"nr_inactive_anon" : "mem.zoneinfo.nr_inactive_anon",
|
||||
@@ -71,104 +77,109 @@ ZONEINFO_PER_NODE = {
|
||||
"nr_writeback" : "mem.zoneinfo.nr_writeback",
|
||||
"nr_writeback_temp" : "mem.zoneinfo.nr_writeback_temp",
|
||||
"nr_shmem" : "mem.zoneinfo.nr_shmem",
|
||||
- "nr_shmem_hugepages" : "mem.zoneinfo.nr_shmem_hugepages",
|
||||
- "nr_shmem_pmdmapped" : "mem.zoneinfo.nr_shmem_pmdmapped",
|
||||
- "nr_file_hugepages" : "mem.zoneinfo.nr_file_hugepages",
|
||||
- "nr_file_pmdmapped" : "mem.zoneinfo.nr_file_pmdmapped",
|
||||
"nr_anon_transparent_hugepages" : "mem.zoneinfo.nr_anon_transparent_hugepages",
|
||||
"nr_unstable" : "mem.zoneinfo.nr_unstable",
|
||||
"nr_vmscan_write" : "mem.zoneinfo.nr_vmscan_write",
|
||||
"nr_vmscan_immediate_reclaim" : "mem.zoneinfo.nr_vmscan_immediate_reclaim",
|
||||
"nr_dirtied" : "mem.zoneinfo.nr_dirtied",
|
||||
"nr_written" : "mem.zoneinfo.nr_written",
|
||||
+ "nr_shmem_hugepages" : "mem.zoneinfo.nr_shmem_hugepages",
|
||||
+ "nr_shmem_pmdmapped" : "mem.zoneinfo.nr_shmem_pmdmapped",
|
||||
+ "nr_file_hugepages" : "mem.zoneinfo.nr_file_hugepages",
|
||||
+ "nr_file_pmdmapped" : "mem.zoneinfo.nr_file_pmdmapped",
|
||||
"nr_kernel_misc_reclaimable" : "mem.zoneinfo.nr_kernel_misc_reclaimable"
|
||||
}
|
||||
|
||||
-ZONEINFO_PAGE_INFO = {
|
||||
- "pages free" : "mem.zoneinfo.free",
|
||||
- " min" : "mem.zoneinfo.min",
|
||||
- " low" : "mem.zoneinfo.low",
|
||||
- " high" : "mem.zoneinfo.high",
|
||||
- " spanned" : "mem.zoneinfo.spanned",
|
||||
- " present" : "mem.zoneinfo.present",
|
||||
- " managed" : "mem.zoneinfo.managed"
|
||||
-}
|
||||
+ZONEINFO_PAGE_INFO = [
|
||||
+ ("pages free" , "mem.zoneinfo.free"),
|
||||
+ (" min" , "mem.zoneinfo.min"),
|
||||
+ (" low" , "mem.zoneinfo.low"),
|
||||
+ (" high" , "mem.zoneinfo.high"),
|
||||
+ (" spanned", "mem.zoneinfo.spanned"),
|
||||
+ (" present", "mem.zoneinfo.present"),
|
||||
+ (" managed", "mem.zoneinfo.managed")
|
||||
+]
|
||||
|
||||
ZONEINFO_NUMBER_ZONE = {
|
||||
"nr_free_pages" : "mem.zoneinfo.nr_free_pages",
|
||||
- "nr_zone_inactive_anon" : "mem.zoneinfo.nr_zone_inactive_anon",
|
||||
- "nr_zone_active_anon" : "mem.zoneinfo.nr_zone_active_anon",
|
||||
- "nr_zone_inactive_file" : "mem.zoneinfo.nr_zone_inactive_file",
|
||||
- "nr_zone_active_file" : "mem.zoneinfo.nr_zone_active_file",
|
||||
- "nr_zone_unevictable" : "mem.zoneinfo.nr_zone_unevictable",
|
||||
- "nr_zone_write_pending" : "mem.zoneinfo.nr_zone_write_pending",
|
||||
"nr_mlock" : "mem.zoneinfo.nr_mlock",
|
||||
"nr_page_table_pages" : "mem.zoneinfo.nr_page_table_pages",
|
||||
"nr_kernel_stack" : "mem.zoneinfo.nr_kernel_stack",
|
||||
"nr_bounce" : "mem.zoneinfo.nr_bounce",
|
||||
- "nr_zspages" : "mem.zoneinfo.nr_zspages",
|
||||
"nr_free_cma" : "mem.zoneinfo.nr_free_cma",
|
||||
"numa_hit" : "mem.zoneinfo.numa_hit",
|
||||
"numa_miss" : "mem.zoneinfo.numa_miss",
|
||||
"numa_foreign" : "mem.zoneinfo.numa_foreign",
|
||||
"numa_interleave" : "mem.zoneinfo.numa_interleave",
|
||||
+ "numa_other" : "mem.zoneinfo.numa_other",
|
||||
+ "nr_zone_active_anon" : "mem.zoneinfo.nr_zone_active_anon",
|
||||
+ "nr_zone_inactive_file" : "mem.zoneinfo.nr_zone_inactive_file",
|
||||
+ "nr_zone_active_file" : "mem.zoneinfo.nr_zone_active_file",
|
||||
+ "nr_zone_unevictable" : "mem.zoneinfo.nr_zone_unevictable",
|
||||
+ "nr_zone_write_pending" : "mem.zoneinfo.nr_zone_write_pending",
|
||||
+ "nr_zspages" : "mem.zoneinfo.nr_zspages",
|
||||
"numa_local" : "mem.zoneinfo.numa_local",
|
||||
- "numa_other" : "mem.zoneinfo.numa_other"
|
||||
+ "nr_zone_inactive_anon" : "mem.zoneinfo.nr_zone_inactive_anon"
|
||||
}
|
||||
|
||||
+
|
||||
class ReportingMetricRepository:
|
||||
|
||||
- def __init__(self,group):
|
||||
+ def __init__(self, group):
|
||||
self.group = group
|
||||
self.current_cached_values = {}
|
||||
|
||||
- def __sorted(self,data):
|
||||
+ def __sorted(self, data):
|
||||
return dict(sorted(data.items(), key=lambda item: item[0].lower()))
|
||||
|
||||
- def __fetch_current_value(self,metric):
|
||||
+ def __fetch_current_value(self, metric):
|
||||
val = dict(map(lambda x: (x[1], x[2]), self.group[metric].netValues))
|
||||
val = self.__sorted(val)
|
||||
return dict(val)
|
||||
|
||||
- def current_value(self,metric):
|
||||
+ def current_value(self, metric):
|
||||
if metric not in self.group:
|
||||
return None
|
||||
if self.current_cached_values.get(metric) is None:
|
||||
- first_value=self.__fetch_current_value(metric)
|
||||
- self.current_cached_values[metric]=first_value
|
||||
+ first_value = self.__fetch_current_value(metric)
|
||||
+ self.current_cached_values[metric] = first_value
|
||||
return self.current_cached_values[metric]
|
||||
|
||||
+
|
||||
class ZoneStatUtil:
|
||||
- def __init__(self,metrics_repository):
|
||||
- self.__metric_repository=metrics_repository
|
||||
- self.report=ReportingMetricRepository(self.__metric_repository)
|
||||
+ def __init__(self, metrics_repository):
|
||||
+ self.__metric_repository = metrics_repository
|
||||
+ self.report = ReportingMetricRepository(self.__metric_repository)
|
||||
|
||||
def names(self):
|
||||
data = self.report.current_value('mem.zoneinfo.present')
|
||||
- return data.keys()
|
||||
+ return sorted(data.keys())
|
||||
|
||||
- def metric_value(self,metric,node):
|
||||
- data = self.report.current_value(metric)
|
||||
- data = data.get(node,"0")
|
||||
- if data != "0":
|
||||
- data = data/4
|
||||
- return int(data)
|
||||
+ def metric_value(self, metric, node):
|
||||
+ if metric in ALL_METRICS:
|
||||
+ data = self.report.current_value(metric)
|
||||
+ data = data.get(node, "0")
|
||||
+ if data != "0":
|
||||
+ data = data/4
|
||||
+ return int(data)
|
||||
+ return None
|
||||
|
||||
- #creating the list of protection values for particular node
|
||||
- def protection(self,node):
|
||||
+ # creating the list of protection values for particular node
|
||||
+ def protection(self, node):
|
||||
data = self.report.current_value("mem.zoneinfo.protection")
|
||||
- values = [value // 4 for key, value in data.items() if key.startswith(node)]
|
||||
- return values
|
||||
+ return [value // 4 for key, value in data.items() if key.startswith(node)]
|
||||
|
||||
- def protection_names(self,node_name):
|
||||
+ def protection_names(self, node_name):
|
||||
data = self.report.current_value("mem.zoneinfo.protection")
|
||||
- filtered_nodes = {key.split("::" + node_name)[0] + "::" + node_name for key in data.keys() if node_name in key}
|
||||
- if len(filtered_nodes) == 0:
|
||||
- filtered_nodes = None
|
||||
- return filtered_nodes
|
||||
+ return {
|
||||
+ key.split("::" + node_name)[0] + "::" + node_name
|
||||
+ for key in data.keys()
|
||||
+ if node_name in key
|
||||
+ } or None
|
||||
+
|
||||
|
||||
class ZoneinfoReport(pmcc.MetricGroupPrinter):
|
||||
- def __init__(self,samples,group,context):
|
||||
+ def __init__(self, samples, group, context):
|
||||
self.samples = samples
|
||||
self.group = group
|
||||
self.context = context
|
||||
@@ -191,64 +202,84 @@ class ZoneinfoReport(pmcc.MetricGroupPrinter):
|
||||
header_string += context['kernel.uname.machine'].netValues[0][2] + ' '
|
||||
print("%s (%s CPU)" % (header_string, self.__get_ncpu(context)))
|
||||
|
||||
- #Function to format the node name as per the /proc/zoneinfo naming convention
|
||||
- def __format_node_name(self,input_str):
|
||||
+ # Function to format the node name as per the /proc/zoneinfo naming convention
|
||||
+ def __format_node_name(self, input_str):
|
||||
parts = input_str.split("::")
|
||||
- if len(parts) == 2 and parts[1].startswith("node"):
|
||||
- node_num = parts[1][4:]
|
||||
- zone_name = parts[0]
|
||||
- return "Node {}, zone {}".format(node_num, zone_name)
|
||||
- else:
|
||||
+ if len(parts) != 2 or not parts[1].startswith("node"):
|
||||
return "Invalid input format"
|
||||
+ node_num = parts[1][4:]
|
||||
+ return "Node {}, zone {}".format(node_num, parts[0])
|
||||
|
||||
- def __print_values(self,timestamp,header_indentation,value_indentation,manager):
|
||||
+ def __print_old_version_values(self, manager):
|
||||
+ try:
|
||||
+ total_nodes = manager.names()
|
||||
+ total_nodes = sorted(total_nodes, key=lambda
|
||||
+ x: ((x.split('::')[0] != 'DMA')*2, # Move 'DMA' entries to the front
|
||||
+ x.split('::')[0]))
|
||||
+ for node in total_nodes:
|
||||
+ print(self.__format_node_name(str(node)))
|
||||
+ print("\tper-node status")
|
||||
+ # print(ZONEINFO_PER_NODE)
|
||||
+ for key, value in ZONEINFO_PER_NODE.items():
|
||||
+ print("\t{} {}".format(key, manager.metric_value(value, node)))
|
||||
+ for key, value in ZONEINFO_PAGE_INFO:
|
||||
+ print("\t{} {}".format(key, manager.metric_value(value, node)))
|
||||
+ print(" " * 14 + "protection " + str([int(i) for i in sorted(manager.protection(node))]))
|
||||
+ for key, value in ZONEINFO_NUMBER_ZONE.items():
|
||||
+ print("\t{} {}".format(key, manager.metric_value(value, node)))
|
||||
+ except IndexError:
|
||||
+ print("Got some error while printing values for old version zoneinfo")
|
||||
+
|
||||
+ def __print_values(self, manager):
|
||||
total_nodes = manager.names()
|
||||
- node_names = set(key.split('::')[1] for key in total_nodes)
|
||||
- #sort the node names in decreasing order
|
||||
+ node_names = {key.split('::')[1] for key in total_nodes}
|
||||
+ # sort the node names in decreasing order
|
||||
node_names = sorted(node_names, key=lambda x: int(x[4:]) if x.startswith('node') else float('inf'))
|
||||
try:
|
||||
- #lopping through all the available nodes
|
||||
+ # lopping through all the available nodes
|
||||
for node_name in node_names:
|
||||
- #get all the types available for the current node on which i'm
|
||||
- node_types=manager.protection_names(node_name)
|
||||
+ # get all the types available for the current node on which i'm
|
||||
+ node_types = manager.protection_names(node_name)
|
||||
if node_types is None:
|
||||
return
|
||||
nodes = set(node_types).intersection(total_nodes)
|
||||
- print("NODE {:>2},".format(node_name[4:]),"per-node status")
|
||||
- for key,value in ZONEINFO_PER_NODE.items():
|
||||
- print ("\t",key,manager.metric_value(value,node_name))
|
||||
+ nodes = sorted(nodes, key=lambda x: ((x.split('::')[0] != 'DMA')*2, # Move 'DMA' entries to the front
|
||||
+ x.split('::')[0]))
|
||||
+ print("NODE {:>2}, per-node status".format(node_name[4:]))
|
||||
+ for key, value in ZONEINFO_PER_NODE.items():
|
||||
+ print("\t{} {}".format(key, manager.metric_value(value, node_name)))
|
||||
for node in nodes:
|
||||
print(self.__format_node_name(node))
|
||||
- for key,value in ZONEINFO_PAGE_INFO.items():
|
||||
- print("\t",key,manager.metric_value(value,node))
|
||||
- print(" "*14,"protection",manager.protection(node))
|
||||
- for key,value in ZONEINFO_NUMBER_ZONE.items():
|
||||
- print ("\t",key,manager.metric_value(value,node))
|
||||
- #finding the remaining type of nodes data which i haven't printed so far
|
||||
- #these nodes will have only pages information for them so just printing them out
|
||||
- remaining_nodes = set(node_types) - nodes
|
||||
+ for key, value in ZONEINFO_PAGE_INFO:
|
||||
+ print("\t{} {}".format(key, manager.metric_value(value, node)))
|
||||
+ print(" " * 14 + "protection " + str([int(i) for i in manager.protection(node)]))
|
||||
+ for key, value in ZONEINFO_NUMBER_ZONE.items():
|
||||
+ print("\t{} {}".format(key, manager.metric_value(value, node)))
|
||||
+ # finding the remaining type of nodes data which i haven't printed so far
|
||||
+ # these nodes will have only pages information for them so just printing them out
|
||||
+ remaining_nodes = set(node_types) - set(nodes)
|
||||
if remaining_nodes:
|
||||
- for node in remaining_nodes:
|
||||
+ for node in sorted(remaining_nodes):
|
||||
print(self.__format_node_name(node))
|
||||
- for key,value in ZONEINFO_PAGE_INFO.items():
|
||||
- print("\t",key,manager.metric_value(value,node))
|
||||
- print(" "*14,"protection",manager.protection(node))
|
||||
+ for key, value in ZONEINFO_PAGE_INFO:
|
||||
+ print("\t", key, manager.metric_value(value, node))
|
||||
+ print(" " * 14 + "protection " + str([int(i) for i in manager.protection(node)]))
|
||||
else:
|
||||
continue
|
||||
except IndexError:
|
||||
print("Got some error while printing values for zoneinfo")
|
||||
|
||||
-
|
||||
-
|
||||
-
|
||||
- def print_report(self,group,timestamp,header_indentation,value_indentation,manager_zoneinfo):
|
||||
+ def print_report(self, group, timestamp, manager_zoneinfo):
|
||||
def __print_zone_status():
|
||||
zonestatus = ZoneStatUtil(manager_zoneinfo)
|
||||
if zonestatus.names():
|
||||
try:
|
||||
self.__print_machine_info(group)
|
||||
- print("TimeStamp = ",timestamp)
|
||||
- self.__print_values(timestamp, header_indentation, value_indentation, zonestatus)
|
||||
+ print("TimeStamp = {}".format(timestamp))
|
||||
+ if newVersionFlag:
|
||||
+ self.__print_values(zonestatus)
|
||||
+ else:
|
||||
+ self.__print_old_version_values(zonestatus)
|
||||
except IndexError:
|
||||
print("Incorrect machine info due to some missing metrics")
|
||||
return
|
||||
@@ -271,12 +302,12 @@ class ZoneinfoReport(pmcc.MetricGroupPrinter):
|
||||
self.samples = opts.pmGetOptionSamples()
|
||||
t_s = group.contextCache.pmLocaltime(int(group.timestamp))
|
||||
timestamp = time.strftime(ZoneinfoOptions.timefmt, t_s.struct_time())
|
||||
- header_indentation = " " if len(timestamp) < 9 else (len(timestamp) - 7) * " "
|
||||
- value_indentation = ((len(header_indentation) + 9) - len(timestamp)) * " "
|
||||
- self.print_report(group,timestamp,header_indentation,value_indentation,manager['zoneinfo'])
|
||||
+ self.print_report(group, timestamp, manager['zoneinfo'])
|
||||
+
|
||||
|
||||
class ZoneinfoOptions(pmapi.pmOptions):
|
||||
timefmt = "%H:%M:%S"
|
||||
+
|
||||
def __init__(self):
|
||||
pmapi.pmOptions.__init__(self, "a:s:Z:zV?")
|
||||
self.pmSetLongOptionHeader("General options")
|
||||
@@ -288,19 +319,59 @@ class ZoneinfoOptions(pmapi.pmOptions):
|
||||
self.samples = None
|
||||
self.context = None
|
||||
|
||||
+
|
||||
+def updateZoneinfoDataToBeFetched():
|
||||
+ ZoneinfoNumberZoneElementsToBeRemoved = {
|
||||
+ "nr_zone_active_anon" : "mem.zoneinfo.nr_zone_active_anon",
|
||||
+ "nr_zone_inactive_file" : "mem.zoneinfo.nr_zone_inactive_file",
|
||||
+ "nr_zone_active_file" : "mem.zoneinfo.nr_zone_active_file",
|
||||
+ "nr_zone_unevictable" : "mem.zoneinfo.nr_zone_unevictable",
|
||||
+ "nr_zone_write_pending" : "mem.zoneinfo.nr_zone_write_pending",
|
||||
+ "nr_zspages" : "mem.zoneinfo.nr_zspages",
|
||||
+ "numa_local" : "mem.zoneinfo.numa_local",
|
||||
+ "nr_zone_inactive_anon" : "mem.zoneinfo.nr_zone_inactive_anon"
|
||||
+ }
|
||||
+
|
||||
+ ZoneinfoPerNodeElementsToBeRemoved = {
|
||||
+ "nr_shmem_hugepages" : "mem.zoneinfo.nr_shmem_hugepages",
|
||||
+ "nr_shmem_pmdmapped" : "mem.zoneinfo.nr_shmem_pmdmapped",
|
||||
+ "nr_file_hugepages" : "mem.zoneinfo.nr_file_hugepages",
|
||||
+ "nr_file_pmdmapped" : "mem.zoneinfo.nr_file_pmdmapped",
|
||||
+ "nr_kernel_misc_reclaimable" : "mem.zoneinfo.nr_kernel_misc_reclaimable"
|
||||
+ }
|
||||
+
|
||||
+ for key in ZoneinfoNumberZoneElementsToBeRemoved:
|
||||
+ del ZONEINFO_NUMBER_ZONE[key]
|
||||
+
|
||||
+ for key in ZoneinfoPerNodeElementsToBeRemoved:
|
||||
+ del ZONEINFO_PER_NODE[key]
|
||||
+
|
||||
+
|
||||
+
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
opts = ZoneinfoOptions()
|
||||
- mngr = pmcc.MetricGroupManager.builder(opts,sys.argv)
|
||||
+ mngr = pmcc.MetricGroupManager.builder(opts, sys.argv)
|
||||
opts.context = mngr.type
|
||||
- missing = mngr.checkMissingMetrics(ALL_METRICS)
|
||||
- if missing is not None:
|
||||
- sys.stderr.write("\nError:some metrics are unavailable ".join(missing) + '\n')
|
||||
- sys.exit(1)
|
||||
+ ZONESTAT_METRICS = ZONESTAT_METRICS + ZONESTAT_NEW_METRICS
|
||||
+
|
||||
+ if opts.context == PM_CONTEXT_HOST:
|
||||
+ context = pmapi.pmContext(opts.context)
|
||||
+ elif opts.context == PM_CONTEXT_ARCHIVE:
|
||||
+ context = pmapi.pmContext.fromOptions(opts,sys.argv)
|
||||
+ try:
|
||||
+ metric_ids =context.pmLookupName(ZONESTAT_METRICS)
|
||||
+ descs= context.pmLookupDescs(metric_ids)
|
||||
+ except pmapi.pmErr as error:
|
||||
+ newVersionFlag = False
|
||||
+ updateZoneinfoDataToBeFetched()
|
||||
+ ZONESTAT_METRICS = [x for x in ZONESTAT_METRICS if x not in ZONESTAT_NEW_METRICS]
|
||||
+
|
||||
+ ALL_METRICS = ZONESTAT_METRICS + SYS_METRICS
|
||||
mngr["zoneinfo"] = ZONESTAT_METRICS
|
||||
- mngr["sysinfo"] = SYS_MECTRICS
|
||||
+ mngr["sysinfo"] = SYS_METRICS
|
||||
mngr["allinfo"] = ALL_METRICS
|
||||
- mngr.printer = ZoneinfoReport(opts.samples,mngr,opts.context)
|
||||
+ mngr.printer = ZoneinfoReport(opts.samples, mngr, opts.context)
|
||||
sts = mngr.run()
|
||||
sys.exit(sts)
|
||||
except pmapi.pmErr as error:
|
||||
--
|
||||
2.39.3
|
||||
|
||||
@ -1,640 +0,0 @@
|
||||
From 0f582f5914cd774cdeb1ce5425760941641ffbc7 Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Fri, 25 Oct 2024 16:14:26 +0530
|
||||
Subject: [PATCH ol8 1012/1015] pmlogcheck: Fixed o(n*n) nested hash table
|
||||
lookup
|
||||
|
||||
upstream ref:- 441b804dee08a7c506ccb4848fe5833d62e79335
|
||||
Orabug:36995894
|
||||
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
|
||||
---
|
||||
src/pmlogcheck/GNUmakefile | 7 +-
|
||||
src/pmlogcheck/logdecompress | 258 +++++++++++++++++++++++++++++++++++
|
||||
src/pmlogcheck/pass3.c | 121 ++++++++--------
|
||||
src/pmlogcheck/pmlogcheck.c | 60 +++++++-
|
||||
4 files changed, 369 insertions(+), 77 deletions(-)
|
||||
create mode 100755 src/pmlogcheck/logdecompress
|
||||
|
||||
diff --git a/src/pmlogcheck/GNUmakefile b/src/pmlogcheck/GNUmakefile
|
||||
index 8308b40..c7a0d01 100644
|
||||
--- a/src/pmlogcheck/GNUmakefile
|
||||
+++ b/src/pmlogcheck/GNUmakefile
|
||||
@@ -18,14 +18,16 @@ include $(TOPDIR)/src/include/builddefs
|
||||
CFILES = pmlogcheck.c pass0.c pass1.c pass2.c pass3.c
|
||||
HFILES = logcheck.h
|
||||
CMDTARGET = pmlogcheck$(EXECSUFFIX)
|
||||
+HELPER = logdecompress
|
||||
LLDLIBS = $(PCPLIB) $(LIB_FOR_MATH)
|
||||
|
||||
-default: $(CMDTARGET)
|
||||
+default: $(CMDTARGET) $(HELPER)
|
||||
|
||||
include $(BUILDRULES)
|
||||
|
||||
install: $(CMDTARGET)
|
||||
$(INSTALL) -m 755 $(CMDTARGET) $(PCP_BIN_DIR)/$(CMDTARGET)
|
||||
+ $(INSTALL) -m 755 $(HELPER) $(PCP_SHARE_DIR)/bin/$(HELPER)
|
||||
|
||||
default_pcp: default
|
||||
|
||||
@@ -34,6 +36,3 @@ install_pcp: install
|
||||
$(OBJECTS): logcheck.h
|
||||
|
||||
$(OBJECTS): $(TOPDIR)/src/include/pcp/libpcp.h
|
||||
-
|
||||
-check:: $(CFILES) $(HFILES)
|
||||
- $(CLINT) $^
|
||||
diff --git a/src/pmlogcheck/logdecompress b/src/pmlogcheck/logdecompress
|
||||
new file mode 100755
|
||||
index 0000000..f78757d
|
||||
--- /dev/null
|
||||
+++ b/src/pmlogcheck/logdecompress
|
||||
@@ -0,0 +1,258 @@
|
||||
+#!/bin/sh
|
||||
+#
|
||||
+# Decompress files of a PCP archive.
|
||||
+#
|
||||
+# Usage: logdecompress [-vn] [-d dir] archive
|
||||
+#
|
||||
+# Copyright (c) 2024 Ken McDonell, Inc. All Rights Reserved.
|
||||
+#
|
||||
+# This program is free software; you can redistribute it and/or modify it
|
||||
+# under the terms of the GNU General Public License as published by the
|
||||
+# Free Software Foundation; either version 2 of the License, or (at your
|
||||
+# option) any later version.
|
||||
+#
|
||||
+# This program is distributed in the hope that it will be useful, but
|
||||
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
+# for more details.
|
||||
+#
|
||||
+# You should have received a copy of the GNU General Public License along
|
||||
+# with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
+#
|
||||
+
|
||||
+. $PCP_DIR/etc/pcp.env
|
||||
+
|
||||
+prog=`basename $0`
|
||||
+usage="Usage: $prog [-nv] [-d dir] archive"
|
||||
+
|
||||
+tmp=/var/tmp/logdecompress.$$
|
||||
+status=1
|
||||
+trap "rm -f $tmp.*; exit \$status" 0 1 2 3 15
|
||||
+
|
||||
+_decompress()
|
||||
+{
|
||||
+ if $showme
|
||||
+ then
|
||||
+ echo >&2 "+ $*"
|
||||
+ elif ! $*
|
||||
+ then
|
||||
+ echo >&2 "$* file failed"
|
||||
+ exit
|
||||
+ fi
|
||||
+}
|
||||
+
|
||||
+showme=false
|
||||
+verbose=false
|
||||
+dir=''
|
||||
+while getopts 'd:nv\?' c
|
||||
+do
|
||||
+ case "$c"
|
||||
+ in
|
||||
+ d)
|
||||
+ if [ -z "$OPTARG" ]
|
||||
+ then
|
||||
+ echo >&2 "$prog: -c requires a dir argument"
|
||||
+ exit
|
||||
+ fi
|
||||
+ if [ ! -d "$OPTARG" ]
|
||||
+ then
|
||||
+ echo >&2 "$prog: $OPTARG is not an existing directory"
|
||||
+ exit
|
||||
+ fi
|
||||
+ dir="$OPTARG"
|
||||
+ ;;
|
||||
+ n)
|
||||
+ showme=true
|
||||
+ ;;
|
||||
+ v)
|
||||
+ verbose=true
|
||||
+ ;;
|
||||
+ \?)
|
||||
+ echo >&2 "$usage"
|
||||
+ status=0
|
||||
+ exit
|
||||
+ ;;
|
||||
+ esac
|
||||
+done
|
||||
+
|
||||
+if [ $OPTIND != $# ]
|
||||
+then
|
||||
+ echo >&2 "$usage"
|
||||
+ exit
|
||||
+fi
|
||||
+shift `expr $OPTIND - 1`
|
||||
+
|
||||
+archbase="`pmlogbasename $1`"
|
||||
+$verbose && echo >&2 "archbase=$archbase"
|
||||
+
|
||||
+# for all the files that seem to be part of this archive,
|
||||
+# count the files and the number that are compressed
|
||||
+#
|
||||
+nfile=0
|
||||
+ncompress=0
|
||||
+for file in ${archbase}*
|
||||
+do
|
||||
+ [ ! -f "$file" ] && continue
|
||||
+ if [ "$archbase" = `pmlogbasename "$file"` ]
|
||||
+ then
|
||||
+ nfile=`expr $nfile + 1`
|
||||
+ case $file
|
||||
+ in
|
||||
+ *.xz|*.lzma|*.bz2|*.bz|*.gz|*.Z|*.z)
|
||||
+ ncompress=`expr $ncompress + 1`
|
||||
+ if [ -z "$dir" ]
|
||||
+ then
|
||||
+ outfile=`echo "$file" | sed -e 's/\.[^.]*$//'`
|
||||
+ else
|
||||
+ outfile="$dir"/`basename "$file" | sed -e 's/\.[^.]*$//'`
|
||||
+ fi
|
||||
+ if [ -f "$outfile" ]
|
||||
+ then
|
||||
+ echo >&2 "$prog: $outfile exists and will not be clobbered"
|
||||
+ exit
|
||||
+ fi
|
||||
+ ;;
|
||||
+ esac
|
||||
+ fi
|
||||
+done
|
||||
+
|
||||
+if [ "$nfile" -eq 0 ]
|
||||
+then
|
||||
+ echo >&2 "No PCP archive files match \"$1\""
|
||||
+ exit
|
||||
+fi
|
||||
+
|
||||
+if [ "$ncompress" -eq 0 ]
|
||||
+then
|
||||
+ $verbose && echo >&2 "No compresssed PCP archive files match \"$1\""
|
||||
+ echo "$archbase"
|
||||
+ status=0
|
||||
+ exit
|
||||
+fi
|
||||
+$verbose && echo >&2 "$ncompress of $nfile files in the archive are compressed"
|
||||
+
|
||||
+for file in ${archbase}*
|
||||
+do
|
||||
+ [ ! -f "$file" ] && continue
|
||||
+ if [ "$archbase" = `pmlogbasename "$file"` ]
|
||||
+ then
|
||||
+ case $file
|
||||
+ in
|
||||
+ *.xz)
|
||||
+ if [ -z "$dir" ]
|
||||
+ then
|
||||
+ outfile=`basename "$file" .xz`
|
||||
+ _decompress xz --decompress "$file"
|
||||
+ else
|
||||
+ outfile="$dir"/`basename "$file" .xz`
|
||||
+ _decompress xz --decompress --stdout "$file" >"$outfile"
|
||||
+ fi
|
||||
+ $verbose && echo >&2 "$outfile: decompressed"
|
||||
+ ;;
|
||||
+
|
||||
+ *.lzma)
|
||||
+ if [ -z "$dir" ]
|
||||
+ then
|
||||
+ outfile=`basename "$file" .lzma`
|
||||
+ _decompress xz --decompress --format=lzma "$file"
|
||||
+ else
|
||||
+ outfile="$dir"/`basename "$file" .lzma`
|
||||
+ _decompress xz --decompress --format=lzma --stdout "$file" >"$outfile"
|
||||
+ fi
|
||||
+ $verbose && echo >&2 "$outfile: decompressed"
|
||||
+ ;;
|
||||
+
|
||||
+ *.bz2)
|
||||
+ if [ -z "$dir" ]
|
||||
+ then
|
||||
+ outfile=`basename "$file" .bz2`
|
||||
+ _decompress bzip2 -d "$file"
|
||||
+ else
|
||||
+ outfile="$dir"/`basename "$file" .bz2`
|
||||
+ _decompress bzip2 -dc "$file" >"$outfile"
|
||||
+ fi
|
||||
+ $verbose && echo >&2 "$outfile: decompressed"
|
||||
+ ;;
|
||||
+
|
||||
+ *.bz)
|
||||
+ if [ -z "$dir" ]
|
||||
+ then
|
||||
+ outfile=`basename "$file" .bz`
|
||||
+ _decompress bzip2 -d "$file"
|
||||
+ else
|
||||
+ outfile="$dir"/`basename "$file" .bz`
|
||||
+ _decompress bzip2 -dc "$file" >"$outfile"
|
||||
+ fi
|
||||
+ $verbose && echo >&2 "$outfile: decompressed"
|
||||
+ ;;
|
||||
+
|
||||
+ *.gz)
|
||||
+ if [ -z "$dir" ]
|
||||
+ then
|
||||
+ outfile=`basename "$file" .gz`
|
||||
+ _decompress gzip -d "$file"
|
||||
+ else
|
||||
+ outfile="$dir"/`basename "$file" .gz`
|
||||
+ _decompress gzip -dc "$file" >"$outfile"
|
||||
+ fi
|
||||
+ $verbose && echo >&2 "$outfile: decompressed"
|
||||
+ ;;
|
||||
+
|
||||
+ *.z)
|
||||
+ if [ -z "$dir" ]
|
||||
+ then
|
||||
+ outfile=`basename "$file" .z`
|
||||
+ _decompress gzip -d "$file"
|
||||
+ else
|
||||
+ outfile="$dir"/`basename "$file" .z`
|
||||
+ _decompress gzip -dc "$file" >"$outfile"
|
||||
+ fi
|
||||
+ $verbose && echo >&2 "$outfile: decompressed"
|
||||
+ ;;
|
||||
+
|
||||
+ *.Z)
|
||||
+ echo >&2 TODO: $file
|
||||
+ ;;
|
||||
+
|
||||
+ *) # not compressed ... do nothing unless -d, and then
|
||||
+ # try ln(1), failing that cp(1)
|
||||
+ #
|
||||
+ if [ -n "$dir" ]
|
||||
+ then
|
||||
+ outfile="$dir/`basename $file`"
|
||||
+ if $showme
|
||||
+ then
|
||||
+ echo >&2 "+ ln $file $outfile"
|
||||
+ echo >&2 "+ if that fails ... cp $file $outfile"
|
||||
+ else
|
||||
+ if ! ln "$file" "$outfile" >$tmp.err 2>&1
|
||||
+ then
|
||||
+ $verbose && cat >&2 $tmp.err
|
||||
+ if ! cp "$file" "$outfile" >>$tmp.err 2>&1
|
||||
+ then
|
||||
+ cat >&2 $tmp.err
|
||||
+ echo >&2 "Failed to link or copy $file"
|
||||
+ exit
|
||||
+ else
|
||||
+ $verbose && echo >&2 "$outfile: copied"
|
||||
+ fi
|
||||
+ else
|
||||
+ $verbose && echo >&2 "$outfile: linked"
|
||||
+ fi
|
||||
+ fi
|
||||
+ fi
|
||||
+ ;;
|
||||
+ esac
|
||||
+ fi
|
||||
+done
|
||||
+
|
||||
+if [ -z "$dir" ]
|
||||
+then
|
||||
+ echo "$archbase"
|
||||
+else
|
||||
+ echo "$dir/$archbase"
|
||||
+fi
|
||||
+
|
||||
+status=0
|
||||
+exit
|
||||
diff --git a/src/pmlogcheck/pass3.c b/src/pmlogcheck/pass3.c
|
||||
index 6f17419..f6b80d3 100644
|
||||
--- a/src/pmlogcheck/pass3.c
|
||||
+++ b/src/pmlogcheck/pass3.c
|
||||
@@ -28,8 +28,7 @@ typedef struct {
|
||||
pmDesc desc;
|
||||
int valfmt;
|
||||
double scale;
|
||||
- instData **instlist;
|
||||
- unsigned int listsize;
|
||||
+ __pmHashCtl insthash;
|
||||
} checkData;
|
||||
|
||||
static __pmHashCtl hashlist; /* hash statistics about each metric */
|
||||
@@ -101,14 +100,13 @@ print_stamp(FILE *f, struct timeval *stamp)
|
||||
}
|
||||
|
||||
static double
|
||||
-unwrap(double current, struct timeval *curtime, checkData *checkdata, int index)
|
||||
+unwrap(double current, struct timeval *curtime, checkData *checkdata, instData *idp)
|
||||
{
|
||||
double outval = current;
|
||||
int wrapflag = 0;
|
||||
char *str = NULL;
|
||||
|
||||
- if ((current - checkdata->instlist[index]->lastval) < 0.0 &&
|
||||
- checkdata->instlist[index]->lasttime.tv_sec > 0) {
|
||||
+ if ((current - idp->lastval) < 0.0 && idp->lasttime.tv_sec > 0) {
|
||||
switch (checkdata->desc.type) {
|
||||
case PM_TYPE_32:
|
||||
case PM_TYPE_U32:
|
||||
@@ -130,16 +128,18 @@ unwrap(double current, struct timeval *curtime, checkData *checkdata, int index)
|
||||
print_stamp(stderr, curtime);
|
||||
fprintf(stderr, "]: ");
|
||||
print_metric(stderr, checkdata->desc.pmid);
|
||||
- if (pmNameInDomArchive(checkdata->desc.indom, checkdata->instlist[index]->inst, &str) < 0)
|
||||
+ if (pmNameInDomArchive(checkdata->desc.indom, idp->inst, &str) < 0)
|
||||
fprintf(stderr, ": %s wrap", typeStr(checkdata->desc.type));
|
||||
else {
|
||||
fprintf(stderr, "[%s]: %s wrap", str, typeStr(checkdata->desc.type));
|
||||
free(str);
|
||||
}
|
||||
- fprintf(stderr, "\n\tvalue %.0f at ", checkdata->instlist[index]->lastval);
|
||||
- print_stamp(stderr, &checkdata->instlist[index]->lasttime);
|
||||
+ fprintf(stderr, "\n\tvalue %.0f at ", idp->lastval);
|
||||
+ print_stamp(stderr, &idp->lasttime);
|
||||
fprintf(stderr, "\n\tvalue %.0f at ", current);
|
||||
print_stamp(stderr, curtime);
|
||||
+ if (vflag)
|
||||
+ fprintf(stderr, "\n\tdifference %.0f", current - idp->lastval);
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
|
||||
@@ -150,12 +150,13 @@ static void
|
||||
newHashInst(pmValue *vp,
|
||||
checkData *checkdata, /* updated by this function */
|
||||
int valfmt,
|
||||
- struct timeval *timestamp, /* timestamp for this sample */
|
||||
- int pos) /* position of this inst in instlist */
|
||||
+ struct timeval *timestamp /* timestamp for this sample */
|
||||
+ )
|
||||
{
|
||||
int sts;
|
||||
size_t size;
|
||||
pmAtomValue av;
|
||||
+ instData *idp;
|
||||
|
||||
if ((sts = pmExtractValue(valfmt, vp, checkdata->desc.type, &av, PM_TYPE_DOUBLE)) < 0) {
|
||||
fprintf(stderr, "%s.%d:[", l_archname, l_ctxp->c_archctl->ac_vol);
|
||||
@@ -166,18 +167,19 @@ newHashInst(pmValue *vp,
|
||||
fprintf(stderr, "%s: possibly corrupt archive?\n", pmGetProgname());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
- size = (pos+1)*sizeof(instData*);
|
||||
- checkdata->instlist = (instData**) realloc(checkdata->instlist, size);
|
||||
- if (!checkdata->instlist)
|
||||
- pmNoMem("newHashInst.instlist", size, PM_FATAL_ERR);
|
||||
- size = sizeof(instData);
|
||||
- checkdata->instlist[pos] = (instData*) malloc(size);
|
||||
- if (!checkdata->instlist[pos])
|
||||
- pmNoMem("newHashInst.instlist[pos]", size, PM_FATAL_ERR);
|
||||
- checkdata->instlist[pos]->inst = vp->inst;
|
||||
- checkdata->instlist[pos]->lastval = av.d;
|
||||
- checkdata->instlist[pos]->lasttime = *timestamp;
|
||||
- checkdata->listsize++;
|
||||
+ size = sizeof(instData);
|
||||
+ if ((idp = (instData *)malloc(sizeof(instData))) == NULL) {
|
||||
+ pmNoMem("newHashInst: instData", size, PM_FATAL_ERR);
|
||||
+ }
|
||||
+ idp->inst = vp->inst;
|
||||
+ idp->lastval = av.d;
|
||||
+ idp->lasttime = *timestamp;
|
||||
+ if ((sts = __pmHashAdd(vp->inst, (void *)idp, &checkdata->insthash)) < 0) {
|
||||
+ fprintf(stderr, "newHashInst: __pmHashAdd(%d, ...) for pmID %s failed: %s\n",
|
||||
+ vp->inst, pmIDStr(checkdata->desc.pmid), pmErrStr(sts));
|
||||
+ exit(EXIT_FAILURE);
|
||||
+ }
|
||||
+
|
||||
if (pmDebugOptions.appl1) {
|
||||
char *name;
|
||||
|
||||
@@ -207,6 +209,7 @@ newHashItem(pmValueSet *vsp,
|
||||
struct timeval *timestamp) /* timestamp for this sample */
|
||||
{
|
||||
int j;
|
||||
+ int sts;
|
||||
|
||||
checkdata->desc = *desc;
|
||||
checkdata->scale = 0.0;
|
||||
@@ -225,17 +228,21 @@ newHashItem(pmValueSet *vsp,
|
||||
checkdata->desc.units.dimTime--;
|
||||
}
|
||||
|
||||
- checkdata->listsize = 0;
|
||||
- checkdata->instlist = NULL;
|
||||
- for (j = 0; j < vsp->numval; j++) {
|
||||
- newHashInst(&vsp->vlist[j], checkdata, vsp->valfmt, timestamp, j);
|
||||
+ memset(&checkdata->insthash, 0, sizeof(checkdata->insthash));
|
||||
+ if ((sts = __pmHashPreAlloc(vsp->numval, &checkdata->insthash)) < 0) {
|
||||
+ fprintf(stderr, "newHashItem: __pmHashPreAlloc(%d, ...) for pmID %s failed: %s\n",
|
||||
+ vsp->numval, pmIDStr(checkdata->desc.pmid), pmErrStr(sts));
|
||||
+ exit(EXIT_FAILURE);
|
||||
+ }
|
||||
+ for (j = 0; j < vsp->numval; j++) {
|
||||
+ newHashInst(&vsp->vlist[j], checkdata, vsp->valfmt, timestamp);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
docheck(pmResult *result)
|
||||
{
|
||||
- int i, j, k;
|
||||
+ int i, j;
|
||||
int sts;
|
||||
pmDesc desc;
|
||||
pmAtomValue av;
|
||||
@@ -312,13 +319,6 @@ docheck(pmResult *result)
|
||||
fprintf(stderr, "] ");
|
||||
print_metric(stderr, vsp->pmid);
|
||||
fprintf(stderr, ": __pmHashAdd good failed (internal pmlogcheck error)\n");
|
||||
- /* free memory allocated above on insert failure */
|
||||
- for (j = 0; j < vsp->numval; j++) {
|
||||
- if (checkdata->instlist[j] != NULL)
|
||||
- free(checkdata->instlist[j]);
|
||||
- }
|
||||
- if (checkdata->instlist != NULL)
|
||||
- free(checkdata->instlist);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
@@ -343,35 +343,19 @@ docheck(pmResult *result)
|
||||
}
|
||||
}
|
||||
for (j = 0; j < vsp->numval; j++) { /* iterate thro result values */
|
||||
+ __pmHashNode *hnp;
|
||||
+ instData *idp = NULL;
|
||||
+
|
||||
vp = &vsp->vlist[j];
|
||||
- k = j; /* index into stored inst list, result may differ */
|
||||
- if ((vsp->numval > 1) || (checkdata->desc.indom != PM_INDOM_NULL)) {
|
||||
- /* must store values using correct inst - probably in correct order already */
|
||||
- if ((k < checkdata->listsize) && (checkdata->instlist[k]->inst != vp->inst)) {
|
||||
- for (k = 0; k < checkdata->listsize; k++) {
|
||||
- if (vp->inst == checkdata->instlist[k]->inst) {
|
||||
- break; /* k now correct */
|
||||
- }
|
||||
- }
|
||||
- if (k == checkdata->listsize) { /* no matching inst was found */
|
||||
- newHashInst(vp, checkdata, vsp->valfmt, &result->timestamp, k);
|
||||
- continue;
|
||||
- }
|
||||
- }
|
||||
- else if (k >= checkdata->listsize) {
|
||||
- k = checkdata->listsize;
|
||||
- newHashInst(vp, checkdata, vsp->valfmt, &result->timestamp, k);
|
||||
- continue;
|
||||
- }
|
||||
- }
|
||||
- if (k >= checkdata->listsize) { /* only error values observed so far */
|
||||
- k = checkdata->listsize;
|
||||
- newHashInst(vp, checkdata, vsp->valfmt, &result->timestamp, k);
|
||||
+ hnp = __pmHashSearch(vp->inst, &checkdata->insthash);
|
||||
+ if (hnp == NULL) {
|
||||
+ /* first time for this inst and this metric */
|
||||
+ newHashInst(vp, checkdata, vsp->valfmt, &result->timestamp);
|
||||
continue;
|
||||
}
|
||||
-
|
||||
+ idp = (instData *)hnp->data;
|
||||
timediff = result->timestamp;
|
||||
- tsub(&timediff, &(checkdata->instlist[k]->lasttime));
|
||||
+ tsub(&timediff, &idp->lasttime);
|
||||
if (timediff.tv_sec < 0 || timediff.tv_usec < 0) {
|
||||
/* clip negative values at zero */
|
||||
timediff.tv_sec = 0;
|
||||
@@ -397,10 +381,10 @@ docheck(pmResult *result)
|
||||
fprintf(stderr, ": current counter value is %.0f\n", av.d);
|
||||
}
|
||||
if (nowrap == 0)
|
||||
- unwrap(av.d, &(result->timestamp), checkdata, k);
|
||||
+ unwrap(av.d, &(result->timestamp), checkdata, idp);
|
||||
}
|
||||
- checkdata->instlist[k]->lastval = av.d;
|
||||
- checkdata->instlist[k]->lasttime = result->timestamp;
|
||||
+ idp->lastval = av.d;
|
||||
+ idp->lasttime = result->timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -513,14 +497,17 @@ pass3(__pmContext *ctxp, char *archname, pmOptions *opts)
|
||||
* at next fetch (mimic interp.c from libpcp)
|
||||
*/
|
||||
__pmHashNode *hptr;
|
||||
- checkData *checkdata;
|
||||
- int k;
|
||||
+ /* walk hash list of metrics */
|
||||
for (hptr = __pmHashWalk(&hashlist, PM_HASH_WALK_START);
|
||||
hptr != NULL;
|
||||
hptr = __pmHashWalk(&hashlist, PM_HASH_WALK_NEXT)) {
|
||||
- checkdata = (checkData *)hptr->data;
|
||||
- for (k = 0; k < checkdata->listsize; k++) {
|
||||
- checkdata->instlist[k]->lasttime.tv_sec = 0;
|
||||
+ checkData *checkdata;
|
||||
+ __pmHashNode *hnp;
|
||||
+ checkdata = (checkData *)hptr->data;
|
||||
+ for (hnp = __pmHashWalk(&checkdata->insthash, PM_HASH_WALK_START);
|
||||
+ hnp != NULL;
|
||||
+ hnp = __pmHashWalk(&checkdata->insthash, PM_HASH_WALK_NEXT)) {
|
||||
+ ((instData *)(hnp->data))->lasttime.tv_sec = 0;
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/src/pmlogcheck/pmlogcheck.c b/src/pmlogcheck/pmlogcheck.c
|
||||
index 9927a79..0f73011 100644
|
||||
--- a/src/pmlogcheck/pmlogcheck.c
|
||||
+++ b/src/pmlogcheck/pmlogcheck.c
|
||||
@@ -170,6 +170,10 @@ main(int argc, char *argv[])
|
||||
char *archpathname; /* from the command line */
|
||||
char *archdirname; /* after dirname() */
|
||||
char archname[MAXPATHLEN]; /* full pathname to base of archive name */
|
||||
+ struct timespec then_real;
|
||||
+ struct timespec now_real;
|
||||
+ struct timespec then_cpu;
|
||||
+ struct timespec now_cpu;
|
||||
|
||||
while ((c = pmGetOptions(argc, argv, &opts)) != EOF) {
|
||||
switch (c) {
|
||||
@@ -251,9 +255,21 @@ main(int argc, char *argv[])
|
||||
else {
|
||||
pmsprintf(path, sizeof(path), "%s%c%s", archdirname, sep, namelist[i]->d_name);
|
||||
}
|
||||
+ if (pmDebugOptions.appl3) {
|
||||
+ clock_gettime(CLOCK_MONOTONIC_RAW, &then_real);
|
||||
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &then_cpu);
|
||||
+ }
|
||||
if (pass0(path) == STS_FATAL)
|
||||
/* unrepairable or unrepaired error */
|
||||
sts = STS_FATAL;
|
||||
+ if (pmDebugOptions.appl3) {
|
||||
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now_real);
|
||||
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &now_cpu);
|
||||
+ fprintf(stderr, "pass0(%s) elapsed %.3fs cpu %.6fs\n",
|
||||
+ namelist[i]->d_name,
|
||||
+ pmtimespecSub(&now_real, &then_real),
|
||||
+ pmtimespecSub(&now_cpu, &then_cpu));
|
||||
+ }
|
||||
}
|
||||
if (meta_state == STATE_MISSING) {
|
||||
fprintf(stderr, "%s%c%s.meta: missing metadata file\n", archdirname, sep, archbasename);
|
||||
@@ -307,19 +323,51 @@ main(int argc, char *argv[])
|
||||
strncpy(archname, archbasename, sizeof(archname) - 1);
|
||||
else
|
||||
pmsprintf(archname, sizeof(archname), "%s%c%s", archdirname, sep, archbasename);
|
||||
-
|
||||
+ if (pmDebugOptions.appl3) {
|
||||
+ clock_gettime(CLOCK_MONOTONIC_RAW, &then_real);
|
||||
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &then_cpu);
|
||||
+ }
|
||||
sts = pass1(ctxp, archname);
|
||||
-
|
||||
+ if (pmDebugOptions.appl3) {
|
||||
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now_real);
|
||||
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &now_cpu);
|
||||
+ fprintf(stderr, "pass1(%s) elapsed %.3fs cpu %.6fs\n",
|
||||
+ archbasename,
|
||||
+ pmtimespecSub(&now_real, &then_real),
|
||||
+ pmtimespecSub(&now_cpu, &then_cpu));
|
||||
+ }
|
||||
if (index_state == STATE_BAD) {
|
||||
/* prevent subsequent use of bad temporal index */
|
||||
ctxp->c_archctl->ac_log->numti = 0;
|
||||
}
|
||||
-
|
||||
+ if (pmDebugOptions.appl3) {
|
||||
+ clock_gettime(CLOCK_MONOTONIC_RAW, &then_real);
|
||||
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &then_cpu);
|
||||
+ }
|
||||
sts = pass2(ctxp, archname);
|
||||
-
|
||||
- if (!mflag)
|
||||
+ if (pmDebugOptions.appl3) {
|
||||
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now_real);
|
||||
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &now_cpu);
|
||||
+ fprintf(stderr, "pass2(%s) elapsed %.3fs cpu %.6fs\n",
|
||||
+ archbasename,
|
||||
+ pmtimespecSub(&now_real, &then_real),
|
||||
+ pmtimespecSub(&now_cpu, &then_cpu));
|
||||
+ }
|
||||
+ if (!mflag){
|
||||
+ if (pmDebugOptions.appl3) {
|
||||
+ clock_gettime(CLOCK_MONOTONIC_RAW, &then_real);
|
||||
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &then_cpu);
|
||||
+ }
|
||||
sts = pass3(ctxp, archname, &opts);
|
||||
-
|
||||
+ if (pmDebugOptions.appl3) {
|
||||
+ clock_gettime(CLOCK_MONOTONIC_RAW, &now_real);
|
||||
+ clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &now_cpu);
|
||||
+ fprintf(stderr, "pass3(%s) elapsed %.3fs cpu %.6fs\n",
|
||||
+ archbasename,
|
||||
+ pmtimespecSub(&now_real, &then_real),
|
||||
+ pmtimespecSub(&now_cpu, &then_cpu));
|
||||
+ }
|
||||
+ }
|
||||
if (vflag) {
|
||||
if (result_count > 0)
|
||||
fprintf(stderr, "Processed %d pmResult records\n", result_count);
|
||||
--
|
||||
2.43.5
|
||||
|
||||
@ -1,419 +0,0 @@
|
||||
From e773134d1cd8ee998ddaac3dd9b9f5039eef4897 Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Fri, 25 Oct 2024 16:15:56 +0530
|
||||
Subject: [PATCH ol8 1013/1015] pcp-ps: added args option to view full process
|
||||
argument
|
||||
|
||||
updated the man page for the changes as well
|
||||
upstream ref:- https://github.com/performancecopilot/pcp/pull/2094
|
||||
|
||||
Orabug: 37062125
|
||||
Orabug: 37063335
|
||||
Orabug: 37062126
|
||||
Orabug: 37064841
|
||||
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
|
||||
---
|
||||
src/pcp/ps/pcp-ps.1 | 30 +++++--
|
||||
src/pcp/ps/pcp-ps.py | 188 ++++++++++++++++++-------------------------
|
||||
2 files changed, 99 insertions(+), 119 deletions(-)
|
||||
|
||||
diff --git a/src/pcp/ps/pcp-ps.1 b/src/pcp/ps/pcp-ps.1
|
||||
index 7ca98db..8c44e67 100755
|
||||
--- a/src/pcp/ps/pcp-ps.1
|
||||
+++ b/src/pcp/ps/pcp-ps.1
|
||||
@@ -69,14 +69,14 @@ Display all the process.
|
||||
.br
|
||||
.TS
|
||||
l l.
|
||||
-PID Process idenfier.
|
||||
-TTY The termianl assoicated with the prcoess.
|
||||
+PID Process identifier.
|
||||
+TTY The terminal associated with the process.
|
||||
TIME T{
|
||||
.ad l
|
||||
.hy 0
|
||||
The cumulated CPU time in [DD-]hh:mm:ss format (time=TIME).
|
||||
T}
|
||||
-CMD The command name of the task.
|
||||
+CMD The task name along with its complete arguments.
|
||||
.TE
|
||||
.TP
|
||||
.B \-c [\fIcommand name\fR]
|
||||
@@ -106,7 +106,7 @@ It is a single argument in the form of a blank-separated or comma-separated list
|
||||
The argument to -o are following:
|
||||
|
||||
.TS
|
||||
-lfB lfB lfB
|
||||
+lfB lfB lfB
|
||||
lfB l lx.
|
||||
COL HEADER DESCRIPTION
|
||||
_ _ _
|
||||
@@ -118,8 +118,14 @@ physical memory on the machine expressed as a percentage
|
||||
T}
|
||||
start START time the command started
|
||||
time TIME accumulated cpu time, user + system
|
||||
-cls CLS scheduling class of the process
|
||||
-cmd CMD see \fBargs\fR. (alias args, command).
|
||||
+cls CLS scheduling class of the process.
|
||||
+cmd CMD see \fBTask name\fR. (alias args, command).
|
||||
+args COMMAND T{
|
||||
+.ad l
|
||||
+.hy 0
|
||||
+To display the full program name with its arguments (use at last
|
||||
+position in -o list to view full command)
|
||||
+T}
|
||||
pid PID The process ID
|
||||
ppid PPID Parent process ID
|
||||
pri PRI Priority of the process
|
||||
@@ -129,7 +135,7 @@ rss RSS T{
|
||||
.hy 0
|
||||
the non-swapped physical memory that a task has used
|
||||
T}
|
||||
-rtprio RTPRIO realtime priority
|
||||
+rtprio RTPRIO real-time priority
|
||||
pname Pname Process name
|
||||
tty TT controlling tty (terminal)
|
||||
uid UID see \fBeuid\fR
|
||||
@@ -151,7 +157,7 @@ For example:
|
||||
.B pcp-ps \-o pid,\:user,\:args
|
||||
|
||||
.TS
|
||||
-lfB lfB lfB
|
||||
+lfB lfB lfB
|
||||
lfB l lx.
|
||||
CODE HEADER DESCRIPTION
|
||||
_ _ _
|
||||
@@ -286,3 +292,11 @@ For environment variables affecting PCP tools, see \fBpmGetOptions\fP(3).
|
||||
.BR strftime (3)
|
||||
and
|
||||
.BR environ (7).
|
||||
+
|
||||
+.\" control lines for scripts/man-spell
|
||||
+.\" +ok+ SCHED_DEADLINE SCHED_BATCH SCHED_OTHER SCHED_FIFO SCHED_IDLE
|
||||
+.\" +ok+ SCHED_ISO SCHED_RR cputime
|
||||
+.\" +ok+ RTPRIO rtprio WCHAN PPIDs EUSER wchan Pname pname uname
|
||||
+.\" +ok+ vsize euser PPID EUID ppid args euid IDL DLN CLS CMD COL PRI RSS
|
||||
+.\" +ok+ VSZ cls cmd col pri vsz DD FF RR TT hh ss
|
||||
+.\" +ok+ realtime {not real-time, from (cputime/realtime ratio)}
|
||||
diff --git a/src/pcp/ps/pcp-ps.py b/src/pcp/ps/pcp-ps.py
|
||||
index dbd5565..94a428f 100755
|
||||
--- a/src/pcp/ps/pcp-ps.py
|
||||
+++ b/src/pcp/ps/pcp-ps.py
|
||||
@@ -162,8 +162,9 @@ class ProcessFilter:
|
||||
|
||||
|
||||
class ProcessStatusUtil:
|
||||
- def __init__(self, instance, delta_time, metrics_repository):
|
||||
+ def __init__(self, instance, manager, delta_time, metrics_repository):
|
||||
self.instance = instance
|
||||
+ self.manager = manager
|
||||
self.__delta_time = delta_time
|
||||
self.__metric_repository = metrics_repository
|
||||
|
||||
@@ -207,8 +208,11 @@ class ProcessStatusUtil:
|
||||
data = '-'
|
||||
return data
|
||||
|
||||
- def process_name_with_args(self):
|
||||
- data = self.__metric_repository.current_value('proc.psinfo.psargs', self.instance)[:30]
|
||||
+ def process_name_with_args(self,flag = False):
|
||||
+ if flag is True:
|
||||
+ data = self.__metric_repository.current_value('proc.psinfo.psargs', self.instance)
|
||||
+ else:
|
||||
+ data = self.__metric_repository.current_value('proc.psinfo.psargs', self.instance)[:30]
|
||||
if len(data) < 30:
|
||||
whitespace = 30 - len(data)
|
||||
res = data.ljust(whitespace + len(data), ' ')
|
||||
@@ -216,6 +220,8 @@ class ProcessStatusUtil:
|
||||
else:
|
||||
return data
|
||||
|
||||
+ def process_name_with_args_last(self):
|
||||
+ return self.process_name_with_args(True)
|
||||
def vsize(self):
|
||||
return self.__metric_repository.current_value('proc.psinfo.vsize', self.instance)
|
||||
|
||||
@@ -294,10 +300,10 @@ class ProcessStatusUtil:
|
||||
|
||||
def start(self):
|
||||
s_time = self.__metric_repository.current_value('proc.psinfo.start_time', self.instance)
|
||||
- group = manager['psstat']
|
||||
+ group = self.manager['psstat']
|
||||
kernel_boottime = group['kernel.all.boottime'].netValues[0][2]
|
||||
- ts = group.contextCache.pmLocaltime(int(0.5 + kernel_boottime + (s_time / 1000)))
|
||||
- if group.timestamp.tv_sec - int(0.5 + kernel_boottime + s_time / 1000) >= 24*60*60:
|
||||
+ ts = group.contextCache.pmLocaltime(int(kernel_boottime + (s_time / 1000)))
|
||||
+ if group.timestamp.tv_sec - (kernel_boottime + s_time / 1000) >= 24*60*60:
|
||||
# started one day or more ago, use MmmDD HH:MM
|
||||
return time.strftime("%b%d %H:%M", ts.struct_time())
|
||||
else:
|
||||
@@ -346,14 +352,16 @@ PIDINFO_PAIR = {"%cpu": ('%CPU', ProcessStatusUtil.system_percent),
|
||||
"start": ("START\t", ProcessStatusUtil.start),
|
||||
"time": ("TIME\t", ProcessStatusUtil.total_time),
|
||||
"cls": ("CLS", ProcessStatusUtil.policy),
|
||||
- "cmd": ("Command\t\t\t", ProcessStatusUtil.process_name_with_args),
|
||||
+ "cmd": ("Command\t\t\t", ProcessStatusUtil.process_name),
|
||||
+ "args": ("Command\t\t\t", ProcessStatusUtil.process_name_with_args),
|
||||
+ "args_last": ("Command\t\t\t", ProcessStatusUtil.process_name_with_args_last),
|
||||
"pid": ("PID\t", ProcessStatusUtil.pid),
|
||||
"ppid": ("PPID\t", ProcessStatusUtil.ppid),
|
||||
"pri": ("PRI", ProcessStatusUtil.priority),
|
||||
"state": ("S", ProcessStatusUtil.s_name),
|
||||
"rss": ("RSS", ProcessStatusUtil.rss),
|
||||
"rtprio": ("RTPRIO", ProcessStatusUtil.priority),
|
||||
- "tty": ("TT", ProcessStatusUtil.tty_name),
|
||||
+ "tty": ("TTY\t", ProcessStatusUtil.tty_name),
|
||||
"pname": ("Pname\t\t", ProcessStatusUtil.process_name),
|
||||
"vsize": ("VSZ", ProcessStatusUtil.vsize),
|
||||
"uname": ("USER\t", ProcessStatusUtil.user_name),
|
||||
@@ -362,11 +370,13 @@ PIDINFO_PAIR = {"%cpu": ('%CPU', ProcessStatusUtil.system_percent),
|
||||
|
||||
|
||||
class ProcessStatus:
|
||||
- def __init__(self, metric_repository):
|
||||
+ def __init__(self, manager, metric_repository):
|
||||
+ self.__manager = manager
|
||||
self.__metric_repository = metric_repository
|
||||
|
||||
def get_processes(self, delta_time):
|
||||
- return map((lambda pid: (ProcessStatusUtil(pid, delta_time, self.__metric_repository))), self.__pids())
|
||||
+ return map(lambda pid:
|
||||
+ (ProcessStatusUtil(pid, self.__manager, delta_time, self.__metric_repository)), self.__pids())
|
||||
|
||||
def __pids(self):
|
||||
pid_dict = self.__metric_repository.current_values('proc.psinfo.pid')
|
||||
@@ -381,6 +391,9 @@ class DynamicProcessReporter:
|
||||
self.printer = printer
|
||||
self.processStatOptions = processStatOptions
|
||||
|
||||
+ def _is_last_and_args(self, key):
|
||||
+ return (key == "args") and \
|
||||
+ self.processStatOptions.colum_list.index(key) == len(self.processStatOptions.colum_list) - 1
|
||||
def print_report(self, timestamp, header_indentation, value_indentation):
|
||||
|
||||
# when the print count is exhausted exit the program gracefully
|
||||
@@ -437,7 +450,9 @@ class DynamicProcessReporter:
|
||||
for process in processes:
|
||||
data_to_print = timestamp + '\t'
|
||||
for key in self.processStatOptions.colum_list:
|
||||
- if key in PIDINFO_PAIR:
|
||||
+ if self._is_last_and_args(key):
|
||||
+ data_to_print += str(PIDINFO_PAIR["args_last"][1](process)) + '\t\t'
|
||||
+ elif key in PIDINFO_PAIR:
|
||||
data_to_print += str(PIDINFO_PAIR[key][1](process)) + '\t\t'
|
||||
print(data_to_print)
|
||||
|
||||
@@ -464,7 +479,7 @@ class ProcessStatusReporter:
|
||||
self.printer("Timestamp" + header_indentation + "PID\t\t\tTTY\tTIME\t\tCMD")
|
||||
processes = self.process_filter.filter_processes(self.process_report.get_processes(self.delta_time))
|
||||
for process in processes:
|
||||
- command = process.process_name()
|
||||
+ command = process.process_name_with_args(True)
|
||||
ttyname = process.tty_name()
|
||||
self.printer("%s%s%s\t\t%s\t%s\t%s" % (timestamp, value_indentation, process.pid(), ttyname,
|
||||
process.total_time(), command))
|
||||
@@ -527,134 +542,85 @@ class ProcessStatusReporter:
|
||||
process.total_time(), command))
|
||||
|
||||
|
||||
-class ProcessstatReport(pmcc.MetricGroupPrinter):
|
||||
+class ProcessStatReport(pmcc.MetricGroupPrinter):
|
||||
Machine_info_count = 0
|
||||
+ group = None
|
||||
+ def __init__(self, group=None):
|
||||
+ self.group = group
|
||||
|
||||
- def timeStampDelta(self, group):
|
||||
- s = group.timestamp.tv_sec - group.prevTimestamp.tv_sec
|
||||
- u = group.timestamp.tv_usec - group.prevTimestamp.tv_usec
|
||||
+ def timeStampDelta(self):
|
||||
+ s = self.group.timestamp.tv_sec - self.group.prevTimestamp.tv_sec
|
||||
+ u = self.group.timestamp.tv_usec - self.group.prevTimestamp.tv_usec
|
||||
return s + u / 1000000.0
|
||||
|
||||
- def print_machine_info(self, group, context):
|
||||
- timestamp = context.pmLocaltime(group.timestamp.tv_sec)
|
||||
+ def print_machine_info(self,context):
|
||||
+ timestamp = context.pmLocaltime(self.group.timestamp.tv_sec)
|
||||
# Please check strftime(3) for different formatting options.
|
||||
# Also check TZ and LC_TIME environment variables for more
|
||||
# information on how to override the default formatting of
|
||||
# the date display in the header
|
||||
time_string = time.strftime("%x", timestamp.struct_time())
|
||||
header_string = ''
|
||||
- header_string += group['kernel.uname.sysname'].netValues[0][2] + ' '
|
||||
- header_string += group['kernel.uname.release'].netValues[0][2] + ' '
|
||||
- header_string += '(' + group['kernel.uname.nodename'].netValues[0][2] + ') '
|
||||
+ header_string += self.group['kernel.uname.sysname'].netValues[0][2] + ' '
|
||||
+ header_string += self.group['kernel.uname.release'].netValues[0][2] + ' '
|
||||
+ header_string += '(' + self.group['kernel.uname.nodename'].netValues[0][2] + ') '
|
||||
header_string += time_string + ' '
|
||||
- header_string += group['kernel.uname.machine'].netValues[0][2] + ' '
|
||||
- print("%s (%s CPU)" % (header_string, self.get_ncpu(group)))
|
||||
+ header_string += self.group['kernel.uname.machine'].netValues[0][2] + ' '
|
||||
+ print("%s (%s CPU)" % (header_string, self.__get_ncpu(self.group)))
|
||||
|
||||
- def get_ncpu(self, group):
|
||||
+ def __get_ncpu(self, group):
|
||||
return group['hinv.ncpu'].netValues[0][2]
|
||||
|
||||
+ def __print_report(self, manager,timestamp, header_indentation, value_indentation,interval_in_seconds):
|
||||
+ metric_repository = ReportingMetricRepository(self.group)
|
||||
+ process_report = ProcessStatus(manager, metric_repository)
|
||||
+ process_filter = ProcessFilter(ProcessStatOptions)
|
||||
+ stdout = StdoutPrinter()
|
||||
+ printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
+ report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
+ printdecorator.Print, ProcessStatOptions)
|
||||
+ report.print_report(timestamp, header_indentation, value_indentation)
|
||||
+ def __print_dynamic_report(self, manager,timestamp, header_indentation, value_indentation,interval_in_seconds):
|
||||
+ metric_repository = ReportingMetricRepository(self.group)
|
||||
+ process_report = ProcessStatus(manager, metric_repository)
|
||||
+ process_filter = ProcessFilter(ProcessStatOptions)
|
||||
+ stdout = StdoutPrinter()
|
||||
+ printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
+ report = DynamicProcessReporter(process_report, process_filter, interval_in_seconds,
|
||||
+ printdecorator.Print, ProcessStatOptions)
|
||||
+ report.print_report(timestamp, header_indentation, value_indentation)
|
||||
+ def __get_timestamp(self):
|
||||
+ ts = self.group.contextCache.pmLocaltime(int(self.group.timestamp))
|
||||
+ timestamp = time.strftime(ProcessStatOptions.timefmt, ts.struct_time())
|
||||
+ return timestamp
|
||||
+
|
||||
def report(self, manager):
|
||||
try:
|
||||
- group = manager['psstat']
|
||||
- if group['proc.psinfo.utime'].netPrevValues is None:
|
||||
+ if self.group['proc.psinfo.utime'].netPrevValues is None:
|
||||
# need two fetches to report rate converted counter metrics
|
||||
return
|
||||
-
|
||||
- if not group['hinv.ncpu'].netValues or not group['kernel.uname.sysname'].netValues:
|
||||
+ if not self.group['hinv.ncpu'].netValues or not self.group['kernel.uname.sysname'].netValues:
|
||||
return
|
||||
-
|
||||
try:
|
||||
if not self.Machine_info_count:
|
||||
- self.print_machine_info(group, manager)
|
||||
+ self.print_machine_info(manager)
|
||||
self.Machine_info_count = 1
|
||||
except IndexError:
|
||||
- # missing some metrics
|
||||
return
|
||||
-
|
||||
- ts = group.contextCache.pmLocaltime(int(group.timestamp))
|
||||
- timestamp = time.strftime(ProcessStatOptions.timefmt, ts.struct_time())
|
||||
- interval_in_seconds = self.timeStampDelta(group)
|
||||
+ timestamp = self.__get_timestamp()
|
||||
+ interval_in_seconds = self.timeStampDelta()
|
||||
header_indentation = " " if len(timestamp) < 9 else (len(timestamp) - 7) * " "
|
||||
value_indentation = ((len(header_indentation) + 9) - len(timestamp)) * " "
|
||||
|
||||
- metric_repository = ReportingMetricRepository(group)
|
||||
-
|
||||
# Doing this for one single print instance in case there is no count specified
|
||||
if ProcessStatOptions.print_count is None:
|
||||
ProcessStatOptions.print_count = 1
|
||||
-
|
||||
- # ================================================================
|
||||
- if ProcessStatOptions.show_all_process:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
- printdecorator.Print, ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
-
|
||||
- if ProcessStatOptions.empty_arg_flag:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
- printdecorator.Print, ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
-
|
||||
- if ProcessStatOptions.pid_filter_flag:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
- printdecorator.Print, ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
- if ProcessStatOptions.ppid_filter_flag:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
- printdecorator.Print, ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
-
|
||||
- if ProcessStatOptions.command_filter_flag:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
- printdecorator.Print, ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
-
|
||||
- if ProcessStatOptions.user_oriented_format:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
- printdecorator.Print, ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
-
|
||||
- if ProcessStatOptions.username_filter_flag:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = ProcessStatusReporter(process_report, process_filter, interval_in_seconds,
|
||||
- printdecorator.Print, ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
-
|
||||
# ================================================================
|
||||
if ProcessStatOptions.selective_colum_flag:
|
||||
- process_report = ProcessStatus(metric_repository)
|
||||
- process_filter = ProcessFilter(ProcessStatOptions)
|
||||
- stdout = StdoutPrinter()
|
||||
- printdecorator = NoneHandlingPrinterDecorator(stdout)
|
||||
- report = DynamicProcessReporter(process_report, process_filter, interval_in_seconds,
|
||||
- printdecorator.Print, ProcessStatOptions)
|
||||
- report.print_report(timestamp, header_indentation, value_indentation)
|
||||
+ self.__print_dynamic_report(manager,timestamp, header_indentation,
|
||||
+ value_indentation, interval_in_seconds)
|
||||
+ else:
|
||||
+ self.__print_report(manager,timestamp, header_indentation, value_indentation, interval_in_seconds)
|
||||
finally:
|
||||
sys.stdout.flush()
|
||||
|
||||
@@ -779,8 +745,8 @@ class ProcessStatOptions(pmapi.pmOptions):
|
||||
else:
|
||||
raise ValueError
|
||||
except ValueError:
|
||||
- print("Invalid ppid Id List: Either colum name is not correct "
|
||||
- "or use comma separated colum names without whitespaces")
|
||||
+ print("Invalid ppid Id List: Either column name is not correct "
|
||||
+ "or use comma separated column names without whitespaces")
|
||||
sys.exit(1)
|
||||
elif opts == 'U':
|
||||
ProcessStatOptions.username_filter_flag = True
|
||||
@@ -812,7 +778,7 @@ if __name__ == "__main__":
|
||||
sys.stderr.write('Error: not all required metrics are available\nMissing %s\n' % missing)
|
||||
sys.exit(1)
|
||||
manager['psstat'] = PSSTAT_METRICS
|
||||
- manager.printer = ProcessstatReport()
|
||||
+ manager.printer = ProcessStatReport(manager['psstat'])
|
||||
sts = manager.run()
|
||||
sys.exit(sts)
|
||||
except pmapi.pmErr as pmerror:
|
||||
--
|
||||
2.43.5
|
||||
|
||||
@ -1,340 +0,0 @@
|
||||
From a2f0e1e303ce482bd5305f2062027fd2ed17e704 Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Fri, 25 Oct 2024 16:17:22 +0530
|
||||
Subject: [PATCH ol8 1014/1015] pcp-buddyinfo: Added timestamp and no
|
||||
interpolation option
|
||||
|
||||
upstream ref:- https://github.com/performancecopilot/pcp/pull/2078
|
||||
Orabug:36985368
|
||||
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
|
||||
---
|
||||
src/pcp/buddyinfo/pcp-buddyinfo.1 | 118 ++++++++++++++++++++---------
|
||||
src/pcp/buddyinfo/pcp-buddyinfo.py | 92 +++++++++++++++-------
|
||||
2 files changed, 144 insertions(+), 66 deletions(-)
|
||||
|
||||
diff --git a/src/pcp/buddyinfo/pcp-buddyinfo.1 b/src/pcp/buddyinfo/pcp-buddyinfo.1
|
||||
index 26c06ba..7bffccd 100644
|
||||
--- a/src/pcp/buddyinfo/pcp-buddyinfo.1
|
||||
+++ b/src/pcp/buddyinfo/pcp-buddyinfo.1
|
||||
@@ -15,22 +15,33 @@
|
||||
.\" or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
.\" for more details.
|
||||
.\"
|
||||
-
|
||||
.TH PCP-BUDDYINFO 1 "PCP" "Performance Co-Pilot"
|
||||
-
|
||||
.SH NAME
|
||||
-\fBpcp-buddyinfo\fP \- Report statistics for buddy algorithm shown in cat /proc/buddyinfo
|
||||
-
|
||||
+\f3pcp-buddyinfo\f3 \- report Linux kernel buddy algorithm statistics
|
||||
.SH SYNOPSIS
|
||||
-\fBpcp\fP [\fBpcp options\fP] \fBbuddyinfo\fP [\fB-s\fP \fBsamples\fP] [\fB-a\fP \fBarchive\fP] [\fB-Z\fP \fB--timezone\fP] [\fB-z\fP \fB--hostzone\fP] [\fB-V\fP \fBVersion\fP]
|
||||
-
|
||||
+\f3pcp\f1
|
||||
+[\f2pcp\ options\f1]
|
||||
+\f3buddyinfo\f1
|
||||
+[\f3\-uVz?\f1]
|
||||
+[\f3\-s\f1 \f2samples\f1]
|
||||
+[\f3\-a\f1 \f2archive\f1]
|
||||
+[\f3\-Z\f1 \f2timezone\f1]
|
||||
.SH DESCRIPTION
|
||||
-The \fBpcp-buddyinfo\fP command is used for viewing different stats related to buddyinfo. It helps users analyze useful information related to the buddy algorithm. The information includes the total number of zones that are currently active, order pages etc. By default, \fBpcp-buddyinfo\fP reports live data for the local host.
|
||||
-
|
||||
+The
|
||||
+.B pcp-buddyinfo
|
||||
+command is used for viewing different stats related to buddyinfo.
|
||||
+It helps users analyze the dynamic behaviour of the buddy algorithm
|
||||
+used in the Linux kernel virtual memory subsystem.
|
||||
+The information includes the total number of zones that are currently
|
||||
+active, counts of different order pages, and so on.
|
||||
+By default,
|
||||
+.B pcp-buddyinfo
|
||||
+reports live data for the local host.
|
||||
+.PP
|
||||
The statistics shown are as follows:
|
||||
|
||||
.TS
|
||||
-lfB lfB
|
||||
+lfB lfB
|
||||
l lx.
|
||||
HEADER DESCRIPTION
|
||||
_ _
|
||||
@@ -51,44 +62,77 @@ Order10 available pages of order 10
|
||||
|
||||
|
||||
Each column represents the number of pages of a certain order (a certain size) that are available at any given time. For example, for zone DMA (direct memory access), there are 90 of 2^(0*PAGE_SIZE) chunks of memory. Similarly, there are 6 of 2^(1*PAGE_SIZE) chunks, and 2 of 2^(2*PAGE_SIZE) chunks of memory available.
|
||||
-
|
||||
+.PP
|
||||
The DMA row references the first 16 MB on a system, the HighMem row references all memory greater than 4 GB on a system, and the Normal row references all memory in between.
|
||||
-
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
-\fB-a\fP, \fB\-\-archive\fP
|
||||
-Fetch /proc/buddyinfo for a specified archive file
|
||||
-
|
||||
+\fB\-a\fR \fIfile\fR, \fB\-\-archive\fR=\fIfile\fR
|
||||
+Fetch buddyinfo metrics from the specified PCP archive.
|
||||
.TP
|
||||
-\fB-s\fP, \fB\-\-samples\fP
|
||||
-Get the buddyinfo for the specified number of samples count
|
||||
-
|
||||
+\fB\-s\fR \fIcount\fR, \fB\-\-samples\fR=\fIcount\fR
|
||||
+Limit reporting to \fIcount\fR samples of buddyinfo statistics.
|
||||
.TP
|
||||
-\fB-z\fP, \fB\-\-hostzone\fP
|
||||
-Set the reporting timezone to the local time of metrics source
|
||||
-
|
||||
+\fB\-u\fR, \fB\-\-no-interpol\fR
|
||||
+disable the default interpolation mode when replaying an archive.
|
||||
+With the
|
||||
+.B \-u
|
||||
+option, interpolated reporting is disabled and each sample is
|
||||
+reported according to native sampling intervals in the archive.
|
||||
+In this mode the
|
||||
+.B \-t
|
||||
+option is ignored.
|
||||
+Additionally, non-interpolated replay makes sense only when
|
||||
+replaying an archive (using the
|
||||
+.B \-a
|
||||
+option described above), so the
|
||||
+.B \-a
|
||||
+option is mandatory when
|
||||
+.B \-u
|
||||
+is specified.
|
||||
.TP
|
||||
-\fB-Z\fP, \fB\-\-timezone\fP
|
||||
-Set the reporting timezone
|
||||
-
|
||||
+\fB\-z\fR, \fB\-\-hostzone\fR
|
||||
+Use the local timezone of the host that is the source of the
|
||||
+performance metrics, as identified by either the
|
||||
+.B \-h
|
||||
+or the
|
||||
+.B \-a
|
||||
+options.
|
||||
+The default is to use the timezone of the local host.
|
||||
.TP
|
||||
-\fB-V\fP, \fB\-\-version\fP
|
||||
-Display the version number and exit.
|
||||
-
|
||||
+\fB\-Z\fR \fItimezone\fR, \fB\-\-timezone\fR=\fItimezone\fR
|
||||
+Use
|
||||
+.I timezone
|
||||
+for the date and time.
|
||||
+.I Timezone
|
||||
+is in the format of the environment variable
|
||||
+.B TZ
|
||||
+as described in
|
||||
+.BR environ (7).
|
||||
.TP
|
||||
-\fB-?\fP, \fB\-\-help\fP
|
||||
-Display the usage message and exit.
|
||||
-
|
||||
+\fB\-V\fR, \fB\-\-version\fR
|
||||
+Display version number and exit.
|
||||
+.TP
|
||||
+\fB\-?\fR, \fB\-\-help\fR
|
||||
+Display usage message and exit.
|
||||
.SH NOTES
|
||||
-\fBpcp-buddyinfo\fP collects information from \fI/proc/buddyinfo\fP and aims to be command-line and output compatible with it.
|
||||
-
|
||||
+.B pcp-buddyinfo
|
||||
+reports information extracted from the \fI/proc/buddyinfo\fP Linux
|
||||
+kernel procfs file.
|
||||
+.PP
|
||||
+If the default interpolation mode is disabled, it is possible that
|
||||
+some metrics (recorded at different intervals) will be reported as
|
||||
+having missing values for some samples even if they were recorded.
|
||||
.SH PCP ENVIRONMENT
|
||||
Environment variables with the prefix \fBPCP_\fP are used to parameterize the file and directory names used by PCP. On each installation, the file \fI/etc/pcp.conf\fP contains the local values for these variables. The \fB$PCP_CONF\fP variable may be used to specify an alternative configuration file, as described in \fBpcp.conf\fP(5).
|
||||
-
|
||||
+.PP
|
||||
For environment variables affecting PCP tools, see \fBpmGetOptions\fP(3).
|
||||
-
|
||||
.SH SEE ALSO
|
||||
-.BR PCPIntro(1),
|
||||
-.BR pcp(1),
|
||||
-.BR pmParseInterval(3),
|
||||
-.BR environ(7).
|
||||
+.BR PCPIntro (1),
|
||||
+.BR pcp (1),
|
||||
+.BR pmGetOptions (3),
|
||||
+.BR pcp.conf (5)
|
||||
+and
|
||||
+.BR environ (7).
|
||||
+
|
||||
+.\" control lines for scripts/man-spell
|
||||
+.\" +ok+ PAGE_SIZE buddyinfo {from pcp-buddyinfo} HighMem
|
||||
diff --git a/src/pcp/buddyinfo/pcp-buddyinfo.py b/src/pcp/buddyinfo/pcp-buddyinfo.py
|
||||
index 012fe7c..3a7e9c9 100644
|
||||
--- a/src/pcp/buddyinfo/pcp-buddyinfo.py
|
||||
+++ b/src/pcp/buddyinfo/pcp-buddyinfo.py
|
||||
@@ -20,13 +20,13 @@ import signal
|
||||
import sys
|
||||
import time
|
||||
from pcp import pmapi, pmcc
|
||||
-from cpmapi import PM_CONTEXT_ARCHIVE
|
||||
+from cpmapi import PM_CONTEXT_ARCHIVE, PM_MODE_FORW
|
||||
|
||||
-SYS_MECTRICS= ["kernel.uname.sysname","kernel.uname.release",
|
||||
+SYS_METRICS= ["kernel.uname.sysname","kernel.uname.release",
|
||||
"kernel.uname.nodename","kernel.uname.machine","hinv.ncpu"]
|
||||
BUDDYSTAT_METRICS = ["mem.buddyinfo.pages","mem.buddyinfo.total"]
|
||||
|
||||
-ALL_METRICS = BUDDYSTAT_METRICS + SYS_MECTRICS
|
||||
+ALL_METRICS = BUDDYSTAT_METRICS + SYS_METRICS
|
||||
|
||||
def adjust_length(name,size):
|
||||
return name.ljust(size)
|
||||
@@ -65,28 +65,38 @@ class BuddyStatUtil:
|
||||
return data.keys()
|
||||
|
||||
class BuddyinfoReport(pmcc.MetricGroupPrinter):
|
||||
- def __init__(self,samples,group,context):
|
||||
- self.samples = samples
|
||||
+ def __init__(self,opts,group):
|
||||
+ self.opts=opts
|
||||
self.group=group
|
||||
- self.context=context
|
||||
+ self.context=opts.context
|
||||
+ self.samples=opts.samples
|
||||
+ self.header = "unknown"
|
||||
|
||||
def __get_ncpu(self, group):
|
||||
return group['hinv.ncpu'].netValues[0][2]
|
||||
|
||||
def __print_machine_info(self, context):
|
||||
- timestamp = self.group.pmLocaltime(context.timestamp.tv_sec)
|
||||
- # Please check strftime(3) for different formatting options.
|
||||
- # Also check TZ and LC_TIME environment variables for more
|
||||
- # information on how to override the default formatting of
|
||||
- # the date display in the header
|
||||
- time_string = time.strftime("%x", timestamp.struct_time())
|
||||
- header_string = ''
|
||||
- header_string += context['kernel.uname.sysname'].netValues[0][2] + ' '
|
||||
- header_string += context['kernel.uname.release'].netValues[0][2] + ' '
|
||||
- header_string += '(' + context['kernel.uname.nodename'].netValues[0][2] + ') '
|
||||
- header_string += time_string + ' '
|
||||
- header_string += context['kernel.uname.machine'].netValues[0][2] + ' '
|
||||
- print("%s (%s CPU)" % (header_string, self.__get_ncpu(context)))
|
||||
+ if self.header == "unknown":
|
||||
+ try:
|
||||
+ timestamp = self.group.pmLocaltime(context.timestamp.tv_sec)
|
||||
+ # Please check strftime(3) for different formatting options.
|
||||
+ # Also check TZ and LC_TIME environment variables for more
|
||||
+ # information on how to override the default formatting of
|
||||
+ # the date display in the header
|
||||
+ time_string = time.strftime("%x", timestamp.struct_time())
|
||||
+ header_string = ''
|
||||
+ header_string += context['kernel.uname.sysname'].netValues[0][2] + ' '
|
||||
+ header_string += context['kernel.uname.release'].netValues[0][2] + ' '
|
||||
+ header_string += '(' + context['kernel.uname.nodename'].netValues[0][2] + ') '
|
||||
+ header_string += time_string + ' '
|
||||
+ header_string += context['kernel.uname.machine'].netValues[0][2] + ' '
|
||||
+ self.header = header_string
|
||||
+ except IndexError:
|
||||
+ pass
|
||||
+ try:
|
||||
+ print("%s (%s CPU)" % (self.header, self.__get_ncpu(context)))
|
||||
+ except IndexError:
|
||||
+ pass
|
||||
|
||||
def __print_header(self,header_indentation,value_indentation):
|
||||
value_indentation+=" "*2
|
||||
@@ -117,7 +127,7 @@ class BuddyinfoReport(pmcc.MetricGroupPrinter):
|
||||
value=""
|
||||
for order in sorted(order_set,key=__extract_numeric_part):
|
||||
nodename = adjust_length(Normal,9) if len(Normal) < 9 else Normal
|
||||
- key = f"{Normal}::{order}::{node}"
|
||||
+ key = "%s::%s::%s" % (Normal, order, node)
|
||||
data = str(pages.get(key,0))
|
||||
value += adjust_length(data,8) if len(data) < 8 else data
|
||||
value += value_indentation
|
||||
@@ -125,10 +135,10 @@ class BuddyinfoReport(pmcc.MetricGroupPrinter):
|
||||
print("%s %s %s %s %s %s %s "%(timestamp,header_indentation,nodename,header_indentation,node,
|
||||
header_indentation,value))
|
||||
|
||||
- def print_report(self,group,timestamp,header_indentation,value_indentation,manager_buddyinfo):
|
||||
+ def print_report(self,group,timestamp,header_indentation,value_indentation):
|
||||
|
||||
def __print_buddy_status():
|
||||
- buddystatus = BuddyStatUtil(manager_buddyinfo)
|
||||
+ buddystatus = BuddyStatUtil(group)
|
||||
if buddystatus.names():
|
||||
try:
|
||||
self.__print_machine_info(group)
|
||||
@@ -150,19 +160,28 @@ class BuddyinfoReport(pmcc.MetricGroupPrinter):
|
||||
self.samples-=1
|
||||
|
||||
def report(self, manager):
|
||||
- group = manager["sysinfo"]
|
||||
- self.samples=opts.pmGetOptionSamples()
|
||||
+ group = manager["allinfo"]
|
||||
+ self.samples=self.opts.pmGetOptionSamples()
|
||||
t_s = group.contextCache.pmLocaltime(int(group.timestamp))
|
||||
timestamp = time.strftime(BuddyinfoOptions.timefmt, t_s.struct_time())
|
||||
header_indentation = " " if len(timestamp) < 9 else (len(timestamp) - 7) * " "
|
||||
value_indentation = ((len(header_indentation) + 2) - len(timestamp)) * " "
|
||||
- self.print_report(group,timestamp,header_indentation,value_indentation,manager['buddyinfo'])
|
||||
+ self.print_report(group,timestamp,header_indentation,value_indentation)
|
||||
|
||||
class BuddyinfoOptions(pmapi.pmOptions):
|
||||
timefmt = "%H:%M:%S"
|
||||
+ uflag = False
|
||||
+
|
||||
+ def extraOptions(self,opt,optarg,index):
|
||||
+ if opt == 'u':
|
||||
+ BuddyinfoOptions.uflag = True
|
||||
+
|
||||
def __init__(self):
|
||||
- pmapi.pmOptions.__init__(self, "a:s:Z:zV?")
|
||||
+ pmapi.pmOptions.__init__(self, "a:s:Z:uzV?")
|
||||
self.pmSetLongOptionHeader("General options")
|
||||
+ self.pmSetOptionCallback(self.extraOptions)
|
||||
+ self.pmSetLongOptionArchive()
|
||||
+ self.pmSetLongOption("no-interpol", 0, "u", "", "disable interpolation mode with archives")
|
||||
self.pmSetLongOptionHostZone()
|
||||
self.pmSetLongOptionTimeZone()
|
||||
self.pmSetLongOptionHelp()
|
||||
@@ -170,20 +189,35 @@ class BuddyinfoOptions(pmapi.pmOptions):
|
||||
self.pmSetLongOptionVersion()
|
||||
self.samples=None
|
||||
self.context=None
|
||||
+ def checkOptions(self, manager):
|
||||
+ if BuddyinfoOptions.uflag:
|
||||
+ if manager._options.pmGetOptionInterval(): # pylint: disable=protected-access
|
||||
+ print("Error: -t incompatible with -u")
|
||||
+ return False
|
||||
+ if manager.type != PM_CONTEXT_ARCHIVE:
|
||||
+ print("Error: -u can only be specified with -a archive")
|
||||
+ return False
|
||||
+ return True
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
opts = BuddyinfoOptions()
|
||||
mngr = pmcc.MetricGroupManager.builder(opts,sys.argv)
|
||||
opts.context=mngr.type
|
||||
+
|
||||
+ if not opts.checkOptions(mngr):
|
||||
+ raise pmapi.pmUsageErr
|
||||
+ if BuddyinfoOptions.uflag:
|
||||
+ # -u turns off interpolation
|
||||
+ mngr.pmSetMode(PM_MODE_FORW, mngr._options.pmGetOptionOrigin(), 0) # pylint: disable=protected-access
|
||||
+
|
||||
missing = mngr.checkMissingMetrics(ALL_METRICS)
|
||||
if missing is not None:
|
||||
- sys.stderr.write("Error:some metrics are unavailable",missing)
|
||||
+ sys.stderr.write('Error: not all required metrics are available\nMissing: %s\n' % (missing))
|
||||
sys.exit(1)
|
||||
mngr["buddyinfo"] = BUDDYSTAT_METRICS
|
||||
- mngr["sysinfo"] = SYS_MECTRICS
|
||||
mngr["allinfo"]=ALL_METRICS
|
||||
- mngr.printer = BuddyinfoReport(opts.samples,mngr,opts.context)
|
||||
+ mngr.printer = BuddyinfoReport(opts,mngr)
|
||||
sts = mngr.run()
|
||||
sys.exit(sts)
|
||||
except pmapi.pmErr as error:
|
||||
--
|
||||
2.43.5
|
||||
|
||||
@ -1,152 +0,0 @@
|
||||
From ad598761e495c69733973497182917ad24b69916 Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Fri, 25 Oct 2024 16:18:35 +0530
|
||||
Subject: [PATCH ol8 1015/1015] pcp-meminfo: Added timestamp in pcp meinfo
|
||||
|
||||
fixed broken pipe issue as well
|
||||
upstream ref:- 18bea5fe3f8b6f0fded54f4020ed9ad9986012ea
|
||||
Orabug: 36985368
|
||||
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
|
||||
---
|
||||
src/pcp/meminfo/pcp-meminfo.py | 73 ++++++++++++++++++++++++++--------
|
||||
1 file changed, 57 insertions(+), 16 deletions(-)
|
||||
|
||||
diff --git a/src/pcp/meminfo/pcp-meminfo.py b/src/pcp/meminfo/pcp-meminfo.py
|
||||
index 57ac05e..103aa52 100755
|
||||
--- a/src/pcp/meminfo/pcp-meminfo.py
|
||||
+++ b/src/pcp/meminfo/pcp-meminfo.py
|
||||
@@ -19,7 +19,9 @@
|
||||
|
||||
import sys
|
||||
import time
|
||||
+import signal
|
||||
from pcp import pmapi, pmcc
|
||||
+from cpmapi import PM_CONTEXT_ARCHIVE
|
||||
|
||||
METRICS = ["mem.physmem",
|
||||
"mem.util.free",
|
||||
@@ -117,11 +119,37 @@ METRICS_DESC = ["MemTotal",
|
||||
"DirectMap2M",
|
||||
"DirectMap1G"]
|
||||
|
||||
-class MeminfoReport(pmcc.MetricGroupPrinter):
|
||||
- samples = 0
|
||||
+SYS_METRICS = ["kernel.uname.sysname",
|
||||
+ "kernel.uname.release",
|
||||
+ "kernel.uname.nodename",
|
||||
+ "kernel.uname.machine",
|
||||
+ "hinv.ncpu"]
|
||||
+
|
||||
+ALL_METRICS = METRICS + SYS_METRICS
|
||||
|
||||
- def __init__(self, samples):
|
||||
- self.samples = samples
|
||||
+class MeminfoReport(pmcc.MetricGroupPrinter):
|
||||
+ def __init__(self, opts):
|
||||
+ self.opts = opts
|
||||
+ self.Machine_info_count = 0
|
||||
+
|
||||
+ def __get_ncpu(self, group):
|
||||
+ return group['hinv.ncpu'].netValues[0][2]
|
||||
+
|
||||
+ def __print_machine_info(self, group, context):
|
||||
+ timestamp = context.pmLocaltime(group.timestamp.tv_sec)
|
||||
+ # Please check strftime(3) for different formatting options.
|
||||
+ # Also check TZ and LC_TIME environment variables for more
|
||||
+ # information on how to override the default formatting of
|
||||
+ # the date display in the header
|
||||
+ time_string = time.strftime("%x", timestamp.struct_time())
|
||||
+ header_string = ''
|
||||
+ header_string += group['kernel.uname.sysname'].netValues[0][2] + ' '
|
||||
+ header_string += group['kernel.uname.release'].netValues[0][2] + ' '
|
||||
+ header_string += '(' + group['kernel.uname.nodename'].netValues[0][2] + ') '
|
||||
+ header_string += time_string + ' '
|
||||
+ header_string += group['kernel.uname.machine'].netValues[0][2] + ' '
|
||||
+
|
||||
+ print("%s (%s CPU)" % (header_string, self.__get_ncpu(group)))
|
||||
|
||||
def getMetricName(self, idx):
|
||||
metric_name = ""
|
||||
@@ -134,13 +162,22 @@ class MeminfoReport(pmcc.MetricGroupPrinter):
|
||||
return metric_name, units
|
||||
|
||||
def report(self, manager):
|
||||
+ group = manager["sysinfo"]
|
||||
+ try:
|
||||
+ if not self.Machine_info_count:
|
||||
+ self.__print_machine_info(group, manager)
|
||||
+ self.Machine_info_count = 1
|
||||
+ except IndexError:
|
||||
+ # missing some metrics
|
||||
+ return
|
||||
+
|
||||
group = manager["meminfo"]
|
||||
|
||||
- opts.pmGetOptionSamples()
|
||||
+ self.opts.pmGetOptionSamples()
|
||||
|
||||
t_s = group.contextCache.pmLocaltime(int(group.timestamp))
|
||||
time_string = time.strftime(MeminfoOptions.timefmt, t_s.struct_time())
|
||||
- print(time_string)
|
||||
+ print("Timestamp".ljust(18) + ": " + time_string)
|
||||
|
||||
idx = 0
|
||||
for metric in METRICS:
|
||||
@@ -148,21 +185,23 @@ class MeminfoReport(pmcc.MetricGroupPrinter):
|
||||
val = group[metric].netValues[0][2]
|
||||
except IndexError:
|
||||
metric_name, units = self.getMetricName(idx)
|
||||
- print(F"{metric_name:17} : NA")
|
||||
+ print("%-17s : NA"%(metric_name))
|
||||
|
||||
idx += 1
|
||||
continue
|
||||
|
||||
metric_name, units = self.getMetricName(idx)
|
||||
- print(F"{metric_name:17} : {val} {units}")
|
||||
+ print("%-17s : %s %s"%(metric_name, val, units))
|
||||
|
||||
idx += 1
|
||||
- print()
|
||||
+ print("")
|
||||
+
|
||||
+ if MeminfoOptions.context is not PM_CONTEXT_ARCHIVE and self.opts.pmGetOptionSamples() is None:
|
||||
+ sys.exit(0)
|
||||
|
||||
class MeminfoOptions(pmapi.pmOptions):
|
||||
context = None
|
||||
- timefmt = "%H:%M:%S"
|
||||
- samples = 0
|
||||
+ timefmt = "%m/%d/%Y %H:%M:%S"
|
||||
|
||||
def __init__(self):
|
||||
pmapi.pmOptions.__init__(self, "a:s:S:T:z:A:t:")
|
||||
@@ -176,18 +215,20 @@ if __name__ == '__main__':
|
||||
mngr = pmcc.MetricGroupManager.builder(opts,sys.argv)
|
||||
MeminfoOptions.context = mngr.type
|
||||
|
||||
- missing = mngr.checkMissingMetrics(METRICS)
|
||||
+ missing = mngr.checkMissingMetrics(ALL_METRICS)
|
||||
if missing is not None:
|
||||
- sys.stderr.write(F"Error:Metric is {missing} missing\n")
|
||||
+ sys.stderr.write('Error: not all required metrics are available\nMissing: %s\n' % (missing))
|
||||
sys.exit(1)
|
||||
|
||||
mngr["meminfo"] = METRICS
|
||||
- mngr.printer = MeminfoReport(opts.samples)
|
||||
+ mngr["sysinfo"] = SYS_METRICS
|
||||
+ mngr.printer = MeminfoReport(opts)
|
||||
sts = mngr.run()
|
||||
sys.exit(sts)
|
||||
-
|
||||
+ except IOError:
|
||||
+ signal.signal(signal.SIGPIPE,signal.SIG_DFL)
|
||||
except pmapi.pmErr as error:
|
||||
- sys.stderr.write(F"{error.progname()} {error.message()}")
|
||||
+ sys.stderr.write("%s %s\n"%(error.progname(), error.message()))
|
||||
except pmapi.pmUsageErr as usage:
|
||||
usage.message()
|
||||
sys.exit(1)
|
||||
--
|
||||
2.43.5
|
||||
|
||||
@ -1,46 +0,0 @@
|
||||
From be84a0cc9d880e2e9cd91e9e983b22ffec6bd126 Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Mon, 3 Mar 2025 13:31:31 +0530
|
||||
Subject: [PATCH OL8 1016/1016] pmstat: avoid corner-case infinite loop When -a
|
||||
is in play, if the archive contains only one pmResult for the metrics of
|
||||
interest to pmstat, pmstat goes into an infinite loop.
|
||||
|
||||
Simple fix in pmstat.c.
|
||||
|
||||
upstream ref:- 8ed9735e93f2f0e4dbe038e36939f40e5e87b5ae
|
||||
Orabug: 37638447
|
||||
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
|
||||
---
|
||||
src/pmstat/pmstat.c | 7 +++++--
|
||||
1 file changed, 5 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/pmstat/pmstat.c b/src/pmstat/pmstat.c
|
||||
index 57ec954..0901df9 100644
|
||||
--- a/src/pmstat/pmstat.c
|
||||
+++ b/src/pmstat/pmstat.c
|
||||
@@ -67,6 +67,7 @@ struct statsrc {
|
||||
|
||||
pmLongOptions longopts[] = {
|
||||
PMAPI_GENERAL_OPTIONS,
|
||||
+ PMOPT_DEBUG,
|
||||
PMAPI_OPTIONS_HEADER("Alternate sources"),
|
||||
PMOPT_HOSTSFILE,
|
||||
PMOPT_LOCALPMDA,
|
||||
@@ -577,10 +578,12 @@ main(int argc, char *argv[])
|
||||
} else if ((opts.context == PM_CONTEXT_ARCHIVE) &&
|
||||
(sts == PM_ERR_EOL) && (!s->fetched)) {
|
||||
/*
|
||||
- * We are yet to see something from this archive - so
|
||||
- * don't discard it just yet.
|
||||
+ * We are yet to see something from this archive and
|
||||
+ * we're at the end of the archive.
|
||||
*/
|
||||
puts(" No data in the archive");
|
||||
+ printf(" pmFetchGroup: %s\n", pmErrStr(sts));
|
||||
+ exit(0);
|
||||
} else {
|
||||
int valid = 0;
|
||||
|
||||
--
|
||||
2.43.5
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,56 +0,0 @@
|
||||
From e1c81c356a176fa244b3b32a2b98362d75fd2842 Mon Sep 17 00:00:00 2001
|
||||
From: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
Date: Thu, 9 Oct 2025 20:30:48 +0530
|
||||
Subject: [PATCH] pcp-meminfo: additional metrics added for mem.util to show
|
||||
newly added kernel metrics by following commit
|
||||
1c559713ebc5c02759de59013066c3d29e4395e4
|
||||
|
||||
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
|
||||
|
||||
Cherry-pick-commit: https://github.com/performancecopilot/pcp/pull/2180/commits/43025e54a97cb8aa2a3cc955a9fb1d0cb3f39cf4
|
||||
|
||||
Orabug: 38526356
|
||||
|
||||
Signed-off-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
|
||||
---
|
||||
src/pcp/meminfo/pcp-meminfo.py | 8 ++++++++
|
||||
1 file changed, 8 insertions(+)
|
||||
|
||||
diff --git a/src/pcp/meminfo/pcp-meminfo.py b/src/pcp/meminfo/pcp-meminfo.py
|
||||
index 103aa52..bae8931 100755
|
||||
--- a/src/pcp/meminfo/pcp-meminfo.py
|
||||
+++ b/src/pcp/meminfo/pcp-meminfo.py
|
||||
@@ -57,10 +57,14 @@ METRICS = ["mem.physmem",
|
||||
"mem.util.vmallocTotal",
|
||||
"mem.util.vmallocUsed",
|
||||
"mem.util.vmallocChunk",
|
||||
+ "mem.util.percpu",
|
||||
"mem.util.corrupthardware",
|
||||
"mem.util.anonhugepages",
|
||||
"mem.vmstat.nr_shmem_hugepages",
|
||||
"mem.vmstat.nr_shmem_pmdmapped",
|
||||
+ "mem.util.filehugepages",
|
||||
+ "mem.util.filepmdmapped",
|
||||
+ "mem.util.cmatotal",
|
||||
"mem.zoneinfo.nr_free_cma",
|
||||
"mem.util.hugepagesTotal",
|
||||
"mem.util.hugepagesFree",
|
||||
@@ -105,10 +109,14 @@ METRICS_DESC = ["MemTotal",
|
||||
"VmallocTotal",
|
||||
"VmallocUsed",
|
||||
"VmallocChunk",
|
||||
+ "Percpu",
|
||||
"HardwareCorrupted",
|
||||
"AnonHugePages",
|
||||
"ShmemHugePages",
|
||||
"ShmemPmdMapped",
|
||||
+ "FileHugePages",
|
||||
+ "FilePmdMapped",
|
||||
+ "CmaTotal",
|
||||
"CmaFree",
|
||||
"HugePages_Total_NO_kb",
|
||||
"HugePages_Free_NO_kb",
|
||||
--
|
||||
2.43.7
|
||||
|
||||
@ -1,302 +0,0 @@
|
||||
From b80dfd69c7872a7d1c90d2e9617bdf312c47f587 Mon Sep 17 00:00:00 2001
|
||||
From: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
Date: Fri, 10 Oct 2025 17:26:37 +0530
|
||||
Subject: [PATCH] pmdalinux: additional mem.util metrics from recent Linux kernel versions
|
||||
Ten additional Linux kernel /proc/meminfo memory metrics relating to new
|
||||
aspects of hugepages, the contiguous and percpu memory allocators, zswap,
|
||||
shadow call stacks and unaccepted memory.
|
||||
|
||||
Cherry-pick-commit: https://github.com/performancecopilot/pcp/commit/1c559713ebc5c02759de59013066c3d29e4395e4
|
||||
|
||||
Signed-off-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
---
|
||||
src/pmdas/linux/help | 23 ++++++-
|
||||
src/pmdas/linux/pmda.c | 114 ++++++++++++++++++++++++++++++++-
|
||||
src/pmdas/linux/proc_meminfo.c | 11 ++++
|
||||
src/pmdas/linux/proc_meminfo.h | 12 ++++
|
||||
src/pmdas/linux/root_linux | 11 ++++
|
||||
5 files changed, 168 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/src/pmdas/linux/help b/src/pmdas/linux/help
|
||||
index 640a21a..3700809 100644
|
||||
--- a/src/pmdas/linux/help
|
||||
+++ b/src/pmdas/linux/help
|
||||
@@ -937,8 +937,29 @@ you will get real memory on demand, and just as much as you use.
|
||||
@ mem.util.hugepagesFreeBytes amount of memory in free hugepages
|
||||
@ mem.util.hugepagesRsvdBytes amount of memory in reserved hugepages
|
||||
@ mem.util.hugepagesSurpBytes amount of memory in surplus hugepages
|
||||
-
|
||||
User memory (Kbytes) in pages not backed by files, e.g. from malloc()
|
||||
+@ mem.util.shmemhugepages amount of shared memory allocated with hugepages
|
||||
+@ mem.util.shmempmdmapped shared memory mapped into userspace with hugepages
|
||||
+@ mem.util.filehugepages page cache (file) pages allocated with hugepages
|
||||
+@ mem.util.filepmdmapped page cache mapped into userspace with hugepages
|
||||
+@ mem.util.cmatotal total contiguous memory allocator memory
|
||||
+@ mem.util.cmafree free contiguous memory allocator memory
|
||||
+@ mem.util.unaccepted amount of unaccepted memory
|
||||
+When this mechanism is in use, a virtual machine can be launched with
|
||||
+its memory in an unaccepted state. Such a system will not be able to
|
||||
+make use of the memory provided until that memory has been explicitly
|
||||
+accepted. On these systems, the bootloader will typically pre-accept
|
||||
+enough memory to allow the guest kernel to boot then that kernel must
|
||||
+take responsibility for accepting the rest before using it.
|
||||
+@ mem.util.zswap total size of the zswap memory pool
|
||||
+@ mem.util.zswapped current memory used from the zswap memory pool
|
||||
+@ mem.util.shadowcallstack memory used for shadown call stacks
|
||||
+Shadow stacks are a modern security feature allowing for detection of
|
||||
+corruption of the call stack, allowing the kernel to react to such an
|
||||
+attach in an appropriate fashion. Shadow stacks are often maintained
|
||||
+by the processor hardware and require additional stack memory.
|
||||
+@ mem.util.percpu amount of per CPU allocator memory
|
||||
+
|
||||
@ mem.numa.util.total per-node total memory
|
||||
@ mem.numa.util.free per-node free memory
|
||||
@ mem.numa.util.used per-node used memory
|
||||
diff --git a/src/pmdas/linux/pmda.c b/src/pmdas/linux/pmda.c
|
||||
index 428a222..23d8033 100644
|
||||
--- a/src/pmdas/linux/pmda.c
|
||||
+++ b/src/pmdas/linux/pmda.c
|
||||
@@ -1094,7 +1094,7 @@ static pmdaMetric metrictab[] = {
|
||||
{ PMDA_PMID(CLUSTER_MEMINFO,55), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
|
||||
-/* mem.util.mmap_copy */
|
||||
+/* mem.util.anonhugepages */
|
||||
{ NULL,
|
||||
{ PMDA_PMID(CLUSTER_MEMINFO,56), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
@@ -1134,6 +1134,61 @@ static pmdaMetric metrictab[] = {
|
||||
{ PMDA_PMID(CLUSTER_MEMINFO,63), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, },
|
||||
|
||||
+/* mem.util.shmemhugepages */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_MEMINFO,64), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.util.shmempmdmapped */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_MEMINFO,65), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.util.filehugepages */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_MEMINFO,66), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.util.filepmdmapped */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_MEMINFO,67), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.util.cmatotal */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_MEMINFO,68), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.util.cmafree */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_MEMINFO,69), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.util.unaccepted */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_MEMINFO,70), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.util.zswap */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_MEMINFO,71), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.util.zswapped */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_MEMINFO,72), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.util.shadowcallstack */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_MEMINFO,73), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.util.percpu */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_MEMINFO,74), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
/* mem.numa.util.total */
|
||||
{ NULL,
|
||||
{ PMDA_PMID(CLUSTER_NUMA_MEMINFO,0), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT,
|
||||
@@ -8201,7 +8256,7 @@ linux_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom)
|
||||
return 0; /* no values available */
|
||||
atom->ull = proc_meminfo.HardwareCorrupted;
|
||||
break;
|
||||
- case 56: /* mem.util.anonhugepages (in pages) */
|
||||
+ case 56: /* mem.util.anonhugepages (in kbytes) */
|
||||
if (!MEMINFO_VALID_VALUE(proc_meminfo.AnonHugePages))
|
||||
return 0; /* no values available */
|
||||
atom->ull = proc_meminfo.AnonHugePages;
|
||||
@@ -8245,6 +8300,61 @@ linux_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom)
|
||||
atom->ull = proc_meminfo.HugepagesSurp *
|
||||
(proc_meminfo.Hugepagesize << 10);
|
||||
break;
|
||||
+ case 64: /* mem.util.shmemhugepages (in kbytes) */
|
||||
+ if (!MEMINFO_VALID_VALUE(proc_meminfo.ShmemHugePages))
|
||||
+ return 0; /* no values available */
|
||||
+ atom->ull = proc_meminfo.ShmemHugePages;
|
||||
+ break;
|
||||
+ case 65: /* mem.util.shmempmdmapped (in kbytes) */
|
||||
+ if (!MEMINFO_VALID_VALUE(proc_meminfo.ShmemPmdMapped))
|
||||
+ return 0; /* no values available */
|
||||
+ atom->ull = proc_meminfo.ShmemPmdMapped;
|
||||
+ break;
|
||||
+ case 66: /* mem.util.filehugepages (in kbytes) */
|
||||
+ if (!MEMINFO_VALID_VALUE(proc_meminfo.FileHugePages))
|
||||
+ return 0; /* no values available */
|
||||
+ atom->ull = proc_meminfo.FileHugePages;
|
||||
+ break;
|
||||
+ case 67: /* mem.util.filepmdmapped (in kbytes) */
|
||||
+ if (!MEMINFO_VALID_VALUE(proc_meminfo.FilePmdMapped))
|
||||
+ return 0; /* no values available */
|
||||
+ atom->ull = proc_meminfo.FilePmdMapped;
|
||||
+ break;
|
||||
+ case 68: /* mem.util.cmatotal (in kbytes) */
|
||||
+ if (!MEMINFO_VALID_VALUE(proc_meminfo.CmaTotal))
|
||||
+ return 0; /* no values available */
|
||||
+ atom->ull = proc_meminfo.CmaTotal;
|
||||
+ break;
|
||||
+ case 69: /* mem.util.cmafree (in kbytes) */
|
||||
+ if (!MEMINFO_VALID_VALUE(proc_meminfo.CmaFree))
|
||||
+ return 0; /* no values available */
|
||||
+ atom->ull = proc_meminfo.CmaFree;
|
||||
+ break;
|
||||
+ case 70: /* mem.util.unaccepted (in kbytes) */
|
||||
+ if (!MEMINFO_VALID_VALUE(proc_meminfo.Unaccepted))
|
||||
+ return 0; /* no values available */
|
||||
+ atom->ull = proc_meminfo.Unaccepted;
|
||||
+ break;
|
||||
+ case 71: /* mem.util.zswap (in kbytes) */
|
||||
+ if (!MEMINFO_VALID_VALUE(proc_meminfo.Zswap))
|
||||
+ return 0; /* no values available */
|
||||
+ atom->ull = proc_meminfo.Zswap;
|
||||
+ break;
|
||||
+ case 72: /* mem.util.zswapped (in kbytes) */
|
||||
+ if (!MEMINFO_VALID_VALUE(proc_meminfo.Zswapped))
|
||||
+ return 0; /* no values available */
|
||||
+ atom->ull = proc_meminfo.Zswapped;
|
||||
+ break;
|
||||
+ case 73: /* mem.util.shadowcallstack (in kbytes) */
|
||||
+ if (!MEMINFO_VALID_VALUE(proc_meminfo.ShadowCallStack))
|
||||
+ return 0; /* no values available */
|
||||
+ atom->ull = proc_meminfo.ShadowCallStack;
|
||||
+ break;
|
||||
+ case 74: /* mem.util.percpu (in kbytes) */
|
||||
+ if (!MEMINFO_VALID_VALUE(proc_meminfo.Percpu))
|
||||
+ return 0; /* no values available */
|
||||
+ atom->ull = proc_meminfo.Percpu;
|
||||
+ break;
|
||||
default:
|
||||
return PM_ERR_PMID;
|
||||
}
|
||||
diff --git a/src/pmdas/linux/proc_meminfo.c b/src/pmdas/linux/proc_meminfo.c
|
||||
index f7599e2..dff30bc 100644
|
||||
--- a/src/pmdas/linux/proc_meminfo.c
|
||||
+++ b/src/pmdas/linux/proc_meminfo.c
|
||||
@@ -47,6 +47,8 @@ static struct {
|
||||
{ "MmapCopy", &moff.MmapCopy },
|
||||
{ "SwapTotal", &moff.SwapTotal },
|
||||
{ "SwapFree", &moff.SwapFree },
|
||||
+ { "Zswap", &moff.Zswap },
|
||||
+ { "Zswapped", &moff.Zswapped },
|
||||
{ "Dirty", &moff.Dirty },
|
||||
{ "Writeback", &moff.Writeback },
|
||||
{ "AnonPages", &moff.AnonPages },
|
||||
@@ -56,6 +58,7 @@ static struct {
|
||||
{ "SReclaimable", &moff.SlabReclaimable },
|
||||
{ "SUnreclaim", &moff.SlabUnreclaimable },
|
||||
{ "KernelStack", &moff.KernelStack },
|
||||
+ { "ShadowCallStack", &moff.ShadowCallStack },
|
||||
{ "PageTables", &moff.PageTables },
|
||||
{ "Quicklists", &moff.Quicklists },
|
||||
{ "NFS_Unstable", &moff.NFS_Unstable },
|
||||
@@ -66,8 +69,16 @@ static struct {
|
||||
{ "VmallocTotal", &moff.VmallocTotal },
|
||||
{ "VmallocUsed", &moff.VmallocUsed },
|
||||
{ "VmallocChunk", &moff.VmallocChunk },
|
||||
+ { "Percpu", &moff.Percpu },
|
||||
{ "HardwareCorrupted", &moff.HardwareCorrupted },
|
||||
{ "AnonHugePages", &moff.AnonHugePages },
|
||||
+ { "ShmemHugePages", &moff.ShmemHugePages },
|
||||
+ { "ShmemPmdMapped", &moff.ShmemPmdMapped },
|
||||
+ { "FileHugePages", &moff.FileHugePages },
|
||||
+ { "FilePmdMapped", &moff.FilePmdMapped },
|
||||
+ { "CmaTotal", &moff.CmaTotal },
|
||||
+ { "CmaFree", &moff.CmaFree },
|
||||
+ { "Unaccepted", &moff.Unaccepted },
|
||||
/* vendor kernel patches, some outdated now */
|
||||
{ "MemShared", &moff.MemShared },
|
||||
{ "ReverseMaps", &moff.ReverseMaps },
|
||||
diff --git a/src/pmdas/linux/proc_meminfo.h b/src/pmdas/linux/proc_meminfo.h
|
||||
index c353606..afcf837 100644
|
||||
--- a/src/pmdas/linux/proc_meminfo.h
|
||||
+++ b/src/pmdas/linux/proc_meminfo.h
|
||||
@@ -45,6 +45,8 @@ typedef struct {
|
||||
int64_t SwapTotal;
|
||||
int64_t SwapFree;
|
||||
int64_t SwapUsed; /* computed */
|
||||
+ int64_t Zswap;
|
||||
+ int64_t Zswapped;
|
||||
int64_t Dirty;
|
||||
int64_t Writeback;
|
||||
int64_t Mapped;
|
||||
@@ -53,6 +55,7 @@ typedef struct {
|
||||
int64_t SlabReclaimable;
|
||||
int64_t SlabUnreclaimable;
|
||||
int64_t KernelStack;
|
||||
+ int64_t ShadowCallStack;
|
||||
int64_t CommitLimit;
|
||||
int64_t Committed_AS;
|
||||
int64_t PageTables;
|
||||
@@ -65,8 +68,17 @@ typedef struct {
|
||||
int64_t VmallocTotal;
|
||||
int64_t VmallocUsed;
|
||||
int64_t VmallocChunk;
|
||||
+ int64_t Percpu;
|
||||
int64_t HardwareCorrupted;
|
||||
int64_t AnonHugePages;
|
||||
+ int64_t ShmemHugePages;
|
||||
+ int64_t ShmemPmdMapped;
|
||||
+ int64_t FileHugePages;
|
||||
+ int64_t FilePmdMapped;
|
||||
+ int64_t CmaTotal;
|
||||
+ int64_t CmaFree;
|
||||
+ int64_t Unaccepted;
|
||||
+ /* vendor patches (old) */
|
||||
int64_t HugepagesTotal;
|
||||
int64_t HugepagesFree;
|
||||
int64_t HugepagesRsvd;
|
||||
diff --git a/src/pmdas/linux/root_linux b/src/pmdas/linux/root_linux
|
||||
index 58ef225..18f70c7 100644
|
||||
--- a/src/pmdas/linux/root_linux
|
||||
+++ b/src/pmdas/linux/root_linux
|
||||
@@ -695,6 +695,17 @@ mem.util {
|
||||
hugepagesFreeBytes 60:1:61
|
||||
hugepagesRsvdBytes 60:1:62
|
||||
hugepagesSurpBytes 60:1:63
|
||||
+ shmemhugepages 60:1:64
|
||||
+ shmempmdmapped 60:1:65
|
||||
+ filehugepages 60:1:66
|
||||
+ filepmdmapped 60:1:67
|
||||
+ cmatotal 60:1:68
|
||||
+ cmafree 60:1:69
|
||||
+ unaccepted 60:1:70
|
||||
+ zswap 60:1:71
|
||||
+ zswapped 60:1:72
|
||||
+ shadowcallstack 60:1:73
|
||||
+ percpu 60:1:74
|
||||
}
|
||||
|
||||
mem.numa {
|
||||
--
|
||||
2.43.7
|
||||
|
||||
@ -1,171 +0,0 @@
|
||||
From 02568f90a0cb92feb95d5b7f65810cbc99bfed4e Mon Sep 17 00:00:00 2001
|
||||
From: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
Date: Fri, 10 Oct 2025 17:35:40 +0530
|
||||
Subject: [PATCH] meminfo: Added mem.util.kreclaimable and mem.util.hugetlb
|
||||
metrics for meminfo
|
||||
|
||||
updated pcp-meminfo tool as well two show these metrics
|
||||
data
|
||||
|
||||
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
|
||||
|
||||
Cherry-pick-commit: https://github.com/performancecopilot/pcp/pull/2180/commits/81393407ec8d31642f637ab25eb3f9c082dab821
|
||||
|
||||
Orabug: 38526356
|
||||
|
||||
Signed-off-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
---
|
||||
src/pcp/meminfo/pcp-meminfo.py | 4 ++++
|
||||
src/pmdas/linux/help | 3 +++
|
||||
src/pmdas/linux/pmda.c | 22 +++++++++++++++++++++-
|
||||
src/pmdas/linux/proc_meminfo.c | 2 ++
|
||||
src/pmdas/linux/proc_meminfo.h | 2 ++
|
||||
src/pmdas/linux/root_linux | 2 ++
|
||||
6 files changed, 34 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/pcp/meminfo/pcp-meminfo.py b/src/pcp/meminfo/pcp-meminfo.py
|
||||
index bae8931..8492859 100755
|
||||
--- a/src/pcp/meminfo/pcp-meminfo.py
|
||||
+++ b/src/pcp/meminfo/pcp-meminfo.py
|
||||
@@ -44,6 +44,7 @@ METRICS = ["mem.physmem",
|
||||
"mem.util.anonpages",
|
||||
"mem.util.mapped",
|
||||
"mem.util.shared",
|
||||
+ "mem.util.kreclaimable",
|
||||
"mem.util.slab",
|
||||
"mem.util.slabReclaimable",
|
||||
"mem.util.slabUnreclaimable",
|
||||
@@ -71,6 +72,7 @@ METRICS = ["mem.physmem",
|
||||
"mem.util.hugepagesRsvd",
|
||||
"mem.util.hugepagesSurp",
|
||||
"hinv.hugepagesize",
|
||||
+ "mem.util.hugetlb",
|
||||
"mem.util.directMap4k",
|
||||
"mem.util.directMap2M",
|
||||
"mem.util.directMap1G"]
|
||||
@@ -96,6 +98,7 @@ METRICS_DESC = ["MemTotal",
|
||||
"AnonPages",
|
||||
"Mapped",
|
||||
"Shmem",
|
||||
+ "KReclaimable",
|
||||
"Slab",
|
||||
"SReclaimable",
|
||||
"SUnreclaim",
|
||||
@@ -123,6 +126,7 @@ METRICS_DESC = ["MemTotal",
|
||||
"HugePages_Rsvd_NO_kb",
|
||||
"HugePages_Surp_NO_kb",
|
||||
"Hugepagesize",
|
||||
+ "Hugetlb",
|
||||
"DirectMap4k",
|
||||
"DirectMap2M",
|
||||
"DirectMap1G"]
|
||||
diff --git a/src/pmdas/linux/help b/src/pmdas/linux/help
|
||||
index 3700809..531b136 100644
|
||||
--- a/src/pmdas/linux/help
|
||||
+++ b/src/pmdas/linux/help
|
||||
@@ -959,6 +959,9 @@ corruption of the call stack, allowing the kernel to react to such an
|
||||
attach in an appropriate fashion. Shadow stacks are often maintained
|
||||
by the processor hardware and require additional stack memory.
|
||||
@ mem.util.percpu amount of per CPU allocator memory
|
||||
+@ mem.util.kreclaimable Kbytes in kernel reclaimable memory, from /proc/meminfo
|
||||
+Kernel allocations that the kernel will attempt to reclaim under memory pressure.
|
||||
+@ mem.util.hugetlb the total amount of memory (in kB), consumed by huge pages of all sizes.
|
||||
|
||||
@ mem.numa.util.total per-node total memory
|
||||
@ mem.numa.util.free per-node free memory
|
||||
diff --git a/src/pmdas/linux/pmda.c b/src/pmdas/linux/pmda.c
|
||||
index 23d8033..37a65f1 100644
|
||||
--- a/src/pmdas/linux/pmda.c
|
||||
+++ b/src/pmdas/linux/pmda.c
|
||||
@@ -1189,6 +1189,16 @@ static pmdaMetric metrictab[] = {
|
||||
{ PMDA_PMID(CLUSTER_MEMINFO,74), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
|
||||
+/* mem.util.kreclaimable */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_MEMINFO,75), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.util.hugetlb */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_MEMINFO,76), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
/* mem.numa.util.total */
|
||||
{ NULL,
|
||||
{ PMDA_PMID(CLUSTER_NUMA_MEMINFO,0), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT,
|
||||
@@ -8354,7 +8364,17 @@ linux_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom)
|
||||
if (!MEMINFO_VALID_VALUE(proc_meminfo.Percpu))
|
||||
return 0; /* no values available */
|
||||
atom->ull = proc_meminfo.Percpu;
|
||||
- break;
|
||||
+ break;
|
||||
+ case 75: /* mem.util.kreclaimable (in kbytes) */
|
||||
+ if (!MEMINFO_VALID_VALUE(proc_meminfo.KReclaimable))
|
||||
+ return 0; /* no values available */
|
||||
+ atom->ull = proc_meminfo.KReclaimable;
|
||||
+ break;
|
||||
+ case 76: /* mem.util.hugetlb (in kbytes) */
|
||||
+ if (!MEMINFO_VALID_VALUE(proc_meminfo.Hugetlb))
|
||||
+ return 0; /* no values available */
|
||||
+ atom->ull = proc_meminfo.Hugetlb;
|
||||
+ break;
|
||||
default:
|
||||
return PM_ERR_PMID;
|
||||
}
|
||||
diff --git a/src/pmdas/linux/proc_meminfo.c b/src/pmdas/linux/proc_meminfo.c
|
||||
index dff30bc..4891610 100644
|
||||
--- a/src/pmdas/linux/proc_meminfo.c
|
||||
+++ b/src/pmdas/linux/proc_meminfo.c
|
||||
@@ -54,6 +54,7 @@ static struct {
|
||||
{ "AnonPages", &moff.AnonPages },
|
||||
{ "Mapped", &moff.Mapped },
|
||||
{ "Shmem", &moff.Shmem },
|
||||
+ { "KReclaimable", &moff.KReclaimable },
|
||||
{ "Slab", &moff.Slab },
|
||||
{ "SReclaimable", &moff.SlabReclaimable },
|
||||
{ "SUnreclaim", &moff.SlabUnreclaimable },
|
||||
@@ -87,6 +88,7 @@ static struct {
|
||||
{ "HugePages_Rsvd", &moff.HugepagesRsvd },
|
||||
{ "HugePages_Surp", &moff.HugepagesSurp },
|
||||
{ "Hugepagesize", &moff.Hugepagesize },
|
||||
+ { "Hugetlb", &moff.Hugetlb },
|
||||
{ "DirectMap4k", &moff.directMap4k },
|
||||
{ "DirectMap2M", &moff.directMap2M },
|
||||
{ "DirectMap1G", &moff.directMap1G },
|
||||
diff --git a/src/pmdas/linux/proc_meminfo.h b/src/pmdas/linux/proc_meminfo.h
|
||||
index afcf837..56512d1 100644
|
||||
--- a/src/pmdas/linux/proc_meminfo.h
|
||||
+++ b/src/pmdas/linux/proc_meminfo.h
|
||||
@@ -51,6 +51,7 @@ typedef struct {
|
||||
int64_t Writeback;
|
||||
int64_t Mapped;
|
||||
int64_t Shmem;
|
||||
+ int64_t KReclaimable;
|
||||
int64_t Slab;
|
||||
int64_t SlabReclaimable;
|
||||
int64_t SlabUnreclaimable;
|
||||
@@ -84,6 +85,7 @@ typedef struct {
|
||||
int64_t HugepagesRsvd;
|
||||
int64_t HugepagesSurp;
|
||||
int64_t Hugepagesize;
|
||||
+ int64_t Hugetlb;
|
||||
int64_t directMap4k;
|
||||
int64_t directMap2M;
|
||||
int64_t directMap1G;
|
||||
diff --git a/src/pmdas/linux/root_linux b/src/pmdas/linux/root_linux
|
||||
index 18f70c7..0a3a92a 100644
|
||||
--- a/src/pmdas/linux/root_linux
|
||||
+++ b/src/pmdas/linux/root_linux
|
||||
@@ -706,6 +706,8 @@ mem.util {
|
||||
zswapped 60:1:72
|
||||
shadowcallstack 60:1:73
|
||||
percpu 60:1:74
|
||||
+ kreclaimable 60:1:75
|
||||
+ hugetlb 60:1:76
|
||||
}
|
||||
|
||||
mem.numa {
|
||||
--
|
||||
2.43.7
|
||||
|
||||
@ -1,267 +0,0 @@
|
||||
From a02f695771e7534a90391fe243badb47b7b2371d Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Thu, 28 Aug 2025 19:37:54 +0530
|
||||
Subject: [PATCH] pcp-mpstat: refactor to use vuser/vnice metrics and unify CPU
|
||||
utilization calculation
|
||||
|
||||
Switch all references in MPSTAT_METRICS from cpu.user/cpu.nice to the newer cpu.vuser/cpu.vnice metrics
|
||||
for both kernel.all and kernel.percpu to be inline with mpstat tool.
|
||||
Refactor all per-metric calculation methods in CoreCpuUtil to delegate to a new _compute_metric helper,
|
||||
eliminating redundant code.
|
||||
Optimize CPU count retrieval by caching the result of hinv.ncpu in the CoreCpuUtil class.
|
||||
Remove unused or redundant calls to cpu_online, clarifying the core class interface.
|
||||
Simplify and centralize the per-metric computation logic by using self._all_or_percpu() for proper metric
|
||||
path construction and handling instance (per-core vs global) logic in a single location.
|
||||
|
||||
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
|
||||
|
||||
Orabug: 38526338
|
||||
|
||||
Cherry-pick-commit: https://github.com/performancecopilot/pcp/commit/a02f695771e7534a90391fe243badb47b7b2371d
|
||||
|
||||
Signed-off-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
|
||||
---
|
||||
src/pcp/mpstat/pcp-mpstat.py | 184 ++++++++---------------------------
|
||||
1 file changed, 42 insertions(+), 142 deletions(-)
|
||||
|
||||
diff --git a/src/pcp/mpstat/pcp-mpstat.py b/src/pcp/mpstat/pcp-mpstat.py
|
||||
index 89f308ac39..fed1b91ae1 100755
|
||||
--- a/src/pcp/mpstat/pcp-mpstat.py
|
||||
+++ b/src/pcp/mpstat/pcp-mpstat.py
|
||||
@@ -22,14 +22,14 @@
|
||||
import time
|
||||
MPSTAT_METRICS = ['kernel.uname.nodename', 'kernel.uname.release', 'kernel.uname.sysname',
|
||||
'kernel.uname.machine', 'hinv.map.cpu_num', 'hinv.ncpu', 'hinv.cpu.online',
|
||||
- 'kernel.all.cpu.user',
|
||||
- 'kernel.all.cpu.nice', 'kernel.all.cpu.sys', 'kernel.all.cpu.wait.total',
|
||||
- 'kernel.all.cpu.irq.hard', 'kernel.all.cpu.irq.soft', 'kernel.all.cpu.steal',
|
||||
- 'kernel.all.cpu.guest', 'kernel.all.cpu.guest_nice', 'kernel.all.cpu.idle',
|
||||
- 'kernel.percpu.cpu.user', 'kernel.percpu.cpu.nice', 'kernel.percpu.cpu.sys',
|
||||
- 'kernel.percpu.cpu.wait.total', 'kernel.percpu.cpu.irq.hard', 'kernel.percpu.cpu.irq.soft',
|
||||
- 'kernel.percpu.cpu.steal', 'kernel.percpu.cpu.guest','kernel.percpu.cpu.guest_nice',
|
||||
- 'kernel.percpu.cpu.idle', 'kernel.all.intr', 'kernel.percpu.intr']
|
||||
+ 'kernel.all.cpu.vuser', 'kernel.all.cpu.vnice', 'kernel.all.cpu.sys',
|
||||
+ 'kernel.all.cpu.wait.total', 'kernel.all.cpu.irq.hard', 'kernel.all.cpu.irq.soft',
|
||||
+ 'kernel.all.cpu.steal', 'kernel.all.cpu.guest', 'kernel.all.cpu.guest_nice',
|
||||
+ 'kernel.all.cpu.idle','kernel.percpu.cpu.vuser','kernel.percpu.cpu.vnice',
|
||||
+ 'kernel.percpu.cpu.sys','kernel.percpu.cpu.wait.total', 'kernel.percpu.cpu.irq.hard',
|
||||
+ 'kernel.percpu.cpu.irq.soft','kernel.percpu.cpu.steal', 'kernel.percpu.cpu.guest',
|
||||
+ 'kernel.percpu.cpu.guest_nice','kernel.percpu.cpu.idle', 'kernel.all.intr', 'kernel.percpu.intr']
|
||||
+
|
||||
interrupts_list = []
|
||||
soft_interrupts_list = []
|
||||
|
||||
@@ -126,170 +126,70 @@ def __fetch_previous_values(self,metric,instance):
|
||||
|
||||
class CoreCpuUtil:
|
||||
def __init__(self, instance, delta_time, metric_repository):
|
||||
- self.delta_time = delta_time
|
||||
self.instance = instance
|
||||
+ self.delta_time = delta_time
|
||||
self.metric_repository = metric_repository
|
||||
+ self._total_cpus = None # Cache for performance
|
||||
|
||||
def total_cpus(self):
|
||||
- return self.metric_repository.current_value('hinv.ncpu', None)
|
||||
+ if self._total_cpus is None:
|
||||
+ self._total_cpus = self.metric_repository.current_value('hinv.ncpu', None)
|
||||
+ return self._total_cpus
|
||||
+
|
||||
def cpu_number(self):
|
||||
return self.instance
|
||||
|
||||
- def cpu_online(self):
|
||||
- return self.metric_repository.current_value('hinv.cpu.online', self.instance)
|
||||
-
|
||||
def user_time(self):
|
||||
- metric = 'kernel.' + self.__all_or_percpu() + '.cpu.user'
|
||||
- p_time = self.metric_repository.previous_value(metric, self.instance)
|
||||
- c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
- if p_time is not None and c_time is not None:
|
||||
- value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None and self.total_cpus() is not None:
|
||||
- return float("%.2f"%(value/self.total_cpus()))
|
||||
- else:
|
||||
- if self.total_cpus() is None:
|
||||
- return None
|
||||
- return float("%.2f"%(value))
|
||||
-
|
||||
- else:
|
||||
- return None
|
||||
+ return self._compute_metric('cpu.vuser')
|
||||
|
||||
def nice_time(self):
|
||||
- metric = 'kernel.' + self.__all_or_percpu() + '.cpu.nice'
|
||||
- p_time = self.metric_repository.previous_value(metric, self.instance)
|
||||
- c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
- if p_time is not None and c_time is not None:
|
||||
- value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None and self.total_cpus() is not None:
|
||||
- return float("%.2f"%(value/self.total_cpus()))
|
||||
- else:
|
||||
- if self.total_cpus() is None:
|
||||
- return None
|
||||
- return float("%.2f"%(value))
|
||||
- else:
|
||||
- return None
|
||||
+ return self._compute_metric('cpu.vnice')
|
||||
|
||||
def sys_time(self):
|
||||
- metric = 'kernel.' + self.__all_or_percpu() + '.cpu.sys'
|
||||
- p_time = self.metric_repository.previous_value(metric, self.instance)
|
||||
- c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
- if p_time is not None and c_time is not None:
|
||||
- value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None and self.total_cpus() is not None:
|
||||
- return float("%.2f"%(value/self.total_cpus()))
|
||||
- else:
|
||||
- if self.total_cpus() is None:
|
||||
- return None
|
||||
- return float("%.2f"%(value))
|
||||
- else:
|
||||
- return None
|
||||
+ return self._compute_metric('cpu.sys')
|
||||
|
||||
def iowait_time(self):
|
||||
- metric = 'kernel.' + self.__all_or_percpu() + '.cpu.wait.total'
|
||||
- p_time = self.metric_repository.previous_value(metric, self.instance)
|
||||
- c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
- if p_time is not None and c_time is not None:
|
||||
- value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None and self.total_cpus() is not None:
|
||||
- return float("%.2f"%(value/self.total_cpus()))
|
||||
- else:
|
||||
- if self.total_cpus() is None:
|
||||
- return None
|
||||
- return float("%.2f"%(value))
|
||||
- else:
|
||||
- return None
|
||||
+ return self._compute_metric('cpu.wait.total')
|
||||
|
||||
def irq_hard(self):
|
||||
- metric = 'kernel.' + self.__all_or_percpu() + '.cpu.irq.hard'
|
||||
- p_time = self.metric_repository.previous_value(metric, self.instance)
|
||||
- c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
- if p_time is not None and c_time is not None:
|
||||
- value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None and self.total_cpus() is not None:
|
||||
- return float("%.2f"%(value/self.total_cpus()))
|
||||
- else:
|
||||
- if self.total_cpus() is None:
|
||||
- return None
|
||||
- return float("%.2f"%(value))
|
||||
- else:
|
||||
- return None
|
||||
+ return self._compute_metric('cpu.irq.hard')
|
||||
|
||||
def irq_soft(self):
|
||||
- metric = 'kernel.' + self.__all_or_percpu() + '.cpu.irq.soft'
|
||||
- p_time = self.metric_repository.previous_value(metric, self.instance)
|
||||
- c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
- if p_time is not None and c_time is not None:
|
||||
- value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None and self.total_cpus() is not None:
|
||||
- return float("%.2f"%(value/self.total_cpus()))
|
||||
- else:
|
||||
- if self.total_cpus() is None:
|
||||
- return None
|
||||
- return float("%.2f"%(value))
|
||||
- else:
|
||||
- return None
|
||||
+ return self._compute_metric('cpu.irq.soft')
|
||||
|
||||
def steal(self):
|
||||
- metric = 'kernel.' + self.__all_or_percpu() + '.cpu.steal'
|
||||
- p_time = self.metric_repository.previous_value(metric, self.instance)
|
||||
- c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
- if p_time is not None and c_time is not None:
|
||||
- value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None and self.total_cpus() is not None:
|
||||
- return float("%.2f"%(value/self.total_cpus()))
|
||||
- else:
|
||||
- if self.total_cpus() is None:
|
||||
- return None
|
||||
- return float("%.2f"%(value))
|
||||
- else:
|
||||
- return None
|
||||
+ return self._compute_metric('cpu.steal')
|
||||
|
||||
def guest_time(self):
|
||||
- metric = 'kernel.' + self.__all_or_percpu() + '.cpu.guest'
|
||||
- p_time = self.metric_repository.previous_value(metric, self.instance)
|
||||
- c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
- if p_time is not None and c_time is not None:
|
||||
- value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None and self.total_cpus() is not None:
|
||||
- return float("%.2f"%(value/self.total_cpus()))
|
||||
- else:
|
||||
- if self.total_cpus() is None:
|
||||
- return None
|
||||
- return float("%.2f"%(value))
|
||||
- else:
|
||||
- return None
|
||||
+ return self._compute_metric('cpu.guest')
|
||||
|
||||
def guest_nice(self):
|
||||
- metric = 'kernel.' + self.__all_or_percpu() + '.cpu.guest_nice'
|
||||
- p_time = self.metric_repository.previous_value(metric, self.instance)
|
||||
- c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
- if p_time is not None and c_time is not None:
|
||||
- value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None and self.total_cpus() is not None:
|
||||
- return float("%.2f"%(value/self.total_cpus()))
|
||||
- else:
|
||||
- if self.total_cpus() is None:
|
||||
- return None
|
||||
- return float("%.2f"%(value))
|
||||
- else:
|
||||
- return None
|
||||
+ return self._compute_metric('cpu.guest_nice')
|
||||
|
||||
def idle_time(self):
|
||||
- metric = 'kernel.' + self.__all_or_percpu() + '.cpu.idle'
|
||||
+ return self._compute_metric('cpu.idle')
|
||||
+
|
||||
+ def _compute_metric(self, metric_suffix):
|
||||
+ metric = f'kernel.{self._all_or_percpu()}.{metric_suffix}'
|
||||
p_time = self.metric_repository.previous_value(metric, self.instance)
|
||||
c_time = self.metric_repository.current_value(metric, self.instance)
|
||||
- if p_time is not None and c_time is not None:
|
||||
- value = (100*(c_time - p_time))/(1000*self.delta_time)
|
||||
- if self.instance is None and self.total_cpus() is not None:
|
||||
- return float("%.2f"%(value/self.total_cpus()))
|
||||
- else:
|
||||
- if self.total_cpus() is None:
|
||||
+
|
||||
+ if p_time is None or c_time is None or self.delta_time == 0:
|
||||
+ return None
|
||||
+
|
||||
+ try:
|
||||
+ value = (100 * (c_time - p_time)) / (1000 * self.delta_time)
|
||||
+ if self.instance is None:
|
||||
+ total = self.total_cpus()
|
||||
+ if total:
|
||||
+ value /= total
|
||||
+ else:
|
||||
return None
|
||||
- return float("%.2f"%(value))
|
||||
- else:
|
||||
+ return min (round(value, 2),100)
|
||||
+ except (ZeroDivisionError, TypeError):
|
||||
return None
|
||||
|
||||
- def __all_or_percpu(self):
|
||||
+ def _all_or_percpu(self):
|
||||
return 'all' if self.instance is None else 'percpu'
|
||||
|
||||
class CpuUtil:
|
||||
@@ -652,7 +552,7 @@ def get_summary_metrics(self,group):
|
||||
def report(self,manager):
|
||||
try:
|
||||
group = manager['mpstat']
|
||||
- if group['kernel.all.cpu.user'].netPrevValues is None:
|
||||
+ if group['kernel.all.cpu.vuser'].netPrevValues is None:
|
||||
# need two fetches to report rate converted counter metrics
|
||||
self.get_summary_metrics(group)
|
||||
return
|
||||
@ -1,29 +0,0 @@
|
||||
From 64cfffa8777d349d58aac07684abe0d834695e22 Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Fri, 2 May 2025 14:54:08 +0530
|
||||
Subject: [PATCH] pcp-iostat:fixed broken pipe issue in pcp-iostat utility.
|
||||
|
||||
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
|
||||
|
||||
Orabug: 37655753
|
||||
|
||||
Cherry-pick-commit: https://github.com/performancecopilot/pcp/pull/2200/commits/64cfffa8777d349d58aac07684abe0d834695e22
|
||||
|
||||
Signed-off-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
|
||||
---
|
||||
src/pcp/iostat/pcp-iostat.py | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/src/pcp/iostat/pcp-iostat.py b/src/pcp/iostat/pcp-iostat.py
|
||||
index 909f2ac70d..ae503ed83f 100755
|
||||
--- a/src/pcp/iostat/pcp-iostat.py
|
||||
+++ b/src/pcp/iostat/pcp-iostat.py
|
||||
@@ -461,5 +461,7 @@ def __init__(self):
|
||||
except pmapi.pmUsageErr as usage:
|
||||
usage.message()
|
||||
sys.exit(1)
|
||||
+ except IOError:
|
||||
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
@ -1,220 +0,0 @@
|
||||
From 8e6a5ac18c06f813bad74494134ad5ac2202684e Mon Sep 17 00:00:00 2001
|
||||
From: Sagar Sagar <sagar.sagar@oracle.com>
|
||||
Date: Mon, 22 Sep 2025 15:39:18 +0000
|
||||
Subject: [PATCH] Added new metrics for numastat per node
|
||||
|
||||
newly added metrics are follows:-
|
||||
mem.numa.util.swapCached
|
||||
mem.numa.util.kreclaimable
|
||||
mem.numa.util.anonhugepages
|
||||
mem.numa.util.shmemhugepages
|
||||
mem.numa.util.shmempmdmapped
|
||||
mem.numa.util.filehugepages
|
||||
mem.numa.util.filepmdmapped
|
||||
|
||||
Fixed issue with mem.numa.util.mapped metric where it was not giving correct values.
|
||||
|
||||
Signed-off-by: Sagar Sagar <sagar.sagar@oracle.com>
|
||||
Co-authored-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
|
||||
Orabug: 38435551
|
||||
|
||||
Cherry-pick-commit: https://github.com/orasagar/pcp/commit/8e6a5ac18c06f813bad74494134ad5ac2202684e
|
||||
|
||||
Signed-off-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
|
||||
---
|
||||
src/pmdas/linux/help | 7 ++++
|
||||
src/pmdas/linux/numa_meminfo.c | 50 +++++++++++++++++------------
|
||||
src/pmdas/linux/pmda.c | 58 +++++++++++++++++++++++++++++++++-
|
||||
src/pmdas/linux/root_linux | 7 ++++
|
||||
4 files changed, 100 insertions(+), 22 deletions(-)
|
||||
|
||||
diff --git a/src/pmdas/linux/help b/src/pmdas/linux/help
|
||||
index c608d10ba5..482e099d66 100644
|
||||
--- a/src/pmdas/linux/help
|
||||
+++ b/src/pmdas/linux/help
|
||||
@@ -1030,6 +1030,13 @@ pressure.
|
||||
@ mem.numa.util.hugepagesTotal per-node total count of hugepages
|
||||
@ mem.numa.util.hugepagesFree per-node count of free hugepages
|
||||
@ mem.numa.util.hugepagesSurp per-node count of surplus hugepages
|
||||
+@ mem.numa.util.swapCached per-node amount of memory in swap cache
|
||||
+@ mem.numa.util.kreclaimable per-node kernel reclaimable memory
|
||||
+@ mem.numa.util.anonhugepages per-node amount of memory in anonymous huge pages
|
||||
+@ mem.numa.util.shmemhugepages per-node amount of shared memory allocated with huge
|
||||
+@ mem.numa.util.shmempmdmapped per-node shared memory mapped into userspace with hugepages
|
||||
+@ mem.numa.util.filehugepages per-node page cache (file) pages allocated with hugepages
|
||||
+@ mem.numa.util.filepmdmapped per-node page cache mapped into userspace with hugepages
|
||||
@ mem.numa.alloc.hit per-node count of times a task wanted alloc on local node and succeeded
|
||||
@ mem.numa.alloc.miss per-node count of times a task wanted alloc on local node but got another node
|
||||
@ mem.numa.alloc.foreign count of times a task on another node alloced on that node, but got this node
|
||||
diff --git a/src/pmdas/linux/numa_meminfo.c b/src/pmdas/linux/numa_meminfo.c
|
||||
index 9024962b2e..461bed0965 100644
|
||||
--- a/src/pmdas/linux/numa_meminfo.c
|
||||
+++ b/src/pmdas/linux/numa_meminfo.c
|
||||
@@ -24,42 +24,50 @@
|
||||
|
||||
/* sysfs file for numa meminfo */
|
||||
static struct linux_table numa_meminfo_table[] = {
|
||||
- { field: "MemTotal:", maxval: 0x0 },
|
||||
- { field: "MemFree:", maxval: 0x0 },
|
||||
- { field: "MemUsed:", maxval: 0x0 },
|
||||
- { field: "Active:", maxval: 0x0 },
|
||||
- { field: "Inactive:", maxval: 0x0 },
|
||||
+ { field: "MemTotal:", maxval: 0x0 },
|
||||
+ { field: "MemFree:", maxval: 0x0 },
|
||||
+ { field: "MemUsed:", maxval: 0x0 },
|
||||
+ { field: "SwapCached:", maxval: 0x0 },
|
||||
+ { field: "Active:", maxval: 0x0 },
|
||||
+ { field: "Inactive:", maxval: 0x0 },
|
||||
{ field: "Active(anon):", maxval: 0x0 },
|
||||
{ field: "Inactive(anon):", maxval: 0x0 },
|
||||
{ field: "Active(file):", maxval: 0x0 },
|
||||
{ field: "Inactive(file):", maxval: 0x0 },
|
||||
- { field: "HighTotal:", maxval: 0x0 },
|
||||
- { field: "HighFree:", maxval: 0x0 },
|
||||
- { field: "LowTotal:", maxval: 0x0 },
|
||||
- { field: "LowFree:", maxval: 0x0 },
|
||||
+ { field: "HighTotal:", maxval: 0x0 },
|
||||
+ { field: "HighFree:", maxval: 0x0 },
|
||||
+ { field: "LowTotal:", maxval: 0x0 },
|
||||
+ { field: "LowFree:", maxval: 0x0 },
|
||||
{ field: "Unevictable:", maxval: 0x0 },
|
||||
- { field: "Mlocked:", maxval: 0x0 },
|
||||
- { field: "Dirty:", maxval: 0x0 },
|
||||
- { field: "Writeback:", maxval: 0x0 },
|
||||
- { field: "FilePages:", maxval: 0x0 },
|
||||
- { field: "Mapped:", maxval: 0x0 },
|
||||
- { field: "AnonPages:", maxval: 0x0 },
|
||||
- { field: "Shmem:", maxval: 0x0 },
|
||||
+ { field: "Mlocked:", maxval: 0x0 },
|
||||
+ { field: "Dirty:", maxval: 0x0 },
|
||||
+ { field: "Writeback:", maxval: 0x0 },
|
||||
+ { field: "FilePages:", maxval: 0x0 },
|
||||
+ { field: "AnonPages:", maxval: 0x0 },
|
||||
+ { field: "Shmem:", maxval: 0x0 },
|
||||
{ field: "KernelStack:", maxval: 0x0 },
|
||||
- { field: "PageTables:", maxval: 0x0 },
|
||||
+ { field: "PageTables:", maxval: 0x0 },
|
||||
+ { field: "SecPageTables:", maxval: 0x0 },
|
||||
{ field: "NFS_Unstable:", maxval: 0x0 },
|
||||
- { field: "Bounce:", maxval: 0x0 },
|
||||
+ { field: "Bounce:", maxval: 0x0 },
|
||||
{ field: "WritebackTmp:", maxval: 0x0 },
|
||||
- { field: "Slab:", maxval: 0x0 },
|
||||
+ { field: "KReclaimable:", maxval: 0x0 },
|
||||
+ { field: "Slab:", maxval: 0x0 },
|
||||
{ field: "SReclaimable:", maxval: 0x0 },
|
||||
- { field: "SUnreclaim:", maxval: 0x0 },
|
||||
+ { field: "SUnreclaim:", maxval: 0x0 },
|
||||
+ { field: "AnonHugePages:", maxval: 0x0 },
|
||||
+ { field: "ShmemHugePages:", maxval: 0x0 },
|
||||
+ { field: "ShmemPmdMapped:", maxval: 0x0 },
|
||||
+ { field: "FileHugePages:", maxval: 0x0 },
|
||||
+ { field: "FilePmdMapped:", maxval: 0x0 },
|
||||
+ { field: "Mapped:", maxval: 0x0 },
|
||||
{ field: "HugePages_Total:", maxval: 0x0 },
|
||||
{ field: "HugePages_Free:", maxval: 0x0 },
|
||||
{ field: "HugePages_Surp:", maxval: 0x0 },
|
||||
{ field: NULL }
|
||||
};
|
||||
|
||||
-/* sysfs file for numastat */
|
||||
+/* sysfs file for numastat */
|
||||
static struct linux_table numa_memstat_table[] = {
|
||||
{ field: "numa_hit", maxval: ULONGLONG_MAX },
|
||||
{ field: "numa_miss", maxval: ULONGLONG_MAX },
|
||||
diff --git a/src/pmdas/linux/pmda.c b/src/pmdas/linux/pmda.c
|
||||
index 1026caf860..b30496b4b8 100644
|
||||
--- a/src/pmdas/linux/pmda.c
|
||||
+++ b/src/pmdas/linux/pmda.c
|
||||
@@ -1416,6 +1416,42 @@ static pmdaMetric metrictab[] = {
|
||||
{ PMDA_PMID(CLUSTER_NUMA_MEMINFO,41), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT,
|
||||
PMDA_PMUNITS(1,0,0,PM_SPACE_BYTE,0,0) }, },
|
||||
|
||||
+/* mem.numa.util.swapCached */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_NUMA_MEMINFO,42), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.numa.util.kreclaimable */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_NUMA_MEMINFO,43), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.numa.util.anonhugepages */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_NUMA_MEMINFO,44), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.numa.util.shmemhugepages */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_NUMA_MEMINFO,45), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.numa.util.shemempmdmapped */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_NUMA_MEMINFO,46), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.numa.util.filehugepages */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_NUMA_MEMINFO,47), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+/* mem.numa.util.filepmdmapped */
|
||||
+ { NULL,
|
||||
+ { PMDA_PMID(CLUSTER_NUMA_MEMINFO,48), PM_TYPE_U64, NODE_INDOM, PM_SEM_INSTANT,
|
||||
+ PMDA_PMUNITS(1,0,0,PM_SPACE_KBYTE,0,0) }, },
|
||||
+
|
||||
+
|
||||
/* swap.length */
|
||||
{ NULL,
|
||||
{ PMDA_PMID(CLUSTER_MEMINFO,6), PM_TYPE_U64, PM_INDOM_NULL, PM_SEM_INSTANT,
|
||||
@@ -9952,7 +9988,27 @@ linux_fetchCallBack(pmdaMetric *mdesc, unsigned int inst, pmAtomValue *atom)
|
||||
sts = linux_table_lookup("HugePages_Surp:", np->meminfo, &atom->ull);
|
||||
atom->ull *= (proc_meminfo.Hugepagesize << 10);
|
||||
break;
|
||||
-
|
||||
+ case 42: /* mem.numa.util.swapCached */
|
||||
+ sts = linux_table_lookup("SwapCached:", np->meminfo, &atom->ull);
|
||||
+ break;
|
||||
+ case 43: /* mem.numa.util.kreclaimable */
|
||||
+ sts = linux_table_lookup("KReclaimable:", np->meminfo, &atom->ull);
|
||||
+ break;
|
||||
+ case 44: /* mem.numa.util.anonhugepages */
|
||||
+ sts = linux_table_lookup("AnonHugePages:", np->meminfo, &atom->ull);
|
||||
+ break;
|
||||
+ case 45: /* mem.numa.util.shmemhugepages */
|
||||
+ sts = linux_table_lookup("ShmemHugePages:", np->meminfo, &atom->ull);
|
||||
+ break;
|
||||
+ case 46: /* mem.numa.util.shmempmdmapped */
|
||||
+ sts = linux_table_lookup("ShmemPmdMapped:", np->meminfo, &atom->ull);
|
||||
+ break;
|
||||
+ case 47: /* mem.numa.util.filehugepages */
|
||||
+ sts = linux_table_lookup("FileHugePages:", np->meminfo, &atom->ull);
|
||||
+ break;
|
||||
+ case 48: /* mem.numa.util.filepmdmapped */
|
||||
+ sts = linux_table_lookup("FilePmdMapped:", np->meminfo, &atom->ull);
|
||||
+ break;
|
||||
default:
|
||||
return PM_ERR_PMID;
|
||||
}
|
||||
diff --git a/src/pmdas/linux/root_linux b/src/pmdas/linux/root_linux
|
||||
index 1add9a43cf..054b77b67d 100644
|
||||
--- a/src/pmdas/linux/root_linux
|
||||
+++ b/src/pmdas/linux/root_linux
|
||||
@@ -793,6 +793,13 @@ mem.numa.util {
|
||||
hugepagesTotalBytes 60:36:39
|
||||
hugepagesFreeBytes 60:36:40
|
||||
hugepagesSurpBytes 60:36:41
|
||||
+ swapCached 60:36:42
|
||||
+ kreclaimable 60:36:43
|
||||
+ anonhugepages 60:36:44
|
||||
+ shmemhugepages 60:36:45
|
||||
+ shmempmdmapped 60:36:46
|
||||
+ filehugepages 60:36:47
|
||||
+ filepmdmapped 60:36:48
|
||||
}
|
||||
|
||||
mem.numa.alloc {
|
||||
@ -1,495 +0,0 @@
|
||||
From b09b0435b4fafd56a5930de2adeeea4c7f45ea57 Mon Sep 17 00:00:00 2001
|
||||
From: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
Date: Wed, 8 Oct 2025 13:17:18 +0530
|
||||
Subject: [PATCH] Modifies pcp numastat tool with pmcc metric group printer Add
|
||||
support for -m and -n options in pcp numastat tool like numastat Provides
|
||||
meminfo data per node
|
||||
|
||||
Orabug: 38380185
|
||||
|
||||
Cherry-pick-commit: https://github.com/performancecopilot/pcp/commit/e4d513fd503cd3f0e7a2ccab0e0f069acf872427
|
||||
|
||||
Signed-off-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
|
||||
---
|
||||
src/pcp/numastat/pcp-numastat.1 | 8 +-
|
||||
src/pcp/numastat/pcp-numastat.py | 414 +++++++++++++++------
|
||||
2 files changed, 422 insertions(+), 1 deletions(-)
|
||||
|
||||
diff --git a/src/pcp/numastat/pcp-numastat.1 b/src/pcp/numastat/pcp-numastat.1
|
||||
index d9165ea86e..65910a4877 100644
|
||||
--- a/src/pcp/numastat/pcp-numastat.1
|
||||
+++ b/src/pcp/numastat/pcp-numastat.1
|
||||
@@ -18,7 +18,7 @@
|
||||
\f3pcp-numastat\f1 \- report on NUMA memory allocation
|
||||
.SH SYNOPSIS
|
||||
\f3pcp\f1 [\f2pcp\ options\f1] \f3numastat\f1
|
||||
-[\f3\-Vw?\f1]
|
||||
+[\fB-V\fR] [\fB-w\fR \fIwidth\fR] [\fB-m\fR][-n\fR] [\fB-?\fR]
|
||||
.SH DESCRIPTION
|
||||
.B pcp-numastat
|
||||
displays NUMA allocation statistics from the kernel memory
|
||||
@@ -69,6 +69,12 @@ Display the current version of the command.
|
||||
Limit display to
|
||||
.IR width .
|
||||
.TP
|
||||
+\fB-m\fR, \fB--meminfo\fR
|
||||
+Display meminfo-like system-wide memory usage.
|
||||
+.TP
|
||||
+\fB-n\fR, \fB--numastat\fR
|
||||
+Display the numastat statistics info.
|
||||
+.TP
|
||||
\fB\-?\fR, \fB\-\-help\fR
|
||||
Display usage message and exit.
|
||||
.SH NOTES
|
||||
diff --git a/src/pcp/numastat/pcp-numastat.py b/src/pcp/numastat/pcp-numastat.py
|
||||
index 46c2fcb97d..2a044c9bbd 100755
|
||||
--- a/src/pcp/numastat/pcp-numastat.py
|
||||
+++ b/src/pcp/numastat/pcp-numastat.py
|
||||
@@ -17,150 +17,328 @@
|
||||
""" Display NUMA memory allocation statistucs """
|
||||
|
||||
import os
|
||||
+import signal
|
||||
import sys
|
||||
+import time
|
||||
+
|
||||
from pcp import pmapi
|
||||
-from cpmapi import PM_TYPE_U64, PM_CONTEXT_ARCHIVE
|
||||
+from pcp import pmcc
|
||||
+from cpmapi import PM_CONTEXT_ARCHIVE
|
||||
|
||||
if sys.version >= '3':
|
||||
long = int # python2 to python3 portability (no long() in python3)
|
||||
xrange = range # more back-compat (xrange() is range() in python3)
|
||||
|
||||
-class NUMAStat(object):
|
||||
- """ Gives a short summary of per-node NUMA memory information.
|
||||
+NUMA_METRICS = [
|
||||
+ "mem.numa.alloc.hit",
|
||||
+ "mem.numa.alloc.miss",
|
||||
+ "mem.numa.alloc.foreign",
|
||||
+ "mem.numa.alloc.interleave_hit",
|
||||
+ "mem.numa.alloc.local_node",
|
||||
+ "mem.numa.alloc.other_node",
|
||||
+]
|
||||
|
||||
- Knows about some of the default PCP arguments - can function
|
||||
- using remote hosts or historical data, using the timezone of
|
||||
- the metric source, at an offset within an archive, and so on.
|
||||
- """
|
||||
+MEM_METRICS = [
|
||||
+ "mem.numa.util.total",
|
||||
+ "mem.numa.util.free",
|
||||
+ "mem.numa.util.used",
|
||||
+ "mem.numa.util.active",
|
||||
+ "mem.numa.util.inactive",
|
||||
+ "mem.numa.util.active_anon",
|
||||
+ "mem.numa.util.inactive_anon",
|
||||
+ "mem.numa.util.active_file",
|
||||
+ "mem.numa.util.inactive_file",
|
||||
+ "mem.numa.util.unevictable",
|
||||
+ "mem.numa.util.mlocked",
|
||||
+ "mem.numa.util.dirty",
|
||||
+ "mem.numa.util.writeback",
|
||||
+ "mem.numa.util.filePages",
|
||||
+ "mem.numa.util.mapped",
|
||||
+ "mem.numa.util.anonpages",
|
||||
+ "mem.numa.util.shmem",
|
||||
+ "mem.numa.util.kernelStack",
|
||||
+ "mem.numa.util.pageTables",
|
||||
+ "mem.numa.util.NFS_Unstable",
|
||||
+ "mem.numa.util.bounce",
|
||||
+ "mem.numa.util.writebackTmp",
|
||||
+ "mem.numa.util.filehugepages",
|
||||
+ "mem.numa.util.filepmdmapped",
|
||||
+ "mem.numa.util.slab",
|
||||
+ "mem.numa.util.slabReclaimable",
|
||||
+ "mem.numa.util.slabUnreclaimable",
|
||||
+ "mem.numa.util.anonhugepages",
|
||||
+ "mem.numa.util.shmemhugepages",
|
||||
+ "mem.numa.util.shmempmdmapped",
|
||||
+ "mem.numa.util.hugepagesTotal",
|
||||
+ "mem.numa.util.hugepagesFree",
|
||||
+ "mem.numa.util.hugepagesSurp",
|
||||
+ "mem.numa.util.swapCached",
|
||||
+ "mem.numa.util.kreclaimable",
|
||||
+]
|
||||
|
||||
- def __init__(self):
|
||||
- """ Construct object - prepare for command line handling """
|
||||
- self.opts = self.options()
|
||||
- self.context = None
|
||||
- self.width = 0
|
||||
+SYS_METRICS = [
|
||||
+ 'kernel.uname.nodename',
|
||||
+ 'kernel.uname.release',
|
||||
+ 'kernel.uname.sysname',
|
||||
+ 'kernel.uname.machine',
|
||||
+ 'hinv.ncpu',
|
||||
+]
|
||||
+
|
||||
+ALL_METRICS = NUMA_METRICS + MEM_METRICS
|
||||
|
||||
- def resize(self):
|
||||
+def prefix(metric):
|
||||
+ last_part = metric.split('.')[-1]
|
||||
+ result = last_part[0].upper() + last_part[1:]
|
||||
+ return result
|
||||
+
|
||||
+class MetricRepository:
|
||||
+ def __init__(self, group):
|
||||
+ self.group = group
|
||||
+ self.current_cached_values = {}
|
||||
+ self.previous_cached_values = {}
|
||||
+
|
||||
+ def _fetch_current_values(self, metric, instance):
|
||||
+ if instance is not None:
|
||||
+ return dict(
|
||||
+ map(lambda x: (x[0].inst, x[2]), self.group[metric].netValues)
|
||||
+ )
|
||||
+ else:
|
||||
+ if self.group[metric].netValues == []:
|
||||
+ return None
|
||||
+ else:
|
||||
+ return self.group[metric].netValues[0][2]
|
||||
+ def current_values(self, metric_name):
|
||||
+ if self.group.get(metric_name, None) is None:
|
||||
+ return None
|
||||
+ if self.current_cached_values.get(metric_name, None) is None:
|
||||
+ self.current_cached_values[
|
||||
+ metric_name
|
||||
+ ] = self._fetch_current_values(metric_name, True)
|
||||
+ return self.current_cached_values.get(metric_name, None)
|
||||
+
|
||||
+class NUMAStat:
|
||||
+
|
||||
+ def __init__(self, group):
|
||||
+ self.group = group
|
||||
+ self.repo = MetricRepository(group)
|
||||
+
|
||||
+ def resize(self, width):
|
||||
""" Find a suitable display width limit """
|
||||
- if self.width == 0:
|
||||
+ if width == 0:
|
||||
if not sys.stdout.isatty():
|
||||
- self.width = 1000000000 # mimic numastat(1) here
|
||||
+ width = 1000000000 # mimic numastat(1) here
|
||||
else:
|
||||
- # popen() is SAFE, command is a literal string
|
||||
+ # popen() is SAFE, command is a literal string
|
||||
(_, width) = os.popen('stty size', 'r').read().split()
|
||||
- self.width = int(width)
|
||||
- self.width = int(os.getenv('NUMASTAT_WIDTH', str(self.width)))
|
||||
- self.width = max(self.width, 32)
|
||||
+ width = int(width)
|
||||
+ width = int(os.getenv('NUMASTAT_WIDTH', str(width)))
|
||||
+ return max(width, 32)
|
||||
|
||||
- def option(self, opt, optarg, index):
|
||||
- """ Perform setup for an individual command line option """
|
||||
- if opt == 'w':
|
||||
- self.width = int(optarg)
|
||||
+ def __format_table(self, width, nodes, data):
|
||||
+ null_output = False
|
||||
+ if not nodes:
|
||||
+ null_output = True
|
||||
+ nodes = [(0, 'Node ')]
|
||||
|
||||
- def options(self):
|
||||
- """ Setup default command line argument option handling """
|
||||
- opts = pmapi.pmOptions()
|
||||
- opts.pmSetOptionCallback(self.option)
|
||||
- opts.pmSetShortOptions("w:V?")
|
||||
- opts.pmSetLongOptionHeader("Options")
|
||||
- opts.pmSetLongOption("width", 1, 'w', "N", "limit the display width")
|
||||
- opts.pmSetLongOptionVersion()
|
||||
- opts.pmSetLongOptionHelp()
|
||||
- return opts
|
||||
-
|
||||
- def extract(self, descs, insts, result):
|
||||
- """ Extract the set of metric values from a given pmResult """
|
||||
- values = [[]]
|
||||
- for metrics in xrange(len(descs)):
|
||||
- values.append([])
|
||||
- for nodes in xrange(len(insts)):
|
||||
- if result.contents.get_numval(metrics) > 0:
|
||||
- atom = self.context.pmExtractValue(
|
||||
- result.contents.get_valfmt(metrics),
|
||||
- result.contents.get_vlist(metrics, nodes),
|
||||
- descs[metrics].contents.type, PM_TYPE_U64)
|
||||
- values[metrics].append(long(atom.ull))
|
||||
- else:
|
||||
- values[metrics].append(long(0))
|
||||
- return values
|
||||
-
|
||||
- def execute(self):
|
||||
- """ Using a PMAPI context (could be either host or archive),
|
||||
- fetch and report per-node values related to NUMA memory.
|
||||
- """
|
||||
- metrics = ('mem.numa.alloc.hit', 'mem.numa.alloc.miss',
|
||||
- 'mem.numa.alloc.foreign', 'mem.numa.alloc.interleave_hit',
|
||||
- 'mem.numa.alloc.local_node', 'mem.numa.alloc.other_node')
|
||||
-
|
||||
- pmids = self.context.pmLookupName(metrics)
|
||||
- descs = self.context.pmLookupDescs(pmids)
|
||||
- if self.context.type == PM_CONTEXT_ARCHIVE:
|
||||
- (insts, nodes) = self.context.pmGetInDomArchive(descs[0])
|
||||
+ if "numastat" in data:
|
||||
+ metrics = NUMA_METRICS
|
||||
+ title = "NUMA memory allocation statistics (pages)"
|
||||
else:
|
||||
- (insts, nodes) = self.context.pmGetInDom(descs[0])
|
||||
- result = self.context.pmFetch(pmids)
|
||||
- values = self.extract(descs, insts, result)
|
||||
- self.context.pmFreeResult(result)
|
||||
- self.report(metrics, nodes, values)
|
||||
-
|
||||
- def report(self, metrics, nodes, values):
|
||||
- """ Given per-node metric names and values, dump 'em like numastat(1)
|
||||
- Nodes is a list of strings, values is a list of lists of values.
|
||||
- """
|
||||
- columns = len(nodes) * 16
|
||||
- if columns == 0:
|
||||
- print("No NUMA nodes found, exiting")
|
||||
- sys.exit(1)
|
||||
- self.resize()
|
||||
- maxnodes = int((self.width - 16) / 16)
|
||||
+ metrics = MEM_METRICS
|
||||
+ title = "Per-node system memory usage (KB)"
|
||||
+
|
||||
+ total_w = max(42, int(width))
|
||||
+ print(title[:total_w])
|
||||
+
|
||||
+ width = self.resize(width)
|
||||
+ maxnodes = int((width - 16) / 16)
|
||||
if maxnodes > len(nodes): # just an initial header suffices
|
||||
- header = '%-16s' % ''
|
||||
- for node in nodes:
|
||||
- header += '%16s' % node
|
||||
+ header = '%30s' % ''
|
||||
+ for _, node in nodes:
|
||||
+ header += '%-12s' % node
|
||||
print(header)
|
||||
- for index in xrange(len(metrics)):
|
||||
- title = self.prefix(metrics[index])
|
||||
- self.metric(title, nodes, values[index], maxnodes)
|
||||
-
|
||||
- def metric(self, prefix, nodes, values, maxnodes):
|
||||
- """ Given one metric and its per-node values, produce one or more
|
||||
- lines of output with the values, each line node-name prefixed
|
||||
- and with a new node header for each.
|
||||
- """
|
||||
- done = 0
|
||||
- while done < len(nodes):
|
||||
- header = '%-16s' % ''
|
||||
- window = '%-16s' % prefix
|
||||
- for index in xrange(maxnodes):
|
||||
- current = done + index
|
||||
- if current >= len(nodes):
|
||||
- break
|
||||
- header += '%16s' % (nodes[current])
|
||||
- window += '%16d' % (values[current])
|
||||
- if done > maxnodes or maxnodes <= len(nodes):
|
||||
- print('%s\n%s' % (header, window))
|
||||
- else:
|
||||
- print('%s' % window)
|
||||
- done += maxnodes
|
||||
|
||||
- def prefix(self, metric):
|
||||
- """ Transform the PCP metric names into the reported sub-headings """
|
||||
- title = metric[15:]
|
||||
- if '_' not in title:
|
||||
- title = 'numa_' + title
|
||||
- return title
|
||||
+ for m in metrics:
|
||||
+ if not null_output:
|
||||
+ vals = self.repo.current_values(m)
|
||||
+ done = 0 # reset for each metric
|
||||
+
|
||||
+ # Loop through nodes in chunks of 'maxnodes'
|
||||
+ while done < len(nodes):
|
||||
+ header = '%-30s' % ''
|
||||
+ window = '%-20s : ' % prefix(m)
|
||||
+
|
||||
+ # Slice the range we'll print in this batch
|
||||
+ chunk = nodes[done:done + maxnodes]
|
||||
+
|
||||
+ for i, ( _, name) in enumerate(chunk):
|
||||
+ header += '%-12s' % name
|
||||
+ if not null_output:
|
||||
+ window += '%12s' % vals[done + i]
|
||||
+ else:
|
||||
+ window += '%12s' % "NA"
|
||||
+
|
||||
+ # Print header once per row group (not every metric)
|
||||
+ if done > maxnodes or maxnodes <= len(nodes):
|
||||
+ print('%s\n%s' % (header, window))
|
||||
+ else:
|
||||
+ print('%s' % window)
|
||||
+ done += maxnodes
|
||||
+ print()
|
||||
+
|
||||
+ def print_mem(self, width, nodes, data):
|
||||
+ self.__format_table(width, nodes, data)
|
||||
+
|
||||
+ def print_numa(self, width, nodes, data):
|
||||
+ self.__format_table(width, nodes, data)
|
||||
+
|
||||
+class NumaStatOption(pmapi.pmOptions):
|
||||
+ context = None
|
||||
+ timefmt = "%m/%d/%Y %H:%M:%S"
|
||||
+ width = 0
|
||||
+ mem_out = False
|
||||
+ numa_out = False
|
||||
+
|
||||
+ def override(self,opt):
|
||||
+ """ Override standard PCP options to match numastat(1) """
|
||||
+ if opt == 'n':
|
||||
+ return True
|
||||
+ return False
|
||||
+
|
||||
+ def __init__(self):
|
||||
+ pmapi.pmOptions.__init__(self)
|
||||
+ self.pmSetShortOptions("w:mV?:n")
|
||||
+ self.pmSetOptionCallback(self.extraOptions)
|
||||
+ self.pmSetOverrideCallback(self.override)
|
||||
+ self.pmSetLongOptionHeader("Numastat options")
|
||||
+ self.pmSetLongOption("width", 1, 'w', "n", "limit the display width")
|
||||
+ # Map long options to our non-conflicting short letters
|
||||
+ self.pmSetLongOption("meminfo", 0, 'm', "", "show meminfo-like system-wide memory usage")
|
||||
+ self.pmSetLongOption("numastat", 0, 'n', "", "show the numastat statistics info")
|
||||
+ self.pmSetLongOptionVersion()
|
||||
+ self.pmSetLongOptionHelp()
|
||||
+
|
||||
+ def extraOptions(self, opt, optarg, index):
|
||||
+ if opt == 'w':
|
||||
+ self.width = int(optarg)
|
||||
+ elif opt == "m":
|
||||
+ self.mem_out = True
|
||||
+ elif opt == "n":
|
||||
+ self.numa_out = True
|
||||
+ elif opt == "V":
|
||||
+ pass
|
||||
+ else:
|
||||
+ raise pmapi.pmUsageErr()
|
||||
+ return True
|
||||
+
|
||||
+ def checkoptions(self):
|
||||
+ if (not self.mem_out) and (not self.numa_out) and (self.width == 0):
|
||||
+ self.numa_out = True
|
||||
+ if self.width < 0:
|
||||
+ return False
|
||||
+ return True
|
||||
|
||||
- def connect(self):
|
||||
- """ Establish a PMAPI context to archive, host or local, via args """
|
||||
- self.context = pmapi.pmContext.fromOptions(self.opts, sys.argv)
|
||||
+class NumaStatReport(pmcc.MetricGroupPrinter):
|
||||
+ machine_info_count = 0
|
||||
+
|
||||
+ def __init__(self, options):
|
||||
+ self.options = options
|
||||
+ self.timestamp = None
|
||||
+
|
||||
+ def __get_timestamp(self, group):
|
||||
+ ts = group.contextCache.pmLocaltime(int(group.timestamp))
|
||||
+ self.timestamp = time.strftime(NumaStatOption.timefmt, ts.struct_time())
|
||||
+ return self.timestamp
|
||||
+
|
||||
+ def __get_ncpu(self, group):
|
||||
+ return group['hinv.ncpu'].netValues[0][2]
|
||||
+
|
||||
+ def print_machine_info(self,group, context):
|
||||
+ timestamp = context.pmLocaltime(group.timestamp.tv_sec)
|
||||
+ # Please check strftime(3) for different formatting options.
|
||||
+ # Also check TZ and LC_TIME environment variables for more
|
||||
+ # information on how to override the default formatting of
|
||||
+ # the date display in the header
|
||||
+ time_string = time.strftime("%x", timestamp.struct_time())
|
||||
+ header_string = ''
|
||||
+ header_string += group['kernel.uname.sysname'].netValues[0][2] + ' '
|
||||
+ header_string += group['kernel.uname.release'].netValues[0][2] + ' '
|
||||
+ header_string += '(' + group['kernel.uname.nodename'].netValues[0][2] + ') '
|
||||
+ header_string += time_string + ' '
|
||||
+ header_string += group['kernel.uname.machine'].netValues[0][2] + ' '
|
||||
+ print("%s (%s CPU)" % (header_string, self.__get_ncpu(group)))
|
||||
+
|
||||
+ def __discover_nodes(self, group, name):
|
||||
+ # Build list of online nodes (instance id, instance name)
|
||||
+ nodes = []
|
||||
+ try:
|
||||
+ for ent in group[name].netValues:
|
||||
+ inst_id = ent[0].inst
|
||||
+ inst_name = ent[1] # usually "node0", "node1", ...
|
||||
+ online = int(ent[2]) != 0
|
||||
+ if online:
|
||||
+ nodes.append((inst_id, inst_name))
|
||||
+ except Exception:
|
||||
+ pass
|
||||
+ # Sort by instance id (node number)
|
||||
+ nodes.sort(key=lambda t: t[0])
|
||||
+ return nodes
|
||||
+
|
||||
+ def report(self, manager):
|
||||
+ # Print in a stable order
|
||||
+ group = manager["sys_info"]
|
||||
+ try:
|
||||
+ if not self.machine_info_count:
|
||||
+ self.print_machine_info(group, manager)
|
||||
+ self.machine_info_count = 1
|
||||
+ except IndexError:
|
||||
+ return
|
||||
+
|
||||
+ output_numa = (
|
||||
+ self.options.numa_out
|
||||
+ or (not self.options.mem_out and not self.options.numa_out)
|
||||
+ )
|
||||
+ output_mem = self.options.mem_out
|
||||
+ group = manager["numastat"]
|
||||
+ nodes = self.__discover_nodes(group, "mem.numa.util.total")
|
||||
+ timestamp = self.__get_timestamp(group)
|
||||
+ print("%-20s : %s"%("Timestamp", timestamp))
|
||||
+ if output_mem:
|
||||
+ NUMAStat(group).print_mem(self.options.width, nodes, "meminfo")
|
||||
+ if output_numa:
|
||||
+ NUMAStat(group).print_numa(self.options.width, nodes, "numastat")
|
||||
+
|
||||
+ if (
|
||||
+ NumaStatOption.context is not PM_CONTEXT_ARCHIVE
|
||||
+ and self.options.pmGetOptionSamples() is None
|
||||
+ ):
|
||||
+ sys.exit(0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
- NUMASTAT = NUMAStat()
|
||||
- NUMASTAT.connect()
|
||||
- NUMASTAT.execute()
|
||||
+ opts = NumaStatOption()
|
||||
+ mngr = pmcc.MetricGroupManager.builder(opts, sys.argv)
|
||||
+ if not opts.checkoptions():
|
||||
+ print("Invalid options from command line")
|
||||
+ raise pmapi.pmUsageErr()
|
||||
+ NumaStatOption.context = mngr.type
|
||||
+
|
||||
+ missing = mngr.checkMissingMetrics(ALL_METRICS)
|
||||
+ if missing is not None:
|
||||
+ sys.stderr.write('Error: not all required metrics are available\nMissing: %s\n' % (missing))
|
||||
+ sys.exit(1)
|
||||
+
|
||||
+ mngr["numastat"] = ALL_METRICS
|
||||
+ mngr["sys_info"] = SYS_METRICS
|
||||
+ mngr.printer = NumaStatReport(opts)
|
||||
+ sts = mngr.run()
|
||||
+ sys.exit(sts)
|
||||
+ except IOError:
|
||||
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
except pmapi.pmErr as error:
|
||||
- print("%s: %s" % (error.progname(), error.message()))
|
||||
+ sys.stderr.write("%s %s\n" % (error.progname(), error.message()))
|
||||
except pmapi.pmUsageErr as usage:
|
||||
usage.message()
|
||||
+ sys.exit(1)
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
@ -1,45 +0,0 @@
|
||||
From d2e92db5d6ee2a07e52ff72da471976aafaeb7ec Mon Sep 17 00:00:00 2001
|
||||
From: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
Date: Fri, 10 Oct 2025 16:39:51 +0530
|
||||
Subject: [PATCH] Fixes higepagesize metric value from bytes to KB as per
|
||||
/proc/meminfo
|
||||
|
||||
[sagar@vbox ~]$ pcp meminfo | grep Hugepagesize ; cat /proc/meminfo | grep Hugepagesize
|
||||
Hugepagesize : 2097152 kB
|
||||
Hugepagesize: 2048 kB
|
||||
|
||||
Cherry-pick-commit: https://github.com/performancecopilot/pcp/pull/2371/commits/84bf35c442cedab82bdd5d71da6cca21cbf628db
|
||||
|
||||
Orabug: 38531609
|
||||
|
||||
Signed-off-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
---
|
||||
src/pcp/meminfo/pcp-meminfo.py | 6 ++++++
|
||||
1 file changed, 6 insertions(+)
|
||||
|
||||
diff --git a/src/pcp/meminfo/pcp-meminfo.py b/src/pcp/meminfo/pcp-meminfo.py
|
||||
index 8492859..f956910 100755
|
||||
--- a/src/pcp/meminfo/pcp-meminfo.py
|
||||
+++ b/src/pcp/meminfo/pcp-meminfo.py
|
||||
@@ -168,6 +168,9 @@ class MeminfoReport(pmcc.MetricGroupPrinter):
|
||||
units = ""
|
||||
if METRICS_DESC[idx][-6:] == "_NO_kb":
|
||||
metric_name = METRICS_DESC[idx][:-6]
|
||||
+ elif METRICS_DESC[idx] == "Hugepagesize":
|
||||
+ metric_name = METRICS_DESC[idx]
|
||||
+ units = "B"
|
||||
else:
|
||||
metric_name = METRICS_DESC[idx]
|
||||
units = "kB"
|
||||
@@ -203,6 +206,9 @@ class MeminfoReport(pmcc.MetricGroupPrinter):
|
||||
continue
|
||||
|
||||
metric_name, units = self.getMetricName(idx)
|
||||
+ if units == "B":
|
||||
+ val = int(val / 1024)
|
||||
+ units = "kB"
|
||||
print("%-17s : %s %s"%(metric_name, val, units))
|
||||
|
||||
idx += 1
|
||||
--
|
||||
2.43.7
|
||||
@ -1,32 +0,0 @@
|
||||
From 82d8675ebfc244f73314f6a19e171443019a6fbb Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Fri, 31 Oct 2025 14:02:13 +0530
|
||||
Subject: [OL8 1027/1027] xz default compression changed to level 3
|
||||
|
||||
to reduce the size of the compressed pcp archives
|
||||
Orabug: 38592558
|
||||
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
|
||||
---
|
||||
src/pmlogger/pmlogger_daily.sh | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/pmlogger/pmlogger_daily.sh b/src/pmlogger/pmlogger_daily.sh
|
||||
index db66b1d..27b1550 100755
|
||||
--- a/src/pmlogger/pmlogger_daily.sh
|
||||
+++ b/src/pmlogger/pmlogger_daily.sh
|
||||
@@ -187,10 +187,10 @@ COMPRESS=""
|
||||
COMPRESS_CMDLINE=""
|
||||
if which xz >/dev/null 2>&1
|
||||
then
|
||||
- if xz -0 --block-size=10MiB </dev/null >/dev/null 2>&1
|
||||
+ if xz -3 --block-size=10MiB </dev/null >/dev/null 2>&1
|
||||
then
|
||||
# want minimal overheads, -0 is the same as --fast
|
||||
- COMPRESS_DEFAULT="xz -0 --block-size=10MiB"
|
||||
+ COMPRESS_DEFAULT="xz -3 --block-size=10MiB"
|
||||
else
|
||||
COMPRESS_DEFAULT=xz
|
||||
fi
|
||||
--
|
||||
2.43.7
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
From c80dc758eb0eb27fafa0b594d6c2aa1f4c3803fa Mon Sep 17 00:00:00 2001
|
||||
From: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
Date: Wed, 3 Dec 2025 20:40:52 +0530
|
||||
Subject: [PATCH] pmdanfsclient: fix regex to correctly parse NFS op stats
|
||||
|
||||
The regex used to match NFS operation statistics in /proc/self/mountstats was
|
||||
missing a capture group, causing parsing failures when an additional field
|
||||
was present in newer kernel formats. Updated the regex to include the extra
|
||||
numeric field so that all opstats lines are parsed correctly.
|
||||
|
||||
Signed-off-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
|
||||
Cherry-pick-commit: https://github.com/performancecopilot/pcp/commit/85671f8874d7d4b4e57eb45bfe295de92b95415c
|
||||
|
||||
Orabug: 38724884
|
||||
|
||||
Signed-off-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
|
||||
---
|
||||
src/pmdas/nfsclient/pmdanfsclient.python | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/src/pmdas/nfsclient/pmdanfsclient.python b/src/pmdas/nfsclient/pmdanfsclient.python
|
||||
index 786d958..f08b93a 100644
|
||||
--- a/src/pmdas/nfsclient/pmdanfsclient.python
|
||||
+++ b/src/pmdas/nfsclient/pmdanfsclient.python
|
||||
@@ -636,7 +636,7 @@ class NFSCLIENTPMDA(PMDA):
|
||||
line = STATS.readline()
|
||||
if line == '':
|
||||
break
|
||||
- m = re.match(r'\s*([A-Z_]*): (\d*) (\d*) (\d*) (\d*) (\d*) (\d*) (\d*) (\d*)$', line)
|
||||
+ m = re.match(r'\s*([A-Z_]*): (\d*) (\d*) (\d*) (\d*) (\d*) (\d*) (\d*) (\d*) (\d*)$', line)
|
||||
if not m:
|
||||
break
|
||||
opname = m.group(1).lower()
|
||||
--
|
||||
2.43.7
|
||||
|
||||
@ -1,575 +0,0 @@
|
||||
From ca8fbd2a827e3e7caa72331e003f93523c9f5241 Mon Sep 17 00:00:00 2001
|
||||
From: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
Date: Tue, 6 Jan 2026 05:53:08 +0000
|
||||
Subject: [PATCH] pcp-nfsiostat: Introduce PCP implementation of nfsiostat
|
||||
parser
|
||||
|
||||
Cherry-pick-commit: https://github.com/performancecopilot/pcp/commit/c0a0c53b57774dfac0b727e62472fb1ae6065540
|
||||
|
||||
Orabug: 38817091
|
||||
|
||||
Signed-off-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
---
|
||||
src/pcp/GNUmakefile | 1 +
|
||||
src/pcp/nfsiostat/GNUmakefile | 43 ++++
|
||||
src/pcp/nfsiostat/pcp-nfsiostat.1 | 132 +++++++++++
|
||||
src/pcp/nfsiostat/pcp-nfsiostat.py | 346 +++++++++++++++++++++++++++++
|
||||
4 files changed, 522 insertions(+)
|
||||
create mode 100644 src/pcp/nfsiostat/GNUmakefile
|
||||
create mode 100644 src/pcp/nfsiostat/pcp-nfsiostat.1
|
||||
create mode 100644 src/pcp/nfsiostat/pcp-nfsiostat.py
|
||||
|
||||
diff --git a/src/pcp/GNUmakefile b/src/pcp/GNUmakefile
|
||||
index da29269..2b74fc0 100644
|
||||
--- a/src/pcp/GNUmakefile
|
||||
+++ b/src/pcp/GNUmakefile
|
||||
@@ -30,6 +30,7 @@ SUBDIRS = \
|
||||
mpstat \
|
||||
netstat \
|
||||
numastat \
|
||||
+ nfsiostat \
|
||||
pidstat \
|
||||
ps \
|
||||
python \
|
||||
diff --git a/src/pcp/nfsiostat/GNUmakefile b/src/pcp/nfsiostat/GNUmakefile
|
||||
new file mode 100644
|
||||
index 0000000..d8c5356
|
||||
--- /dev/null
|
||||
+++ b/src/pcp/nfsiostat/GNUmakefile
|
||||
@@ -0,0 +1,43 @@
|
||||
+#
|
||||
+# Copyright (c) 2023 Oracle and/or its affiliates.
|
||||
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
+#
|
||||
+# This program is free software; you can redistribute it and/or modify it
|
||||
+# under the terms of the GNU General Public License as published by the
|
||||
+# Free Software Foundation; either version 2 of the License, or (at your
|
||||
+# option) any later version.
|
||||
+#
|
||||
+# This program is distributed in the hope that it will be useful, but
|
||||
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
+# for more details.
|
||||
+#
|
||||
+
|
||||
+TOPDIR = ../../..
|
||||
+include $(TOPDIR)/src/include/builddefs
|
||||
+
|
||||
+TARGET = pcp-nfsiostat
|
||||
+SCRIPT = $(TARGET).py
|
||||
+MAN_SECTION = 1
|
||||
+MAN_PAGES = $(TARGET).$(MAN_SECTION)
|
||||
+MAN_DEST = $(PCP_MAN_DIR)/man$(MAN_SECTION)
|
||||
+
|
||||
+default: $(SCRIPT) $(MAN_PAGES)
|
||||
+
|
||||
+include $(BUILDRULES)
|
||||
+
|
||||
+install: default
|
||||
+ifeq "$(HAVE_PYTHON)" "true"
|
||||
+ $(INSTALL) -m 755 $(SCRIPT) $(PCP_BINADM_DIR)/$(TARGET)
|
||||
+ @$(INSTALL_MAN)
|
||||
+endif
|
||||
+
|
||||
+default_pcp : default
|
||||
+
|
||||
+install_pcp : install
|
||||
+
|
||||
+check:: $(SCRIPT)
|
||||
+ $(PYLINT) $^
|
||||
+
|
||||
+check :: $(MAN_PAGES)
|
||||
+ $(MANLINT) $^
|
||||
diff --git a/src/pcp/nfsiostat/pcp-nfsiostat.1 b/src/pcp/nfsiostat/pcp-nfsiostat.1
|
||||
new file mode 100644
|
||||
index 0000000..b297285
|
||||
--- /dev/null
|
||||
+++ b/src/pcp/nfsiostat/pcp-nfsiostat.1
|
||||
@@ -0,0 +1,132 @@
|
||||
+'\"! tbl | mmdoc
|
||||
+'\"macro stdmacro
|
||||
+.\"
|
||||
+.\" Man page for pcp-nfsiostat
|
||||
+.\" Copyright (c) 2023 Oracle and/or its affiliates.
|
||||
+.\" DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
+.\"
|
||||
+.\" This program is free software; you can redistribute it and/or modify it
|
||||
+.\" under the terms of the GNU General Public License as published by the
|
||||
+.\" Free Software Foundation; either version 2 of the License, or (at your
|
||||
+.\" option) any later version.
|
||||
+.\"
|
||||
+.\" This program is distributed in the hope that it will be useful, but
|
||||
+.\" WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
+.\" or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
+.\" for more details.
|
||||
+.\"
|
||||
+
|
||||
+.TH PCP-NFSIOSTAT 1 "PCP" "Performance Co-Pilot"
|
||||
+
|
||||
+.SH NAME
|
||||
+\f3pcp-nfsiostat\f1 \- Emulate iostat for NFS mount points using /proc/self/mountstats
|
||||
+
|
||||
+.SH SYNOPSIS
|
||||
+\fBpcp\fP [\fBpcp options\fP] \fBnfsiostat\fP [\fB-s\fP \fBsamples\fP] [\fB-a\fP \fBarchive\fP] [\fB-Z\fP \fB--timezone\fP] [\fB-z\fP \fB--hostzone\fP] [\fB-V\fP \fBVersion\fP]
|
||||
+
|
||||
+.SH DESCRIPTION
|
||||
+The\fB pcp-nfsiostat \fPcommand reports client-side\fB Network File System (NFS) \fPI/O statistics for each mounted NFS filesystem. For every NFS mountpoint, the output consists of a summary section followed by detailed read and write statistics blocks. These statistics include operation rates, data throughput, latency, retransmissions, queueing delays, and error counts. By default,\fB pcp-nfsiostat \fPmonitors NFS mountpoints on the local host and reports live metrics collected via the \fB PCP NFS PMDA\fP. When an archive is specified, historical metrics are reported instead.
|
||||
+
|
||||
+.SH OUTPUT FORMAT
|
||||
+Statistics are reported per mounted NFS filesystem using the following layout:
|
||||
+
|
||||
+.nf
|
||||
+server:/export mounted on /mount/point:
|
||||
+
|
||||
+ ops/s rpc bklog
|
||||
+ 0.017 0.000
|
||||
+
|
||||
+read: ops/s kB/s kB/op retrans avg RTT (ms) avg exe (ms) avg queue (ms) errors
|
||||
+ ...
|
||||
+
|
||||
+write: ops/s kB/s kB/op retrans avg RTT (ms) avg exe (ms) avg queue (ms) errors
|
||||
+ ...
|
||||
+.fi
|
||||
+
|
||||
+.SH FIELD DESCRIPTIONS
|
||||
+.TP
|
||||
+.B ops/s
|
||||
+Number of NFS operations performed per second.
|
||||
+
|
||||
+.TP
|
||||
+.B rpc bklog
|
||||
+Average number of RPC requests waiting to be transmitted.
|
||||
+
|
||||
+.TP
|
||||
+.B kB/s
|
||||
+Kilobytes transferred per second.
|
||||
+
|
||||
+.TP
|
||||
+.B kB/op
|
||||
+Average number of kilobytes transferred per operation.
|
||||
+
|
||||
+.TP
|
||||
+.B retrans
|
||||
+Number of RPC retransmissions and the retransmission percentage.
|
||||
+
|
||||
+.TP
|
||||
+.B avg RTT (ms)
|
||||
+Average round-trip time in milliseconds for RPC requests.
|
||||
+
|
||||
+.TP
|
||||
+.B avg exe (ms)
|
||||
+Average execution time in milliseconds spent servicing requests on the server.
|
||||
+
|
||||
+.TP
|
||||
+.B avg queue (ms)
|
||||
+Average time in milliseconds spent waiting in the RPC transmission queue.
|
||||
+
|
||||
+.TP
|
||||
+.B errors
|
||||
+Number of failed operations and failure percentage.
|
||||
+
|
||||
+.SH OPTIONS
|
||||
+.TP
|
||||
+.BR \-a ", " \-\-archive " " I archive
|
||||
+Fetch NFS I/O statistics from the specified PCP archive.
|
||||
+
|
||||
+.TP
|
||||
+.BR \-s ", " \-\-samples " " I samples
|
||||
+Number of samples to collect before exiting.
|
||||
+
|
||||
+.TP
|
||||
+.BR \-z ", " \-\-hostzone
|
||||
+Set the reporting timezone to the local timezone of the metrics source.
|
||||
+
|
||||
+.TP
|
||||
+.BR \-Z ", " \-\-timezone " " I tz
|
||||
+Set the reporting timezone.
|
||||
+
|
||||
+.TP
|
||||
+.BR \-V ", " \-\-version
|
||||
+Display version information and exit.
|
||||
+
|
||||
+.TP
|
||||
+.BR \-? ", " \-\-help
|
||||
+Display usage information and exit.
|
||||
+
|
||||
+.SH NOTES
|
||||
+.B pcp-nfsiostat
|
||||
+reports client-side NFS statistics collected from the kernel and exposed
|
||||
+via the PCP NFS PMDA. The output format and metrics are similar to those
|
||||
+reported by the
|
||||
+.BR nfsiostat (1)
|
||||
+tool from the nfs-utils package.
|
||||
+
|
||||
+.SH PCP ENVIRONMENT
|
||||
+Environment variables with the prefix \fBPCP_\fP are used to parameterize
|
||||
+the file and directory names used by PCP.
|
||||
+On each installation, the
|
||||
+file \fI/etc/pcp.conf\fP contains the local values for these variables.
|
||||
+The \fB$PCP_CONF\fP variable may be used to specify an alternative
|
||||
+configuration file, as described in
|
||||
+.BR pcp.conf (5).
|
||||
+
|
||||
+For environment variables affecting PCP tools, see
|
||||
+.BR pmGetOptions (3).
|
||||
+
|
||||
+.SH SEE ALSO
|
||||
+.BR PCPIntro (1),
|
||||
+.BR pcp (1),
|
||||
+.BR nfsiostat (1),
|
||||
+.BR environ (7).
|
||||
diff --git a/src/pcp/nfsiostat/pcp-nfsiostat.py b/src/pcp/nfsiostat/pcp-nfsiostat.py
|
||||
new file mode 100644
|
||||
index 0000000..0b0376d
|
||||
--- /dev/null
|
||||
+++ b/src/pcp/nfsiostat/pcp-nfsiostat.py
|
||||
@@ -0,0 +1,346 @@
|
||||
+#!/usr/bin/pmpython
|
||||
+#
|
||||
+# Copyright (c) 2023 Oracle and/or its affiliates.
|
||||
+# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
+#
|
||||
+# This program is free software; you can redistribute it and/or modify it
|
||||
+# under the terms of the GNU General Public License as published by the
|
||||
+# Free Software Foundation; either version 2 of the License, or (at your
|
||||
+# option) any later version.
|
||||
+#
|
||||
+# This program is distributed in the hope that it will be useful, but
|
||||
+# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
+# for more details.
|
||||
+#
|
||||
+# pylint: disable=bad-whitespace,too-many-lines,bad-continuation
|
||||
+# pylint: disable=too-many-arguments,too-many-positional-arguments
|
||||
+# pylint: disable=redefined-outer-name,unnecessary-lambda
|
||||
+#
|
||||
+
|
||||
+import signal
|
||||
+import sys
|
||||
+import time
|
||||
+from pcp import pmapi, pmcc
|
||||
+from cpmapi import PM_CONTEXT_ARCHIVE
|
||||
+
|
||||
+SYS_METRICS= ["kernel.uname.sysname","kernel.uname.release",
|
||||
+ "kernel.uname.nodename","kernel.uname.machine","hinv.ncpu"]
|
||||
+NFSIOSTAT_METRICS = ["nfsclient.mountpoint","nfsclient.export","nfsclient.age",
|
||||
+ "nfsclient.xprt.sends","nfsclient.xprt.backlog_u","nfsclient.ops.read.ops",
|
||||
+ "nfsclient.ops.read.errors","nfsclient.ops.read.execute","nfsclient.ops.read.rtt",
|
||||
+ "nfsclient.ops.read.queue","nfsclient.ops.read.bytes_recv","nfsclient.ops.read.bytes_sent",
|
||||
+ "nfsclient.ops.read.ntrans","nfsclient.ops.write.ops","nfsclient.ops.write.errors",
|
||||
+ "nfsclient.ops.write.execute","nfsclient.ops.write.rtt","nfsclient.ops.write.queue",
|
||||
+ "nfsclient.ops.write.bytes_recv","nfsclient.ops.write.bytes_sent","nfsclient.ops.write.ntrans"]
|
||||
+ALL_METRICS = NFSIOSTAT_METRICS + SYS_METRICS
|
||||
+
|
||||
+def adjust_length(name):
|
||||
+ return name.ljust(25)
|
||||
+class ReportingMetricRepository:
|
||||
+
|
||||
+ def __init__(self,group):
|
||||
+ self.group=group
|
||||
+ self.current_cached_values = {}
|
||||
+
|
||||
+ def __sorted(self,data):
|
||||
+ return dict(sorted(data.items(), key=lambda item: item[0].lower()))
|
||||
+
|
||||
+ def __fetch_current_value(self,metric):
|
||||
+ val=dict(map(lambda x: (x[1], x[2]), self.group[metric].netValues))
|
||||
+ val=self.__sorted(val)
|
||||
+ return dict(val)
|
||||
+
|
||||
+ def current_value(self,metric):
|
||||
+ if not metric in self.group:
|
||||
+ return None
|
||||
+ if self.current_cached_values.get(metric) is None:
|
||||
+ first_value=self.__fetch_current_value(metric)
|
||||
+ self.current_cached_values[metric]=first_value
|
||||
+ return self.current_cached_values[metric]
|
||||
+
|
||||
+class NfsioStatUtil:
|
||||
+ def __init__(self,metrics_repository):
|
||||
+ self.__metric_repository=metrics_repository
|
||||
+ self.report=ReportingMetricRepository(self.__metric_repository)
|
||||
+
|
||||
+ def mount_point(self):
|
||||
+ return self.report.current_value('nfsclient.mountpoint')
|
||||
+
|
||||
+ def mount_share(self):
|
||||
+ return self.report.current_value('nfsclient.export')
|
||||
+
|
||||
+ def mount_share_keys(self):
|
||||
+ data = self.report.current_value('nfsclient.export')
|
||||
+ return data.keys()
|
||||
+
|
||||
+ def sample_time(self):
|
||||
+ return self.report.current_value('nfsclient.age')
|
||||
+
|
||||
+ def xprt_sends(self):
|
||||
+ return self.report.current_value('nfsclient.xprt.sends')
|
||||
+
|
||||
+ def xprt_backlog(self):
|
||||
+ return self.report.current_value('nfsclient.xprt.backlog_u')
|
||||
+
|
||||
+ def readops(self):
|
||||
+ return self.report.current_value('nfsclient.ops.read.ops')
|
||||
+
|
||||
+ def readerrors(self):
|
||||
+ return self.report.current_value('nfsclient.ops.read.errors')
|
||||
+
|
||||
+ def readexecute(self):
|
||||
+ return self.report.current_value('nfsclient.ops.read.execute')
|
||||
+
|
||||
+ def readrtt(self):
|
||||
+ return self.report.current_value('nfsclient.ops.read.rtt')
|
||||
+
|
||||
+ def readqueue(self):
|
||||
+ return self.report.current_value('nfsclient.ops.read.queue')
|
||||
+
|
||||
+ def readbytesrecv(self):
|
||||
+ return self.report.current_value('nfsclient.ops.read.bytes_recv')
|
||||
+
|
||||
+ def readbytessent(self):
|
||||
+ return self.report.current_value('nfsclient.ops.read.bytes_sent')
|
||||
+
|
||||
+ def readntrans(self):
|
||||
+ return self.report.current_value('nfsclient.ops.read.ntrans')
|
||||
+
|
||||
+ def writeops(self):
|
||||
+ return self.report.current_value('nfsclient.ops.write.ops')
|
||||
+
|
||||
+ def writeerrors(self):
|
||||
+ return self.report.current_value('nfsclient.ops.write.errors')
|
||||
+
|
||||
+ def writeexecute(self):
|
||||
+ return self.report.current_value('nfsclient.ops.write.execute')
|
||||
+
|
||||
+ def writertt(self):
|
||||
+ return self.report.current_value('nfsclient.ops.write.rtt')
|
||||
+
|
||||
+ def writequeue(self):
|
||||
+ return self.report.current_value('nfsclient.ops.write.queue')
|
||||
+
|
||||
+ def writebytesrecv(self):
|
||||
+ return self.report.current_value('nfsclient.ops.write.bytes_recv')
|
||||
+
|
||||
+ def writebytessent(self):
|
||||
+ return self.report.current_value('nfsclient.ops.write.bytes_sent')
|
||||
+
|
||||
+ def writentrans(self):
|
||||
+ return self.report.current_value('nfsclient.ops.write.ntrans')
|
||||
+
|
||||
+class NfsiostatReport(pmcc.MetricGroupPrinter):
|
||||
+ def __init__(self,opts,group):
|
||||
+ self.opts = opts
|
||||
+ self.group = group
|
||||
+ self.samples = opts.samples
|
||||
+ self.context = opts.context
|
||||
+
|
||||
+ def __get_ncpu(self, group):
|
||||
+ return group['hinv.ncpu'].netValues[0][2]
|
||||
+
|
||||
+ def __print_machine_info(self, context):
|
||||
+ timestamp = self.group.pmLocaltime(context.timestamp.tv_sec)
|
||||
+ # Please check strftime(3) for different formatting options.
|
||||
+ # Also check TZ and LC_TIME environment variables for more
|
||||
+ # information on how to override the default formatting of
|
||||
+ # the date display in the header
|
||||
+ time_string = time.strftime("%m/%d/%Y %H:%M:%S", timestamp.struct_time())
|
||||
+ header_string = ''
|
||||
+ header_string += context['kernel.uname.sysname'].netValues[0][2] + ' '
|
||||
+ header_string += context['kernel.uname.release'].netValues[0][2] + ' '
|
||||
+ header_string += '(' + context['kernel.uname.nodename'].netValues[0][2] + ') '
|
||||
+ header_string += time_string + ' '
|
||||
+ header_string += context['kernel.uname.machine'].netValues[0][2] + ' '
|
||||
+ print("%s (%s CPU)" % (header_string, self.__get_ncpu(context)))
|
||||
+
|
||||
+ def __print_values(self,timestamp, nfsstatus):
|
||||
+ n_shares = nfsstatus.mount_share_keys()
|
||||
+ mountshare = nfsstatus.mount_share()
|
||||
+ mountpoint = nfsstatus.mount_point()
|
||||
+ sampletime = nfsstatus.sample_time()
|
||||
+ sends = nfsstatus.xprt_sends()
|
||||
+ backlog = nfsstatus.xprt_backlog()
|
||||
+ readops = nfsstatus.readops()
|
||||
+ readerrors = nfsstatus.readerrors()
|
||||
+ readexecute = nfsstatus.readexecute()
|
||||
+ readrtt = nfsstatus.readrtt()
|
||||
+ readqueue = nfsstatus.readqueue()
|
||||
+ readbytesrecv = nfsstatus.readbytesrecv()
|
||||
+ readbytessent = nfsstatus.readbytessent()
|
||||
+ readntrans = nfsstatus.readntrans()
|
||||
+ writeops = nfsstatus.writeops()
|
||||
+ writeerrors = nfsstatus.writeerrors()
|
||||
+ writeexecute = nfsstatus.writeexecute()
|
||||
+ writertt = nfsstatus.writertt()
|
||||
+ writequeue = nfsstatus.writequeue()
|
||||
+ writebytesrecv = nfsstatus.writebytesrecv()
|
||||
+ writebytessent = nfsstatus.writebytessent()
|
||||
+ writentrans = nfsstatus.writentrans()
|
||||
+
|
||||
+ print("%-18s:%s"%("Timestamp", timestamp))
|
||||
+ print()
|
||||
+
|
||||
+ for name in n_shares:
|
||||
+ # read
|
||||
+ r_kilobytes = (readbytessent[name] + readbytesrecv[name]) / 1024
|
||||
+ if sampletime[name] > 0:
|
||||
+ ops_per_sample = sends[name] / sampletime[name]
|
||||
+ ops_per_sample_read = readops[name] / sampletime[name]
|
||||
+ r_kilobytes_per_sample = r_kilobytes / sampletime[name]
|
||||
+ else:
|
||||
+ ops_per_sample = 0.0
|
||||
+ ops_per_sample_read = 0.0
|
||||
+ r_kilobytes_per_sample = 0.0
|
||||
+
|
||||
+ r_retrans = readntrans[name] - readops[name]
|
||||
+ if readops[name] > 0:
|
||||
+ r_kilobytes_per_op = r_kilobytes / readops[name]
|
||||
+ r_retrans_percent = (r_retrans * 100) / readops[name]
|
||||
+ r_rtt_per_op = readrtt[name] / readops[name]
|
||||
+ r_exe_per_op = readexecute[name] / readops[name]
|
||||
+ r_queued_for_per_op = readqueue[name] / readops[name]
|
||||
+ r_errs_percent = (readerrors[name] * 100) / readops[name]
|
||||
+ else:
|
||||
+ r_kilobytes_per_op = 0.0
|
||||
+ r_retrans_percent = 0.0
|
||||
+ r_rtt_per_op = 0.0
|
||||
+ r_exe_per_op = 0.0
|
||||
+ r_queued_for_per_op = 0.0
|
||||
+ r_errs_percent = 0.0
|
||||
+
|
||||
+ # write
|
||||
+ w_kilobytes = (writebytessent[name] + writebytesrecv[name]) / 1024
|
||||
+ if sampletime[name] > 0:
|
||||
+ ops_per_sample_write = writeops[name] / sampletime[name]
|
||||
+ w_kilobytes_per_sample = w_kilobytes / sampletime[name]
|
||||
+ else:
|
||||
+ ops_per_sample_write = 0.0
|
||||
+ w_kilobytes_per_sample = 0.0
|
||||
+
|
||||
+ w_retrans = writentrans[name] - writeops[name]
|
||||
+ if writeops[name] > 0:
|
||||
+ w_kilobytes_per_op = w_kilobytes / writeops[name]
|
||||
+ w_retrans_percent = (w_retrans * 100) / writeops[name]
|
||||
+ w_rtt_per_op = writertt[name] / writeops[name]
|
||||
+ w_exe_per_op = writeexecute[name] / writeops[name]
|
||||
+ w_queued_for_per_op = writequeue[name] / writeops[name]
|
||||
+ w_errs_percent = (writeerrors[name] * 100) / writeops[name]
|
||||
+ else:
|
||||
+ w_kilobytes_per_op = 0.0
|
||||
+ w_retrans_percent = 0.0
|
||||
+ w_rtt_per_op = 0.0
|
||||
+ w_exe_per_op = 0.0
|
||||
+ w_queued_for_per_op = 0.0
|
||||
+ w_errs_percent = 0.0
|
||||
+
|
||||
+ print(f"{mountshare[name]} mounted on {mountpoint[name]}:")
|
||||
+
|
||||
+ print(f"{'':14}ops/s{'':7}rpc bklog")
|
||||
+ print(f"{ops_per_sample:19.3f}{backlog[name]:16.3f}")
|
||||
+ print()
|
||||
+ print(
|
||||
+ "read: "
|
||||
+ "ops/s kB/s kB/op retrans "
|
||||
+ "avg RTT (ms) avg exe (ms) avg queue (ms) errors"
|
||||
+ )
|
||||
+ print(
|
||||
+ f"{'':19}"
|
||||
+ f"{ops_per_sample_read:5.3f}"
|
||||
+ f"{r_kilobytes_per_sample:12.3f}"
|
||||
+ f"{r_kilobytes_per_op:13.3f} "
|
||||
+ f"{int(r_retrans):2d} ({r_retrans_percent:2.1f}%)"
|
||||
+ f"{r_rtt_per_op:15.3f}"
|
||||
+ f"{r_exe_per_op:15.3f}"
|
||||
+ f"{r_queued_for_per_op:17.3f} "
|
||||
+ f"{int(readerrors[name]):4d} ({r_errs_percent:2.1f}%)"
|
||||
+ )
|
||||
+
|
||||
+ print(
|
||||
+ "write: "
|
||||
+ "ops/s kB/s kB/op retrans "
|
||||
+ "avg RTT (ms) avg exe (ms) avg queue (ms) errors"
|
||||
+ )
|
||||
+
|
||||
+ print(
|
||||
+ f"{'':19}"
|
||||
+ f"{ops_per_sample_write:5.3f}"
|
||||
+ f"{w_kilobytes_per_sample:12.3f}"
|
||||
+ f"{w_kilobytes_per_op:13.3f} "
|
||||
+ f"{int(w_retrans):2d} ({w_retrans_percent:2.1f}%)"
|
||||
+ f"{w_rtt_per_op:15.3f}"
|
||||
+ f"{w_exe_per_op:15.3f}"
|
||||
+ f"{w_queued_for_per_op:17.3f} "
|
||||
+ f"{int(writeerrors[name]):4d} ({w_errs_percent:2.1f}%)"
|
||||
+ )
|
||||
+ print()
|
||||
+
|
||||
+ def print_report(self,group,timestamp, manager_nfsiostat):
|
||||
+ def __print_nfs_status():
|
||||
+ nfsstatus = NfsioStatUtil(manager_nfsiostat)
|
||||
+ if nfsstatus.mount_share():
|
||||
+ try:
|
||||
+ self.__print_machine_info(group)
|
||||
+ self.__print_values(timestamp, nfsstatus)
|
||||
+ except IndexError:
|
||||
+ print("Incorrect machine info due to some missing metrics")
|
||||
+ return
|
||||
+ else:
|
||||
+ pass
|
||||
+
|
||||
+ if self.context != PM_CONTEXT_ARCHIVE and self.samples is None:
|
||||
+ __print_nfs_status()
|
||||
+ sys.exit(0)
|
||||
+ elif self.context == PM_CONTEXT_ARCHIVE and self.samples is None:
|
||||
+ __print_nfs_status()
|
||||
+ elif self.samples >=1:
|
||||
+ __print_nfs_status()
|
||||
+ self.samples-=1
|
||||
+ else:
|
||||
+ pass
|
||||
+
|
||||
+ def report(self, manager):
|
||||
+ group = manager["sysinfo"]
|
||||
+ self.samples = self.opts.pmGetOptionSamples()
|
||||
+ t_s = group.contextCache.pmLocaltime(int(group.timestamp))
|
||||
+ timestamp = time.strftime(NfsiostatOptions.timefmt, t_s.struct_time())
|
||||
+ self.print_report(group,timestamp,manager['nfsiostat'])
|
||||
+
|
||||
+class NfsiostatOptions(pmapi.pmOptions):
|
||||
+ timefmt = "%m/%d/%Y %H:%M:%S"
|
||||
+ def __init__(self):
|
||||
+ pmapi.pmOptions.__init__(self, "a:s:Z:zV?")
|
||||
+ self.pmSetLongOptionHeader("General options")
|
||||
+ self.pmSetLongOptionHostZone()
|
||||
+ self.pmSetLongOptionTimeZone()
|
||||
+ self.pmSetLongOptionHelp()
|
||||
+ self.pmSetLongOptionSamples()
|
||||
+ self.pmSetLongOptionVersion()
|
||||
+ self.samples=None
|
||||
+ self.context=None
|
||||
+
|
||||
+if __name__ == '__main__':
|
||||
+ try:
|
||||
+ opts = NfsiostatOptions()
|
||||
+ mngr = pmcc.MetricGroupManager.builder(opts,sys.argv)
|
||||
+ opts.context=mngr.type
|
||||
+ missing = mngr.checkMissingMetrics(ALL_METRICS)
|
||||
+ if missing is not None:
|
||||
+ sys.stderr.write('Error: not all required metrics are available\nMissing %s\n' % missing)
|
||||
+ sys.exit(1)
|
||||
+ mngr["nfsiostat"] = ALL_METRICS
|
||||
+ mngr["sysinfo"] = SYS_METRICS
|
||||
+ mngr.printer = NfsiostatReport(opts,mngr)
|
||||
+ sts = mngr.run()
|
||||
+ sys.exit(sts)
|
||||
+ except pmapi.pmErr as error:
|
||||
+ sys.stderr.write('%s\n' % (error.message()))
|
||||
+ except pmapi.pmUsageErr as usage:
|
||||
+ usage.message()
|
||||
+ sys.exit(1)
|
||||
+ except IOError:
|
||||
+ signal.signal(signal.SIGPIPE, signal.SIG_DFL)
|
||||
+ except KeyboardInterrupt:
|
||||
+ pass
|
||||
--
|
||||
2.43.7
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,366 +0,0 @@
|
||||
From a60882f460d54c039c905aff24962e107ee59c93 Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Tue, 3 Mar 2026 16:13:45 +0530
|
||||
Subject: [PATCH OL8 1031/1032] pmlogger_daily: add disk usage limit option and
|
||||
purge logic
|
||||
|
||||
Introduce -d/--disk option to set max disk usage per pmlogger instance.
|
||||
Implement logic to check total archive size and purge oldest files when limit is exceeded.
|
||||
|
||||
upstream ref:- 7b7c38c16c6904960ede400efa5225732935b830
|
||||
|
||||
[Orabug: 38991685]
|
||||
Signed-off-by: sagar sagar <sagar.sagar@oracle.com>
|
||||
---
|
||||
src/pmlogger/pmlogger_daily.sh | 193 ++++++++++++++++++++++++++++++++-
|
||||
src/pmlogger/utilproc.sh | 83 ++++++++++++++
|
||||
2 files changed, 274 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/src/pmlogger/pmlogger_daily.sh b/src/pmlogger/pmlogger_daily.sh
|
||||
index 27b1550..53e5eab 100755
|
||||
--- a/src/pmlogger/pmlogger_daily.sh
|
||||
+++ b/src/pmlogger/pmlogger_daily.sh
|
||||
@@ -261,6 +261,7 @@ Options:
|
||||
-E,--expunge expunge metrics with metadata inconsistencies when merging archives
|
||||
-f,--force force actions (intended for QA, not production)
|
||||
-k=TIME,--discard=TIME remove archives after TIME (format DD[:HH[:MM]])
|
||||
+ -d=fssize,--disk=fssize set maximum disk usage for archives for each pmlogger instance
|
||||
-K compress, but no other changes
|
||||
-l=FILE,--logfile=FILE send important diagnostic messages to FILE
|
||||
-m=ADDRs,--mail=ADDRs send daily NOTICES entries to email addresses
|
||||
@@ -302,6 +303,8 @@ MFLAG=false
|
||||
EXPUNGE=""
|
||||
FORCE=false
|
||||
KILL=pmsignal
|
||||
+SPACELIMIT_CMDLINE=""
|
||||
+SPACELIMIT_DEFAULT="unlimited"
|
||||
|
||||
ARGS=`pmgetopt --progname=$prog --config=$tmp/usage -- "$@"`
|
||||
[ $? != 0 ] && exit 1
|
||||
@@ -315,6 +318,24 @@ do
|
||||
CONTROLDIR="$2.d"
|
||||
shift
|
||||
;;
|
||||
+ -d) SPACELIMIT_CMDLINE="$2"
|
||||
+ shift
|
||||
+ if [ -n "$PCP_SPACELIMIT" -a "$PCP_SPACELIMIT" != "$SPACELIMIT_CMDLINE" ]
|
||||
+ then
|
||||
+ echo "Warning: -d value ($SPACELIMIT_CMDLINE) ignored because \$PCP_SPACELIMIT ($PCP_SPACELIMIT) set in environment"
|
||||
+ SPACELIMIT_CMDLINE=""
|
||||
+ continue
|
||||
+ fi
|
||||
+ if [ "$SPACELIMIT_CMDLINE" != unlimited ]
|
||||
+ then
|
||||
+ if ! _convert_to_kb "$SPACELIMIT_CMDLINE" >/dev/null
|
||||
+ then
|
||||
+ echo "Error: -d value ($SPACELIMIT_CMDLINE) not valid"
|
||||
+ $NOERROR || status=1
|
||||
+ exit
|
||||
+ fi
|
||||
+ fi
|
||||
+ ;;
|
||||
-E) EXPUNGE="-E"
|
||||
;;
|
||||
-f) FORCE=true
|
||||
@@ -448,7 +469,7 @@ do
|
||||
COMPRESSREGEX_CMDLINE=""
|
||||
continue
|
||||
fi
|
||||
- ;;
|
||||
+ ;;
|
||||
--) shift
|
||||
break
|
||||
;;
|
||||
@@ -610,6 +631,43 @@ _warning()
|
||||
echo "Warning: $@"
|
||||
}
|
||||
|
||||
+# Given a list of candidate directories, calculate the total
|
||||
+# size below them in Kbytes
|
||||
+#
|
||||
+_calculate_total_size()
|
||||
+{
|
||||
+ du -sk "$@" 2>$tmp/cts_err \
|
||||
+ | awk >$tmp/cts_out '
|
||||
+BEGIN { kb = 0 }
|
||||
+ { kb += $1 }
|
||||
+END { print kb }' >$tmp/cts_out
|
||||
+ if [ -s $tmp/cts_err ] && $VERY_VERBOSE
|
||||
+ then
|
||||
+ echo >&2 "Warning: _calculate_total_size: du -sk $@ produced errors ..."
|
||||
+ cat >&2 $tmp/cts_err
|
||||
+ fi
|
||||
+ $VERY_VERBOSE && echo >&2 "Info: _calculate_total_size -> `cat $tmp/cts_out` Kbytes"
|
||||
+ cat $tmp/cts_out
|
||||
+}
|
||||
+
|
||||
+# Calculate the total size of the archive with basename $1
|
||||
+#
|
||||
+_calculate_archive_size()
|
||||
+{
|
||||
+ du -sk "$1".* 2>$tmp/cts_err \
|
||||
+ | awk >$tmp/cts_out '
|
||||
+BEGIN { kb = 0 }
|
||||
+ { kb += $1 }
|
||||
+END { print kb }' >$tmp/cts_out
|
||||
+ if [ -s $tmp/cts_err ] && $VERY_VERBOSE
|
||||
+ then
|
||||
+ echo >&2 "Warning: _calculate_archive_size: du -sk $1.* produced errors ..."
|
||||
+ cat >&2 $tmp/cts_err
|
||||
+ fi
|
||||
+ $VERY_VERBOSE && echo >&2 "Info: _calculate_archive_size $1 -> `cat $tmp/cts_out` Kbytes"
|
||||
+ cat $tmp/cts_out
|
||||
+}
|
||||
+
|
||||
_skipping()
|
||||
{
|
||||
echo "$prog: Warning: $@"
|
||||
@@ -713,6 +771,86 @@ _get_primary_logger_pid()
|
||||
echo "$pid"
|
||||
}
|
||||
|
||||
+# Check disk space allocation for one pmlogger instance, and if more
|
||||
+# than $SPACELIMIT Kbytes, purge files to try and get the space
|
||||
+# allocated to be not more than $SPACELIMIT Kbytes.
|
||||
+#
|
||||
+# Oldest files are purged first and the most recent archives for today
|
||||
+# (including those required by active pmlogger (1) processes are
|
||||
+# never deleted.
|
||||
+#
|
||||
+# On entry, $find_dirs is a list of one or more directories holding
|
||||
+# archives for a pmlogger instance, set in _callback_log_control(),
|
||||
+# and $host is the host name field from the control line, set in
|
||||
+# _parse_log_control().
|
||||
+#
|
||||
+_do_purge()
|
||||
+{
|
||||
+ SPACELIMIT="$PCP_SPACELIMIT"
|
||||
+ [ -z "$SPACELIMIT" ] && SPACELIMIT="$SPACELIMIT_CMDLINE"
|
||||
+ [ -z "$SPACELIMIT" ] && SPACELIMIT="$SPACELIMIT_DEFAULT"
|
||||
+ [ "$SPACELIMIT" = unlimited ] && return
|
||||
+ if ! _convert_to_kb "$SPACELIMIT" >$tmp/tmp
|
||||
+ then
|
||||
+ _warning "skipping purging because of invalid space limit"
|
||||
+ return
|
||||
+ fi
|
||||
+ SPACELIMIT=`cat $tmp/tmp`
|
||||
+ $VERY_VERBOSE && echo >&2 "SPACELIMIT=$SPACELIMIT"
|
||||
+ __total_size=`_calculate_total_size $find_dirs`
|
||||
+ if [ "$__total_size" -le "$SPACELIMIT" ]
|
||||
+ then
|
||||
+ $VERBOSE && echo "Info: No purging required for $host, archives ($__total_size Kbytes) <= size limit ($SPACELIMIT Kbytes)"
|
||||
+ return
|
||||
+ fi
|
||||
+ $VERBOSE && echo "Info: archives for $host ($__total_size Kbytes) >= size limit ($SPACELIMIT Kbytes)"
|
||||
+
|
||||
+ # algorithm to find archive basenames borrowed from _do_merge()
|
||||
+ # output is in this format ...
|
||||
+ # <directory>|<archive>
|
||||
+ #
|
||||
+ TODAY=`date +%Y%m%d`
|
||||
+ find $find_dirs -maxdepth 1 -type f \
|
||||
+ | sed -n \
|
||||
+ -e '/\(.*\)\/\([12][0-9][0-9][0-9][0-1][0-9][0-3][0-9]\)\(\.meta.*\)/s//\1|\2/p' \
|
||||
+ -e '/\(.*\)\/\([12][0-9][0-9][0-9][0-1][0-9][0-3][0-9]\)\(\.[0-2][0-9].[0-5][0-9]\)\(\.meta.*\)/s//\1|\2\3/p' \
|
||||
+ -e '/\(.*\)\/\([12][0-9][0-9][0-9][0-1][0-9][0-3][0-9]\)\(\.[0-2][0-9].[0-5][0-9]-[0-9][0-9]\)\(\.meta.*\)/s//\1|\2\3/p' \
|
||||
+ -e '/\(.*\)\/\([0-9][0-9][0-1][0-9][0-3][0-9]\)\(\.meta.*\)/s//\1|\2/p' \
|
||||
+ -e '/\(.*\)\/\([0-9][0-9][0-1][0-9][0-3][0-9]\)\(\.[0-2][0-9].[0-5][0-9]\)\(\.meta.*\)/s//\1|\2\3/p' \
|
||||
+ -e '/\(.*\)\/\([0-9][0-9][0-1][0-9][0-3][0-9]\)\(\.[0-2][0-9].[0-5][0-9]-[0-9][0-9]\)\(\.meta.*\)/s//\1|\2\3/p' \
|
||||
+ | sort -t'|' -n -k2,2 \
|
||||
+ | $PCP_AWK_PROG -F'|' '
|
||||
+$2 == "'$TODAY'" { next }
|
||||
+$2 ~ /^'$TODAY'/ { next }
|
||||
+ { print }' >$tmp/purge_candidates
|
||||
+ if [ ! -s $tmp/purge_candidates ]
|
||||
+ then
|
||||
+ $VERBOSE && echo "Info: No candidates to purge."
|
||||
+ return
|
||||
+ fi
|
||||
+
|
||||
+ sed -e 's/|/ /' <$tmp/purge_candidates \
|
||||
+ | while read __dir __file
|
||||
+ do
|
||||
+ [ "$__dir" != "." ] && __file="$__dir/$__file"
|
||||
+ __arch_size=`_calculate_archive_size "$__file"`
|
||||
+ if $SHOWME
|
||||
+ then
|
||||
+ echo "+ rm `echo $__file.**`"
|
||||
+ __total_size=`expr $__total_size - $__arch_size`
|
||||
+ else
|
||||
+ if rm "$__file".*
|
||||
+ then
|
||||
+ $VERBOSE && echo "Info: purge $__file, reclaims $__arch_size Kbytes"
|
||||
+ __total_size=`expr $__total_size - $__arch_size`
|
||||
+ else
|
||||
+ $VERBOSE && echo "Warning: rm $__file.* failed"
|
||||
+ fi
|
||||
+ fi
|
||||
+ [ "$__total_size" -le "$SPACELIMIT" ] && break
|
||||
+ done
|
||||
+}
|
||||
+
|
||||
# mails out any entries for the previous 24hrs from the PCP notices file
|
||||
#
|
||||
if [ ! -z "$MAILME" ]
|
||||
@@ -999,7 +1137,50 @@ s/^\([A-Za-z][A-Za-z0-9_]*\)=/export \1; \1=/p
|
||||
fi
|
||||
fi
|
||||
;;
|
||||
-
|
||||
+ 'export PCP_SPACELIMIT;'*)
|
||||
+ _old_value="$PCP_SPACELIMIT"
|
||||
+ _check=`echo "$_cmd" | sed -e 's/.*=//' -e 's/ *$//'`
|
||||
+ if [ -n "$_check" ]
|
||||
+ then
|
||||
+ if [ "$_check" = unlimited ]
|
||||
+ then
|
||||
+ # no conversion
|
||||
+ :
|
||||
+ else
|
||||
+ # check syntax & convert to canonical Kbytes
|
||||
+ #
|
||||
+ _kb=`_convert_to_kb "$_check"`
|
||||
+ if [ $? != 0 ]
|
||||
+ then
|
||||
+ _warning "\$PCP_SPACELIMIT value ($_check) is invalid. Must be a positive integer and a unit (e.g. 100M)"
|
||||
+ _cmd=''
|
||||
+ else
|
||||
+ $SHOWME && echo "+ $_cmd (normalized to $_kb Kbytes)"
|
||||
+ # need to put back the "K" units here
|
||||
+ # # because it will get re-processed by
|
||||
+ # _convert_to_kb() later
|
||||
+ #
|
||||
+ _cmd=`echo "$_cmd" | sed -e "s/=.*/=${_kb}K/"`
|
||||
+ fi
|
||||
+ fi
|
||||
+ if [ -n "$_cmd" ]
|
||||
+ then
|
||||
+ echo eval $_cmd >>$tmp/_cmd
|
||||
+ eval $_cmd
|
||||
+ if [ -n "$_old_value" -a "$_old_value" != "$PCP_SPACELIMIT" ]
|
||||
+ then
|
||||
+ _warning "\$PCP_SPACELIMIT ($PCP_SPACELIMIT) reset from control file, previous value ($_old_value) ignored"
|
||||
+ fi
|
||||
+ if [ -n "$PCP_SPACELIMIT" -a -n "$SPACELIMIT_CMDLINE" -a "$PCP_SPACELIMIT" != "$SPACELIMIT_CMDLINE" ]
|
||||
+ then
|
||||
+ _warning "\$PCP_SPACELIMIT ($PCP_SPACELIMIT) reset from control file, -d value ($SPACELIMIT_CMDLINE) ignored"
|
||||
+ SPACELIMIT_CMDLINE=""
|
||||
+ fi
|
||||
+ fi
|
||||
+ else
|
||||
+ _warning "\$PCP_SPACELIMIT from control file missing a value, will be ignored"
|
||||
+ fi
|
||||
+ ;;
|
||||
'export PCP_COMPRESSREGEX;'*)
|
||||
old_value="$PCP_COMPRESSREGEX"
|
||||
$SHOWME && echo "+ $cmd"
|
||||
@@ -1671,6 +1852,14 @@ p
|
||||
$VERY_VERBOSE && echo >&2 "Warning: no trace files found to cull"
|
||||
fi
|
||||
fi
|
||||
+
|
||||
+ # if space limit specified, potentially purge old archives if
|
||||
+ # space limit exceeded
|
||||
+ #
|
||||
+ if [ -n "$PCP_SPACELIMIT" -o -n "$SPACELIMIT_CMDLINE" ]
|
||||
+ then
|
||||
+ _do_purge
|
||||
+ fi
|
||||
|
||||
_unlock "$dir"
|
||||
done
|
||||
diff --git a/src/pmlogger/utilproc.sh b/src/pmlogger/utilproc.sh
|
||||
index 168d040..8c77b37 100644
|
||||
--- a/src/pmlogger/utilproc.sh
|
||||
+++ b/src/pmlogger/utilproc.sh
|
||||
@@ -152,3 +152,86 @@ END { exit sts }'
|
||||
fi
|
||||
return $?
|
||||
}
|
||||
+
|
||||
+# Converts a size string like 10G, 100M, 100K to integer KBytes for limit
|
||||
+# checks.
|
||||
+# Usage: _convert_to_kb size_string
|
||||
+# Outputs: integer KBytes on stdout, returns 0 if OK, 1 if error
|
||||
+_convert_to_kb()
|
||||
+{
|
||||
+ __input="$1"
|
||||
+ __num=
|
||||
+ __unit=
|
||||
+ __kb=
|
||||
+ if [ -z "$__input" ]
|
||||
+ then
|
||||
+ echo "Error: _convert_to_kb(): missing argument" >&2
|
||||
+ return 1
|
||||
+ fi
|
||||
+ __num=`echo "$__input" | sed -E 's/^([0-9]+)\s*([a-zA-Z]*)$/\1/'`
|
||||
+ case "$__num"
|
||||
+ in
|
||||
+ "" )
|
||||
+ echo "Error: _convert_to_kb(): argument '$__input' does not start with a number" >&2
|
||||
+ return 1
|
||||
+ ;;
|
||||
+ *[!0-9]* )
|
||||
+ echo "Error: _convert_to_kb(): argument '$__input' has a non-numeric value" >&2
|
||||
+ return 1
|
||||
+ ;;
|
||||
+ 0 )
|
||||
+ echo "Error: _convert_to_kb(): argument '$__input' resolves to zero (not allowed)" >&2
|
||||
+ return 1
|
||||
+ ;;
|
||||
+ esac
|
||||
+ __unit=`echo "$__input" | sed -E 's/^([0-9]+)\s*([a-zA-Z]*)$/\2/'`
|
||||
+ case "$__unit"
|
||||
+ in
|
||||
+ G|g|M|m|K|k)
|
||||
+ ;;
|
||||
+ '')
|
||||
+ echo "Error: _convert_to_kb(): missing unit after '$__num'" >&2
|
||||
+ return 1
|
||||
+ ;;
|
||||
+ *)
|
||||
+ echo "Error: _convert_to_kb(): invalid unit '$__unit'" >&2
|
||||
+ return 1
|
||||
+ ;;
|
||||
+ esac
|
||||
+
|
||||
+ MAX_INT32=2147483647
|
||||
+
|
||||
+ case "$__unit"
|
||||
+ in
|
||||
+ G|g)
|
||||
+ # Check before multiplying
|
||||
+ __max=`expr $MAX_INT32 / \( 1024 \* 1024 \)`
|
||||
+ if [ $__num -gt $__max ]
|
||||
+ then
|
||||
+ echo "Error: overflow, $__num Gbytes too large for Kbytes in a 32-bit signed int" >&2
|
||||
+ return 1
|
||||
+ fi
|
||||
+ __kb=`expr $__num \* 1024 \* 1024`
|
||||
+ ;;
|
||||
+ M|m)
|
||||
+ # Check before multiplying
|
||||
+ __max=`expr $MAX_INT32 / 1024`
|
||||
+ if [ $__num -gt $__max ]
|
||||
+ then
|
||||
+ echo "Error: overflow, $__num Mbytes too large for Kbytes in a 32-bit signed int" >&2
|
||||
+ return 1
|
||||
+ fi
|
||||
+ __kb=`expr $__num \* 1024`
|
||||
+ ;;
|
||||
+ K|k)
|
||||
+ if [ $__num -gt $MAX_INT32 ]
|
||||
+ then
|
||||
+ echo "Error: overflow, $__num Kbytes too large for 32-bit signed int" >&2
|
||||
+ return 1
|
||||
+ fi
|
||||
+ __kb=$__num
|
||||
+ ;;
|
||||
+ esac
|
||||
+ echo "$__kb"
|
||||
+ return 0
|
||||
+}
|
||||
\ No newline at end of file
|
||||
--
|
||||
2.43.7
|
||||
|
||||
@ -1,328 +0,0 @@
|
||||
From 28b612d8743b36429df222a4baa5cb48dcaf8bc7 Mon Sep 17 00:00:00 2001
|
||||
From: sagar sagar <sagar.sagar@oracle.com>
|
||||
Date: Tue, 3 Mar 2026 16:15:10 +0530
|
||||
Subject: [PATCH OL8 1032/1032] pcp-system-tools: restore backward
|
||||
compatibility with older pmapi versions
|
||||
|
||||
Commit 0a37ed0 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
|
||||
|
||||
- optimized code in pcp-ps for dynamic sorting
|
||||
- fixed sorting colum indexes in -u case
|
||||
|
||||
[Orabug: 38991685]
|
||||
Signed-off-by: Sagar Sagar <sagar.sagar@oracle.com>
|
||||
---
|
||||
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 | 123 ++++++++++++-------------------
|
||||
src/pcp/tapestat/pcp-tapestat.py | 15 +++-
|
||||
5 files changed, 95 insertions(+), 84 deletions(-)
|
||||
|
||||
diff --git a/src/pcp/iostat/pcp-iostat.py b/src/pcp/iostat/pcp-iostat.py
|
||||
index 6b7d32d..80799a2 100755
|
||||
--- a/src/pcp/iostat/pcp-iostat.py
|
||||
+++ b/src/pcp/iostat/pcp-iostat.py
|
||||
@@ -55,9 +55,18 @@ class IostatReport(pmcc.MetricGroupPrinter):
|
||||
Hcount = 0
|
||||
def timeStampDelta(self, group):
|
||||
s = group.timestamp.tv_sec - group.prevTimestamp.tv_sec
|
||||
- u = group.timestamp.tv_usec - group.prevTimestamp.tv_usec
|
||||
- # u may be negative here, calculation is still correct.
|
||||
- return s + u / 1000000.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 / 1000000000.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 / 1000000.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 09a8182..9ffee11 100755
|
||||
--- a/src/pcp/mpstat/pcp-mpstat.py
|
||||
+++ b/src/pcp/mpstat/pcp-mpstat.py
|
||||
@@ -503,8 +503,17 @@ class MpstatReport(pmcc.MetricGroupPrinter):
|
||||
|
||||
def timeStampDelta(self, group):
|
||||
s = group.timestamp.tv_sec - group.prevTimestamp.tv_sec
|
||||
- u = group.timestamp.tv_usec - group.prevTimestamp.tv_usec
|
||||
- return s + u / 1000000.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 / 1000000000.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 / 1000000.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 a086e0f..a479cc4 100755
|
||||
--- a/src/pcp/pidstat/pcp-pidstat.py
|
||||
+++ b/src/pcp/pidstat/pcp-pidstat.py
|
||||
@@ -907,8 +907,17 @@ class PidstatReport(pmcc.MetricGroupPrinter):
|
||||
|
||||
def timeStampDelta(self, group):
|
||||
s = group.timestamp.tv_sec - group.prevTimestamp.tv_sec
|
||||
- u = group.timestamp.tv_usec - group.prevTimestamp.tv_usec
|
||||
- return s + u / 1000000.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 / 1000000000.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 / 1000000.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 5b80589..1c27cf1 100755
|
||||
--- a/src/pcp/ps/pcp-ps.py
|
||||
+++ b/src/pcp/ps/pcp-ps.py
|
||||
@@ -248,7 +248,7 @@ class ProcessStatusUtil:
|
||||
if self.user_percent() is not None and self.guest_percent() is not None and self.system_percent() is not None:
|
||||
return float("%.2f" % (self.user_percent() + self.guest_percent() + self.system_percent()))
|
||||
else:
|
||||
- return None
|
||||
+ return 0.0
|
||||
|
||||
def stime(self):
|
||||
c_systime = self.__get_value('proc.psinfo.stime', self.instance)
|
||||
@@ -358,6 +358,20 @@ class DynamicProcessReporter:
|
||||
self.printer = printer
|
||||
self.processStatOptions = processStatOptions
|
||||
|
||||
+ def __sort_by_idx(self, output_list, sorting_idx, reverse=True):
|
||||
+ # Rows are tab-delimited; splitting on whitespace breaks when
|
||||
+ # command/args contain spaces and shifts sortable column indexes.
|
||||
+ return sorted(
|
||||
+ output_list,
|
||||
+ key=lambda row: (
|
||||
+ float(row.split('\t')[sorting_idx].strip())
|
||||
+ if sorting_idx < len(row.split('\t'))
|
||||
+ and row.split('\t')[sorting_idx].strip().replace('.', '', 1).replace('-', '', 1).isdigit()
|
||||
+ else float('-inf')
|
||||
+ ),
|
||||
+ reverse=reverse
|
||||
+ )
|
||||
+
|
||||
def _is_last_and_args(self, key):
|
||||
return (key == "args") and \
|
||||
self.processStatOptions.colum_list.index(key) == len(self.processStatOptions.colum_list) - 1
|
||||
@@ -370,19 +384,15 @@ class DynamicProcessReporter:
|
||||
# Sorting validations
|
||||
sorting_idx = None
|
||||
if self.processStatOptions.sorting_flag:
|
||||
- if self.processStatOptions.filterstate == "ALL":
|
||||
- if self.processStatOptions.sorting_order == '%mem':
|
||||
- sorting_idx = 7
|
||||
- elif self.processStatOptions.sorting_order == '%cpu':
|
||||
- sorting_idx = 8
|
||||
- else:
|
||||
- sorting_idx = next((idx for idx, key in enumerate(self.processStatOptions.colum_list)
|
||||
- if key == self.processStatOptions.sorting_order),
|
||||
- None)
|
||||
- if sorting_idx is None:
|
||||
- raise ValueError("Sorting order not found in output columns")
|
||||
- # Adjust for timestamp column
|
||||
- sorting_idx += 1
|
||||
+ # For dynamic output, sorting key must be present in selected columns.
|
||||
+ if self.processStatOptions.sorting_order not in self.processStatOptions.colum_list:
|
||||
+ raise ValueError("Sorting order not found in output columns")
|
||||
+
|
||||
+ # Find sorting column index and adjust for Timestamp at position 0.
|
||||
+ sorting_idx = next((idx for idx, key in enumerate(self.processStatOptions.colum_list)
|
||||
+ if key == self.processStatOptions.sorting_order), None)
|
||||
+ # to account for Timestamp colum
|
||||
+ sorting_idx += 1
|
||||
|
||||
# Always compute process list ONCE
|
||||
processes = self.process_filter.filter_processes(
|
||||
@@ -392,47 +402,8 @@ class DynamicProcessReporter:
|
||||
output_list = []
|
||||
header = None
|
||||
|
||||
- # -------- PATH 1: With filterstate -------- #
|
||||
- if self.processStatOptions.filterstate == "ALL":
|
||||
- header = (
|
||||
- "Timestamp\tUSER\t\tPID\t\tPPID\t\tPRI\t%CPU\t%MEM\tVSZ"
|
||||
- "\tRSS\tS\tSTARTED\t\tTIME\t\t"
|
||||
- "WCHAN\t\t\t\tCommand"
|
||||
- )
|
||||
-
|
||||
- # Precompute format string
|
||||
- # fmt = (
|
||||
- # "{ts}{indent}{user}\t{pid}\t{ppid}\t{pri}\t{cpu}\t{mem}\t"
|
||||
- # "{vsz}\t{rss}\t{s}\t{started}\t{time}\t{wchan}\t{cmd}"
|
||||
- # )
|
||||
- for process in processes:
|
||||
- # Maintain state info
|
||||
- key = (process.s_name(), process.pid())
|
||||
- process_state_info[key] = process_state_info.get(key, 0) + self.delta_time
|
||||
- row = [timestamp]
|
||||
- row.extend([
|
||||
- process.user_name(),
|
||||
- process.pid(),
|
||||
- process.ppid(),
|
||||
- process.priority(),
|
||||
- process.total_percent(),
|
||||
- process.system_percent(),
|
||||
- process.vsize(),
|
||||
- process.rss(),
|
||||
- process.s_name(),
|
||||
- process.start(),
|
||||
- process.total_time(),
|
||||
- process.wchan_s(),
|
||||
- process.process_name_with_args_last()[:45]
|
||||
- ])
|
||||
- output_list.append(
|
||||
- "\t".join(
|
||||
- str(x) if x is not None else '' for x in row
|
||||
- )
|
||||
- )
|
||||
-
|
||||
- # -------- PATH 2: Customized column list -------- #
|
||||
- elif self.processStatOptions.colum_list is not None:
|
||||
+ # -------- Dynamic column list path -------- #
|
||||
+ if self.processStatOptions.colum_list is not None:
|
||||
|
||||
header = "Timestamp\t"
|
||||
for key in self.processStatOptions.colum_list:
|
||||
@@ -449,23 +420,14 @@ class DynamicProcessReporter:
|
||||
# print(row)
|
||||
output_list.append("\t".join(str(x) if x is not None else '' for x in row))
|
||||
|
||||
- # -------- PATH 3: Invalid filterstate or column list -------- #
|
||||
+ # -------- Invalid column list -------- #
|
||||
# This should never happen, but just in case
|
||||
else:
|
||||
raise ValueError("No valid filterstate or column list provided")
|
||||
|
||||
# Sorting logic
|
||||
if self.processStatOptions.sorting_flag and sorting_idx is not None:
|
||||
- output_list.sort(
|
||||
- key=lambda x: (
|
||||
- sorting_idx,
|
||||
- float('inf') if (
|
||||
- len(x.split()) <= sorting_idx or
|
||||
- not x.split()[sorting_idx].replace('.', '', 1).isdigit()
|
||||
- ) else float(x.split()[sorting_idx])
|
||||
- ),
|
||||
- reverse=True
|
||||
- )
|
||||
+ output_list = self.__sort_by_idx(output_list, sorting_idx, reverse=True)
|
||||
|
||||
# --------- Print output --------- #
|
||||
self.printer(header)
|
||||
@@ -530,25 +492,25 @@ class ProcessStatusReporter:
|
||||
for process in processes:
|
||||
output_rows.append("%s%s%s\t\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" % (
|
||||
safe_str(timestamp), safe_str(value_indentation), safe_str(process.user_name()),
|
||||
- safe_str(process.pid()),safe_str(process.system_percent()), safe_str(process.total_percent()),
|
||||
+ safe_str(process.pid()),safe_str(process.system_percent()), safe_str(process.mem()),
|
||||
safe_str(process.vsize()), safe_str(process.rss()),
|
||||
safe_str(process.tty_name()), safe_str(process.ppid()), safe_str(process.total_time()),
|
||||
safe_str(process.start()),
|
||||
safe_str(process.process_name())))
|
||||
- cpu_idx = 5
|
||||
- mem_idx = 6
|
||||
+ cpu_idx = 3
|
||||
+ mem_idx = 4
|
||||
elif selected_flag == "user":
|
||||
for process in processes:
|
||||
output_rows.append("%s%s%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s" % (
|
||||
safe_str(timestamp), safe_str(value_indentation), safe_str(process.user_name()),
|
||||
safe_str(process.pid()),
|
||||
- safe_str(process.system_percent()), safe_str(process.total_percent()),
|
||||
+ safe_str(process.system_percent()), safe_str(process.mem()),
|
||||
safe_str(process.vsize()), safe_str(process.rss()),
|
||||
safe_str(process.tty_name()), safe_str(process.s_name()),
|
||||
safe_str(process.total_time()), safe_str(process.start()),
|
||||
safe_str(process.process_name())))
|
||||
- cpu_idx = 5
|
||||
- mem_idx = 6
|
||||
+ cpu_idx = 3
|
||||
+ mem_idx = 4
|
||||
elif selected_flag == "command":
|
||||
for process in processes:
|
||||
output_rows.append("%s%s%s\t%s\t%s\t%s\t%s" % (
|
||||
@@ -584,8 +546,17 @@ class ProcessStatReport(pmcc.MetricGroupPrinter):
|
||||
|
||||
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 / 1000000000.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 / 1000000.0
|
||||
+ # it should not reach here
|
||||
+ return s
|
||||
|
||||
def print_machine_info(self,context):
|
||||
timestamp = context.pmLocaltime(self.group.timestamp.tv_sec)
|
||||
@@ -854,6 +825,10 @@ class ProcessStatOptions(pmapi.pmOptions):
|
||||
try:
|
||||
if optarg.upper() == "ALL":
|
||||
self.filterstate = optarg.upper()
|
||||
+ self.colum_list = [
|
||||
+ "uname", "pid", "ppid", "pri", "%cpu", "%mem",
|
||||
+ "vsize", "rss", "state", "start", "time", "wchan", "args"
|
||||
+ ]
|
||||
else:
|
||||
dummy_list = optarg.replace(',', ' ').split(' ')
|
||||
if self.debug_mode:
|
||||
diff --git a/src/pcp/tapestat/pcp-tapestat.py b/src/pcp/tapestat/pcp-tapestat.py
|
||||
index 4f01c56..42c1a7a 100644
|
||||
--- a/src/pcp/tapestat/pcp-tapestat.py
|
||||
+++ b/src/pcp/tapestat/pcp-tapestat.py
|
||||
@@ -67,9 +67,18 @@ class TapestatReport(pmcc.MetricGroupPrinter):
|
||||
Hcount = 0
|
||||
def timeStampDelta(self, group):
|
||||
s = group.timestamp.tv_sec - group.prevTimestamp.tv_sec
|
||||
- u = group.timestamp.tv_usec - group.prevTimestamp.tv_usec
|
||||
- # u may be negative here, calculation is still correct.
|
||||
- return s + u / 1000000.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 / 1000000000.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 / 1000000.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()
|
||||
|
||||
--
|
||||
2.43.7
|
||||
|
||||
@ -1,364 +0,0 @@
|
||||
From c79976edb317272dcc8ae9352778d5d335763f10 Mon Sep 17 00:00:00 2001
|
||||
From: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
Date: Wed, 25 Feb 2026 12:45:43 +0000
|
||||
Subject: [PATCH] Add interval and count support for nfsiostat tool
|
||||
|
||||
Signed-off-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
|
||||
Cherry-pick-commit: https://github.com/performancecopilot/pcp/commit/02d9b5a7b3c2ea16de2665c1f8cd6b81843fee15
|
||||
|
||||
Orabug: 38902336
|
||||
|
||||
Signed-off-by: Sourav Sharma <sourav.ss.sharma@oracle.com>
|
||||
|
||||
---
|
||||
src/pcp/nfsiostat/pcp-nfsiostat.py | 269 ++++++++++++++++-------------
|
||||
1 file changed, 145 insertions(+), 124 deletions(-)
|
||||
|
||||
diff --git a/src/pcp/nfsiostat/pcp-nfsiostat.py b/src/pcp/nfsiostat/pcp-nfsiostat.py
|
||||
index 0b0376d..e19fa41 100644
|
||||
--- a/src/pcp/nfsiostat/pcp-nfsiostat.py
|
||||
+++ b/src/pcp/nfsiostat/pcp-nfsiostat.py
|
||||
@@ -22,7 +22,7 @@ import signal
|
||||
import sys
|
||||
import time
|
||||
from pcp import pmapi, pmcc
|
||||
-from cpmapi import PM_CONTEXT_ARCHIVE
|
||||
+from cpmapi import PM_CONTEXT_ARCHIVE, PM_MODE_FORW
|
||||
|
||||
SYS_METRICS= ["kernel.uname.sysname","kernel.uname.release",
|
||||
"kernel.uname.nodename","kernel.uname.machine","hinv.ncpu"]
|
||||
@@ -40,98 +40,54 @@ def adjust_length(name):
|
||||
class ReportingMetricRepository:
|
||||
|
||||
def __init__(self,group):
|
||||
- self.group=group
|
||||
- self.current_cached_values = {}
|
||||
-
|
||||
- def __sorted(self,data):
|
||||
- return dict(sorted(data.items(), key=lambda item: item[0].lower()))
|
||||
-
|
||||
- def __fetch_current_value(self,metric):
|
||||
- val=dict(map(lambda x: (x[1], x[2]), self.group[metric].netValues))
|
||||
- val=self.__sorted(val)
|
||||
- return dict(val)
|
||||
-
|
||||
- def current_value(self,metric):
|
||||
- if not metric in self.group:
|
||||
- return None
|
||||
- if self.current_cached_values.get(metric) is None:
|
||||
- first_value=self.__fetch_current_value(metric)
|
||||
- self.current_cached_values[metric]=first_value
|
||||
- return self.current_cached_values[metric]
|
||||
+ self.group = group
|
||||
+ self._current_cache = {}
|
||||
+ self._previous_cache = {}
|
||||
+
|
||||
+ def _fetch_values(self, metric, use_previous=False):
|
||||
+ """Fetch values - always returns a dictionary."""
|
||||
+ if metric not in self.group:
|
||||
+ return {}
|
||||
+ attr = "netPrevValues" if use_previous else "netValues"
|
||||
+ values = getattr(self.group[metric], attr, [])
|
||||
+ return {x[0].inst: x[2] for x in values} if values else {}
|
||||
+
|
||||
+ def _get_values_dict(self, metric, use_previous=False):
|
||||
+ """Get cached dictionary of all values for a metric."""
|
||||
+ cache = self._previous_cache if use_previous else self._current_cache
|
||||
+ if metric not in cache:
|
||||
+ cache[metric] = self._fetch_values(metric, use_previous)
|
||||
+ return cache[metric]
|
||||
+
|
||||
+ def previous_value(self, metric, instance=None):
|
||||
+ """Get previous value. Returns single value if instance given, else returns dict."""
|
||||
+ values_dict = self._get_values_dict(metric, use_previous=True)
|
||||
+ if instance is not None:
|
||||
+ return values_dict.get(instance)
|
||||
+ return values_dict
|
||||
+
|
||||
+ def current_value(self, metric, instance=None):
|
||||
+ """Get current value. Returns single value if instance given, else returns dict."""
|
||||
+ values_dict = self._get_values_dict(metric, use_previous=False)
|
||||
+ if instance is not None:
|
||||
+ return values_dict.get(instance)
|
||||
+ return values_dict
|
||||
+
|
||||
+ def previous_value(self, metric, instance=None):
|
||||
+ """Get previous value. Returns single value if instance given, else returns dict."""
|
||||
+ values_dict = self._get_values_dict(metric, use_previous=True)
|
||||
+ if instance is not None:
|
||||
+ return values_dict.get(instance)
|
||||
+ return values_dict
|
||||
|
||||
class NfsioStatUtil:
|
||||
def __init__(self,metrics_repository):
|
||||
self.__metric_repository=metrics_repository
|
||||
self.report=ReportingMetricRepository(self.__metric_repository)
|
||||
|
||||
- def mount_point(self):
|
||||
- return self.report.current_value('nfsclient.mountpoint')
|
||||
-
|
||||
- def mount_share(self):
|
||||
- return self.report.current_value('nfsclient.export')
|
||||
-
|
||||
- def mount_share_keys(self):
|
||||
- data = self.report.current_value('nfsclient.export')
|
||||
- return data.keys()
|
||||
-
|
||||
- def sample_time(self):
|
||||
- return self.report.current_value('nfsclient.age')
|
||||
-
|
||||
- def xprt_sends(self):
|
||||
- return self.report.current_value('nfsclient.xprt.sends')
|
||||
-
|
||||
- def xprt_backlog(self):
|
||||
- return self.report.current_value('nfsclient.xprt.backlog_u')
|
||||
-
|
||||
- def readops(self):
|
||||
- return self.report.current_value('nfsclient.ops.read.ops')
|
||||
-
|
||||
- def readerrors(self):
|
||||
- return self.report.current_value('nfsclient.ops.read.errors')
|
||||
-
|
||||
- def readexecute(self):
|
||||
- return self.report.current_value('nfsclient.ops.read.execute')
|
||||
-
|
||||
- def readrtt(self):
|
||||
- return self.report.current_value('nfsclient.ops.read.rtt')
|
||||
-
|
||||
- def readqueue(self):
|
||||
- return self.report.current_value('nfsclient.ops.read.queue')
|
||||
-
|
||||
- def readbytesrecv(self):
|
||||
- return self.report.current_value('nfsclient.ops.read.bytes_recv')
|
||||
-
|
||||
- def readbytessent(self):
|
||||
- return self.report.current_value('nfsclient.ops.read.bytes_sent')
|
||||
-
|
||||
- def readntrans(self):
|
||||
- return self.report.current_value('nfsclient.ops.read.ntrans')
|
||||
-
|
||||
- def writeops(self):
|
||||
- return self.report.current_value('nfsclient.ops.write.ops')
|
||||
-
|
||||
- def writeerrors(self):
|
||||
- return self.report.current_value('nfsclient.ops.write.errors')
|
||||
-
|
||||
- def writeexecute(self):
|
||||
- return self.report.current_value('nfsclient.ops.write.execute')
|
||||
-
|
||||
- def writertt(self):
|
||||
- return self.report.current_value('nfsclient.ops.write.rtt')
|
||||
-
|
||||
- def writequeue(self):
|
||||
- return self.report.current_value('nfsclient.ops.write.queue')
|
||||
-
|
||||
- def writebytesrecv(self):
|
||||
- return self.report.current_value('nfsclient.ops.write.bytes_recv')
|
||||
-
|
||||
- def writebytessent(self):
|
||||
- return self.report.current_value('nfsclient.ops.write.bytes_sent')
|
||||
-
|
||||
- def writentrans(self):
|
||||
- return self.report.current_value('nfsclient.ops.write.ntrans')
|
||||
-
|
||||
class NfsiostatReport(pmcc.MetricGroupPrinter):
|
||||
+ machine_info_count = 0
|
||||
+
|
||||
def __init__(self,opts,group):
|
||||
self.opts = opts
|
||||
self.group = group
|
||||
@@ -156,34 +112,67 @@ class NfsiostatReport(pmcc.MetricGroupPrinter):
|
||||
header_string += context['kernel.uname.machine'].netValues[0][2] + ' '
|
||||
print("%s (%s CPU)" % (header_string, self.__get_ncpu(context)))
|
||||
|
||||
- def __print_values(self,timestamp, nfsstatus):
|
||||
- n_shares = nfsstatus.mount_share_keys()
|
||||
- mountshare = nfsstatus.mount_share()
|
||||
- mountpoint = nfsstatus.mount_point()
|
||||
- sampletime = nfsstatus.sample_time()
|
||||
- sends = nfsstatus.xprt_sends()
|
||||
- backlog = nfsstatus.xprt_backlog()
|
||||
- readops = nfsstatus.readops()
|
||||
- readerrors = nfsstatus.readerrors()
|
||||
- readexecute = nfsstatus.readexecute()
|
||||
- readrtt = nfsstatus.readrtt()
|
||||
- readqueue = nfsstatus.readqueue()
|
||||
- readbytesrecv = nfsstatus.readbytesrecv()
|
||||
- readbytessent = nfsstatus.readbytessent()
|
||||
- readntrans = nfsstatus.readntrans()
|
||||
- writeops = nfsstatus.writeops()
|
||||
- writeerrors = nfsstatus.writeerrors()
|
||||
- writeexecute = nfsstatus.writeexecute()
|
||||
- writertt = nfsstatus.writertt()
|
||||
- writequeue = nfsstatus.writequeue()
|
||||
- writebytesrecv = nfsstatus.writebytesrecv()
|
||||
- writebytessent = nfsstatus.writebytessent()
|
||||
- writentrans = nfsstatus.writentrans()
|
||||
+ # --------------------------------------------------------
|
||||
+
|
||||
+ def __collect(self, nfs):
|
||||
+ return {
|
||||
+ metric: nfs.report.current_value(metric)
|
||||
+ for metric in NFSIOSTAT_METRICS
|
||||
+ }
|
||||
+
|
||||
+ # --------------------------------------------------------
|
||||
+
|
||||
+ def __delta(self, new: dict, nfs):
|
||||
+ delta = {}
|
||||
+ old = {
|
||||
+ metric: nfs.report.previous_value(metric)
|
||||
+ for metric in NFSIOSTAT_METRICS
|
||||
+ }
|
||||
+
|
||||
+ for metric in new:
|
||||
+ delta[metric] = {}
|
||||
+
|
||||
+ for inst in new[metric]:
|
||||
+ new_val = new[metric][inst]
|
||||
+ old_val = old.get(metric, {}).get(inst, 0)
|
||||
+
|
||||
+ # If value is numeric → subtract
|
||||
+ if isinstance(new_val, (int, float)):
|
||||
+ delta[metric][inst] = new_val - old_val
|
||||
+ else:
|
||||
+ # If string → just copy (no subtraction)
|
||||
+ delta[metric][inst] = new_val
|
||||
+
|
||||
+ return delta
|
||||
+
|
||||
+ def __print_values(self,timestamp, delta):
|
||||
+
|
||||
+ sampletime = delta["nfsclient.age"]
|
||||
+ readops = delta["nfsclient.ops.read.ops"]
|
||||
+ writeops = delta["nfsclient.ops.write.ops"]
|
||||
+ readbytesrecv = delta["nfsclient.ops.read.bytes_recv"]
|
||||
+ writebytesrecv = delta["nfsclient.ops.write.bytes_recv"]
|
||||
+ mountpoint = delta["nfsclient.mountpoint"]
|
||||
+ mountshare = delta["nfsclient.export"]
|
||||
+ sends = delta["nfsclient.xprt.sends"]
|
||||
+ backlog = delta["nfsclient.xprt.backlog_u"]
|
||||
+ readerrors = delta["nfsclient.ops.read.errors"]
|
||||
+ readexecute = delta["nfsclient.ops.read.execute"]
|
||||
+ readrtt = delta["nfsclient.ops.read.rtt"]
|
||||
+ readqueue = delta["nfsclient.ops.read.queue"]
|
||||
+ readbytessent = delta["nfsclient.ops.read.bytes_sent"]
|
||||
+ readntrans = delta["nfsclient.ops.read.ntrans"]
|
||||
+ writeerrors = delta["nfsclient.ops.write.errors"]
|
||||
+ writeexecute = delta["nfsclient.ops.write.execute"]
|
||||
+ writertt = delta["nfsclient.ops.write.rtt"]
|
||||
+ writequeue = delta["nfsclient.ops.write.queue"]
|
||||
+ writebytessent = delta["nfsclient.ops.write.bytes_sent"]
|
||||
+ writentrans = delta["nfsclient.ops.write.ntrans"]
|
||||
|
||||
print("%-18s:%s"%("Timestamp", timestamp))
|
||||
print()
|
||||
|
||||
- for name in n_shares:
|
||||
+ for name in mountshare:
|
||||
# read
|
||||
r_kilobytes = (readbytessent[name] + readbytesrecv[name]) / 1024
|
||||
if sampletime[name] > 0:
|
||||
@@ -277,13 +266,24 @@ class NfsiostatReport(pmcc.MetricGroupPrinter):
|
||||
)
|
||||
print()
|
||||
|
||||
- def print_report(self,group,timestamp, manager_nfsiostat):
|
||||
+ def get_timestamp(self, group):
|
||||
+ t_s = group.contextCache.pmLocaltime(int(group.timestamp))
|
||||
+ timestamp = time.strftime(NfsiostatOptions.timefmt, t_s.struct_time())
|
||||
+ return timestamp
|
||||
+
|
||||
+ def print_report(self,group, manager_nfsiostat, mgr):
|
||||
def __print_nfs_status():
|
||||
- nfsstatus = NfsioStatUtil(manager_nfsiostat)
|
||||
- if nfsstatus.mount_share():
|
||||
+ timestamp = self.get_timestamp(group)
|
||||
+ nfs = NfsioStatUtil(manager_nfsiostat)
|
||||
+ if nfs.report.current_value("nfsclient.export"):
|
||||
try:
|
||||
- self.__print_machine_info(group)
|
||||
- self.__print_values(timestamp, nfsstatus)
|
||||
+ if self.machine_info_count == 0:
|
||||
+ self.__print_machine_info(group)
|
||||
+ self.machine_info_count = 1
|
||||
+ current = self.__collect(nfs)
|
||||
+ diff_dict = self.__delta(current, nfs)
|
||||
+ self. __print_values(timestamp, diff_dict)
|
||||
+
|
||||
except IndexError:
|
||||
print("Incorrect machine info due to some missing metrics")
|
||||
return
|
||||
@@ -292,7 +292,6 @@ class NfsiostatReport(pmcc.MetricGroupPrinter):
|
||||
|
||||
if self.context != PM_CONTEXT_ARCHIVE and self.samples is None:
|
||||
__print_nfs_status()
|
||||
- sys.exit(0)
|
||||
elif self.context == PM_CONTEXT_ARCHIVE and self.samples is None:
|
||||
__print_nfs_status()
|
||||
elif self.samples >=1:
|
||||
@@ -302,30 +301,52 @@ class NfsiostatReport(pmcc.MetricGroupPrinter):
|
||||
pass
|
||||
|
||||
def report(self, manager):
|
||||
- group = manager["sysinfo"]
|
||||
self.samples = self.opts.pmGetOptionSamples()
|
||||
- t_s = group.contextCache.pmLocaltime(int(group.timestamp))
|
||||
- timestamp = time.strftime(NfsiostatOptions.timefmt, t_s.struct_time())
|
||||
- self.print_report(group,timestamp,manager['nfsiostat'])
|
||||
+ self.print_report(manager["sysinfo"] ,manager['nfsiostat'], manager)
|
||||
|
||||
class NfsiostatOptions(pmapi.pmOptions):
|
||||
timefmt = "%m/%d/%Y %H:%M:%S"
|
||||
+ uflag = False
|
||||
+ def checkOptions(self, manager):
|
||||
+ if NfsiostatOptions.uflag:
|
||||
+ if manager._options.pmGetOptionInterval():
|
||||
+ print("Error: -t incompatible with -u")
|
||||
+ return False
|
||||
+ if manager.type != PM_CONTEXT_ARCHIVE:
|
||||
+ print("Error: -u can only be specified with -a archive")
|
||||
+ return False
|
||||
+ return True
|
||||
+
|
||||
+ def extraOptions(self, opt, optarg, index):
|
||||
+ if opt == "u":
|
||||
+ NfsiostatOptions.uflag = True
|
||||
+
|
||||
def __init__(self):
|
||||
- pmapi.pmOptions.__init__(self, "a:s:Z:zV?")
|
||||
+ pmapi.pmOptions.__init__(self, "a:s:Z:t:uzV?")
|
||||
+ self.pmSetOptionCallback(self.extraOptions)
|
||||
self.pmSetLongOptionHeader("General options")
|
||||
self.pmSetLongOptionHostZone()
|
||||
self.pmSetLongOptionTimeZone()
|
||||
- self.pmSetLongOptionHelp()
|
||||
+ self.pmSetLongOptionArchive()
|
||||
self.pmSetLongOptionSamples()
|
||||
+ self.pmSetLongOptionInterval()
|
||||
+ self.pmSetLongOption("no-interpolation", 0, "u", "", "disable interpolation mode with archives")
|
||||
+ self.pmSetLongOptionHelp()
|
||||
self.pmSetLongOptionVersion()
|
||||
- self.samples=None
|
||||
- self.context=None
|
||||
+ self.context = None
|
||||
+ self.samples = None
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
opts = NfsiostatOptions()
|
||||
mngr = pmcc.MetricGroupManager.builder(opts,sys.argv)
|
||||
opts.context=mngr.type
|
||||
+ if not opts.checkOptions(mngr):
|
||||
+ raise pmapi.pmUsageErr
|
||||
+
|
||||
+ if NfsiostatOptions.uflag:
|
||||
+ # -u turns off interpolation
|
||||
+ mngr.pmSetMode(PM_MODE_FORW, mngr._options.pmGetOptionOrigin(), None)
|
||||
missing = mngr.checkMissingMetrics(ALL_METRICS)
|
||||
if missing is not None:
|
||||
sys.stderr.write('Error: not all required metrics are available\nMissing %s\n' % missing)
|
||||
--
|
||||
2.43.7
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,776 +0,0 @@
|
||||
From 18ad74bfbec889c1a68a5ba055248b3aeb8a5280 Mon Sep 17 00:00:00 2001
|
||||
From: Sagar Sagar <sagar.sagar@oracle.com>
|
||||
Date: Mon, 11 May 2026 12:51:12 +0000
|
||||
Subject: [PATCH OL8 Patch1035 1/1] pmrep: add unit information option for CSV
|
||||
headers
|
||||
|
||||
Add --csv-unit-info to include metric unit strings in CSV header fields,
|
||||
using metric(unit) and metric[instance](unit) formatting. Keep stdout
|
||||
unit header behavior separate by clarifying --no-unit-info as stdout-only.
|
||||
|
||||
Centralize CSV header sanitization, update instance formatting in CSV
|
||||
headers, and refresh pmrep documentation for the new option, pcp2csv,
|
||||
debug options, and derived metric handling.
|
||||
|
||||
Orabug: 39064482
|
||||
Signed-off-by: Sagar Sagar <sagar.sagar@oracle.com>
|
||||
---
|
||||
src/pmrep/pmrep.1 | 225 +++++++++++++++++++++++++++++++++++----------
|
||||
src/pmrep/pmrep.py | 92 ++++++++++--------
|
||||
2 files changed, 231 insertions(+), 86 deletions(-)
|
||||
|
||||
diff --git a/src/pmrep/pmrep.1 b/src/pmrep/pmrep.1
|
||||
index bafecbb..3d3a11d 100644
|
||||
--- a/src/pmrep/pmrep.1
|
||||
+++ b/src/pmrep/pmrep.1
|
||||
@@ -1,6 +1,6 @@
|
||||
-'\"macro stdmacro
|
||||
+'\" t
|
||||
.\"
|
||||
-.\" Copyright (C) 2015-2021 Marko Myllynen <myllynen@redhat.com>
|
||||
+.\" Copyright (C) 2015-2026 Marko Myllynen <myllynen@redhat.com>
|
||||
.\" Copyright (c) 2016-2018 Red Hat.
|
||||
.\"
|
||||
.\" This program is free software; you can redistribute it and/or modify it
|
||||
@@ -16,7 +16,7 @@
|
||||
.\"
|
||||
.TH PMREP 1 "PCP" "Performance Co-Pilot"
|
||||
.SH NAME
|
||||
-\f3pmrep\f1 \- performance metrics reporter
|
||||
+\f3pmrep\f1, \f3pcp2csv\f1 \- performance metrics reporter
|
||||
.SH SYNOPSIS
|
||||
\fBpmrep\fP
|
||||
[\fB\-12357CdgGHIjkLmnprRuUvVxz?\fP]
|
||||
@@ -29,6 +29,8 @@
|
||||
[\fB\-b\fP|\fB\-B\fP \fIspace-scale\fP]
|
||||
[\fB\-c\fP \fIconfig\fP]
|
||||
[\fB\-\-container\fP \fIcontainer\fP]
|
||||
+[\fB\-\-csv\-unit\-info\fP]
|
||||
+[\f3\-D\f1 \f2debug\f1]
|
||||
[\fB\-\-daemonize\fP]
|
||||
[\fB\-e\fP \fIderived\fP]
|
||||
[\fB\-E\fP \fIlines\fP]
|
||||
@@ -84,7 +86,7 @@ collects selected metric values through the facilities of the
|
||||
Performance Co-Pilot (PCP), see
|
||||
.BR PCPIntro (1).
|
||||
The metrics to be reported are specified on the command line,
|
||||
-in a configuration file, or both.
|
||||
+in configuration files, or both.
|
||||
Metrics can be automatically converted and scaled using the PCP facilities,
|
||||
either by default or by per-metric scaling specifications.
|
||||
In addition to the existing metrics, derived metrics can be defined using
|
||||
@@ -95,7 +97,7 @@ A wide range of metricsets (see below) is included by default, providing
|
||||
reports on per-process details, NUMA performance, mimicking other tools
|
||||
like
|
||||
.BR sar(1)
|
||||
-and more, see the \fBpmrep\fP configuration files under
|
||||
+and more, see the \fBpmrep\fP configuration files in
|
||||
.I $PCP_SYSCONF_DIR/pmrep
|
||||
(typically \fI/etc/pcp/pmrep\fP) for details.
|
||||
Tab completion for options, metrics, and metricsets
|
||||
@@ -113,7 +115,7 @@ The
|
||||
.B \-a
|
||||
option causes
|
||||
.B pmrep
|
||||
-to use the specified set of archive logs rather than connecting to a PMCD.
|
||||
+to use the specified set of archives rather than connecting to a PMCD.
|
||||
The
|
||||
.B \-a
|
||||
and
|
||||
@@ -141,7 +143,7 @@ If a metricspec specifies a non-leaf node in the
|
||||
Performance Metrics Name Space (PMNS), then
|
||||
.B pmrep
|
||||
will recursively descend the PMNS and report on all leaf nodes
|
||||
-(i.e., metrics) for that metricspec.
|
||||
+(i.e. metrics) for that metricspec.
|
||||
Use
|
||||
.BR pminfo (1)
|
||||
to list all the metrics (PMNS lead nodes) and their descriptions.
|
||||
@@ -151,11 +153,13 @@ A
|
||||
has three different forms.
|
||||
First, on the command line it can start with a colon (``:'') to indicate a
|
||||
.I metricset
|
||||
-to be read from a
|
||||
+to be read from
|
||||
.B pmrep
|
||||
-configuration file (see
|
||||
+configuration files (see
|
||||
+.B \-c
|
||||
+and
|
||||
.BR pmrep.conf (5)),
|
||||
-which can then consist of any number of metrics.
|
||||
+which may then consist of any number of metrics.
|
||||
Second, a
|
||||
.I metricspec
|
||||
starting with non-colon specifies a PMNS node as described above,
|
||||
@@ -165,7 +169,7 @@ This so-called
|
||||
of a metricspec is defined as follows:
|
||||
.PP
|
||||
.in 0.5i
|
||||
-.ft CW
|
||||
+.ft CR
|
||||
.nf
|
||||
metric[,label[,instances[,unit/scale[,type[,width[,precision[,limit]]]]]]]
|
||||
.fi
|
||||
@@ -175,7 +179,7 @@ metric[,label[,instances[,unit/scale[,type[,width[,precision[,limit]]]]]]]
|
||||
A valid PMNS node
|
||||
.RI ( metric )
|
||||
is mandatory.
|
||||
-It can be followed by a text
|
||||
+It may be followed by a text
|
||||
.I label
|
||||
used with
|
||||
.I stdout
|
||||
@@ -240,7 +244,7 @@ are optional, they must always be provided in the order specified above,
|
||||
thus the commas.
|
||||
.PP
|
||||
.in 1.5i
|
||||
-.ft CW
|
||||
+.ft CR
|
||||
.nf
|
||||
kernel.all.sysfork,forks,,,,8
|
||||
.fi
|
||||
@@ -261,6 +265,10 @@ Configuration file options override the corresponding
|
||||
environment variables (if any).
|
||||
Command line options override the corresponding configuration
|
||||
file options (if any).
|
||||
+.PP
|
||||
+.B pcp2csv
|
||||
+is an alias for
|
||||
+.BR pmrep .
|
||||
.SH OPTIONS
|
||||
The available command line options are:
|
||||
.TP 5
|
||||
@@ -271,7 +279,7 @@ but this option \fIwill\fP override per-metric specifications.
|
||||
.TP
|
||||
\fB\-1\fR, \fB\-\-dynamic\-header\fR
|
||||
Print a new dynamically adjusted header every time changes in
|
||||
-availability of metric and instance values occur.
|
||||
+the availability of metric and instance values occur.
|
||||
By default a static header that never changes is printed once.
|
||||
See also
|
||||
.BR \-4 ,
|
||||
@@ -308,12 +316,37 @@ Valid values for
|
||||
.I action
|
||||
are \fBupdate\fP (refresh metrics being sampled),
|
||||
\fBignore\fP (do nothing \- the default behaviour)
|
||||
-and \fBabort\fP (exit the program if such an event happens).
|
||||
+and \fBabort\fP (exit the program if such an event occurs).
|
||||
\fIupdate\fP implies \fB\-\-dynamic\-header\fP.
|
||||
.TP
|
||||
\fB\-5\fR, \fB\-\-ignore\-unknown\fR
|
||||
-Silently ignore any metric name that cannot be resolved.
|
||||
-At least one metric must be found for the tool to start.
|
||||
+A metric that
|
||||
+.B pmrep
|
||||
+is asked to report may be ``unknown'' for a number of reasons:
|
||||
+the metric is not in the PMNS of the requested source of metrics (\c
|
||||
+.BR pmcd (1)
|
||||
+for live metrics from a host, or a PCP archive for historical metrics),
|
||||
+or one or more operand metrics in a derived metric definition is not available,
|
||||
+or a derived metric definition contains syntax errors.
|
||||
+By default,
|
||||
+.B pmrep
|
||||
+will report errors and exit if
|
||||
+.B any
|
||||
+requested metric is unknown.
|
||||
+The
|
||||
+.B \-5
|
||||
+option changes the default behaviour and
|
||||
+.B pmrep
|
||||
+will silently ignore any metric that is unknown, provided
|
||||
+at least one known metric remains to be reported.
|
||||
+.RS
|
||||
+.PP
|
||||
+If an unknown metric is associated with a derived metric definition,
|
||||
+refer to
|
||||
+.BR pmRegisterDerivedMetric (3)
|
||||
+for a deeper explanation of derived metrics and the options available
|
||||
+for diagnosing issues with their definition and evaluation.
|
||||
+.RE
|
||||
.TP
|
||||
\fB\-6\fR, \fB\-\-sort\-metric\fR=\fIsort-metric\fR
|
||||
Specify a sort reference metric to sort output by values with
|
||||
@@ -355,7 +388,7 @@ but this option \fIwill\fP override per-metric specifications.
|
||||
.TP
|
||||
\fB\-a\fR \fIarchive\fR, \fB\-\-archive\fR=\fIarchive\fR
|
||||
Performance metric values are retrieved from the set of Performance
|
||||
-Co-Pilot (PCP) archive log files identified by the
|
||||
+Co-Pilot (PCP) archive files identified by the
|
||||
.I archive
|
||||
argument, which is a comma-separated list of names,
|
||||
each of which may be the base name of an archive or the name of
|
||||
@@ -402,7 +435,7 @@ but this option \fIwill\fP override per-metric specifications.
|
||||
Specify the
|
||||
.I config
|
||||
file or directory to use.
|
||||
-In case \fIconfig\fP is a directory all files under it ending
|
||||
+In case \fIconfig\fP is a directory all files in it ending
|
||||
\fB.conf\fP will be included.
|
||||
The default is the first found of:
|
||||
.IR ./pmrep.conf ,
|
||||
@@ -420,6 +453,14 @@ Fetch performance metrics from the specified
|
||||
either local or remote (see
|
||||
.BR \-h ).
|
||||
.TP
|
||||
+\fB\-\-csv\-unit\-info\fR
|
||||
+Append unit strings to metric names in the CSV header using the form
|
||||
+.I "metric(unit)"
|
||||
+or
|
||||
+.IR "metric[inst](unit)" .
|
||||
+See also
|
||||
+.BR \-k .
|
||||
+.TP
|
||||
\fB\-C\fR, \fB\-\-check\fR
|
||||
Exit before reporting any values, but after parsing the configuration
|
||||
and metrics and printing possible headers.
|
||||
@@ -432,7 +473,7 @@ to effect a pause, rather than
|
||||
the default behaviour of replaying at full speed.
|
||||
.TP
|
||||
.B \-\-daemonize
|
||||
-Daemonize on startup.
|
||||
+Daemonise on startup.
|
||||
.TP
|
||||
\fB\-e\fR \fIderived\fR, \fB\-\-derived\fR=\fIderived\fR
|
||||
Specify
|
||||
@@ -441,12 +482,25 @@ performance metrics.
|
||||
If
|
||||
.I derived
|
||||
starts with a slash (``/'') or with a dot (``.'') it will be
|
||||
-interpreted as a derived metrics configuration file, otherwise it will
|
||||
+interpreted as a PCP derived metrics configuration file, otherwise it will
|
||||
be interpreted as comma- or semicolon-separated derived metric expressions.
|
||||
-For details see
|
||||
+For complete description of derived metrics and PCP derived metrics
|
||||
+configuration files see
|
||||
.BR pmLoadDerivedConfig (3)
|
||||
and
|
||||
.BR pmRegisterDerived (3).
|
||||
+Alternatively, using
|
||||
+.BR pmrep.conf (5)
|
||||
+configuration syntax allows defining derived metrics as part of metricsets.
|
||||
+.RS
|
||||
+.PP
|
||||
+In case of issues with derived metrics, review the aforementioned manual
|
||||
+pages in detail and ensure all the required metrics are available,
|
||||
+especially when using archives.
|
||||
+Use
|
||||
+.B \-Dderive
|
||||
+to see additional debug information about parsing and evaluating derived metrics.
|
||||
+.RE
|
||||
.TP
|
||||
\fB\-E\fR \fIlines\fR, \fB\-\-repeat\-header\fR=\fIlines\fR
|
||||
Repeat the header every
|
||||
@@ -469,7 +523,7 @@ method which is mostly the same as that described in
|
||||
.BR strftime (3).
|
||||
An empty
|
||||
.I format
|
||||
-string (i.e., "") will remove the timestamps from the output.
|
||||
+string (i.e. "") will remove the timestamps from the output.
|
||||
Defaults to
|
||||
.B %H:%M:%S
|
||||
when using the
|
||||
@@ -550,7 +604,7 @@ Multiple
|
||||
options are allowed as an alternative way of specifying more than
|
||||
one non-metric-specific instance filters.
|
||||
.PP
|
||||
-An individual instance filter may be one of the following:
|
||||
+An individual instance filter can be one of the following:
|
||||
.TP 10
|
||||
.I name
|
||||
Full instance name.
|
||||
@@ -598,16 +652,30 @@ $ pmrep \-i '1 minute','5 minute' 'kernel.all.load,,.*'
|
||||
.RE
|
||||
.TP
|
||||
\fB\-I\fR, \fB\-\-ignore\-incompat\fR
|
||||
-Ignore incompatible metrics.
|
||||
-By default incompatible metrics (that is,
|
||||
-their type is unsupported or they cannot be scaled as requested)
|
||||
-will cause
|
||||
+Similar to unknown metrics described above for the
|
||||
+.B \-5
|
||||
+option, a metric that
|
||||
+.B pmrep
|
||||
+is asked to report may be considered ``incompatible'' for a number of reasons:
|
||||
+a metric type that is not supported by
|
||||
+.B pmrep
|
||||
+(e.g. PM_TYPE_AGGREGATE or PM_TYPE_EVENT),
|
||||
+or a derived metric definition that contains semantic errors,
|
||||
+or the metric is known in the PMNS but the metric's metadata is not available.
|
||||
+By default,
|
||||
+.B pmrep
|
||||
+will report errors and exit if
|
||||
+.B any
|
||||
+requested metric is incompatible.
|
||||
+The
|
||||
+.B \-I
|
||||
+option changes the default behaviour and
|
||||
.B pmrep
|
||||
-to terminate with an error message.
|
||||
-With this option all incompatible metrics are silently omitted
|
||||
-from reporting.
|
||||
+will silently ignore any metric that is incompatible, provided
|
||||
+at least one compatible metric remains to be reported.
|
||||
This may be especially useful when requesting
|
||||
-non-leaf nodes of the PMNS tree for reporting.
|
||||
+non-leaf nodes of the PMNS tree for reporting when the
|
||||
+associated metrics have different metadata.
|
||||
.TP
|
||||
\fB\-\-include\-texts\fR
|
||||
When writing a PCP archive,
|
||||
@@ -640,6 +708,8 @@ and
|
||||
\fB\-k\fR, \fB\-\-extended\-csv\fR
|
||||
Write extended CSV output, similar to
|
||||
.BR sadf (1).
|
||||
+See also
|
||||
+.BR \-\-csv\-unit\-info .
|
||||
.TP
|
||||
\fB\-K\fR \fIspec\fR, \fB\-\-spec\-local\fR=\fIspec\fR
|
||||
When fetching metrics from a local context (see
|
||||
@@ -667,9 +737,14 @@ The default for
|
||||
.I stdout
|
||||
is two spaces (`` '') and comma (``,'') for
|
||||
.IR csv .
|
||||
-In case of CSV output or stdout output with non-whitespace delimiter,
|
||||
-any instances of the delimiter in string values will be replaced by
|
||||
+When using a non-whitespace delimiter,
|
||||
+all instances of the delimiter in string values will be replaced by
|
||||
the underscore (``_'') character.
|
||||
+Note that many default metricsets specify a delimiter (that may
|
||||
+not be a comma) so it might be
|
||||
+necessary to use this option with metricsets to explicitly
|
||||
+set the delimiter as comma for CSV output, i.e. \c.
|
||||
+.B \-\-delimiter=,
|
||||
.TP
|
||||
\fB\-L\fR, \fB\-\-local\-PMDA\fR
|
||||
Use a local context to collect metrics from DSO PMDAs on the local host
|
||||
@@ -677,8 +752,8 @@ without PMCD.
|
||||
See also
|
||||
.BR \-K .
|
||||
.TP
|
||||
-\fB-m\fR, \fB\-\-include\-labels\fR
|
||||
-Include metric labels in the output.
|
||||
+\fB\-m\fR, \fB\-\-include\-labels\fR
|
||||
+Include PCP metric labels in the output.
|
||||
.TP
|
||||
\fB\-n\fR, \fB\-\-invert\-filter\fR
|
||||
Perform ranking before live filtering.
|
||||
@@ -917,7 +992,7 @@ to disable).
|
||||
.RE
|
||||
.TP
|
||||
\fB\-U\fR, \fB\-\-no\-unit\-info\fR
|
||||
-Omit unit information from headers.
|
||||
+Omit unit information from \fIstdout\fP headers.
|
||||
.TP
|
||||
\fB\-v\fR, \fB\-\-omit\-flat\fR
|
||||
Report only set-valued metrics with instances (e.g. disk.dev.read) and
|
||||
@@ -1012,6 +1087,8 @@ the metric values, no external utilities are needed.
|
||||
The referenced colon-starting
|
||||
.I metricsets
|
||||
are part of the default \fBpmrep\fR configuration.
|
||||
+With bash and zsh tab completes available options, metrics, and after a
|
||||
+colon metricsets.
|
||||
.PP
|
||||
Display network interface metrics on the local host:
|
||||
.RS +4
|
||||
@@ -1037,7 +1114,7 @@ Display the slab total usage (in MB) of two specific slab instances:
|
||||
.RS +4
|
||||
.ft B
|
||||
.nf
|
||||
-$ pmrep mem.slabinfo.slabs.total_size,,'kmalloc-4k|xfs_inode',MB
|
||||
+$ pmrep mem.slabinfo.slabs.total_size,,'kmalloc\-4k|xfs_inode',MB
|
||||
.fi
|
||||
.ft P
|
||||
.RE
|
||||
@@ -1045,8 +1122,7 @@ $ pmrep mem.slabinfo.slabs.total_size,,'kmalloc-4k|xfs_inode',MB
|
||||
Display timestamped
|
||||
.BR vmstat (8)
|
||||
like information using megabytes instead of kilobytes and also include
|
||||
-the number of inodes used (tab completes available metrics and
|
||||
-after a colon metricsets with bash and zsh):
|
||||
+the number of inodes used:
|
||||
.RS +4
|
||||
.ft B
|
||||
.nf
|
||||
@@ -1087,7 +1163,7 @@ containing details about I/O requests by current
|
||||
.RS +4
|
||||
.ft B
|
||||
.nf
|
||||
-$ pmrep \-gp \-i pmlogger :proc-io
|
||||
+$ pmrep \-gp \-i pmlogger :proc\-io
|
||||
.fi
|
||||
.ft P
|
||||
.RE
|
||||
@@ -1124,7 +1200,7 @@ on one minute interval at every full minute in a background process:
|
||||
.ft B
|
||||
.nf
|
||||
$ pmrep \-\-daemonize \-A 1m \-t 1m \-i '.*java.*' \-j \-o archive \-F ./a \\
|
||||
- :proc-info :proc-cpu :proc-mem :proc-io
|
||||
+ :proc\-info :proc\-cpu :proc\-mem :proc\-io
|
||||
.fi
|
||||
.ft P
|
||||
.RE
|
||||
@@ -1154,7 +1230,7 @@ $ pmrep \-o archive \-F ./a \-J 3 \-N proc.memory.rss proc.memory proc.io
|
||||
.I pmrep.conf
|
||||
\fBpmrep\fP configuration file (see \fB\-c\fP)
|
||||
.TP
|
||||
-.I \f(CW$PCP_SYSCONF_DIR\fP/pmrep/*.conf
|
||||
+.I \f(CR$PCP_SYSCONF_DIR\fP/pmrep/*.conf
|
||||
system provided default \fBpmrep\fP configuration files
|
||||
.SH PCP ENVIRONMENT
|
||||
Environment variables with the prefix \fBPCP_\fP are used to parameterize
|
||||
@@ -1165,11 +1241,51 @@ The \fB$PCP_CONF\fP variable may be used to specify an alternative
|
||||
configuration file, as described in \fBpcp.conf\fP(5).
|
||||
.PP
|
||||
For environment variables affecting PCP tools, see \fBpmGetOptions\fP(3).
|
||||
+.PP
|
||||
+Of particular note,
|
||||
+.B PCP_DISCRETE_ONCE
|
||||
+can be set to ensure that discrete metric values are reported only once,
|
||||
+unless they change at some point.
|
||||
+.SH DEBUGGING OPTIONS
|
||||
+The
|
||||
+.B \-D
|
||||
+or
|
||||
+.B \-\-debug
|
||||
+option enables the output of additional diagnostics on
|
||||
+.I stderr
|
||||
+to help triage problems, although the information is sometimes cryptic and
|
||||
+primarily intended to provide guidance for developers rather end-users.
|
||||
+.I debug
|
||||
+is a comma separated list of debugging options; use
|
||||
+.BR pmdbg (1)
|
||||
+with the
|
||||
+.B \-l
|
||||
+option to obtain
|
||||
+a list of the available debugging options and their meaning.
|
||||
+.PP
|
||||
+Debugging options specific to
|
||||
+.B pmrep
|
||||
+are as follows:
|
||||
+.TS
|
||||
+box;
|
||||
+lf(B) | lf(B)
|
||||
+lf(B) | lxf(R) .
|
||||
+Option Description
|
||||
+_
|
||||
+appl1 T{
|
||||
+.ad l
|
||||
+dump keywords for configurations and specifications
|
||||
+T}
|
||||
+_
|
||||
+derive T{
|
||||
+.ad l
|
||||
+dump details as each derived metric is parsed
|
||||
+T}
|
||||
+.TE
|
||||
.SH SEE ALSO
|
||||
-.BR mkaf (1),
|
||||
.BR PCPIntro (1),
|
||||
+.BR mkaf (1),
|
||||
.BR pcp (1),
|
||||
-.BR pcp-atop (1),
|
||||
.BR pcp2elasticsearch (1),
|
||||
.BR pcp2graphite (1),
|
||||
.BR pcp2influxdb (1),
|
||||
@@ -1178,13 +1294,14 @@ For environment variables affecting PCP tools, see \fBpmGetOptions\fP(3).
|
||||
.BR pcp2xlsx (1),
|
||||
.BR pcp2xml (1),
|
||||
.BR pcp2zabbix (1),
|
||||
+.BR pcp\-atop (1),
|
||||
.BR pmcd (1),
|
||||
.BR pmchart (1),
|
||||
.BR pmdiff (1),
|
||||
-.BR pmdumplog (1),
|
||||
.BR pmdumptext (1),
|
||||
.BR pminfo (1),
|
||||
.BR pmiostat (1),
|
||||
+.BR pmlogdump (1),
|
||||
.BR pmlogextract (1),
|
||||
.BR pmlogsummary (1),
|
||||
.BR pmprobe (1),
|
||||
@@ -1192,16 +1309,28 @@ For environment variables affecting PCP tools, see \fBpmGetOptions\fP(3).
|
||||
.BR pmval (1),
|
||||
.BR sadf (1),
|
||||
.BR sar (1),
|
||||
+.BR PMAPI (3),
|
||||
.BR pmGetOptions (3),
|
||||
-.BR pmSpecLocalPMDA (3),
|
||||
.BR pmLoadDerivedConfig (3),
|
||||
.BR pmParseUnitsStr (3),
|
||||
.BR pmRegisterDerived (3),
|
||||
+.BR pmSpecLocalPMDA (3),
|
||||
.BR strftime (3),
|
||||
.BR LOGARCHIVE (5),
|
||||
-.BR pcp.conf (5),
|
||||
.BR PMNS (5),
|
||||
+.BR pcp.conf (5),
|
||||
.BR pmrep.conf (5),
|
||||
.BR environ (7)
|
||||
and
|
||||
.BR vmstat (8).
|
||||
+
|
||||
+.\" control lines for scripts/man-spell
|
||||
+.\" +ok+ CdgGHIjkLmnprRuUvVxz EST NUMA XFS csv datetime
|
||||
+.\" +ok+ eth incompat influxdb inodes java
|
||||
+.\" +ok+ kmalloc metricset metricsets sda slabinfo
|
||||
+.\" +ok+ total_bytes total_size vfs vmstat wlan
|
||||
+.\" +ok+ xfs xfs_inode zsh
|
||||
+.\" +ok+ ds {from ds389} gp gUJ {all from command line -xxx args}
|
||||
+.\" +ok+ sp {from .sp in troff macro} un {from (un)available)}
|
||||
+.\" +ok+ Dderive {from -Dderive}
|
||||
+.\" +ok+ PCP_DISCRETE_ONCE {from pmrep-only env var}
|
||||
diff --git a/src/pmrep/pmrep.py b/src/pmrep/pmrep.py
|
||||
index 69d1c9c..a7b0dde 100755
|
||||
--- a/src/pmrep/pmrep.py
|
||||
+++ b/src/pmrep/pmrep.py
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env pmpython
|
||||
#
|
||||
-# Copyright (C) 2015-2021 Marko Myllynen <myllynen@redhat.com>
|
||||
+# Copyright (C) 2015-2026 Marko Myllynen <myllynen@redhat.com>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify it
|
||||
# under the terms of the GNU General Public License as published by the
|
||||
@@ -17,7 +17,7 @@
|
||||
# pylint: disable=too-many-boolean-expressions, too-many-statements
|
||||
# pylint: disable=too-many-instance-attributes, too-many-locals
|
||||
# pylint: disable=too-many-branches, too-many-nested-blocks
|
||||
-# pylint: disable=broad-except, too-many-arguments
|
||||
+# pylint: disable=broad-except, too-many-arguments, too-many-positional-arguments
|
||||
# pylint: disable=too-many-lines, too-many-public-methods
|
||||
|
||||
""" Performance Metrics Reporter """
|
||||
@@ -38,17 +38,14 @@ import os
|
||||
|
||||
# PCP Python PMAPI
|
||||
from pcp import pmapi, pmi, pmconfig
|
||||
-from cpmapi import PM_CONTEXT_ARCHIVE, PM_CONTEXT_LOCAL
|
||||
-from cpmapi import PM_INDOM_NULL, PM_IN_NULL, PM_DEBUG_APPL1, PM_TIME_SEC
|
||||
+from cpmapi import PM_CONTEXT_ARCHIVE, PM_CONTEXT_LOCAL, PM_DEBUG_APPL1
|
||||
+from cpmapi import PM_INDOM_NULL, PM_IN_NULL, PM_TIME_SEC
|
||||
from cpmapi import PM_SEM_DISCRETE, PM_TYPE_STRING
|
||||
from cpmapi import PM_TEXT_PMID, PM_TEXT_INDOM, PM_TEXT_ONELINE, PM_TEXT_HELP
|
||||
from cpmapi import PM_LABEL_INDOM, PM_LABEL_INSTANCES
|
||||
from cpmapi import PM_LABEL_DOMAIN, PM_LABEL_CLUSTER, PM_LABEL_ITEM
|
||||
from cpmi import PMI_ERR_DUPINSTNAME, PMI_ERR_DUPTEXT
|
||||
|
||||
-if sys.version_info[0] >= 3:
|
||||
- long = int # pylint: disable=redefined-builtin
|
||||
-
|
||||
# Default config
|
||||
DEFAULT_CONFIG = ["./pmrep.conf", "$HOME/.pmrep.conf", "$HOME/.pcp/pmrep.conf", "$PCP_SYSCONF_DIR/pmrep/pmrep.conf", "$PCP_SYSCONF_DIR/pmrep"]
|
||||
|
||||
@@ -80,7 +77,7 @@ class PMReporter(object):
|
||||
self.keys = ('source', 'output', 'derived', 'header', 'globals',
|
||||
'samples', 'interval', 'type', 'precision', 'daemonize',
|
||||
'timestamp', 'unitinfo', 'colxrow', 'separate_header', 'fixed_header',
|
||||
- 'delay', 'width', 'delimiter', 'extcsv', 'width_force',
|
||||
+ 'delay', 'width', 'delimiter', 'extcsv', 'width_force', 'csv_unitinfo',
|
||||
'extheader', 'repeat_header', 'timefmt', 'interpol',
|
||||
'dynamic_header', 'overall_rank', 'overall_rank_alt', 'sort_metric',
|
||||
'count_scale', 'space_scale', 'time_scale', 'version',
|
||||
@@ -106,7 +103,7 @@ class PMReporter(object):
|
||||
self.globals = 1
|
||||
self.timestamp = 0
|
||||
self.samples = None # forever
|
||||
- self.interval = pmapi.timeval(1) # 1 sec
|
||||
+ self.interval = pmapi.timespec(1) # 1 sec
|
||||
self.opts.pmSetOptionInterval(str(1)) # 1 sec
|
||||
self.delay = 0
|
||||
self.type = 0
|
||||
@@ -159,6 +156,7 @@ class PMReporter(object):
|
||||
self.localtz = None
|
||||
self.prev_ts = None
|
||||
self.runtime = -1
|
||||
+ self.csv_unitinfo = 0
|
||||
self.found_insts = []
|
||||
self.prev_insts = None
|
||||
self.static_header = 1
|
||||
@@ -206,7 +204,8 @@ class PMReporter(object):
|
||||
|
||||
opts.pmSetLongOptionHeader("Reporting options")
|
||||
opts.pmSetLongOption("no-header", 0, "H", "", "omit headers")
|
||||
- opts.pmSetLongOption("no-unit-info", 0, "U", "", "omit unit info from headers")
|
||||
+ opts.pmSetLongOption("no-unit-info", 0, "U", "", "omit unit info from stdout headers")
|
||||
+ opts.pmSetLongOption("csv-unit-info", 0, "", "", "include unit info in CSV headers")
|
||||
opts.pmSetLongOption("no-inst-info", 0, "", "", "omit instance info from headers")
|
||||
opts.pmSetLongOption("no-globals", 0, "G", "", "omit global metrics")
|
||||
opts.pmSetLongOption("timestamps", 0, "p", "", "print timestamps")
|
||||
@@ -272,6 +271,8 @@ class PMReporter(object):
|
||||
self.daemonize = 1
|
||||
elif opt == 'include-texts':
|
||||
self.include_texts = 1
|
||||
+ elif opt == 'csv-unit-info':
|
||||
+ self.csv_unitinfo = 1
|
||||
elif opt == 'no-inst-info':
|
||||
self.instinfo = 0
|
||||
elif opt == 'K':
|
||||
@@ -413,9 +414,7 @@ class PMReporter(object):
|
||||
|
||||
self.pmconfig.validate_common_options()
|
||||
|
||||
- if self.output != OUTPUT_ARCHIVE and \
|
||||
- self.output != OUTPUT_CSV and \
|
||||
- self.output != OUTPUT_STDOUT:
|
||||
+ if self.output not in (OUTPUT_ARCHIVE, OUTPUT_CSV, OUTPUT_STDOUT):
|
||||
sys.stderr.write("Error while parsing options: Invalid output target specified.\n")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -489,8 +488,8 @@ class PMReporter(object):
|
||||
""" Fetch and report """
|
||||
# Debug
|
||||
if self.context.pmDebug(PM_DEBUG_APPL1):
|
||||
- sys.stdout.write("Known config file keywords: " + str(self.keys) + "\n")
|
||||
- sys.stdout.write("Known metric spec keywords: " + str(self.pmconfig.metricspec) + "\n")
|
||||
+ sys.stderr.write("Known config file keywords: " + str(self.keys) + "\n")
|
||||
+ sys.stderr.write("Known metric spec keywords: " + str(self.pmconfig.metricspec) + "\n")
|
||||
|
||||
# Set delay mode, interpolation
|
||||
if self.context.type != PM_CONTEXT_ARCHIVE:
|
||||
@@ -750,13 +749,22 @@ class PMReporter(object):
|
||||
samples = self.samples
|
||||
else:
|
||||
duration = float(self.opts.pmGetOptionFinish()) - origin
|
||||
+ # Avoid "too large value" with time.asctime(time.localtime(endtime))
|
||||
+ now = datetime.now()
|
||||
+ try:
|
||||
+ next_year = now.replace(year=now.year + 1)
|
||||
+ except ValueError:
|
||||
+ # Handle Feb 29
|
||||
+ next_year = now.replace(year=now.year + 1, month=2, day=28)
|
||||
+ year_secs = (next_year - now).total_seconds()
|
||||
+ duration = min(duration, year_secs)
|
||||
samples = int(duration / float(self.interval) + 1)
|
||||
samples = max(0, samples)
|
||||
duration = (samples - 1) * float(self.interval)
|
||||
duration = max(0, duration)
|
||||
endtime = origin + duration
|
||||
|
||||
- instances = sum([len(x[0]) for x in self.pmconfig.insts])
|
||||
+ instances = sum(len(x[0]) for x in self.pmconfig.insts)
|
||||
insts_txt = "instances" if instances != 1 else "instance"
|
||||
if not self.static_header:
|
||||
if self.context.type == PM_CONTEXT_ARCHIVE:
|
||||
@@ -786,9 +794,9 @@ class PMReporter(object):
|
||||
samples = "N/A"
|
||||
|
||||
comm = "#" if self.output == OUTPUT_CSV else ""
|
||||
+ source = self.source + " (archive)" if self.context.type == PM_CONTEXT_ARCHIVE else "pmcd (live)"
|
||||
self.writer.write(comm + "\n")
|
||||
- if self.context.type == PM_CONTEXT_ARCHIVE:
|
||||
- self.writer.write(comm + " archive: " + self.source + "\n")
|
||||
+ self.writer.write(comm + " source: " + source + "\n")
|
||||
self.writer.write(comm + " host: " + host + "\n")
|
||||
self.writer.write(comm + " timezone: " + timezone + "\n")
|
||||
self.writer.write(comm + " start: " + time.asctime(time.localtime(origin)) + "\n")
|
||||
@@ -812,12 +820,12 @@ class PMReporter(object):
|
||||
""" Helper to get number of instances of current results """
|
||||
if self.static_header:
|
||||
if self.colxrow is None:
|
||||
- c = len(str(sum([len(i[0]) for i in self.pmconfig.insts])))
|
||||
+ c = len(str(sum(len(i[0]) for i in self.pmconfig.insts)))
|
||||
else:
|
||||
c = len(str(len(self.metrics)))
|
||||
else:
|
||||
if self.colxrow is None:
|
||||
- c = len(str(sum([len(results[i]) for i in results])))
|
||||
+ c = len(str(sum(len(results[i]) for i in results)))
|
||||
else:
|
||||
c = len(str(len(results)))
|
||||
return c
|
||||
@@ -927,19 +935,21 @@ class PMReporter(object):
|
||||
for i, metric in enumerate(self.metrics):
|
||||
for j, n in self.get_results_iter(i, metric, results):
|
||||
name = metric
|
||||
- if not self.dynamic_header:
|
||||
- if self.pmconfig.descs[i].contents.indom != PM_INDOM_NULL:
|
||||
- # Always mark metrics with instance domain
|
||||
- name += "-"
|
||||
- if self.pmconfig.insts[i][1][j]:
|
||||
- # Append instance name when present
|
||||
- name += self.pmconfig.insts[i][1][j]
|
||||
- else:
|
||||
- if self.pmconfig.descs[i].contents.indom != PM_INDOM_NULL:
|
||||
- name += "-" + n[1]
|
||||
- if self.delimiter:
|
||||
- name = name.replace(self.delimiter, " ")
|
||||
- name = name.replace("\n", " ").replace("\"", " ")
|
||||
+ if self.instinfo:
|
||||
+ if not self.dynamic_header:
|
||||
+ if self.pmconfig.descs[i].contents.indom != PM_INDOM_NULL:
|
||||
+ # Always mark metrics with instance domain
|
||||
+ name += "["
|
||||
+ if self.pmconfig.insts[i][1][j]:
|
||||
+ # Append instance name when present
|
||||
+ name += self.pmconfig.insts[i][1][j]
|
||||
+ name += "]"
|
||||
+ else:
|
||||
+ if self.pmconfig.descs[i].contents.indom != PM_INDOM_NULL:
|
||||
+ name += "[" + n[1] + "]"
|
||||
+ if self.csv_unitinfo and self.metrics[metric][2][0] is not None:
|
||||
+ name += "(" + self.metrics[metric][2][0] + ")"
|
||||
+ name = self.sanitize_csv_header_item(name)
|
||||
self.writer.write(self.delimiter + "\"" + name + "\"")
|
||||
if self.include_labels:
|
||||
ins = j if not self.dynamic_header else n[0]
|
||||
@@ -947,7 +957,7 @@ class PMReporter(object):
|
||||
if self.delimiter:
|
||||
repl = ";" if self.delimiter == "," else ","
|
||||
labels = labels.replace(self.delimiter, repl)
|
||||
- labels = labels.replace("\n", " ").replace("\"", " ")
|
||||
+ labels = self.sanitize_csv_header_item(labels)
|
||||
self.writer.write(self.delimiter + "\"" + labels + "\"")
|
||||
self.writer.write("\n")
|
||||
|
||||
@@ -1132,14 +1142,14 @@ class PMReporter(object):
|
||||
continue
|
||||
try:
|
||||
self.pmi.pmiPutValue(metric, name, str(value))
|
||||
- except pmi.pmiErr as pmierror:
|
||||
+ except pmi.pmiErr:
|
||||
pass
|
||||
data = 1
|
||||
self.prev_res = results # pylint: disable=attribute-defined-outside-init
|
||||
|
||||
# Flush
|
||||
if data:
|
||||
- self.pmi.pmiWrite(int(self.pmfg_ts().strftime('%s')), self.pmfg_ts().microsecond)
|
||||
+ self.pmi.pmiWrite(int(self.pmfg_ts().timestamp()), self.pmfg_ts().microsecond)
|
||||
|
||||
def dynamic_header_update(self, results, line=None):
|
||||
""" Update dynamic header as needed """
|
||||
@@ -1187,6 +1197,12 @@ class PMReporter(object):
|
||||
value = value.replace(self.delimiter, " ")
|
||||
return value
|
||||
|
||||
+ def sanitize_csv_header_item(self, item):
|
||||
+ """ Sanitize CSV header item """
|
||||
+ if self.delimiter:
|
||||
+ item = item.replace(self.delimiter, " ")
|
||||
+ return item.replace("\n", " ").replace("\"", " ")
|
||||
+
|
||||
def write_csv(self, timestamp):
|
||||
""" Write results in CSV format """
|
||||
if timestamp is None:
|
||||
@@ -1255,7 +1271,7 @@ class PMReporter(object):
|
||||
|
||||
def format_stdout_value(self, value, width, precision, fmt, k):
|
||||
""" Format value for stdout output """
|
||||
- if isinstance(value, (int, long)):
|
||||
+ if isinstance(value, int):
|
||||
if len(str(value)) > width:
|
||||
value = pmconfig.TRUNC
|
||||
else:
|
||||
@@ -1542,7 +1558,7 @@ class PMReporter(object):
|
||||
self.writer.flush()
|
||||
except IOError as ioerror:
|
||||
if ioerror.errno != errno.EPIPE:
|
||||
- raise error
|
||||
+ raise ioerror
|
||||
try:
|
||||
self.writer.close()
|
||||
except Exception:
|
||||
--
|
||||
2.43.7
|
||||
|
||||
138
SPECS/pcp.spec
138
SPECS/pcp.spec
@ -1,6 +1,6 @@
|
||||
Name: pcp
|
||||
Version: 5.3.7
|
||||
Release: 22.0.12%{?dist}.5
|
||||
Release: 22%{?dist}.5
|
||||
Summary: System-level performance monitoring and performance management
|
||||
License: GPLv2+ and LGPLv2+ and CC-BY
|
||||
URL: https://pcp.io
|
||||
@ -45,59 +45,6 @@ Patch28: redhat-issues-RHEL-213686-CVE-2026-16526.patch
|
||||
# https://github.com/performancecopilot/pcp/commit/c5cbeceb7d3c2af357c04065cdd911efdc270de0
|
||||
Patch29: redhat-issues-RHEL-213662-CVE-2026-16524.patch
|
||||
|
||||
#oracle patch
|
||||
Patch1001: 1001-pcp-ps-utilty-to-view-ps-like-output-for-pcp.patch
|
||||
Patch1002: 1002-pcp-ps-python2-print-issue-and-o-option-fix.patch
|
||||
Patch1003: 1003-pcp-ps-added-capabilities-to-show-n-sample-with-arch.patch
|
||||
Patch1004: 1004-Fixed-multiple-pcp-ps-and-pcp-mpstat-issue.patch
|
||||
Patch1005: 1005-Fixed-PCP-python-utility-issues.patch
|
||||
#pcp-meminfo
|
||||
Patch1006: 1006-pcp-meminfo-initial-PCP-implementation-of-meminfo.patch
|
||||
#pcp-slabinfo
|
||||
Patch1007: 1007-pcp-slabinfo-initial-PCP-implementation-of-slabinfo.patch
|
||||
#pcp-buddyinfo
|
||||
Patch1008: 1008-pcp-buddyinfo-initial-commit-of-pcp-implementation.patch
|
||||
#pcp-netstat
|
||||
Patch1009: 1009-pcp-netstat-Initial-implemenation-of-pcp-netstat-tool.patch
|
||||
#pcp-zoneinfo
|
||||
Patch1010: 1010-pcp-zoneinfo-Initial-implemenation.patch
|
||||
Patch1011: 1011-pcp-zoneinfo-ol7-replay-fix.patch
|
||||
#pmlogcheck
|
||||
Patch1012: 1012-pmlogcheck-Fixed-o-n-n-nested-hash-table-lookup.patch
|
||||
#pcp-ps
|
||||
Patch1013: 1013-pcp-ps-added-args-option-to-view-full-process-argume.patch
|
||||
#pcp-buddyinfo
|
||||
Patch1014: 1014-pcp-buddyinfo-Added-timestamp-and-no-interpolation.patch
|
||||
#pcp-meminfo
|
||||
Patch1015: 1015-pcp-meminfo-Added-timestamp-in-pcp-meinfo.patch
|
||||
#pmstat
|
||||
Patch1016: 1016-pmstat-avoid-corner-case-infinite-loop.patch
|
||||
#rocestat-pmda
|
||||
Patch1017: 1017-Merge-branch-mohith-kumar-thummaluru-rocestat-pmda.patch
|
||||
#rocestat-client
|
||||
Patch1018: 1018-Merge-branch-pcp-rocestat-of-https-github.com-mohith.patch
|
||||
Patch1019: 1019-pcp-meminfo-additional-metrics-added-for-mem.util-to.patch
|
||||
Patch1020: 1020-additional-mem.util-metrics-recent-kernel.patch
|
||||
Patch1021: 1021-meminfo-added-kreclaimable-and-hugtlb-metrics.patch
|
||||
Patch1022: 1022-fix-mpstat-showing-inconsistent-values.patch
|
||||
Patch1023: 1023-fix-broken-pipe-error-iostat.patch
|
||||
Patch1024: 1024-add-missing-numastat-metrics.patch
|
||||
Patch1025: 1025-add-numastat-support-for-mn-options.patch
|
||||
Patch1026: 1026-Fixes-higepagesize-metric-value-from-bytes-to-KB.patch
|
||||
|
||||
#pmlogger_daily
|
||||
Patch1027: 1027-xz-default-compression-changed-to-level-3.patch
|
||||
|
||||
Patch1028: 1028-orabug38724884-fix-nfsclient-per-op-parsing.patch
|
||||
Patch1029: 1029-orabug38817091-Introduce-PCP-implementation-of-nfsiostat.patch
|
||||
|
||||
Patch1030: 1030-pcp-ps-implement-sort-option-to-allow-sorting-by-cpu.patch
|
||||
Patch1031: 1031-pmlogger_daily-add-disk-usage-limit-option-and-purge.patch
|
||||
Patch1032: 1032-pcp-system-tools-restore-backward-compatibility-with.patch
|
||||
Patch1033: 1033-orabug38902336-adds-interval-option-in-nfsiostat.patch
|
||||
Patch1034: 1034-orabug38509848-introduces-numa-maps-metrics-and-adds-numastat-process-option.patch
|
||||
Patch1035: 1035-pmrep-add-unit-information-option-for-CSV-headers.patch
|
||||
|
||||
# The additional linker flags break out-of-tree PMDAs.
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=2043092
|
||||
%undefine _package_note_flags
|
||||
@ -639,7 +586,7 @@ Requires: pcp-pmda-bpftrace
|
||||
Requires: pcp-pmda-gluster pcp-pmda-zswap pcp-pmda-unbound pcp-pmda-mic
|
||||
Requires: pcp-pmda-libvirt pcp-pmda-lio pcp-pmda-openmetrics pcp-pmda-haproxy
|
||||
Requires: pcp-pmda-lmsensors pcp-pmda-netcheck pcp-pmda-rabbitmq
|
||||
Requires: pcp-pmda-openvswitch pcp-pmda-mongodb pcp-pmda-rocestat
|
||||
Requires: pcp-pmda-openvswitch pcp-pmda-mongodb
|
||||
%endif
|
||||
%if !%{disable_mssql}
|
||||
Requires: pcp-pmda-mssql
|
||||
@ -1597,24 +1544,6 @@ This package contains the PCP Performance Metrics Domain Agent (PMDA) for
|
||||
collecting metrics about the gluster filesystem.
|
||||
# end pcp-pmda-gluster
|
||||
|
||||
#
|
||||
# pcp-pmda-rocestat
|
||||
#
|
||||
%package pmda-rocestat
|
||||
License: GPLv2+
|
||||
Summary: Performance Co-Pilot (PCP) metrics for the reporting Nvidia RoCE device metrics
|
||||
URL: https://pcp.io
|
||||
Requires: pcp = %{version}-%{release} pcp-libs = %{version}-%{release}
|
||||
%if !%{disable_python3}
|
||||
Requires: python3-pcp
|
||||
%else
|
||||
Requires: %{__python2}-pcp
|
||||
%endif
|
||||
%description pmda-rocestat
|
||||
This package contains the PCP Performance Metrics Domain Agent (PMDA) for
|
||||
collecting metrics about the Nvidia RoCE device metrics.
|
||||
# end pcp-pmda-rocestat
|
||||
|
||||
#
|
||||
# pcp-pmda-nfsclient
|
||||
#
|
||||
@ -2614,7 +2543,6 @@ basic_manifest | keep '(etc/pcp|pmdas)/postfix(/|$)' >pcp-pmda-postfix-files
|
||||
basic_manifest | keep '(etc/pcp|pmdas)/postgresql(/|$)' >pcp-pmda-postgresql-files
|
||||
basic_manifest | keep '(etc/pcp|pmdas)/rabbitmq(/|$)' >pcp-pmda-rabbitmq-files
|
||||
basic_manifest | keep '(etc/pcp|pmdas)/redis(/|$)' >pcp-pmda-redis-files
|
||||
basic_manifest | keep '(etc/pcp|pmdas)/rocestat(/|$)' >pcp-pmda-rocestat-files
|
||||
basic_manifest | keep '(etc/pcp|pmdas)/roomtemp(/|$)' >pcp-pmda-roomtemp-files
|
||||
basic_manifest | keep '(etc/pcp|pmdas)/rpm(/|$)' >pcp-pmda-rpm-files
|
||||
basic_manifest | keep '(etc/pcp|pmdas)/rsyslog(/|$)' >pcp-pmda-rsyslog-files
|
||||
@ -2651,7 +2579,7 @@ for pmda_package in \
|
||||
nutcracker nvidia \
|
||||
openmetrics openvswitch oracle \
|
||||
pdns perfevent podman postfix postgresql \
|
||||
rabbitmq redis rocestat roomtemp rpm rsyslog \
|
||||
rabbitmq redis roomtemp rpm rsyslog \
|
||||
samba sendmail shping slurm smart snmp \
|
||||
sockets statsd summary systemd \
|
||||
unbound \
|
||||
@ -2981,9 +2909,6 @@ exit 0
|
||||
%preun pmda-gluster
|
||||
%{pmda_remove "$1" "gluster"}
|
||||
|
||||
%preun pmda-rocestat
|
||||
%{pmda_remove "$1" "rocestat"}
|
||||
|
||||
%preun pmda-zswap
|
||||
%{pmda_remove "$1" "zswap"}
|
||||
|
||||
@ -3130,26 +3055,6 @@ done
|
||||
%endif
|
||||
%endif
|
||||
|
||||
%post pmda-rocestat
|
||||
PCP_PMDAS_DIR=%{_pmdasdir}
|
||||
PCP_SYSCONFIG_DIR=%{_sysconfdir}/sysconfig
|
||||
PCP_PMCDCONF_PATH=%{_confdir}/pmcd/pmcd.conf
|
||||
|
||||
# Auto-install rocestat PMDA if not already in pmcd.conf
|
||||
if ! grep -q "rocestat/pmdarocestat" "$PCP_PMCDCONF_PATH"; then
|
||||
if [ ! -d /sys/class/infiniband ]; then
|
||||
if ! lsmod | grep -q '^ib_core'; then
|
||||
echo "Skipping install for PMDA Rocestat (IB kernel modules are not loaded)" >&2
|
||||
else
|
||||
echo "Skipping install for PMDA Rocestat (No IB devices detected)" >&2
|
||||
fi
|
||||
else
|
||||
cd "$PCP_PMDAS_DIR/rocestat" && \
|
||||
chmod +x Install && \
|
||||
./Install < /dev/null
|
||||
fi
|
||||
fi
|
||||
|
||||
%post
|
||||
PCP_PMNS_DIR=%{_pmnsdir}
|
||||
PCP_LOG_DIR=%{_logsdir}
|
||||
@ -3343,8 +3248,6 @@ fi
|
||||
%if !%{disable_python2} || !%{disable_python3}
|
||||
%files pmda-gluster -f pcp-pmda-gluster-files.rpm
|
||||
|
||||
%files pmda-rocestat -f pcp-pmda-rocestat-files.rpm
|
||||
|
||||
%files pmda-zswap -f pcp-pmda-zswap-files.rpm
|
||||
|
||||
%files pmda-unbound -f pcp-pmda-unbound-files.rpm
|
||||
@ -3484,41 +3387,6 @@ fi
|
||||
%files zeroconf -f pcp-zeroconf-files.rpm
|
||||
|
||||
%changelog
|
||||
* Mon Aug 17 2026 EL Errata <el-errata_ww@oracle.com> - 5.3.7-22.0.12.el8_10.5
|
||||
- Implemented units header in csv output using pmrep[Orabug: 39064482]
|
||||
- Adds interval option support in PCP nfsiostat tool [Orabug: 38902336]
|
||||
- Introduces proc.numa_maps metrics in linux_proc PMDA [Orabug: 38509848]
|
||||
- adds numastat process option in the tool
|
||||
- Added support for size based cleanup for pcp archives [Orabug: 38991685]
|
||||
- Added --sort option in pcp ps utility [Orabug: 38991685]
|
||||
- Merges new PCP nfsiostat parser in OL [Orabug: 38817091]
|
||||
- pmdanfsclient: fix regex to correctly parse NFS op stats [Orabug: 38724884]
|
||||
- pmda/rocestat: skip installation when IB is absent [Orabug: 38742706]
|
||||
- pmlogger_daily default xz compression changed to level 3 [Orabug: 38592558]
|
||||
- Backports various pcp bugs and enhancements [Orabug: 35778072]
|
||||
- [Orabug: 36932629] [Orabug: 38526407] [Orabug: 38526462]
|
||||
- [Orabug: 38526444] [Orabug: 38531609]
|
||||
- Add support for rocestat pmda [Orabug: 38109218]
|
||||
- Add support for rocestat client [Orabug: 38109360]
|
||||
- Fixed pmstat infinte loop issue in archive replay [Orabug: 37638447]
|
||||
- Fixed pmlogcheck time consuming issue [Orabug: 36995894]
|
||||
- Added pcp-ps arg option to view full command argument [Orabug: 37062125]
|
||||
- Added no interpolation option in pcp-buddyinfo [Orabug: 36985368]
|
||||
- Added timestamp and fixed broken pipe in pcp-meminfo [Orabug: 36985368]
|
||||
- pcp-zoneinfo fix to replay ol7 archives [Orabug: 35903733]
|
||||
- Backporting of python tool pcp-meminfo [Orabug: 35759707]
|
||||
- Backporting of python tool pcp-slabinfo [Orabug: 35560940]
|
||||
- Backporting of python tool pcp-buddyinfo [Orabug: 35660932]
|
||||
- Backporting of python tool pcp-netstat [Orabug: 34324779]
|
||||
- Backporting of python tool pcp-zoneinfo [Orabug: 35660927]
|
||||
- Fixed multiple pcp python utiltites issues[Orabug: 35434363]
|
||||
- Fixed broken pipe issue in pcp ps utlity[Orabug: 34830203]
|
||||
- Fixed pcp mpstat utiltiy crash issue [Orabug: 34891338]
|
||||
- Pcp mpstat utiltiy initial archive file read error fix [Orabug: 34869451]
|
||||
- Fix pcp-ps to show n sample with archives[Orabug: 34849959]
|
||||
- Pcp ps Utility -o option and print issue fix [Orabug: 34321683]
|
||||
- Pcp ps utilty has been added [Orabug: 34321683]
|
||||
|
||||
* Fri Aug 14 2026 Jan Kurik <jkurik@redhat.com> - 5.3.7-22.5
|
||||
- Fix qa/2101 and qa/common.pmcd.pdu for PCP 5.3.7 testsuite compatibility
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user