pmemstat 3.0.2__tar.gz → 3.0.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pmemstat-3.0.2 → pmemstat-3.0.3}/PKG-INFO +1 -1
- {pmemstat-3.0.2 → pmemstat-3.0.3}/pmemstat/main.py +102 -23
- {pmemstat-3.0.2 → pmemstat-3.0.3}/pyproject.toml +1 -1
- {pmemstat-3.0.2 → pmemstat-3.0.3}/.gitignore +0 -0
- {pmemstat-3.0.2 → pmemstat-3.0.3}/LICENSE.txt +0 -0
- {pmemstat-3.0.2 → pmemstat-3.0.3}/README.md +0 -0
- {pmemstat-3.0.2 → pmemstat-3.0.3}/TODO.md +0 -0
- {pmemstat-3.0.2 → pmemstat-3.0.3}/images/help-screen.png +0 -0
- {pmemstat-3.0.2 → pmemstat-3.0.3}/images/pmemstat.png +0 -0
- {pmemstat-3.0.2 → pmemstat-3.0.3}/images/scroll-pos.png +0 -0
- {pmemstat-3.0.2 → pmemstat-3.0.3}/memtest.c +0 -0
- {pmemstat-3.0.2 → pmemstat-3.0.3}/pmemstat/CpuSmooth.py +0 -0
- {pmemstat-3.0.2 → pmemstat-3.0.3}/pmemstat/KillThem.py +0 -0
- {pmemstat-3.0.2 → pmemstat-3.0.3}/pmemstat/__init__.py +0 -0
- {pmemstat-3.0.2 → pmemstat-3.0.3}/run.sh +0 -0
|
@@ -246,7 +246,7 @@ class ProcMem:
|
|
|
246
246
|
# units = '' # determined by arguments
|
|
247
247
|
# fwidth = 11
|
|
248
248
|
pmemstat = None # the main program object
|
|
249
|
-
max_cmd_len =
|
|
249
|
+
max_cmd_len = 96 # command line maximum length
|
|
250
250
|
chunk_dict = {
|
|
251
251
|
'cat': None,
|
|
252
252
|
'beg': 0,
|
|
@@ -286,58 +286,137 @@ class ProcMem:
|
|
|
286
286
|
self.cpu = CpuSmooth(self.pid, avg_secs= ProcMem.opts.cpu_avg_secs)
|
|
287
287
|
return self.cpu.refresh_cpu() # sets self.cpu.percent
|
|
288
288
|
|
|
289
|
+
def _get_exebasename(self, exepath, wds):
|
|
290
|
+
"""
|
|
291
|
+
Final, definitive, robust executable base name resolver.
|
|
292
|
+
"""
|
|
293
|
+
|
|
294
|
+
# --- Phase 1: Determine the Initial Best Name Candidate ---
|
|
295
|
+
# ... (same initial logic as before, using os.readlink)
|
|
296
|
+
basename = os.path.basename(exepath)
|
|
297
|
+
is_bad_name = not basename or basename == 'exe' or len(basename) > 16 or not re.search(r'[a-zA-Z]', basename)
|
|
298
|
+
|
|
299
|
+
if is_bad_name:
|
|
300
|
+
try:
|
|
301
|
+
link_target = os.readlink(f'/proc/{self.pid}/exe')
|
|
302
|
+
if ' (deleted)' not in link_target and 'a.out' not in link_target:
|
|
303
|
+
basename = os.path.basename(link_target)
|
|
304
|
+
except (FileNotFoundError, OSError):
|
|
305
|
+
pass
|
|
306
|
+
|
|
307
|
+
# --- Phase 2: Multi-Step Aggressive Cleanup (The FINAL Final Fix) ---
|
|
308
|
+
|
|
309
|
+
# 1. Remove VQ... style hashes and generic suffixes
|
|
310
|
+
hash_and_bin_pattern = r'(VQ[A-Z0-9]{10,}|-bin|\.bin)'
|
|
311
|
+
basename = re.sub(hash_and_bin_pattern, '', basename).strip()
|
|
312
|
+
|
|
313
|
+
# 2. **CRITICAL FIX:** Remove all tab/PID/browser-related arguments from the tail.
|
|
314
|
+
# This addresses both 'browser 14 tab' AND any simple trailing numbers (like 'browser 4')
|
|
315
|
+
tab_and_num_pattern = r'(?:\s+\d+)?\s+(tab.*|socket.*|rdd.*|process.*|\d+)$'
|
|
316
|
+
basename = re.sub(tab_and_num_pattern, '', basename, flags=re.IGNORECASE).strip()
|
|
317
|
+
|
|
318
|
+
# 3. Remove common internal browser-role suffixes (renderer, rdd, content, etc.)
|
|
319
|
+
roles_pattern = r'-(renderer|gpu|utility|plugin|content|chrm|bsp|extension)$'
|
|
320
|
+
basename = re.sub(roles_pattern, '', basename, flags=re.IGNORECASE).strip()
|
|
321
|
+
|
|
322
|
+
# Final safety cleanup for any leading/trailing punctuation/spaces leftover
|
|
323
|
+
basename = re.sub(r'^\W+|\W+$', '', basename).strip()
|
|
324
|
+
|
|
325
|
+
# --- Phase 3: Last Resort Fallback ---
|
|
326
|
+
|
|
327
|
+
if not basename or basename in ('exe', 'a.out'):
|
|
328
|
+
try:
|
|
329
|
+
basename = os.path.basename(wds[0])
|
|
330
|
+
except IndexError:
|
|
331
|
+
basename = 'exe'
|
|
332
|
+
|
|
333
|
+
# Special Case Protection: Restore I3 if it was reduced
|
|
334
|
+
if basename == 'i':
|
|
335
|
+
basename = 'i3'
|
|
336
|
+
|
|
337
|
+
return basename
|
|
338
|
+
|
|
289
339
|
def get_cmdline(self):
|
|
290
340
|
"""Get the command line of the PID."""
|
|
291
|
-
try:
|
|
292
341
|
|
|
342
|
+
try:
|
|
293
343
|
cmdline_file = f'/proc/{self.pid}/cmdline'
|
|
294
344
|
try:
|
|
295
345
|
# pylint: disable=consider-using-with
|
|
296
346
|
line = open(cmdline_file, encoding='utf-8').read()[:-1]
|
|
297
347
|
except FileNotFoundError as exc:
|
|
298
|
-
# this seems to be a race which ignore; either the process is just
|
|
299
|
-
# started or just quickly ended before even identified
|
|
300
348
|
if DebugLevel:
|
|
301
349
|
DB(1, f'skip pid={self.pid} no-rollup-lines exc={type(exc).__name__}')
|
|
302
350
|
return
|
|
351
|
+
|
|
303
352
|
arguments = line.split('\0')
|
|
304
353
|
if not arguments or not arguments[0]: # kernel process
|
|
305
354
|
self.wanted, self.kernel = False, True
|
|
306
|
-
# print(f'{self.pid}: kernel thread')
|
|
307
355
|
return
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
356
|
+
|
|
357
|
+
exepath = arguments[0]
|
|
358
|
+
|
|
359
|
+
# Prepare wds (words/arguments) for use in the helper and cmdline rebuild
|
|
360
|
+
base_name_list = os.path.basename(exepath).split()
|
|
361
|
+
if base_name_list:
|
|
362
|
+
initial_basename = base_name_list.pop(0)
|
|
363
|
+
wds = base_name_list + arguments[1:]
|
|
364
|
+
else:
|
|
365
|
+
initial_basename = ''
|
|
366
|
+
wds = arguments[1:]
|
|
367
|
+
|
|
368
|
+
# Use the robust helper to determine the final, clean basename
|
|
369
|
+
self.exebasename = self._get_exebasename(exepath, wds)
|
|
370
|
+
|
|
371
|
+
# --- START ELABORATION LOGIC ---
|
|
372
|
+
|
|
373
|
+
# 1. Elaboration: The Sudo Wrapper
|
|
374
|
+
if self.exebasename == 'sudo' and wds:
|
|
375
|
+
actual_cmd_name = os.path.basename(wds[0])
|
|
376
|
+
if actual_cmd_name:
|
|
377
|
+
self.exebasename = actual_cmd_name
|
|
378
|
+
del wds[0]
|
|
379
|
+
|
|
380
|
+
# 2. Elaboration: The Interpreter (Python, Perl, etc.)
|
|
381
|
+
if self.exebasename in ('python', 'python2', 'python3', 'perl', 'bash', 'ruby',
|
|
315
382
|
'sh', 'ksh', 'zsh') and wds:
|
|
316
|
-
|
|
317
|
-
#
|
|
318
|
-
if
|
|
319
|
-
|
|
383
|
+
|
|
384
|
+
# 2.a. Catch the 'python -m <module>' pattern (The New Fix)
|
|
385
|
+
if len(wds) >= 2 and wds[0] == '-m':
|
|
386
|
+
module_name = wds[1]
|
|
387
|
+
self.exebasename = f'{self.exebasename}->{module_name}'
|
|
388
|
+
# Remove '-m' and the module name from arguments
|
|
320
389
|
del wds[0]
|
|
321
|
-
|
|
322
|
-
|
|
390
|
+
del wds[0]
|
|
391
|
+
|
|
392
|
+
# 2.b. Catch the standard '<script.py>' pattern (Original Logic)
|
|
393
|
+
elif wds[0] and os.path.exists(wds[0]) and os.path.isfile(wds[0]):
|
|
394
|
+
script = os.path.basename(wds[0])
|
|
395
|
+
if script != wds[0]:
|
|
396
|
+
# Only elaborate if it's a clear script name, not a generic main
|
|
397
|
+
if not re.search(r'^__main__\.', script):
|
|
398
|
+
self.exebasename = f'{self.exebasename}->{script}'
|
|
399
|
+
del wds[0] # Remove the script path from arguments
|
|
400
|
+
|
|
401
|
+
# --- END ELABORATION LOGIC ---
|
|
402
|
+
|
|
403
|
+
self.cmdline = ' '.join([self.exebasename] + wds)
|
|
323
404
|
self.cmdline_trunc = self.cmdline[0:ProcMem.max_cmd_len]
|
|
324
|
-
|
|
405
|
+
|
|
325
406
|
except Exception as exc:
|
|
326
|
-
#
|
|
407
|
+
# ... (rest of exception handling is the same)
|
|
327
408
|
print(f' WARNING: skip pid={self.pid} no-basename exc={exc}')
|
|
328
409
|
print(traceback.format_exc())
|
|
329
410
|
self.wanted = self.kernel = False
|
|
330
411
|
self.why_not = 'CannotGetCmdline'
|
|
331
412
|
return
|
|
332
413
|
|
|
333
|
-
## print(f'DBDB: {self.pid} {ProcMem.opts.pids}')
|
|
334
414
|
# filter unwanted before too much work
|
|
335
415
|
if (ProcMem.opts.pids and str(self.pid) not in ProcMem.opts.pids
|
|
336
416
|
and self.exebasename not in ProcMem.opts.pids):
|
|
337
417
|
self.wanted = False
|
|
338
418
|
self.why_not = 'FilteredByArgs'
|
|
339
|
-
|
|
340
|
-
## print(f'DBDB: {self.pid} wanted={self.wanted} why_not={self.why_not}')
|
|
419
|
+
|
|
341
420
|
self.set_key()
|
|
342
421
|
|
|
343
422
|
def set_key(self):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|