1 | # -*- coding: utf-8 -*- |
||
2 | # |
||
3 | # This file is part of Glances. |
||
4 | # |
||
5 | # Copyright (C) 2018 Angelo Poerio <[email protected]> |
||
6 | # |
||
7 | # Glances is free software; you can redistribute it and/or modify |
||
8 | # it under the terms of the GNU Lesser General Public License as published by |
||
9 | # the Free Software Foundation, either version 3 of the License, or |
||
10 | # (at your option) any later version. |
||
11 | # |
||
12 | # Glances is distributed in the hope that it will be useful, |
||
13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
15 | # GNU Lesser General Public License for more details. |
||
16 | # |
||
17 | # You should have received a copy of the GNU Lesser General Public License |
||
18 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
||
19 | |||
20 | """IRQ plugin.""" |
||
21 | |||
22 | import os |
||
23 | import operator |
||
0 ignored issues
–
show
introduced
by
Loading history...
|
|||
24 | |||
25 | from glances.globals import LINUX |
||
0 ignored issues
–
show
|
|||
26 | from glances.timer import getTimeSinceLastUpdate |
||
0 ignored issues
–
show
|
|||
27 | from glances.plugins.glances_plugin import GlancesPlugin |
||
0 ignored issues
–
show
|
|||
28 | |||
29 | |||
30 | class Plugin(GlancesPlugin): |
||
31 | """Glances IRQ plugin. |
||
32 | |||
33 | stats is a list |
||
34 | """ |
||
35 | |||
36 | def __init__(self, args=None, config=None): |
||
37 | """Init the plugin.""" |
||
38 | super(Plugin, self).__init__(args=args, |
||
39 | config=config, |
||
40 | stats_init_value=[]) |
||
41 | |||
42 | # We want to display the stat in the curse interface |
||
43 | self.display_curse = True |
||
44 | |||
45 | # Init the stats |
||
46 | self.irq = GlancesIRQ() |
||
47 | |||
48 | def get_key(self): |
||
49 | """Return the key of the list.""" |
||
50 | return self.irq.get_key() |
||
51 | |||
52 | @GlancesPlugin._check_decorator |
||
53 | @GlancesPlugin._log_result_decorator |
||
54 | def update(self): |
||
55 | """Update the IRQ stats.""" |
||
56 | # Init new stats |
||
57 | stats = self.get_init_value() |
||
58 | |||
59 | # IRQ plugin only available on GNU/Linux |
||
60 | if not LINUX: |
||
61 | return self.stats |
||
62 | |||
63 | if self.input_method == 'local': |
||
64 | # Grab the stats |
||
65 | stats = self.irq.get() |
||
66 | |||
67 | elif self.input_method == 'snmp': |
||
68 | # not available |
||
69 | pass |
||
70 | |||
71 | # Get the TOP 5 (by rate/s) |
||
72 | stats = sorted(stats, |
||
73 | key=operator.itemgetter('irq_rate'), |
||
74 | reverse=True)[:5] |
||
75 | |||
76 | # Update the stats |
||
77 | self.stats = stats |
||
78 | |||
79 | return self.stats |
||
80 | |||
81 | def update_views(self): |
||
0 ignored issues
–
show
|
|||
82 | """Update stats views.""" |
||
83 | # Call the father's method |
||
84 | super(Plugin, self).update_views() |
||
85 | |||
86 | def msg_curse(self, args=None, max_width=None): |
||
87 | """Return the dict to display in the curse interface.""" |
||
88 | # Init the return message |
||
89 | ret = [] |
||
90 | |||
91 | # Only available on GNU/Linux |
||
92 | # Only process if stats exist and display plugin enable... |
||
93 | if not LINUX or not self.stats or not self.args.enable_irq: |
||
94 | return ret |
||
95 | |||
96 | # Max size for the interface name |
||
97 | name_max_width = max_width - 7 |
||
98 | |||
99 | # Build the string message |
||
100 | # Header |
||
101 | msg = '{:{width}}'.format('IRQ', width=name_max_width) |
||
102 | ret.append(self.curse_add_line(msg, "TITLE")) |
||
103 | msg = '{:>9}'.format('Rate/s') |
||
104 | ret.append(self.curse_add_line(msg)) |
||
105 | |||
106 | for i in self.stats: |
||
107 | ret.append(self.curse_new_line()) |
||
108 | msg = '{:{width}}'.format(i['irq_line'][:name_max_width], |
||
109 | width=name_max_width) |
||
110 | ret.append(self.curse_add_line(msg)) |
||
111 | msg = '{:>9}'.format(str(i['irq_rate'])) |
||
112 | ret.append(self.curse_add_line(msg)) |
||
113 | |||
114 | return ret |
||
115 | |||
116 | |||
117 | class GlancesIRQ(object): |
||
118 | """This class manages the IRQ file.""" |
||
119 | |||
120 | IRQ_FILE = '/proc/interrupts' |
||
121 | |||
122 | def __init__(self): |
||
123 | """Init the class. |
||
124 | |||
125 | The stat are stored in a internal list of dict |
||
126 | """ |
||
127 | self.lasts = {} |
||
128 | self.reset() |
||
129 | |||
130 | def reset(self): |
||
131 | """Reset the stats.""" |
||
132 | self.stats = [] |
||
133 | self.cpu_number = 0 |
||
134 | |||
135 | def get(self): |
||
136 | """Return the current IRQ stats.""" |
||
137 | return self.__update() |
||
138 | |||
139 | def get_key(self): |
||
140 | """Return the key of the dict.""" |
||
141 | return 'irq_line' |
||
142 | |||
143 | def __header(self, line): |
||
144 | """Build the header (contain the number of CPU). |
||
145 | |||
146 | CPU0 CPU1 CPU2 CPU3 |
||
147 | 0: 21 0 0 0 IO-APIC 2-edge timer |
||
148 | """ |
||
149 | self.cpu_number = len(line.split()) |
||
150 | return self.cpu_number |
||
151 | |||
152 | def __humanname(self, line): |
||
153 | """Return the IRQ name, alias or number (choose the best for human). |
||
154 | |||
155 | IRQ line samples: |
||
156 | 1: 44487 341 44 72 IO-APIC 1-edge i8042 |
||
157 | LOC: 33549868 22394684 32474570 21855077 Local timer interrupts |
||
158 | """ |
||
159 | splitted_line = line.split() |
||
160 | irq_line = splitted_line[0].replace(':', '') |
||
161 | if irq_line.isdigit(): |
||
162 | # If the first column is a digit, use the alias (last column) |
||
163 | irq_line += '_{}'.format(splitted_line[-1]) |
||
164 | return irq_line |
||
165 | |||
166 | def __sum(self, line): |
||
167 | """Return the IRQ sum number. |
||
168 | |||
169 | IRQ line samples: |
||
170 | 1: 44487 341 44 72 IO-APIC 1-edge i8042 |
||
171 | LOC: 33549868 22394684 32474570 21855077 Local timer interrupts |
||
172 | FIQ: usb_fiq |
||
173 | """ |
||
174 | splitted_line = line.split() |
||
175 | try: |
||
176 | ret = sum(map(int, splitted_line[1:(self.cpu_number + 1)])) |
||
177 | except ValueError: |
||
178 | # Correct issue #1007 on some conf (Raspberry Pi with Raspbian) |
||
179 | ret = 0 |
||
180 | return ret |
||
181 | |||
182 | def __update(self): |
||
183 | """Load the IRQ file and update the internal dict.""" |
||
184 | self.reset() |
||
185 | |||
186 | if not os.path.exists(self.IRQ_FILE): |
||
187 | # Correct issue #947: IRQ file do not exist on OpenVZ container |
||
188 | return self.stats |
||
189 | |||
190 | try: |
||
191 | with open(self.IRQ_FILE) as irq_proc: |
||
192 | time_since_update = getTimeSinceLastUpdate('irq') |
||
193 | # Read the header |
||
194 | self.__header(irq_proc.readline()) |
||
195 | # Read the rest of the lines (one line per IRQ) |
||
196 | for line in irq_proc.readlines(): |
||
197 | irq_line = self.__humanname(line) |
||
198 | current_irqs = self.__sum(line) |
||
199 | irq_rate = int( |
||
200 | current_irqs - self.lasts.get(irq_line) |
||
201 | if self.lasts.get(irq_line) |
||
202 | else 0 // time_since_update) |
||
203 | irq_current = { |
||
204 | 'irq_line': irq_line, |
||
205 | 'irq_rate': irq_rate, |
||
206 | 'key': self.get_key(), |
||
207 | 'time_since_update': time_since_update |
||
208 | } |
||
209 | self.stats.append(irq_current) |
||
210 | self.lasts[irq_line] = current_irqs |
||
211 | except (OSError, IOError): |
||
212 | pass |
||
213 | |||
214 | return self.stats |
||
215 |