blob: 3c993c29aead64a84503efd4c553d44a7b641dc2 [file] [log] [blame]
Mike Frysingere58c0e22017-10-04 15:43:30 -04001# -*- coding: utf-8 -*-
Allen Li51bb6122017-06-21 12:04:13 -07002# Copyright 2017 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6"""Process metrics."""
7
8from __future__ import absolute_import
9from __future__ import print_function
10
11import psutil
12
13from chromite.lib import cros_logging as logging
Allen Lia9c6e802017-07-11 15:42:47 -070014from chromite.lib import metrics
Allen Li51bb6122017-06-21 12:04:13 -070015
16logger = logging.getLogger(__name__)
17
Allen Lia9c6e802017-07-11 15:42:47 -070018_count_metric = metrics.GaugeMetric(
Allen Li6bb74d52017-06-22 14:44:53 -070019 'proc/count',
Allen Li51bb6122017-06-21 12:04:13 -070020 description='Number of processes currently running.')
Allen Lia9c6e802017-07-11 15:42:47 -070021_cpu_percent_metric = metrics.GaugeMetric(
Allen Li6bb74d52017-06-22 14:44:53 -070022 'proc/cpu_percent',
23 description='CPU usage percent of processes.')
Allen Li51bb6122017-06-21 12:04:13 -070024
25
26def collect_proc_info():
Allen Li6bb74d52017-06-22 14:44:53 -070027 collector = _ProcessMetricsCollector()
28 collector.collect()
29
30
31class _ProcessMetricsCollector(object):
32 """Class for collecting process metrics."""
33
34 def __init__(self):
35 self._metrics = [
Allen Li22989bd2017-07-12 10:34:37 -070036 _ProcessMetric('autoserv',
37 test_func=_is_parent_autoserv),
38 _ProcessMetric('sysmon',
39 test_func=_is_sysmon),
40 _ProcessMetric('apache',
41 test_func=_is_apache),
Allen Li6bb74d52017-06-22 14:44:53 -070042 ]
43 self._other_metric = _ProcessMetric('other')
44
45 def collect(self):
46 for proc in psutil.process_iter():
47 self._collect_proc(proc)
48 self._flush()
49
50 def _collect_proc(self, proc):
Allen Li6bb74d52017-06-22 14:44:53 -070051 for metric in self._metrics:
Allen Lif8397a82017-07-13 13:19:44 -070052 if metric.add(proc):
53 break
54 else:
Allen Li6bb74d52017-06-22 14:44:53 -070055 self._other_metric.add(proc)
56
57 def _flush(self):
58 for metric in self._metrics:
59 metric.flush()
60 self._other_metric.flush()
61
62
63class _ProcessMetric(object):
64 """Class for gathering process metrics."""
65
66 def __init__(self, process_name, test_func=lambda proc: True):
67 """Initialize instance.
68
69 process_name is used to identify the metric stream.
70
71 test_func is a function called
72 for each process. If it returns True, the process is counted. The
73 default test is to count every process.
74 """
75 self._fields = {
Allen Li22989bd2017-07-12 10:34:37 -070076 'process_name': process_name,
Allen Li6bb74d52017-06-22 14:44:53 -070077 }
78 self._test_func = test_func
79 self._count = 0
80 self._cpu_percent = 0
81
82 def add(self, proc):
83 """Do metric collection for the given process.
84
85 Returns True if the process was collected.
86 """
87 if not self._test_func(proc):
88 return False
89 self._count += 1
90 self._cpu_percent += proc.cpu_percent()
91 return True
92
93 def flush(self):
94 """Finish collection and send metrics."""
95 _count_metric.set(self._count, fields=self._fields)
96 self._count = 0
Aviv Keshet0b634e92017-07-14 14:29:48 -070097 _cpu_percent_metric.set(int(round(self._cpu_percent)), fields=self._fields)
Allen Li6bb74d52017-06-22 14:44:53 -070098 self._cpu_percent = 0
Allen Li51bb6122017-06-21 12:04:13 -070099
100
101def _is_parent_autoserv(proc):
102 """Return whether proc is a parent (not forked) autoserv process."""
103 return _is_autoserv(proc) and not _is_autoserv(proc.parent())
104
105
106def _is_autoserv(proc):
107 """Return whether proc is an autoserv process."""
108 # This relies on the autoserv script being run directly. The script should
109 # be named autoserv exactly and start with a shebang that is /usr/bin/python,
110 # NOT /bin/env
111 return proc.name() == 'autoserv'
112
113
114def _is_apache(proc):
115 """Return whether a proc is an apache2 process."""
116 return proc.name() == 'apache2'
117
118
119def _is_sysmon(proc):
120 """Return whether proc is a sysmon process."""
Aviv Keshet70a91c52017-07-17 16:09:09 -0700121 cmdline = proc.cmdline()
122 return (cmdline and
123 cmdline[0].endswith('python') and
124 cmdline[1:3] == ['-m', 'chromite.scripts.sysmon'])