blob: 245397f5a5bc0c9ff23b2fd9aacd26ececd94620 [file] [log] [blame]
Allen Li51bb6122017-06-21 12:04:13 -07001# Copyright 2017 The Chromium OS 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
5"""Process metrics."""
6
7from __future__ import absolute_import
8from __future__ import print_function
9
10import psutil
11
12from chromite.lib import cros_logging as logging
Allen Lia9c6e802017-07-11 15:42:47 -070013from chromite.lib import metrics
Allen Li51bb6122017-06-21 12:04:13 -070014
15logger = logging.getLogger(__name__)
16
Allen Lia9c6e802017-07-11 15:42:47 -070017_count_metric = metrics.GaugeMetric(
Allen Li6bb74d52017-06-22 14:44:53 -070018 'proc/count',
Allen Li51bb6122017-06-21 12:04:13 -070019 description='Number of processes currently running.')
Allen Lia9c6e802017-07-11 15:42:47 -070020_cpu_percent_metric = metrics.GaugeMetric(
Allen Li6bb74d52017-06-22 14:44:53 -070021 'proc/cpu_percent',
22 description='CPU usage percent of processes.')
Allen Li51bb6122017-06-21 12:04:13 -070023
24
25def collect_proc_info():
Allen Li6bb74d52017-06-22 14:44:53 -070026 collector = _ProcessMetricsCollector()
27 collector.collect()
28
29
30class _ProcessMetricsCollector(object):
31 """Class for collecting process metrics."""
32
33 def __init__(self):
34 self._metrics = [
Allen Li22989bd2017-07-12 10:34:37 -070035 _ProcessMetric('autoserv',
36 test_func=_is_parent_autoserv),
37 _ProcessMetric('sysmon',
38 test_func=_is_sysmon),
39 _ProcessMetric('apache',
40 test_func=_is_apache),
Allen Li6bb74d52017-06-22 14:44:53 -070041 ]
42 self._other_metric = _ProcessMetric('other')
43
44 def collect(self):
45 for proc in psutil.process_iter():
46 self._collect_proc(proc)
47 self._flush()
48
49 def _collect_proc(self, proc):
Allen Li6bb74d52017-06-22 14:44:53 -070050 for metric in self._metrics:
Allen Lif8397a82017-07-13 13:19:44 -070051 if metric.add(proc):
52 break
53 else:
Allen Li6bb74d52017-06-22 14:44:53 -070054 self._other_metric.add(proc)
55
56 def _flush(self):
57 for metric in self._metrics:
58 metric.flush()
59 self._other_metric.flush()
60
61
62class _ProcessMetric(object):
63 """Class for gathering process metrics."""
64
65 def __init__(self, process_name, test_func=lambda proc: True):
66 """Initialize instance.
67
68 process_name is used to identify the metric stream.
69
70 test_func is a function called
71 for each process. If it returns True, the process is counted. The
72 default test is to count every process.
73 """
74 self._fields = {
Allen Li22989bd2017-07-12 10:34:37 -070075 'process_name': process_name,
Allen Li6bb74d52017-06-22 14:44:53 -070076 }
77 self._test_func = test_func
78 self._count = 0
79 self._cpu_percent = 0
80
81 def add(self, proc):
82 """Do metric collection for the given process.
83
84 Returns True if the process was collected.
85 """
86 if not self._test_func(proc):
87 return False
88 self._count += 1
89 self._cpu_percent += proc.cpu_percent()
90 return True
91
92 def flush(self):
93 """Finish collection and send metrics."""
94 _count_metric.set(self._count, fields=self._fields)
95 self._count = 0
Aviv Keshet0b634e92017-07-14 14:29:48 -070096 _cpu_percent_metric.set(int(round(self._cpu_percent)), fields=self._fields)
Allen Li6bb74d52017-06-22 14:44:53 -070097 self._cpu_percent = 0
Allen Li51bb6122017-06-21 12:04:13 -070098
99
100def _is_parent_autoserv(proc):
101 """Return whether proc is a parent (not forked) autoserv process."""
102 return _is_autoserv(proc) and not _is_autoserv(proc.parent())
103
104
105def _is_autoserv(proc):
106 """Return whether proc is an autoserv process."""
107 # This relies on the autoserv script being run directly. The script should
108 # be named autoserv exactly and start with a shebang that is /usr/bin/python,
109 # NOT /bin/env
110 return proc.name() == 'autoserv'
111
112
113def _is_apache(proc):
114 """Return whether a proc is an apache2 process."""
115 return proc.name() == 'apache2'
116
117
118def _is_sysmon(proc):
119 """Return whether proc is a sysmon process."""
Aviv Keshet70a91c52017-07-17 16:09:09 -0700120 cmdline = proc.cmdline()
121 return (cmdline and
122 cmdline[0].endswith('python') and
123 cmdline[1:3] == ['-m', 'chromite.scripts.sysmon'])