Total Complexity | 40 |
Total Lines | 328 |
Duplicated Lines | 47.87 % |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like glances.plugins.programlist often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | # |
||
2 | # This file is part of Glances. |
||
3 | # |
||
4 | # SPDX-FileCopyrightText: 2024 Nicolas Hennion <[email protected]> |
||
5 | # |
||
6 | # SPDX-License-Identifier: LGPL-3.0-only |
||
7 | # |
||
8 | |||
9 | """Program list plugin.""" |
||
10 | |||
11 | import copy |
||
12 | |||
13 | from glances.plugins.processlist import PluginModel as GlancesProcessListPluginModel |
||
14 | from glances.processes import glances_processes |
||
15 | |||
16 | # Fields description |
||
17 | # description: human readable description |
||
18 | # short_name: shortname to use un UI |
||
19 | # unit: unit type |
||
20 | # rate: if True then compute and add *_gauge and *_rate_per_is fields |
||
21 | # min_symbol: Auto unit should be used if value > than 1 'X' (K, M, G)... |
||
22 | fields_description = { |
||
23 | 'name': { |
||
24 | 'description': 'Process name', |
||
25 | 'unit': 'string', |
||
26 | }, |
||
27 | 'cmdline': { |
||
28 | 'description': 'Command line with arguments', |
||
29 | 'unit': 'list', |
||
30 | }, |
||
31 | 'username': { |
||
32 | 'description': 'Process owner', |
||
33 | 'unit': 'string', |
||
34 | }, |
||
35 | 'nprocs': { |
||
36 | 'description': 'Number of children processes', |
||
37 | 'unit': 'number', |
||
38 | }, |
||
39 | 'num_threads': { |
||
40 | 'description': 'Number of threads', |
||
41 | 'unit': 'number', |
||
42 | }, |
||
43 | 'cpu_percent': { |
||
44 | 'description': 'Process CPU consumption', |
||
45 | 'unit': 'percent', |
||
46 | }, |
||
47 | 'memory_percent': { |
||
48 | 'description': 'Process memory consumption', |
||
49 | 'unit': 'percent', |
||
50 | }, |
||
51 | 'memory_info': { |
||
52 | 'description': 'Process memory information (dict with rss, vms, shared, text, lib, data, dirty keys)', |
||
53 | 'unit': 'byte', |
||
54 | }, |
||
55 | 'status': { |
||
56 | 'description': 'Process status', |
||
57 | 'unit': 'string', |
||
58 | }, |
||
59 | 'nice': { |
||
60 | 'description': 'Process nice value', |
||
61 | 'unit': 'number', |
||
62 | }, |
||
63 | 'cpu_times': { |
||
64 | 'description': 'Process CPU times (dict with user, system, iowait keys)', |
||
65 | 'unit': 'second', |
||
66 | }, |
||
67 | 'gids': { |
||
68 | 'description': 'Process group IDs (dict with real, effective, saved keys)', |
||
69 | 'unit': 'number', |
||
70 | }, |
||
71 | 'io_counters': { |
||
72 | 'description': 'Process IO counters (list with read_count, write_count, read_bytes, write_bytes, io_tag keys)', |
||
73 | 'unit': 'byte', |
||
74 | }, |
||
75 | } |
||
76 | |||
77 | |||
78 | class PluginModel(GlancesProcessListPluginModel): |
||
79 | """Glances' processes plugin. |
||
80 | |||
81 | stats is a list |
||
82 | """ |
||
83 | |||
84 | # Default list of processes stats to be grabbed / displayed |
||
85 | # Can be altered by glances_processes.disable_stats |
||
86 | enable_stats = [ |
||
87 | 'cpu_percent', |
||
88 | 'memory_percent', |
||
89 | 'memory_info', # vms and rss |
||
90 | 'nprocs', |
||
91 | 'username', |
||
92 | 'cpu_times', |
||
93 | 'num_threads', |
||
94 | 'nice', |
||
95 | 'status', |
||
96 | 'io_counters', # ior and iow |
||
97 | 'cmdline', |
||
98 | ] |
||
99 | |||
100 | # Define the header layout of the processes list columns |
||
101 | layout_header = { |
||
102 | 'cpu': '{:<6} ', |
||
103 | 'mem': '{:<5} ', |
||
104 | 'virt': '{:<5} ', |
||
105 | 'res': '{:<5} ', |
||
106 | 'nprocs': '{:>7} ', |
||
107 | 'user': '{:<10} ', |
||
108 | 'time': '{:>8} ', |
||
109 | 'thread': '{:<3} ', |
||
110 | 'nice': '{:>3} ', |
||
111 | 'status': '{:>1} ', |
||
112 | 'ior': '{:>4} ', |
||
113 | 'iow': '{:<4} ', |
||
114 | 'command': '{} {}', |
||
115 | } |
||
116 | |||
117 | # Define the stat layout of the processes list columns |
||
118 | layout_stat = { |
||
119 | 'cpu': '{:<6.1f}', |
||
120 | 'cpu_no_digit': '{:<6.0f}', |
||
121 | 'mem': '{:<5.1f} ', |
||
122 | 'virt': '{:<5} ', |
||
123 | 'res': '{:<5} ', |
||
124 | 'nprocs': '{:>7} ', |
||
125 | 'user': '{:<10} ', |
||
126 | 'time': '{:>8} ', |
||
127 | 'thread': '{:<3} ', |
||
128 | 'nice': '{:>3} ', |
||
129 | 'status': '{:>1} ', |
||
130 | 'ior': '{:>4} ', |
||
131 | 'iow': '{:<4} ', |
||
132 | 'command': '{}', |
||
133 | 'name': '[{}]', |
||
134 | } |
||
135 | |||
136 | def __init__(self, args=None, config=None): |
||
137 | """Init the plugin.""" |
||
138 | super().__init__(args=args, config=config) |
||
139 | |||
140 | def get_key(self): |
||
141 | """Return the key of the list.""" |
||
142 | return 'name' |
||
143 | |||
144 | def update(self): |
||
145 | """Update processes stats using the input method.""" |
||
146 | # Update the stats |
||
147 | if self.input_method == 'local': |
||
148 | # Update stats using the standard system lib |
||
149 | # Note: Update is done in the processcount plugin |
||
150 | # Just return the result |
||
151 | stats = glances_processes.get_list(as_programs=True) |
||
152 | else: |
||
153 | stats = self.get_init_value() |
||
154 | |||
155 | # Get the max values (dict) |
||
156 | # Use Deep copy to avoid change between update and display |
||
157 | self.max_values = copy.deepcopy(glances_processes.max_values()) |
||
158 | |||
159 | # Update the stats |
||
160 | self.stats = stats |
||
161 | |||
162 | return self.stats |
||
163 | |||
164 | def _get_process_curses_nprocs(self, p, selected, args): |
||
165 | """Return process NPROCS curses""" |
||
166 | # Display the number of children processes |
||
167 | msg = self.layout_stat['nprocs'].format(p['nprocs']) |
||
168 | return self.curse_add_line(msg) |
||
169 | |||
170 | View Code Duplication | def _msg_curse_header(self, ret, process_sort_key, args=None): |
|
|
|||
171 | """Build the header and add it to the ret dict.""" |
||
172 | sort_style = 'SORT' |
||
173 | |||
174 | display_stats = [i for i in self.enable_stats if i not in glances_processes.disable_stats] |
||
175 | |||
176 | if 'cpu_percent' in display_stats: |
||
177 | if args.disable_irix and 0 < self.nb_log_core < 10: |
||
178 | msg = self.layout_header['cpu'].format('CPU%/' + str(self.nb_log_core)) |
||
179 | elif args.disable_irix and self.nb_log_core != 0: |
||
180 | msg = self.layout_header['cpu'].format('CPU%/C') |
||
181 | else: |
||
182 | msg = self.layout_header['cpu'].format('CPU%') |
||
183 | ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'cpu_percent' else 'DEFAULT')) |
||
184 | |||
185 | if 'memory_percent' in display_stats: |
||
186 | msg = self.layout_header['mem'].format('MEM%') |
||
187 | ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'memory_percent' else 'DEFAULT')) |
||
188 | if 'memory_info' in display_stats: |
||
189 | msg = self.layout_header['virt'].format('VIRT') |
||
190 | ret.append(self.curse_add_line(msg, optional=True)) |
||
191 | msg = self.layout_header['res'].format('RES') |
||
192 | ret.append(self.curse_add_line(msg, optional=True)) |
||
193 | if 'nprocs' in display_stats: |
||
194 | msg = self.layout_header['nprocs'].format('NPROCS') |
||
195 | ret.append(self.curse_add_line(msg)) |
||
196 | if 'username' in display_stats: |
||
197 | msg = self.layout_header['user'].format('USER') |
||
198 | ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'username' else 'DEFAULT')) |
||
199 | if 'cpu_times' in display_stats: |
||
200 | msg = self.layout_header['time'].format('TIME+') |
||
201 | ret.append( |
||
202 | self.curse_add_line(msg, sort_style if process_sort_key == 'cpu_times' else 'DEFAULT', optional=True) |
||
203 | ) |
||
204 | if 'num_threads' in display_stats: |
||
205 | msg = self.layout_header['thread'].format('THR') |
||
206 | ret.append(self.curse_add_line(msg)) |
||
207 | if 'nice' in display_stats: |
||
208 | msg = self.layout_header['nice'].format('NI') |
||
209 | ret.append(self.curse_add_line(msg)) |
||
210 | if 'status' in display_stats: |
||
211 | msg = self.layout_header['status'].format('S') |
||
212 | ret.append(self.curse_add_line(msg)) |
||
213 | if 'io_counters' in display_stats: |
||
214 | msg = self.layout_header['ior'].format('R/s') |
||
215 | ret.append( |
||
216 | self.curse_add_line( |
||
217 | msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True |
||
218 | ) |
||
219 | ) |
||
220 | msg = self.layout_header['iow'].format('W/s') |
||
221 | ret.append( |
||
222 | self.curse_add_line( |
||
223 | msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True |
||
224 | ) |
||
225 | ) |
||
226 | if args.is_standalone and not args.disable_cursor: |
||
227 | shortkey = "('k' to kill)" |
||
228 | else: |
||
229 | shortkey = "" |
||
230 | if 'cmdline' in display_stats: |
||
231 | msg = self.layout_header['command'].format("Programs", shortkey) |
||
232 | ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'name' else 'DEFAULT')) |
||
233 | |||
234 | View Code Duplication | def _msg_curse_sum(self, ret, sep_char='_', mmm=None, args=None): |
|
235 | """ |
||
236 | Build the sum message (only when filter is on) and add it to the ret dict. |
||
237 | |||
238 | :param ret: list of string where the message is added |
||
239 | :param sep_char: define the line separation char |
||
240 | :param mmm: display min, max, mean or current (if mmm=None) |
||
241 | :param args: Glances args |
||
242 | """ |
||
243 | ret.append(self.curse_new_line()) |
||
244 | if mmm is None: |
||
245 | ret.append(self.curse_add_line(sep_char * 69)) |
||
246 | ret.append(self.curse_new_line()) |
||
247 | # CPU percent sum |
||
248 | msg = ' ' |
||
249 | msg += self.layout_stat['cpu'].format(self._sum_stats('cpu_percent', mmm=mmm)) |
||
250 | ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm))) |
||
251 | # MEM percent sum |
||
252 | msg = self.layout_stat['mem'].format(self._sum_stats('memory_percent', mmm=mmm)) |
||
253 | ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm))) |
||
254 | # VIRT and RES memory sum |
||
255 | if ( |
||
256 | 'memory_info' in self.stats[0] |
||
257 | and self.stats[0]['memory_info'] is not None |
||
258 | and self.stats[0]['memory_info'] != '' |
||
259 | ): |
||
260 | # VMS |
||
261 | msg = self.layout_stat['virt'].format( |
||
262 | self.auto_unit(self._sum_stats('memory_info', sub_key='vms', mmm=mmm), low_precision=False) |
||
263 | ) |
||
264 | ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True)) |
||
265 | # RSS |
||
266 | msg = self.layout_stat['res'].format( |
||
267 | self.auto_unit(self._sum_stats('memory_info', sub_key='rss', mmm=mmm), low_precision=False) |
||
268 | ) |
||
269 | ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True)) |
||
270 | else: |
||
271 | msg = self.layout_header['virt'].format('') |
||
272 | ret.append(self.curse_add_line(msg)) |
||
273 | msg = self.layout_header['res'].format('') |
||
274 | ret.append(self.curse_add_line(msg)) |
||
275 | # PID |
||
276 | msg = self.layout_header['nprocs'].format('') |
||
277 | ret.append(self.curse_add_line(msg)) |
||
278 | # USER |
||
279 | msg = self.layout_header['user'].format('') |
||
280 | ret.append(self.curse_add_line(msg)) |
||
281 | # TIME+ |
||
282 | msg = self.layout_header['time'].format('') |
||
283 | ret.append(self.curse_add_line(msg, optional=True)) |
||
284 | # THREAD |
||
285 | msg = self.layout_header['thread'].format('') |
||
286 | ret.append(self.curse_add_line(msg)) |
||
287 | # NICE |
||
288 | msg = self.layout_header['nice'].format('') |
||
289 | ret.append(self.curse_add_line(msg)) |
||
290 | # STATUS |
||
291 | msg = self.layout_header['status'].format('') |
||
292 | ret.append(self.curse_add_line(msg)) |
||
293 | # IO read/write |
||
294 | if 'io_counters' in self.stats[0] and mmm is None: |
||
295 | # IO read |
||
296 | io_rs = int( |
||
297 | (self._sum_stats('io_counters', 0) - self._sum_stats('io_counters', sub_key=2, mmm=mmm)) |
||
298 | / self.stats[0]['time_since_update'] |
||
299 | ) |
||
300 | if io_rs == 0: |
||
301 | msg = self.layout_stat['ior'].format('0') |
||
302 | else: |
||
303 | msg = self.layout_stat['ior'].format(self.auto_unit(io_rs, low_precision=True)) |
||
304 | ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True, additional=True)) |
||
305 | # IO write |
||
306 | io_ws = int( |
||
307 | (self._sum_stats('io_counters', 1) - self._sum_stats('io_counters', sub_key=3, mmm=mmm)) |
||
308 | / self.stats[0]['time_since_update'] |
||
309 | ) |
||
310 | if io_ws == 0: |
||
311 | msg = self.layout_stat['iow'].format('0') |
||
312 | else: |
||
313 | msg = self.layout_stat['iow'].format(self.auto_unit(io_ws, low_precision=True)) |
||
314 | ret.append(self.curse_add_line(msg, decoration=self._mmm_deco(mmm), optional=True, additional=True)) |
||
315 | else: |
||
316 | msg = self.layout_header['ior'].format('') |
||
317 | ret.append(self.curse_add_line(msg, optional=True, additional=True)) |
||
318 | msg = self.layout_header['iow'].format('') |
||
319 | ret.append(self.curse_add_line(msg, optional=True, additional=True)) |
||
320 | if mmm is None: |
||
321 | msg = '< {}'.format('current') |
||
322 | ret.append(self.curse_add_line(msg, optional=True)) |
||
323 | else: |
||
324 | msg = f'< {mmm}' |
||
325 | ret.append(self.curse_add_line(msg, optional=True)) |
||
326 | msg = '(\'M\' to reset)' |
||
327 | ret.append(self.curse_add_line(msg, optional=True)) |
||
328 |