This file is indexed.

/usr/lib/python3/dist-packages/glances/processes_tree.py is in glances 2.7.1.1-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# -*- coding: utf-8 -*-
#
# This file is part of Glances.
#
# Copyright (C) 2016 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/>.

import collections

from glances.compat import iteritems

import psutil


class ProcessTreeNode(object):

    """Represent a process tree.

    We avoid recursive algorithm to manipulate the tree because function
    calls are expensive with CPython.
    """

    def __init__(self, process=None, stats=None, sort_key=None, sort_reverse=True, root=False):
        self.process = process
        self.stats = stats
        self.children = []
        self.children_sorted = False
        self.sort_key = sort_key
        self.sort_reverse = sort_reverse
        self.is_root = root

    def __str__(self):
        """Return the tree as a string for debugging."""
        lines = []
        nodes_to_print = collections.deque([collections.deque([("#", self)])])
        while nodes_to_print:
            indent_str, current_node = nodes_to_print[-1].pop()
            if not nodes_to_print[-1]:
                nodes_to_print.pop()
            if current_node.is_root:
                lines.append(indent_str)
            else:
                lines.append("%s[%s]" %
                             (indent_str, current_node.process.name()))
            indent_str = " " * (len(lines[-1]) - 1)
            children_nodes_to_print = collections.deque()
            for child in current_node.children:
                if child is current_node.children[-1]:
                    tree_char = "└─"
                else:
                    tree_char = "├─"
                children_nodes_to_print.appendleft(
                    (indent_str + tree_char, child))
            if children_nodes_to_print:
                nodes_to_print.append(children_nodes_to_print)
        return "\n".join(lines)

    def set_sorting(self, key, reverse):
        """Set sorting key or func for use with __iter__.

        This affects the whole tree from this node.
        """
        if self.sort_key != key or self.sort_reverse != reverse:
            nodes_to_flag_unsorted = collections.deque([self])
            while nodes_to_flag_unsorted:
                current_node = nodes_to_flag_unsorted.pop()
                current_node.children_sorted = False
                current_node.sort_key = key
                current_node.reverse_sorting = reverse
                nodes_to_flag_unsorted.extend(current_node.children)

    def get_weight(self):
        """Return 'weight' of a process and all its children for sorting."""
        if self.sort_key == 'name' or self.sort_key == 'username':
            return self.stats[self.sort_key]

        # sum ressource usage for self and children
        total = 0
        nodes_to_sum = collections.deque([self])
        while nodes_to_sum:
            current_node = nodes_to_sum.pop()
            if isinstance(self.sort_key, collections.Callable):
                total += self.sort_key(current_node.stats)
            elif self.sort_key == "io_counters":
                stats = current_node.stats[self.sort_key]
                total += stats[0] - stats[2] + stats[1] - stats[3]
            elif self.sort_key == "cpu_times":
                total += sum(current_node.stats[self.sort_key])
            else:
                total += current_node.stats[self.sort_key]
            nodes_to_sum.extend(current_node.children)

        return total

    def __len__(self):
        """Return the number of nodes in the tree."""
        total = 0
        nodes_to_sum = collections.deque([self])
        while nodes_to_sum:
            current_node = nodes_to_sum.pop()
            if not current_node.is_root:
                total += 1
            nodes_to_sum.extend(current_node.children)
        return total

    def __iter__(self):
        """Iterator returning ProcessTreeNode in sorted order, recursively."""
        if not self.is_root:
            yield self
        if not self.children_sorted:
            # optimization to avoid sorting twice (once when limiting the maximum processes to grab stats for,
            # and once before displaying)
            self.children.sort(
                key=self.__class__.get_weight, reverse=self.sort_reverse)
            self.children_sorted = True
        for child in self.children:
            for n in iter(child):
                yield n

    def iter_children(self, exclude_incomplete_stats=True):
        """Iterator returning ProcessTreeNode in sorted order.

        Return only children of this node, non recursive.

        If exclude_incomplete_stats is True, exclude processes not
        having full statistics. It can happen after a resort (change of
        sort key) because process stats are not grabbed immediately, but
        only at next full update.
        """
        if not self.children_sorted:
            # optimization to avoid sorting twice (once when limiting the maximum processes to grab stats for,
            # and once before displaying)
            self.children.sort(
                key=self.__class__.get_weight, reverse=self.sort_reverse)
            self.children_sorted = True
        for child in self.children:
            if not exclude_incomplete_stats or "time_since_update" in child.stats:
                yield child

    def find_process(self, process):
        """Search in tree for the ProcessTreeNode owning process.

        Return it or None if not found.
        """
        nodes_to_search = collections.deque([self])
        while nodes_to_search:
            current_node = nodes_to_search.pop()
            if not current_node.is_root and current_node.process.pid == process.pid:
                return current_node
            nodes_to_search.extend(current_node.children)

    @staticmethod
    def build_tree(process_dict, sort_key, sort_reverse, hide_kernel_threads, excluded_processes):
        """Build a process tree using using parent/child relationships.

        Return the tree root node.
        """
        tree_root = ProcessTreeNode(root=True)
        nodes_to_add_last = collections.deque()

        # first pass: add nodes whose parent are in the tree
        for process, stats in iteritems(process_dict):
            new_node = ProcessTreeNode(process, stats, sort_key, sort_reverse)
            try:
                parent_process = process.parent()
            except psutil.NoSuchProcess:
                # parent is dead, consider no parent
                parent_process = None
            if (parent_process is None) or (parent_process in excluded_processes):
                # no parent, or excluded parent, add this node at the top level
                tree_root.children.append(new_node)
            else:
                parent_node = tree_root.find_process(parent_process)
                if parent_node is not None:
                    # parent is already in the tree, add a new child
                    parent_node.children.append(new_node)
                else:
                    # parent is not in tree, add this node later
                    nodes_to_add_last.append(new_node)

        # next pass(es): add nodes to their parents if it could not be done in
        # previous pass
        while nodes_to_add_last:
            # pop from left and append to right to avoid infinite loop
            node_to_add = nodes_to_add_last.popleft()
            try:
                parent_process = node_to_add.process.parent()
            except psutil.NoSuchProcess:
                # parent is dead, consider no parent, add this node at the top
                # level
                tree_root.children.append(node_to_add)
            else:
                if (parent_process is None) or (parent_process in excluded_processes):
                    # no parent, or excluded parent, add this node at the top level
                    tree_root.children.append(node_to_add)
                else:
                    parent_node = tree_root.find_process(parent_process)
                    if parent_node is not None:
                        # parent is already in the tree, add a new child
                        parent_node.children.append(node_to_add)
                    else:
                        # parent is not in tree, add this node later
                        nodes_to_add_last.append(node_to_add)

        return tree_root