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
#!/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
But each field (system user nice etc...) can be controlled separately:
28
For example:
29

    
30
  env.system_warning 70
31
  env.user_warning 70
32
  env.idle_critical 1
33

    
34
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
=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
  2.0
58

    
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

    
86

    
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
    'title'    => 'CPU:t: frequency (total)',
117
    'args'     =>  '--base 1000 -r --lower-limit 0 --upper-limit 100',
118
    'vlabel'   => '% of total',
119
    'info'     => 'This graph shows CPU:t: frequency :i:',
120
    'category' => 'cpu'
121
  },
122
  '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
  'cpu_freq_trans_table' => # child of cpu_freq_trans
131
  {
132
    'title'    => 'CPU:t: frequency switches (total)',
133
    'args'     =>  '--base 1000 -r --lower-limit 0 --upper-limit 100',
134
    'vlabel'   => '% of total',
135
    'scale'    => 'no',
136
    'info'     => 'This graph shows CPU:t: frequency switches :i:',
137
  },
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
  }
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
      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
    }
329
    return $items_exists->{$t}{$cpu_num};
330
  }
331
  else
332
  {
333
    unless(exists($items_exists->{$t}{'total'}))
334
    {
335
      my $c = 0;
336
      for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++) { $c++ if (check_exists($t, $i)); }
337
      $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
  for my $cname (@cnames) { append_field($pg, 'cpu_utilisation', $cname, $cname, '', ''); append_utilisation_limits($pg, 'cpu_utilisation', $cname, undef); }
455
  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
      for my $cname (@cnames) { append_field($pg, $graph_name, $cname, $cname, '', ''); append_utilisation_limits($pg, $graph_name, $cname, $i); }
464
      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
    # - cpu frequency transitions -
474
    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
          # - cpu frequencyes -
486
          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
          # - 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
        }
494
      }
495
    }
496
    if(check_exists('freq_ttable'))
497
    {
498
      my $f_table = get_frequency_trans_table();
499
      for (my $i=0; $i < $cpuinfo->{'cpu_count'}; $i++)
500
      {
501
        if(check_exists('freq_ttable', $i))
502
        {
503
          # - cpu frequencyes table -
504
          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
              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
            }
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
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
{
568
  $limits->{'utilisation'}{'warning'}  = $ENV{warning}  || undef;
569
  $limits->{'utilisation'}{'critical'} = $ENV{critical} || undef;
570
  for my $cname (@cnames)
571
  {
572
    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
  }
583
}
584

    
585
# --------------------------------- graph configs ----------------------------
586
sub print_config
587
{
588
  load_limits();
589
  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
  $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

    
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
  
632

    
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
        my $ps_total = 0;
818
        for my $hz (@{$cstats->{'f_times'}{'names'}{$i}})
819
        {
820
          $result->{'f_times'}{$i}{$hz} = divide($cstats->{'f_times'}{'values'}{$i}{$hz}, $oneprc);
821
          $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
        }
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
        my $ps_total = 0;
841
        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
            $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
          }
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
          $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
        }
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
          $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
        }
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