glances_processlist.py 11.6 KB
Newer Older
A
Alessio Sergi 已提交
1 2
# -*- coding: utf-8 -*-
#
3
# This file is part of Glances.
A
Alessio Sergi 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#
# Copyright (C) 2014 Nicolargo <nicolas@nicolargo.com>
#
# Glances is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Glances is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

A
PEP 257  
Alessio Sergi 已提交
20 21
"""Process list plugin."""

N
Nicolas Hennion 已提交
22
# Import sys libs
23
import os
A
Alessio Sergi 已提交
24 25
from datetime import timedelta

N
Nicolas Hennion 已提交
26
# Import Glances libs
27
from glances.core.glances_globals import glances_processes, is_linux, is_bsd, is_mac, is_windows
A
Alessio Sergi 已提交
28
from glances.plugins.glances_plugin import GlancesPlugin
A
Alessio Sergi 已提交
29 30 31


class Plugin(GlancesPlugin):
A
PEP 257  
Alessio Sergi 已提交
32 33

    """Glances' processes plugin.
A
Alessio Sergi 已提交
34 35 36 37

    stats is a list
    """

38
    def __init__(self, args=None):
A
PEP 257  
Alessio Sergi 已提交
39
        """Init the plugin."""
40
        GlancesPlugin.__init__(self, args=args)
A
Alessio Sergi 已提交
41 42 43 44

        # We want to display the stat in the curse interface
        self.display_curse = True

45
        # Note: 'glances_processes' is already init in the glances_processes.py script
46

47
    def reset(self):
A
PEP 257  
Alessio Sergi 已提交
48
        """Reset/init the stats."""
49
        self.stats = []
50 51

    def update(self):
A
PEP 257  
Alessio Sergi 已提交
52
        """Update processes stats using the input method."""
53 54
        # Reset stats
        self.reset()
55

56
        if self.get_input() == 'local':
57 58 59
            # Update stats using the standard system lib
            # Note: Update is done in the processcount plugin
            # Just return the processes list
N
Nicolargo 已提交
60
            self.stats = glances_processes.getlist()            
61
        elif self.get_input() == 'snmp':
N
Nicolargo 已提交
62
            # No SNMP grab for processes
63
            pass
64

65
        return self.stats
66

A
Alessio Sergi 已提交
67
    def msg_curse(self, args=None):
A
PEP 257  
Alessio Sergi 已提交
68
        """Return the dict to display in the curse interface."""
A
Alessio Sergi 已提交
69 70 71
        # Init the return message
        ret = []

72
        # Only process if stats exist and display plugin enable...
73
        if self.stats == [] or args.disable_process:
74 75
            return ret

A
Alessio Sergi 已提交
76
        # Compute the sort key
77 78 79
        try:
            args.process_sorted_by
        except AttributeError:
A
Alessio Sergi 已提交
80
            args.process_sorted_by = glances_processes.getsortkey()
81
        if args.process_sorted_by == 'auto':
N
Nicolas Hennion 已提交
82
            process_sort_key = glances_processes.getsortkey()
A
Alessio Sergi 已提交
83 84
        else:
            process_sort_key = args.process_sorted_by
85
        sort_style = 'SORT'
A
Alessio Sergi 已提交
86 87

        # Header
A
Alessio Sergi 已提交
88
        msg = '{0:>6}'.format(_("CPU%"))
A
Alessio Sergi 已提交
89
        ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'cpu_percent' else 'DEFAULT'))
A
Alessio Sergi 已提交
90
        msg = '{0:>6}'.format(_("MEM%"))
A
Alessio Sergi 已提交
91
        ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'memory_percent' else 'DEFAULT'))
A
Alessio Sergi 已提交
92
        msg = '{0:>6}'.format(_("VIRT"))
A
Alessio Sergi 已提交
93
        ret.append(self.curse_add_line(msg, optional=True))
A
Alessio Sergi 已提交
94
        msg = '{0:>6}'.format(_("RES"))
A
Alessio Sergi 已提交
95
        ret.append(self.curse_add_line(msg, optional=True))
A
Alessio Sergi 已提交
96
        msg = '{0:>6}'.format(_("PID"))
97
        ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
98
        msg = ' {0:10}'.format(_("USER"))
99
        ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
100
        msg = '{0:>4}'.format(_("NI"))
101
        ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
102
        msg = '{0:>2}'.format(_("S"))
103
        ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
104
        msg = '{0:>9}'.format(_("TIME+"))
A
Alessio Sergi 已提交
105
        ret.append(self.curse_add_line(msg, optional=True))
A
Alessio Sergi 已提交
106
        msg = '{0:>6}'.format(_("IOR/s"))
107
        ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True))
A
Alessio Sergi 已提交
108
        msg = '{0:>6}'.format(_("IOW/s"))
109
        ret.append(self.curse_add_line(msg, sort_style if process_sort_key == 'io_counters' else 'DEFAULT', optional=True, additional=True))
A
Alessio Sergi 已提交
110
        msg = ' {0:8}'.format(_("Command"))
111
        ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
112 113 114 115 116

        # Trying to display proc time
        tag_proc_time = True

        # Loop over processes (sorted by the sort key previously compute)
117
        for p in self.sortlist(process_sort_key):
A
Alessio Sergi 已提交
118 119
            ret.append(self.curse_new_line())
            # CPU
N
Nicolargo 已提交
120 121 122 123 124 125 126
            if 'cpu_percent' in p:
                msg = '{0:>6.1f}'.format(p['cpu_percent'])
                ret.append(self.curse_add_line(msg,
                                               self.get_alert(p['cpu_percent'], header="cpu")))
            else:
                msg = '{0:>6}'.format('?')
                ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
127
            # MEM
N
Nicolargo 已提交
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
            if 'memory_percent' in p:
                msg = '{0:>6.1f}'.format(p['memory_percent'])
                ret.append(self.curse_add_line(msg,
                                               self.get_alert(p['memory_percent'], header="mem")))
            else:
                msg = '{0:>6}'.format('?')
                ret.append(self.curse_add_line(msg))
            # VMS/RSS
            if 'memory_info' in p:
                # VMS
                msg = '{0:>6}'.format(self.auto_unit(p['memory_info'][1], low_precision=False))
                ret.append(self.curse_add_line(msg, optional=True))
                # RSS
                msg = '{0:>6}'.format(self.auto_unit(p['memory_info'][0], low_precision=False))
                ret.append(self.curse_add_line(msg, optional=True))
            else:
                msg = '{0:>6}'.format('?')
                ret.append(self.curse_add_line(msg))
                ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
147
            # PID
A
Alessio Sergi 已提交
148
            msg = '{0:>6}'.format(p['pid'])
149
            ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
150
            # USER
N
Nicolargo 已提交
151 152 153 154 155 156 157
            if 'username' in p:
                # docker internal users are displayed as ints only, therefore str()
                msg = ' {0:9}'.format(str(p['username'])[:9])
                ret.append(self.curse_add_line(msg))
            else:
                msg = ' {0:9}'.format('?')
                ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
158
            # NICE
N
Nicolargo 已提交
159 160 161 162 163 164 165 166 167 168
            if 'nice' in p:
                nice = p['nice']
                if nice is None:
                    nice = '?'
                msg = '{0:>5}'.format(nice)
                if isinstance(nice, int) and ((is_windows and nice != 32) or
                                              (not is_windows and nice != 0)):
                    ret.append(self.curse_add_line(msg, decoration='NICE'))
                else:
                    ret.append(self.curse_add_line(msg))
169
            else:
N
Nicolargo 已提交
170
                msg = '{0:>5}'.format('?')
171
                ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
172
            # STATUS
N
Nicolargo 已提交
173 174 175 176 177 178 179
            if 'status' in p:
                status = p['status']
                msg = '{0:>2}'.format(status)
                if status == 'R':
                    ret.append(self.curse_add_line(msg, decoration='STATUS'))
                else:
                    ret.append(self.curse_add_line(msg))
180
            else:
N
Nicolargo 已提交
181
                msg = '{0:>2}'.format('?')
182
                ret.append(self.curse_add_line(msg))
A
Alessio Sergi 已提交
183
            # TIME+
184
            if tag_proc_time:
A
Alessio Sergi 已提交
185 186 187 188 189 190 191
                try:
                    dtime = timedelta(seconds=sum(p['cpu_times']))
                except Exception:
                    # Catched on some Amazon EC2 server
                    # See https://github.com/nicolargo/glances/issues/87
                    tag_proc_time = False
                else:
A
Alessio Sergi 已提交
192
                    msg = '{0}:{1}.{2}'.format(str(dtime.seconds // 60 % 60),
A
Alessio Sergi 已提交
193 194 195
                                               str(dtime.seconds % 60).zfill(2),
                                               str(dtime.microseconds)[:2].zfill(2))
            else:
A
Alessio Sergi 已提交
196 197
                msg = ' '
            msg = '{0:>9}'.format(msg)
A
Alessio Sergi 已提交
198
            ret.append(self.curse_add_line(msg, optional=True))
N
Nicolas Hennion 已提交
199
            # IO read/write
200
            if 'io_counters' in p:
N
Nicolas Hennion 已提交
201 202
                # IO read
                io_rs = (p['io_counters'][0] - p['io_counters'][2]) / p['time_since_update']
203
                if io_rs == 0:
A
Alessio Sergi 已提交
204
                    msg = '{0:>6}'.format("0")
N
Nicolas Hennion 已提交
205
                else:
A
Alessio Sergi 已提交
206
                    msg = '{0:>6}'.format(self.auto_unit(io_rs, low_precision=False))
207
                ret.append(self.curse_add_line(msg, optional=True, additional=True))
N
Nicolas Hennion 已提交
208 209
                # IO write
                io_ws = (p['io_counters'][1] - p['io_counters'][3]) / p['time_since_update']
210
                if io_ws == 0:
A
Alessio Sergi 已提交
211
                    msg = '{0:>6}'.format("0")
N
Nicolas Hennion 已提交
212
                else:
A
Alessio Sergi 已提交
213
                    msg = '{0:>6}'.format(self.auto_unit(io_ws, low_precision=False))
214
                ret.append(self.curse_add_line(msg, optional=True, additional=True))
A
Alessio Sergi 已提交
215
            else:
A
Alessio Sergi 已提交
216
                msg = '{0:>6}'.format("?")
217 218
                ret.append(self.curse_add_line(msg, optional=True, additional=True))
                ret.append(self.curse_add_line(msg, optional=True, additional=True))
219

A
Alessio Sergi 已提交
220
            # Command line
221 222 223 224
            # If no command line for the process is available, fallback to
            # the bare process name instead
            cmdline = p['cmdline']
            if cmdline == "":
A
Alessio Sergi 已提交
225
                msg = ' {0}'.format(p['name'])
226
                ret.append(self.curse_add_line(msg, splittable=True))
227 228 229 230 231 232
            else:
                try:
                    cmd = cmdline.split()[0]
                    args = ' '.join(cmdline.split()[1:])
                    path, basename = os.path.split(cmd)
                    if os.path.isdir(path):
A
Alessio Sergi 已提交
233
                        msg = ' {0}'.format(path) + os.sep
234 235
                        ret.append(self.curse_add_line(msg, splittable=True))
                        ret.append(self.curse_add_line(basename, decoration='PROCESS', splittable=True))
236
                    else:
A
Alessio Sergi 已提交
237
                        msg = ' {0}'.format(basename)
238
                        ret.append(self.curse_add_line(msg, decoration='PROCESS', splittable=True))
239
                    msg = " {0}".format(args)
240
                    ret.append(self.curse_add_line(msg, splittable=True))
241
                except UnicodeEncodeError:
242
                    ret.append(self.curse_add_line("", splittable=True))
A
Alessio Sergi 已提交
243 244 245

        # Return the message with decoration
        return ret
246 247

    def sortlist(self, sortedby=None):
A
PEP 257  
Alessio Sergi 已提交
248
        """Return the stats sorted by sortedby variable."""
249
        if sortedby is None:
250 251 252 253
            # No need to sort...
            return self.stats

        sortedreverse = True
254
        if sortedby == 'name':
255 256
            sortedreverse = False

257
        if sortedby == 'io_counters':
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
            # Specific case for io_counters
            # Sum of io_r + io_w
            try:
                # Sort process by IO rate (sum IO read + IO write)
                listsorted = sorted(self.stats,
                                    key=lambda process: process[sortedby][0] -
                                    process[sortedby][2] + process[sortedby][1] -
                                    process[sortedby][3],
                                    reverse=sortedreverse)
            except Exception:
                listsorted = sorted(self.stats,
                                    key=lambda process: process['cpu_percent'],
                                    reverse=sortedreverse)
        else:
            # Others sorts
N
Nicolargo 已提交
273 274 275 276 277 278 279 280
            try:
                listsorted = sorted(self.stats,
                                    key=lambda process: process[sortedby],
                                    reverse=sortedreverse)
            except KeyError:
                listsorted = sorted(self.stats,
                                    key=lambda process: process['name'],
                                    reverse=False)
281 282 283

        self.stats = listsorted

A
Alessio Sergi 已提交
284
        return self.stats