Projet

Général

Profil

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

root / plugins / system / meminfo @ c29d25a8

Historique | Voir | Annoter | Télécharger (65,9 ko)

1 c0e11736 Gorlow Maxim aka Sheridan
#!/usr/bin/perl -w
2
# -*- perl -*-
3
4
=head1 NAME
5
6
meminfo - Plugin to monitor memory usage.
7
This plugin show many graphs (on my pc ~90 graphs with total ~750 values)
8
Graph splitted to 8 groups, every group has child graphs:
9
10
  Application groups memory usage                  - show applications groups memory using by group (env.) [env.applications_group]
11
    Group `groupName1` applications memory usage     v
12
    ...                                            - show applications groups memory using per application in every group
13
    Group `groupNameX` applications memory usage     ^
14
    Summary group `groupName1` memory usage          v
15
    ...                                            - show applications groups memory using as summ of every parametr of application in every group
16
    Summary group `groupNameX` memory usage          ^
17
  Application memory usage                         - show memory usage per application [env.applications]
18
    Application processes                          - show processes count of every application
19
    Summary `applicationName1` memory usage          v
20
    ...                                            - Summary application processes memory using
21
    Summary `applicationNameX` memory usage          ^
22
  External fragmentztion: Buddyinfo                - Buddyinfo/ Nodes and zones summary
23
     Node X, zone ZZZ                              - Chunks for each zone
24
  External fragmentztion: Page type info           - Page type info, summary node-zone-type pages
25
    Node X, zone ZZZ, type TTT                     - Free pages per node-zone-page by order
26
  Physical memory usage                            - Main graph, show phisical memory usage
27
    Active/Inactive memory                         - subj :)
28
    Memory usage by cashes and buffers             - subj :)
29
    mmap                                           - mmapp'ed memory
30
    HugePages count                                - subj :)
31
    HugePages size                                 - subj :)
32
    Kernel memory                                  - subj :)
33
    Low and high memory                            - subj :)
34
  Slab objects size                                - subj :)
35
    Slabs size [GroupName1]                          v
36
    ...                                            - show slabs size. I try split fields by this types. 
37
    Slabs size [GroupNameX]                          ^
38
    Slabs sizes of groups                          - "Groups" sizes
39
    Slab objects                                   - Active/inactive objects
40
    Slabs                                          - Active/inactive and shared slabs
41
    Reclaim                                        - Reclaimed
42
  Swap usage                                       - subj :)
43
    First candidates to swap out                   - subj :)
44
    Writeback                                      - writeback and dirty
45
  Virtual memory usage                             - subj :)
46
    Vmalloc                                        - show vmalloc usage
47
    Vmalloc allocates                              - allocates per object
48
49
50
=head1 APPLICABLE SYSTEMS
51
52
All Linux systems
53
54
=head1 CONFIGURATION
55
56
The plugin automatically selects which graphics drawing.
57
But you can select graphs to draw by enabled_graphs environment.
58
enabled_graphs is a regexp, where you include graphs to draw
59
Default: undefined
60
Examples:
61
62
  env.enabled_graphs meminfo_\w+\.?       # Draw only meminfo graphs
63
  env.enabled_graphs (meminfo|swapinfo)$  # Draw only meminfo and swapinfo graphs, without 'childs'
64
65
Also you can select applications to monitor it by applications environment
66
applications environment is regexp
67
Default: undefined
68
Examples:
69
70
  env.applications (firefox|\w{1,3}?sh) # Monitor firefox and shells (bash, zsh, sh, etc...)
71
72
Also you can group applications and show memory using of this groups, per application
73
in group and summmary applications in group. Use env.applications_group for this.
74
Format: env.applications_group regexp:groupName;regexp:groupName;...
75
where regexp - it applications names regexp, and groupName - on-screen group name
76
Default: undefined
77
Examples:
78
  
79
  env.applications_group ^(firefox|chrome|opera|konqu|arora):Browsers;(^lx|openbox|menu|gnome|slim|^X):X;^(hal|console-kit|syslog|cron|dhcp|udev|dbus|bluetoo|agett|login|automount):System;
80
81
And you can select time to display data after application close (all values return as 'NaN'),
82
for this you must used environment env.application_wait Value - secunds
83
Default: 1800
84
Example:
85
86
  env.application_wait 86400 #24h
87
88
=head2 RIGHTS NOTE
89
90
Please check, can your munin user read files such as 
91
  /proc/meminfo
92
  /proc/slabinfo
93
  /proc/vmallocinfo
94
  /proc/buddyinfo
95
  /proc/pagetypeinfo
96
  /proc/[pid]/status
97
If no access, just write in plugin-config
98
  [meminfo]
99
  user root
100
  group root
101
102
=head2 WARNING AND CRITICAL SETTINGS
103
104
You can set warning and critical levels for *each* of the data
105
series the plugin reports.
106
Template for limits: env.limit_%field% (warning_num:crytical_num|critical_num)[kMG], where
107
%field% - field name. You can show it in graph viev, 'field internal name' field
108
warning_num - number, warning value
109
crytical_num - number, critical value
110
k - kilobytes, M - megabytes, G - gigabytes
111
Examples:
112
113
  env.limit_apps 300M:500M    # Applications, warning - 300M, critical - 500m
114
  env.limit_committed_as 700M # Committed AS, critical - 700M
115
116
=head1 INTERPRETATION
117
118
The plugin shows
119
120
=head1 MAGIC MARKERS
121
122
  #%# family=auto
123
  #%# capabilities=autoconf
124
125
126
=head1 VERSION
127
128
  1.0
129
130
=head1 BUGS
131
132
none known
133
134
=head1 AUTHOR
135
136
Gorlow Maxim aka Sheridan <sheridan@sheridan-home.ru> (email and jabber)
137
138
=head1 LICENSE
139
140
GPLv2
141
142
=cut
143
144
145
146
use strict;
147
use warnings;
148
use Munin::Plugin;
149
use IO::Dir;
150
use Data::Dumper;
151
152
# -----------------------------------------------------------------------------------------------------------------------------------------------------
153
my $graphs_source =
154
{
155
  # ----------------------------------------------
156
  'meminfo_phisical' =>
157
  {
158
    'munin' =>
159
    {
160
      'args'     => '--base 1024',
161
      'title'    => 'Physical memory usage',
162
      'vtitle'   => 'bytes',
163
      'category' => 'memory'
164
     },
165
     'fields' => [qw(apps:AREASTACK:- slab:AREASTACK:- buffers:AREASTACK:- cached:AREASTACK:- page_tables:AREASTACK:- cached_swap:AREASTACK:- free:AREASTACK:FFFFCCCC swap_used:AREASTACK:- used:LINE1:- usedwswap:LINE1:- hardware_corrupted:LINE1:- total:LINE1:000000)]
166
  },
167
168
  # ----------------------------------------------
169
  'meminfo_phisical.cb' =>
170
  {
171
    'munin' =>
172
    {
173
      'args'     => '--base 1024',
174
      'title'    => 'Memory usage by cashes and buffers',
175
      'vtitle'   => 'bytes',
176
      'category' => 'cache',
177
      'total'    => 'Total'
178
    },
179
    'fields' => [qw(cached:AREASTACK:- buffers:AREASTACK:- cached_swap:AREASTACK:- nfs_unstable:AREASTACK:- writeback_fuse:AREASTACK:- bounce:AREASTACK:-)]
180
  },
181
  # ----------------------------------------------
182
  'meminfo_phisical.active_inactive' =>
183
  {
184
    'munin' =>
185
    {
186
      'args'     => '--base 1024',
187
      'title'    => 'Active/Inactive memory',
188
      'vtitle'   => 'bytes',
189
      'category' => 'active_inactive'
190
    },
191
    'fields' => [qw(active_file:AREA:FF444488 active_anon:STACK:44FF4488 active:LINE1:000000 inactive_file:AREA:4444FF88 inactive_anon:STACK:FFFF4499 inactive:LINE1:FFFFFF)]
192
  },
193
  # ----------------------------------------------
194
  'meminfo_phisical.mmap' =>
195
  {
196
    'munin' =>
197
    {
198
      'args'     => '--base 1024',
199
      'title'    => 'mmap',
200
      'vtitle'   => 'bytes',
201
      'category' => 'direct_map'
202
    },
203
    'fields' => [qw(direct_map_4k:LINE1:- direct_map_2m:LINE1:- direct_map_4m:LINE1:- direct_map_1g:LINE1:- mapped:LINE1:- mmap_copy:LINE1:-)]
204
  },
205
  # ----------------------------------------------
206
  'meminfo_phisical.kernel' =>
207
  {
208
    'munin' =>
209
    {
210
      'args'     => '--base 1024',
211
      'title'    => 'Kernel memory',
212
      'vtitle'   => 'bytes',
213
      'category' => 'kernel',
214
      'total'    => 'Total'
215
    },
216
    'fields' => [qw(kernel_stack:AREASTACK:- slab:AREASTACK:- qlists:LINE1:-)]
217
  },
218
  # ----------------------------------------------
219
  'meminfo_phisical.low_and_high' =>
220
  {
221
    'munin' =>
222
    {
223
      'args'     => '--base 1024',
224
      'title'    => 'Low and high memory',
225
      'vtitle'   => 'bytes',
226
      'category' => 'low_and_high'
227
    },
228
    'fields' => [qw(low_used:AREASTACK:- low_free:AREASTACK:CCFFCCCC high_used:AREASTACK:- high_free:AREASTACK:FFFFCCCC low_total:LINE1:- high_total:LINE1:- l_h_total:LINE1:000000)]
229
  },
230
  # ----------------------------------------------
231
  'meminfo_phisical.hugepages' =>
232
  {
233
    'munin' =>
234
    {
235
      'args'     => '--base 1000 ',
236
      'title'    => 'HugePages count',
237
      'vtitle'   => 'count',
238
      'category' => 'hugepages'
239
    },
240
    'fields' => [qw(huge_pages_total:LINE1:000000 huge_pages_free:AREASTACK:- huge_pages_rsvd:AREASTACK:- huge_pages_surp:AREASTACK:-)]
241
  },
242
  # ----------------------------------------------
243
  'meminfo_phisical.hugepages_sizes' =>
244
  {
245
    'munin' =>
246
    {
247
      'args'     => '--base 1024',
248
      'title'    => 'HugePages size',
249
      'vtitle'   => 'bytes',
250
      'category' => 'hugepages'
251
    },
252
    'fields' => [qw(huge_page_size:LINE1:00FF00 huge_pages_total_size:LINE1:000000 huge_pages_free_size:AREASTACK:- huge_pages_rsvd_size:AREASTACK:- huge_pages_surp_size:AREASTACK:-)]
253
  },
254
  # ----------------------------------------------
255
  'meminfo_virtual' =>
256
  {
257
    'munin' =>
258
    {
259
      'args'     => '--base 1024',
260
      'title'    => 'Virtual memory usage',
261
      'vtitle'   => 'bytes',
262
      'category' => 'memory'
263
     },
264
     'fields' => [qw(commit_limit:AREA:88884477 committed_as:AREA:- mlocked:LINE1:- shmem:LINE1:- vmalloc_used:LINE1:-)]
265
  },
266
  # ----------------------------------------------
267
  'meminfo_virtual.vmalloc' =>
268
  {
269
    'munin' =>
270
    {
271
      'args'     => '--base 1024',
272
      'title'    => 'Vmalloc',
273
      'vtitle'   => 'bytes',
274
      'category' => 'total'
275
    },
276
    'fields' => [qw(vmalloc_used:AREASTACK:- vmalloc_free:AREASTACK:FFFFCCCC vmalloc_total:LINE1:000000 vmalloc_chunk:LINE1:-)]
277
  },
278
  # ----------------------------------------------
279
  'meminfo_virtual.vmalloc_allocates' =>
280
  {
281
    'munin' =>
282
    {
283
      'args'     => '--base 1024',
284
      'title'    => 'Vmalloc allocates',
285
      'vtitle'   => 'bytes',
286
      'category' => 'allocates',
287
      'total'    => 'Total'
288
    },
289
    'fields' => []
290
  },
291
  # ----------------------------------------------
292
  'slab' =>
293
  {
294
    'munin' =>
295
    {
296
      'args'     => '--base 1024',
297
      'title'    => 'Slab objects size',
298
      'vtitle'   => 'bytes',
299
      'category' => 'memory'
300
    },
301
    'fields' => [qw(min_obj_size:AREA:00000000 max_obj_size:STACK:- avg_obj_size:LINE1:-)]
302
  },
303
  # ----------------------------------------------
304
  'slab.slab_objects' =>
305
  {
306
    'munin' =>
307
    {
308
      'args'     => '--base 1000 ',
309
      'title'    => 'Slab objects',
310
      'vtitle'   => 'count',
311
      'category' => 'objects'
312
    },
313
    'fields' => [qw(active_objects:AREASTACK:- inactive_objects:AREASTACK:FFFFCCCC total_objects:LINE1:000000)]
314
  },
315
  # ----------------------------------------------
316
  'slab.slabs' =>
317
  {
318
    'munin' =>
319
    {
320
      'args'     => '--base 1000 ',
321
      'title'    => 'Slabs',
322
      'vtitle'   => 'count',
323
      'category' => 'objects'
324
    },
325
    'fields' => [qw(active_slabs:AREASTACK:- inactive_slabs:AREASTACK:FFFFCCCC shared_slabs:LINE1:- total_slabs:LINE1:000000)]
326
  },
327
  # ----------------------------------------------
328
  'slab.reclaim' =>
329
  {
330
    'munin' =>
331
    {
332
      'args'     => '--base 1024',
333
      'title'    => 'Reclaim',
334
      'vtitle'   => 'bytes',
335
      'category' => 'reclaim',
336
      'total'    => 'Total',
337
    },
338
    'fields' => [qw(slab_reclaimable:AREA:- slab_unreclaimable:STACK:-)]
339
  },
340
  # ----------------------------------------------
341
  'slab.slabs_size_all_groups' =>
342
  {
343
    'munin' =>
344
    {
345
      'args'     => '--base 1024',
346
      'title'    => "Slabs sizes of groups",
347
      'vtitle'   => 'bytes',
348
      'total'    => 'Total',
349
      'category' => 'groups_size'
350
    },
351
    'fields' => []
352
  },
353
  # ----------------------------------------------
354
  'swapinfo' =>
355
  {
356
    'munin' =>
357
    {
358
      'args'     => '--base 1024',
359
      'title'    => 'Swap usage',
360
      'vtitle'   => 'bytes',
361
      'category' => 'memory'
362
    },
363
    'fields' => [qw(swap_used:AREA:- swap_free:STACK:FFFFCCCC cached_swap:LINE1:- swap_total:LINE1:000000)]
364
  },
365
  # ----------------------------------------------
366
  'swapinfo.wrback' =>
367
  {
368
    'munin' =>
369
    {
370
      'args'     => '--base 1024',
371
      'title'    => 'Writeback',
372
      'vtitle'   => 'bytes',
373
      'category' => 'writeback'
374
    },
375
    'fields' => [qw(dirty:LINE1:- writeback:LINE1:-)]
376
  },
377
  # ----------------------------------------------
378
  'swapinfo.candidates' =>
379
  {
380
    'munin' =>
381
    {
382
      'args'     => '--base 1024',
383
      'title'    => 'First candidates to swap out',
384
      'vtitle'   => 'bytes',
385
      'category' => 'candidates'
386
    },
387
    'fields' => [qw(inactive_file:AREASTACK:- inactive_anon:AREASTACK:- anon_pages:AREASTACK:- inactive:LINE1:-)]
388
  },
389
  # ----------------------------------------------
390
  'buddyinfo' =>
391
  {
392
    'munin' =>
393
    {
394
      'args'     => '--base 1000',
395
      'title'    => 'External fragmentztion: Buddyinfo',
396
      'vtitle'   => 'chunks',
397
      'category' => 'memory',
398
      'info'     => 'External fragmentation is a problem under some workloads, and buddyinfo is a useful tool for helping diagnose these problems. Buddyinfo will give you a clue as to how big an area you can safely allocate, or why a previous allocation failed.',
399
      'total'    => 'Total'
400
    },
401
    'fields' => []
402
  },
403
  # ----------------------------------------------
404
  'appinfo' =>
405
  {
406
    'munin' =>
407
    {
408
      'args'     => '--base 1024',
409
      'title'    => 'Application memory usage',
410
      'vtitle'   => 'bytes',
411
      'category' => 'memory'
412
    },
413
    'fields' => []
414
  },
415
  # ----------------------------------------------
416
  'appinfo.processes' =>
417
  {
418
    'munin' =>
419
    {
420
      'args'     => '--base 1000',
421
      'title'    => 'Application processes',
422
      'vtitle'   => 'count',
423
      'category' => 'processes'
424
    },
425
    'fields' => []
426
  },
427
  # ----------------------------------------------
428
  'appgroupinfo' =>
429
  {
430
    'munin' =>
431
    {
432
      'args'     => '--base 1024',
433
      'title'    => 'Application groups memory usage',
434
      'vtitle'   => 'bytes',
435
      'category' => 'memory'
436
    },
437
    'fields' => []
438
  },
439
  # ----------------------------------------------
440
  'pagetypeinfo' =>
441
  {
442
    'munin' =>
443
    {
444
      'args'     => '--base 1000',
445
      'title'    => 'External fragmentztion: Page type info',
446
      'vtitle'   => 'pages',
447
      'info'     => 'The pagetypinfo gives the same type of information as buddyinfo except broken down by migrate-type and finishes with details on how many page blocks of each type exist.',
448
      'category' => 'memory'
449
    },
450
    'fields' => []
451
  }
452
};
453
454
my $fields_source =
455
{
456
  # ----------------------------------------------
457
  'apps' =>  {
458
    'src' =>
459
    {
460
      # y - must exists, n - no
461
      'calculated' => 'used:y - buffers:y - cached:y - slab:n - page_tables:n - cached_swap:n',
462
    },
463
    'munin' =>
464
    {
465
      'label' => 'Applications',
466
      'info'  => 'Memory, used by applications'
467
    }
468
  },
469
  # ----------------------------------------------
470
  'free' =>  {
471
    'src' =>
472
    {
473
      'target' => 'meminfo',
474
      'field' => 'MemFree'
475
    },
476
    'munin' =>
477
    {
478
      'label' => 'Free',
479
      'info'  => 'The sum of LowFree+HighFree'
480
    }
481
  },
482
  # ----------------------------------------------
483
  'used' =>  {
484
    'src' =>
485
    {
486
      'calculated' => 'total:y - free:y',
487
    },
488
    'munin' =>
489
    {
490
      'label' => 'Used',
491
      'info'  => 'Used memory (see "Total" and "Free")'
492
    }
493
  },
494
  # ----------------------------------------------
495
  'usedwswap' =>  {
496
    'src' =>
497
    {
498
      'calculated' => 'used:y + swap_used:y',
499
    },
500
    'munin' =>
501
    {
502
      'label' => 'Used + Swap used',
503
      'info'  => 'See "Used" and "Swap used"'
504
    }
505
  },
506
  # ----------------------------------------------
507
  'total' =>  {
508
    'src' =>
509
    {
510
      'target' => 'meminfo',
511
      'field' => 'MemTotal'
512
    },
513
    'munin' =>
514
    {
515
      'label' => 'Total',
516
      'info'  => 'Total usable ram (i.e. physical ram minus a few reserved bits and the kernel binary code)'
517
    }
518
  },
519
  # ----------------------------------------------
520
  'buffers' =>  {
521
    'src' =>
522
    {
523
      'target' => 'meminfo',
524
      'field' => 'Buffers'
525
    },
526
    'munin' =>
527
    {
528
      'label' => 'Buffers',
529
      'info'  => 'Relatively temporary storage for raw disk blocks shouldn\'t get tremendously large (20MB or so)'
530
    }
531
  },
532
  # ----------------------------------------------
533
  'cached' =>  {
534
    'src' =>
535
    {
536
      'target' => 'meminfo',
537
      'field' => 'Cached'
538
    },
539
    'munin' =>
540
    {
541
      'label' => 'Cached',
542
      'info'  => 'In-memory cache for files read from the disk (the pagecache).  Doesn\'t include SwapCached'
543
    }
544
  },
545
  # ----------------------------------------------
546
  'cached_swap' =>  {
547
    'src' =>
548
    {
549
      'target' => 'meminfo',
550
      'field' => 'SwapCached'
551
    },
552
    'munin' =>
553
    {
554
      'label' => 'Swap cached',
555
      'info'  => 'Memory that once was swapped out, is swapped back in but still also is in the swapfile (if memory is needed it doesn\'t need to be swapped out AGAIN because it is already in the swapfile. This saves I/O)'
556
    }
557
  },
558
  # ----------------------------------------------
559
  'qlists' =>  {
560
    'src' =>
561
    {
562
      'target' => 'meminfo',
563
      'field' => 'Quicklists'
564
    },
565
    'munin' =>
566
    {
567
      'label' => 'Quicklists',
568
      'info'  => ''
569
    }
570
  },
571
  # ----------------------------------------------
572
  'active' =>  {
573
    'src' =>
574
    {
575
      'target' => 'meminfo',
576
      'field' => 'Active'
577
    },
578
    'munin' =>
579
    {
580
      'label' => 'Active',
581
      'info'  => 'Memory that has been used more recently and usually not reclaimed unless absolutely necessary.'
582
    }
583
  },
584
  # ----------------------------------------------
585
  'inactive' =>  {
586
    'src' =>
587
    {
588
      'target' => 'meminfo',
589
      'field' => 'Inactive'
590
    },
591
    'munin' =>
592
    {
593
      'label' => 'Inactive',
594
      'info'  => 'Memory which has been less recently used.  It is more eligible to be reclaimed for other purposes'
595
    }
596
  },
597
  # ----------------------------------------------
598
  'active_file' =>  {
599
    'src' =>
600
    {
601
      'target' => 'meminfo',
602
      'field' => 'Active(file)'
603
    },
604
    'munin' =>
605
    {
606
      'label' => 'Active (file)',
607
      'info'  => 'See "Active"'
608
    }
609
  },
610
  # ----------------------------------------------
611
  'inactive_file' =>  {
612
    'src' =>
613
    {
614
      'target' => 'meminfo',
615
      'field' => 'Inactive(file)'
616
    },
617
    'munin' =>
618
    {
619
      'label' => 'Inactive (file)',
620
      'info'  => 'See "Inactive"'
621
    }
622
  },
623
  # ----------------------------------------------
624
  'active_anon' =>  {
625
    'src' =>
626
    {
627
      'target' => 'meminfo',
628
      'field' => 'Active(anon)'
629
    },
630
    'munin' =>
631
    {
632
      'label' => 'Active (anonymous)',
633
      'info'  => 'See "Active"'
634
    }
635
  },
636
  # ----------------------------------------------
637
  'inactive_anon' =>  {
638
    'src' =>
639
    {
640
      'target' => 'meminfo',
641
      'field' => 'Inactive(anon)'
642
    },
643
    'munin' =>
644
    {
645
      'label' => 'Inactive (anonymous)',
646
      'info'  => 'See "Inactive"'
647
    }
648
  },
649
  # ----------------------------------------------
650
  'high_total' =>  {
651
    'src' =>
652
    {
653
      'target' => 'meminfo',
654
      'field' => 'HighTotal'
655
    },
656
    'munin' =>
657
    {
658
      'label' => 'High total',
659
      'info'  => 'See "High Free"'
660
    }
661
  },
662
  # ----------------------------------------------
663
  'high_free' =>  {
664
    'src' =>
665
    {
666
      'target' => 'meminfo',
667
      'field' => 'HighFree'
668
    },
669
    'munin' =>
670
    {
671
      'label' => 'High free',
672
      'info'  => 'Highmem is all memory above ~860MB of physical memory Highmem areas are for use by userspace programs, or for the pagecache. The kernel must use tricks to access this memory, making it slower to access than lowmem.'
673
    }
674
  },
675
  # ----------------------------------------------
676
  'high_used' =>  {
677
    'src' =>
678
    {
679
      'calculated' => 'high_total:y - high_free:y'
680
    },
681
    'munin' =>
682
    {
683
      'label' => 'High used',
684
      'info'  => 'Used high memory'
685
    }
686
  },
687
  # ----------------------------------------------
688
  'low_total' =>  {
689
    'src' =>
690
    {
691
      'target' => 'meminfo',
692
      'field' => 'LowTotal'
693
    },
694
    'munin' =>
695
    {
696
      'label' => 'Low total',
697
      'info'  => 'See "Low Free"'
698
    }
699
  },
700
  # ----------------------------------------------
701
  'low_free' =>  {
702
    'src' =>
703
    {
704
      'target' => 'meminfo',
705
      'field' => 'LowFree'
706
    },
707
    'munin' =>
708
    {
709
      'label' => 'Low free',
710
      'info'  => 'Lowmem is memory which can be used for everything that highmem can be used for, but it is also available for the kernel\'s use for its own data structures. Among many other things, it is where everything from the Slab is allocated.  Bad things happen when you\'re out of lowmem.'
711
    }
712
  },
713
  # ----------------------------------------------
714
  'low_used' =>  {
715
    'src' =>
716
    {
717
      'calculated' => 'low_total:y - low_free:y'
718
    },
719
    'munin' =>
720
    {
721
      'label' => 'Low used',
722
      'info'  => 'Used low memory'
723
    }
724
  },
725
  # ----------------------------------------------
726
  'l_h_total' =>  {
727
    'src' =>
728
    {
729
      'calculated' => 'low_total:y + high_total:y'
730
    },
731
    'munin' =>
732
    {
733
      'label' => 'Total',
734
      'info'  => 'Total phisical memory'
735
    }
736
  },
737
  # ----------------------------------------------
738
  'swap_total' =>  {
739
    'src' =>
740
    {
741
      'target' => 'meminfo',
742
      'field' => 'SwapTotal'
743
    },
744
    'munin' =>
745
    {
746
      'label' => 'Swap total',
747
      'info'  => 'Total amount of swap space available'
748
    }
749
  },
750
  # ----------------------------------------------
751
  'swap_free' =>  {
752
    'src' =>
753
    {
754
      'target' => 'meminfo',
755
      'field' => 'SwapFree'
756
    },
757
    'munin' =>
758
    {
759
      'label' => 'Swap free',
760
      'info'  => 'Memory which has been evicted from RAM, and is temporarily on the disk'
761
    }
762
  },
763
  # ----------------------------------------------
764
  'swap_used' =>  {
765
    'src' =>
766
    {
767
      'calculated' => 'swap_total:y - swap_free:y'
768
    },
769
    'munin' =>
770
    {
771
      'label' => 'Swap used',
772
      'info'  => 'Memory which has been evicted from RAM, and is temporarily on the disk'
773
    }
774
  },
775
  # ----------------------------------------------
776
  'dirty' =>  {
777
    'src' =>
778
    {
779
      'target' => 'meminfo',
780
      'field' => 'Dirty'
781
    },
782
    'munin' =>
783
    {
784
      'label' => 'Dirty',
785
      'info'  => 'Memory which is waiting to get written back to the disk'
786
    }
787
  },
788
  # ----------------------------------------------
789
  'writeback' =>  {
790
    'src'  =>
791
    {
792
      'target' => 'meminfo',
793
      'field' => 'Writeback'
794
    },
795
    'munin' =>
796
    {
797
      'label' => 'Writeback',
798
      'info'  => 'Memory which is actively being written back to the disk'
799
    }
800
  },
801
  # ----------------------------------------------
802
  'anon_pages' =>  {
803
    'src' =>
804
    {
805
      'target' => 'meminfo',
806
      'field' => 'AnonPages'
807
    },
808
    'munin' =>
809
    {
810
      'label' => 'Anonymous pages',
811
      'info'  => 'Non-file backed pages mapped into userspace page tables'
812
    }
813
  },
814
  # ----------------------------------------------
815
  'mapped' =>  {
816
    'src' =>
817
    {
818
      'target' => 'meminfo',
819
      'field' => 'Mapped'
820
    },
821
    'munin' =>
822
    {
823
      'label' => 'Mapped',
824
      'info'  => 'Files which have been mmaped, such as libraries'
825
    }
826
  },
827
  # ----------------------------------------------
828
  'mmap_copy' =>  {
829
    'src' =>
830
    {
831
      'target' => 'meminfo',
832
      'field' => 'MmapCopy'
833
    },
834
    'munin' =>
835
    {
836
      'label' => 'mmap copy',
837
      'info'  => 'Indicates the amount of RAM currently allocated by mmap to hold mappable regions that can\'t be mapped directly.  These are copies of the backing device or file if not anonymous.'
838
    }
839
  },
840
  # ----------------------------------------------
841
  'slab' =>  {
842
    'src' =>
843
    {
844
      'target' => 'meminfo',
845
      'field' => 'Slab'
846
    },
847
    'munin' =>
848
    {
849
      'label' => 'Slab',
850
      'info'  => 'In-kernel data structures cache'
851
    }
852
  },
853
  # ----------------------------------------------
854
  'slab_reclaimable' =>  {
855
    'src' =>
856
    {
857
      'target' => 'meminfo',
858
      'field' => 'SReclaimable'
859
    },
860
    'munin' =>
861
    {
862
      'label' => 'Slab reclaimable',
863
      'info'  => 'Part of Slab, that might be reclaimed, such as caches'
864
    }
865
  },
866
  # ----------------------------------------------
867
  'slab_unreclaimable' =>  {
868
    'src' =>
869
    {
870
      'target' => 'meminfo',
871
      'field' => 'SUnreclaim'
872
    },
873
    'munin' =>
874
    {
875
      'label' => 'Slab unreclaimable',
876
      'info'  => 'Part of Slab, that cannot be reclaimed on memory pressure'
877
    }
878
  },
879
  # ----------------------------------------------
880
  'page_tables' =>  {
881
    'src' =>
882
    {
883
      'target' => 'meminfo',
884
      'field' => 'PageTables'
885
    },
886
    'munin' =>
887
    {
888
      'label' => 'Page tables',
889
      'info'  => 'amount of memory dedicated to the lowest level of page tables'
890
    }
891
  },
892
  # ----------------------------------------------
893
  'nfs_unstable' =>  {
894
    'src' =>
895
    {
896
      'target' => 'meminfo',
897
      'field' => 'NFS_Unstable'
898
    },
899
    'munin' =>
900
    {
901
      'label' => 'NFS unstable',
902
      'info'  => 'NFS pages sent to the server, but not yet committed to stable <--> storage'
903
    }
904
  },
905
  # ----------------------------------------------
906
  'bounce' =>  {
907
    'src' =>
908
    {
909
      'target' => 'meminfo',
910
      'field' => 'Bounce'
911
    },
912
    'munin' =>
913
    {
914
      'label' => 'Bounce',
915
      'info'  => 'Memory used for block device "bounce buffers"'
916
    }
917
  },
918
  # ----------------------------------------------
919
  'writeback_fuse' =>  {
920
    'src' =>
921
    {
922
      'target' => 'meminfo',
923
      'field' => 'WritebackTmp'
924
    },
925
    'munin' =>
926
    {
927
      'label' => 'FUSE buffers',
928
      'info'  => 'Memory used by FUSE for temporary writeback buffers'
929
    }
930
  },
931
  # ----------------------------------------------
932
  'commit_limit' =>  {
933
    'src' =>
934
    {
935
      'target' => 'meminfo',
936
      'field' => 'CommitLimit'
937
    },
938
    'munin' =>
939
    {
940
      'label' => 'Commit limit',
941
      'info'  => 'Based on the overcommit ratio ("vm.overcommit_ratio"), this is the total amount of  memory currently available to be allocated on the system. This limit is only adhered to if strict overcommit accounting is enabled (mode 2 in "vm.overcommit_memory"). The CommitLimit is calculated with the following formula=> CommitLimit = ("vm.overcommit_ratio" * Physical RAM) + Swap For example, on a system with 1G of physical RAM and 7G of swap with a `vm.overcommit_ratio` of 30 it would yield a CommitLimit of 7.3G. For more details, see the memory overcommit documentation in vm/overcommit-accounting.',
942
    }
943
  },
944
  # ----------------------------------------------
945
  'committed_as' =>  {
946
    'src' =>
947
    {
948
      'target' => 'meminfo',
949
      'field' => 'Committed_AS'
950
    },
951
    'munin' =>
952
    {
953
      'label' => 'Committed AS',
954
      'info'  => 'The amount of memory presently allocated on the system. The committed memory is a sum of all of the memory which has been allocated by processes, even if it has not been "used" by them as of yet. A process which malloc()\'s 1G of memory, but only touches 300M of it will only show up as using 300M of memory even if it has the address space allocated for the entire 1G. This 1G is memory which has been "committed" to by the VM and can be used at any time by the allocating application. With strict overcommit enabled on the system (mode 2 in "vm.overcommit_memory"), allocations which would exceed the CommitLimit (detailed above) will not be permitted. This is useful if one needs to guarantee that processes will not fail due to lack of memory once that memory has been successfully allocated.'
955
    }
956
  },
957
  # ----------------------------------------------
958
  'vmalloc_total' =>  {
959
    'src'  =>
960
    {
961
      'target' => 'meminfo',
962
      'field' => 'VmallocTotal'
963
    },
964
    'munin' =>
965
    {
966
      'label' => 'Vmalloc total',
967
      'info'  => 'Total size of vmalloc memory area'
968
    }
969
  },
970
  # ----------------------------------------------
971
  'vmalloc_used' =>  {
972
    'src' =>
973
    {
974
      'target' => 'meminfo',
975
      'field' => 'VmallocUsed'
976
    },
977
    'munin' =>
978
    {
979
      'label' => 'Vmalloc used',
980
      'info'  => 'Amount of vmalloc area which is used'
981
    }
982
  },
983
  # ----------------------------------------------
984
  'vmalloc_chunk' =>  {
985
    'src' =>
986
    {
987
      'target' => 'meminfo',
988
      'field' => 'VmallocChunk'
989
    },
990
    'munin' =>
991
    {
992
      'label' => 'Vmalloc chunk',
993
      'info'  => 'largest contiguous block of vmalloc area which is free'
994
    }
995
  },
996
  # ----------------------------------------------
997
  'vmalloc_free' =>  {
998
    'src' =>
999
    {
1000
      # y - must exists, n - no
1001
      'calculated' => 'vmalloc_total:y - vmalloc_used:y'
1002
    },
1003
    'munin' =>
1004
    {
1005
      'label' => 'Vmalloc free',
1006
      'info'  => 'Free size of vmalloc memory area'
1007
    }
1008
  },
1009
  # ----------------------------------------------
1010
  'mlocked' =>  {
1011
    'src' =>
1012
    {
1013
      'target' => 'meminfo',
1014
      'field' => 'Mlocked'
1015
    },
1016
    'munin' =>
1017
    {
1018
      'label' => 'Mlocked',
1019
      'info'  => ''
1020
    }
1021
  },
1022
  # ----------------------------------------------
1023
  'shmem' =>  {
1024
    'src' =>
1025
    {
1026
      'target' => 'meminfo',
1027
      'field' => 'Shmem'
1028
    },
1029
    'munin' =>
1030
    {
1031
      'label' => 'Shared memory',
1032
      'info'  => ''
1033
    }
1034
  },
1035
  # ----------------------------------------------
1036
  'kernel_stack' =>  {
1037
    'src' =>
1038
    {
1039
      'target' => 'meminfo',
1040
      'field' => 'KernelStack'
1041
    },
1042
    'munin' =>
1043
    {
1044
      'label' => 'Kernel stack',
1045
      'info'  => 'Kernel stack'
1046
    }
1047
  },
1048
  # ----------------------------------------------
1049
  'hardware_corrupted' =>  {
1050
    'src' =>
1051
    {
1052
      'target' => 'meminfo',
1053
      'field' => 'HardwareCorrupted'
1054
    },
1055
    'munin' =>
1056
    {
1057
      'label' => 'Hardware corrupted',
1058
      'info'  => 'Hardware corrupted'
1059
    }
1060
  },
1061
  # ----------------------------------------------
1062
  'huge_pages_total' =>  {
1063
    'src' =>
1064
    {
1065
      'target' => 'meminfo',
1066
      'field' => 'HugePages_Total'
1067
    },
1068
    'munin' =>
1069
    {
1070
      'label' => 'HugePages total',
1071
      'info'  => 'Huge Pages is a feature available in later Linux kernels that provides two important benefits: 1. Locks the memory available to huge pages, so it cannot be paged to disk 2. Make the TLB (translation lookaside buffer) much smaller on the processor, as the number of entries is much smaller. This is due to the fact the standard page size in Linux is 4K, whereas a huge page is either 2MB of 4MB in size. This makes managing virtual memory much more efficient, as the processor does not have to work as hard switching pages in and out.'
1072
    }
1073
  },
1074
  # ----------------------------------------------
1075
  'huge_pages_free' =>  {
1076
    'src' =>
1077
    {
1078
      'target' => 'meminfo',
1079
      'field' => 'HugePages_Free'
1080
    },
1081
    'munin' =>
1082
    {
1083
      'label' => 'HugePages free',
1084
      'info'  => 'See "HugePages_Total"'
1085
    }
1086
  },
1087
  # ----------------------------------------------
1088
  'huge_pages_rsvd' =>  {
1089
    'src' =>
1090
    {
1091
      'target' => 'meminfo',
1092
      'field' => 'HugePages_Rsvd'
1093
    },
1094
    'munin' =>
1095
    {
1096
      'label' => 'HugePages reserved',
1097
      'info'  => 'See "HugePages_Total"'
1098
    }
1099
  },
1100
  # ----------------------------------------------
1101
  'huge_pages_surp' =>  {
1102
    'src' =>
1103
    {
1104
      'target' => 'meminfo',
1105
      'field' => 'HugePages_Surp'
1106
    },
1107
    'munin' =>
1108
    {
1109
      'label' => 'HugePages surp',
1110
      'info'  => 'See "HugePages_Total"'
1111
    }
1112
  },
1113
  # ----------------------------------------------
1114
  'huge_pages_total_size' =>  {
1115
    'src' =>
1116
    {
1117
      'calculated' => 'huge_page_size:y * huge_pages_total:y'
1118
    },
1119
    'munin' =>
1120
    {
1121
      'label' => 'HugePages size',
1122
      'info'  => 'Total size of HugePages'
1123
    }
1124
  },
1125
  # ----------------------------------------------
1126
  'huge_pages_free_size' =>  {
1127
    'src' =>
1128
    {
1129
      'calculated' => 'huge_page_size:y * huge_pages_free:y'
1130
    },
1131
    'munin' =>
1132
    {
1133
      'label' => 'HugePages free',
1134
      'info'  => 'Free HugePages memory size'
1135
    }
1136
  },
1137
  # ----------------------------------------------
1138
  'huge_pages_rsvd_size' =>  {
1139
    'src' =>
1140
    {
1141
      'calculated' => 'huge_page_size:y * huge_pages_rsvd:y'
1142
    },
1143
    'munin' =>
1144
    {
1145
      'label' => 'HugePages rsvd',
1146
      'info'  => 'HugePages reserved size'
1147
    }
1148
  },
1149
  # ----------------------------------------------
1150
  'huge_pages_surp_size' =>  {
1151
    'src' =>
1152
    {
1153
      'calculated' => 'huge_page_size:y * huge_pages_surp:y'
1154
    },
1155
    'munin' =>
1156
    {
1157
      'label' => 'HugePages surp',
1158
      'info'  => 'HugePages surp size'
1159
    }
1160
  },
1161
  # ----------------------------------------------
1162
  'huge_page_size' =>  {
1163
    'src' =>
1164
    {
1165
      'target' => 'meminfo',
1166
      'field' => 'Hugepagesize'
1167
    },
1168
    'munin' =>
1169
    {
1170
      'label' => 'HugePage size',
1171
      'info'  => 'HugePage size'
1172
    }
1173
  },
1174
  # ----------------------------------------------
1175
  'direct_map_4k' =>  {
1176
    'src' =>
1177
    {
1178
      'target' => 'meminfo',
1179
      'field' => 'DirectMap4k'
1180
    },
1181
    'munin' =>
1182
    {
1183
      'label' => 'Direct map 4k',
1184
      'info'  => ''
1185
    }
1186
  },
1187
  # ----------------------------------------------
1188
  'direct_map_2m' =>  {
1189
    'src' =>
1190
    {
1191
      'target' => 'meminfo',
1192
      'field' => 'DirectMap2M'
1193
    },
1194
    'munin' =>
1195
    {
1196
      'label' => 'Direct map 2M',
1197
      'info'  => ''
1198
    }
1199
  },
1200
  # ----------------------------------------------
1201
  'direct_map_4m' =>  {
1202
    'src' =>
1203
    {
1204
      'target' => 'meminfo',
1205
      'field' => 'DirectMap4M'
1206
    },
1207
    'munin' =>
1208
    {
1209
      'label' => 'Direct map 4M',
1210
      'info'  => ''
1211
    }
1212
  },
1213
  # ----------------------------------------------
1214
  'direct_map_1g' =>  {
1215
    'src' =>
1216
    {
1217
      'target' => 'meminfo',
1218
      'field' => 'DirectMap1G'
1219
    },
1220
    'munin' =>
1221
    {
1222
      'label' => 'Direct map 1G',
1223
      'info'  => ''
1224
    }
1225
  },
1226
  # ---------------------------------------------- slabinfo -------------
1227
  # ----------------------------------------------
1228
  'min_obj_size' =>  {
1229
    'src' =>
1230
    {
1231
      'target' => 'slabinfo',
1232
      'field' => 'min_obj_size'
1233
    },
1234
    'munin' =>
1235
    {
1236
      'label' => 'Min. object size',
1237
      'info'  => 'Minimal object size'
1238
    }
1239
  },
1240
  # ----------------------------------------------
1241
  'avg_obj_size' =>  {
1242
    'src' =>
1243
    {
1244
      'target' => 'slabinfo',
1245
      'field' => 'avg_obj_size'
1246
    },
1247
    'munin' =>
1248
    {
1249
      'label' => 'Avg. object size',
1250
      'info'  => 'Average object size'
1251
    }
1252
  },
1253
  # ----------------------------------------------
1254
  'max_obj_size' =>  {
1255
    'src' =>
1256
    {
1257
      'target' => 'slabinfo',
1258
      'field' => 'max_obj_size'
1259
    },
1260
    'munin' =>
1261
    {
1262
      'label' => 'Max. object size',
1263
      'info'  => 'Maximal object size'
1264
    }
1265
  },
1266
  # ----------------------------------------------
1267
  'total_objects' =>  {
1268
    'src' =>
1269
    {
1270
      'target' => 'slabinfo',
1271
      'field' => 'total_objects'
1272
    },
1273
    'munin' =>
1274
    {
1275
      'label' => 'Total objects',
1276
      'info'  => 'Total objects'
1277
    }
1278
  },
1279
  # ----------------------------------------------
1280
  'active_objects' =>  {
1281
    'src' =>
1282
    {
1283
      'target' => 'slabinfo',
1284
      'field' => 'active_objects'
1285
    },
1286
    'munin' =>
1287
    {
1288
      'label' => 'Active objects',
1289
      'info'  => 'Active objects'
1290
    }
1291
  },
1292
  # ----------------------------------------------
1293
  'inactive_objects' =>  {
1294
    'src' =>
1295
    {
1296
      'calculated' => 'total_objects:y - active_objects:y'
1297
    },
1298
    'munin' =>
1299
    {
1300
      'label' => 'Inactive objects',
1301
      'info'  => 'Inactive objects'
1302
    }
1303
  },
1304
  # ----------------------------------------------
1305
  'total_slabs' =>  {
1306
    'src' =>
1307
    {
1308
      'target' => 'slabinfo',
1309
      'field' => 'total_slabs'
1310
    },
1311
    'munin' =>
1312
    {
1313
      'label' => 'Total slabs',
1314
      'info'  => 'Total slabs'
1315
    }
1316
  },
1317
  # ----------------------------------------------
1318
  'active_slabs' =>  {
1319
    'src' =>
1320
    {
1321
      'target' => 'slabinfo',
1322
      'field' => 'active_slabs'
1323
    },
1324
    'munin' =>
1325
    {
1326
      'label' => 'Active slabs',
1327
      'info'  => 'Active slabs'
1328
    }
1329
  },
1330
  # ----------------------------------------------
1331
  'shared_slabs' =>  {
1332
    'src' =>
1333
    {
1334
      'target' => 'slabinfo',
1335
      'field' => 'shared_slabs'
1336
    },
1337
    'munin' =>
1338
    {
1339
      'label' => 'Shared slabs',
1340
      'info'  => 'Shared slabs'
1341
    }
1342
  },
1343
  # ----------------------------------------------
1344
  'inactive_slabs' =>  {
1345
    'src' =>
1346
    {
1347
      'calculated' => 'total_slabs:y - active_slabs:y'
1348
    },
1349
    'munin' =>
1350
    {
1351
      'label' => 'Inactive slabs',
1352
      'info'  => 'Inactive slabs'
1353
    }
1354
  }
1355
};
1356
1357
# appdata descriptions
1358
my $application_fields =
1359
{
1360
  'label' =>
1361
  {
1362
    'VmLck'  => 'Locked in mem',
1363
    'VmHWM'  => 'Peak resident',
1364
    'VmPeak' => 'Peak virtual',
1365
    'VmSwap' => 'Swap usage',
1366
    'VmExe'  => 'Code segment',
1367
    'VmPTE'  => 'Page table entries',
1368
    'VmData' => 'Data segment',
1369
    'VmLib'  => 'Shared library',
1370
    'VmSize' => 'Allocated virtual',
1371
    'VmRSS'  => 'Mapped in RAM',
1372
    'VmStk'  => 'Stack'
1373
   },
1374
  'info' =>
1375
  {
1376
    'VmLck'  => 'The amount of locked memory',
1377
    'VmHWM'  => 'Peak resident set size ("high water mark")',
1378
    'VmPeak' => 'Peak virtual memory size',
1379
    'VmSwap' => 'Size of swap usage (the number of referred swapents)',
1380
    'VmExe'  => 'The size of the executable segment',
1381
    'VmPTE'  => 'Size of page table entries',
1382
    'VmData' => 'The size of the Data segment',
1383
    'VmLib'  => 'Size of shared library code',
1384
    'VmSize' => 'The size of the virtual memory allocated to the process',
1385
    'VmRSS'  => 'The amount of memory mapped in RAM ( instead of swapped out )',
1386
    'VmStk'  => 'The stack size'
1387
   },
1388
  'draw' =>
1389
  {
1390
    'VmSize' => 'AREA',
1391
    'VmRSS'  => 'AREA',
1392
    'VmStk'  => 'STACK',
1393
    'VmData' => 'STACK',
1394
    'VmExe'  => 'LINE1.5',
1395
    'VmLib'  => 'LINE1.5',
1396
    'VmLck'  => 'LINE1',
1397
    'VmPTE'  => 'LINE1',
1398
    'VmSwap' => 'AREA',
1399
    'VmHWM'  => 'LINE2',
1400
    'VmPeak' => 'LINE2'
1401
    
1402
   },
1403
   'color' =>
1404
   {
1405
     'VmSize' => '0066CCBB',
1406
     'VmPeak' => '000000',
1407
     'VmRSS'  => '00FF33BB',
1408
     'VmStk'  => 'FFCC55BB',
1409
     'VmData' => '00FFFFBB',
1410
     'VmExe'  => 'FF1100',
1411
     'VmLib'  => 'DD9900',
1412
     'VmLck'  => 'AA00AA',
1413
     'VmPTE'  => '7E7E7E',
1414
     'VmSwap' => 'FF447788',
1415
     'VmHWM'  => 'FFFFFF',
1416
   },
1417
   'order' => [qw(VmSize VmPeak VmRSS VmStk VmData VmSwap VmExe VmLib VmLck VmPTE VmHWM)]
1418
};
1419
1420
1421
# ----------------- main ----------------
1422
need_multigraph();
1423
1424
my @graphs_to_sort = ();
1425
my $stats          = {};
1426
my $storage        = {};
1427
my $applications   = {};
1428
my $enabled_graphs       = $ENV{'enabled_graphs'}     || '.*';
1429
$applications->{'show'}  = $ENV{'applications'}       || undef; # '(psi|chrome|mc|\w{1,3}sh|firefox)';
1430
$applications->{'group'} = $ENV{'applications_group'} || undef; # '(chrome|firefox):browsers;\w{1,3}sh:sh';
1431
$applications->{'wait'}  = $ENV{'application_wait'}   || 1800;
1432
1433
restore_state_data();
1434
load_stats();
1435
save_state_data();
1436
1437
if (defined($ARGV[0]) and ($ARGV[0] eq 'autoconf'))
1438
{
1439
  printf("%s\n", -e "/proc/meminfo" ? "yes" : "no (/proc/meminfo not exists)");
1440
  exit (0);
1441
}
1442
1443
if (defined($ARGV[0]) and ($ARGV[0] eq 'config'))
1444
{
1445
  sort_graphs_fields();
1446
  print_config();
1447
  exit (0);
1448
}
1449
1450
print_values();
1451
exit(0);
1452
1453
# ---------------------------------------- data load ---------------------------------
1454
# ----------------------------------- saving state data using munin --------------------
1455
sub save_state_data
1456
{
1457
  $storage->{'timestamp'}{'previous'} = time();
1458
  my $d = Data::Dumper->new([$storage]);
1459
  $d->Indent(0);
1460
  save_state($d->Dump);
1461
}
1462
1463
# -------------------------------- loading previous state data using munin -------------------
1464
sub restore_state_data
1465
{
1466
  my $VAR1;
1467
  my $states = (restore_state())[0];
1468
  eval $states if defined $states;
1469
  $storage = $VAR1;
1470
  $storage->{'timestamp'}{'current'} = time();
1471
}
1472
1473
# -------------------------- loading memory info ---------------------------------
1474
sub load_meminfo
1475
{
1476
  my $file = "/proc/meminfo";
1477
  my $result = {};
1478
  open (FH, '<', $file) or die "$! $file \n";
1479
  my ($var, $val) = ('',0);
1480
  for my $line (<FH>)
1481
  {
1482
    chomp $line;
1483
    ($var, $val) = split(/:?\s+/, $line);
1484
    $val = $val * 1024 unless ($var =~ m/HugePages_.*/);
1485
    $result->{$var} = $val;
1486
  }
1487
  close (FH);
1488
  return $result;
1489
}
1490
# -------------------------- loading vmalloc info ---------------------------------
1491
sub load_vmallocinfo
1492
{
1493
  my $file = "/proc/vmallocinfo";
1494
  my $result = {};
1495
  open (FH, '<', $file) or die "$! $file \n";
1496
  my @splitted = ();
1497
  my $name = '';
1498
  for my $line (<FH>)
1499
  {
1500
    chomp $line;
1501
    @splitted =  split(/\s+/, $line);
1502
    $name     = (split(/\+/ , $splitted[2]))[0];
1503
    $result->{$name}  = 0 unless (exists($result->{$name}));
1504
    $result->{$name} += $splitted[1];
1505
1506
    my $field_name = sprintf("vi_%s", clean_fieldname($name));
1507
    append_field         ($field_name, 'vmallocinfo', $name, $name, $name);
1508
    append_field_to_graph($field_name, 'meminfo_virtual.vmalloc_allocates', 'AREASTACK', '-');
1509
  }
1510
  push(@graphs_to_sort, 'meminfo_virtual.vmalloc_allocates');
1511
  close (FH);
1512
  #print Dumper $result;
1513
1514
  return $result;
1515
}
1516
# -------------------------- loading slab info ---------------------------------
1517
sub load_slabinfo
1518
{
1519
  my $file = "/proc/slabinfo";
1520
  my $result = {};
1521
  open (FH, '<', $file) or die "$! $file \n";
1522
  my $groups = {};
1523
1524
  my ($temp, $objects, $tunables, $slabdata) = ('','','','');
1525
  # objects
1526
  my ($name, $active_objs, $num_objs, $objsize, $objperslab, $pagesperslab) = ('','','','','','');
1527
  # tunables
1528
  #my ($limit, $batchcount, $sharedfactor) = ('','','');
1529
  # slabdata
1530
  my ($active_slabs, $num_slabs, $sharedavail) = ('','','');
1531
1532
  my ($min_obj_size, $max_obj_size, $total_active_objects, $total_objects_size, $total_objects) = (1024, 0, 0, 0);
1533
  my ($total_slabs, $total_active_slabs, $total_shared_slabs) = (0, 0, 0);
1534
  # name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
1535
  for my $line (<FH>)
1536
  {
1537
    next if $line =~ m/^(#\s+|slabinfo)/;
1538
    chomp $line;
1539
    ($objects, $tunables, $slabdata)                                       = split(/\s+:\s+/, $line    );
1540
    ($name, $active_objs, $num_objs, $objsize, $objperslab, $pagesperslab) = split(/\s+/    , $objects );
1541
    #($temp, $limit, $batchcount, $sharedfactor)                            = split(/\s+/    , $tunables);
1542
    ($temp, $active_slabs, $num_slabs, $sharedavail)                       = split(/\s+/    , $slabdata);
1543
    # splitting slabs to groups
1544
    if ($name =~ m/((TC|UD|SCT)P|RAW|UNIX|inet|ip6)/i)
1545
    {
1546
      push(@{$groups->{'network'}}, $name);
1547
    }
1548
    elsif($name =~ m/[-_]/)
1549
    {
1550
      my @parts = split(/[-_]/, $name);
1551
      push(@{$groups->{$parts[0]}}, $name);
1552
    }
1553
    else
1554
    {
1555
      push(@{$groups->{'other'}}, $name);
1556
    }
1557
1558
    if($num_objs > 0)
1559
    {
1560
      $min_obj_size          = $objsize if $objsize < $min_obj_size;
1561
      $max_obj_size          = $objsize if $objsize > $max_obj_size;
1562
      $total_objects_size   += $objsize * $num_objs;
1563
      $total_objects        += $num_objs;
1564
      $total_active_objects += $active_objs;
1565
    }
1566
    if($num_slabs > 0)
1567
    {
1568
      $total_slabs        += $num_slabs;
1569
      $total_active_slabs += $active_slabs;
1570
      $total_shared_slabs += $sharedavail;
1571
    }
1572
    # foreach slab field append this field to global fields list
1573
    my $field_name = sprintf("slab_%s_size", clean_fieldname($name));
1574
    append_field($field_name, 'slabinfo', $field_name, $name, '');
1575
    $result->{$field_name} = $objsize*$num_objs;
1576
  }
1577
  # second iteration for better splitting slabs to groups
1578
  for my $group ( keys %{$groups} )
1579
  {
1580
    if ( scalar(@{$groups->{$group}}) == 1 )
1581
    {
1582
      my $name = $groups->{$group}[0];
1583
      if  ($name =~ m/cache/)  { push(@{$groups->{'caches'}}, $name); }
1584
      else                     { push(@{$groups->{'other'}}, $name) ; }
1585
      delete($groups->{$group});
1586
    }
1587
  }
1588
  # appending grsaph of slabs groups to global graphs list
1589
1590
  # groups graphs
1591
  for my $group ( sort keys %{$groups} )
1592
  {
1593
    my @group_size_formula = ();
1594
    # appending group graph to global graphs list
1595
    my $graph_name = sprintf("slab.slabs_size_%s", $group);
1596
    append_graph($graph_name, '--base 1024',
1597
                 sprintf("Slabs size [%s]"                , $group),
1598
                 sprintf("Slabs size jf group, named '%s'", $group),
1599
                 'bytes', 'groups_size', 'Total');
1600
    # appending fields to graph and calculating formula
1601
    for my $group_field ($groups->{$group}[0] =~ m/-\d+(\(.*\))?$/ ? @{$groups->{$group}} : sort @{$groups->{$group}})
1602
    {
1603
      my $group_field_name = sprintf("slab_%s_size", clean_fieldname($group_field));
1604
      append_field_to_graph($group_field_name, $graph_name, 'AREASTACK', '-');
1605
      push (@group_size_formula, $group_field_name)
1606
    }
1607
    # appending `summ of group` field to global fields list
1608
    my $summ_field_name = sprintf("slab_size_summ_%s", clean_fieldname($group));
1609
    append_formula_field ($summ_field_name, join(":n + ", @group_size_formula).':n', $group, '');
1610
    append_field_to_graph($summ_field_name, 'slab.slabs_size_all_groups', 'AREASTACK', '-');
1611
  }
1612
  #$graphs_source->{'slab.slabs_size_all_groups'}{'fields'} = sort(@{$graphs_source->{'slab.slabs_size_all_groups'}{'fields'}});
1613
  #print Dumper $groups;
1614
  $result->{'min_obj_size'}   = $min_obj_size;
1615
  $result->{'avg_obj_size'}   = $total_objects_size / $total_objects;
1616
  $result->{'max_obj_size'}   = $max_obj_size;
1617
  $result->{'total_objects'}  = $total_objects;
1618
  $result->{'active_objects'} = $total_active_objects;
1619
  $result->{'total_slabs'}    = $total_slabs;
1620
  $result->{'active_slabs'}   = $total_active_slabs;
1621
  $result->{'shared_slabs'}   = $total_shared_slabs;
1622
  close (FH);
1623
  return $result;
1624
}
1625
# -------------------------- loading buddyinfo ---------------------------------
1626
sub load_buddyinfo
1627
{
1628
  my $file = "/proc/buddyinfo";
1629
  my $result = {};
1630
  open (FH, '<', $file) or die "$! $file \n";
1631
  my ($node, $zone, $info) = ('','','');
1632
  my ($graph_name, $field_name, $summ_field_name, $num) = ('','','',0);
1633
  my @splitted_info = ();
1634
  for my $line (<FH>)
1635
  {
1636
    chomp $line;
1637
    ($node, $info) = split(/,\s+zone\s+/, $line);
1638
    @splitted_info = split(/\s+/, $info);
1639
    $zone = shift (@splitted_info);
1640
    $summ_field_name = clean_fieldname(sprintf("summ_%s_%s", $node, $zone));
1641
    append_field         ($summ_field_name, 'buddyinfo', $summ_field_name, sprintf("%s, zone %s", $node, $zone), 'Chunks count');
1642
    append_field_to_graph($summ_field_name, 'buddyinfo', 'AREASTACK', '-');
1643
    $result->{$summ_field_name} = 0;
1644
    $graph_name = 'buddyinfo.'.clean_fieldname(sprintf("%s_%s", $node, $zone));
1645
    append_graph($graph_name, '--base 1000',
1646
                 sprintf("%s, zone %s"          , $node, $zone),
1647
                 sprintf("Chunks of %s, zone %s", $node, $zone),
1648
                 'chunks', $node, 'Total');
1649
    $num = 0;
1650
1651
    for my $chunk (@splitted_info)
1652
    {
1653
      $result->{$summ_field_name} += $chunk;
1654
      $field_name = clean_fieldname(sprintf("chunk_%s_%s_%s", $node, $zone, $num));
1655
      append_field         ($field_name, 'buddyinfo', $field_name, sprintf("Chunks of (2^%2d) * Page size", $num), 'Chunks count');
1656
      append_field_to_graph($field_name, $graph_name, 'AREASTACK', '-');
1657
      $num++;
1658
      $result->{$field_name} = $chunk;
1659
    }
1660
  }
1661
  close (FH);
1662
  return $result;
1663
}
1664
1665
# ---------------- applications info -----------
1666
sub summ_app_usage
1667
{
1668
  my $app_data = $_[0];
1669
  return exists($app_data->{'data'}{'VmData'}) ? $app_data->{'data'}{'VmData'} : 0 +
1670
         exists($app_data->{'data'}{'VmRSS'})  ? $app_data->{'data'}{'VmRSS'}  : 0 +
1671
         exists($app_data->{'data'}{'VmStk'})  ? $app_data->{'data'}{'VmStk'}  : 0;
1672
}
1673
sub load_appinfo
1674
{
1675
  my $result = {};
1676
  my $procdir = IO::Dir->new("/proc");
1677
  if(defined $procdir)
1678
  {
1679
    push(@graphs_to_sort, qw(appinfo appgroupinfo appinfo.processes));
1680
    my $current_time = time();
1681
    my $status_file = '';
1682
    my ($pid, $label, $data, $app_name, $field_name, $summ_field_name, $processes_field_name, $group_summ_field_name, $graph_name, $bytes) = (0,'','','','','','','','',0);
1683
    #loading application groups
1684
    my $groups = {};
1685
    if(defined($applications->{'group'}))
1686
    {
1687
      my ($g_name, $g_reg) = ('','');
1688
      for my $gr (split(/;/, $applications->{'group'}))
1689
      {
1690
        ($g_reg, $g_name) = split(/:/, $gr);
1691
        $groups->{$g_name} = $g_reg;
1692
      }
1693
    }
1694
1695
    # for each /proc/{pid}
1696
    while (defined ($pid = $procdir->read))
1697
    {
1698
      next unless $pid =~ m/\d+/; # only pids
1699
      my $status_file = sprintf("/proc/%s/status", $pid);
1700
      my $app_data = {};
1701
      open (FH, '<', $status_file) or next;
1702
      # collect application data
1703
      for my $line (<FH>)
1704
      {
1705
        chomp $line;
1706
        ($label, $data) = split(/:\s+/, $line);
1707
        $app_data->{'name'} = $data if $label eq 'Name';
1708
        $app_data->{'data'}{$label} = (split(/\s+/, $data))[0] * 1024 if($label =~ m/^Vm.*/);
1709
      }
1710
      close (FH);
1711
      # application data has memory information?
1712
      if(exists($app_data->{'data'}{'VmSize'}))
1713
      {
1714
1715
        # generate graphs/fields, collect values
1716
        # application in list?
1717
        if(defined($applications->{'show'}) and $app_data->{'name'} =~ m/$applications->{'show'}/)
1718
        {
1719
          $storage->{'applications'}{'single'}{$app_data->{'name'}} = $current_time;
1720
          append_application($app_data, $result);
1721
        }
1722
        for my $group (keys %{$groups})
1723
        {
1724
          if($app_data->{'name'} =~ m/$groups->{$group}/)
1725
          {
1726
            $storage->{'applications'}{'groups'}{$group}{$app_data->{'name'}} = $current_time;
1727
            append_application_to_group($app_data, $group, $result);
1728
          }
1729
        }
1730
      }
1731
    }
1732
    # not runned applications detect and show
1733
    my $dummy_app_data = {};
1734
    for my $app (keys %{$storage->{'applications'}{'single'}})
1735
    {
1736
      next if($storage->{'applications'}{'single'}{$app} == $current_time);
1737
      if($current_time - $storage->{'applications'}{'single'}{$app} >= $applications->{'wait'})
1738
      {
1739
        delete ($storage->{'applications'}{'single'}{$app});
1740
        next;
1741
      }
1742
      $dummy_app_data->{'name'} = $app;
1743
      append_application($dummy_app_data, $result);
1744
    }
1745
    for my $group (keys %{$storage->{'applications'}{'groups'}})
1746
    {
1747
      for my $app (keys %{$storage->{'applications'}{'groups'}{$group}})
1748
      {
1749
        next if($storage->{'applications'}{'groups'}{$group}{$app} == $current_time);
1750
        if ($current_time - $storage->{'applications'}{'groups'}{$group}{$app} >= $applications->{'wait'})
1751
        {
1752
          delete ($storage->{'applications'}{'groups'}{$group}{$app});
1753
          next;
1754
        }
1755
        $dummy_app_data->{'name'} = $app;
1756
        append_application_to_group($dummy_app_data, $group, $result);
1757
      }
1758
    }
1759
  }
1760
  return $result;
1761
}
1762
1763
sub append_application
1764
{
1765
  my ($app_data, $result) = @_[0..1];
1766
1767
  # field for summary
1768
  my $summ_field_name = clean_fieldname(sprintf("summ_%s", $app_data->{'name'}));
1769
  append_field         ($summ_field_name, 'appinfo', $summ_field_name, $app_data->{'name'}, $app_data->{'name'});
1770
  append_field_to_graph($summ_field_name, 'appinfo', 'LINE1', '-');
1771
1772
  # field for processes count
1773
  my $processes_field_name = clean_fieldname(sprintf("pr_%s", $app_data->{'name'}));
1774
  append_field         ($processes_field_name, 'appinfo', $processes_field_name, $app_data->{'name'}, sprintf("%s processes count", $app_data->{'name'}));
1775
  append_field_to_graph($processes_field_name, 'appinfo.processes', 'LINE1', '-');
1776
1777
  # values
1778
  $result->{$summ_field_name}       = 0     unless  (exists($result->{$summ_field_name}));
1779
  $result->{$processes_field_name}  = 0     unless  (exists($result->{$processes_field_name}));
1780
  $result->{$processes_field_name} ++       if      (exists($app_data->{'data'}));
1781
  $result->{$summ_field_name}      += summ_app_usage($app_data) if exists($app_data->{'data'});
1782
  $result->{$summ_field_name}       = 'NaN' unless  (exists($app_data->{'data'}));
1783
1784
  # per-application graphs
1785
  my $graph_name = sprintf("appinfo.app_%s", clean_fieldname($app_data->{'name'}));
1786
  append_graph($graph_name, '--base 1024',
1787
               sprintf("Summary `%s` memory usage"                , $app_data->{'name'}),
1788
               sprintf("All `%s` processes summ by each parameter", $app_data->{'name'}),
1789
              'bytes', 'summary');
1790
1791
  for my $label (@{$application_fields->{'order'}})
1792
  {
1793
    my $field_name = clean_fieldname(sprintf("app_%s_%s", $app_data->{'name'}, $label));
1794
    append_field         ($field_name, 'appinfo', $field_name, $application_fields->{'label'}{$label}, $application_fields->{'info'}{$label});
1795
    append_field_to_graph($field_name, $graph_name, $application_fields->{'draw'}{$label}, $application_fields->{'color'}{$label});
1796
1797
    $result->{$field_name}  =     0 unless (exists($result->{$field_name}));
1798
    $result->{$field_name} += $app_data->{'data'}{$label} if (exists($app_data->{'data'}{$label}));
1799
    $result->{$field_name}  = 'NaN' unless (exists($app_data->{'data'}{$label}));
1800
  }
1801
  #return $graph_name;
1802
}
1803
1804
sub append_application_to_group
1805
{
1806
  my ($app_data, $group, $result) = @_[0..2];
1807
  my $group_summ_field_name = clean_fieldname(sprintf("group_summ_%s", $group));
1808
  append_field         ($group_summ_field_name, 'appinfo', $group_summ_field_name, $group, $group);
1809
  append_field_to_graph($group_summ_field_name, 'appgroupinfo', 'LINE1', '-');
1810
1811
  # per process in group
1812
  my $graph_name = sprintf("appgroupinfo.group_%s_applications", clean_fieldname($group));
1813
  append_graph($graph_name, '--base 1024',
1814
               sprintf("Group `%s` applications memory usage", $group),
1815
               sprintf("All group `%s` applications summ"    , $group),
1816
               'bytes', 'applications', 'Total');
1817
  push(@graphs_to_sort, $graph_name);
1818
1819
  my $summ_field_name = clean_fieldname(sprintf("group_%s_proc_%s", $group, $app_data->{'name'}));
1820
  append_field         ($summ_field_name, 'appinfo', $summ_field_name, $app_data->{'name'}, $app_data->{'name'});
1821
  append_field_to_graph($summ_field_name, $graph_name, 'AREASTACK', '-');
1822
1823
  $result->{$summ_field_name}        = 0     unless (exists($result->{$summ_field_name}));
1824
  $result->{$summ_field_name}       += summ_app_usage($app_data) if exists($app_data->{'data'});
1825
  $result->{$summ_field_name}        = 'NaN' unless (exists($app_data->{'data'}));
1826
  $result->{$group_summ_field_name}  = 0     unless (exists($result->{$group_summ_field_name}));
1827
  $result->{$group_summ_field_name} += $result->{$summ_field_name} if exists($app_data->{'data'});
1828
1829
  # per group summary
1830
  $graph_name = sprintf("appgroupinfo.group_%s_summ", clean_fieldname($group));
1831
  append_graph($graph_name, '--base 1024',
1832
                 sprintf("Summary group `%s` memory usage"                , $group),
1833
                 sprintf("All group `%s` processes summ by each parameter", $group),
1834
                 'bytes', 'summary');
1835
1836
  for my $label (@{$application_fields->{'order'}})
1837
  {
1838
    my $field_name = clean_fieldname(sprintf("group_%s_%s", $group, $label));
1839
    append_field         ($field_name, 'appinfo', $field_name, $application_fields->{'label'}{$label}, $application_fields->{'info'}{$label});
1840
    append_field_to_graph($field_name, $graph_name, $application_fields->{'draw'}{$label}, $application_fields->{'color'}{$label});
1841
1842
    $result->{$field_name}  = 0 unless (exists($result->{$field_name}));
1843
    $result->{$field_name} += $app_data->{'data'}{$label} if exists($app_data->{'data'}{$label});
1844
  }
1845
}
1846
1847
# -------------------------- loading pagetype info ---------------------------------
1848
sub load_pagetypeinfo
1849
{
1850
  my $file = "/proc/pagetypeinfo";
1851
  my $result = {};
1852
  my ($temp, $node, $zone, $type) = ('','','','');
1853
  my ($field_name, $graph_name) = ('','');
1854
  my @types = ();
1855
  my @values = ();
1856
  open (FH, '<', $file) or die "$! $file \n";
1857
  for my $line (<FH>)
1858
  {
1859
    chomp $line;
1860
    next if($line =~ m/^(Page|Free|\s*)$/);
1861
    if ($line =~ m/^Number/)
1862
    {
1863
      my $num = 0;
1864
      for $temp (split(/\s+/, $line))
1865
      {
1866
        push(@types, $temp) if ($num>3);
1867
        $num++;
1868
      }
1869
      next;
1870
    }
1871
    if ($line =~ m/,\s+type/)
1872
    {
1873
      ($node, $zone, $temp) = split(/,\s+/, $line);
1874
      $node   = (split(/\s+/, $node))[1];
1875
      $zone   = (split(/\s+/, $zone))[1];
1876
      @values =  split(/\s+/, $temp);
1877
      shift(@values); $type = shift(@values);
1878
      $graph_name = 'pagetypeinfo.'.clean_fieldname(sprintf("n%s_z%s_t%s", $node, $zone, $type));
1879
      append_graph($graph_name, '--base 1024',
1880
                   sprintf("Node %s, zone %s, type %s"                                    , $node, $zone, $type),
1881
                   sprintf("Free pages count per migrate type (Node %s, zone %s, type %s)", $node, $zone, $type),
1882
                   'pages', 'free');
1883
      my $num = 0;
1884
      for my $value (@values)
1885
      {
1886
        $field_name = clean_fieldname(sprintf("fp_n%s_z%s_t%s_o%s", $node, $zone, $type, $num));
1887
        append_field         ($field_name, 'pagetypeinfo', $field_name, sprintf("Free pages at order %2s", $num), sprintf("Free pages count per migrate type at order %2s", $num));
1888
        append_field_to_graph($field_name, $graph_name, 'AREASTACK', '-');
1889
        $result->{$field_name} = $value;
1890
        $num++;
1891
      }
1892
      next;
1893
    }
1894
    if($line =~ m/,\s+zone/)
1895
    {
1896
      ($node, $temp) = split(/,\s+/, $line);
1897
      $node   = (split(/\s+/, $node))[1];
1898
      @values =  split(/\s+/, $temp);
1899
      shift(@values); $zone = shift(@values);
1900
      my $num = 0;
1901
      for my $value (@values)
1902
      {
1903
        $field_name = clean_fieldname(sprintf("blocks_n%s_z%s_t%s", $node, $zone, $types[$num]));
1904
        append_field         ($field_name, 'pagetypeinfo', $field_name, sprintf("Node %2s, zone %9s, type %11s", $node, $zone, $types[$num]), sprintf("Number of blocks type %s", $types[$num]));
1905
        append_field_to_graph($field_name, 'pagetypeinfo', 'AREASTACK', '-');
1906
        $result->{$field_name} = $value;
1907
        $num++;
1908
      }
1909
    }
1910
  }
1911
  close (FH);
1912
  return $result;
1913
}
1914
1915
1916
# ----------------------- all stats ------------------
1917
sub load_stats
1918
{
1919
  $stats->{'meminfo'}      = load_meminfo()      if (-e "/proc/meminfo"       and -r "/proc/meminfo"                      );
1920
  $stats->{'slabinfo'}     = load_slabinfo()     if (-e "/proc/slabinfo"      and -r "/proc/slabinfo"                     );
1921
  $stats->{'vmallocinfo'}  = load_vmallocinfo()  if (-e "/proc/vmallocinfo"   and -r "/proc/vmallocinfo"                  );
1922
  $stats->{'buddyinfo'}    = load_buddyinfo()    if (-e "/proc/buddyinfo"     and -r "/proc/buddyinfo"                    );
1923
  $stats->{'pagetypeinfo'} = load_pagetypeinfo() if (-e "/proc/pagetypeinfo"  and -r "/proc/pagetypeinfo"                 );
1924
  $stats->{'appinfo'}      = load_appinfo()      if (defined($applications->{'show'}) or defined($applications->{'group'}));
1925
  #print Dumper $stats;
1926
}
1927
# appending graphs and fields
1928
sub append_field
1929
{
1930
  my ($field_name, $target, $name, $label, $info) = @_[0..4];
1931
  unless (exists($fields_source->{$field_name}))
1932
  {
1933
    $fields_source->{$field_name} =
1934
    {
1935
      'src'   => { 'target' => $target, 'field' => $name },
1936
      'munin' => { 'label'  => $label,  'info'  => $info }
1937
    };
1938
  }
1939
}
1940
1941
sub append_formula_field
1942
{
1943
  my ($field_name, $formula, $label, $info) = @_[0..4];
1944
  unless (exists($fields_source->{$field_name}))
1945
  {
1946
    $fields_source->{$field_name} =
1947
    {
1948
      'src'   => { 'calculated' => $formula },
1949
      'munin' => { 'label' => $label, 'info'  => $info }
1950
    };
1951
  }
1952
}
1953
1954
sub append_field_to_graph
1955
{
1956
  my ($field_name, $graph_name, $draw, $color) = @_[0..3];
1957
  my $item = sprintf("%s:%s:%s", $field_name, $draw, $color);
1958
  push(@{$graphs_source->{$graph_name}{'fields'}}, $item) unless (grep $_ eq $item, @{$graphs_source->{$graph_name}{'fields'}});
1959
}
1960
1961
sub append_graph
1962
{
1963
  my ($graph_name, $args, $title, $info, $vtitle, $category, $total) = @_[0..6];
1964
  unless (exists($graphs_source->{$graph_name}))
1965
  {
1966
    $graphs_source->{$graph_name} =
1967
    {
1968
      'munin' =>
1969
      {
1970
        'args'     => $args,    'title'    => $title,
1971
        'info'     => $info,    'vtitle'   => $vtitle,
1972
        'category' => $category
1973
      },
1974
      'fields' => []
1975
    };
1976
    if (defined($total)) { $graphs_source->{$graph_name}{'munin'}{'total'} = $total; }
1977
  }
1978
}
1979
1980
sub sort_graphs_fields
1981
{
1982
  for my $graph_name (@graphs_to_sort)
1983
  {
1984
      my @fields = ();
1985
      push  (@fields, @{$graphs_source->{$graph_name}{'fields'}});
1986
      delete($graphs_source->{$graph_name}{'fields'});
1987
      push  (@{$graphs_source->{$graph_name}{'fields'}}, sort(@fields));
1988
  }
1989
}
1990
1991
# --------- convert num[kMG] values to numbers --------------------------------
1992
sub convert_postfixed
1993
{
1994
  my $src = $_[0];
1995
  $src =~ s/,/./ig;
1996
  my $result = $src;
1997
  my ($num, $suff) = $src =~ /([\d\.]+)(\w)/;
1998
  if   ($suff eq 'k') { $result = $num * 1024;               }
1999
  elsif($suff eq 'M') { $result = $num * 1024 * 1024;        }
2000
  elsif($suff eq 'G') { $result = $num * 1024 * 1024 * 1024; }
2001
  return $result;
2002
}
2003
2004
# ---------------- loading limits -------------
2005
sub load_limits
2006
{
2007
  for my $field_name (keys (%{$fields_source}))
2008
  {
2009
    my $limit_name = sprintf("limit_%s", $field_name);
2010
    my $limit = $ENV{$limit_name} || undef;
2011
    if(defined($limit))
2012
    {
2013
      my ($crit, $warn) = (undef, undef);
2014
      if($limit =~ m/:/) { ($warn, $crit) = split(/:/, $limit); }
2015
      else               {  $crit = $limit;                     }
2016
      $fields_source->{$field_name}{'munin'}{'warning'}  = convert_postfixed($warn) if defined($warn);
2017
      $fields_source->{$field_name}{'munin'}{'critical'} = convert_postfixed($crit) if defined($crit);
2018
    }
2019
  }
2020
}
2021
2022
2023
# --------------------------------------- both -----------------------------
2024
# check graph enabled or not
2025
sub graph_is_enabled
2026
{
2027
  return $_[0] =~ m/$enabled_graphs/ig;
2028
}
2029
# extract calculated fields
2030
my $calculated_sources = {};
2031
sub calculated_sources
2032
{
2033
  my $field_name = $_[0];
2034
  unless(exists($calculated_sources->{$field_name}))
2035
  {
2036
    my $field = $fields_source->{$field_name};
2037
    $calculated_sources->{$field_name}{'must_count'} = 0;
2038
    my ($fld, $must) = ('','');
2039
    for my $val (split(/\s.\s/, $field->{'src'}{'calculated'}))
2040
    {
2041
     ($fld, $must) = split(/:/, $val);
2042
      push(@{$calculated_sources->{$field_name}{'fields'}}, $fld);
2043
      $calculated_sources->{$field_name}{$fld} = 0;
2044
      if($must eq 'y')
2045
      {
2046
        $calculated_sources->{$field_name}{$fld} = 1;
2047
        $calculated_sources->{$field_name}{'must_count'} ++;
2048
      }
2049
    }
2050
    #print Dumper $calculated_sources->{$field_name};
2051
  }
2052
  return $calculated_sources->{$field_name};
2053
}
2054
# --- graph fields ----
2055
sub graph_fields
2056
{
2057
  my $graph_name = $_[0];
2058
  my $graph_fields = {};
2059
  my ($field_name, $draw, $colour) = ('','','');
2060
  my $graph = $graphs_source->{$graph_name};
2061
  for my $field (@{$graph->{'fields'}})
2062
  {
2063
    ($field_name, $draw, $colour) = split(/:/, $field);
2064
    push(@{$graph_fields->{'fields'}}, $field_name);
2065
    $graph_fields->{$field_name}{'draw'}   = $draw;
2066
    $graph_fields->{$field_name}{'colour'} = $colour if $colour ne '-';
2067
  }
2068
  return $graph_fields;
2069
}
2070
# ---- exists field or not --------------
2071
my $fields_exists = {};
2072
sub field_exists
2073
{
2074
  my $field_name = $_[0];
2075
  unless(exists($fields_exists->{$field_name}))
2076
  {
2077
    my $field = $fields_source->{$field_name};
2078
    if (exists($field->{'src'}{'target'}))
2079
    {
2080
      $fields_exists->{$field_name} = exists($stats->{$field->{'src'}{'target'}}{$field->{'src'}{'field'}});
2081
    }
2082
    elsif (exists($field->{'src'}{'calculated'}))
2083
    {
2084
      my $sources = calculated_sources($field_name);
2085
      my $must_count = 0;
2086
      for my $fld (@{$sources->{'fields'}})
2087
      {
2088
        $must_count++ if ($sources->{$fld} && field_exists($fld));
2089
      }
2090
      $fields_exists->{$field_name} = ($must_count == $sources->{'must_count'});
2091
    }
2092
  }
2093
  return $fields_exists->{$field_name};
2094
}
2095
# ---------------------------------------- config -------------------------------------
2096
sub print_config
2097
{
2098
  my $gr = {};
2099
  load_limits();
2100
  for my $graph_name (keys(%{$graphs_source}))
2101
  {
2102
    my $graph = $graphs_source->{$graph_name};
2103
    my $order = '';
2104
    my $graph_fields = graph_fields($graph_name);
2105
    for my $field_name (@{$graph_fields->{'fields'}})
2106
    {
2107
      my $field = $fields_source->{$field_name};
2108
      if (field_exists($field_name))
2109
      {
2110
        for my $option (keys(%{$field->{'munin'}}))
2111
        {
2112
          $gr->{$graph_name}{'fields'}{$field_name}{$option} = $field->{'munin'}{$option};
2113
        }
2114
        $gr->{$graph_name}{'fields'}{$field_name}{'draw'} = $graph_fields->{$field_name}{'draw'};
2115
        $gr->{$graph_name}{'fields'}{$field_name}{'colour'} = $graph_fields->{$field_name}{'colour'} if exists($graph_fields->{$field_name}{'colour'});
2116
        $order .= $field_name.' ';
2117
      }
2118
    }
2119
    if(exists($gr->{$graph_name}{'fields'}))
2120
    {
2121
      $gr->{$graph_name}{'munin'} = $graph->{'munin'};
2122
      $gr->{$graph_name}{'munin'}{'order'} = $order;
2123
    }
2124
  }
2125
  #print Dumper $gr;
2126
  for my $graph (sort keys %{$gr})
2127
  {
2128
    next unless (graph_is_enabled($graph));
2129
    printf ("multigraph %s\n", $graph);
2130
    for my $option (sort keys %{$gr->{$graph}{'munin'}})
2131
    {
2132
      printf ("graph_%s %s\n", $option, $gr->{$graph}{'munin'}{$option});
2133
    }
2134
    for my $field (sort keys %{$gr->{$graph}{'fields'}})
2135
    {
2136
      for my $type (sort keys %{$gr->{$graph}{'fields'}{$field}})
2137
      {
2138
        printf ("%s.%s %s\n", $field, $type, $gr->{$graph}{'fields'}{$field}{$type});
2139
      }
2140
    }
2141
    print "\n";
2142
  }
2143
}
2144
2145
# -------------------------------------- values --------------------------------
2146
my $values = {};
2147
sub field_value
2148
{
2149
  my $field_name = $_[0];
2150
  unless(exists($values->{$field_name}))
2151
  {
2152
    my $field = $fields_source->{$field_name};
2153
    if (exists($field->{'src'}{'target'}))
2154
    {
2155
      $values->{$field_name} = $stats->{$field->{'src'}{'target'}}{$field->{'src'}{'field'}};
2156
    }
2157
    elsif (exists($field->{'src'}{'calculated'}))
2158
    {
2159
      my $sources = calculated_sources($field_name);
2160
      my $formula = $field->{'src'}{'calculated'};
2161
      for my $form_field (@{$sources->{'fields'}})
2162
      {
2163
        my $val = field_exists($form_field) ? field_value($form_field) : 0;
2164
        $formula =~ s/$form_field:./$val/;
2165
      }
2166
      #print $formula." !!\n";
2167
      my $result = 0;
2168
      eval '$result = '.$formula.';';
2169
      $values->{$field_name} = $result;
2170
    }
2171
  }
2172
  return $values->{$field_name};
2173
}
2174
2175
sub print_values
2176
{
2177
  my $gr = {};
2178
  for my $graph_name (keys(%{$graphs_source}))
2179
  {
2180
    my $graph = $graphs_source->{$graph_name};
2181
    my $graph_fields = graph_fields($graph_name);
2182
    for my $field_name (@{$graph_fields->{'fields'}})
2183
    {
2184
      my $field = $fields_source->{$field_name};
2185
      $gr->{$graph_name}{$field_name} = field_value($field_name) if field_exists($field_name);
2186
    }
2187
  }
2188
2189
  for my $graph_name (sort (keys %{$gr}))
2190
  {
2191
    next unless (graph_is_enabled($graph_name));
2192
    printf ("multigraph %s\n", $graph_name);
2193
    for my $field_name (sort keys %{$gr->{$graph_name}})
2194
    {
2195
      printf("%s.value %s\n", $field_name, $gr->{$graph_name}{$field_name});
2196
    }
2197
    print "\n";
2198
  }
2199
2200
}