Merge lp:~cafuego/ourdelta/ourdelta-mariadb51-2-cafuego into lp:~ourdelta-core/ourdelta/ourdelta-mariadb51-2
- ourdelta-mariadb51-2-cafuego
- Merge into ourdelta-mariadb51-2
Proposed by
Cafuego
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~cafuego/ourdelta/ourdelta-mariadb51-2-cafuego |
Merge into: | lp:~ourdelta-core/ourdelta/ourdelta-mariadb51-2 |
Diff against target: |
17659 lines 97 files modified
.bzrignore (+1/-0) bakery/autobake-deb.sh (+41/-20) bakery/debian-5.1/README.Maintainer (+2/-11) bakery/debian-5.1/additions/debian-start.inc.sh (+12/-10) bakery/debian-5.1/additions/innotop/InnoDBParser.pm (+0/-1089) bakery/debian-5.1/additions/innotop/changelog.innotop (+40/-1) bakery/debian-5.1/additions/innotop/innotop (+1616/-155) bakery/debian-5.1/additions/innotop/innotop.1 (+192/-194) bakery/debian-5.1/additions/my.cnf (+11/-20) bakery/debian-5.1/additions/mysqld_safe_syslog.cnf (+2/-0) bakery/debian-5.1/additions/mysqlmanager.1 (+0/-49) bakery/debian-5.1/additions/mysqlreport (+279/-132) bakery/debian-5.1/additions/ndb_mgmd.cnf (+0/-35) bakery/debian-5.1/changelog (+5/-3873) bakery/debian-5.1/compat (+1/-1) bakery/debian-5.1/control (+0/-146) bakery/debian-5.1/copyright (+1/-6) bakery/debian-5.1/dist/Debian/control (+136/-0) bakery/debian-5.1/dist/Debian/mariadb-server-5.1.README.Debian (+109/-0) bakery/debian-5.1/dist/Debian/mariadb-server-5.1.dirs (+9/-0) bakery/debian-5.1/dist/Debian/mariadb-server-5.1.files (+54/-0) bakery/debian-5.1/dist/Debian/mariadb-server-5.1.postinst (+277/-0) bakery/debian-5.1/dist/Debian/mariadb-server-5.1.postrm (+83/-0) bakery/debian-5.1/dist/Debian/rules (+310/-0) bakery/debian-5.1/dist/Ubuntu/apparmor-profile (+36/-0) bakery/debian-5.1/dist/Ubuntu/control (+159/-0) bakery/debian-5.1/dist/Ubuntu/mariadb-server-5.1.README.Debian (+119/-0) bakery/debian-5.1/dist/Ubuntu/mariadb-server-5.1.dirs (+10/-0) bakery/debian-5.1/dist/Ubuntu/mariadb-server-5.1.files (+63/-0) bakery/debian-5.1/dist/Ubuntu/mariadb-server-5.1.postinst (+284/-0) bakery/debian-5.1/dist/Ubuntu/mariadb-server-5.1.postrm (+86/-0) bakery/debian-5.1/dist/Ubuntu/mariadb-server-5.1.py (+53/-0) bakery/debian-5.1/dist/Ubuntu/mariadb-server-core-5.1.files (+26/-0) bakery/debian-5.1/dist/Ubuntu/rules (+316/-0) bakery/debian-5.1/libmariadbclient-dev.files (+4/-3) bakery/debian-5.1/libmariadbd-dev.files (+2/-0) bakery/debian-5.1/libmysqlclient-dev.lintian-overrides (+0/-2) bakery/debian-5.1/libndbclient-dev.dirs (+0/-4) bakery/debian-5.1/libndbclient-dev.files (+0/-4) bakery/debian-5.1/libndbclient-dev.links (+0/-2) bakery/debian-5.1/libndbclient3.dirs (+0/-1) bakery/debian-5.1/libndbclient3.files (+0/-1) bakery/debian-5.1/mariadb-client-5.1.files (+7/-6) bakery/debian-5.1/mariadb-client-5.1.links (+3/-0) bakery/debian-5.1/mariadb-client-5.1.lintian-overrides (+3/-3) bakery/debian-5.1/mariadb-server-5.1.NEWS (+8/-0) bakery/debian-5.1/mariadb-server-5.1.config (+28/-4) bakery/debian-5.1/mariadb-server-5.1.lintian-overrides (+5/-4) bakery/debian-5.1/mariadb-server-5.1.mysql-server.logrotate (+0/-1) bakery/debian-5.1/mariadb-server-5.1.mysql.init (+14/-9) bakery/debian-5.1/mariadb-server-5.1.preinst (+7/-13) bakery/debian-5.1/mariadb-server-5.1.templates (+26/-21) bakery/debian-5.1/mysql-common.README.Debian (+0/-20) bakery/debian-5.1/mysql-common.files (+1/-0) bakery/debian-5.1/mysql-common.lintian-overrides (+0/-2) bakery/debian-5.1/mysql-common.postrm (+1/-1) bakery/debian-5.1/mysql-server-5.1.README.Debian (+0/-118) bakery/debian-5.1/mysql-server-5.1.dirs (+0/-9) bakery/debian-5.1/mysql-server-5.1.files (+0/-82) bakery/debian-5.1/mysql-server-5.1.mysql-ndb-mgm.init (+0/-86) bakery/debian-5.1/mysql-server-5.1.mysql-ndb.init (+0/-103) bakery/debian-5.1/mysql-server-5.1.postinst (+0/-289) bakery/debian-5.1/mysql-server-5.1.postrm (+0/-94) bakery/debian-5.1/patches/00list (+10/-0) bakery/debian-5.1/patches/00list.delta (+0/-6) bakery/debian-5.1/patches/00list.delta-sail (+0/-6) bakery/debian-5.1/patches/02_no_builtin_ndbcluster_plugin.dpatch (+18/-0) bakery/debian-5.1/patches/21_init__openquery_configtest.dpatch (+33/-0) bakery/debian-5.1/patches/33_scripts__mysql_create_system_tables__no_test.dpatch (+11/-4) bakery/debian-5.1/patches/38_scripts__mysqld_safe.sh__signals.dpatch (+1/-1) bakery/debian-5.1/patches/44_scripts__mysql_config__libs.dpatch (+8/-8) bakery/debian-5.1/patches/50_mysql-test__db_test.dpatch (+23/-0) bakery/debian-5.1/patches/60_zlib_innodb_workaround.dpatch (+31/-0) bakery/debian-5.1/po/POTFILES.in (+1/-1) bakery/debian-5.1/po/ar.po (+40/-23) bakery/debian-5.1/po/ca.po (+38/-23) bakery/debian-5.1/po/cs.po (+58/-29) bakery/debian-5.1/po/da.po (+58/-29) bakery/debian-5.1/po/de.po (+59/-59) bakery/debian-5.1/po/es.po (+67/-38) bakery/debian-5.1/po/eu.po (+75/-116) bakery/debian-5.1/po/fr.po (+66/-51) bakery/debian-5.1/po/gl.po (+40/-23) bakery/debian-5.1/po/it.po (+213/-218) bakery/debian-5.1/po/ja.po (+73/-219) bakery/debian-5.1/po/nb.po (+40/-23) bakery/debian-5.1/po/nl.po (+40/-23) bakery/debian-5.1/po/pt.po (+58/-29) bakery/debian-5.1/po/pt_BR.po (+40/-23) bakery/debian-5.1/po/ro.po (+59/-29) bakery/debian-5.1/po/ru.po (+87/-145) bakery/debian-5.1/po/sv.po (+162/-302) bakery/debian-5.1/po/templates.pot (+54/-39) bakery/debian-5.1/po/tr.po (+39/-24) bakery/debian-5.1/rules (+0/-359) bakery/debian-5.1/source.lintian-overrides (+2/-2) bakery/tarbake51.sh (+7/-1) |
To merge this branch: | bzr merge lp:~cafuego/ourdelta/ourdelta-mariadb51-2-cafuego |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
OurDelta-core | Pending | ||
Review via email: mp+12775@code.launchpad.net |
Commit message
Description of the change
To post a comment you must log in.
- 71. By Arjen Lentz
-
Various naming fixups for 5.1 build
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file '.bzrignore' |
2 | --- .bzrignore 2009-03-19 05:58:44 +0000 |
3 | +++ .bzrignore 2009-10-02 06:45:25 +0000 |
4 | @@ -0,0 +1,1 @@ |
5 | +.mariadb-version |
6 | |
7 | === modified file 'bakery/autobake-deb.sh' |
8 | --- bakery/autobake-deb.sh 2009-03-25 12:54:42 +0000 |
9 | +++ bakery/autobake-deb.sh 2009-10-02 06:45:25 +0000 |
10 | @@ -24,7 +24,7 @@ |
11 | |
12 | if [ ! $# -eq 1 ]; then |
13 | echo "Usage: $0 [tarball]" |
14 | - echo " tarball: ourdelta or ourdelta-sail tarball" |
15 | + echo " tarball: source tarball" |
16 | exit 1 |
17 | else |
18 | TARBALL="$(readlink -f ${1})" |
19 | @@ -39,22 +39,23 @@ |
20 | # |
21 | SOURCE_DIR=$(basename ${TARBALL} .tar.gz) |
22 | |
23 | -# Find out the bakery version, so we can adjust the changelog file later. |
24 | -# |
25 | -pushd ${BAKERY_BASE_DIR}/.. |
26 | -if [ ! -f .bzr-version ]; then |
27 | - echo "No bzr version info. You are not running this script from a" |
28 | - echo "properly exported bakery. Please do so. I will now quit." |
29 | - exit 1 |
30 | -else |
31 | - source .bzr-version |
32 | -fi |
33 | -popd |
34 | - |
35 | # Find major.minor version. |
36 | # |
37 | UPSTREAM="$(echo ${SOURCE_DIR} | cut -d- -f 2)" |
38 | MYVER="$(echo ${SOURCE_DIR} | cut -d- -f 2 | cut -d. -f1,2)" |
39 | +RELEASE_EXTRA="$(echo ${SOURCE_DIR} | cut -d- -f 3-)" |
40 | + |
41 | +pushd ${BAKERY_BASE_DIR} |
42 | +cd .. |
43 | +if [ ! -f .bzr-version ]; then |
44 | + echo "No bzr version info. You are not running this script from a" |
45 | + echo "properly exported bakery. Please do so. I will now quit." |
46 | + popd |
47 | + exit 1 |
48 | +else |
49 | + source .bzr-version |
50 | +fi |
51 | +popd |
52 | |
53 | # Whatup? |
54 | # |
55 | @@ -78,12 +79,32 @@ |
56 | |
57 | # Set version info (again) |
58 | # |
59 | -source .ourdelta-version |
60 | - |
61 | -# Set symlinks for build files in the debian dir, so we avoid adding AppArmor on Debian. |
62 | +if [ -e .ourdelta-version ]; then |
63 | + source .ourdelta-version |
64 | + RELEASE_NAME="${RELEASE_NAME}" |
65 | + PATCHLEVEL="-d${PATCHLEVEL}" |
66 | + LOGSTRING="OurDelta patch set ${PATCHLEVEL}" |
67 | +else |
68 | + PATCHLEVEL="" |
69 | + RELEASE_NAME=${RELEASE_EXTRA} |
70 | + RELEASE_EXTRA="ourdelta" |
71 | + LOGSTRING="${RELEASE_NAME}" |
72 | +fi |
73 | + |
74 | +# Clean up build file symlinks that are distro-specific. First remove all, then set |
75 | +# new links. |
76 | +DISTRODIRS="$(ls ./debian/dist)" |
77 | +for distrodir in ${DISTRODIRS}; do |
78 | + DISTROFILES="$(ls ./debian/dist/${distrodir})" |
79 | + for distrofile in ${DISTROFILES}; do |
80 | + rm -f "./debian/${distrofile}"; |
81 | + done; |
82 | +done; |
83 | + |
84 | +# Set no symlinks for build files in the debian dir, so we avoid adding AppArmor on Debian. |
85 | DISTRO="$(lsb_release -si)" |
86 | echo "Copying distribution specific build files for ${DISTRO}" |
87 | -DISTROFILES="control mysql-server-5.0.postinst rules mysql-server-5.0.dirs mysql-server-5.0.postrm mysql-server-5.0.files mysql-server-5.0.preinst" |
88 | +DISTROFILES="$(ls ./debian/dist/${DISTRO})" |
89 | for distrofile in ${DISTROFILES}; do |
90 | rm -f "./debian/${distrofile}" && cp -a "./debian/dist/${DISTRO}/${distrofile}" "./debian/${distrofile}"; |
91 | done; |
92 | @@ -91,10 +112,10 @@ |
93 | # Adjust changelog, add new version. |
94 | # |
95 | echo "Incrementing changelog and starting build scripts" |
96 | -CODENAME="$(lsb_release -c | cut -d: -f2 | sed 's/\s//g')" |
97 | -dch -b -D ${CODENAME} -v "${UPSTREAM}-d${PATCHLEVEL}-${RELEASE_NAME}${RELEASE_EXTRA:+-${RELEASE_EXTRA}}${REPO_VERSION}" "Automatic build with OurDelta patchset ${PATCHLEVEL} (including Sphinx storage engine)." |
98 | +CODENAME="$(lsb_release -sc)" |
99 | +dch -b -D ${CODENAME} -v "${UPSTREAM}${PATCHLEVEL}-${RELEASE_NAME}${RELEASE_EXTRA:+-${RELEASE_EXTRA}}${REPO_VERSION}" "Automatic build with ${LOGSTRING} (including Sphinx storage engine)." |
100 | |
101 | -echo "Creating package version ${UPSTREAM}-d${PATCHLEVEL}-${RELEASE_NAME}${RELEASE_EXTRA:+-${RELEASE_EXTRA}}${REPO_VERSION} ... " |
102 | +echo "Creating package version ${UPSTREAM}${PATCHLEVEL}-${RELEASE_NAME}${RELEASE_EXTRA:+-${RELEASE_EXTRA}}${REPO_VERSION} ... " |
103 | |
104 | # Copy in up-to-date gpg keys |
105 | if [ -d /srv/shared/gnupg ]; then |
106 | |
107 | === modified file 'bakery/debian-5.1/README.Maintainer' |
108 | --- bakery/debian-5.1/README.Maintainer 2008-10-20 22:54:11 +0000 |
109 | +++ bakery/debian-5.1/README.Maintainer 2009-10-02 06:45:25 +0000 |
110 | @@ -3,23 +3,14 @@ |
111 | ## FIXME for 5.1 ## |
112 | ########################### |
113 | |
114 | -# http://dev.mysql.com/doc/refman/5.1/en/upgrading-from-5-0.html |
115 | -* debian-sys-maint is not created properly in postinst |
116 | -* Asking for root passwort has not yet been backported^H^H^H^Htested. |
117 | -* Debconf template translations need testing |
118 | -* Incompatible change: The table_cache system variable has been renamed to table_open_cache. |
119 | - -> sed over /etc/mysql/* |
120 | -* Append --syslog to /etc/mysql/* |
121 | -* call the REPAIR TABLE statement for each table that contains any FULLTEXT indexes. |
122 | -* put this trigger-recreation thing into the init scripts |
123 | +* put this trigger-recreation thing into the init scripts -- what?! |
124 | |
125 | ########################################################################### |
126 | # Here are some information that are only of interest for the current and # |
127 | # following Debian maintainers of MySQL. # |
128 | ########################################################################### |
129 | |
130 | -The debian/ directory is under SVN control at |
131 | - svn+ssh://svn.debian.org/svn/pkg-mysql/branches/sid-5.1/debian |
132 | +The debian/ directory is under SVN control, see debian/control for URL. |
133 | |
134 | # |
135 | # Preparing a new version |
136 | |
137 | === modified file 'bakery/debian-5.1/additions/debian-start.inc.sh' |
138 | --- bakery/debian-5.1/additions/debian-start.inc.sh 2008-10-20 22:54:11 +0000 |
139 | +++ bakery/debian-5.1/additions/debian-start.inc.sh 2009-10-02 06:45:25 +0000 |
140 | @@ -11,19 +11,19 @@ |
141 | set -u |
142 | |
143 | # But do it in the background to not stall the boot process. |
144 | - logger -p daemon.info -i -t$0 "Checking for crashed MySQL tables." |
145 | + logger -p daemon.info -i -t$0 "Triggering myisam-recover for all MyISAM tables" |
146 | |
147 | # Checking for $? is unreliable so the size of the output is checked. |
148 | # Some table handlers like HEAP do not support CHECK TABLE. |
149 | tempfile=`tempfile` |
150 | - LC_ALL=C $MYCHECK $MYCHECK_PARAMS \ |
151 | - 2>&1 \ |
152 | - | perl -e " |
153 | - \$_=join('', <>); |
154 | - s/^[^\\n]+\\n(error|note)\s+: You can't use locks with log tables.\\n//simg; |
155 | - s/^[^\\n]+\\n(error|note)\s+: The (handler|storage engine) for the table doesn.t support check\\n//smg; |
156 | - print;" \ |
157 | - > $tempfile |
158 | + # We have to use xargs in this case, because a for loop barfs on the |
159 | + # spaces in the thing to be looped over. |
160 | + LC_ALL=C $MYSQL --skip-column-names --batch -e ' |
161 | + select concat('\''select count(*) into @discard from `'\'', |
162 | + TABLE_SCHEMA, '\''`.`'\'', TABLE_NAME, '\''`'\'') |
163 | + from information_schema.TABLES where ENGINE='\''MyISAM'\' | \ |
164 | + xargs -i $MYSQL --skip-column-names --silent --batch \ |
165 | + --force -e "{}" >$tempfile |
166 | if [ -s $tempfile ]; then |
167 | ( |
168 | /bin/echo -e "\n" \ |
169 | @@ -32,7 +32,9 @@ |
170 | $MYADMIN processlist status |
171 | ) >> $tempfile |
172 | # Check for presence as a dependency on mailx would require an MTA. |
173 | - if [ -x /usr/bin/mailx ]; then mailx -e -s"$MYCHECK_SUBJECT" $MYCHECK_RCPT < $tempfile; fi |
174 | + if [ -x /usr/bin/mailx ]; then |
175 | + mailx -e -s"$MYCHECK_SUBJECT" $MYCHECK_RCPT < $tempfile |
176 | + fi |
177 | (echo "$MYCHECK_SUBJECT"; cat $tempfile) | logger -p daemon.warn -i -t$0 |
178 | fi |
179 | rm $tempfile |
180 | |
181 | === removed file 'bakery/debian-5.1/additions/innotop/InnoDBParser.pm' |
182 | --- bakery/debian-5.1/additions/innotop/InnoDBParser.pm 2008-10-20 22:54:11 +0000 |
183 | +++ bakery/debian-5.1/additions/innotop/InnoDBParser.pm 1970-01-01 00:00:00 +0000 |
184 | @@ -1,1089 +0,0 @@ |
185 | -use strict; |
186 | -use warnings FATAL => 'all'; |
187 | - |
188 | -package InnoDBParser; |
189 | - |
190 | -# This program is copyright (c) 2006 Baron Schwartz, baron at xaprb dot com. |
191 | -# Feedback and improvements are gratefully received. |
192 | -# |
193 | -# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED |
194 | -# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF |
195 | -# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
196 | -# |
197 | -# This program is free software; you can redistribute it and/or modify it under |
198 | -# the terms of the GNU General Public License as published by the Free Software |
199 | -# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar |
200 | -# systems, you can issue `man perlgpl' or `man perlartistic' to read these |
201 | - |
202 | -# You should have received a copy of the GNU General Public License along with |
203 | -# this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
204 | -# Place, Suite 330, Boston, MA 02111-1307 USA |
205 | - |
206 | -our $VERSION = '1.6.0'; |
207 | - |
208 | -use Data::Dumper; |
209 | -$Data::Dumper::Sortkeys = 1; |
210 | -use English qw(-no_match_vars); |
211 | -use List::Util qw(max); |
212 | - |
213 | -# Some common patterns |
214 | -my $d = qr/(\d+)/; # Digit |
215 | -my $f = qr/(\d+\.\d+)/; # Float |
216 | -my $t = qr/(\d+ \d+)/; # Transaction ID |
217 | -my $i = qr/((?:\d{1,3}\.){3}\d+)/; # IP address |
218 | -my $n = qr/([^`\s]+)/; # MySQL object name |
219 | -my $w = qr/(\w+)/; # Words |
220 | -my $fl = qr/([\w\.\/]+) line $d/; # Filename and line number |
221 | -my $h = qr/((?:0x)?[0-9a-f]*)/; # Hex |
222 | -my $s = qr/(\d{6} .\d:\d\d:\d\d)/; # InnoDB timestamp |
223 | - |
224 | -# If you update this variable, also update the SYNOPSIS in the pod. |
225 | -my %innodb_section_headers = ( |
226 | - "TRANSACTIONS" => "tx", |
227 | - "BUFFER POOL AND MEMORY" => "bp", |
228 | - "SEMAPHORES" => "sm", |
229 | - "LOG" => "lg", |
230 | - "ROW OPERATIONS" => "ro", |
231 | - "INSERT BUFFER AND ADAPTIVE HASH INDEX" => "ib", |
232 | - "FILE I/O" => "io", |
233 | - "LATEST DETECTED DEADLOCK" => "dl", |
234 | - "LATEST FOREIGN KEY ERROR" => "fk", |
235 | -); |
236 | - |
237 | -my %parser_for = ( |
238 | - tx => \&parse_tx_section, |
239 | - bp => \&parse_bp_section, |
240 | - sm => \&parse_sm_section, |
241 | - lg => \&parse_lg_section, |
242 | - ro => \&parse_ro_section, |
243 | - ib => \&parse_ib_section, |
244 | - io => \&parse_io_section, |
245 | - dl => \&parse_dl_section, |
246 | - fk => \&parse_fk_section, |
247 | -); |
248 | - |
249 | -my %fk_parser_for = ( |
250 | - Transaction => \&parse_fk_transaction_error, |
251 | - Error => \&parse_fk_bad_constraint_error, |
252 | - Cannot => \&parse_fk_cant_drop_parent_error, |
253 | -); |
254 | - |
255 | -# A thread's proc_info can be at least 98 different things I've found in the |
256 | -# source. Fortunately, most of them begin with a gerunded verb. These are |
257 | -# the ones that don't. |
258 | -my %is_proc_info = ( |
259 | - 'After create' => 1, |
260 | - 'Execution of init_command' => 1, |
261 | - 'FULLTEXT initialization' => 1, |
262 | - 'Reopen tables' => 1, |
263 | - 'Repair done' => 1, |
264 | - 'Repair with keycache' => 1, |
265 | - 'System lock' => 1, |
266 | - 'Table lock' => 1, |
267 | - 'Thread initialized' => 1, |
268 | - 'User lock' => 1, |
269 | - 'copy to tmp table' => 1, |
270 | - 'discard_or_import_tablespace' => 1, |
271 | - 'end' => 1, |
272 | - 'got handler lock' => 1, |
273 | - 'got old table' => 1, |
274 | - 'init' => 1, |
275 | - 'key cache' => 1, |
276 | - 'locks' => 1, |
277 | - 'malloc' => 1, |
278 | - 'query end' => 1, |
279 | - 'rename result table' => 1, |
280 | - 'rename' => 1, |
281 | - 'setup' => 1, |
282 | - 'statistics' => 1, |
283 | - 'status' => 1, |
284 | - 'table cache' => 1, |
285 | - 'update' => 1, |
286 | -); |
287 | - |
288 | -sub new { |
289 | - bless {}, shift; |
290 | -} |
291 | - |
292 | -# Parse the status and return it. |
293 | -# See srv_printf_innodb_monitor in innobase/srv/srv0srv.c |
294 | -# Pass in the text to parse, whether to be in debugging mode, which sections |
295 | -# to parse (hashref; if empty, parse all), and whether to parse full info from |
296 | -# locks and such (probably shouldn't unless you need to). |
297 | -sub parse_status_text { |
298 | - my ( $self, $fulltext, $debug, $sections, $full ) = @_; |
299 | - |
300 | - die "I can't parse undef" unless defined $fulltext; |
301 | - $fulltext =~ s/[\r\n]+/\n/g; |
302 | - |
303 | - $sections ||= {}; |
304 | - die '$sections must be a hashref' unless ref($sections) eq 'HASH'; |
305 | - |
306 | - my %innodb_data = ( |
307 | - got_all => 0, # Whether I was able to get the whole thing |
308 | - ts => '', # Timestamp the server put on it |
309 | - last_secs => 0, # Num seconds the averages are over |
310 | - sections => {}, # Parsed values from each section |
311 | - ); |
312 | - |
313 | - if ( $debug ) { |
314 | - $innodb_data{'fulltext'} = $fulltext; |
315 | - } |
316 | - |
317 | - # Get the most basic info about the status: beginning and end, and whether |
318 | - # I got the whole thing (if there has been a big deadlock and there are |
319 | - # too many locks to print, the output might be truncated) |
320 | - my ( $time_text ) = $fulltext =~ m/^$s INNODB MONITOR OUTPUT$/m; |
321 | - $innodb_data{'ts'} = [ parse_innodb_timestamp( $time_text ) ]; |
322 | - $innodb_data{'timestring'} = ts_to_string($innodb_data{'ts'}); |
323 | - ( $innodb_data{'last_secs'} ) = $fulltext |
324 | - =~ m/Per second averages calculated from the last $d seconds/; |
325 | - |
326 | - ( my $got_all ) = $fulltext =~ m/END OF INNODB MONITOR OUTPUT/; |
327 | - $innodb_data{'got_all'} = $got_all || 0; |
328 | - |
329 | - # Split it into sections. Each section begins with |
330 | - # ----- |
331 | - # LABEL |
332 | - # ----- |
333 | - my %innodb_sections; |
334 | - my @matches = $fulltext |
335 | - =~ m#\n(---+)\n([A-Z /]+)\n\1\n(.*?)(?=\n(---+)\n[A-Z /]+\n\4\n|$)#gs; |
336 | - while ( my ( $start, $name, $text, $end ) = splice(@matches, 0, 4) ) { |
337 | - $innodb_sections{$name} = [ $text, $end ? 1 : 0 ]; |
338 | - } |
339 | - # The Row Operations section is a special case, because instead of ending |
340 | - # with the beginning of another section, it ends with the end of the file. |
341 | - # So this section is complete if the entire file is complete. |
342 | - $innodb_sections{'ROW OPERATIONS'}->[1] ||= $innodb_data{'got_all'}; |
343 | - |
344 | - # Just for sanity's sake, make sure I understand what to do with each |
345 | - # section |
346 | - eval { |
347 | - foreach my $section ( keys %innodb_sections ) { |
348 | - my $header = $innodb_section_headers{$section}; |
349 | - die "Unknown section $section in $fulltext\n" |
350 | - unless $header; |
351 | - $innodb_data{'sections'}->{ $header } |
352 | - ->{'fulltext'} = $innodb_sections{$section}->[0]; |
353 | - $innodb_data{'sections'}->{ $header } |
354 | - ->{'complete'} = $innodb_sections{$section}->[1]; |
355 | - } |
356 | - }; |
357 | - if ( $EVAL_ERROR ) { |
358 | - _debug( $debug, $EVAL_ERROR); |
359 | - } |
360 | - |
361 | - # ################################################################ |
362 | - # Parse the detailed data out of the sections. |
363 | - # ################################################################ |
364 | - eval { |
365 | - foreach my $section ( keys %parser_for ) { |
366 | - if ( defined $innodb_data{'sections'}->{$section} |
367 | - && (!%$sections || (defined($sections->{$section} && $sections->{$section})) )) { |
368 | - $parser_for{$section}->( |
369 | - $innodb_data{'sections'}->{$section}, |
370 | - $innodb_data{'sections'}->{$section}->{'complete'}, |
371 | - $debug, |
372 | - $full ) |
373 | - or delete $innodb_data{'sections'}->{$section}; |
374 | - } |
375 | - else { |
376 | - delete $innodb_data{'sections'}->{$section}; |
377 | - } |
378 | - } |
379 | - }; |
380 | - if ( $EVAL_ERROR ) { |
381 | - _debug( $debug, $EVAL_ERROR); |
382 | - } |
383 | - |
384 | - return \%innodb_data; |
385 | -} |
386 | - |
387 | -# Parses the status text and returns it flattened out as a single hash. |
388 | -sub get_status_hash { |
389 | - my ( $self, $fulltext, $debug, $sections, $full ) = @_; |
390 | - |
391 | - # Parse the status text... |
392 | - my $innodb_status |
393 | - = $self->parse_status_text($fulltext, $debug, $sections, $full ); |
394 | - |
395 | - # Flatten the hierarchical structure into a single list by grabbing desired |
396 | - # sections from it. |
397 | - return |
398 | - (map { 'IB_' . $_ => $innodb_status->{$_} } qw(timestring last_secs got_all)), |
399 | - (map { 'IB_bp_' . $_ => $innodb_status->{'sections'}->{'bp'}->{$_} } |
400 | - qw( writes_pending buf_pool_hit_rate total_mem_alloc buf_pool_reads |
401 | - awe_mem_alloc pages_modified writes_pending_lru page_creates_sec |
402 | - reads_pending pages_total buf_pool_hits writes_pending_single_page |
403 | - page_writes_sec pages_read pages_written page_reads_sec |
404 | - writes_pending_flush_list buf_pool_size add_pool_alloc |
405 | - dict_mem_alloc pages_created buf_free complete )), |
406 | - (map { 'IB_tx_' . $_ => $innodb_status->{'sections'}->{'tx'}->{$_} } |
407 | - qw( num_lock_structs history_list_len purge_done_for transactions |
408 | - purge_undo_for is_truncated trx_id_counter complete )), |
409 | - (map { 'IB_ib_' . $_ => $innodb_status->{'sections'}->{'ib'}->{$_} } |
410 | - qw( hash_table_size hash_searches_s non_hash_searches_s |
411 | - bufs_in_node_heap used_cells size free_list_len seg_size inserts |
412 | - merged_recs merges complete )), |
413 | - (map { 'IB_lg_' . $_ => $innodb_status->{'sections'}->{'lg'}->{$_} } |
414 | - qw( log_ios_done pending_chkp_writes last_chkp log_ios_s |
415 | - log_flushed_to log_seq_no pending_log_writes complete )), |
416 | - (map { 'IB_sm_' . $_ => $innodb_status->{'sections'}->{'sm'}->{$_} } |
417 | - qw( wait_array_size rw_shared_spins rw_excl_os_waits mutex_os_waits |
418 | - mutex_spin_rounds mutex_spin_waits rw_excl_spins rw_shared_os_waits |
419 | - waits signal_count reservation_count complete )), |
420 | - (map { 'IB_ro_' . $_ => $innodb_status->{'sections'}->{'ro'}->{$_} } |
421 | - qw( queries_in_queue n_reserved_extents main_thread_state |
422 | - main_thread_proc_no main_thread_id read_sec del_sec upd_sec ins_sec |
423 | - read_views_open num_rows_upd num_rows_ins num_rows_read |
424 | - queries_inside num_rows_del complete )), |
425 | - (map { 'IB_fk_' . $_ => $innodb_status->{'sections'}->{'fk'}->{$_} } |
426 | - qw( trigger parent_table child_index parent_index attempted_op |
427 | - child_db timestring fk_name records col_name reason txn parent_db |
428 | - type child_table parent_col complete )), |
429 | - (map { 'IB_io_' . $_ => $innodb_status->{'sections'}->{'io'}->{$_} } |
430 | - qw( pending_buffer_pool_flushes pending_pwrites pending_preads |
431 | - pending_normal_aio_reads fsyncs_s os_file_writes pending_sync_ios |
432 | - reads_s flush_type avg_bytes_s pending_ibuf_aio_reads writes_s |
433 | - threads os_file_reads pending_aio_writes pending_log_ios os_fsyncs |
434 | - pending_log_flushes complete )), |
435 | - (map { 'IB_dl_' . $_ => $innodb_status->{'sections'}->{'dl'}->{$_} } |
436 | - qw( timestring rolled_back txns complete )); |
437 | - |
438 | -} |
439 | - |
440 | -sub ts_to_string { |
441 | - my $parts = shift; |
442 | - return sprintf('%02d-%02d-%02d %02d:%02d:%02d', @$parts); |
443 | -} |
444 | - |
445 | -sub parse_innodb_timestamp { |
446 | - my $text = shift; |
447 | - my ( $y, $m, $d, $h, $i, $s ) |
448 | - = $text =~ m/^(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)$/; |
449 | - die("Can't get timestamp from $text\n") unless $y; |
450 | - $y += 2000; |
451 | - return ( $y, $m, $d, $h, $i, $s ); |
452 | -} |
453 | - |
454 | -sub parse_fk_section { |
455 | - my ( $section, $complete, $debug, $full ) = @_; |
456 | - my $fulltext = $section->{'fulltext'}; |
457 | - |
458 | - return 0 unless $fulltext; |
459 | - |
460 | - my ( $ts, $type ) = $fulltext =~ m/^$s\s+(\w+)/m; |
461 | - $section->{'ts'} = [ parse_innodb_timestamp( $ts ) ]; |
462 | - $section->{'timestring'} = ts_to_string($section->{'ts'}); |
463 | - $section->{'type'} = $type; |
464 | - |
465 | - # Decide which type of FK error happened, and dispatch to the right parser. |
466 | - if ( $type && $fk_parser_for{$type} ) { |
467 | - $fk_parser_for{$type}->( $section, $complete, $debug, $fulltext, $full ); |
468 | - } |
469 | - |
470 | - delete $section->{'fulltext'} unless $debug; |
471 | - |
472 | - return 1; |
473 | -} |
474 | - |
475 | -sub parse_fk_cant_drop_parent_error { |
476 | - my ( $section, $complete, $debug, $fulltext, $full ) = @_; |
477 | - |
478 | - # Parse the parent/child table info out |
479 | - @{$section}{ qw(attempted_op parent_db parent_table) } = $fulltext |
480 | - =~ m{Cannot $w table `(.*)/(.*)`}m; |
481 | - @{$section}{ qw(child_db child_table) } = $fulltext |
482 | - =~ m{because it is referenced by `(.*)/(.*)`}m; |
483 | - |
484 | - ( $section->{'reason'} ) = $fulltext =~ m/(Cannot .*)/s; |
485 | - $section->{'reason'} =~ s/\n(?:InnoDB: )?/ /gm |
486 | - if $section->{'reason'}; |
487 | - |
488 | - # Certain data may not be present. Make them '' if not present. |
489 | - map { $section->{$_} ||= "" } |
490 | - qw(child_index fk_name col_name parent_col); |
491 | -} |
492 | - |
493 | -# See dict/dict0dict.c, function dict_foreign_error_report |
494 | -# I don't care much about these. There are lots of different messages, and |
495 | -# they come from someone trying to create a foreign key, or similar |
496 | -# statements. They aren't indicative of some transaction trying to insert, |
497 | -# delete or update data. Sometimes it is possible to parse out a lot of |
498 | -# information about the tables and indexes involved, but often the message |
499 | -# contains the DDL string the user entered, which is way too much for this |
500 | -# module to try to handle. |
501 | -sub parse_fk_bad_constraint_error { |
502 | - my ( $section, $complete, $debug, $fulltext, $full ) = @_; |
503 | - |
504 | - # Parse the parent/child table and index info out |
505 | - @{$section}{ qw(child_db child_table) } = $fulltext |
506 | - =~ m{Error in foreign key constraint of table (.*)/(.*):$}m; |
507 | - $section->{'attempted_op'} = 'DDL'; |
508 | - |
509 | - # FK name, parent info... if possible. |
510 | - @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) } |
511 | - = $fulltext |
512 | - =~ m/CONSTRAINT `?$n`? FOREIGN KEY \(`?$n`?\) REFERENCES (?:`?$n`?\.)?`?$n`? \(`?$n`?\)/; |
513 | - |
514 | - if ( !defined($section->{'fk_name'}) ) { |
515 | - # Try to parse SQL a user might have typed in a CREATE statement or such |
516 | - @{$section}{ qw(col_name parent_db parent_table parent_col) } |
517 | - = $fulltext |
518 | - =~ m/FOREIGN\s+KEY\s*\(`?$n`?\)\s+REFERENCES\s+(?:`?$n`?\.)?`?$n`?\s*\(`?$n`?\)/i; |
519 | - } |
520 | - $section->{'parent_db'} ||= $section->{'child_db'}; |
521 | - |
522 | - # Name of the child index (index in the same table where the FK is, see |
523 | - # definition of dict_foreign_struct in include/dict0mem.h, where it is |
524 | - # called foreign_index, as opposed to referenced_index which is in the |
525 | - # parent table. This may not be possible to find. |
526 | - @{$section}{ qw(child_index) } = $fulltext |
527 | - =~ m/^The index in the foreign key in table is $n$/m; |
528 | - |
529 | - @{$section}{ qw(reason) } = $fulltext =~ m/:\s*([^:]+)(?= Constraint:|$)/ms; |
530 | - $section->{'reason'} =~ s/\s+/ /g |
531 | - if $section->{'reason'}; |
532 | - |
533 | - # Certain data may not be present. Make them '' if not present. |
534 | - map { $section->{$_} ||= "" } |
535 | - qw(child_index fk_name col_name parent_table parent_col); |
536 | -} |
537 | - |
538 | -# see source file row/row0ins.c |
539 | -sub parse_fk_transaction_error { |
540 | - my ( $section, $complete, $debug, $fulltext, $full ) = @_; |
541 | - |
542 | - # Parse the txn info out |
543 | - my ( $txn ) = $fulltext |
544 | - =~ m/Transaction:\n(TRANSACTION.*)\nForeign key constraint fails/s; |
545 | - if ( $txn ) { |
546 | - $section->{'txn'} = parse_tx_text( $txn, $complete, $debug, $full ); |
547 | - } |
548 | - |
549 | - # Parse the parent/child table and index info out. There are two types: an |
550 | - # update or a delete of a parent record leaves a child orphaned |
551 | - # (row_ins_foreign_report_err), and an insert or update of a child record has |
552 | - # no matching parent record (row_ins_foreign_report_add_err). |
553 | - |
554 | - @{$section}{ qw(reason child_db child_table) } |
555 | - = $fulltext =~ m{^(Foreign key constraint fails for table `(.*)/(.*)`:)$}m; |
556 | - |
557 | - @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) } |
558 | - = $fulltext |
559 | - =~ m/CONSTRAINT `$n` FOREIGN KEY \(`$n`\) REFERENCES (?:`$n`\.)?`$n` \(`$n`\)/; |
560 | - $section->{'parent_db'} ||= $section->{'child_db'}; |
561 | - |
562 | - # Special case, which I don't know how to trigger, but see |
563 | - # innobase/row/row0ins.c row_ins_check_foreign_constraint |
564 | - if ( $fulltext =~ m/ibd file does not currently exist!/ ) { |
565 | - my ( $attempted_op, $index, $records ) |
566 | - = $fulltext =~ m/^Trying to (add to index) `$n` tuple:\n(.*))?/sm; |
567 | - $section->{'child_index'} = $index; |
568 | - $section->{'attempted_op'} = $attempted_op || ''; |
569 | - if ( $records && $full ) { |
570 | - ( $section->{'records'} ) |
571 | - = parse_innodb_record_dump( $records, $complete, $debug ); |
572 | - } |
573 | - @{$section}{qw(parent_db parent_table)} |
574 | - =~ m/^But the parent table `$n`\.`$n`$/m; |
575 | - } |
576 | - else { |
577 | - my ( $attempted_op, $which, $index ) |
578 | - = $fulltext =~ m/^Trying to ([\w ]*) in (child|parent) table, in index `$n` tuple:$/m; |
579 | - if ( $which ) { |
580 | - $section->{$which . '_index'} = $index; |
581 | - $section->{'attempted_op'} = $attempted_op || ''; |
582 | - |
583 | - # Parse out the related records in the other table. |
584 | - my ( $search_index, $records ); |
585 | - if ( $which eq 'child' ) { |
586 | - ( $search_index, $records ) = $fulltext |
587 | - =~ m/^But in parent table [^,]*, in index `$n`,\nthe closest match we can find is record:\n(.*)/ms; |
588 | - $section->{'parent_index'} = $search_index; |
589 | - } |
590 | - else { |
591 | - ( $search_index, $records ) = $fulltext |
592 | - =~ m/^But in child table [^,]*, in index `$n`, (?:the record is not available|there is a record:\n(.*))?/ms; |
593 | - $section->{'child_index'} = $search_index; |
594 | - } |
595 | - if ( $records && $full ) { |
596 | - $section->{'records'} |
597 | - = parse_innodb_record_dump( $records, $complete, $debug ); |
598 | - } |
599 | - else { |
600 | - $section->{'records'} = ''; |
601 | - } |
602 | - } |
603 | - } |
604 | - |
605 | - # Parse out the tuple trying to be updated, deleted or inserted. |
606 | - my ( $trigger ) = $fulltext =~ m/^(DATA TUPLE: \d+ fields;\n.*)$/m; |
607 | - if ( $trigger ) { |
608 | - $section->{'trigger'} = parse_innodb_record_dump( $trigger, $complete, $debug ); |
609 | - } |
610 | - |
611 | - # Certain data may not be present. Make them '' if not present. |
612 | - map { $section->{$_} ||= "" } |
613 | - qw(child_index fk_name col_name parent_table parent_col); |
614 | -} |
615 | - |
616 | -# There are new-style and old-style record formats. See rem/rem0rec.c |
617 | -# TODO: write some tests for this |
618 | -sub parse_innodb_record_dump { |
619 | - my ( $dump, $complete, $debug ) = @_; |
620 | - return undef unless $dump; |
621 | - |
622 | - my $result = {}; |
623 | - |
624 | - if ( $dump =~ m/PHYSICAL RECORD/ ) { |
625 | - my $style = $dump =~ m/compact format/ ? 'new' : 'old'; |
626 | - $result->{'style'} = $style; |
627 | - |
628 | - # This is a new-style record. |
629 | - if ( $style eq 'new' ) { |
630 | - @{$result}{qw( heap_no type num_fields info_bits )} |
631 | - = $dump |
632 | - =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; compact format; info bits $d$/m; |
633 | - } |
634 | - |
635 | - # OK, it's old-style. Unfortunately there are variations here too. |
636 | - elsif ( $dump =~ m/-byte offs / ) { |
637 | - # Older-old style. |
638 | - @{$result}{qw( heap_no type num_fields byte_offset info_bits )} |
639 | - = $dump |
640 | - =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offs [A-Z]+; info bits $d$/m; |
641 | - if ( $dump !~ m/-byte offs TRUE/ ) { |
642 | - $result->{'byte_offset'} = 0; |
643 | - } |
644 | - } |
645 | - else { |
646 | - # Newer-old style. |
647 | - @{$result}{qw( heap_no type num_fields byte_offset info_bits )} |
648 | - = $dump |
649 | - =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offsets; info bits $d$/m; |
650 | - } |
651 | - |
652 | - } |
653 | - else { |
654 | - $result->{'style'} = 'tuple'; |
655 | - @{$result}{qw( type num_fields )} |
656 | - = $dump =~ m/^(DATA TUPLE): $d fields;$/m; |
657 | - } |
658 | - |
659 | - # Fill in default values for things that couldn't be parsed. |
660 | - map { $result->{$_} ||= 0 } |
661 | - qw(heap_no num_fields byte_offset info_bits); |
662 | - map { $result->{$_} ||= '' } |
663 | - qw(style type ); |
664 | - |
665 | - my @fields = $dump =~ m/ (\d+:.*?;?);(?=$| \d+:)/gm; |
666 | - $result->{'fields'} = [ map { parse_field($_, $complete, $debug ) } @fields ]; |
667 | - |
668 | - return $result; |
669 | -} |
670 | - |
671 | -# New/old-style applies here. See rem/rem0rec.c |
672 | -# $text should not include the leading space or the second trailing semicolon. |
673 | -sub parse_field { |
674 | - my ( $text, $complete, $debug ) = @_; |
675 | - |
676 | - # Sample fields: |
677 | - # '4: SQL NULL, size 4 ' |
678 | - # '1: len 6; hex 000000005601; asc V ;' |
679 | - # '6: SQL NULL' |
680 | - # '5: len 30; hex 687474703a2f2f7777772e737765657477617465722e636f6d2f73746f72; asc http://www.sweetwater.com/stor;...(truncated)' |
681 | - my ( $id, $nullsize, $len, $hex, $asc, $truncated ); |
682 | - ( $id, $nullsize ) = $text =~ m/^$d: SQL NULL, size $d $/; |
683 | - if ( !defined($id) ) { |
684 | - ( $id ) = $text =~ m/^$d: SQL NULL$/; |
685 | - } |
686 | - if ( !defined($id) ) { |
687 | - ( $id, $len, $hex, $asc, $truncated ) |
688 | - = $text =~ m/^$d: len $d; hex $h; asc (.*);(\.\.\.\(truncated\))?$/; |
689 | - } |
690 | - |
691 | - die "Could not parse this field: '$text'" unless defined $id; |
692 | - return { |
693 | - id => $id, |
694 | - len => defined($len) ? $len : defined($nullsize) ? $nullsize : 0, |
695 | - 'hex' => defined($hex) ? $hex : '', |
696 | - asc => defined($asc) ? $asc : '', |
697 | - trunc => $truncated ? 1 : 0, |
698 | - }; |
699 | - |
700 | -} |
701 | - |
702 | -sub parse_dl_section { |
703 | - my ( $dl, $complete, $debug, $full ) = @_; |
704 | - return unless $dl; |
705 | - my $fulltext = $dl->{'fulltext'}; |
706 | - return 0 unless $fulltext; |
707 | - |
708 | - my ( $ts ) = $fulltext =~ m/^$s$/m; |
709 | - return 0 unless $ts; |
710 | - |
711 | - $dl->{'ts'} = [ parse_innodb_timestamp( $ts ) ]; |
712 | - $dl->{'timestring'} = ts_to_string($dl->{'ts'}); |
713 | - $dl->{'txns'} = {}; |
714 | - |
715 | - my @sections |
716 | - = $fulltext |
717 | - =~ m{ |
718 | - ^\*{3}\s([^\n]*) # *** (1) WAITING FOR THIS... |
719 | - (.*?) # Followed by anything, non-greedy |
720 | - (?=(?:^\*{3})|\z) # Followed by another three stars or EOF |
721 | - }gmsx; |
722 | - |
723 | - |
724 | - # Loop through each section. There are no assumptions about how many |
725 | - # there are, who holds and wants what locks, and who gets rolled back. |
726 | - while ( my ($header, $body) = splice(@sections, 0, 2) ) { |
727 | - my ( $txn_id, $what ) = $header =~ m/^\($d\) (.*):$/; |
728 | - next unless $txn_id; |
729 | - $dl->{'txns'}->{$txn_id} ||= {}; |
730 | - my $txn = $dl->{'txns'}->{$txn_id}; |
731 | - |
732 | - if ( $what eq 'TRANSACTION' ) { |
733 | - $txn->{'tx'} = parse_tx_text( $body, $complete, $debug, $full ); |
734 | - } |
735 | - else { |
736 | - push @{$txn->{'locks'}}, parse_innodb_record_locks( $body, $complete, $debug, $full ); |
737 | - } |
738 | - } |
739 | - |
740 | - @{ $dl }{ qw(rolled_back) } |
741 | - = $fulltext =~ m/^\*\*\* WE ROLL BACK TRANSACTION \($d\)$/m; |
742 | - |
743 | - # Make sure certain values aren't undef |
744 | - map { $dl->{$_} ||= '' } qw(rolled_back); |
745 | - |
746 | - delete $dl->{'fulltext'} unless $debug; |
747 | - return 1; |
748 | -} |
749 | - |
750 | -sub parse_innodb_record_locks { |
751 | - my ( $text, $complete, $debug, $full ) = @_; |
752 | - my @result; |
753 | - |
754 | - foreach my $lock ( $text =~ m/(^(?:RECORD|TABLE) LOCKS?.*$)/gm ) { |
755 | - my $hash = {}; |
756 | - @{$hash}{ qw(lock_type space_id page_no n_bits index db table txn_id lock_mode) } |
757 | - = $lock |
758 | - =~ 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; |
759 | - ( $hash->{'special'} ) |
760 | - = $lock =~ m/^(?:RECORD|TABLE) .*? locks (rec but not gap|gap before rec)/m; |
761 | - $hash->{'insert_intention'} |
762 | - = $lock =~ m/^(?:RECORD|TABLE) .*? insert intention/m ? 1 : 0; |
763 | - $hash->{'waiting'} |
764 | - = $lock =~ m/^(?:RECORD|TABLE) .*? waiting/m ? 1 : 0; |
765 | - |
766 | - # Some things may not be in the text, so make sure they are not |
767 | - # undef. |
768 | - map { $hash->{$_} ||= 0 } qw(n_bits page_no space_id); |
769 | - map { $hash->{$_} ||= "" } qw(index special); |
770 | - push @result, $hash; |
771 | - } |
772 | - |
773 | - return @result; |
774 | -} |
775 | - |
776 | -sub parse_tx_text { |
777 | - my ( $txn, $complete, $debug, $full ) = @_; |
778 | - |
779 | - my ( $txn_id, $txn_status, $active_secs, $proc_no, $os_thread_id ) |
780 | - = $txn |
781 | - =~ m/^(?:---)?TRANSACTION $t, (\D*?)(?: $d sec)?, (?:process no $d, )?OS thread id $d/m; |
782 | - my ( $thread_status, $thread_decl_inside ) |
783 | - = $txn |
784 | - =~ m/OS thread id \d+(?: ([^,]+?))?(?:, thread declared inside InnoDB $d)?$/m; |
785 | - |
786 | - # Parsing the line that begins 'MySQL thread id' is complicated. The only |
787 | - # thing always in the line is the thread and query id. See function |
788 | - # innobase_mysql_print_thd in InnoDB source file sql/ha_innodb.cc. |
789 | - my ( $thread_line ) = $txn =~ m/^(MySQL thread id .*)$/m; |
790 | - my ( $mysql_thread_id, $query_id, $hostname, $ip, $user, $query_status ); |
791 | - |
792 | - if ( $thread_line ) { |
793 | - # These parts can always be gotten. |
794 | - ( $mysql_thread_id, $query_id ) = $thread_line =~ m/^MySQL thread id $d, query id $d/m; |
795 | - |
796 | - # If it's a master/slave thread, "Has (read|sent) all" may be the thread's |
797 | - # proc_info. In these cases, there won't be any host/ip/user info |
798 | - ( $query_status ) = $thread_line =~ m/(Has (?:read|sent) all .*$)/m; |
799 | - if ( defined($query_status) ) { |
800 | - $user = 'system user'; |
801 | - } |
802 | - |
803 | - # It may be the case that the query id is the last thing in the line. |
804 | - elsif ( $thread_line =~ m/query id \d+ / ) { |
805 | - # The IP address is the only non-word thing left, so it's the most |
806 | - # useful marker for where I have to start guessing. |
807 | - ( $hostname, $ip ) = $thread_line =~ m/query id \d+(?: ([A-Za-z]\S+))? $i/m; |
808 | - if ( defined $ip ) { |
809 | - ( $user, $query_status ) = $thread_line =~ m/$ip $w(?: (.*))?$/; |
810 | - } |
811 | - else { # OK, there wasn't an IP address. |
812 | - # There might not be ANYTHING except the query status. |
813 | - ( $query_status ) = $thread_line =~ m/query id \d+ (.*)$/; |
814 | - if ( $query_status !~ m/^\w+ing/ && !exists($is_proc_info{$query_status}) ) { |
815 | - # The remaining tokens are, in order: hostname, user, query_status. |
816 | - # It's basically impossible to know which is which. |
817 | - ( $hostname, $user, $query_status ) = $thread_line |
818 | - =~ m/query id \d+(?: ([A-Za-z]\S+))?(?: $w(?: (.*))?)?$/m; |
819 | - } |
820 | - else { |
821 | - $user = 'system user'; |
822 | - } |
823 | - } |
824 | - } |
825 | - } |
826 | - |
827 | - my ( $lock_wait_status, $lock_structs, $heap_size, $row_locks, $undo_log_entries ) |
828 | - = $txn |
829 | - =~ m/^(?:(\D*) )?$d lock struct\(s\), heap size $d(?:, $d row lock\(s\))?(?:, undo log entries $d)?$/m; |
830 | - my ( $lock_wait_time ) |
831 | - = $txn |
832 | - =~ m/^------- TRX HAS BEEN WAITING $d SEC/m; |
833 | - |
834 | - my $locks; |
835 | - # If the transaction has locks, grab the locks. |
836 | - if ( $txn =~ m/^TABLE LOCK|RECORD LOCKS/ ) { |
837 | - $locks = [parse_innodb_record_locks($txn, $complete, $debug, $full)]; |
838 | - } |
839 | - |
840 | - my ( $tables_in_use, $tables_locked ) |
841 | - = $txn |
842 | - =~ m/^mysql tables in use $d, locked $d$/m; |
843 | - my ( $txn_doesnt_see_ge, $txn_sees_lt ) |
844 | - = $txn |
845 | - =~ m/^Trx read view will not see trx with id >= $t, sees < $t$/m; |
846 | - my $has_read_view = defined($txn_doesnt_see_ge); |
847 | - # Only a certain number of bytes of the query text are included here, at least |
848 | - # under some circumstances. Some versions include 300, some 600. |
849 | - my ( $query_text ) |
850 | - = $txn |
851 | - =~ m{ |
852 | - ^MySQL\sthread\sid\s[^\n]+\n # This comes before the query text |
853 | - (.*?) # The query text |
854 | - (?= # Followed by any of... |
855 | - ^Trx\sread\sview |
856 | - |^-------\sTRX\sHAS\sBEEN\sWAITING |
857 | - |^TABLE\sLOCK |
858 | - |^RECORD\sLOCKS\sspace\sid |
859 | - |^(?:---)?TRANSACTION |
860 | - |^\*\*\*\s\(\d\) |
861 | - |\Z |
862 | - ) |
863 | - }xms; |
864 | - if ( $query_text ) { |
865 | - $query_text =~ s/\s+$//; |
866 | - } |
867 | - else { |
868 | - $query_text = ''; |
869 | - } |
870 | - |
871 | - my %stuff = ( |
872 | - active_secs => $active_secs, |
873 | - has_read_view => $has_read_view, |
874 | - heap_size => $heap_size, |
875 | - hostname => $hostname, |
876 | - ip => $ip, |
877 | - lock_structs => $lock_structs, |
878 | - lock_wait_status => $lock_wait_status, |
879 | - lock_wait_time => $lock_wait_time, |
880 | - mysql_thread_id => $mysql_thread_id, |
881 | - os_thread_id => $os_thread_id, |
882 | - proc_no => $proc_no, |
883 | - query_id => $query_id, |
884 | - query_status => $query_status, |
885 | - query_text => $query_text, |
886 | - row_locks => $row_locks, |
887 | - tables_in_use => $tables_in_use, |
888 | - tables_locked => $tables_locked, |
889 | - thread_decl_inside => $thread_decl_inside, |
890 | - thread_status => $thread_status, |
891 | - txn_doesnt_see_ge => $txn_doesnt_see_ge, |
892 | - txn_id => $txn_id, |
893 | - txn_sees_lt => $txn_sees_lt, |
894 | - txn_status => $txn_status, |
895 | - undo_log_entries => $undo_log_entries, |
896 | - user => $user, |
897 | - ); |
898 | - $stuff{'fulltext'} = $txn if $debug; |
899 | - $stuff{'locks'} = $locks if $locks; |
900 | - |
901 | - # Some things may not be in the txn text, so make sure they are not |
902 | - # undef. |
903 | - map { $stuff{$_} ||= 0 } qw(active_secs heap_size lock_structs |
904 | - tables_in_use undo_log_entries tables_locked has_read_view |
905 | - thread_decl_inside lock_wait_time proc_no row_locks); |
906 | - map { $stuff{$_} ||= "" } qw(thread_status txn_doesnt_see_ge |
907 | - txn_sees_lt query_status ip query_text lock_wait_status user); |
908 | - $stuff{'hostname'} ||= $stuff{'ip'}; |
909 | - |
910 | - return \%stuff; |
911 | -} |
912 | - |
913 | -sub parse_tx_section { |
914 | - my ( $section, $complete, $debug, $full ) = @_; |
915 | - return unless $section && $section->{'fulltext'}; |
916 | - my $fulltext = $section->{'fulltext'}; |
917 | - $section->{'transactions'} = []; |
918 | - |
919 | - # Handle the individual transactions |
920 | - my @transactions = $fulltext =~ m/(---TRANSACTION \d.*?)(?=\n---TRANSACTION|$)/gs; |
921 | - foreach my $txn ( @transactions ) { |
922 | - my $stuff = parse_tx_text( $txn, $complete, $debug, $full ); |
923 | - delete $stuff->{'fulltext'} unless $debug; |
924 | - push @{$section->{'transactions'}}, $stuff; |
925 | - } |
926 | - |
927 | - # Handle the general info |
928 | - @{$section}{ 'trx_id_counter' } |
929 | - = $fulltext =~ m/^Trx id counter $t$/m; |
930 | - @{$section}{ 'purge_done_for', 'purge_undo_for' } |
931 | - = $fulltext =~ m/^Purge done for trx's n:o < $t undo n:o < $t$/m; |
932 | - @{$section}{ 'history_list_len' } # This isn't present in some 4.x versions |
933 | - = $fulltext =~ m/^History list length $d$/m; |
934 | - @{$section}{ 'num_lock_structs' } |
935 | - = $fulltext =~ m/^Total number of lock structs in row lock hash table $d$/m; |
936 | - @{$section}{ 'is_truncated' } |
937 | - = $fulltext =~ m/^\.\.\. truncated\.\.\.$/m ? 1 : 0; |
938 | - |
939 | - # Fill in things that might not be present |
940 | - foreach ( qw(history_list_len) ) { |
941 | - $section->{$_} ||= 0; |
942 | - } |
943 | - |
944 | - delete $section->{'fulltext'} unless $debug; |
945 | - return 1; |
946 | -} |
947 | - |
948 | -# I've read the source for this section. |
949 | -sub parse_ro_section { |
950 | - my ( $section, $complete, $debug, $full ) = @_; |
951 | - return unless $section && $section->{'fulltext'}; |
952 | - my $fulltext = $section->{'fulltext'}; |
953 | - |
954 | - # Grab the info |
955 | - @{$section}{ 'queries_inside', 'queries_in_queue' } |
956 | - = $fulltext =~ m/^$d queries inside InnoDB, $d queries in queue$/m; |
957 | - ( $section->{ 'read_views_open' } ) |
958 | - = $fulltext =~ m/^$d read views open inside InnoDB$/m; |
959 | - ( $section->{ 'n_reserved_extents' } ) |
960 | - = $fulltext =~ m/^$d tablespace extents now reserved for B-tree/m; |
961 | - @{$section}{ 'main_thread_proc_no', 'main_thread_id', 'main_thread_state' } |
962 | - = $fulltext =~ m/^Main thread (?:process no. $d, )?id $d, state: (.*)$/m; |
963 | - @{$section}{ 'num_rows_ins', 'num_rows_upd', 'num_rows_del', 'num_rows_read' } |
964 | - = $fulltext =~ m/^Number of rows inserted $d, updated $d, deleted $d, read $d$/m; |
965 | - @{$section}{ 'ins_sec', 'upd_sec', 'del_sec', 'read_sec' } |
966 | - = $fulltext =~ m#^$f inserts/s, $f updates/s, $f deletes/s, $f reads/s$#m; |
967 | - $section->{'main_thread_proc_no'} ||= 0; |
968 | - |
969 | - map { $section->{$_} ||= 0 } qw(read_views_open n_reserved_extents); |
970 | - delete $section->{'fulltext'} unless $debug; |
971 | - return 1; |
972 | -} |
973 | - |
974 | -sub parse_lg_section { |
975 | - my ( $section, $complete, $debug, $full ) = @_; |
976 | - return unless $section; |
977 | - my $fulltext = $section->{'fulltext'}; |
978 | - |
979 | - # Grab the info |
980 | - ( $section->{ 'log_seq_no' } ) |
981 | - = $fulltext =~ m/Log sequence number \s*(\d.*)$/m; |
982 | - ( $section->{ 'log_flushed_to' } ) |
983 | - = $fulltext =~ m/Log flushed up to \s*(\d.*)$/m; |
984 | - ( $section->{ 'last_chkp' } ) |
985 | - = $fulltext =~ m/Last checkpoint at \s*(\d.*)$/m; |
986 | - @{$section}{ 'pending_log_writes', 'pending_chkp_writes' } |
987 | - = $fulltext =~ m/$d pending log writes, $d pending chkp writes/; |
988 | - @{$section}{ 'log_ios_done', 'log_ios_s' } |
989 | - = $fulltext =~ m#$d log i/o's done, $f log i/o's/second#; |
990 | - |
991 | - delete $section->{'fulltext'} unless $debug; |
992 | - return 1; |
993 | -} |
994 | - |
995 | -sub parse_ib_section { |
996 | - my ( $section, $complete, $debug, $full ) = @_; |
997 | - return unless $section && $section->{'fulltext'}; |
998 | - my $fulltext = $section->{'fulltext'}; |
999 | - |
1000 | - # Some servers will output ibuf information for tablespace 0, as though there |
1001 | - # might be many tablespaces with insert buffers. (In practice I believe |
1002 | - # the source code shows there will only ever be one). I have to parse both |
1003 | - # cases here, but I assume there will only be one. |
1004 | - @{$section}{ 'size', 'free_list_len', 'seg_size' } |
1005 | - = $fulltext =~ m/^Ibuf(?: for space 0)?: size $d, free list len $d, seg size $d,$/m; |
1006 | - @{$section}{ 'inserts', 'merged_recs', 'merges' } |
1007 | - = $fulltext =~ m/^$d inserts, $d merged recs, $d merges$/m; |
1008 | - |
1009 | - @{$section}{ 'hash_table_size', 'used_cells', 'bufs_in_node_heap' } |
1010 | - = $fulltext =~ m/^Hash table size $d, used cells $d, node heap has $d buffer\(s\)$/m; |
1011 | - @{$section}{ 'hash_searches_s', 'non_hash_searches_s' } |
1012 | - = $fulltext =~ m{^$f hash searches/s, $f non-hash searches/s$}m; |
1013 | - |
1014 | - delete $section->{'fulltext'} unless $debug; |
1015 | - return 1; |
1016 | -} |
1017 | - |
1018 | -sub parse_wait_array { |
1019 | - my ( $text, $complete, $debug, $full ) = @_; |
1020 | - my %result; |
1021 | - |
1022 | - @result{ qw(thread waited_at_filename waited_at_line waited_secs) } |
1023 | - = $text =~ m/^--Thread $d has waited at $fl for $f seconds/m; |
1024 | - |
1025 | - # Depending on whether it's a SYNC_MUTEX,RW_LOCK_EX,RW_LOCK_SHARED, |
1026 | - # there will be different text output |
1027 | - if ( $text =~ m/^Mutex at/m ) { |
1028 | - $result{'request_type'} = 'M'; |
1029 | - @result{ qw( lock_mem_addr lock_cfile_name lock_cline lock_var) } |
1030 | - = $text =~ m/^Mutex at $h created file $fl, lock var $d$/m; |
1031 | - @result{ qw( waiters_flag )} |
1032 | - = $text =~ m/^waiters flag $d$/m; |
1033 | - } |
1034 | - else { |
1035 | - @result{ qw( request_type lock_mem_addr lock_cfile_name lock_cline) } |
1036 | - = $text =~ m/^(.)-lock on RW-latch at $h created in file $fl$/m; |
1037 | - @result{ qw( writer_thread writer_lock_mode ) } |
1038 | - = $text =~ m/^a writer \(thread id $d\) has reserved it in mode (.*)$/m; |
1039 | - @result{ qw( num_readers waiters_flag )} |
1040 | - = $text =~ m/^number of readers $d, waiters flag $d$/m; |
1041 | - @result{ qw(last_s_file_name last_s_line ) } |
1042 | - = $text =~ m/Last time read locked in file $fl$/m; |
1043 | - @result{ qw(last_x_file_name last_x_line ) } |
1044 | - = $text =~ m/Last time write locked in file $fl$/m; |
1045 | - } |
1046 | - |
1047 | - $result{'cell_waiting'} = $text =~ m/^wait has ended$/m ? 0 : 1; |
1048 | - $result{'cell_event_set'} = $text =~ m/^wait is ending$/m ? 1 : 0; |
1049 | - |
1050 | - # Because there are two code paths, some things won't get set. |
1051 | - map { $result{$_} ||= '' } |
1052 | - qw(last_s_file_name last_x_file_name writer_lock_mode); |
1053 | - map { $result{$_} ||= 0 } |
1054 | - qw(num_readers lock_var last_s_line last_x_line writer_thread); |
1055 | - |
1056 | - return \%result; |
1057 | -} |
1058 | - |
1059 | -sub parse_sm_section { |
1060 | - my ( $section, $complete, $debug, $full ) = @_; |
1061 | - return 0 unless $section && $section->{'fulltext'}; |
1062 | - my $fulltext = $section->{'fulltext'}; |
1063 | - |
1064 | - # Grab the info |
1065 | - @{$section}{ 'reservation_count', 'signal_count' } |
1066 | - = $fulltext =~ m/^OS WAIT ARRAY INFO: reservation count $d, signal count $d$/m; |
1067 | - @{$section}{ 'mutex_spin_waits', 'mutex_spin_rounds', 'mutex_os_waits' } |
1068 | - = $fulltext =~ m/^Mutex spin waits $d, rounds $d, OS waits $d$/m; |
1069 | - @{$section}{ 'rw_shared_spins', 'rw_shared_os_waits', 'rw_excl_spins', 'rw_excl_os_waits' } |
1070 | - = $fulltext =~ m/^RW-shared spins $d, OS waits $d; RW-excl spins $d, OS waits $d$/m; |
1071 | - |
1072 | - # Look for info on waits. |
1073 | - my @waits = $fulltext =~ m/^(--Thread.*?)^(?=Mutex spin|--Thread)/gms; |
1074 | - $section->{'waits'} = [ map { parse_wait_array($_, $complete, $debug) } @waits ]; |
1075 | - $section->{'wait_array_size'} = scalar(@waits); |
1076 | - |
1077 | - delete $section->{'fulltext'} unless $debug; |
1078 | - return 1; |
1079 | -} |
1080 | - |
1081 | -# I've read the source for this section. |
1082 | -sub parse_bp_section { |
1083 | - my ( $section, $complete, $debug, $full ) = @_; |
1084 | - return unless $section && $section->{'fulltext'}; |
1085 | - my $fulltext = $section->{'fulltext'}; |
1086 | - |
1087 | - # Grab the info |
1088 | - @{$section}{ 'total_mem_alloc', 'add_pool_alloc' } |
1089 | - = $fulltext =~ m/^Total memory allocated $d; in additional pool allocated $d$/m; |
1090 | - @{$section}{'dict_mem_alloc'} = $fulltext =~ m/Dictionary memory allocated $d/; |
1091 | - @{$section}{'awe_mem_alloc'} = $fulltext =~ m/$d MB of AWE memory/; |
1092 | - @{$section}{'buf_pool_size'} = $fulltext =~ m/^Buffer pool size\s*$d$/m; |
1093 | - @{$section}{'buf_free'} = $fulltext =~ m/^Free buffers\s*$d$/m; |
1094 | - @{$section}{'pages_total'} = $fulltext =~ m/^Database pages\s*$d$/m; |
1095 | - @{$section}{'pages_modified'} = $fulltext =~ m/^Modified db pages\s*$d$/m; |
1096 | - @{$section}{'pages_read', 'pages_created', 'pages_written'} |
1097 | - = $fulltext =~ m/^Pages read $d, created $d, written $d$/m; |
1098 | - @{$section}{'page_reads_sec', 'page_creates_sec', 'page_writes_sec'} |
1099 | - = $fulltext =~ m{^$f reads/s, $f creates/s, $f writes/s$}m; |
1100 | - @{$section}{'buf_pool_hits', 'buf_pool_reads'} |
1101 | - = $fulltext =~ m{Buffer pool hit rate $d / $d$}m; |
1102 | - if ($fulltext =~ m/^No buffer pool page gets since the last printout$/m) { |
1103 | - @{$section}{'buf_pool_hits', 'buf_pool_reads'} = (0, 0); |
1104 | - @{$section}{'buf_pool_hit_rate'} = '--'; |
1105 | - } |
1106 | - else { |
1107 | - @{$section}{'buf_pool_hit_rate'} |
1108 | - = $fulltext =~ m{Buffer pool hit rate (\d+ / \d+)$}m; |
1109 | - } |
1110 | - @{$section}{'reads_pending'} = $fulltext =~ m/^Pending reads $d/m; |
1111 | - @{$section}{'writes_pending_lru', 'writes_pending_flush_list', 'writes_pending_single_page' } |
1112 | - = $fulltext =~ m/^Pending writes: LRU $d, flush list $d, single page $d$/m; |
1113 | - |
1114 | - map { $section->{$_} ||= 0 } |
1115 | - qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page |
1116 | - awe_mem_alloc dict_mem_alloc); |
1117 | - @{$section}{'writes_pending'} = List::Util::sum( |
1118 | - @{$section}{ qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page) }); |
1119 | - |
1120 | - delete $section->{'fulltext'} unless $debug; |
1121 | - return 1; |
1122 | -} |
1123 | - |
1124 | -# I've read the source for this. |
1125 | -sub parse_io_section { |
1126 | - my ( $section, $complete, $debug, $full ) = @_; |
1127 | - return unless $section && $section->{'fulltext'}; |
1128 | - my $fulltext = $section->{'fulltext'}; |
1129 | - $section->{'threads'} = {}; |
1130 | - |
1131 | - # Grab the I/O thread info |
1132 | - my @threads = $fulltext =~ m<^(I/O thread \d+ .*)$>gm; |
1133 | - foreach my $thread (@threads) { |
1134 | - my ( $tid, $state, $purpose, $event_set ) |
1135 | - = $thread =~ m{I/O thread $d state: (.+?) \((.*)\)(?: ev set)?$}m; |
1136 | - if ( defined $tid ) { |
1137 | - $section->{'threads'}->{$tid} = { |
1138 | - thread => $tid, |
1139 | - state => $state, |
1140 | - purpose => $purpose, |
1141 | - event_set => $event_set ? 1 : 0, |
1142 | - }; |
1143 | - } |
1144 | - } |
1145 | - |
1146 | - # Grab the reads/writes/flushes info |
1147 | - @{$section}{ 'pending_normal_aio_reads', 'pending_aio_writes' } |
1148 | - = $fulltext =~ m/^Pending normal aio reads: $d, aio writes: $d,$/m; |
1149 | - @{$section}{ 'pending_ibuf_aio_reads', 'pending_log_ios', 'pending_sync_ios' } |
1150 | - = $fulltext =~ m{^ ibuf aio reads: $d, log i/o's: $d, sync i/o's: $d$}m; |
1151 | - @{$section}{ 'flush_type', 'pending_log_flushes', 'pending_buffer_pool_flushes' } |
1152 | - = $fulltext =~ m/^Pending flushes \($w\) log: $d; buffer pool: $d$/m; |
1153 | - @{$section}{ 'os_file_reads', 'os_file_writes', 'os_fsyncs' } |
1154 | - = $fulltext =~ m/^$d OS file reads, $d OS file writes, $d OS fsyncs$/m; |
1155 | - @{$section}{ 'reads_s', 'avg_bytes_s', 'writes_s', 'fsyncs_s' } |
1156 | - = $fulltext =~ m{^$f reads/s, $d avg bytes/read, $f writes/s, $f fsyncs/s$}m; |
1157 | - @{$section}{ 'pending_preads', 'pending_pwrites' } |
1158 | - = $fulltext =~ m/$d pending preads, $d pending pwrites$/m; |
1159 | - @{$section}{ 'pending_preads', 'pending_pwrites' } = (0, 0) |
1160 | - unless defined($section->{'pending_preads'}); |
1161 | - |
1162 | - delete $section->{'fulltext'} unless $debug; |
1163 | - return 1; |
1164 | -} |
1165 | - |
1166 | -sub _debug { |
1167 | - my ( $debug, $msg ) = @_; |
1168 | - if ( $debug ) { |
1169 | - die $msg; |
1170 | - } |
1171 | - else { |
1172 | - warn $msg; |
1173 | - } |
1174 | - return 1; |
1175 | -} |
1176 | - |
1177 | -1; |
1178 | - |
1179 | -# end_of_package |
1180 | -# ############################################################################ |
1181 | -# Perldoc section. I put this last as per the Dog book. |
1182 | -# ############################################################################ |
1183 | -=pod |
1184 | - |
1185 | -=head1 NAME |
1186 | - |
1187 | -InnoDBParser - Parse InnoDB monitor text. |
1188 | - |
1189 | -=head1 DESCRIPTION |
1190 | - |
1191 | -InnoDBParser tries to parse the output of the InnoDB monitor. One way to get |
1192 | -this output is to connect to a MySQL server and issue the command SHOW ENGINE |
1193 | -INNODB STATUS (omit 'ENGINE' on earlier versions of MySQL). The goal is to |
1194 | -turn text into data that something else (e.g. innotop) can use. |
1195 | - |
1196 | -The output comes from all over, but the place to start in the source is |
1197 | -innobase/srv/srv0srv.c. |
1198 | - |
1199 | -=head1 SYNOPSIS |
1200 | - |
1201 | - use InnoDBParser; |
1202 | - use DBI; |
1203 | - |
1204 | - # Get the status text. |
1205 | - my $dbh = DBI->connect( |
1206 | - "DBI::mysql:test;host=localhost", |
1207 | - 'user', |
1208 | - 'password' |
1209 | - ); |
1210 | - my $query = 'SHOW /*!5 ENGINE */ INNODB STATUS'; |
1211 | - my $text = $dbh->selectcol_arrayref($query)->[0]; |
1212 | - |
1213 | - # 1 or 0 |
1214 | - my $debug = 1; |
1215 | - |
1216 | - # Choose sections of the monitor text you want. Possible values: |
1217 | - # TRANSACTIONS => tx |
1218 | - # BUFFER POOL AND MEMORY => bp |
1219 | - # SEMAPHORES => sm |
1220 | - # LOG => lg |
1221 | - # ROW OPERATIONS => ro |
1222 | - # INSERT BUFFER AND ADAPTIVE HASH INDEX => ib |
1223 | - # FILE I/O => io |
1224 | - # LATEST DETECTED DEADLOCK => dl |
1225 | - # LATEST FOREIGN KEY ERROR => fk |
1226 | - |
1227 | - my $required_sections = { |
1228 | - tx => 1, |
1229 | - }; |
1230 | - |
1231 | - # Parse the status text. |
1232 | - my $parser = InnoDBParser->new; |
1233 | - $innodb_status = $parser->parse_status_text( |
1234 | - $text, |
1235 | - $debug, |
1236 | - # Omit the following parameter to get all sections. |
1237 | - $required_sections, |
1238 | - ); |
1239 | - |
1240 | -=head1 COPYRIGHT, LICENSE AND WARRANTY |
1241 | - |
1242 | -This package is copyright (c) 2006 Baron Schwartz, baron at xaprb dot com. |
1243 | -Feedback and improvements are gratefully received. |
1244 | - |
1245 | -THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED |
1246 | -WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF |
1247 | -MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
1248 | - |
1249 | -This program is free software; you can redistribute it and/or modify it under |
1250 | -the terms of the GNU General Public License as published by the Free Software |
1251 | -Foundation, version 2; OR the Perl Artistic License. On UNIX and similar |
1252 | -systems, you can issue `man perlgpl' or `man perlartistic' to read these |
1253 | -licenses. |
1254 | - |
1255 | -You should have received a copy of the GNU General Public License along with |
1256 | -this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
1257 | -Place, Suite 330, Boston, MA 02111-1307 USA |
1258 | - |
1259 | -=head1 AUTHOR |
1260 | - |
1261 | -Baron Schwartz, baron at xaprb dot com. |
1262 | - |
1263 | -=head1 BUGS |
1264 | - |
1265 | -None known, but I bet there are some. The InnoDB monitor text wasn't really |
1266 | -designed to be parsable. |
1267 | - |
1268 | -=head1 SEE ALSO |
1269 | - |
1270 | -innotop - a program that can format the parsed status information for humans |
1271 | -to read and enjoy. |
1272 | - |
1273 | -=cut |
1274 | |
1275 | === modified file 'bakery/debian-5.1/additions/innotop/changelog.innotop' |
1276 | --- bakery/debian-5.1/additions/innotop/changelog.innotop 2008-10-20 22:54:11 +0000 |
1277 | +++ bakery/debian-5.1/additions/innotop/changelog.innotop 2009-10-02 06:45:25 +0000 |
1278 | @@ -1,4 +1,43 @@ |
1279 | -Changelog for innotop and InnoDBParser: |
1280 | +Changelog for innotop: |
1281 | + |
1282 | +2009-03-09: version 1.7.1 |
1283 | + |
1284 | + Changes: |
1285 | + * Don't display the CXN column if only one connection is active in |
1286 | + the current view |
1287 | + |
1288 | + Bugs fixed: |
1289 | + * fixed bug where trying to aggregate the time column would result |
1290 | + in a crash if the time column had an undef value in it, which is |
1291 | + the case when a thread is in the 'Connect' state |
1292 | + * updated innotop.spec file to reflect current version |
1293 | + |
1294 | +2009-02-23: version 1.7.0 |
1295 | + |
1296 | + Changes: |
1297 | + * supports a central config (/etc/innotop/innotop.conf) |
1298 | + * changed the default home directory config to ~/.innotop/innotop.conf |
1299 | + (away from .ini) |
1300 | + * embedded InnoDBParser.pm into innotop so it can be run with no |
1301 | + installation |
1302 | + * no longer writes a new config file by default |
1303 | + * added --skipcentral (skip reading central config) and --write (write |
1304 | + a config if none were loaded at start-up) |
1305 | + * if no config file is loaded, connect to a MySQL database on |
1306 | + localhost using mysql_read_default_group=client |
1307 | + * embedded maatkit's DSNParser.pm and added support for --user, |
1308 | + --password, --host, --port |
1309 | + * changed default mode from T (InnoDB Transactions) to Q (Query List) |
1310 | + * in addition to connected threads, now displays running and cached |
1311 | + threads in statusbar |
1312 | + * don't load connections from a config file if any DSN information or |
1313 | + a username or password is specified on the command-line |
1314 | + |
1315 | + Bugs fixed: |
1316 | + * fixed bug preventing utilization of command-line options that |
1317 | + override default config settings if no config file was loaded |
1318 | + * fixed a bug where migrating from an old version of the config will |
1319 | + delete ~/innotop.ini, if it exists. Now uses File::Temp::tempfile(). |
1320 | |
1321 | 2007-11-09: version 1.6.0 |
1322 | |
1323 | |
1324 | === modified file 'bakery/debian-5.1/additions/innotop/innotop' |
1325 | --- bakery/debian-5.1/additions/innotop/innotop 2008-10-20 22:54:11 +0000 |
1326 | +++ bakery/debian-5.1/additions/innotop/innotop 2009-10-02 06:45:25 +0000 |
1327 | @@ -2,26 +2,1346 @@ |
1328 | |
1329 | # vim: tw=160:nowrap:expandtab:tabstop=3:shiftwidth=3:softtabstop=3 |
1330 | |
1331 | +# This program is copyright (c) 2006 Baron Schwartz, baron at xaprb dot com. |
1332 | +# Feedback and improvements are gratefully received. |
1333 | +# |
1334 | +# THIS PROGRAM IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED |
1335 | +# WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF |
1336 | +# MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. |
1337 | +# |
1338 | +# This program is free software; you can redistribute it and/or modify it under |
1339 | +# the terms of the GNU General Public License as published by the Free Software |
1340 | +# Foundation, version 2; OR the Perl Artistic License. On UNIX and similar |
1341 | +# systems, you can issue `man perlgpl' or `man perlartistic' to read these |
1342 | + |
1343 | +# You should have received a copy of the GNU General Public License along with |
1344 | +# this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
1345 | +# Place, Suite 330, Boston, MA 02111-1307 USA |
1346 | + |
1347 | use strict; |
1348 | use warnings FATAL => 'all'; |
1349 | + |
1350 | +our $VERSION = '1.7.1'; |
1351 | + |
1352 | +# Find the home directory; it's different on different OSes. |
1353 | +our $homepath = $ENV{HOME} || $ENV{HOMEPATH} || $ENV{USERPROFILE} || '.'; |
1354 | + |
1355 | +# Configuration files |
1356 | +our $default_home_conf = "$homepath/.innotop/innotop.conf"; |
1357 | +our $default_central_conf = "/etc/innotop/innotop.conf"; |
1358 | +our $conf_file = ""; |
1359 | + |
1360 | +## Begin packages ## |
1361 | + |
1362 | +package DSNParser; |
1363 | + |
1364 | +use DBI; |
1365 | +use Data::Dumper; |
1366 | +$Data::Dumper::Indent = 0; |
1367 | +$Data::Dumper::Quotekeys = 0; |
1368 | +use English qw(-no_match_vars); |
1369 | + |
1370 | +use constant MKDEBUG => $ENV{MKDEBUG}; |
1371 | + |
1372 | +# Defaults are built-in, but you can add/replace items by passing them as |
1373 | +# hashrefs of {key, desc, copy, dsn}. The desc and dsn items are optional. |
1374 | +# You can set properties with the prop() sub. Don't set the 'opts' property. |
1375 | +sub new { |
1376 | + my ( $class, @opts ) = @_; |
1377 | + my $self = { |
1378 | + opts => { |
1379 | + A => { |
1380 | + desc => 'Default character set', |
1381 | + dsn => 'charset', |
1382 | + copy => 1, |
1383 | + }, |
1384 | + D => { |
1385 | + desc => 'Database to use', |
1386 | + dsn => 'database', |
1387 | + copy => 1, |
1388 | + }, |
1389 | + F => { |
1390 | + desc => 'Only read default options from the given file', |
1391 | + dsn => 'mysql_read_default_file', |
1392 | + copy => 1, |
1393 | + }, |
1394 | + h => { |
1395 | + desc => 'Connect to host', |
1396 | + dsn => 'host', |
1397 | + copy => 1, |
1398 | + }, |
1399 | + p => { |
1400 | + desc => 'Password to use when connecting', |
1401 | + dsn => 'password', |
1402 | + copy => 1, |
1403 | + }, |
1404 | + P => { |
1405 | + desc => 'Port number to use for connection', |
1406 | + dsn => 'port', |
1407 | + copy => 1, |
1408 | + }, |
1409 | + S => { |
1410 | + desc => 'Socket file to use for connection', |
1411 | + dsn => 'mysql_socket', |
1412 | + copy => 1, |
1413 | + }, |
1414 | + u => { |
1415 | + desc => 'User for login if not current user', |
1416 | + dsn => 'user', |
1417 | + copy => 1, |
1418 | + }, |
1419 | + }, |
1420 | + }; |
1421 | + foreach my $opt ( @opts ) { |
1422 | + MKDEBUG && _d('Adding extra property ' . $opt->{key}); |
1423 | + $self->{opts}->{$opt->{key}} = { desc => $opt->{desc}, copy => $opt->{copy} }; |
1424 | + } |
1425 | + return bless $self, $class; |
1426 | +} |
1427 | + |
1428 | +# Recognized properties: |
1429 | +# * autokey: which key to treat a bareword as (typically h=host). |
1430 | +# * dbidriver: which DBI driver to use; assumes mysql, supports Pg. |
1431 | +# * required: which parts are required (hashref). |
1432 | +# * setvars: a list of variables to set after connecting |
1433 | +sub prop { |
1434 | + my ( $self, $prop, $value ) = @_; |
1435 | + if ( @_ > 2 ) { |
1436 | + MKDEBUG && _d("Setting $prop property"); |
1437 | + $self->{$prop} = $value; |
1438 | + } |
1439 | + return $self->{$prop}; |
1440 | +} |
1441 | + |
1442 | +sub parse { |
1443 | + my ( $self, $dsn, $prev, $defaults ) = @_; |
1444 | + if ( !$dsn ) { |
1445 | + MKDEBUG && _d('No DSN to parse'); |
1446 | + return; |
1447 | + } |
1448 | + MKDEBUG && _d("Parsing $dsn"); |
1449 | + $prev ||= {}; |
1450 | + $defaults ||= {}; |
1451 | + my %given_props; |
1452 | + my %final_props; |
1453 | + my %opts = %{$self->{opts}}; |
1454 | + my $prop_autokey = $self->prop('autokey'); |
1455 | + |
1456 | + # Parse given props |
1457 | + foreach my $dsn_part ( split(/,/, $dsn) ) { |
1458 | + if ( my ($prop_key, $prop_val) = $dsn_part =~ m/^(.)=(.*)$/ ) { |
1459 | + # Handle the typical DSN parts like h=host, P=3306, etc. |
1460 | + $given_props{$prop_key} = $prop_val; |
1461 | + } |
1462 | + elsif ( $prop_autokey ) { |
1463 | + # Handle barewords |
1464 | + MKDEBUG && _d("Interpreting $dsn_part as $prop_autokey=$dsn_part"); |
1465 | + $given_props{$prop_autokey} = $dsn_part; |
1466 | + } |
1467 | + else { |
1468 | + MKDEBUG && _d("Bad DSN part: $dsn_part"); |
1469 | + } |
1470 | + } |
1471 | + |
1472 | + # Fill in final props from given, previous, and/or default props |
1473 | + foreach my $key ( keys %opts ) { |
1474 | + MKDEBUG && _d("Finding value for $key"); |
1475 | + $final_props{$key} = $given_props{$key}; |
1476 | + if ( !defined $final_props{$key} |
1477 | + && defined $prev->{$key} && $opts{$key}->{copy} ) |
1478 | + { |
1479 | + $final_props{$key} = $prev->{$key}; |
1480 | + MKDEBUG && _d("Copying value for $key from previous DSN"); |
1481 | + } |
1482 | + if ( !defined $final_props{$key} ) { |
1483 | + $final_props{$key} = $defaults->{$key}; |
1484 | + MKDEBUG && _d("Copying value for $key from defaults"); |
1485 | + } |
1486 | + } |
1487 | + |
1488 | + # Sanity check props |
1489 | + foreach my $key ( keys %given_props ) { |
1490 | + die "Unrecognized DSN part '$key' in '$dsn'\n" |
1491 | + unless exists $opts{$key}; |
1492 | + } |
1493 | + if ( (my $required = $self->prop('required')) ) { |
1494 | + foreach my $key ( keys %$required ) { |
1495 | + die "Missing DSN part '$key' in '$dsn'\n" unless $final_props{$key}; |
1496 | + } |
1497 | + } |
1498 | + |
1499 | + return \%final_props; |
1500 | +} |
1501 | + |
1502 | +sub as_string { |
1503 | + my ( $self, $dsn ) = @_; |
1504 | + return $dsn unless ref $dsn; |
1505 | + return join(',', |
1506 | + map { "$_=" . ($_ eq 'p' ? '...' : $dsn->{$_}) } |
1507 | + grep { defined $dsn->{$_} && $self->{opts}->{$_} } |
1508 | + sort keys %$dsn ); |
1509 | +} |
1510 | + |
1511 | +sub usage { |
1512 | + my ( $self ) = @_; |
1513 | + my $usage |
1514 | + = "DSN syntax is key=value[,key=value...] Allowable DSN keys:\n" |
1515 | + . " KEY COPY MEANING\n" |
1516 | + . " === ==== =============================================\n"; |
1517 | + my %opts = %{$self->{opts}}; |
1518 | + foreach my $key ( sort keys %opts ) { |
1519 | + $usage .= " $key " |
1520 | + . ($opts{$key}->{copy} ? 'yes ' : 'no ') |
1521 | + . ($opts{$key}->{desc} || '[No description]') |
1522 | + . "\n"; |
1523 | + } |
1524 | + if ( (my $key = $self->prop('autokey')) ) { |
1525 | + $usage .= " If the DSN is a bareword, the word is treated as the '$key' key.\n"; |
1526 | + } |
1527 | + return $usage; |
1528 | +} |
1529 | + |
1530 | +# Supports PostgreSQL via the dbidriver element of $info, but assumes MySQL by |
1531 | +# default. |
1532 | +sub get_cxn_params { |
1533 | + my ( $self, $info ) = @_; |
1534 | + my $dsn; |
1535 | + my %opts = %{$self->{opts}}; |
1536 | + my $driver = $self->prop('dbidriver') || ''; |
1537 | + if ( $driver eq 'Pg' ) { |
1538 | + $dsn = 'DBI:Pg:dbname=' . ( $info->{D} || '' ) . ';' |
1539 | + . join(';', map { "$opts{$_}->{dsn}=$info->{$_}" } |
1540 | + grep { defined $info->{$_} } |
1541 | + qw(h P)); |
1542 | + } |
1543 | + else { |
1544 | + $dsn = 'DBI:mysql:' . ( $info->{D} || '' ) . ';' |
1545 | + . join(';', map { "$opts{$_}->{dsn}=$info->{$_}" } |
1546 | + grep { defined $info->{$_} } |
1547 | + qw(F h P S A)) |
1548 | + . ';mysql_read_default_group=client'; |
1549 | + } |
1550 | + MKDEBUG && _d($dsn); |
1551 | + return ($dsn, $info->{u}, $info->{p}); |
1552 | +} |
1553 | + |
1554 | + |
1555 | +# Fills in missing info from a DSN after successfully connecting to the server. |
1556 | +sub fill_in_dsn { |
1557 | + my ( $self, $dbh, $dsn ) = @_; |
1558 | + my $vars = $dbh->selectall_hashref('SHOW VARIABLES', 'Variable_name'); |
1559 | + my ($user, $db) = $dbh->selectrow_array('SELECT USER(), DATABASE()'); |
1560 | + $user =~ s/@.*//; |
1561 | + $dsn->{h} ||= $vars->{hostname}->{Value}; |
1562 | + $dsn->{S} ||= $vars->{'socket'}->{Value}; |
1563 | + $dsn->{P} ||= $vars->{port}->{Value}; |
1564 | + $dsn->{u} ||= $user; |
1565 | + $dsn->{D} ||= $db; |
1566 | +} |
1567 | + |
1568 | +sub get_dbh { |
1569 | + my ( $self, $cxn_string, $user, $pass, $opts ) = @_; |
1570 | + $opts ||= {}; |
1571 | + my $defaults = { |
1572 | + AutoCommit => 0, |
1573 | + RaiseError => 1, |
1574 | + PrintError => 0, |
1575 | + mysql_enable_utf8 => ($cxn_string =~ m/charset=utf8/ ? 1 : 0), |
1576 | + }; |
1577 | + @{$defaults}{ keys %$opts } = values %$opts; |
1578 | + my $dbh; |
1579 | + my $tries = 2; |
1580 | + while ( !$dbh && $tries-- ) { |
1581 | + eval { |
1582 | + MKDEBUG && _d($cxn_string, ' ', $user, ' ', $pass, ' {', |
1583 | + join(', ', map { "$_=>$defaults->{$_}" } keys %$defaults ), '}'); |
1584 | + $dbh = DBI->connect($cxn_string, $user, $pass, $defaults); |
1585 | + # Immediately set character set and binmode on STDOUT. |
1586 | + if ( my ($charset) = $cxn_string =~ m/charset=(\w+)/ ) { |
1587 | + my $sql = "/*!40101 SET NAMES $charset*/"; |
1588 | + MKDEBUG && _d("$dbh: $sql"); |
1589 | + $dbh->do($sql); |
1590 | + MKDEBUG && _d('Enabling charset for STDOUT'); |
1591 | + if ( $charset eq 'utf8' ) { |
1592 | + binmode(STDOUT, ':utf8') |
1593 | + or die "Can't binmode(STDOUT, ':utf8'): $OS_ERROR"; |
1594 | + } |
1595 | + else { |
1596 | + binmode(STDOUT) or die "Can't binmode(STDOUT): $OS_ERROR"; |
1597 | + } |
1598 | + } |
1599 | + }; |
1600 | + if ( !$dbh && $EVAL_ERROR ) { |
1601 | + MKDEBUG && _d($EVAL_ERROR); |
1602 | + if ( $EVAL_ERROR =~ m/not a compiled character set|character set utf8/ ) { |
1603 | + MKDEBUG && _d("Going to try again without utf8 support"); |
1604 | + delete $defaults->{mysql_enable_utf8}; |
1605 | + } |
1606 | + if ( !$tries ) { |
1607 | + die $EVAL_ERROR; |
1608 | + } |
1609 | + } |
1610 | + } |
1611 | + # If setvars exists and it's MySQL connection, set them |
1612 | + my $setvars = $self->prop('setvars'); |
1613 | + if ( $cxn_string =~ m/mysql/i && $setvars ) { |
1614 | + my $sql = "SET $setvars"; |
1615 | + MKDEBUG && _d("$dbh: $sql"); |
1616 | + eval { |
1617 | + $dbh->do($sql); |
1618 | + }; |
1619 | + if ( $EVAL_ERROR ) { |
1620 | + MKDEBUG && _d($EVAL_ERROR); |
1621 | + } |
1622 | + } |
1623 | + MKDEBUG && _d('DBH info: ', |
1624 | + $dbh, |
1625 | + Dumper($dbh->selectrow_hashref( |
1626 | + 'SELECT DATABASE(), CONNECTION_ID(), VERSION()/*!50038 , @@hostname*/')), |
1627 | + ' Connection info: ', ($dbh->{mysql_hostinfo} || 'undef'), |
1628 | + ' Character set info: ', |
1629 | + Dumper($dbh->selectall_arrayref( |
1630 | + 'SHOW VARIABLES LIKE "character_set%"', { Slice => {}})), |
1631 | + ' $DBD::mysql::VERSION: ', $DBD::mysql::VERSION, |
1632 | + ' $DBI::VERSION: ', $DBI::VERSION, |
1633 | + ); |
1634 | + return $dbh; |
1635 | +} |
1636 | + |
1637 | +# Tries to figure out a hostname for the connection. |
1638 | +sub get_hostname { |
1639 | + my ( $self, $dbh ) = @_; |
1640 | + if ( my ($host) = ($dbh->{mysql_hostinfo} || '') =~ m/^(\w+) via/ ) { |
1641 | + return $host; |
1642 | + } |
1643 | + my ( $hostname, $one ) = $dbh->selectrow_array( |
1644 | + 'SELECT /*!50038 @@hostname, */ 1'); |
1645 | + return $hostname; |
1646 | +} |
1647 | + |
1648 | +# Disconnects a database handle, but complains verbosely if there are any active |
1649 | +# children. These are usually $sth handles that haven't been finish()ed. |
1650 | +sub disconnect { |
1651 | + my ( $self, $dbh ) = @_; |
1652 | + MKDEBUG && $self->print_active_handles($dbh); |
1653 | + $dbh->disconnect; |
1654 | +} |
1655 | + |
1656 | +sub print_active_handles { |
1657 | + my ( $self, $thing, $level ) = @_; |
1658 | + $level ||= 0; |
1659 | + printf("# Active %sh: %s %s %s\n", ($thing->{Type} || 'undef'), "\t" x $level, |
1660 | + $thing, (($thing->{Type} || '') eq 'st' ? $thing->{Statement} || '' : '')) |
1661 | + or die "Cannot print: $OS_ERROR"; |
1662 | + foreach my $handle ( grep {defined} @{ $thing->{ChildHandles} } ) { |
1663 | + $self->print_active_handles( $handle, $level + 1 ); |
1664 | + } |
1665 | +} |
1666 | + |
1667 | +sub _d { |
1668 | + my ($package, undef, $line) = caller 0; |
1669 | + @_ = map { (my $temp = $_) =~ s/\n/\n# /g; $temp; } |
1670 | + map { defined $_ ? $_ : 'undef' } |
1671 | + @_; |
1672 | + # Use $$ instead of $PID in case the package |
1673 | + # does not use English. |
1674 | + print "# $package:$line $$ ", @_, "\n"; |
1675 | +} |
1676 | + |
1677 | +1; |
1678 | + |
1679 | +package InnoDBParser; |
1680 | + |
1681 | +use Data::Dumper; |
1682 | +$Data::Dumper::Sortkeys = 1; |
1683 | +use English qw(-no_match_vars); |
1684 | +use List::Util qw(max); |
1685 | + |
1686 | +# Some common patterns |
1687 | +my $d = qr/(\d+)/; # Digit |
1688 | +my $f = qr/(\d+\.\d+)/; # Float |
1689 | +my $t = qr/(\d+ \d+)/; # Transaction ID |
1690 | +my $i = qr/((?:\d{1,3}\.){3}\d+)/; # IP address |
1691 | +my $n = qr/([^`\s]+)/; # MySQL object name |
1692 | +my $w = qr/(\w+)/; # Words |
1693 | +my $fl = qr/([\w\.\/]+) line $d/; # Filename and line number |
1694 | +my $h = qr/((?:0x)?[0-9a-f]*)/; # Hex |
1695 | +my $s = qr/(\d{6} .\d:\d\d:\d\d)/; # InnoDB timestamp |
1696 | + |
1697 | +# If you update this variable, also update the SYNOPSIS in the pod. |
1698 | +my %innodb_section_headers = ( |
1699 | + "TRANSACTIONS" => "tx", |
1700 | + "BUFFER POOL AND MEMORY" => "bp", |
1701 | + "SEMAPHORES" => "sm", |
1702 | + "LOG" => "lg", |
1703 | + "ROW OPERATIONS" => "ro", |
1704 | + "INSERT BUFFER AND ADAPTIVE HASH INDEX" => "ib", |
1705 | + "FILE I/O" => "io", |
1706 | + "LATEST DETECTED DEADLOCK" => "dl", |
1707 | + "LATEST FOREIGN KEY ERROR" => "fk", |
1708 | +); |
1709 | + |
1710 | +my %parser_for = ( |
1711 | + tx => \&parse_tx_section, |
1712 | + bp => \&parse_bp_section, |
1713 | + sm => \&parse_sm_section, |
1714 | + lg => \&parse_lg_section, |
1715 | + ro => \&parse_ro_section, |
1716 | + ib => \&parse_ib_section, |
1717 | + io => \&parse_io_section, |
1718 | + dl => \&parse_dl_section, |
1719 | + fk => \&parse_fk_section, |
1720 | +); |
1721 | + |
1722 | +my %fk_parser_for = ( |
1723 | + Transaction => \&parse_fk_transaction_error, |
1724 | + Error => \&parse_fk_bad_constraint_error, |
1725 | + Cannot => \&parse_fk_cant_drop_parent_error, |
1726 | +); |
1727 | + |
1728 | +# A thread's proc_info can be at least 98 different things I've found in the |
1729 | +# source. Fortunately, most of them begin with a gerunded verb. These are |
1730 | +# the ones that don't. |
1731 | +my %is_proc_info = ( |
1732 | + 'After create' => 1, |
1733 | + 'Execution of init_command' => 1, |
1734 | + 'FULLTEXT initialization' => 1, |
1735 | + 'Reopen tables' => 1, |
1736 | + 'Repair done' => 1, |
1737 | + 'Repair with keycache' => 1, |
1738 | + 'System lock' => 1, |
1739 | + 'Table lock' => 1, |
1740 | + 'Thread initialized' => 1, |
1741 | + 'User lock' => 1, |
1742 | + 'copy to tmp table' => 1, |
1743 | + 'discard_or_import_tablespace' => 1, |
1744 | + 'end' => 1, |
1745 | + 'got handler lock' => 1, |
1746 | + 'got old table' => 1, |
1747 | + 'init' => 1, |
1748 | + 'key cache' => 1, |
1749 | + 'locks' => 1, |
1750 | + 'malloc' => 1, |
1751 | + 'query end' => 1, |
1752 | + 'rename result table' => 1, |
1753 | + 'rename' => 1, |
1754 | + 'setup' => 1, |
1755 | + 'statistics' => 1, |
1756 | + 'status' => 1, |
1757 | + 'table cache' => 1, |
1758 | + 'update' => 1, |
1759 | +); |
1760 | + |
1761 | +sub new { |
1762 | + bless {}, shift; |
1763 | +} |
1764 | + |
1765 | +# Parse the status and return it. |
1766 | +# See srv_printf_innodb_monitor in innobase/srv/srv0srv.c |
1767 | +# Pass in the text to parse, whether to be in debugging mode, which sections |
1768 | +# to parse (hashref; if empty, parse all), and whether to parse full info from |
1769 | +# locks and such (probably shouldn't unless you need to). |
1770 | +sub parse_status_text { |
1771 | + my ( $self, $fulltext, $debug, $sections, $full ) = @_; |
1772 | + |
1773 | + die "I can't parse undef" unless defined $fulltext; |
1774 | + $fulltext =~ s/[\r\n]+/\n/g; |
1775 | + |
1776 | + $sections ||= {}; |
1777 | + die '$sections must be a hashref' unless ref($sections) eq 'HASH'; |
1778 | + |
1779 | + my %innodb_data = ( |
1780 | + got_all => 0, # Whether I was able to get the whole thing |
1781 | + ts => '', # Timestamp the server put on it |
1782 | + last_secs => 0, # Num seconds the averages are over |
1783 | + sections => {}, # Parsed values from each section |
1784 | + ); |
1785 | + |
1786 | + if ( $debug ) { |
1787 | + $innodb_data{'fulltext'} = $fulltext; |
1788 | + } |
1789 | + |
1790 | + # Get the most basic info about the status: beginning and end, and whether |
1791 | + # I got the whole thing (if there has been a big deadlock and there are |
1792 | + # too many locks to print, the output might be truncated) |
1793 | + my ( $time_text ) = $fulltext =~ m/^$s INNODB MONITOR OUTPUT$/m; |
1794 | + $innodb_data{'ts'} = [ parse_innodb_timestamp( $time_text ) ]; |
1795 | + $innodb_data{'timestring'} = ts_to_string($innodb_data{'ts'}); |
1796 | + ( $innodb_data{'last_secs'} ) = $fulltext |
1797 | + =~ m/Per second averages calculated from the last $d seconds/; |
1798 | + |
1799 | + ( my $got_all ) = $fulltext =~ m/END OF INNODB MONITOR OUTPUT/; |
1800 | + $innodb_data{'got_all'} = $got_all || 0; |
1801 | + |
1802 | + # Split it into sections. Each section begins with |
1803 | + # ----- |
1804 | + # LABEL |
1805 | + # ----- |
1806 | + my %innodb_sections; |
1807 | + my @matches = $fulltext |
1808 | + =~ m#\n(---+)\n([A-Z /]+)\n\1\n(.*?)(?=\n(---+)\n[A-Z /]+\n\4\n|$)#gs; |
1809 | + while ( my ( $start, $name, $text, $end ) = splice(@matches, 0, 4) ) { |
1810 | + $innodb_sections{$name} = [ $text, $end ? 1 : 0 ]; |
1811 | + } |
1812 | + # The Row Operations section is a special case, because instead of ending |
1813 | + # with the beginning of another section, it ends with the end of the file. |
1814 | + # So this section is complete if the entire file is complete. |
1815 | + $innodb_sections{'ROW OPERATIONS'}->[1] ||= $innodb_data{'got_all'}; |
1816 | + |
1817 | + # Just for sanity's sake, make sure I understand what to do with each |
1818 | + # section |
1819 | + eval { |
1820 | + foreach my $section ( keys %innodb_sections ) { |
1821 | + my $header = $innodb_section_headers{$section}; |
1822 | + die "Unknown section $section in $fulltext\n" |
1823 | + unless $header; |
1824 | + $innodb_data{'sections'}->{ $header } |
1825 | + ->{'fulltext'} = $innodb_sections{$section}->[0]; |
1826 | + $innodb_data{'sections'}->{ $header } |
1827 | + ->{'complete'} = $innodb_sections{$section}->[1]; |
1828 | + } |
1829 | + }; |
1830 | + if ( $EVAL_ERROR ) { |
1831 | + _debug( $debug, $EVAL_ERROR); |
1832 | + } |
1833 | + |
1834 | + # ################################################################ |
1835 | + # Parse the detailed data out of the sections. |
1836 | + # ################################################################ |
1837 | + eval { |
1838 | + foreach my $section ( keys %parser_for ) { |
1839 | + if ( defined $innodb_data{'sections'}->{$section} |
1840 | + && (!%$sections || (defined($sections->{$section} && $sections->{$section})) )) { |
1841 | + $parser_for{$section}->( |
1842 | + $innodb_data{'sections'}->{$section}, |
1843 | + $innodb_data{'sections'}->{$section}->{'complete'}, |
1844 | + $debug, |
1845 | + $full ) |
1846 | + or delete $innodb_data{'sections'}->{$section}; |
1847 | + } |
1848 | + else { |
1849 | + delete $innodb_data{'sections'}->{$section}; |
1850 | + } |
1851 | + } |
1852 | + }; |
1853 | + if ( $EVAL_ERROR ) { |
1854 | + _debug( $debug, $EVAL_ERROR); |
1855 | + } |
1856 | + |
1857 | + return \%innodb_data; |
1858 | +} |
1859 | + |
1860 | +# Parses the status text and returns it flattened out as a single hash. |
1861 | +sub get_status_hash { |
1862 | + my ( $self, $fulltext, $debug, $sections, $full ) = @_; |
1863 | + |
1864 | + # Parse the status text... |
1865 | + my $innodb_status |
1866 | + = $self->parse_status_text($fulltext, $debug, $sections, $full ); |
1867 | + |
1868 | + # Flatten the hierarchical structure into a single list by grabbing desired |
1869 | + # sections from it. |
1870 | + return |
1871 | + (map { 'IB_' . $_ => $innodb_status->{$_} } qw(timestring last_secs got_all)), |
1872 | + (map { 'IB_bp_' . $_ => $innodb_status->{'sections'}->{'bp'}->{$_} } |
1873 | + qw( writes_pending buf_pool_hit_rate total_mem_alloc buf_pool_reads |
1874 | + awe_mem_alloc pages_modified writes_pending_lru page_creates_sec |
1875 | + reads_pending pages_total buf_pool_hits writes_pending_single_page |
1876 | + page_writes_sec pages_read pages_written page_reads_sec |
1877 | + writes_pending_flush_list buf_pool_size add_pool_alloc |
1878 | + dict_mem_alloc pages_created buf_free complete )), |
1879 | + (map { 'IB_tx_' . $_ => $innodb_status->{'sections'}->{'tx'}->{$_} } |
1880 | + qw( num_lock_structs history_list_len purge_done_for transactions |
1881 | + purge_undo_for is_truncated trx_id_counter complete )), |
1882 | + (map { 'IB_ib_' . $_ => $innodb_status->{'sections'}->{'ib'}->{$_} } |
1883 | + qw( hash_table_size hash_searches_s non_hash_searches_s |
1884 | + bufs_in_node_heap used_cells size free_list_len seg_size inserts |
1885 | + merged_recs merges complete )), |
1886 | + (map { 'IB_lg_' . $_ => $innodb_status->{'sections'}->{'lg'}->{$_} } |
1887 | + qw( log_ios_done pending_chkp_writes last_chkp log_ios_s |
1888 | + log_flushed_to log_seq_no pending_log_writes complete )), |
1889 | + (map { 'IB_sm_' . $_ => $innodb_status->{'sections'}->{'sm'}->{$_} } |
1890 | + qw( wait_array_size rw_shared_spins rw_excl_os_waits mutex_os_waits |
1891 | + mutex_spin_rounds mutex_spin_waits rw_excl_spins rw_shared_os_waits |
1892 | + waits signal_count reservation_count complete )), |
1893 | + (map { 'IB_ro_' . $_ => $innodb_status->{'sections'}->{'ro'}->{$_} } |
1894 | + qw( queries_in_queue n_reserved_extents main_thread_state |
1895 | + main_thread_proc_no main_thread_id read_sec del_sec upd_sec ins_sec |
1896 | + read_views_open num_rows_upd num_rows_ins num_rows_read |
1897 | + queries_inside num_rows_del complete )), |
1898 | + (map { 'IB_fk_' . $_ => $innodb_status->{'sections'}->{'fk'}->{$_} } |
1899 | + qw( trigger parent_table child_index parent_index attempted_op |
1900 | + child_db timestring fk_name records col_name reason txn parent_db |
1901 | + type child_table parent_col complete )), |
1902 | + (map { 'IB_io_' . $_ => $innodb_status->{'sections'}->{'io'}->{$_} } |
1903 | + qw( pending_buffer_pool_flushes pending_pwrites pending_preads |
1904 | + pending_normal_aio_reads fsyncs_s os_file_writes pending_sync_ios |
1905 | + reads_s flush_type avg_bytes_s pending_ibuf_aio_reads writes_s |
1906 | + threads os_file_reads pending_aio_writes pending_log_ios os_fsyncs |
1907 | + pending_log_flushes complete )), |
1908 | + (map { 'IB_dl_' . $_ => $innodb_status->{'sections'}->{'dl'}->{$_} } |
1909 | + qw( timestring rolled_back txns complete )); |
1910 | + |
1911 | +} |
1912 | + |
1913 | +sub ts_to_string { |
1914 | + my $parts = shift; |
1915 | + return sprintf('%02d-%02d-%02d %02d:%02d:%02d', @$parts); |
1916 | +} |
1917 | + |
1918 | +sub parse_innodb_timestamp { |
1919 | + my $text = shift; |
1920 | + my ( $y, $m, $d, $h, $i, $s ) |
1921 | + = $text =~ m/^(\d\d)(\d\d)(\d\d) +(\d+):(\d+):(\d+)$/; |
1922 | + die("Can't get timestamp from $text\n") unless $y; |
1923 | + $y += 2000; |
1924 | + return ( $y, $m, $d, $h, $i, $s ); |
1925 | +} |
1926 | + |
1927 | +sub parse_fk_section { |
1928 | + my ( $section, $complete, $debug, $full ) = @_; |
1929 | + my $fulltext = $section->{'fulltext'}; |
1930 | + |
1931 | + return 0 unless $fulltext; |
1932 | + |
1933 | + my ( $ts, $type ) = $fulltext =~ m/^$s\s+(\w+)/m; |
1934 | + $section->{'ts'} = [ parse_innodb_timestamp( $ts ) ]; |
1935 | + $section->{'timestring'} = ts_to_string($section->{'ts'}); |
1936 | + $section->{'type'} = $type; |
1937 | + |
1938 | + # Decide which type of FK error happened, and dispatch to the right parser. |
1939 | + if ( $type && $fk_parser_for{$type} ) { |
1940 | + $fk_parser_for{$type}->( $section, $complete, $debug, $fulltext, $full ); |
1941 | + } |
1942 | + |
1943 | + delete $section->{'fulltext'} unless $debug; |
1944 | + |
1945 | + return 1; |
1946 | +} |
1947 | + |
1948 | +sub parse_fk_cant_drop_parent_error { |
1949 | + my ( $section, $complete, $debug, $fulltext, $full ) = @_; |
1950 | + |
1951 | + # Parse the parent/child table info out |
1952 | + @{$section}{ qw(attempted_op parent_db parent_table) } = $fulltext |
1953 | + =~ m{Cannot $w table `(.*)/(.*)`}m; |
1954 | + @{$section}{ qw(child_db child_table) } = $fulltext |
1955 | + =~ m{because it is referenced by `(.*)/(.*)`}m; |
1956 | + |
1957 | + ( $section->{'reason'} ) = $fulltext =~ m/(Cannot .*)/s; |
1958 | + $section->{'reason'} =~ s/\n(?:InnoDB: )?/ /gm |
1959 | + if $section->{'reason'}; |
1960 | + |
1961 | + # Certain data may not be present. Make them '' if not present. |
1962 | + map { $section->{$_} ||= "" } |
1963 | + qw(child_index fk_name col_name parent_col); |
1964 | +} |
1965 | + |
1966 | +# See dict/dict0dict.c, function dict_foreign_error_report |
1967 | +# I don't care much about these. There are lots of different messages, and |
1968 | +# they come from someone trying to create a foreign key, or similar |
1969 | +# statements. They aren't indicative of some transaction trying to insert, |
1970 | +# delete or update data. Sometimes it is possible to parse out a lot of |
1971 | +# information about the tables and indexes involved, but often the message |
1972 | +# contains the DDL string the user entered, which is way too much for this |
1973 | +# module to try to handle. |
1974 | +sub parse_fk_bad_constraint_error { |
1975 | + my ( $section, $complete, $debug, $fulltext, $full ) = @_; |
1976 | + |
1977 | + # Parse the parent/child table and index info out |
1978 | + @{$section}{ qw(child_db child_table) } = $fulltext |
1979 | + =~ m{Error in foreign key constraint of table (.*)/(.*):$}m; |
1980 | + $section->{'attempted_op'} = 'DDL'; |
1981 | + |
1982 | + # FK name, parent info... if possible. |
1983 | + @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) } |
1984 | + = $fulltext |
1985 | + =~ m/CONSTRAINT `?$n`? FOREIGN KEY \(`?$n`?\) REFERENCES (?:`?$n`?\.)?`?$n`? \(`?$n`?\)/; |
1986 | + |
1987 | + if ( !defined($section->{'fk_name'}) ) { |
1988 | + # Try to parse SQL a user might have typed in a CREATE statement or such |
1989 | + @{$section}{ qw(col_name parent_db parent_table parent_col) } |
1990 | + = $fulltext |
1991 | + =~ m/FOREIGN\s+KEY\s*\(`?$n`?\)\s+REFERENCES\s+(?:`?$n`?\.)?`?$n`?\s*\(`?$n`?\)/i; |
1992 | + } |
1993 | + $section->{'parent_db'} ||= $section->{'child_db'}; |
1994 | + |
1995 | + # Name of the child index (index in the same table where the FK is, see |
1996 | + # definition of dict_foreign_struct in include/dict0mem.h, where it is |
1997 | + # called foreign_index, as opposed to referenced_index which is in the |
1998 | + # parent table. This may not be possible to find. |
1999 | + @{$section}{ qw(child_index) } = $fulltext |
2000 | + =~ m/^The index in the foreign key in table is $n$/m; |
2001 | + |
2002 | + @{$section}{ qw(reason) } = $fulltext =~ m/:\s*([^:]+)(?= Constraint:|$)/ms; |
2003 | + $section->{'reason'} =~ s/\s+/ /g |
2004 | + if $section->{'reason'}; |
2005 | + |
2006 | + # Certain data may not be present. Make them '' if not present. |
2007 | + map { $section->{$_} ||= "" } |
2008 | + qw(child_index fk_name col_name parent_table parent_col); |
2009 | +} |
2010 | + |
2011 | +# see source file row/row0ins.c |
2012 | +sub parse_fk_transaction_error { |
2013 | + my ( $section, $complete, $debug, $fulltext, $full ) = @_; |
2014 | + |
2015 | + # Parse the txn info out |
2016 | + my ( $txn ) = $fulltext |
2017 | + =~ m/Transaction:\n(TRANSACTION.*)\nForeign key constraint fails/s; |
2018 | + if ( $txn ) { |
2019 | + $section->{'txn'} = parse_tx_text( $txn, $complete, $debug, $full ); |
2020 | + } |
2021 | + |
2022 | + # Parse the parent/child table and index info out. There are two types: an |
2023 | + # update or a delete of a parent record leaves a child orphaned |
2024 | + # (row_ins_foreign_report_err), and an insert or update of a child record has |
2025 | + # no matching parent record (row_ins_foreign_report_add_err). |
2026 | + |
2027 | + @{$section}{ qw(reason child_db child_table) } |
2028 | + = $fulltext =~ m{^(Foreign key constraint fails for table `(.*)/(.*)`:)$}m; |
2029 | + |
2030 | + @{$section}{ qw(fk_name col_name parent_db parent_table parent_col) } |
2031 | + = $fulltext |
2032 | + =~ m/CONSTRAINT `$n` FOREIGN KEY \(`$n`\) REFERENCES (?:`$n`\.)?`$n` \(`$n`\)/; |
2033 | + $section->{'parent_db'} ||= $section->{'child_db'}; |
2034 | + |
2035 | + # Special case, which I don't know how to trigger, but see |
2036 | + # innobase/row/row0ins.c row_ins_check_foreign_constraint |
2037 | + if ( $fulltext =~ m/ibd file does not currently exist!/ ) { |
2038 | + my ( $attempted_op, $index, $records ) |
2039 | + = $fulltext =~ m/^Trying to (add to index) `$n` tuple:\n(.*))?/sm; |
2040 | + $section->{'child_index'} = $index; |
2041 | + $section->{'attempted_op'} = $attempted_op || ''; |
2042 | + if ( $records && $full ) { |
2043 | + ( $section->{'records'} ) |
2044 | + = parse_innodb_record_dump( $records, $complete, $debug ); |
2045 | + } |
2046 | + @{$section}{qw(parent_db parent_table)} |
2047 | + =~ m/^But the parent table `$n`\.`$n`$/m; |
2048 | + } |
2049 | + else { |
2050 | + my ( $attempted_op, $which, $index ) |
2051 | + = $fulltext =~ m/^Trying to ([\w ]*) in (child|parent) table, in index `$n` tuple:$/m; |
2052 | + if ( $which ) { |
2053 | + $section->{$which . '_index'} = $index; |
2054 | + $section->{'attempted_op'} = $attempted_op || ''; |
2055 | + |
2056 | + # Parse out the related records in the other table. |
2057 | + my ( $search_index, $records ); |
2058 | + if ( $which eq 'child' ) { |
2059 | + ( $search_index, $records ) = $fulltext |
2060 | + =~ m/^But in parent table [^,]*, in index `$n`,\nthe closest match we can find is record:\n(.*)/ms; |
2061 | + $section->{'parent_index'} = $search_index; |
2062 | + } |
2063 | + else { |
2064 | + ( $search_index, $records ) = $fulltext |
2065 | + =~ m/^But in child table [^,]*, in index `$n`, (?:the record is not available|there is a record:\n(.*))?/ms; |
2066 | + $section->{'child_index'} = $search_index; |
2067 | + } |
2068 | + if ( $records && $full ) { |
2069 | + $section->{'records'} |
2070 | + = parse_innodb_record_dump( $records, $complete, $debug ); |
2071 | + } |
2072 | + else { |
2073 | + $section->{'records'} = ''; |
2074 | + } |
2075 | + } |
2076 | + } |
2077 | + |
2078 | + # Parse out the tuple trying to be updated, deleted or inserted. |
2079 | + my ( $trigger ) = $fulltext =~ m/^(DATA TUPLE: \d+ fields;\n.*)$/m; |
2080 | + if ( $trigger ) { |
2081 | + $section->{'trigger'} = parse_innodb_record_dump( $trigger, $complete, $debug ); |
2082 | + } |
2083 | + |
2084 | + # Certain data may not be present. Make them '' if not present. |
2085 | + map { $section->{$_} ||= "" } |
2086 | + qw(child_index fk_name col_name parent_table parent_col); |
2087 | +} |
2088 | + |
2089 | +# There are new-style and old-style record formats. See rem/rem0rec.c |
2090 | +# TODO: write some tests for this |
2091 | +sub parse_innodb_record_dump { |
2092 | + my ( $dump, $complete, $debug ) = @_; |
2093 | + return undef unless $dump; |
2094 | + |
2095 | + my $result = {}; |
2096 | + |
2097 | + if ( $dump =~ m/PHYSICAL RECORD/ ) { |
2098 | + my $style = $dump =~ m/compact format/ ? 'new' : 'old'; |
2099 | + $result->{'style'} = $style; |
2100 | + |
2101 | + # This is a new-style record. |
2102 | + if ( $style eq 'new' ) { |
2103 | + @{$result}{qw( heap_no type num_fields info_bits )} |
2104 | + = $dump |
2105 | + =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; compact format; info bits $d$/m; |
2106 | + } |
2107 | + |
2108 | + # OK, it's old-style. Unfortunately there are variations here too. |
2109 | + elsif ( $dump =~ m/-byte offs / ) { |
2110 | + # Older-old style. |
2111 | + @{$result}{qw( heap_no type num_fields byte_offset info_bits )} |
2112 | + = $dump |
2113 | + =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offs [A-Z]+; info bits $d$/m; |
2114 | + if ( $dump !~ m/-byte offs TRUE/ ) { |
2115 | + $result->{'byte_offset'} = 0; |
2116 | + } |
2117 | + } |
2118 | + else { |
2119 | + # Newer-old style. |
2120 | + @{$result}{qw( heap_no type num_fields byte_offset info_bits )} |
2121 | + = $dump |
2122 | + =~ m/^(?:Record lock, heap no $d )?([A-Z ]+): n_fields $d; $d-byte offsets; info bits $d$/m; |
2123 | + } |
2124 | + |
2125 | + } |
2126 | + else { |
2127 | + $result->{'style'} = 'tuple'; |
2128 | + @{$result}{qw( type num_fields )} |
2129 | + = $dump =~ m/^(DATA TUPLE): $d fields;$/m; |
2130 | + } |
2131 | + |
2132 | + # Fill in default values for things that couldn't be parsed. |
2133 | + map { $result->{$_} ||= 0 } |
2134 | + qw(heap_no num_fields byte_offset info_bits); |
2135 | + map { $result->{$_} ||= '' } |
2136 | + qw(style type ); |
2137 | + |
2138 | + my @fields = $dump =~ m/ (\d+:.*?;?);(?=$| \d+:)/gm; |
2139 | + $result->{'fields'} = [ map { parse_field($_, $complete, $debug ) } @fields ]; |
2140 | + |
2141 | + return $result; |
2142 | +} |
2143 | + |
2144 | +# New/old-style applies here. See rem/rem0rec.c |
2145 | +# $text should not include the leading space or the second trailing semicolon. |
2146 | +sub parse_field { |
2147 | + my ( $text, $complete, $debug ) = @_; |
2148 | + |
2149 | + # Sample fields: |
2150 | + # '4: SQL NULL, size 4 ' |
2151 | + # '1: len 6; hex 000000005601; asc V ;' |
2152 | + # '6: SQL NULL' |
2153 | + # '5: len 30; hex 687474703a2f2f7777772e737765657477617465722e636f6d2f73746f72; asc http://www.sweetwater.com/stor;...(truncated)' |
2154 | + my ( $id, $nullsize, $len, $hex, $asc, $truncated ); |
2155 | + ( $id, $nullsize ) = $text =~ m/^$d: SQL NULL, size $d $/; |
2156 | + if ( !defined($id) ) { |
2157 | + ( $id ) = $text =~ m/^$d: SQL NULL$/; |
2158 | + } |
2159 | + if ( !defined($id) ) { |
2160 | + ( $id, $len, $hex, $asc, $truncated ) |
2161 | + = $text =~ m/^$d: len $d; hex $h; asc (.*);(\.\.\.\(truncated\))?$/; |
2162 | + } |
2163 | + |
2164 | + die "Could not parse this field: '$text'" unless defined $id; |
2165 | + return { |
2166 | + id => $id, |
2167 | + len => defined($len) ? $len : defined($nullsize) ? $nullsize : 0, |
2168 | + 'hex' => defined($hex) ? $hex : '', |
2169 | + asc => defined($asc) ? $asc : '', |
2170 | + trunc => $truncated ? 1 : 0, |
2171 | + }; |
2172 | + |
2173 | +} |
2174 | + |
2175 | +sub parse_dl_section { |
2176 | + my ( $dl, $complete, $debug, $full ) = @_; |
2177 | + return unless $dl; |
2178 | + my $fulltext = $dl->{'fulltext'}; |
2179 | + return 0 unless $fulltext; |
2180 | + |
2181 | + my ( $ts ) = $fulltext =~ m/^$s$/m; |
2182 | + return 0 unless $ts; |
2183 | + |
2184 | + $dl->{'ts'} = [ parse_innodb_timestamp( $ts ) ]; |
2185 | + $dl->{'timestring'} = ts_to_string($dl->{'ts'}); |
2186 | + $dl->{'txns'} = {}; |
2187 | + |
2188 | + my @sections |
2189 | + = $fulltext |
2190 | + =~ m{ |
2191 | + ^\*{3}\s([^\n]*) # *** (1) WAITING FOR THIS... |
2192 | + (.*?) # Followed by anything, non-greedy |
2193 | + (?=(?:^\*{3})|\z) # Followed by another three stars or EOF |
2194 | + }gmsx; |
2195 | + |
2196 | + |
2197 | + # Loop through each section. There are no assumptions about how many |
2198 | + # there are, who holds and wants what locks, and who gets rolled back. |
2199 | + while ( my ($header, $body) = splice(@sections, 0, 2) ) { |
2200 | + my ( $txn_id, $what ) = $header =~ m/^\($d\) (.*):$/; |
2201 | + next unless $txn_id; |
2202 | + $dl->{'txns'}->{$txn_id} ||= {}; |
2203 | + my $txn = $dl->{'txns'}->{$txn_id}; |
2204 | + |
2205 | + if ( $what eq 'TRANSACTION' ) { |
2206 | + $txn->{'tx'} = parse_tx_text( $body, $complete, $debug, $full ); |
2207 | + } |
2208 | + else { |
2209 | + push @{$txn->{'locks'}}, parse_innodb_record_locks( $body, $complete, $debug, $full ); |
2210 | + } |
2211 | + } |
2212 | + |
2213 | + @{ $dl }{ qw(rolled_back) } |
2214 | + = $fulltext =~ m/^\*\*\* WE ROLL BACK TRANSACTION \($d\)$/m; |
2215 | + |
2216 | + # Make sure certain values aren't undef |
2217 | + map { $dl->{$_} ||= '' } qw(rolled_back); |
2218 | + |
2219 | + delete $dl->{'fulltext'} unless $debug; |
2220 | + return 1; |
2221 | +} |
2222 | + |
2223 | +sub parse_innodb_record_locks { |
2224 | + my ( $text, $complete, $debug, $full ) = @_; |
2225 | + my @result; |
2226 | + |
2227 | + foreach my $lock ( $text =~ m/(^(?:RECORD|TABLE) LOCKS?.*$)/gm ) { |
2228 | + my $hash = {}; |
2229 | + @{$hash}{ qw(lock_type space_id page_no n_bits index db table txn_id lock_mode) } |
2230 | + = $lock |
2231 | + =~ 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; |
2232 | + ( $hash->{'special'} ) |
2233 | + = $lock =~ m/^(?:RECORD|TABLE) .*? locks (rec but not gap|gap before rec)/m; |
2234 | + $hash->{'insert_intention'} |
2235 | + = $lock =~ m/^(?:RECORD|TABLE) .*? insert intention/m ? 1 : 0; |
2236 | + $hash->{'waiting'} |
2237 | + = $lock =~ m/^(?:RECORD|TABLE) .*? waiting/m ? 1 : 0; |
2238 | + |
2239 | + # Some things may not be in the text, so make sure they are not |
2240 | + # undef. |
2241 | + map { $hash->{$_} ||= 0 } qw(n_bits page_no space_id); |
2242 | + map { $hash->{$_} ||= "" } qw(index special); |
2243 | + push @result, $hash; |
2244 | + } |
2245 | + |
2246 | + return @result; |
2247 | +} |
2248 | + |
2249 | +sub parse_tx_text { |
2250 | + my ( $txn, $complete, $debug, $full ) = @_; |
2251 | + |
2252 | + my ( $txn_id, $txn_status, $active_secs, $proc_no, $os_thread_id ) |
2253 | + = $txn |
2254 | + =~ m/^(?:---)?TRANSACTION $t, (\D*?)(?: $d sec)?, (?:process no $d, )?OS thread id $d/m; |
2255 | + my ( $thread_status, $thread_decl_inside ) |
2256 | + = $txn |
2257 | + =~ m/OS thread id \d+(?: ([^,]+?))?(?:, thread declared inside InnoDB $d)?$/m; |
2258 | + |
2259 | + # Parsing the line that begins 'MySQL thread id' is complicated. The only |
2260 | + # thing always in the line is the thread and query id. See function |
2261 | + # innobase_mysql_print_thd in InnoDB source file sql/ha_innodb.cc. |
2262 | + my ( $thread_line ) = $txn =~ m/^(MySQL thread id .*)$/m; |
2263 | + my ( $mysql_thread_id, $query_id, $hostname, $ip, $user, $query_status ); |
2264 | + |
2265 | + if ( $thread_line ) { |
2266 | + # These parts can always be gotten. |
2267 | + ( $mysql_thread_id, $query_id ) = $thread_line =~ m/^MySQL thread id $d, query id $d/m; |
2268 | + |
2269 | + # If it's a master/slave thread, "Has (read|sent) all" may be the thread's |
2270 | + # proc_info. In these cases, there won't be any host/ip/user info |
2271 | + ( $query_status ) = $thread_line =~ m/(Has (?:read|sent) all .*$)/m; |
2272 | + if ( defined($query_status) ) { |
2273 | + $user = 'system user'; |
2274 | + } |
2275 | + |
2276 | + # It may be the case that the query id is the last thing in the line. |
2277 | + elsif ( $thread_line =~ m/query id \d+ / ) { |
2278 | + # The IP address is the only non-word thing left, so it's the most |
2279 | + # useful marker for where I have to start guessing. |
2280 | + ( $hostname, $ip ) = $thread_line =~ m/query id \d+(?: ([A-Za-z]\S+))? $i/m; |
2281 | + if ( defined $ip ) { |
2282 | + ( $user, $query_status ) = $thread_line =~ m/$ip $w(?: (.*))?$/; |
2283 | + } |
2284 | + else { # OK, there wasn't an IP address. |
2285 | + # There might not be ANYTHING except the query status. |
2286 | + ( $query_status ) = $thread_line =~ m/query id \d+ (.*)$/; |
2287 | + if ( $query_status !~ m/^\w+ing/ && !exists($is_proc_info{$query_status}) ) { |
2288 | + # The remaining tokens are, in order: hostname, user, query_status. |
2289 | + # It's basically impossible to know which is which. |
2290 | + ( $hostname, $user, $query_status ) = $thread_line |
2291 | + =~ m/query id \d+(?: ([A-Za-z]\S+))?(?: $w(?: (.*))?)?$/m; |
2292 | + } |
2293 | + else { |
2294 | + $user = 'system user'; |
2295 | + } |
2296 | + } |
2297 | + } |
2298 | + } |
2299 | + |
2300 | + my ( $lock_wait_status, $lock_structs, $heap_size, $row_locks, $undo_log_entries ) |
2301 | + = $txn |
2302 | + =~ m/^(?:(\D*) )?$d lock struct\(s\), heap size $d(?:, $d row lock\(s\))?(?:, undo log entries $d)?$/m; |
2303 | + my ( $lock_wait_time ) |
2304 | + = $txn |
2305 | + =~ m/^------- TRX HAS BEEN WAITING $d SEC/m; |
2306 | + |
2307 | + my $locks; |
2308 | + # If the transaction has locks, grab the locks. |
2309 | + if ( $txn =~ m/^TABLE LOCK|RECORD LOCKS/ ) { |
2310 | + $locks = [parse_innodb_record_locks($txn, $complete, $debug, $full)]; |
2311 | + } |
2312 | + |
2313 | + my ( $tables_in_use, $tables_locked ) |
2314 | + = $txn |
2315 | + =~ m/^mysql tables in use $d, locked $d$/m; |
2316 | + my ( $txn_doesnt_see_ge, $txn_sees_lt ) |
2317 | + = $txn |
2318 | + =~ m/^Trx read view will not see trx with id >= $t, sees < $t$/m; |
2319 | + my $has_read_view = defined($txn_doesnt_see_ge); |
2320 | + # Only a certain number of bytes of the query text are included here, at least |
2321 | + # under some circumstances. Some versions include 300, some 600. |
2322 | + my ( $query_text ) |
2323 | + = $txn |
2324 | + =~ m{ |
2325 | + ^MySQL\sthread\sid\s[^\n]+\n # This comes before the query text |
2326 | + (.*?) # The query text |
2327 | + (?= # Followed by any of... |
2328 | + ^Trx\sread\sview |
2329 | + |^-------\sTRX\sHAS\sBEEN\sWAITING |
2330 | + |^TABLE\sLOCK |
2331 | + |^RECORD\sLOCKS\sspace\sid |
2332 | + |^(?:---)?TRANSACTION |
2333 | + |^\*\*\*\s\(\d\) |
2334 | + |\Z |
2335 | + ) |
2336 | + }xms; |
2337 | + if ( $query_text ) { |
2338 | + $query_text =~ s/\s+$//; |
2339 | + } |
2340 | + else { |
2341 | + $query_text = ''; |
2342 | + } |
2343 | + |
2344 | + my %stuff = ( |
2345 | + active_secs => $active_secs, |
2346 | + has_read_view => $has_read_view, |
2347 | + heap_size => $heap_size, |
2348 | + hostname => $hostname, |
2349 | + ip => $ip, |
2350 | + lock_structs => $lock_structs, |
2351 | + lock_wait_status => $lock_wait_status, |
2352 | + lock_wait_time => $lock_wait_time, |
2353 | + mysql_thread_id => $mysql_thread_id, |
2354 | + os_thread_id => $os_thread_id, |
2355 | + proc_no => $proc_no, |
2356 | + query_id => $query_id, |
2357 | + query_status => $query_status, |
2358 | + query_text => $query_text, |
2359 | + row_locks => $row_locks, |
2360 | + tables_in_use => $tables_in_use, |
2361 | + tables_locked => $tables_locked, |
2362 | + thread_decl_inside => $thread_decl_inside, |
2363 | + thread_status => $thread_status, |
2364 | + txn_doesnt_see_ge => $txn_doesnt_see_ge, |
2365 | + txn_id => $txn_id, |
2366 | + txn_sees_lt => $txn_sees_lt, |
2367 | + txn_status => $txn_status, |
2368 | + undo_log_entries => $undo_log_entries, |
2369 | + user => $user, |
2370 | + ); |
2371 | + $stuff{'fulltext'} = $txn if $debug; |
2372 | + $stuff{'locks'} = $locks if $locks; |
2373 | + |
2374 | + # Some things may not be in the txn text, so make sure they are not |
2375 | + # undef. |
2376 | + map { $stuff{$_} ||= 0 } qw(active_secs heap_size lock_structs |
2377 | + tables_in_use undo_log_entries tables_locked has_read_view |
2378 | + thread_decl_inside lock_wait_time proc_no row_locks); |
2379 | + map { $stuff{$_} ||= "" } qw(thread_status txn_doesnt_see_ge |
2380 | + txn_sees_lt query_status ip query_text lock_wait_status user); |
2381 | + $stuff{'hostname'} ||= $stuff{'ip'}; |
2382 | + |
2383 | + return \%stuff; |
2384 | +} |
2385 | + |
2386 | +sub parse_tx_section { |
2387 | + my ( $section, $complete, $debug, $full ) = @_; |
2388 | + return unless $section && $section->{'fulltext'}; |
2389 | + my $fulltext = $section->{'fulltext'}; |
2390 | + $section->{'transactions'} = []; |
2391 | + |
2392 | + # Handle the individual transactions |
2393 | + my @transactions = $fulltext =~ m/(---TRANSACTION \d.*?)(?=\n---TRANSACTION|$)/gs; |
2394 | + foreach my $txn ( @transactions ) { |
2395 | + my $stuff = parse_tx_text( $txn, $complete, $debug, $full ); |
2396 | + delete $stuff->{'fulltext'} unless $debug; |
2397 | + push @{$section->{'transactions'}}, $stuff; |
2398 | + } |
2399 | + |
2400 | + # Handle the general info |
2401 | + @{$section}{ 'trx_id_counter' } |
2402 | + = $fulltext =~ m/^Trx id counter $t$/m; |
2403 | + @{$section}{ 'purge_done_for', 'purge_undo_for' } |
2404 | + = $fulltext =~ m/^Purge done for trx's n:o < $t undo n:o < $t$/m; |
2405 | + @{$section}{ 'history_list_len' } # This isn't present in some 4.x versions |
2406 | + = $fulltext =~ m/^History list length $d$/m; |
2407 | + @{$section}{ 'num_lock_structs' } |
2408 | + = $fulltext =~ m/^Total number of lock structs in row lock hash table $d$/m; |
2409 | + @{$section}{ 'is_truncated' } |
2410 | + = $fulltext =~ m/^\.\.\. truncated\.\.\.$/m ? 1 : 0; |
2411 | + |
2412 | + # Fill in things that might not be present |
2413 | + foreach ( qw(history_list_len) ) { |
2414 | + $section->{$_} ||= 0; |
2415 | + } |
2416 | + |
2417 | + delete $section->{'fulltext'} unless $debug; |
2418 | + return 1; |
2419 | +} |
2420 | + |
2421 | +# I've read the source for this section. |
2422 | +sub parse_ro_section { |
2423 | + my ( $section, $complete, $debug, $full ) = @_; |
2424 | + return unless $section && $section->{'fulltext'}; |
2425 | + my $fulltext = $section->{'fulltext'}; |
2426 | + |
2427 | + # Grab the info |
2428 | + @{$section}{ 'queries_inside', 'queries_in_queue' } |
2429 | + = $fulltext =~ m/^$d queries inside InnoDB, $d queries in queue$/m; |
2430 | + ( $section->{ 'read_views_open' } ) |
2431 | + = $fulltext =~ m/^$d read views open inside InnoDB$/m; |
2432 | + ( $section->{ 'n_reserved_extents' } ) |
2433 | + = $fulltext =~ m/^$d tablespace extents now reserved for B-tree/m; |
2434 | + @{$section}{ 'main_thread_proc_no', 'main_thread_id', 'main_thread_state' } |
2435 | + = $fulltext =~ m/^Main thread (?:process no. $d, )?id $d, state: (.*)$/m; |
2436 | + @{$section}{ 'num_rows_ins', 'num_rows_upd', 'num_rows_del', 'num_rows_read' } |
2437 | + = $fulltext =~ m/^Number of rows inserted $d, updated $d, deleted $d, read $d$/m; |
2438 | + @{$section}{ 'ins_sec', 'upd_sec', 'del_sec', 'read_sec' } |
2439 | + = $fulltext =~ m#^$f inserts/s, $f updates/s, $f deletes/s, $f reads/s$#m; |
2440 | + $section->{'main_thread_proc_no'} ||= 0; |
2441 | + |
2442 | + map { $section->{$_} ||= 0 } qw(read_views_open n_reserved_extents); |
2443 | + delete $section->{'fulltext'} unless $debug; |
2444 | + return 1; |
2445 | +} |
2446 | + |
2447 | +sub parse_lg_section { |
2448 | + my ( $section, $complete, $debug, $full ) = @_; |
2449 | + return unless $section; |
2450 | + my $fulltext = $section->{'fulltext'}; |
2451 | + |
2452 | + # Grab the info |
2453 | + ( $section->{ 'log_seq_no' } ) |
2454 | + = $fulltext =~ m/Log sequence number \s*(\d.*)$/m; |
2455 | + ( $section->{ 'log_flushed_to' } ) |
2456 | + = $fulltext =~ m/Log flushed up to \s*(\d.*)$/m; |
2457 | + ( $section->{ 'last_chkp' } ) |
2458 | + = $fulltext =~ m/Last checkpoint at \s*(\d.*)$/m; |
2459 | + @{$section}{ 'pending_log_writes', 'pending_chkp_writes' } |
2460 | + = $fulltext =~ m/$d pending log writes, $d pending chkp writes/; |
2461 | + @{$section}{ 'log_ios_done', 'log_ios_s' } |
2462 | + = $fulltext =~ m#$d log i/o's done, $f log i/o's/second#; |
2463 | + |
2464 | + delete $section->{'fulltext'} unless $debug; |
2465 | + return 1; |
2466 | +} |
2467 | + |
2468 | +sub parse_ib_section { |
2469 | + my ( $section, $complete, $debug, $full ) = @_; |
2470 | + return unless $section && $section->{'fulltext'}; |
2471 | + my $fulltext = $section->{'fulltext'}; |
2472 | + |
2473 | + # Some servers will output ibuf information for tablespace 0, as though there |
2474 | + # might be many tablespaces with insert buffers. (In practice I believe |
2475 | + # the source code shows there will only ever be one). I have to parse both |
2476 | + # cases here, but I assume there will only be one. |
2477 | + @{$section}{ 'size', 'free_list_len', 'seg_size' } |
2478 | + = $fulltext =~ m/^Ibuf(?: for space 0)?: size $d, free list len $d, seg size $d,$/m; |
2479 | + @{$section}{ 'inserts', 'merged_recs', 'merges' } |
2480 | + = $fulltext =~ m/^$d inserts, $d merged recs, $d merges$/m; |
2481 | + |
2482 | + @{$section}{ 'hash_table_size', 'used_cells', 'bufs_in_node_heap' } |
2483 | + = $fulltext =~ m/^Hash table size $d, used cells $d, node heap has $d buffer\(s\)$/m; |
2484 | + @{$section}{ 'hash_searches_s', 'non_hash_searches_s' } |
2485 | + = $fulltext =~ m{^$f hash searches/s, $f non-hash searches/s$}m; |
2486 | + |
2487 | + delete $section->{'fulltext'} unless $debug; |
2488 | + return 1; |
2489 | +} |
2490 | + |
2491 | +sub parse_wait_array { |
2492 | + my ( $text, $complete, $debug, $full ) = @_; |
2493 | + my %result; |
2494 | + |
2495 | + @result{ qw(thread waited_at_filename waited_at_line waited_secs) } |
2496 | + = $text =~ m/^--Thread $d has waited at $fl for $f seconds/m; |
2497 | + |
2498 | + # Depending on whether it's a SYNC_MUTEX,RW_LOCK_EX,RW_LOCK_SHARED, |
2499 | + # there will be different text output |
2500 | + if ( $text =~ m/^Mutex at/m ) { |
2501 | + $result{'request_type'} = 'M'; |
2502 | + @result{ qw( lock_mem_addr lock_cfile_name lock_cline lock_var) } |
2503 | + = $text =~ m/^Mutex at $h created file $fl, lock var $d$/m; |
2504 | + @result{ qw( waiters_flag )} |
2505 | + = $text =~ m/^waiters flag $d$/m; |
2506 | + } |
2507 | + else { |
2508 | + @result{ qw( request_type lock_mem_addr lock_cfile_name lock_cline) } |
2509 | + = $text =~ m/^(.)-lock on RW-latch at $h created in file $fl$/m; |
2510 | + @result{ qw( writer_thread writer_lock_mode ) } |
2511 | + = $text =~ m/^a writer \(thread id $d\) has reserved it in mode (.*)$/m; |
2512 | + @result{ qw( num_readers waiters_flag )} |
2513 | + = $text =~ m/^number of readers $d, waiters flag $d$/m; |
2514 | + @result{ qw(last_s_file_name last_s_line ) } |
2515 | + = $text =~ m/Last time read locked in file $fl$/m; |
2516 | + @result{ qw(last_x_file_name last_x_line ) } |
2517 | + = $text =~ m/Last time write locked in file $fl$/m; |
2518 | + } |
2519 | + |
2520 | + $result{'cell_waiting'} = $text =~ m/^wait has ended$/m ? 0 : 1; |
2521 | + $result{'cell_event_set'} = $text =~ m/^wait is ending$/m ? 1 : 0; |
2522 | + |
2523 | + # Because there are two code paths, some things won't get set. |
2524 | + map { $result{$_} ||= '' } |
2525 | + qw(last_s_file_name last_x_file_name writer_lock_mode); |
2526 | + map { $result{$_} ||= 0 } |
2527 | + qw(num_readers lock_var last_s_line last_x_line writer_thread); |
2528 | + |
2529 | + return \%result; |
2530 | +} |
2531 | + |
2532 | +sub parse_sm_section { |
2533 | + my ( $section, $complete, $debug, $full ) = @_; |
2534 | + return 0 unless $section && $section->{'fulltext'}; |
2535 | + my $fulltext = $section->{'fulltext'}; |
2536 | + |
2537 | + # Grab the info |
2538 | + @{$section}{ 'reservation_count', 'signal_count' } |
2539 | + = $fulltext =~ m/^OS WAIT ARRAY INFO: reservation count $d, signal count $d$/m; |
2540 | + @{$section}{ 'mutex_spin_waits', 'mutex_spin_rounds', 'mutex_os_waits' } |
2541 | + = $fulltext =~ m/^Mutex spin waits $d, rounds $d, OS waits $d$/m; |
2542 | + @{$section}{ 'rw_shared_spins', 'rw_shared_os_waits', 'rw_excl_spins', 'rw_excl_os_waits' } |
2543 | + = $fulltext =~ m/^RW-shared spins $d, OS waits $d; RW-excl spins $d, OS waits $d$/m; |
2544 | + |
2545 | + # Look for info on waits. |
2546 | + my @waits = $fulltext =~ m/^(--Thread.*?)^(?=Mutex spin|--Thread)/gms; |
2547 | + $section->{'waits'} = [ map { parse_wait_array($_, $complete, $debug) } @waits ]; |
2548 | + $section->{'wait_array_size'} = scalar(@waits); |
2549 | + |
2550 | + delete $section->{'fulltext'} unless $debug; |
2551 | + return 1; |
2552 | +} |
2553 | + |
2554 | +# I've read the source for this section. |
2555 | +sub parse_bp_section { |
2556 | + my ( $section, $complete, $debug, $full ) = @_; |
2557 | + return unless $section && $section->{'fulltext'}; |
2558 | + my $fulltext = $section->{'fulltext'}; |
2559 | + |
2560 | + # Grab the info |
2561 | + @{$section}{ 'total_mem_alloc', 'add_pool_alloc' } |
2562 | + = $fulltext =~ m/^Total memory allocated $d; in additional pool allocated $d$/m; |
2563 | + @{$section}{'dict_mem_alloc'} = $fulltext =~ m/Dictionary memory allocated $d/; |
2564 | + @{$section}{'awe_mem_alloc'} = $fulltext =~ m/$d MB of AWE memory/; |
2565 | + @{$section}{'buf_pool_size'} = $fulltext =~ m/^Buffer pool size\s*$d$/m; |
2566 | + @{$section}{'buf_free'} = $fulltext =~ m/^Free buffers\s*$d$/m; |
2567 | + @{$section}{'pages_total'} = $fulltext =~ m/^Database pages\s*$d$/m; |
2568 | + @{$section}{'pages_modified'} = $fulltext =~ m/^Modified db pages\s*$d$/m; |
2569 | + @{$section}{'pages_read', 'pages_created', 'pages_written'} |
2570 | + = $fulltext =~ m/^Pages read $d, created $d, written $d$/m; |
2571 | + @{$section}{'page_reads_sec', 'page_creates_sec', 'page_writes_sec'} |
2572 | + = $fulltext =~ m{^$f reads/s, $f creates/s, $f writes/s$}m; |
2573 | + @{$section}{'buf_pool_hits', 'buf_pool_reads'} |
2574 | + = $fulltext =~ m{Buffer pool hit rate $d / $d$}m; |
2575 | + if ($fulltext =~ m/^No buffer pool page gets since the last printout$/m) { |
2576 | + @{$section}{'buf_pool_hits', 'buf_pool_reads'} = (0, 0); |
2577 | + @{$section}{'buf_pool_hit_rate'} = '--'; |
2578 | + } |
2579 | + else { |
2580 | + @{$section}{'buf_pool_hit_rate'} |
2581 | + = $fulltext =~ m{Buffer pool hit rate (\d+ / \d+)$}m; |
2582 | + } |
2583 | + @{$section}{'reads_pending'} = $fulltext =~ m/^Pending reads $d/m; |
2584 | + @{$section}{'writes_pending_lru', 'writes_pending_flush_list', 'writes_pending_single_page' } |
2585 | + = $fulltext =~ m/^Pending writes: LRU $d, flush list $d, single page $d$/m; |
2586 | + |
2587 | + map { $section->{$_} ||= 0 } |
2588 | + qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page |
2589 | + awe_mem_alloc dict_mem_alloc); |
2590 | + @{$section}{'writes_pending'} = List::Util::sum( |
2591 | + @{$section}{ qw(writes_pending_lru writes_pending_flush_list writes_pending_single_page) }); |
2592 | + |
2593 | + delete $section->{'fulltext'} unless $debug; |
2594 | + return 1; |
2595 | +} |
2596 | + |
2597 | +# I've read the source for this. |
2598 | +sub parse_io_section { |
2599 | + my ( $section, $complete, $debug, $full ) = @_; |
2600 | + return unless $section && $section->{'fulltext'}; |
2601 | + my $fulltext = $section->{'fulltext'}; |
2602 | + $section->{'threads'} = {}; |
2603 | + |
2604 | + # Grab the I/O thread info |
2605 | + my @threads = $fulltext =~ m<^(I/O thread \d+ .*)$>gm; |
2606 | + foreach my $thread (@threads) { |
2607 | + my ( $tid, $state, $purpose, $event_set ) |
2608 | + = $thread =~ m{I/O thread $d state: (.+?) \((.*)\)(?: ev set)?$}m; |
2609 | + if ( defined $tid ) { |
2610 | + $section->{'threads'}->{$tid} = { |
2611 | + thread => $tid, |
2612 | + state => $state, |
2613 | + purpose => $purpose, |
2614 | + event_set => $event_set ? 1 : 0, |
2615 | + }; |
2616 | + } |
2617 | + } |
2618 | + |
2619 | + # Grab the reads/writes/flushes info |
2620 | + @{$section}{ 'pending_normal_aio_reads', 'pending_aio_writes' } |
2621 | + = $fulltext =~ m/^Pending normal aio reads: $d, aio writes: $d,$/m; |
2622 | + @{$section}{ 'pending_ibuf_aio_reads', 'pending_log_ios', 'pending_sync_ios' } |
2623 | + = $fulltext =~ m{^ ibuf aio reads: $d, log i/o's: $d, sync i/o's: $d$}m; |
2624 | + @{$section}{ 'flush_type', 'pending_log_flushes', 'pending_buffer_pool_flushes' } |
2625 | + = $fulltext =~ m/^Pending flushes \($w\) log: $d; buffer pool: $d$/m; |
2626 | + @{$section}{ 'os_file_reads', 'os_file_writes', 'os_fsyncs' } |
2627 | + = $fulltext =~ m/^$d OS file reads, $d OS file writes, $d OS fsyncs$/m; |
2628 | + @{$section}{ 'reads_s', 'avg_bytes_s', 'writes_s', 'fsyncs_s' } |
2629 | + = $fulltext =~ m{^$f reads/s, $d avg bytes/read, $f writes/s, $f fsyncs/s$}m; |
2630 | + @{$section}{ 'pending_preads', 'pending_pwrites' } |
2631 | + = $fulltext =~ m/$d pending preads, $d pending pwrites$/m; |
2632 | + @{$section}{ 'pending_preads', 'pending_pwrites' } = (0, 0) |
2633 | + unless defined($section->{'pending_preads'}); |
2634 | + |
2635 | + delete $section->{'fulltext'} unless $debug; |
2636 | + return 1; |
2637 | +} |
2638 | + |
2639 | +sub _debug { |
2640 | + my ( $debug, $msg ) = @_; |
2641 | + if ( $debug ) { |
2642 | + die $msg; |
2643 | + } |
2644 | + else { |
2645 | + warn $msg; |
2646 | + } |
2647 | + return 1; |
2648 | +} |
2649 | + |
2650 | +1; |
2651 | + |
2652 | +# end_of_package InnoDBParser |
2653 | + |
2654 | +package main; |
2655 | + |
2656 | use sigtrap qw(handler finish untrapped normal-signals); |
2657 | |
2658 | use Data::Dumper; |
2659 | use DBI; |
2660 | use English qw(-no_match_vars); |
2661 | use File::Basename qw(dirname); |
2662 | +use File::Temp; |
2663 | use Getopt::Long; |
2664 | use List::Util qw(max min maxstr sum); |
2665 | -use InnoDBParser; |
2666 | use POSIX qw(ceil); |
2667 | use Time::HiRes qw(time sleep); |
2668 | use Term::ReadKey qw(ReadMode ReadKey); |
2669 | |
2670 | -# Version, license and warranty information. {{{1 |
2671 | +# License and warranty information. {{{1 |
2672 | # ########################################################################### |
2673 | -our $VERSION = '1.6.0'; |
2674 | -our $SVN_REV = sprintf("%d", q$Revision: 383 $ =~ m/(\d+)/g); |
2675 | -our $SVN_URL = sprintf("%s", q$URL: https://innotop.svn.sourceforge.net/svnroot/innotop/trunk/innotop $ =~ m$svnroot/innotop/(\S+)$g); |
2676 | |
2677 | my $innotop_license = <<"LICENSE"; |
2678 | |
2679 | @@ -55,6 +1375,7 @@ |
2680 | ); |
2681 | |
2682 | my $clear_screen_sub; |
2683 | +my $dsn_parser = new DSNParser(); |
2684 | |
2685 | # This defines expected properties and defaults for the column definitions that |
2686 | # eventually end up in tbl_meta. |
2687 | @@ -89,7 +1410,13 @@ |
2688 | { s => 'delay|d=f', d => 'Delay between updates in seconds', c => 'interval' }, |
2689 | { s => 'mode|m=s', d => 'Operating mode to start in', c => 'mode' }, |
2690 | { s => 'inc|i!', d => 'Measure incremental differences', c => 'status_inc' }, |
2691 | + { s => 'write|w', d => 'Write running configuration into home directory if no config files were loaded' }, |
2692 | + { s => 'skipcentral|s', d => 'Skip reading the central configuration file' }, |
2693 | { s => 'version', d => 'Output version information and exit' }, |
2694 | + { s => 'user|u=s', d => 'User for login if not current user' }, |
2695 | + { s => 'password|p=s', d => 'Password to use for connection' }, |
2696 | + { s => 'host|h=s', d => 'Connect to host' }, |
2697 | + { s => 'port|P=i', d => 'Port number to use for connection' }, |
2698 | ); |
2699 | |
2700 | # This is the container for the command-line options' values to be stored in |
2701 | @@ -113,10 +1440,14 @@ |
2702 | GetOptions( map { $_->{s} => \$opts{$_->{k}} } @opt_spec) or $opts{help} = 1; |
2703 | |
2704 | if ( $opts{version} ) { |
2705 | - print "innotop Ver $VERSION Changeset $SVN_REV from $SVN_URL\n"; |
2706 | + print "innotop Ver $VERSION\n"; |
2707 | exit(0); |
2708 | } |
2709 | |
2710 | +if ( $opts{c} and ! -f $opts{c} ) { |
2711 | + print $opts{c} . " doesn't exist. Exiting.\n"; |
2712 | + exit(1); |
2713 | +} |
2714 | if ( $opts{'help'} ) { |
2715 | print "Usage: innotop <options> <innodb-status-file>\n\n"; |
2716 | my $maxw = max(map { length($_->{l}) + ($_->{n} ? 4 : 0)} @opt_spec); |
2717 | @@ -695,7 +2026,10 @@ |
2718 | my @args = grep { defined $_ } @_; |
2719 | return (sum(map { m/([\d\.-]+)/g } @args) || 0) / (scalar(@args) || 1); |
2720 | }, |
2721 | - sum => \&sum, |
2722 | + sum => sub { |
2723 | + my @args = grep { defined $_ } @_; |
2724 | + return sum(@args); |
2725 | + } |
2726 | ); |
2727 | |
2728 | # ########################################################################### |
2729 | @@ -1188,7 +2522,7 @@ |
2730 | info => { src => 'info', minw => 0, maxw => 0, trans => [ qw(no_ctrl_char) ] }, |
2731 | cnt => { src => 'id', minw => 0, maxw => 0 }, |
2732 | }, |
2733 | - visible => [ qw(cxn cmd cnt mysql_thread_id user hostname db time info)], |
2734 | + visible => [ qw(cxn cmd cnt mysql_thread_id state user hostname db time info)], |
2735 | filters => [ qw(hide_self hide_inactive hide_slave_io) ], |
2736 | sort_cols => '-time cxn hostname mysql_thread_id', |
2737 | sort_dir => '1', |
2738 | @@ -2440,9 +3774,6 @@ |
2739 | my $clock = 0; # Incremented with every wake-sleep cycle |
2740 | my $clearing_deadlocks = 0; |
2741 | |
2742 | -# Find the home directory; it's different on different OSes. |
2743 | -my $homepath = $ENV{HOME} || $ENV{HOMEPATH} || $ENV{USERPROFILE} || '.'; |
2744 | - |
2745 | # If terminal coloring is available, use it. The only function I want from |
2746 | # the module is the colored() function. |
2747 | eval { |
2748 | @@ -2541,7 +3872,7 @@ |
2749 | pat => $bool_regex, |
2750 | }, |
2751 | readonly => { |
2752 | - val => 0, |
2753 | + val => 1, |
2754 | note => 'Whether the config file is read-only', |
2755 | conf => [ qw() ], |
2756 | pat => $bool_regex, |
2757 | @@ -2604,7 +3935,7 @@ |
2758 | conf => 'ALL', |
2759 | }, |
2760 | mode => { |
2761 | - val => "T", |
2762 | + val => "Q", |
2763 | note => "Which mode to start in", |
2764 | cmdline => 1, |
2765 | }, |
2766 | @@ -2822,6 +4153,21 @@ |
2767 | # Clear the screen and load the configuration. |
2768 | $clear_screen_sub->(); |
2769 | load_config(); |
2770 | + |
2771 | +# Override config variables with command-line options |
2772 | +my %cmdline = |
2773 | + map { $_->{c} => $opts{$_->{k}} } |
2774 | + grep { exists $_->{c} && exists $opts{$_->{k}} } |
2775 | + @opt_spec; |
2776 | + |
2777 | +foreach my $name (keys %cmdline) { |
2778 | + next if not defined $cmdline{$name}; |
2779 | + my $val = $cmdline{$name}; |
2780 | + if ( exists($config{$name}) and (!$config{$name}->{pat} or $val =~ m/$config{$name}->{pat}/ )) { |
2781 | + $config{$name}->{val} = $val; |
2782 | + } |
2783 | +} |
2784 | + |
2785 | post_process_tbl_meta(); |
2786 | |
2787 | # Make sure no changes are written to config file in non-interactive mode. |
2788 | @@ -2848,7 +4194,7 @@ |
2789 | |
2790 | while (++$clock) { |
2791 | |
2792 | - my $mode = $config{mode}->{val} || 'T'; |
2793 | + my $mode = $config{mode}->{val} || 'Q'; |
2794 | if ( !$modes{$mode} ) { |
2795 | die "Mode '$mode' doesn't exist; try one of these:\n" |
2796 | . join("\n", map { " $_ $modes{$_}->{hdr}" } sort keys %modes) |
2797 | @@ -4435,6 +5781,16 @@ |
2798 | my ( $rows, $tbl ) = @_; |
2799 | my $meta = $tbl_meta{$tbl} or die "No such table $tbl in tbl_meta"; |
2800 | |
2801 | + # don't show cxn if there's only one connection being displayed |
2802 | + my @visible; |
2803 | + if (scalar @{$modes{$config{mode}->{val}}->{connections}} == 1) { |
2804 | + map { push @visible, $_ if $_ !~ /^cxn$/ } @{$meta->{visible}}; |
2805 | + delete $$rows[0]{cxn} if defined $$rows[0]{cxn}; |
2806 | + } |
2807 | + else { |
2808 | + @visible = @{$meta->{visible}}; |
2809 | + } |
2810 | + |
2811 | if ( !$meta->{pivot} ) { |
2812 | |
2813 | # Hook in event listeners |
2814 | @@ -4587,7 +5943,7 @@ |
2815 | # If the table isn't pivoted, just show all columns that are supposed to |
2816 | # be shown; but eliminate aggonly columns if the table isn't aggregated. |
2817 | my $aggregated = $meta->{aggregate}; |
2818 | - $fmt_cols = [ grep { $aggregated || !$meta->{cols}->{$_}->{aggonly} } @{$meta->{visible}} ]; |
2819 | + $fmt_cols = [ grep { $aggregated || !$meta->{cols}->{$_}->{aggonly} } @visible ]; |
2820 | $fmt_meta = { map { $_ => $meta->{cols}->{$_} } @$fmt_cols }; |
2821 | |
2822 | # If the table is aggregated, re-order the group_by columns to the left of |
2823 | @@ -4988,7 +6344,7 @@ |
2824 | $uptime, |
2825 | $ibinfo, |
2826 | shorten($qps) . " QPS", |
2827 | - ($vars->{Threads} || 0) . " thd", |
2828 | + ($vars->{Threads} || 0) . "/" . ($vars->{Threads_running} || 0) . "/" . ($vars->{Threads_cached} || 0) . " con/run/cac thds", |
2829 | $cxn))); |
2830 | } |
2831 | else { |
2832 | @@ -5003,7 +6359,7 @@ |
2833 | |
2834 | # Database connections {{{3 |
2835 | sub add_new_dsn { |
2836 | - my ( $name ) = @_; |
2837 | + my ( $name, $dsn, $dl_table, $have_user, $user, $have_pass, $pass, $savepass ) = @_; |
2838 | |
2839 | if ( defined $name ) { |
2840 | $name =~ s/[\s:;]//g; |
2841 | @@ -5018,23 +6374,30 @@ |
2842 | } until ( $name ); |
2843 | } |
2844 | |
2845 | - my $dsn; |
2846 | - do { |
2847 | + if ( !$dsn ) { |
2848 | + do { |
2849 | + $clear_screen_sub->(); |
2850 | + print "Typical DSN strings look like\n DBI:mysql:;host=hostname;port=port\n" |
2851 | + . "The db and port are optional and can usually be omitted.\n" |
2852 | + . "If you specify 'mysql_read_default_group=mysql' many options can be read\n" |
2853 | + . "from your mysql options files (~/.my.cnf, /etc/my.cnf).\n\n"; |
2854 | + $dsn = prompt("Enter a DSN string", undef, "DBI:mysql:;mysql_read_default_group=mysql;host=$name"); |
2855 | + } until ( $dsn ); |
2856 | + } |
2857 | + if ( !$dl_table ) { |
2858 | $clear_screen_sub->(); |
2859 | - print "Typical DSN strings look like\n DBI:mysql:;host=hostname;port=port\n" |
2860 | - . "The db and port are optional and can usually be omitted.\n" |
2861 | - . "If you specify 'mysql_read_default_group=mysql' many options can be read\n" |
2862 | - . "from your mysql options files (~/.my.cnf, /etc/my.cnf).\n\n"; |
2863 | - $dsn = prompt("Enter a DSN string", undef, "DBI:mysql:;mysql_read_default_group=mysql;host=$name"); |
2864 | - } until ( $dsn ); |
2865 | - |
2866 | - $clear_screen_sub->(); |
2867 | - my $dl_table = prompt("Optional: enter a table (must not exist) to use when resetting InnoDB deadlock information", |
2868 | - undef, 'test.innotop_dl'); |
2869 | + my $dl_table = prompt("Optional: enter a table (must not exist) to use when resetting InnoDB deadlock information", |
2870 | + undef, 'test.innotop_dl'); |
2871 | + } |
2872 | |
2873 | $connections{$name} = { |
2874 | - dsn => $dsn, |
2875 | - dl_table => $dl_table, |
2876 | + dsn => $dsn, |
2877 | + dl_table => $dl_table, |
2878 | + have_user => $have_user, |
2879 | + user => $user, |
2880 | + have_pass => $have_pass, |
2881 | + pass => $pass, |
2882 | + savepass => $savepass |
2883 | }; |
2884 | } |
2885 | |
2886 | @@ -5375,12 +6738,14 @@ |
2887 | my $dsn = $connections{$connection} |
2888 | or die "No connection named '$connection' is defined in your configuration"; |
2889 | |
2890 | - if ( !defined $dsn->{have_user} ) { |
2891 | + # don't ask for a username if mysql_read_default_group=client is in the DSN |
2892 | + if ( !defined $dsn->{have_user} and $dsn->{dsn} !~ /mysql_read_default_group=client/ ) { |
2893 | my $answer = prompt("Do you want to specify a username for $connection?", undef, 'n'); |
2894 | $dsn->{have_user} = $answer && $answer =~ m/1|y/i; |
2895 | } |
2896 | |
2897 | - if ( !defined $dsn->{have_pass} ) { |
2898 | + # don't ask for a password if mysql_read_default_group=client is in the DSN |
2899 | + if ( !defined $dsn->{have_pass} and $dsn->{dsn} !~ /mysql_read_default_group=client/ ) { |
2900 | my $answer = prompt("Do you want to specify a password for $connection?", undef, 'n'); |
2901 | $dsn->{have_pass} = $answer && $answer =~ m/1|y/i; |
2902 | } |
2903 | @@ -5523,39 +6888,91 @@ |
2904 | print $msg; |
2905 | } |
2906 | |
2907 | +# migrate_config {{{3 |
2908 | +sub migrate_config { |
2909 | + |
2910 | + my ($old_filename, $new_filename) = @_; |
2911 | + |
2912 | + # don't proceed if old file doesn't exist |
2913 | + if ( ! -f $old_filename ) { |
2914 | + die "Error migrating '$old_filename': file doesn't exist.\n"; |
2915 | + } |
2916 | + # don't migrate files if new file exists |
2917 | + elsif ( -f $new_filename ) { |
2918 | + die "Error migrating '$old_filename' to '$new_filename': new file already exists.\n"; |
2919 | + } |
2920 | + # if migrating from one file to another in the same directory, just rename them |
2921 | + if (dirname($old_filename) eq dirname($new_filename)) { |
2922 | + rename($old_filename, $new_filename) |
2923 | + or die "Can't rename '$old_filename' to '$new_filename': $OS_ERROR"; |
2924 | + } |
2925 | + # otherwise, move the existing conf file to a temp file, make the necessary directory structure, |
2926 | + # and move the temp conf file to its new home |
2927 | + else { |
2928 | + my $tmp = File::Temp->new( TEMPLATE => 'innotopXXXXX', DIR => $homepath, SUFFIX => '.conf'); |
2929 | + my $tmp_filename = $tmp->filename; |
2930 | + my $dirname = dirname($new_filename); |
2931 | + rename($old_filename, $tmp_filename) |
2932 | + or die "Can't rename '$old_filename' to '$tmp_filename': $OS_ERROR"; |
2933 | + mkdir($dirname) or die "Can't create directory '$dirname': $OS_ERROR"; |
2934 | + mkdir("$dirname/plugins") or die "Can't create directory '$dirname/plugins': $OS_ERROR"; |
2935 | + rename($tmp_filename, $new_filename) |
2936 | + or die "Can't rename '$tmp_filename' to '$new_filename': $OS_ERROR"; |
2937 | + } |
2938 | +} |
2939 | + |
2940 | # load_config {{{3 |
2941 | sub load_config { |
2942 | + |
2943 | + my ($old_filename, $answer); |
2944 | |
2945 | - my $filename = $opts{c} || "$homepath/.innotop/innotop.ini"; |
2946 | - my $dirname = dirname($filename); |
2947 | - if ( -f $dirname && !$opts{c} ) { |
2948 | - # innotop got upgraded and this is the old config file. |
2949 | - my $answer = pause("Innotop's default config location has moved to $filename. Move old config file $dirname there now? y/n"); |
2950 | + if ( $opts{u} or $opts{p} or $opts{h} or $opts{P} ) { |
2951 | + my @params = $dsn_parser->get_cxn_params(\%opts); # dsn=$params[0] |
2952 | + add_new_dsn($opts{h} || 'localhost', $params[0], 'test.innotop_dl', |
2953 | + $opts{u} ? 1 : 0, $opts{u}, $opts{p} ? 1 : 0, $opts{p}); |
2954 | + } |
2955 | + if ($opts{c}) { |
2956 | + $conf_file = $opts{c}; |
2957 | + } |
2958 | + # innotop got upgraded and this is an old config file. |
2959 | + elsif ( -f "$homepath/.innotop" or -f "$homepath/.innotop/innotop.ini" ) { |
2960 | + $conf_file = $default_home_conf; |
2961 | + if ( -f "$homepath/.innotop") { |
2962 | + $old_filename = "$homepath/.innotop"; |
2963 | + } |
2964 | + elsif ( -f "$homepath/.innotop/innotop.ini" ) { |
2965 | + $old_filename = "$homepath/.innotop/innotop.ini"; |
2966 | + } |
2967 | + $answer = pause("Innotop's default config location has moved to '$conf_file'. Move old config file '$old_filename' there now? y/n"); |
2968 | if ( lc $answer eq 'y' ) { |
2969 | - rename($dirname, "$homepath/innotop.ini") |
2970 | - or die "Can't rename '$dirname': $OS_ERROR"; |
2971 | - mkdir($dirname) or die "Can't create directory '$dirname': $OS_ERROR"; |
2972 | - mkdir("$dirname/plugins") or die "Can't create directory '$dirname/plugins': $OS_ERROR"; |
2973 | - rename("$homepath/innotop.ini", $filename) |
2974 | - or die "Can't rename '$homepath/innotop.ini' to '$filename': $OS_ERROR"; |
2975 | + migrate_config($old_filename, $conf_file); |
2976 | } |
2977 | else { |
2978 | print "\nInnotop will now exit so you can fix the config file.\n"; |
2979 | exit(0); |
2980 | } |
2981 | } |
2982 | - |
2983 | - if ( ! -d $dirname ) { |
2984 | - mkdir $dirname |
2985 | - or die "Can't create directory '$dirname': $OS_ERROR"; |
2986 | - } |
2987 | - if ( ! -d "$dirname/plugins" ) { |
2988 | - mkdir "$dirname/plugins" |
2989 | - or die "Can't create directory '$dirname/plugins': $OS_ERROR"; |
2990 | - } |
2991 | - |
2992 | - if ( -f $filename ) { |
2993 | - open my $file, "<", $filename or die("Can't open '$filename': $OS_ERROR"); |
2994 | + elsif ( -f $default_home_conf ) { |
2995 | + $conf_file = $default_home_conf; |
2996 | + } |
2997 | + elsif ( -f $default_central_conf and not $opts{s} ) { |
2998 | + $conf_file = $default_central_conf; |
2999 | + } |
3000 | + else { |
3001 | + # If no config file was loaded, set readonly to 0 if the user wants to |
3002 | + # write a config |
3003 | + $config{readonly}->{val} = 0 if $opts{w}; |
3004 | + # If no connections have been defined, connect to a MySQL database |
3005 | + # on localhost using mysql_read_default_group=client |
3006 | + if (!%connections) { |
3007 | + add_new_dsn('localhost', |
3008 | + 'DBI:mysql:;host=localhost;mysql_read_default_group=client', |
3009 | + 'test.innotop_dl'); |
3010 | + } |
3011 | + } |
3012 | + |
3013 | + if ( -f "$conf_file" ) { |
3014 | + open my $file, "<", $conf_file or die("Can't open '$conf_file': $OS_ERROR"); |
3015 | |
3016 | # Check config file version. Just ignore if either innotop or the file has |
3017 | # garbage in the version number. |
3018 | @@ -5577,7 +6994,7 @@ |
3019 | # If the config file is between the endpoints and innotop is greater than |
3020 | # the endpoint, innotop has a newer config file format than the file. |
3021 | if ( $cfg_ver ge $start && $cfg_ver lt $end && $innotop_ver ge $end ) { |
3022 | - my $msg = "innotop's config file format has changed. Overwrite $filename? y or n"; |
3023 | + my $msg = "innotop's config file format has changed. Overwrite $conf_file? y or n"; |
3024 | if ( pause($msg) eq 'n' ) { |
3025 | $config{readonly}->{val} = 1; |
3026 | print "\ninnotop will not save any configuration changes you make."; |
3027 | @@ -5602,7 +7019,7 @@ |
3028 | warn "Unknown config file section '$1'"; |
3029 | } |
3030 | } |
3031 | - close $file or die("Can't close $filename: $OS_ERROR"); |
3032 | + close $file or die("Can't close $conf_file: $OS_ERROR"); |
3033 | } |
3034 | |
3035 | } |
3036 | @@ -5999,12 +7416,6 @@ |
3037 | sub load_config_config { |
3038 | my ( $file ) = @_; |
3039 | |
3040 | - # Look in the command-line parameters for things stored in the same slot. |
3041 | - my %cmdline = |
3042 | - map { $_->{c} => $opts{$_->{k}} } |
3043 | - grep { exists $_->{c} && exists $opts{$_->{k}} } |
3044 | - @opt_spec; |
3045 | - |
3046 | while ( my $line = <$file> ) { |
3047 | chomp $line; |
3048 | next if $line =~ m/^#/; |
3049 | @@ -6013,9 +7424,6 @@ |
3050 | my ( $name, $val ) = $line =~ m/^(.+?)=(.*)$/; |
3051 | next unless defined $name && defined $val; |
3052 | |
3053 | - # Values might already have been set at the command line. |
3054 | - $val = defined($cmdline{$name}) ? $cmdline{$name} : $val; |
3055 | - |
3056 | # Validate the incoming values... |
3057 | if ( $name && exists( $config{$name} ) ) { |
3058 | if ( !$config{$name}->{pat} || $val =~ m/$config{$name}->{pat}/ ) { |
3059 | @@ -6080,12 +7488,41 @@ |
3060 | |
3061 | # save_config {{{3 |
3062 | sub save_config { |
3063 | + print "\n"; |
3064 | return if $config{readonly}->{val}; |
3065 | + # return if no config file was loaded and -w wasn't specified |
3066 | + if (not $conf_file) { |
3067 | + if (not $opts{w}) { |
3068 | + return; |
3069 | + } |
3070 | + else { |
3071 | + # if no config was loaded but -w was specified, |
3072 | + # write to $default_home_conf |
3073 | + $conf_file = $default_home_conf; |
3074 | + } |
3075 | + } |
3076 | + elsif ($conf_file and $opts{w}) { |
3077 | + print "Loaded config file on start-up, so ignoring -w (see --help)\n" |
3078 | + } |
3079 | + |
3080 | + my $dirname = dirname($conf_file); |
3081 | + |
3082 | + # if directories don't exist, create them. This could cause errors |
3083 | + # or warnings if a central config doesn't have readonly=1, but being |
3084 | + # flexible requires giving the user enough rope to hang themselves with. |
3085 | + if ( ! -d $dirname ) { |
3086 | + mkdir $dirname |
3087 | + or die "Can't create directory '$dirname': $OS_ERROR"; |
3088 | + } |
3089 | + if ( ! -d "$dirname/plugins" ) { |
3090 | + mkdir "$dirname/plugins" |
3091 | + or warn "Can't create directory '$dirname/plugins': $OS_ERROR\n"; |
3092 | + } |
3093 | + |
3094 | # Save to a temp file first, so a crash doesn't destroy the main config file |
3095 | - my $newname = $opts{c} || "$homepath/.innotop/innotop.ini"; |
3096 | - my $filename = $newname . '_tmp'; |
3097 | - open my $file, "+>", $filename |
3098 | - or die("Can't write to $filename: $OS_ERROR"); |
3099 | + my $tmpfile = File::Temp->new( TEMPLATE => 'innotopXXXXX', DIR => $dirname, SUFFIX => '.conf.tmp'); |
3100 | + open my $file, "+>", $tmpfile |
3101 | + or die("Can't write to $tmpfile: $OS_ERROR"); |
3102 | print $file "version=$VERSION\n"; |
3103 | |
3104 | foreach my $section ( @ordered_config_file_sections ) { |
3105 | @@ -6096,12 +7533,13 @@ |
3106 | } |
3107 | |
3108 | # Now clobber the main config file with the temp. |
3109 | - close $file or die("Can't close $filename: $OS_ERROR"); |
3110 | - rename($filename, $newname) or die("Can't rename $filename to $newname: $OS_ERROR"); |
3111 | + close $file or die("Can't close $tmpfile: $OS_ERROR"); |
3112 | + rename($tmpfile, $conf_file) or die("Can't rename $tmpfile to $conf_file: $OS_ERROR"); |
3113 | } |
3114 | |
3115 | # load_config_connections {{{3 |
3116 | sub load_config_connections { |
3117 | + return if $opts{u} or $opts{p} or $opts{h} or $opts{P}; # don't load connections if DSN or user/pass options used |
3118 | my ( $file ) = @_; |
3119 | while ( my $line = <$file> ) { |
3120 | chomp $line; |
3121 | @@ -7250,6 +8688,10 @@ |
3122 | |
3123 | innotop --count 5 -d 1 -n |
3124 | |
3125 | +To monitor a database on another system using a particular username and password: |
3126 | + |
3127 | + innotop -u <username> -p <password> -h <hostname> |
3128 | + |
3129 | =head1 DESCRIPTION |
3130 | |
3131 | innotop monitors MySQL servers. Each of its modes shows you a different aspect |
3132 | @@ -7269,42 +8711,30 @@ |
3133 | Enter; otherwise, you will need to change to innotop's directory and type "perl |
3134 | innotop". |
3135 | |
3136 | -The first thing innotop needs to know is how to connect to a MySQL server. You |
3137 | -can just enter the hostname of the server, for example "localhost" or |
3138 | -"127.0.0.1" if the server is on the same machine as innotop. After this innotop |
3139 | -will prompt you for a DSN (data source name). You should be able to just accept |
3140 | -the defaults by pressing Enter. |
3141 | - |
3142 | -When innotop asks you about a table to use when resetting InnoDB deadlock |
3143 | -information, just accept the default for now. This is an advanced feature you |
3144 | -can configure later (see L<"D: InnoDB Deadlocks"> for more). |
3145 | - |
3146 | -If you have a .my.cnf file with your MySQL connection defaults, innotop can read |
3147 | -it, and you won't need to specify a username and password if it's in that file. |
3148 | -Otherwise, you should answer 'y' to the next couple of prompts. |
3149 | - |
3150 | -After this, you should be connected, and innotop should show you something like |
3151 | -the following: |
3152 | - |
3153 | - InnoDB Txns (? for help) localhost, 01:11:19, InnoDB 10s :-), 50 QPS, |
3154 | - |
3155 | - CXN History Versions Undo Dirty Buf Used Bufs Txns MaxTxn |
3156 | - localhost 7 2035 0 0 0.00% 92.19% 1 07:34 |
3157 | - |
3158 | - CXN ID User Host Txn Status Time Undo Query Tex |
3159 | - localhost 98379 user1 webserver ACTIVE 07:34 0 SELECT `c |
3160 | - localhost 98450 user1 webserver ACTIVE 01:06 0 INSERT IN |
3161 | - localhost 97750 user1 webserver not starte 00:00 0 |
3162 | - localhost 98375 user1 appserver not starte 00:00 0 |
3163 | +With no options specified, innotop will attempt to connect to a MySQL server on |
3164 | +localhost using mysql_read_default_group=client for other connection |
3165 | +parameters. If you need to specify a different username and password, use the |
3166 | +-u and -p options, respectively. To monitor a MySQL database on another |
3167 | +host, use the -h option. |
3168 | + |
3169 | +After you've connected, innotop should show you something like the following: |
3170 | + |
3171 | + [RO] Query List (? for help) localhost, 01:11:19, 449.44 QPS, 14/7/163 con/run |
3172 | + |
3173 | + CXN When Load QPS Slow QCacheHit KCacheHit BpsIn BpsOut |
3174 | + localhost Total 0.00 1.07k 697 0.00% 98.17% 476.83k 242.83k |
3175 | + |
3176 | + CXN Cmd ID User Host DB Time Query |
3177 | + localhost Query 766446598 test 10.0.0.1 foo 00:02 INSERT INTO table ( |
3178 | + |
3179 | |
3180 | (This sample is truncated at the right so it will fit on a terminal when running |
3181 | 'man innotop') |
3182 | |
3183 | -This sample comes from a quiet server with few transactions active. If your |
3184 | -server is busy, you'll see more output. Notice the first line on the screen, |
3185 | -which tells you what mode you're in and what server you're connected to. You |
3186 | -can change to other modes with keystrokes; press 'Q' to switch to a list of |
3187 | -currently running queries. |
3188 | +If your server is busy, you'll see more output. Notice the first line on the |
3189 | +screen, which tells you that readonly is set to true ([RO]), what mode you're |
3190 | +in and what server you're connected to. You can change to other modes with |
3191 | +keystrokes; press 'T' to switch to a list of InnoDB transactions, for example. |
3192 | |
3193 | Press the '?' key to see what keys are active in the current mode. You can |
3194 | press any of these keys and innotop will either take the requested action or |
3195 | @@ -7325,10 +8755,6 @@ |
3196 | |
3197 | =over |
3198 | |
3199 | -=item --help |
3200 | - |
3201 | -Print a summary of command-line usage and exit. |
3202 | - |
3203 | =item --color |
3204 | |
3205 | Enable or disable terminal coloring. Corresponds to the L<"color"> config file |
3206 | @@ -7339,10 +8765,6 @@ |
3207 | Specifies a configuration file to read. This option is non-sticky, that is to |
3208 | say it does not persist to the configuration file itself. |
3209 | |
3210 | -=item --nonint |
3211 | - |
3212 | -Enable non-interactive operation. See L<"NON-INTERACTIVE OPERATION"> for more. |
3213 | - |
3214 | =item --count |
3215 | |
3216 | Refresh only the specified number of times (ticks) before exiting. Each refresh |
3217 | @@ -7354,10 +8776,13 @@ |
3218 | Specifies the amount of time to pause between ticks (refreshes). Corresponds to |
3219 | the configuration option L<"interval">. |
3220 | |
3221 | -=item --mode |
3222 | - |
3223 | -Specifies the mode in which innotop should start. Corresponds to the |
3224 | -configuration option L<"mode">. |
3225 | +=item --help |
3226 | + |
3227 | +Print a summary of command-line usage and exit. |
3228 | + |
3229 | +=item --host |
3230 | + |
3231 | +Host to connect to. |
3232 | |
3233 | =item --inc |
3234 | |
3235 | @@ -7365,10 +8790,41 @@ |
3236 | (offsets from their previous values). Corresponds to the configuration option |
3237 | L<"status_inc">. |
3238 | |
3239 | +=item --mode |
3240 | + |
3241 | +Specifies the mode in which innotop should start. Corresponds to the |
3242 | +configuration option L<"mode">. |
3243 | + |
3244 | +=item --nonint |
3245 | + |
3246 | +Enable non-interactive operation. See L<"NON-INTERACTIVE OPERATION"> for more. |
3247 | + |
3248 | +=item --password |
3249 | + |
3250 | +Password to use for connection. |
3251 | + |
3252 | +=item --port |
3253 | + |
3254 | +Port to use for connection. |
3255 | + |
3256 | +=item --skipcentral |
3257 | + |
3258 | +Don't read the central configuration file. |
3259 | + |
3260 | +=item --user |
3261 | + |
3262 | +User to use for connection. |
3263 | + |
3264 | =item --version |
3265 | |
3266 | Output version information and exit. |
3267 | |
3268 | +=item --write |
3269 | + |
3270 | +Sets the configuration option L<"readonly"> to 0, making innotop write the |
3271 | +running configuration to ~/.innotop/innotop.conf on exit, if no configuration |
3272 | +file was loaded at start-up. |
3273 | + |
3274 | =back |
3275 | |
3276 | =head1 HOTKEYS |
3277 | @@ -7623,8 +9079,9 @@ |
3278 | |
3279 | The first line innotop displays is a "status bar" of sorts. What it contains |
3280 | depends on the mode you're in, and what servers you're monitoring. The first |
3281 | -few words are always the innotop mode, such as "InnoDB Txns" for T mode, |
3282 | -followed by a reminder to press '?' for help at any time. |
3283 | +few words are always [RO] (if readonly is set to 1), the innotop mode, such as |
3284 | +"InnoDB Txns" for T mode, followed by a reminder to press '?' for help at any |
3285 | +time. |
3286 | |
3287 | =head2 ONE SERVER |
3288 | |
3289 | @@ -7701,8 +9158,8 @@ |
3290 | |
3291 | =head1 SERVER CONNECTIONS |
3292 | |
3293 | -When you create a server connection, innotop asks you for a series of inputs, as |
3294 | -follows: |
3295 | +When you create a server connection using '@', innotop asks you for a series of |
3296 | +inputs, as follows: |
3297 | |
3298 | =over |
3299 | |
3300 | @@ -7755,9 +9212,6 @@ |
3301 | server connections and switch between them by pressing the '@' key. See |
3302 | L<"SWITCHING BETWEEN CONNECTIONS">. |
3303 | |
3304 | -To create a new connection, press the '@' key and type the name of the new |
3305 | -connection, then follow the steps given above. |
3306 | - |
3307 | =head1 SERVER GROUPS |
3308 | |
3309 | If you have multiple MySQL instances, you can put them into named groups, such |
3310 | @@ -7958,20 +9412,23 @@ |
3311 | |
3312 | =head1 CONFIGURATION FILE |
3313 | |
3314 | -innotop's default configuration file location is in $HOME/.innotop, but can be |
3315 | +innotop's default configuration file locations are $HOME/.innotop and |
3316 | +/etc/innotop/innotop.conf, and they are looked for in that order. If the first |
3317 | +configuration file exists, the second will not be processed. Those can be |
3318 | overridden with the L<"--config"> command-line option. You can edit it by hand |
3319 | -safely. innotop reads the configuration file when it starts, and writes it out |
3320 | -again when it exits, so any changes you make while innotop is running will be |
3321 | -lost. |
3322 | +safely, however innotop reads the configuration file when it starts, and, if |
3323 | +readonly is set to 0, writes it out again when it exits. Thus, if readonly is |
3324 | +set to 0, any changes you make by hand while innotop is running will be lost. |
3325 | |
3326 | innotop doesn't store its entire configuration in the configuration file. It |
3327 | -has a huge set of default configuration that it holds only in memory, and the |
3328 | -configuration file only overrides these defaults. When you customize a default |
3329 | -setting, innotop notices, and then stores the customizations into the file. |
3330 | -This keeps the file size down, makes it easier to edit, and makes upgrades |
3331 | -easier. |
3332 | +has a huge set of default configuration values that it holds only in memory, |
3333 | +and the configuration file only overrides these defaults. When you customize a |
3334 | +default setting, innotop notices, and then stores the customizations into the |
3335 | +file. This keeps the file size down, makes it easier to edit, and makes |
3336 | +upgrades easier. |
3337 | |
3338 | -A configuration file can be made read-only. See L<"readonly">. |
3339 | +A configuration file is read-only be default. You can override that with |
3340 | +L<"--write">. See L<"readonly">. |
3341 | |
3342 | The configuration file is arranged into sections like an INI file. Each |
3343 | section begins with [section-name] and ends with [/section-name]. Each |
3344 | @@ -8119,8 +9576,7 @@ |
3345 | |
3346 | =item readonly |
3347 | |
3348 | -Whether the configuration file is readonly. This cannot be set interactively, |
3349 | -because it would prevent itself from being written to the configuration file. |
3350 | +Whether the configuration file is readonly. This cannot be set interactively. |
3351 | |
3352 | =item show_cxn_errors |
3353 | |
3354 | @@ -8185,10 +9641,12 @@ |
3355 | |
3356 | =item connections |
3357 | |
3358 | -This section holds the server connections you have defined. Each line is in the |
3359 | -format name=properties, where the properties are a name=value list. The |
3360 | +This section holds the server connections you have defined. Each line is in |
3361 | +the format name=properties, where the properties are a name=value list. The |
3362 | properties are self-explanatory, and the only one that is treated specially is |
3363 | -'pass' which is only present if 'savepass' is set. See L<"SERVER CONNECTIONS">. |
3364 | +'pass' which is only present if 'savepass' is set. This section of the |
3365 | +configuration file will be skipped if any DSN, username, or password |
3366 | +command-line options are used. See L<"SERVER CONNECTIONS">. |
3367 | |
3368 | =item active_connections |
3369 | |
3370 | @@ -8646,7 +10104,7 @@ |
3371 | |
3372 | =head2 GROUPING |
3373 | |
3374 | -innotop can group, or aggregate, rows together (I use the terms |
3375 | +innotop can group, or aggregate, rows together (the terms are used |
3376 | interchangeably). This is quite similar to an SQL GROUP BY clause. You can |
3377 | specify to group on certain columns, or if you don't specify any, the entire set |
3378 | of rows is treated as one group. This is quite like SQL so far, but unlike SQL, |
3379 | @@ -9389,15 +10847,16 @@ |
3380 | I don't know for sure. It also runs on Windows under ActivePerl without |
3381 | problem. |
3382 | |
3383 | -I use innotop on MySQL versions 3.23.58, 4.0.27, 4.1.0, 4.1.22, 5.0.26, 5.1.15, |
3384 | -and 5.2.3. If it doesn't run correctly for you, that is a bug and I hope you |
3385 | -report it. |
3386 | +innotop has been used on MySQL versions 3.23.58, 4.0.27, 4.1.0, 4.1.22, 5.0.26, |
3387 | +5.1.15, and 5.2.3. If it doesn't run correctly for you, that is a bug that |
3388 | +should be reported. |
3389 | |
3390 | =head1 FILES |
3391 | |
3392 | -$HOMEDIR/.innotop is used to store configuration information. Files include the |
3393 | -configuration file innotop.ini, the core_dump file which contains verbose error |
3394 | -messages if L<"debug"> is enabled, and the plugins/ subdirectory. |
3395 | +$HOMEDIR/.innotop and/or /etc/innotop are used to store |
3396 | +configuration information. Files include the configuration file innotop.conf, |
3397 | +the core_dump file which contains verbose error messages if L<"debug"> is |
3398 | +enabled, and the plugins/ subdirectory. |
3399 | |
3400 | =head1 GLOSSARY OF TERMS |
3401 | |
3402 | @@ -9412,8 +10871,8 @@ |
3403 | |
3404 | =head1 ACKNOWLEDGEMENTS |
3405 | |
3406 | -I'm grateful to the following people for various reasons, and hope I haven't |
3407 | -forgotten to include anyone: |
3408 | +The following people and organizations are acknowledged for various reasons. |
3409 | +Hopefully no one has been forgotten. |
3410 | |
3411 | Allen K. Smith, |
3412 | Aurimas Mikalauskas, |
3413 | @@ -9426,6 +10885,7 @@ |
3414 | Dr. Frank Ullrich, |
3415 | Giuseppe Maxia, |
3416 | Google.com Site Reliability Engineers, |
3417 | +Google Code, |
3418 | Jan Pieter Kunst, |
3419 | Jari Aalto, |
3420 | Jay Pipes, |
3421 | @@ -9443,9 +10903,9 @@ |
3422 | The Gentoo MySQL Team, |
3423 | Trevor Price, |
3424 | Yaar Schnitman, |
3425 | -and probably more people I've neglected to include. |
3426 | +and probably more people that have not been included. |
3427 | |
3428 | -(If I misspelled your name, it's probably because I'm afraid of putting |
3429 | +(If your name has been misspelled, it's probably out of fear of putting |
3430 | international characters into this documentation; earlier versions of Perl might |
3431 | not be able to compile it then). |
3432 | |
3433 | @@ -9472,14 +10932,15 @@ |
3434 | |
3435 | =head1 AUTHOR |
3436 | |
3437 | -Baron Schwartz. |
3438 | +Originally written by Baron Schwartz; currently maintained by Aaron Racine. |
3439 | |
3440 | =head1 BUGS |
3441 | |
3442 | You can report bugs, ask for improvements, and get other help and support at |
3443 | -L<http://sourceforge.net/projects/innotop>. There are mailing lists, forums, |
3444 | -a bug tracker, etc. Please use these instead of contacting me directly, as it |
3445 | -makes my job easier and benefits others if the discussions are permanent and |
3446 | -public. Of course, if you need to contact me in private, please do. |
3447 | +L<http://code.google.com/p/innotop/>. There are mailing lists, a source code |
3448 | +browser, a bug tracker, etc. Please use these instead of contacting the |
3449 | +maintainer or author directly, as it makes our job easier and benefits others if the |
3450 | +discussions are permanent and public. Of course, if you need to contact us in |
3451 | +private, please do. |
3452 | |
3453 | =cut |
3454 | |
3455 | === modified file 'bakery/debian-5.1/additions/innotop/innotop.1' |
3456 | --- bakery/debian-5.1/additions/innotop/innotop.1 2008-10-20 22:54:11 +0000 |
3457 | +++ bakery/debian-5.1/additions/innotop/innotop.1 2009-10-02 06:45:25 +0000 |
3458 | @@ -1,15 +1,7 @@ |
3459 | -.\" Automatically generated by Pod::Man v1.37, Pod::Parser v1.32 |
3460 | +.\" Automatically generated by Pod::Man 2.1801 (Pod::Simple 3.07) |
3461 | .\" |
3462 | .\" Standard preamble: |
3463 | .\" ======================================================================== |
3464 | -.de Sh \" Subsection heading |
3465 | -.br |
3466 | -.if t .Sp |
3467 | -.ne 5 |
3468 | -.PP |
3469 | -\fB\\$1\fR |
3470 | -.PP |
3471 | -.. |
3472 | .de Sp \" Vertical space (when we can't use .PP) |
3473 | .if t .sp .5v |
3474 | .if n .sp |
3475 | @@ -48,22 +40,25 @@ |
3476 | . ds R" '' |
3477 | 'br\} |
3478 | .\" |
3479 | +.\" Escape single quotes in literal strings from groff's Unicode transform. |
3480 | +.ie \n(.g .ds Aq \(aq |
3481 | +.el .ds Aq ' |
3482 | +.\" |
3483 | .\" If the F register is turned on, we'll generate index entries on stderr for |
3484 | -.\" titles (.TH), headers (.SH), subsections (.Sh), items (.Ip), and index |
3485 | +.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index |
3486 | .\" entries marked with X<> in POD. Of course, you'll have to process the |
3487 | .\" output yourself in some meaningful fashion. |
3488 | -.if \nF \{\ |
3489 | +.ie \nF \{\ |
3490 | . de IX |
3491 | . tm Index:\\$1\t\\n%\t"\\$2" |
3492 | .. |
3493 | . nr % 0 |
3494 | . rr F |
3495 | .\} |
3496 | -.\" |
3497 | -.\" For nroff, turn off justification. Always turn off hyphenation; it makes |
3498 | -.\" way too many mistakes in technical documents. |
3499 | -.hy 0 |
3500 | -.if n .na |
3501 | +.el \{\ |
3502 | +. de IX |
3503 | +.. |
3504 | +.\} |
3505 | .\" |
3506 | .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). |
3507 | .\" Fear. Run. Save yourself. No user-serviceable parts. |
3508 | @@ -128,8 +123,12 @@ |
3509 | .rm #[ #] #H #V #F C |
3510 | .\" ======================================================================== |
3511 | .\" |
3512 | -.IX Title "INNOTOP 1p" |
3513 | -.TH INNOTOP 1p "2007-11-09" "perl v5.8.8" "User Contributed Perl Documentation" |
3514 | +.IX Title "INNOTOP 1" |
3515 | +.TH INNOTOP 1 "2009-03-09" "perl v5.10.0" "User Contributed Perl Documentation" |
3516 | +.\" For nroff, turn off justification. Always turn off hyphenation; it makes |
3517 | +.\" way too many mistakes in technical documents. |
3518 | +.if n .ad l |
3519 | +.nh |
3520 | .SH "NAME" |
3521 | innotop \- MySQL and InnoDB transaction/status monitor. |
3522 | .SH "SYNOPSIS" |
3523 | @@ -151,6 +150,12 @@ |
3524 | .Vb 1 |
3525 | \& innotop \-\-count 5 \-d 1 \-n |
3526 | .Ve |
3527 | +.PP |
3528 | +To monitor a database on another system using a particular username and password: |
3529 | +.PP |
3530 | +.Vb 1 |
3531 | +\& innotop \-u <username> \-p <password> \-h <hostname> |
3532 | +.Ve |
3533 | .SH "DESCRIPTION" |
3534 | .IX Header "DESCRIPTION" |
3535 | innotop monitors MySQL servers. Each of its modes shows you a different aspect |
3536 | @@ -160,7 +165,7 @@ |
3537 | .PP |
3538 | innotop has lots of features for power users, but you can start and run it with |
3539 | virtually no configuration. If you're just getting started, see |
3540 | -\&\*(L"\s-1QUICK\-START\s0\*(R". Press '?' at any time while running innotop for |
3541 | +\&\*(L"QUICK-START\*(R". Press '?' at any time while running innotop for |
3542 | context-sensitive help. |
3543 | .SH "QUICK-START" |
3544 | .IX Header "QUICK-START" |
3545 | @@ -169,48 +174,31 @@ |
3546 | Enter; otherwise, you will need to change to innotop's directory and type \*(L"perl |
3547 | innotop\*(R". |
3548 | .PP |
3549 | -The first thing innotop needs to know is how to connect to a MySQL server. You |
3550 | -can just enter the hostname of the server, for example \*(L"localhost\*(R" or |
3551 | -\&\*(L"127.0.0.1\*(R" if the server is on the same machine as innotop. After this innotop |
3552 | -will prompt you for a \s-1DSN\s0 (data source name). You should be able to just accept |
3553 | -the defaults by pressing Enter. |
3554 | -.PP |
3555 | -When innotop asks you about a table to use when resetting InnoDB deadlock |
3556 | -information, just accept the default for now. This is an advanced feature you |
3557 | -can configure later (see \*(L"D: InnoDB Deadlocks\*(R" for more). |
3558 | -.PP |
3559 | -If you have a .my.cnf file with your MySQL connection defaults, innotop can read |
3560 | -it, and you won't need to specify a username and password if it's in that file. |
3561 | -Otherwise, you should answer 'y' to the next couple of prompts. |
3562 | -.PP |
3563 | -After this, you should be connected, and innotop should show you something like |
3564 | -the following: |
3565 | +With no options specified, innotop will attempt to connect to a MySQL server on |
3566 | +localhost using mysql_read_default_group=client for other connection |
3567 | +parameters. If you need to specify a different username and password, use the |
3568 | +\&\-u and \-p options, respectively. To monitor a MySQL database on another |
3569 | +host, use the \-h option. |
3570 | +.PP |
3571 | +After you've connected, innotop should show you something like the following: |
3572 | .PP |
3573 | .Vb 1 |
3574 | -\& InnoDB Txns (? for help) localhost, 01:11:19, InnoDB 10s :\-), 50 QPS, |
3575 | -.Ve |
3576 | -.PP |
3577 | -.Vb 2 |
3578 | -\& CXN History Versions Undo Dirty Buf Used Bufs Txns MaxTxn |
3579 | -\& localhost 7 2035 0 0 0.00% 92.19% 1 07:34 |
3580 | -.Ve |
3581 | -.PP |
3582 | -.Vb 5 |
3583 | -\& CXN ID User Host Txn Status Time Undo Query Tex |
3584 | -\& localhost 98379 user1 webserver ACTIVE 07:34 0 SELECT `c |
3585 | -\& localhost 98450 user1 webserver ACTIVE 01:06 0 INSERT IN |
3586 | -\& localhost 97750 user1 webserver not starte 00:00 0 |
3587 | -\& localhost 98375 user1 appserver not starte 00:00 0 |
3588 | +\& [RO] Query List (? for help) localhost, 01:11:19, 449.44 QPS, 14/7/163 con/run |
3589 | +\& |
3590 | +\& CXN When Load QPS Slow QCacheHit KCacheHit BpsIn BpsOut |
3591 | +\& localhost Total 0.00 1.07k 697 0.00% 98.17% 476.83k 242.83k |
3592 | +\& |
3593 | +\& CXN Cmd ID User Host DB Time Query |
3594 | +\& localhost Query 766446598 test 10.0.0.1 foo 00:02 INSERT INTO table ( |
3595 | .Ve |
3596 | .PP |
3597 | (This sample is truncated at the right so it will fit on a terminal when running |
3598 | \&'man innotop') |
3599 | .PP |
3600 | -This sample comes from a quiet server with few transactions active. If your |
3601 | -server is busy, you'll see more output. Notice the first line on the screen, |
3602 | -which tells you what mode you're in and what server you're connected to. You |
3603 | -can change to other modes with keystrokes; press 'Q' to switch to a list of |
3604 | -currently running queries. |
3605 | +If your server is busy, you'll see more output. Notice the first line on the |
3606 | +screen, which tells you that readonly is set to true ([\s-1RO\s0]), what mode you're |
3607 | +in and what server you're connected to. You can change to other modes with |
3608 | +keystrokes; press 'T' to switch to a list of InnoDB transactions, for example. |
3609 | .PP |
3610 | Press the '?' key to see what keys are active in the current mode. You can |
3611 | press any of these keys and innotop will either take the requested action or |
3612 | @@ -227,20 +215,14 @@ |
3613 | .PP |
3614 | You can negate some options by prefixing the option name with \-\-no. For |
3615 | example, \-\-noinc (or \-\-no\-inc) negates \*(L"\-\-inc\*(R". |
3616 | -.IP "\-\-help" 4 |
3617 | -.IX Item "--help" |
3618 | -Print a summary of command-line usage and exit. |
3619 | .IP "\-\-color" 4 |
3620 | .IX Item "--color" |
3621 | Enable or disable terminal coloring. Corresponds to the \*(L"color\*(R" config file |
3622 | setting. |
3623 | .IP "\-\-config" 4 |
3624 | .IX Item "--config" |
3625 | -Specifies a configuration file to read. This option is non\-sticky, that is to |
3626 | +Specifies a configuration file to read. This option is non-sticky, that is to |
3627 | say it does not persist to the configuration file itself. |
3628 | -.IP "\-\-nonint" 4 |
3629 | -.IX Item "--nonint" |
3630 | -Enable non-interactive operation. See \*(L"\s-1NON\-INTERACTIVE\s0 \s-1OPERATION\s0\*(R" for more. |
3631 | .IP "\-\-count" 4 |
3632 | .IX Item "--count" |
3633 | Refresh only the specified number of times (ticks) before exiting. Each refresh |
3634 | @@ -250,21 +232,47 @@ |
3635 | .IX Item "--delay" |
3636 | Specifies the amount of time to pause between ticks (refreshes). Corresponds to |
3637 | the configuration option \*(L"interval\*(R". |
3638 | -.IP "\-\-mode" 4 |
3639 | -.IX Item "--mode" |
3640 | -Specifies the mode in which innotop should start. Corresponds to the |
3641 | -configuration option \*(L"mode\*(R". |
3642 | +.IP "\-\-help" 4 |
3643 | +.IX Item "--help" |
3644 | +Print a summary of command-line usage and exit. |
3645 | +.IP "\-\-host" 4 |
3646 | +.IX Item "--host" |
3647 | +Host to connect to. |
3648 | .IP "\-\-inc" 4 |
3649 | .IX Item "--inc" |
3650 | Specifies whether innotop should display absolute numbers or relative numbers |
3651 | (offsets from their previous values). Corresponds to the configuration option |
3652 | \&\*(L"status_inc\*(R". |
3653 | +.IP "\-\-mode" 4 |
3654 | +.IX Item "--mode" |
3655 | +Specifies the mode in which innotop should start. Corresponds to the |
3656 | +configuration option \*(L"mode\*(R". |
3657 | +.IP "\-\-nonint" 4 |
3658 | +.IX Item "--nonint" |
3659 | +Enable non-interactive operation. See \*(L"NON-INTERACTIVE \s-1OPERATION\s0\*(R" for more. |
3660 | +.IP "\-\-password" 4 |
3661 | +.IX Item "--password" |
3662 | +Password to use for connection. |
3663 | +.IP "\-\-port" 4 |
3664 | +.IX Item "--port" |
3665 | +Port to use for connection. |
3666 | +.IP "\-\-skipcentral" 4 |
3667 | +.IX Item "--skipcentral" |
3668 | +Don't read the central configuration file. |
3669 | +.IP "\-\-user" 4 |
3670 | +.IX Item "--user" |
3671 | +User to use for connection. |
3672 | .IP "\-\-version" 4 |
3673 | .IX Item "--version" |
3674 | Output version information and exit. |
3675 | +.IP "\-\-write" 4 |
3676 | +.IX Item "--write" |
3677 | +Sets the configuration option \*(L"readonly\*(R" to 0, making innotop write the |
3678 | +running configuration to ~/.innotop/innotop.conf on exit, if no configuration |
3679 | +file was loaded at start-up. |
3680 | .SH "HOTKEYS" |
3681 | .IX Header "HOTKEYS" |
3682 | -innotop is interactive, and you control it with key\-presses. |
3683 | +innotop is interactive, and you control it with key-presses. |
3684 | .IP "\(bu" 4 |
3685 | Uppercase keys switch between modes. |
3686 | .IP "\(bu" 4 |
3687 | @@ -295,7 +303,7 @@ |
3688 | .Sp |
3689 | .Vb 8 |
3690 | \& Command Summary (? for help) localhost, 25+07:16:43, 2.45 QPS, 3 thd, 5.0.40 |
3691 | -\& _____________________ Command Summary _____________________ |
3692 | +\& _\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_ Command Summary _\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_ |
3693 | \& Name Value Pct Last Incr Pct |
3694 | \& Select_scan 3244858 69.89% 2 100.00% |
3695 | \& Select_range 1354177 29.17% 0 0.00% |
3696 | @@ -338,7 +346,8 @@ |
3697 | .Sp |
3698 | If it has, you can create a small deadlock to replace the large one. Use the |
3699 | \&'w' key to 'wipe' the large deadlock with a small one. This will not work |
3700 | -unless you have defined a deadlock table for the connection (see \*(L"\s-1SERVER\s0 \s-1CONNECTIONS\s0\*(R"). |
3701 | +unless you have defined a deadlock table for the connection (see \*(L"\s-1SERVER\s0 |
3702 | +\&\s-1CONNECTIONS\s0\*(R"). |
3703 | .Sp |
3704 | You can also configure innotop to automatically detect when a large deadlock |
3705 | needs to be replaced with a small one (see \*(L"auto_wipe_dl\*(R"). |
3706 | @@ -396,7 +405,7 @@ |
3707 | the screen when one connection is waiting for locks another connection holds: |
3708 | .Sp |
3709 | .Vb 7 |
3710 | -\& _________________________________ InnoDB Locks __________________________ |
3711 | +\& _\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_ InnoDB Locks _\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_\|_ |
3712 | \& CXN ID Type Waiting Wait Active Mode DB Table Index |
3713 | \& localhost 12 RECORD 1 00:10 00:10 X test t1 PRIMARY |
3714 | \& localhost 12 TABLE 0 00:10 00:10 IX test t1 |
3715 | @@ -440,7 +449,7 @@ |
3716 | You can \s-1EXPLAIN\s0 a query from this mode with the 'e' key. This displays the |
3717 | query's full text, the results of \s-1EXPLAIN\s0, and in newer MySQL versions, even |
3718 | the optimized query resulting from \s-1EXPLAIN\s0 \s-1EXTENDED\s0. innotop also tries to |
3719 | -rewrite certain queries to make them EXPLAIN\-able. For example, \s-1INSERT/SELECT\s0 |
3720 | +rewrite certain queries to make them EXPLAIN-able. For example, \s-1INSERT/SELECT\s0 |
3721 | statements are rewritable. |
3722 | .Sp |
3723 | This mode displays the \*(L"q_header\*(R" and \*(L"processlist\*(R" tables by default. |
3724 | @@ -491,9 +500,10 @@ |
3725 | .IX Header "INNOTOP STATUS" |
3726 | The first line innotop displays is a \*(L"status bar\*(R" of sorts. What it contains |
3727 | depends on the mode you're in, and what servers you're monitoring. The first |
3728 | -few words are always the innotop mode, such as \*(L"InnoDB Txns\*(R" for T mode, |
3729 | -followed by a reminder to press '?' for help at any time. |
3730 | -.Sh "\s-1ONE\s0 \s-1SERVER\s0" |
3731 | +few words are always [\s-1RO\s0] (if readonly is set to 1), the innotop mode, such as |
3732 | +\&\*(L"InnoDB Txns\*(R" for T mode, followed by a reminder to press '?' for help at any |
3733 | +time. |
3734 | +.SS "\s-1ONE\s0 \s-1SERVER\s0" |
3735 | .IX Subsection "ONE SERVER" |
3736 | The simplest case is when you're monitoring a single server. In this case, the |
3737 | name of the connection is next on the status line. This is the name you gave |
3738 | @@ -513,7 +523,7 @@ |
3739 | The next two words indicate the server's queries per second (\s-1QPS\s0) and how many |
3740 | threads (connections) exist. Finally, the server's version number is the last |
3741 | thing on the line. |
3742 | -.Sh "\s-1MULTIPLE\s0 \s-1SERVERS\s0" |
3743 | +.SS "\s-1MULTIPLE\s0 \s-1SERVERS\s0" |
3744 | .IX Subsection "MULTIPLE SERVERS" |
3745 | If you are monitoring multiple servers (see \*(L"\s-1SERVER\s0 \s-1CONNECTIONS\s0\*(R"), the status |
3746 | line does not show any details about individual servers. Instead, it shows the |
3747 | @@ -527,7 +537,7 @@ |
3748 | don't have errors. |
3749 | .PP |
3750 | See \*(L"\s-1ERROR\s0 \s-1HANDLING\s0\*(R" for more details about innotop's error handling. |
3751 | -.Sh "\s-1MONITORING\s0 A \s-1FILE\s0" |
3752 | +.SS "\s-1MONITORING\s0 A \s-1FILE\s0" |
3753 | .IX Subsection "MONITORING A FILE" |
3754 | If you give a filename on the command line, innotop will not connect to \s-1ANY\s0 |
3755 | servers at all. It will watch the specified file for InnoDB status output and |
3756 | @@ -549,7 +559,7 @@ |
3757 | innotop pre-selects the longest-running query, or the oldest connection. |
3758 | Confirm the command with 'y'. |
3759 | .PP |
3760 | -In \*(L"M: Master/Slave Replication Status\*(R" mode, you can start and stop slaves |
3761 | +In \*(L"Slave Replication Status\*(R"\*(L" in \*(R"M: Master mode, you can start and stop slaves |
3762 | with the 'a' and 'o' keys, respectively. You can send these commands to many |
3763 | slaves at once. innotop fills in a default command of \s-1START\s0 \s-1SLAVE\s0 or \s-1STOP\s0 \s-1SLAVE\s0 |
3764 | for you, but you can actually edit the command and send anything you wish, such |
3765 | @@ -564,8 +574,8 @@ |
3766 | these slave connections and suggest it as the argument to \s-1PURGE\s0 \s-1MASTER\s0 \s-1LOGS\s0. |
3767 | .SH "SERVER CONNECTIONS" |
3768 | .IX Header "SERVER CONNECTIONS" |
3769 | -When you create a server connection, innotop asks you for a series of inputs, as |
3770 | -follows: |
3771 | +When you create a server connection using '@', innotop asks you for a series of |
3772 | +inputs, as follows: |
3773 | .IP "\s-1DSN\s0" 4 |
3774 | .IX Item "DSN" |
3775 | A \s-1DSN\s0 is a Data Source Name, which is the initial argument passed to the \s-1DBI\s0 |
3776 | @@ -576,7 +586,7 @@ |
3777 | .Ve |
3778 | .Sp |
3779 | Since this \s-1DSN\s0 is passed to the DBD::mysql driver, you should read the driver's |
3780 | -documentation at \*(L"http://search.cpan.org/dist/DBD\-mysql/lib/DBD/mysql.pm\*(R" for |
3781 | +documentation at \*(L"/search.cpan.org/dist/DBD\-mysql/lib/DBD/mysql.pm\*(R"\*(L" in \*(R"http: for |
3782 | the exact details on all the options you can pass the driver in the \s-1DSN\s0. You |
3783 | can read more about \s-1DBI\s0 at <http://dbi.perl.org/docs/>, and especially at |
3784 | <http://search.cpan.org/~timb/DBI/DBI.pm>. |
3785 | @@ -611,9 +621,6 @@ |
3786 | But innotop isn't limited to monitoring a single server; you can define many |
3787 | server connections and switch between them by pressing the '@' key. See |
3788 | \&\*(L"\s-1SWITCHING\s0 \s-1BETWEEN\s0 \s-1CONNECTIONS\s0\*(R". |
3789 | -.PP |
3790 | -To create a new connection, press the '@' key and type the name of the new |
3791 | -connection, then follow the steps given above. |
3792 | .SH "SERVER GROUPS" |
3793 | .IX Header "SERVER GROUPS" |
3794 | If you have multiple MySQL instances, you can put them into named groups, such |
3795 | @@ -629,7 +636,7 @@ |
3796 | .IX Header "SWITCHING BETWEEN CONNECTIONS" |
3797 | innotop lets you quickly switch which servers you're monitoring. The most basic |
3798 | way is by pressing the '@' key and typing the name(s) of the connection(s) you |
3799 | -want to use. This setting is per\-mode, so you can monitor different connections |
3800 | +want to use. This setting is per-mode, so you can monitor different connections |
3801 | in each mode, and innotop remembers which connections you choose. |
3802 | .PP |
3803 | You can quickly switch to the 'next' connection in alphabetical order with the |
3804 | @@ -663,7 +670,7 @@ |
3805 | .PP |
3806 | innotop does not continue to query connections that have errors, because they |
3807 | may slow innotop and make it hard to use, especially if the error is a problem |
3808 | -connecting and causes a long time\-out. Instead, innotop retries the connection |
3809 | +connecting and causes a long time-out. Instead, innotop retries the connection |
3810 | occasionally to see if the error still exists. If so, it will wait until some |
3811 | point in the future. The wait time increases in ticks as the Fibonacci series, |
3812 | so it tries less frequently as time passes. |
3813 | @@ -704,7 +711,8 @@ |
3814 | .IP "\(bu" 4 |
3815 | innotop only displays the first table in each mode. This is so the output can |
3816 | be easily processed with other command-line utilities such as awk and sed. To |
3817 | -change which tables display in each mode, see \*(L"\s-1TABLES\s0\*(R". Since \*(L"Q: Query List\*(R" mode is so important, innotop automatically disables the \*(L"q_header\*(R" |
3818 | +change which tables display in each mode, see \*(L"\s-1TABLES\s0\*(R". Since \*(L"Q: Query |
3819 | +List\*(R" mode is so important, innotop automatically disables the \*(L"q_header\*(R" |
3820 | table. This ensures you'll see the \*(L"processlist\*(R" table, even if you have |
3821 | innotop configured to show the q_header table during interactive operation. |
3822 | Similarly, in \*(L"T: InnoDB Transactions\*(R" mode, the \*(L"t_header\*(R" table is |
3823 | @@ -775,20 +783,23 @@ |
3824 | \&\*(L"\s-1TABLES\s0\*(R". |
3825 | .SH "CONFIGURATION FILE" |
3826 | .IX Header "CONFIGURATION FILE" |
3827 | -innotop's default configuration file location is in \f(CW$HOME\fR/.innotop, but can be |
3828 | +innotop's default configuration file locations are \f(CW$HOME\fR/.innotop and |
3829 | +/etc/innotop/innotop.conf, and they are looked for in that order. If the first |
3830 | +configuration file exists, the second will not be processed. Those can be |
3831 | overridden with the \*(L"\-\-config\*(R" command-line option. You can edit it by hand |
3832 | -safely. innotop reads the configuration file when it starts, and writes it out |
3833 | -again when it exits, so any changes you make while innotop is running will be |
3834 | -lost. |
3835 | +safely, however innotop reads the configuration file when it starts, and, if |
3836 | +readonly is set to 0, writes it out again when it exits. Thus, if readonly is |
3837 | +set to 0, any changes you make by hand while innotop is running will be lost. |
3838 | .PP |
3839 | innotop doesn't store its entire configuration in the configuration file. It |
3840 | -has a huge set of default configuration that it holds only in memory, and the |
3841 | -configuration file only overrides these defaults. When you customize a default |
3842 | -setting, innotop notices, and then stores the customizations into the file. |
3843 | -This keeps the file size down, makes it easier to edit, and makes upgrades |
3844 | -easier. |
3845 | +has a huge set of default configuration values that it holds only in memory, |
3846 | +and the configuration file only overrides these defaults. When you customize a |
3847 | +default setting, innotop notices, and then stores the customizations into the |
3848 | +file. This keeps the file size down, makes it easier to edit, and makes |
3849 | +upgrades easier. |
3850 | .PP |
3851 | -A configuration file can be made read\-only. See \*(L"readonly\*(R". |
3852 | +A configuration file is read-only be default. You can override that with |
3853 | +\&\*(L"\-\-write\*(R". See \*(L"readonly\*(R". |
3854 | .PP |
3855 | The configuration file is arranged into sections like an \s-1INI\s0 file. Each |
3856 | section begins with [section\-name] and ends with [/section\-name]. Each |
3857 | @@ -799,7 +810,7 @@ |
3858 | files are still useful, though. |
3859 | .PP |
3860 | The first line in the file is innotop's version number. This lets innotop |
3861 | -notice when the file format is not backwards\-compatible, and upgrade smoothly |
3862 | +notice when the file format is not backwards-compatible, and upgrade smoothly |
3863 | without destroying your customized configuration. |
3864 | .PP |
3865 | The following list describes each section of the configuration file and the data |
3866 | @@ -807,7 +818,7 @@ |
3867 | .IP "general" 4 |
3868 | .IX Item "general" |
3869 | The 'general' section contains global configuration variables and variables that |
3870 | -may be mode\-specific, but don't belong in any other section. The syntax is a |
3871 | +may be mode-specific, but don't belong in any other section. The syntax is a |
3872 | simple key=value list. innotop writes a comment above each value to help you |
3873 | edit the file by hand. |
3874 | .RS 4 |
3875 | @@ -902,7 +913,8 @@ |
3876 | \&\*(L"shorten\*(R", and \*(L"percent\*(R" transformations. |
3877 | .IP "num_status_sets" 4 |
3878 | .IX Item "num_status_sets" |
3879 | -Controls how many sets of status variables to display in pivoted \*(L"S: Variables & Status\*(R" mode. It also controls the number of old sets of variables innotop |
3880 | +Controls how many sets of status variables to display in pivoted \*(L"S: Variables |
3881 | +& Status\*(R" mode. It also controls the number of old sets of variables innotop |
3882 | keeps in its memory, so the larger this variable is, the more memory innotop |
3883 | uses. |
3884 | .IP "plugin_dir" 4 |
3885 | @@ -911,8 +923,7 @@ |
3886 | \&'plugins' subdirectory of your innotop configuration directory. |
3887 | .IP "readonly" 4 |
3888 | .IX Item "readonly" |
3889 | -Whether the configuration file is readonly. This cannot be set interactively, |
3890 | -because it would prevent itself from being written to the configuration file. |
3891 | +Whether the configuration file is readonly. This cannot be set interactively. |
3892 | .IP "show_cxn_errors" 4 |
3893 | .IX Item "show_cxn_errors" |
3894 | Makes innotop print connection errors to \s-1STDOUT\s0. See \*(L"\s-1ERROR\s0 \s-1HANDLING\s0\*(R". |
3895 | @@ -926,7 +937,8 @@ |
3896 | transformation. |
3897 | .IP "show_statusbar" 4 |
3898 | .IX Item "show_statusbar" |
3899 | -Controls whether to show the status bar in the display. See \*(L"\s-1INNOTOP\s0 \s-1STATUS\s0\*(R". |
3900 | +Controls whether to show the status bar in the display. See \*(L"\s-1INNOTOP\s0 |
3901 | +\&\s-1STATUS\s0\*(R". |
3902 | .IP "skip_innodb" 4 |
3903 | .IX Item "skip_innodb" |
3904 | Disables fetching \s-1SHOW\s0 \s-1INNODB\s0 \s-1STATUS\s0, in case your server(s) do not have InnoDB |
3905 | @@ -966,10 +978,12 @@ |
3906 | name=quoted\-value list. |
3907 | .IP "connections" 4 |
3908 | .IX Item "connections" |
3909 | -This section holds the server connections you have defined. Each line is in the |
3910 | -format name=properties, where the properties are a name=value list. The |
3911 | -properties are self\-explanatory, and the only one that is treated specially is |
3912 | -\&'pass' which is only present if 'savepass' is set. See \*(L"\s-1SERVER\s0 \s-1CONNECTIONS\s0\*(R". |
3913 | +This section holds the server connections you have defined. Each line is in |
3914 | +the format name=properties, where the properties are a name=value list. The |
3915 | +properties are self-explanatory, and the only one that is treated specially is |
3916 | +\&'pass' which is only present if 'savepass' is set. This section of the |
3917 | +configuration file will be skipped if any \s-1DSN\s0, username, or password |
3918 | +command-line options are used. See \*(L"\s-1SERVER\s0 \s-1CONNECTIONS\s0\*(R". |
3919 | .IP "active_connections" 4 |
3920 | .IX Item "active_connections" |
3921 | This section holds a list of which connections are active in each mode. Each |
3922 | @@ -1025,7 +1039,7 @@ |
3923 | Choose which columns are in those tables, and create new columns. |
3924 | .IP "\(bu" 4 |
3925 | Filter which rows display with built-in filters, user-defined filters, and |
3926 | -quick\-filters. |
3927 | +quick-filters. |
3928 | .IP "\(bu" 4 |
3929 | Sort the rows to put important data first or group together related rows. |
3930 | .IP "\(bu" 4 |
3931 | @@ -1039,7 +1053,7 @@ |
3932 | you unlimited flexibility. |
3933 | .PP |
3934 | All these and more are explained in the following sections. |
3935 | -.Sh "\s-1TABLES\s0" |
3936 | +.SS "\s-1TABLES\s0" |
3937 | .IX Subsection "TABLES" |
3938 | A table is what you'd expect: a collection of columns. It also has some other |
3939 | properties, such as a caption. Filters, sorting rules, and colorization rules |
3940 | @@ -1192,7 +1206,7 @@ |
3941 | .IP "wait_array" 4 |
3942 | .IX Item "wait_array" |
3943 | Displays data about InnoDB's \s-1OS\s0 wait array. Data source: \*(L"\s-1OS_WAIT_ARRAY\s0\*(R". |
3944 | -.Sh "\s-1COLUMNS\s0" |
3945 | +.SS "\s-1COLUMNS\s0" |
3946 | .IX Subsection "COLUMNS" |
3947 | Columns belong to tables. You can choose a table's columns by pressing the '^' |
3948 | key, which starts the \*(L"\s-1TABLE\s0 \s-1EDITOR\s0\*(R" and lets you choose and edit columns. |
3949 | @@ -1200,7 +1214,7 @@ |
3950 | .IP "\(bu" 4 |
3951 | hdr: a column header. This appears in the first row of the table. |
3952 | .IP "\(bu" 4 |
3953 | -just: justification. '\-' means left-justified and '' means right\-justified, |
3954 | +just: justification. '\-' means left-justified and '' means right-justified, |
3955 | just as with printf formatting codes (not a coincidence). |
3956 | .IP "\(bu" 4 |
3957 | dec: whether to further align the column on the decimal point. |
3958 | @@ -1230,12 +1244,12 @@ |
3959 | the table. Several columns are set this way, such as the count column on |
3960 | \&\*(L"processlist\*(R" and \*(L"innodb_transactions\*(R", so you don't see a count when the |
3961 | grouping isn't enabled, but you do when it is. |
3962 | -.Sh "\s-1FILTERS\s0" |
3963 | +.SS "\s-1FILTERS\s0" |
3964 | .IX Subsection "FILTERS" |
3965 | Filters remove rows from the display. They behave much like a \s-1WHERE\s0 clause in |
3966 | \&\s-1SQL\s0. innotop has several built-in filters, which remove irrelevant information |
3967 | like inactive queries, but you can define your own as well. innotop also lets |
3968 | -you create quick\-filters, which do not get saved to the configuration file, and |
3969 | +you create quick-filters, which do not get saved to the configuration file, and |
3970 | are just an easy way to quickly view only some rows. |
3971 | .PP |
3972 | You can enable or disable a filter on any table. Press the '%' key (mnemonic: % |
3973 | @@ -1255,7 +1269,7 @@ |
3974 | For example, imagine you want to filter the processlist table so you only see |
3975 | queries that have been running more than five minutes. Type a new name for your |
3976 | filter, and when prompted for the subroutine body, press \s-1TAB\s0 to initiate your |
3977 | -terminal's auto\-completion. You'll see the names of the columns in the |
3978 | +terminal's auto-completion. You'll see the names of the columns in the |
3979 | \&\*(L"processlist\*(R" table (innotop generally tries to help you with auto-completion |
3980 | lists). You want to filter on the 'time' column. Type the text \*(L"$set\->{time} > |
3981 | 300\*(R" to return true when the query is more than five minutes old. That's all |
3982 | @@ -1279,7 +1293,7 @@ |
3983 | .IX Subsection "QUICK-FILTERS" |
3984 | .PP |
3985 | innotop's quick-filters are a shortcut to create a temporary filter that doesn't |
3986 | -persist when you restart innotop. To create a quick\-filter, press the '/' key. |
3987 | +persist when you restart innotop. To create a quick-filter, press the '/' key. |
3988 | innotop will prompt you for the column name and filter text. Again, you can use |
3989 | auto-completion on column names. The filter text can be just the text you want |
3990 | to \*(L"search for.\*(R" For example, to filter the \*(L"processlist\*(R" table on queries |
3991 | @@ -1292,9 +1306,9 @@ |
3992 | filter that is otherwise like any other filter. It just isn't saved to the |
3993 | configuration file. |
3994 | .PP |
3995 | -To clear quick\-filters, press the '\e' key and innotop will clear them all at |
3996 | +To clear quick-filters, press the '\e' key and innotop will clear them all at |
3997 | once. |
3998 | -.Sh "\s-1SORTING\s0" |
3999 | +.SS "\s-1SORTING\s0" |
4000 | .IX Subsection "SORTING" |
4001 | innotop has sensible built-in defaults to sort the most important rows to the |
4002 | top of the table. Like anything else in innotop, you can customize how any |
4003 | @@ -1311,9 +1325,9 @@ |
4004 | .PP |
4005 | Some modes have keys mapped to open this dialog directly, and to quickly reverse |
4006 | sort direction. Press '?' as usual to see which keys are mapped in any mode. |
4007 | -.Sh "\s-1GROUPING\s0" |
4008 | +.SS "\s-1GROUPING\s0" |
4009 | .IX Subsection "GROUPING" |
4010 | -innotop can group, or aggregate, rows together (I use the terms |
4011 | +innotop can group, or aggregate, rows together (the terms are used |
4012 | interchangeably). This is quite similar to an \s-1SQL\s0 \s-1GROUP\s0 \s-1BY\s0 clause. You can |
4013 | specify to group on certain columns, or if you don't specify any, the entire set |
4014 | of rows is treated as one group. This is quite like \s-1SQL\s0 so far, but unlike \s-1SQL\s0, |
4015 | @@ -1365,9 +1379,7 @@ |
4016 | .PP |
4017 | .Vb 1 |
4018 | \& Query List (? for help) localhost, 32:33, 0.11 QPS, 1 thd, 5.0.38\-log |
4019 | -.Ve |
4020 | -.PP |
4021 | -.Vb 5 |
4022 | +\& |
4023 | \& CXN Cmd Cnt ID User Host Time Query |
4024 | \& localhost Query 49 12933 webusr localhost 19:38 SELECT * FROM |
4025 | \& localhost Sending Da 23 2383 webusr localhost 12:43 SELECT col1, |
4026 | @@ -1383,7 +1395,7 @@ |
4027 | milliseconds to optimize queries. You might not have seen this pattern if you |
4028 | didn't look at your connections in aggregate. (This is a made-up example, but |
4029 | it can happen in real life). |
4030 | -.Sh "\s-1PIVOTING\s0" |
4031 | +.SS "\s-1PIVOTING\s0" |
4032 | .IX Subsection "PIVOTING" |
4033 | innotop can pivot a table for more compact display, similar to a Pivot Table in |
4034 | a spreadsheet (also known as a crosstab). Pivoting a table makes columns into |
4035 | @@ -1407,7 +1419,7 @@ |
4036 | .PP |
4037 | To get reasonable results, you might need to group as well as pivoting. |
4038 | innotop currently does this for \*(L"S: Variables & Status\*(R" mode. |
4039 | -.Sh "\s-1COLORS\s0" |
4040 | +.SS "\s-1COLORS\s0" |
4041 | .IX Subsection "COLORS" |
4042 | By default, innotop highlights rows with color so you can see at a glance which |
4043 | rows are more important. You can customize the colorization rules and add your |
4044 | @@ -1451,7 +1463,7 @@ |
4045 | for example in the first rule above, you should enter 'Locked' surrounded by |
4046 | quotes. If you get an error message about a bareword, you probably should have |
4047 | quoted something. |
4048 | -.Sh "\s-1EXPRESSIONS\s0" |
4049 | +.SS "\s-1EXPRESSIONS\s0" |
4050 | .IX Subsection "EXPRESSIONS" |
4051 | Expressions are at the core of how innotop works, and are what enables you to |
4052 | extend innotop as you wish. Recall the table lifecycle explained in |
4053 | @@ -1486,7 +1498,8 @@ |
4054 | \& } |
4055 | .Ve |
4056 | .PP |
4057 | -Here's a concrete example, taken from the header table \*(L"q_header\*(R" in \*(L"Q: Query List\*(R" mode. This expression calculates the qps, or Queries Per Second, |
4058 | +Here's a concrete example, taken from the header table \*(L"q_header\*(R" in \*(L"Q: |
4059 | +Query List\*(R" mode. This expression calculates the qps, or Queries Per Second, |
4060 | column's values, from the values returned by \s-1SHOW\s0 \s-1STATUS:\s0 |
4061 | .PP |
4062 | .Vb 1 |
4063 | @@ -1511,7 +1524,7 @@ |
4064 | Every column in innotop is computed by subroutines compiled in the same fashion. |
4065 | There is no difference between innotop's built-in columns and user-defined |
4066 | columns. This keeps things consistent and predictable. |
4067 | -.Sh "\s-1TRANSFORMATIONS\s0" |
4068 | +.SS "\s-1TRANSFORMATIONS\s0" |
4069 | .IX Subsection "TRANSFORMATIONS" |
4070 | Transformations change how a value is rendered. For example, they can take a |
4071 | number of seconds and display it in H:M:S format. The following transformations |
4072 | @@ -1548,7 +1561,7 @@ |
4073 | .IX Item "shorten" |
4074 | Formats a number as a unit of 1024 (k/M/G/T) and with \*(L"num_digits\*(R" number of |
4075 | digits after the decimal point. |
4076 | -.Sh "\s-1TABLE\s0 \s-1EDITOR\s0" |
4077 | +.SS "\s-1TABLE\s0 \s-1EDITOR\s0" |
4078 | .IX Subsection "TABLE EDITOR" |
4079 | The innotop table editor lets you customize tables with keystrokes. You start |
4080 | the table editor with the '^' key. If there's more than one table on the |
4081 | @@ -1557,9 +1570,7 @@ |
4082 | .PP |
4083 | .Vb 1 |
4084 | \& Editing table definition for Buffer Pool. Press ? for help, q to quit. |
4085 | -.Ve |
4086 | -.PP |
4087 | -.Vb 9 |
4088 | +\& |
4089 | \& name hdr label src |
4090 | \& cxn CXN Connection from which cxn |
4091 | \& buf_pool_size Size Buffer pool size IB_bp_buf_poo |
4092 | @@ -1568,7 +1579,7 @@ |
4093 | \& pages_modified Dirty Pages Pages modified (dirty IB_bp_pages_m |
4094 | \& buf_pool_hit_rate Hit Rate Buffer pool hit rate IB_bp_buf_poo |
4095 | \& total_mem_alloc Memory Total memory allocate IB_bp_total_m |
4096 | -\& add_pool_alloc Add\(aql Pool Additonal pool alloca IB_bp_add_poo |
4097 | +\& add_pool_alloc Add\*(Aql Pool Additonal pool alloca IB_bp_add_poo |
4098 | .Ve |
4099 | .PP |
4100 | The first line shows which table you're editing, and reminds you again to press |
4101 | @@ -1581,7 +1592,7 @@ |
4102 | with a couple of its properties such as its header and source expression (see |
4103 | \&\*(L"\s-1EXPRESSIONS\s0\*(R"). |
4104 | .PP |
4105 | -The key mappings are Vim\-style, as in many other places. Pressing 'j' and 'k' |
4106 | +The key mappings are Vim-style, as in many other places. Pressing 'j' and 'k' |
4107 | moves the highlight up or down. You can then (d)elete or (e)dit the highlighted |
4108 | column. You can also (a)dd a column to the table. This actually just activates |
4109 | one of the columns already defined for the table; it prompts you to choose from |
4110 | @@ -1601,7 +1612,7 @@ |
4111 | .IP "\(bu" 4 |
4112 | The column header: this is the label that appears at the top of the column, in |
4113 | the table header. This can have spaces and funny characters, but be careful not |
4114 | -to make it too wide and waste space on\-screen. |
4115 | +to make it too wide and waste space on-screen. |
4116 | .IP "\(bu" 4 |
4117 | The column's data source: this is an expression that determines what data from |
4118 | the source (see \*(L"\s-1TABLES\s0\*(R") innotop will put into the column. This can just be |
4119 | @@ -1680,7 +1691,7 @@ |
4120 | .IX Header "PLUGINS" |
4121 | innotop has a simple but powerful plugin mechanism by which you can extend |
4122 | or modify its existing functionality, and add new functionality. innotop's |
4123 | -plugin functionality is event\-based: plugins register themselves to be called |
4124 | +plugin functionality is event-based: plugins register themselves to be called |
4125 | when events happen. They then have a chance to influence the event. |
4126 | .PP |
4127 | An innotop plugin is a Perl module placed in innotop's \*(L"plugin_dir\*(R" |
4128 | @@ -1692,7 +1703,7 @@ |
4129 | The module must conform to innotop's plugin interface. Additionally, the source |
4130 | code of the module must be written in such a way that innotop can inspect the |
4131 | file and determine the package name and description. |
4132 | -.Sh "Package Source Convention" |
4133 | +.SS "Package Source Convention" |
4134 | .IX Subsection "Package Source Convention" |
4135 | innotop inspects the plugin module's source to determine the Perl package name. |
4136 | It looks for a line of the form \*(L"package Foo;\*(R" and if found, considers the |
4137 | @@ -1700,10 +1711,10 @@ |
4138 | package name, with double semicolons and so on. |
4139 | .PP |
4140 | It also looks for a description in the source code, to make the plugin editor |
4141 | -more human\-friendly. The description is a comment line of the form \*(L"# |
4142 | +more human-friendly. The description is a comment line of the form \*(L"# |
4143 | description: Foo\*(R", where \*(L"Foo\*(R" is the text innotop will consider to be the |
4144 | plugin's description. |
4145 | -.Sh "Plugin Interface" |
4146 | +.SS "Plugin Interface" |
4147 | .IX Subsection "Plugin Interface" |
4148 | The innotop plugin interface is quite simple: innotop expects the plugin to be |
4149 | an object-oriented module it can call certain methods on. The methods are |
4150 | @@ -1736,7 +1747,7 @@ |
4151 | \&\fIregister_for_events()\fR, it must have \fIfoo()\fR and \fIbar()\fR methods. These methods are |
4152 | callbacks for the events. See \*(L"Plugin Events\*(R" for more details about each |
4153 | event. |
4154 | -.Sh "Plugin Variables" |
4155 | +.SS "Plugin Variables" |
4156 | .IX Subsection "Plugin Variables" |
4157 | The plugin's constructor is passed a hash of innotop's variables, which it can |
4158 | manipulate. It is probably a good idea if the plugin object saves a copy of it |
4159 | @@ -1744,7 +1755,7 @@ |
4160 | \&\f(CW%pluggable_vars\fR, and are as follows: |
4161 | .IP "action_for" 4 |
4162 | .IX Item "action_for" |
4163 | -A hashref of key mappings. These are innotop's global hot\-keys. |
4164 | +A hashref of key mappings. These are innotop's global hot-keys. |
4165 | .IP "agg_funcs" 4 |
4166 | .IX Item "agg_funcs" |
4167 | A hashref of functions that can be used for grouping. See \*(L"\s-1GROUPING\s0\*(R". |
4168 | @@ -1770,7 +1781,7 @@ |
4169 | A hashref of server groups. See \*(L"\s-1SERVER\s0 \s-1GROUPS\s0\*(R". |
4170 | .IP "tbl_meta" 4 |
4171 | .IX Item "tbl_meta" |
4172 | -A hashref of innotop's table meta\-data, with one entry per table (see |
4173 | +A hashref of innotop's table meta-data, with one entry per table (see |
4174 | \&\*(L"\s-1TABLES\s0\*(R" for more information). |
4175 | .IP "trans_funcs" 4 |
4176 | .IX Item "trans_funcs" |
4177 | @@ -1778,13 +1789,13 @@ |
4178 | .IP "var_sets" 4 |
4179 | .IX Item "var_sets" |
4180 | A hashref of variable sets. See \*(L"\s-1VARIABLE\s0 \s-1SETS\s0\*(R". |
4181 | -.Sh "Plugin Events" |
4182 | +.SS "Plugin Events" |
4183 | .IX Subsection "Plugin Events" |
4184 | Each event is defined somewhere in the innotop source code. When innotop runs |
4185 | that code, it executes the callback function for each plugin that expressed its |
4186 | interest in the event. innotop passes some data for each event. The events are |
4187 | defined in the \f(CW%event_listener_for\fR variable, and are as follows: |
4188 | -.ie n .IP "extract_values($set, $cur\fR, \f(CW$pre\fR, \f(CW$tbl)" 4 |
4189 | +.ie n .IP "extract_values($set, $cur, $pre, $tbl)" 4 |
4190 | .el .IP "extract_values($set, \f(CW$cur\fR, \f(CW$pre\fR, \f(CW$tbl\fR)" 4 |
4191 | .IX Item "extract_values($set, $cur, $pre, $tbl)" |
4192 | This event occurs inside the function that extracts values from a data source. |
4193 | @@ -1803,7 +1814,7 @@ |
4194 | .IX Item "draw_screen($lines)" |
4195 | This event occurs inside the subroutine that prints the lines to the screen. |
4196 | \&\f(CW$lines\fR is an arrayref of strings. |
4197 | -.Sh "Simple Plugin Example" |
4198 | +.SS "Simple Plugin Example" |
4199 | .IX Subsection "Simple Plugin Example" |
4200 | The easiest way to explain the plugin functionality is probably with a simple |
4201 | example. The following module adds a column to the beginning of every table and |
4202 | @@ -1811,75 +1822,59 @@ |
4203 | .PP |
4204 | .Vb 2 |
4205 | \& use strict; |
4206 | -\& use warnings FATAL => \(aqall\(aq; |
4207 | -.Ve |
4208 | -.PP |
4209 | -.Vb 2 |
4210 | +\& use warnings FATAL => \*(Aqall\*(Aq; |
4211 | +\& |
4212 | \& package Innotop::Plugin::Example; |
4213 | -\& # description: Adds an \(aqexample\(aq column to every table |
4214 | -.Ve |
4215 | -.PP |
4216 | -.Vb 4 |
4217 | +\& # description: Adds an \*(Aqexample\*(Aq column to every table |
4218 | +\& |
4219 | \& sub new { |
4220 | \& my ( $class, %vars ) = @_; |
4221 | -\& # Store reference to innotop\(aqs variables in $self |
4222 | +\& # Store reference to innotop\*(Aqs variables in $self |
4223 | \& my $self = bless { %vars }, $class; |
4224 | -.Ve |
4225 | -.PP |
4226 | -.Vb 11 |
4227 | +\& |
4228 | \& # Design the example column |
4229 | \& my $col = { |
4230 | -\& hdr => \(aqExample\(aq, |
4231 | -\& just => \(aq\(aq, |
4232 | +\& hdr => \*(AqExample\*(Aq, |
4233 | +\& just => \*(Aq\*(Aq, |
4234 | \& dec => 0, |
4235 | \& num => 1, |
4236 | -\& label => \(aqExample\(aq, |
4237 | -\& src => \(aqexample\(aq, # Get data from this column in the data source |
4238 | -\& tbl => \(aq\(aq, |
4239 | +\& label => \*(AqExample\*(Aq, |
4240 | +\& src => \*(Aqexample\*(Aq, # Get data from this column in the data source |
4241 | +\& tbl => \*(Aq\*(Aq, |
4242 | \& trans => [], |
4243 | \& }; |
4244 | -.Ve |
4245 | -.PP |
4246 | -.Vb 8 |
4247 | +\& |
4248 | \& # Add the column to every table. |
4249 | \& my $tbl_meta = $vars{tbl_meta}; |
4250 | \& foreach my $tbl ( values %$tbl_meta ) { |
4251 | \& # Add the column to the list of defined columns |
4252 | \& $tbl\->{cols}\->{example} = $col; |
4253 | \& # Add the column to the list of visible columns |
4254 | -\& unshift @{$tbl\->{visible}}, \(aqexample\(aq; |
4255 | +\& unshift @{$tbl\->{visible}}, \*(Aqexample\*(Aq; |
4256 | \& } |
4257 | -.Ve |
4258 | -.PP |
4259 | -.Vb 3 |
4260 | +\& |
4261 | \& # Be sure to return a reference to the object. |
4262 | \& return $self; |
4263 | \& } |
4264 | -.Ve |
4265 | -.PP |
4266 | -.Vb 5 |
4267 | -\& # I\(aqd like to be called when a data set is being rendered into a table, please. |
4268 | +\& |
4269 | +\& # I\*(Aqd like to be called when a data set is being rendered into a table, please. |
4270 | \& sub register_for_events { |
4271 | \& my ( $self ) = @_; |
4272 | \& return qw(set_to_tbl_pre_filter); |
4273 | \& } |
4274 | -.Ve |
4275 | -.PP |
4276 | -.Vb 8 |
4277 | +\& |
4278 | \& # This method will be called when the event fires. |
4279 | \& sub set_to_tbl_pre_filter { |
4280 | \& my ( $self, $rows, $tbl ) = @_; |
4281 | -\& # Set the example column\(aqs data source to the value 1. |
4282 | +\& # Set the example column\*(Aqs data source to the value 1. |
4283 | \& foreach my $row ( @$rows ) { |
4284 | \& $row\->{example} = 1; |
4285 | \& } |
4286 | \& } |
4287 | -.Ve |
4288 | -.PP |
4289 | -.Vb 1 |
4290 | +\& |
4291 | \& 1; |
4292 | .Ve |
4293 | -.Sh "Plugin Editor" |
4294 | +.SS "Plugin Editor" |
4295 | .IX Subsection "Plugin Editor" |
4296 | The plugin editor lets you view the plugins innotop discovered and activate or |
4297 | deactivate them. Start the editor by pressing $ to start the configuration |
4298 | @@ -1920,7 +1915,7 @@ |
4299 | Whenever innotop fetches data from MySQL, it adds two extra bits to each set: |
4300 | cxn and Uptime_hires. cxn is the name of the connection from which the data |
4301 | came. Uptime_hires is a high-resolution version of the server's Uptime status |
4302 | -variable, which is important if your \*(L"interval\*(R" setting is sub\-second. |
4303 | +variable, which is important if your \*(L"interval\*(R" setting is sub-second. |
4304 | .PP |
4305 | Here are the kinds of data sources from which data is extracted: |
4306 | .IP "\s-1STATUS_VARIABLES\s0" 4 |
4307 | @@ -2002,14 +1997,15 @@ |
4308 | I don't know for sure. It also runs on Windows under ActivePerl without |
4309 | problem. |
4310 | .PP |
4311 | -I use innotop on MySQL versions 3.23.58, 4.0.27, 4.1.0, 4.1.22, 5.0.26, 5.1.15, |
4312 | -and 5.2.3. If it doesn't run correctly for you, that is a bug and I hope you |
4313 | -report it. |
4314 | +innotop has been used on MySQL versions 3.23.58, 4.0.27, 4.1.0, 4.1.22, 5.0.26, |
4315 | +5.1.15, and 5.2.3. If it doesn't run correctly for you, that is a bug that |
4316 | +should be reported. |
4317 | .SH "FILES" |
4318 | .IX Header "FILES" |
4319 | -$HOMEDIR/.innotop is used to store configuration information. Files include the |
4320 | -configuration file innotop.ini, the core_dump file which contains verbose error |
4321 | -messages if \*(L"debug\*(R" is enabled, and the plugins/ subdirectory. |
4322 | +\&\f(CW$HOMEDIR\fR/.innotop and/or /etc/innotop are used to store |
4323 | +configuration information. Files include the configuration file innotop.conf, |
4324 | +the core_dump file which contains verbose error messages if \*(L"debug\*(R" is |
4325 | +enabled, and the plugins/ subdirectory. |
4326 | .SH "GLOSSARY OF TERMS" |
4327 | .IX Header "GLOSSARY OF TERMS" |
4328 | .IP "tick" 4 |
4329 | @@ -2018,8 +2014,8 @@ |
4330 | displays it. |
4331 | .SH "ACKNOWLEDGEMENTS" |
4332 | .IX Header "ACKNOWLEDGEMENTS" |
4333 | -I'm grateful to the following people for various reasons, and hope I haven't |
4334 | -forgotten to include anyone: |
4335 | +The following people and organizations are acknowledged for various reasons. |
4336 | +Hopefully no one has been forgotten. |
4337 | .PP |
4338 | Allen K. Smith, |
4339 | Aurimas Mikalauskas, |
4340 | @@ -2032,6 +2028,7 @@ |
4341 | Dr. Frank Ullrich, |
4342 | Giuseppe Maxia, |
4343 | Google.com Site Reliability Engineers, |
4344 | +Google Code, |
4345 | Jan Pieter Kunst, |
4346 | Jari Aalto, |
4347 | Jay Pipes, |
4348 | @@ -2049,9 +2046,9 @@ |
4349 | The Gentoo MySQL Team, |
4350 | Trevor Price, |
4351 | Yaar Schnitman, |
4352 | -and probably more people I've neglected to include. |
4353 | +and probably more people that have not been included. |
4354 | .PP |
4355 | -(If I misspelled your name, it's probably because I'm afraid of putting |
4356 | +(If your name has been misspelled, it's probably out of fear of putting |
4357 | international characters into this documentation; earlier versions of Perl might |
4358 | not be able to compile it then). |
4359 | .SH "COPYRIGHT, LICENSE AND WARRANTY" |
4360 | @@ -2076,11 +2073,12 @@ |
4361 | Execute innotop and press '!' to see this information at any time. |
4362 | .SH "AUTHOR" |
4363 | .IX Header "AUTHOR" |
4364 | -Baron Schwartz. |
4365 | +Originally written by Baron Schwartz; currently maintained by Aaron Racine. |
4366 | .SH "BUGS" |
4367 | .IX Header "BUGS" |
4368 | You can report bugs, ask for improvements, and get other help and support at |
4369 | -<http://sourceforge.net/projects/innotop>. There are mailing lists, forums, |
4370 | -a bug tracker, etc. Please use these instead of contacting me directly, as it |
4371 | -makes my job easier and benefits others if the discussions are permanent and |
4372 | -public. Of course, if you need to contact me in private, please do. |
4373 | +<http://code.google.com/p/innotop/>. There are mailing lists, a source code |
4374 | +browser, a bug tracker, etc. Please use these instead of contacting the |
4375 | +maintainer or author directly, as it makes our job easier and benefits others if the |
4376 | +discussions are permanent and public. Of course, if you need to contact us in |
4377 | +private, please do. |
4378 | |
4379 | === modified file 'bakery/debian-5.1/additions/my.cnf' |
4380 | --- bakery/debian-5.1/additions/my.cnf 2008-10-20 22:54:11 +0000 |
4381 | +++ bakery/debian-5.1/additions/my.cnf 2009-10-02 06:45:25 +0000 |
4382 | @@ -42,10 +42,6 @@ |
4383 | language = /usr/share/mysql/english |
4384 | skip-external-locking |
4385 | # |
4386 | -# For compatibility to other Debian packages that still use |
4387 | -# libmysqlclient10 and libmysqlclient12. |
4388 | -old_passwords = 1 |
4389 | -# |
4390 | # Instead of skip-networking the default is now to listen only on |
4391 | # localhost which is more compatible and is not less secure. |
4392 | bind-address = 127.0.0.1 |
4393 | @@ -56,22 +52,28 @@ |
4394 | max_allowed_packet = 16M |
4395 | thread_stack = 128K |
4396 | thread_cache_size = 8 |
4397 | +# This replaces the startup script and checks MyISAM tables if needed |
4398 | +# the first time they are touched |
4399 | +myisam-recover = BACKUP |
4400 | #max_connections = 100 |
4401 | #table_cache = 64 |
4402 | #thread_concurrency = 10 |
4403 | # |
4404 | # * Query Cache Configuration |
4405 | # |
4406 | -query_cache_limit = 1M |
4407 | -query_cache_size = 16M |
4408 | +# Cache only tiny result sets, so we can fit more in the query cache. |
4409 | +query_cache_limit = 128K |
4410 | +query_cache_size = 16M |
4411 | # |
4412 | # * Logging and Replication |
4413 | # |
4414 | # Both location gets rotated by the cronjob. |
4415 | # Be aware that this log type is a performance killer. |
4416 | -#log = /var/log/mysql/mysql.log |
4417 | +# As of 5.1 you can enable the log at runtime! |
4418 | +#general_log_file = /var/log/mysql/mysql.log |
4419 | +#general_log = 1 |
4420 | # |
4421 | -# Error logging goes to syslog. This is a Debian improvement :) |
4422 | +# Error logging goes to syslog due to /etc/mysql/conf.d/mysqld_safe_syslog.cnf. |
4423 | # |
4424 | # Here you can see queries with especially long duration |
4425 | #log_slow_queries = /var/log/mysql/mysql-slow.log |
4426 | @@ -118,18 +120,7 @@ |
4427 | key_buffer = 16M |
4428 | |
4429 | # |
4430 | -# * NDB Cluster |
4431 | -# |
4432 | -# See /usr/share/doc/mysql-server-*/README.Debian for more information. |
4433 | -# |
4434 | -# The following configuration is read by the NDB Data Nodes (ndbd processes) |
4435 | -# not from the NDB Management Nodes (ndb_mgmd processes). |
4436 | -# |
4437 | -# [MYSQL_CLUSTER] |
4438 | -# ndb-connectstring=127.0.0.1 |
4439 | - |
4440 | - |
4441 | -# |
4442 | # * IMPORTANT: Additional settings that can override those from this file! |
4443 | +# The files must end with '.cnf', otherwise they'll be ignored. |
4444 | # |
4445 | !includedir /etc/mysql/conf.d/ |
4446 | |
4447 | === removed file 'bakery/debian-5.1/additions/mysql_explain_log.1' |
4448 | === added file 'bakery/debian-5.1/additions/mysqld_safe_syslog.cnf' |
4449 | --- bakery/debian-5.1/additions/mysqld_safe_syslog.cnf 1970-01-01 00:00:00 +0000 |
4450 | +++ bakery/debian-5.1/additions/mysqld_safe_syslog.cnf 2009-10-02 06:45:25 +0000 |
4451 | @@ -0,0 +1,2 @@ |
4452 | +[mysqld_safe] |
4453 | +syslog |
4454 | |
4455 | === removed file 'bakery/debian-5.1/additions/mysqlmanager.1' |
4456 | --- bakery/debian-5.1/additions/mysqlmanager.1 2008-10-20 22:54:11 +0000 |
4457 | +++ bakery/debian-5.1/additions/mysqlmanager.1 1970-01-01 00:00:00 +0000 |
4458 | @@ -1,49 +0,0 @@ |
4459 | -.TH mysql 1 "March 2005" "MySQL 4.1" "MySQL database" |
4460 | -.SH NAME |
4461 | -mysqlmanager \- Manages instances of MySQL server. |
4462 | -.SH SYNOPSIS |
4463 | -.B mysqlmanager |
4464 | -[\fIOPTIONS\fR] |
4465 | -.SH DESCRIPTION |
4466 | -Manages instances of MySQL server. |
4467 | -.TP |
4468 | -\-?, \fB\-\-help\fR |
4469 | -Display this help and exit. |
4470 | -.TP |
4471 | -\fB\-P\fR, \fB\-\-port=\fR# |
4472 | -Port number to listen on. |
4473 | -.TP |
4474 | -\fB\-l\fR, \fB\-\-log\fR=\fIname\fR |
4475 | -Path to log file. |
4476 | -.TP |
4477 | -\fB\-b\fR, \fB\-\-bind\-address=\fR# |
4478 | -Address to listen on. |
4479 | -.HP |
4480 | -\fB\-B\fR, \fB\-\-tcp\-backlog=\fR# Size of TCP/IP listen queue. |
4481 | -.HP |
4482 | -\fB\-g\fR, \fB\-\-greeting\fR=\fIname\fR Set greeting on connect. |
4483 | -.TP |
4484 | -\fB\-m\fR, \fB\-\-max\-command\-len=\fR# |
4485 | -Maximum command length. |
4486 | -.TP |
4487 | -\fB\-d\fR, \fB\-\-one\-thread\fR |
4488 | -Use one thread ( for debugging). |
4489 | -.TP |
4490 | -\fB\-C\fR, \fB\-\-connect\-retries=\fR# |
4491 | -Number of attempts to establish MySQL connection. |
4492 | -.TP |
4493 | -\fB\-p\fR, \fB\-\-password\-file\fR=\fIname\fR |
4494 | -Password file for manager. |
4495 | -.HP |
4496 | -\fB\-f\fR, \fB\-\-pid\-file\fR=\fIname\fR Pid file to use. |
4497 | -.TP |
4498 | -\fB\-V\fR, \fB\-\-version\fR |
4499 | -Output version information and exit. |
4500 | -.SH "SEE ALSO" |
4501 | -The full documentation for |
4502 | -.B mysqlmanager |
4503 | -is available in the package mysql-doc-4.1 or on the MySQL |
4504 | -homepage www.mysql.com. |
4505 | -.SH AUTHOR |
4506 | -This manpage was created by Christian Hammers <ch@debian.org> |
4507 | -using help2man. |
4508 | |
4509 | === modified file 'bakery/debian-5.1/additions/mysqlreport' |
4510 | --- bakery/debian-5.1/additions/mysqlreport 2008-10-20 22:54:11 +0000 |
4511 | +++ bakery/debian-5.1/additions/mysqlreport 2009-10-02 06:45:25 +0000 |
4512 | @@ -1,6 +1,6 @@ |
4513 | #!/usr/bin/perl -w |
4514 | |
4515 | -# mysqlreport v3.4a Jan 23 2008 |
4516 | +# mysqlreport v3.5 Apr 16 2008 |
4517 | # http://hackmysql.com/mysqlreport |
4518 | |
4519 | # mysqlreport makes an easy-to-read report of important MySQL status values. |
4520 | @@ -33,7 +33,7 @@ |
4521 | my %mycnf; # ~/.my.cnf |
4522 | my ($tmpfile_fh, $tmpfile); |
4523 | my ($stat_name, $stat_val, $stat_label); |
4524 | -my ($major, $minor, $patch, $x); # MySQL version |
4525 | +my $MySQL_version; |
4526 | my (%stats, %vars); # SHOW STATUS, SHOW VARIABLES |
4527 | my (%DMS_vals, %Com_vals, %ib_vals); |
4528 | my ($dbh, $query); |
4529 | @@ -92,15 +92,18 @@ |
4530 | $op{'com'} ||= 3; |
4531 | $op{'c'} ||= 1; # Used in collect_reports() if --r given integer value |
4532 | |
4533 | +$relative_live = 0; |
4534 | +$relative_infiles = 0; |
4535 | + |
4536 | if(defined $op{'r'}) |
4537 | { |
4538 | - if($op{r}) { $relative_live = 1; } |
4539 | + if($op{r}) { $relative_live = 1; } # if -r was given an integer value |
4540 | else { $relative_infiles = 1; } |
4541 | } |
4542 | |
4543 | # The report is written to a tmp file first. |
4544 | # Later it will be moved to $op{'outfile'} or emailed $op{'email'} if needed. |
4545 | -($tmpfile_fh, $tmpfile) = tempfile() or die("Cannot open temporary file for writing: $!\n"); |
4546 | +($tmpfile_fh, $tmpfile) = tempfile() or die "Cannot open temporary file for writing: $!\n"; |
4547 | |
4548 | if($op{'detach'}) |
4549 | { |
4550 | @@ -122,30 +125,12 @@ |
4551 | select $tmpfile_fh; |
4552 | $| = 1 if ($op{'detach'} || $relative_live); |
4553 | |
4554 | +print "tmp file: $tmpfile\n" if $op{debug}; |
4555 | + |
4556 | # Connect to MySQL |
4557 | if(!$op{'infile'} && !$relative_infiles) |
4558 | { |
4559 | - my $dsn; |
4560 | - |
4561 | - if($mycnf{'socket'} && -S $mycnf{'socket'}) |
4562 | - { |
4563 | - $dsn = "DBI:mysql:mysql_socket=$mycnf{socket}"; |
4564 | - } |
4565 | - elsif($mycnf{'host'}) |
4566 | - { |
4567 | - $dsn = "DBI:mysql:host=$mycnf{host}" . ($mycnf{port} ? ";port=$mycnf{port}" : ""); |
4568 | - } |
4569 | - else |
4570 | - { |
4571 | - $dsn = "DBI:mysql:host=localhost"; |
4572 | - } |
4573 | - |
4574 | - if($op{debug}) |
4575 | - { |
4576 | - print "DBI DSN: $dsn\n"; |
4577 | - } |
4578 | - |
4579 | - $dbh = DBI->connect($dsn, $mycnf{'user'}, $mycnf{'pass'}) or die; |
4580 | + connect_to_MySQL(); |
4581 | } |
4582 | |
4583 | $have_innodb_vals = 1; # This might be set to 0 later in get_MySQL_version() |
4584 | @@ -192,7 +177,7 @@ |
4585 | sub show_help_and_exit |
4586 | { |
4587 | print <<"HELP"; |
4588 | -mysqlreport v3.4a Jan 23 2008 |
4589 | +mysqlreport v3.5 Apr 16 2008 |
4590 | mysqlreport makes an easy-to-read report of important MySQL status values. |
4591 | |
4592 | Command line options (abbreviations work): |
4593 | @@ -224,6 +209,8 @@ |
4594 | |
4595 | sub get_user_mycnf |
4596 | { |
4597 | + print "get_user_mycnf\n" if $op{debug}; |
4598 | + |
4599 | return if $WIN; |
4600 | open MYCNF, "$ENV{HOME}/.my.cnf" or return; |
4601 | while(<MYCNF>) |
4602 | @@ -238,8 +225,34 @@ |
4603 | close MYCNF; |
4604 | } |
4605 | |
4606 | +sub connect_to_MySQL |
4607 | +{ |
4608 | + print "connect_to_MySQL\n" if $op{debug}; |
4609 | + |
4610 | + my $dsn; |
4611 | + |
4612 | + if($mycnf{'socket'} && -S $mycnf{'socket'}) |
4613 | + { |
4614 | + $dsn = "DBI:mysql:mysql_socket=$mycnf{socket}"; |
4615 | + } |
4616 | + elsif($mycnf{'host'}) |
4617 | + { |
4618 | + $dsn = "DBI:mysql:host=$mycnf{host}" . ($mycnf{port} ? ";port=$mycnf{port}" : ""); |
4619 | + } |
4620 | + else |
4621 | + { |
4622 | + $dsn = "DBI:mysql:host=localhost"; |
4623 | + } |
4624 | + |
4625 | + print "connect_to_MySQL: DBI DSN: $dsn\n" if $op{debug}; |
4626 | + |
4627 | + $dbh = DBI->connect($dsn, $mycnf{'user'}, $mycnf{'pass'}) or die; |
4628 | +} |
4629 | + |
4630 | sub collect_reports |
4631 | { |
4632 | + print "collect_reports\n" if $op{debug}; |
4633 | + |
4634 | my $i; |
4635 | |
4636 | get_vals(); |
4637 | @@ -258,8 +271,12 @@ |
4638 | |
4639 | for($i = 0; $i < $op{'c'}; $i++) |
4640 | { |
4641 | + $dbh->disconnect(); |
4642 | + |
4643 | sleep($op{'r'}); |
4644 | |
4645 | + connect_to_MySQL(); |
4646 | + |
4647 | print "\n#\n# Interval report " , $i + 1 , ", +", sec_to_dhms(($i + 1) * $op{'r'}), "\n#\n"; |
4648 | |
4649 | get_vals(); |
4650 | @@ -270,6 +287,8 @@ |
4651 | |
4652 | sub read_relative_infiles |
4653 | { |
4654 | + print "read_relative_infiles\n" if $op{debug}; |
4655 | + |
4656 | my $slurp; # Used to check infiles for multiple sets of status values |
4657 | my $n_stats; # Number of multiple sets of status values in an infile |
4658 | my $infile; |
4659 | @@ -309,12 +328,12 @@ |
4660 | for($i = 0; $i < $n_stats; $i++) |
4661 | { |
4662 | my ($fh, $name) = tempfile() |
4663 | - or die("read_relative_infiles: cannot open temporary file for writing: $!\n"); |
4664 | + or die "read_relative_infiles: cannot open temporary file for writing: $!\n"; |
4665 | |
4666 | push(@tmpfile_fh, $fh); |
4667 | push(@tmpfile_name, $name); |
4668 | |
4669 | - print "read_relative_infiles: created tmp file '$name'\n" if $op{debug}; |
4670 | + print "read_relative_infiles: created tmp file '$name' for set $i\n" if $op{debug}; |
4671 | } |
4672 | |
4673 | $i = 0; |
4674 | @@ -322,31 +341,27 @@ |
4675 | |
4676 | select $tmpfile_fh[$i]; |
4677 | |
4678 | - # Read infile again and copy each set of status values |
4679 | - # to seperate tmp files |
4680 | + # Read infile again and copy each set of status values to seperate tmp files |
4681 | open INFILE, "< $infile" or warn and next; |
4682 | while(<INFILE>) |
4683 | { |
4684 | next if /^\+/; |
4685 | next if /^$/; |
4686 | |
4687 | - print; |
4688 | + # The infile must begin with the system variable values. |
4689 | + # Therefore, the first occurance of Aborted_clients indicates the beginning |
4690 | + # of the first set of status values if no sets have occured yet ($stat_n == 0). |
4691 | + # In this case, the following status values are printed to the current fh, |
4692 | + # along with the system variable values read thus far, until Aborted_clients |
4693 | + # occurs again. Then begins the second and subsequent sets of status values. |
4694 | |
4695 | if(/Aborted_clients/) |
4696 | { |
4697 | - # The infile should begin with manually inserted system |
4698 | - # var values (like key_buffer_size = 128M). Therefore, |
4699 | - # the first occurance of /Aborted_clients/ indicates |
4700 | - # the first set of status values only if no sets have |
4701 | - # occured yet ($stat_n = 0). In this case, the following |
4702 | - # status values are printed to the current fh along with |
4703 | - # the system var values read so far until /Aborted_clients/ |
4704 | - # occurs again. Then begins the second and subsequent sets |
4705 | - # of status values. |
4706 | - next if $stat_n++ == 0; |
4707 | - |
4708 | + print and next if $stat_n++ == 0; |
4709 | select $tmpfile_fh[++$i]; |
4710 | } |
4711 | + |
4712 | + print; |
4713 | } |
4714 | close INFILE; |
4715 | |
4716 | @@ -357,13 +372,16 @@ |
4717 | { |
4718 | close $tmpfile_fh[$i]; |
4719 | |
4720 | + print "read_relative_infiles: reading set $i tmp file '$tmpfile_name[$i]'\n" |
4721 | + if $op{debug}; |
4722 | + |
4723 | read_infile($tmpfile_name[$i]); |
4724 | relative_infile_report($report_n++); |
4725 | |
4726 | if($WIN) { `del $tmpfile_name[$i]`; } |
4727 | else { `rm -f $tmpfile_name[$i]`; } |
4728 | |
4729 | - print "read_relative_infiles: deleted tmp file '$tmpfile_name[$i]'\n" |
4730 | + print "read_relative_infiles: deleted set $i tmp file '$tmpfile_name[$i]'\n" |
4731 | if $op{debug}; |
4732 | } |
4733 | |
4734 | @@ -373,6 +391,8 @@ |
4735 | |
4736 | sub relative_infile_report |
4737 | { |
4738 | + print "relative_infile_report\n" if $op{debug}; |
4739 | + |
4740 | my $report_n = shift; |
4741 | |
4742 | if($report_n == 1) |
4743 | @@ -400,10 +420,12 @@ |
4744 | |
4745 | sub get_vals |
4746 | { |
4747 | + print "get_vals\n" if $op{debug}; |
4748 | + |
4749 | my @row; |
4750 | |
4751 | # Get status values |
4752 | - if($major >= 5 && (($minor == 0 && $patch >= 2) || $minor > 0)) |
4753 | + if($MySQL_version >= 50002) |
4754 | { |
4755 | $query = $dbh->prepare("SHOW GLOBAL STATUS;"); |
4756 | } |
4757 | @@ -419,6 +441,8 @@ |
4758 | |
4759 | sub get_vars |
4760 | { |
4761 | + print "get_vars\n" if $op{debug}; |
4762 | + |
4763 | my @row; |
4764 | |
4765 | # Get server system variables |
4766 | @@ -427,7 +451,7 @@ |
4767 | while(@row = $query->fetchrow_array()) { $vars{$row[0]} = $row[1]; } |
4768 | |
4769 | # table_cache was renamed to table_open_cache in MySQL 5.1.3 |
4770 | - if($major >= 5 && (($minor == 1 && $patch >= 3) || $minor > 1)) |
4771 | + if($MySQL_version >= 50103) |
4772 | { |
4773 | $vars{'table_cache'} = $vars{'table_open_cache'}; |
4774 | } |
4775 | @@ -435,9 +459,14 @@ |
4776 | |
4777 | sub read_infile |
4778 | { |
4779 | + print "read_infile\n" if $op{debug}; |
4780 | + |
4781 | my $infile = shift; |
4782 | |
4783 | - # Default values if not set in INFILE |
4784 | + # Default required system variable values if not set in INFILE. |
4785 | + # As of mysqlreport v3.5 the direct output from SHOW VARIABLES; |
4786 | + # can be put into INFILE instead. See http://hackmysql.com/mysqlreportdoc |
4787 | + # for details. |
4788 | $vars{'version'} = "0.0.0" if !exists $vars{'version'}; |
4789 | $vars{'table_cache'} = 64 if !exists $vars{'table_cache'}; |
4790 | $vars{'max_connections'} = 100 if !exists $vars{'max_connections'}; |
4791 | @@ -450,34 +479,80 @@ |
4792 | # One should also add: |
4793 | # key_cache_block_size |
4794 | # query_cache_size |
4795 | - # to their infile if needed. |
4796 | - |
4797 | - open INFILE, "< $infile" or warn; |
4798 | + # to INFILE if needed. |
4799 | + |
4800 | + open INFILE, "< $infile" or die "Cannot open INFILE '$infile': $!\n"; |
4801 | + |
4802 | while(<INFILE>) |
4803 | { |
4804 | - next if /^\+\-/; |
4805 | - next if /^$/; |
4806 | - |
4807 | - chomp; |
4808 | - |
4809 | - if(/([A-Za-z_]+)[\s\t|]+(\d+)/) |
4810 | - { |
4811 | - $stats{$1} = $2; |
4812 | - next; |
4813 | - } |
4814 | - |
4815 | - # Explicit var = val (e.g. key_buffer_size = 128M) |
4816 | - if( /^\s*(\w+)\s*=\s*([0-9.]+)(M*)\s*$/ ) |
4817 | - { |
4818 | - $vars{$1} = ($3 ? $2 * 1024 * 1024 : $2); |
4819 | - print "read_infile: read '$_' as $1 = $vars{$1}\n" if $op{debug}; |
4820 | - next; |
4821 | + last if !defined $_; |
4822 | + |
4823 | + next if /^\+/; # skip divider lines |
4824 | + next if /^$/; # skip blank lines |
4825 | + |
4826 | + next until /(Aborted_clients|back_log|=)/; |
4827 | + |
4828 | + if($1 eq 'Aborted_clients') # status values |
4829 | + { |
4830 | + print "read_infile: start stats\n" if $op{debug}; |
4831 | + |
4832 | + while($_) |
4833 | + { |
4834 | + chomp; |
4835 | + if(/([A-Za-z_]+)[\s\t|]+(\d+)/) |
4836 | + { |
4837 | + $stats{$1} = $2; |
4838 | + print "read_infile: save $1 = $2\n" if $op{debug}; |
4839 | + } |
4840 | + else { print "read_infile: ignore '$_'\n" if $op{debug}; } |
4841 | + |
4842 | + last if $1 eq 'Uptime'; # exit while() if end of status values |
4843 | + $_ = <INFILE>; # otherwise, read next line of status values |
4844 | + } |
4845 | + } |
4846 | + elsif($1 eq 'back_log') # system variable values |
4847 | + { |
4848 | + print "read_infile: start vars\n" if $op{debug}; |
4849 | + |
4850 | + while($_) |
4851 | + { |
4852 | + chomp; |
4853 | + if(/([A-Za-z_]+)[\s\t|]+([\w\.\-]+)/) # This will exclude some vars |
4854 | + { # like pid_file which we don't need |
4855 | + $vars{$1} = $2; |
4856 | + print "read_infile: save $1 = $2\n" if $op{debug}; |
4857 | + } |
4858 | + else { print "read_infile: ignore '$_'\n" if $op{debug}; } |
4859 | + |
4860 | + last if $1 eq 'wait_timeout'; # exit while() if end of vars |
4861 | + $_ = <INFILE>; # otherwise, read next line of vars |
4862 | + } |
4863 | + } |
4864 | + elsif($1 eq '=') # old style, manually added system variable values |
4865 | + { |
4866 | + print "read_infile: start old vars\n" if $op{debug}; |
4867 | + |
4868 | + while($_ && $_ =~ /=/) |
4869 | + { |
4870 | + chomp; |
4871 | + if(/^\s*(\w+)\s*=\s*([0-9.]+)(M*)\s*$/) # e.g.: key_buffer_size = 128M |
4872 | + { |
4873 | + $vars{$1} = ($3 ? $2 * 1024 * 1024 : $2); |
4874 | + print "read_infile: read '$_' as $1 = $vars{$1}\n" if $op{debug}; |
4875 | + } |
4876 | + else { print "read_infile: ignore '$_'\n" if $op{debug}; } |
4877 | + |
4878 | + $_ = <INFILE>; # otherwise, read next line of old vars |
4879 | + } |
4880 | + |
4881 | + redo; |
4882 | } |
4883 | else |
4884 | { |
4885 | print "read_infile: unrecognized line: '$_'\n" if $op{debug}; |
4886 | } |
4887 | } |
4888 | + |
4889 | close INFILE; |
4890 | |
4891 | $real_uptime = $stats{'Uptime'}; |
4892 | @@ -489,7 +564,11 @@ |
4893 | |
4894 | sub get_MySQL_version |
4895 | { |
4896 | - return if $major; |
4897 | + print "get_MySQL_version\n" if $op{debug}; |
4898 | + |
4899 | + return if $MySQL_version; |
4900 | + |
4901 | + my ($major, $minor, $patch); |
4902 | |
4903 | if($op{'infile'} || $relative_infiles) |
4904 | { |
4905 | @@ -505,8 +584,10 @@ |
4906 | ($major, $minor, $patch) = ($row[1] =~ /(\d{1,2})\.(\d{1,2})\.(\d{1,2})/); |
4907 | } |
4908 | |
4909 | + $MySQL_version = sprintf("%d%02d%02d", $major, $minor, $patch); |
4910 | + |
4911 | # Innodb_ status values were added in 5.0.2 |
4912 | - if($major <= 4 || $patch < 2) |
4913 | + if($MySQL_version < 50002) |
4914 | { |
4915 | $have_innodb_vals = 0; |
4916 | print "get_MySQL_version: no InnoDB reports because MySQL version is older than 5.0.2\n" if $op{debug}; |
4917 | @@ -515,6 +596,8 @@ |
4918 | |
4919 | sub set_myisam_vals |
4920 | { |
4921 | + print "set_myisam_vals\n" if $op{debug}; |
4922 | + |
4923 | $questions = $stats{'Questions'}; |
4924 | |
4925 | $key_read_ratio = sprintf "%.2f", |
4926 | @@ -554,12 +637,14 @@ |
4927 | |
4928 | $dms = $DMS_vals{SELECT} + $DMS_vals{INSERT} + $DMS_vals{REPLACE} + $DMS_vals{UPDATE} + $DMS_vals{DELETE}; |
4929 | |
4930 | - $slow_query_t = "($vars{long_query_time})"; |
4931 | + $slow_query_t = format_u_time($vars{long_query_time}); |
4932 | |
4933 | } |
4934 | |
4935 | sub set_ib_vals |
4936 | { |
4937 | + print "set_ib_vals\n" if $op{debug}; |
4938 | + |
4939 | $ib_bp_used = ($stats{'Innodb_buffer_pool_pages_total'} - |
4940 | $stats{'Innodb_buffer_pool_pages_free'}) * |
4941 | $stats{'Innodb_page_size'}; |
4942 | @@ -575,65 +660,69 @@ |
4943 | |
4944 | sub write_relative_report |
4945 | { |
4946 | - %stats_present = %stats; |
4947 | - |
4948 | - for(keys %stats) |
4949 | - { |
4950 | - if($stats_past{$_} =~ /\d+/) |
4951 | - { |
4952 | - if($stats_present{$_} >= $stats_past{$_}) # Avoid negative values |
4953 | - { |
4954 | - $stats{$_} = $stats_present{$_} - $stats_past{$_}; |
4955 | - } |
4956 | - } |
4957 | - } |
4958 | - |
4959 | - # These values are either "at present" or "high water marks". |
4960 | - # Therefore, it is more logical to not relativize these values. |
4961 | - # Doing otherwise causes strange and misleading values. |
4962 | - $stats{'Key_blocks_used'} = $stats_present{'Key_blocks_used'}; |
4963 | - $stats{'Open_tables'} = $stats_present{'Open_tables'}; |
4964 | - $stats{'Max_used_connections'} = $stats_present{'Max_used_connections'}; |
4965 | - $stats{'Threads_running'} = $stats_present{'Threads_running'}; |
4966 | - $stats{'Threads_connected'} = $stats_present{'Threads_connected'}; |
4967 | - $stats{'Threads_cached'} = $stats_present{'Threads_cached'}; |
4968 | - $stats{'Qcache_free_blocks'} = $stats_present{'Qcache_free_blocks'}; |
4969 | - $stats{'Qcache_total_blocks'} = $stats_present{'Qcache_total_blocks'}; |
4970 | - $stats{'Qcache_free_memory'} = $stats_present{'Qcache_free_memory'}; |
4971 | - if($have_innodb_vals) |
4972 | - { |
4973 | - $stats{'Innodb_page_size'} = $stats_present{'Innodb_page_size'}; |
4974 | - $stats{'Innodb_buffer_pool_pages_data'} = $stats_present{'Innodb_buffer_pool_pages_data'}; |
4975 | - $stats{'Innodb_buffer_pool_pages_dirty'} = $stats_present{'Innodb_buffer_pool_pages_dirty'}; |
4976 | - $stats{'Innodb_buffer_pool_pages_free'} = $stats_present{'Innodb_buffer_pool_pages_free'}; |
4977 | - $stats{'Innodb_buffer_pool_pages_latched'} = $stats_present{'Innodb_buffer_pool_pages_latched'}; |
4978 | - $stats{'Innodb_buffer_pool_pages_misc'} = $stats_present{'Innodb_buffer_pool_pages_misc'}; |
4979 | - $stats{'Innodb_buffer_pool_pages_total'} = $stats_present{'Innodb_buffer_pool_pages_total'}; |
4980 | - $stats{'Innodb_data_pending_fsyncs'} = $stats_present{'Innodb_data_pending_fsyncs'}; |
4981 | - $stats{'Innodb_data_pending_reads'} = $stats_present{'Innodb_data_pending_reads'}; |
4982 | - $stats{'Innodb_data_pending_writes'} = $stats_present{'Innodb_data_pending_writes'}; |
4983 | - |
4984 | - # Innodb_row_lock_ values were added in MySQL 5.0.3 |
4985 | - if((($minor == 0 && $patch >= 3) || $minor > 0)) |
4986 | - { |
4987 | - $stats{'Innodb_row_lock_current_waits'} = $stats_present{'Innodb_row_lock_current_waits'}; |
4988 | - $stats{'Innodb_row_lock_time_avg'} = $stats_present{'Innodb_row_lock_time_avg'}; |
4989 | - $stats{'Innodb_row_lock_time_max'} = $stats_present{'Innodb_row_lock_time_max'}; |
4990 | - } |
4991 | - } |
4992 | - |
4993 | - get_Com_values(); |
4994 | - |
4995 | - %stats_past = %stats_present; |
4996 | - |
4997 | - set_myisam_vals(); |
4998 | - set_ib_vals() if $have_innodb_vals; |
4999 | - |
5000 | - write_report(); |
The diff has been truncated for viewing.