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
29
30def parse_cpumask(mask):
31 cpu_mask = set()
32 if type(mask) == list:
33 cpu_mask = set(mask)
34 return list(cpu_mask)
35 elif type(mask) == str:
36 for region_str in mask.split(','):
37 try:
38 region = region_str.replace(" ", "").split('-')
39 if len(region) == 1:
40 cpu_mask.add(int(region[0]))
41 elif len(region) == 2:
42 cpu_from = int(region[0])
43 cpu_to = int(region[1])
44 for cpu in range(cpu_from, cpu_to+1):
45 cpu_mask.add(cpu)
46 else:
47 raise Exception('This is neither a single CPU or a range of CPUs:', mask)
48 except Exception as e:
49 raise Exception('Corrcupt CPU mask region! Mask string:', mask, 'Exception:', e)
50 else:
51 raise Exception('CPU mask needs to be string or list. Current:', mask)
52 return list(cpu_mask)
53
54
55print('### Parsing CPU mask file...')
56affinity_dict = {}
57try:
58 with open(pinfile, 'r') as f:
59 affinity_json = json.load(f)
60 for proc in affinity_json:
61 if proc == '_comment':
62 continue
63 else:
64 affinity_dict[proc] = {}
65 for proc_opts in affinity_json[proc]:
66 affinity_dict[proc][proc_opts] = {}
67 for aff_field in affinity_json[proc][proc_opts]:
68 if aff_field == 'parent':
69 aff_value = affinity_json[proc][proc_opts][aff_field]
70 affinity_dict[proc][proc_opts]['parent'] = parse_cpumask(aff_value)
71 elif aff_field == 'threads':
72 affinity_dict[proc][proc_opts]['threads'] = {}
73 for thr_aff_field in affinity_json[proc][proc_opts][aff_field]:
74 aff_value = affinity_json[proc][proc_opts][aff_field][thr_aff_field]
75 affinity_dict[proc][proc_opts]['threads'][thr_aff_field] = {
76 'regex': re.compile(thr_aff_field),
77 'cpu_list': parse_cpumask(aff_value)
78 }
79 else:
80 print('Expected affinity fields are \'parent\' or \'threads\'! Ignoring field: ' + aff_field)
81 continue
82except Exception as e:
83 print(e)
84 exit(2)
85print('\n')
86
87
88proc_names = affinity_dict.keys()
89procs = []
90print('### Attempt to find and apply cpu mask for the following processes:', proc_names)
91for proc in psutil.process_iter():
92 if proc.name() in proc_names:
93 procs.append(proc)
94
95for proc in procs:
96 cmdline_dict = affinity_dict[proc.name()].keys()
97 proc_cmdline = ' '.join(proc.cmdline())
98 for cmdl in cmdline_dict:
99 if cmdl in proc_cmdline:
100 print(' -> Found process to mask!')
101 print(' + Command line:', proc_cmdline)
102 print(' + Process ID (PID):', proc.pid)
103 print(' + Detailed memory info:', proc.memory_info())
104 rss = 'Resident Set Size (RSS): ' + str('{:.2f}'.format(proc.memory_info().rss/1024/1024)) + ' [MB]'
105 vms = 'Virtual Memory Size (VMS): ' + str('{:.2f}'.format(proc.memory_info().vms/1024/1024)) + ' [MB]'
106 print(' + Memory info:', rss, '|', vms)
107 connections = proc.connections()
108 print(' + Network connection count:', len(connections))
109 children = proc.children(recursive=True)
110 print(' + Children count:', len(children))
111 threads = proc.threads()
112 print(' + Thread count:', len(threads))
113
114 if 'parent' in affinity_dict[proc.name()][cmdl].keys():
115 mask = affinity_dict[proc.name()][cmdl]['parent']
116 print(' + Parent mask specified! Applying mask for every children and thread!')
117 print(' - mask:', mask)
118 if max(mask) > lcpu_count:
119 print('WARNING! CPU Mask contains higher CPU IDs than logical CPU count visible by the kernel!')
120 #raise Exception('Error! The CPU mask contains higher CPU IDs than logical CPU count on system!')
121
122 for child in children:
123 cid = psutil.Process(child.id)
124 cid.cpu_affinity(mask)
125 for thread in threads:
126 tid = psutil.Process(thread.id)
127 tid.cpu_affinity(mask)
128
129 if 'threads' in affinity_dict[proc.name()][cmdl]:
130 print(' + Thread masks specified! Applying thread specific masks!')
131 for thread in threads:
132 tid = psutil.Process(thread.id)
133 thread_masks = affinity_dict[proc.name()][cmdl]['threads']
134 # print(thread_masks)
135
136 # Match thread names with the mask regex
137 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]
138 if len(mask_matches) > 1:
139 raise ValueError(f"Thread {tid.name()} mask_matches multiple masks {mask_matches}")
140 elif len(mask_matches) == 0:
141 continue
142
143 # Extract the cpu list
144 ((_,cpu_list),) = mask_matches
145 # if tid.name() in affinity_dict[proc.name()][cmdl]['threads'].keys():
146 # tmask = affinity_dict[proc.name()][cmdl]['threads'][tid.name()]
147 # print(' - For thread', tid.name(), 'applying mask', cpu_list)
148 print(f' - For thread {tid.name()} applying mask {cpu_list}')
149 if max(cpu_list) > lcpu_count:
150 print('WARNING! CPU Mask contains higher CPU IDs than logical CPU count visible by the kernel!')
151 #raise Exception('Error! The CPU mask contains higher CPU IDs than logical CPU count on system!')
152
153 tid.cpu_affinity(cpu_list)
154
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', tmask)
158 # tid.cpu_affinity(tmask)
159 print('\n')
160
161print('### CPU affinity applied!')
162exit(0)
163
parse_cpumask(mask)
Parses CPU mask strings and lists.