Merge lp:~abychko/percona-server/ps-5.1-bug1032139 into lp:percona-server/5.1
- ps-5.1-bug1032139
- Merge into 5.1
Proposed by
Alexey Bychko
Status: | Merged | ||||||||
---|---|---|---|---|---|---|---|---|---|
Approved by: | Alexey Kopytov | ||||||||
Approved revision: | no longer in the source branch. | ||||||||
Merged at revision: | 541 | ||||||||
Proposed branch: | lp:~abychko/percona-server/ps-5.1-bug1032139 | ||||||||
Merge into: | lp:percona-server/5.1 | ||||||||
Diff against target: |
13050 lines (+0/-12986) 8 files modified
build/debian/additions/innotop/InnoDBParser.pm (+0/-1089) build/debian/additions/innotop/changelog.innotop (+0/-318) build/debian/additions/innotop/innotop (+0/-9485) build/debian/additions/innotop/innotop.1 (+0/-2086) build/debian/percona-server-client-5.1.dirs (+0/-1) build/debian/percona-server-client-5.1.docs (+0/-1) build/debian/percona-server-client-5.1.files (+0/-3) build/debian/rules (+0/-3) |
||||||||
To merge this branch: | bzr merge lp:~abychko/percona-server/ps-5.1-bug1032139 | ||||||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Alexey Kopytov (community) | Approve | ||
Ignacio Nin (community) | Approve | ||
Review via email: mp+147021@code.launchpad.net |
Commit message
Description of the change
[-] removed innotop and its InnoDBParser perl package from source and debian installation
To post a comment you must log in.
Revision history for this message
Alexey Kopytov (akopytov) wrote : | # |
Should also reference bug #1050536 as this fix also fixes that bug. And resubmitted as a proper upmerge for 5.5.
review:
Needs Fixing
Revision history for this message
Alexey Bychko (abychko) wrote : | # |
Revision history for this message
Alexey Kopytov (akopytov) : | # |
review:
Approve
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === removed directory 'build/debian/additions/innotop' | |||
2 | === removed file 'build/debian/additions/innotop/InnoDBParser.pm' | |||
3 | --- build/debian/additions/innotop/InnoDBParser.pm 2010-05-31 13:57:21 +0000 | |||
4 | +++ build/debian/additions/innotop/InnoDBParser.pm 1970-01-01 00:00:00 +0000 | |||
5 | @@ -1,1089 +0,0 @@ | |||
6 | 1 | use strict; | ||
7 | 2 | use warnings FATAL => 'all'; | ||
8 | 3 | |||
9 | 4 | package InnoDBParser; | ||
10 | 5 | |||
11 | 6 | # This program is copyright (c) 2006 Baron Schwartz, baron at xaprb dot com. | ||
12 | 7 | # Feedback and improvements are gratefully received. | ||
13 | 8 | # | ||
14 | 9 | # THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED | ||
15 | 10 | # WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF | ||
16 | 11 | # MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
17 | 12 | # | ||
18 | 13 | # This program is free software; you can redistribute it and/or modify it under | ||
19 | 14 | # the terms of the GNU General Public License as published by the Free Software | ||
20 | 15 | # Foundation, version 2; OR the Perl Artistic License. On UNIX and similar | ||
21 | 16 | # systems, you can issue `man perlgpl' or `man perlartistic' to read these | ||
22 | 17 | |||
23 | 18 | # You should have received a copy of the GNU General Public License along with | ||
24 | 19 | # this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
25 | 20 | # Place, Suite 330, Boston, MA 02111-1307 USA | ||
26 | 21 | |||
27 | 22 | our $VERSION = '1.6.0'; | ||
28 | 23 | |||
29 | 24 | use Data::Dumper; | ||
30 | 25 | $Data::Dumper::Sortkeys = 1; | ||
31 | 26 | use English qw(-no_match_vars); | ||
32 | 27 | use List::Util qw(max); | ||
33 | 28 | |||
34 | 29 | # Some common patterns | ||
35 | 30 | my $d = qr/(\d+)/; # Digit | ||
36 | 31 | my $f = qr/(\d+\.\d+)/; # Float | ||
37 | 32 | my $t = qr/(\d+ \d+)/; # Transaction ID | ||
38 | 33 | my $i = qr/((?:\d{1,3}\.){3}\d+)/; # IP address | ||
39 | 34 | my $n = qr/([^`\s]+)/; # MySQL object name | ||
40 | 35 | my $w = qr/(\w+)/; # Words | ||
41 | 36 | my $fl = qr/([\w\.\/]+) line $d/; # Filename and line number | ||
42 | 37 | my $h = qr/((?:0x)?[0-9a-f]*)/; # Hex | ||
43 | 38 | my $s = qr/(\d{6} .\d:\d\d:\d\d)/; # InnoDB timestamp | ||
44 | 39 | |||
45 | 40 | # If you update this variable, also update the SYNOPSIS in the pod. | ||
46 | 41 | my %innodb_section_headers = ( | ||
47 | 42 | "TRANSACTIONS" => "tx", | ||
48 | 43 | "BUFFER POOL AND MEMORY" => "bp", | ||
49 | 44 | "SEMAPHORES" => "sm", | ||
50 | 45 | "LOG" => "lg", | ||
51 | 46 | "ROW OPERATIONS" => "ro", | ||
52 | 47 | "INSERT BUFFER AND ADAPTIVE HASH INDEX" => "ib", | ||
53 | 48 | "FILE I/O" => "io", | ||
54 | 49 | "LATEST DETECTED DEADLOCK" => "dl", | ||
55 | 50 | "LATEST FOREIGN KEY ERROR" => "fk", | ||
56 | 51 | ); | ||
57 | 52 | |||
58 | 53 | my %parser_for = ( | ||
59 | 54 | tx => \&parse_tx_section, | ||
60 | 55 | bp => \&parse_bp_section, | ||
61 | 56 | sm => \&parse_sm_section, | ||
62 | 57 | lg => \&parse_lg_section, | ||
63 | 58 | ro => \&parse_ro_section, | ||
64 | 59 | ib => \&parse_ib_section, | ||
65 | 60 | io => \&parse_io_section, | ||
66 | 61 | dl => \&parse_dl_section, | ||
67 | 62 | fk => \&parse_fk_section, | ||
68 | 63 | ); | ||
69 | 64 | |||
70 | 65 | my %fk_parser_for = ( | ||
71 | 66 | Transaction => \&parse_fk_transaction_error, | ||
72 | 67 | Error => \&parse_fk_bad_constraint_error, | ||
73 | 68 | Cannot => \&parse_fk_cant_drop_parent_error, | ||
74 | 69 | ); | ||
75 | 70 | |||
76 | 71 | # A thread's proc_info can be at least 98 different things I've found in the | ||
77 | 72 | # source. Fortunately, most of them begin with a gerunded verb. These are | ||
78 | 73 | # the ones that don't. | ||
79 | 74 | my %is_proc_info = ( | ||
80 | 75 | 'After create' => 1, | ||
81 | 76 | 'Execution of init_command' => 1, | ||
82 | 77 | 'FULLTEXT initialization' => 1, | ||
83 | 78 | 'Reopen tables' => 1, | ||
84 | 79 | 'Repair done' => 1, | ||
85 | 80 | 'Repair with keycache' => 1, | ||
86 | 81 | 'System lock' => 1, | ||
87 | 82 | 'Table lock' => 1, | ||
88 | 83 | 'Thread initialized' => 1, | ||
89 | 84 | 'User lock' => 1, | ||
90 | 85 | 'copy to tmp table' => 1, | ||
91 | 86 | 'discard_or_import_tablespace' => 1, | ||
92 | 87 | 'end' => 1, | ||
93 | 88 | 'got handler lock' => 1, | ||
94 | 89 | 'got old table' => 1, | ||
95 | 90 | 'init' => 1, | ||
96 | 91 | 'key cache' => 1, | ||
97 | 92 | 'locks' => 1, | ||
98 | 93 | 'malloc' => 1, | ||
99 | 94 | 'query end' => 1, | ||
100 | 95 | 'rename result table' => 1, | ||
101 | 96 | 'rename' => 1, | ||
102 | 97 | 'setup' => 1, | ||
103 | 98 | 'statistics' => 1, | ||
104 | 99 | 'status' => 1, | ||
105 | 100 | 'table cache' => 1, | ||
106 | 101 | 'update' => 1, | ||
107 | 102 | ); | ||
108 | 103 | |||
109 | 104 | sub new { | ||
110 | 105 | bless {}, shift; | ||
111 | 106 | } | ||
112 | 107 | |||
113 | 108 | # Parse the status and return it. | ||
114 | 109 | # See srv_printf_innodb_monitor in innobase/srv/srv0srv.c | ||
115 | 110 | # Pass in the text to parse, whether to be in debugging mode, which sections | ||
116 | 111 | # to parse (hashref; if empty, parse all), and whether to parse full info from | ||
117 | 112 | # locks and such (probably shouldn't unless you need to). | ||
118 | 113 | sub parse_status_text { | ||
119 | 114 | my ( $self, $fulltext, $debug, $sections, $full ) = @_; | ||
120 | 115 | |||
121 | 116 | die "I can't parse undef" unless defined $fulltext; | ||
122 | 117 | $fulltext =~ s/[\r\n]+/\n/g; | ||
123 | 118 | |||
124 | 119 | $sections ||= {}; | ||
125 | 120 | die '$sections must be a hashref' unless ref($sections) eq 'HASH'; | ||
126 | 121 | |||
127 | 122 | my %innodb_data = ( | ||
128 | 123 | got_all => 0, # Whether I was able to get the whole thing | ||
129 | 124 | ts => '', # Timestamp the server put on it | ||
130 | 125 | last_secs => 0, # Num seconds the averages are over | ||
131 | 126 | sections => {}, # Parsed values from each section | ||
132 | 127 | ); | ||
133 | 128 | |||
134 | 129 | if ( $debug ) { | ||
135 | 130 | $innodb_data{'fulltext'} = $fulltext; | ||
136 | 131 | } | ||
137 | 132 | |||
138 | 133 | # Get the most basic info about the status: beginning and end, and whether | ||
139 | 134 | # I got the whole thing (if there has been a big deadlock and there are | ||
140 | 135 | # too many locks to print, the output might be truncated) | ||
141 | 136 | my ( $time_text ) = $fulltext =~ m/^$s INNODB MONITOR OUTPUT$/m; | ||
142 | 137 | $innodb_data{'ts'} = [ parse_innodb_timestamp( $time_text ) ]; | ||
143 | 138 | $innodb_data{'timestring'} = ts_to_string($innodb_data{'ts'}); | ||
144 | 139 | ( $innodb_data{'last_secs'} ) = $fulltext | ||
145 | 140 | =~ m/Per second averages calculated from the last $d seconds/; | ||
146 | 141 | |||
147 | 142 | ( my $got_all ) = $fulltext =~ m/END OF INNODB MONITOR OUTPUT/; | ||
148 | 143 | $innodb_data{'got_all'} = $got_all || 0; | ||
149 | 144 | |||
150 | 145 | # Split it into sections. Each section begins with | ||
151 | 146 | # ----- | ||
152 | 147 | # LABEL | ||
153 | 148 | # ----- | ||
154 | 149 | my %innodb_sections; | ||
155 | 150 | my @matches = $fulltext | ||
156 | 151 | =~ m#\n(---+)\n([A-Z /]+)\n\1\n(.*?)(?=\n(---+)\n[A-Z /]+\n\4\n|$)#gs; | ||
157 | 152 | while ( my ( $start, $name, $text, $end ) = splice(@matches, 0, 4) ) { | ||
158 | 153 | $innodb_sections{$name} = [ $text, $end ? 1 : 0 ]; | ||
159 | 154 | } | ||
160 | 155 | # The Row Operations section is a special case, because instead of ending | ||
161 | 156 | # with the beginning of another section, it ends with the end of the file. | ||
162 | 157 | # So this section is complete if the entire file is complete. | ||
163 | 158 | $innodb_sections{'ROW OPERATIONS'}->[1] ||= $innodb_data{'got_all'}; | ||
164 | 159 | |||
165 | 160 | # Just for sanity's sake, make sure I understand what to do with each | ||
166 | 161 | # section | ||
167 | 162 | eval { | ||
168 | 163 | foreach my $section ( keys %innodb_sections ) { | ||
169 | 164 | my $header = $innodb_section_headers{$section}; | ||
170 | 165 | die "Unknown section $section in $fulltext\n" | ||
171 | 166 | unless $header; | ||
172 | 167 | $innodb_data{'sections'}->{ $header } | ||
173 | 168 | ->{'fulltext'} = $innodb_sections{$section}->[0]; | ||
174 | 169 | $innodb_data{'sections'}->{ $header } | ||
175 | 170 | ->{'complete'} = $innodb_sections{$section}->[1]; | ||
176 | 171 | } | ||
177 | 172 | }; | ||
178 | 173 | if ( $EVAL_ERROR ) { | ||
179 | 174 | _debug( $debug, $EVAL_ERROR); | ||
180 | 175 | } | ||
181 | 176 | |||
182 | 177 | # ################################################################ | ||
183 | 178 | # Parse the detailed data out of the sections. | ||
184 | 179 | # ################################################################ | ||
185 | 180 | eval { | ||
186 | 181 | foreach my $section ( keys %parser_for ) { | ||
187 | 182 | if ( defined $innodb_data{'sections'}->{$section} | ||
188 | 183 | && (!%$sections || (defined($sections->{$section} && $sections->{$section})) )) { | ||
189 | 184 | $parser_for{$section}->( | ||
190 | 185 | $innodb_data{'sections'}->{$section}, | ||
191 | 186 | $innodb_data{'sections'}->{$section}->{'complete'}, | ||
192 | 187 | $debug, | ||
193 | 188 | $full ) | ||
194 | 189 | or delete $innodb_data{'sections'}->{$section}; | ||
195 | 190 | } | ||
196 | 191 | else { | ||
197 | 192 | delete $innodb_data{'sections'}->{$section}; | ||
198 | 193 | } | ||
199 | 194 | } | ||
200 | 195 | }; | ||
201 | 196 | if ( $EVAL_ERROR ) { | ||
202 | 197 | _debug( $debug, $EVAL_ERROR); | ||
203 | 198 | } | ||
204 | 199 | |||
205 | 200 | return \%innodb_data; | ||
206 | 201 | } | ||
207 | 202 | |||
208 | 203 | # Parses the status text and returns it flattened out as a single hash. | ||
209 | 204 | sub get_status_hash { | ||
210 | 205 | my ( $self, $fulltext, $debug, $sections, $full ) = @_; | ||
211 | 206 | |||
212 | 207 | # Parse the status text... | ||
213 | 208 | my $innodb_status | ||
214 | 209 | = $self->parse_status_text($fulltext, $debug, $sections, $full ); | ||
215 | 210 | |||
216 | 211 | # Flatten the hierarchical structure into a single list by grabbing desired | ||
217 | 212 | # sections from it. | ||
218 | 213 | return | ||
219 | 214 | (map { 'IB_' . $_ => $innodb_status->{$_} } qw(timestring last_secs got_all)), | ||
220 | 215 | (map { 'IB_bp_' . $_ => $innodb_status->{'sections'}->{'bp'}->{$_} } | ||
221 | 216 | qw( writes_pending buf_pool_hit_rate total_mem_alloc buf_pool_reads | ||
222 | 217 | awe_mem_alloc pages_modified writes_pending_lru page_creates_sec | ||
223 | 218 | reads_pending pages_total buf_pool_hits writes_pending_single_page | ||
224 | 219 | page_writes_sec pages_read pages_written page_reads_sec | ||
225 | 220 | writes_pending_flush_list buf_pool_size add_pool_alloc | ||
226 | 221 | dict_mem_alloc pages_created buf_free complete )), | ||
227 | 222 | (map { 'IB_tx_' . $_ => $innodb_status->{'sections'}->{'tx'}->{$_} } | ||
228 | 223 | qw( num_lock_structs history_list_len purge_done_for transactions | ||
229 | 224 | purge_undo_for is_truncated trx_id_counter complete )), | ||
230 | 225 | (map { 'IB_ib_' . $_ => $innodb_status->{'sections'}->{'ib'}->{$_} } | ||
231 | 226 | qw( hash_table_size hash_searches_s non_hash_searches_s | ||
232 | 227 | bufs_in_node_heap used_cells size free_list_len seg_size inserts | ||
233 | 228 | merged_recs merges complete )), | ||
234 | 229 | (map { 'IB_lg_' . $_ => $innodb_status->{'sections'}->{'lg'}->{$_} } | ||
235 | 230 | qw( log_ios_done pending_chkp_writes last_chkp log_ios_s | ||
236 | 231 | log_flushed_to log_seq_no pending_log_writes complete )), | ||
237 | 232 | (map { 'IB_sm_' . $_ => $innodb_status->{'sections'}->{'sm'}->{$_} } | ||
238 | 233 | qw( wait_array_size rw_shared_spins rw_excl_os_waits mutex_os_waits | ||
239 | 234 | mutex_spin_rounds mutex_spin_waits rw_excl_spins rw_shared_os_waits | ||
240 | 235 | waits signal_count reservation_count complete )), | ||
241 | 236 | (map { 'IB_ro_' . $_ => $innodb_status->{'sections'}->{'ro'}->{$_} } | ||
242 | 237 | qw( queries_in_queue n_reserved_extents main_thread_state | ||
243 | 238 | main_thread_proc_no main_thread_id read_sec del_sec upd_sec ins_sec | ||
244 | 239 | read_views_open num_rows_upd num_rows_ins num_rows_read | ||
245 | 240 | queries_inside num_rows_del complete )), | ||
246 | 241 | (map { 'IB_fk_' . $_ => $innodb_status->{'sections'}->{'fk'}->{$_} } | ||
247 | 242 | qw( trigger parent_table child_index parent_index attempted_op | ||
248 | 243 | child_db timestring fk_name records col_name reason txn parent_db | ||
249 | 244 | type child_table parent_col complete )), | ||
250 | 245 | (map { 'IB_io_' . $_ => $innodb_status->{'sections'}->{'io'}->{$_} } | ||
251 | 246 | qw( pending_buffer_pool_flushes pending_pwrites pending_preads | ||
252 | 247 | pending_normal_aio_reads fsyncs_s os_file_writes pending_sync_ios | ||
253 | 248 | reads_s flush_type avg_bytes_s pending_ibuf_aio_reads writes_s | ||
254 | 249 | threads os_file_reads pending_aio_writes pending_log_ios os_fsyncs | ||
255 | 250 | pending_log_flushes complete )), | ||
256 | 251 | (map { 'IB_dl_' . $_ => $innodb_status->{'sections'}->{'dl'}->{$_} } | ||
257 | 252 | qw( timestring rolled_back txns complete )); | ||
258 | 253 | |||
259 | 254 | } | ||
260 | 255 | |||
261 | 256 | sub ts_to_string { | ||
262 | 257 | my $parts = shift; | ||
263 | 258 | return sprintf('%02d-%02d-%02d %02d:%02d:%02d', @$parts); | ||
264 | 259 | } | ||
265 | 260 | |||
266 | 261 | sub parse_innodb_timestamp { | ||
267 | 262 | my $text = shift; | ||
268 | 263 | my ( $y, $m, $d, $h, $i, $s ) | ||
269 | 264 | = $text =~ m/^(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)$/; | ||
270 | 265 | die("Can't get timestamp from $text\n") unless $y; | ||
271 | 266 | $y += 2000; | ||
272 | 267 | return ( $y, $m, $d, $h, $i, $s ); | ||
273 | 268 | } | ||
274 | 269 | |||
275 | 270 | sub parse_fk_section { | ||
276 | 271 | my ( $section, $complete, $debug, $full ) = @_; | ||
277 | 272 | my $fulltext = $section->{'fulltext'}; | ||
278 | 273 | |||
279 | 274 | return 0 unless $fulltext; | ||
280 | 275 | |||
281 | 276 | my ( $ts, $type ) = $fulltext =~ m/^$s\s+(\w+)/m; | ||
282 | 277 | $section->{'ts'} = [ parse_innodb_timestamp( $ts ) ]; | ||
283 | 278 | $section->{'timestring'} = ts_to_string($section->{'ts'}); | ||
284 | 279 | $section->{'type'} = $type; | ||
285 | 280 | |||
286 | 281 | # Decide which type of FK error happened, and dispatch to the right parser. | ||
287 | 282 | if ( $type && $fk_parser_for{$type} ) { | ||
288 | 283 | $fk_parser_for{$type}->( $section, $complete, $debug, $fulltext, $full ); | ||
289 | 284 | } | ||
290 | 285 | |||
291 | 286 | delete $section->{'fulltext'} unless $debug; | ||
292 | 287 | |||
293 | 288 | return 1; | ||
294 | 289 | } | ||
295 | 290 | |||
296 | 291 | sub parse_fk_cant_drop_parent_error { | ||
297 | 292 | my ( $section, $complete, $debug, $fulltext, $full ) = @_; | ||
298 | 293 | |||
299 | 294 | # Parse the parent/child table info out | ||
300 | 295 | @{$section}{ qw(attempted_op parent_db parent_table) } = $fulltext | ||
301 | 296 | =~ m{Cannot $w table `(.*)/(.*)`}m; | ||
302 | 297 | @{$section}{ qw(child_db child_table) } = $fulltext | ||
303 | 298 | =~ m{because it is referenced by `(.*)/(.*)`}m; | ||
304 | 299 | |||
305 | 300 | ( $section->{'reason'} ) = $fulltext =~ m/(Cannot .*)/s; | ||
306 | 301 | $section->{'reason'} =~ s/\n(?:InnoDB: )?/ /gm | ||
307 | 302 | if $section->{'reason'}; | ||
308 | 303 | |||
309 | 304 | # Certain data may not be present. Make them '' if not present. | ||
310 | 305 | map { $section->{$_} ||= "" } | ||
311 | 306 | qw(child_index fk_name col_name parent_col); | ||
312 | 307 | } | ||
313 | 308 | |||
314 | 309 | # See dict/dict0dict.c, function dict_foreign_error_report | ||
315 | 310 | # I don't care much about these. There are lots of different messages, and | ||
316 | 311 | # they come from someone trying to create a foreign key, or similar | ||
317 | 312 | # statements. They aren't indicative of some transaction trying to insert, | ||
318 | 313 | # delete or update data. Sometimes it is possible to parse out a lot of | ||
319 | 314 | # information about the tables and indexes involved, but often the message | ||
320 | 315 | # contains the DDL string the user entered, which is way too much for this | ||
321 | 316 | # module to try to handle. | ||
322 | 317 | sub parse_fk_bad_constraint_error { | ||
323 | 318 | my ( $section, $complete, $debug, $fulltext, $full ) = @_; | ||
324 | 319 | |||
325 | 320 | # Parse the parent/child table and index info out | ||
326 | 321 | @{$section}{ qw(child_db child_table) } = $fulltext | ||
327 | 322 | =~ m{Error in foreign key constraint of table (.*)/(.*):$}m; | ||
328 | 323 | $section->{'attempted_op'} = 'DDL'; | ||
329 | 324 | |||
330 | 325 | # FK name, parent info... if possible. | ||
331 | 326 | @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) } | ||
332 | 327 | = $fulltext | ||
333 | 328 | =~ m/CONSTRAINT `?$n`? FOREIGN KEY \(`?$n`?\) REFERENCES (?:`?$n`?\.)?`?$n`? \(`?$n`?\)/; | ||
334 | 329 | |||
335 | 330 | if ( !defined($section->{'fk_name'}) ) { | ||
336 | 331 | # Try to parse SQL a user might have typed in a CREATE statement or such | ||
337 | 332 | @{$section}{ qw(col_name parent_db parent_table parent_col) } | ||
338 | 333 | = $fulltext | ||
339 | 334 | =~ m/FOREIGN\s+KEY\s*\(`?$n`?\)\s+REFERENCES\s+(?:`?$n`?\.)?`?$n`?\s*\(`?$n`?\)/i; | ||
340 | 335 | } | ||
341 | 336 | $section->{'parent_db'} ||= $section->{'child_db'}; | ||
342 | 337 | |||
343 | 338 | # Name of the child index (index in the same table where the FK is, see | ||
344 | 339 | # definition of dict_foreign_struct in include/dict0mem.h, where it is | ||
345 | 340 | # called foreign_index, as opposed to referenced_index which is in the | ||
346 | 341 | # parent table. This may not be possible to find. | ||
347 | 342 | @{$section}{ qw(child_index) } = $fulltext | ||
348 | 343 | =~ m/^The index in the foreign key in table is $n$/m; | ||
349 | 344 | |||
350 | 345 | @{$section}{ qw(reason) } = $fulltext =~ m/:\s*([^:]+)(?= Constraint:|$)/ms; | ||
351 | 346 | $section->{'reason'} =~ s/\s+/ /g | ||
352 | 347 | if $section->{'reason'}; | ||
353 | 348 | |||
354 | 349 | # Certain data may not be present. Make them '' if not present. | ||
355 | 350 | map { $section->{$_} ||= "" } | ||
356 | 351 | qw(child_index fk_name col_name parent_table parent_col); | ||
357 | 352 | } | ||
358 | 353 | |||
359 | 354 | # see source file row/row0ins.c | ||
360 | 355 | sub parse_fk_transaction_error { | ||
361 | 356 | my ( $section, $complete, $debug, $fulltext, $full ) = @_; | ||
362 | 357 | |||
363 | 358 | # Parse the txn info out | ||
364 | 359 | my ( $txn ) = $fulltext | ||
365 | 360 | =~ m/Transaction:\n(TRANSACTION.*)\nForeign key constraint fails/s; | ||
366 | 361 | if ( $txn ) { | ||
367 | 362 | $section->{'txn'} = parse_tx_text( $txn, $complete, $debug, $full ); | ||
368 | 363 | } | ||
369 | 364 | |||
370 | 365 | # Parse the parent/child table and index info out. There are two types: an | ||
371 | 366 | # update or a delete of a parent record leaves a child orphaned | ||
372 | 367 | # (row_ins_foreign_report_err), and an insert or update of a child record has | ||
373 | 368 | # no matching parent record (row_ins_foreign_report_add_err). | ||
374 | 369 | |||
375 | 370 | @{$section}{ qw(reason child_db child_table) } | ||
376 | 371 | = $fulltext =~ m{^(Foreign key constraint fails for table `(.*)/(.*)`:)$}m; | ||
377 | 372 | |||
378 | 373 | @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) } | ||
379 | 374 | = $fulltext | ||
380 | 375 | =~ m/CONSTRAINT `$n` FOREIGN KEY \(`$n`\) REFERENCES (?:`$n`\.)?`$n` \(`$n`\)/; | ||
381 | 376 | $section->{'parent_db'} ||= $section->{'child_db'}; | ||
382 | 377 | |||
383 | 378 | # Special case, which I don't know how to trigger, but see | ||
384 | 379 | # innobase/row/row0ins.c row_ins_check_foreign_constraint | ||
385 | 380 | if ( $fulltext =~ m/ibd file does not currently exist!/ ) { | ||
386 | 381 | my ( $attempted_op, $index, $records ) | ||
387 | 382 | = $fulltext =~ m/^Trying to (add to index) `$n` tuple:\n(.*))?/sm; | ||
388 | 383 | $section->{'child_index'} = $index; | ||
389 | 384 | $section->{'attempted_op'} = $attempted_op || ''; | ||
390 | 385 | if ( $records && $full ) { | ||
391 | 386 | ( $section->{'records'} ) | ||
392 | 387 | = parse_innodb_record_dump( $records, $complete, $debug ); | ||
393 | 388 | } | ||
394 | 389 | @{$section}{qw(parent_db parent_table)} | ||
395 | 390 | =~ m/^But the parent table `$n`\.`$n`$/m; | ||
396 | 391 | } | ||
397 | 392 | else { | ||
398 | 393 | my ( $attempted_op, $which, $index ) | ||
399 | 394 | = $fulltext =~ m/^Trying to ([\w ]*) in (child|parent) table, in index `$n` tuple:$/m; | ||
400 | 395 | if ( $which ) { | ||
401 | 396 | $section->{$which . '_index'} = $index; | ||
402 | 397 | $section->{'attempted_op'} = $attempted_op || ''; | ||
403 | 398 | |||
404 | 399 | # Parse out the related records in the other table. | ||
405 | 400 | my ( $search_index, $records ); | ||
406 | 401 | if ( $which eq 'child' ) { | ||
407 | 402 | ( $search_index, $records ) = $fulltext | ||
408 | 403 | =~ m/^But in parent table [^,]*, in index `$n`,\nthe closest match we can find is record:\n(.*)/ms; | ||
409 | 404 | $section->{'parent_index'} = $search_index; | ||
410 | 405 | } | ||
411 | 406 | else { | ||
412 | 407 | ( $search_index, $records ) = $fulltext | ||
413 | 408 | =~ m/^But in child table [^,]*, in index `$n`, (?:the record is not available|there is a record:\n(.*))?/ms; | ||
414 | 409 | $section->{'child_index'} = $search_index; | ||
415 | 410 | } | ||
416 | 411 | if ( $records && $full ) { | ||
417 | 412 | $section->{'records'} | ||
418 | 413 | = parse_innodb_record_dump( $records, $complete, $debug ); | ||
419 | 414 | } | ||
420 | 415 | else { | ||
421 | 416 | $section->{'records'} = ''; | ||
422 | 417 | } | ||
423 | 418 | } | ||
424 | 419 | } | ||
425 | 420 | |||
426 | 421 | # Parse out the tuple trying to be updated, deleted or inserted. | ||
427 | 422 | my ( $trigger ) = $fulltext =~ m/^(DATA TUPLE: \d+ fields;\n.*)$/m; | ||
428 | 423 | if ( $trigger ) { | ||
429 | 424 | $section->{'trigger'} = parse_innodb_record_dump( $trigger, $complete, $debug ); | ||
430 | 425 | } | ||
431 | 426 | |||
432 | 427 | # Certain data may not be present. Make them '' if not present. | ||
433 | 428 | map { $section->{$_} ||= "" } | ||
434 | 429 | qw(child_index fk_name col_name parent_table parent_col); | ||
435 | 430 | } | ||
436 | 431 | |||
437 | 432 | # There are new-style and old-style record formats. See rem/rem0rec.c | ||
438 | 433 | # TODO: write some tests for this | ||
439 | 434 | sub parse_innodb_record_dump { | ||
440 | 435 | my ( $dump, $complete, $debug ) = @_; | ||
441 | 436 | return undef unless $dump; | ||
442 | 437 | |||
443 | 438 | my $result = {}; | ||
444 | 439 | |||
445 | 440 | if ( $dump =~ m/PHYSICAL RECORD/ ) { | ||
446 | 441 | my $style = $dump =~ m/compact format/ ? 'new' : 'old'; | ||
447 | 442 | $result->{'style'} = $style; | ||
448 | 443 | |||
449 | 444 | # This is a new-style record. | ||
450 | 445 | if ( $style eq 'new' ) { | ||
451 | 446 | @{$result}{qw( heap_no type num_fields info_bits )} | ||
452 | 447 | = $dump | ||
453 | 448 | =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; compact format; info bits $d$/m; | ||
454 | 449 | } | ||
455 | 450 | |||
456 | 451 | # OK, it's old-style. Unfortunately there are variations here too. | ||
457 | 452 | elsif ( $dump =~ m/-byte offs / ) { | ||
458 | 453 | # Older-old style. | ||
459 | 454 | @{$result}{qw( heap_no type num_fields byte_offset info_bits )} | ||
460 | 455 | = $dump | ||
461 | 456 | =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offs [A-Z]+; info bits $d$/m; | ||
462 | 457 | if ( $dump !~ m/-byte offs TRUE/ ) { | ||
463 | 458 | $result->{'byte_offset'} = 0; | ||
464 | 459 | } | ||
465 | 460 | } | ||
466 | 461 | else { | ||
467 | 462 | # Newer-old style. | ||
468 | 463 | @{$result}{qw( heap_no type num_fields byte_offset info_bits )} | ||
469 | 464 | = $dump | ||
470 | 465 | =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offsets; info bits $d$/m; | ||
471 | 466 | } | ||
472 | 467 | |||
473 | 468 | } | ||
474 | 469 | else { | ||
475 | 470 | $result->{'style'} = 'tuple'; | ||
476 | 471 | @{$result}{qw( type num_fields )} | ||
477 | 472 | = $dump =~ m/^(DATA TUPLE): $d fields;$/m; | ||
478 | 473 | } | ||
479 | 474 | |||
480 | 475 | # Fill in default values for things that couldn't be parsed. | ||
481 | 476 | map { $result->{$_} ||= 0 } | ||
482 | 477 | qw(heap_no num_fields byte_offset info_bits); | ||
483 | 478 | map { $result->{$_} ||= '' } | ||
484 | 479 | qw(style type ); | ||
485 | 480 | |||
486 | 481 | my @fields = $dump =~ m/ (\d+:.*?;?);(?=$| \d+:)/gm; | ||
487 | 482 | $result->{'fields'} = [ map { parse_field($_, $complete, $debug ) } @fields ]; | ||
488 | 483 | |||
489 | 484 | return $result; | ||
490 | 485 | } | ||
491 | 486 | |||
492 | 487 | # New/old-style applies here. See rem/rem0rec.c | ||
493 | 488 | # $text should not include the leading space or the second trailing semicolon. | ||
494 | 489 | sub parse_field { | ||
495 | 490 | my ( $text, $complete, $debug ) = @_; | ||
496 | 491 | |||
497 | 492 | # Sample fields: | ||
498 | 493 | # '4: SQL NULL, size 4 ' | ||
499 | 494 | # '1: len 6; hex 000000005601; asc V ;' | ||
500 | 495 | # '6: SQL NULL' | ||
501 | 496 | # '5: len 30; hex 687474703a2f2f7777772e737765657477617465722e636f6d2f73746f72; asc http://www.sweetwater.com/stor;...(truncated)' | ||
502 | 497 | my ( $id, $nullsize, $len, $hex, $asc, $truncated ); | ||
503 | 498 | ( $id, $nullsize ) = $text =~ m/^$d: SQL NULL, size $d $/; | ||
504 | 499 | if ( !defined($id) ) { | ||
505 | 500 | ( $id ) = $text =~ m/^$d: SQL NULL$/; | ||
506 | 501 | } | ||
507 | 502 | if ( !defined($id) ) { | ||
508 | 503 | ( $id, $len, $hex, $asc, $truncated ) | ||
509 | 504 | = $text =~ m/^$d: len $d; hex $h; asc (.*);(\.\.\.\(truncated\))?$/; | ||
510 | 505 | } | ||
511 | 506 | |||
512 | 507 | die "Could not parse this field: '$text'" unless defined $id; | ||
513 | 508 | return { | ||
514 | 509 | id => $id, | ||
515 | 510 | len => defined($len) ? $len : defined($nullsize) ? $nullsize : 0, | ||
516 | 511 | 'hex' => defined($hex) ? $hex : '', | ||
517 | 512 | asc => defined($asc) ? $asc : '', | ||
518 | 513 | trunc => $truncated ? 1 : 0, | ||
519 | 514 | }; | ||
520 | 515 | |||
521 | 516 | } | ||
522 | 517 | |||
523 | 518 | sub parse_dl_section { | ||
524 | 519 | my ( $dl, $complete, $debug, $full ) = @_; | ||
525 | 520 | return unless $dl; | ||
526 | 521 | my $fulltext = $dl->{'fulltext'}; | ||
527 | 522 | return 0 unless $fulltext; | ||
528 | 523 | |||
529 | 524 | my ( $ts ) = $fulltext =~ m/^$s$/m; | ||
530 | 525 | return 0 unless $ts; | ||
531 | 526 | |||
532 | 527 | $dl->{'ts'} = [ parse_innodb_timestamp( $ts ) ]; | ||
533 | 528 | $dl->{'timestring'} = ts_to_string($dl->{'ts'}); | ||
534 | 529 | $dl->{'txns'} = {}; | ||
535 | 530 | |||
536 | 531 | my @sections | ||
537 | 532 | = $fulltext | ||
538 | 533 | =~ m{ | ||
539 | 534 | ^\*{3}\s([^\n]*) # *** (1) WAITING FOR THIS... | ||
540 | 535 | (.*?) # Followed by anything, non-greedy | ||
541 | 536 | (?=(?:^\*{3})|\z) # Followed by another three stars or EOF | ||
542 | 537 | }gmsx; | ||
543 | 538 | |||
544 | 539 | |||
545 | 540 | # Loop through each section. There are no assumptions about how many | ||
546 | 541 | # there are, who holds and wants what locks, and who gets rolled back. | ||
547 | 542 | while ( my ($header, $body) = splice(@sections, 0, 2) ) { | ||
548 | 543 | my ( $txn_id, $what ) = $header =~ m/^\($d\) (.*):$/; | ||
549 | 544 | next unless $txn_id; | ||
550 | 545 | $dl->{'txns'}->{$txn_id} ||= {}; | ||
551 | 546 | my $txn = $dl->{'txns'}->{$txn_id}; | ||
552 | 547 | |||
553 | 548 | if ( $what eq 'TRANSACTION' ) { | ||
554 | 549 | $txn->{'tx'} = parse_tx_text( $body, $complete, $debug, $full ); | ||
555 | 550 | } | ||
556 | 551 | else { | ||
557 | 552 | push @{$txn->{'locks'}}, parse_innodb_record_locks( $body, $complete, $debug, $full ); | ||
558 | 553 | } | ||
559 | 554 | } | ||
560 | 555 | |||
561 | 556 | @{ $dl }{ qw(rolled_back) } | ||
562 | 557 | = $fulltext =~ m/^\*\*\* WE ROLL BACK TRANSACTION \($d\)$/m; | ||
563 | 558 | |||
564 | 559 | # Make sure certain values aren't undef | ||
565 | 560 | map { $dl->{$_} ||= '' } qw(rolled_back); | ||
566 | 561 | |||
567 | 562 | delete $dl->{'fulltext'} unless $debug; | ||
568 | 563 | return 1; | ||
569 | 564 | } | ||
570 | 565 | |||
571 | 566 | sub parse_innodb_record_locks { | ||
572 | 567 | my ( $text, $complete, $debug, $full ) = @_; | ||
573 | 568 | my @result; | ||
574 | 569 | |||
575 | 570 | foreach my $lock ( $text =~ m/(^(?:RECORD|TABLE) LOCKS?.*$)/gm ) { | ||
576 | 571 | my $hash = {}; | ||
577 | 572 | @{$hash}{ qw(lock_type space_id page_no n_bits index db table txn_id lock_mode) } | ||
578 | 573 | = $lock | ||
579 | 574 | =~ m{^(RECORD|TABLE) LOCKS? (?:space id $d page no $d n bits $d index `?$n`? of )?table `$n(?:/|`\.`)$n` trx id $t lock.mode (\S+)}m; | ||
580 | 575 | ( $hash->{'special'} ) | ||
581 | 576 | = $lock =~ m/^(?:RECORD|TABLE) .*? locks (rec but not gap|gap before rec)/m; | ||
582 | 577 | $hash->{'insert_intention'} | ||
583 | 578 | = $lock =~ m/^(?:RECORD|TABLE) .*? insert intention/m ? 1 : 0; | ||
584 | 579 | $hash->{'waiting'} | ||
585 | 580 | = $lock =~ m/^(?:RECORD|TABLE) .*? waiting/m ? 1 : 0; | ||
586 | 581 | |||
587 | 582 | # Some things may not be in the text, so make sure they are not | ||
588 | 583 | # undef. | ||
589 | 584 | map { $hash->{$_} ||= 0 } qw(n_bits page_no space_id); | ||
590 | 585 | map { $hash->{$_} ||= "" } qw(index special); | ||
591 | 586 | push @result, $hash; | ||
592 | 587 | } | ||
593 | 588 | |||
594 | 589 | return @result; | ||
595 | 590 | } | ||
596 | 591 | |||
597 | 592 | sub parse_tx_text { | ||
598 | 593 | my ( $txn, $complete, $debug, $full ) = @_; | ||
599 | 594 | |||
600 | 595 | my ( $txn_id, $txn_status, $active_secs, $proc_no, $os_thread_id ) | ||
601 | 596 | = $txn | ||
602 | 597 | =~ m/^(?:---)?TRANSACTION $t, (\D*?)(?: $d sec)?, (?:process no $d, )?OS thread id $d/m; | ||
603 | 598 | my ( $thread_status, $thread_decl_inside ) | ||
604 | 599 | = $txn | ||
605 | 600 | =~ m/OS thread id \d+(?: ([^,]+?))?(?:, thread declared inside InnoDB $d)?$/m; | ||
606 | 601 | |||
607 | 602 | # Parsing the line that begins 'MySQL thread id' is complicated. The only | ||
608 | 603 | # thing always in the line is the thread and query id. See function | ||
609 | 604 | # innobase_mysql_print_thd in InnoDB source file sql/ha_innodb.cc. | ||
610 | 605 | my ( $thread_line ) = $txn =~ m/^(MySQL thread id .*)$/m; | ||
611 | 606 | my ( $mysql_thread_id, $query_id, $hostname, $ip, $user, $query_status ); | ||
612 | 607 | |||
613 | 608 | if ( $thread_line ) { | ||
614 | 609 | # These parts can always be gotten. | ||
615 | 610 | ( $mysql_thread_id, $query_id ) = $thread_line =~ m/^MySQL thread id $d, query id $d/m; | ||
616 | 611 | |||
617 | 612 | # If it's a master/slave thread, "Has (read|sent) all" may be the thread's | ||
618 | 613 | # proc_info. In these cases, there won't be any host/ip/user info | ||
619 | 614 | ( $query_status ) = $thread_line =~ m/(Has (?:read|sent) all .*$)/m; | ||
620 | 615 | if ( defined($query_status) ) { | ||
621 | 616 | $user = 'system user'; | ||
622 | 617 | } | ||
623 | 618 | |||
624 | 619 | # It may be the case that the query id is the last thing in the line. | ||
625 | 620 | elsif ( $thread_line =~ m/query id \d+ / ) { | ||
626 | 621 | # The IP address is the only non-word thing left, so it's the most | ||
627 | 622 | # useful marker for where I have to start guessing. | ||
628 | 623 | ( $hostname, $ip ) = $thread_line =~ m/query id \d+(?: ([A-Za-z]\S+))? $i/m; | ||
629 | 624 | if ( defined $ip ) { | ||
630 | 625 | ( $user, $query_status ) = $thread_line =~ m/$ip $w(?: (.*))?$/; | ||
631 | 626 | } | ||
632 | 627 | else { # OK, there wasn't an IP address. | ||
633 | 628 | # There might not be ANYTHING except the query status. | ||
634 | 629 | ( $query_status ) = $thread_line =~ m/query id \d+ (.*)$/; | ||
635 | 630 | if ( $query_status !~ m/^\w+ing/ && !exists($is_proc_info{$query_status}) ) { | ||
636 | 631 | # The remaining tokens are, in order: hostname, user, query_status. | ||
637 | 632 | # It's basically impossible to know which is which. | ||
638 | 633 | ( $hostname, $user, $query_status ) = $thread_line | ||
639 | 634 | =~ m/query id \d+(?: ([A-Za-z]\S+))?(?: $w(?: (.*))?)?$/m; | ||
640 | 635 | } | ||
641 | 636 | else { | ||
642 | 637 | $user = 'system user'; | ||
643 | 638 | } | ||
644 | 639 | } | ||
645 | 640 | } | ||
646 | 641 | } | ||
647 | 642 | |||
648 | 643 | my ( $lock_wait_status, $lock_structs, $heap_size, $row_locks, $undo_log_entries ) | ||
649 | 644 | = $txn | ||
650 | 645 | =~ m/^(?:(\D*) )?$d lock struct\(s\), heap size $d(?:, $d row lock\(s\))?(?:, undo log entries $d)?$/m; | ||
651 | 646 | my ( $lock_wait_time ) | ||
652 | 647 | = $txn | ||
653 | 648 | =~ m/^------- TRX HAS BEEN WAITING $d SEC/m; | ||
654 | 649 | |||
655 | 650 | my $locks; | ||
656 | 651 | # If the transaction has locks, grab the locks. | ||
657 | 652 | if ( $txn =~ m/^TABLE LOCK|RECORD LOCKS/ ) { | ||
658 | 653 | $locks = [parse_innodb_record_locks($txn, $complete, $debug, $full)]; | ||
659 | 654 | } | ||
660 | 655 | |||
661 | 656 | my ( $tables_in_use, $tables_locked ) | ||
662 | 657 | = $txn | ||
663 | 658 | =~ m/^mysql tables in use $d, locked $d$/m; | ||
664 | 659 | my ( $txn_doesnt_see_ge, $txn_sees_lt ) | ||
665 | 660 | = $txn | ||
666 | 661 | =~ m/^Trx read view will not see trx with id >= $t, sees < $t$/m; | ||
667 | 662 | my $has_read_view = defined($txn_doesnt_see_ge); | ||
668 | 663 | # Only a certain number of bytes of the query text are included here, at least | ||
669 | 664 | # under some circumstances. Some versions include 300, some 600. | ||
670 | 665 | my ( $query_text ) | ||
671 | 666 | = $txn | ||
672 | 667 | =~ m{ | ||
673 | 668 | ^MySQL\sthread\sid\s[^\n]+\n # This comes before the query text | ||
674 | 669 | (.*?) # The query text | ||
675 | 670 | (?= # Followed by any of... | ||
676 | 671 | ^Trx\sread\sview | ||
677 | 672 | |^-------\sTRX\sHAS\sBEEN\sWAITING | ||
678 | 673 | |^TABLE\sLOCK | ||
679 | 674 | |^RECORD\sLOCKS\sspace\sid | ||
680 | 675 | |^(?:---)?TRANSACTION | ||
681 | 676 | |^\*\*\*\s\(\d\) | ||
682 | 677 | |\Z | ||
683 | 678 | ) | ||
684 | 679 | }xms; | ||
685 | 680 | if ( $query_text ) { | ||
686 | 681 | $query_text =~ s/\s+$//; | ||
687 | 682 | } | ||
688 | 683 | else { | ||
689 | 684 | $query_text = ''; | ||
690 | 685 | } | ||
691 | 686 | |||
692 | 687 | my %stuff = ( | ||
693 | 688 | active_secs => $active_secs, | ||
694 | 689 | has_read_view => $has_read_view, | ||
695 | 690 | heap_size => $heap_size, | ||
696 | 691 | hostname => $hostname, | ||
697 | 692 | ip => $ip, | ||
698 | 693 | lock_structs => $lock_structs, | ||
699 | 694 | lock_wait_status => $lock_wait_status, | ||
700 | 695 | lock_wait_time => $lock_wait_time, | ||
701 | 696 | mysql_thread_id => $mysql_thread_id, | ||
702 | 697 | os_thread_id => $os_thread_id, | ||
703 | 698 | proc_no => $proc_no, | ||
704 | 699 | query_id => $query_id, | ||
705 | 700 | query_status => $query_status, | ||
706 | 701 | query_text => $query_text, | ||
707 | 702 | row_locks => $row_locks, | ||
708 | 703 | tables_in_use => $tables_in_use, | ||
709 | 704 | tables_locked => $tables_locked, | ||
710 | 705 | thread_decl_inside => $thread_decl_inside, | ||
711 | 706 | thread_status => $thread_status, | ||
712 | 707 | txn_doesnt_see_ge => $txn_doesnt_see_ge, | ||
713 | 708 | txn_id => $txn_id, | ||
714 | 709 | txn_sees_lt => $txn_sees_lt, | ||
715 | 710 | txn_status => $txn_status, | ||
716 | 711 | undo_log_entries => $undo_log_entries, | ||
717 | 712 | user => $user, | ||
718 | 713 | ); | ||
719 | 714 | $stuff{'fulltext'} = $txn if $debug; | ||
720 | 715 | $stuff{'locks'} = $locks if $locks; | ||
721 | 716 | |||
722 | 717 | # Some things may not be in the txn text, so make sure they are not | ||
723 | 718 | # undef. | ||
724 | 719 | map { $stuff{$_} ||= 0 } qw(active_secs heap_size lock_structs | ||
725 | 720 | tables_in_use undo_log_entries tables_locked has_read_view | ||
726 | 721 | thread_decl_inside lock_wait_time proc_no row_locks); | ||
727 | 722 | map { $stuff{$_} ||= "" } qw(thread_status txn_doesnt_see_ge | ||
728 | 723 | txn_sees_lt query_status ip query_text lock_wait_status user); | ||
729 | 724 | $stuff{'hostname'} ||= $stuff{'ip'}; | ||
730 | 725 | |||
731 | 726 | return \%stuff; | ||
732 | 727 | } | ||
733 | 728 | |||
734 | 729 | sub parse_tx_section { | ||
735 | 730 | my ( $section, $complete, $debug, $full ) = @_; | ||
736 | 731 | return unless $section && $section->{'fulltext'}; | ||
737 | 732 | my $fulltext = $section->{'fulltext'}; | ||
738 | 733 | $section->{'transactions'} = []; | ||
739 | 734 | |||
740 | 735 | # Handle the individual transactions | ||
741 | 736 | my @transactions = $fulltext =~ m/(---TRANSACTION \d.*?)(?=\n---TRANSACTION|$)/gs; | ||
742 | 737 | foreach my $txn ( @transactions ) { | ||
743 | 738 | my $stuff = parse_tx_text( $txn, $complete, $debug, $full ); | ||
744 | 739 | delete $stuff->{'fulltext'} unless $debug; | ||
745 | 740 | push @{$section->{'transactions'}}, $stuff; | ||
746 | 741 | } | ||
747 | 742 | |||
748 | 743 | # Handle the general info | ||
749 | 744 | @{$section}{ 'trx_id_counter' } | ||
750 | 745 | = $fulltext =~ m/^Trx id counter $t$/m; | ||
751 | 746 | @{$section}{ 'purge_done_for', 'purge_undo_for' } | ||
752 | 747 | = $fulltext =~ m/^Purge done for trx's n:o < $t undo n:o < $t$/m; | ||
753 | 748 | @{$section}{ 'history_list_len' } # This isn't present in some 4.x versions | ||
754 | 749 | = $fulltext =~ m/^History list length $d$/m; | ||
755 | 750 | @{$section}{ 'num_lock_structs' } | ||
756 | 751 | = $fulltext =~ m/^Total number of lock structs in row lock hash table $d$/m; | ||
757 | 752 | @{$section}{ 'is_truncated' } | ||
758 | 753 | = $fulltext =~ m/^\.\.\. truncated\.\.\.$/m ? 1 : 0; | ||
759 | 754 | |||
760 | 755 | # Fill in things that might not be present | ||
761 | 756 | foreach ( qw(history_list_len) ) { | ||
762 | 757 | $section->{$_} ||= 0; | ||
763 | 758 | } | ||
764 | 759 | |||
765 | 760 | delete $section->{'fulltext'} unless $debug; | ||
766 | 761 | return 1; | ||
767 | 762 | } | ||
768 | 763 | |||
769 | 764 | # I've read the source for this section. | ||
770 | 765 | sub parse_ro_section { | ||
771 | 766 | my ( $section, $complete, $debug, $full ) = @_; | ||
772 | 767 | return unless $section && $section->{'fulltext'}; | ||
773 | 768 | my $fulltext = $section->{'fulltext'}; | ||
774 | 769 | |||
775 | 770 | # Grab the info | ||
776 | 771 | @{$section}{ 'queries_inside', 'queries_in_queue' } | ||
777 | 772 | = $fulltext =~ m/^$d queries inside InnoDB, $d queries in queue$/m; | ||
778 | 773 | ( $section->{ 'read_views_open' } ) | ||
779 | 774 | = $fulltext =~ m/^$d read views open inside InnoDB$/m; | ||
780 | 775 | ( $section->{ 'n_reserved_extents' } ) | ||
781 | 776 | = $fulltext =~ m/^$d tablespace extents now reserved for B-tree/m; | ||
782 | 777 | @{$section}{ 'main_thread_proc_no', 'main_thread_id', 'main_thread_state' } | ||
783 | 778 | = $fulltext =~ m/^Main thread (?:process no. $d, )?id $d, state: (.*)$/m; | ||
784 | 779 | @{$section}{ 'num_rows_ins', 'num_rows_upd', 'num_rows_del', 'num_rows_read' } | ||
785 | 780 | = $fulltext =~ m/^Number of rows inserted $d, updated $d, deleted $d, read $d$/m; | ||
786 | 781 | @{$section}{ 'ins_sec', 'upd_sec', 'del_sec', 'read_sec' } | ||
787 | 782 | = $fulltext =~ m#^$f inserts/s, $f updates/s, $f deletes/s, $f reads/s$#m; | ||
788 | 783 | $section->{'main_thread_proc_no'} ||= 0; | ||
789 | 784 | |||
790 | 785 | map { $section->{$_} ||= 0 } qw(read_views_open n_reserved_extents); | ||
791 | 786 | delete $section->{'fulltext'} unless $debug; | ||
792 | 787 | return 1; | ||
793 | 788 | } | ||
794 | 789 | |||
795 | 790 | sub parse_lg_section { | ||
796 | 791 | my ( $section, $complete, $debug, $full ) = @_; | ||
797 | 792 | return unless $section; | ||
798 | 793 | my $fulltext = $section->{'fulltext'}; | ||
799 | 794 | |||
800 | 795 | # Grab the info | ||
801 | 796 | ( $section->{ 'log_seq_no' } ) | ||
802 | 797 | = $fulltext =~ m/Log sequence number \s*(\d.*)$/m; | ||
803 | 798 | ( $section->{ 'log_flushed_to' } ) | ||
804 | 799 | = $fulltext =~ m/Log flushed up to \s*(\d.*)$/m; | ||
805 | 800 | ( $section->{ 'last_chkp' } ) | ||
806 | 801 | = $fulltext =~ m/Last checkpoint at \s*(\d.*)$/m; | ||
807 | 802 | @{$section}{ 'pending_log_writes', 'pending_chkp_writes' } | ||
808 | 803 | = $fulltext =~ m/$d pending log writes, $d pending chkp writes/; | ||
809 | 804 | @{$section}{ 'log_ios_done', 'log_ios_s' } | ||
810 | 805 | = $fulltext =~ m#$d log i/o's done, $f log i/o's/second#; | ||
811 | 806 | |||
812 | 807 | delete $section->{'fulltext'} unless $debug; | ||
813 | 808 | return 1; | ||
814 | 809 | } | ||
815 | 810 | |||
816 | 811 | sub parse_ib_section { | ||
817 | 812 | my ( $section, $complete, $debug, $full ) = @_; | ||
818 | 813 | return unless $section && $section->{'fulltext'}; | ||
819 | 814 | my $fulltext = $section->{'fulltext'}; | ||
820 | 815 | |||
821 | 816 | # Some servers will output ibuf information for tablespace 0, as though there | ||
822 | 817 | # might be many tablespaces with insert buffers. (In practice I believe | ||
823 | 818 | # the source code shows there will only ever be one). I have to parse both | ||
824 | 819 | # cases here, but I assume there will only be one. | ||
825 | 820 | @{$section}{ 'size', 'free_list_len', 'seg_size' } | ||
826 | 821 | = $fulltext =~ m/^Ibuf(?: for space 0)?: size $d, free list len $d, seg size $d,$/m; | ||
827 | 822 | @{$section}{ 'inserts', 'merged_recs', 'merges' } | ||
828 | 823 | = $fulltext =~ m/^$d inserts, $d merged recs, $d merges$/m; | ||
829 | 824 | |||
830 | 825 | @{$section}{ 'hash_table_size', 'used_cells', 'bufs_in_node_heap' } | ||
831 | 826 | = $fulltext =~ m/^Hash table size $d, used cells $d, node heap has $d buffer\(s\)$/m; | ||
832 | 827 | @{$section}{ 'hash_searches_s', 'non_hash_searches_s' } | ||
833 | 828 | = $fulltext =~ m{^$f hash searches/s, $f non-hash searches/s$}m; | ||
834 | 829 | |||
835 | 830 | delete $section->{'fulltext'} unless $debug; | ||
836 | 831 | return 1; | ||
837 | 832 | } | ||
838 | 833 | |||
839 | 834 | sub parse_wait_array { | ||
840 | 835 | my ( $text, $complete, $debug, $full ) = @_; | ||
841 | 836 | my %result; | ||
842 | 837 | |||
843 | 838 | @result{ qw(thread waited_at_filename waited_at_line waited_secs) } | ||
844 | 839 | = $text =~ m/^--Thread $d has waited at $fl for $f seconds/m; | ||
845 | 840 | |||
846 | 841 | # Depending on whether it's a SYNC_MUTEX,RW_LOCK_EX,RW_LOCK_SHARED, | ||
847 | 842 | # there will be different text output | ||
848 | 843 | if ( $text =~ m/^Mutex at/m ) { | ||
849 | 844 | $result{'request_type'} = 'M'; | ||
850 | 845 | @result{ qw( lock_mem_addr lock_cfile_name lock_cline lock_var) } | ||
851 | 846 | = $text =~ m/^Mutex at $h created file $fl, lock var $d$/m; | ||
852 | 847 | @result{ qw( waiters_flag )} | ||
853 | 848 | = $text =~ m/^waiters flag $d$/m; | ||
854 | 849 | } | ||
855 | 850 | else { | ||
856 | 851 | @result{ qw( request_type lock_mem_addr lock_cfile_name lock_cline) } | ||
857 | 852 | = $text =~ m/^(.)-lock on RW-latch at $h created in file $fl$/m; | ||
858 | 853 | @result{ qw( writer_thread writer_lock_mode ) } | ||
859 | 854 | = $text =~ m/^a writer \(thread id $d\) has reserved it in mode (.*)$/m; | ||
860 | 855 | @result{ qw( num_readers waiters_flag )} | ||
861 | 856 | = $text =~ m/^number of readers $d, waiters flag $d$/m; | ||
862 | 857 | @result{ qw(last_s_file_name last_s_line ) } | ||
863 | 858 | = $text =~ m/Last time read locked in file $fl$/m; | ||
864 | 859 | @result{ qw(last_x_file_name last_x_line ) } | ||
865 | 860 | = $text =~ m/Last time write locked in file $fl$/m; | ||
866 | 861 | } | ||
867 | 862 | |||
868 | 863 | $result{'cell_waiting'} = $text =~ m/^wait has ended$/m ? 0 : 1; | ||
869 | 864 | $result{'cell_event_set'} = $text =~ m/^wait is ending$/m ? 1 : 0; | ||
870 | 865 | |||
871 | 866 | # Because there are two code paths, some things won't get set. | ||
872 | 867 | map { $result{$_} ||= '' } | ||
873 | 868 | qw(last_s_file_name last_x_file_name writer_lock_mode); | ||
874 | 869 | map { $result{$_} ||= 0 } | ||
875 | 870 | qw(num_readers lock_var last_s_line last_x_line writer_thread); | ||
876 | 871 | |||
877 | 872 | return \%result; | ||
878 | 873 | } | ||
879 | 874 | |||
880 | 875 | sub parse_sm_section { | ||
881 | 876 | my ( $section, $complete, $debug, $full ) = @_; | ||
882 | 877 | return 0 unless $section && $section->{'fulltext'}; | ||
883 | 878 | my $fulltext = $section->{'fulltext'}; | ||
884 | 879 | |||
885 | 880 | # Grab the info | ||
886 | 881 | @{$section}{ 'reservation_count', 'signal_count' } | ||
887 | 882 | = $fulltext =~ m/^OS WAIT ARRAY INFO: reservation count $d, signal count $d$/m; | ||
888 | 883 | @{$section}{ 'mutex_spin_waits', 'mutex_spin_rounds', 'mutex_os_waits' } | ||
889 | 884 | = $fulltext =~ m/^Mutex spin waits $d, rounds $d, OS waits $d$/m; | ||
890 | 885 | @{$section}{ 'rw_shared_spins', 'rw_shared_os_waits', 'rw_excl_spins', 'rw_excl_os_waits' } | ||
891 | 886 | = $fulltext =~ m/^RW-shared spins $d, OS waits $d; RW-excl spins $d, OS waits $d$/m; | ||
892 | 887 | |||
893 | 888 | # Look for info on waits. | ||
894 | 889 | my @waits = $fulltext =~ m/^(--Thread.*?)^(?=Mutex spin|--Thread)/gms; | ||
895 | 890 | $section->{'waits'} = [ map { parse_wait_array($_, $complete, $debug) } @waits ]; | ||
896 | 891 | $section->{'wait_array_size'} = scalar(@waits); | ||
897 | 892 | |||
898 | 893 | delete $section->{'fulltext'} unless $debug; | ||
899 | 894 | return 1; | ||
900 | 895 | } | ||
901 | 896 | |||
902 | 897 | # I've read the source for this section. | ||
903 | 898 | sub parse_bp_section { | ||
904 | 899 | my ( $section, $complete, $debug, $full ) = @_; | ||
905 | 900 | return unless $section && $section->{'fulltext'}; | ||
906 | 901 | my $fulltext = $section->{'fulltext'}; | ||
907 | 902 | |||
908 | 903 | # Grab the info | ||
909 | 904 | @{$section}{ 'total_mem_alloc', 'add_pool_alloc' } | ||
910 | 905 | = $fulltext =~ m/^Total memory allocated $d; in additional pool allocated $d$/m; | ||
911 | 906 | @{$section}{'dict_mem_alloc'} = $fulltext =~ m/Dictionary memory allocated $d/; | ||
912 | 907 | @{$section}{'awe_mem_alloc'} = $fulltext =~ m/$d MB of AWE memory/; | ||
913 | 908 | @{$section}{'buf_pool_size'} = $fulltext =~ m/^Buffer pool size\s*$d$/m; | ||
914 | 909 | @{$section}{'buf_free'} = $fulltext =~ m/^Free buffers\s*$d$/m; | ||
915 | 910 | @{$section}{'pages_total'} = $fulltext =~ m/^Database pages\s*$d$/m; | ||
916 | 911 | @{$section}{'pages_modified'} = $fulltext =~ m/^Modified db pages\s*$d$/m; | ||
917 | 912 | @{$section}{'pages_read', 'pages_created', 'pages_written'} | ||
918 | 913 | = $fulltext =~ m/^Pages read $d, created $d, written $d$/m; | ||
919 | 914 | @{$section}{'page_reads_sec', 'page_creates_sec', 'page_writes_sec'} | ||
920 | 915 | = $fulltext =~ m{^$f reads/s, $f creates/s, $f writes/s$}m; | ||
921 | 916 | @{$section}{'buf_pool_hits', 'buf_pool_reads'} | ||
922 | 917 | = $fulltext =~ m{Buffer pool hit rate $d / $d$}m; | ||
923 | 918 | if ($fulltext =~ m/^No buffer pool page gets since the last printout$/m) { | ||
924 | 919 | @{$section}{'buf_pool_hits', 'buf_pool_reads'} = (0, 0); | ||
925 | 920 | @{$section}{'buf_pool_hit_rate'} = '--'; | ||
926 | 921 | } | ||
927 | 922 | else { | ||
928 | 923 | @{$section}{'buf_pool_hit_rate'} | ||
929 | 924 | = $fulltext =~ m{Buffer pool hit rate (\d+ / \d+)$}m; | ||
930 | 925 | } | ||
931 | 926 | @{$section}{'reads_pending'} = $fulltext =~ m/^Pending reads $d/m; | ||
932 | 927 | @{$section}{'writes_pending_lru', 'writes_pending_flush_list', 'writes_pending_single_page' } | ||
933 | 928 | = $fulltext =~ m/^Pending writes: LRU $d, flush list $d, single page $d$/m; | ||
934 | 929 | |||
935 | 930 | map { $section->{$_} ||= 0 } | ||
936 | 931 | qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page | ||
937 | 932 | awe_mem_alloc dict_mem_alloc); | ||
938 | 933 | @{$section}{'writes_pending'} = List::Util::sum( | ||
939 | 934 | @{$section}{ qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page) }); | ||
940 | 935 | |||
941 | 936 | delete $section->{'fulltext'} unless $debug; | ||
942 | 937 | return 1; | ||
943 | 938 | } | ||
944 | 939 | |||
945 | 940 | # I've read the source for this. | ||
946 | 941 | sub parse_io_section { | ||
947 | 942 | my ( $section, $complete, $debug, $full ) = @_; | ||
948 | 943 | return unless $section && $section->{'fulltext'}; | ||
949 | 944 | my $fulltext = $section->{'fulltext'}; | ||
950 | 945 | $section->{'threads'} = {}; | ||
951 | 946 | |||
952 | 947 | # Grab the I/O thread info | ||
953 | 948 | my @threads = $fulltext =~ m<^(I/O thread \d+ .*)$>gm; | ||
954 | 949 | foreach my $thread (@threads) { | ||
955 | 950 | my ( $tid, $state, $purpose, $event_set ) | ||
956 | 951 | = $thread =~ m{I/O thread $d state: (.+?) \((.*)\)(?: ev set)?$}m; | ||
957 | 952 | if ( defined $tid ) { | ||
958 | 953 | $section->{'threads'}->{$tid} = { | ||
959 | 954 | thread => $tid, | ||
960 | 955 | state => $state, | ||
961 | 956 | purpose => $purpose, | ||
962 | 957 | event_set => $event_set ? 1 : 0, | ||
963 | 958 | }; | ||
964 | 959 | } | ||
965 | 960 | } | ||
966 | 961 | |||
967 | 962 | # Grab the reads/writes/flushes info | ||
968 | 963 | @{$section}{ 'pending_normal_aio_reads', 'pending_aio_writes' } | ||
969 | 964 | = $fulltext =~ m/^Pending normal aio reads: $d, aio writes: $d,$/m; | ||
970 | 965 | @{$section}{ 'pending_ibuf_aio_reads', 'pending_log_ios', 'pending_sync_ios' } | ||
971 | 966 | = $fulltext =~ m{^ ibuf aio reads: $d, log i/o's: $d, sync i/o's: $d$}m; | ||
972 | 967 | @{$section}{ 'flush_type', 'pending_log_flushes', 'pending_buffer_pool_flushes' } | ||
973 | 968 | = $fulltext =~ m/^Pending flushes \($w\) log: $d; buffer pool: $d$/m; | ||
974 | 969 | @{$section}{ 'os_file_reads', 'os_file_writes', 'os_fsyncs' } | ||
975 | 970 | = $fulltext =~ m/^$d OS file reads, $d OS file writes, $d OS fsyncs$/m; | ||
976 | 971 | @{$section}{ 'reads_s', 'avg_bytes_s', 'writes_s', 'fsyncs_s' } | ||
977 | 972 | = $fulltext =~ m{^$f reads/s, $d avg bytes/read, $f writes/s, $f fsyncs/s$}m; | ||
978 | 973 | @{$section}{ 'pending_preads', 'pending_pwrites' } | ||
979 | 974 | = $fulltext =~ m/$d pending preads, $d pending pwrites$/m; | ||
980 | 975 | @{$section}{ 'pending_preads', 'pending_pwrites' } = (0, 0) | ||
981 | 976 | unless defined($section->{'pending_preads'}); | ||
982 | 977 | |||
983 | 978 | delete $section->{'fulltext'} unless $debug; | ||
984 | 979 | return 1; | ||
985 | 980 | } | ||
986 | 981 | |||
987 | 982 | sub _debug { | ||
988 | 983 | my ( $debug, $msg ) = @_; | ||
989 | 984 | if ( $debug ) { | ||
990 | 985 | die $msg; | ||
991 | 986 | } | ||
992 | 987 | else { | ||
993 | 988 | warn $msg; | ||
994 | 989 | } | ||
995 | 990 | return 1; | ||
996 | 991 | } | ||
997 | 992 | |||
998 | 993 | 1; | ||
999 | 994 | |||
1000 | 995 | # end_of_package | ||
1001 | 996 | # ############################################################################ | ||
1002 | 997 | # Perldoc section. I put this last as per the Dog book. | ||
1003 | 998 | # ############################################################################ | ||
1004 | 999 | =pod | ||
1005 | 1000 | |||
1006 | 1001 | =head1 NAME | ||
1007 | 1002 | |||
1008 | 1003 | InnoDBParser - Parse InnoDB monitor text. | ||
1009 | 1004 | |||
1010 | 1005 | =head1 DESCRIPTION | ||
1011 | 1006 | |||
1012 | 1007 | InnoDBParser tries to parse the output of the InnoDB monitor. One way to get | ||
1013 | 1008 | this output is to connect to a MySQL server and issue the command SHOW ENGINE | ||
1014 | 1009 | INNODB STATUS (omit 'ENGINE' on earlier versions of MySQL). The goal is to | ||
1015 | 1010 | turn text into data that something else (e.g. innotop) can use. | ||
1016 | 1011 | |||
1017 | 1012 | The output comes from all over, but the place to start in the source is | ||
1018 | 1013 | innobase/srv/srv0srv.c. | ||
1019 | 1014 | |||
1020 | 1015 | =head1 SYNOPSIS | ||
1021 | 1016 | |||
1022 | 1017 | use InnoDBParser; | ||
1023 | 1018 | use DBI; | ||
1024 | 1019 | |||
1025 | 1020 | # Get the status text. | ||
1026 | 1021 | my $dbh = DBI->connect( | ||
1027 | 1022 | "DBI::mysql:test;host=localhost", | ||
1028 | 1023 | 'user', | ||
1029 | 1024 | 'password' | ||
1030 | 1025 | ); | ||
1031 | 1026 | my $query = 'SHOW /*!5 ENGINE */ INNODB STATUS'; | ||
1032 | 1027 | my $text = $dbh->selectcol_arrayref($query)->[0]; | ||
1033 | 1028 | |||
1034 | 1029 | # 1 or 0 | ||
1035 | 1030 | my $debug = 1; | ||
1036 | 1031 | |||
1037 | 1032 | # Choose sections of the monitor text you want. Possible values: | ||
1038 | 1033 | # TRANSACTIONS => tx | ||
1039 | 1034 | # BUFFER POOL AND MEMORY => bp | ||
1040 | 1035 | # SEMAPHORES => sm | ||
1041 | 1036 | # LOG => lg | ||
1042 | 1037 | # ROW OPERATIONS => ro | ||
1043 | 1038 | # INSERT BUFFER AND ADAPTIVE HASH INDEX => ib | ||
1044 | 1039 | # FILE I/O => io | ||
1045 | 1040 | # LATEST DETECTED DEADLOCK => dl | ||
1046 | 1041 | # LATEST FOREIGN KEY ERROR => fk | ||
1047 | 1042 | |||
1048 | 1043 | my $required_sections = { | ||
1049 | 1044 | tx => 1, | ||
1050 | 1045 | }; | ||
1051 | 1046 | |||
1052 | 1047 | # Parse the status text. | ||
1053 | 1048 | my $parser = InnoDBParser->new; | ||
1054 | 1049 | $innodb_status = $parser->parse_status_text( | ||
1055 | 1050 | $text, | ||
1056 | 1051 | $debug, | ||
1057 | 1052 | # Omit the following parameter to get all sections. | ||
1058 | 1053 | $required_sections, | ||
1059 | 1054 | ); | ||
1060 | 1055 | |||
1061 | 1056 | =head1 COPYRIGHT, LICENSE AND WARRANTY | ||
1062 | 1057 | |||
1063 | 1058 | This package is copyright (c) 2006 Baron Schwartz, baron at xaprb dot com. | ||
1064 | 1059 | Feedback and improvements are gratefully received. | ||
1065 | 1060 | |||
1066 | 1061 | THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED | ||
1067 | 1062 | WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF | ||
1068 | 1063 | MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
1069 | 1064 | |||
1070 | 1065 | This program is free software; you can redistribute it and/or modify it under | ||
1071 | 1066 | the terms of the GNU General Public License as published by the Free Software | ||
1072 | 1067 | Foundation, version 2; OR the Perl Artistic License. On UNIX and similar | ||
1073 | 1068 | systems, you can issue `man perlgpl' or `man perlartistic' to read these | ||
1074 | 1069 | licenses. | ||
1075 | 1070 | |||
1076 | 1071 | You should have received a copy of the GNU General Public License along with | ||
1077 | 1072 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
1078 | 1073 | Place, Suite 330, Boston, MA 02111-1307 USA | ||
1079 | 1074 | |||
1080 | 1075 | =head1 AUTHOR | ||
1081 | 1076 | |||
1082 | 1077 | Baron Schwartz, baron at xaprb dot com. | ||
1083 | 1078 | |||
1084 | 1079 | =head1 BUGS | ||
1085 | 1080 | |||
1086 | 1081 | None known, but I bet there are some. The InnoDB monitor text wasn't really | ||
1087 | 1082 | designed to be parsable. | ||
1088 | 1083 | |||
1089 | 1084 | =head1 SEE ALSO | ||
1090 | 1085 | |||
1091 | 1086 | innotop - a program that can format the parsed status information for humans | ||
1092 | 1087 | to read and enjoy. | ||
1093 | 1088 | |||
1094 | 1089 | =cut | ||
1095 | 1090 | 0 | ||
1096 | === removed file 'build/debian/additions/innotop/changelog.innotop' | |||
1097 | --- build/debian/additions/innotop/changelog.innotop 2010-05-31 13:57:21 +0000 | |||
1098 | +++ build/debian/additions/innotop/changelog.innotop 1970-01-01 00:00:00 +0000 | |||
1099 | @@ -1,318 +0,0 @@ | |||
1100 | 1 | Changelog for innotop and InnoDBParser: | ||
1101 | 2 | |||
1102 | 3 | 2007-11-09: version 1.6.0 | ||
1103 | 4 | |||
1104 | 5 | * S mode crashed on non-numeric values. | ||
1105 | 6 | * New user-defined columns crashed upon restart. | ||
1106 | 7 | * Added --color option to control terminal coloring. | ||
1107 | 8 | |||
1108 | 9 | 2007-09-18: version 1.5.2 | ||
1109 | 10 | |||
1110 | 11 | * Added the ability to monitor InnoDB status from a file. | ||
1111 | 12 | * Changed W mode to L mode; it monitors all locks, not just lock waits. | ||
1112 | 13 | |||
1113 | 14 | 2007-09-16: version 1.5.1 | ||
1114 | 15 | |||
1115 | 16 | * Added C (Command Summary) mode. | ||
1116 | 17 | * Fixed a bug in the 'avg' aggregate function. | ||
1117 | 18 | |||
1118 | 19 | 2007-09-10: version 1.5.0 | ||
1119 | 20 | |||
1120 | 21 | Changes: | ||
1121 | 22 | * Added plugin functionality. | ||
1122 | 23 | * Added group-by functionality. | ||
1123 | 24 | * Moved the configuration file to a directory. | ||
1124 | 25 | * Enhanced filtering and sorting on pivoted tables. | ||
1125 | 26 | * Many small bug fixes. | ||
1126 | 27 | |||
1127 | 28 | 2007-07-16: version 1.4.3 | ||
1128 | 29 | |||
1129 | 30 | Changes: | ||
1130 | 31 | * Added standard --version command-line option | ||
1131 | 32 | * Changed colors to cyan instead of blue; more visible on dark terminals. | ||
1132 | 33 | * Added information to the filter-choosing dialog. | ||
1133 | 34 | * Added column auto-completion when entering a filter expression. | ||
1134 | 35 | * Changed Term::ReadKey from optional to mandatory. | ||
1135 | 36 | * Clarified username in password prompting. | ||
1136 | 37 | * Ten thousand words of documentation! | ||
1137 | 38 | |||
1138 | 39 | Bugs fixed: | ||
1139 | 40 | * innotop crashed in W mode when InnoDB status data was truncated. | ||
1140 | 41 | * innotop didn't display errors in tables if debug was enabled. | ||
1141 | 42 | * The colored() subroutine wasn't being created in non-interactive mode. | ||
1142 | 43 | * Don't prompt to save password except the first time. | ||
1143 | 44 | |||
1144 | 45 | 2007-05-03: version 1.4.2 | ||
1145 | 46 | |||
1146 | 47 | This version contains all changes to the trunk until revision 239; some | ||
1147 | 48 | changes in revisions 240:250 are included. | ||
1148 | 49 | |||
1149 | 50 | MAJOR CHANGES: | ||
1150 | 51 | |||
1151 | 52 | * Quick-filters to easily filter any column in any display | ||
1152 | 53 | * Compatibility with MySQL 3.23 through 6.0 | ||
1153 | 54 | * Improved error handling when a server is down, permissions denied, etc | ||
1154 | 55 | * Use additional SHOW INNODB STATUS information in 5.1.x | ||
1155 | 56 | * Make all modes use tables consistently, so they can all be edited, | ||
1156 | 57 | filtered, colored and sorted consistently | ||
1157 | 58 | * Combine V, G and S modes into S mode, with v, g, and s hot-keys | ||
1158 | 59 | * Let DBD driver read MySQL option files; permit connections without | ||
1159 | 60 | user/pass/etc | ||
1160 | 61 | * Compile SQL-like expressions into Perl subroutines; eliminate need to | ||
1161 | 62 | know Perl | ||
1162 | 63 | * Do not save all config data to config file, only save user's customizations | ||
1163 | 64 | * Rewritten and improved command-line option handling | ||
1164 | 65 | * Added --count, --delay, and other command-line options to support | ||
1165 | 66 | run-and-exit operation | ||
1166 | 67 | * Improve built-in variable sets | ||
1167 | 68 | * Improve help screen with three-part balanced-column layout | ||
1168 | 69 | * Simplify table-editor and improve hotkey support | ||
1169 | 70 | * Require Perl to have high-resolution time support (Time::HiRes) | ||
1170 | 71 | * Help the user choose a query to analyze or kill | ||
1171 | 72 | * Enable EXPLAIN, show-full-query in T mode just like Q mode | ||
1172 | 73 | * Let data-extraction access current, previous and incremental data sets | ||
1173 | 74 | all at once | ||
1174 | 75 | |||
1175 | 76 | MINOR CHANGES: | ||
1176 | 77 | |||
1177 | 78 | * Column stabilizing for Q mode | ||
1178 | 79 | * New color rules for T, Q, W modes | ||
1179 | 80 | * Apply slave I/O filter to Q mode | ||
1180 | 81 | * Improve detection of server version and other meta-data | ||
1181 | 82 | * Make connection timeout a config variable | ||
1182 | 83 | * Improve cross-version-compatible SQL syntax | ||
1183 | 84 | * Get some information from the DBD driver instead of asking MySQL for it | ||
1184 | 85 | * Improved error messages | ||
1185 | 86 | * Improve server group creation/editing | ||
1186 | 87 | * Improve connection/thread killing | ||
1187 | 88 | * Fix broken key bindings and restore previously mapped hot-keys for | ||
1188 | 89 | choosing columns | ||
1189 | 90 | * Some documentation updates (but not nearly enough) | ||
1190 | 91 | * Allow the user to specify graphing char in S mode (formerly G mode) | ||
1191 | 92 | * Allow easy switching between variable sets in S mode | ||
1192 | 93 | * Bind 'n' key globally to choose the 'next' server connection | ||
1193 | 94 | * Bind '%' key globally to filter displayed tables | ||
1194 | 95 | * Allow aligning columns on the decimal place for easy readability | ||
1195 | 96 | * Add hide_hdr config variable to hide column headers in tables | ||
1196 | 97 | * Add a feature to smartly run PURGE MASTER LOGS in Replication mode | ||
1197 | 98 | * Enable debug mode as a globally configurable variable | ||
1198 | 99 | * Improve error messages when an expression or filter doesn't compile or has | ||
1199 | 100 | a run-time error; die on error when debug is enabled | ||
1200 | 101 | * Allow user-configurable delays after executing SQL (to let the server | ||
1201 | 102 | settle down before taking another measurement) | ||
1202 | 103 | * Add an expression to show how long until a transaction is finished | ||
1203 | 104 | * Add skip_innodb as a global config variable | ||
1204 | 105 | * Add '%' after percentages to help disambiguate (user-configurable) | ||
1205 | 106 | * Add column to M mode to help see how fast slave is catching up to master | ||
1206 | 107 | |||
1207 | 108 | BUG FIXES: | ||
1208 | 109 | |||
1209 | 110 | * T and W modes had wrong value for wait_status column | ||
1210 | 111 | * Error tracking on connections didn't reset when the connection recovered | ||
1211 | 112 | * wait_timeout on connections couldn't be set before MySQL 4.0.3 | ||
1212 | 113 | * There was a crash on 3.23 when wiping deadlocks | ||
1213 | 114 | * Lettercase changes in some result sets (SHOW MASTER/SLAVE STATUS) between | ||
1214 | 115 | MySQL versions crashed innotop | ||
1215 | 116 | * Inactive connections crashed innotop upon access to DBD driver | ||
1216 | 117 | * set_precision did not respect user defaults for number of digits | ||
1217 | 118 | * --inc command-line option could not be negated | ||
1218 | 119 | * InnoDB status parsing was not always parsing all needed information | ||
1219 | 120 | * S mode (formerly G mode) could crash trying to divide non-numeric data | ||
1220 | 121 | * M table didn't show Slave_open_temp_tables variable; incorrect lettercase | ||
1221 | 122 | * DBD drivers with broken AutoCommit would crash innotop | ||
1222 | 123 | * Some key bindings had incorrect labels | ||
1223 | 124 | * Some config-file loading routines could load data for things that didn't | ||
1224 | 125 | exist | ||
1225 | 126 | * Headers printed too often in S mode | ||
1226 | 127 | * High-resolution time was not used even when the user had it | ||
1227 | 128 | * Non-interactive mode printed blank lines sometimes | ||
1228 | 129 | * Q-mode header and statusbar showed different QPS numbers | ||
1229 | 130 | * Formulas for key-cache and query-cache hit ratios were wrong | ||
1230 | 131 | * Mac OS "Darwin" machines were mis-identified as Microsoft Windows | ||
1231 | 132 | * Some multiplications crashed when given undefined input | ||
1232 | 133 | * The commify transformation did not check its input and could crash | ||
1233 | 134 | * Specifying an invalid mode on the command line or config file could crash | ||
1234 | 135 | innotop | ||
1235 | 136 | |||
1236 | 137 | 2007-03-29: version 1.4.1 | ||
1237 | 138 | |||
1238 | 139 | * More tweaks to display of connection errors. | ||
1239 | 140 | * Fixed a problem with skip-innodb in MySQL 5.1. | ||
1240 | 141 | * Fix a bug with dead connections in single-connection mode. | ||
1241 | 142 | * Fix a regex to allow parsing more data from truncated deadlocks. | ||
1242 | 143 | * Don't load active cxns from the config file if the cxn isn't defined. | ||
1243 | 144 | |||
1244 | 145 | 2007-03-03: version 1.4.0 | ||
1245 | 146 | |||
1246 | 147 | * Further tweak error handling and display of connection errors | ||
1247 | 148 | * More centralization of querying | ||
1248 | 149 | * Fix forking so it doesn't kill all database connections | ||
1249 | 150 | * Allow user to run innotop without permissions for GLOBAL variables and status | ||
1250 | 151 | |||
1251 | 152 | 2007-02-11: version 1.3.6 | ||
1252 | 153 | |||
1253 | 154 | * Handle some connection failures so innotop doesn't crash because of one server. | ||
1254 | 155 | * Enable incremental display in more modes. | ||
1255 | 156 | * Tweaks to colorizing, color editor, and default color rules. | ||
1256 | 157 | * Tweaks to default sorting rules. | ||
1257 | 158 | * Use prepared statements for efficiency. | ||
1258 | 159 | * Bug fixes and code cleanups. | ||
1259 | 160 | * Data storage is keyed on clock ticks now. | ||
1260 | 161 | |||
1261 | 162 | 2007-02-03: version 1.3.5 | ||
1262 | 163 | |||
1263 | 164 | * Bug fixes. | ||
1264 | 165 | * More tools for editing configuration from within innotop. | ||
1265 | 166 | * Filters and transformations are constrained to valid values. | ||
1266 | 167 | * Support for colorizing rows. | ||
1267 | 168 | * Sorting by multiple columns. | ||
1268 | 169 | * Compress headers when display is very wide. | ||
1269 | 170 | * Stabilize and limit column widths. | ||
1270 | 171 | * Check config file formats when upgrading so upgrades go smoothly. | ||
1271 | 172 | * Make D mode handle many connections at once. | ||
1272 | 173 | * Extract simple expressions from data sets in column src property. | ||
1273 | 174 | This makes innotop more awk-ish. | ||
1274 | 175 | |||
1275 | 176 | 2007-01-16: version 1.3 | ||
1276 | 177 | |||
1277 | 178 | * Readline support. | ||
1278 | 179 | * Can be used unattended, or in a pipe-and-filter mode | ||
1279 | 180 | where it outputs tab-separated data to standard output. | ||
1280 | 181 | * You can specify a config file on the command line. | ||
1281 | 182 | Config files can be marked read-only. | ||
1282 | 183 | * Monitor multiple servers simultaneously. | ||
1283 | 184 | * Server groups to help manage many servers conveniently. | ||
1284 | 185 | * Monitor master/slave status, and control slaves. | ||
1285 | 186 | * Columns can have user-defined expressions as their data sources. | ||
1286 | 187 | * Better configuration tools. | ||
1287 | 188 | * InnoDB status information is merged into SHOW VARIABLES and | ||
1288 | 189 | SHOW STATUS information, so you can access it all together. | ||
1289 | 190 | * High-precision time support in more places. | ||
1290 | 191 | * Lots of tweaks to make things display more readably and compactly. | ||
1291 | 192 | * Column transformations and filters. | ||
1292 | 193 | |||
1293 | 194 | 2007-01-16: version 1.0.1 | ||
1294 | 195 | * NOTE: innotop is now hosted at Sourceforge, in Subversion not CVS. | ||
1295 | 196 | The new project homepage is http://sourceforge.net/projects/innotop/ | ||
1296 | 197 | * Tweak default T/Q mode sort columns to match what people expect. | ||
1297 | 198 | * Fix broken InnoDBParser.pm documentation (and hence man page). | ||
1298 | 199 | |||
1299 | 200 | 2007-01-06: version 1.0 | ||
1300 | 201 | * NOTE: innotop is now hosted at Sourceforge, in Subversion not CVS. | ||
1301 | 202 | The new project homepage is http://sourceforge.net/projects/innotop/ | ||
1302 | 203 | * Prevent control characters from freaking terminal out. | ||
1303 | 204 | * Set timeout to keep busy servers from closing connection. | ||
1304 | 205 | * There is only one InnoDB insert buffer. | ||
1305 | 206 | * Make licenses clear and consistent. | ||
1306 | 207 | |||
1307 | 208 | 2006-11-14: innotop 0.1.160, InnoDBParser version 1.69 | ||
1308 | 209 | * Support for ANSI color on Microsoft Windows (more readable, compact | ||
1309 | 210 | display; thanks Gisbert W. Selke). | ||
1310 | 211 | * Better handling of $ENV{HOME} on Windows. | ||
1311 | 212 | * Added a LICENSE file to the package as per Gentoo bug: | ||
1312 | 213 | http://bugs.gentoo.org/show_bug.cgi?id=147600 | ||
1313 | 214 | |||
1314 | 215 | 2006-11-11: innotop 0.1.157, InnoDBParser version 1.69 | ||
1315 | 216 | * Add Microsoft Windows support. | ||
1316 | 217 | |||
1317 | 218 | 2006-10-19: innotop 0.1.154, InnoDBParser version 1.69 | ||
1318 | 219 | * Add O (Open Tables) mode | ||
1319 | 220 | * Add some more checks to handle incomplete InnoDB status information | ||
1320 | 221 | |||
1321 | 222 | 2006-09-30: innotop 0.1.152, InnoDBParser version 1.69 | ||
1322 | 223 | * Figured out what was wrong with package $VERSION variable: it wasn't | ||
1323 | 224 | after the package declaration! | ||
1324 | 225 | |||
1325 | 226 | 2006-09-28: innotop 0.1.152, InnoDBParser version 1.67 | ||
1326 | 227 | * Make more efforts towards crash-resistance and tolerance of completely | ||
1327 | 228 | messed-up inputs. If innotop itself is broken, it is now much harder to | ||
1328 | 229 | tell, because it just keeps on running without complaining. | ||
1329 | 230 | * Fix a small bug parsing out some information and displaying it. | ||
1330 | 231 | |||
1331 | 232 | 2006-09-05: innotop 0.1.149, InnoDBParser version 1.64 | ||
1332 | 233 | * Try to find and eliminate any parsing code that assumes pattern matches | ||
1333 | 234 | will succeed. | ||
1334 | 235 | |||
1335 | 236 | 2006-09-05: innotop 0.1.149, InnoDBParser version 1.62 | ||
1336 | 237 | * Make innotop crash-resistant, so I can declare it STABLE finally. | ||
1337 | 238 | * Instead of using SQL conditional comments, detect MySQL version. | ||
1338 | 239 | |||
1339 | 240 | 2006-08-22: innotop 0.1.147, InnoDBParser version 1.60 | ||
1340 | 241 | * Fix some innotop bugs with undefined values, bad formatting etc. | ||
1341 | 242 | |||
1342 | 243 | 2006-08-19: innotop 0.1.146, InnoDBParser version 1.60 | ||
1343 | 244 | * Make innotop handle some unexpected NULL values in Q mode. | ||
1344 | 245 | * Add OS wait information to W mode, so it is now "everything that waits." | ||
1345 | 246 | * Center section captions better. | ||
1346 | 247 | * Make R mode more readable and compact. | ||
1347 | 248 | * Make InnoDBParser parse lock waits even when they've been waiting 0 secs. | ||
1348 | 249 | |||
1349 | 250 | 2006-08-12: innotop 0.1.139, InnoDBParser version 1.59 | ||
1350 | 251 | * Add more documentation | ||
1351 | 252 | * Tweak V mode to show more info in less space. | ||
1352 | 253 | * Fix a bug in G mode. | ||
1353 | 254 | |||
1354 | 255 | 2006-08-10: innotop 0.1.132, InnoDBParser version 1.58 | ||
1355 | 256 | * Handle yet more types of FK error... it will never end! | ||
1356 | 257 | * Handle some special cases when DEADLOCK info truncated | ||
1357 | 258 | * Add a bit more FK info to F mode in innotop | ||
1358 | 259 | * More tests added to the test suite | ||
1359 | 260 | |||
1360 | 261 | 2006-08-07: innotop 0.1.131, InnoDBParser version 1.55 | ||
1361 | 262 | * Fix another issue with configuration | ||
1362 | 263 | * Handle another type of FK error | ||
1363 | 264 | |||
1364 | 265 | 2006-08-03: innotop 0.1.130, InnoDBParser version 1.54 | ||
1365 | 266 | * Fix an issue loading config file | ||
1366 | 267 | * Add heap_no to 'D' (InnoDB Deadlock) mode to ease deadlock debugging. | ||
1367 | 268 | |||
1368 | 269 | 2006-08-02: innotop 0.1.128, InnoDBParser version 1.54 | ||
1369 | 270 | * Parse lock wait information from the TRANSACTION section. | ||
1370 | 271 | * Even more OS-specific parsing... pain in the butt... | ||
1371 | 272 | * Add 'W' (InnoDB Lock Wait) mode. | ||
1372 | 273 | * Fix some minor display issues with statusbar. | ||
1373 | 274 | |||
1374 | 275 | 2006-08-02: innotop 0.1.125, InnoDBParser version 1.50 | ||
1375 | 276 | * Don't try to get references to Perl built-in functions like time() | ||
1376 | 277 | * Handle more OS-specific variations of InnoDB status text | ||
1377 | 278 | * Add some more information to various places in innotop | ||
1378 | 279 | |||
1379 | 280 | 2006-08-01: innotop 0.1.123, InnoDBParser version 1.47 | ||
1380 | 281 | |||
1381 | 282 | * Enhance S and G modes: clear screen and re-print headers | ||
1382 | 283 | * Don't crash when deadlock data is truncated | ||
1383 | 284 | * Make Analyze mode say how to get back to whatever you came from | ||
1384 | 285 | * Display 'nothing to display' when there is nothing | ||
1385 | 286 | * Add ability to read InnoDB status text from a file (mostly helps test) | ||
1386 | 287 | * Add table of Wait Array Information in Row Op/Semaphore mode | ||
1387 | 288 | * Add table of lock information in InnoDB deadlock mode | ||
1388 | 289 | * Ensure new features in upgrades don't get masked by existing config files | ||
1389 | 290 | * Tweak default column choices for T mode | ||
1390 | 291 | * Enhance foreign key parsing | ||
1391 | 292 | * Enhance physical record and data tuple parsing | ||
1392 | 293 | * Enhance lock parsing (handle old-style and new-style formats) | ||
1393 | 294 | |||
1394 | 295 | 2006-07-24: innotop 0.1.112, InnoDBParser version 1.36 | ||
1395 | 296 | |||
1396 | 297 | * InnoDBParser enhancements for FK error messages. | ||
1397 | 298 | * A fix to innotop to prevent it from crashing while trying to display a FK | ||
1398 | 299 | error message. | ||
1399 | 300 | * Some minor cosmetic changes to number formatting in innotop. | ||
1400 | 301 | |||
1401 | 302 | 2006-07-22: innotop 0.1.106, InnoDBParser version 1.35 | ||
1402 | 303 | |||
1403 | 304 | * InnoDBParser is much more complete and accurate. | ||
1404 | 305 | * Tons of bug fixes. | ||
1405 | 306 | * Add partitions to EXPLAIN mode. | ||
1406 | 307 | * Enhance Q mode header, add T mode header. | ||
1407 | 308 | * Share some configuration variables across modes. | ||
1408 | 309 | * Add formatted time columns to Q, T modes. | ||
1409 | 310 | * Add command-line argument parsing. | ||
1410 | 311 | * Turn off echo when asking for password. | ||
1411 | 312 | * Add option to specify port when connecting. | ||
1412 | 313 | * Let display-optimized-query display multiple notes. | ||
1413 | 314 | * Lots of small improvements, such as showing more info in statusbar. | ||
1414 | 315 | |||
1415 | 316 | 2006-07-02: innotop 0.1.74, InnoDBParser version 1.24 | ||
1416 | 317 | |||
1417 | 318 | * Initial release for public consumption. | ||
1418 | 319 | 0 | ||
1419 | === removed file 'build/debian/additions/innotop/innotop' | |||
1420 | --- build/debian/additions/innotop/innotop 2010-05-31 13:57:21 +0000 | |||
1421 | +++ build/debian/additions/innotop/innotop 1970-01-01 00:00:00 +0000 | |||
1422 | @@ -1,9485 +0,0 @@ | |||
1423 | 1 | #!/usr/bin/perl | ||
1424 | 2 | |||
1425 | 3 | # vim: tw=160:nowrap:expandtab:tabstop=3:shiftwidth=3:softtabstop=3 | ||
1426 | 4 | |||
1427 | 5 | use strict; | ||
1428 | 6 | use warnings FATAL => 'all'; | ||
1429 | 7 | use sigtrap qw(handler finish untrapped normal-signals); | ||
1430 | 8 | |||
1431 | 9 | use Data::Dumper; | ||
1432 | 10 | use DBI; | ||
1433 | 11 | use English qw(-no_match_vars); | ||
1434 | 12 | use File::Basename qw(dirname); | ||
1435 | 13 | use Getopt::Long; | ||
1436 | 14 | use List::Util qw(max min maxstr sum); | ||
1437 | 15 | use InnoDBParser; | ||
1438 | 16 | use POSIX qw(ceil); | ||
1439 | 17 | use Time::HiRes qw(time sleep); | ||
1440 | 18 | use Term::ReadKey qw(ReadMode ReadKey); | ||
1441 | 19 | |||
1442 | 20 | # Version, license and warranty information. {{{1 | ||
1443 | 21 | # ########################################################################### | ||
1444 | 22 | our $VERSION = '1.6.0'; | ||
1445 | 23 | our $SVN_REV = sprintf("%d", q$Revision: 383 $ =~ m/(\d+)/g); | ||
1446 | 24 | our $SVN_URL = sprintf("%s", q$URL: https://innotop.svn.sourceforge.net/svnroot/innotop/trunk/innotop $ =~ m$svnroot/innotop/(\S+)$g); | ||
1447 | 25 | |||
1448 | 26 | my $innotop_license = <<"LICENSE"; | ||
1449 | 27 | |||
1450 | 28 | This is innotop version $VERSION, a MySQL and InnoDB monitor. | ||
1451 | 29 | |||
1452 | 30 | This program is copyright (c) 2006 Baron Schwartz. | ||
1453 | 31 | Feedback and improvements are welcome. | ||
1454 | 32 | |||
1455 | 33 | THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED | ||
1456 | 34 | WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF | ||
1457 | 35 | MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
1458 | 36 | |||
1459 | 37 | This program is free software; you can redistribute it and/or modify it under | ||
1460 | 38 | the terms of the GNU General Public License as published by the Free Software | ||
1461 | 39 | Foundation, version 2; OR the Perl Artistic License. On UNIX and similar | ||
1462 | 40 | systems, you can issue `man perlgpl' or `man perlartistic' to read these | ||
1463 | 41 | licenses. | ||
1464 | 42 | |||
1465 | 43 | You should have received a copy of the GNU General Public License along with | ||
1466 | 44 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple | ||
1467 | 45 | Place, Suite 330, Boston, MA 02111-1307 USA. | ||
1468 | 46 | LICENSE | ||
1469 | 47 | |||
1470 | 48 | # Configuration information and global setup {{{1 | ||
1471 | 49 | # ########################################################################### | ||
1472 | 50 | |||
1473 | 51 | # Really, really, super-global variables. | ||
1474 | 52 | my @config_versions = ( | ||
1475 | 53 | "000-000-000", "001-003-000", # config file was one big name-value hash. | ||
1476 | 54 | "001-003-000", "001-004-002", # config file contained non-user-defined stuff. | ||
1477 | 55 | ); | ||
1478 | 56 | |||
1479 | 57 | my $clear_screen_sub; | ||
1480 | 58 | |||
1481 | 59 | # This defines expected properties and defaults for the column definitions that | ||
1482 | 60 | # eventually end up in tbl_meta. | ||
1483 | 61 | my %col_props = ( | ||
1484 | 62 | hdr => '', | ||
1485 | 63 | just => '-', | ||
1486 | 64 | dec => 0, # Whether to align the column on the decimal point | ||
1487 | 65 | num => 0, | ||
1488 | 66 | label => '', | ||
1489 | 67 | user => 0, | ||
1490 | 68 | src => '', | ||
1491 | 69 | tbl => '', # Helps when writing/reading custom columns in config files | ||
1492 | 70 | minw => 0, | ||
1493 | 71 | maxw => 0, | ||
1494 | 72 | trans => [], | ||
1495 | 73 | agg => 'first', # Aggregate function | ||
1496 | 74 | aggonly => 0, # Whether to show only when tbl_meta->{aggregate} is true | ||
1497 | 75 | ); | ||
1498 | 76 | |||
1499 | 77 | # Actual DBI connections to MySQL servers. | ||
1500 | 78 | my %dbhs; | ||
1501 | 79 | |||
1502 | 80 | # Command-line parameters {{{2 | ||
1503 | 81 | # ########################################################################### | ||
1504 | 82 | |||
1505 | 83 | my @opt_spec = ( | ||
1506 | 84 | { s => 'help', d => 'Show this help message' }, | ||
1507 | 85 | { s => 'color|C!', d => 'Use terminal coloring (default)', c => 'color' }, | ||
1508 | 86 | { s => 'config|c=s', d => 'Config file to read' }, | ||
1509 | 87 | { s => 'nonint|n', d => 'Non-interactive, output tab-separated fields' }, | ||
1510 | 88 | { s => 'count=i', d => 'Number of updates before exiting' }, | ||
1511 | 89 | { s => 'delay|d=f', d => 'Delay between updates in seconds', c => 'interval' }, | ||
1512 | 90 | { s => 'mode|m=s', d => 'Operating mode to start in', c => 'mode' }, | ||
1513 | 91 | { s => 'inc|i!', d => 'Measure incremental differences', c => 'status_inc' }, | ||
1514 | 92 | { s => 'version', d => 'Output version information and exit' }, | ||
1515 | 93 | ); | ||
1516 | 94 | |||
1517 | 95 | # This is the container for the command-line options' values to be stored in | ||
1518 | 96 | # after processing. Initial values are defaults. | ||
1519 | 97 | my %opts = ( | ||
1520 | 98 | n => !( -t STDIN && -t STDOUT ), # If in/out aren't to terminals, we're interactive | ||
1521 | 99 | ); | ||
1522 | 100 | # Post-process... | ||
1523 | 101 | my %opt_seen; | ||
1524 | 102 | foreach my $spec ( @opt_spec ) { | ||
1525 | 103 | my ( $long, $short ) = $spec->{s} =~ m/^(\w+)(?:\|([^!+=]*))?/; | ||
1526 | 104 | $spec->{k} = $short || $long; | ||
1527 | 105 | $spec->{l} = $long; | ||
1528 | 106 | $spec->{t} = $short; | ||
1529 | 107 | $spec->{n} = $spec->{s} =~ m/!/; | ||
1530 | 108 | $opts{$spec->{k}} = undef unless defined $opts{$spec->{k}}; | ||
1531 | 109 | die "Duplicate option $spec->{k}" if $opt_seen{$spec->{k}}++; | ||
1532 | 110 | } | ||
1533 | 111 | |||
1534 | 112 | Getopt::Long::Configure('no_ignore_case', 'bundling'); | ||
1535 | 113 | GetOptions( map { $_->{s} => \$opts{$_->{k}} } @opt_spec) or $opts{help} = 1; | ||
1536 | 114 | |||
1537 | 115 | if ( $opts{version} ) { | ||
1538 | 116 | print "innotop Ver $VERSION Changeset $SVN_REV from $SVN_URL\n"; | ||
1539 | 117 | exit(0); | ||
1540 | 118 | } | ||
1541 | 119 | |||
1542 | 120 | if ( $opts{'help'} ) { | ||
1543 | 121 | print "Usage: innotop <options> <innodb-status-file>\n\n"; | ||
1544 | 122 | my $maxw = max(map { length($_->{l}) + ($_->{n} ? 4 : 0)} @opt_spec); | ||
1545 | 123 | foreach my $spec ( sort { $a->{l} cmp $b->{l} } @opt_spec ) { | ||
1546 | 124 | my $long = $spec->{n} ? "[no]$spec->{l}" : $spec->{l}; | ||
1547 | 125 | my $short = $spec->{t} ? "-$spec->{t}" : ''; | ||
1548 | 126 | printf(" --%-${maxw}s %-4s %s\n", $long, $short, $spec->{d}); | ||
1549 | 127 | } | ||
1550 | 128 | print <<USAGE; | ||
1551 | 129 | |||
1552 | 130 | innotop is a MySQL and InnoDB transaction/status monitor, like 'top' for | ||
1553 | 131 | MySQL. It displays queries, InnoDB transactions, lock waits, deadlocks, | ||
1554 | 132 | foreign key errors, open tables, replication status, buffer information, | ||
1555 | 133 | row operations, logs, I/O operations, load graph, and more. You can | ||
1556 | 134 | monitor many servers at once with innotop. | ||
1557 | 135 | |||
1558 | 136 | USAGE | ||
1559 | 137 | exit(1); | ||
1560 | 138 | } | ||
1561 | 139 | |||
1562 | 140 | # Meta-data (table definitions etc) {{{2 | ||
1563 | 141 | # ########################################################################### | ||
1564 | 142 | |||
1565 | 143 | # Expressions {{{3 | ||
1566 | 144 | # Convenience so I can copy/paste these in several places... | ||
1567 | 145 | # ########################################################################### | ||
1568 | 146 | my %exprs = ( | ||
1569 | 147 | Host => q{my $host = host || hostname || ''; ($host) = $host =~ m/^((?:[\d.]+(?=:))|(?:[a-zA-Z]\w+))/; return $host || ''}, | ||
1570 | 148 | Port => q{my ($p) = host =~ m/:(.*)$/; return $p || 0}, | ||
1571 | 149 | OldVersions => q{dulint_to_int(IB_tx_trx_id_counter) - dulint_to_int(IB_tx_purge_done_for)}, | ||
1572 | 150 | MaxTxnTime => q/max(map{ $_->{active_secs} } @{ IB_tx_transactions }) || 0/, | ||
1573 | 151 | NumTxns => q{scalar @{ IB_tx_transactions } }, | ||
1574 | 152 | DirtyBufs => q{ $cur->{IB_bp_pages_modified} / ($cur->{IB_bp_buf_pool_size} || 1) }, | ||
1575 | 153 | BufPoolFill => q{ $cur->{IB_bp_pages_total} / ($cur->{IB_bp_buf_pool_size} || 1) }, | ||
1576 | 154 | ServerLoad => q{ $cur->{Threads_connected}/(Questions||1)/Uptime_hires }, | ||
1577 | 155 | TxnTimeRemain => q{ defined undo_log_entries && defined $pre->{undo_log_entries} && undo_log_entries < $pre->{undo_log_entries} ? undo_log_entries / (($pre->{undo_log_entries} - undo_log_entries)/((active_secs-$pre->{active_secs})||1))||1 : 0}, | ||
1578 | 156 | SlaveCatchupRate => ' defined $cur->{seconds_behind_master} && defined $pre->{seconds_behind_master} && $cur->{seconds_behind_master} < $pre->{seconds_behind_master} ? ($pre->{seconds_behind_master}-$cur->{seconds_behind_master})/($cur->{Uptime_hires}-$pre->{Uptime_hires}) : 0', | ||
1579 | 157 | QcacheHitRatio => q{(Qcache_hits||0)/(((Com_select||0)+(Qcache_hits||0))||1)}, | ||
1580 | 158 | ); | ||
1581 | 159 | |||
1582 | 160 | # ########################################################################### | ||
1583 | 161 | # Column definitions {{{3 | ||
1584 | 162 | # Defines every column in every table. A named column has the following | ||
1585 | 163 | # properties: | ||
1586 | 164 | # * hdr Column header/title | ||
1587 | 165 | # * label Documentation for humans. | ||
1588 | 166 | # * num Whether it's numeric (for sorting). | ||
1589 | 167 | # * just Alignment; generated from num, user-overridable in tbl_meta | ||
1590 | 168 | # * minw, maxw Auto-generated, user-overridable. | ||
1591 | 169 | # Values from this hash are just copied to tbl_meta, which is where everything | ||
1592 | 170 | # else in the program should read from. | ||
1593 | 171 | # ########################################################################### | ||
1594 | 172 | |||
1595 | 173 | my %columns = ( | ||
1596 | 174 | active_secs => { hdr => 'SecsActive', num => 1, label => 'Seconds transaction has been active', }, | ||
1597 | 175 | add_pool_alloc => { hdr => 'Add\'l Pool', num => 1, label => 'Additonal pool allocated' }, | ||
1598 | 176 | attempted_op => { hdr => 'Action', num => 0, label => 'The action that caused the error' }, | ||
1599 | 177 | awe_mem_alloc => { hdr => 'AWE Memory', num => 1, label => '[Windows] AWE memory allocated' }, | ||
1600 | 178 | binlog_cache_overflow => { hdr => 'Binlog Cache', num => 1, label => 'Transactions too big for binlog cache that went to disk' }, | ||
1601 | 179 | binlog_do_db => { hdr => 'Binlog Do DB', num => 0, label => 'binlog-do-db setting' }, | ||
1602 | 180 | binlog_ignore_db => { hdr => 'Binlog Ignore DB', num => 0, label => 'binlog-ignore-db setting' }, | ||
1603 | 181 | bps_in => { hdr => 'BpsIn', num => 1, label => 'Bytes per second received by the server', }, | ||
1604 | 182 | bps_out => { hdr => 'BpsOut', num => 1, label => 'Bytes per second sent by the server', }, | ||
1605 | 183 | buf_free => { hdr => 'Free Bufs', num => 1, label => 'Buffers free in the buffer pool' }, | ||
1606 | 184 | buf_pool_hit_rate => { hdr => 'Hit Rate', num => 0, label => 'Buffer pool hit rate' }, | ||
1607 | 185 | buf_pool_hits => { hdr => 'Hits', num => 1, label => 'Buffer pool hits' }, | ||
1608 | 186 | buf_pool_reads => { hdr => 'Reads', num => 1, label => 'Buffer pool reads' }, | ||
1609 | 187 | buf_pool_size => { hdr => 'Size', num => 1, label => 'Buffer pool size' }, | ||
1610 | 188 | bufs_in_node_heap => { hdr => 'Node Heap Bufs', num => 1, label => 'Buffers in buffer pool node heap' }, | ||
1611 | 189 | bytes_behind_master => { hdr => 'ByteLag', num => 1, label => 'Bytes the slave lags the master in binlog' }, | ||
1612 | 190 | cell_event_set => { hdr => 'Ending?', num => 1, label => 'Whether the cell event is set' }, | ||
1613 | 191 | cell_waiting => { hdr => 'Waiting?', num => 1, label => 'Whether the cell is waiting' }, | ||
1614 | 192 | child_db => { hdr => 'Child DB', num => 0, label => 'The database of the child table' }, | ||
1615 | 193 | child_index => { hdr => 'Child Index', num => 0, label => 'The index in the child table' }, | ||
1616 | 194 | child_table => { hdr => 'Child Table', num => 0, label => 'The child table' }, | ||
1617 | 195 | cmd => { hdr => 'Cmd', num => 0, label => 'Type of command being executed', }, | ||
1618 | 196 | cnt => { hdr => 'Cnt', num => 0, label => 'Count', agg => 'count', aggonly => 1 }, | ||
1619 | 197 | connect_retry => { hdr => 'Connect Retry', num => 1, label => 'Slave connect-retry timeout' }, | ||
1620 | 198 | cxn => { hdr => 'CXN', num => 0, label => 'Connection from which the data came', }, | ||
1621 | 199 | db => { hdr => 'DB', num => 0, label => 'Current database', }, | ||
1622 | 200 | dict_mem_alloc => { hdr => 'Dict Mem', num => 1, label => 'Dictionary memory allocated' }, | ||
1623 | 201 | dirty_bufs => { hdr => 'Dirty Buf', num => 1, label => 'Dirty buffer pool pages' }, | ||
1624 | 202 | dl_txn_num => { hdr => 'Num', num => 0, label => 'Deadlocked transaction number', }, | ||
1625 | 203 | event_set => { hdr => 'Evt Set?', num => 1, label => '[Win32] if a wait event is set', }, | ||
1626 | 204 | exec_master_log_pos => { hdr => 'Exec Master Log Pos', num => 1, label => 'Exec Master Log Position' }, | ||
1627 | 205 | fk_name => { hdr => 'Constraint', num => 0, label => 'The name of the FK constraint' }, | ||
1628 | 206 | free_list_len => { hdr => 'Free List Len', num => 1, label => 'Length of the free list' }, | ||
1629 | 207 | has_read_view => { hdr => 'Rd View', num => 1, label => 'Whether the transaction has a read view' }, | ||
1630 | 208 | hash_searches_s => { hdr => 'Hash/Sec', num => 1, label => 'Number of hash searches/sec' }, | ||
1631 | 209 | hash_table_size => { hdr => 'Size', num => 1, label => 'Number of non-hash searches/sec' }, | ||
1632 | 210 | heap_no => { hdr => 'Heap', num => 1, label => 'Heap number' }, | ||
1633 | 211 | heap_size => { hdr => 'Heap', num => 1, label => 'Heap size' }, | ||
1634 | 212 | history_list_len => { hdr => 'History', num => 1, label => 'History list length' }, | ||
1635 | 213 | host_and_domain => { hdr => 'Host', num => 0, label => 'Hostname/IP and domain' }, | ||
1636 | 214 | host_and_port => { hdr => 'Host/IP', num => 0, label => 'Hostname or IP address, and port number', }, | ||
1637 | 215 | hostname => { hdr => 'Host', num => 0, label => 'Hostname' }, | ||
1638 | 216 | index => { hdr => 'Index', num => 0, label => 'The index involved' }, | ||
1639 | 217 | index_ref => { hdr => 'Index Ref', num => 0, label => 'Index referenced' }, | ||
1640 | 218 | info => { hdr => 'Query', num => 0, label => 'Info or the current query', }, | ||
1641 | 219 | insert_intention => { hdr => 'Ins Intent', num => 1, label => 'Whether the thread was trying to insert' }, | ||
1642 | 220 | inserts => { hdr => 'Inserts', num => 1, label => 'Inserts' }, | ||
1643 | 221 | io_bytes_s => { hdr => 'Bytes/Sec', num => 1, label => 'Average I/O bytes/sec' }, | ||
1644 | 222 | io_flush_type => { hdr => 'Flush Type', num => 0, label => 'I/O Flush Type' }, | ||
1645 | 223 | io_fsyncs_s => { hdr => 'fsyncs/sec', num => 1, label => 'I/O fsyncs/sec' }, | ||
1646 | 224 | io_reads_s => { hdr => 'Reads/Sec', num => 1, label => 'Average I/O reads/sec' }, | ||
1647 | 225 | io_writes_s => { hdr => 'Writes/Sec', num => 1, label => 'Average I/O writes/sec' }, | ||
1648 | 226 | ip => { hdr => 'IP', num => 0, label => 'IP address' }, | ||
1649 | 227 | is_name_locked => { hdr => 'Locked', num => 1, label => 'Whether table is name locked', }, | ||
1650 | 228 | key_buffer_hit => { hdr => 'KCacheHit', num => 1, label => 'Key cache hit ratio', }, | ||
1651 | 229 | key_len => { hdr => 'Key Length', num => 1, label => 'Number of bytes used in the key' }, | ||
1652 | 230 | last_chkp => { hdr => 'Last Checkpoint', num => 0, label => 'Last log checkpoint' }, | ||
1653 | 231 | last_errno => { hdr => 'Last Errno', num => 1, label => 'Last error number' }, | ||
1654 | 232 | last_error => { hdr => 'Last Error', num => 0, label => 'Last error' }, | ||
1655 | 233 | last_s_file_name => { hdr => 'S-File', num => 0, label => 'Filename where last read locked' }, | ||
1656 | 234 | last_s_line => { hdr => 'S-Line', num => 1, label => 'Line where last read locked' }, | ||
1657 | 235 | last_x_file_name => { hdr => 'X-File', num => 0, label => 'Filename where last write locked' }, | ||
1658 | 236 | last_x_line => { hdr => 'X-Line', num => 1, label => 'Line where last write locked' }, | ||
1659 | 237 | last_pct => { hdr => 'Pct', num => 1, label => 'Last Percentage' }, | ||
1660 | 238 | last_total => { hdr => 'Last Total', num => 1, label => 'Last Total' }, | ||
1661 | 239 | last_value => { hdr => 'Last Incr', num => 1, label => 'Last Value' }, | ||
1662 | 240 | load => { hdr => 'Load', num => 1, label => 'Server load' }, | ||
1663 | 241 | lock_cfile_name => { hdr => 'Crtd File', num => 0, label => 'Filename where lock created' }, | ||
1664 | 242 | lock_cline => { hdr => 'Crtd Line', num => 1, label => 'Line where lock created' }, | ||
1665 | 243 | lock_mem_addr => { hdr => 'Addr', num => 0, label => 'The lock memory address' }, | ||
1666 | 244 | lock_mode => { hdr => 'Mode', num => 0, label => 'The lock mode' }, | ||
1667 | 245 | lock_structs => { hdr => 'LStrcts', num => 1, label => 'Number of lock structs' }, | ||
1668 | 246 | lock_type => { hdr => 'Type', num => 0, label => 'The lock type' }, | ||
1669 | 247 | lock_var => { hdr => 'Lck Var', num => 1, label => 'The lock variable' }, | ||
1670 | 248 | lock_wait_time => { hdr => 'Wait', num => 1, label => 'How long txn has waited for a lock' }, | ||
1671 | 249 | log_flushed_to => { hdr => 'Flushed To', num => 0, label => 'Log position flushed to' }, | ||
1672 | 250 | log_ios_done => { hdr => 'IO Done', num => 1, label => 'Log I/Os done' }, | ||
1673 | 251 | log_ios_s => { hdr => 'IO/Sec', num => 1, label => 'Average log I/Os per sec' }, | ||
1674 | 252 | log_seq_no => { hdr => 'Sequence No.', num => 0, label => 'Log sequence number' }, | ||
1675 | 253 | main_thread_id => { hdr => 'Main Thread ID', num => 1, label => 'Main thread ID' }, | ||
1676 | 254 | main_thread_proc_no => { hdr => 'Main Thread Proc', num => 1, label => 'Main thread process number' }, | ||
1677 | 255 | main_thread_state => { hdr => 'Main Thread State', num => 0, label => 'Main thread state' }, | ||
1678 | 256 | master_file => { hdr => 'File', num => 0, label => 'Master file' }, | ||
1679 | 257 | master_host => { hdr => 'Master', num => 0, label => 'Master server hostname' }, | ||
1680 | 258 | master_log_file => { hdr => 'Master Log File', num => 0, label => 'Master log file' }, | ||
1681 | 259 | master_port => { hdr => 'Master Port', num => 1, label => 'Master port' }, | ||
1682 | 260 | master_pos => { hdr => 'Position', num => 1, label => 'Master position' }, | ||
1683 | 261 | master_ssl_allowed => { hdr => 'Master SSL Allowed', num => 0, label => 'Master SSL Allowed' }, | ||
1684 | 262 | master_ssl_ca_file => { hdr => 'Master SSL CA File', num => 0, label => 'Master SSL Cert Auth File' }, | ||
1685 | 263 | master_ssl_ca_path => { hdr => 'Master SSL CA Path', num => 0, label => 'Master SSL Cert Auth Path' }, | ||
1686 | 264 | master_ssl_cert => { hdr => 'Master SSL Cert', num => 0, label => 'Master SSL Cert' }, | ||
1687 | 265 | master_ssl_cipher => { hdr => 'Master SSL Cipher', num => 0, label => 'Master SSL Cipher' }, | ||
1688 | 266 | master_ssl_key => { hdr => 'Master SSL Key', num => 0, label => 'Master SSL Key' }, | ||
1689 | 267 | master_user => { hdr => 'Master User', num => 0, label => 'Master username' }, | ||
1690 | 268 | max_txn => { hdr => 'MaxTxnTime', num => 1, label => 'MaxTxn' }, | ||
1691 | 269 | merged_recs => { hdr => 'Merged Recs', num => 1, label => 'Merged records' }, | ||
1692 | 270 | merges => { hdr => 'Merges', num => 1, label => 'Merges' }, | ||
1693 | 271 | mutex_os_waits => { hdr => 'Waits', num => 1, label => 'Mutex OS Waits' }, | ||
1694 | 272 | mutex_spin_rounds => { hdr => 'Rounds', num => 1, label => 'Mutex Spin Rounds' }, | ||
1695 | 273 | mutex_spin_waits => { hdr => 'Spins', num => 1, label => 'Mutex Spin Waits' }, | ||
1696 | 274 | mysql_thread_id => { hdr => 'ID', num => 1, label => 'MySQL connection (thread) ID', }, | ||
1697 | 275 | name => { hdr => 'Name', num => 0, label => 'Variable Name' }, | ||
1698 | 276 | n_bits => { hdr => '# Bits', num => 1, label => 'Number of bits' }, | ||
1699 | 277 | non_hash_searches_s => { hdr => 'Non-Hash/Sec', num => 1, label => 'Non-hash searches/sec' }, | ||
1700 | 278 | num_deletes => { hdr => 'Del', num => 1, label => 'Number of deletes' }, | ||
1701 | 279 | num_deletes_sec => { hdr => 'Del/Sec', num => 1, label => 'Number of deletes' }, | ||
1702 | 280 | num_inserts => { hdr => 'Ins', num => 1, label => 'Number of inserts' }, | ||
1703 | 281 | num_inserts_sec => { hdr => 'Ins/Sec', num => 1, label => 'Number of inserts' }, | ||
1704 | 282 | num_readers => { hdr => 'Readers', num => 1, label => 'Number of readers' }, | ||
1705 | 283 | num_reads => { hdr => 'Read', num => 1, label => 'Number of reads' }, | ||
1706 | 284 | num_reads_sec => { hdr => 'Read/Sec', num => 1, label => 'Number of reads' }, | ||
1707 | 285 | num_res_ext => { hdr => 'BTree Extents', num => 1, label => 'Number of extents reserved for B-Tree' }, | ||
1708 | 286 | num_rows => { hdr => 'Row Count', num => 1, label => 'Number of rows estimated to examine' }, | ||
1709 | 287 | num_times_open => { hdr => 'In Use', num => 1, label => '# times table is opened', }, | ||
1710 | 288 | num_txns => { hdr => 'Txns', num => 1, label => 'Number of transactions' }, | ||
1711 | 289 | num_updates => { hdr => 'Upd', num => 1, label => 'Number of updates' }, | ||
1712 | 290 | num_updates_sec => { hdr => 'Upd/Sec', num => 1, label => 'Number of updates' }, | ||
1713 | 291 | os_file_reads => { hdr => 'OS Reads', num => 1, label => 'OS file reads' }, | ||
1714 | 292 | os_file_writes => { hdr => 'OS Writes', num => 1, label => 'OS file writes' }, | ||
1715 | 293 | os_fsyncs => { hdr => 'OS fsyncs', num => 1, label => 'OS fsyncs' }, | ||
1716 | 294 | os_thread_id => { hdr => 'OS Thread', num => 1, label => 'The operating system thread ID' }, | ||
1717 | 295 | p_aio_writes => { hdr => 'Async Wrt', num => 1, label => 'Pending asynchronous I/O writes' }, | ||
1718 | 296 | p_buf_pool_flushes => { hdr => 'Buffer Pool Flushes', num => 1, label => 'Pending buffer pool flushes' }, | ||
1719 | 297 | p_ibuf_aio_reads => { hdr => 'IBuf Async Rds', num => 1, label => 'Pending insert buffer asynch I/O reads' }, | ||
1720 | 298 | p_log_flushes => { hdr => 'Log Flushes', num => 1, label => 'Pending log flushes' }, | ||
1721 | 299 | p_log_ios => { hdr => 'Log I/Os', num => 1, label => 'Pending log I/O operations' }, | ||
1722 | 300 | p_normal_aio_reads => { hdr => 'Async Rds', num => 1, label => 'Pending asynchronous I/O reads' }, | ||
1723 | 301 | p_preads => { hdr => 'preads', num => 1, label => 'Pending p-reads' }, | ||
1724 | 302 | p_pwrites => { hdr => 'pwrites', num => 1, label => 'Pending p-writes' }, | ||
1725 | 303 | p_sync_ios => { hdr => 'Sync I/Os', num => 1, label => 'Pending synchronous I/O operations' }, | ||
1726 | 304 | page_creates_sec => { hdr => 'Creates/Sec', num => 1, label => 'Page creates/sec' }, | ||
1727 | 305 | page_no => { hdr => 'Page', num => 1, label => 'Page number' }, | ||
1728 | 306 | page_reads_sec => { hdr => 'Reads/Sec', num => 1, label => 'Page reads per second' }, | ||
1729 | 307 | page_writes_sec => { hdr => 'Writes/Sec', num => 1, label => 'Page writes per second' }, | ||
1730 | 308 | pages_created => { hdr => 'Created', num => 1, label => 'Pages created' }, | ||
1731 | 309 | pages_modified => { hdr => 'Dirty Pages', num => 1, label => 'Pages modified (dirty)' }, | ||
1732 | 310 | pages_read => { hdr => 'Reads', num => 1, label => 'Pages read' }, | ||
1733 | 311 | pages_total => { hdr => 'Pages', num => 1, label => 'Pages total' }, | ||
1734 | 312 | pages_written => { hdr => 'Writes', num => 1, label => 'Pages written' }, | ||
1735 | 313 | parent_col => { hdr => 'Parent Column', num => 0, label => 'The referred column in the parent table', }, | ||
1736 | 314 | parent_db => { hdr => 'Parent DB', num => 0, label => 'The database of the parent table' }, | ||
1737 | 315 | parent_index => { hdr => 'Parent Index', num => 0, label => 'The referred index in the parent table' }, | ||
1738 | 316 | parent_table => { hdr => 'Parent Table', num => 0, label => 'The parent table' }, | ||
1739 | 317 | part_id => { hdr => 'Part ID', num => 1, label => 'Sub-part ID of the query' }, | ||
1740 | 318 | partitions => { hdr => 'Partitions', num => 0, label => 'Query partitions used' }, | ||
1741 | 319 | pct => { hdr => 'Pct', num => 1, label => 'Percentage' }, | ||
1742 | 320 | pending_chkp_writes => { hdr => 'Chkpt Writes', num => 1, label => 'Pending log checkpoint writes' }, | ||
1743 | 321 | pending_log_writes => { hdr => 'Log Writes', num => 1, label => 'Pending log writes' }, | ||
1744 | 322 | port => { hdr => 'Port', num => 1, label => 'Client port number', }, | ||
1745 | 323 | possible_keys => { hdr => 'Poss. Keys', num => 0, label => 'Possible keys' }, | ||
1746 | 324 | proc_no => { hdr => 'Proc', num => 1, label => 'Process number' }, | ||
1747 | 325 | q_cache_hit => { hdr => 'QCacheHit', num => 1, label => 'Query cache hit ratio', }, | ||
1748 | 326 | qps => { hdr => 'QPS', num => 1, label => 'How many queries/sec', }, | ||
1749 | 327 | queries_in_queue => { hdr => 'Queries Queued', num => 1, label => 'Queries in queue' }, | ||
1750 | 328 | queries_inside => { hdr => 'Queries Inside', num => 1, label => 'Queries inside InnoDB' }, | ||
1751 | 329 | query_id => { hdr => 'Query ID', num => 1, label => 'Query ID' }, | ||
1752 | 330 | query_status => { hdr => 'Query Status', num => 0, label => 'The query status' }, | ||
1753 | 331 | query_text => { hdr => 'Query Text', num => 0, label => 'The query text' }, | ||
1754 | 332 | questions => { hdr => 'Questions', num => 1, label => 'How many queries the server has gotten', }, | ||
1755 | 333 | read_master_log_pos => { hdr => 'Read Master Pos', num => 1, label => 'Read master log position' }, | ||
1756 | 334 | read_views_open => { hdr => 'Rd Views', num => 1, label => 'Number of read views open' }, | ||
1757 | 335 | reads_pending => { hdr => 'Pending Reads', num => 1, label => 'Reads pending' }, | ||
1758 | 336 | relay_log_file => { hdr => 'Relay File', num => 0, label => 'Relay log file' }, | ||
1759 | 337 | relay_log_pos => { hdr => 'Relay Pos', num => 1, label => 'Relay log position' }, | ||
1760 | 338 | relay_log_size => { hdr => 'Relay Size', num => 1, label => 'Relay log size' }, | ||
1761 | 339 | relay_master_log_file => { hdr => 'Relay Master File', num => 0, label => 'Relay master log file' }, | ||
1762 | 340 | replicate_do_db => { hdr => 'Do DB', num => 0, label => 'Replicate-do-db setting' }, | ||
1763 | 341 | replicate_do_table => { hdr => 'Do Table', num => 0, label => 'Replicate-do-table setting' }, | ||
1764 | 342 | replicate_ignore_db => { hdr => 'Ignore DB', num => 0, label => 'Replicate-ignore-db setting' }, | ||
1765 | 343 | replicate_ignore_table => { hdr => 'Ignore Table', num => 0, label => 'Replicate-do-table setting' }, | ||
1766 | 344 | replicate_wild_do_table => { hdr => 'Wild Do Table', num => 0, label => 'Replicate-wild-do-table setting' }, | ||
1767 | 345 | replicate_wild_ignore_table => { hdr => 'Wild Ignore Table', num => 0, label => 'Replicate-wild-ignore-table setting' }, | ||
1768 | 346 | request_type => { hdr => 'Type', num => 0, label => 'Type of lock the thread waits for' }, | ||
1769 | 347 | reservation_count => { hdr => 'ResCnt', num => 1, label => 'Reservation Count' }, | ||
1770 | 348 | row_locks => { hdr => 'RLocks', num => 1, label => 'Number of row locks' }, | ||
1771 | 349 | rw_excl_os_waits => { hdr => 'RW Waits', num => 1, label => 'R/W Excl. OS Waits' }, | ||
1772 | 350 | rw_excl_spins => { hdr => 'RW Spins', num => 1, label => 'R/W Excl. Spins' }, | ||
1773 | 351 | rw_shared_os_waits => { hdr => 'Sh Waits', num => 1, label => 'R/W Shared OS Waits' }, | ||
1774 | 352 | rw_shared_spins => { hdr => 'Sh Spins', num => 1, label => 'R/W Shared Spins' }, | ||
1775 | 353 | scan_type => { hdr => 'Type', num => 0, label => 'Scan type in chosen' }, | ||
1776 | 354 | seg_size => { hdr => 'Seg. Size', num => 1, label => 'Segment size' }, | ||
1777 | 355 | select_type => { hdr => 'Select Type', num => 0, label => 'Type of select used' }, | ||
1778 | 356 | signal_count => { hdr => 'Signals', num => 1, label => 'Signal Count' }, | ||
1779 | 357 | size => { hdr => 'Size', num => 1, label => 'Size of the tablespace' }, | ||
1780 | 358 | skip_counter => { hdr => 'Skip Counter', num => 1, label => 'Skip counter' }, | ||
1781 | 359 | slave_catchup_rate => { hdr => 'Catchup', num => 1, label => 'How fast the slave is catching up in the binlog' }, | ||
1782 | 360 | slave_io_running => { hdr => 'Slave-IO', num => 0, label => 'Whether the slave I/O thread is running' }, | ||
1783 | 361 | slave_io_state => { hdr => 'Slave IO State', num => 0, label => 'Slave I/O thread state' }, | ||
1784 | 362 | slave_open_temp_tables => { hdr => 'Temp', num => 1, label => 'Slave open temp tables' }, | ||
1785 | 363 | slave_sql_running => { hdr => 'Slave-SQL', num => 0, label => 'Whether the slave SQL thread is running' }, | ||
1786 | 364 | slow => { hdr => 'Slow', num => 1, label => 'How many slow queries', }, | ||
1787 | 365 | space_id => { hdr => 'Space', num => 1, label => 'Tablespace ID' }, | ||
1788 | 366 | special => { hdr => 'Special', num => 0, label => 'Special/Other info' }, | ||
1789 | 367 | state => { hdr => 'State', num => 0, label => 'Connection state', maxw => 18, }, | ||
1790 | 368 | tables_in_use => { hdr => 'Tbl Used', num => 1, label => 'Number of tables in use' }, | ||
1791 | 369 | tables_locked => { hdr => 'Tbl Lck', num => 1, label => 'Number of tables locked' }, | ||
1792 | 370 | tbl => { hdr => 'Table', num => 0, label => 'Table', }, | ||
1793 | 371 | thread => { hdr => 'Thread', num => 1, label => 'Thread number' }, | ||
1794 | 372 | thread_decl_inside => { hdr => 'Thread Inside', num => 0, label => 'What the thread is declared inside' }, | ||
1795 | 373 | thread_purpose => { hdr => 'Purpose', num => 0, label => "The thread's purpose" }, | ||
1796 | 374 | thread_status => { hdr => 'Thread Status', num => 0, label => 'The thread status' }, | ||
1797 | 375 | time => { hdr => 'Time', num => 1, label => 'Time since the last event', }, | ||
1798 | 376 | time_behind_master => { hdr => 'TimeLag', num => 1, label => 'Time slave lags master' }, | ||
1799 | 377 | timestring => { hdr => 'Timestring', num => 0, label => 'Time the event occurred' }, | ||
1800 | 378 | total => { hdr => 'Total', num => 1, label => 'Total' }, | ||
1801 | 379 | total_mem_alloc => { hdr => 'Memory', num => 1, label => 'Total memory allocated' }, | ||
1802 | 380 | truncates => { hdr => 'Trunc', num => 0, label => 'Whether the deadlock is truncating InnoDB status' }, | ||
1803 | 381 | txn_doesnt_see_ge => { hdr => "Txn Won't See", num => 0, label => 'Where txn read view is limited' }, | ||
1804 | 382 | txn_id => { hdr => 'ID', num => 0, label => 'Transaction ID' }, | ||
1805 | 383 | txn_sees_lt => { hdr => 'Txn Sees', num => 1, label => 'Where txn read view is limited' }, | ||
1806 | 384 | txn_status => { hdr => 'Txn Status', num => 0, label => 'Transaction status' }, | ||
1807 | 385 | txn_time_remain => { hdr => 'Remaining', num => 1, label => 'Time until txn rollback/commit completes' }, | ||
1808 | 386 | undo_log_entries => { hdr => 'Undo', num => 1, label => 'Number of undo log entries' }, | ||
1809 | 387 | undo_for => { hdr => 'Undo', num => 0, label => 'Undo for' }, | ||
1810 | 388 | until_condition => { hdr => 'Until Condition', num => 0, label => 'Slave until condition' }, | ||
1811 | 389 | until_log_file => { hdr => 'Until Log File', num => 0, label => 'Slave until log file' }, | ||
1812 | 390 | until_log_pos => { hdr => 'Until Log Pos', num => 1, label => 'Slave until log position' }, | ||
1813 | 391 | used_cells => { hdr => 'Cells Used', num => 1, label => 'Number of cells used' }, | ||
1814 | 392 | used_bufs => { hdr => 'Used Bufs', num => 1, label => 'Number of buffer pool pages used' }, | ||
1815 | 393 | user => { hdr => 'User', num => 0, label => 'Database username', }, | ||
1816 | 394 | value => { hdr => 'Value', num => 1, label => 'Value' }, | ||
1817 | 395 | versions => { hdr => 'Versions', num => 1, label => 'Number of InnoDB MVCC versions unpurged' }, | ||
1818 | 396 | victim => { hdr => 'Victim', num => 0, label => 'Whether this txn was the deadlock victim' }, | ||
1819 | 397 | wait_array_size => { hdr => 'Wait Array Size', num => 1, label => 'Wait Array Size' }, | ||
1820 | 398 | wait_status => { hdr => 'Lock Status', num => 0, label => 'Status of txn locks' }, | ||
1821 | 399 | waited_at_filename => { hdr => 'File', num => 0, label => 'Filename at which thread waits' }, | ||
1822 | 400 | waited_at_line => { hdr => 'Line', num => 1, label => 'Line at which thread waits' }, | ||
1823 | 401 | waiters_flag => { hdr => 'Waiters', num => 1, label => 'Waiters Flag' }, | ||
1824 | 402 | waiting => { hdr => 'Waiting', num => 1, label => 'Whether lock is being waited for' }, | ||
1825 | 403 | when => { hdr => 'When', num => 0, label => 'Time scale' }, | ||
1826 | 404 | writer_lock_mode => { hdr => 'Wrtr Lck Mode', num => 0, label => 'Writer lock mode' }, | ||
1827 | 405 | writer_thread => { hdr => 'Wrtr Thread', num => 1, label => 'Writer thread ID' }, | ||
1828 | 406 | writes_pending => { hdr => 'Writes', num => 1, label => 'Number of writes pending' }, | ||
1829 | 407 | writes_pending_flush_list => { hdr => 'Flush List Writes', num => 1, label => 'Number of flush list writes pending' }, | ||
1830 | 408 | writes_pending_lru => { hdr => 'LRU Writes', num => 1, label => 'Number of LRU writes pending' }, | ||
1831 | 409 | writes_pending_single_page => { hdr => '1-Page Writes', num => 1, label => 'Number of 1-page writes pending' }, | ||
1832 | 410 | ); | ||
1833 | 411 | |||
1834 | 412 | # Apply a default property or three. By default, columns are not width-constrained, | ||
1835 | 413 | # aligned left, and sorted alphabetically, not numerically. | ||
1836 | 414 | foreach my $col ( values %columns ) { | ||
1837 | 415 | map { $col->{$_} ||= 0 } qw(num minw maxw); | ||
1838 | 416 | $col->{just} = $col->{num} ? '' : '-'; | ||
1839 | 417 | } | ||
1840 | 418 | |||
1841 | 419 | # Filters {{{3 | ||
1842 | 420 | # This hash defines every filter that can be applied to a table. These | ||
1843 | 421 | # become part of tbl_meta as well. Each filter is just an expression that | ||
1844 | 422 | # returns true or false. | ||
1845 | 423 | # Properties of each entry: | ||
1846 | 424 | # * func: the subroutine | ||
1847 | 425 | # * name: the name, repeated | ||
1848 | 426 | # * user: whether it's a user-defined filter (saved in config) | ||
1849 | 427 | # * text: text of the subroutine | ||
1850 | 428 | # * note: explanation | ||
1851 | 429 | my %filters = (); | ||
1852 | 430 | |||
1853 | 431 | # These are pre-processed to live in %filters above, by compiling them. | ||
1854 | 432 | my %builtin_filters = ( | ||
1855 | 433 | hide_self => { | ||
1856 | 434 | text => <<' END', | ||
1857 | 435 | return ( !$set->{info} || $set->{info} ne 'SHOW FULL PROCESSLIST' ) | ||
1858 | 436 | && ( !$set->{query_text} || $set->{query_text} !~ m/INNODB STATUS$/ ); | ||
1859 | 437 | END | ||
1860 | 438 | note => 'Removes the innotop processes from the list', | ||
1861 | 439 | tbls => [qw(innodb_transactions processlist)], | ||
1862 | 440 | }, | ||
1863 | 441 | hide_inactive => { | ||
1864 | 442 | text => <<' END', | ||
1865 | 443 | return ( !defined($set->{txn_status}) || $set->{txn_status} ne 'not started' ) | ||
1866 | 444 | && ( !defined($set->{cmd}) || $set->{cmd} !~ m/Sleep|Binlog Dump/ ) | ||
1867 | 445 | && ( !defined($set->{info}) || $set->{info} =~ m/\S/ ); | ||
1868 | 446 | END | ||
1869 | 447 | note => 'Removes processes which are not doing anything', | ||
1870 | 448 | tbls => [qw(innodb_transactions processlist)], | ||
1871 | 449 | }, | ||
1872 | 450 | hide_slave_io => { | ||
1873 | 451 | text => <<' END', | ||
1874 | 452 | return !$set->{state} || $set->{state} !~ m/^(?:Waiting for master|Has read all relay)/; | ||
1875 | 453 | END | ||
1876 | 454 | note => 'Removes slave I/O threads from the list', | ||
1877 | 455 | tbls => [qw(processlist slave_io_status)], | ||
1878 | 456 | }, | ||
1879 | 457 | table_is_open => { | ||
1880 | 458 | text => <<' END', | ||
1881 | 459 | return $set->{num_times_open} + $set->{is_name_locked}; | ||
1882 | 460 | END | ||
1883 | 461 | note => 'Removes tables that are not in use or locked', | ||
1884 | 462 | tbls => [qw(open_tables)], | ||
1885 | 463 | }, | ||
1886 | 464 | cxn_is_master => { | ||
1887 | 465 | text => <<' END', | ||
1888 | 466 | return $set->{master_file} ? 1 : 0; | ||
1889 | 467 | END | ||
1890 | 468 | note => 'Removes servers that are not masters', | ||
1891 | 469 | tbls => [qw(master_status)], | ||
1892 | 470 | }, | ||
1893 | 471 | cxn_is_slave => { | ||
1894 | 472 | text => <<' END', | ||
1895 | 473 | return $set->{master_host} ? 1 : 0; | ||
1896 | 474 | END | ||
1897 | 475 | note => 'Removes servers that are not slaves', | ||
1898 | 476 | tbls => [qw(slave_io_status slave_sql_status)], | ||
1899 | 477 | }, | ||
1900 | 478 | thd_is_not_waiting => { | ||
1901 | 479 | text => <<' END', | ||
1902 | 480 | return $set->{thread_status} !~ m#waiting for i/o request#; | ||
1903 | 481 | END | ||
1904 | 482 | note => 'Removes idle I/O threads', | ||
1905 | 483 | tbls => [qw(io_threads)], | ||
1906 | 484 | }, | ||
1907 | 485 | ); | ||
1908 | 486 | foreach my $key ( keys %builtin_filters ) { | ||
1909 | 487 | my ( $sub, $err ) = compile_filter($builtin_filters{$key}->{text}); | ||
1910 | 488 | $filters{$key} = { | ||
1911 | 489 | func => $sub, | ||
1912 | 490 | text => $builtin_filters{$key}->{text}, | ||
1913 | 491 | user => 0, | ||
1914 | 492 | name => $key, # useful for later | ||
1915 | 493 | note => $builtin_filters{$key}->{note}, | ||
1916 | 494 | tbls => $builtin_filters{$key}->{tbls}, | ||
1917 | 495 | } | ||
1918 | 496 | } | ||
1919 | 497 | |||
1920 | 498 | # Variable sets {{{3 | ||
1921 | 499 | # Sets (arrayrefs) of variables that are used in S mode. They are read/written to | ||
1922 | 500 | # the config file. | ||
1923 | 501 | my %var_sets = ( | ||
1924 | 502 | general => { | ||
1925 | 503 | text => join( | ||
1926 | 504 | ', ', | ||
1927 | 505 | 'set_precision(Questions/Uptime_hires) as QPS', | ||
1928 | 506 | 'set_precision(Com_commit/Uptime_hires) as Commit_PS', | ||
1929 | 507 | 'set_precision((Com_rollback||0)/(Com_commit||1)) as Rollback_Commit', | ||
1930 | 508 | 'set_precision((' | ||
1931 | 509 | . join('+', map { "($_||0)" } | ||
1932 | 510 | qw(Com_delete Com_delete_multi Com_insert Com_insert_select Com_replace | ||
1933 | 511 | Com_replace_select Com_select Com_update Com_update_multi)) | ||
1934 | 512 | . ')/(Com_commit||1)) as Write_Commit', | ||
1935 | 513 | 'set_precision((Com_select+(Qcache_hits||0))/((' | ||
1936 | 514 | . join('+', map { "($_||0)" } | ||
1937 | 515 | qw(Com_delete Com_delete_multi Com_insert Com_insert_select Com_replace | ||
1938 | 516 | Com_replace_select Com_select Com_update Com_update_multi)) | ||
1939 | 517 | . ')||1)) as R_W_Ratio', | ||
1940 | 518 | 'set_precision(Opened_tables/Uptime_hires) as Opens_PS', | ||
1941 | 519 | 'percent($cur->{Open_tables}/($cur->{table_cache})) as Table_Cache_Used', | ||
1942 | 520 | 'set_precision(Threads_created/Uptime_hires) as Threads_PS', | ||
1943 | 521 | 'percent($cur->{Threads_cached}/($cur->{thread_cache_size}||1)) as Thread_Cache_Used', | ||
1944 | 522 | 'percent($cur->{Max_used_connections}/($cur->{max_connections}||1)) as CXN_Used_Ever', | ||
1945 | 523 | 'percent($cur->{Threads_connected}/($cur->{max_connections}||1)) as CXN_Used_Now', | ||
1946 | 524 | ), | ||
1947 | 525 | }, | ||
1948 | 526 | commands => { | ||
1949 | 527 | text => join( | ||
1950 | 528 | ', ', | ||
1951 | 529 | qw(Uptime Questions Com_delete Com_delete_multi Com_insert | ||
1952 | 530 | Com_insert_select Com_replace Com_replace_select Com_select Com_update | ||
1953 | 531 | Com_update_multi) | ||
1954 | 532 | ), | ||
1955 | 533 | }, | ||
1956 | 534 | query_status => { | ||
1957 | 535 | text => join( | ||
1958 | 536 | ',', | ||
1959 | 537 | qw( Uptime Select_full_join Select_full_range_join Select_range | ||
1960 | 538 | Select_range_check Select_scan Slow_queries Sort_merge_passes | ||
1961 | 539 | Sort_range Sort_rows Sort_scan) | ||
1962 | 540 | ), | ||
1963 | 541 | }, | ||
1964 | 542 | innodb => { | ||
1965 | 543 | text => join( | ||
1966 | 544 | ',', | ||
1967 | 545 | qw( Uptime Innodb_row_lock_current_waits Innodb_row_lock_time | ||
1968 | 546 | Innodb_row_lock_time_avg Innodb_row_lock_time_max Innodb_row_lock_waits | ||
1969 | 547 | Innodb_rows_deleted Innodb_rows_inserted Innodb_rows_read | ||
1970 | 548 | Innodb_rows_updated) | ||
1971 | 549 | ), | ||
1972 | 550 | }, | ||
1973 | 551 | txn => { | ||
1974 | 552 | text => join( | ||
1975 | 553 | ',', | ||
1976 | 554 | qw( Uptime Com_begin Com_commit Com_rollback Com_savepoint | ||
1977 | 555 | Com_xa_commit Com_xa_end Com_xa_prepare Com_xa_recover Com_xa_rollback | ||
1978 | 556 | Com_xa_start) | ||
1979 | 557 | ), | ||
1980 | 558 | }, | ||
1981 | 559 | key_cache => { | ||
1982 | 560 | text => join( | ||
1983 | 561 | ',', | ||
1984 | 562 | qw( Uptime Key_blocks_not_flushed Key_blocks_unused Key_blocks_used | ||
1985 | 563 | Key_read_requests Key_reads Key_write_requests Key_writes ) | ||
1986 | 564 | ), | ||
1987 | 565 | }, | ||
1988 | 566 | query_cache => { | ||
1989 | 567 | text => join( | ||
1990 | 568 | ',', | ||
1991 | 569 | "percent($exprs{QcacheHitRatio}) as Hit_Pct", | ||
1992 | 570 | 'set_precision((Qcache_hits||0)/(Qcache_inserts||1)) as Hit_Ins', | ||
1993 | 571 | 'set_precision((Qcache_lowmem_prunes||0)/Uptime_hires) as Lowmem_Prunes_sec', | ||
1994 | 572 | 'percent(1-((Qcache_free_blocks||0)/(Qcache_total_blocks||1))) as Blocks_used', | ||
1995 | 573 | qw( Qcache_free_blocks Qcache_free_memory Qcache_not_cached Qcache_queries_in_cache) | ||
1996 | 574 | ), | ||
1997 | 575 | }, | ||
1998 | 576 | handler => { | ||
1999 | 577 | text => join( | ||
2000 | 578 | ',', | ||
2001 | 579 | qw( Uptime Handler_read_key Handler_read_first Handler_read_next | ||
2002 | 580 | Handler_read_prev Handler_read_rnd Handler_read_rnd_next Handler_delete | ||
2003 | 581 | Handler_update Handler_write) | ||
2004 | 582 | ), | ||
2005 | 583 | }, | ||
2006 | 584 | cxns_files_threads => { | ||
2007 | 585 | text => join( | ||
2008 | 586 | ',', | ||
2009 | 587 | qw( Uptime Aborted_clients Aborted_connects Bytes_received Bytes_sent | ||
2010 | 588 | Compression Connections Created_tmp_disk_tables Created_tmp_files | ||
2011 | 589 | Created_tmp_tables Max_used_connections Open_files Open_streams | ||
2012 | 590 | Open_tables Opened_tables Table_locks_immediate Table_locks_waited | ||
2013 | 591 | Threads_cached Threads_connected Threads_created Threads_running) | ||
2014 | 592 | ), | ||
2015 | 593 | }, | ||
2016 | 594 | prep_stmt => { | ||
2017 | 595 | text => join( | ||
2018 | 596 | ',', | ||
2019 | 597 | qw( Uptime Com_dealloc_sql Com_execute_sql Com_prepare_sql Com_reset | ||
2020 | 598 | Com_stmt_close Com_stmt_execute Com_stmt_fetch Com_stmt_prepare | ||
2021 | 599 | Com_stmt_reset Com_stmt_send_long_data ) | ||
2022 | 600 | ), | ||
2023 | 601 | }, | ||
2024 | 602 | innodb_health => { | ||
2025 | 603 | text => join( | ||
2026 | 604 | ',', | ||
2027 | 605 | "$exprs{OldVersions} as OldVersions", | ||
2028 | 606 | qw(IB_sm_mutex_spin_waits IB_sm_mutex_spin_rounds IB_sm_mutex_os_waits), | ||
2029 | 607 | "$exprs{NumTxns} as NumTxns", | ||
2030 | 608 | "$exprs{MaxTxnTime} as MaxTxnTime", | ||
2031 | 609 | qw(IB_ro_queries_inside IB_ro_queries_in_queue), | ||
2032 | 610 | "set_precision($exprs{DirtyBufs} * 100) as dirty_bufs", | ||
2033 | 611 | "set_precision($exprs{BufPoolFill} * 100) as buf_fill", | ||
2034 | 612 | qw(IB_bp_pages_total IB_bp_pages_read IB_bp_pages_written IB_bp_pages_created) | ||
2035 | 613 | ), | ||
2036 | 614 | }, | ||
2037 | 615 | innodb_health2 => { | ||
2038 | 616 | text => join( | ||
2039 | 617 | ', ', | ||
2040 | 618 | 'percent(1-((Innodb_buffer_pool_pages_free||0)/($cur->{Innodb_buffer_pool_pages_total}||1))) as BP_page_cache_usage', | ||
2041 | 619 | 'percent(1-((Innodb_buffer_pool_reads||0)/(Innodb_buffer_pool_read_requests||1))) as BP_cache_hit_ratio', | ||
2042 | 620 | 'Innodb_buffer_pool_wait_free', | ||
2043 | 621 | 'Innodb_log_waits', | ||
2044 | 622 | ), | ||
2045 | 623 | }, | ||
2046 | 624 | slow_queries => { | ||
2047 | 625 | text => join( | ||
2048 | 626 | ', ', | ||
2049 | 627 | 'set_precision(Slow_queries/Uptime_hires) as Slow_PS', | ||
2050 | 628 | 'set_precision(Select_full_join/Uptime_hires) as Full_Join_PS', | ||
2051 | 629 | 'percent(Select_full_join/(Com_select||1)) as Full_Join_Ratio', | ||
2052 | 630 | ), | ||
2053 | 631 | }, | ||
2054 | 632 | ); | ||
2055 | 633 | |||
2056 | 634 | # Server sets {{{3 | ||
2057 | 635 | # Defines sets of servers between which the user can quickly switch. | ||
2058 | 636 | my %server_groups; | ||
2059 | 637 | |||
2060 | 638 | # Connections {{{3 | ||
2061 | 639 | # This hash defines server connections. Each connection is a string that can be passed to | ||
2062 | 640 | # the DBI connection. These are saved in the connections section in the config file. | ||
2063 | 641 | my %connections; | ||
2064 | 642 | # Defines the parts of connections. | ||
2065 | 643 | my @conn_parts = qw(user have_user pass have_pass dsn savepass dl_table); | ||
2066 | 644 | |||
2067 | 645 | # Graph widths {{{3 | ||
2068 | 646 | # This hash defines the max values seen for various status/variable values, for graphing. | ||
2069 | 647 | # These are stored in their own section in the config file. These are just initial values: | ||
2070 | 648 | my %mvs = ( | ||
2071 | 649 | Com_select => 50, | ||
2072 | 650 | Com_insert => 50, | ||
2073 | 651 | Com_update => 50, | ||
2074 | 652 | Com_delete => 50, | ||
2075 | 653 | Questions => 100, | ||
2076 | 654 | ); | ||
2077 | 655 | |||
2078 | 656 | # ########################################################################### | ||
2079 | 657 | # Valid Term::ANSIColor color strings. | ||
2080 | 658 | # ########################################################################### | ||
2081 | 659 | my %ansicolors = map { $_ => 1 } | ||
2082 | 660 | qw( black blink blue bold clear concealed cyan dark green magenta on_black | ||
2083 | 661 | on_blue on_cyan on_green on_magenta on_red on_white on_yellow red reset | ||
2084 | 662 | reverse underline underscore white yellow); | ||
2085 | 663 | |||
2086 | 664 | # ########################################################################### | ||
2087 | 665 | # Valid comparison operators for color rules | ||
2088 | 666 | # ########################################################################### | ||
2089 | 667 | my %comp_ops = ( | ||
2090 | 668 | '==' => 'Numeric equality', | ||
2091 | 669 | '>' => 'Numeric greater-than', | ||
2092 | 670 | '<' => 'Numeric less-than', | ||
2093 | 671 | '>=' => 'Numeric greater-than/equal', | ||
2094 | 672 | '<=' => 'Numeric less-than/equal', | ||
2095 | 673 | '!=' => 'Numeric not-equal', | ||
2096 | 674 | 'eq' => 'String equality', | ||
2097 | 675 | 'gt' => 'String greater-than', | ||
2098 | 676 | 'lt' => 'String less-than', | ||
2099 | 677 | 'ge' => 'String greater-than/equal', | ||
2100 | 678 | 'le' => 'String less-than/equal', | ||
2101 | 679 | 'ne' => 'String not-equal', | ||
2102 | 680 | '=~' => 'Pattern match', | ||
2103 | 681 | '!~' => 'Negated pattern match', | ||
2104 | 682 | ); | ||
2105 | 683 | |||
2106 | 684 | # ########################################################################### | ||
2107 | 685 | # Valid aggregate functions. | ||
2108 | 686 | # ########################################################################### | ||
2109 | 687 | my %agg_funcs = ( | ||
2110 | 688 | first => sub { | ||
2111 | 689 | return $_[0] | ||
2112 | 690 | }, | ||
2113 | 691 | count => sub { | ||
2114 | 692 | return 0 + @_; | ||
2115 | 693 | }, | ||
2116 | 694 | avg => sub { | ||
2117 | 695 | my @args = grep { defined $_ } @_; | ||
2118 | 696 | return (sum(map { m/([\d\.-]+)/g } @args) || 0) / (scalar(@args) || 1); | ||
2119 | 697 | }, | ||
2120 | 698 | sum => \&sum, | ||
2121 | 699 | ); | ||
2122 | 700 | |||
2123 | 701 | # ########################################################################### | ||
2124 | 702 | # Valid functions for transformations. | ||
2125 | 703 | # ########################################################################### | ||
2126 | 704 | my %trans_funcs = ( | ||
2127 | 705 | shorten => \&shorten, | ||
2128 | 706 | secs_to_time => \&secs_to_time, | ||
2129 | 707 | no_ctrl_char => \&no_ctrl_char, | ||
2130 | 708 | percent => \&percent, | ||
2131 | 709 | commify => \&commify, | ||
2132 | 710 | dulint_to_int => \&dulint_to_int, | ||
2133 | 711 | set_precision => \&set_precision, | ||
2134 | 712 | ); | ||
2135 | 713 | |||
2136 | 714 | # Table definitions {{{3 | ||
2137 | 715 | # This hash defines every table that can get displayed in every mode. Each | ||
2138 | 716 | # table specifies columns and column data sources. The column is | ||
2139 | 717 | # defined by the %columns hash. | ||
2140 | 718 | # | ||
2141 | 719 | # Example: foo => { src => 'bar' } means the foo column (look at | ||
2142 | 720 | # $columns{foo} for its definition) gets its data from the 'bar' element of | ||
2143 | 721 | # the current data set, whatever that is. | ||
2144 | 722 | # | ||
2145 | 723 | # These columns are post-processed after being defined, because they get stuff | ||
2146 | 724 | # from %columns. After all the config is loaded for columns, there's more | ||
2147 | 725 | # post-processing too; the subroutines compiled from src get added to | ||
2148 | 726 | # the hash elements for extract_values to use. | ||
2149 | 727 | # ########################################################################### | ||
2150 | 728 | |||
2151 | 729 | my %tbl_meta = ( | ||
2152 | 730 | adaptive_hash_index => { | ||
2153 | 731 | capt => 'Adaptive Hash Index', | ||
2154 | 732 | cust => {}, | ||
2155 | 733 | cols => { | ||
2156 | 734 | cxn => { src => 'cxn' }, | ||
2157 | 735 | hash_table_size => { src => 'IB_ib_hash_table_size', trans => [qw(shorten)], }, | ||
2158 | 736 | used_cells => { src => 'IB_ib_used_cells' }, | ||
2159 | 737 | bufs_in_node_heap => { src => 'IB_ib_bufs_in_node_heap' }, | ||
2160 | 738 | hash_searches_s => { src => 'IB_ib_hash_searches_s' }, | ||
2161 | 739 | non_hash_searches_s => { src => 'IB_ib_non_hash_searches_s' }, | ||
2162 | 740 | }, | ||
2163 | 741 | visible => [ qw(cxn hash_table_size used_cells bufs_in_node_heap hash_searches_s non_hash_searches_s) ], | ||
2164 | 742 | filters => [], | ||
2165 | 743 | sort_cols => 'cxn', | ||
2166 | 744 | sort_dir => '1', | ||
2167 | 745 | innodb => 'ib', | ||
2168 | 746 | group_by => [], | ||
2169 | 747 | aggregate => 0, | ||
2170 | 748 | }, | ||
2171 | 749 | buffer_pool => { | ||
2172 | 750 | capt => 'Buffer Pool', | ||
2173 | 751 | cust => {}, | ||
2174 | 752 | cols => { | ||
2175 | 753 | cxn => { src => 'cxn' }, | ||
2176 | 754 | total_mem_alloc => { src => 'IB_bp_total_mem_alloc', trans => [qw(shorten)], }, | ||
2177 | 755 | awe_mem_alloc => { src => 'IB_bp_awe_mem_alloc', trans => [qw(shorten)], }, | ||
2178 | 756 | add_pool_alloc => { src => 'IB_bp_add_pool_alloc', trans => [qw(shorten)], }, | ||
2179 | 757 | buf_pool_size => { src => 'IB_bp_buf_pool_size', trans => [qw(shorten)], }, | ||
2180 | 758 | buf_free => { src => 'IB_bp_buf_free' }, | ||
2181 | 759 | buf_pool_hit_rate => { src => 'IB_bp_buf_pool_hit_rate' }, | ||
2182 | 760 | buf_pool_reads => { src => 'IB_bp_buf_pool_reads' }, | ||
2183 | 761 | buf_pool_hits => { src => 'IB_bp_buf_pool_hits' }, | ||
2184 | 762 | dict_mem_alloc => { src => 'IB_bp_dict_mem_alloc' }, | ||
2185 | 763 | pages_total => { src => 'IB_bp_pages_total' }, | ||
2186 | 764 | pages_modified => { src => 'IB_bp_pages_modified' }, | ||
2187 | 765 | reads_pending => { src => 'IB_bp_reads_pending' }, | ||
2188 | 766 | writes_pending => { src => 'IB_bp_writes_pending' }, | ||
2189 | 767 | writes_pending_lru => { src => 'IB_bp_writes_pending_lru' }, | ||
2190 | 768 | writes_pending_flush_list => { src => 'IB_bp_writes_pending_flush_list' }, | ||
2191 | 769 | writes_pending_single_page => { src => 'IB_bp_writes_pending_single_page' }, | ||
2192 | 770 | page_creates_sec => { src => 'IB_bp_page_creates_sec' }, | ||
2193 | 771 | page_reads_sec => { src => 'IB_bp_page_reads_sec' }, | ||
2194 | 772 | page_writes_sec => { src => 'IB_bp_page_writes_sec' }, | ||
2195 | 773 | pages_created => { src => 'IB_bp_pages_created' }, | ||
2196 | 774 | pages_read => { src => 'IB_bp_pages_read' }, | ||
2197 | 775 | pages_written => { src => 'IB_bp_pages_written' }, | ||
2198 | 776 | }, | ||
2199 | 777 | visible => [ qw(cxn buf_pool_size buf_free pages_total pages_modified buf_pool_hit_rate total_mem_alloc add_pool_alloc)], | ||
2200 | 778 | filters => [], | ||
2201 | 779 | sort_cols => 'cxn', | ||
2202 | 780 | sort_dir => '1', | ||
2203 | 781 | innodb => 'bp', | ||
2204 | 782 | group_by => [], | ||
2205 | 783 | aggregate => 0, | ||
2206 | 784 | }, | ||
2207 | 785 | # TODO: a new step in set_to_tbl: join result to itself, grouped? | ||
2208 | 786 | # TODO: this would also enable pulling Q and T data together. | ||
2209 | 787 | # TODO: using a SQL-ish language would also allow pivots to be easier -- treat the pivoted data as a view and SELECT from it. | ||
2210 | 788 | cmd_summary => { | ||
2211 | 789 | capt => 'Command Summary', | ||
2212 | 790 | cust => {}, | ||
2213 | 791 | cols => { | ||
2214 | 792 | name => { src => 'name' }, | ||
2215 | 793 | total => { src => 'total' }, | ||
2216 | 794 | value => { src => 'value', agg => 'sum'}, | ||
2217 | 795 | pct => { src => 'value/total', trans => [qw(percent)] }, | ||
2218 | 796 | last_total => { src => 'last_total' }, | ||
2219 | 797 | last_value => { src => 'last_value', agg => 'sum'}, | ||
2220 | 798 | last_pct => { src => 'last_value/last_total', trans => [qw(percent)] }, | ||
2221 | 799 | }, | ||
2222 | 800 | visible => [qw(name value pct last_value last_pct)], | ||
2223 | 801 | filters => [qw()], | ||
2224 | 802 | sort_cols => '-value', | ||
2225 | 803 | sort_dir => '1', | ||
2226 | 804 | innodb => '', | ||
2227 | 805 | group_by => [qw(name)], | ||
2228 | 806 | aggregate => 1, | ||
2229 | 807 | }, | ||
2230 | 808 | deadlock_locks => { | ||
2231 | 809 | capt => 'Deadlock Locks', | ||
2232 | 810 | cust => {}, | ||
2233 | 811 | cols => { | ||
2234 | 812 | cxn => { src => 'cxn' }, | ||
2235 | 813 | mysql_thread_id => { src => 'mysql_thread_id' }, | ||
2236 | 814 | dl_txn_num => { src => 'dl_txn_num' }, | ||
2237 | 815 | lock_type => { src => 'lock_type' }, | ||
2238 | 816 | space_id => { src => 'space_id' }, | ||
2239 | 817 | page_no => { src => 'page_no' }, | ||
2240 | 818 | heap_no => { src => 'heap_no' }, | ||
2241 | 819 | n_bits => { src => 'n_bits' }, | ||
2242 | 820 | index => { src => 'index' }, | ||
2243 | 821 | db => { src => 'db' }, | ||
2244 | 822 | tbl => { src => 'table' }, | ||
2245 | 823 | lock_mode => { src => 'lock_mode' }, | ||
2246 | 824 | special => { src => 'special' }, | ||
2247 | 825 | insert_intention => { src => 'insert_intention' }, | ||
2248 | 826 | waiting => { src => 'waiting' }, | ||
2249 | 827 | }, | ||
2250 | 828 | visible => [ qw(cxn mysql_thread_id waiting lock_mode db tbl index special insert_intention)], | ||
2251 | 829 | filters => [], | ||
2252 | 830 | sort_cols => 'cxn mysql_thread_id', | ||
2253 | 831 | sort_dir => '1', | ||
2254 | 832 | innodb => 'dl', | ||
2255 | 833 | group_by => [], | ||
2256 | 834 | aggregate => 0, | ||
2257 | 835 | }, | ||
2258 | 836 | deadlock_transactions => { | ||
2259 | 837 | capt => 'Deadlock Transactions', | ||
2260 | 838 | cust => {}, | ||
2261 | 839 | cols => { | ||
2262 | 840 | cxn => { src => 'cxn' }, | ||
2263 | 841 | active_secs => { src => 'active_secs' }, | ||
2264 | 842 | dl_txn_num => { src => 'dl_txn_num' }, | ||
2265 | 843 | has_read_view => { src => 'has_read_view' }, | ||
2266 | 844 | heap_size => { src => 'heap_size' }, | ||
2267 | 845 | host_and_domain => { src => 'hostname' }, | ||
2268 | 846 | hostname => { src => $exprs{Host} }, | ||
2269 | 847 | ip => { src => 'ip' }, | ||
2270 | 848 | lock_structs => { src => 'lock_structs' }, | ||
2271 | 849 | lock_wait_time => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] }, | ||
2272 | 850 | mysql_thread_id => { src => 'mysql_thread_id' }, | ||
2273 | 851 | os_thread_id => { src => 'os_thread_id' }, | ||
2274 | 852 | proc_no => { src => 'proc_no' }, | ||
2275 | 853 | query_id => { src => 'query_id' }, | ||
2276 | 854 | query_status => { src => 'query_status' }, | ||
2277 | 855 | query_text => { src => 'query_text', trans => [ qw(no_ctrl_char) ] }, | ||
2278 | 856 | row_locks => { src => 'row_locks' }, | ||
2279 | 857 | tables_in_use => { src => 'tables_in_use' }, | ||
2280 | 858 | tables_locked => { src => 'tables_locked' }, | ||
2281 | 859 | thread_decl_inside => { src => 'thread_decl_inside' }, | ||
2282 | 860 | thread_status => { src => 'thread_status' }, | ||
2283 | 861 | 'time' => { src => 'active_secs', trans => [ qw(secs_to_time) ] }, | ||
2284 | 862 | timestring => { src => 'timestring' }, | ||
2285 | 863 | txn_doesnt_see_ge => { src => 'txn_doesnt_see_ge' }, | ||
2286 | 864 | txn_id => { src => 'txn_id' }, | ||
2287 | 865 | txn_sees_lt => { src => 'txn_sees_lt' }, | ||
2288 | 866 | txn_status => { src => 'txn_status' }, | ||
2289 | 867 | truncates => { src => 'truncates' }, | ||
2290 | 868 | undo_log_entries => { src => 'undo_log_entries' }, | ||
2291 | 869 | user => { src => 'user' }, | ||
2292 | 870 | victim => { src => 'victim' }, | ||
2293 | 871 | wait_status => { src => 'lock_wait_status' }, | ||
2294 | 872 | }, | ||
2295 | 873 | visible => [ qw(cxn mysql_thread_id timestring user hostname victim time undo_log_entries lock_structs query_text)], | ||
2296 | 874 | filters => [], | ||
2297 | 875 | sort_cols => 'cxn mysql_thread_id', | ||
2298 | 876 | sort_dir => '1', | ||
2299 | 877 | innodb => 'dl', | ||
2300 | 878 | group_by => [], | ||
2301 | 879 | aggregate => 0, | ||
2302 | 880 | }, | ||
2303 | 881 | explain => { | ||
2304 | 882 | capt => 'EXPLAIN Results', | ||
2305 | 883 | cust => {}, | ||
2306 | 884 | cols => { | ||
2307 | 885 | part_id => { src => 'id' }, | ||
2308 | 886 | select_type => { src => 'select_type' }, | ||
2309 | 887 | tbl => { src => 'table' }, | ||
2310 | 888 | partitions => { src => 'partitions' }, | ||
2311 | 889 | scan_type => { src => 'type' }, | ||
2312 | 890 | possible_keys => { src => 'possible_keys' }, | ||
2313 | 891 | index => { src => 'key' }, | ||
2314 | 892 | key_len => { src => 'key_len' }, | ||
2315 | 893 | index_ref => { src => 'ref' }, | ||
2316 | 894 | num_rows => { src => 'rows' }, | ||
2317 | 895 | special => { src => 'extra' }, | ||
2318 | 896 | }, | ||
2319 | 897 | visible => [ qw(select_type tbl partitions scan_type possible_keys index key_len index_ref num_rows special)], | ||
2320 | 898 | filters => [], | ||
2321 | 899 | sort_cols => '', | ||
2322 | 900 | sort_dir => '1', | ||
2323 | 901 | innodb => '', | ||
2324 | 902 | group_by => [], | ||
2325 | 903 | aggregate => 0, | ||
2326 | 904 | }, | ||
2327 | 905 | file_io_misc => { | ||
2328 | 906 | capt => 'File I/O Misc', | ||
2329 | 907 | cust => {}, | ||
2330 | 908 | cols => { | ||
2331 | 909 | cxn => { src => 'cxn' }, | ||
2332 | 910 | io_bytes_s => { src => 'IB_io_avg_bytes_s' }, | ||
2333 | 911 | io_flush_type => { src => 'IB_io_flush_type' }, | ||
2334 | 912 | io_fsyncs_s => { src => 'IB_io_fsyncs_s' }, | ||
2335 | 913 | io_reads_s => { src => 'IB_io_reads_s' }, | ||
2336 | 914 | io_writes_s => { src => 'IB_io_writes_s' }, | ||
2337 | 915 | os_file_reads => { src => 'IB_io_os_file_reads' }, | ||
2338 | 916 | os_file_writes => { src => 'IB_io_os_file_writes' }, | ||
2339 | 917 | os_fsyncs => { src => 'IB_io_os_fsyncs' }, | ||
2340 | 918 | }, | ||
2341 | 919 | visible => [ qw(cxn os_file_reads os_file_writes os_fsyncs io_reads_s io_writes_s io_bytes_s)], | ||
2342 | 920 | filters => [], | ||
2343 | 921 | sort_cols => 'cxn', | ||
2344 | 922 | sort_dir => '1', | ||
2345 | 923 | innodb => 'io', | ||
2346 | 924 | group_by => [], | ||
2347 | 925 | aggregate => 0, | ||
2348 | 926 | }, | ||
2349 | 927 | fk_error => { | ||
2350 | 928 | capt => 'Foreign Key Error Info', | ||
2351 | 929 | cust => {}, | ||
2352 | 930 | cols => { | ||
2353 | 931 | timestring => { src => 'IB_fk_timestring' }, | ||
2354 | 932 | child_db => { src => 'IB_fk_child_db' }, | ||
2355 | 933 | child_table => { src => 'IB_fk_child_table' }, | ||
2356 | 934 | child_index => { src => 'IB_fk_child_index' }, | ||
2357 | 935 | fk_name => { src => 'IB_fk_fk_name' }, | ||
2358 | 936 | parent_db => { src => 'IB_fk_parent_db' }, | ||
2359 | 937 | parent_table => { src => 'IB_fk_parent_table' }, | ||
2360 | 938 | parent_col => { src => 'IB_fk_parent_col' }, | ||
2361 | 939 | parent_index => { src => 'IB_fk_parent_index' }, | ||
2362 | 940 | attempted_op => { src => 'IB_fk_attempted_op' }, | ||
2363 | 941 | }, | ||
2364 | 942 | visible => [ qw(timestring child_db child_table child_index parent_db parent_table parent_col parent_index fk_name attempted_op)], | ||
2365 | 943 | filters => [], | ||
2366 | 944 | sort_cols => '', | ||
2367 | 945 | sort_dir => '1', | ||
2368 | 946 | innodb => 'fk', | ||
2369 | 947 | group_by => [], | ||
2370 | 948 | aggregate => 0, | ||
2371 | 949 | }, | ||
2372 | 950 | insert_buffers => { | ||
2373 | 951 | capt => 'Insert Buffers', | ||
2374 | 952 | cust => {}, | ||
2375 | 953 | cols => { | ||
2376 | 954 | cxn => { src => 'cxn' }, | ||
2377 | 955 | inserts => { src => 'IB_ib_inserts' }, | ||
2378 | 956 | merged_recs => { src => 'IB_ib_merged_recs' }, | ||
2379 | 957 | merges => { src => 'IB_ib_merges' }, | ||
2380 | 958 | size => { src => 'IB_ib_size' }, | ||
2381 | 959 | free_list_len => { src => 'IB_ib_free_list_len' }, | ||
2382 | 960 | seg_size => { src => 'IB_ib_seg_size' }, | ||
2383 | 961 | }, | ||
2384 | 962 | visible => [ qw(cxn inserts merged_recs merges size free_list_len seg_size)], | ||
2385 | 963 | filters => [], | ||
2386 | 964 | sort_cols => 'cxn', | ||
2387 | 965 | sort_dir => '1', | ||
2388 | 966 | innodb => 'ib', | ||
2389 | 967 | group_by => [], | ||
2390 | 968 | aggregate => 0, | ||
2391 | 969 | }, | ||
2392 | 970 | innodb_locks => { | ||
2393 | 971 | capt => 'InnoDB Locks', | ||
2394 | 972 | cust => {}, | ||
2395 | 973 | cols => { | ||
2396 | 974 | cxn => { src => 'cxn' }, | ||
2397 | 975 | db => { src => 'db' }, | ||
2398 | 976 | index => { src => 'index' }, | ||
2399 | 977 | insert_intention => { src => 'insert_intention' }, | ||
2400 | 978 | lock_mode => { src => 'lock_mode' }, | ||
2401 | 979 | lock_type => { src => 'lock_type' }, | ||
2402 | 980 | lock_wait_time => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] }, | ||
2403 | 981 | mysql_thread_id => { src => 'mysql_thread_id' }, | ||
2404 | 982 | n_bits => { src => 'n_bits' }, | ||
2405 | 983 | page_no => { src => 'page_no' }, | ||
2406 | 984 | space_id => { src => 'space_id' }, | ||
2407 | 985 | special => { src => 'special' }, | ||
2408 | 986 | tbl => { src => 'table' }, | ||
2409 | 987 | 'time' => { src => 'active_secs', hdr => 'Active', trans => [ qw(secs_to_time) ] }, | ||
2410 | 988 | txn_id => { src => 'txn_id' }, | ||
2411 | 989 | waiting => { src => 'waiting' }, | ||
2412 | 990 | }, | ||
2413 | 991 | visible => [ qw(cxn mysql_thread_id lock_type waiting lock_wait_time time lock_mode db tbl index insert_intention special)], | ||
2414 | 992 | filters => [], | ||
2415 | 993 | sort_cols => 'cxn -lock_wait_time', | ||
2416 | 994 | sort_dir => '1', | ||
2417 | 995 | innodb => 'tx', | ||
2418 | 996 | colors => [ | ||
2419 | 997 | { col => 'lock_wait_time', op => '>', arg => 60, color => 'red' }, | ||
2420 | 998 | { col => 'lock_wait_time', op => '>', arg => 30, color => 'yellow' }, | ||
2421 | 999 | { col => 'lock_wait_time', op => '>', arg => 10, color => 'green' }, | ||
2422 | 1000 | ], | ||
2423 | 1001 | group_by => [], | ||
2424 | 1002 | aggregate => 0, | ||
2425 | 1003 | }, | ||
2426 | 1004 | innodb_transactions => { | ||
2427 | 1005 | capt => 'InnoDB Transactions', | ||
2428 | 1006 | cust => {}, | ||
2429 | 1007 | cols => { | ||
2430 | 1008 | cxn => { src => 'cxn' }, | ||
2431 | 1009 | active_secs => { src => 'active_secs' }, | ||
2432 | 1010 | has_read_view => { src => 'has_read_view' }, | ||
2433 | 1011 | heap_size => { src => 'heap_size' }, | ||
2434 | 1012 | hostname => { src => $exprs{Host} }, | ||
2435 | 1013 | ip => { src => 'ip' }, | ||
2436 | 1014 | wait_status => { src => 'lock_wait_status' }, | ||
2437 | 1015 | lock_wait_time => { src => 'lock_wait_time', trans => [ qw(secs_to_time) ] }, | ||
2438 | 1016 | lock_structs => { src => 'lock_structs' }, | ||
2439 | 1017 | mysql_thread_id => { src => 'mysql_thread_id' }, | ||
2440 | 1018 | os_thread_id => { src => 'os_thread_id' }, | ||
2441 | 1019 | proc_no => { src => 'proc_no' }, | ||
2442 | 1020 | query_id => { src => 'query_id' }, | ||
2443 | 1021 | query_status => { src => 'query_status' }, | ||
2444 | 1022 | query_text => { src => 'query_text', trans => [ qw(no_ctrl_char) ] }, | ||
2445 | 1023 | txn_time_remain => { src => $exprs{TxnTimeRemain}, trans => [ qw(secs_to_time) ] }, | ||
2446 | 1024 | row_locks => { src => 'row_locks' }, | ||
2447 | 1025 | tables_in_use => { src => 'tables_in_use' }, | ||
2448 | 1026 | tables_locked => { src => 'tables_locked' }, | ||
2449 | 1027 | thread_decl_inside => { src => 'thread_decl_inside' }, | ||
2450 | 1028 | thread_status => { src => 'thread_status' }, | ||
2451 | 1029 | 'time' => { src => 'active_secs', trans => [ qw(secs_to_time) ], agg => 'sum' }, | ||
2452 | 1030 | txn_doesnt_see_ge => { src => 'txn_doesnt_see_ge' }, | ||
2453 | 1031 | txn_id => { src => 'txn_id' }, | ||
2454 | 1032 | txn_sees_lt => { src => 'txn_sees_lt' }, | ||
2455 | 1033 | txn_status => { src => 'txn_status', minw => 10, maxw => 10 }, | ||
2456 | 1034 | undo_log_entries => { src => 'undo_log_entries' }, | ||
2457 | 1035 | user => { src => 'user', maxw => 10 }, | ||
2458 | 1036 | cnt => { src => 'mysql_thread_id', minw => 0 }, | ||
2459 | 1037 | }, | ||
2460 | 1038 | visible => [ qw(cxn cnt mysql_thread_id user hostname txn_status time undo_log_entries query_text)], | ||
2461 | 1039 | filters => [ qw( hide_self hide_inactive ) ], | ||
2462 | 1040 | sort_cols => '-active_secs txn_status cxn mysql_thread_id', | ||
2463 | 1041 | sort_dir => '1', | ||
2464 | 1042 | innodb => 'tx', | ||
2465 | 1043 | hide_caption => 1, | ||
2466 | 1044 | colors => [ | ||
2467 | 1045 | { col => 'wait_status', op => 'eq', arg => 'LOCK WAIT', color => 'black on_red' }, | ||
2468 | 1046 | { col => 'time', op => '>', arg => 600, color => 'red' }, | ||
2469 | 1047 | { col => 'time', op => '>', arg => 300, color => 'yellow' }, | ||
2470 | 1048 | { col => 'time', op => '>', arg => 60, color => 'green' }, | ||
2471 | 1049 | { col => 'time', op => '>', arg => 30, color => 'cyan' }, | ||
2472 | 1050 | { col => 'txn_status', op => 'eq', arg => 'not started', color => 'white' }, | ||
2473 | 1051 | ], | ||
2474 | 1052 | group_by => [ qw(cxn txn_status) ], | ||
2475 | 1053 | aggregate => 0, | ||
2476 | 1054 | }, | ||
2477 | 1055 | io_threads => { | ||
2478 | 1056 | capt => 'I/O Threads', | ||
2479 | 1057 | cust => {}, | ||
2480 | 1058 | cols => { | ||
2481 | 1059 | cxn => { src => 'cxn' }, | ||
2482 | 1060 | thread => { src => 'thread' }, | ||
2483 | 1061 | thread_purpose => { src => 'purpose' }, | ||
2484 | 1062 | event_set => { src => 'event_set' }, | ||
2485 | 1063 | thread_status => { src => 'state' }, | ||
2486 | 1064 | }, | ||
2487 | 1065 | visible => [ qw(cxn thread thread_purpose thread_status)], | ||
2488 | 1066 | filters => [ qw() ], | ||
2489 | 1067 | sort_cols => 'cxn thread', | ||
2490 | 1068 | sort_dir => '1', | ||
2491 | 1069 | innodb => 'io', | ||
2492 | 1070 | group_by => [], | ||
2493 | 1071 | aggregate => 0, | ||
2494 | 1072 | }, | ||
2495 | 1073 | log_statistics => { | ||
2496 | 1074 | capt => 'Log Statistics', | ||
2497 | 1075 | cust => {}, | ||
2498 | 1076 | cols => { | ||
2499 | 1077 | cxn => { src => 'cxn' }, | ||
2500 | 1078 | last_chkp => { src => 'IB_lg_last_chkp' }, | ||
2501 | 1079 | log_flushed_to => { src => 'IB_lg_log_flushed_to' }, | ||
2502 | 1080 | log_ios_done => { src => 'IB_lg_log_ios_done' }, | ||
2503 | 1081 | log_ios_s => { src => 'IB_lg_log_ios_s' }, | ||
2504 | 1082 | log_seq_no => { src => 'IB_lg_log_seq_no' }, | ||
2505 | 1083 | pending_chkp_writes => { src => 'IB_lg_pending_chkp_writes' }, | ||
2506 | 1084 | pending_log_writes => { src => 'IB_lg_pending_log_writes' }, | ||
2507 | 1085 | }, | ||
2508 | 1086 | visible => [ qw(cxn log_seq_no log_flushed_to last_chkp log_ios_done log_ios_s)], | ||
2509 | 1087 | filters => [], | ||
2510 | 1088 | sort_cols => 'cxn', | ||
2511 | 1089 | sort_dir => '1', | ||
2512 | 1090 | innodb => 'lg', | ||
2513 | 1091 | group_by => [], | ||
2514 | 1092 | aggregate => 0, | ||
2515 | 1093 | }, | ||
2516 | 1094 | master_status => { | ||
2517 | 1095 | capt => 'Master Status', | ||
2518 | 1096 | cust => {}, | ||
2519 | 1097 | cols => { | ||
2520 | 1098 | cxn => { src => 'cxn' }, | ||
2521 | 1099 | binlog_do_db => { src => 'binlog_do_db' }, | ||
2522 | 1100 | binlog_ignore_db => { src => 'binlog_ignore_db' }, | ||
2523 | 1101 | master_file => { src => 'file' }, | ||
2524 | 1102 | master_pos => { src => 'position' }, | ||
2525 | 1103 | binlog_cache_overflow => { src => '(Binlog_cache_disk_use||0)/(Binlog_cache_use||1)', trans => [ qw(percent) ] }, | ||
2526 | 1104 | }, | ||
2527 | 1105 | visible => [ qw(cxn master_file master_pos binlog_cache_overflow)], | ||
2528 | 1106 | filters => [ qw(cxn_is_master) ], | ||
2529 | 1107 | sort_cols => 'cxn', | ||
2530 | 1108 | sort_dir => '1', | ||
2531 | 1109 | innodb => '', | ||
2532 | 1110 | group_by => [], | ||
2533 | 1111 | aggregate => 0, | ||
2534 | 1112 | }, | ||
2535 | 1113 | pending_io => { | ||
2536 | 1114 | capt => 'Pending I/O', | ||
2537 | 1115 | cust => {}, | ||
2538 | 1116 | cols => { | ||
2539 | 1117 | cxn => { src => 'cxn' }, | ||
2540 | 1118 | p_normal_aio_reads => { src => 'IB_io_pending_normal_aio_reads' }, | ||
2541 | 1119 | p_aio_writes => { src => 'IB_io_pending_aio_writes' }, | ||
2542 | 1120 | p_ibuf_aio_reads => { src => 'IB_io_pending_ibuf_aio_reads' }, | ||
2543 | 1121 | p_sync_ios => { src => 'IB_io_pending_sync_ios' }, | ||
2544 | 1122 | p_buf_pool_flushes => { src => 'IB_io_pending_buffer_pool_flushes' }, | ||
2545 | 1123 | p_log_flushes => { src => 'IB_io_pending_log_flushes' }, | ||
2546 | 1124 | p_log_ios => { src => 'IB_io_pending_log_ios' }, | ||
2547 | 1125 | p_preads => { src => 'IB_io_pending_preads' }, | ||
2548 | 1126 | p_pwrites => { src => 'IB_io_pending_pwrites' }, | ||
2549 | 1127 | }, | ||
2550 | 1128 | visible => [ qw(cxn p_normal_aio_reads p_aio_writes p_ibuf_aio_reads p_sync_ios p_log_flushes p_log_ios)], | ||
2551 | 1129 | filters => [], | ||
2552 | 1130 | sort_cols => 'cxn', | ||
2553 | 1131 | sort_dir => '1', | ||
2554 | 1132 | innodb => 'io', | ||
2555 | 1133 | group_by => [], | ||
2556 | 1134 | aggregate => 0, | ||
2557 | 1135 | }, | ||
2558 | 1136 | open_tables => { | ||
2559 | 1137 | capt => 'Open Tables', | ||
2560 | 1138 | cust => {}, | ||
2561 | 1139 | cols => { | ||
2562 | 1140 | cxn => { src => 'cxn' }, | ||
2563 | 1141 | db => { src => 'database' }, | ||
2564 | 1142 | tbl => { src => 'table' }, | ||
2565 | 1143 | num_times_open => { src => 'in_use' }, | ||
2566 | 1144 | is_name_locked => { src => 'name_locked' }, | ||
2567 | 1145 | }, | ||
2568 | 1146 | visible => [ qw(cxn db tbl num_times_open is_name_locked)], | ||
2569 | 1147 | filters => [ qw(table_is_open) ], | ||
2570 | 1148 | sort_cols => '-num_times_open cxn db tbl', | ||
2571 | 1149 | sort_dir => '1', | ||
2572 | 1150 | innodb => '', | ||
2573 | 1151 | group_by => [], | ||
2574 | 1152 | aggregate => 0, | ||
2575 | 1153 | }, | ||
2576 | 1154 | page_statistics => { | ||
2577 | 1155 | capt => 'Page Statistics', | ||
2578 | 1156 | cust => {}, | ||
2579 | 1157 | cols => { | ||
2580 | 1158 | cxn => { src => 'cxn' }, | ||
2581 | 1159 | pages_read => { src => 'IB_bp_pages_read' }, | ||
2582 | 1160 | pages_written => { src => 'IB_bp_pages_written' }, | ||
2583 | 1161 | pages_created => { src => 'IB_bp_pages_created' }, | ||
2584 | 1162 | page_reads_sec => { src => 'IB_bp_page_reads_sec' }, | ||
2585 | 1163 | page_writes_sec => { src => 'IB_bp_page_writes_sec' }, | ||
2586 | 1164 | page_creates_sec => { src => 'IB_bp_page_creates_sec' }, | ||
2587 | 1165 | }, | ||
2588 | 1166 | visible => [ qw(cxn pages_read pages_written pages_created page_reads_sec page_writes_sec page_creates_sec)], | ||
2589 | 1167 | filters => [], | ||
2590 | 1168 | sort_cols => 'cxn', | ||
2591 | 1169 | sort_dir => '1', | ||
2592 | 1170 | innodb => 'bp', | ||
2593 | 1171 | group_by => [], | ||
2594 | 1172 | aggregate => 0, | ||
2595 | 1173 | }, | ||
2596 | 1174 | processlist => { | ||
2597 | 1175 | capt => 'MySQL Process List', | ||
2598 | 1176 | cust => {}, | ||
2599 | 1177 | cols => { | ||
2600 | 1178 | cxn => { src => 'cxn', minw => 6, maxw => 10 }, | ||
2601 | 1179 | mysql_thread_id => { src => 'id', minw => 6, maxw => 0 }, | ||
2602 | 1180 | user => { src => 'user', minw => 5, maxw => 8 }, | ||
2603 | 1181 | hostname => { src => $exprs{Host}, minw => 13, maxw => 8, }, | ||
2604 | 1182 | port => { src => $exprs{Port}, minw => 0, maxw => 0, }, | ||
2605 | 1183 | host_and_port => { src => 'host', minw => 0, maxw => 0 }, | ||
2606 | 1184 | db => { src => 'db', minw => 6, maxw => 12 }, | ||
2607 | 1185 | cmd => { src => 'command', minw => 5, maxw => 0 }, | ||
2608 | 1186 | time => { src => 'time', minw => 5, maxw => 0, trans => [ qw(secs_to_time) ], agg => 'sum' }, | ||
2609 | 1187 | state => { src => 'state', minw => 0, maxw => 0 }, | ||
2610 | 1188 | info => { src => 'info', minw => 0, maxw => 0, trans => [ qw(no_ctrl_char) ] }, | ||
2611 | 1189 | cnt => { src => 'id', minw => 0, maxw => 0 }, | ||
2612 | 1190 | }, | ||
2613 | 1191 | visible => [ qw(cxn cmd cnt mysql_thread_id user hostname db time info)], | ||
2614 | 1192 | filters => [ qw(hide_self hide_inactive hide_slave_io) ], | ||
2615 | 1193 | sort_cols => '-time cxn hostname mysql_thread_id', | ||
2616 | 1194 | sort_dir => '1', | ||
2617 | 1195 | innodb => '', | ||
2618 | 1196 | hide_caption => 1, | ||
2619 | 1197 | colors => [ | ||
2620 | 1198 | { col => 'state', op => 'eq', arg => 'Locked', color => 'black on_red' }, | ||
2621 | 1199 | { col => 'cmd', op => 'eq', arg => 'Sleep', color => 'white' }, | ||
2622 | 1200 | { col => 'user', op => 'eq', arg => 'system user', color => 'white' }, | ||
2623 | 1201 | { col => 'cmd', op => 'eq', arg => 'Connect', color => 'white' }, | ||
2624 | 1202 | { col => 'cmd', op => 'eq', arg => 'Binlog Dump', color => 'white' }, | ||
2625 | 1203 | { col => 'time', op => '>', arg => 600, color => 'red' }, | ||
2626 | 1204 | { col => 'time', op => '>', arg => 120, color => 'yellow' }, | ||
2627 | 1205 | { col => 'time', op => '>', arg => 60, color => 'green' }, | ||
2628 | 1206 | { col => 'time', op => '>', arg => 30, color => 'cyan' }, | ||
2629 | 1207 | ], | ||
2630 | 1208 | group_by => [qw(cxn cmd)], | ||
2631 | 1209 | aggregate => 0, | ||
2632 | 1210 | }, | ||
2633 | 1211 | |||
2634 | 1212 | # TODO: some more columns: | ||
2635 | 1213 | # kb_used=hdr='BufUsed' minw='0' num='0' src='percent(1 - ((Key_blocks_unused * key_cache_block_size) / (key_buffer_size||1)))' dec='0' trans='' tbl='q_header' just='-' user='1' maxw='0' label='User-defined' | ||
2636 | 1214 | # retries=hdr='Retries' minw='0' num='0' src='Slave_retried_transactions' dec='0' trans='' tbl='slave_sql_status' just='-' user='1' maxw='0' label='User-defined' | ||
2637 | 1215 | # thd=hdr='Thd' minw='0' num='0' src='Threads_connected' dec='0' trans='' tbl='slave_sql_status' just='-' user='1' maxw='0' label='User-defined' | ||
2638 | 1216 | |||
2639 | 1217 | q_header => { | ||
2640 | 1218 | capt => 'Q-mode Header', | ||
2641 | 1219 | cust => {}, | ||
2642 | 1220 | cols => { | ||
2643 | 1221 | cxn => { src => 'cxn' }, | ||
2644 | 1222 | questions => { src => 'Questions' }, | ||
2645 | 1223 | qps => { src => 'Questions/Uptime_hires', dec => 1, trans => [qw(shorten)] }, | ||
2646 | 1224 | load => { src => $exprs{ServerLoad}, dec => 1, trans => [qw(shorten)] }, | ||
2647 | 1225 | slow => { src => 'Slow_queries', dec => 1, trans => [qw(shorten)] }, | ||
2648 | 1226 | q_cache_hit => { src => $exprs{QcacheHitRatio}, dec => 1, trans => [qw(percent)] }, | ||
2649 | 1227 | key_buffer_hit => { src => '1-(Key_reads/(Key_read_requests||1))', dec => 1, trans => [qw(percent)] }, | ||
2650 | 1228 | bps_in => { src => 'Bytes_received/Uptime_hires', dec => 1, trans => [qw(shorten)] }, | ||
2651 | 1229 | bps_out => { src => 'Bytes_sent/Uptime_hires', dec => 1, trans => [qw(shorten)] }, | ||
2652 | 1230 | when => { src => 'when' }, | ||
2653 | 1231 | }, | ||
2654 | 1232 | visible => [ qw(cxn when load qps slow q_cache_hit key_buffer_hit bps_in bps_out)], | ||
2655 | 1233 | filters => [], | ||
2656 | 1234 | sort_cols => 'when cxn', | ||
2657 | 1235 | sort_dir => '1', | ||
2658 | 1236 | innodb => '', | ||
2659 | 1237 | hide_caption => 1, | ||
2660 | 1238 | group_by => [], | ||
2661 | 1239 | aggregate => 0, | ||
2662 | 1240 | }, | ||
2663 | 1241 | row_operations => { | ||
2664 | 1242 | capt => 'InnoDB Row Operations', | ||
2665 | 1243 | cust => {}, | ||
2666 | 1244 | cols => { | ||
2667 | 1245 | cxn => { src => 'cxn' }, | ||
2668 | 1246 | num_inserts => { src => 'IB_ro_num_rows_ins' }, | ||
2669 | 1247 | num_updates => { src => 'IB_ro_num_rows_upd' }, | ||
2670 | 1248 | num_reads => { src => 'IB_ro_num_rows_read' }, | ||
2671 | 1249 | num_deletes => { src => 'IB_ro_num_rows_del' }, | ||
2672 | 1250 | num_inserts_sec => { src => 'IB_ro_ins_sec' }, | ||
2673 | 1251 | num_updates_sec => { src => 'IB_ro_upd_sec' }, | ||
2674 | 1252 | num_reads_sec => { src => 'IB_ro_read_sec' }, | ||
2675 | 1253 | num_deletes_sec => { src => 'IB_ro_del_sec' }, | ||
2676 | 1254 | }, | ||
2677 | 1255 | visible => [ qw(cxn num_inserts num_updates num_reads num_deletes num_inserts_sec | ||
2678 | 1256 | num_updates_sec num_reads_sec num_deletes_sec)], | ||
2679 | 1257 | filters => [], | ||
2680 | 1258 | sort_cols => 'cxn', | ||
2681 | 1259 | sort_dir => '1', | ||
2682 | 1260 | innodb => 'ro', | ||
2683 | 1261 | group_by => [], | ||
2684 | 1262 | aggregate => 0, | ||
2685 | 1263 | }, | ||
2686 | 1264 | row_operation_misc => { | ||
2687 | 1265 | capt => 'Row Operation Misc', | ||
2688 | 1266 | cust => {}, | ||
2689 | 1267 | cols => { | ||
2690 | 1268 | cxn => { src => 'cxn' }, | ||
2691 | 1269 | queries_in_queue => { src => 'IB_ro_queries_in_queue' }, | ||
2692 | 1270 | queries_inside => { src => 'IB_ro_queries_inside' }, | ||
2693 | 1271 | read_views_open => { src => 'IB_ro_read_views_open' }, | ||
2694 | 1272 | main_thread_id => { src => 'IB_ro_main_thread_id' }, | ||
2695 | 1273 | main_thread_proc_no => { src => 'IB_ro_main_thread_proc_no' }, | ||
2696 | 1274 | main_thread_state => { src => 'IB_ro_main_thread_state' }, | ||
2697 | 1275 | num_res_ext => { src => 'IB_ro_n_reserved_extents' }, | ||
2698 | 1276 | }, | ||
2699 | 1277 | visible => [ qw(cxn queries_in_queue queries_inside read_views_open main_thread_state)], | ||
2700 | 1278 | filters => [], | ||
2701 | 1279 | sort_cols => 'cxn', | ||
2702 | 1280 | sort_dir => '1', | ||
2703 | 1281 | innodb => 'ro', | ||
2704 | 1282 | group_by => [], | ||
2705 | 1283 | aggregate => 0, | ||
2706 | 1284 | }, | ||
2707 | 1285 | semaphores => { | ||
2708 | 1286 | capt => 'InnoDB Semaphores', | ||
2709 | 1287 | cust => {}, | ||
2710 | 1288 | cols => { | ||
2711 | 1289 | cxn => { src => 'cxn' }, | ||
2712 | 1290 | mutex_os_waits => { src => 'IB_sm_mutex_os_waits' }, | ||
2713 | 1291 | mutex_spin_rounds => { src => 'IB_sm_mutex_spin_rounds' }, | ||
2714 | 1292 | mutex_spin_waits => { src => 'IB_sm_mutex_spin_waits' }, | ||
2715 | 1293 | reservation_count => { src => 'IB_sm_reservation_count' }, | ||
2716 | 1294 | rw_excl_os_waits => { src => 'IB_sm_rw_excl_os_waits' }, | ||
2717 | 1295 | rw_excl_spins => { src => 'IB_sm_rw_excl_spins' }, | ||
2718 | 1296 | rw_shared_os_waits => { src => 'IB_sm_rw_shared_os_waits' }, | ||
2719 | 1297 | rw_shared_spins => { src => 'IB_sm_rw_shared_spins' }, | ||
2720 | 1298 | signal_count => { src => 'IB_sm_signal_count' }, | ||
2721 | 1299 | wait_array_size => { src => 'IB_sm_wait_array_size' }, | ||
2722 | 1300 | }, | ||
2723 | 1301 | visible => [ qw(cxn mutex_os_waits mutex_spin_waits mutex_spin_rounds | ||
2724 | 1302 | rw_excl_os_waits rw_excl_spins rw_shared_os_waits rw_shared_spins | ||
2725 | 1303 | signal_count reservation_count )], | ||
2726 | 1304 | filters => [], | ||
2727 | 1305 | sort_cols => 'cxn', | ||
2728 | 1306 | sort_dir => '1', | ||
2729 | 1307 | innodb => 'sm', | ||
2730 | 1308 | group_by => [], | ||
2731 | 1309 | aggregate => 0, | ||
2732 | 1310 | }, | ||
2733 | 1311 | slave_io_status => { | ||
2734 | 1312 | capt => 'Slave I/O Status', | ||
2735 | 1313 | cust => {}, | ||
2736 | 1314 | cols => { | ||
2737 | 1315 | cxn => { src => 'cxn' }, | ||
2738 | 1316 | connect_retry => { src => 'connect_retry' }, | ||
2739 | 1317 | master_host => { src => 'master_host', hdr => 'Master'}, | ||
2740 | 1318 | master_log_file => { src => 'master_log_file', hdr => 'File' }, | ||
2741 | 1319 | master_port => { src => 'master_port' }, | ||
2742 | 1320 | master_ssl_allowed => { src => 'master_ssl_allowed' }, | ||
2743 | 1321 | master_ssl_ca_file => { src => 'master_ssl_ca_file' }, | ||
2744 | 1322 | master_ssl_ca_path => { src => 'master_ssl_ca_path' }, | ||
2745 | 1323 | master_ssl_cert => { src => 'master_ssl_cert' }, | ||
2746 | 1324 | master_ssl_cipher => { src => 'master_ssl_cipher' }, | ||
2747 | 1325 | master_ssl_key => { src => 'master_ssl_key' }, | ||
2748 | 1326 | master_user => { src => 'master_user' }, | ||
2749 | 1327 | read_master_log_pos => { src => 'read_master_log_pos', hdr => 'Pos' }, | ||
2750 | 1328 | relay_log_size => { src => 'relay_log_space', trans => [qw(shorten)] }, | ||
2751 | 1329 | slave_io_running => { src => 'slave_io_running', hdr => 'On?' }, | ||
2752 | 1330 | slave_io_state => { src => 'slave_io_state', hdr => 'State' }, | ||
2753 | 1331 | }, | ||
2754 | 1332 | visible => [ qw(cxn master_host slave_io_running master_log_file relay_log_size read_master_log_pos slave_io_state)], | ||
2755 | 1333 | filters => [ qw( cxn_is_slave ) ], | ||
2756 | 1334 | sort_cols => 'slave_io_running cxn', | ||
2757 | 1335 | colors => [ | ||
2758 | 1336 | { col => 'slave_io_running', op => 'ne', arg => 'Yes', color => 'black on_red' }, | ||
2759 | 1337 | ], | ||
2760 | 1338 | sort_dir => '1', | ||
2761 | 1339 | innodb => '', | ||
2762 | 1340 | group_by => [], | ||
2763 | 1341 | aggregate => 0, | ||
2764 | 1342 | }, | ||
2765 | 1343 | slave_sql_status => { | ||
2766 | 1344 | capt => 'Slave SQL Status', | ||
2767 | 1345 | cust => {}, | ||
2768 | 1346 | cols => { | ||
2769 | 1347 | cxn => { src => 'cxn' }, | ||
2770 | 1348 | exec_master_log_pos => { src => 'exec_master_log_pos', hdr => 'Master Pos' }, | ||
2771 | 1349 | last_errno => { src => 'last_errno' }, | ||
2772 | 1350 | last_error => { src => 'last_error' }, | ||
2773 | 1351 | master_host => { src => 'master_host', hdr => 'Master' }, | ||
2774 | 1352 | relay_log_file => { src => 'relay_log_file' }, | ||
2775 | 1353 | relay_log_pos => { src => 'relay_log_pos' }, | ||
2776 | 1354 | relay_log_size => { src => 'relay_log_space', trans => [qw(shorten)] }, | ||
2777 | 1355 | relay_master_log_file => { src => 'relay_master_log_file', hdr => 'Master File' }, | ||
2778 | 1356 | replicate_do_db => { src => 'replicate_do_db' }, | ||
2779 | 1357 | replicate_do_table => { src => 'replicate_do_table' }, | ||
2780 | 1358 | replicate_ignore_db => { src => 'replicate_ignore_db' }, | ||
2781 | 1359 | replicate_ignore_table => { src => 'replicate_ignore_table' }, | ||
2782 | 1360 | replicate_wild_do_table => { src => 'replicate_wild_do_table' }, | ||
2783 | 1361 | replicate_wild_ignore_table => { src => 'replicate_wild_ignore_table' }, | ||
2784 | 1362 | skip_counter => { src => 'skip_counter' }, | ||
2785 | 1363 | slave_sql_running => { src => 'slave_sql_running', hdr => 'On?' }, | ||
2786 | 1364 | until_condition => { src => 'until_condition' }, | ||
2787 | 1365 | until_log_file => { src => 'until_log_file' }, | ||
2788 | 1366 | until_log_pos => { src => 'until_log_pos' }, | ||
2789 | 1367 | time_behind_master => { src => 'seconds_behind_master', trans => [ qw(secs_to_time) ] }, | ||
2790 | 1368 | bytes_behind_master => { src => 'master_log_file && master_log_file eq relay_master_log_file ? read_master_log_pos - exec_master_log_pos : 0', trans => [qw(shorten)] }, | ||
2791 | 1369 | slave_catchup_rate => { src => $exprs{SlaveCatchupRate}, trans => [ qw(set_precision) ] }, | ||
2792 | 1370 | slave_open_temp_tables => { src => 'Slave_open_temp_tables' }, | ||
2793 | 1371 | }, | ||
2794 | 1372 | visible => [ qw(cxn master_host slave_sql_running time_behind_master slave_catchup_rate slave_open_temp_tables relay_log_pos last_error)], | ||
2795 | 1373 | filters => [ qw( cxn_is_slave ) ], | ||
2796 | 1374 | sort_cols => 'slave_sql_running cxn', | ||
2797 | 1375 | sort_dir => '1', | ||
2798 | 1376 | innodb => '', | ||
2799 | 1377 | colors => [ | ||
2800 | 1378 | { col => 'slave_sql_running', op => 'ne', arg => 'Yes', color => 'black on_red' }, | ||
2801 | 1379 | { col => 'time_behind_master', op => '>', arg => 600, color => 'red' }, | ||
2802 | 1380 | { col => 'time_behind_master', op => '>', arg => 60, color => 'yellow' }, | ||
2803 | 1381 | { col => 'time_behind_master', op => '==', arg => 0, color => 'white' }, | ||
2804 | 1382 | ], | ||
2805 | 1383 | group_by => [], | ||
2806 | 1384 | aggregate => 0, | ||
2807 | 1385 | }, | ||
2808 | 1386 | t_header => { | ||
2809 | 1387 | capt => 'T-Mode Header', | ||
2810 | 1388 | cust => {}, | ||
2811 | 1389 | cols => { | ||
2812 | 1390 | cxn => { src => 'cxn' }, | ||
2813 | 1391 | dirty_bufs => { src => $exprs{DirtyBufs}, trans => [qw(percent)] }, | ||
2814 | 1392 | history_list_len => { src => 'IB_tx_history_list_len' }, | ||
2815 | 1393 | lock_structs => { src => 'IB_tx_num_lock_structs' }, | ||
2816 | 1394 | num_txns => { src => $exprs{NumTxns} }, | ||
2817 | 1395 | max_txn => { src => $exprs{MaxTxnTime}, trans => [qw(secs_to_time)] }, | ||
2818 | 1396 | undo_for => { src => 'IB_tx_purge_undo_for' }, | ||
2819 | 1397 | used_bufs => { src => $exprs{BufPoolFill}, trans => [qw(percent)]}, | ||
2820 | 1398 | versions => { src => $exprs{OldVersions} }, | ||
2821 | 1399 | }, | ||
2822 | 1400 | visible => [ qw(cxn history_list_len versions undo_for dirty_bufs used_bufs num_txns max_txn lock_structs)], | ||
2823 | 1401 | filters => [ ], | ||
2824 | 1402 | sort_cols => 'cxn', | ||
2825 | 1403 | sort_dir => '1', | ||
2826 | 1404 | innodb => '', | ||
2827 | 1405 | colors => [], | ||
2828 | 1406 | hide_caption => 1, | ||
2829 | 1407 | group_by => [], | ||
2830 | 1408 | aggregate => 0, | ||
2831 | 1409 | }, | ||
2832 | 1410 | var_status => { | ||
2833 | 1411 | capt => 'Variables & Status', | ||
2834 | 1412 | cust => {}, | ||
2835 | 1413 | cols => {}, # Generated from current varset | ||
2836 | 1414 | visible => [], # Generated from current varset | ||
2837 | 1415 | filters => [], | ||
2838 | 1416 | sort_cols => '', | ||
2839 | 1417 | sort_dir => 1, | ||
2840 | 1418 | innodb => '', | ||
2841 | 1419 | temp => 1, # Do not persist to config file. | ||
2842 | 1420 | hide_caption => 1, | ||
2843 | 1421 | pivot => 0, | ||
2844 | 1422 | group_by => [], | ||
2845 | 1423 | aggregate => 0, | ||
2846 | 1424 | }, | ||
2847 | 1425 | wait_array => { | ||
2848 | 1426 | capt => 'InnoDB Wait Array', | ||
2849 | 1427 | cust => {}, | ||
2850 | 1428 | cols => { | ||
2851 | 1429 | cxn => { src => 'cxn' }, | ||
2852 | 1430 | thread => { src => 'thread' }, | ||
2853 | 1431 | waited_at_filename => { src => 'waited_at_filename' }, | ||
2854 | 1432 | waited_at_line => { src => 'waited_at_line' }, | ||
2855 | 1433 | 'time' => { src => 'waited_secs', trans => [ qw(secs_to_time) ] }, | ||
2856 | 1434 | request_type => { src => 'request_type' }, | ||
2857 | 1435 | lock_mem_addr => { src => 'lock_mem_addr' }, | ||
2858 | 1436 | lock_cfile_name => { src => 'lock_cfile_name' }, | ||
2859 | 1437 | lock_cline => { src => 'lock_cline' }, | ||
2860 | 1438 | writer_thread => { src => 'writer_thread' }, | ||
2861 | 1439 | writer_lock_mode => { src => 'writer_lock_mode' }, | ||
2862 | 1440 | num_readers => { src => 'num_readers' }, | ||
2863 | 1441 | lock_var => { src => 'lock_var' }, | ||
2864 | 1442 | waiters_flag => { src => 'waiters_flag' }, | ||
2865 | 1443 | last_s_file_name => { src => 'last_s_file_name' }, | ||
2866 | 1444 | last_s_line => { src => 'last_s_line' }, | ||
2867 | 1445 | last_x_file_name => { src => 'last_x_file_name' }, | ||
2868 | 1446 | last_x_line => { src => 'last_x_line' }, | ||
2869 | 1447 | cell_waiting => { src => 'cell_waiting' }, | ||
2870 | 1448 | cell_event_set => { src => 'cell_event_set' }, | ||
2871 | 1449 | }, | ||
2872 | 1450 | visible => [ qw(cxn thread time waited_at_filename waited_at_line request_type num_readers lock_var waiters_flag cell_waiting cell_event_set)], | ||
2873 | 1451 | filters => [], | ||
2874 | 1452 | sort_cols => 'cxn -time', | ||
2875 | 1453 | sort_dir => '1', | ||
2876 | 1454 | innodb => 'sm', | ||
2877 | 1455 | group_by => [], | ||
2878 | 1456 | aggregate => 0, | ||
2879 | 1457 | }, | ||
2880 | 1458 | ); | ||
2881 | 1459 | |||
2882 | 1460 | # Initialize %tbl_meta from %columns and do some checks. | ||
2883 | 1461 | foreach my $table_name ( keys %tbl_meta ) { | ||
2884 | 1462 | my $table = $tbl_meta{$table_name}; | ||
2885 | 1463 | my $cols = $table->{cols}; | ||
2886 | 1464 | |||
2887 | 1465 | foreach my $col_name ( keys %$cols ) { | ||
2888 | 1466 | my $col_def = $table->{cols}->{$col_name}; | ||
2889 | 1467 | die "I can't find a column named '$col_name' for '$table_name'" unless $columns{$col_name}; | ||
2890 | 1468 | $columns{$col_name}->{referenced} = 1; | ||
2891 | 1469 | |||
2892 | 1470 | foreach my $prop ( keys %col_props ) { | ||
2893 | 1471 | # Each column gets non-existing values set from %columns or defaults from %col_props. | ||
2894 | 1472 | if ( !$col_def->{$prop} ) { | ||
2895 | 1473 | $col_def->{$prop} | ||
2896 | 1474 | = defined($columns{$col_name}->{$prop}) | ||
2897 | 1475 | ? $columns{$col_name}->{$prop} | ||
2898 | 1476 | : $col_props{$prop}; | ||
2899 | 1477 | } | ||
2900 | 1478 | } | ||
2901 | 1479 | |||
2902 | 1480 | # Ensure transformations and aggregate functions are valid | ||
2903 | 1481 | die "Unknown aggregate function '$col_def->{agg}' " | ||
2904 | 1482 | . "for column '$col_name' in table '$table_name'" | ||
2905 | 1483 | unless exists $agg_funcs{$col_def->{agg}}; | ||
2906 | 1484 | foreach my $trans ( @{$col_def->{trans}} ) { | ||
2907 | 1485 | die "Unknown transformation '$trans' " | ||
2908 | 1486 | . "for column '$col_name' in table '$table_name'" | ||
2909 | 1487 | unless exists $trans_funcs{$trans}; | ||
2910 | 1488 | } | ||
2911 | 1489 | } | ||
2912 | 1490 | |||
2913 | 1491 | # Ensure each column in visible and group_by exists in cols | ||
2914 | 1492 | foreach my $place ( qw(visible group_by) ) { | ||
2915 | 1493 | foreach my $col_name ( @{$table->{$place}} ) { | ||
2916 | 1494 | if ( !exists $cols->{$col_name} ) { | ||
2917 | 1495 | die "Column '$col_name' is listed in '$place' for '$table_name', but doesn't exist"; | ||
2918 | 1496 | } | ||
2919 | 1497 | } | ||
2920 | 1498 | } | ||
2921 | 1499 | |||
2922 | 1500 | # Compile sort and color subroutines | ||
2923 | 1501 | $table->{sort_func} = make_sort_func($table); | ||
2924 | 1502 | $table->{color_func} = make_color_func($table); | ||
2925 | 1503 | } | ||
2926 | 1504 | |||
2927 | 1505 | # This is for code cleanup: | ||
2928 | 1506 | { | ||
2929 | 1507 | my @unused_cols = grep { !$columns{$_}->{referenced} } sort keys %columns; | ||
2930 | 1508 | if ( @unused_cols ) { | ||
2931 | 1509 | die "The following columns are not used: " | ||
2932 | 1510 | . join(' ', @unused_cols); | ||
2933 | 1511 | } | ||
2934 | 1512 | } | ||
2935 | 1513 | |||
2936 | 1514 | # ########################################################################### | ||
2937 | 1515 | # Operating modes {{{3 | ||
2938 | 1516 | # ########################################################################### | ||
2939 | 1517 | my %modes = ( | ||
2940 | 1518 | B => { | ||
2941 | 1519 | hdr => 'InnoDB Buffers', | ||
2942 | 1520 | cust => {}, | ||
2943 | 1521 | note => 'Shows buffer info from InnoDB', | ||
2944 | 1522 | action_for => { | ||
2945 | 1523 | i => { | ||
2946 | 1524 | action => sub { toggle_config('status_inc') }, | ||
2947 | 1525 | label => 'Toggle incremental status display', | ||
2948 | 1526 | }, | ||
2949 | 1527 | }, | ||
2950 | 1528 | display_sub => \&display_B, | ||
2951 | 1529 | connections => [], | ||
2952 | 1530 | server_group => '', | ||
2953 | 1531 | one_connection => 0, | ||
2954 | 1532 | tables => [qw(buffer_pool page_statistics insert_buffers adaptive_hash_index)], | ||
2955 | 1533 | visible_tables => [qw(buffer_pool page_statistics insert_buffers adaptive_hash_index)], | ||
2956 | 1534 | }, | ||
2957 | 1535 | C => { | ||
2958 | 1536 | hdr => 'Command Summary', | ||
2959 | 1537 | cust => {}, | ||
2960 | 1538 | note => 'Shows relative magnitude of variables', | ||
2961 | 1539 | action_for => { | ||
2962 | 1540 | s => { | ||
2963 | 1541 | action => sub { get_config_interactive('cmd_filter') }, | ||
2964 | 1542 | label => 'Choose variable prefix', | ||
2965 | 1543 | }, | ||
2966 | 1544 | }, | ||
2967 | 1545 | display_sub => \&display_C, | ||
2968 | 1546 | connections => [], | ||
2969 | 1547 | server_group => '', | ||
2970 | 1548 | one_connection => 0, | ||
2971 | 1549 | tables => [qw(cmd_summary)], | ||
2972 | 1550 | visible_tables => [qw(cmd_summary)], | ||
2973 | 1551 | }, | ||
2974 | 1552 | D => { | ||
2975 | 1553 | hdr => 'InnoDB Deadlocks', | ||
2976 | 1554 | cust => {}, | ||
2977 | 1555 | note => 'View InnoDB deadlock information', | ||
2978 | 1556 | action_for => { | ||
2979 | 1557 | c => { | ||
2980 | 1558 | action => sub { edit_table('deadlock_transactions') }, | ||
2981 | 1559 | label => 'Choose visible columns', | ||
2982 | 1560 | }, | ||
2983 | 1561 | w => { | ||
2984 | 1562 | action => \&create_deadlock, | ||
2985 | 1563 | label => 'Wipe deadlock status info by creating a deadlock', | ||
2986 | 1564 | }, | ||
2987 | 1565 | }, | ||
2988 | 1566 | display_sub => \&display_D, | ||
2989 | 1567 | connections => [], | ||
2990 | 1568 | server_group => '', | ||
2991 | 1569 | one_connection => 0, | ||
2992 | 1570 | tables => [qw(deadlock_transactions deadlock_locks)], | ||
2993 | 1571 | visible_tables => [qw(deadlock_transactions deadlock_locks)], | ||
2994 | 1572 | }, | ||
2995 | 1573 | F => { | ||
2996 | 1574 | hdr => 'InnoDB FK Err', | ||
2997 | 1575 | cust => {}, | ||
2998 | 1576 | note => 'View the latest InnoDB foreign key error', | ||
2999 | 1577 | action_for => {}, | ||
3000 | 1578 | display_sub => \&display_F, | ||
3001 | 1579 | connections => [], | ||
3002 | 1580 | server_group => '', | ||
3003 | 1581 | one_connection => 1, | ||
3004 | 1582 | tables => [qw(fk_error)], | ||
3005 | 1583 | visible_tables => [qw(fk_error)], | ||
3006 | 1584 | }, | ||
3007 | 1585 | I => { | ||
3008 | 1586 | hdr => 'InnoDB I/O Info', | ||
3009 | 1587 | cust => {}, | ||
3010 | 1588 | note => 'Shows I/O info (i/o, log...) from InnoDB', | ||
3011 | 1589 | action_for => { | ||
3012 | 1590 | i => { | ||
3013 | 1591 | action => sub { toggle_config('status_inc') }, | ||
3014 | 1592 | label => 'Toggle incremental status display', | ||
3015 | 1593 | }, | ||
3016 | 1594 | }, | ||
3017 | 1595 | display_sub => \&display_I, | ||
3018 | 1596 | connections => [], | ||
3019 | 1597 | server_group => '', | ||
3020 | 1598 | one_connection => 0, | ||
3021 | 1599 | tables => [qw(io_threads pending_io file_io_misc log_statistics)], | ||
3022 | 1600 | visible_tables => [qw(io_threads pending_io file_io_misc log_statistics)], | ||
3023 | 1601 | }, | ||
3024 | 1602 | L => { | ||
3025 | 1603 | hdr => 'Locks', | ||
3026 | 1604 | cust => {}, | ||
3027 | 1605 | note => 'Shows transaction locks', | ||
3028 | 1606 | action_for => { | ||
3029 | 1607 | a => { | ||
3030 | 1608 | action => sub { send_cmd_to_servers('CREATE TABLE IF NOT EXISTS test.innodb_lock_monitor(a int) ENGINE=InnoDB', 0, '', []); }, | ||
3031 | 1609 | label => 'Start the InnoDB Lock Monitor', | ||
3032 | 1610 | }, | ||
3033 | 1611 | o => { | ||
3034 | 1612 | action => sub { send_cmd_to_servers('DROP TABLE IF EXISTS test.innodb_lock_monitor', 0, '', []); }, | ||
3035 | 1613 | label => 'Stop the InnoDB Lock Monitor', | ||
3036 | 1614 | }, | ||
3037 | 1615 | }, | ||
3038 | 1616 | display_sub => \&display_L, | ||
3039 | 1617 | connections => [], | ||
3040 | 1618 | server_group => '', | ||
3041 | 1619 | one_connection => 0, | ||
3042 | 1620 | tables => [qw(innodb_locks)], | ||
3043 | 1621 | visible_tables => [qw(innodb_locks)], | ||
3044 | 1622 | }, | ||
3045 | 1623 | M => { | ||
3046 | 1624 | hdr => 'Replication Status', | ||
3047 | 1625 | cust => {}, | ||
3048 | 1626 | note => 'Shows replication (master and slave) status', | ||
3049 | 1627 | action_for => { | ||
3050 | 1628 | a => { | ||
3051 | 1629 | action => sub { send_cmd_to_servers('START SLAVE', 0, 'START SLAVE SQL_THREAD UNTIL MASTER_LOG_FILE = ?, MASTER_LOG_POS = ?', []); }, | ||
3052 | 1630 | label => 'Start slave(s)', | ||
3053 | 1631 | }, | ||
3054 | 1632 | i => { | ||
3055 | 1633 | action => sub { toggle_config('status_inc') }, | ||
3056 | 1634 | label => 'Toggle incremental status display', | ||
3057 | 1635 | }, | ||
3058 | 1636 | o => { | ||
3059 | 1637 | action => sub { send_cmd_to_servers('STOP SLAVE', 0, '', []); }, | ||
3060 | 1638 | label => 'Stop slave(s)', | ||
3061 | 1639 | }, | ||
3062 | 1640 | b => { | ||
3063 | 1641 | action => sub { purge_master_logs() }, | ||
3064 | 1642 | label => 'Purge unused master logs', | ||
3065 | 1643 | }, | ||
3066 | 1644 | }, | ||
3067 | 1645 | display_sub => \&display_M, | ||
3068 | 1646 | connections => [], | ||
3069 | 1647 | server_group => '', | ||
3070 | 1648 | one_connection => 0, | ||
3071 | 1649 | tables => [qw(slave_sql_status slave_io_status master_status)], | ||
3072 | 1650 | visible_tables => [qw(slave_sql_status slave_io_status master_status)], | ||
3073 | 1651 | }, | ||
3074 | 1652 | O => { | ||
3075 | 1653 | hdr => 'Open Tables', | ||
3076 | 1654 | cust => {}, | ||
3077 | 1655 | note => 'Shows open tables in MySQL', | ||
3078 | 1656 | action_for => { | ||
3079 | 1657 | r => { | ||
3080 | 1658 | action => sub { reverse_sort('open_tables'); }, | ||
3081 | 1659 | label => 'Reverse sort order', | ||
3082 | 1660 | }, | ||
3083 | 1661 | s => { | ||
3084 | 1662 | action => sub { choose_sort_cols('open_tables'); }, | ||
3085 | 1663 | label => "Choose sort column", | ||
3086 | 1664 | }, | ||
3087 | 1665 | }, | ||
3088 | 1666 | display_sub => \&display_O, | ||
3089 | 1667 | connections => [], | ||
3090 | 1668 | server_group => '', | ||
3091 | 1669 | one_connection => 0, | ||
3092 | 1670 | tables => [qw(open_tables)], | ||
3093 | 1671 | visible_tables => [qw(open_tables)], | ||
3094 | 1672 | }, | ||
3095 | 1673 | Q => { | ||
3096 | 1674 | hdr => 'Query List', | ||
3097 | 1675 | cust => {}, | ||
3098 | 1676 | note => 'Shows queries from SHOW FULL PROCESSLIST', | ||
3099 | 1677 | action_for => { | ||
3100 | 1678 | a => { | ||
3101 | 1679 | action => sub { toggle_filter('processlist', 'hide_self') }, | ||
3102 | 1680 | label => 'Toggle the innotop process', | ||
3103 | 1681 | }, | ||
3104 | 1682 | c => { | ||
3105 | 1683 | action => sub { edit_table('processlist') }, | ||
3106 | 1684 | label => 'Choose visible columns', | ||
3107 | 1685 | }, | ||
3108 | 1686 | e => { | ||
3109 | 1687 | action => sub { analyze_query('e'); }, | ||
3110 | 1688 | label => "Explain a thread's query", | ||
3111 | 1689 | }, | ||
3112 | 1690 | f => { | ||
3113 | 1691 | action => sub { analyze_query('f'); }, | ||
3114 | 1692 | label => "Show a thread's full query", | ||
3115 | 1693 | }, | ||
3116 | 1694 | h => { | ||
3117 | 1695 | action => sub { toggle_visible_table('Q', 'q_header') }, | ||
3118 | 1696 | label => 'Toggle the header on and off', | ||
3119 | 1697 | }, | ||
3120 | 1698 | i => { | ||
3121 | 1699 | action => sub { toggle_filter('processlist', 'hide_inactive') }, | ||
3122 | 1700 | label => 'Toggle idle processes', | ||
3123 | 1701 | }, | ||
3124 | 1702 | k => { | ||
3125 | 1703 | action => sub { kill_query('CONNECTION') }, | ||
3126 | 1704 | label => "Kill a query's connection", | ||
3127 | 1705 | }, | ||
3128 | 1706 | r => { | ||
3129 | 1707 | action => sub { reverse_sort('processlist'); }, | ||
3130 | 1708 | label => 'Reverse sort order', | ||
3131 | 1709 | }, | ||
3132 | 1710 | s => { | ||
3133 | 1711 | action => sub { choose_sort_cols('processlist'); }, | ||
3134 | 1712 | label => "Change the display's sort column", | ||
3135 | 1713 | }, | ||
3136 | 1714 | x => { | ||
3137 | 1715 | action => sub { kill_query('QUERY') }, | ||
3138 | 1716 | label => "Kill a query", | ||
3139 | 1717 | }, | ||
3140 | 1718 | }, | ||
3141 | 1719 | display_sub => \&display_Q, | ||
3142 | 1720 | connections => [], | ||
3143 | 1721 | server_group => '', | ||
3144 | 1722 | one_connection => 0, | ||
3145 | 1723 | tables => [qw(q_header processlist)], | ||
3146 | 1724 | visible_tables => [qw(q_header processlist)], | ||
3147 | 1725 | }, | ||
3148 | 1726 | R => { | ||
3149 | 1727 | hdr => 'InnoDB Row Ops', | ||
3150 | 1728 | cust => {}, | ||
3151 | 1729 | note => 'Shows InnoDB row operation and semaphore info', | ||
3152 | 1730 | action_for => { | ||
3153 | 1731 | i => { | ||
3154 | 1732 | action => sub { toggle_config('status_inc') }, | ||
3155 | 1733 | label => 'Toggle incremental status display', | ||
3156 | 1734 | }, | ||
3157 | 1735 | }, | ||
3158 | 1736 | display_sub => \&display_R, | ||
3159 | 1737 | connections => [], | ||
3160 | 1738 | server_group => '', | ||
3161 | 1739 | one_connection => 0, | ||
3162 | 1740 | tables => [qw(row_operations row_operation_misc semaphores wait_array)], | ||
3163 | 1741 | visible_tables => [qw(row_operations row_operation_misc semaphores wait_array)], | ||
3164 | 1742 | }, | ||
3165 | 1743 | S => { | ||
3166 | 1744 | hdr => 'Variables & Status', | ||
3167 | 1745 | cust => {}, | ||
3168 | 1746 | note => 'Shows query load statistics a la vmstat', | ||
3169 | 1747 | action_for => { | ||
3170 | 1748 | '>' => { | ||
3171 | 1749 | action => sub { switch_var_set('S_set', 1) }, | ||
3172 | 1750 | label => 'Switch to next variable set', | ||
3173 | 1751 | }, | ||
3174 | 1752 | '<' => { | ||
3175 | 1753 | action => sub { switch_var_set('S_set', -1) }, | ||
3176 | 1754 | label => 'Switch to prev variable set', | ||
3177 | 1755 | }, | ||
3178 | 1756 | c => { | ||
3179 | 1757 | action => sub { | ||
3180 | 1758 | choose_var_set('S_set'); | ||
3181 | 1759 | start_S_mode(); | ||
3182 | 1760 | }, | ||
3183 | 1761 | label => "Choose which set to display", | ||
3184 | 1762 | }, | ||
3185 | 1763 | e => { | ||
3186 | 1764 | action => \&edit_current_var_set, | ||
3187 | 1765 | label => 'Edit the current set of variables', | ||
3188 | 1766 | }, | ||
3189 | 1767 | i => { | ||
3190 | 1768 | action => sub { $clear_screen_sub->(); toggle_config('status_inc') }, | ||
3191 | 1769 | label => 'Toggle incremental status display', | ||
3192 | 1770 | }, | ||
3193 | 1771 | '-' => { | ||
3194 | 1772 | action => sub { set_display_precision(-1) }, | ||
3195 | 1773 | label => 'Decrease fractional display precision', | ||
3196 | 1774 | }, | ||
3197 | 1775 | '+' => { | ||
3198 | 1776 | action => sub { set_display_precision(1) }, | ||
3199 | 1777 | label => 'Increase fractional display precision', | ||
3200 | 1778 | }, | ||
3201 | 1779 | g => { | ||
3202 | 1780 | action => sub { set_s_mode('g') }, | ||
3203 | 1781 | label => 'Switch to graph (tload) view', | ||
3204 | 1782 | }, | ||
3205 | 1783 | s => { | ||
3206 | 1784 | action => sub { set_s_mode('s') }, | ||
3207 | 1785 | label => 'Switch to standard (vmstat) view', | ||
3208 | 1786 | }, | ||
3209 | 1787 | v => { | ||
3210 | 1788 | action => sub { set_s_mode('v') }, | ||
3211 | 1789 | label => 'Switch to pivoted view', | ||
3212 | 1790 | }, | ||
3213 | 1791 | }, | ||
3214 | 1792 | display_sub => \&display_S, | ||
3215 | 1793 | no_clear_screen => 1, | ||
3216 | 1794 | connections => [], | ||
3217 | 1795 | server_group => '', | ||
3218 | 1796 | one_connection => 0, | ||
3219 | 1797 | tables => [qw(var_status)], | ||
3220 | 1798 | visible_tables => [qw(var_status)], | ||
3221 | 1799 | }, | ||
3222 | 1800 | T => { | ||
3223 | 1801 | hdr => 'InnoDB Txns', | ||
3224 | 1802 | cust => {}, | ||
3225 | 1803 | note => 'Shows InnoDB transactions in top-like format', | ||
3226 | 1804 | action_for => { | ||
3227 | 1805 | a => { | ||
3228 | 1806 | action => sub { toggle_filter('innodb_transactions', 'hide_self') }, | ||
3229 | 1807 | label => 'Toggle the innotop process', | ||
3230 | 1808 | }, | ||
3231 | 1809 | c => { | ||
3232 | 1810 | action => sub { edit_table('innodb_transactions') }, | ||
3233 | 1811 | label => 'Choose visible columns', | ||
3234 | 1812 | }, | ||
3235 | 1813 | e => { | ||
3236 | 1814 | action => sub { analyze_query('e'); }, | ||
3237 | 1815 | label => "Explain a thread's query", | ||
3238 | 1816 | }, | ||
3239 | 1817 | f => { | ||
3240 | 1818 | action => sub { analyze_query('f'); }, | ||
3241 | 1819 | label => "Show a thread's full query", | ||
3242 | 1820 | }, | ||
3243 | 1821 | h => { | ||
3244 | 1822 | action => sub { toggle_visible_table('T', 't_header') }, | ||
3245 | 1823 | label => 'Toggle the header on and off', | ||
3246 | 1824 | }, | ||
3247 | 1825 | i => { | ||
3248 | 1826 | action => sub { toggle_filter('innodb_transactions', 'hide_inactive') }, | ||
3249 | 1827 | label => 'Toggle inactive transactions', | ||
3250 | 1828 | }, | ||
3251 | 1829 | k => { | ||
3252 | 1830 | action => sub { kill_query('CONNECTION') }, | ||
3253 | 1831 | label => "Kill a transaction's connection", | ||
3254 | 1832 | }, | ||
3255 | 1833 | r => { | ||
3256 | 1834 | action => sub { reverse_sort('innodb_transactions'); }, | ||
3257 | 1835 | label => 'Reverse sort order', | ||
3258 | 1836 | }, | ||
3259 | 1837 | s => { | ||
3260 | 1838 | action => sub { choose_sort_cols('innodb_transactions'); }, | ||
3261 | 1839 | label => "Change the display's sort column", | ||
3262 | 1840 | }, | ||
3263 | 1841 | x => { | ||
3264 | 1842 | action => sub { kill_query('QUERY') }, | ||
3265 | 1843 | label => "Kill a query", | ||
3266 | 1844 | }, | ||
3267 | 1845 | }, | ||
3268 | 1846 | display_sub => \&display_T, | ||
3269 | 1847 | connections => [], | ||
3270 | 1848 | server_group => '', | ||
3271 | 1849 | one_connection => 0, | ||
3272 | 1850 | tables => [qw(t_header innodb_transactions)], | ||
3273 | 1851 | visible_tables => [qw(t_header innodb_transactions)], | ||
3274 | 1852 | }, | ||
3275 | 1853 | ); | ||
3276 | 1854 | |||
3277 | 1855 | # ########################################################################### | ||
3278 | 1856 | # Global key mappings {{{3 | ||
3279 | 1857 | # Keyed on a single character, which is read from the keyboard. Uppercase | ||
3280 | 1858 | # letters switch modes. Lowercase letters access commands when in a mode. | ||
3281 | 1859 | # These can be overridden by action_for in %modes. | ||
3282 | 1860 | # ########################################################################### | ||
3283 | 1861 | my %action_for = ( | ||
3284 | 1862 | '$' => { | ||
3285 | 1863 | action => \&edit_configuration, | ||
3286 | 1864 | label => 'Edit configuration settings', | ||
3287 | 1865 | }, | ||
3288 | 1866 | '?' => { | ||
3289 | 1867 | action => \&display_help, | ||
3290 | 1868 | label => 'Show help', | ||
3291 | 1869 | }, | ||
3292 | 1870 | '!' => { | ||
3293 | 1871 | action => \&display_license, | ||
3294 | 1872 | label => 'Show license and warranty', | ||
3295 | 1873 | }, | ||
3296 | 1874 | '^' => { | ||
3297 | 1875 | action => \&edit_table, | ||
3298 | 1876 | label => "Edit the displayed table(s)", | ||
3299 | 1877 | }, | ||
3300 | 1878 | '#' => { | ||
3301 | 1879 | action => \&choose_server_groups, | ||
3302 | 1880 | label => 'Select/create server groups', | ||
3303 | 1881 | }, | ||
3304 | 1882 | '@' => { | ||
3305 | 1883 | action => \&choose_servers, | ||
3306 | 1884 | label => 'Select/create server connections', | ||
3307 | 1885 | }, | ||
3308 | 1886 | '/' => { | ||
3309 | 1887 | action => \&add_quick_filter, | ||
3310 | 1888 | label => 'Quickly filter what you see', | ||
3311 | 1889 | }, | ||
3312 | 1890 | '\\' => { | ||
3313 | 1891 | action => \&clear_quick_filters, | ||
3314 | 1892 | label => 'Clear quick-filters', | ||
3315 | 1893 | }, | ||
3316 | 1894 | '%' => { | ||
3317 | 1895 | action => \&choose_filters, | ||
3318 | 1896 | label => 'Choose and edit table filters', | ||
3319 | 1897 | }, | ||
3320 | 1898 | "\t" => { | ||
3321 | 1899 | action => \&next_server_group, | ||
3322 | 1900 | label => 'Switch to the next server group', | ||
3323 | 1901 | key => 'TAB', | ||
3324 | 1902 | }, | ||
3325 | 1903 | '=' => { | ||
3326 | 1904 | action => \&toggle_aggregate, | ||
3327 | 1905 | label => 'Toggle aggregation', | ||
3328 | 1906 | }, | ||
3329 | 1907 | # TODO: can these be auto-generated from %modes? | ||
3330 | 1908 | B => { | ||
3331 | 1909 | action => sub { switch_mode('B') }, | ||
3332 | 1910 | label => '', | ||
3333 | 1911 | }, | ||
3334 | 1912 | C => { | ||
3335 | 1913 | action => sub { switch_mode('C') }, | ||
3336 | 1914 | label => '', | ||
3337 | 1915 | }, | ||
3338 | 1916 | D => { | ||
3339 | 1917 | action => sub { switch_mode('D') }, | ||
3340 | 1918 | label => '', | ||
3341 | 1919 | }, | ||
3342 | 1920 | F => { | ||
3343 | 1921 | action => sub { switch_mode('F') }, | ||
3344 | 1922 | label => '', | ||
3345 | 1923 | }, | ||
3346 | 1924 | I => { | ||
3347 | 1925 | action => sub { switch_mode('I') }, | ||
3348 | 1926 | label => '', | ||
3349 | 1927 | }, | ||
3350 | 1928 | L => { | ||
3351 | 1929 | action => sub { switch_mode('L') }, | ||
3352 | 1930 | label => '', | ||
3353 | 1931 | }, | ||
3354 | 1932 | M => { | ||
3355 | 1933 | action => sub { switch_mode('M') }, | ||
3356 | 1934 | label => '', | ||
3357 | 1935 | }, | ||
3358 | 1936 | O => { | ||
3359 | 1937 | action => sub { switch_mode('O') }, | ||
3360 | 1938 | label => '', | ||
3361 | 1939 | }, | ||
3362 | 1940 | Q => { | ||
3363 | 1941 | action => sub { switch_mode('Q') }, | ||
3364 | 1942 | label => '', | ||
3365 | 1943 | }, | ||
3366 | 1944 | R => { | ||
3367 | 1945 | action => sub { switch_mode('R') }, | ||
3368 | 1946 | label => '', | ||
3369 | 1947 | }, | ||
3370 | 1948 | S => { | ||
3371 | 1949 | action => \&start_S_mode, | ||
3372 | 1950 | label => '', | ||
3373 | 1951 | }, | ||
3374 | 1952 | T => { | ||
3375 | 1953 | action => sub { switch_mode('T') }, | ||
3376 | 1954 | label => '', | ||
3377 | 1955 | }, | ||
3378 | 1956 | d => { | ||
3379 | 1957 | action => sub { get_config_interactive('interval') }, | ||
3380 | 1958 | label => 'Change refresh interval', | ||
3381 | 1959 | }, | ||
3382 | 1960 | n => { action => \&next_server, label => 'Switch to the next connection' }, | ||
3383 | 1961 | p => { action => \&pause, label => 'Pause innotop', }, | ||
3384 | 1962 | q => { action => \&finish, label => 'Quit innotop', }, | ||
3385 | 1963 | ); | ||
3386 | 1964 | |||
3387 | 1965 | # ########################################################################### | ||
3388 | 1966 | # Sleep times after certain statements {{{3 | ||
3389 | 1967 | # ########################################################################### | ||
3390 | 1968 | my %stmt_sleep_time_for = (); | ||
3391 | 1969 | |||
3392 | 1970 | # ########################################################################### | ||
3393 | 1971 | # Config editor key mappings {{{3 | ||
3394 | 1972 | # ########################################################################### | ||
3395 | 1973 | my %cfg_editor_action = ( | ||
3396 | 1974 | c => { | ||
3397 | 1975 | note => 'Edit columns, etc in the displayed table(s)', | ||
3398 | 1976 | func => \&edit_table, | ||
3399 | 1977 | }, | ||
3400 | 1978 | g => { | ||
3401 | 1979 | note => 'Edit general configuration', | ||
3402 | 1980 | func => \&edit_configuration_variables, | ||
3403 | 1981 | }, | ||
3404 | 1982 | k => { | ||
3405 | 1983 | note => 'Edit row-coloring rules', | ||
3406 | 1984 | func => \&edit_color_rules, | ||
3407 | 1985 | }, | ||
3408 | 1986 | p => { | ||
3409 | 1987 | note => 'Manage plugins', | ||
3410 | 1988 | func => \&edit_plugins, | ||
3411 | 1989 | }, | ||
3412 | 1990 | s => { | ||
3413 | 1991 | note => 'Edit server groups', | ||
3414 | 1992 | func => \&edit_server_groups, | ||
3415 | 1993 | }, | ||
3416 | 1994 | S => { | ||
3417 | 1995 | note => 'Edit SQL statement sleep delays', | ||
3418 | 1996 | func => \&edit_stmt_sleep_times, | ||
3419 | 1997 | }, | ||
3420 | 1998 | t => { | ||
3421 | 1999 | note => 'Choose which table(s) to display in this mode', | ||
3422 | 2000 | func => \&choose_mode_tables, | ||
3423 | 2001 | }, | ||
3424 | 2002 | ); | ||
3425 | 2003 | |||
3426 | 2004 | # ########################################################################### | ||
3427 | 2005 | # Color editor key mappings {{{3 | ||
3428 | 2006 | # ########################################################################### | ||
3429 | 2007 | my %color_editor_action = ( | ||
3430 | 2008 | n => { | ||
3431 | 2009 | note => 'Create a new color rule', | ||
3432 | 2010 | func => sub { | ||
3433 | 2011 | my ( $tbl, $idx ) = @_; | ||
3434 | 2012 | my $meta = $tbl_meta{$tbl}; | ||
3435 | 2013 | |||
3436 | 2014 | $clear_screen_sub->(); | ||
3437 | 2015 | my $col; | ||
3438 | 2016 | do { | ||
3439 | 2017 | $col = prompt_list( | ||
3440 | 2018 | 'Choose the target column for the rule', | ||
3441 | 2019 | '', | ||
3442 | 2020 | sub { return keys %{$meta->{cols}} }, | ||
3443 | 2021 | { map { $_ => $meta->{cols}->{$_}->{label} } keys %{$meta->{cols}} }); | ||
3444 | 2022 | } while ( !$col ); | ||
3445 | 2023 | ( $col ) = grep { $_ } split(/\W+/, $col); | ||
3446 | 2024 | return $idx unless $col && exists $meta->{cols}->{$col}; | ||
3447 | 2025 | |||
3448 | 2026 | $clear_screen_sub->(); | ||
3449 | 2027 | my $op; | ||
3450 | 2028 | do { | ||
3451 | 2029 | $op = prompt_list( | ||
3452 | 2030 | 'Choose the comparison operator for the rule', | ||
3453 | 2031 | '', | ||
3454 | 2032 | sub { return keys %comp_ops }, | ||
3455 | 2033 | { map { $_ => $comp_ops{$_} } keys %comp_ops } ); | ||
3456 | 2034 | } until ( $op ); | ||
3457 | 2035 | $op =~ s/\s+//g; | ||
3458 | 2036 | return $idx unless $op && exists $comp_ops{$op}; | ||
3459 | 2037 | |||
3460 | 2038 | my $arg; | ||
3461 | 2039 | do { | ||
3462 | 2040 | $arg = prompt('Specify an argument for the comparison'); | ||
3463 | 2041 | } until defined $arg; | ||
3464 | 2042 | |||
3465 | 2043 | my $color; | ||
3466 | 2044 | do { | ||
3467 | 2045 | $color = prompt_list( | ||
3468 | 2046 | 'Choose the color(s) the row should be when the rule matches', | ||
3469 | 2047 | '', | ||
3470 | 2048 | sub { return keys %ansicolors }, | ||
3471 | 2049 | { map { $_ => $_ } keys %ansicolors } ); | ||
3472 | 2050 | } until defined $color; | ||
3473 | 2051 | $color = join(' ', unique(grep { exists $ansicolors{$_} } split(/\W+/, $color))); | ||
3474 | 2052 | return $idx unless $color; | ||
3475 | 2053 | |||
3476 | 2054 | push @{$tbl_meta{$tbl}->{colors}}, { | ||
3477 | 2055 | col => $col, | ||
3478 | 2056 | op => $op, | ||
3479 | 2057 | arg => $arg, | ||
3480 | 2058 | color => $color | ||
3481 | 2059 | }; | ||
3482 | 2060 | $tbl_meta{$tbl}->{cust}->{colors} = 1; | ||
3483 | 2061 | |||
3484 | 2062 | return $idx; | ||
3485 | 2063 | }, | ||
3486 | 2064 | }, | ||
3487 | 2065 | d => { | ||
3488 | 2066 | note => 'Remove the selected rule', | ||
3489 | 2067 | func => sub { | ||
3490 | 2068 | my ( $tbl, $idx ) = @_; | ||
3491 | 2069 | my @rules = @{ $tbl_meta{$tbl}->{colors} }; | ||
3492 | 2070 | return 0 unless @rules > 0 && $idx < @rules && $idx >= 0; | ||
3493 | 2071 | splice(@{$tbl_meta{$tbl}->{colors}}, $idx, 1); | ||
3494 | 2072 | $tbl_meta{$tbl}->{cust}->{colors} = 1; | ||
3495 | 2073 | return $idx == @rules ? $#rules : $idx; | ||
3496 | 2074 | }, | ||
3497 | 2075 | }, | ||
3498 | 2076 | j => { | ||
3499 | 2077 | note => 'Move highlight down one', | ||
3500 | 2078 | func => sub { | ||
3501 | 2079 | my ( $tbl, $idx ) = @_; | ||
3502 | 2080 | my $num_rules = scalar @{$tbl_meta{$tbl}->{colors}}; | ||
3503 | 2081 | return ($idx + 1) % $num_rules; | ||
3504 | 2082 | }, | ||
3505 | 2083 | }, | ||
3506 | 2084 | k => { | ||
3507 | 2085 | note => 'Move highlight up one', | ||
3508 | 2086 | func => sub { | ||
3509 | 2087 | my ( $tbl, $idx ) = @_; | ||
3510 | 2088 | my $num_rules = scalar @{$tbl_meta{$tbl}->{colors}}; | ||
3511 | 2089 | return ($idx - 1) % $num_rules; | ||
3512 | 2090 | }, | ||
3513 | 2091 | }, | ||
3514 | 2092 | '+' => { | ||
3515 | 2093 | note => 'Move selected rule up one', | ||
3516 | 2094 | func => sub { | ||
3517 | 2095 | my ( $tbl, $idx ) = @_; | ||
3518 | 2096 | my $meta = $tbl_meta{$tbl}; | ||
3519 | 2097 | my $dest = $idx == 0 ? scalar(@{$meta->{colors}} - 1) : $idx - 1; | ||
3520 | 2098 | my $temp = $meta->{colors}->[$idx]; | ||
3521 | 2099 | $meta->{colors}->[$idx] = $meta->{colors}->[$dest]; | ||
3522 | 2100 | $meta->{colors}->[$dest] = $temp; | ||
3523 | 2101 | $meta->{cust}->{colors} = 1; | ||
3524 | 2102 | return $dest; | ||
3525 | 2103 | }, | ||
3526 | 2104 | }, | ||
3527 | 2105 | '-' => { | ||
3528 | 2106 | note => 'Move selected rule down one', | ||
3529 | 2107 | func => sub { | ||
3530 | 2108 | my ( $tbl, $idx ) = @_; | ||
3531 | 2109 | my $meta = $tbl_meta{$tbl}; | ||
3532 | 2110 | my $dest = $idx == scalar(@{$meta->{colors}} - 1) ? 0 : $idx + 1; | ||
3533 | 2111 | my $temp = $meta->{colors}->[$idx]; | ||
3534 | 2112 | $meta->{colors}->[$idx] = $meta->{colors}->[$dest]; | ||
3535 | 2113 | $meta->{colors}->[$dest] = $temp; | ||
3536 | 2114 | $meta->{cust}->{colors} = 1; | ||
3537 | 2115 | return $dest; | ||
3538 | 2116 | }, | ||
3539 | 2117 | }, | ||
3540 | 2118 | ); | ||
3541 | 2119 | |||
3542 | 2120 | # ########################################################################### | ||
3543 | 2121 | # Plugin editor key mappings {{{3 | ||
3544 | 2122 | # ########################################################################### | ||
3545 | 2123 | my %plugin_editor_action = ( | ||
3546 | 2124 | '*' => { | ||
3547 | 2125 | note => 'Toggle selected plugin active/inactive', | ||
3548 | 2126 | func => sub { | ||
3549 | 2127 | my ( $plugins, $idx ) = @_; | ||
3550 | 2128 | my $plugin = $plugins->[$idx]; | ||
3551 | 2129 | $plugin->{active} = $plugin->{active} ? 0 : 1; | ||
3552 | 2130 | return $idx; | ||
3553 | 2131 | }, | ||
3554 | 2132 | }, | ||
3555 | 2133 | j => { | ||
3556 | 2134 | note => 'Move highlight down one', | ||
3557 | 2135 | func => sub { | ||
3558 | 2136 | my ( $plugins, $idx ) = @_; | ||
3559 | 2137 | return ($idx + 1) % scalar(@$plugins); | ||
3560 | 2138 | }, | ||
3561 | 2139 | }, | ||
3562 | 2140 | k => { | ||
3563 | 2141 | note => 'Move highlight up one', | ||
3564 | 2142 | func => sub { | ||
3565 | 2143 | my ( $plugins, $idx ) = @_; | ||
3566 | 2144 | return $idx == 0 ? @$plugins - 1 : $idx - 1; | ||
3567 | 2145 | }, | ||
3568 | 2146 | }, | ||
3569 | 2147 | ); | ||
3570 | 2148 | |||
3571 | 2149 | # ########################################################################### | ||
3572 | 2150 | # Table editor key mappings {{{3 | ||
3573 | 2151 | # ########################################################################### | ||
3574 | 2152 | my %tbl_editor_action = ( | ||
3575 | 2153 | a => { | ||
3576 | 2154 | note => 'Add a column to the table', | ||
3577 | 2155 | func => sub { | ||
3578 | 2156 | my ( $tbl, $col ) = @_; | ||
3579 | 2157 | my @visible_cols = @{ $tbl_meta{$tbl}->{visible} }; | ||
3580 | 2158 | my %all_cols = %{ $tbl_meta{$tbl}->{cols} }; | ||
3581 | 2159 | delete @all_cols{@visible_cols}; | ||
3582 | 2160 | my $choice = prompt_list( | ||
3583 | 2161 | 'Choose a column', | ||
3584 | 2162 | '', | ||
3585 | 2163 | sub { return keys %all_cols; }, | ||
3586 | 2164 | { map { $_ => $all_cols{$_}->{label} || $all_cols{$_}->{hdr} } keys %all_cols }); | ||
3587 | 2165 | if ( $all_cols{$choice} ) { | ||
3588 | 2166 | push @{$tbl_meta{$tbl}->{visible}}, $choice; | ||
3589 | 2167 | $tbl_meta{$tbl}->{cust}->{visible} = 1; | ||
3590 | 2168 | return $choice; | ||
3591 | 2169 | } | ||
3592 | 2170 | return $col; | ||
3593 | 2171 | }, | ||
3594 | 2172 | }, | ||
3595 | 2173 | n => { | ||
3596 | 2174 | note => 'Create a new column and add it to the table', | ||
3597 | 2175 | func => sub { | ||
3598 | 2176 | my ( $tbl, $col ) = @_; | ||
3599 | 2177 | |||
3600 | 2178 | $clear_screen_sub->(); | ||
3601 | 2179 | print word_wrap("Choose a name for the column. This name is not displayed, and is used only " | ||
3602 | 2180 | . "for internal reference. It can contain only lowercase letters, numbers, " | ||
3603 | 2181 | . "and underscores."); | ||
3604 | 2182 | print "\n\n"; | ||
3605 | 2183 | do { | ||
3606 | 2184 | $col = prompt("Enter column name"); | ||
3607 | 2185 | $col = '' if $col =~ m/[^a-z0-9_]/; | ||
3608 | 2186 | } while ( !$col ); | ||
3609 | 2187 | |||
3610 | 2188 | $clear_screen_sub->(); | ||
3611 | 2189 | my $hdr; | ||
3612 | 2190 | do { | ||
3613 | 2191 | $hdr = prompt("Enter column header"); | ||
3614 | 2192 | } while ( !$hdr ); | ||
3615 | 2193 | |||
3616 | 2194 | $clear_screen_sub->(); | ||
3617 | 2195 | print "Choose a source for the column's data\n\n"; | ||
3618 | 2196 | my ( $src, $sub, $err ); | ||
3619 | 2197 | do { | ||
3620 | 2198 | if ( $err ) { | ||
3621 | 2199 | print "Error: $err\n\n"; | ||
3622 | 2200 | } | ||
3623 | 2201 | $src = prompt("Enter column source"); | ||
3624 | 2202 | if ( $src ) { | ||
3625 | 2203 | ( $sub, $err ) = compile_expr($src); | ||
3626 | 2204 | } | ||
3627 | 2205 | } until ( !$err); | ||
3628 | 2206 | |||
3629 | 2207 | # TODO: this duplicates %col_props. | ||
3630 | 2208 | $tbl_meta{$tbl}->{cols}->{$col} = { | ||
3631 | 2209 | hdr => $hdr, | ||
3632 | 2210 | src => $src, | ||
3633 | 2211 | just => '-', | ||
3634 | 2212 | num => 0, | ||
3635 | 2213 | label => 'User-defined', | ||
3636 | 2214 | user => 1, | ||
3637 | 2215 | tbl => $tbl, | ||
3638 | 2216 | minw => 0, | ||
3639 | 2217 | maxw => 0, | ||
3640 | 2218 | trans => [], | ||
3641 | 2219 | func => $sub, | ||
3642 | 2220 | dec => 0, | ||
3643 | 2221 | agg => 0, | ||
3644 | 2222 | aggonly => 0, | ||
3645 | 2223 | }; | ||
3646 | 2224 | |||
3647 | 2225 | $tbl_meta{$tbl}->{visible} = [ unique(@{$tbl_meta{$tbl}->{visible}}, $col) ]; | ||
3648 | 2226 | $tbl_meta{$tbl}->{cust}->{visible} = 1; | ||
3649 | 2227 | return $col; | ||
3650 | 2228 | }, | ||
3651 | 2229 | }, | ||
3652 | 2230 | d => { | ||
3653 | 2231 | note => 'Remove selected column', | ||
3654 | 2232 | func => sub { | ||
3655 | 2233 | my ( $tbl, $col ) = @_; | ||
3656 | 2234 | my @visible_cols = @{ $tbl_meta{$tbl}->{visible} }; | ||
3657 | 2235 | my $idx = 0; | ||
3658 | 2236 | return $col unless @visible_cols > 1; | ||
3659 | 2237 | while ( $visible_cols[$idx] ne $col ) { | ||
3660 | 2238 | $idx++; | ||
3661 | 2239 | } | ||
3662 | 2240 | $tbl_meta{$tbl}->{visible} = [ grep { $_ ne $col } @visible_cols ]; | ||
3663 | 2241 | $tbl_meta{$tbl}->{cust}->{visible} = 1; | ||
3664 | 2242 | return $idx == $#visible_cols ? $visible_cols[$idx - 1] : $visible_cols[$idx + 1]; | ||
3665 | 2243 | }, | ||
3666 | 2244 | }, | ||
3667 | 2245 | e => { | ||
3668 | 2246 | note => 'Edit selected column', | ||
3669 | 2247 | func => sub { | ||
3670 | 2248 | # TODO: make this editor hotkey-driven and give readline support. | ||
3671 | 2249 | my ( $tbl, $col ) = @_; | ||
3672 | 2250 | $clear_screen_sub->(); | ||
3673 | 2251 | my $meta = $tbl_meta{$tbl}->{cols}->{$col}; | ||
3674 | 2252 | my @prop = qw(hdr label src just num minw maxw trans agg); # TODO redundant | ||
3675 | 2253 | |||
3676 | 2254 | my $answer; | ||
3677 | 2255 | do { | ||
3678 | 2256 | # Do what the user asked... | ||
3679 | 2257 | if ( $answer && grep { $_ eq $answer } @prop ) { | ||
3680 | 2258 | # Some properties are arrays, others scalars. | ||
3681 | 2259 | my $ini = ref $col_props{$answer} ? join(' ', @{$meta->{$answer}}) : $meta->{$answer}; | ||
3682 | 2260 | my $val = prompt("New value for $answer", undef, $ini); | ||
3683 | 2261 | $val = [ split(' ', $val) ] if ref($col_props{$answer}); | ||
3684 | 2262 | if ( $answer eq 'trans' ) { | ||
3685 | 2263 | $val = [ unique(grep{ exists $trans_funcs{$_} } @$val) ]; | ||
3686 | 2264 | } | ||
3687 | 2265 | @{$meta}{$answer, 'user', 'tbl' } = ( $val, 1, $tbl ); | ||
3688 | 2266 | } | ||
3689 | 2267 | |||
3690 | 2268 | my @display_lines = ( | ||
3691 | 2269 | '', | ||
3692 | 2270 | "You are editing column $tbl.$col.\n", | ||
3693 | 2271 | ); | ||
3694 | 2272 | |||
3695 | 2273 | push @display_lines, create_table2( | ||
3696 | 2274 | \@prop, | ||
3697 | 2275 | { map { $_ => $_ } @prop }, | ||
3698 | 2276 | { map { $_ => ref $meta->{$_} eq 'ARRAY' ? join(' ', @{$meta->{$_}}) | ||
3699 | 2277 | : ref $meta->{$_} ? '[expression code]' | ||
3700 | 2278 | : $meta->{$_} | ||
3701 | 2279 | } @prop | ||
3702 | 2280 | }, | ||
3703 | 2281 | { sep => ' ' }); | ||
3704 | 2282 | draw_screen(\@display_lines, { raw => 1 }); | ||
3705 | 2283 | print "\n\n"; # One to add space, one to clear readline artifacts | ||
3706 | 2284 | $answer = prompt('Edit what? (q to quit)'); | ||
3707 | 2285 | } while ( $answer ne 'q' ); | ||
3708 | 2286 | |||
3709 | 2287 | return $col; | ||
3710 | 2288 | }, | ||
3711 | 2289 | }, | ||
3712 | 2290 | j => { | ||
3713 | 2291 | note => 'Move highlight down one', | ||
3714 | 2292 | func => sub { | ||
3715 | 2293 | my ( $tbl, $col ) = @_; | ||
3716 | 2294 | my @visible_cols = @{ $tbl_meta{$tbl}->{visible} }; | ||
3717 | 2295 | my $idx = 0; | ||
3718 | 2296 | while ( $visible_cols[$idx] ne $col ) { | ||
3719 | 2297 | $idx++; | ||
3720 | 2298 | } | ||
3721 | 2299 | return $visible_cols[ ($idx + 1) % @visible_cols ]; | ||
3722 | 2300 | }, | ||
3723 | 2301 | }, | ||
3724 | 2302 | k => { | ||
3725 | 2303 | note => 'Move highlight up one', | ||
3726 | 2304 | func => sub { | ||
3727 | 2305 | my ( $tbl, $col ) = @_; | ||
3728 | 2306 | my @visible_cols = @{ $tbl_meta{$tbl}->{visible} }; | ||
3729 | 2307 | my $idx = 0; | ||
3730 | 2308 | while ( $visible_cols[$idx] ne $col ) { | ||
3731 | 2309 | $idx++; | ||
3732 | 2310 | } | ||
3733 | 2311 | return $visible_cols[ $idx - 1 ]; | ||
3734 | 2312 | }, | ||
3735 | 2313 | }, | ||
3736 | 2314 | '+' => { | ||
3737 | 2315 | note => 'Move selected column up one', | ||
3738 | 2316 | func => sub { | ||
3739 | 2317 | my ( $tbl, $col ) = @_; | ||
3740 | 2318 | my $meta = $tbl_meta{$tbl}; | ||
3741 | 2319 | my @visible_cols = @{$meta->{visible}}; | ||
3742 | 2320 | my $idx = 0; | ||
3743 | 2321 | while ( $visible_cols[$idx] ne $col ) { | ||
3744 | 2322 | $idx++; | ||
3745 | 2323 | } | ||
3746 | 2324 | if ( $idx ) { | ||
3747 | 2325 | $visible_cols[$idx] = $visible_cols[$idx - 1]; | ||
3748 | 2326 | $visible_cols[$idx - 1] = $col; | ||
3749 | 2327 | $meta->{visible} = \@visible_cols; | ||
3750 | 2328 | } | ||
3751 | 2329 | else { | ||
3752 | 2330 | shift @{$meta->{visible}}; | ||
3753 | 2331 | push @{$meta->{visible}}, $col; | ||
3754 | 2332 | } | ||
3755 | 2333 | $meta->{cust}->{visible} = 1; | ||
3756 | 2334 | return $col; | ||
3757 | 2335 | }, | ||
3758 | 2336 | }, | ||
3759 | 2337 | '-' => { | ||
3760 | 2338 | note => 'Move selected column down one', | ||
3761 | 2339 | func => sub { | ||
3762 | 2340 | my ( $tbl, $col ) = @_; | ||
3763 | 2341 | my $meta = $tbl_meta{$tbl}; | ||
3764 | 2342 | my @visible_cols = @{$meta->{visible}}; | ||
3765 | 2343 | my $idx = 0; | ||
3766 | 2344 | while ( $visible_cols[$idx] ne $col ) { | ||
3767 | 2345 | $idx++; | ||
3768 | 2346 | } | ||
3769 | 2347 | if ( $idx == $#visible_cols ) { | ||
3770 | 2348 | unshift @{$meta->{visible}}, $col; | ||
3771 | 2349 | pop @{$meta->{visible}}; | ||
3772 | 2350 | } | ||
3773 | 2351 | else { | ||
3774 | 2352 | $visible_cols[$idx] = $visible_cols[$idx + 1]; | ||
3775 | 2353 | $visible_cols[$idx + 1] = $col; | ||
3776 | 2354 | $meta->{visible} = \@visible_cols; | ||
3777 | 2355 | } | ||
3778 | 2356 | $meta->{cust}->{visible} = 1; | ||
3779 | 2357 | return $col; | ||
3780 | 2358 | }, | ||
3781 | 2359 | }, | ||
3782 | 2360 | f => { | ||
3783 | 2361 | note => 'Choose filters', | ||
3784 | 2362 | func => sub { | ||
3785 | 2363 | my ( $tbl, $col ) = @_; | ||
3786 | 2364 | choose_filters($tbl); | ||
3787 | 2365 | return $col; | ||
3788 | 2366 | }, | ||
3789 | 2367 | }, | ||
3790 | 2368 | o => { | ||
3791 | 2369 | note => 'Edit color rules', | ||
3792 | 2370 | func => sub { | ||
3793 | 2371 | my ( $tbl, $col ) = @_; | ||
3794 | 2372 | edit_color_rules($tbl); | ||
3795 | 2373 | return $col; | ||
3796 | 2374 | }, | ||
3797 | 2375 | }, | ||
3798 | 2376 | s => { | ||
3799 | 2377 | note => 'Choose sort columns', | ||
3800 | 2378 | func => sub { | ||
3801 | 2379 | my ( $tbl, $col ) = @_; | ||
3802 | 2380 | choose_sort_cols($tbl); | ||
3803 | 2381 | return $col; | ||
3804 | 2382 | }, | ||
3805 | 2383 | }, | ||
3806 | 2384 | g => { | ||
3807 | 2385 | note => 'Choose group-by (aggregate) columns', | ||
3808 | 2386 | func => sub { | ||
3809 | 2387 | my ( $tbl, $col ) = @_; | ||
3810 | 2388 | choose_group_cols($tbl); | ||
3811 | 2389 | return $col; | ||
3812 | 2390 | }, | ||
3813 | 2391 | }, | ||
3814 | 2392 | ); | ||
3815 | 2393 | |||
3816 | 2394 | # ########################################################################### | ||
3817 | 2395 | # Global variables and environment {{{2 | ||
3818 | 2396 | # ########################################################################### | ||
3819 | 2397 | |||
3820 | 2398 | my @this_term_size; # w_chars, h_chars, w_pix, h_pix | ||
3821 | 2399 | my @last_term_size; # w_chars, h_chars, w_pix, h_pix | ||
3822 | 2400 | my $char; | ||
3823 | 2401 | my $windows = $OSNAME =~ m/MSWin/; | ||
3824 | 2402 | my $have_color = 0; | ||
3825 | 2403 | my $MAX_ULONG = 4294967295; # 2^32-1 | ||
3826 | 2404 | my $num_regex = qr/^[+-]?(?=\d|\.)\d*(?:\.\d+)?(?:E[+-]?\d+|)$/i; | ||
3827 | 2405 | my $int_regex = qr/^\d+$/; | ||
3828 | 2406 | my $bool_regex = qr/^[01]$/; | ||
3829 | 2407 | my $term = undef; | ||
3830 | 2408 | my $file = undef; # File to watch for InnoDB monitor output | ||
3831 | 2409 | my $file_mtime = undef; # Status of watched file | ||
3832 | 2410 | my $file_data = undef; # Last chunk of text read from file | ||
3833 | 2411 | my $innodb_parser = InnoDBParser->new; | ||
3834 | 2412 | |||
3835 | 2413 | my $nonfatal_errs = join('|', | ||
3836 | 2414 | 'Access denied for user', | ||
3837 | 2415 | 'Unknown MySQL server host', | ||
3838 | 2416 | 'Unknown database', | ||
3839 | 2417 | 'Can\'t connect to local MySQL server through socket', | ||
3840 | 2418 | 'Can\'t connect to MySQL server on', | ||
3841 | 2419 | 'MySQL server has gone away', | ||
3842 | 2420 | 'Cannot call SHOW INNODB STATUS', | ||
3843 | 2421 | 'Access denied', | ||
3844 | 2422 | 'AutoCommit', | ||
3845 | 2423 | ); | ||
3846 | 2424 | |||
3847 | 2425 | if ( !$opts{n} ) { | ||
3848 | 2426 | require Term::ReadLine; | ||
3849 | 2427 | $term = Term::ReadLine->new('innotop'); | ||
3850 | 2428 | } | ||
3851 | 2429 | |||
3852 | 2430 | # Stores status, variables, innodb status, master/slave status etc. | ||
3853 | 2431 | # Keyed on connection name. Each entry is a hashref of current and past data sets, | ||
3854 | 2432 | # keyed on clock tick. | ||
3855 | 2433 | my %vars; | ||
3856 | 2434 | my %info_gotten = (); # Which things have been retrieved for the current clock tick. | ||
3857 | 2435 | |||
3858 | 2436 | # Stores info on currently displayed queries: cxn, connection ID, query text. | ||
3859 | 2437 | my @current_queries; | ||
3860 | 2438 | |||
3861 | 2439 | my $lines_printed = 0; | ||
3862 | 2440 | my $clock = 0; # Incremented with every wake-sleep cycle | ||
3863 | 2441 | my $clearing_deadlocks = 0; | ||
3864 | 2442 | |||
3865 | 2443 | # Find the home directory; it's different on different OSes. | ||
3866 | 2444 | my $homepath = $ENV{HOME} || $ENV{HOMEPATH} || $ENV{USERPROFILE} || '.'; | ||
3867 | 2445 | |||
3868 | 2446 | # If terminal coloring is available, use it. The only function I want from | ||
3869 | 2447 | # the module is the colored() function. | ||
3870 | 2448 | eval { | ||
3871 | 2449 | if ( !$opts{n} ) { | ||
3872 | 2450 | if ( $windows ) { | ||
3873 | 2451 | require Win32::Console::ANSI; | ||
3874 | 2452 | } | ||
3875 | 2453 | require Term::ANSIColor; | ||
3876 | 2454 | import Term::ANSIColor qw(colored); | ||
3877 | 2455 | $have_color = 1; | ||
3878 | 2456 | } | ||
3879 | 2457 | }; | ||
3880 | 2458 | if ( $EVAL_ERROR || $opts{n} ) { | ||
3881 | 2459 | # If there was an error, manufacture my own colored() function that does no | ||
3882 | 2460 | # coloring. | ||
3883 | 2461 | *colored = sub { pop @_; @_; }; | ||
3884 | 2462 | } | ||
3885 | 2463 | |||
3886 | 2464 | if ( $opts{n} ) { | ||
3887 | 2465 | $clear_screen_sub = sub {}; | ||
3888 | 2466 | } | ||
3889 | 2467 | elsif ( $windows ) { | ||
3890 | 2468 | $clear_screen_sub = sub { $lines_printed = 0; system("cls") }; | ||
3891 | 2469 | } | ||
3892 | 2470 | else { | ||
3893 | 2471 | my $clear = `clear`; | ||
3894 | 2472 | $clear_screen_sub = sub { $lines_printed = 0; print $clear }; | ||
3895 | 2473 | } | ||
3896 | 2474 | |||
3897 | 2475 | # ########################################################################### | ||
3898 | 2476 | # Config storage. {{{2 | ||
3899 | 2477 | # ########################################################################### | ||
3900 | 2478 | my %config = ( | ||
3901 | 2479 | color => { | ||
3902 | 2480 | val => $have_color, | ||
3903 | 2481 | note => 'Whether to use terminal coloring', | ||
3904 | 2482 | conf => 'ALL', | ||
3905 | 2483 | pat => $bool_regex, | ||
3906 | 2484 | }, | ||
3907 | 2485 | cmd_filter => { | ||
3908 | 2486 | val => 'Com_', | ||
3909 | 2487 | note => 'Prefix for values in C mode', | ||
3910 | 2488 | conf => [qw(C)], | ||
3911 | 2489 | }, | ||
3912 | 2490 | plugin_dir => { | ||
3913 | 2491 | val => "$homepath/.innotop/plugins", | ||
3914 | 2492 | note => 'Directory where plugins can be found', | ||
3915 | 2493 | conf => 'ALL', | ||
3916 | 2494 | }, | ||
3917 | 2495 | show_percent => { | ||
3918 | 2496 | val => 1, | ||
3919 | 2497 | note => 'Show the % symbol after percentages', | ||
3920 | 2498 | conf => 'ALL', | ||
3921 | 2499 | pat => $bool_regex, | ||
3922 | 2500 | }, | ||
3923 | 2501 | skip_innodb => { | ||
3924 | 2502 | val => 0, | ||
3925 | 2503 | note => 'Disable SHOW INNODB STATUS', | ||
3926 | 2504 | conf => 'ALL', | ||
3927 | 2505 | pat => $bool_regex, | ||
3928 | 2506 | }, | ||
3929 | 2507 | S_func => { | ||
3930 | 2508 | val => 's', | ||
3931 | 2509 | note => 'What to display in S mode: graph, status, pivoted status', | ||
3932 | 2510 | conf => [qw(S)], | ||
3933 | 2511 | pat => qr/^[gsv]$/, | ||
3934 | 2512 | }, | ||
3935 | 2513 | cxn_timeout => { | ||
3936 | 2514 | val => 28800, | ||
3937 | 2515 | note => 'Connection timeout for keeping unused connections alive', | ||
3938 | 2516 | conf => 'ALL', | ||
3939 | 2517 | pat => $int_regex, | ||
3940 | 2518 | }, | ||
3941 | 2519 | graph_char => { | ||
3942 | 2520 | val => '*', | ||
3943 | 2521 | note => 'Character for drawing graphs', | ||
3944 | 2522 | conf => [ qw(S) ], | ||
3945 | 2523 | pat => qr/^.$/, | ||
3946 | 2524 | }, | ||
3947 | 2525 | show_cxn_errors_in_tbl => { | ||
3948 | 2526 | val => 1, | ||
3949 | 2527 | note => 'Whether to display connection errors as rows in the table', | ||
3950 | 2528 | conf => 'ALL', | ||
3951 | 2529 | pat => $bool_regex, | ||
3952 | 2530 | }, | ||
3953 | 2531 | hide_hdr => { | ||
3954 | 2532 | val => 0, | ||
3955 | 2533 | note => 'Whether to show column headers', | ||
3956 | 2534 | conf => 'ALL', | ||
3957 | 2535 | pat => $bool_regex, | ||
3958 | 2536 | }, | ||
3959 | 2537 | show_cxn_errors => { | ||
3960 | 2538 | val => 1, | ||
3961 | 2539 | note => 'Whether to print connection errors to STDOUT', | ||
3962 | 2540 | conf => 'ALL', | ||
3963 | 2541 | pat => $bool_regex, | ||
3964 | 2542 | }, | ||
3965 | 2543 | readonly => { | ||
3966 | 2544 | val => 0, | ||
3967 | 2545 | note => 'Whether the config file is read-only', | ||
3968 | 2546 | conf => [ qw() ], | ||
3969 | 2547 | pat => $bool_regex, | ||
3970 | 2548 | }, | ||
3971 | 2549 | global => { | ||
3972 | 2550 | val => 1, | ||
3973 | 2551 | note => 'Whether to show GLOBAL variables and status', | ||
3974 | 2552 | conf => 'ALL', | ||
3975 | 2553 | pat => $bool_regex, | ||
3976 | 2554 | }, | ||
3977 | 2555 | header_highlight => { | ||
3978 | 2556 | val => 'bold', | ||
3979 | 2557 | note => 'How to highlight table column headers', | ||
3980 | 2558 | conf => 'ALL', | ||
3981 | 2559 | pat => qr/^(?:bold|underline)$/, | ||
3982 | 2560 | }, | ||
3983 | 2561 | display_table_captions => { | ||
3984 | 2562 | val => 1, | ||
3985 | 2563 | note => 'Whether to put captions on tables', | ||
3986 | 2564 | conf => 'ALL', | ||
3987 | 2565 | pat => $bool_regex, | ||
3988 | 2566 | }, | ||
3989 | 2567 | charset => { | ||
3990 | 2568 | val => 'ascii', | ||
3991 | 2569 | note => 'What type of characters should be displayed in queries (ascii, unicode, none)', | ||
3992 | 2570 | conf => 'ALL', | ||
3993 | 2571 | pat => qr/^(?:ascii|unicode|none)$/, | ||
3994 | 2572 | }, | ||
3995 | 2573 | auto_wipe_dl => { | ||
3996 | 2574 | val => 0, | ||
3997 | 2575 | note => 'Whether to auto-wipe InnoDB deadlocks', | ||
3998 | 2576 | conf => 'ALL', | ||
3999 | 2577 | pat => $bool_regex, | ||
4000 | 2578 | }, | ||
4001 | 2579 | max_height => { | ||
4002 | 2580 | val => 30, | ||
4003 | 2581 | note => '[Win32] Max window height', | ||
4004 | 2582 | conf => 'ALL', | ||
4005 | 2583 | }, | ||
4006 | 2584 | debug => { | ||
4007 | 2585 | val => 0, | ||
4008 | 2586 | pat => $bool_regex, | ||
4009 | 2587 | note => 'Debug mode (more verbose errors, uses more memory)', | ||
4010 | 2588 | conf => 'ALL', | ||
4011 | 2589 | }, | ||
4012 | 2590 | num_digits => { | ||
4013 | 2591 | val => 2, | ||
4014 | 2592 | pat => $int_regex, | ||
4015 | 2593 | note => 'How many digits to show in fractional numbers and percents', | ||
4016 | 2594 | conf => 'ALL', | ||
4017 | 2595 | }, | ||
4018 | 2596 | debugfile => { | ||
4019 | 2597 | val => "$homepath/.innotop/core_dump", | ||
4020 | 2598 | note => 'A debug file in case you are interested in error output', | ||
4021 | 2599 | }, | ||
4022 | 2600 | show_statusbar => { | ||
4023 | 2601 | val => 1, | ||
4024 | 2602 | pat => $bool_regex, | ||
4025 | 2603 | note => 'Whether to show the status bar in the display', | ||
4026 | 2604 | conf => 'ALL', | ||
4027 | 2605 | }, | ||
4028 | 2606 | mode => { | ||
4029 | 2607 | val => "T", | ||
4030 | 2608 | note => "Which mode to start in", | ||
4031 | 2609 | cmdline => 1, | ||
4032 | 2610 | }, | ||
4033 | 2611 | status_inc => { | ||
4034 | 2612 | val => 0, | ||
4035 | 2613 | note => 'Whether to show raw or incremental values for status variables', | ||
4036 | 2614 | pat => $bool_regex, | ||
4037 | 2615 | }, | ||
4038 | 2616 | interval => { | ||
4039 | 2617 | val => 10, | ||
4040 | 2618 | pat => qr/^(?:(?:\d*?[1-9]\d*(?:\.\d*)?)|(?:\d*\.\d*?[1-9]\d*))$/, | ||
4041 | 2619 | note => "The interval at which the display will be refreshed. Fractional values allowed.", | ||
4042 | 2620 | }, | ||
4043 | 2621 | num_status_sets => { | ||
4044 | 2622 | val => 9, | ||
4045 | 2623 | pat => $int_regex, | ||
4046 | 2624 | note => 'How many sets of STATUS and VARIABLES values to show', | ||
4047 | 2625 | conf => [ qw(S) ], | ||
4048 | 2626 | }, | ||
4049 | 2627 | S_set => { | ||
4050 | 2628 | val => 'general', | ||
4051 | 2629 | pat => qr/^\w+$/, | ||
4052 | 2630 | note => 'Which set of variables to display in S (Variables & Status) mode', | ||
4053 | 2631 | conf => [ qw(S) ], | ||
4054 | 2632 | }, | ||
4055 | 2633 | ); | ||
4056 | 2634 | |||
4057 | 2635 | # ########################################################################### | ||
4058 | 2636 | # Config file sections {{{2 | ||
4059 | 2637 | # The configuration file is broken up into sections like a .ini file. This | ||
4060 | 2638 | # variable defines those sections and the subroutines responsible for reading | ||
4061 | 2639 | # and writing them. | ||
4062 | 2640 | # ########################################################################### | ||
4063 | 2641 | my %config_file_sections = ( | ||
4064 | 2642 | plugins => { | ||
4065 | 2643 | reader => \&load_config_plugins, | ||
4066 | 2644 | writer => \&save_config_plugins, | ||
4067 | 2645 | }, | ||
4068 | 2646 | group_by => { | ||
4069 | 2647 | reader => \&load_config_group_by, | ||
4070 | 2648 | writer => \&save_config_group_by, | ||
4071 | 2649 | }, | ||
4072 | 2650 | filters => { | ||
4073 | 2651 | reader => \&load_config_filters, | ||
4074 | 2652 | writer => \&save_config_filters, | ||
4075 | 2653 | }, | ||
4076 | 2654 | active_filters => { | ||
4077 | 2655 | reader => \&load_config_active_filters, | ||
4078 | 2656 | writer => \&save_config_active_filters, | ||
4079 | 2657 | }, | ||
4080 | 2658 | visible_tables => { | ||
4081 | 2659 | reader => \&load_config_visible_tables, | ||
4082 | 2660 | writer => \&save_config_visible_tables, | ||
4083 | 2661 | }, | ||
4084 | 2662 | sort_cols => { | ||
4085 | 2663 | reader => \&load_config_sort_cols, | ||
4086 | 2664 | writer => \&save_config_sort_cols, | ||
4087 | 2665 | }, | ||
4088 | 2666 | active_columns => { | ||
4089 | 2667 | reader => \&load_config_active_columns, | ||
4090 | 2668 | writer => \&save_config_active_columns, | ||
4091 | 2669 | }, | ||
4092 | 2670 | tbl_meta => { | ||
4093 | 2671 | reader => \&load_config_tbl_meta, | ||
4094 | 2672 | writer => \&save_config_tbl_meta, | ||
4095 | 2673 | }, | ||
4096 | 2674 | general => { | ||
4097 | 2675 | reader => \&load_config_config, | ||
4098 | 2676 | writer => \&save_config_config, | ||
4099 | 2677 | }, | ||
4100 | 2678 | connections => { | ||
4101 | 2679 | reader => \&load_config_connections, | ||
4102 | 2680 | writer => \&save_config_connections, | ||
4103 | 2681 | }, | ||
4104 | 2682 | active_connections => { | ||
4105 | 2683 | reader => \&load_config_active_connections, | ||
4106 | 2684 | writer => \&save_config_active_connections, | ||
4107 | 2685 | }, | ||
4108 | 2686 | server_groups => { | ||
4109 | 2687 | reader => \&load_config_server_groups, | ||
4110 | 2688 | writer => \&save_config_server_groups, | ||
4111 | 2689 | }, | ||
4112 | 2690 | active_server_groups => { | ||
4113 | 2691 | reader => \&load_config_active_server_groups, | ||
4114 | 2692 | writer => \&save_config_active_server_groups, | ||
4115 | 2693 | }, | ||
4116 | 2694 | max_values_seen => { | ||
4117 | 2695 | reader => \&load_config_mvs, | ||
4118 | 2696 | writer => \&save_config_mvs, | ||
4119 | 2697 | }, | ||
4120 | 2698 | varsets => { | ||
4121 | 2699 | reader => \&load_config_varsets, | ||
4122 | 2700 | writer => \&save_config_varsets, | ||
4123 | 2701 | }, | ||
4124 | 2702 | colors => { | ||
4125 | 2703 | reader => \&load_config_colors, | ||
4126 | 2704 | writer => \&save_config_colors, | ||
4127 | 2705 | }, | ||
4128 | 2706 | stmt_sleep_times => { | ||
4129 | 2707 | reader => \&load_config_stmt_sleep_times, | ||
4130 | 2708 | writer => \&save_config_stmt_sleep_times, | ||
4131 | 2709 | }, | ||
4132 | 2710 | ); | ||
4133 | 2711 | |||
4134 | 2712 | # Config file sections have some dependencies, so they have to be read/written in order. | ||
4135 | 2713 | my @ordered_config_file_sections = qw(general plugins filters active_filters tbl_meta | ||
4136 | 2714 | connections active_connections server_groups active_server_groups max_values_seen | ||
4137 | 2715 | active_columns sort_cols visible_tables varsets colors stmt_sleep_times | ||
4138 | 2716 | group_by); | ||
4139 | 2717 | |||
4140 | 2718 | # All events for which plugins may register themselves. Entries are arrayrefs. | ||
4141 | 2719 | my %event_listener_for = map { $_ => [] } | ||
4142 | 2720 | qw( | ||
4143 | 2721 | extract_values | ||
4144 | 2722 | set_to_tbl_pre_filter set_to_tbl_pre_sort set_to_tbl_pre_group | ||
4145 | 2723 | set_to_tbl_pre_colorize set_to_tbl_pre_transform set_to_tbl_pre_pivot | ||
4146 | 2724 | set_to_tbl_pre_create set_to_tbl_post_create | ||
4147 | 2725 | draw_screen | ||
4148 | 2726 | ); | ||
4149 | 2727 | |||
4150 | 2728 | # All variables to which plugins have access. | ||
4151 | 2729 | my %pluggable_vars = ( | ||
4152 | 2730 | action_for => \%action_for, | ||
4153 | 2731 | agg_funcs => \%agg_funcs, | ||
4154 | 2732 | config => \%config, | ||
4155 | 2733 | connections => \%connections, | ||
4156 | 2734 | dbhs => \%dbhs, | ||
4157 | 2735 | filters => \%filters, | ||
4158 | 2736 | modes => \%modes, | ||
4159 | 2737 | server_groups => \%server_groups, | ||
4160 | 2738 | tbl_meta => \%tbl_meta, | ||
4161 | 2739 | trans_funcs => \%trans_funcs, | ||
4162 | 2740 | var_sets => \%var_sets, | ||
4163 | 2741 | ); | ||
4164 | 2742 | |||
4165 | 2743 | # ########################################################################### | ||
4166 | 2744 | # Contains logic to generate prepared statements for a given function for a | ||
4167 | 2745 | # given DB connection. Returns a $sth. | ||
4168 | 2746 | # ########################################################################### | ||
4169 | 2747 | my %stmt_maker_for = ( | ||
4170 | 2748 | INNODB_STATUS => sub { | ||
4171 | 2749 | my ( $dbh ) = @_; | ||
4172 | 2750 | return $dbh->prepare(version_ge( $dbh, '5.0.0' ) | ||
4173 | 2751 | ? 'SHOW ENGINE INNODB STATUS' | ||
4174 | 2752 | : 'SHOW INNODB STATUS'); | ||
4175 | 2753 | }, | ||
4176 | 2754 | SHOW_VARIABLES => sub { | ||
4177 | 2755 | my ( $dbh ) = @_; | ||
4178 | 2756 | return $dbh->prepare($config{global}->{val} && version_ge( $dbh, '4.0.3' ) | ||
4179 | 2757 | ? 'SHOW GLOBAL VARIABLES' | ||
4180 | 2758 | : 'SHOW VARIABLES'); | ||
4181 | 2759 | }, | ||
4182 | 2760 | SHOW_STATUS => sub { | ||
4183 | 2761 | my ( $dbh ) = @_; | ||
4184 | 2762 | return $dbh->prepare($config{global}->{val} && version_ge( $dbh, '5.0.2' ) | ||
4185 | 2763 | ? 'SHOW GLOBAL STATUS' | ||
4186 | 2764 | : 'SHOW STATUS'); | ||
4187 | 2765 | }, | ||
4188 | 2766 | KILL_QUERY => sub { | ||
4189 | 2767 | my ( $dbh ) = @_; | ||
4190 | 2768 | return $dbh->prepare(version_ge( $dbh, '5.0.0' ) | ||
4191 | 2769 | ? 'KILL QUERY ?' | ||
4192 | 2770 | : 'KILL ?'); | ||
4193 | 2771 | }, | ||
4194 | 2772 | SHOW_MASTER_LOGS => sub { | ||
4195 | 2773 | my ( $dbh ) = @_; | ||
4196 | 2774 | return $dbh->prepare('SHOW MASTER LOGS'); | ||
4197 | 2775 | }, | ||
4198 | 2776 | SHOW_MASTER_STATUS => sub { | ||
4199 | 2777 | my ( $dbh ) = @_; | ||
4200 | 2778 | return $dbh->prepare('SHOW MASTER STATUS'); | ||
4201 | 2779 | }, | ||
4202 | 2780 | SHOW_SLAVE_STATUS => sub { | ||
4203 | 2781 | my ( $dbh ) = @_; | ||
4204 | 2782 | return $dbh->prepare('SHOW SLAVE STATUS'); | ||
4205 | 2783 | }, | ||
4206 | 2784 | KILL_CONNECTION => sub { | ||
4207 | 2785 | my ( $dbh ) = @_; | ||
4208 | 2786 | return $dbh->prepare(version_ge( $dbh, '5.0.0' ) | ||
4209 | 2787 | ? 'KILL CONNECTION ?' | ||
4210 | 2788 | : 'KILL ?'); | ||
4211 | 2789 | }, | ||
4212 | 2790 | OPEN_TABLES => sub { | ||
4213 | 2791 | my ( $dbh ) = @_; | ||
4214 | 2792 | return version_ge($dbh, '4.0.0') | ||
4215 | 2793 | ? $dbh->prepare('SHOW OPEN TABLES') | ||
4216 | 2794 | : undef; | ||
4217 | 2795 | }, | ||
4218 | 2796 | PROCESSLIST => sub { | ||
4219 | 2797 | my ( $dbh ) = @_; | ||
4220 | 2798 | return $dbh->prepare('SHOW FULL PROCESSLIST'); | ||
4221 | 2799 | }, | ||
4222 | 2800 | ); | ||
4223 | 2801 | |||
4224 | 2802 | # Plugins! | ||
4225 | 2803 | my %plugins = ( | ||
4226 | 2804 | ); | ||
4227 | 2805 | |||
4228 | 2806 | # ########################################################################### | ||
4229 | 2807 | # Run the program {{{1 | ||
4230 | 2808 | # ########################################################################### | ||
4231 | 2809 | |||
4232 | 2810 | # This config variable is only useful for MS Windows because its terminal | ||
4233 | 2811 | # can't tell how tall it is. | ||
4234 | 2812 | if ( !$windows ) { | ||
4235 | 2813 | delete $config{max_height}; | ||
4236 | 2814 | } | ||
4237 | 2815 | |||
4238 | 2816 | # Try to lower my priority. | ||
4239 | 2817 | eval { setpriority(0, 0, getpriority(0, 0) + 10); }; | ||
4240 | 2818 | |||
4241 | 2819 | # Print stuff to the screen immediately, don't wait for a newline. | ||
4242 | 2820 | $OUTPUT_AUTOFLUSH = 1; | ||
4243 | 2821 | |||
4244 | 2822 | # Clear the screen and load the configuration. | ||
4245 | 2823 | $clear_screen_sub->(); | ||
4246 | 2824 | load_config(); | ||
4247 | 2825 | post_process_tbl_meta(); | ||
4248 | 2826 | |||
4249 | 2827 | # Make sure no changes are written to config file in non-interactive mode. | ||
4250 | 2828 | if ( $opts{n} ) { | ||
4251 | 2829 | $config{readonly}->{val} = 1; | ||
4252 | 2830 | } | ||
4253 | 2831 | |||
4254 | 2832 | eval { | ||
4255 | 2833 | |||
4256 | 2834 | # Open the file for InnoDB status | ||
4257 | 2835 | if ( @ARGV ) { | ||
4258 | 2836 | my $filename = shift @ARGV; | ||
4259 | 2837 | open $file, "<", $filename | ||
4260 | 2838 | or die "Cannot open '$filename': $OS_ERROR"; | ||
4261 | 2839 | } | ||
4262 | 2840 | |||
4263 | 2841 | # In certain modes we might have to collect data for two cycles | ||
4264 | 2842 | # before printing anything out, so we need to bump up the count one. | ||
4265 | 2843 | if ( $opts{n} && $opts{count} && $config{status_inc}->{val} | ||
4266 | 2844 | && $config{mode}->{val} =~ m/[S]/ ) | ||
4267 | 2845 | { | ||
4268 | 2846 | $opts{count}++; | ||
4269 | 2847 | } | ||
4270 | 2848 | |||
4271 | 2849 | while (++$clock) { | ||
4272 | 2850 | |||
4273 | 2851 | my $mode = $config{mode}->{val} || 'T'; | ||
4274 | 2852 | if ( !$modes{$mode} ) { | ||
4275 | 2853 | die "Mode '$mode' doesn't exist; try one of these:\n" | ||
4276 | 2854 | . join("\n", map { " $_ $modes{$_}->{hdr}" } sort keys %modes) | ||
4277 | 2855 | . "\n"; | ||
4278 | 2856 | } | ||
4279 | 2857 | |||
4280 | 2858 | if ( !$opts{n} ) { | ||
4281 | 2859 | @last_term_size = @this_term_size; | ||
4282 | 2860 | @this_term_size = Term::ReadKey::GetTerminalSize(\*STDOUT); | ||
4283 | 2861 | if ( $windows ) { | ||
4284 | 2862 | $this_term_size[0]--; | ||
4285 | 2863 | $this_term_size[1] | ||
4286 | 2864 | = min($this_term_size[1], $config{max_height}->{val}); | ||
4287 | 2865 | } | ||
4288 | 2866 | die("Can't read terminal size") unless @this_term_size; | ||
4289 | 2867 | } | ||
4290 | 2868 | |||
4291 | 2869 | # If there's no connection to a database server, we need to fix that... | ||
4292 | 2870 | if ( !%connections ) { | ||
4293 | 2871 | print "You have not defined any database connections.\n\n"; | ||
4294 | 2872 | add_new_dsn(); | ||
4295 | 2873 | } | ||
4296 | 2874 | |||
4297 | 2875 | # See whether there are any connections defined for this mode. If there's only one | ||
4298 | 2876 | # connection total, assume the user wants to just use innotop for a single server | ||
4299 | 2877 | # and don't ask which server to connect to. Also, if we're monitoring from a file, | ||
4300 | 2878 | # we just use the first connection. | ||
4301 | 2879 | if ( !get_connections() ) { | ||
4302 | 2880 | if ( $file || 1 == scalar keys %connections ) { | ||
4303 | 2881 | $modes{$config{mode}->{val}}->{connections} = [ keys %connections ]; | ||
4304 | 2882 | } | ||
4305 | 2883 | else { | ||
4306 | 2884 | choose_connections(); | ||
4307 | 2885 | } | ||
4308 | 2886 | } | ||
4309 | 2887 | |||
4310 | 2888 | # Term::ReadLine might have re-set $OUTPUT_AUTOFLUSH. | ||
4311 | 2889 | $OUTPUT_AUTOFLUSH = 1; | ||
4312 | 2890 | |||
4313 | 2891 | # Prune old data | ||
4314 | 2892 | my $sets = $config{num_status_sets}->{val}; | ||
4315 | 2893 | foreach my $store ( values %vars ) { | ||
4316 | 2894 | delete @{$store}{ grep { $_ < $clock - $sets } keys %$store }; | ||
4317 | 2895 | } | ||
4318 | 2896 | %info_gotten = (); | ||
4319 | 2897 | |||
4320 | 2898 | # Call the subroutine to display this mode. | ||
4321 | 2899 | $modes{$mode}->{display_sub}->(); | ||
4322 | 2900 | |||
4323 | 2901 | # It may be time to quit now. | ||
4324 | 2902 | if ( $opts{count} && $clock >= $opts{count} ) { | ||
4325 | 2903 | finish(); | ||
4326 | 2904 | } | ||
4327 | 2905 | |||
4328 | 2906 | # Wait for a bit. | ||
4329 | 2907 | if ( $opts{n} ) { | ||
4330 | 2908 | sleep($config{interval}->{val}); | ||
4331 | 2909 | } | ||
4332 | 2910 | else { | ||
4333 | 2911 | ReadMode('cbreak'); | ||
4334 | 2912 | $char = ReadKey($config{interval}->{val}); | ||
4335 | 2913 | ReadMode('normal'); | ||
4336 | 2914 | } | ||
4337 | 2915 | |||
4338 | 2916 | # Handle whatever action the key indicates. | ||
4339 | 2917 | do_key_action(); | ||
4340 | 2918 | |||
4341 | 2919 | } | ||
4342 | 2920 | }; | ||
4343 | 2921 | if ( $EVAL_ERROR ) { | ||
4344 | 2922 | core_dump( $EVAL_ERROR ); | ||
4345 | 2923 | } | ||
4346 | 2924 | finish(); | ||
4347 | 2925 | |||
4348 | 2926 | # Subroutines {{{1 | ||
4349 | 2927 | # Mode functions{{{2 | ||
4350 | 2928 | # switch_mode {{{3 | ||
4351 | 2929 | sub switch_mode { | ||
4352 | 2930 | my $mode = shift; | ||
4353 | 2931 | $config{mode}->{val} = $mode; | ||
4354 | 2932 | } | ||
4355 | 2933 | |||
4356 | 2934 | # Prompting functions {{{2 | ||
4357 | 2935 | # prompt_list {{{3 | ||
4358 | 2936 | # Prompts the user for a value, given a question, initial value, | ||
4359 | 2937 | # a completion function and a hashref of hints. | ||
4360 | 2938 | sub prompt_list { | ||
4361 | 2939 | die "Can't call in non-interactive mode" if $opts{n}; | ||
4362 | 2940 | my ( $question, $init, $completion, $hints ) = @_; | ||
4363 | 2941 | if ( $hints ) { | ||
4364 | 2942 | # Figure out how wide the table will be | ||
4365 | 2943 | my $max_name = max(map { length($_) } keys %$hints ); | ||
4366 | 2944 | $max_name ||= 0; | ||
4367 | 2945 | $max_name += 3; | ||
4368 | 2946 | my @meta_rows = create_table2( | ||
4369 | 2947 | [ sort keys %$hints ], | ||
4370 | 2948 | { map { $_ => $_ } keys %$hints }, | ||
4371 | 2949 | { map { $_ => trunc($hints->{$_}, $this_term_size[0] - $max_name) } keys %$hints }, | ||
4372 | 2950 | { sep => ' ' }); | ||
4373 | 2951 | if (@meta_rows > 10) { | ||
4374 | 2952 | # Try to split and stack the meta rows next to each other | ||
4375 | 2953 | my $split = int(@meta_rows / 2); | ||
4376 | 2954 | @meta_rows = stack_next( | ||
4377 | 2955 | [@meta_rows[0..$split - 1]], | ||
4378 | 2956 | [@meta_rows[$split..$#meta_rows]], | ||
4379 | 2957 | { pad => ' | '}, | ||
4380 | 2958 | ); | ||
4381 | 2959 | } | ||
4382 | 2960 | print join( "\n", | ||
4383 | 2961 | '', | ||
4384 | 2962 | map { ref $_ ? colored(@$_) : $_ } create_caption('Choose from', @meta_rows), ''), | ||
4385 | 2963 | "\n"; | ||
4386 | 2964 | } | ||
4387 | 2965 | $term->Attribs->{completion_function} = $completion; | ||
4388 | 2966 | my $answer = $term->readline("$question: ", $init); | ||
4389 | 2967 | $OUTPUT_AUTOFLUSH = 1; | ||
4390 | 2968 | $answer = '' if !defined($answer); | ||
4391 | 2969 | $answer =~ s/\s+$//; | ||
4392 | 2970 | return $answer; | ||
4393 | 2971 | } | ||
4394 | 2972 | |||
4395 | 2973 | # prompt {{{3 | ||
4396 | 2974 | # Prints out a prompt and reads from the keyboard, then validates with the | ||
4397 | 2975 | # validation regex until the input is correct. | ||
4398 | 2976 | sub prompt { | ||
4399 | 2977 | die "Can't call in non-interactive mode" if $opts{n}; | ||
4400 | 2978 | my ( $prompt, $regex, $init, $completion ) = @_; | ||
4401 | 2979 | my $response; | ||
4402 | 2980 | my $success = 0; | ||
4403 | 2981 | do { | ||
4404 | 2982 | if ( $completion ) { | ||
4405 | 2983 | $term->Attribs->{completion_function} = $completion; | ||
4406 | 2984 | } | ||
4407 | 2985 | $response = $term->readline("$prompt: ", $init); | ||
4408 | 2986 | if ( $regex && $response !~ m/$regex/ ) { | ||
4409 | 2987 | print "Invalid response.\n\n"; | ||
4410 | 2988 | } | ||
4411 | 2989 | else { | ||
4412 | 2990 | $success = 1; | ||
4413 | 2991 | } | ||
4414 | 2992 | } while ( !$success ); | ||
4415 | 2993 | $OUTPUT_AUTOFLUSH = 1; | ||
4416 | 2994 | $response =~ s/\s+$//; | ||
4417 | 2995 | return $response; | ||
4418 | 2996 | } | ||
4419 | 2997 | |||
4420 | 2998 | # prompt_noecho {{{3 | ||
4421 | 2999 | # Unfortunately, suppressing echo with Term::ReadLine isn't reliable; the user might not | ||
4422 | 3000 | # have that library, or it might not support that feature. | ||
4423 | 3001 | sub prompt_noecho { | ||
4424 | 3002 | my ( $prompt ) = @_; | ||
4425 | 3003 | print colored("$prompt: ", 'underline'); | ||
4426 | 3004 | my $response; | ||
4427 | 3005 | ReadMode('noecho'); | ||
4428 | 3006 | $response = <STDIN>; | ||
4429 | 3007 | chomp($response); | ||
4430 | 3008 | ReadMode('normal'); | ||
4431 | 3009 | return $response; | ||
4432 | 3010 | } | ||
4433 | 3011 | |||
4434 | 3012 | # do_key_action {{{3 | ||
4435 | 3013 | # Depending on whether a key was read, do something. Keys have certain | ||
4436 | 3014 | # actions defined in lookup tables. Each mode may have its own lookup table, | ||
4437 | 3015 | # which trumps the global table -- so keys can be context-sensitive. The key | ||
4438 | 3016 | # may be read and written in a subroutine, so it's a global. | ||
4439 | 3017 | sub do_key_action { | ||
4440 | 3018 | if ( defined $char ) { | ||
4441 | 3019 | my $mode = $config{mode}->{val}; | ||
4442 | 3020 | my $action | ||
4443 | 3021 | = defined($modes{$mode}->{action_for}->{$char}) | ||
4444 | 3022 | ? $modes{$mode}->{action_for}->{$char}->{action} | ||
4445 | 3023 | : defined($action_for{$char}) | ||
4446 | 3024 | ? $action_for{$char}->{action} | ||
4447 | 3025 | : sub{}; | ||
4448 | 3026 | $action->(); | ||
4449 | 3027 | } | ||
4450 | 3028 | } | ||
4451 | 3029 | |||
4452 | 3030 | # pause {{{3 | ||
4453 | 3031 | sub pause { | ||
4454 | 3032 | die "Can't call in non-interactive mode" if $opts{n}; | ||
4455 | 3033 | my $msg = shift; | ||
4456 | 3034 | print defined($msg) ? "\n$msg" : "\nPress any key to continue"; | ||
4457 | 3035 | ReadMode('cbreak'); | ||
4458 | 3036 | my $char = ReadKey(0); | ||
4459 | 3037 | ReadMode('normal'); | ||
4460 | 3038 | return $char; | ||
4461 | 3039 | } | ||
4462 | 3040 | |||
4463 | 3041 | # reverse_sort {{{3 | ||
4464 | 3042 | sub reverse_sort { | ||
4465 | 3043 | my $tbl = shift; | ||
4466 | 3044 | $tbl_meta{$tbl}->{sort_dir} *= -1; | ||
4467 | 3045 | } | ||
4468 | 3046 | |||
4469 | 3047 | # select_cxn {{{3 | ||
4470 | 3048 | # Selects connection(s). If the mode (or argument list) has only one, returns | ||
4471 | 3049 | # it without prompt. | ||
4472 | 3050 | sub select_cxn { | ||
4473 | 3051 | my ( $prompt, @cxns ) = @_; | ||
4474 | 3052 | if ( !@cxns ) { | ||
4475 | 3053 | @cxns = get_connections(); | ||
4476 | 3054 | } | ||
4477 | 3055 | if ( @cxns == 1 ) { | ||
4478 | 3056 | return $cxns[0]; | ||
4479 | 3057 | } | ||
4480 | 3058 | my $choices = prompt_list( | ||
4481 | 3059 | $prompt, | ||
4482 | 3060 | $cxns[0], | ||
4483 | 3061 | sub{ return @cxns }, | ||
4484 | 3062 | { map { $_ => $connections{$_}->{dsn} } @cxns }); | ||
4485 | 3063 | my @result = unique(grep { my $a = $_; grep { $_ eq $a } @cxns } split(/\s+/, $choices)); | ||
4486 | 3064 | return @result; | ||
4487 | 3065 | } | ||
4488 | 3066 | |||
4489 | 3067 | # kill_query {{{3 | ||
4490 | 3068 | # Kills a connection, or on new versions, optionally a query but not connection. | ||
4491 | 3069 | sub kill_query { | ||
4492 | 3070 | my ( $q_or_c ) = @_; | ||
4493 | 3071 | |||
4494 | 3072 | my $info = choose_thread( | ||
4495 | 3073 | sub { 1 }, | ||
4496 | 3074 | 'Select a thread to kill the ' . $q_or_c, | ||
4497 | 3075 | ); | ||
4498 | 3076 | return unless $info; | ||
4499 | 3077 | return unless pause("Kill $info->{id}?") =~ m/y/i; | ||
4500 | 3078 | |||
4501 | 3079 | eval { | ||
4502 | 3080 | do_stmt($info->{cxn}, $q_or_c eq 'QUERY' ? 'KILL_QUERY' : 'KILL_CONNECTION', $info->{id} ); | ||
4503 | 3081 | }; | ||
4504 | 3082 | |||
4505 | 3083 | if ( $EVAL_ERROR ) { | ||
4506 | 3084 | print "\nError: $EVAL_ERROR"; | ||
4507 | 3085 | pause(); | ||
4508 | 3086 | } | ||
4509 | 3087 | } | ||
4510 | 3088 | |||
4511 | 3089 | # set_display_precision {{{3 | ||
4512 | 3090 | sub set_display_precision { | ||
4513 | 3091 | my $dir = shift; | ||
4514 | 3092 | $config{num_digits}->{val} = min(9, max(0, $config{num_digits}->{val} + $dir)); | ||
4515 | 3093 | } | ||
4516 | 3094 | |||
4517 | 3095 | sub toggle_visible_table { | ||
4518 | 3096 | my ( $mode, $table ) = @_; | ||
4519 | 3097 | my $visible = $modes{$mode}->{visible_tables}; | ||
4520 | 3098 | if ( grep { $_ eq $table } @$visible ) { | ||
4521 | 3099 | $modes{$mode}->{visible_tables} = [ grep { $_ ne $table } @$visible ]; | ||
4522 | 3100 | } | ||
4523 | 3101 | else { | ||
4524 | 3102 | unshift @$visible, $table; | ||
4525 | 3103 | } | ||
4526 | 3104 | $modes{$mode}->{cust}->{visible_tables} = 1; | ||
4527 | 3105 | } | ||
4528 | 3106 | |||
4529 | 3107 | # toggle_filter{{{3 | ||
4530 | 3108 | sub toggle_filter { | ||
4531 | 3109 | my ( $tbl, $filter ) = @_; | ||
4532 | 3110 | my $filters = $tbl_meta{$tbl}->{filters}; | ||
4533 | 3111 | if ( grep { $_ eq $filter } @$filters ) { | ||
4534 | 3112 | $tbl_meta{$tbl}->{filters} = [ grep { $_ ne $filter } @$filters ]; | ||
4535 | 3113 | } | ||
4536 | 3114 | else { | ||
4537 | 3115 | push @$filters, $filter; | ||
4538 | 3116 | } | ||
4539 | 3117 | $tbl_meta{$tbl}->{cust}->{filters} = 1; | ||
4540 | 3118 | } | ||
4541 | 3119 | |||
4542 | 3120 | # toggle_config {{{3 | ||
4543 | 3121 | sub toggle_config { | ||
4544 | 3122 | my ( $key ) = @_; | ||
4545 | 3123 | $config{$key}->{val} ^= 1; | ||
4546 | 3124 | } | ||
4547 | 3125 | |||
4548 | 3126 | # create_deadlock {{{3 | ||
4549 | 3127 | sub create_deadlock { | ||
4550 | 3128 | $clear_screen_sub->(); | ||
4551 | 3129 | |||
4552 | 3130 | print "This function will deliberately cause a small deadlock, " | ||
4553 | 3131 | . "clearing deadlock information from the InnoDB monitor.\n\n"; | ||
4554 | 3132 | |||
4555 | 3133 | my $answer = prompt("Are you sure you want to proceed? Say 'y' if you do"); | ||
4556 | 3134 | return 0 unless $answer eq 'y'; | ||
4557 | 3135 | |||
4558 | 3136 | my ( $cxn ) = select_cxn('Clear on which server? '); | ||
4559 | 3137 | return unless $cxn && exists($connections{$cxn}); | ||
4560 | 3138 | |||
4561 | 3139 | clear_deadlock($cxn); | ||
4562 | 3140 | } | ||
4563 | 3141 | |||
4564 | 3142 | # deadlock_thread {{{3 | ||
4565 | 3143 | sub deadlock_thread { | ||
4566 | 3144 | my ( $id, $tbl, $cxn ) = @_; | ||
4567 | 3145 | |||
4568 | 3146 | eval { | ||
4569 | 3147 | my $dbh = get_new_db_connection($cxn, 1); | ||
4570 | 3148 | my @stmts = ( | ||
4571 | 3149 | "set transaction isolation level serializable", | ||
4572 | 3150 | (version_ge($dbh, '4.0.11') ? "start transaction" : 'begin'), | ||
4573 | 3151 | "select * from $tbl where a = $id", | ||
4574 | 3152 | "update $tbl set a = $id where a <> $id", | ||
4575 | 3153 | ); | ||
4576 | 3154 | |||
4577 | 3155 | foreach my $stmt (@stmts[0..2]) { | ||
4578 | 3156 | $dbh->do($stmt); | ||
4579 | 3157 | } | ||
4580 | 3158 | sleep(1 + $id); | ||
4581 | 3159 | $dbh->do($stmts[-1]); | ||
4582 | 3160 | }; | ||
4583 | 3161 | if ( $EVAL_ERROR ) { | ||
4584 | 3162 | if ( $EVAL_ERROR !~ m/Deadlock found/ ) { | ||
4585 | 3163 | die $EVAL_ERROR; | ||
4586 | 3164 | } | ||
4587 | 3165 | } | ||
4588 | 3166 | exit(0); | ||
4589 | 3167 | } | ||
4590 | 3168 | |||
4591 | 3169 | # Purges unused binlogs on the master, up to but not including the latest log. | ||
4592 | 3170 | # TODO: guess which connections are slaves of a given master. | ||
4593 | 3171 | sub purge_master_logs { | ||
4594 | 3172 | my @cxns = get_connections(); | ||
4595 | 3173 | |||
4596 | 3174 | get_master_slave_status(@cxns); | ||
4597 | 3175 | |||
4598 | 3176 | # Toss out the rows that don't have master/slave status... | ||
4599 | 3177 | my @vars = | ||
4600 | 3178 | grep { $_ && ($_->{file} || $_->{master_host}) } | ||
4601 | 3179 | map { $vars{$_}->{$clock} } @cxns; | ||
4602 | 3180 | @cxns = map { $_->{cxn} } @vars; | ||
4603 | 3181 | |||
4604 | 3182 | # Figure out which master to purge ons. | ||
4605 | 3183 | my @masters = map { $_->{cxn} } grep { $_->{file} } @vars; | ||
4606 | 3184 | my ( $master ) = select_cxn('Which master?', @masters ); | ||
4607 | 3185 | return unless $master; | ||
4608 | 3186 | my ($master_status) = grep { $_->{cxn} eq $master } @vars; | ||
4609 | 3187 | |||
4610 | 3188 | # Figure out the result order (not lexical order) of master logs. | ||
4611 | 3189 | my @master_logs = get_master_logs($master); | ||
4612 | 3190 | my $i = 0; | ||
4613 | 3191 | my %master_logs = map { $_->{log_name} => $i++ } @master_logs; | ||
4614 | 3192 | |||
4615 | 3193 | # Ask which slave(s) are reading from this master. | ||
4616 | 3194 | my @slave_status = grep { $_->{master_host} } @vars; | ||
4617 | 3195 | my @slaves = map { $_->{cxn} } @slave_status; | ||
4618 | 3196 | @slaves = select_cxn("Which slaves are reading from $master?", @slaves); | ||
4619 | 3197 | @slave_status = grep { my $item = $_; grep { $item->{cxn} eq $_ } @slaves } @slave_status; | ||
4620 | 3198 | return unless @slave_status; | ||
4621 | 3199 | |||
4622 | 3200 | # Find the minimum binary log in use. | ||
4623 | 3201 | my $min_log = min(map { $master_logs{$_->{master_log_file}} } @slave_status); | ||
4624 | 3202 | my $log_name = $master_logs[$min_log]->{log_name}; | ||
4625 | 3203 | |||
4626 | 3204 | my $stmt = "PURGE MASTER LOGS TO '$log_name'"; | ||
4627 | 3205 | send_cmd_to_servers($stmt, 0, 'PURGE {MASTER | BINARY} LOGS {TO "log_name" | BEFORE "date"}', [$master]); | ||
4628 | 3206 | } | ||
4629 | 3207 | |||
4630 | 3208 | sub send_cmd_to_servers { | ||
4631 | 3209 | my ( $cmd, $all, $hint, $cxns ) = @_; | ||
4632 | 3210 | if ( $all ) { | ||
4633 | 3211 | @$cxns = get_connections(); | ||
4634 | 3212 | } | ||
4635 | 3213 | elsif ( !@$cxns ) { | ||
4636 | 3214 | @$cxns = select_cxn('Which servers?', @$cxns); | ||
4637 | 3215 | } | ||
4638 | 3216 | if ( $hint ) { | ||
4639 | 3217 | print "\nHint: $hint\n"; | ||
4640 | 3218 | } | ||
4641 | 3219 | $cmd = prompt('Command to send', undef, $cmd); | ||
4642 | 3220 | foreach my $cxn ( @$cxns ) { | ||
4643 | 3221 | eval { | ||
4644 | 3222 | my $sth = do_query($cxn, $cmd); | ||
4645 | 3223 | }; | ||
4646 | 3224 | if ( $EVAL_ERROR ) { | ||
4647 | 3225 | print "Error from $cxn: $EVAL_ERROR\n"; | ||
4648 | 3226 | } | ||
4649 | 3227 | else { | ||
4650 | 3228 | print "Success on $cxn\n"; | ||
4651 | 3229 | } | ||
4652 | 3230 | } | ||
4653 | 3231 | pause(); | ||
4654 | 3232 | } | ||
4655 | 3233 | |||
4656 | 3234 | # Display functions {{{2 | ||
4657 | 3235 | |||
4658 | 3236 | sub set_s_mode { | ||
4659 | 3237 | my ( $func ) = @_; | ||
4660 | 3238 | $clear_screen_sub->(); | ||
4661 | 3239 | $config{S_func}->{val} = $func; | ||
4662 | 3240 | } | ||
4663 | 3241 | |||
4664 | 3242 | # start_S_mode {{{3 | ||
4665 | 3243 | sub start_S_mode { | ||
4666 | 3244 | $clear_screen_sub->(); | ||
4667 | 3245 | switch_mode('S'); | ||
4668 | 3246 | } | ||
4669 | 3247 | |||
4670 | 3248 | # display_B {{{3 | ||
4671 | 3249 | sub display_B { | ||
4672 | 3250 | my @display_lines; | ||
4673 | 3251 | my @cxns = get_connections(); | ||
4674 | 3252 | get_innodb_status(\@cxns); | ||
4675 | 3253 | |||
4676 | 3254 | my @buffer_pool; | ||
4677 | 3255 | my @page_statistics; | ||
4678 | 3256 | my @insert_buffers; | ||
4679 | 3257 | my @adaptive_hash_index; | ||
4680 | 3258 | my %rows_for = ( | ||
4681 | 3259 | buffer_pool => \@buffer_pool, | ||
4682 | 3260 | page_statistics => \@page_statistics, | ||
4683 | 3261 | insert_buffers => \@insert_buffers, | ||
4684 | 3262 | adaptive_hash_index => \@adaptive_hash_index, | ||
4685 | 3263 | ); | ||
4686 | 3264 | |||
4687 | 3265 | my @visible = get_visible_tables(); | ||
4688 | 3266 | my %wanted = map { $_ => 1 } @visible; | ||
4689 | 3267 | |||
4690 | 3268 | foreach my $cxn ( @cxns ) { | ||
4691 | 3269 | my $set = $vars{$cxn}->{$clock}; | ||
4692 | 3270 | my $pre = $vars{$cxn}->{$clock-1} || $set; | ||
4693 | 3271 | |||
4694 | 3272 | if ( $set->{IB_bp_complete} ) { | ||
4695 | 3273 | if ( $wanted{buffer_pool} ) { | ||
4696 | 3274 | push @buffer_pool, extract_values($set, $set, $pre, 'buffer_pool'); | ||
4697 | 3275 | } | ||
4698 | 3276 | if ( $wanted{page_statistics} ) { | ||
4699 | 3277 | push @page_statistics, extract_values($set, $set, $pre, 'page_statistics'); | ||
4700 | 3278 | } | ||
4701 | 3279 | } | ||
4702 | 3280 | if ( $set->{IB_ib_complete} ) { | ||
4703 | 3281 | if ( $wanted{insert_buffers} ) { | ||
4704 | 3282 | push @insert_buffers, extract_values( | ||
4705 | 3283 | $config{status_inc}->{val} ? inc(0, $cxn) : $set, $set, $pre, | ||
4706 | 3284 | 'insert_buffers'); | ||
4707 | 3285 | } | ||
4708 | 3286 | if ( $wanted{adaptive_hash_index} ) { | ||
4709 | 3287 | push @adaptive_hash_index, extract_values($set, $set, $pre, 'adaptive_hash_index'); | ||
4710 | 3288 | } | ||
4711 | 3289 | } | ||
4712 | 3290 | } | ||
4713 | 3291 | |||
4714 | 3292 | my $first_table = 0; | ||
4715 | 3293 | foreach my $tbl ( @visible ) { | ||
4716 | 3294 | push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl); | ||
4717 | 3295 | push @display_lines, get_cxn_errors(@cxns) | ||
4718 | 3296 | if ( $config{debug}->{val} || !$first_table++ ); | ||
4719 | 3297 | } | ||
4720 | 3298 | |||
4721 | 3299 | draw_screen(\@display_lines); | ||
4722 | 3300 | } | ||
4723 | 3301 | |||
4724 | 3302 | # display_C {{{3 | ||
4725 | 3303 | sub display_C { | ||
4726 | 3304 | my @display_lines; | ||
4727 | 3305 | my @cxns = get_connections(); | ||
4728 | 3306 | get_status_info(@cxns); | ||
4729 | 3307 | |||
4730 | 3308 | my @cmd_summary; | ||
4731 | 3309 | my %rows_for = ( | ||
4732 | 3310 | cmd_summary => \@cmd_summary, | ||
4733 | 3311 | ); | ||
4734 | 3312 | |||
4735 | 3313 | my @visible = get_visible_tables(); | ||
4736 | 3314 | my %wanted = map { $_ => 1 } @visible; | ||
4737 | 3315 | |||
4738 | 3316 | # For now, I'm manually pulling these variables out and pivoting. Eventually a SQL-ish | ||
4739 | 3317 | # dialect should let me join a table to a grouped and pivoted table and do this more easily. | ||
4740 | 3318 | # TODO: make it so. | ||
4741 | 3319 | my $prefix = qr/^$config{cmd_filter}->{val}/; # TODO: this is a total hack | ||
4742 | 3320 | my @values; | ||
4743 | 3321 | my ($total, $last_total) = (0, 0); | ||
4744 | 3322 | foreach my $cxn ( @cxns ) { | ||
4745 | 3323 | my $set = $vars{$cxn}->{$clock}; | ||
4746 | 3324 | my $pre = $vars{$cxn}->{$clock-1} || $set; | ||
4747 | 3325 | foreach my $key ( keys %$set ) { | ||
4748 | 3326 | next unless $key =~ m/$prefix/i; | ||
4749 | 3327 | my $val = $set->{$key}; | ||
4750 | 3328 | next unless defined $val && $val =~ m/^\d+$/; | ||
4751 | 3329 | my $last_val = $val - ($pre->{$key} || 0); | ||
4752 | 3330 | $total += $val; | ||
4753 | 3331 | $last_total += $last_val; | ||
4754 | 3332 | push @values, { | ||
4755 | 3333 | name => $key, | ||
4756 | 3334 | value => $val, | ||
4757 | 3335 | last_value => $last_val, | ||
4758 | 3336 | }; | ||
4759 | 3337 | } | ||
4760 | 3338 | } | ||
4761 | 3339 | |||
4762 | 3340 | # Add aggregation and turn into a real set TODO: total hack | ||
4763 | 3341 | if ( $wanted{cmd_summary} ) { | ||
4764 | 3342 | foreach my $value ( @values ) { | ||
4765 | 3343 | @{$value}{qw(total last_total)} = ($total, $last_total); | ||
4766 | 3344 | push @cmd_summary, extract_values($value, $value, $value, 'cmd_summary'); | ||
4767 | 3345 | } | ||
4768 | 3346 | } | ||
4769 | 3347 | |||
4770 | 3348 | my $first_table = 0; | ||
4771 | 3349 | foreach my $tbl ( @visible ) { | ||
4772 | 3350 | push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl); | ||
4773 | 3351 | push @display_lines, get_cxn_errors(@cxns) | ||
4774 | 3352 | if ( $config{debug}->{val} || !$first_table++ ); | ||
4775 | 3353 | } | ||
4776 | 3354 | |||
4777 | 3355 | draw_screen(\@display_lines); | ||
4778 | 3356 | } | ||
4779 | 3357 | |||
4780 | 3358 | # display_D {{{3 | ||
4781 | 3359 | sub display_D { | ||
4782 | 3360 | my @display_lines; | ||
4783 | 3361 | my @cxns = get_connections(); | ||
4784 | 3362 | get_innodb_status(\@cxns); | ||
4785 | 3363 | |||
4786 | 3364 | my @deadlock_transactions; | ||
4787 | 3365 | my @deadlock_locks; | ||
4788 | 3366 | my %rows_for = ( | ||
4789 | 3367 | deadlock_transactions => \@deadlock_transactions, | ||
4790 | 3368 | deadlock_locks => \@deadlock_locks, | ||
4791 | 3369 | ); | ||
4792 | 3370 | |||
4793 | 3371 | my @visible = get_visible_tables(); | ||
4794 | 3372 | my %wanted = map { $_ => 1 } @visible; | ||
4795 | 3373 | |||
4796 | 3374 | foreach my $cxn ( @cxns ) { | ||
4797 | 3375 | my $innodb_status = $vars{$cxn}->{$clock}; | ||
4798 | 3376 | my $prev_status = $vars{$cxn}->{$clock-1} || $innodb_status; | ||
4799 | 3377 | |||
4800 | 3378 | if ( $innodb_status->{IB_dl_timestring} ) { | ||
4801 | 3379 | |||
4802 | 3380 | my $victim = $innodb_status->{IB_dl_rolled_back} || 0; | ||
4803 | 3381 | |||
4804 | 3382 | if ( %wanted ) { | ||
4805 | 3383 | foreach my $txn_id ( keys %{$innodb_status->{IB_dl_txns}} ) { | ||
4806 | 3384 | my $txn = $innodb_status->{IB_dl_txns}->{$txn_id}; | ||
4807 | 3385 | my $pre = $prev_status->{IB_dl_txns}->{$txn_id} || $txn; | ||
4808 | 3386 | |||
4809 | 3387 | if ( $wanted{deadlock_transactions} ) { | ||
4810 | 3388 | my $hash = extract_values($txn->{tx}, $txn->{tx}, $pre->{tx}, 'deadlock_transactions'); | ||
4811 | 3389 | $hash->{cxn} = $cxn; | ||
4812 | 3390 | $hash->{dl_txn_num} = $txn_id; | ||
4813 | 3391 | $hash->{victim} = $txn_id == $victim ? 'Yes' : 'No'; | ||
4814 | 3392 | $hash->{timestring} = $innodb_status->{IB_dl_timestring}; | ||
4815 | 3393 | $hash->{truncates} = $innodb_status->{IB_dl_complete} ? 'No' : 'Yes'; | ||
4816 | 3394 | push @deadlock_transactions, $hash; | ||
4817 | 3395 | } | ||
4818 | 3396 | |||
4819 | 3397 | if ( $wanted{deadlock_locks} ) { | ||
4820 | 3398 | foreach my $lock ( @{$txn->{locks}} ) { | ||
4821 | 3399 | my $hash = extract_values($lock, $lock, $lock, 'deadlock_locks'); | ||
4822 | 3400 | $hash->{dl_txn_num} = $txn_id; | ||
4823 | 3401 | $hash->{cxn} = $cxn; | ||
4824 | 3402 | $hash->{mysql_thread_id} = $txn->{tx}->{mysql_thread_id}; | ||
4825 | 3403 | push @deadlock_locks, $hash; | ||
4826 | 3404 | } | ||
4827 | 3405 | } | ||
4828 | 3406 | |||
4829 | 3407 | } | ||
4830 | 3408 | } | ||
4831 | 3409 | } | ||
4832 | 3410 | } | ||
4833 | 3411 | |||
4834 | 3412 | my $first_table = 0; | ||
4835 | 3413 | foreach my $tbl ( @visible ) { | ||
4836 | 3414 | push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl); | ||
4837 | 3415 | push @display_lines, get_cxn_errors(@cxns) | ||
4838 | 3416 | if ( $config{debug}->{val} || !$first_table++ ); | ||
4839 | 3417 | } | ||
4840 | 3418 | |||
4841 | 3419 | draw_screen(\@display_lines); | ||
4842 | 3420 | } | ||
4843 | 3421 | |||
4844 | 3422 | # display_F {{{3 | ||
4845 | 3423 | sub display_F { | ||
4846 | 3424 | my @display_lines; | ||
4847 | 3425 | my ( $cxn ) = get_connections(); | ||
4848 | 3426 | get_innodb_status([$cxn]); | ||
4849 | 3427 | my $innodb_status = $vars{$cxn}->{$clock}; | ||
4850 | 3428 | |||
4851 | 3429 | if ( $innodb_status->{IB_fk_timestring} ) { | ||
4852 | 3430 | |||
4853 | 3431 | push @display_lines, 'Reason: ' . $innodb_status->{IB_fk_reason}; | ||
4854 | 3432 | |||
4855 | 3433 | # Display FK errors caused by invalid DML. | ||
4856 | 3434 | if ( $innodb_status->{IB_fk_txn} ) { | ||
4857 | 3435 | my $txn = $innodb_status->{IB_fk_txn}; | ||
4858 | 3436 | push @display_lines, | ||
4859 | 3437 | '', | ||
4860 | 3438 | "User $txn->{user} from $txn->{hostname}, thread $txn->{mysql_thread_id} was executing:", | ||
4861 | 3439 | '', no_ctrl_char($txn->{query_text}); | ||
4862 | 3440 | } | ||
4863 | 3441 | |||
4864 | 3442 | my @fk_table = create_table2( | ||
4865 | 3443 | $tbl_meta{fk_error}->{visible}, | ||
4866 | 3444 | meta_to_hdr('fk_error'), | ||
4867 | 3445 | extract_values($innodb_status, $innodb_status, $innodb_status, 'fk_error'), | ||
4868 | 3446 | { just => '-', sep => ' '}); | ||
4869 | 3447 | push @display_lines, '', @fk_table; | ||
4870 | 3448 | |||
4871 | 3449 | } | ||
4872 | 3450 | else { | ||
4873 | 3451 | push @display_lines, '', 'No foreign key error data.'; | ||
4874 | 3452 | } | ||
4875 | 3453 | draw_screen(\@display_lines, { raw => 1 } ); | ||
4876 | 3454 | } | ||
4877 | 3455 | |||
4878 | 3456 | # display_I {{{3 | ||
4879 | 3457 | sub display_I { | ||
4880 | 3458 | my @display_lines; | ||
4881 | 3459 | my @cxns = get_connections(); | ||
4882 | 3460 | get_innodb_status(\@cxns); | ||
4883 | 3461 | |||
4884 | 3462 | my @io_threads; | ||
4885 | 3463 | my @pending_io; | ||
4886 | 3464 | my @file_io_misc; | ||
4887 | 3465 | my @log_statistics; | ||
4888 | 3466 | my %rows_for = ( | ||
4889 | 3467 | io_threads => \@io_threads, | ||
4890 | 3468 | pending_io => \@pending_io, | ||
4891 | 3469 | file_io_misc => \@file_io_misc, | ||
4892 | 3470 | log_statistics => \@log_statistics, | ||
4893 | 3471 | ); | ||
4894 | 3472 | |||
4895 | 3473 | my @visible = get_visible_tables(); | ||
4896 | 3474 | my %wanted = map { $_ => 1 } @visible; | ||
4897 | 3475 | |||
4898 | 3476 | foreach my $cxn ( @cxns ) { | ||
4899 | 3477 | my $set = $vars{$cxn}->{$clock}; | ||
4900 | 3478 | my $pre = $vars{$cxn}->{$clock-1} || $set; | ||
4901 | 3479 | |||
4902 | 3480 | if ( $set->{IB_io_complete} ) { | ||
4903 | 3481 | if ( $wanted{io_threads} ) { | ||
4904 | 3482 | my $cur_threads = $set->{IB_io_threads}; | ||
4905 | 3483 | my $pre_threads = $pre->{IB_io_threads} || $cur_threads; | ||
4906 | 3484 | foreach my $key ( sort keys %$cur_threads ) { | ||
4907 | 3485 | my $cur_thd = $cur_threads->{$key}; | ||
4908 | 3486 | my $pre_thd = $pre_threads->{$key} || $cur_thd; | ||
4909 | 3487 | my $hash = extract_values($cur_thd, $cur_thd, $pre_thd, 'io_threads'); | ||
4910 | 3488 | $hash->{cxn} = $cxn; | ||
4911 | 3489 | push @io_threads, $hash; | ||
4912 | 3490 | } | ||
4913 | 3491 | } | ||
4914 | 3492 | if ( $wanted{pending_io} ) { | ||
4915 | 3493 | push @pending_io, extract_values($set, $set, $pre, 'pending_io'); | ||
4916 | 3494 | } | ||
4917 | 3495 | if ( $wanted{file_io_misc} ) { | ||
4918 | 3496 | push @file_io_misc, extract_values( | ||
4919 | 3497 | $config{status_inc}->{val} ? inc(0, $cxn) : $set, | ||
4920 | 3498 | $set, $pre, 'file_io_misc'); | ||
4921 | 3499 | } | ||
4922 | 3500 | } | ||
4923 | 3501 | if ( $set->{IB_lg_complete} && $wanted{log_statistics} ) { | ||
4924 | 3502 | push @log_statistics, extract_values($set, $set, $pre, 'log_statistics'); | ||
4925 | 3503 | } | ||
4926 | 3504 | } | ||
4927 | 3505 | |||
4928 | 3506 | my $first_table = 0; | ||
4929 | 3507 | foreach my $tbl ( @visible ) { | ||
4930 | 3508 | push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl); | ||
4931 | 3509 | push @display_lines, get_cxn_errors(@cxns) | ||
4932 | 3510 | if ( $config{debug}->{val} || !$first_table++ ); | ||
4933 | 3511 | } | ||
4934 | 3512 | |||
4935 | 3513 | draw_screen(\@display_lines); | ||
4936 | 3514 | } | ||
4937 | 3515 | |||
4938 | 3516 | # display_L {{{3 | ||
4939 | 3517 | sub display_L { | ||
4940 | 3518 | my @display_lines; | ||
4941 | 3519 | my @cxns = get_connections(); | ||
4942 | 3520 | get_innodb_status(\@cxns); | ||
4943 | 3521 | |||
4944 | 3522 | my @innodb_locks; | ||
4945 | 3523 | my %rows_for = ( | ||
4946 | 3524 | innodb_locks => \@innodb_locks, | ||
4947 | 3525 | ); | ||
4948 | 3526 | |||
4949 | 3527 | my @visible = get_visible_tables(); | ||
4950 | 3528 | my %wanted = map { $_ => 1 } @visible; | ||
4951 | 3529 | |||
4952 | 3530 | # Get info on locks | ||
4953 | 3531 | foreach my $cxn ( @cxns ) { | ||
4954 | 3532 | my $set = $vars{$cxn}->{$clock} or next; | ||
4955 | 3533 | my $pre = $vars{$cxn}->{$clock-1} || $set; | ||
4956 | 3534 | |||
4957 | 3535 | if ( $wanted{innodb_locks} && defined $set->{IB_tx_transactions} && @{$set->{IB_tx_transactions}} ) { | ||
4958 | 3536 | |||
4959 | 3537 | my $cur_txns = $set->{IB_tx_transactions}; | ||
4960 | 3538 | my $pre_txns = $pre->{IB_tx_transactions} || $cur_txns; | ||
4961 | 3539 | my %cur_txns = map { $_->{mysql_thread_id} => $_ } @$cur_txns; | ||
4962 | 3540 | my %pre_txns = map { $_->{mysql_thread_id} => $_ } @$pre_txns; | ||
4963 | 3541 | foreach my $txn ( @$cur_txns ) { | ||
4964 | 3542 | foreach my $lock ( @{$txn->{locks}} ) { | ||
4965 | 3543 | my %hash = map { $_ => $txn->{$_} } qw(txn_id mysql_thread_id lock_wait_time active_secs); | ||
4966 | 3544 | map { $hash{$_} = $lock->{$_} } qw(lock_type space_id page_no n_bits index db table txn_id lock_mode special insert_intention waiting); | ||
4967 | 3545 | $hash{cxn} = $cxn; | ||
4968 | 3546 | push @innodb_locks, extract_values(\%hash, \%hash, \%hash, 'innodb_locks'); | ||
4969 | 3547 | } | ||
4970 | 3548 | } | ||
4971 | 3549 | } | ||
4972 | 3550 | } | ||
4973 | 3551 | |||
4974 | 3552 | my $first_table = 0; | ||
4975 | 3553 | foreach my $tbl ( @visible ) { | ||
4976 | 3554 | push @display_lines, '', set_to_tbl($rows_for{$tbl}, $tbl); | ||
4977 | 3555 | push @display_lines, get_cxn_errors(@cxns) | ||
4978 | 3556 | if ( $config{debug}->{val} || !$first_table++ ); | ||
4979 | 3557 | } | ||
4980 | 3558 | |||
4981 | 3559 | draw_screen(\@display_lines); | ||
4982 | 3560 | } | ||
4983 | 3561 | |||
4984 | 3562 | # display_M {{{3 | ||
4985 | 3563 | sub display_M { | ||
4986 | 3564 | my @display_lines; | ||
4987 | 3565 | my @cxns = get_connections(); | ||
4988 | 3566 | get_master_slave_status(@cxns); | ||
4989 | 3567 | get_status_info(@cxns); | ||
4990 | 3568 | |||
4991 | 3569 | my @slave_sql_status; | ||
4992 | 3570 | my @slave_io_status; | ||
4993 | 3571 | my @master_status; | ||
4994 | 3572 | my %rows_for = ( | ||
4995 | 3573 | slave_sql_status => \@slave_sql_status, | ||
4996 | 3574 | slave_io_status => \@slave_io_status, | ||
4997 | 3575 | master_status => \@master_status, | ||
4998 | 3576 | ); | ||
4999 | 3577 | |||
5000 | 3578 | my @visible = get_visible_tables(); |
The diff has been truncated for viewing.
Seems good to me, tested working in squeeze