forked from mirrors/linux
		
	 8034b31464
			
		
	
	
		8034b31464
		
	
	
	
	
		
			
			Remove unnecessary import and function in wq_monitor.py Signed-off-by: Kemeng Shi <shikemeng@huaweicloud.com> Signed-off-by: Tejun Heo <tj@kernel.org>
		
			
				
	
	
		
			168 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			168 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| #!/usr/bin/env drgn
 | |
| #
 | |
| # Copyright (C) 2023 Tejun Heo <tj@kernel.org>
 | |
| # Copyright (C) 2023 Meta Platforms, Inc. and affiliates.
 | |
| 
 | |
| desc = """
 | |
| This is a drgn script to monitor workqueues. For more info on drgn, visit
 | |
| https://github.com/osandov/drgn.
 | |
| 
 | |
|   total    Total number of work items executed by the workqueue.
 | |
| 
 | |
|   infl     The number of currently in-flight work items.
 | |
| 
 | |
|   CPUtime  Total CPU time consumed by the workqueue in seconds. This is
 | |
|            sampled from scheduler ticks and only provides ballpark
 | |
|            measurement. "nohz_full=" CPUs are excluded from measurement.
 | |
| 
 | |
|   CPUitsv  The number of times a concurrency-managed work item hogged CPU
 | |
|            longer than the threshold (workqueue.cpu_intensive_thresh_us)
 | |
|            and got excluded from concurrency management to avoid stalling
 | |
|            other work items.
 | |
| 
 | |
|   CMW/RPR  For per-cpu workqueues, the number of concurrency-management
 | |
|            wake-ups while executing a work item of the workqueue. For
 | |
|            unbound workqueues, the number of times a worker was repatriated
 | |
|            to its affinity scope after being migrated to an off-scope CPU by
 | |
|            the scheduler.
 | |
| 
 | |
|   mayday   The number of times the rescuer was requested while waiting for
 | |
|            new worker creation.
 | |
| 
 | |
|   rescued  The number of work items executed by the rescuer.
 | |
| """
 | |
| 
 | |
| import signal
 | |
| import re
 | |
| import time
 | |
| import json
 | |
| 
 | |
| import drgn
 | |
| from drgn.helpers.linux.list import list_for_each_entry
 | |
| 
 | |
| import argparse
 | |
| parser = argparse.ArgumentParser(description=desc,
 | |
|                                  formatter_class=argparse.RawTextHelpFormatter)
 | |
| parser.add_argument('workqueue', metavar='REGEX', nargs='*',
 | |
|                     help='Target workqueue name patterns (all if empty)')
 | |
| parser.add_argument('-i', '--interval', metavar='SECS', type=float, default=1,
 | |
|                     help='Monitoring interval (0 to print once and exit)')
 | |
| parser.add_argument('-j', '--json', action='store_true',
 | |
|                     help='Output in json')
 | |
| args = parser.parse_args()
 | |
| 
 | |
| workqueues              = prog['workqueues']
 | |
| 
 | |
| WQ_UNBOUND              = prog['WQ_UNBOUND']
 | |
| WQ_MEM_RECLAIM          = prog['WQ_MEM_RECLAIM']
 | |
| 
 | |
| PWQ_STAT_STARTED        = prog['PWQ_STAT_STARTED']      # work items started execution
 | |
| PWQ_STAT_COMPLETED      = prog['PWQ_STAT_COMPLETED']	# work items completed execution
 | |
| PWQ_STAT_CPU_TIME       = prog['PWQ_STAT_CPU_TIME']     # total CPU time consumed
 | |
| PWQ_STAT_CPU_INTENSIVE  = prog['PWQ_STAT_CPU_INTENSIVE'] # wq_cpu_intensive_thresh_us violations
 | |
| PWQ_STAT_CM_WAKEUP      = prog['PWQ_STAT_CM_WAKEUP']    # concurrency-management worker wakeups
 | |
| PWQ_STAT_REPATRIATED    = prog['PWQ_STAT_REPATRIATED']  # unbound workers brought back into scope
 | |
| PWQ_STAT_MAYDAY         = prog['PWQ_STAT_MAYDAY']	# maydays to rescuer
 | |
| PWQ_STAT_RESCUED        = prog['PWQ_STAT_RESCUED']	# linked work items executed by rescuer
 | |
| PWQ_NR_STATS            = prog['PWQ_NR_STATS']
 | |
| 
 | |
| class WqStats:
 | |
|     def __init__(self, wq):
 | |
|         self.name = wq.name.string_().decode()
 | |
|         self.unbound = wq.flags & WQ_UNBOUND != 0
 | |
|         self.mem_reclaim = wq.flags & WQ_MEM_RECLAIM != 0
 | |
|         self.stats = [0] * PWQ_NR_STATS
 | |
|         for pwq in list_for_each_entry('struct pool_workqueue', wq.pwqs.address_of_(), 'pwqs_node'):
 | |
|             for i in range(PWQ_NR_STATS):
 | |
|                 self.stats[i] += int(pwq.stats[i])
 | |
| 
 | |
|     def dict(self, now):
 | |
|         return { 'timestamp'            : now,
 | |
|                  'name'                 : self.name,
 | |
|                  'unbound'              : self.unbound,
 | |
|                  'mem_reclaim'          : self.mem_reclaim,
 | |
|                  'started'              : self.stats[PWQ_STAT_STARTED],
 | |
|                  'completed'            : self.stats[PWQ_STAT_COMPLETED],
 | |
|                  'cpu_time'             : self.stats[PWQ_STAT_CPU_TIME],
 | |
|                  'cpu_intensive'        : self.stats[PWQ_STAT_CPU_INTENSIVE],
 | |
|                  'cm_wakeup'            : self.stats[PWQ_STAT_CM_WAKEUP],
 | |
|                  'repatriated'          : self.stats[PWQ_STAT_REPATRIATED],
 | |
|                  'mayday'               : self.stats[PWQ_STAT_MAYDAY],
 | |
|                  'rescued'              : self.stats[PWQ_STAT_RESCUED], }
 | |
| 
 | |
|     def table_header_str():
 | |
|         return f'{"":>24} {"total":>8} {"infl":>5} {"CPUtime":>8} '\
 | |
|             f'{"CPUitsv":>7} {"CMW/RPR":>7} {"mayday":>7} {"rescued":>7}'
 | |
| 
 | |
|     def table_row_str(self):
 | |
|         cpu_intensive = '-'
 | |
|         cmw_rpr = '-'
 | |
|         mayday = '-'
 | |
|         rescued = '-'
 | |
| 
 | |
|         if self.unbound:
 | |
|             cmw_rpr = str(self.stats[PWQ_STAT_REPATRIATED]);
 | |
|         else:
 | |
|             cpu_intensive = str(self.stats[PWQ_STAT_CPU_INTENSIVE])
 | |
|             cmw_rpr = str(self.stats[PWQ_STAT_CM_WAKEUP])
 | |
| 
 | |
|         if self.mem_reclaim:
 | |
|             mayday = str(self.stats[PWQ_STAT_MAYDAY])
 | |
|             rescued = str(self.stats[PWQ_STAT_RESCUED])
 | |
| 
 | |
|         out = f'{self.name[-24:]:24} ' \
 | |
|               f'{self.stats[PWQ_STAT_STARTED]:8} ' \
 | |
|               f'{max(self.stats[PWQ_STAT_STARTED] - self.stats[PWQ_STAT_COMPLETED], 0):5} ' \
 | |
|               f'{self.stats[PWQ_STAT_CPU_TIME] / 1000000:8.1f} ' \
 | |
|               f'{cpu_intensive:>7} ' \
 | |
|               f'{cmw_rpr:>7} ' \
 | |
|               f'{mayday:>7} ' \
 | |
|               f'{rescued:>7} '
 | |
|         return out.rstrip(':')
 | |
| 
 | |
| exit_req = False
 | |
| 
 | |
| def sigint_handler(signr, frame):
 | |
|     global exit_req
 | |
|     exit_req = True
 | |
| 
 | |
| def main():
 | |
|     # handle args
 | |
|     table_fmt = not args.json
 | |
|     interval = args.interval
 | |
| 
 | |
|     re_str = None
 | |
|     if args.workqueue:
 | |
|         for r in args.workqueue:
 | |
|             if re_str is None:
 | |
|                 re_str = r
 | |
|             else:
 | |
|                 re_str += '|' + r
 | |
| 
 | |
|     filter_re = re.compile(re_str) if re_str else None
 | |
| 
 | |
|     # monitoring loop
 | |
|     signal.signal(signal.SIGINT, sigint_handler)
 | |
| 
 | |
|     while not exit_req:
 | |
|         now = time.time()
 | |
| 
 | |
|         if table_fmt:
 | |
|             print()
 | |
|             print(WqStats.table_header_str())
 | |
| 
 | |
|         for wq in list_for_each_entry('struct workqueue_struct', workqueues.address_of_(), 'list'):
 | |
|             stats = WqStats(wq)
 | |
|             if filter_re and not filter_re.search(stats.name):
 | |
|                 continue
 | |
|             if table_fmt:
 | |
|                 print(stats.table_row_str())
 | |
|             else:
 | |
|                 print(stats.dict(now))
 | |
| 
 | |
|         if interval == 0:
 | |
|             break
 | |
|         time.sleep(interval)
 | |
| 
 | |
| if __name__ == "__main__":
 | |
|     main()
 |