]> git.openstreetmap.org Git - chef.git/blob - cookbooks/munin/files/default/plugins/mysql_
Anchor passenger graphs to zero
[chef.git] / cookbooks / munin / files / default / plugins / mysql_
1 #!/usr/bin/perl
2
3 =encoding utf8
4
5 =head1 NAME
6
7 mysql_ - Munin plugin to display misc MySQL server status
8
9 =head1 APPLICABLE SYSTEMS
10
11 Any MySQL platform, tested by the authors on:
12 * MySQL 5.6.12
13 * MySQL 5.5.32, 5.5.37
14 * MySQL 5.1.29,
15 * MySQL 5.0.51
16 * MariaDB 5.5.39
17 * MariaDB-5.5.39(galera).
18
19 Plugins:
20 * MariaDB-10 Query Response Time: https://mariadb.com/kb/en/mariadb/query_response_time-plugin/
21
22 Information Schema tables:
23 * User statistics - MariaDB-5.2+, OurDelta, Percona Server - https://mariadb.com/kb/en/mariadb/user-statistics
24
25 =head1 CONFIGURATION
26
27 This script is used to generate data for several graphs. To generate
28 data for one specific graph, you need to create a symbolic link with a
29 name like mysql_<GRAPH> to this script.
30
31 If you need to run against multiple MySQL instances on the same host,
32 create your symlinks with names like mysql<N>_<GRAPH> where N is any
33 non-negative integer. You must also set the env.cachenamespace variable
34 to a unique value for each group of symlinks.
35
36 To get a list of symlinks that can be created, run:
37
38   ./mysql_ suggest
39
40 In addition you might need to specify connection parameters in the
41 plugin configuration to override the defaults. These are the defaults:
42
43   [mysql_*]
44     env.mysqlconnection DBI:mysql:information_schema
45     env.mysqluser root
46
47 Non-default example:
48
49   [mysql_*]
50     env.mysqlconnection DBI:mysql:information_schema;host=127.0.0.1;port=3306
51     env.mysqluser munin
52     env.mysqlpassword geheim
53     env.cachenamespace munin_mysql_pri
54   [mysql2_*]
55     env.mysqlconnection DBI:mysql:information_schema;host=127.0.0.1;port=13306
56     env.mysqluser munin;
57     env.mysqlpassword ryuWyawEv
58     env.cachenamespace munin_mysql_alt
59   [mysql10_*]
60     user munin
61     env.mysqluser munin
62     env.mysqlconnection DBI:mysql:information_schema;mysql_read_default_file=/etc/munin/.my-10.cnf
63     env.cachenamespace munin_mysql_10
64     # here the [client] section of /etc/munin/.my-10.cnf is read. socket= can
65     # be specified here.
66
67 Creating a munin user:
68
69   CREATE USER 'munin'@'localhost' IDENTIFIED BY 'ryuWyawEv';
70
71 or with a unix_socket plugin (INSTALL PLUGIN unix_socket SONAME 'auth_socket')
72
73   CREATE USER 'munin'@'localhost' IDENTIFIED WITH unix_socket;
74
75 Note: requires 'user munin' in the configuration.
76
77 The minimum required priviledges of the munin database user is:
78
79   GRANT PROCESS, REPLICATION CLIENT ON *.* TO 'munin'@'localhost';
80
81
82 Warning and critical values can be set via the environment in the usual way.
83 For example:
84
85   [mysql_replication]
86     env.slave_io_running_warning 0.5
87     env.slave_sql_running_warning 0.5
88     env.seconds_behind_master_warning 300
89     env.seconds_behind_master_critical 600
90
91 =head1 DEPENDENCIES
92
93 =over
94
95 =item Cache::Cache
96
97 The plugin uses shared memory to cache the statistics gathered from
98 MySQL. This ensures minimal inpact on the MySQL server.
99
100 =item DBD::mysql
101
102 =back
103
104 =head1 INTERPRETATION
105
106 =head2 InnoDB
107
108 The statistics from innodb are mainly collected from the command
109
110   SHOW ENGINE INNODB STATUS
111
112 A nice walk through is found at
113 L<http://www.mysqlperformanceblog.com/2006/07/17/show-innodb-status-walk-through/>
114
115 Undo logs:
116 L<http://blog.jcole.us/2014/04/16/the-basics-of-the-innodb-undo-logging-and-history-system/>
117
118 =head2 The graphs
119
120 FIX point to relevant sections in the MySQL manual and other www
121 resources for each graph
122
123 =over
124
125 =item mysql_replication
126
127 slave_io_running and slave_sql_running both translate the "Yes" values to 0 and
128 anything else to 1 for their respective fields in the "SHOW SLAVE STATUS" output.
129 This can be used to warn on slave failure if the warning and critical values
130 are set as seen in a previous section.
131
132 =item wsrep_cluster_status
133
134 "Primary" is translated 0 and "non-Primary" to 1.
135
136 =back
137
138 =head1 LICENSE
139
140 Copyright (C) 2008,2009 Kjell-Magne Ã˜ierud, 2014 Open Query
141
142 This program is free software; you can redistribute it and/or modify
143 it under the terms of the GNU General Public License as published by
144 the Free Software Foundation; version 2 dated June, 1991.
145
146 This program is distributed in the hope that it will be useful, but
147 WITHOUT ANY WARRANTY; without even the implied warranty of
148 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
149 General Public License for more details.
150
151 You should have received a copy of the GNU General Public License along
152 with this program; if not, write to the Free Software Foundation, Inc.,
153 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
154
155 =head1 VERSION
156
157 git-master + a few munin modifications
158
159 This plugin was downloaded from L<http://github.com/kjellm/munin-mysql/>
160
161 =head1 MAGICK MARKERS
162
163   #%# family=auto
164   #%# capabilities=suggest autoconf
165
166 =cut
167
168 use warnings;
169 use strict;
170 use utf8;
171
172 use DBI;
173 use File::Basename;
174 use Math::BigInt; # Used to append "=> lib 'GMP'" here, but GMP caused
175                   # segfault on some occasions. Removed as I don't
176                   # think the tiny performance boost is worth the
177                   # debugging effort.
178 use Storable qw(nfreeze thaw);
179
180 use Munin::Plugin;
181
182 my $has_cache;
183
184 BEGIN {
185     eval 'require Cache::SharedMemoryCache';
186     $has_cache = $@ ? 0 : 1;
187 }
188
189
190 #---------------------------------------------------------------------
191 #  C O N F I G
192 #---------------------------------------------------------------------
193
194 my %config = (
195     'dsn'        => $ENV{'mysqlconnection'} || 'DBI:mysql:information_schema',
196     'user'       => $ENV{'mysqluser'}       || 'root',
197     'password'   => $ENV{'mysqlpassword'}   || '',
198     'cache_namespace' => $ENV{'cachenamespace'} || 'munin_mysql',
199 );
200
201
202 #---------------------------------------------------------------------
203 #  C A C H E
204 #---------------------------------------------------------------------
205
206 my %cache_options = (
207     'namespace'          => $config{cache_namespace},
208     'default_expires_in' => 60,
209 );
210
211 my $shared_memory_cache ;
212 if ($has_cache)
213 {
214   $shared_memory_cache = Cache::SharedMemoryCache->new(\%cache_options)
215     or die("Couldn't instantiate SharedMemoryCache");
216 }
217
218 #---------------------------------------------------------------------
219 #  G R A P H   D E F I N I T I O N S
220 #---------------------------------------------------------------------
221
222 # These are defaults to save typing in the graph definitions
223 my %defaults = (
224     global_attrs => {
225         args   => '--base 1000',
226     },
227     data_source_attrs => {
228         min   => '0',
229         type  => 'DERIVE',
230         draw  => 'AREASTACK',
231     },
232 );
233
234 # %graphs contains the graph definitions, it is indexed on the graph
235 # name. The information stored for each graph is used for both showing
236 # data source values and for printing the graph configuration. Each
237 # graph follows the followingformat:
238 #
239 # $graph{NAME} => {
240 #     config => {
241 #         # The global attributes for this graph
242 #         global_attrs => {}
243 #         # Attributes common to all data sources in this graph
244 #         data_source_attrs => {}
245 #     },
246 #     data_sources => [
247 #         # NAME - The name of the data source (e.g. variable names
248 #         #        from SHOW STATUS)
249 #         # DATA_SOURCE_ATTRS - key-value pairs with data source
250 #         #                     attributes
251 #         {name => 'NAME', (DATA_SOURCE_ATTRS)},
252 #         {...},
253 #     ],
254 my %graphs = ();
255
256 #---------------------------------------------------------------------
257
258
259 $graphs{wsrep_cluster_status} = {
260         config => {
261             global_attrs => {
262                 title  => 'Galera Status',
263                 vlabel => 'PRIMARY',
264             },
265             data_source_attrs => {
266                 draw  => 'LINE1',
267                     min   => '0',
268                     max   => '1',
269             },
270         },
271         data_sources => [
272             {name => 'wsrep_cluster_status', label => '0-Primary, 1-Non-Primary',
273                                            info  => 'If the host is primary',
274                                            type  => 'GAUGE',
275                                            critical => '1'},
276         ],
277 };
278
279 $graphs{wsrep_cluster_size} = {
280         config => {
281             global_attrs => {
282                 title  => 'Galera cluster size',
283                 vlabel => 'Hosts',
284             },
285             data_source_attrs => {
286                 draw  => 'LINE1',
287             },
288         },
289         data_sources => [
290             {name => 'wsrep_cluster_size', label => 'Cluster size',
291                                            info  => 'The number of hosts in the cluster.',
292                                            type  => 'GAUGE'},
293         ],
294 };
295
296
297 # http://www.codership.com/wiki/doku.php?id=galera_node_fsm
298 $graphs{wsrep_local_state} = {
299         config => {
300             global_attrs => {
301                 title  => 'Galera node state',
302                 vlabel => 'State (galera_node_fsm)',
303                 args   => '--lower-limit 0 --upper-limit 6',
304             },
305             data_source_attrs => {
306                 draw  => 'LINE1',
307                     min   => '0',
308                     max   => '6',
309             },
310         },
311         data_sources => [
312             {name => 'wsrep_local_state', label => '1-Joining, 2-Donor, 3-Joined, 4-Synced, 5-Donor, 6-Join after Donor ',
313                                           info  => 'The state of the node in the cluster.',
314                                           type  => 'GAUGE',
315                                           warning => '3.5:4.5'},
316         ],
317 };
318
319 #---------------------------------------------------------------------
320
321 $graphs{wsrep_transactions} = {
322         config => {
323             global_attrs => {
324                 title  => 'Galera transactions',
325             },
326             data_source_attrs => {
327                 draw  => 'LINE1',
328             },
329         },
330         data_sources => [
331             {name => 'wsrep_last_committed', label => 'Committed transactions',
332                                              info  => '# of committed transactions.',
333                                              type  => 'COUNTER',
334                                              min   => 0},
335             {name => 'wsrep_local_commits',  label => 'Locally Committed transactions',
336                                              info  => '# of locally committed transactions.',
337                                              type  => 'COUNTER',
338                                              min   => 0},
339         ],
340 };
341
342 #---------------------------------------------------------------------
343
344 $graphs{wsrep_writesets} = {
345         config => {
346             global_attrs => {
347                 title  => 'Galera writesets',
348             },
349             data_source_attrs => {
350                 draw  => 'LINE1',
351             },
352         },
353         data_sources => [
354             {name => 'wsrep_replicated', label => 'Writesets sent',
355                                          info  => '# of writesets sent to other nodes',
356                                          type  => 'COUNTER',
357                                          min   => '0',
358                                          graph => 'no'},
359             {name => 'wsrep_received',   label => 'Writesets received',
360                                          info  => '# of writesets received from other nodes',
361                                          type  => 'COUNTER',
362                                          min   => '0',
363                                          negative => 'wsrep_replicated'},
364         ],
365 };
366
367 #-------------------------
368
369
370 $graphs{wsrep_writesetbytes} = {
371         config => {
372             global_attrs => {
373                 title  => 'Galera writesets bytes/sec',
374             },
375             data_source_attrs => {
376             },
377         },
378         data_sources => [
379             {name => 'wsrep_received_bytes',   label => 'Writesets bytes received',
380                                                info  => '# of bytes in writesets received from other nodes',
381                                                type  => 'DERIVE',
382                                                min   => 0,
383                                                graph => 'no'},
384             {name => 'wsrep_replicated_bytes', label => 'Writesets bytes sent',
385                                                info  => '# of bytes in writesets sent to other nodes',
386                                                type  => 'DERIVE',
387                                                draw  => 'LINE1',
388                                                min   => 0,
389                                                negative => 'wsrep_received_bytes'},
390             {name => 'wsrep_repl_keys_bytes', label => 'Writeset key size sent',
391                                                info  => '# of bytes in writesets of keys sent to other nodes',
392                                                type  => 'DERIVE',
393                                                min   => 0},
394             {name => 'wsrep_repl_data_bytes', label => 'Writeset data size sent',
395                                                info  => '# of bytes in writesets of data sent to other nodes',
396                                                type  => 'DERIVE',
397                                                min   => 0},
398             {name => 'wsrep_repl_other_bytes', label => 'Writeset other size sent',
399                                                info  => '# of bytes in writesets of other data sent to other nodes',
400                                                type  => 'DERIVE',
401                                                min   => 0},
402         ],
403 };
404
405
406 #-------------------------
407
408 $graphs{wsrep_errors} = {
409         config => {
410             global_attrs => {
411                 title  => 'Galera transaction problems'
412             },
413             data_source_attrs => {
414                 draw  => 'LINE1',
415             },
416         },
417         data_sources => [
418             {name => 'wsrep_local_cert_failures', label => 'Certification failures',
419                                                   type  => 'DERIVE',
420                                                   min   => 0},
421             {name => 'wsrep_local_bf_aborts',     label => 'Aborted local transactions',
422                                                   type  => 'DERIVE',
423                                                   min   => 0},
424             {name => 'wsrep_local_replays',       label => 'Replays',
425                                                   type  => 'DERIVE',
426                                                   min   => 0},
427         ],
428 };
429
430 #-------------------------
431
432 $graphs{wsrep_queue} = {
433         config => {
434             global_attrs => {
435                 title  => 'Galera queues',
436                       vlabel => 'queue length received (-) / sent (+) per ${graph_period}',
437             },
438             data_source_attrs => {
439                 draw  => 'LINE1',
440             },
441         },
442         data_sources => [
443             {name => 'wsrep_local_recv_queue',     label => 'Receive queue length',
444                                                    type  => 'GAUGE',
445                                                    graph => 'no'},
446             {name => 'wsrep_local_recv_queue_min', label => 'Receive queue length min',
447                                                    type  => 'GAUGE',
448                                                    graph => 'no'},
449             {name => 'wsrep_local_recv_queue_avg', label => 'Average receive queue length',
450                                                    type  => 'GAUGE',
451                                                    graph => 'no'},
452             {name => 'wsrep_local_recv_queue_max', label => 'Receive queue length max',
453                                                    type  => 'GAUGE',
454                                                    graph => 'no'},
455             {name => 'wsrep_local_send_queue',     label => 'Send queue length',
456                                                    type  => 'GAUGE',
457                                                    negative => 'wsrep_local_recv_queue'},
458             {name => 'wsrep_local_send_queue_min', label => 'Send queue length min',
459                                                    type  => 'GAUGE',
460                                                    negative => 'wsrep_local_recv_queue_min'},
461             {name => 'wsrep_local_send_queue_avg', label => 'Average send queue length',
462                                                    type  => 'GAUGE',
463                                                    negative => 'wsrep_local_recv_queue_avg'},
464             {name => 'wsrep_local_send_queue_max', label => 'Send queue length max',
465                                                    type  => 'GAUGE',
466                                                    negative => 'wsrep_local_recv_queue_max'},
467         ],
468 };
469
470 #-------------------------
471
472 $graphs{wsrep_concurrency} = {
473         config => {
474             global_attrs => {
475                 title  => 'Galera Performance - Apply to Commit',
476                 vlabel => 'commit + / apply - '
477             },
478             data_source_attrs => {
479                 draw  => 'LINE1',
480                 type  => 'GAUGE',
481             },
482         },
483         data_sources => [
484             {name => 'wsrep_apply_window',         label => 'apply window',
485                                                    graph => 'no'},
486             {name => 'wsrep_apply_oooe',           label => 'apply out of order',
487                                                    graph => 'no'},
488             {name => 'wsrep_apply_oool',           label => 'apply out of order (slowness)',
489                                                    graph => 'no'},
490             {name => 'wsrep_commit_window',        label => 'commit window',
491                                                    negative => 'wsrep_apply_window'},
492             {name => 'wsrep_commit_oooe',          label => 'commit out of order',
493                                                    negative => 'wsrep_apply_oooe'},
494             {name => 'wsrep_commit_oool',          label => 'commit out of order (slowness)',
495                                                    negative => 'wsrep_apply_oool'},
496         ],
497 };
498
499 #-------------------------
500
501 $graphs{wsrep_flow} = {
502         config => {
503             global_attrs => {
504                 title  => 'Galera flow control',
505                       vlabel => 'events received (-) / sent (+) per ${graph_period}',
506             },
507             data_source_attrs => {
508                 draw  => 'LINE1',
509             },
510         },
511         data_sources => [
512             {name => 'wsrep_flow_control_recv',      label => 'Pause events received',
513                                                      type  => 'DERIVE',
514                                                      min   => 0,
515                                                      graph => 'no'},
516             {name => 'wsrep_flow_control_sent',      label => 'Pause events sent',
517                                                      type  => 'DERIVE',
518                                                      min   => 0,
519                                                      negative => 'wsrep_flow_control_recv'},
520         ],
521   };
522
523
524
525 #-------------------------
526
527 $graphs{wsrep_flow_paused} = {
528         config => {
529             global_attrs => {
530                 title  => 'Galera flow control paused ratio',
531             },
532             data_source_attrs => {
533                 draw  => 'LINE1',
534             },
535         },
536         data_sources => [
537             {name => 'wsrep_flow_control_paused',    label => 'Ratio flow control was paused',
538                                                      type  => 'GAUGE',
539                                                      min   => '0',
540                                                      max   => '1',
541                                                      warning => 0.1,
542                                                      critical => 0.9},
543         ],
544    };
545
546 #-------------------------
547
548 $graphs{wsrep_flow_paused_ns} = {
549         config => {
550             global_attrs => {
551                 title  => 'Galera flow control paused time',
552                 vlabel => 'nanoseconds',
553             },
554             data_source_attrs => {
555                 draw  => 'LINE1',
556             },
557         },
558         data_sources => [
559             {name => 'wsrep_flow_control_paused_ns',    label => 'Time flow control was paused (ns)',
560                                                      min   => '0',
561                                                      warning => 20000,
562                                                      critical => 1000000},
563         ],
564    };
565
566 #-------------------------
567
568 $graphs{wsrep_distance} = {
569         config => {
570             global_attrs => {
571                 title  => 'Galera distance',
572             },
573             data_source_attrs => {
574                 draw  => 'LINE1',
575             },
576         },
577         data_sources => [
578             {name => 'wsrep_cert_deps_distance',    label => 'cert_deps_distance',
579                                                     type  => 'GAUGE'},
580             {name => 'wsrep_cert_index_size',       label => 'wsrep_cert_index_size',
581                                                     type  => 'GAUGE'},
582             {name => 'wsrep_slave_threads',         label => 'wsrep_slave_threads',
583                                                     type  => 'GAUGE'},
584             {name => 'wsrep_commit_window',         label => 'commit_window',
585                                                     type  => 'GAUGE'},
586         ],
587 };
588
589 #-------------------------
590 $graphs{bin_relay_log} = {
591     config => {
592         global_attrs => {
593             title  => 'Binary/Relay Logs',
594             vlabel => 'Log activity',
595         },
596         data_source_attrs => {
597             draw  => 'LINE1',
598         },
599     },
600     data_sources => [
601         {name => 'Binlog_cache_disk_use', label => 'Binlog Cache Disk Use'},
602         {name => 'Binlog_cache_use',      label => 'Binlog Cache Use'},
603         {name => 'Binlog_stmt_cache_disk_use', label => 'Binlog Statement Cache Disk Use'},
604         {name => 'Binlog_stmt_cache_use',      label => 'Binlog Statement Cache Use'},
605         {name => 'ma_binlog_size',        label => 'Binary Log Space'},
606         {name => 'relay_log_space',       label => 'Relay Log Space'},
607     ],
608 };
609
610 #---------------------------------------------------------------------
611
612 $graphs{commands} = {
613     config => {
614         global_attrs => {
615             title  => 'Command Counters',
616             vlabel => 'Commands per ${graph_period}',
617             total  => 'Questions',
618         },
619         data_source_attrs => {},
620     },
621     data_sources => [
622         {name => 'Com_delete',         label => 'Delete'},
623         {name => 'Com_insert',         label => 'Insert'},
624         {name => 'Com_insert_select',  label => 'Insert select'},
625         {name => 'Com_load',           label => 'Load Data'},
626         {name => 'Com_replace',        label => 'Replace'},
627         {name => 'Com_replace_select', label => 'Replace select'},
628         {name => 'Com_select',         label => 'Select'},
629         {name => 'Com_update',         label => 'Update'},
630         {name => 'Com_update_multi',   label => 'Update multi'},
631     ],
632 };
633
634 #---------------------------------------------------------------------
635
636 $graphs{connections} = {
637     config => {
638         global_attrs => {
639             title  => 'Connections',
640             vlabel => 'Connections per ${graph_period}',
641         },
642         data_source_attrs => {
643             draw  => 'LINE1',
644         },
645     },
646     data_sources => [
647         {name => 'max_connections',      label  => 'Max connections',
648                                          type   => 'GAUGE',
649                                          draw   => 'AREA',
650                                          colour => 'cdcfc4'},
651         {name => 'Max_used_connections', label  => 'Max used',
652                                          type   => 'GAUGE',
653                                          draw   => 'AREA',
654                                          colour => 'ffd660'},
655         {name => 'Aborted_clients',      label => 'Aborted clients'},
656         {name => 'Aborted_connects',     label => 'Aborted connects'},
657         {name => 'Threads_connected',    label => 'Threads connected',
658                                          type  => 'GAUGE'},
659         {name => 'Threads_running',      label => 'Threads running',
660                                          type  => 'GAUGE'},
661         {name => 'Connections',          label => 'New connections'},
662     ],
663 };
664
665 #---------------------------------------------------------------------
666
667 $graphs{files} = {
668     config => {
669         global_attrs => {
670             title  => 'Files',
671         },
672         data_source_attrs => {
673             type  => 'GAUGE',
674             draw  => 'LINE1',
675         },
676     },
677     data_sources => [
678         {name => 'open_files_limit', label  => 'File Limit',
679                                      draw   => 'AREA',
680                                      colour => 'cdcfc4'},
681         {name => 'Open_files',       label => 'Open files',
682                                      type  => 'DERIVE',
683                    min   => 0},
684     ],
685 };
686
687 #---------------------------------------------------------------------
688
689 $graphs{tables} = {
690     config => {
691         global_attrs => {
692             title  => 'Tables',
693         },
694         data_source_attrs => {
695             type  => 'GAUGE',
696             draw  => 'LINE1',
697         },
698     },
699     data_sources => [
700         {name => 'table_open_cache',   label  => 'Table cache',
701                                      draw   => 'AREA',
702                                      colour => 'cdcfc4'},
703         {name => 'innodb_open_files',  label => 'Innodb Table Cache Limit',
704                                      draw   => 'AREA',
705                                      colour => 'ffd660'},
706         {name => 'Open_tables',        label => 'Open tables'},
707         {name => 'Slave_open_temp_tables',  label => 'Open Slave Temp Tables'},
708         {name => 'Opened_tables',      label => 'Opened tables',
709                                      type  => 'DERIVE',
710                    min   => 0},
711         {name => 'Opened_views',       label => 'Opened Views',
712                                      type  => 'DERIVE',
713                    min   => 0},
714     ],
715 };
716
717 #---------------------------------------------------------------------
718
719 $graphs{table_definitions} = {
720     config => {
721         global_attrs => {
722             title  => 'Tables Definitions',
723         },
724         data_source_attrs => {
725             type  => 'GAUGE',
726             draw  => 'LINE1',
727         },
728     },
729     data_sources => [
730         {name => 'table_definition_cache',  label => 'Cache Limit',
731                                      draw   => 'AREA',
732                                      colour => 'cdcfc4'},
733         {name => 'Open_table_definitions',  label => 'Open'},
734         {name => 'Opened_table_definitions',  label => 'Opened',
735                                      type  => 'DERIVE',
736                    min   => 0},
737     ],
738 };
739
740 #---------------------------------------------------------------------
741
742 $graphs{innodb_bpool} = {
743     config => {
744         global_attrs => {
745             title  => 'InnoDB Buffer Pool',
746             vlabel => 'Pages',
747             args   => '--base 1024',
748         },
749         data_source_attrs => {
750             draw => 'LINE2',
751             type => 'GAUGE',
752         },
753     },
754     data_sources => [
755         {name => 'ib_bpool_size',     label  => 'Buffer pool size',
756                                       draw   => 'AREA',
757                                       colour => 'ffd660'},
758         {name => 'ib_bpool_dbpages',  label  => 'Database pages',
759                                       draw   => 'AREA',
760                                       colour => 'cdcfc4'},
761         {name => 'ib_bpool_free',     label => 'Free pages'},
762         {name => 'ib_bpool_modpages', label => 'Modified pages'},
763         {name => 'ib_bpool_oldpages', label => 'Old pages'},
764     ],
765 };
766
767 #---------------------------------------------------------------------
768
769 $graphs{innodb_bpool_act} = {
770     config => {
771         global_attrs => {
772             title  => 'InnoDB Buffer Pool Activity',
773             vlabel => 'Pages per ${graph_period}',
774             total  => 'Total',
775         },
776         data_source_attrs => {
777             draw => 'LINE2',
778         },
779     },
780     data_sources => [
781         {name => 'ib_bpool_read',           label => 'Read'},
782         {name => 'ib_bpool_created',        label => 'Created'},
783         {name => 'ib_bpool_written',        label => 'Written'},
784         {name => 'ib_bpool_made_young',     label => 'Made young'},
785         {name => 'ib_bpool_made_not_young', label => 'Made not young'},
786     ],
787 };
788
789 #---------------------------------------------------------------------
790
791 $graphs{innodb_bpool_internal_breakdown} = {
792     config => {
793         global_attrs => {
794             title  => 'InnoDB Buffer Pool Internal breakdown',
795             args   => '--base 1024 --lower-limit 0',
796             vlabel => 'bytes',
797         },
798         data_source_attrs => {
799             min  => '0',
800             draw => 'AREASTACK',
801             type => 'GAUGE',
802         },
803     },
804     data_sources => [
805         {name => 'ib_bpool_internal_adaptive_hash_size_const',    label => 'Adaptive Hash const'},
806         {name => 'ib_bpool_internal_adaptive_hash_size_var',      label => 'Adaptive Hash var'},
807         {name => 'ib_bpool_internal_page_hash_size_total',        label => 'Page Hash'},
808         {name => 'ib_bpool_internal_dictionary_cache_size_const', label => 'Dictionary const'},
809         {name => 'ib_bpool_internal_dictionary_cache_size_var',   label => 'Dictionary var'},
810         {name => 'ib_bpool_internal_file_system_size_const',      label => 'Filesystem const'},
811         {name => 'ib_bpool_internal_file_system_size_var',        label => 'Filesystem var'},
812         {name => 'ib_bpool_internal_lock_system_size_const',      label => 'Lock system const'},
813         {name => 'ib_bpool_internal_lock_system_size_var',        label => 'Lock system var'},
814         {name => 'ib_bpool_internal_recovery_system_size_const',  label => 'Recovery system const'},
815         {name => 'ib_bpool_internal_recovery_system_size_var',    label => 'Recovery system var'},
816     ],
817 };
818
819 #---------------------------------------------------------------------
820
821 $graphs{innodb_insert_buf} = {
822     config => {
823         global_attrs => {
824             title  => 'InnoDB Insert Buffer',
825             vlabel => 'Activity per ${graph_period}',
826         },
827         data_source_attrs => {
828             draw => 'LINE1',
829         },
830     },
831     data_sources => [
832       {name => 'ib_ibuf_inserts',    label => 'Merge Inserts'},
833       {name => 'ib_ibuf_delete',     label => 'Merge Deletes'},
834       {name => 'ib_ibuf_merged_rec', label => 'Merged Records'},
835       {name => 'ib_ibuf_merges',     label => 'Merges'},
836       {name => 'ib_ibuf_discard_inserts', label => 'Discard Inserts'},
837       {name => 'ib_ibuf_discard_delete',  label => 'Discard Deletes'},
838     ],
839 };
840
841 #---------------------------------------------------------------------
842
843 $graphs{innodb_adaptive_hash} = {
844     config => {
845         global_attrs => {
846             title  => 'InnoDB Adaptive Hash Optimiser',
847         },
848         data_source_attrs => {
849             draw => 'LINE1',
850         },
851     },
852     data_sources => [
853         {name => 'Innodb_adaptive_hash_hash_searches',    label => 'Hash Searches'},
854         {name => 'Innodb_adaptive_hash_non_hash_searches', label => 'Nonhash Searches'},
855     ],
856 };
857
858 #---------------------------------------------------------------------
859
860 $graphs{innodb_io} = {
861     config => {
862         global_attrs => {
863             title  => 'InnoDB IO',
864             vlabel => 'IO operations per ${graph_period}',
865         },
866         data_source_attrs => {
867             draw => 'LINE1',
868         },
869     },
870     data_sources => [
871         {name => 'ib_io_read',  label => 'File reads'},
872         {name => 'ib_io_write', label => 'File writes'},
873         {name => 'ib_io_log',   label => 'Log writes'},
874         {name => 'ib_io_fsync', label => 'File syncs'},
875     ],
876 };
877
878 #---------------------------------------------------------------------
879
880 $graphs{innodb_io_pend} = {
881     config => {
882         global_attrs => {
883             title  => 'InnoDB IO Pending',
884             vlabel => 'Pending operations',
885         },
886         data_source_attrs => {
887             draw => 'LINE1',
888         },
889     },
890     data_sources => [
891         {name => 'ib_iop_log',         label => 'AIO Log'},
892         {name => 'ib_iop_sync',        label => 'AIO Sync'},
893         {name => 'ib_iop_flush_bpool', label => 'Buf Pool Flush'},
894         {name => 'ib_iop_flush_log',   label => 'Log Flushes'},
895         {name => 'ib_iop_ibuf_aio',    label => 'Insert Buf AIO Read'},
896         {name => 'ib_iop_aioread',     label => 'Normal AIO Reads'},
897         {name => 'ib_iop_aiowrite',    label => 'Normal AIO Writes'},
898     ],
899 };
900
901 #---------------------------------------------------------------------
902
903 $graphs{innodb_log} = {
904     config => {
905         global_attrs => {
906             title  => 'InnoDB Log',
907             vlabel => 'Log activity per ${graph_period}',
908         },
909         data_source_attrs => {
910             draw => 'LINE1',
911         },
912     },
913     data_sources => [
914         {name => 'innodb_log_buffer_size', label  => 'Buffer Size',
915                                            type   => 'GAUGE',
916                                            draw   => 'AREA',
917                                            colour => 'fafd9e'},
918         {name => 'ib_log_flush',           label => 'KB Flushed'},
919         {name => 'ib_log_written',         label => 'KB Written'},
920     ],
921 };
922
923 #---------------------------------------------------------------------
924
925 $graphs{innodb_rows} = {
926     config => {
927         global_attrs => {
928             title  => 'InnoDB Row Operations',
929             vlabel => 'Operations per ${graph_period}',
930             total  => 'Total',
931         },
932         data_source_attrs => {},
933     },
934     data_sources => [
935         {name => 'Innodb_rows_deleted',  label => 'Deletes'},
936         {name => 'Innodb_rows_inserted', label => 'Inserts'},
937         {name => 'Innodb_rows_read',     label => 'Reads'},
938         {name => 'Innodb_rows_updated',  label => 'Updates'},
939     ],
940 };
941
942 #---------------------------------------------------------------------
943
944 $graphs{innodb_semaphores} = {
945     config => {
946         global_attrs => {
947             title  => 'InnoDB Semaphores',
948             vlabel => 'Semaphores per ${graph_period}',
949         },
950         data_source_attrs => {
951             draw  => 'AREASTACK',
952         },
953     },
954     data_sources => [
955         {name => 'ib_spin_rounds', label => 'Spin Rounds'},
956         {name => 'ib_spin_waits',  label => 'Spin Waits'},
957         {name => 'ib_os_waits',    label => 'OS Waits'},
958   {name => 'ib_rw_shared_rounds', label => 'RW/S Rounds'},
959   {name => 'ib_rw_shared_waits',  label => 'RW/S Waits'},
960   {name => 'ib_rw_shared_os_waits',    label => 'RW/S OS Waits'},
961   {name => 'ib_rw_excl_rounds', label => 'RW/X Rounds'},
962   {name => 'ib_rw_excl_waits',  label => 'RW/X Waits'},
963   {name => 'ib_rw_excl_os_waits',    label => 'RW/X OS Waits'},
964     ],
965 };
966
967 #---------------------------------------------------------------------
968
969 $graphs{innodb_tnx} = {
970     config => {
971         global_attrs => {
972             title  => 'InnoDB Transactions',
973             vlabel => 'Transactions per ${graph_period}',
974         },
975         data_source_attrs => {
976             draw  => 'LINE1',
977         },
978     },
979     data_sources => [
980         {name => 'ib_tnx', label => 'Transactions created'},
981     ],
982 };
983
984 #---------------------------------------------------------------------
985
986 $graphs{innodb_history_list_length} = {
987     config => {
988         global_attrs => {
989             title  => 'InnoDB History List Length',
990             vlabel => 'Undo log units',
991         },
992         data_source_attrs => {
993             draw  => 'LINE1',
994       type  => 'GAUGE',
995         },
996     },
997     data_sources => [
998         {name => 'ib_tnx_hist', label => 'History List Length'},
999     ],
1000 };
1001
1002 #---------------------------------------------------------------------
1003
1004 $graphs{innodb_srv_master_thread} = {
1005     config => {
1006         global_attrs => {
1007             title  => 'InnoDB Master Thread',
1008         },
1009         data_source_attrs => {
1010             type  => 'DERIVE',
1011             draw  => 'AREASTACK',
1012         },
1013     },
1014     data_sources => [
1015         {name => 'ib_srv_main_flush_loops', label => 'Flush Loop'},
1016         {name => 'ib_srv_main_background_loops', label => 'Background Loop'},
1017         {name => 'ib_srv_main_flushs_writes', label => 'Flushes/Writes', draw  => 'LINE1'},
1018     ],
1019 };
1020
1021 #---------------------------------------------------------------------
1022
1023 $graphs{innodb_queries} = {
1024     config => {
1025        global_attrs => {
1026            title  => 'InnoDB Engine Queries and Transactions',
1027            args   => '--lower-limit 0',
1028        },
1029        data_source_attrs => {
1030            type  => 'GAUGE',
1031        },
1032     },
1033     data_sources => [
1034        {name => 'ib_innodb_queries', label => 'Active'},
1035        {name => 'ib_innodb_transactions_active', label => 'Transactions'},
1036        {name => 'ib_innodb_query_queue_len', label => 'Queued'},
1037     ],
1038 };
1039
1040 #---------------------------------------------------------------------
1041
1042 $graphs{innodb_read_views} = {
1043     config => {
1044        global_attrs => {
1045            title  => 'InnoDB Read Views',
1046            args   => '--lower-limit 0',
1047        },
1048        data_source_attrs => {
1049            type  => 'GAUGE',
1050        },
1051     },
1052     data_sources => [
1053        {name => 'ib_innodb_read_views', label => 'Views'},
1054     ],
1055 };
1056
1057 #---------------------------------------------------------------------
1058
1059 $graphs{innodb_descriptors} = {
1060     config => {
1061        global_attrs => {
1062            title  => 'InnoDB Descriptors',
1063            args   => '--lower-limit 0',
1064        },
1065        data_source_attrs => {
1066            type  => 'GAUGE',
1067        },
1068     },
1069     data_sources => [
1070        {name => 'ib_innodb_descriptors', label => 'Descriptors'},
1071        {name => 'ib_innodb_descriptors_max', label => 'Max', draw => 'AREA', colour => 'ffd660'},
1072     ],
1073 };
1074
1075 #---------------------------------------------------------------------
1076
1077 $graphs{performance} = {
1078     config => {
1079         global_attrs => {
1080             title  => 'Performance Schema Losses',
1081         },
1082         data_source_attrs => {
1083             draw  => 'LINE1',
1084         },
1085     },
1086     data_sources => [
1087   {name => 'Performance_schema_cond_classes_lost',     label => 'Condition classes'},
1088   {name => 'Performance_schema_cond_instances_lost',   label => 'Condition instances'},
1089   {name => 'Performance_schema_file_classes_lost',     label => 'File classes'},
1090   {name => 'Performance_schema_file_handles_lost',     label => 'File handles'},
1091   {name => 'Performance_schema_file_instances_lost',   label => 'File instances'},
1092   {name => 'Performance_schema_locker_lost',           label => 'Locker'},
1093   {name => 'Performance_schema_mutex_classes_lost',    label => 'Mutex classes'},
1094   {name => 'Performance_schema_mutex_instances_lost',  label => 'Mutex instances'},
1095   {name => 'Performance_schema_rwlock_classes_lost',   label => 'Read/Write lock classes'},
1096   {name => 'Performance_schema_rwlock_instances_lost', label => 'Read/Write lock instances'},
1097   {name => 'Performance_schema_table_handles_lost',    label => 'Table handles'},
1098   {name => 'Performance_schema_table_instances_lost',  label => 'Table instances'},
1099   {name => 'Performance_schema_thread_classes_lost',   label => 'Thread classes'},
1100   {name => 'Performance_schema_thread_instances_lost', label => 'Thread instances'},
1101     ],
1102 };
1103
1104 #---------------------------------------------------------------------
1105
1106 $graphs{myisam_indexes} = {
1107     config => {
1108         global_attrs => {
1109             title  => 'MyISAM Indexes',
1110             vlabel => 'Requests per ${graph_period}',
1111         },
1112         data_source_attrs => {
1113             draw  => 'LINE2',
1114         },
1115     },
1116     data_sources => [
1117         {name => 'Key_read_requests',  label => 'Key read requests'},
1118         {name => 'Key_reads',          label => 'Key reads'},
1119         {name => 'Key_write_requests', label => 'Key write requests'},
1120         {name => 'Key_writes',         label => 'Key writes'},
1121    ],
1122 };
1123
1124 #---------------------------------------------------------------------
1125
1126 $graphs{network_traffic} = {
1127     config => {
1128         global_attrs => {
1129             title  => 'Network Traffic',
1130             args   => '--base 1024',
1131             vlabel => 'Bytes received (-) / sent (+) per ${graph_period}',
1132         },
1133         data_source_attrs => {
1134             draw  => 'LINE2',
1135         },
1136     },
1137     data_sources => [
1138         {name => 'Bytes_received', label => 'Bytes transferred',
1139                                    graph => 'no'},
1140         {name => 'Bytes_sent',     label    => 'Bytes transferred',
1141                                    negative => 'Bytes_received'},
1142     ],
1143 };
1144
1145 #---------------------------------------------------------------------
1146
1147 $graphs{qcache} = {
1148     config => {
1149         global_attrs => {
1150             title  => 'Query Cache',
1151             vlabel => 'Commands per ${graph_period}',
1152         },
1153         data_source_attrs => {
1154              draw => 'LINE1',
1155         },
1156     },
1157     data_sources => [
1158         {name => 'Qcache_queries_in_cache', label => 'Queries in cache', type  => 'GAUGE'},
1159         {name => 'Qcache_hits',             label => 'Cache hits'},
1160         {name => 'Subquery_cache_hit',      label => 'Subquery Cache hits'},
1161         {name => 'Subquery_cache_miss',     label => 'Subquery Cache misses'},
1162         {name => 'Qcache_inserts',          label => 'Inserts'},
1163         {name => 'Qcache_not_cached',       label => 'Not cached'},
1164         {name => 'Qcache_lowmem_prunes',    label => 'Low-memory prunes'},
1165     ],
1166 };
1167
1168 #---------------------------------------------------------------------
1169
1170 $graphs{qcache_mem} = {
1171     config => {
1172         global_attrs => {
1173             title  => 'Query Cache Memory',
1174             vlabel => 'Bytes',
1175             args   => '--base 1024 --lower-limit 0',
1176         },
1177         data_source_attrs => {
1178             draw => 'AREA',
1179             type => 'GAUGE',
1180         },
1181     },
1182     data_sources => [
1183         {name => 'query_cache_size',    label => 'Cache size'},
1184         {name => 'Qcache_free_memory',  label => 'Free mem'},
1185     ],
1186 };
1187
1188
1189 #---------------------------------------------------------------------
1190
1191 $graphs{max_mem} = {
1192     config => {
1193         global_attrs => {
1194             title  => 'Maximum memory that Mysql could use',
1195             vlabel => 'Bytes',
1196             args   => '--base 1024 --lower-limit 0',
1197         },
1198         data_source_attrs => {
1199             draw => 'AREASTACK',
1200             type => 'GAUGE',
1201         },
1202     },
1203     data_sources => [
1204         {name => 'mysql_connection_memory',    label => 'Connection Memory'},
1205         {name => 'mysql_base_memory',  label => 'Base MYSQL Memory'},
1206     ],
1207 };
1208
1209
1210 #---------------------------------------------------------------------
1211
1212 $graphs{replication} = {
1213     config => {
1214         global_attrs => {
1215             title  => 'Replication',
1216             vlabel => 'Activity',
1217         },
1218         data_source_attrs => {
1219             draw => 'LINE1',
1220         },
1221     },
1222     data_sources => [
1223         {name => 'slave_io_running',           label => 'Slave IO Running',
1224                                                type  => 'GAUGE',
1225                                                draw  => 'AREA'},
1226         {name => 'slave_sql_running',          label => 'Slave SQL Running',
1227                                                type  => 'GAUGE',
1228                                                draw  => 'AREA'},
1229         {name => 'Slave_retried_transactions', label => 'Retried Transactions'},
1230         {name => 'Slave_open_temp_tables',     label => 'Open Temp Tables'},
1231         {name => 'seconds_behind_master',      label => 'Secs Behind Master',
1232                                                type  => 'GAUGE'},
1233     ],
1234 };
1235
1236 #---------------------------------------------------------------------
1237
1238 $graphs{select_types} = {
1239     config => {
1240         global_attrs => {
1241             title  => 'Select types',
1242             vlabel => 'Commands per ${graph_period}',
1243             total  => 'Total',
1244         },
1245         data_source_attrs => {},
1246     },
1247     data_sources => [
1248         {name => 'Select_full_join',       label => 'Full join'},
1249         {name => 'Select_full_range_join', label => 'Full range'},
1250         {name => 'Select_range',           label => 'Range'},
1251         {name => 'Select_range_check',     label => 'Range check'},
1252         {name => 'Select_scan',            label => 'Scan'},
1253     ],
1254 };
1255
1256 #---------------------------------------------------------------------
1257
1258 $graphs{slow} = {
1259     config => {
1260         global_attrs => {
1261             title  => 'Slow Queries',
1262             vlabel => 'Slow queries per ${graph_period}',
1263         },
1264         data_source_attrs => {
1265             draw  => 'LINE2',
1266         },
1267     },
1268     data_sources => [
1269         {name => 'Slow_queries', label => 'Slow queries'},
1270     ],
1271 };
1272
1273 #---------------------------------------------------------------------
1274
1275 $graphs{sorts} = {
1276     config => {
1277         global_attrs => {
1278             title  => 'Sorts',
1279             vlabel => 'Sorts / ${graph_period}',
1280         },
1281         data_source_attrs => {
1282             draw  => 'LINE2',
1283         },
1284     },
1285     data_sources => [
1286         {name => 'Sort_rows',         label => 'Rows sorted'},
1287         {name => 'Sort_range',        label => 'Range'},
1288         {name => 'Sort_merge_passes', label => 'Merge passes'},
1289         {name => 'Sort_scan',         label => 'Scan'},
1290     ],
1291 };
1292
1293 #---------------------------------------------------------------------
1294
1295 $graphs{table_locks} = {
1296     config => {
1297         global_attrs => {
1298             title  => 'Table locks',
1299             vlabel => 'locks per ${graph_period}',
1300         },
1301         data_source_attrs => {
1302             draw  => 'LINE2',
1303         },
1304     },
1305     data_sources => [
1306         {name => 'Table_locks_immediate', label => 'Table locks immed'},
1307         {name => 'Table_locks_waited',    label => 'Table locks waited'},
1308     ],
1309 };
1310
1311 #---------------------------------------------------------------------
1312
1313 $graphs{tmp_tables} = {
1314     config => {
1315         global_attrs => {
1316             title  => 'Temporary objects',
1317             vlabel => 'Objects per ${graph_period}',
1318         },
1319         data_source_attrs => {
1320             draw  => 'LINE2',
1321         },
1322     },
1323     data_sources => [
1324         {name => 'Created_tmp_disk_tables', label => 'Temp disk tables'},
1325         {name => 'Created_tmp_tables',      label => 'Temp tables'},
1326         {name => 'Created_tmp_files',       label => 'Temp files'},
1327     ],
1328 };
1329
1330
1331 #---------------------------------------------------------------------
1332
1333 $graphs{rows} = {
1334     config => {
1335   global_attrs => {
1336       title  => 'Rows',
1337   },
1338   data_source_attrs => {
1339       draw  => 'LINE2',
1340       type => 'DERIVE',
1341   },
1342     },
1343     data_sources => [
1344   {name => 'Rows_read',     label => 'Read'},
1345   {name => 'Rows_sent',     label => 'Sent'},
1346   {name => 'Rows_tmp_read', label => 'Temp Read'},
1347     ],
1348   };
1349
1350 #---------------------------------------------------------------------
1351
1352 $graphs{handler_read} = {
1353     config => {
1354   global_attrs => {
1355       title  => 'Read Handler',
1356   },
1357   data_source_attrs => {
1358       draw  => 'AREASTACK',
1359       type => 'DERIVE',
1360   },
1361     },
1362     data_sources => [
1363   {name => 'Handler_read_first',       label => 'Key First'},
1364   {name => 'Handler_read_key',         label => 'Key Read'},
1365   {name => 'Handler_read_last',        label => 'Key Last'},
1366   {name => 'Handler_read_prev',        label => 'Key Prev'},
1367   {name => 'Handler_read_rnd',         label => 'Row position'},
1368   {name => 'Handler_read_rnd_deleted', label => 'Row position delete'},
1369   {name => 'Handler_read_rnd_next',    label => 'Row position next'},
1370     ],
1371   };
1372
1373 #---------------------------------------------------------------------
1374
1375 $graphs{handler_transaction} = {
1376     config => {
1377   global_attrs => {
1378       title  => 'Transactions Handler',
1379   },
1380   data_source_attrs => {
1381       draw  => 'AREASTACK',
1382       type => 'DERIVE',
1383   },
1384     },
1385     data_sources => [
1386   {name => 'Handler_commit',             label => 'Commit'},
1387   {name => 'Handler_rollback',           label => 'Rollback'},
1388   {name => 'Handler_savepoint',          label => 'Savepoint'},
1389   {name => 'Handler_savepoint_rollback', label => 'Savepoint Rollback'},
1390     ],
1391   };
1392
1393 #---------------------------------------------------------------------
1394
1395 $graphs{handler_write} = {
1396     config => {
1397   global_attrs => {
1398       title  => 'Write/Update Handler',
1399   },
1400   data_source_attrs => {
1401       draw => 'LINE2',
1402       type => 'DERIVE',
1403   },
1404     },
1405     data_sources => [
1406   {name => 'Handler_write',  label => 'Writes'},
1407   {name => 'Handler_update', label => 'Updates'},
1408     ],
1409   };
1410
1411 #---------------------------------------------------------------------
1412
1413 $graphs{handler_tmp} = {
1414     config => {
1415   global_attrs => {
1416       title  => 'Temporary Write/Update Handler',
1417   },
1418   data_source_attrs => {
1419       draw => 'LINE2',
1420       type => 'DERIVE',
1421   },
1422     },
1423     data_sources => [
1424   {name => 'Handler_tmp_write',  label => 'Writes'},
1425   {name => 'Handler_tmp_update', label => 'Updates'},
1426     ],
1427   };
1428
1429 #---------------------------------------------------------------------
1430
1431 $graphs{execution} = {
1432     config => {
1433   global_attrs => {
1434       title  => 'Execution Events',
1435   },
1436   data_source_attrs => {
1437       draw => 'LINE2',
1438       type => 'DERIVE',
1439   },
1440     },
1441     data_sources => [
1442   {name => 'Executed_events',   label => 'Events'},
1443   {name => 'Executed_triggers', label => 'Triggers'},
1444     ],
1445   };
1446
1447 #---------------------------------------------------------------------
1448
1449 $graphs{icp} = {
1450     config => {
1451         global_attrs => {
1452             title  => 'Index Condition Pushdown',
1453         },
1454         data_source_attrs => {
1455             draw  => 'LINE2',
1456             type => 'DERIVE',
1457         },
1458     },
1459     data_sources => [
1460         {name => 'Handler_icp_attempts', label => 'Attempts'},
1461         {name => 'Handler_icp_match',    label => 'Matches'},
1462     ],
1463 };
1464
1465 #---------------------------------------------------------------------
1466
1467 $graphs{mrr} = {
1468     config => {
1469         global_attrs => {
1470             title  => 'Multi Range Read optimizations',
1471         },
1472         data_source_attrs => {
1473             draw  => 'LINE2',
1474             type => 'DERIVE',
1475         },
1476     },
1477     data_sources => [
1478         {name => 'Handler_mrr_init', label => 'Uses'},
1479         {name => 'Handler_mrr_key_refills',  label => 'Key refills'},
1480         {name => 'Handler_mrr_rowid_refills',  label => 'Row refills'},
1481     ],
1482 };
1483
1484 #---------------------------------------------------------------------
1485 #  Plugin Graphs
1486 #  These are mysql plugins of type INFORMATION SCHEMA
1487 #
1488 #  These will be added to $graphs if available
1489 #---------------------------------------------------------------------
1490
1491 my %graph_plugins = ();
1492
1493 $graph_plugins{query_response_time} = {
1494     count => {
1495         config => {
1496             global_attrs => {
1497                 title  => 'Query Response Time Count',
1498                 vlabel  => 'queries per ${graph_period}',
1499             },
1500             data_source_attrs => {
1501                 draw  => 'LINE2',
1502                 type => 'DERIVE',
1503             },
1504         },
1505         # data_sources are populated by sub plugin_query_response_time
1506         data_sources => [
1507         ],
1508    },
1509    total => {
1510         config => {
1511             global_attrs => {
1512                 title  => 'Query Response Time Total',
1513                 vlabel  => 'query time (microseconds) per ${graph_period}',
1514             },
1515             data_source_attrs => {
1516                 draw  => 'LINE2',
1517                 type => 'DERIVE',
1518             },
1519         },
1520         # data_sources are populated by sub plugin_query_response_time
1521         data_sources => [
1522         ],
1523    }
1524 };
1525
1526 $graph_plugins{user_statistics} = {
1527     connections => {
1528         config => {
1529             global_attrs => {
1530                 title  => 'User Connections',
1531                 vlabel  => 'connections per ${graph_period}',
1532             },
1533             data_source_attrs => {
1534                 draw  => 'LINE2',
1535                 type => 'DERIVE',
1536             },
1537         },
1538         cols => { 'total_connections' => {}, 'concurrent_connections' => {}, 'denied_connections' => {}, 'lost_connections'  => {}},
1539         data_sources => [
1540         ],
1541    },
1542    usertime => {
1543         config => {
1544             global_attrs => {
1545                 title  => 'User Time',
1546                 vlabel  => 'seconds',
1547             },
1548             data_source_attrs => {
1549                 draw  => 'LINE2',
1550                 type => 'DERIVE',
1551             },
1552         },
1553         cols => { 'connected_time' => {}, 'busy_time' => {}, 'cpu_time' => {} },
1554         data_sources => [
1555         ],
1556    },
1557    bytes => {
1558         config => {
1559             global_attrs => {
1560                 title  => 'User Bytes',
1561                 vlabel  => 'bytes',
1562             },
1563             data_source_attrs => {
1564                 draw  => 'LINE2',
1565                 type => 'DERIVE',
1566             },
1567         },
1568         cols => { 'bytes_received' => {}, 'bytes_sent' => {}, 'binlog_bytes_written' => {} },
1569         data_sources => [
1570         ],
1571    },
1572    rows => {
1573         config => {
1574             global_attrs => {
1575                 title  => 'User Rows',
1576                 vlabel  => 'rows',
1577             },
1578             data_source_attrs => {
1579                 draw  => 'LINE2',
1580                 type => 'DERIVE',
1581             },
1582         },
1583         cols => { 'rows_read' => {}, 'rows_sent' => {}, 'rows_deleted' => {}, 'rows_inserted' => {}, 'rows_updated' => {} },
1584         data_sources => [
1585         ],
1586    },
1587    commands => {
1588         config => {
1589             global_attrs => {
1590                 title  => 'Command breakdown by user',
1591                 vlabel  => 'commands',
1592             },
1593             data_source_attrs => {
1594                 draw  => 'LINE2',
1595                 type => 'DERIVE',
1596             },
1597         },
1598         cols => { 'select_commands' => {}, 'update_commands' => {}, 'other_commands' => {}, 'commit_transactions' => {}, 'rollback_transactions' => {} },
1599         data_sources => [
1600         ],
1601    }
1602
1603 };
1604 #---------------------------------------------------------------------
1605 #  M A I N
1606 #---------------------------------------------------------------------
1607
1608
1609 #
1610 # Global hash holding the data collected from mysql.
1611 #
1612 our $data; # Was 'my'. Changed to 'our' to facilitate testing.
1613
1614
1615 sub main {
1616     my $graph = basename($0);
1617     $graph =~ s/^mysql[0-9]*_//g; # allow multiple instances
1618     my $command = $ARGV[0] || 'show';
1619
1620     my %command_map = (
1621         'autoconf' => \&autoconf,
1622         'config'   => \&config,
1623         'show'     => \&show,
1624         'suggest'  => \&suggest,
1625     );
1626
1627     die "Unknown command: $command"
1628         unless exists $command_map{$command};
1629
1630     die "Missing dependency Cache::Cache"
1631         unless $has_cache || $command eq 'autoconf';
1632
1633     return $command_map{$command}->($graph);
1634 }
1635
1636
1637 #---------------------------------------------------------------------
1638 #  C O M M A N D   H A N D L E R S
1639 #---------------------------------------------------------------------
1640
1641 # Each command handler should return an appropriate exit code
1642
1643
1644 # http://munin-monitoring.org/wiki/ConcisePlugins#autoconf
1645 sub autoconf {
1646     unless ($has_cache) {
1647         print "no (Missing dependency Cache::Cache)\n";
1648         return 0;
1649     }
1650
1651     eval {
1652         db_connect();
1653     };
1654     if ($@) {
1655         my $err = $@;
1656         $err =~ s{\s at \s \S+ \s line .*}{}xms;
1657         print "no ($err)\n";
1658         return 0;
1659     }
1660     print "yes\n";
1661     return 0;
1662 }
1663
1664
1665 # http://munin-monitoring.org/wiki/ConcisePlugins#suggest
1666 sub suggest {
1667
1668     # What is the best way to decide which graphs is applicable to a
1669     # given system?
1670     # Answer:
1671     # Use lack of variables to indicate that the capability doesn't exist
1672     # Use variable values to indicate some graph isn't currently used.
1673     # update_data() now does this.
1674
1675     update_data();
1676
1677
1678     foreach my $graph (sort keys(%graphs)) {
1679         next if $graph =~ /innodb_/ && $data->{_innodb_disabled};
1680         next if $graph =~ /wsrep_/ && $data->{_galera_disabled};
1681         print "$graph\n";
1682     }
1683
1684     return 0;
1685 }
1686
1687
1688 sub config {
1689     my $graph_name = shift;
1690
1691     # In MySQL 5.1 (and probably erlier versions as well) status
1692     # variables are unique when looking at the last 19 characters.
1693     #
1694     #   SELECT RIGHT(variable_name, 19), COUNT(*)
1695     #     FROM information_schema.global_status
1696     #    GROUP BY RIGHT(variable_name, 19)
1697     #   HAVING COUNT(*) > 1;
1698     #
1699     #   Empty set (0.06 sec)
1700     #
1701     # There is one duplicate when looking at server variables
1702     #
1703     #   SELECT RIGHT(variable_name, 19), COUNT(*)
1704     #     FROM information_schema.global_variables
1705     #    GROUP BY RIGHT(variable_name, 19)
1706     #   HAVING COUNT(*) > 1;
1707     #
1708     #   +--------------------------+----------+
1709     #   | RIGHT(variable_name, 19) | COUNT(*) |
1710     #   +--------------------------+----------+
1711     #   | OW_PRIORITY_UPDATES      |        2 |
1712     #   +--------------------------+----------+
1713     #   1 row in set (0.05 sec)
1714     #
1715     #   show global variables like '%OW_PRIORITY_UPDATES';
1716     #
1717     #   +--------------------------+-------+
1718     #   | Variable_name            | Value |
1719     #   +--------------------------+-------+
1720     #   | low_priority_updates     | OFF   |
1721     #   | sql_low_priority_updates | OFF   |
1722     #   +--------------------------+-------+
1723     #   2 rows in set (0.00 sec)
1724     #
1725     # Not a problem since we don't graph these
1726
1727     update_data();
1728
1729     die 'Unknown graph ' . ($graph_name ? $graph_name : '')
1730         unless $graphs{$graph_name};
1731
1732     my $graph = $graphs{$graph_name};
1733
1734     my %conf = (%{$defaults{global_attrs}}, %{$graph->{config}{global_attrs}});
1735     while (my ($k, $v) = each %conf) {
1736         print "graph_$k $v\n";
1737     }
1738     if ($graph_name =~ /wsrep_/) {
1739       print "graph_category galera\n";
1740     } else {
1741       print "graph_category mysql\n";
1742     }
1743
1744     my $i = 0;
1745     for my $ds (@{$graph->{data_sources}}) {
1746         my %ds_spec = (
1747             %{$defaults{data_source_attrs}},
1748             %{$graph->{config}{data_source_attrs}},
1749             %$ds,
1750         );
1751         while (my ($k, $v) = each %ds_spec) {
1752             # 'name' is only used internally in this script, not
1753             # understood by munin.
1754             next if ($k eq 'name');
1755
1756             # AREASTACK is part of munin as of version 1.3.3 (not
1757             # released yet). Until then ...
1758             if ($k eq 'draw' && $v eq 'AREASTACK') {
1759                 printf("%s.%s %s\n",
1760                        clean_fieldname($ds->{name}), $k, ($i ? 'STACK' : 'AREA'));
1761             }
1762             else {
1763                 printf("%s.%s %s\n", clean_fieldname($ds->{name}), $k, $v);
1764             }
1765             $i++;
1766         }
1767         print_thresholds(clean_fieldname($ds->{name}));
1768     }
1769
1770     return 0;
1771 }
1772
1773 sub show {
1774     my $graph_name = shift;
1775
1776     update_data();
1777
1778     die 'Unknown graph ' . ($graph_name ? $graph_name : '')
1779         unless $graphs{$graph_name};
1780
1781     my $graph = $graphs{$graph_name};
1782
1783     die "Can't show data for '$graph_name' because InnoDB is disabled."
1784         if $graph_name =~ /innodb_/ && $data->{_innodb_disabled};
1785
1786     die "Can't show data for '$graph_name' because not a Galera mysql version."
1787         if $graph_name =~ /wsrep_/ && $data->{_galera_disabled};
1788
1789     for my $ds (@{$graph->{data_sources}}) {
1790         printf "%s.value %s\n",
1791             clean_fieldname($ds->{name}),
1792             defined $data->{$ds->{name}} ? $data->{$ds->{name}} : 'U';
1793     }
1794
1795     return 0;
1796 }
1797
1798
1799
1800 #---------------------------------------------------------------------
1801 #  U T I L I T Y   S U B S
1802 #---------------------------------------------------------------------
1803
1804
1805 sub db_connect {
1806     my $dsn = "$config{dsn};mysql_connect_timeout=5";
1807
1808     return DBI->connect($dsn, $config{user}, $config{password}, {
1809         RaiseError       => 1,
1810         PrintError       => 0,
1811         FetchHashKeyName => 'NAME_lc',
1812     });
1813 }
1814
1815
1816 sub update_data {
1817     $data = $shared_memory_cache->get('data');
1818     my $graphs_stored = $shared_memory_cache->get('graphs');
1819     %graphs = %{thaw($graphs_stored)} if $graphs_stored;
1820     return if $data;
1821
1822     $data = {};
1823
1824     my $dbh = db_connect();
1825
1826     update_variables($dbh);
1827     update_plugins($dbh);
1828     update_innodb($dbh);
1829     update_master($dbh);
1830     delete $graphs{replication} if update_slave($dbh)==1;
1831
1832     delete $graphs{bin_relay_log} if not defined $data->{relay_log_space}
1833         && not defined $data->{ma_binlog_size};
1834
1835     delete $graphs{execution} if not defined $data->{Executed_events}
1836         && not defined $data->{Executed_triggers};
1837
1838     delete $graphs{icp} if not defined $data->{Handler_icp_attempts}
1839         && not defined $data->{Handler_icp_matches};
1840
1841     delete $graphs{innodb_adaptive_hash}
1842         if not defined $data->{Innodb_adaptive_hash_hash_searches}
1843         && not defined $data->{Innodb_adaptive_hash_non_hash_searches};
1844
1845     delete $graphs{innodb_bpool_internal_breakdown}
1846         if not defined $data->{ib_bpool_internal_adaptive_hash_size_const};
1847
1848     delete $graphs{innodb_descriptors}
1849         if not defined $data->{ib_innodb_descriptors};
1850
1851     delete $graphs{mrr} if not defined $data->{Handler_mrr_init};
1852
1853     delete $graphs{rows} if not defined $data->{Rows_sent};
1854
1855     delete $graphs{handler_temp} if not defined $data->{Handler_tmp_write};
1856
1857     $shared_memory_cache->set('data', $data);
1858     $shared_memory_cache->set('graphs', nfreeze(\%graphs));
1859 }
1860
1861
1862 sub update_plugins {
1863     my ($dbh) = @_;
1864
1865     my %plugin_map = (
1866         'query_response_time'         => \&plugin_query_response_time,
1867     );
1868
1869     sub add_graphs {
1870       my ($f, $sec, $dbh, %g) = @_;
1871       if ($f->($dbh) == 0) {
1872         while (my ($k, $v) = each %g) {
1873           $graphs{$sec . '_' . $k} = $v;
1874         }
1875       }
1876     }
1877
1878     my $sth = $dbh->prepare("SHOW PLUGINS");
1879     $sth->execute();
1880     while (my $row = $sth->fetchrow_hashref()) {
1881         next if $row->{'type'} ne 'INFORMATION SCHEMA';
1882         my $sec = lc $row->{'name'};
1883         next if not exists $plugin_map{$sec};
1884         add_graphs($plugin_map{$sec}, $sec, $dbh, %{$graph_plugins{$sec}});
1885     }
1886     $sth->finish();
1887
1888     my %is_map = (
1889         'user_statistics'         => \&is_user_statistics,
1890     );
1891
1892     $sth = $dbh->prepare("SHOW TABLES IN INFORMATION_SCHEMA");
1893     $sth->execute();
1894     while (my $row = $sth->fetchrow_hashref()) {
1895         my $sec = lc $row->{'tables_in_information_schema'};
1896         next if not exists $is_map{$sec};
1897         add_graphs($is_map{$sec}, $sec, $dbh, %{$graph_plugins{$sec}});
1898     }
1899     $sth->finish();
1900 }
1901
1902 sub update_variables {
1903     my ($dbh) = @_;
1904     my @queries = (
1905         'SHOW GLOBAL STATUS',
1906         'SHOW GLOBAL VARIABLES',
1907     );
1908
1909     my %variable_name_map = (
1910         table_cache => 'table_open_cache', # table_open_cache was
1911                                            # previously known as
1912                                            # table_cache in MySQL
1913                                            # 5.1.2 and earlier.
1914     );
1915     my %wsrep_cluster_status_map = (
1916         'Primary' =>  0,
1917         'non-Primary' => 1,
1918     );
1919
1920     for my $query (@queries) {
1921         $data->{$query} = {};
1922
1923         my $sth = $dbh->prepare($query);
1924         $sth->execute();
1925         while (my $row = $sth->fetch) {
1926             my $var = $variable_name_map{$row->[0]} || $row->[0];
1927             $data->{$var} = $row->[1];
1928         }
1929         $sth->finish();
1930     }
1931
1932     $data->{'mysql_base_memory'} = $data->{'key_buffer_size'} 
1933                                          + $data->{'query_cache_size'}
1934                                          + $data->{'innodb_buffer_pool_size'}
1935                                          + ( $data->{'innodb_additional_mem_pool_size'} || 0 )
1936                                          + $data->{'innodb_log_buffer_size'}
1937                                          + ( $data->{'tokudb_cache_size'} || 0 );
1938
1939     my $tmp_table_size = $data->{'tmp_table_size'};
1940     my $max_heap_table_size = $data->{'max_heap_table_size'};
1941     $data->{'mysql_connection_memory'} = $data->{'read_buffer_size'}
1942                                    + $data->{'read_rnd_buffer_size'}
1943                                    + $data->{'sort_buffer_size'}
1944                                    + $data->{'join_buffer_size'}
1945                                    + $data->{'binlog_cache_size'}
1946                                    + $data->{'thread_stack'}
1947                                    + ( $tmp_table_size >= $max_heap_table_size ? $tmp_table_size : $max_heap_table_size )
1948                                    + ( $data->{'tokudb_read_buf_size'} || 0 );
1949
1950    # wsrep_thread_count was separated from max_connections for mariadb-5.5.38 https://mariadb.atlassian.net/browse/MDEV-6206
1951    $data->{'mysql_connection_memory'} *= $data->{'max_connections'} + ( $data->{'wsrep_thread_count'} || 0 );
1952
1953     if ($data->{wsrep_cluster_status}) {
1954       my $var = $wsrep_cluster_status_map{$data->{wsrep_cluster_status}};
1955       $data->{wsrep_cluster_status} = $var;
1956     }
1957     $data->{_galera_disabled} = 1 unless ($data->{wsrep_provider_name});
1958 }
1959
1960
1961 sub update_innodb {
1962     my ($dbh) = @_;
1963
1964     my $sth = $dbh->prepare('SHOW /*!50000 ENGINE*/ INNODB STATUS');
1965     eval {
1966         $sth->execute();
1967     };
1968     if ($@) {
1969         if ($@ =~ /Unknown (storage|table) engine 'INNODB'|Cannot call SHOW INNODB STATUS because skip-innodb is defined/i) {
1970             $data->{_innodb_disabled} = 1;
1971             return;
1972         }
1973         die $@;
1974     }
1975     my $row = $sth->fetchrow_hashref();
1976     my $status = $row->{'status'};
1977     $sth->finish();
1978
1979     parse_innodb_status($status);
1980 }
1981
1982
1983 sub update_master {
1984     my ($dbh) = @_;
1985
1986     my $sth = $dbh->prepare('SHOW MASTER LOGS');
1987     eval {
1988         $sth->execute();
1989     };
1990     if ($@) {
1991         # SHOW MASTER LOGS failed because binlog is not enabled
1992         return if $@ =~ /You are not using binary logging/;
1993         die $@;
1994     }
1995
1996     while (my $row = $sth->fetch) {
1997         $data->{ma_binlog_size} += $row->[1];
1998     }
1999
2000     $sth->finish();
2001 }
2002
2003
2004 sub update_slave {
2005     my ($dbh) = @_;
2006
2007     my $sth = $dbh->prepare('SHOW SLAVE STATUS');
2008     $sth->execute();
2009     my $row = $sth->fetchrow_hashref();
2010     return 1 unless $row;
2011     while (my ($k, $v) = each %$row) {
2012         $data->{$k} = $v;
2013     }
2014     $sth->finish();
2015
2016     # We choose master_host here as a stopped slave
2017     # may not indicate that we have reset all slave capability
2018     # however the minimium requirement is a master_host
2019     return 1 if not defined $data->{master_host};
2020
2021     # undef when slave is stopped, or when MySQL fails to calculate
2022     # the lag (which happens depresingly often). (mk-heartbeat fixes
2023     # this problem.)
2024     $data->{seconds_behind_master} ||= 0;
2025
2026     # Track these two fields so we can trigger warnings if the slave stops
2027     # running
2028     $data->{slave_sql_running} = ($data->{slave_sql_running} eq 'Yes')
2029             ? 0 : 1;
2030     $data->{slave_io_running} = ($data->{slave_io_running} eq 'Yes')
2031             ? 0 : 1;
2032     return 0;
2033 }
2034
2035
2036 #---------------------------------------------------------------------
2037 #  Information SCHEMA tables represent data to be processed
2038 #---------------------------------------------------------------------
2039
2040
2041 sub plugin_query_response_time {
2042     my ($dbh) = @_;
2043
2044     return 1 if not defined $data->{query_response_time_stats};
2045     return 1 if $data->{query_response_time_stats} eq 'OFF';
2046
2047     my $sth = $dbh->prepare("SELECT * FROM INFORMATION_SCHEMA.QUERY_RESPONSE_TIME");
2048     $sth->execute();
2049     while (my $row = $sth->fetchrow_hashref()) {
2050         my $time = $row->{'time'};
2051         $data->{'query_response_time_count_' . $time} = $row->{'count'};
2052         push @{$graph_plugins{query_response_time}->{count}->{data_sources}}, {name => 'query_response_time_count_' . $time, label => $time };
2053         next if $row->{'total'} eq 'TOO LONG';
2054         $data->{'query_response_time_total_' . $time} = $row->{'total'} * 1e6;
2055         push @{$graph_plugins{query_response_time}->{total}->{data_sources}}, {name => 'query_response_time_total_' . $time, label => $time };
2056     }
2057     $sth->finish();
2058
2059     return 0;
2060 }
2061
2062 sub is_user_statistics {
2063     my ($dbh) = @_;
2064
2065     return 1 if not defined $data->{userstat};
2066     return 1 if $data->{userstat} eq 'OFF';
2067
2068     my $sth = $dbh->prepare("SELECT * FROM INFORMATION_SCHEMA.USER_STATISTICS");
2069     $sth->execute();
2070     while (my $row = $sth->fetchrow_hashref()) {
2071         my $user = $row->{'user'};
2072         my $var;
2073         while (my ($g, $v) = each %{$graph_plugins{user_statistics}}) {
2074           while (my ($userstat,$conf) = each %{$v->{cols}}) {
2075             $var = 'user_stats_' . $user . '_' . $userstat;
2076             $data->{$var} = int $row->{$userstat};
2077             my $ds = { %$conf };
2078             $ds->{name} = $var;
2079             $ds->{label} = $user . ' ' . $userstat;
2080             push @{$graph_plugins{user_statistics}->{$g}->{data_sources}}, $ds;
2081           }
2082         }
2083     }
2084     $sth->finish();
2085     return 0;
2086 }
2087
2088 #
2089 # In 'SHOW ENGINE INNODB STATUS' 64 bit integers are not formated as
2090 # plain integers. They are either:
2091 #
2092 #   - split in two and needs to be shifted together,
2093 #   - or hexadecimal
2094 #
2095 sub innodb_bigint {
2096     my ($x, $y) = @_;
2097
2098     return defined $y
2099         ? Math::BigInt->new($x)->blsft(32) + $y
2100         : Math::BigInt->new("0x$x");
2101 }
2102
2103 #---------------------------------------------------------------------
2104 #  P A R S E   'SHOW ENGINE INNODB STATUS'   O U T P U T
2105 #---------------------------------------------------------------------
2106
2107
2108 # A nice walk through
2109 # http://www.mysqlperformanceblog.com/2006/07/17/show-innodb-status-walk-through/
2110
2111 # The parsing is split in one subrutine per section. Each subroutine
2112 # should parse a block with the following structure
2113 #
2114 # block body ...
2115 # more lines ....
2116 # ----------
2117
2118 sub parse_innodb_status {
2119     local $_ = shift;
2120
2121     # Add a dummy section to the end in case the innodb status output
2122     # has been truncated (Happens for status > 64K characters)
2123     $_ .= "\n----------\nDUMMY\n----------\n";
2124
2125     my %section_map = (
2126
2127         'BUFFER POOL AND MEMORY'      => \&parse_buffer_pool_and_memory,
2128         'INDIVIDUAL BUFFER POOL INFO' => \&parse_individual_buffer_pool,
2129         'FILE I/O'                    => \&parse_file_io,
2130         'INSERT BUFFER AND ADAPTIVE HASH INDEX'
2131             => \&parse_insert_buffer_and_adaptive_hash_index,
2132         'LATEST DETECTED DEADLOCK'    => \&skip,
2133         'LATEST FOREIGN KEY ERROR'    => \&skip,
2134         'LOG'                         => \&parse_log,
2135         'ROW OPERATIONS'              => \&parse_row_operations,
2136         'SEMAPHORES'                  => \&parse_semaphores,
2137         'TRANSACTIONS'                => \&parse_transactions,
2138         'BACKGROUND THREAD'           => \&parse_background_thread,
2139     );
2140
2141     skip_heading();
2142     parse_header_seconds();
2143     for (;;) {
2144         m/\G(.*)\n/gc;
2145         my $sec = $1;
2146
2147         last if $sec eq 'END OF INNODB MONITOR OUTPUT';
2148         if ($sec eq 'DUMMY') {
2149             handle_incomplete_innodb_status();
2150             last;
2151         }
2152
2153         if (exists $section_map{$sec}) {
2154       $section_map{$sec}->();
2155         } else {
2156       #warn "Unknown section: $1";
2157       skip();
2158         }
2159     }
2160 }
2161
2162
2163 # This regular expression handles the different formating of 64-bit
2164 # integers in different versions of the innodb engine. Either two
2165 # decimal 32-bit integers separated by a space, or a single
2166 # hexadecimal 64-bit integer.
2167 my $innodb_bigint_rx = qr{([[a-fA-F\d]+)(?: (\d+))?};
2168
2169
2170 sub match_new_section {
2171    return m/\G
2172             -+\n                 # ---------------------------
2173             (?= [A-Z\/ ]+\n      # SECTION NAME
2174                 [=-]+\n)/gcx;    # --------------------------- ('=' on end of output)
2175 }
2176
2177
2178 sub skip_line    { return m/\G.*\n/gc; }
2179
2180
2181 sub skip_heading {
2182     # Heading is 3 lines
2183     for my $foo (1...3) {
2184         skip_line or die('Parse error');
2185     }
2186 }
2187
2188
2189 sub parse_section {
2190     my ($parser) = @_;
2191
2192     #warn substr($_, pos(), 10);
2193     for (;;) {
2194         return if match_new_section;
2195         next if $parser->();
2196         skip_line();
2197     }
2198 }
2199
2200
2201 sub skip { parse_section(sub {}); }
2202
2203
2204 sub parse_header_seconds {
2205     parse_section(
2206         sub {
2207             m/\GPer second averages calculated from the last (\d+) seconds\n/gc && do {
2208                 $data->{innodb_engine_status_seconds}  = $1;
2209                 return 1;
2210             };
2211         }
2212     );
2213 }
2214
2215
2216 sub parse_background_thread {
2217     parse_section(
2218         sub {
2219             m/\Gsrv_master_thread loops: \d+ 1_second, \d+ sleeps, \d+ 10_second, (\d+) background, (\d+) flush\n/gc && do {
2220                 $data->{ib_srv_main_flush_loops} = $1;
2221                 $data->{ib_srv_main_background_loops} = $2;
2222                 return 1;
2223             };
2224             m/\Gsrv_master_thread log flush and writes: (\d+)\n/gc && do {
2225                 $data->{ib_srv_main_flushs_writes}  = $1;
2226                 return 1;
2227             };
2228         }
2229     );
2230 }
2231
2232 sub parse_row_operations {
2233     parse_section(
2234         sub {
2235             m/\G(\d+) queries inside InnoDB, (\d+) queries in queue\n/gc && do {
2236                 $data->{ib_innodb_queries}  = $1;
2237                 $data->{ib_innodb_query_queue_len} = $2;
2238                 return 1;
2239             };
2240             m/\G(\d+) read views open inside InnoDB\n/gc && do {
2241                 $data->{ib_innodb_read_views}  = $1;
2242                 return 1;
2243             };
2244             m/\G(\d+) transactions active inside InnoDB\n/gc && do {
2245                 $data->{ib_innodb_transactions_active}  = $1;
2246                 return 1;
2247             };
2248             m/\G(\d+) out of (\d+) descriptors used\n/gc && do {
2249                 $data->{ib_innodb_descriptors}  = $1;
2250                 $data->{ib_innodb_descriptors_max} = $2;
2251                 return 1;
2252             };
2253             # no need for this - its exposed as status variables
2254             # m/\GNumber of rows inserted (\d+), updated (\d+), deleted (\d+), read (\d+)\n/gc && do {
2255             #     $data->{ib_innodb_rows_inserted} = $1;
2256             #     $data->{ib_innodb_rows_updated}  = $2;
2257             #     $data->{ib_innodb_rows_deleted}  = $3;
2258             #     $data->{ib_innodb_rows_read}     = $4;
2259             #     return 1;
2260             # };
2261         }
2262    );
2263 }
2264
2265 sub parse_semaphores {
2266     parse_section(
2267         sub {
2268             m/\GMutex spin waits (\d+), rounds (\d+), OS waits (\d+)\n/gc && do {
2269                 $data->{ib_spin_waits}  = $1;
2270                 $data->{ib_spin_rounds} = $2;
2271                 $data->{ib_os_waits}    = $3;
2272                 return 1;
2273             };
2274             m/\GRW-shared spins (\d+), rounds (\d+), OS waits (\d+)\n/gc && do {
2275                 $data->{ib_rw_shared_waits}  = $1;
2276                 $data->{ib_rw_shared_rounds} = $2;
2277                 $data->{ib_rw_shared_os_waits}    = $3;
2278                 return 1;
2279             };
2280             m/\GRW-excl spins (\d+), rounds (\d+), OS waits (\d+)\n/gc && do {
2281                 $data->{ib_rw_excl_waits}  = $1;
2282                 $data->{ib_rw_excl_rounds} = $2;
2283                 $data->{ib_rw_excl_os_waits}    = $3;
2284                 return 1;
2285             };
2286         }
2287     );
2288 }
2289
2290
2291 sub parse_transactions {
2292     parse_section(
2293         sub {
2294             m/\GTrx id counter $innodb_bigint_rx\n/gc && do {
2295                 $data->{ib_tnx} = innodb_bigint($1, $2);
2296                 return 1;
2297             };
2298             m/\GPurge done for trx's n:o < $innodb_bigint_rx undo n:o < $innodb_bigint_rx\n/gc && do {
2299                 if (defined $3) {
2300                     # old format
2301                     $data->{ib_tnx_prg} = innodb_bigint($1, $2);
2302                     # FIX add to data? innodb_bigint($3, $4);
2303                 }
2304                 else {
2305                     # new format
2306                     $data->{ib_tnx_prg} = innodb_bigint($1);
2307                     # FIX add to data? innodb_bigint($2);
2308                 }
2309                 return 1;
2310             };
2311             m/\GHistory list length (\d+)\n/gc && do {
2312                 $data->{ib_tnx_hist} = $1;
2313                 return 1;
2314             };
2315         }
2316     );
2317
2318 }
2319
2320
2321 sub parse_file_io {
2322     parse_section(
2323         sub {
2324             m/\GPending normal aio reads: (\d+)(?: \[(?:\d+, )*\d+\] )?, aio writes: (\d+)(?: \[(?:\d+, )*\d+\] )?,\n\s*ibuf aio reads: (\d+), log i\/o's: (\d+), sync i\/o's: (\d+)\n/gc && do {
2325                 $data->{ib_iop_aioread}  = $1;
2326                 $data->{ib_iop_aiowrite} = $2;
2327                 $data->{ib_iop_ibuf_aio} = $3;
2328                 $data->{ib_iop_log}      = $4;
2329                 $data->{ib_iop_sync}     = $5;
2330                 return 1;
2331             };
2332             m/\GPending flushes \(fsync\) log: (\d+); buffer pool: (\d+)\n/gc && do {
2333                 $data->{ib_iop_flush_log}   = $1;
2334                 $data->{ib_iop_flush_bpool} = $2;
2335                 return 1;
2336             };
2337             m/\G(\d+) OS file reads, (\d+) OS file writes, (\d+) OS fsyncs\n/gc && do {
2338                 $data->{ib_io_read}  = $1;
2339                 $data->{ib_io_write} = $2;
2340                 $data->{ib_io_fsync} = $3;
2341                 return 1;
2342             };
2343         }
2344     );
2345 }
2346
2347
2348 sub parse_insert_buffer_and_adaptive_hash_index {
2349     parse_section(
2350         sub {
2351       # MySQL < 5.5
2352       m/\G(\d+) inserts, (\d+) merged recs, (\d+) merges\n/gc && do {
2353         $data->{ib_ibuf_inserts}    = $1;
2354         $data->{ib_ibuf_merged_rec} = $2;
2355         $data->{ib_ibuf_merges}     = $3;
2356         return 1;
2357       };
2358       # MySQL >= 5.5
2359       m/\Gmerged operations:\n insert (\d+), delete mark (\d+), delete (\d+)\ndiscarded operations:\n insert (\d+), delete mark (\d+), delete (\d+)\n/gc && do {
2360         $data->{ib_ibuf_inserts} = $1;
2361         $data->{ib_ibuf_delete_mark} = $2;
2362         $data->{ib_ibuf_delete} = $3;
2363         $data->{ib_ibuf_discard_inserts} = $4;
2364         $data->{ib_ibuf_discard_delete_mark} = $5;
2365         $data->{ib_ibuf_discard_delete} = $6;
2366         $data->{ib_ibuf_merged_rec} = $data->{ib_ibuf_inserts} +  $data->{ib_ibuf_discard_inserts};
2367         return 1;
2368       };
2369
2370       m/\GIbuf: size (\d+), free list len (\d+), seg size (\d+),(?: (\d+) merges)?\n/gc && do {
2371     $data->{ib_ibuf_size}     = $1;
2372     $data->{ib_ibuf_free_len} = $2;
2373     $data->{ib_ibuf_seg_size} = $3;
2374     $data->{ib_ibuf_merges}   = $4 if defined $4; # MySQL >= 5.5
2375                 return 1;
2376             };
2377         }
2378     );
2379 }
2380
2381
2382 sub parse_log {
2383     parse_section(
2384         sub {
2385             m/\GLog sequence number $innodb_bigint_rx\n/gc && do {
2386                 $data->{ib_log_written} = innodb_bigint($1, $2);
2387                 return 1;
2388             };
2389             m/\GLog flushed up to\s+$innodb_bigint_rx\n/gc && do {
2390                 $data->{ib_log_flush} = innodb_bigint($1, $2);
2391                 return 1;
2392             };
2393             m/\G(\d+) log i\/o's done.*\n/gc && do {
2394                 $data->{ib_io_log} = $1;
2395                 return 1;
2396             };
2397         }
2398     );
2399 }
2400
2401
2402 sub parse_buffer_pool_and_memory {
2403     parse_section(
2404         sub {
2405             m/\GBuffer pool size\s+(\d+)\n/gc && do {
2406                 $data->{ib_bpool_size} = $1;
2407                 return 1;
2408             };
2409       m/\GBuffer pool size, bytes\s+(\d+)\n/gc && do {
2410     $data->{ib_bpool_size_bytes} = $1;
2411                 return 1;
2412       };
2413             m/\GFree buffers\s+(\d+)\n/gc && do {
2414                 $data->{ib_bpool_free} = $1;
2415                 return 1;
2416             };
2417             m/\GDatabase pages\s+(\d+)\n/gc && do {
2418                 $data->{ib_bpool_dbpages} = $1;
2419                 return 1;
2420             };
2421             m/\GModified db pages\s+(\d+)\n/gc && do {
2422                 $data->{ib_bpool_modpages} = $1;
2423                 return 1;
2424             };
2425             m/\GOld database pages\s+(\d+)\n/gc && do {
2426                 $data->{ib_bpool_oldpages} = $1;
2427                 return 1;
2428             };
2429       m/\GPages made young (\d+), not young (\d+)\n/gc && do {
2430     $data->{ib_bpool_made_young}     = $1;
2431     $data->{ib_bpool_made_not_young} = $2;
2432                 return 1;
2433     };
2434             m/\GPages read (\d+), created (\d+), written (\d+)\n/gc && do {
2435                 $data->{ib_bpool_read}    = $1;
2436                 $data->{ib_bpool_created} = $2;
2437                 $data->{ib_bpool_written} = $3;
2438                 return 1;
2439             };
2440       # mariadb-5.5
2441       m/\GInternal hash tables \(constant factor \+ variable factor\)\n\s*Adaptive hash index\s*(\d+)\s*\((\d+) \+ (\d+)\) *\n\s+Page hash +(\d+) +\(buffer pool \d+ only\) *\n\s+Dictionary cache\s*(\d+)\s+\((\d+) \+ (\d+)\) *\n\s+File system\s+(\d+)\s+\((\d+) \+ (\d+)\) *\n\s+Lock system\s+(\d+)\s+\((\d+) \+ (\d+)\) *\n\s+Recovery system\s*(\d+)\s+\((\d+) \+ (\d+)\) *\n/gc  
2442           && do {
2443           $data->{ib_bpool_internal_adaptive_hash_size_total}  = $1;
2444           $data->{ib_bpool_internal_adaptive_hash_size_const}  = $2;
2445           $data->{ib_bpool_internal_adaptive_hash_size_var}    = $3;
2446           $data->{ib_bpool_internal_page_hash_size_total}  = $4;
2447           $data->{ib_bpool_internal_dictionary_cache_size_total}  = $5;
2448           $data->{ib_bpool_internal_dictionary_cache_size_const}  = $6;
2449           $data->{ib_bpool_internal_dictionary_cache_size_var}    = $7;
2450           $data->{ib_bpool_internal_file_system_size_total}  = $8;
2451           $data->{ib_bpool_internal_file_system_size_const}  = $9;
2452           $data->{ib_bpool_internal_file_system_size_var}    = $10;
2453           $data->{ib_bpool_internal_lock_system_size_total}  = $11;
2454           $data->{ib_bpool_internal_lock_system_size_const}  = $12;
2455           $data->{ib_bpool_internal_lock_system_size_var}    = $13;
2456           $data->{ib_bpool_internal_recovery_system_size_total}  = $14;
2457           $data->{ib_bpool_internal_recovery_system_size_const}  = $15;
2458           $data->{ib_bpool_internal_recovery_system_size_var}    = $16;
2459           return 1;
2460       };
2461
2462         }
2463     );
2464 }
2465
2466 sub parse_individual_buffer_pool {
2467     parse_section(
2468   sub {
2469     m/\G---BUFFER POOL (\d+)\n/gc && do {
2470       my $pool = $1;
2471       $data->{ib_bpool_individual_pool_count} = $pool + 1;
2472       m/\GBuffer pool size\s+(\d+)\n/gc && do {
2473         $data->{"ib_bpool_individual_pool_${pool}_size"} = $1;
2474       };
2475       m/\GBuffer pool size, bytes\s+(\d+)\n/gc && do {
2476         $data->{"ib_bpool_individual_pool_${pool}_size_bytes"} = $1;
2477       };
2478       m/\GFree buffers\s+(\d+)\nDatabase pages\s+(\d+)\nOld database pages\s+(\d+)\nModified db pages\s+(\d+)\nPending reads\s+(\d+)\nPending writes: LRU\s+(\d+), flush list\s+(\d+), single page\s+(\d+)\n/gc && do {
2479         $data->{"ib_bpool_individual_pool_${pool}_free"} = $1;
2480         $data->{"ib_bpool_individual_pool_${pool}_dbpages"} = $2;
2481         $data->{"ib_bpool_individual_pool_${pool}_oldpages"} = $3;
2482         $data->{"ib_bpool_individual_pool_${pool}_modpages"} = $4;
2483         $data->{"ib_bpool_individual_pool_${pool}_pending_reads"} = $5;
2484         $data->{"ib_bpool_individual_pool_${pool}_pending_writes_lru"} = $6;
2485         $data->{"ib_bpool_individual_pool_${pool}_pending_writes_flush"} = $7;
2486         $data->{"ib_bpool_individual_pool_${pool}_pending_writes_single"} = $8;
2487       };
2488       skip_line();
2489       m/\GPages read (\d+), created (\d+), written (\d+)\n/gc && do {
2490         $data->{"ib_bpool_individual_pool_${pool}_read"}    = $1;
2491         $data->{"ib_bpool_individual_pool_${pool}_created"} = $2;
2492         $data->{"ib_bpool_individual_pool_${pool}_written"} = $3;
2493       };
2494       return 1;
2495     };
2496         }
2497   );
2498 }
2499
2500 sub handle_incomplete_innodb_status {
2501
2502     warn "Output from SHOW ENGINE INNODB STATUS was truncated. "
2503         . "This happens if the output of STATUS exceeds 64KB. "
2504         . "Several of the InnoDB graphs might be affected by this.";
2505
2506     # FIX Is it possible to find some of the missing values from SHOW
2507     # STATUS?
2508 }
2509
2510
2511 exit main() unless caller;
2512
2513
2514 1;