1# Copyright 2013 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import atexit 6import inspect 7import logging 8import os 9 10from collections import defaultdict 11 12 13def GetChildPids(processes, pid): 14 """Returns all child processes of |pid| from the given |processes| list. 15 16 Args: 17 processes: A tuple of (pid, ppid, state) as generated by ps. 18 pid: The pid for which to get children. 19 20 Returns: 21 A list of child pids. 22 """ 23 child_dict = defaultdict(list) 24 for curr_pid, curr_ppid, state in processes: 25 if 'Z' in state: 26 continue # Ignore zombie processes 27 child_dict[int(curr_ppid)].append(int(curr_pid)) 28 queue = [pid] 29 child_ids = [] 30 while queue: 31 parent = queue.pop() 32 if parent in child_dict: 33 children = child_dict[parent] 34 queue.extend(children) 35 child_ids.extend(children) 36 return child_ids 37 38 39def GetPsOutputWithPlatformBackend(platform_backend, columns, pid): 40 """Returns output of the 'ps' command as a list of lines. 41 42 Args: 43 platform_backend: The platform backend (LinuxBasedPlatformBackend or 44 PosixPlatformBackend). 45 columns: A list of require columns, e.g., ['pid', 'pss']. 46 pid: If not None, returns only the information of the process with the pid. 47 """ 48 args = ['ps'] 49 args.extend(['-p', str(pid)] if pid != None else ['-e']) 50 for c in columns: 51 args.extend(['-o', c + '=']) 52 return platform_backend.RunCommand(args).splitlines() 53 54 55def EnableListingStrayProcessesUponExitHook(): 56 def _ListAllSubprocesses(): 57 try: 58 import psutil 59 except ImportError: 60 logging.warning( 61 'psutil is not installed on the system. Not listing possible ' 62 'leaked processes. To install psutil, see: ' 63 'https://pypi.python.org/pypi/psutil') 64 return 65 telemetry_pid = os.getpid() 66 parent = psutil.Process(telemetry_pid) 67 if hasattr(parent, 'children'): 68 children = parent.children(recursive=True) 69 else: # Some old version of psutil use get_children instead children. 70 children = parent.get_children() 71 if children: 72 leak_processes_info = [] 73 for p in children: 74 if inspect.ismethod(p.name): 75 name = p.name() 76 else: # Process.name is a property in old versions of psutil. 77 name = p.name 78 process_info = '%s (%s)' % (name, p.pid) 79 try: 80 if inspect.ismethod(p.cmdline): 81 cmdline = p.cmdline() 82 else: 83 cmdline = p.cmdline 84 process_info += ' - %s' % cmdline 85 except Exception as e: 86 logging.warning(str(e)) 87 leak_processes_info.append(process_info) 88 logging.error('Telemetry leaks these processes: %s', 89 ', '.join(leak_processes_info)) 90 91 atexit.register(_ListAllSubprocesses) 92