DUNE-DAQ
DUNE Trigger and Data Acquisition software
Loading...
Searching...
No Matches
readout-affinity.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2
3import argparse
4import os
5import sys
6import psutil
7import json
8import re
9
10info = 'Alters CPU masks of running processes with different arguments, based on a configuration file.'
11parser = argparse.ArgumentParser(description=info)
12parser.add_argument('--pinfile', '-p', type=str, required=True, help='file with process to CPU mask list')
13try:
14 args = parser.parse_args()
15except:
16 parser.print_help()
17 exit(1)
18pinfile = args.pinfile
19
20print('### Basic information...')
21lcpu_count = len(os.sched_getaffinity(0))
22pcpu_count = psutil.cpu_count(logical=False)
23vmem = psutil.virtual_memory()
24print(' -> Logical CPU count: ', lcpu_count)
25print(' -> Physical CPU count: ', pcpu_count)
26print(' -> Memory status: ', vmem)
27print('\n')
28
29def get_app_name(proc_cmdline):
30 proc_name = proc_cmdline.split(" ")
31 target = "--name" if "--name" in proc_name else "-n"
32 try:
33 proc_name = proc_name[proc_name.index(target) + 1]
34 except ValueError:
35 raise ValueError("Cannot find application name in the command line using -n or --name")
36 return proc_name
37
38
39def parse_cpumask(mask):
40 cpu_mask = set()
41 if type(mask) == list:
42 cpu_mask = set(mask)
43 return list(cpu_mask)
44 elif type(mask) == str:
45 for region_str in mask.split(','):
46 try:
47 region = region_str.replace(" ", "").split('-')
48 if len(region) == 1:
49 cpu_mask.add(int(region[0]))
50 elif len(region) == 2:
51 cpu_from = int(region[0])
52 cpu_to = int(region[1])
53 for cpu in range(cpu_from, cpu_to+1):
54 cpu_mask.add(cpu)
55 else:
56 raise Exception('This is neither a single CPU or a range of CPUs:', mask)
57 except Exception as e:
58 raise Exception('Corrcupt CPU mask region! Mask string:', mask, 'Exception:', e)
59 else:
60 raise Exception('CPU mask needs to be string or list. Current:', mask)
61 return list(cpu_mask)
62
63
64print('### Parsing CPU mask file...')
65affinity_dict = {}
66try:
67 with open(pinfile, 'r') as f:
68 affinity_json = json.load(f)
69 for proc in affinity_json:
70 if proc == '_comment':
71 continue
72 else:
73 affinity_dict[proc] = {}
74 for proc_opts in affinity_json[proc]:
75 affinity_dict[proc][proc_opts] = {}
76 for aff_field in affinity_json[proc][proc_opts]:
77 if aff_field == 'parent':
78 aff_value = affinity_json[proc][proc_opts][aff_field]
79 affinity_dict[proc][proc_opts]['parent'] = parse_cpumask(aff_value)
80 elif aff_field == 'threads':
81 affinity_dict[proc][proc_opts]['threads'] = {}
82 for thr_aff_field in affinity_json[proc][proc_opts][aff_field]:
83 aff_value = affinity_json[proc][proc_opts][aff_field][thr_aff_field]
84 affinity_dict[proc][proc_opts]['threads'][thr_aff_field] = {
85 'regex': re.compile(thr_aff_field),
86 'cpu_list': parse_cpumask(aff_value)
87 }
88 else:
89 print('Expected affinity fields are \'parent\' or \'threads\'! Ignoring field: ' + aff_field)
90 continue
91except Exception as e:
92 print(e)
93 exit(2)
94print('\n')
95
96
97proc_names = affinity_dict.keys()
98procs = []
99print('### Attempt to find and apply cpu mask for the following processes:', proc_names)
100for proc in psutil.process_iter():
101 if proc.name() in proc_names:
102 procs.append(proc)
103
104for proc in procs:
105 names_dict = affinity_dict[proc.name()].keys()
106 proc_cmdline = ' '.join(proc.cmdline())
107 proc_name = get_app_name(proc_cmdline)
108 for mask_name in names_dict:
109 if mask_name == proc_name:
110 print(' -> Found process to mask!')
111 print(' + Command line:', proc_cmdline)
112 print(' + Process ID (PID):', proc.pid)
113 print(' + Detailed memory info:', proc.memory_info())
114 rss = 'Resident Set Size (RSS): ' + str('{:.2f}'.format(proc.memory_info().rss/1024/1024)) + ' [MB]'
115 vms = 'Virtual Memory Size (VMS): ' + str('{:.2f}'.format(proc.memory_info().vms/1024/1024)) + ' [MB]'
116 print(' + Memory info:', rss, '|', vms)
117 connections = proc.connections()
118 print(' + Network connection count:', len(connections))
119 children = proc.children(recursive=True)
120 print(' + Children count:', len(children))
121 threads = proc.threads()
122 print(' + Thread count:', len(threads))
123
124 if 'parent' in affinity_dict[proc.name()][mask_name].keys():
125 mask = affinity_dict[proc.name()][mask_name]['parent']
126 print(' + Parent mask specified! Applying mask for every children and thread!')
127 print(' - mask:', mask)
128 if max(mask) > lcpu_count:
129 print('WARNING! CPU Mask contains higher CPU IDs than logical CPU count visible by the kernel!')
130 #raise Exception('Error! The CPU mask contains higher CPU IDs than logical CPU count on system!')
131
132 for child in children:
133 cid = psutil.Process(child.pid)
134 cid.cpu_affinity(mask)
135 for thread in threads:
136 tid = psutil.Process(thread.id)
137 tid.cpu_affinity(mask)
138
139 if 'threads' in affinity_dict[proc.name()][mask_name]:
140 print(' + Thread masks specified! Applying thread specific masks!')
141 for thread in threads:
142 tid = psutil.Process(thread.id)
143 thread_masks = affinity_dict[proc.name()][mask_name]['threads']
144 # print(thread_masks)
145
146 # Match thread names with the mask regex
147 mask_matches = [ m for m in [(th_mask['regex'].fullmatch(tid.name()),th_mask['cpu_list']) for th_mask in thread_masks.values()] if m[0] is not None]
148 if len(mask_matches) > 1:
149 raise ValueError(f"Thread {tid.name()} mask_matches multiple masks {mask_matches}")
150 elif len(mask_matches) == 0:
151 continue
152
153 # Extract the cpu list
154 ((_,cpu_list),) = mask_matches
155 # if tid.name() in affinity_dict[proc.name()][cmdl]['threads'].keys():
156 # tmask = affinity_dict[proc.name()][cmdl]['threads'][tid.name()]
157 # print(' - For thread', tid.name(), 'applying mask', cpu_list)
158 print(f' - For thread {tid.name()} applying mask {cpu_list}')
159 if max(cpu_list) > lcpu_count:
160 print('WARNING! CPU Mask contains higher CPU IDs than logical CPU count visible by the kernel!')
161 #raise Exception('Error! The CPU mask contains higher CPU IDs than logical CPU count on system!')
162
163 tid.cpu_affinity(cpu_list)
164
165 # if tid.name() in affinity_dict[proc.name()][cmdl]['threads'].keys():
166 # tmask = affinity_dict[proc.name()][cmdl]['threads'][tid.name()]
167 # print(' - For thread', tid.name(), 'applying mask', tmask)
168 # tid.cpu_affinity(tmask)
169 print('\n')
170
171print('### CPU affinity applied!')
172exit(0)
173
parse_cpumask(mask)
Parses CPU mask strings and lists.
get_app_name(proc_cmdline)