Projet

Général

Profil

Paste
Télécharger au format
Statistiques
| Branche: | Révision:

root / plugins / system / cpu @ f14628ad

Historique | Voir | Annoter | Télécharger (31,1 ko)

1 6552dd3f Gorlow Maxim aka Sheridan
#!/usr/bin/perl -w
2
# -*- perl -*-
3
4
=head1 NAME
5
6
cpu - Plugin to monitor CPU usage and frequencies.
7
8
=head1 APPLICABLE SYSTEMS
9
10
All Linux systems, but read below section
11
12
=head1 CONFIGURATION
13
14
The plugin automatically selects which graphics drawing.
15
Charts related to the frequencies of processors depends on the settings of the kernel:
16
Power management and ACPI options -> CPU Frequency scaling -> CPU frequency translation statistics -> Advanced statistics
17
18
=head2 WARNING AND CRITICAL SETTINGS
19
20
You can set warning and critical levels for each of the data
21
series the plugin reports.  The following environment variables are
22
used as default for all fields:
23
24
  env.warning
25
  env.critical
26
27 1a5575ed Gorlow Maxim aka Sheridan
But each field (system user nice etc...) can be controlled separately:
28 6552dd3f Gorlow Maxim aka Sheridan
For example:
29
30
  env.system_warning 70
31
  env.user_warning 70
32
  env.idle_critical 1
33
34 1a5575ed Gorlow Maxim aka Sheridan
Also each field of each cpu can be controlled separately
35
For example:
36
37
  env.cpu1_system_warning 70
38
  env.cpu0_user_warning 70
39
  env.cpu0_idle_critical 1
40
41
Algoritm is easy: for example current graph limit is env.cpu0_idle_critical if defined env.cpu0_idle_critical or env.idle_critical if defined env.idle_critical
42
or env.critical if defined env.critical. Or no limit
43
44 6552dd3f Gorlow Maxim aka Sheridan
=head1 INTERPRETATION
45
46
The plugin shows each cpu usage in percent, shows the CPU frequency, 
47
frequency shift frequencies, the percentage of use of frequencies
48
49
=head1 MAGIC MARKERS
50
51
  #%# family=auto
52
  #%# capabilities=autoconf
53
54
55
=head1 VERSION
56
57 1a5575ed Gorlow Maxim aka Sheridan
  2.0
58 6552dd3f Gorlow Maxim aka Sheridan
59
=head1 BUGS
60
61
none known
62
63
=head1 AUTHOR
64
65
Gorlow Maxim aka Sheridan <sheridan@sheridan-home.ru> (email and jabber)
66
67
=head1 LICENSE
68
69
GPLv2
70
71
=cut
72
73
use strict;
74
use warnings;
75
use Munin::Plugin;
76
use Data::Dumper;
77
my @cnames = qw(user nice system idle iowait irq softirq steal guest);
78
my $stat_file = "/proc/stat";
79
my $freq_path = "/sys/devices/system/cpu";
80
my $limits = {};
81
my $cpuinfo = {}; $cpuinfo->{'cpu_count'} = 0;
82
my @stat_file_content = ();
83
my $freq_mul = 1000; # CPU frequency multiplier from kHz to Hz
84
85 1a5575ed Gorlow Maxim aka Sheridan
86 6552dd3f Gorlow Maxim aka Sheridan
87
my $graphs =
88
{
89
  'cpu_utilisation' => # multigraph
90
  {
91
    'title'  => 'CPU:t: utilisation',
92
    'args'   =>  '--base 1000 -r --lower-limit 0 --upper-limit 100',
93
    'vlabel' => '%',
94
    'scale'  => 'no',
95
    'info'   => 'This graph shows how CPU:t: time is spent :i:'
96
  },
97
  'cpu_all' => # single
98
  {
99
    'title'    => 'All CPU utilisation',
100
    'args'     =>  '--base 1000 -r --lower-limit 0 --upper-limit 100',
101
    'vlabel'   => '%',
102
    'scale'    => 'no',
103
    'info'     => 'This graph shows how CPU time is spent on each processor',
104
    'category' => 'cpu'
105
  },
106
  'cpu_freq_trans' => # multi
107
  {
108
    'title'    => 'CPU frequency transitions',
109
    'args'     =>  '--base 1000',
110
    'vlabel'   => 'count',
111
    'scale'    => 'no',
112
    'info'     => 'This graph shows CPU transitions of each processor',
113
  },
114
  'cpu_freq' => # child of cpu_freq_trans
115
  {
116 1a5575ed Gorlow Maxim aka Sheridan
    'title'    => 'CPU:t: frequency (total)',
117 6552dd3f Gorlow Maxim aka Sheridan
    'args'     =>  '--base 1000 -r --lower-limit 0 --upper-limit 100',
118 1a5575ed Gorlow Maxim aka Sheridan
    'vlabel'   => '% of total',
119 6552dd3f Gorlow Maxim aka Sheridan
    'info'     => 'This graph shows CPU:t: frequency :i:',
120
    'category' => 'cpu'
121
  },
122 1a5575ed Gorlow Maxim aka Sheridan
  'cpu_freq_ps' => # child of cpu_freq_trans
123
  {
124
    'title'    => 'CPU:t: frequency (per secund)',
125
    'args'     =>  '--base 1000 -r --lower-limit 0 --upper-limit 100',
126
    'vlabel'   => '% per secund',
127
    'info'     => 'This graph shows CPU:t: frequency per secund from last update :i:',
128
    'category' => 'cpu'
129
  },
130 6552dd3f Gorlow Maxim aka Sheridan
  'cpu_freq_trans_table' => # child of cpu_freq_trans
131
  {
132 1a5575ed Gorlow Maxim aka Sheridan
    'title'    => 'CPU:t: frequency switches (total)',
133 6552dd3f Gorlow Maxim aka Sheridan
    'args'     =>  '--base 1000 -r --lower-limit 0 --upper-limit 100',
134 1a5575ed Gorlow Maxim aka Sheridan
    'vlabel'   => '% of total',
135 6552dd3f Gorlow Maxim aka Sheridan
    'scale'    => 'no',
136
    'info'     => 'This graph shows CPU:t: frequency switches :i:',
137 1a5575ed Gorlow Maxim aka Sheridan
  },
138
  'cpu_freq_trans_table_ps' => # child of cpu_freq_trans
139
  {
140
    'title'    => 'CPU:t: frequency switches (per secund)',
141
    'args'     =>  '--base 1000 -r --lower-limit 0 --upper-limit 100',
142
    'vlabel'   => '% per secund',
143
    'scale'    => 'no',
144
    'info'     => 'This graph shows CPU:t: frequency switches per secund from last update :i:',
145 6552dd3f Gorlow Maxim aka Sheridan
  }
146
};
147
148
my $transparent = 'CC';
149
my $fields =
150
{
151
  'user' =>
152
  {
153
    'label' => 'User',
154
    'info'  => 'Normal processes executing in user mode',
155
    'type'  => 'GAUGE',
156
    'draw'  => 'AREASTACK',
157
    'min'   => 0,
158
    'max'   => 100
159
  },
160
  'nice' =>
161
  {
162
    'label' => 'Nice',
163
    'info'  => 'Niced processes executing in user mode',
164
    'type'  => 'GAUGE',
165
    'draw'  => 'AREASTACK',
166
    'min'   => 0,
167
    'max'   => 100
168
  },
169
  'system' =>
170
  {
171
    'label' => 'System',
172
    'info'  => 'Processes executing in kernel mode',
173
    'type'  => 'GAUGE',
174
    'draw'  => 'AREASTACK',
175
    'min'   => 0,
176
    'max'   => 100
177
  },
178
  'idle' =>
179
  {
180
    'label' => 'Idle',
181
    'info'  => 'Twiddling thumbs',
182
    'type'  => 'GAUGE',
183
    'draw'  => 'AREASTACK',
184
    'colour'=> 'FFFFDD'.$transparent,
185
    'min'   => 0,
186
    'max'   => 100
187
  },
188
  'iowait' =>
189
  {
190
    'label' => 'I/O wait',
191
    'info'  => 'Waiting for I/O to complete',
192
    'type'  => 'GAUGE',
193
    'draw'  => 'AREASTACK',
194
    'min'   => 0,
195
    'max'   => 100
196
  },
197
  'irq' =>
198
  {
199
    'label' => 'IRQ',
200
    'info'  => 'Servicing interrupts',
201
    'type'  => 'GAUGE',
202
    'draw'  => 'AREASTACK',
203
    'min'   => 0,
204
    'max'   => 100
205
  },
206
  'softirq' =>
207
  {
208
    'label' => 'Software IRQ',
209
    'info'  => 'Servicing software interrupts',
210
    'type'  => 'GAUGE',
211
    'draw'  => 'AREASTACK',
212
    'min'   => 0,
213
    'max'   => 100
214
  },
215
  'steal' =>
216
  {
217
    'label' => 'Steal',
218
    'info'  => 'Involuntary wait',
219
    'type'  => 'GAUGE',
220
    'draw'  => 'AREASTACK',
221
    'min'   => 0,
222
    'max'   => 100
223
  },
224
  'guest' =>
225
  {
226
    'label' => 'Guest',
227
    'info'  => 'Running a guest',
228
    'type'  => 'GAUGE',
229
    'draw'  => 'AREASTACK',
230
    'min'   => 0,
231
    'max'   => 100
232
  },
233
  'cpu_util' =>
234
  {
235
    'label' => ':t:',
236
    'info'  => ':t: utilisation',
237
    'type'  => 'GAUGE',
238
    'draw'  => 'LINE0.5',
239
    'min'   => 0,
240
    'max'   => 100
241
  },
242
  'freq_percent' =>
243
  {
244
    'label' => 'CPU:t: frequency',
245
    'info'  => 'CPU:t: frequency percent',
246
    'type'  => 'GAUGE',
247
    'draw'  => 'LINE0.5',
248
    'min'   => 0,
249
    'max'   => 100
250
  },
251
  'freq_hz' =>
252
  {
253
    'label' => ':t:',
254
    'info'  => 'CPU :t: frequency',
255
    'type'  => 'GAUGE',
256
    'draw'  => 'AREASTACK',
257
    'min'   => 0,
258
    'max'   => 100
259
  },
260
  'freq_trans' =>
261
  {
262
    'label' => ':t:',
263
    'info'  => ':t: frequency transitions',
264
    'type'  => 'GAUGE',
265
    'draw'  => 'LINE0.5',
266
    'min'   => 0
267
  },
268
  'freq_trans_table' =>
269
  {
270
    'label' => ':t:',
271
    'info'  => ':t: frequency switch',
272
    'type'  => 'GAUGE',
273
    'draw'  => 'AREASTACK',
274
    'min'   => 0,
275
    'max'   => 100
276
  }
277
  
278
};
279
280
281
282
# ----------------- main ----------------
283
load_cpuinfo();
284
need_multigraph();
285
remove_unavialabled_counters();
286
287
if (defined($ARGV[0]) and ($ARGV[0] eq 'autoconf')) 
288
{
289
  printf("%s\n", -e $stat_file ? "yes" : "no (stats not exists)");
290
  exit (0);
291
}
292
293
if (defined($ARGV[0]) and ($ARGV[0] eq 'config')) 
294
{
295
  print_config();
296
  exit (0);
297
}
298
299
print_values();
300
#print Dumper prepare_graphs_fields();
301
exit(0);
302
303
# ----------------- sub's ----------------
304
305
# ----------------------- trim whitespace at begin and end of string ------------
306
sub trim
307
{
308
  my($string)=@_;
309
  for ($string) { s/^\s+//; s/\s+$//; }
310
  return $string;
311
}
312
313
314
my $items_exists = {};
315
sub check_exists
316
{
317
  my ($t, $cpu_num) = @_[0..1];
318
  if (defined($cpu_num))
319
  {
320
    unless (exists($items_exists->{$t}{$cpu_num}))
321
    {
322 1a5575ed Gorlow Maxim aka Sheridan
      if ($t eq 'freq_hz')        { $items_exists->{$t}{$cpu_num} = ( -e sprintf("%s/cpu%s/cpufreq/scaling_min_freq"   , $freq_path, $cpu_num) and
323
                                                                      -e sprintf("%s/cpu%s/cpufreq/scaling_cur_freq"   , $freq_path, $cpu_num) and
324
                                                                      -e sprintf("%s/cpu%s/cpufreq/scaling_max_freq"   , $freq_path, $cpu_num));  }
325
      elsif ($t eq 'freq_trans')  { $items_exists->{$t}{$cpu_num} =   -e sprintf("%s/cpu%s/cpufreq/stats/time_in_state", $freq_path, $cpu_num);   }
326
      elsif ($t eq 'freq_times')  { $items_exists->{$t}{$cpu_num} =   -e sprintf("%s/cpu%s/cpufreq/stats/total_trans"  , $freq_path, $cpu_num);   }
327
      elsif ($t eq 'freq_ttable') { $items_exists->{$t}{$cpu_num} =   -e sprintf("%s/cpu%s/cpufreq/stats/trans_table"  , $freq_path, $cpu_num);   }
328 6552dd3f Gorlow Maxim aka Sheridan
    }
329
    return $items_exists->{$t}{$cpu_num};
330
  }
331
  else
332
  {
333
    unless(exists($items_exists->{$t}{'total'}))
334
    {
335
      my $c = 0;
336 1a5575ed Gorlow Maxim aka Sheridan
      for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) { $c++ if (check_exists($t, $i)); }
337 6552dd3f Gorlow Maxim aka Sheridan
      $items_exists->{$t}{'total'} = $c > 0;
338
    }
339
    return $items_exists->{$t}{'total'};
340
  }
341
}
342
343
# ------------------------- remove unavialable fields from graph --------------------------
344
sub remove_unavialabled_counters
345
{
346
  my @cpu = split(/\s+/, (grep(/cpu\s/, get_stat_file_content()))[0]);
347
  my $counters_count = scalar(@cpu) - 3;
348
  @cnames = @cnames[0..$counters_count];
349
}
350
351
# ----------------------- get sysfs file content ----------------
352
my $fcontents = {};
353
sub get_sys_file_content
354
{
355
  my $file = $_[0];
356
  return 'nan' if (-z $file);
357
  unless (exists($fcontents->{$file}))
358
  {
359
    open (FH, '<', $file) or die "$! $file \n";
360
    $fcontents->{$file} = <FH>;
361
    close (FH);
362
    chomp $fcontents->{$file};
363
  }
364
  return $fcontents->{$file};
365
}
366
367
# -------------------------- loading cpu info ---------------------------------
368
sub load_cpuinfo
369
{
370
  my $file = "/proc/cpuinfo";
371
  open (FH, '<', $file) or die "$! $file \n";
372
  my $cpu_num = -1;
373
  for my $line (<FH>)
374
  {
375
    chomp $line;
376
    $cpu_num++ if $line =~ m/^processor\s+:/;
377
    $cpuinfo->{$cpu_num}{'name'}     = trim((split(/:/,$line))[1]) if $line =~ m/^model name\s+:/;
378
    $cpuinfo->{$cpu_num}{'bogomips'} = trim((split(/:/,$line))[1]) if $line =~ m/^bogomips\s+:/;
379
    if (not exists($cpuinfo->{$cpu_num}{'info'}) and exists($cpuinfo->{$cpu_num}{'name'}) and exists($cpuinfo->{$cpu_num}{'bogomips'}))
380
    {
381
      $cpuinfo->{$cpu_num}{'info'} = sprintf("[%s (%s bogomips)]", $cpuinfo->{$cpu_num}{'name'}, $cpuinfo->{$cpu_num}{'bogomips'}) ;
382
    }
383
  }
384
  close (FH);
385
  $cpuinfo->{'cpu_count'} = $cpu_num+1;
386
}
387
388
389
# -------------------------- loading stat file lines ---------------------------------
390
sub get_stat_file_content
391
{
392
  if(scalar(@stat_file_content) == 0)
393
  {
394
    open (FH, '<', $stat_file) or die "$! $stat_file \n";
395
    for (<FH>)
396
    {
397
      next unless $_ =~ m/cpu/;
398
      chomp $_;
399
      push(@stat_file_content, $_);
400
    }
401
    close (FH);
402
  }
403
  return @stat_file_content;
404
}
405
406
# -------------------------------- replacing strings ------------------------
407
sub replace_template
408
{
409
  my ($string, $needle, $replacement) = @_[0..2];
410
  $string =~ s/$needle/$replacement/g;
411
  return $string;
412
}
413
414
sub replace_templates
415
{
416
  my ($src, $replacement_t, $replacement_i) = @_[0..2];
417
  my $dst = {};
418
  for my $key ( keys %{$src} )
419
  {
420
    $dst->{$key} = $src->{$key};
421
    if($key =~ m/label|info|title/) { $dst->{$key} = replace_template($dst->{$key}, ':t:', $replacement_t); }
422
    if($key =~ m/info|title/)       { $dst->{$key} = replace_template($dst->{$key}, ':i:', $replacement_i); }
423
  }
424
  return $dst;
425
}
426
427
sub append_order
428
{
429
  my ($pg, $graph_name, $field_name) = @_[0..2];
430
  $pg->{$graph_name}{'graph'}{'order'} = exists($pg->{$graph_name}{'graph'}{'order'}) ? sprintf("%s %s", $pg->{$graph_name}{'graph'}{'order'}, $field_name) : $field_name;
431
}
432
433
sub append_field
434
{
435
  my ($pg, $graph_name, $field_name, $field_src, $replacement_t, $replacement_i) = @_[0..5];
436
  $pg->{$graph_name}{'fields'}{$field_name} = replace_templates($fields->{$field_src}, $replacement_t, $replacement_i);
437
  append_order($pg, $graph_name, $field_name);
438
}
439
440
sub append_graph
441
{
442
  my ($pg, $graph_name, $category, $graph_src, $replacement_t, $replacement_i) = @_[0..5];
443
  $pg->{$graph_name}{'graph'} = replace_templates($graphs->{$graph_src}, $replacement_t, $replacement_i);
444
  $pg->{$graph_name}{'graph'}{'category'} = $category;
445
}
446
447
# ---------------------------------------- preparing data for graphs ------------------------------------
448
sub prepare_graphs_fields
449
{
450
  my $pg = {};
451
  # ------------------ cpu_utilisation -----------------------------------
452
  # ---------- general ----------------------
453
  append_graph($pg, 'cpu_utilisation', 'cpu', 'cpu_utilisation', '', '');
454 1a5575ed Gorlow Maxim aka Sheridan
  for my $cname (@cnames) { append_field($pg, 'cpu_utilisation', $cname, $cname, '', ''); append_utilisation_limits($pg, 'cpu_utilisation', $cname, undef); }
455 6552dd3f Gorlow Maxim aka Sheridan
  if(check_exists('freq_hz')) { for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { append_field($pg, 'cpu_utilisation', sprintf("fp_%s", $i), 'freq_percent', $i, ''); } }
456
  # ---------------- childs -------------------
457
  if ($cpuinfo->{'cpu_count'} > 1)
458
  {
459
    for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++)
460
    {
461
      my $graph_name = sprintf("cpu_utilisation.cpu%s", $i);
462
      append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_utilisation', $i, $cpuinfo->{$i}{'info'});
463 1a5575ed Gorlow Maxim aka Sheridan
      for my $cname (@cnames) { append_field($pg, $graph_name, $cname, $cname, '', ''); append_utilisation_limits($pg, $graph_name, $cname, $i); }
464 6552dd3f Gorlow Maxim aka Sheridan
      if(check_exists('freq_hz', $i)) { append_field($pg, $graph_name, 'fp', 'freq_percent', '', ''); }
465
    }
466
  }
467
  
468
  
469
  # --------------- cpu_frequency --------------------------------------------
470
  if(check_exists('freq_trans'))
471
  { 
472
    # ------------ general --------------------
473 1a5575ed Gorlow Maxim aka Sheridan
    # - cpu frequency transitions -
474 6552dd3f Gorlow Maxim aka Sheridan
    append_graph($pg, 'cpu_frequency', 'cpu', 'cpu_freq_trans', '', '');
475
    for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { if (check_exists('freq_trans', $i)) { append_field($pg, 'cpu_frequency', sprintf("cpu_%s", $i), 'freq_trans', sprintf("CPU%s", $i), ''); } }
476
    append_field($pg, 'cpu_frequency', 'total', 'freq_trans', 'Total', '');
477
    # ---------------- childs -------------------
478
    if(check_exists('freq_times'))
479
    {
480
      my $frequences = get_frequency_times();
481
      for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++)
482
      {
483
        if(check_exists('freq_times', $i))
484
        {
485 1a5575ed Gorlow Maxim aka Sheridan
          # - cpu frequencyes -
486 6552dd3f Gorlow Maxim aka Sheridan
          my $graph_name = sprintf("cpu_frequency.percent_cpu%s", $i);
487
          append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_freq', $i, $cpuinfo->{$i}{'info'});
488
          for my $freq (@{$frequences->{'names'}{$i}}) { append_field($pg, $graph_name, sprintf("hz_%s", $freq), 'freq_hz', scaleNumber($freq, 'Hz'), ''); }
489 1a5575ed Gorlow Maxim aka Sheridan
          # - cpu frequencyes per secund -
490
          $graph_name = sprintf("cpu_frequency.percent_ps_cpu%s", $i);
491
          append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_freq_ps', $i, $cpuinfo->{$i}{'info'});
492
          for my $freq (@{$frequences->{'names'}{$i}}) { append_field($pg, $graph_name, sprintf("hz_%s", $freq), 'freq_hz', scaleNumber($freq, 'Hz'), ''); }
493 6552dd3f Gorlow Maxim aka Sheridan
        }
494
      }
495
    }
496
    if(check_exists('freq_ttable'))
497
    {
498 1a5575ed Gorlow Maxim aka Sheridan
      my $f_table = get_frequency_trans_table();
499 6552dd3f Gorlow Maxim aka Sheridan
      for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++)
500
      {
501
        if(check_exists('freq_ttable', $i))
502
        {
503 1a5575ed Gorlow Maxim aka Sheridan
          # - cpu frequencyes table -
504 6552dd3f Gorlow Maxim aka Sheridan
          my $graph_name = sprintf("cpu_frequency.trans_table_cpu%s", $i);
505
          append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_freq_trans_table', $i, $cpuinfo->{$i}{'info'});
506
          for my $from (sort keys %{$f_table->{'values'}{$i}})
507
          {
508
            for my $to (sort keys %{$f_table->{'values'}{$i}{$from}})
509
            {
510
              next if ($from eq $to);
511 1a5575ed Gorlow Maxim aka Sheridan
              append_field($pg, $graph_name, sprintf("f_%s_t_%s", $from, $to), 'freq_trans_table', sprintf(". %9s -> %s", scaleNumber($from, 'Hz'), scaleNumber($to, 'Hz')), '');
512
            }
513
          }
514
          # - cpu frequencyes table per secund -
515
          $graph_name = sprintf("cpu_frequency.trans_table_ps_cpu%s", $i);
516
          append_graph($pg, $graph_name, sprintf("CPU %s", $i), 'cpu_freq_trans_table_ps', $i, $cpuinfo->{$i}{'info'});
517
          for my $from (sort keys %{$f_table->{'values'}{$i}})
518
          {
519
            for my $to (sort keys %{$f_table->{'values'}{$i}{$from}})
520
            {
521
              next if ($from eq $to);
522
              append_field($pg, $graph_name, sprintf("f_%s_t_%s", $from, $to), 'freq_trans_table', sprintf(". %9s -> %s", scaleNumber($from, 'Hz'), scaleNumber($to, 'Hz')), '');
523 6552dd3f Gorlow Maxim aka Sheridan
            }
524
          }
525
        }
526
      }
527
    }
528
  }
529
  
530
  
531
  # --------------- cpu_all -----------------------------------------
532
  if ($cpuinfo->{'cpu_count'} > 1)
533
  {
534
    append_graph($pg, 'cpu_all', 'cpu', 'cpu_all', '', '');
535
    for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { append_field($pg, 'cpu_all', sprintf("cpu_%s", $i), 'cpu_util', sprintf("CPU%s", $i)); }
536
    append_field($pg, 'cpu_all', 'total', 'cpu_util', 'Combined');
537
  }
538
  return $pg;
539
}
540
541
542
# ------------------------------------ printing limits (for utilisation graphs) ----------------
543 1a5575ed Gorlow Maxim aka Sheridan
sub append_utilisation_limits
544
{
545
  my ($pg, $graph_name, $cname, $i) = @_[0..3];
546
  for my $type (qw(warning critical))
547
  {
548
    my $field = sprintf("%s_%s", $cname, $type);
549
    my $cpu_field = defined($i) ? sprintf("cpu%s_%s_%s", $i, $cname, $type) : undef;
550
    my $limit = (defined($i) and defined($limits->{'utilisation'}{$cpu_field})) ? 
551
                $limits->{'utilisation'}{$cpu_field} : 
552
                (
553
                  defined($limits->{'utilisation'}{$field}) ? 
554
                  $limits->{'utilisation'}{$field} : 
555
                  ( 
556
                    defined($limits->{'utilisation'}{$type})  ? 
557
                    $limits->{'utilisation'}{$type}  : 
558
                    undef 
559
                  ) 
560
                );
561
    if(defined($limit)) { $pg->{$graph_name}{'fields'}{$cname}{$type} = $limit; }
562
  }
563
}
564
565
# ---------------- loading limits -------------
566
sub load_limits
567 6552dd3f Gorlow Maxim aka Sheridan
{
568 1a5575ed Gorlow Maxim aka Sheridan
  $limits->{'utilisation'}{'warning'}  = $ENV{warning}  || undef;
569
  $limits->{'utilisation'}{'critical'} = $ENV{critical} || undef;
570
  for my $cname (@cnames)
571 6552dd3f Gorlow Maxim aka Sheridan
  {
572 1a5575ed Gorlow Maxim aka Sheridan
    for my $t (qw(warning critical))
573
    {
574
      my $name = sprintf("%s_%s", $cname, $t);
575
      $limits->{'utilisation'}{$name} = $ENV{$name} || undef;
576
      for (my $i = 0; $i <= $cpuinfo->{'cpu_count'}; $i++)
577
      {
578
        $name = sprintf("cpu%s_%s_%s",$i, $cname, $t);
579
        $limits->{'utilisation'}{$name} = $ENV{$name} || undef;
580
      }
581
    }
582 6552dd3f Gorlow Maxim aka Sheridan
  }
583
}
584
585
# --------------------------------- graph configs ----------------------------
586
sub print_config
587
{
588 1a5575ed Gorlow Maxim aka Sheridan
  load_limits();
589 6552dd3f Gorlow Maxim aka Sheridan
  my $config = prepare_graphs_fields();
590
  for my $g (sort keys %{$config})
591
  {
592
    printf("multigraph %s\n", $g);
593
    for my $go (sort keys %{$config->{$g}{'graph'}}) { printf("graph_%s %s\n", $go, $config->{$g}{'graph'}{$go}); }
594
    for my $f (sort keys %{$config->{$g}{'fields'}}) { for my $fo (sort keys %{$config->{$g}{'fields'}{$f}}) { printf("%s.%s %s\n", $f, $fo, $config->{$g}{'fields'}{$f}{$fo}); } }
595
    print "\n";
596
  }
597
}
598
599
# ----------------------------------- saving state data using munin --------------------
600
sub save_state_data
601
{
602
  my $data = $_[0];
603
  my $d = Data::Dumper->new([$data]);
604
  $d->Indent(0);
605
  save_state($d->Dump);
606
}
607
608
# -------------------------------- loading previous state data using munin -------------------
609
sub restore_state_data
610
{
611
  my $VAR1;
612
  my $states = (restore_state())[0];
613
  eval $states if defined $states;
614
  return $VAR1;
615
}
616
617
sub load_stats
618
{
619
  my $stats = {};
620
  # need to store --------------------
621
  $stats->{'timestamp'} = time();
622
  $stats->{'cpu_util'}  = get_cpu_utilisation_stats()                                ;
623
  $stats->{'f_trans'}   = get_frequency_transitions()  if check_exists('freq_trans') ;
624 1a5575ed Gorlow Maxim aka Sheridan
  $stats->{'f_times'}   = get_frequency_times()        if check_exists('freq_times') ;
625
  $stats->{'f_ttable'}  = get_frequency_trans_table()  if check_exists('freq_ttable');
626 6552dd3f Gorlow Maxim aka Sheridan
627
  save_state_data($stats);
628
629
  # no need to store --------------------
630
  $stats->{'f_minmax'}  = get_cpu_curr_max_freqences() if check_exists('freq_hz')    ;
631 1a5575ed Gorlow Maxim aka Sheridan
  
632 6552dd3f Gorlow Maxim aka Sheridan
633
  #print Dumper $stats;
634
  return $stats;
635
}
636
637
# ---------------------------------- loading cpu stats from loaded lines ------------------------
638
sub get_cpu_utilisation_stats
639
{
640
  my $stats = {};
641
  for (my $i = 0; $i <= $cpuinfo->{'cpu_count'}; $i++)
642
  {
643
    my $rx = $i == $cpuinfo->{'cpu_count'} ? 'cpu' : sprintf ("cpu%s", $i);
644
    my $cn = $i == $cpuinfo->{'cpu_count'} ? 'total' : $i;
645
    my @tmp = split(/\s+/, (grep(/$rx\s/, get_stat_file_content()))[0]);
646
    my $j = 1;
647
    for my $cname (@cnames)
648
    {
649
      $stats->{$cn}{$cname} = $tmp[$j];
650
      $j++;
651
    }
652
  }
653
  return $stats;
654
}
655
# ------------------ loading frequency transitions for each cpu ----------------------------
656
sub get_frequency_transitions
657
{
658
  my $stats = {};
659
  for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++)
660
  {
661
    next unless (check_exists('freq_trans', $i));
662
    $stats->{$i} = get_sys_file_content(sprintf("%s/cpu%s/cpufreq/stats/total_trans", $freq_path, $i));
663
  }
664
  return $stats;
665
}
666
667
# ------------------ loading frequency times for each cpu ----------------------------
668
sub get_frequency_times
669
{
670
  my $stat = {};
671
  for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++)
672
  {
673
    next unless (check_exists('freq_times', $i));
674
    my $total = 0;
675
    my $file = sprintf("%s/cpu%s/cpufreq/stats/time_in_state", $freq_path, $i);
676
    open (FH, '<', $file) or die "$! $file \n";
677
    for my $line (<FH>)
678
    {
679
      chomp $line;
680
      my ($hz, $count) = split(/\s+/, $line);
681
      $hz = $hz*$freq_mul;
682
      $stat->{'values'}{$i}{$hz} = $count;
683
      push(@{$stat->{'names'}{$i}}, $hz);
684
      $total += $count;
685
    }
686
    close (FH);
687
    $stat->{'total'}{$i} = $total;
688
  }
689
  return $stat;
690
}
691
692
# ------------------ loading current and max frequency for each cpu ----------------------------
693
sub get_cpu_curr_max_freqences
694
{
695
  my $freq = {};
696
  for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++)
697
  {
698
    next unless (check_exists('freq_hz', $i));
699
    my $cpu_path = sprintf("%s/cpu%s/cpufreq", $freq_path, $i);
700
    $freq->{'cur'}{$i} = get_sys_file_content(sprintf("%s/scaling_cur_freq", $cpu_path))*$freq_mul;
701
    $freq->{'max'}{$i} = get_sys_file_content(sprintf("%s/scaling_max_freq", $cpu_path))*$freq_mul;
702
    $freq->{'min'}{$i} = get_sys_file_content(sprintf("%s/scaling_min_freq", $cpu_path))*$freq_mul;
703
  }
704
  return $freq;
705
}
706
707
sub get_frequency_trans_table
708
{
709
  my $tbl = {};
710
  for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++)
711
  {
712
    next unless (check_exists('freq_ttable', $i));
713
    my @frequences;
714
    my $fcount = 0;
715
    my $total = 0;
716
    my $file = sprintf("%s/cpu%s/cpufreq/stats/trans_table", $freq_path, $i);
717
    open (FH, '<', $file) or die "$! $file \n";
718
    for my $line (<FH>)
719
    {
720
      chomp $line;
721
      my ($left, $right) = split(/:/, $line);
722
      next if($left =~ m/From/);
723
      if($left =~ m/\d+/)
724
      {
725
        my $frequence = trim($left)*$freq_mul;
726
        my @counters = split(/\s+/, trim($right));
727
        for (my $j = 0; $j<$fcount; $j++)
728
        {
729
          $tbl->{'values'}{$i}{$frequence}{$frequences[$j]*$freq_mul} = $counters[$j];
730
          $total += $counters[$j];
731
        }
732
      }
733
      else
734
      {
735
        @frequences = split(/\s+/, trim($right));
736
        $fcount = scalar(@frequences);
737
      }
738
    }
739
    $tbl->{'total'}{$i} = $total;
740
    close (FH);
741
  }
742
  return $tbl;
743
}
744
745
sub one_second_part
746
{
747
  my ($curr, $prev, $timediff) = @_[0..2];
748
  #print "$prev, $curr, $timediff\n";
749
  return 'NaN' if ($curr < $prev or $timediff < 0);
750
  return $curr - $prev if $timediff == 0;
751
  return ($curr - $prev)/$timediff;
752
}
753
754
sub divide
755
{
756
  my ($divider, $divident) = @_[0..1];
757
  return 'NaN' if $divident == 0;
758
  return $divider/$divident;
759
}
760
761
# -------------------------------- calculating fields values ------------------------------
762
sub calculate
763
{
764
  my ($pstats, $cstats) = @_[0..1];
765
  my $result = {};
766
  my $timediff = $cstats->{'timestamp'} - $pstats->{'timestamp'};
767
  # --- cpu utilisation ----
768
  for my $cpu (keys %{$cstats->{'cpu_util'}})
769
  {
770
    # ------ calculating 1%
771
    $result->{'cpu_util'}{'1%'}{$cpu} = 0;
772
    for my $cname (@cnames)
773
    {
774
      $result->{'cpu_util'}{'diff'}{$cpu}{$cname} = one_second_part($cstats->{'cpu_util'}{$cpu}{$cname}, $pstats->{'cpu_util'}{$cpu}{$cname}, $timediff);
775
      $result->{'cpu_util'}{'1%'}{$cpu} += $result->{'cpu_util'}{'diff'}{$cpu}{$cname} if $result->{'cpu_util'}{'diff'}{$cpu}{$cname} ne 'NaN';
776
    }
777
    $result->{'cpu_util'}{'1%'}{$cpu} = $result->{'cpu_util'}{'1%'}{$cpu}/100;
778
    # ------ calculating used percents
779
    $result->{'cpu_util'}{'used'}{$cpu} = 0;
780
    for my $cname (@cnames)
781
    {
782
      $result->{'cpu_util'}{'%'}{$cpu}{$cname} = divide($result->{'cpu_util'}{'diff'}{$cpu}{$cname}, $result->{'cpu_util'}{'1%'}{$cpu});
783
      next if $cname eq 'idle';
784
      $result->{'cpu_util'}{'used'}{$cpu} += $result->{'cpu_util'}{'%'}{$cpu}{$cname} if $result->{'cpu_util'}{'%'}{$cpu}{$cname} ne 'NaN';
785
    }
786
  }
787
  # ------ freq min max ----
788
  if (check_exists('freq_hz'))
789
  {
790
    for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++)
791
    {
792
      $result->{'f_minmax'}{'%'}{$i} =     divide($cstats->{'f_minmax'}{'cur'}{$i} - $cstats->{'f_minmax'}{'min'}{$i},
793
                                                (($cstats->{'f_minmax'}{'max'}{$i} - $cstats->{'f_minmax'}{'min'}{$i}) / 100 )) if (check_exists('freq_hz', $i));
794
    }
795
  }
796
  # ---- freq trans ----
797
  if (check_exists('freq_trans'))
798
  {
799
    $result->{'f_trans'}{'total'} = 0;
800
    for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++)
801
    {
802
      if(check_exists('freq_trans', $i))
803
      {
804
        $result->{'f_trans'}{$i} = one_second_part($cstats->{'f_trans'}{$i}, $pstats->{'f_trans'}{$i}, $timediff);
805
        $result->{'f_trans'}{'total'} += $result->{'f_trans'}{$i} if $result->{'f_trans'}{$i}  ne 'NaN';
806
      }
807
    }
808
  }
809
  # -- freq times ---
810
  if (check_exists('freq_times'))
811
  {
812
    for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++)
813
    {
814
      if (check_exists('freq_times', $i))
815
      {
816
        my $oneprc = $cstats->{'f_times'}{'total'}{$i}/100;
817 1a5575ed Gorlow Maxim aka Sheridan
        my $ps_total = 0;
818 6552dd3f Gorlow Maxim aka Sheridan
        for my $hz (@{$cstats->{'f_times'}{'names'}{$i}})
819
        {
820
          $result->{'f_times'}{$i}{$hz} = divide($cstats->{'f_times'}{'values'}{$i}{$hz}, $oneprc);
821 1a5575ed Gorlow Maxim aka Sheridan
          $cstats->{'f_times'}{'%_ps'}{$i}{$hz} = one_second_part($cstats->{'f_times'}{'values'}{$i}{$hz}, $pstats->{'f_times'}{'values'}{$i}{$hz}, $timediff);
822
          $ps_total += $cstats->{'f_times'}{'%_ps'}{$i}{$hz};
823
        }
824
        $ps_total = $ps_total/100;
825
        for my $hz (@{$cstats->{'f_times'}{'names'}{$i}})
826
        {
827
          $result->{'f_times_ps'}{$i}{$hz} = divide($cstats->{'f_times'}{'%_ps'}{$i}{$hz}, $ps_total);
828 6552dd3f Gorlow Maxim aka Sheridan
        }
829
      }
830
    }
831
  }
832
  # ------- freq trans table ---
833
  if (check_exists('freq_ttable'))
834
  {
835
    for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++)
836
    {
837
      if (check_exists('freq_ttable', $i))
838
      {
839
        my $oneprc = $cstats->{'f_ttable'}{'total'}{$i}/100;
840 1a5575ed Gorlow Maxim aka Sheridan
        my $ps_total = 0;
841 6552dd3f Gorlow Maxim aka Sheridan
        for my $from (keys %{$cstats->{'f_ttable'}{'values'}{$i}})
842
        {
843
          for my $to (keys %{$cstats->{'f_ttable'}{'values'}{$i}{$from}})
844
          {
845
            next if ($from eq $to);
846
            $result->{'f_ttable'}{$i}{$from}{$to} = divide($cstats->{'f_ttable'}{'values'}{$i}{$from}{$to}, $oneprc);
847 1a5575ed Gorlow Maxim aka Sheridan
            $cstats->{'f_ttable'}{'%_ps'}{$i}{$from}{$to} = one_second_part($cstats->{'f_ttable'}{'values'}{$i}{$from}{$to}, $pstats->{'f_ttable'}{'values'}{$i}{$from}{$to}, $timediff);
848
            $ps_total += $cstats->{'f_ttable'}{'%_ps'}{$i}{$from}{$to};
849
          }
850
        }
851
        $ps_total = $ps_total/100;
852
        for my $from (keys %{$cstats->{'f_ttable'}{'values'}{$i}})
853
        {
854
          for my $to (keys %{$cstats->{'f_ttable'}{'values'}{$i}{$from}})
855
          {
856
            next if ($from eq $to);
857
            $result->{'f_ttable_ps'}{$i}{$from}{$to} = divide($cstats->{'f_ttable'}{'%_ps'}{$i}{$from}{$to}, $ps_total);
858 6552dd3f Gorlow Maxim aka Sheridan
          }
859
        }
860
      }
861
    }
862
  }
863
  #print Dumper $result;
864
  return $result;
865
}
866
867
# ---------------------------------------- preparing values ------------------------------------
868
sub prepare_graphs_values
869
{
870
  my ($data) = $_[0];
871
  my $pg = {};
872
  # ------------------ cpu_utilisation -----------------------------------
873
  # ---------- general ----------------------
874
  for my $cname (@cnames) { $pg->{'cpu_utilisation'}{$cname} = $data->{'cpu_util'}{'%'}{'total'}{$cname}; }
875
  if(check_exists('freq_hz')) { for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { $pg->{'cpu_utilisation'}{sprintf("fp_%s", $i)} = $data->{'f_minmax'}{'%'}{$i}; } }
876
  # ---------------- childs -------------------
877
  if ($cpuinfo->{'cpu_count'} > 1)
878
  {
879
    for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++)
880
    {
881
      my $graph_name = sprintf("cpu_utilisation.cpu%s", $i);
882
      for my $cname (@cnames) { $pg->{$graph_name}{$cname} = $data->{'cpu_util'}{'%'}{$i}{$cname}; }
883
      if(check_exists('freq_hz', $i)) { $pg->{$graph_name}{'fp'} = $data->{'f_minmax'}{'%'}{$i}; }
884
    }
885
  }
886
  
887
  
888
  # --------------- cpu_frequency --------------------------------------------
889
  if(check_exists('freq_trans'))
890
  { 
891
    # ------------ general --------------------
892
    for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { $pg->{'cpu_frequency'}{sprintf("cpu_%s", $i)} = $data->{'f_trans'}{$i} if (check_exists('freq_trans', $i)); }
893
    $pg->{'cpu_frequency'}{'total'} = $data->{'f_trans'}{'total'};
894
    # ---------------- childs -------------------
895
    if(check_exists('freq_times'))
896
    {
897
      for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++)
898
      {
899
        if(check_exists('freq_times', $i))
900
        {
901
          my $graph_name = sprintf("cpu_frequency.percent_cpu%s", $i);
902
          for my $freq (keys %{$data->{'f_times'}{$i}}) { $pg->{$graph_name}{sprintf("hz_%s", $freq)} = $data->{'f_times'}{$i}{$freq}; }
903 1a5575ed Gorlow Maxim aka Sheridan
          $graph_name = sprintf("cpu_frequency.percent_ps_cpu%s", $i);
904
          for my $freq (keys %{$data->{'f_times_ps'}{$i}}) { $pg->{$graph_name}{sprintf("hz_%s", $freq)} = $data->{'f_times_ps'}{$i}{$freq}; }
905 6552dd3f Gorlow Maxim aka Sheridan
        }
906
      }
907
    }
908
    if(check_exists('freq_ttable'))
909
    {
910
      for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++)
911
      {
912
        if(check_exists('freq_ttable', $i))
913
        {
914
          my $graph_name = sprintf("cpu_frequency.trans_table_cpu%s", $i);
915
          for my $from (keys %{$data->{'f_ttable'}{$i}})
916
          {
917
            for my $to (keys %{$data->{'f_ttable'}{$i}{$from}})
918
            {
919
              $pg->{$graph_name}{sprintf("f_%s_t_%s", $from, $to)} = $data->{'f_ttable'}{$i}{$from}{$to};
920
            }
921
          }
922 1a5575ed Gorlow Maxim aka Sheridan
          $graph_name = sprintf("cpu_frequency.trans_table_ps_cpu%s", $i);
923
          for my $from (keys %{$data->{'f_ttable_ps'}{$i}})
924
          {
925
            for my $to (keys %{$data->{'f_ttable_ps'}{$i}{$from}})
926
            {
927
              $pg->{$graph_name}{sprintf("f_%s_t_%s", $from, $to)} = $data->{'f_ttable_ps'}{$i}{$from}{$to};
928
            }
929
          }
930 6552dd3f Gorlow Maxim aka Sheridan
        }
931
      }
932
    }
933
  }
934
935
  # --------------- cpu_all --------------------------------------------
936
  if ($cpuinfo->{'cpu_count'} > 1)
937
  {
938
    for (my $i = 0; $i < $cpuinfo->{'cpu_count'}; $i++) { $pg->{'cpu_all'}{sprintf("cpu_%s", $i)} = $data->{'cpu_util'}{'used'}{$i}; }
939
    $pg->{'cpu_all'}{'total'} = $data->{'cpu_util'}{'used'}{'total'};
940
  }
941
  return $pg;
942
}
943
944
945
946
# -------------------------------- printing values -----------------------------------
947
sub print_values
948
{
949
  my $pstats = restore_state_data();
950
  my $cstats = load_stats();
951
  if (exists ($pstats->{'timestamp'}))
952
  {
953
    my $values = prepare_graphs_values(calculate($pstats, $cstats));
954
    #print Dumper $values;
955
    for my $g (sort keys %{$values})
956
    {
957
      printf("multigraph %s\n", $g);
958
      for my $f (sort keys %{$values->{$g}}) { printf("%s.value %s\n", $f, $values->{$g}{$f}); }
959
      print "\n";
960
    }
961
  }
962
}
963
964
__END__
965
966
- user: normal processes executing in user mode
967
- nice: niced processes executing in user mode
968
- system: processes executing in kernel mode
969
- idle: twiddling thumbs
970
- iowait: waiting for I/O to complete
971
- irq: servicing interrupts
972
- softirq: servicing softirqs
973
- steal: involuntary wait
974
- guest: running a guest