From 087fbdb6a1522c0d5dc0a9470b664313eb95286b Mon Sep 17 00:00:00 2001 From: Gowtham1729 Date: Thu, 7 Nov 2019 11:34:55 +0530 Subject: [PATCH 1/6] improved UI and included additional details in the process info --- ptop/interfaces/GUI.py | 87 ++++++++++++++++++++++++---------- ptop/plugins/process_sensor.py | 5 ++ 2 files changed, 67 insertions(+), 25 deletions(-) diff --git a/ptop/interfaces/GUI.py b/ptop/interfaces/GUI.py index c55f268..bb689a8 100644 --- a/ptop/interfaces/GUI.py +++ b/ptop/interfaces/GUI.py @@ -28,10 +28,13 @@ class ProcessDetailsInfoBox(npyscreen.PopupWide): SHOW_ATY = 0 DEFAULT_COLUMNS = PREVIOUS_TERMINAL_WIDTH - def __init__(self,local_ports,process_pid,open_files): + def __init__(self,local_ports,process_pid,process_name, open_files, child_processes, parent_processes): self._local_ports = local_ports self._process_pid = process_pid + self._process_name = process_name self._open_files = open_files + self._child_processes = child_processes + self._parent_processes = parent_processes super(ProcessDetailsInfoBox,self).__init__() def create(self,**kwargs): @@ -46,27 +49,41 @@ def create(self,**kwargs): str(self._process_pid))) #logger.info("Showing the local ports {0} and files opened and are being used by the process with pid {1}".format(self._local_ports,str(self._process_pid))) # self.details_box_heading = self.add(npyscreen.TitleText, name='No ports used by the process {0}'.format(str(self._process_pid)),) - self.details_box_heading = self.add(npyscreen.TitleText, name='Showing info for process with PID {0}'.format(str(self._process_pid))) + self.details_box_heading = self.add(npyscreen.TitleText, name='Showing info for process {0} with PID {1}'.format(self._process_name, str(self._process_pid))) self.details_box = self.add(npyscreen.BufferPager) - if len(self._local_ports) != 0 and len(self._open_files)!= 0: - self.details_box.values.extend(['System ports used by the process are:\n']) + + #displays all the child processes(including children of children) + self.details_box.values.extend(['Parent Processes: \n']) + if(self._parent_processes): + self.details_box.values.extend(["{:<7} - {}".format(parent.pid, parent.name()) for parent in self._parent_processes]) + else: + self.details_box.values.extend(['No Parent Process \n']) + self.details_box.values.extend('\n') + + #displays all the parent processes(including parent of parent) + self.details_box.values.extend(['Child Processes: \n']) + if(self._child_processes): + self.details_box.values.extend(["{:<7} - {}".format(child.pid, child.name()) for child in self._child_processes]) + else: + self.details_box.values.extend(['No child Processes \n']) + self.details_box.values.extend('\n') + + #displays all the local ports in use by process + self.details_box.values.extend(['System ports in use: \n']) + if(self._local_ports): self.details_box.values.extend(self._local_ports) - self.details_box.values.extend('\n') - self.details_box.values.extend(['Files opened by this process are:\n']) - self.details_box.values.extend('\n') - self.details_box.values.extend(self._open_files) - elif len(self._open_files) != 0: - self.details_box.values.extend(['Files opened by this process are:\n']) + else: + self.details_box.values.extend(['No System ports are in use\n']) + self.details_box.values.extend('\n') + + #displays all the local ports in use by process + self.details_box.values.extend(['Files opened by this process: \n']) + if(self._open_files): self.details_box.values.extend(self._open_files) - self.details_box.values.extend('\n') - self.details_box.values.extend(['The process is not using any System ports.\n']) - elif len(self._local_ports) != 0: - self.details_box.values.extend(['System ports used by the process are:\n']) - self.details_box.values.extend(self._local_ports) - self.details_box.values.extend('\n') - self.details_box.values.extend(['No files are opened by this process.\n']) else: - self.details_box.values.extend(['No system ports are used and no files are opened by this process.\n']) + self.details_box.values.extend(['No Files are opened by this process\n']) + self.details_box.values.extend('\n') + self.details_box.display() @@ -168,6 +185,23 @@ def _get_list_of_open_files(self,process_pid): opened_files_by_proces.append(i[0]) return opened_files_by_proces + def _get_list_of_child_processes(self, process_pid): + """ + Given the Process ID, return the list of all child process + """ + p = psutil.Process(process_pid) + child_processes = p.children(recursive=True) + return child_processes + + def _get_list_of_parent_processes(self, process_pid): + """ + Given the Process ID, return the list of all parent processes + """ + p = psutil.Process(process_pid) + parent_processes = p.parents() + return parent_processes + + def _sort_by_time(self,*args,**kwargs): # fuck .. that's why NPSManaged was required, i.e you can access the app instance within widgets self._logger.info("Sorting the process table by time") @@ -211,9 +245,12 @@ def _show_detailed_process_info(self,*args,**kwargs): # create method of self._logger = logging.getLogger(__name__) # self.process_details_view_helper = ProcessDetailsInfoBox(local_ports=['1','2','3']) process_pid = self._get_selected_process_pid() + process_name = psutil.Process(pid=process_pid).name() local_ports = self._get_local_ports_used_by_a_process(process_pid) open_files = self._get_list_of_open_files(process_pid) - self.process_details_view_helper = ProcessDetailsInfoBox(local_ports,process_pid,open_files) + child_processes = self._get_list_of_child_processes(process_pid) + parent_processes = self._get_list_of_parent_processes(process_pid) + self.process_details_view_helper = ProcessDetailsInfoBox(local_ports, process_pid, process_name, open_files, child_processes, parent_processes) self.process_details_view_helper.owner_widget = weakref.proxy(self) self.process_details_view_helper.display() self.process_details_view_helper.edit() @@ -458,7 +495,7 @@ def update(self): self.memory_chart.update(clear=True) #### Processes table #### - + self._processes_data = self.statistics['Process']['table'] # check sorting flags @@ -478,15 +515,16 @@ def update(self): # to keep things pre computed curtailed_processes_data = [] for proc in sorted_processes_data: - curtailed_processes_data.append("{0: <30} {1: >5}{6}{2: <10}{6}{3}{6}{4: >6.2f} % {6}{5}\ + curtailed_processes_data.append("{0: <30} {1: >5}{8}{2: <10}{8}{3}{8}{4: >6.2f} % {8}{5: >6.2f} % {8}{6: >6.2f} %{8}{7}\ ".format( (proc['name'][:25] + '...') if len(proc['name']) > 25 else proc['name'], proc['id'], proc['user'], proc['time'], proc['memory'], + proc['disk_read'], + proc['disk_write'], proc['local_ports'], - " "*int(5*self.X_SCALING_FACTOR)) - ) + " "*int(5*self.X_SCALING_FACTOR)),) if not self.processes_table.entry_widget.is_filtering_on(): self.processes_table.entry_widget.values = curtailed_processes_data # Set the processes data dictionary to uncurtailed processes data @@ -610,7 +648,7 @@ def draw(self): PROCESSES_INFO_WIDGET_REL_Y+PROCESSES_INFO_WIDGET_HEIGHT) ) self.processes_table = self.window.add(MultiLineActionWidget, - name="Processes ( name - PID - user - age - memory - system_ports )", + name="───────── Process ────────── PID ─────── user ──────────────────── age ───────── memory ─────── disk_read ──── disk_write ─── ports", relx=PROCESSES_INFO_WIDGET_REL_X, rely=PROCESSES_INFO_WIDGET_REL_Y, max_height=PROCESSES_INFO_WIDGET_HEIGHT, @@ -620,7 +658,6 @@ def draw(self): self.processes_table.entry_widget.scroll_exit = False self.cpu_chart.entry_widget.editable = False - ###### Actions widget ######### # By default this widget takes 3 lines and 1 line for text and 2 for the invisible boundary lines # So (tput lines - rely) should be at least 3 diff --git a/ptop/plugins/process_sensor.py b/ptop/plugins/process_sensor.py index 0569c21..4b529f2 100644 --- a/ptop/plugins/process_sensor.py +++ b/ptop/plugins/process_sensor.py @@ -67,6 +67,11 @@ def update(self): proc_info['local_ports'] = [x.laddr[1] for x in p.connections()] # Add information of the open files by the process proc_info['open_files'] = p.open_files() + #disk io stats + io_counters = p.io_counters() + disk_io_counters = psutil.disk_io_counters() + proc_info["disk_read"] = 100*(io_counters.read_bytes/disk_io_counters.read_bytes) + proc_info["disk_write"] = 100*(io_counters.write_bytes/disk_io_counters.write_bytes) proc_count += 1 # recording the info proc_info_list.append(proc_info) From 2081637f9d05132bd640435641a807a0bd778f35 Mon Sep 17 00:00:00 2001 From: Gowtham1729 Date: Thu, 7 Nov 2019 12:29:57 +0530 Subject: [PATCH 2/6] removed hardcoding in the process title --- ptop/plugins/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ptop/plugins/__init__.py b/ptop/plugins/__init__.py index f2be931..06b2209 100644 --- a/ptop/plugins/__init__.py +++ b/ptop/plugins/__init__.py @@ -8,6 +8,7 @@ from .process_sensor import process_sensor from .system_sensor import system_sensor from .network_sensor import network_sensor +from .temperature_sensor import temperature_sensor # Sensors List SENSORS_LIST = [ @@ -17,4 +18,5 @@ network_sensor, process_sensor, system_sensor, + # temperature_sensor, ] From 0b56826f2818daba5defa832e1706d5fb1954b1b Mon Sep 17 00:00:00 2001 From: Gowtham1729 Date: Thu, 7 Nov 2019 12:33:01 +0530 Subject: [PATCH 3/6] removed test plugin --- ptop/plugins/__init__.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/ptop/plugins/__init__.py b/ptop/plugins/__init__.py index 06b2209..f2be931 100644 --- a/ptop/plugins/__init__.py +++ b/ptop/plugins/__init__.py @@ -8,7 +8,6 @@ from .process_sensor import process_sensor from .system_sensor import system_sensor from .network_sensor import network_sensor -from .temperature_sensor import temperature_sensor # Sensors List SENSORS_LIST = [ @@ -18,5 +17,4 @@ network_sensor, process_sensor, system_sensor, - # temperature_sensor, ] From fdf44345cd6966176bc9ab2a2d0827ba2a576686 Mon Sep 17 00:00:00 2001 From: Gowtham1729 Date: Thu, 7 Nov 2019 12:33:31 +0530 Subject: [PATCH 4/6] removed hardcoding part in title --- ptop/interfaces/GUI.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ptop/interfaces/GUI.py b/ptop/interfaces/GUI.py index bb689a8..ec74103 100644 --- a/ptop/interfaces/GUI.py +++ b/ptop/interfaces/GUI.py @@ -648,7 +648,15 @@ def draw(self): PROCESSES_INFO_WIDGET_REL_Y+PROCESSES_INFO_WIDGET_HEIGHT) ) self.processes_table = self.window.add(MultiLineActionWidget, - name="───────── Process ────────── PID ─────── user ──────────────────── age ───────── memory ─────── disk_read ──── disk_write ─── ports", + name="{0} Process {1} PID {0} user {2} age {3} memory {4} disk_read {5} disk_write {5} ports" + .format("─"*int(6*self.X_SCALING_FACTOR), + "─"*int(8*self.X_SCALING_FACTOR), + "─"*int(9*self.X_SCALING_FACTOR), + "─"*int(11.5*self.X_SCALING_FACTOR), + "─"*int(5*self.X_SCALING_FACTOR), + "─"*int(3*self.X_SCALING_FACTOR), + + ), relx=PROCESSES_INFO_WIDGET_REL_X, rely=PROCESSES_INFO_WIDGET_REL_Y, max_height=PROCESSES_INFO_WIDGET_HEIGHT, From 80e22e7102b64eb53731916695f5645d48655951 Mon Sep 17 00:00:00 2001 From: Gowtham1729 Date: Thu, 7 Nov 2019 14:18:32 +0530 Subject: [PATCH 5/6] Included the Environmental Variables --- ptop/interfaces/GUI.py | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/ptop/interfaces/GUI.py b/ptop/interfaces/GUI.py index ec74103..526de03 100644 --- a/ptop/interfaces/GUI.py +++ b/ptop/interfaces/GUI.py @@ -28,13 +28,14 @@ class ProcessDetailsInfoBox(npyscreen.PopupWide): SHOW_ATY = 0 DEFAULT_COLUMNS = PREVIOUS_TERMINAL_WIDTH - def __init__(self,local_ports,process_pid,process_name, open_files, child_processes, parent_processes): + def __init__(self,local_ports,process_pid,process_name, open_files, child_processes, parent_processes, env_vars): self._local_ports = local_ports self._process_pid = process_pid self._process_name = process_name self._open_files = open_files self._child_processes = child_processes self._parent_processes = parent_processes + self._env_vars = env_vars super(ProcessDetailsInfoBox,self).__init__() def create(self,**kwargs): @@ -76,13 +77,21 @@ def create(self,**kwargs): self.details_box.values.extend(['No System ports are in use\n']) self.details_box.values.extend('\n') - #displays all the local ports in use by process + #displays all the Files opened by the process self.details_box.values.extend(['Files opened by this process: \n']) if(self._open_files): self.details_box.values.extend(self._open_files) else: self.details_box.values.extend(['No Files are opened by this process\n']) - self.details_box.values.extend('\n') + self.details_box.values.extend('\n') + + #displays all the environmental variables used by the process + self.details_box.values.extend(['Environmental variables of the process: \n']) + if(self._open_files): + self.details_box.values.extend(["{0:<25} = {1}".format(key, self._env_vars[key]) for key in list(self._env_vars.keys())]) + else: + self.details_box.values.extend(['No Environmental variables are present\n']) + self.details_box.values.extend('\n') self.details_box.display() @@ -201,6 +210,14 @@ def _get_list_of_parent_processes(self, process_pid): parent_processes = p.parents() return parent_processes + def _get_list_of_env_vars(self, process_pid): + """ + Given the Process ID, return the list of all environmental variables used by the process + """ + p = psutil.Process(process_pid) + env_vars = p.environ() + return env_vars + def _sort_by_time(self,*args,**kwargs): # fuck .. that's why NPSManaged was required, i.e you can access the app instance within widgets @@ -250,7 +267,8 @@ def _show_detailed_process_info(self,*args,**kwargs): open_files = self._get_list_of_open_files(process_pid) child_processes = self._get_list_of_child_processes(process_pid) parent_processes = self._get_list_of_parent_processes(process_pid) - self.process_details_view_helper = ProcessDetailsInfoBox(local_ports, process_pid, process_name, open_files, child_processes, parent_processes) + env_vars = self._get_list_of_env_vars(process_pid) + self.process_details_view_helper = ProcessDetailsInfoBox(local_ports, process_pid, process_name, open_files, child_processes, parent_processes, env_vars) self.process_details_view_helper.owner_widget = weakref.proxy(self) self.process_details_view_helper.display() self.process_details_view_helper.edit() From c80cb128c50f31f82ea32d5c99a3ccfad53f223b Mon Sep 17 00:00:00 2001 From: Gowtham1729 Date: Thu, 7 Nov 2019 14:41:50 +0530 Subject: [PATCH 6/6] changed the info box input style to dictionary --- ptop/interfaces/GUI.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/ptop/interfaces/GUI.py b/ptop/interfaces/GUI.py index 526de03..0f678e0 100644 --- a/ptop/interfaces/GUI.py +++ b/ptop/interfaces/GUI.py @@ -28,14 +28,14 @@ class ProcessDetailsInfoBox(npyscreen.PopupWide): SHOW_ATY = 0 DEFAULT_COLUMNS = PREVIOUS_TERMINAL_WIDTH - def __init__(self,local_ports,process_pid,process_name, open_files, child_processes, parent_processes, env_vars): - self._local_ports = local_ports - self._process_pid = process_pid - self._process_name = process_name - self._open_files = open_files - self._child_processes = child_processes - self._parent_processes = parent_processes - self._env_vars = env_vars + def __init__(self,processes_data): + self._local_ports = processes_data["local_ports"] + self._process_pid = processes_data["process_pid"] + self._process_name = processes_data["process_name"] + self._open_files = processes_data["open_files"] + self._child_processes = processes_data["child_processes"] + self._parent_processes = processes_data["parent_processes"] + self._env_vars = processes_data["env_vars"] super(ProcessDetailsInfoBox,self).__init__() def create(self,**kwargs): @@ -268,7 +268,8 @@ def _show_detailed_process_info(self,*args,**kwargs): child_processes = self._get_list_of_child_processes(process_pid) parent_processes = self._get_list_of_parent_processes(process_pid) env_vars = self._get_list_of_env_vars(process_pid) - self.process_details_view_helper = ProcessDetailsInfoBox(local_ports, process_pid, process_name, open_files, child_processes, parent_processes, env_vars) + processes_data = {"process_pid":process_pid, "process_name":process_name, "local_ports":local_ports, "open_files":open_files, "child_processes":child_processes, "parent_processes":parent_processes, "env_vars":env_vars} + self.process_details_view_helper = ProcessDetailsInfoBox(processes_data) self.process_details_view_helper.owner_widget = weakref.proxy(self) self.process_details_view_helper.display() self.process_details_view_helper.edit()