diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4340ab8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,39 @@ +_configs.sed +Makefile +Makefile.in +config.guess +config.sub +acconfig.h +aclocal.m4 +autom4te.cache +config.cache +config.log +config.status +configure +conftest +conftest.c +generated_lists +meta_cc +meta_ccld +mkinstalldirs +missing +install-sh +libtool +shlibtool +*.lo +*.la +libs +.deps +.libs +_libs +include +confdefs.h +*.gcda +*.gcno +cscope.out +ltmain.sh +depcomp +config.h +config.h.in +stamp-h1 + diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..2cf6990 --- /dev/null +++ b/COPYING @@ -0,0 +1,352 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +Preamble +======== + +The licenses for most software are designed to take away your freedom +to share and change it. By contrast, the GNU General Public License is +intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. +Our General Public Licenses are designed to make sure that you have +the freedom to distribute copies of free software (and charge for this +service if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs; and that you know you can do these things. + +To protect your rights, we need to make restrictions that forbid anyone +to deny you these rights or to ask you to surrender the rights. These +restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis +or for a fee, you must give the recipients all the rights that you +have. You must make sure that they, too, receive or can get the source +code. And you must show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + +Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + +Finally, any free program is threatened constantly by software patents. +We wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program +proprietary. To prevent this, we have made it clear that any patent +must be licensed for everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + 0. This License applies to any program or other work which contains a + notice placed by the copyright holder saying it may be distributed + under the terms of this General Public License. The "Program", + below, refers to any such program or work, and a "work based on + the Program" means either the Program or any derivative work under + copyright law: that is to say, a work containing the Program or a + portion of it, either verbatim or with modifications and/or + translated into another language. (Hereinafter, translation is + included without limitation in the term "modification".) Each + licensee is addressed as "you". + + Activities other than copying, distribution and modification are + not covered by this License; they are outside its scope. The act + of running the Program is not restricted, and the output from the + Program is covered only if its contents constitute a work based on + the Program (independent of having been made by running the + Program). Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's + source code as you receive it, in any medium, provided that you + conspicuously and appropriately publish on each copy an appropriate + copyright notice and disclaimer of warranty; keep intact all the + notices that refer to this License and to the absence of any + warranty; and give any other recipients of the Program a copy of + this License along with the Program. + + You may charge a fee for the physical act of transferring a copy, + and you may at your option offer warranty protection in exchange + for a fee. + + 2. You may modify your copy or copies of the Program or any portion + of it, thus forming a work based on the Program, and copy and + distribute such modifications or work under the terms of Section 1 + above, provided that you also meet all of these conditions: + + a. You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b. You must cause any work that you distribute or publish, that + in whole or in part contains or is derived from the Program + or any part thereof, to be licensed as a whole at no charge + to all third parties under the terms of this License. + + c. If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display + an announcement including an appropriate copyright notice and + a notice that there is no warranty (or else, saying that you + provide a warranty) and that users may redistribute the + program under these conditions, and telling the user how to + view a copy of this License. (Exception: if the Program + itself is interactive but does not normally print such an + announcement, your work based on the Program is not required + to print an announcement.) + + These requirements apply to the modified work as a whole. If + identifiable sections of that work are not derived from the + Program, and can be reasonably considered independent and separate + works in themselves, then this License, and its terms, do not + apply to those sections when you distribute them as separate + works. But when you distribute the same sections as part of a + whole which is a work based on the Program, the distribution of + the whole must be on the terms of this License, whose permissions + for other licensees extend to the entire whole, and thus to each + and every part regardless of who wrote it. + + Thus, it is not the intent of this section to claim rights or + contest your rights to work written entirely by you; rather, the + intent is to exercise the right to control the distribution of + derivative or collective works based on the Program. + + In addition, mere aggregation of another work not based on the + Program with the Program (or with a work based on the Program) on + a volume of a storage or distribution medium does not bring the + other work under the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, + under Section 2) in object code or executable form under the terms + of Sections 1 and 2 above provided that you also do one of the + following: + + a. Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Sections 1 and 2 above on a medium customarily used for + software interchange; or, + + b. Accompany it with a written offer, valid for at least three + years, to give any third-party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a + medium customarily used for software interchange; or, + + c. Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with + such an offer, in accord with Subsection b above.) + + The source code for a work means the preferred form of the work for + making modifications to it. For an executable work, complete + source code means all the source code for all modules it contains, + plus any associated interface definition files, plus the scripts + used to control compilation and installation of the executable. + However, as a special exception, the source code distributed need + not include anything that is normally distributed (in either + source or binary form) with the major components (compiler, + kernel, and so on) of the operating system on which the executable + runs, unless that component itself accompanies the executable. + + If distribution of executable or object code is made by offering + access to copy from a designated place, then offering equivalent + access to copy the source code from the same place counts as + distribution of the source code, even though third parties are not + compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program + except as expressly provided under this License. Any attempt + otherwise to copy, modify, sublicense or distribute the Program is + void, and will automatically terminate your rights under this + License. However, parties who have received copies, or rights, + from you under this License will not have their licenses + terminated so long as such parties remain in full compliance. + + 5. You are not required to accept this License, since you have not + signed it. However, nothing else grants you permission to modify + or distribute the Program or its derivative works. These actions + are prohibited by law if you do not accept this License. + Therefore, by modifying or distributing the Program (or any work + based on the Program), you indicate your acceptance of this + License to do so, and all its terms and conditions for copying, + distributing or modifying the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the + Program), the recipient automatically receives a license from the + original licensor to copy, distribute or modify the Program + subject to these terms and conditions. You may not impose any + further restrictions on the recipients' exercise of the rights + granted herein. You are not responsible for enforcing compliance + by third parties to this License. + + 7. If, as a consequence of a court judgment or allegation of patent + infringement or for any other reason (not limited to patent + issues), conditions are imposed on you (whether by court order, + agreement or otherwise) that contradict the conditions of this + License, they do not excuse you from the conditions of this + License. If you cannot distribute so as to satisfy simultaneously + your obligations under this License and any other pertinent + obligations, then as a consequence you may not distribute the + Program at all. For example, if a patent license would not permit + royalty-free redistribution of the Program by all those who + receive copies directly or indirectly through you, then the only + way you could satisfy both it and this License would be to refrain + entirely from distribution of the Program. + + If any portion of this section is held invalid or unenforceable + under any particular circumstance, the balance of the section is + intended to apply and the section as a whole is intended to apply + in other circumstances. + + It is not the purpose of this section to induce you to infringe any + patents or other property right claims or to contest validity of + any such claims; this section has the sole purpose of protecting + the integrity of the free software distribution system, which is + implemented by public license practices. Many people have made + generous contributions to the wide range of software distributed + through that system in reliance on consistent application of that + system; it is up to the author/donor to decide if he or she is + willing to distribute software through any other system and a + licensee cannot impose that choice. + + This section is intended to make thoroughly clear what is believed + to be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in + certain countries either by patents or by copyrighted interfaces, + the original copyright holder who places the Program under this + License may add an explicit geographical distribution limitation + excluding those countries, so that distribution is permitted only + in or among countries not thus excluded. In such case, this + License incorporates the limitation as if written in the body of + this License. + + 9. The Free Software Foundation may publish revised and/or new + versions of the General Public License from time to time. Such + new versions will be similar in spirit to the present version, but + may differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the + Program specifies a version number of this License which applies + to it and "any later version", you have the option of following + the terms and conditions either of that version or of any later + version published by the Free Software Foundation. If the Program + does not specify a version number of this License, you may choose + any version ever published by the Free Software Foundation. + + 10. If you wish to incorporate parts of the Program into other free + programs whose distribution conditions are different, write to the + author to ask for permission. For software which is copyrighted + by the Free Software Foundation, write to the Free Software + Foundation; we sometimes make exceptions for this. Our decision + will be guided by the two goals of preserving the free status of + all derivatives of our free software and of promoting the sharing + and reuse of software generally. + + NO WARRANTY + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO + WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE + LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT + HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT + WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT + NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE + QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE + PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY + SERVICING, REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN + WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY + MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE + LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, + INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR + INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF + DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU + OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY + OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS +How to Apply These Terms to Your New Programs +============================================= + +If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + +To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + ONE LINE TO GIVE THE PROGRAM'S NAME AND A BRIEF IDEA OF WHAT IT DOES. + Copyright (C) YYYY NAME OF AUTHOR + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19YY NAME OF AUTHOR + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + SIGNATURE OF TY COON, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, +you may consider it more useful to permit linking proprietary +applications with the library. If this is what you want to do, use the +GNU Library General Public License instead of this License. + diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..1a4e7e3 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,3 @@ +AUTOMAKE_OPTIONS=foreign no-dependencies +SUBDIRS = src +EXTRA_DIST = README default_tables.sql diff --git a/README b/README new file mode 100644 index 0000000..5614c54 --- /dev/null +++ b/README @@ -0,0 +1,93 @@ +1. Short overview +----------------- + +Pinba (PHP Is Not A Bottleneck Anymore) is a statistics server using MySQL as an interface. +It accumulates and processes data sent over UDP by multiple PHP processes and +displays statistics in a nice human-readable form of simple "reports", also providing +read-only interface to the raw data in order to make possible generation of more +sophisticated reports. + +Pinba is not a debugging tool in a common sense, since you're not supposed to do +debugging on production servers, but its main goal is to help developers to locate +bottlenecks in realtime and direct developers' attention to the code that really needs it. + +See "How it works" chapter below if you need more details. + +2. Installation requirements +-------------------------------- + +Pinba requires MySQL 5.1.x sources to be built and MySQL 5.1.x installation to run. +MySQL 5.1 is the first version which supports pluggable storage engines, +so older MySQL versions cannot and will never be supported. + +Libraries required: +* protobuf - http://code.google.com/p/protobuf +* Judy - http://judy.sf.net +* libevent - http://monkey.org/~provos/libevent/ +(this one may be already installed in your system, just make +sure you have -devel package installed, too). + +Optionally used: +* Hoard memory allocator - http://www.hoard.org + +Using --with-hoard option you can add compiled-in Hoard support. It helps to reduce +memory consumption (and Pinba is quite memory hungry when it comes to millions of timers). +2x smaller memory footprint with Hoard is what I regularly see. + +3. Installation +--------------- + +Make sure you have all the required libraries installed and proceed with the compilation. + +3.1. Compilation +---------------- +Unpack Pinba archive and start the good old configure & make procedure: + +# ./configure --with-mysql=/path/to/the/sources/of/mysql-5.1 --with-judy=/judy/prefix --with-protobuf=/protobuf/prefix --with-event=/event/prefix --libdir=/path/to/mysql/plugin/dir +# make install + +--libdir option is required in order to get the library installed into the correct directory +(but you can always put the lib there manually). +Usually the path looks like /lib/mysql/plugin, so if you've installed +MySQL using /usr/local/mysql prefix, the path should be /usr/local/mysql/lib/mysql/plugin. + + +3.2. Plugin installation +------------------------ + +And then in MySQL console execute: + +mysql> INSTALL PLUGIN pinba SONAME 'libpinba_engine.so'; + +I'd also suggest you to create a separate database, this way: + +mysql> CREATE DATABASE pinba; + +And then create the default tables: + +# mysql -D pinba < default_tables.sql + + +4. How it works +--------------- + +Each PHP process creates a Protobuf message and sends it to the configured Pinba +server on request shutdown. The packet is sent over UDP, so it doesn't affect PHP's +performance in any way. This also means some packets may be lost, as UDP is not +reliable by its nature, but that should not bother you much. + +Pinba server listening to the configured port (see pinba_port setting) reads and decodes +arriving Protobuf messages, adding them to the temporary pool. The temporary pool is needed +to prevent too frequent locking of the main pool, which might slow down the queries. +Once in a while (see pinba_stats_gathering_period directive) Pinba locks down the main +pool of records and adds the records from the temporary pool. It also drops outdated +records (see pinba_stats_history directive) and updates indexes and base reports in the +same moment. Tag reports are also updated, if any. +Both main and temporary pools are implemented as cyclic buffers of limited size, created +on startup and never re-allocated, so newer records always overwrite older ones. + +Well, this is all I had to say so far. +If you have any questions, feel free to ask them in the list - pinba-engine@googlegroups.com. +Send an empty email to pinba-engine+subscribe@googlegroups.com in order to subscribe. + +Have fun. diff --git a/build.mk b/build.mk new file mode 100644 index 0000000..44d7031 --- /dev/null +++ b/build.mk @@ -0,0 +1,38 @@ + +SUPPRESS_WARNINGS = 2>&1 | (egrep -v '(AC_TRY_RUN called without default to allow cross compiling|AC_PROG_CXXCPP was called before AC_PROG_CXX|defined in acinclude.m4 but never used|AC_PROG_LEX invoked multiple times|AC_DECL_YYTEXT is expanded from...|the top level)'||true) + +AUTOCONF ?= 'autoconf' +ACLOCAL ?= 'aclocal' +AUTOHEADER ?= 'autoheader' +AUTOMAKE ?= 'automake' +LIBTOOLIZE ?= 'libtoolize' + +config_h_in = config.h.in +targets = $(config_h_in) configure makefiles + +all: $(targets) + +ltmain: + $(LIBTOOLIZE) --force --copy + +aclocal.m4: + $(ACLOCAL) + +$(config_h_in): configure + @echo rebuilding $@ + @rm -f $@ + $(AUTOHEADER) $(SUPPRESS_WARNINGS) + +configure: aclocal.m4 configure.in ltmain + @echo rebuilding $@ + $(AUTOCONF) $(SUPPRESS_WARNINGS) + +makefiles: configure Makefile.am src/Makefile.am + @echo rebuilding Makefile.in files + $(AUTOMAKE) --add-missing --copy + +cvsclean: + @rm -rf src/*.lo src/*.la src/*.o src/*.a src/.libs src/.deps src/Makefile src/Makefile.in src/stamp-h1 src/config.h* + rm -rf aclocal.m4 autom4te.cache install.sh libtool Makefile Makefile.in 'configure.in~' missing config.h* configure stamp-h1 + rm -f config.guess config.log config.status config.sub cscope.out install-sh depcomp ltmain.sh _configs.sed + diff --git a/buildconf.sh b/buildconf.sh new file mode 100755 index 0000000..36daf53 --- /dev/null +++ b/buildconf.sh @@ -0,0 +1,23 @@ +#!/bin/sh + +cvsclean=0 +rebuild=0 + +while test $# -gt 0; do + if test "$1" = "--force"; then + rebuild=1 + echo "Forcing buildconf" + fi + if test "$1" = "--clean"; then + cvsclean=1 + fi + shift +done + +if test "$cvsclean" = "1"; then + echo "Cleaning autogenerated files" + ${MAKE:-make} -s -f build.mk cvsclean +else + ${MAKE:-make} -s -f build.mk +fi + diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..368e2d5 --- /dev/null +++ b/configure.in @@ -0,0 +1,416 @@ +dnl {{{ AX_PREFIX_CONFIG_H +AC_DEFUN([AX_PREFIX_CONFIG_H],[dnl +AC_BEFORE([AC_CONFIG_HEADERS],[$0])dnl +AC_CONFIG_COMMANDS([ifelse($1,,$PACKAGE-config.h,$1)],[dnl +AS_VAR_PUSHDEF([_OUT],[ac_prefix_conf_OUT])dnl +AS_VAR_PUSHDEF([_DEF],[ac_prefix_conf_DEF])dnl +AS_VAR_PUSHDEF([_PKG],[ac_prefix_conf_PKG])dnl +AS_VAR_PUSHDEF([_LOW],[ac_prefix_conf_LOW])dnl +AS_VAR_PUSHDEF([_UPP],[ac_prefix_conf_UPP])dnl +AS_VAR_PUSHDEF([_INP],[ac_prefix_conf_INP])dnl +m4_pushdef([_script],[conftest.prefix])dnl +m4_pushdef([_symbol],[m4_cr_Letters[]m4_cr_digits[]_])dnl +_OUT=`echo ifelse($1, , $PACKAGE-config.h, $1)` +_DEF=`echo _$_OUT | sed -e "y:m4_cr_letters:m4_cr_LETTERS[]:" -e "s/@<:@^m4_cr_Letters@:>@/_/g"` +_PKG=`echo ifelse($2, , $PACKAGE, $2)` +_LOW=`echo _$_PKG | sed -e "y:m4_cr_LETTERS-:m4_cr_letters[]_:"` +_UPP=`echo $_PKG | sed -e "y:m4_cr_letters-:m4_cr_LETTERS[]_:" -e "/^@<:@m4_cr_digits@:>@/s/^/_/"` +_INP=`echo "ifelse($3,,,$3)" | sed -e 's/ *//'` +if test ".$_INP" = "."; then + for ac_file in : $CONFIG_HEADERS; do test "_$ac_file" = _: && continue + case "$ac_file" in + *.h) _INP=$ac_file ;; + *) + esac + test ".$_INP" != "." && break + done +fi +if test ".$_INP" = "."; then + case "$_OUT" in + */*) _INP=`basename "$_OUT"` + ;; + *-*) _INP=`echo "$_OUT" | sed -e "s/@<:@_symbol@:>@*-//"` + ;; + *) _INP=config.h + ;; + esac +fi +if test -z "$_PKG" ; then + AC_MSG_ERROR([no prefix for _PREFIX_PKG_CONFIG_H]) +else + if test ! -f "$_INP" ; then if test -f "$srcdir/$_INP" ; then + _INP="$srcdir/$_INP" + fi fi + AC_MSG_NOTICE(creating: $_OUT: prefix $_UPP for $_INP defines) + if test -f $_INP ; then + echo "s/^@%:@undef *\\(@<:@m4_cr_LETTERS[]_@:>@\\)/@%:@undef $_UPP""_\\1/" > _script + echo "s/^@%:@undef *\\(@<:@m4_cr_letters@:>@\\)/@%:@undef $_LOW""_\\1/" >> _script + echo "s/^@%:@def[]ine *\\(@<:@m4_cr_LETTERS[]_@:>@@<:@_symbol@:>@*\\)\\(.*\\)/@%:@ifndef $_UPP""_\\1 \\" >> _script + echo "@%:@def[]ine $_UPP""_\\1 \\2 \\" >> _script + echo "@%:@endif/" >>_script + echo "s/^@%:@def[]ine *\\(@<:@m4_cr_letters@:>@@<:@_symbol@:>@*\\)\\(.*\\)/@%:@ifndef $_LOW""_\\1 \\" >> _script + echo "@%:@define $_LOW""_\\1 \\2 \\" >> _script + echo "@%:@endif/" >> _script + # now executing _script on _DEF input to create _OUT output file + echo "@%:@ifndef $_DEF" >$tmp/pconfig.h + echo "@%:@def[]ine $_DEF 1" >>$tmp/pconfig.h + echo ' ' >>$tmp/pconfig.h + echo /'*' $_OUT. Generated automatically at end of configure. '*'/ >>$tmp/pconfig.h + + sed -f _script $_INP >>$tmp/pconfig.h + echo ' ' >>$tmp/pconfig.h + echo '/* once:' $_DEF '*/' >>$tmp/pconfig.h + echo "@%:@endif" >>$tmp/pconfig.h + if cmp -s $_OUT $tmp/pconfig.h 2>/dev/null; then + rm -f $tmp/pconfig.h + AC_MSG_NOTICE([unchanged $_OUT]) + else + ac_dir=`AS_DIRNAME(["$_OUT"])` + AS_MKDIR_P(["$ac_dir"]) + rm -f "$_OUT" + mv $tmp/pconfig.h "$_OUT" + fi + cp _script _configs.sed + else + AC_MSG_ERROR([input file $_INP does not exist - skip generating $_OUT]) + fi + rm -f conftest.* +fi +m4_popdef([_symbol])dnl +m4_popdef([_script])dnl +AS_VAR_POPDEF([_INP])dnl +AS_VAR_POPDEF([_UPP])dnl +AS_VAR_POPDEF([_LOW])dnl +AS_VAR_POPDEF([_PKG])dnl +AS_VAR_POPDEF([_DEF])dnl +AS_VAR_POPDEF([_OUT])dnl +],[PACKAGE="$PACKAGE"])]) +dnl }}} + +AC_INIT([pinba_engine], [0.0.3]) +AM_CONFIG_HEADER(src/config.h) +AX_PREFIX_CONFIG_H([src/pinba_config.h]) +AM_INIT_AUTOMAKE + +AC_PROG_CC +AC_PROG_CXX + +AC_PROG_LIBTOOL +LIBTOOL="$LIBTOOL --preserve-dup-deps" +AC_SUBST(LIBTOOL) + +AC_SUBST(MYSQL_INC) +dnl CXXFLAGS="$CXXFLAGS -fno-implicit-templates -fno-exceptions -fno-rtti" +CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-rtti" + +AC_C_CONST +AC_TYPE_SIZE_T +AC_CHECK_HEADERS(limits.h syslimits.h string.h strings.h unistd.h stdint.h) + +dnl check for floor and libm +AC_CHECK_LIB([m], [floor], [LIBS="$LIBS -lm"], [AC_MSG_ERROR([can't continue without libm])]) + +STANDARD_PREFIXES="/usr /usr/local /opt /local" + +dnl {{{ --with-libdir +AC_ARG_WITH(libdir, + [AS_HELP_STRING([--with-libdir],[look for libraries in .../NAME rather than .../lib]) + ], + [LIBDIR=$with_libdir], + [LIBDIR=lib] +) +dnl }}} + +dnl {{{ --disable-rpath +AC_ARG_ENABLE(rpath, + [AS_HELP_STRING([--disable-rpath],[disable passing additional runtime library search paths]) + ], + [PINBA_RPATH=no], + [PINBA_RPATH=yes] +) +dnl }}} + +dnl {{{ check for rpath support +AC_MSG_CHECKING([if compiler supports -R]) +AC_CACHE_VAL(pinba_cv_cc_dashr,[ + SAVE_LIBS=$LIBS + LIBS="-R /usr/$LIBDIR $LIBS" + AC_TRY_LINK([], [], pinba_cv_cc_dashr=yes, pinba_cv_cc_dashr=no) + LIBS=$SAVE_LIBS]) +AC_MSG_RESULT([$pinba_cv_cc_dashr]) +if test $pinba_cv_cc_dashr = "yes"; then + ld_runpath_switch=-R +else + AC_MSG_CHECKING([if compiler supports -Wl,-rpath,]) + AC_CACHE_VAL(pinba_cv_cc_rpath,[ + SAVE_LIBS=$LIBS + LIBS="-Wl,-rpath,/usr/$LIBDIR $LIBS" + AC_TRY_LINK([], [], pinba_cv_cc_rpath=yes, pinba_cv_cc_rpath=no) + LIBS=$SAVE_LIBS]) + AC_MSG_RESULT([$pinba_cv_cc_rpath]) + if test $pinba_cv_cc_rpath = "yes"; then + ld_runpath_switch=-Wl,-rpath, + else + ld_runpath_switch=-L + fi +fi +if test "$PINBA_RPATH" = "no"; then + ld_runpath_switch= +fi +dnl }}} + +dnl {{{ --with-mysql +AC_MSG_CHECKING(for mysql source code) +AC_ARG_WITH(mysql, +[AS_HELP_STRING([--with-mysql],[specify MySQL sources directory]) +], +[ +], +[ + AC_MSG_ERROR([Please provide path to the MySQL sources directory]) +]) + +if test "x$with_mysql" = "xno"; then + AC_MSG_ERROR([can't continue without MySQL sources]) +else + + if test "x$with_mysql" = "xyes"; then + AC_MSG_ERROR([sorry, I'm not that smart to guess where the MySQL sources are, please specify the path]) + fi + + HEADERS="sql/mysql_priv.h include/my_dir.h include/mysql/plugin.h include/mysql.h" + for file in $HEADERS; do + if ! test -r "$with_mysql/$file"; then + AC_MSG_ERROR([Failed to find required header file $file in $with_mysql, check the path]) + fi + done + + AC_DEFINE([MYSQL_SRC], [1], [Source directory for MySQL]) + MYSQL_INC="-I$with_mysql/sql -I$with_mysql/include -I$with_mysql/regex -I$with_mysql" + AC_MSG_RESULT([$with_mysql]) +fi + + +dnl }}} + +dnl {{{ --with-protobuf +AC_ARG_WITH(protobuf, + [AS_HELP_STRING([--with-protobuf],[specify Google Protocol Buffers install prefix]) + ], + [ ], + [with_protobuf=yes] +) + +if test "x$with_protobuf" = "xno"; then + AC_MSG_ERROR([can't continue without Google Protocol Buffers]) +else + AC_MSG_CHECKING([Google Protocol Buffers install prefix]) + + if test "x$with_protobuf" = "xyes"; then + for i in `echo "$STANDARD_PREFIXES"`; do + if test -f "$i/include/google/protobuf/descriptor.h"; then + LIBPROTOBUF_DIR="$i" + break; + fi + done + else + if test -f "$with_protobuf/include/google/protobuf/descriptor.h"; then + LIBPROTOBUF_DIR="$with_protobuf" + else + AC_MSG_ERROR([Can't find Google Protocol Buffers headers under $with_protobuf directory]); + fi + fi + + if test "x$LIBPROTOBUF_DIR" = "x"; then + AC_MSG_ERROR([Unable to locate Google Protocol Buffers, please use --with-protobuf=]); + fi + + AC_MSG_RESULT([$LIBPROTOBUF_DIR]) + LDFLAGS="$LDFLAGS -L$LIBPROTOBUF_DIR/lib" + CXXFLAGS="$CXXFLAGS -I$LIBPROTOBUF_DIR/include" + CFLAGS="$CFLAGS -I$LIBPROTOBUF_DIR/include" + DEPS_LIBS="$LIBS -lprotobuf" +fi +dnl }}} + +dnl {{{ --with-event +AC_ARG_WITH(event, + [AS_HELP_STRING([--with-event],[specify libevent install prefix]) + ], + [ ], + [with_event=yes] +) + +if test "x$with_event" = "xno"; then + AC_MSG_ERROR([can't continue without libevent]) +else + AC_MSG_CHECKING([libevent install prefix]) + + if test "x$with_event" = "xyes"; then + for i in `echo "$STANDARD_PREFIXES"`; do + if test -f "$i/include/event.h"; then + LIBEVENT_DIR="$i" + break; + fi + done + else + if test -f "$with_event/include/event.h"; then + LIBEVENT_DIR="$with_event" + else + AC_MSG_ERROR([Can't find libevent headers under $with_event directory]) + fi + fi + + if test "x$LIBEVENT_DIR" = "x"; then + AC_MSG_ERROR([Unable to locate libevent headers, please use --with-event=]) + fi + + AC_MSG_RESULT([$LIBEVENT_DIR]) + LDFLAGS="$LDFLAGS -L$LIBEVENT_DIR/$LIBDIR" + LIBS="$LIBS -levent" + if test "$PINBA_RPATH" != "no"; then + LDFLAGS="$LDFLAGS $ld_runpath_switch$LIBEVENT_DIR/$LIBDIR" + fi + + AC_CHECK_LIB([event], [event_base_new], [], [ + AC_MSG_ERROR([event_base_new() is missing - libevent must be too old. Check config.log for more details]) + ]) +fi + +dnl }}} + +dnl {{{ --with-judy +AC_ARG_WITH(judy, + [AS_HELP_STRING([--with-judy],[specify Judy install prefix]) + ], + [ ], + [with_judy=yes] +) + +if test "x$with_judy" = "xno"; then + AC_MSG_ERROR([can't continue without Judy]) +else + AC_MSG_CHECKING([Judy install prefix]) + + if test "x$with_judy" = "xyes"; then + for i in `echo "$STANDARD_PREFIXES"`; do + if test -f "$i/include/Judy.h"; then + JUDY_DIR="$i" + break; + fi + done + else + if test -f "$with_judy/include/Judy.h"; then + JUDY_DIR="$with_judy" + else + AC_MSG_ERROR([Can't find Judy headers under $with_judy directory]); + fi + fi + + if test "x$JUDY_DIR" = "x"; then + AC_MSG_ERROR([Unable to locate Judy headers, please use --with-judy=]); + fi + + AC_MSG_RESULT([$JUDY_DIR]) + LDFLAGS="$LDFLAGS -L$JUDY_DIR/$LIBDIR" + LIBS="$LIBS -lJudy" + if test "$PINBA_RPATH" != "no"; then + LDFLAGS="$LDFLAGS $ld_runpath_switch$JUDY_DIR/$LIBDIR" + fi +fi +dnl }}} + +dnl {{{ --with-hoard +AC_ARG_WITH(hoard, + [AS_HELP_STRING([--with-hoard],[specify Hoard install prefix]) + ], + [ ], + [with_hoard=no] +) + +AC_MSG_CHECKING([if Hoard compiled-in support is enabled]) + +if test "x$with_hoard" = "xno"; then + AC_MSG_RESULT([no]) +else + AC_MSG_RESULT([yes]) + AC_MSG_CHECKING([Hoard install prefix]) + + if test "x$with_hoard" = "xyes"; then + for i in `echo "$STANDARD_PREFIXES"`; do + if test -f "$i/include/hoard.h"; then + HOARD_DIR="$i" + break; + fi + done + else + if test -f "$with_hoard/include/hoard.h"; then + HOARD_DIR="$with_hoard" + else + AC_MSG_ERROR([Can't find Hoard headers under $with_hoard directory]) + fi + fi + + if test "x$HOARD_DIR" = "x"; then + AC_MSG_ERROR([Unable to locate Hoard headers, please use --with-hoard=]) + fi + + AC_MSG_RESULT([$HOARD_DIR]) + LDFLAGS="$LDFLAGS -L$HOARD_DIR/$LIBDIR" + LIBS="$LIBS -lhoard" + if test "$PINBA_RPATH" != "no"; then + LDFLAGS="$LDFLAGS $ld_runpath_switch$HOARD_DIR/$LIBDIR" + fi + + AC_DEFINE([HAVE_HOARD], [1], [Whether Hoard support is compiled-in]) +fi + +dnl }}} + +dnl {{{ --enable-debug +AC_ARG_ENABLE(debug, + [AS_HELP_STRING([--enable-debug],[enable debugging symbols and compile flags]) + ], + [ + if test x"$enableval" = xyes ; then + debug="yes" + else + debug="no" + fi + ] +) + +if test x"$debug" = xyes ; then + DEBUG_FLAGS="-DSAFE_MUTEX -DDBUG_ON -DEXTRA_DEBUG -DUNIV_MUST_NOT_INLINE -DFORCE_INIT_OF_VARS" + + if test x"$GCC" = xyes; then + + dnl Remove any optimization flags from CFLAGS + changequote({,}) + CXXFLAGS=`echo "$CXXFLAGS" | sed -e 's/-O[0-9s]*//g'` + CXXFLAGS=`echo "$CXXFLAGS" | sed -e 's/-g[0-2]\? //g'` + CFLAGS=`echo "$CFLAGS" | sed -e 's/-O[0-9s]*//g'` + CFLAGS=`echo "$CFLAGS" | sed -e 's/-g[0-2]\? //g'` + changequote([,]) + CXXFLAGS="$CXXFLAGS -g3 -Wall -O0 $DEBUG_FLAGS" + CFLAGS="$CFLAGS -g3 -Wall -O0 $DEBUG_FLAGS" + fi + + dnl Do not strip symbols from developer object files. + INSTALL_STRIP_FLAG="" +else + dnl Make sure to strip symbols from non-developer object files. + INSTALL_STRIP_FLAG="-s" +fi +dnl }}} + +ADD_FLAGS="-DMYSQL_DYNAMIC_PLUGIN -DGOOGLE_PROTOBUF_NO_RTTI" +CXXFLAGS="$CXXFLAGS $ADD_FLAGS" +CFLAGS="$CFLAGS $ADD_FLAGS" + +AC_SUBST(INSTALL_STRIP_FLAG) + +AC_SUBST(DEPS_LIBS) +AC_SUBST(DEPS_CFLAGS) + +AC_OUTPUT(Makefile src/Makefile) diff --git a/default_tables.sql b/default_tables.sql new file mode 100644 index 0000000..01a57f6 --- /dev/null +++ b/default_tables.sql @@ -0,0 +1,202 @@ +DROP TABLE IF EXISTS request; + +CREATE TABLE `request` ( + `id` int(11) NOT NULL DEFAULT '0', + `hostname` varchar(16) DEFAULT NULL, + `req_count` int(11) DEFAULT NULL, + `server_name` varchar(64) DEFAULT NULL, + `script_name` varchar(128) DEFAULT NULL, + `doc_size` float DEFAULT NULL, + `mem_peak_usage` float DEFAULT NULL, + `req_time` float DEFAULT NULL, + `ru_utime` float DEFAULT NULL, + `ru_stime` float DEFAULT NULL, + `timers_cnt` int(11) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=PINBA DEFAULT CHARSET=latin1 COMMENT='request'; + +DROP TABLE IF EXISTS tag; + +CREATE TABLE `tag` ( + `id` int(11) NOT NULL, + `name` varchar(255) NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `name` (`name`) +) ENGINE=PINBA DEFAULT CHARSET=latin1 COMMENT='tag'; + +DROP TABLE IF EXISTS timer; + +CREATE TABLE `timer` ( + `id` int(11) NOT NULL DEFAULT '0', + `request_id` int(11) NOT NULL, + `hit_count` int(11) DEFAULT NULL, + `value` float DEFAULT NULL, + KEY `request_id` (`request_id`) +) ENGINE=PINBA DEFAULT CHARSET=latin1 COMMENT='timer'; + +DROP TABLE IF EXISTS timertag; + +CREATE TABLE `timertag` ( + `timer_id` int(11) NOT NULL, + `tag_id` int(11) NOT NULL, + `value` varchar(64) DEFAULT NULL, + KEY `timer_id` (`timer_id`), + KEY `tag_id` (`tag_id`) +) ENGINE=PINBA DEFAULT CHARSET=latin1 COMMENT='timertag'; + +DROP TABLE IF EXISTS info; + +CREATE TABLE `info` ( + `req_count` int(11) DEFAULT NULL, + `time_total` float DEFAULT NULL, + `ru_utime_total` float DEFAULT NULL, + `ru_stime_total` float DEFAULT NULL, + `time_interval` int(11) DEFAULT NULL, + `kbytes_total` float DEFAULT NULL +) ENGINE=PINBA DEFAULT CHARSET=latin1 COMMENT='info'; + +DROP TABLE IF EXISTS report_by_script_name; + +CREATE TABLE `report_by_script_name` ( + `req_count` int(11) DEFAULT NULL, + `req_per_sec` float DEFAULT NULL, + `req_time_total` float DEFAULT NULL, + `req_time_percent` float DEFAULT NULL, + `req_time_per_sec` float DEFAULT NULL, + `ru_utime_total` float DEFAULT NULL, + `ru_utime_percent` float DEFAULT NULL, + `ru_utime_per_sec` float DEFAULT NULL, + `ru_stime_total` float DEFAULT NULL, + `ru_stime_percent` float DEFAULT NULL, + `ru_stime_per_sec` float DEFAULT NULL, + `traffic_total` float DEFAULT NULL, + `traffic_percent` float DEFAULT NULL, + `traffic_per_sec` float DEFAULT NULL, + `script_name` varchar(128) DEFAULT NULL +) ENGINE=PINBA DEFAULT CHARSET=latin1 COMMENT='report1'; + +DROP TABLE IF EXISTS report_by_server_name; + +CREATE TABLE `report_by_server_name` ( + `req_count` int(11) DEFAULT NULL, + `req_per_sec` float DEFAULT NULL, + `req_time_total` float DEFAULT NULL, + `req_time_percent` float DEFAULT NULL, + `req_time_per_sec` float DEFAULT NULL, + `ru_utime_total` float DEFAULT NULL, + `ru_utime_percent` float DEFAULT NULL, + `ru_utime_per_sec` float DEFAULT NULL, + `ru_stime_total` float DEFAULT NULL, + `ru_stime_percent` float DEFAULT NULL, + `ru_stime_per_sec` float DEFAULT NULL, + `traffic_total` float DEFAULT NULL, + `traffic_percent` float DEFAULT NULL, + `traffic_per_sec` float DEFAULT NULL, + `server_name` varchar(64) DEFAULT NULL +) ENGINE=PINBA DEFAULT CHARSET=latin1 COMMENT='report2'; + +DROP TABLE IF EXISTS report_by_hostname; + +CREATE TABLE `report_by_hostname` ( + `req_count` int(11) DEFAULT NULL, + `req_per_sec` float DEFAULT NULL, + `req_time_total` float DEFAULT NULL, + `req_time_percent` float DEFAULT NULL, + `req_time_per_sec` float DEFAULT NULL, + `ru_utime_total` float DEFAULT NULL, + `ru_utime_percent` float DEFAULT NULL, + `ru_utime_per_sec` float DEFAULT NULL, + `ru_stime_total` float DEFAULT NULL, + `ru_stime_percent` float DEFAULT NULL, + `ru_stime_per_sec` float DEFAULT NULL, + `traffic_total` float DEFAULT NULL, + `traffic_percent` float DEFAULT NULL, + `traffic_per_sec` float DEFAULT NULL, + `hostname` varchar(16) DEFAULT NULL +) ENGINE=PINBA DEFAULT CHARSET=latin1 COMMENT='report3'; + +DROP TABLE IF EXISTS report_by_server_and_script; + +CREATE TABLE `report_by_server_and_script` ( + `req_count` int(11) DEFAULT NULL, + `req_per_sec` float DEFAULT NULL, + `req_time_total` float DEFAULT NULL, + `req_time_percent` float DEFAULT NULL, + `req_time_per_sec` float DEFAULT NULL, + `ru_utime_total` float DEFAULT NULL, + `ru_utime_percent` float DEFAULT NULL, + `ru_utime_per_sec` float DEFAULT NULL, + `ru_stime_total` float DEFAULT NULL, + `ru_stime_percent` float DEFAULT NULL, + `ru_stime_per_sec` float DEFAULT NULL, + `traffic_total` float DEFAULT NULL, + `traffic_percent` float DEFAULT NULL, + `traffic_per_sec` float DEFAULT NULL, + `server_name` varchar(64) DEFAULT NULL, + `script_name` varchar(128) DEFAULT NULL +) ENGINE=PINBA DEFAULT CHARSET=latin1 COMMENT='report4'; + +DROP TABLE IF EXISTS report_by_hostname_and_script; + +CREATE TABLE `report_by_hostname_and_script` ( + `req_count` int(11) DEFAULT NULL, + `req_per_sec` float DEFAULT NULL, + `req_time_total` float DEFAULT NULL, + `req_time_percent` float DEFAULT NULL, + `req_time_per_sec` float DEFAULT NULL, + `ru_utime_total` float DEFAULT NULL, + `ru_utime_percent` float DEFAULT NULL, + `ru_utime_per_sec` float DEFAULT NULL, + `ru_stime_total` float DEFAULT NULL, + `ru_stime_percent` float DEFAULT NULL, + `ru_stime_per_sec` float DEFAULT NULL, + `traffic_total` float DEFAULT NULL, + `traffic_percent` float DEFAULT NULL, + `traffic_per_sec` float DEFAULT NULL, + `hostname` varchar(16) DEFAULT NULL, + `script_name` varchar(128) DEFAULT NULL +) ENGINE=PINBA DEFAULT CHARSET=latin1 COMMENT='report5'; + +DROP TABLE IF EXISTS report_by_hostname_and_server; + +CREATE TABLE `report_by_hostname_and_server` ( + `req_count` int(11) DEFAULT NULL, + `req_per_sec` float DEFAULT NULL, + `req_time_total` float DEFAULT NULL, + `req_time_percent` float DEFAULT NULL, + `req_time_per_sec` float DEFAULT NULL, + `ru_utime_total` float DEFAULT NULL, + `ru_utime_percent` float DEFAULT NULL, + `ru_utime_per_sec` float DEFAULT NULL, + `ru_stime_total` float DEFAULT NULL, + `ru_stime_percent` float DEFAULT NULL, + `ru_stime_per_sec` float DEFAULT NULL, + `traffic_total` float DEFAULT NULL, + `traffic_percent` float DEFAULT NULL, + `traffic_per_sec` float DEFAULT NULL, + `hostname` varchar(16) DEFAULT NULL, + `server_name` varchar(64) DEFAULT NULL +) ENGINE=PINBA DEFAULT CHARSET=latin1 COMMENT='report6'; + +DROP TABLE IF EXISTS report_by_hostname_server_and_script; + +CREATE TABLE `report_by_hostname_server_and_script` ( + `req_count` int(11) DEFAULT NULL, + `req_per_sec` float DEFAULT NULL, + `req_time_total` float DEFAULT NULL, + `req_time_percent` float DEFAULT NULL, + `req_time_per_sec` float DEFAULT NULL, + `ru_utime_total` float DEFAULT NULL, + `ru_utime_percent` float DEFAULT NULL, + `ru_utime_per_sec` float DEFAULT NULL, + `ru_stime_total` float DEFAULT NULL, + `ru_stime_percent` float DEFAULT NULL, + `ru_stime_per_sec` float DEFAULT NULL, + `traffic_total` float DEFAULT NULL, + `traffic_percent` float DEFAULT NULL, + `traffic_per_sec` float DEFAULT NULL, + `hostname` varchar(16) DEFAULT NULL, + `server_name` varchar(64) DEFAULT NULL, + `script_name` varchar(128) DEFAULT NULL +) ENGINE=PINBA DEFAULT CHARSET=latin1 COMMENT='report7'; + diff --git a/pinba.proto b/pinba.proto new file mode 100644 index 0000000..f6557b7 --- /dev/null +++ b/pinba.proto @@ -0,0 +1,21 @@ +package Pinba; +option optimize_for = SPEED; + +message Request { + required string hostname = 1; + required string server_name = 2; + required string script_name = 3; + required uint32 request_count = 4; + required uint32 document_size = 5; + required uint32 memory_peak = 6; + required float request_time = 7; + required float ru_utime = 8; + required float ru_stime = 9; + + repeated uint32 timer_hit_count = 10; + repeated float timer_value = 11; + repeated uint32 timer_tag_count = 12; + repeated uint32 timer_tag_name = 13; + repeated uint32 timer_tag_value = 14; + repeated string dictionary = 15; +} diff --git a/protobuf.supp b/protobuf.supp new file mode 100644 index 0000000..2450c52 --- /dev/null +++ b/protobuf.supp @@ -0,0 +1,7 @@ +{ + GoogleProtobuf: global descriptors leak + Memcheck:Leak + fun:_Znwm + fun:*AssignGlobalDescriptors* + obj:/*/libprotobuf.so* +} diff --git a/src/.gitignore b/src/.gitignore new file mode 100644 index 0000000..df240c0 --- /dev/null +++ b/src/.gitignore @@ -0,0 +1,19 @@ +config.h* +pinba_config.h* +Makefile +Makefile.in +stamp-h1 +*.a +*.so +*.o +*.lo +*.la +libs +.deps +.libs +_libs +include +confdefs.h +*.gcda +*.gcno +test diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..a4e4dbb --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,12 @@ +# Used to build Makefile.in + +EXTRA_DIST = ha_pinba.h pinba.h pinba_types.h pinba-pb.h + +INCLUDES = $(MYSQL_INC) $(DEPS_CFLAGS) + +noinst_HEADERS = ha_pinba.h pinba.h pinba_types.h pinba-pb.h + +lib_LTLIBRARIES = libpinba_engine.la +libpinba_engine_la_SOURCES = pinba-pb.cc ha_pinba.cc data.cc tags.cc pool.cc main.cc +libpinba_engine_la_LIBADD = $(DEPS_LIBS) +libpinba_engine_la_LDFLAGS = -module diff --git a/src/data.cc b/src/data.cc new file mode 100644 index 0000000..3e11666 --- /dev/null +++ b/src/data.cc @@ -0,0 +1,1436 @@ +/* Copyright (c) 2007-2009 Antony Dovgal + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* $Id: data.cc,v 1.1.2.9 2009/04/16 11:53:34 tony Exp $ */ + +#include "pinba.h" +#include +using namespace std; + +static time_t last_warning = 0; + +static inline void pinba_update_report_info_add(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + report->time_total += timeval_to_float(record->data.req_time); + report->ru_utime_total += timeval_to_float(record->data.ru_utime); + report->ru_stime_total += timeval_to_float(record->data.ru_stime); + report->kbytes_total += record->data.doc_size; + report->results_cnt++; +} +/* }}} */ + +static inline void pinba_update_report_info_delete(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + if (UNLIKELY(report->results_cnt == 0)) { + return; + } + + report->time_total -= timeval_to_float(record->data.req_time); + report->ru_utime_total -= timeval_to_float(record->data.ru_utime); + report->ru_stime_total -= timeval_to_float(record->data.ru_stime); + report->kbytes_total -= record->data.doc_size; + report->results_cnt--; + + if (UNLIKELY(report->results_cnt == 0)) { + report->time_total = 0; + report->ru_utime_total = 0; + report->ru_stime_total = 0; + report->kbytes_total = 0; + } +} +/* }}} */ + +static inline void pinba_update_report1_add(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_report1_data *data; + PPvoid_t ppvalue; + + report->time_total += timeval_to_float(record->data.req_time); + report->ru_utime_total += timeval_to_float(record->data.ru_utime); + report->ru_stime_total += timeval_to_float(record->data.ru_stime); + report->kbytes_total += record->data.doc_size; + + ppvalue = JudySLGet(report->results, (uint8_t *)record->data.script_name, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, insert */ + ppvalue = JudySLIns(&report->results, (uint8_t *)record->data.script_name, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return; + } + data = (struct pinba_report1_data *)malloc(sizeof(struct pinba_report1_data)); + + data->req_count = 1; + data->req_time_total = timeval_to_float(record->data.req_time); + data->ru_utime_total = timeval_to_float(record->data.ru_utime); + data->ru_stime_total = timeval_to_float(record->data.ru_stime); + data->kbytes_total = record->data.doc_size; + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_report1_data *)*ppvalue; + data->req_count++; + data->req_time_total += timeval_to_float(record->data.req_time); + data->ru_utime_total += timeval_to_float(record->data.ru_utime); + data->ru_stime_total += timeval_to_float(record->data.ru_stime); + data->kbytes_total += record->data.doc_size; + } +} +/* }}} */ + +static inline void pinba_update_report1_delete(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_report1_data *data; + PPvoid_t ppvalue; + + if (report->results_cnt == 0) { + return; + } + + report->time_total -= timeval_to_float(record->data.req_time); + report->ru_utime_total -= timeval_to_float(record->data.ru_utime); + report->ru_stime_total -= timeval_to_float(record->data.ru_stime); + report->kbytes_total -= record->data.doc_size; + + ppvalue = JudySLGet(report->results, (uint8_t *)record->data.script_name, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, mmm?? */ + return; + } else { + data = (struct pinba_report1_data *)*ppvalue; + if (UNLIKELY(data->req_count == 1)) { + free(data); + JudySLDel(&report->results, (uint8_t *)record->data.script_name, NULL); + report->results_cnt--; + } else { + data->req_count--; + data->req_time_total -= timeval_to_float(record->data.req_time); + data->ru_utime_total -= timeval_to_float(record->data.ru_utime); + data->ru_stime_total -= timeval_to_float(record->data.ru_stime); + data->kbytes_total -= record->data.doc_size; + } + } +} +/* }}} */ + +static inline void pinba_update_report2_add(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_report2_data *data; + PPvoid_t ppvalue; + + report->time_total += timeval_to_float(record->data.req_time); + report->ru_utime_total += timeval_to_float(record->data.ru_utime); + report->ru_stime_total += timeval_to_float(record->data.ru_stime); + report->kbytes_total += record->data.doc_size; + + ppvalue = JudySLGet(report->results, (uint8_t *)record->data.server_name, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, insert */ + ppvalue = JudySLIns(&report->results, (uint8_t *)record->data.server_name, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return; + } + data = (struct pinba_report2_data *)malloc(sizeof(struct pinba_report2_data)); + + data->req_count = 1; + data->req_time_total = timeval_to_float(record->data.req_time); + data->ru_utime_total = timeval_to_float(record->data.ru_utime); + data->ru_stime_total = timeval_to_float(record->data.ru_stime); + data->kbytes_total = record->data.doc_size; + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_report2_data *)*ppvalue; + data->req_count++; + data->req_time_total += timeval_to_float(record->data.req_time); + data->ru_utime_total += timeval_to_float(record->data.ru_utime); + data->ru_stime_total += timeval_to_float(record->data.ru_stime); + data->kbytes_total += record->data.doc_size; + } +} +/* }}} */ + +static inline void pinba_update_report2_delete(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_report2_data *data; + PPvoid_t ppvalue; + + if (report->results_cnt == 0) { + return; + } + + report->time_total -= timeval_to_float(record->data.req_time); + report->ru_utime_total -= timeval_to_float(record->data.ru_utime); + report->ru_stime_total -= timeval_to_float(record->data.ru_stime); + report->kbytes_total -= record->data.doc_size; + + ppvalue = JudySLGet(report->results, (uint8_t *)record->data.server_name, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, mmm?? */ + return; + } else { + data = (struct pinba_report2_data *)*ppvalue; + if (UNLIKELY(data->req_count == 1)) { + free(data); + JudySLDel(&report->results, (uint8_t *)record->data.server_name, NULL); + report->results_cnt--; + } else { + data->req_count--; + data->req_time_total -= timeval_to_float(record->data.req_time); + data->ru_utime_total -= timeval_to_float(record->data.ru_utime); + data->ru_stime_total -= timeval_to_float(record->data.ru_stime); + data->kbytes_total -= record->data.doc_size; + } + } +} +/* }}} */ + +static inline void pinba_update_report3_add(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_report3_data *data; + PPvoid_t ppvalue; + + report->time_total += timeval_to_float(record->data.req_time); + report->ru_utime_total += timeval_to_float(record->data.ru_utime); + report->ru_stime_total += timeval_to_float(record->data.ru_stime); + report->kbytes_total += record->data.doc_size; + + ppvalue = JudySLGet(report->results, (uint8_t *)record->data.hostname, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, insert */ + ppvalue = JudySLIns(&report->results, (uint8_t *)record->data.hostname, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return; + } + data = (struct pinba_report3_data *)malloc(sizeof(struct pinba_report3_data)); + + data->req_count = 1; + data->req_time_total = timeval_to_float(record->data.req_time); + data->ru_utime_total = timeval_to_float(record->data.ru_utime); + data->ru_stime_total = timeval_to_float(record->data.ru_stime); + data->kbytes_total = record->data.doc_size; + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_report3_data *)*ppvalue; + data->req_count++; + data->req_time_total += timeval_to_float(record->data.req_time); + data->ru_utime_total += timeval_to_float(record->data.ru_utime); + data->ru_stime_total += timeval_to_float(record->data.ru_stime); + data->kbytes_total += record->data.doc_size; + } +} +/* }}} */ + +static inline void pinba_update_report3_delete(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_report3_data *data; + PPvoid_t ppvalue; + + if (report->results_cnt == 0) { + return; + } + + report->time_total -= timeval_to_float(record->data.req_time); + report->ru_utime_total -= timeval_to_float(record->data.ru_utime); + report->ru_stime_total -= timeval_to_float(record->data.ru_stime); + report->kbytes_total -= record->data.doc_size; + + ppvalue = JudySLGet(report->results, (uint8_t *)record->data.hostname, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, mmm?? */ + return; + } else { + data = (struct pinba_report3_data *)*ppvalue; + if (UNLIKELY(data->req_count == 1)) { + free(data); + JudySLDel(&report->results, (uint8_t *)record->data.hostname, NULL); + report->results_cnt--; + } else { + data->req_count--; + data->req_time_total -= timeval_to_float(record->data.req_time); + data->ru_utime_total -= timeval_to_float(record->data.ru_utime); + data->ru_stime_total -= timeval_to_float(record->data.ru_stime); + data->kbytes_total -= record->data.doc_size; + } + } +} +/* }}} */ + +static inline void pinba_update_report4_add(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + uint8_t index[PINBA_SERVER_NAME_SIZE + PINBA_SCRIPT_NAME_SIZE + 1] = {0}; + struct pinba_report4_data *data; + PPvoid_t ppvalue; + int index_len, dummy; + + report->time_total += timeval_to_float(record->data.req_time); + report->ru_utime_total += timeval_to_float(record->data.ru_utime); + report->ru_stime_total += timeval_to_float(record->data.ru_stime); + report->kbytes_total += record->data.doc_size; + + memcpy_static(index, record->data.server_name, record->data.server_name_len, index_len); + memcat_static(index, index_len, "/", 1, index_len); + memcat_static(index, index_len, record->data.script_name, record->data.script_name_len, index_len); + + ppvalue = JudySLGet(report->results, index, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, insert */ + ppvalue = JudySLIns(&report->results, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return; + } + data = (struct pinba_report4_data *)malloc(sizeof(struct pinba_report4_data)); + + data->req_count = 1; + data->req_time_total = timeval_to_float(record->data.req_time); + data->ru_utime_total = timeval_to_float(record->data.ru_utime); + data->ru_stime_total = timeval_to_float(record->data.ru_stime); + data->kbytes_total = record->data.doc_size; + memcpy_static(data->server_name, record->data.server_name, record->data.server_name_len, dummy); + memcpy_static(data->script_name, record->data.script_name, record->data.script_name_len, dummy); + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_report4_data *)*ppvalue; + data->req_count++; + data->req_time_total += timeval_to_float(record->data.req_time); + data->ru_utime_total += timeval_to_float(record->data.ru_utime); + data->ru_stime_total += timeval_to_float(record->data.ru_stime); + data->kbytes_total += record->data.doc_size; + } +} +/* }}} */ + +static inline void pinba_update_report4_delete(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + uint8_t index[PINBA_SERVER_NAME_SIZE + PINBA_SCRIPT_NAME_SIZE + 1] = {0}; + struct pinba_report4_data *data; + PPvoid_t ppvalue; + int index_len; + + if (report->results_cnt == 0) { + return; + } + + report->time_total -= timeval_to_float(record->data.req_time); + report->ru_utime_total -= timeval_to_float(record->data.ru_utime); + report->ru_stime_total -= timeval_to_float(record->data.ru_stime); + report->kbytes_total -= record->data.doc_size; + + memcpy_static(index, record->data.server_name, record->data.server_name_len, index_len); + memcat_static(index, index_len, "/", 1, index_len); + memcat_static(index, index_len, record->data.script_name, record->data.script_name_len, index_len); + + ppvalue = JudySLGet(report->results, index, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, mmm?? */ + return; + } else { + data = (struct pinba_report4_data *)*ppvalue; + if (UNLIKELY(data->req_count == 1)) { + free(data); + JudySLDel(&report->results, index, NULL); + report->results_cnt--; + } else { + data->req_count--; + data->req_time_total -= timeval_to_float(record->data.req_time); + data->ru_utime_total -= timeval_to_float(record->data.ru_utime); + data->ru_stime_total -= timeval_to_float(record->data.ru_stime); + data->kbytes_total -= record->data.doc_size; + } + } +} +/* }}} */ + +static inline void pinba_update_report5_add(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + uint8_t index[PINBA_HOSTNAME_SIZE + PINBA_SCRIPT_NAME_SIZE + 1] = {0}; + struct pinba_report5_data *data; + PPvoid_t ppvalue; + int index_len, dummy; + + report->time_total += timeval_to_float(record->data.req_time); + report->ru_utime_total += timeval_to_float(record->data.ru_utime); + report->ru_stime_total += timeval_to_float(record->data.ru_stime); + report->kbytes_total += record->data.doc_size; + + memcpy_static(index, record->data.hostname, record->data.hostname_len, index_len); + memcat_static(index, index_len, ":", 1, index_len); + memcat_static(index, index_len, record->data.script_name, record->data.script_name_len, index_len); + + ppvalue = JudySLGet(report->results, index, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, insert */ + ppvalue = JudySLIns(&report->results, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return; + } + data = (struct pinba_report5_data *)malloc(sizeof(struct pinba_report5_data)); + + data->req_count = 1; + data->req_time_total = timeval_to_float(record->data.req_time); + data->ru_utime_total = timeval_to_float(record->data.ru_utime); + data->ru_stime_total = timeval_to_float(record->data.ru_stime); + data->kbytes_total = record->data.doc_size; + memcpy_static(data->hostname, record->data.hostname, record->data.hostname_len, dummy); + memcpy_static(data->script_name, record->data.script_name, record->data.script_name_len, dummy); + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_report5_data *)*ppvalue; + data->req_count++; + data->req_time_total += timeval_to_float(record->data.req_time); + data->ru_utime_total += timeval_to_float(record->data.ru_utime); + data->ru_stime_total += timeval_to_float(record->data.ru_stime); + data->kbytes_total += record->data.doc_size; + } +} +/* }}} */ + +static inline void pinba_update_report5_delete(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + uint8_t index[PINBA_HOSTNAME_SIZE + PINBA_SCRIPT_NAME_SIZE + 1] = {0}; + struct pinba_report5_data *data; + PPvoid_t ppvalue; + int index_len; + + if (report->results_cnt == 0) { + return; + } + + report->time_total -= timeval_to_float(record->data.req_time); + report->ru_utime_total -= timeval_to_float(record->data.ru_utime); + report->ru_stime_total -= timeval_to_float(record->data.ru_stime); + report->kbytes_total -= record->data.doc_size; + + memcpy_static(index, record->data.hostname, record->data.hostname_len, index_len); + memcat_static(index, index_len, ":", 1, index_len); + memcat_static(index, index_len, record->data.script_name, record->data.script_name_len, index_len); + + ppvalue = JudySLGet(report->results, index, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, mmm?? */ + return; + } else { + data = (struct pinba_report5_data *)*ppvalue; + if (UNLIKELY(data->req_count == 1)) { + free(data); + JudySLDel(&report->results, index, NULL); + report->results_cnt--; + } else { + data->req_count--; + data->req_time_total -= timeval_to_float(record->data.req_time); + data->ru_utime_total -= timeval_to_float(record->data.ru_utime); + data->ru_stime_total -= timeval_to_float(record->data.ru_stime); + data->kbytes_total -= record->data.doc_size; + } + } +} +/* }}} */ + +static inline void pinba_update_report6_add(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + uint8_t index[PINBA_HOSTNAME_SIZE + PINBA_SERVER_NAME_SIZE + 1] = {0}; + struct pinba_report6_data *data; + PPvoid_t ppvalue; + int index_len, dummy; + + report->time_total += timeval_to_float(record->data.req_time); + report->ru_utime_total += timeval_to_float(record->data.ru_utime); + report->ru_stime_total += timeval_to_float(record->data.ru_stime); + report->kbytes_total += record->data.doc_size; + + memcpy_static(index, record->data.hostname, record->data.hostname_len, index_len); + memcat_static(index, index_len, "/", 1, index_len); + memcat_static(index, index_len, record->data.server_name, record->data.server_name_len, index_len); + + ppvalue = JudySLGet(report->results, index, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, insert */ + ppvalue = JudySLIns(&report->results, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return; + } + data = (struct pinba_report6_data *)malloc(sizeof(struct pinba_report6_data)); + + data->req_count = 1; + data->req_time_total = timeval_to_float(record->data.req_time); + data->ru_utime_total = timeval_to_float(record->data.ru_utime); + data->ru_stime_total = timeval_to_float(record->data.ru_stime); + data->kbytes_total = record->data.doc_size; + + memcpy_static(data->hostname, record->data.hostname, record->data.hostname_len, dummy); + memcpy_static(data->server_name, record->data.server_name, record->data.server_name_len, dummy); + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_report6_data *)*ppvalue; + data->req_count++; + data->req_time_total += timeval_to_float(record->data.req_time); + data->ru_utime_total += timeval_to_float(record->data.ru_utime); + data->ru_stime_total += timeval_to_float(record->data.ru_stime); + data->kbytes_total += record->data.doc_size; + } +} +/* }}} */ + +static inline void pinba_update_report6_delete(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + uint8_t index[PINBA_HOSTNAME_SIZE + PINBA_SERVER_NAME_SIZE + 1] = {0}; + struct pinba_report6_data *data; + PPvoid_t ppvalue; + int index_len; + + if (report->results_cnt == 0) { + return; + } + + report->time_total -= timeval_to_float(record->data.req_time); + report->ru_utime_total -= timeval_to_float(record->data.ru_utime); + report->ru_stime_total -= timeval_to_float(record->data.ru_stime); + report->kbytes_total -= record->data.doc_size; + + memcpy_static(index, record->data.hostname, record->data.hostname_len, index_len); + memcat_static(index, index_len, "/", 1, index_len); + memcat_static(index, index_len, record->data.server_name, record->data.server_name_len, index_len); + + ppvalue = JudySLGet(report->results, index, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, mmm?? */ + return; + } else { + data = (struct pinba_report6_data *)*ppvalue; + if (UNLIKELY(data->req_count == 1)) { + free(data); + JudySLDel(&report->results, index, NULL); + report->results_cnt--; + } else { + data->req_count--; + data->req_time_total -= timeval_to_float(record->data.req_time); + data->ru_utime_total -= timeval_to_float(record->data.ru_utime); + data->ru_stime_total -= timeval_to_float(record->data.ru_stime); + data->kbytes_total -= record->data.doc_size; + } + } +} +/* }}} */ + +static inline void pinba_update_report7_add(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + uint8_t index[PINBA_HOSTNAME_SIZE + 1 + PINBA_SERVER_NAME_SIZE + 1 + PINBA_SCRIPT_NAME_SIZE] = {0}; + struct pinba_report7_data *data; + PPvoid_t ppvalue; + int index_len, dummy; + + report->time_total += timeval_to_float(record->data.req_time); + report->ru_utime_total += timeval_to_float(record->data.ru_utime); + report->ru_stime_total += timeval_to_float(record->data.ru_stime); + report->kbytes_total += record->data.doc_size; + + memcpy_static(index, record->data.hostname, record->data.hostname_len, index_len); + memcat_static(index, index_len, ":", 1, index_len); + memcat_static(index, index_len, record->data.server_name, record->data.server_name_len, index_len); + memcat_static(index, index_len, "/", 1, index_len); + memcat_static(index, index_len, record->data.script_name, record->data.script_name_len, index_len); + + ppvalue = JudySLGet(report->results, index, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, insert */ + ppvalue = JudySLIns(&report->results, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return; + } + data = (struct pinba_report7_data *)malloc(sizeof(struct pinba_report7_data)); + + data->req_count = 1; + data->req_time_total = timeval_to_float(record->data.req_time); + data->ru_utime_total = timeval_to_float(record->data.ru_utime); + data->ru_stime_total = timeval_to_float(record->data.ru_stime); + data->kbytes_total = record->data.doc_size; + + memcpy_static(data->hostname, record->data.hostname, record->data.hostname_len, dummy); + memcpy_static(data->server_name, record->data.server_name, record->data.server_name_len, dummy); + memcpy_static(data->script_name, record->data.script_name, record->data.script_name_len, dummy); + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_report7_data *)*ppvalue; + data->req_count++; + data->req_time_total += timeval_to_float(record->data.req_time); + data->ru_utime_total += timeval_to_float(record->data.ru_utime); + data->ru_stime_total += timeval_to_float(record->data.ru_stime); + data->kbytes_total += record->data.doc_size; + } +} +/* }}} */ + +static inline void pinba_update_report7_delete(pinba_report *report, const pinba_stats_record *record) /* {{{ */ +{ + uint8_t index[PINBA_HOSTNAME_SIZE + 1 + PINBA_SERVER_NAME_SIZE + 1 + PINBA_SCRIPT_NAME_SIZE] = {0}; + struct pinba_report7_data *data; + PPvoid_t ppvalue; + int index_len; + + if (report->results_cnt == 0) { + return; + } + + report->time_total -= timeval_to_float(record->data.req_time); + report->ru_utime_total -= timeval_to_float(record->data.ru_utime); + report->ru_stime_total -= timeval_to_float(record->data.ru_stime); + report->kbytes_total -= record->data.doc_size; + + memcpy_static(index, record->data.hostname, record->data.hostname_len, index_len); + memcat_static(index, index_len, ":", 1, index_len); + memcat_static(index, index_len, record->data.server_name, record->data.server_name_len, index_len); + memcat_static(index, index_len, "/", 1, index_len); + memcat_static(index, index_len, record->data.script_name, record->data.script_name_len, index_len); + + ppvalue = JudySLGet(report->results, index, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* no such value, mmm?? */ + return; + } else { + data = (struct pinba_report7_data *)*ppvalue; + if (UNLIKELY(data->req_count == 1)) { + free(data); + JudySLDel(&report->results, index, NULL); + report->results_cnt--; + } else { + data->req_count--; + data->req_time_total -= timeval_to_float(record->data.req_time); + data->ru_utime_total -= timeval_to_float(record->data.ru_utime); + data->ru_stime_total -= timeval_to_float(record->data.ru_stime); + data->kbytes_total -= record->data.doc_size; + } + } +} +/* }}} */ + + +static inline void pinba_update_tag_info_add(int request_id, pinba_tag_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_tag_info_data *data; + PPvoid_t ppvalue; + pinba_timer_record *timer; + int i, j, tag_found; + pinba_word *word; + + for (i = 0; i < record->timers_cnt; i++) { + tag_found = 0; + timer = record->timers + i; + for (j = 0; j < timer->tag_num; j++) { + if (report->tag1_id == timer->tag_ids[j]) { + tag_found = 1; + break; + } + } + + if (!tag_found) { + continue; + } + + word = (pinba_word *)timer->tag_values[j]; + + ppvalue = JudySLGet(report->results, (uint8_t *)word->str, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + + ppvalue = JudySLIns(&report->results, (uint8_t *)word->str, NULL); + if (!ppvalue || ppvalue == PPJERR) { + continue; + } + + data = (struct pinba_tag_info_data *)malloc(sizeof(struct pinba_tag_info_data)); + if (!data) { + continue; + } + + data->req_count = 1; + data->hit_count = timer->hit_count; + data->timer_value = timer->value; + data->prev_add_request_id = request_id; + data->prev_del_request_id = -1; + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_tag_info_data *)*ppvalue; + data->hit_count += timer->hit_count; + timeradd(&data->timer_value, &timer->value, &data->timer_value); + } + + /* count tag values only once per request */ + if (request_id != data->prev_add_request_id) { + data->req_count++; + data->prev_add_request_id = request_id; + } + } +} +/* }}} */ + +static inline void pinba_update_tag_info_delete(int request_id, pinba_tag_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_tag_info_data *data; + PPvoid_t ppvalue; + pinba_timer_record *timer; + int i, j, tag_found; + pinba_word *word; + + for (i = 0; i < record->timers_cnt; i++) { + tag_found = 0; + timer = record->timers + i; + for (j = 0; j < timer->tag_num; j++) { + if (report->tag1_id == timer->tag_ids[j]) { + tag_found = 1; + break; + } + } + + if (!tag_found) { + continue; + } + + word = (pinba_word *)timer->tag_values[j]; + + ppvalue = JudySLGet(report->results, (uint8_t *)word->str, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + continue; + } else { + data = (struct pinba_tag_info_data *)*ppvalue; + if (UNLIKELY(data->req_count == 1)) { + free(data); + JudySLDel(&report->results, (uint8_t *)word->str, NULL); + report->results_cnt--; + continue; + } else { + data->hit_count -= timer->hit_count; + timersub(&data->timer_value, &timer->value, &data->timer_value); + } + } + + /* count tag values only once per request */ + if (request_id != data->prev_del_request_id) { + data->req_count--; + data->prev_del_request_id = request_id; + } + } +} +/* }}} */ + +static inline void pinba_update_tag2_info_add(int request_id, pinba_tag_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_tag2_info_data *data; + PPvoid_t ppvalue; + pinba_timer_record *timer; + int i, j, tag1_pos, tag2_pos, dummy; + pinba_word *word1, *word2; + int index_len; + uint8_t index_val[PINBA_TAG_VALUE_SIZE + 1 + PINBA_TAG_VALUE_SIZE]; + + for (i = 0; i < record->timers_cnt; i++) { + tag1_pos = -1; + tag2_pos = -1; + timer = record->timers + i; + for (j = 0; j < timer->tag_num; j++) { + if (report->tag1_id == timer->tag_ids[j]) { + tag1_pos = j; + continue; + } + if (report->tag2_id == timer->tag_ids[j]) { + tag2_pos = j; + continue; + } + } + + if (tag1_pos < 0 || tag2_pos < 0) { + continue; + } + + word1 = (pinba_word *)timer->tag_values[tag1_pos]; + word2 = (pinba_word *)timer->tag_values[tag2_pos]; + + memcpy_static(index_val, word1->str, word1->len, index_len); + memcat_static(index_val, index_len, "|", 1, index_len); + memcat_static(index_val, index_len, word2->str, word2->len, index_len); + + ppvalue = JudySLGet(report->results, index_val, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + + ppvalue = JudySLIns(&report->results, index_val, NULL); + if (!ppvalue || ppvalue == PPJERR) { + continue; + } + + data = (struct pinba_tag2_info_data *)malloc(sizeof(struct pinba_tag2_info_data)); + if (!data) { + continue; + } + + data->req_count = 1; + data->hit_count = timer->hit_count; + data->timer_value = timer->value; + data->prev_add_request_id = request_id; + data->prev_del_request_id = -1; + + memcpy_static(data->tag1_value, word1->str, word1->len, dummy); + memcpy_static(data->tag2_value, word2->str, word2->len, dummy); + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_tag2_info_data *)*ppvalue; + data->hit_count += timer->hit_count; + timeradd(&data->timer_value, &timer->value, &data->timer_value); + } + + /* count tag values only once per request */ + if (request_id != data->prev_add_request_id) { + data->req_count++; + data->prev_add_request_id = request_id; + } + } +} +/* }}} */ + +static inline void pinba_update_tag2_info_delete(int request_id, pinba_tag_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_tag2_info_data *data; + PPvoid_t ppvalue; + pinba_timer_record *timer; + int i, j, tag1_pos, tag2_pos; + int index_len; + pinba_word *word1, *word2; + uint8_t index_val[PINBA_TAG_VALUE_SIZE + 1 + PINBA_TAG_VALUE_SIZE]; + + for (i = 0; i < record->timers_cnt; i++) { + tag1_pos = -1; + tag2_pos = -1; + timer = record->timers + i; + for (j = 0; j < timer->tag_num; j++) { + if (report->tag1_id == timer->tag_ids[j]) { + tag1_pos = j; + continue; + } + if (report->tag2_id == timer->tag_ids[j]) { + tag2_pos = j; + continue; + } + } + + if (tag1_pos < 0 || tag2_pos < 0) { + continue; + } + + word1 = (pinba_word *)timer->tag_values[tag1_pos]; + word2 = (pinba_word *)timer->tag_values[tag2_pos]; + + memcpy_static(index_val, word1->str, word1->len, index_len); + memcat_static(index_val, index_len, "|", 1, index_len); + memcat_static(index_val, index_len, word2->str, word2->len, index_len); + + ppvalue = JudySLGet(report->results, index_val, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + continue; + } else { + data = (struct pinba_tag2_info_data *)*ppvalue; + if (UNLIKELY(data->req_count == 1)) { + free(data); + JudySLDel(&report->results, (uint8_t *)index_val, NULL); + report->results_cnt--; + continue; + } else { + data->hit_count -= timer->hit_count; + timersub(&data->timer_value, &timer->value, &data->timer_value); + } + } + + /* count tag values only once per request */ + if (request_id != data->prev_del_request_id) { + data->req_count--; + data->prev_del_request_id = request_id; + } + } +} +/* }}} */ + +static inline void pinba_update_tag_report_add(int request_id, pinba_tag_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_tag_report_data *data; + PPvoid_t ppvalue; + pinba_timer_record *timer; + int i, j, tag_found, index_len, dummy; + uint8_t index[PINBA_SCRIPT_NAME_SIZE + 1 + PINBA_TAG_VALUE_SIZE]; + pinba_word *word; + + for (i = 0; i < record->timers_cnt; i++) { + tag_found = 0; + timer = record->timers + i; + for (j = 0; j < timer->tag_num; j++) { + if (report->tag1_id == timer->tag_ids[j]) { + tag_found = 1; + break; + } + } + + if (!tag_found) { + continue; + } + + word = (pinba_word *)timer->tag_values[j]; + + memcpy_static(index, record->data.script_name, record->data.script_name_len, index_len); + memcat_static(index, index_len, "|", 1, index_len); + memcat_static(index, index_len, word->str, word->len, index_len); + + ppvalue = JudySLGet(report->results, index, NULL); + + if (ppvalue == PPJERR) { + pinba_debug("JudySLGet() failed"); + } + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + + ppvalue = JudySLIns(&report->results, index, NULL); + if (!ppvalue) { + continue; + } else if (ppvalue == PPJERR) { + pinba_debug("JudySLIns() failed"); + } + + data = (struct pinba_tag_report_data *)malloc(sizeof(struct pinba_tag_report_data)); + if (!data) { + continue; + } + + data->req_count = 1; + data->hit_count = timer->hit_count; + data->timer_value = timer->value; + data->prev_add_request_id = request_id; + data->prev_del_request_id = -1; + + memcpy_static(data->script_name, record->data.script_name, record->data.script_name_len, dummy); + memcpy_static(data->tag_value, word->str, word->len, dummy); + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_tag_report_data *)*ppvalue; + data->hit_count += timer->hit_count; + timeradd(&data->timer_value, &timer->value, &data->timer_value); + } + + /* count tag values only once per request */ + if (request_id != data->prev_add_request_id) { + data->req_count++; + data->prev_add_request_id = request_id; + } + } +} +/* }}} */ + +static inline void pinba_update_tag_report_delete(int request_id, pinba_tag_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_tag_report_data *data; + PPvoid_t ppvalue; + pinba_timer_record *timer; + int i, j, tag_found, index_len; + uint8_t index[PINBA_SCRIPT_NAME_SIZE + 1 + PINBA_TAG_VALUE_SIZE]; + pinba_word *word; + + for (i = 0; i < record->timers_cnt; i++) { + tag_found = 0; + timer = record->timers + i; + for (j = 0; j < timer->tag_num; j++) { + if (report->tag1_id == timer->tag_ids[j]) { + tag_found = 1; + break; + } + } + + if (!tag_found) { + continue; + } + + word = (pinba_word *)timer->tag_values[j]; + + memcpy_static(index, record->data.script_name, record->data.script_name_len, index_len); + memcat_static(index, index_len, "|", 1, index_len); + memcat_static(index, index_len, word->str, word->len, index_len); + + ppvalue = JudySLGet(report->results, index, NULL); + + if (ppvalue == PPJERR) { + pinba_debug("JudySLGet() failed"); + } + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + continue; + } else { + data = (struct pinba_tag_report_data *)*ppvalue; + if (UNLIKELY(data->req_count == 1)) { + free(data); + JudySLDel(&report->results, index, NULL); + report->results_cnt--; + continue; + } else { + data->hit_count -= timer->hit_count; + timersub(&data->timer_value, &timer->value, &data->timer_value); + } + } + + /* count tag values only once per request */ + if (request_id != data->prev_del_request_id) { + data->req_count--; + data->prev_del_request_id = request_id; + } + } +} +/* }}} */ + +static inline void pinba_update_tag2_report_add(int request_id, pinba_tag_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_tag2_report_data *data; + PPvoid_t ppvalue; + pinba_timer_record *timer; + int i, j, tag1_pos, tag2_pos, dummy; + int index_len; + uint8_t index_val[PINBA_SCRIPT_NAME_SIZE + 1 + PINBA_TAG_VALUE_SIZE + 1 + PINBA_TAG_VALUE_SIZE]; + pinba_word *word1, *word2; + + for (i = 0; i < record->timers_cnt; i++) { + tag1_pos = -1; + tag2_pos = -1; + timer = record->timers + i; + for (j = 0; j < timer->tag_num; j++) { + if (report->tag1_id == timer->tag_ids[j]) { + tag1_pos = j; + continue; + } + if (report->tag2_id == timer->tag_ids[j]) { + tag2_pos = j; + continue; + } + } + + if (tag1_pos < 0 || tag2_pos < 0) { + continue; + } + + word1 = (pinba_word *)timer->tag_values[tag1_pos]; + word2 = (pinba_word *)timer->tag_values[tag2_pos]; + + memcpy_static(index_val, record->data.script_name, record->data.script_name_len, index_len); + memcat_static(index_val, index_len, "|", 1, index_len); + memcat_static(index_val, index_len, word1->str, word1->len, index_len); + memcat_static(index_val, index_len, "|", 1, index_len); + memcat_static(index_val, index_len, word2->str, word2->len, index_len); + + ppvalue = JudySLGet(report->results, index_val, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + + ppvalue = JudySLIns(&report->results, index_val, NULL); + if (!ppvalue || ppvalue == PPJERR) { + continue; + } + + data = (struct pinba_tag2_report_data *)malloc(sizeof(struct pinba_tag2_report_data)); + if (!data) { + continue; + } + + data->req_count = 1; + data->hit_count = timer->hit_count; + data->timer_value = timer->value; + data->prev_add_request_id = request_id; + data->prev_del_request_id = -1; + + memcpy_static(data->script_name, record->data.script_name, record->data.script_name_len, dummy); + memcpy_static(data->tag1_value, word1->str, word1->len, dummy); + memcpy_static(data->tag2_value, word2->str, word2->len, dummy); + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_tag2_report_data *)*ppvalue; + data->hit_count += timer->hit_count; + timeradd(&data->timer_value, &timer->value, &data->timer_value); + } + + /* count tag values only once per request */ + if (request_id != data->prev_add_request_id) { + data->req_count++; + data->prev_add_request_id = request_id; + } + } +} +/* }}} */ + +static inline void pinba_update_tag2_report_delete(int request_id, pinba_tag_report *report, const pinba_stats_record *record) /* {{{ */ +{ + struct pinba_tag2_report_data *data; + PPvoid_t ppvalue; + pinba_timer_record *timer; + int i, j, tag1_pos, tag2_pos; + int index_len; + uint8_t index_val[PINBA_SCRIPT_NAME_SIZE + 1 + PINBA_TAG_VALUE_SIZE + 1 + PINBA_TAG_VALUE_SIZE]; + pinba_word *word1, *word2; + + for (i = 0; i < record->timers_cnt; i++) { + tag1_pos = -1; + tag2_pos = -1; + timer = record->timers + i; + for (j = 0; j < timer->tag_num; j++) { + if (report->tag1_id == timer->tag_ids[j]) { + tag1_pos = j; + continue; + } + if (report->tag2_id == timer->tag_ids[j]) { + tag2_pos = j; + continue; + } + } + + if (tag1_pos < 0 || tag2_pos < 0) { + continue; + } + + word1 = (pinba_word *)timer->tag_values[tag1_pos]; + word2 = (pinba_word *)timer->tag_values[tag2_pos]; + + memcpy_static(index_val, record->data.script_name, record->data.script_name_len, index_len); + memcat_static(index_val, index_len, "|", 1, index_len); + memcat_static(index_val, index_len, word1->str, word1->len, index_len); + memcat_static(index_val, index_len, "|", 1, index_len); + memcat_static(index_val, index_len, word2->str, word2->len, index_len); + + ppvalue = JudySLGet(report->results, index_val, NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + continue; + } else { + data = (struct pinba_tag2_report_data *)*ppvalue; + if (UNLIKELY(data->req_count == 1)) { + free(data); + JudySLDel(&report->results, (uint8_t *)index_val, NULL); + report->results_cnt--; + continue; + } else { + data->hit_count -= timer->hit_count; + timersub(&data->timer_value, &timer->value, &data->timer_value); + } + } + + /* count tag values only once per request */ + if (request_id != data->prev_del_request_id) { + data->req_count--; + data->prev_del_request_id = request_id; + } + } +} +/* }}} */ + + +void pinba_update_tag_reports_add(int request_id, const pinba_stats_record *record) /* {{{ */ +{ + uint8_t index[PINBA_MAX_LINE_LEN] = {0}; + pinba_tag_report *report; + PPvoid_t ppvalue; + + pthread_rwlock_rdlock(&D->tag_reports_lock); + for (ppvalue = JudySLFirst(D->tag_reports, index, NULL); ppvalue != NULL && ppvalue != PPJERR; ppvalue = JudySLNext(D->tag_reports, index, NULL)) { + report = (pinba_tag_report *)*ppvalue; + + pthread_rwlock_wrlock(&report->lock); + switch (report->type) { + case PINBA_TAG_REPORT_INFO: + pinba_update_tag_info_add(request_id, report, record); + break; + case PINBA_TAG2_REPORT_INFO: + pinba_update_tag2_info_add(request_id, report, record); + break; + case PINBA_TAG_REPORT: + pinba_update_tag_report_add(request_id, report, record); + break; + case PINBA_TAG2_REPORT: + pinba_update_tag2_report_add(request_id, report, record); + break; + default: + pinba_error(P_WARNING, "unknown report type '%d'!", report->type); + break; + } + pthread_rwlock_unlock(&report->lock); + } + pthread_rwlock_unlock(&D->tag_reports_lock); +} +/* }}} */ + +void pinba_update_tag_reports_delete(int request_id, const pinba_stats_record *record) /* {{{ */ +{ + uint8_t index[PINBA_MAX_LINE_LEN] = {0}; + pinba_tag_report *report; + PPvoid_t ppvalue; + + pthread_rwlock_rdlock(&D->tag_reports_lock); + for (ppvalue = JudySLFirst(D->tag_reports, index, NULL); ppvalue != NULL && ppvalue != PPJERR; ppvalue = JudySLNext(D->tag_reports, index, NULL)) { + report = (pinba_tag_report *)*ppvalue; + + pthread_rwlock_wrlock(&report->lock); + switch (report->type) { + case PINBA_TAG_REPORT_INFO: + pinba_update_tag_info_delete(request_id, report, record); + break; + case PINBA_TAG2_REPORT_INFO: + pinba_update_tag2_info_delete(request_id, report, record); + break; + case PINBA_TAG_REPORT: + pinba_update_tag_report_delete(request_id, report, record); + break; + case PINBA_TAG2_REPORT: + pinba_update_tag2_report_delete(request_id, report, record); + break; + default: + pinba_error(P_WARNING, "unknown report type '%d'!", report->type); + break; + } + pthread_rwlock_unlock(&report->lock); + } + pthread_rwlock_unlock(&D->tag_reports_lock); +} +/* }}} */ + +void pinba_update_reports_add(const pinba_stats_record *record) /* {{{ */ +{ + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT_INFO].lock); + pinba_update_report_info_add(&D->base_reports[PINBA_BASE_REPORT_INFO], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT_INFO].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT1].lock); + pinba_update_report1_add(&D->base_reports[PINBA_BASE_REPORT1], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT1].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT2].lock); + pinba_update_report2_add(&D->base_reports[PINBA_BASE_REPORT2], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT2].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT3].lock); + pinba_update_report3_add(&D->base_reports[PINBA_BASE_REPORT3], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT3].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT4].lock); + pinba_update_report4_add(&D->base_reports[PINBA_BASE_REPORT4], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT4].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT5].lock); + pinba_update_report5_add(&D->base_reports[PINBA_BASE_REPORT5], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT5].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT6].lock); + pinba_update_report6_add(&D->base_reports[PINBA_BASE_REPORT6], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT6].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT7].lock); + pinba_update_report7_add(&D->base_reports[PINBA_BASE_REPORT7], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT7].lock); +} +/* }}} */ + +void pinba_update_reports_delete(const pinba_stats_record *record) /* {{{ */ +{ + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT_INFO].lock); + pinba_update_report_info_delete(&D->base_reports[PINBA_BASE_REPORT_INFO], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT_INFO].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT1].lock); + pinba_update_report1_delete(&D->base_reports[PINBA_BASE_REPORT1], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT1].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT2].lock); + pinba_update_report2_delete(&D->base_reports[PINBA_BASE_REPORT2], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT2].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT3].lock); + pinba_update_report3_delete(&D->base_reports[PINBA_BASE_REPORT3], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT3].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT4].lock); + pinba_update_report4_delete(&D->base_reports[PINBA_BASE_REPORT4], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT4].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT5].lock); + pinba_update_report5_delete(&D->base_reports[PINBA_BASE_REPORT5], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT5].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT6].lock); + pinba_update_report6_delete(&D->base_reports[PINBA_BASE_REPORT6], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT6].lock); + + pthread_rwlock_wrlock(&D->base_reports[PINBA_BASE_REPORT7].lock); + pinba_update_report7_delete(&D->base_reports[PINBA_BASE_REPORT7], record); + pthread_rwlock_unlock(&D->base_reports[PINBA_BASE_REPORT7].lock); +} +/* }}} */ + +static inline void pinba_report_results_dtor(pinba_report *report) /* {{{ */ +{ + PPvoid_t ppvalue; + uint8_t index[PINBA_MAX_LINE_LEN] = {0}; + + for (ppvalue = JudySLFirst(report->results, index, NULL); ppvalue != NULL && ppvalue != PPJERR; ppvalue = JudySLNext(report->results, index, NULL)) { + free(*ppvalue); + } + JudySLFreeArray(&report->results, NULL); + report->results_cnt = 0; +} +/* }}} */ + +void pinba_reports_destroy() /* {{{ */ +{ + int i; + pinba_report *report; + + for (i = 0; i < PINBA_BASE_REPORT_LAST; i++) { + report = D->base_reports + i; + + pthread_rwlock_wrlock(&D->base_reports[i].lock); + if (report->results_cnt) { + pinba_report_results_dtor(report); + + report->time_interval = 0; + report->results_cnt = 0; + report->results = NULL; + report->time_total = 0; + report->kbytes_total = 0; + report->ru_utime_total = 0; + report->ru_stime_total = 0; + } + pthread_rwlock_unlock(&D->base_reports[i].lock); + } +} +/* }}} */ + +void pinba_tag_reports_destroy(int force) /* {{{ */ +{ + uint8_t index[PINBA_MAX_LINE_LEN] = {0}; + uint8_t sub_index[PINBA_MAX_LINE_LEN] = {0}; + pinba_tag_report *report; + PPvoid_t ppvalue, sub_ppvalue; + time_t now; + + now = time(NULL); + + pthread_rwlock_wrlock(&D->tag_reports_lock); + for (ppvalue = JudySLFirst(D->tag_reports, index, NULL); ppvalue != NULL && ppvalue != PPJERR; ppvalue = JudySLNext(D->tag_reports, index, NULL)) { + report = (pinba_tag_report *)*ppvalue; + + if (force || (D->settings.tag_report_timeout != -1 && (report->last_requested + D->settings.tag_report_timeout) < now)) { + sub_index[0] = 0; + + JudySLDel(&D->tag_reports, index, NULL); + + pthread_rwlock_wrlock(&report->lock); + for (sub_ppvalue = JudySLFirst(report->results, sub_index, NULL); sub_ppvalue != NULL && sub_ppvalue != PPJERR; sub_ppvalue = JudySLNext(report->results, sub_index, NULL)) { + free(*sub_ppvalue); + } + JudySLFreeArray(&report->results, NULL); + pthread_rwlock_unlock(&report->lock); + pthread_rwlock_destroy(&report->lock); + free(report); + } + } + pthread_rwlock_unlock(&D->tag_reports_lock); +} +/* }}} */ + +static int max_buf_len = 0; + +int pinba_process_stats_packet(const unsigned char *buf, int buf_len) /* {{{ */ +{ + time_t now; + bool res; + pinba_tmp_stats_record *tmp_record; + pinba_pool *temp_pool = &D->temp_pool; + string data ((char *)buf, buf_len); + +#ifdef PINBA_DEBUG + if (buf_len > max_buf_len) { + if (max_buf_len) { + pinba_debug("buffer length = %d", buf_len); + } + max_buf_len = buf_len; + } +#endif + + now = time(NULL); + + pthread_rwlock_wrlock(&D->temp_lock); + if (pinba_pool_is_full(temp_pool)) { /* got maximum */ + pthread_rwlock_unlock(&D->temp_lock); + if (now != last_warning) { + pinba_debug("failed to store stats packet - temporary pool is full"); + last_warning = now; + } + return P_FAILURE; + } + + tmp_record = TMP_POOL(temp_pool) + temp_pool->in; + res = tmp_record->request.ParseFromString(data); + + if (UNLIKELY(!res)) { + pthread_rwlock_unlock(&D->temp_lock); + return P_FAILURE; + } else { + tmp_record->time = now; + + if (UNLIKELY(temp_pool->in == (temp_pool->size - 1))) { + temp_pool->in = 0; + } else { + temp_pool->in++; + } + pthread_rwlock_unlock(&D->temp_lock); + return P_SUCCESS; + } +} +/* }}} */ + +/* + * vim600: sw=4 ts=4 fdm=marker + */ diff --git a/src/ha_pinba.cc b/src/ha_pinba.cc new file mode 100644 index 0000000..e75b712 --- /dev/null +++ b/src/ha_pinba.cc @@ -0,0 +1,3630 @@ +/* Copyright (c) 2007-2009 Antony Dovgal + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* $Id: ha_pinba.cc,v 1.66.4.2.2.16 2009/04/16 11:53:34 tony Exp $ */ + +#ifdef USE_PRAGMA_IMPLEMENTATION +#pragma implementation // gcc: Class implementation +#endif + +#include "pinba.h" + +#define MYSQL_SERVER 1 +#include +#include +#include +#include + +#include "ha_pinba.h" + +static pthread_t collector_thread; +static pthread_t stats_thread; + +/* Global variables */ +static int port_var = 0; +static char *address_var = NULL; +static int temp_pool_size_var = 0; +static int request_pool_size_var = 0; +static int stats_history_var = 0; +static int stats_gathering_period_var = 0; +static int tag_report_timeout_var = 0; +static my_bool show_protobuf_errors_var = 0; + +/* global daemon struct, created once per process and used everywhere */ +pinba_daemon *D; + +/* prototypes */ +static handler* pinba_create_handler(handlerton *hton, TABLE_SHARE *table, MEM_ROOT *mem_root); + +/* Variables for pinba share methods */ +static HASH pinba_open_tables; // Hash used to track open tables +pthread_mutex_t pinba_mutex; // This is the mutex we use to init the hash + +/* {{{ */ + +static inline unsigned char pinba_get_table_type(TABLE *table) /* {{{ */ +{ + char *str, *colon; + size_t len; + + if (!table->s || !table->s->comment.length || !table->s->comment.str) { + return PINBA_TABLE_UNKNOWN; + } + + len = table->s->comment.length; + str = table->s->comment.str; + + colon = strchr(str, ':'); + if (colon) { + /* ignore params */ + len = colon - str; + } + + switch(len) { + case 11: /* sizeof("tag2_report") */ + if (!memcmp(str, "tag2_report", len)) { + return PINBA_TABLE_TAG2_REPORT; + } + break; + case 10: /* sizeof("tag_report") - 1 */ + if (!memcmp(str, "tag_report", len)) { + return PINBA_TABLE_TAG_REPORT; + } + break; + case 9: /* sizeof("tag2_info") - 1 */ + if (!memcmp(str, "tag2_info", len)) { + return PINBA_TABLE_TAG2_INFO; + } + case 8: /* sizeof("timertag") - 1 */ + if (!memcmp(str, "timertag", len)) { + return PINBA_TABLE_TIMERTAG; + } + if (!memcmp(str, "tag_info", len)) { + return PINBA_TABLE_TAG_INFO; + } + break; + case 7: /* sizeof("request") - 1 */ + if (!memcmp(str, "request", len)) { + return PINBA_TABLE_REQUEST; + } + if (!memcmp(str, "report1", len)) { + return PINBA_TABLE_REPORT1; + } + if (!memcmp(str, "report2", len)) { + return PINBA_TABLE_REPORT2; + } + if (!memcmp(str, "report3", len)) { + return PINBA_TABLE_REPORT3; + } + if (!memcmp(str, "report4", len)) { + return PINBA_TABLE_REPORT4; + } + if (!memcmp(str, "report5", len)) { + return PINBA_TABLE_REPORT5; + } + if (!memcmp(str, "report6", len)) { + return PINBA_TABLE_REPORT6; + } + if (!memcmp(str, "report7", len)) { + return PINBA_TABLE_REPORT7; + } + break; + case 5: /* sizeof("timer") - 1 */ + if (!memcmp(str, "timer", len)) { + return PINBA_TABLE_TIMER; + } + break; + case 4: /* sizeof("info") - 1 */ + if (!memcmp(str, "info", len)) { + return PINBA_TABLE_INFO; + } + break; + case 3: /* sizeof("tag") - 1 */ + if (!memcmp(str, "tag", len)) { + return PINBA_TABLE_TAG; + } + } + return PINBA_TABLE_UNKNOWN; +} +/* }}} */ + +static inline int pinba_parse_params(TABLE *table, char ***params, int *param_num) /* {{{ */ +{ + char *str, *colon, *comma, *p; + size_t len; + int i, num = 0; + int parse_only = 0; + + if (params && param_num) { + *params = NULL; + *param_num = 0; + } else { + parse_only = 1; + } + + if (!table->s || !table->s->comment.length || !table->s->comment.str) { + return -1; + } + + len = table->s->comment.length; + str = table->s->comment.str; + + colon = strchr(str, ':'); + if (!colon) { + /* no params */ + return 0; + } + + colon++; /* skip the colon */ + if (colon[0] == '\0') { + /* colon was the last character */ + return -1; + } + + comma = strchr(colon, ','); + if (!comma) { + if (!parse_only) { + *params = (char **)realloc(*params, (num + 1) * sizeof(char *)); + (*params)[num] = strdup(colon); + } + num++; + } else { + p = colon; + do { + if ((comma - p) > 0) { + if (!parse_only) { + *params = (char **)realloc(*params, (num + 1) * sizeof(char *)); + (*params)[num] = strndup(p, comma - p); + } + p = comma + 1; + num++; + } else { + goto cleanup; + } + } + while ((comma = strchr(p, ',')) != NULL); + + if (!parse_only) { + *params = (char **)realloc(*params, (num + 1) * sizeof(char *)); + (*params)[num] = strdup(p); + } + num++; + } + + if (!parse_only) { + *param_num = num; + } + return num; +cleanup: + + if (!parse_only) { + for (i = 0; i < num; i++) { + free((*params)[i]); + } + *param_num = 0; + } + return -1; + +} +/* }}} */ + +static inline float pinba_round(float num, int prec_index) /* {{{ */ +{ + double fraction, integral; + long fraction_int; + + fraction = modf(num, &integral); + fraction_int = (long)(fraction*prec_index); + + num = (float)(integral + (double)fraction_int/prec_index); + return num; +} +/* }}} */ + +static unsigned char* pinba_get_key(PINBA_SHARE *share, size_t *length, my_bool not_used __attribute__((unused))) /* {{{ */ +{ + *length = share->table_name_length; + return (unsigned char*) share->table_name; +} +/* }}} */ + +static int pinba_engine_init(void *p) /* {{{ */ +{ + pinba_daemon_settings settings; + handlerton *pinba_hton = (handlerton *)p; + DBUG_ENTER("pinba_engine_init"); + + settings.stats_history = stats_history_var; + settings.stats_gathering_period = stats_gathering_period_var; + settings.request_pool_size = request_pool_size_var; + settings.temp_pool_size = temp_pool_size_var; + settings.tag_report_timeout = tag_report_timeout_var; + settings.show_protobuf_errors = (int)show_protobuf_errors_var; + settings.port = port_var; + settings.address = address_var; + + if (pinba_collector_init(settings) != P_SUCCESS) { + DBUG_RETURN(1); + } + + if (pthread_create(&collector_thread, NULL, pinba_collector_main, NULL)) { + pinba_collector_shutdown(); + DBUG_RETURN(1); + } + + if (pthread_create(&stats_thread, NULL, pinba_stats_main, NULL)) { + pthread_cancel(collector_thread); + pinba_collector_shutdown(); + DBUG_RETURN(1); + } + + VOID(pthread_mutex_init(&pinba_mutex, MY_MUTEX_INIT_FAST)); + (void) hash_init(&pinba_open_tables, system_charset_info, 32, 0, 0, (hash_get_key)pinba_get_key, 0, 0); + + pinba_hton->state = SHOW_OPTION_YES; + pinba_hton->create = pinba_create_handler; + + DBUG_RETURN(0); +} +/* }}} */ + +static int pinba_engine_shutdown(void *p) /* {{{ */ +{ + int error = 0; + DBUG_ENTER("pinba_engine_shutdown"); + + pthread_cancel(collector_thread); + pthread_join(collector_thread, NULL); + + pthread_cancel(stats_thread); + pthread_join(stats_thread, NULL); + + pinba_collector_shutdown(); + + if (pinba_open_tables.records) { + error = 1; + } + hash_free(&pinba_open_tables); + pthread_mutex_destroy(&pinba_mutex); + + DBUG_RETURN(0); +} +/* }}} */ + +static void netstr_to_key(const unsigned char *key, pinba_index_st *index) /* {{{ */ +{ + index->str.len = key[0]; + if (index->str.val) { + free(index->str.val); + } + if (index->str.len > 0) { + index->str.val = (unsigned char *)strdup((const char *)key+2); + } else { + index->str.val = NULL; + } +} +/* }}} */ + +static inline int pinba_get_time_interval() /* {{{ */ +{ + pinba_pool *p = &D->request_pool; + time_t start, end, res; + + start = REQ_POOL(p)[p->out].time; + if (p->in > 0) { + end = REQ_POOL(p)[p->in - 1].time; + } else { + end = start; + } + + res = end - start; + if (res <= 0) { + return 1; + } + return res; +} +/* }}} */ + +/* }}} */ + +/* {{{ */ + +/* tag reports */ +static inline pinba_tag_report *pinba_get_tag_report(int type, char *tag1, char *tag2) /* {{{ */ +{ + uint8_t index[64 + 1 + PINBA_TAG_VALUE_SIZE + 1 + PINBA_TAG_VALUE_SIZE]; + PPvoid_t ppvalue; + + if (tag2) { + sprintf((char *)index, "%d|%s|%s", type, tag1, tag2); + } else { + sprintf((char *)index, "%d|%s", type, tag1); + } + + ppvalue = JudySLGet(D->tag_reports, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return NULL; + } + return (pinba_tag_report *)*ppvalue; +} +/* }}} */ + +/* tag info */ +static inline pinba_tag_report *pinba_regenerate_tag_info(char *tag_name, int tag_name_len) /* {{{ */ +{ + PPvoid_t ppvalue; + pinba_tag_report *report; + int dummy, k; + pinba_timer_record *timer; + pinba_pool *p = &D->request_pool; + pinba_stats_record *record; + struct pinba_tag_info_data *data; + unsigned int i, j; + int tag_found; + pinba_word *word; + uint8_t index[64 + 1 + PINBA_TAG_VALUE_SIZE]; + + sprintf((char *)index, "%d|%s", PINBA_TAG_REPORT_INFO, tag_name); + ppvalue = JudySLGet(D->tag_reports, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + pinba_tag *tag; + + /* no such report */ + ppvalue = JudySLGet(D->tag.name_index, (uint8_t *)tag_name, NULL); + if (!ppvalue || ppvalue == PPJERR) { + /* no such tag! */ + return NULL; + } + + tag = (pinba_tag *)*ppvalue; + + report = (pinba_tag_report *)malloc(sizeof(pinba_tag_report)); + if (!report) { + return NULL; + } + + report->type = PINBA_TAG_REPORT_INFO; + report->time_interval = 0; + report->last_requested = 0; + report->results_cnt = 0; + report->results = NULL; + report->tag1_id = tag->id; + pthread_rwlock_init(&report->lock, 0); + + memcpy_static(report->tag1, tag_name, tag_name_len, dummy); + + pthread_rwlock_wrlock(&report->lock); + + ppvalue = JudySLIns(&D->tag_reports, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + pthread_rwlock_destroy(&report->lock); + free(report); + return NULL; + } + *ppvalue = report; + } else { + report = (pinba_tag_report *)*ppvalue; + pthread_rwlock_wrlock(&report->lock); + } + + if (D->settings.tag_report_timeout == -1 || report->last_requested == 0) { + /* really regenerate */ + } else { + pthread_rwlock_unlock(&report->lock); + return report; + } + + pool_traverse_forward(i, p) { + record = REQ_POOL(p) + i; + + if (!record->timers_cnt) { + continue; + } + + for (j = 0; j < record->timers_cnt; j++) { + tag_found = 0; + timer = record->timers + j; + + for (k = 0; k < timer->tag_num; k++) { + if (report->tag1_id == timer->tag_ids[k]) { + /* ok, timer has this tag */ + tag_found = 1; + break; + } + } + + if (!tag_found) { + /* tag not found in this timer */ + continue; + } + + word = (pinba_word *)timer->tag_values[k]; + ppvalue = JudySLGet(report->results, (uint8_t *)word->str, NULL); + + if (!ppvalue || ppvalue == PPJERR) { + + ppvalue = JudySLIns(&report->results, (uint8_t *)word->str, NULL); + if (!ppvalue || ppvalue == PPJERR) { + continue; + } + + data = (struct pinba_tag_info_data *)malloc(sizeof(struct pinba_tag_info_data)); + if (!data) { + continue; + } + + data->req_count = 1; + data->hit_count = timer->hit_count; + data->timer_value = timer->value; + data->prev_add_request_id = i; + data->prev_del_request_id = -1; + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_tag_info_data *)*ppvalue; + data->hit_count += timer->hit_count; + timeradd(&data->timer_value, &timer->value, &data->timer_value); + } + + /* count tag values only once per request */ + if ((int)i != data->prev_add_request_id) { + data->req_count++; + data->prev_add_request_id = i; + } + } + } + pthread_rwlock_unlock(&report->lock); + return report; +} +/* }}} */ + +/* tag2 info */ +static inline pinba_tag_report *pinba_regenerate_tag2_info(char *tag1_name, int tag1_name_len, char *tag2_name, int tag2_name_len) /* {{{ */ +{ + PPvoid_t ppvalue; + pinba_tag_report *report; + uint8_t index[64 + 1 + PINBA_TAG_NAME_SIZE + 1 + PINBA_TAG_NAME_SIZE]; + uint8_t index_val[PINBA_TAG_VALUE_SIZE + 1 + PINBA_TAG_VALUE_SIZE]; + int index_len, dummy, k; + pinba_timer_record *timer; + pinba_pool *p = &D->request_pool; + pinba_stats_record *record; + struct pinba_tag2_info_data *data; + unsigned int i, j; + int tag1_pos, tag2_pos; + pinba_word *word1, *word2; + + sprintf((char *)index, "%d|%s|%s", PINBA_TAG2_REPORT_INFO, tag1_name, tag2_name); + + ppvalue = JudySLGet(D->tag_reports, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + pinba_tag *tag1, *tag2; + + /* no such report */ + ppvalue = JudySLGet(D->tag.name_index, (uint8_t *)tag1_name, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return NULL; + } + + tag1 = (pinba_tag *)*ppvalue; + + ppvalue = JudySLGet(D->tag.name_index, (uint8_t *)tag2_name, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return NULL; + } + + tag2 = (pinba_tag *)*ppvalue; + + report = (pinba_tag_report *)malloc(sizeof(pinba_tag_report)); + if (!report) { + return NULL; + } + + + report->type = PINBA_TAG2_REPORT_INFO; + report->time_interval = 0; + report->last_requested = 0; + report->results_cnt = 0; + report->results = NULL; + report->tag1_id = tag1->id; + report->tag2_id = tag2->id; + pthread_rwlock_init(&report->lock, 0); + + pthread_rwlock_wrlock(&report->lock); + + memcpy_static(report->tag1, tag1_name, tag1_name_len, dummy); + memcpy_static(report->tag2, tag2_name, tag2_name_len, dummy); + + ppvalue = JudySLIns(&D->tag_reports, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + pthread_rwlock_destroy(&report->lock); + free(report); + return NULL; + } + + *ppvalue = report; + } else { + report = (pinba_tag_report *)*ppvalue; + pthread_rwlock_wrlock(&report->lock); + } + + if (D->settings.tag_report_timeout == -1 || report->last_requested == 0) { + /* really regenerate */ + } else { + pthread_rwlock_unlock(&report->lock); + return report; + } + + pool_traverse_forward(i, p) { + record = REQ_POOL(p) + i; + + if (!record->timers_cnt) { + continue; + } + + for (j = 0; j < record->timers_cnt; j++) { + tag1_pos = tag2_pos = -1; + timer = record->timers + j; + + for (k = 0; k < timer->tag_num; k++) { + if (report->tag1_id == timer->tag_ids[k]) { + /* ok, timer has this tag */ + tag1_pos = k; + continue; + } + if (report->tag2_id == timer->tag_ids[k]) { + /* ok, timer has this tag */ + tag2_pos = k; + continue; + } + } + + if (tag1_pos == -1 || tag2_pos == -1) { + continue; + } + + word1 = (pinba_word *)timer->tag_values[tag1_pos]; + word2 = (pinba_word *)timer->tag_values[tag2_pos]; + + memcpy_static(index_val, word1->str, word1->len, index_len); + memcat_static(index_val, index_len, "|", 1, index_len); + memcat_static(index_val, index_len, word2->str, word2->len, index_len); + + ppvalue = JudySLGet(report->results, index_val, NULL); + + if (!ppvalue || ppvalue == PPJERR) { + + ppvalue = JudySLIns(&report->results, index_val, NULL); + if (!ppvalue || ppvalue == PPJERR) { + continue; + } + + data = (struct pinba_tag2_info_data *)malloc(sizeof(struct pinba_tag2_info_data)); + if (!data) { + continue; + } + + data->req_count = 1; + data->hit_count = timer->hit_count; + data->timer_value = timer->value; + data->prev_add_request_id = i; + data->prev_del_request_id = -1; + + memcpy_static(data->tag1_value, word1->str, word1->len, dummy); + memcpy_static(data->tag2_value, word2->str, word2->len, dummy); + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_tag2_info_data *)*ppvalue; + data->hit_count += timer->hit_count; + timeradd(&data->timer_value, &timer->value, &data->timer_value); + } + + /* count tag values only once per request */ + if ((int)i != data->prev_add_request_id) { + data->req_count++; + data->prev_add_request_id = i; + } + } + } + pthread_rwlock_unlock(&report->lock); + return report; +} +/* }}} */ + +/* tag report */ +static inline pinba_tag_report *pinba_regenerate_tag_report(char *tag_name, int tag_name_len) /* {{{ */ +{ + PPvoid_t ppvalue; + pinba_tag_report *report; + int dummy, k; + pinba_timer_record *timer; + pinba_pool *p = &D->request_pool; + pinba_stats_record *record; + struct pinba_tag_report_data *data; + unsigned int i, j; + int tag_found, index_len; + uint8_t index[64 + 1 + PINBA_SCRIPT_NAME_SIZE + 1 + PINBA_TAG_VALUE_SIZE]; + pinba_word *word; + + sprintf((char *)index, "%d|%s", PINBA_TAG_REPORT, tag_name); + ppvalue = JudySLGet(D->tag_reports, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + pinba_tag *tag; + + /* no such report */ + ppvalue = JudySLGet(D->tag.name_index, (uint8_t *)tag_name, NULL); + if (!ppvalue || ppvalue == PPJERR) { + /* no such tag! */ + return NULL; + } + + tag = (pinba_tag *)*ppvalue; + + report = (pinba_tag_report *)malloc(sizeof(pinba_tag_report)); + if (!report) { + return NULL; + } + + report->type = PINBA_TAG_REPORT; + report->time_interval = 0; + report->last_requested = 0; + report->results_cnt = 0; + report->results = NULL; + report->tag1_id = tag->id; + pthread_rwlock_init(&report->lock, 0); + + memcpy_static(report->tag1, tag_name, tag_name_len, dummy); + + pthread_rwlock_wrlock(&report->lock); + + ppvalue = JudySLIns(&D->tag_reports, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + pthread_rwlock_destroy(&report->lock); + free(report); + return NULL; + } + + *ppvalue = report; + } else { + report = (pinba_tag_report *)*ppvalue; + pthread_rwlock_wrlock(&report->lock); + } + + if (D->settings.tag_report_timeout == -1 || report->last_requested == 0) { + /* really regenerate */ + } else { + pthread_rwlock_unlock(&report->lock); + return report; + } + + pool_traverse_forward(i, p) { + record = REQ_POOL(p) + i; + + if (!record->timers_cnt) { + continue; + } + + for (j = 0; j < record->timers_cnt; j++) { + tag_found = 0; + timer = record->timers + j; + + for (k = 0; k < timer->tag_num; k++) { + if (report->tag1_id == timer->tag_ids[k]) { + /* ok, timer has this tag */ + tag_found = 1; + break; + } + } + + if (!tag_found) { + /* tag not found in this timer */ + continue; + } + + word = (pinba_word *)timer->tag_values[k]; + + memcpy_static(index, record->data.script_name, record->data.script_name_len, index_len); + memcat_static(index, index_len, "|", 1, index_len); + memcat_static(index, index_len, word->str, word->len, index_len); + + ppvalue = JudySLGet(report->results, index, NULL); + + if (!ppvalue || ppvalue == PPJERR) { + + ppvalue = JudySLIns(&report->results, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + continue; + } + + data = (struct pinba_tag_report_data *)malloc(sizeof(struct pinba_tag_report_data)); + if (!data) { + continue; + } + + data->req_count = 1; + data->hit_count = timer->hit_count; + data->timer_value = timer->value; + data->prev_add_request_id = i; + data->prev_del_request_id = -1; + + memcpy_static(data->script_name, record->data.script_name, record->data.script_name_len, dummy); + memcpy_static(data->tag_value, word->str, word->len, dummy); + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_tag_report_data *)*ppvalue; + data->hit_count += timer->hit_count; + timeradd(&data->timer_value, &timer->value, &data->timer_value); + } + + /* count tag values only once per request */ + if ((int)i != data->prev_add_request_id) { + data->req_count++; + data->prev_add_request_id = i; + } + } + } + pthread_rwlock_unlock(&report->lock); + return report; +} +/* }}} */ + +/* tag2 report */ +static inline pinba_tag_report *pinba_regenerate_tag2_report(char *tag1_name, int tag1_name_len, char *tag2_name, int tag2_name_len) /* {{{ */ +{ + PPvoid_t ppvalue; + pinba_tag_report *report; + uint8_t index[64 + 1 + PINBA_TAG_NAME_SIZE + 1 + PINBA_TAG_NAME_SIZE]; + uint8_t index_val[PINBA_SCRIPT_NAME_SIZE + 1 + PINBA_TAG_VALUE_SIZE + 1 + PINBA_TAG_VALUE_SIZE]; + int index_len, dummy, k; + pinba_timer_record *timer; + pinba_pool *p = &D->request_pool; + pinba_stats_record *record; + struct pinba_tag2_report_data *data; + unsigned int i, j; + int tag1_pos, tag2_pos; + pinba_word *word1, *word2; + + sprintf((char *)index, "%d|%s|%s", PINBA_TAG2_REPORT, tag1_name, tag2_name); + + ppvalue = JudySLGet(D->tag_reports, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + pinba_tag *tag1, *tag2; + + /* no such report */ + ppvalue = JudySLGet(D->tag.name_index, (uint8_t *)tag1_name, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return NULL; + } + + tag1 = (pinba_tag *)*ppvalue; + + ppvalue = JudySLGet(D->tag.name_index, (uint8_t *)tag2_name, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return NULL; + } + + tag2 = (pinba_tag *)*ppvalue; + + report = (pinba_tag_report *)malloc(sizeof(pinba_tag_report)); + if (!report) { + return NULL; + } + + + report->type = PINBA_TAG2_REPORT; + report->time_interval = 0; + report->last_requested = 0; + report->results_cnt = 0; + report->results = NULL; + report->tag1_id = tag1->id; + report->tag2_id = tag2->id; + pthread_rwlock_init(&report->lock, 0); + + pthread_rwlock_wrlock(&report->lock); + + memcpy_static(report->tag1, tag1_name, tag1_name_len, dummy); + memcpy_static(report->tag2, tag2_name, tag2_name_len, dummy); + + ppvalue = JudySLIns(&D->tag_reports, index, NULL); + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + pthread_rwlock_destroy(&report->lock); + free(report); + return NULL; + } + + *ppvalue = report; + } else { + report = (pinba_tag_report *)*ppvalue; + pthread_rwlock_wrlock(&report->lock); + } + + if (D->settings.tag_report_timeout == -1 || report->last_requested == 0) { + /* really regenerate */ + } else { + pthread_rwlock_unlock(&report->lock); + return report; + } + + pool_traverse_forward(i, p) { + record = REQ_POOL(p) + i; + + if (!record->timers_cnt) { + continue; + } + + for (j = 0; j < record->timers_cnt; j++) { + tag1_pos = tag2_pos = -1; + timer = record->timers + j; + + for (k = 0; k < timer->tag_num; k++) { + if (report->tag1_id == timer->tag_ids[k]) { + /* ok, timer has this tag */ + tag1_pos = k; + continue; + } + if (report->tag2_id == timer->tag_ids[k]) { + /* ok, timer has this tag */ + tag2_pos = k; + continue; + } + } + + if (tag1_pos == -1 || tag2_pos == -1) { + continue; + } + + word1 = (pinba_word *)timer->tag_values[tag1_pos]; + word2 = (pinba_word *)timer->tag_values[tag2_pos]; + + memcpy_static(index_val, record->data.script_name, record->data.script_name_len, index_len); + memcat_static(index_val, index_len, "|", 1, index_len); + memcat_static(index_val, index_len, word1->str, word1->len, index_len); + memcat_static(index_val, index_len, "|", 1, index_len); + memcat_static(index_val, index_len, word2->str, word2->len, index_len); + + ppvalue = JudySLGet(report->results, index_val, NULL); + + if (!ppvalue || ppvalue == PPJERR) { + + ppvalue = JudySLIns(&report->results, index_val, NULL); + if (!ppvalue || ppvalue == PPJERR) { + continue; + } + + data = (struct pinba_tag2_report_data *)malloc(sizeof(struct pinba_tag2_report_data)); + if (!data) { + continue; + } + + data->req_count = 1; + data->hit_count = timer->hit_count; + data->timer_value = timer->value; + data->prev_add_request_id = i; + data->prev_del_request_id = -1; + + memcpy_static(data->script_name, record->data.script_name, record->data.script_name_len, dummy); + memcpy_static(data->tag1_value, word1->str, word1->len, dummy); + memcpy_static(data->tag2_value, word2->str, word2->len, dummy); + + *ppvalue = data; + report->results_cnt++; + } else { + data = (struct pinba_tag2_report_data *)*ppvalue; + data->hit_count += timer->hit_count; + timeradd(&data->timer_value, &timer->value, &data->timer_value); + } + + /* count tag values only once per request */ + if ((int)i != data->prev_add_request_id) { + data->req_count++; + data->prev_add_request_id = i; + } + } + } + pthread_rwlock_unlock(&report->lock); + return report; +} +/* }}} */ + +/* }}} */ + +/* {{{ */ + +static PINBA_SHARE *get_share(const char *table_name, TABLE *table) /* {{{ */ +{ + PINBA_SHARE *share; + uint length; + char *tmp_name; + char **params; + int param_num; + unsigned char type = PINBA_TABLE_UNKNOWN; + + pthread_mutex_lock(&pinba_mutex); + length = (uint)strlen(table_name); + + if (!(share = (PINBA_SHARE*)hash_search(&pinba_open_tables, (unsigned char*) table_name, length))) { + type = pinba_get_table_type(table); + if (type == PINBA_TABLE_UNKNOWN) { + pthread_mutex_unlock(&pinba_mutex); + return NULL; + } + + if (pinba_parse_params(table, ¶ms, ¶m_num) < 0) { + pthread_mutex_unlock(&pinba_mutex); + return NULL; + } + + if (!my_multi_malloc(MYF(MY_WME | MY_ZEROFILL), &share, sizeof(*share), &tmp_name, length+1, NullS)) { + pthread_mutex_unlock(&pinba_mutex); + return NULL; + } + + share->use_count = 0; + share->table_name_length = length; + share->table_name = tmp_name; + memcpy(share->table_name, table_name, length); + share->table_name[length] = '\0'; + share->table_type = type; + share->params = params; + share->params_num = param_num; + + if (my_hash_insert(&pinba_open_tables, (unsigned char*) share)) { + goto error; + } + thr_lock_init(&share->lock); + } + share->use_count++; + pthread_mutex_unlock(&pinba_mutex); + + return share; + +error: + pthread_mutex_unlock(&pinba_mutex); + my_free((unsigned char *) share, MYF(0)); + + return NULL; +} +/* }}} */ + +static int free_share(PINBA_SHARE *share) /* {{{ */ +{ + pthread_mutex_lock(&pinba_mutex); + if (!--share->use_count) { + if (share->params_num > 0) { + int i; + + for (i = 0; i < share->params_num; i++) { + free(share->params[i]); + } + + free(share->params); + share->params = NULL; + share->params_num = 0; + } + + hash_delete(&pinba_open_tables, (unsigned char*) share); + thr_lock_delete(&share->lock); + my_free((unsigned char *) share, MYF(0)); + } + pthread_mutex_unlock(&pinba_mutex); + + return 0; +} +/* }}} */ + +static handler* pinba_create_handler(handlerton *hton, TABLE_SHARE *table, MEM_ROOT *mem_root) /* {{{ */ +{ + return new (mem_root) ha_pinba(hton, table); +} +/* }}} */ + +ha_pinba::ha_pinba(handlerton *hton, TABLE_SHARE *table_arg) /* {{{ */ +:handler(hton, table_arg) +{ + rec_buff = NULL; + alloced_rec_buff_length = 0; +} +/* }}} */ + +/* {{{ file extensions (none) */ +static const char *ha_pinba_exts[] = { + NullS +}; + +const char **ha_pinba::bas_ext() const +{ + return ha_pinba_exts; +} +/* }}} */ + +int ha_pinba::open(const char *name, int mode, uint test_if_locked) /* {{{ */ +{ + DBUG_ENTER("ha_pinba::open"); + if (!(share = get_share(name, table))) { + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } + + thr_lock_data_init(&share->lock, &lock, NULL); + DBUG_RETURN(0); +} +/* }}} */ + +int ha_pinba::close(void) /* {{{ */ +{ + DBUG_ENTER("ha_pinba::close"); + DBUG_RETURN(free_share(share)); +} +/* }}} */ + +/* }}} */ + +/* {{{ */ + +int ha_pinba::index_init(uint keynr, bool sorted) /* {{{ */ +{ + DBUG_ENTER("ha_pinba::index_init"); + active_index = keynr; + + if (active_index < 0 || active_index >= PINBA_MAX_KEYS) { + DBUG_RETURN(HA_ERR_WRONG_INDEX); + } + + this_index[active_index].position = 0; + DBUG_RETURN(0); +} +/* }}} */ + +int ha_pinba::index_read(unsigned char *buf, const unsigned char *key, uint key_len, enum ha_rkey_function find_flag) /* {{{ */ +{ + DBUG_ENTER("ha_pinba::index_read"); + int ret; + + if (active_index < 0 || active_index >= PINBA_MAX_KEYS) { + DBUG_RETURN(HA_ERR_WRONG_INDEX); + } + + this_index[active_index].position = 0; + ret = read_row_by_key(buf, active_index, key, key_len, 1); + if (!ret) { + this_index[active_index].position++; + } + DBUG_RETURN(ret); +} +/* }}} */ + +int ha_pinba::index_next(unsigned char *buf) /* {{{ */ +{ + DBUG_ENTER("ha_pinba::index_next"); + int ret; + + if (active_index < 0 || active_index >= PINBA_MAX_KEYS) { + DBUG_RETURN(HA_ERR_WRONG_INDEX); + } + + ret = read_next_row(buf, active_index); + if (!ret) { + this_index[active_index].position++; + } + DBUG_RETURN(ret); +} +/* }}} */ + +int ha_pinba::index_prev(unsigned char *buf) /* {{{ */ +{ + DBUG_ENTER("ha_pinba::index_prev"); + int ret; + + if (active_index < 0 || active_index >= PINBA_MAX_KEYS) { + DBUG_RETURN(HA_ERR_WRONG_INDEX); + } + + ret = read_next_row(buf, active_index); + if (!ret) { + this_index[active_index].position--; + } + DBUG_RETURN(ret); +} +/* }}} */ + +int ha_pinba::index_first(unsigned char *buf) /* {{{ */ +{ + DBUG_ENTER("ha_pinba::index_first"); + int ret; + + if (active_index < 0 || active_index >= PINBA_MAX_KEYS) { + DBUG_RETURN(HA_ERR_WRONG_INDEX); + } + + this_index[active_index].position = 0; + ret = read_index_first(buf, active_index); + if (!ret) { + this_index[active_index].position++; + } + DBUG_RETURN(ret); +} +/* }}} */ + +/* }}} */ + +/* {{{ */ + +int ha_pinba::rnd_init(bool scan) /* {{{ */ +{ + int i; + pinba_pool *p = &D->request_pool; + pinba_pool *timer_pool = &D->timer_pool; + DBUG_ENTER("ha_pinba::rnd_init"); + + pthread_rwlock_rdlock(&D->collector_lock); + for (i = 0; i < PINBA_MAX_KEYS; i++) { + memset(&this_index[i], 0, sizeof(pinba_index_st)); + } + + if (share->table_type == PINBA_TABLE_REQUEST) { + this_index[0].ival = p->out; + this_index[0].position = p->out; + } else if (share->table_type == PINBA_TABLE_TIMERTAG || share->table_type == PINBA_TABLE_TIMER) { + this_index[0].ival = timer_pool->out; + this_index[0].position = 0; + } + + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(0); +} +/* }}} */ + +int ha_pinba::rnd_end() /* {{{ */ +{ + DBUG_ENTER("ha_pinba::rnd_end"); + + /* Theoretically the number of records in the report may have grown + when we were reading it, so we won't reach the end of the array and + the index value may leak. + Hence this check. */ + switch (share->table_type) { + case PINBA_TABLE_REPORT1: + case PINBA_TABLE_REPORT2: + case PINBA_TABLE_REPORT3: + case PINBA_TABLE_REPORT4: + case PINBA_TABLE_REPORT5: + case PINBA_TABLE_REPORT6: + case PINBA_TABLE_REPORT7: + case PINBA_TABLE_TAG_REPORT: + case PINBA_TABLE_TAG2_REPORT: + case PINBA_TABLE_TAG_INFO: + case PINBA_TABLE_TAG2_INFO: + if (this_index[0].str.val != NULL) { + free(this_index[0].str.val); + this_index[0].str.val = NULL; + } + break; + default: + break; + } + + DBUG_RETURN(0); +} +/* }}} */ + +int ha_pinba::rnd_next(unsigned char *buf) /* {{{ */ +{ + int ret; + + DBUG_ENTER("ha_pinba::rnd_next"); + + ret = read_next_row(buf, 0); + DBUG_RETURN(ret); +} +/* }}} */ + +int ha_pinba::rnd_pos(unsigned char * buf, unsigned char *pos) /* {{{ */ +{ + int ret; + unsigned int key_length; + DBUG_ENTER("ha_pinba::rnd_pos"); + + if (active_index < 0 || active_index >= PINBA_MAX_KEYS) { + DBUG_RETURN(HA_ERR_WRONG_INDEX); + } + + memcpy(&key_length, pos, sizeof(unsigned int)); + ret = read_row_by_key(buf, 0, pos + sizeof(unsigned int), key_length, 1); + if (!ret) { + this_index[active_index].position++; + } + DBUG_RETURN(ret); +} +/* }}} */ + +/*
}}} */ + +/* read wrappers for all table types */ +int ha_pinba::read_index_first(unsigned char *buf, uint active_index) /* {{{ */ +{ + DBUG_ENTER("ha_pinba::read_index_first"); + int ret = HA_ERR_INTERNAL_ERROR; + Word_t index_value = 0; + pinba_pool *p = &D->request_pool; + pinba_pool *timer_pool = &D->timer_pool; + + if (active_index < 0 || active_index >= PINBA_MAX_KEYS) { + DBUG_RETURN(HA_ERR_WRONG_INDEX); + } + + switch(share->table_type) { + case PINBA_TABLE_REQUEST: + if (active_index > 0) { + ret = HA_ERR_WRONG_INDEX; + goto failure; + } + + this_index[0].ival = p->out; + this_index[0].position = p->out; + + ret = requests_fetch_row(buf, this_index[0].ival, &(this_index[0].position)); + this_index[0].ival = this_index[0].position; + break; + case PINBA_TABLE_TIMER: + if (active_index == 0) { + + this_index[active_index].ival = timer_pool->out; + this_index[active_index].position = 0; + + ret = timers_fetch_row(buf, this_index[active_index].ival, &(this_index[active_index].ival), 1); + } else if (active_index == 1) { + this_index[active_index].ival = p->out; + this_index[active_index].position = 0; + ret = timers_fetch_row_by_request_id(buf, this_index[active_index].ival, &(this_index[active_index].ival)); + } else { + ret = HA_ERR_WRONG_INDEX; + goto failure; + } + break; + case PINBA_TABLE_TAG: + if (active_index == 0) { + PPvoid_t ppvalue; + + ppvalue = JudyLFirst(D->tag.table, &index_value, NULL); + if (!ppvalue) { + ret = HA_ERR_END_OF_FILE; + goto failure; + } + + this_index[active_index].ival = index_value; + this_index[active_index].position = 0; + + ret = tags_fetch_row(buf, this_index[active_index].ival, &(this_index[active_index].ival)); + } else if (active_index == 1) { + PPvoid_t ppvalue; + char name[PINBA_MAX_LINE_LEN] = {0}; + + ppvalue = JudySLFirst(D->tag.name_index, (uint8_t *)name, NULL); + if (!ppvalue) { + ret = HA_ERR_END_OF_FILE; + goto failure; + } + + this_index[active_index].str.len = strlen(name); + this_index[active_index].str.val = (unsigned char *)strdup(name); + ret = tags_fetch_row_by_name(buf, this_index[active_index].str.val, this_index[active_index].str.len); + } else { + ret = HA_ERR_WRONG_INDEX; + goto failure; + } + break; + case PINBA_TABLE_TIMERTAG: + if (active_index == 0) { + this_index[active_index].ival = timer_pool->out; + this_index[active_index].position = 0; + ret = tag_values_fetch_by_timer_id(buf); + } else { + ret = HA_ERR_WRONG_INDEX; + goto failure; + } + break; + default: + ret = HA_ERR_INTERNAL_ERROR; + goto failure; + } + +failure: + table->status = ret ? STATUS_NOT_FOUND : 0; + DBUG_RETURN(ret); +} +/* }}} */ + +int ha_pinba::read_row_by_key(unsigned char *buf, uint active_index, const unsigned char *key, uint key_len, int exact) /* {{{ */ +{ + DBUG_ENTER("ha_pinba::read_row_by_key"); + int ret = HA_ERR_INTERNAL_ERROR; + + if (active_index < 0 || active_index >= PINBA_MAX_KEYS) { + DBUG_RETURN(HA_ERR_WRONG_INDEX); + } + + switch(share->table_type) { + case PINBA_TABLE_REQUEST: + if (active_index > 0) { + ret = HA_ERR_WRONG_INDEX; + goto failure; + } + + memset(&(this_index[active_index].ival), 0, sizeof(this_index[active_index].ival)); + memcpy(&(this_index[active_index].ival), key, key_len); + ret = requests_fetch_row(buf, this_index[active_index].ival, NULL); + break; + case PINBA_TABLE_TIMER: + if (active_index == 0) { + memset(&(this_index[active_index].ival), 0, sizeof(this_index[active_index].ival)); + memcpy(&(this_index[active_index].ival), key, key_len); + ret = timers_fetch_row(buf, this_index[active_index].ival, NULL, exact); + } else if (active_index == 1) { + memset(&(this_index[active_index].ival), 0, sizeof(this_index[active_index].ival)); + memcpy(&(this_index[active_index].ival), key, key_len); + ret = timers_fetch_row_by_request_id(buf, this_index[active_index].ival, NULL); + } else { + ret = HA_ERR_WRONG_INDEX; + goto failure; + } + break; + case PINBA_TABLE_TAG: + if (active_index == 0) { + memset(&(this_index[active_index].ival), 0, sizeof(this_index[active_index].ival)); + memcpy(&(this_index[active_index].ival), key, key_len); + ret = tags_fetch_row(buf, this_index[active_index].ival, NULL); + } else if (active_index == 1) { + memset(&(this_index[active_index]), 0, sizeof(this_index[active_index])); + netstr_to_key(key, &this_index[active_index]); + ret = tags_fetch_row_by_name(buf, this_index[active_index].str.val, this_index[active_index].str.len); + free(this_index[active_index].str.val); + this_index[active_index].str.val = NULL; + } else { + ret = HA_ERR_WRONG_INDEX; + goto failure; + } + break; + case PINBA_TABLE_TIMERTAG: + if (active_index == 0) { + memset(&(this_index[active_index].ival), 0, sizeof(this_index[active_index].ival)); + memcpy(&(this_index[active_index].ival), key, key_len); + ret = tag_values_fetch_by_timer_id(buf); + } else { + ret = HA_ERR_WRONG_INDEX; + goto failure; + } + break; + default: + ret = HA_ERR_INTERNAL_ERROR; + goto failure; + } + +failure: + table->status = ret ? STATUS_NOT_FOUND : 0; + DBUG_RETURN(ret); +} +/* }}} */ + +int ha_pinba::read_next_row(unsigned char *buf, uint active_index) /* {{{ */ +{ + DBUG_ENTER("ha_pinba::read_next_row"); + int ret = HA_ERR_INTERNAL_ERROR; + + if (active_index < 0 || active_index >= PINBA_MAX_KEYS) { + DBUG_RETURN(HA_ERR_WRONG_INDEX); + } + + switch(share->table_type) { + case PINBA_TABLE_REQUEST: + if (active_index > 0) { + ret = HA_ERR_WRONG_INDEX; + goto failure; + } + + ret = requests_fetch_row(buf, this_index[active_index].ival, &(this_index[0].position)); + this_index[active_index].ival = this_index[0].position; + break; + case PINBA_TABLE_TIMER: + if (active_index == 0) { + ret = timers_fetch_row(buf, this_index[active_index].ival, &(this_index[active_index].ival), 0); + } else if (active_index == 1) { + ret = timers_fetch_row_by_request_id(buf, this_index[active_index].ival, &(this_index[active_index].ival)); + } else { + ret = HA_ERR_WRONG_INDEX; + goto failure; + } + break; + case PINBA_TABLE_TAG: + if (active_index == 0) { + ret = tags_fetch_row(buf, this_index[active_index].ival, &(this_index[0].position)); + this_index[active_index].ival = this_index[0].position; + } else if (active_index == 1) { + PPvoid_t ppvalue; + char name[PINBA_MAX_LINE_LEN] = {0}; + pinba_tag *tag; + + if (this_index[active_index].str.val) { + memcpy(name, this_index[active_index].str.val, this_index[active_index].str.len); + } + + ppvalue = JudySLNext(D->tag.name_index, (uint8_t *)name, NULL); + if (!ppvalue) { + ret = HA_ERR_END_OF_FILE; + free(this_index[active_index].str.val); + this_index[active_index].str.val = NULL; + goto failure; + } + tag = (pinba_tag *)*ppvalue; + + this_index[active_index].str.len = tag->name_len; + if (this_index[active_index].str.val) { + free(this_index[active_index].str.val); + this_index[active_index].str.val = NULL; + } + this_index[active_index].str.val = (unsigned char *)strndup(name, tag->name_len); + ret = tags_fetch_row_by_name(buf, this_index[active_index].str.val, this_index[active_index].str.len); + if (ret) { + free(this_index[active_index].str.val); + this_index[active_index].str.val = NULL; + } + } else { + ret = HA_ERR_WRONG_INDEX; + goto failure; + } + break; + case PINBA_TABLE_TIMERTAG: + ret = tag_values_fetch_next(buf, &(this_index[0].ival), &(this_index[0].position)); + break; + case PINBA_TABLE_INFO: + ret = info_fetch_row(buf); + break; + case PINBA_TABLE_REPORT1: + ret = report1_fetch_row(buf); + break; + case PINBA_TABLE_REPORT2: + ret = report2_fetch_row(buf); + break; + case PINBA_TABLE_REPORT3: + ret = report3_fetch_row(buf); + break; + case PINBA_TABLE_REPORT4: + ret = report4_fetch_row(buf); + break; + case PINBA_TABLE_REPORT5: + ret = report5_fetch_row(buf); + break; + case PINBA_TABLE_REPORT6: + ret = report6_fetch_row(buf); + break; + case PINBA_TABLE_REPORT7: + ret = report7_fetch_row(buf); + break; + case PINBA_TABLE_TAG_INFO: + pthread_rwlock_rdlock(&D->collector_lock); + pthread_rwlock_rdlock(&D->tag_reports_lock); + ret = tag_info_fetch_row(buf); + pthread_rwlock_unlock(&D->tag_reports_lock); + pthread_rwlock_unlock(&D->collector_lock); + break; + case PINBA_TABLE_TAG2_INFO: + pthread_rwlock_rdlock(&D->collector_lock); + pthread_rwlock_rdlock(&D->tag_reports_lock); + ret = tag2_info_fetch_row(buf); + pthread_rwlock_unlock(&D->tag_reports_lock); + pthread_rwlock_unlock(&D->collector_lock); + break; + case PINBA_TABLE_TAG_REPORT: + pthread_rwlock_rdlock(&D->collector_lock); + pthread_rwlock_rdlock(&D->tag_reports_lock); + ret = tag_report_fetch_row(buf); + pthread_rwlock_unlock(&D->tag_reports_lock); + pthread_rwlock_unlock(&D->collector_lock); + break; + case PINBA_TABLE_TAG2_REPORT: + pthread_rwlock_rdlock(&D->collector_lock); + pthread_rwlock_rdlock(&D->tag_reports_lock); + ret = tag2_report_fetch_row(buf); + pthread_rwlock_unlock(&D->tag_reports_lock); + pthread_rwlock_unlock(&D->collector_lock); + break; + default: + /* unsupported table type */ + ret = HA_ERR_INTERNAL_ERROR; + goto failure; + } + + if (ret == HA_ERR_KEY_NOT_FOUND) { + ret = HA_ERR_END_OF_FILE; + } + +failure: + table->status = ret ? STATUS_NOT_FOUND : 0; + DBUG_RETURN(ret); +} +/* }}} */ + +/* {{{ */ + +inline int ha_pinba::requests_fetch_row(unsigned char *buf, size_t index, size_t *new_index) /* {{{ */ +{ + Field **field; + pinba_pool *p = &D->request_pool; + my_bitmap_map *old_map; + pinba_stats_record record; + + DBUG_ENTER("ha_pinba::requests_fetch_row"); + + pthread_rwlock_rdlock(&D->collector_lock); + + if (new_index) { + *new_index = index; + } + + if (index == (p->size - 1)) { + index = 0; + } + + if (index == p->in || index < 0 || index >= (unsigned int)p->size || p->in == p->out) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } + + record = REQ_POOL(p)[index]; + + if (record.time == 0) { /* invalid record */ + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* index */ + (*field)->set_notnull(); + (*field)->store((long)index); + break; + case 1: /* hostname */ + (*field)->set_notnull(); + (*field)->store(record.data.hostname, strlen(record.data.hostname), &my_charset_bin); + break; + case 2: /* req_count */ + (*field)->set_notnull(); + (*field)->store((long)record.data.req_count); + break; + case 3: /* server_name */ + (*field)->set_notnull(); + (*field)->store(record.data.server_name, strlen(record.data.server_name), &my_charset_bin); + break; + case 4: /* script_name */ + (*field)->set_notnull(); + (*field)->store(record.data.script_name, strlen(record.data.script_name), &my_charset_bin); + break; + case 5: /* doc_size */ + (*field)->set_notnull(); + (*field)->store(pinba_round((float)(record.data.doc_size), 1000)); + break; + case 6: /* mem_peak_usage */ + (*field)->set_notnull(); + (*field)->store(pinba_round((float)(record.data.mem_peak_usage), 1000)); + break; + case 7: /* req_time */ + (*field)->set_notnull(); + (*field)->store(pinba_round(timeval_to_float(record.data.req_time), 1000)); + break; + case 8: /* ru_utime */ + (*field)->set_notnull(); + (*field)->store(pinba_round(timeval_to_float(record.data.ru_utime), 10000)); + break; + case 9: /* ru_stime */ + (*field)->set_notnull(); + (*field)->store(pinba_round(timeval_to_float(record.data.ru_stime), 10000)); + break; + case 10: /* timers_cnt */ + (*field)->set_notnull(); + (*field)->store((long)record.timers_cnt); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + + if (new_index) { + *new_index = index + 1; + } + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::timers_fetch_row(unsigned char *buf, size_t index, size_t *new_index, int exact) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + pinba_pool *p = &D->request_pool; + pinba_pool *timer_pool = &D->timer_pool; + pinba_timer_record timer; + pinba_timer_position *timer_pos = NULL; + pinba_stats_record record; + + DBUG_ENTER("ha_pinba::timers_fetch_row"); + + pthread_rwlock_rdlock(&D->collector_lock); + + if (new_index) { + *new_index = index; + } + +try_next: + + if (index == (timer_pool->size - 1)) { + index = 0; + } + + if (index == timer_pool->in || index < 0 || index >= (unsigned int)timer_pool->size || timer_pool->in == timer_pool->out) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } + + timer_pos = TIMER_POOL(timer_pool) + index; + + if (!exact && REQ_POOL(p)[timer_pos->request_id].time == 0) { + index++; + goto try_next; + } + + record = REQ_POOL(p)[timer_pos->request_id]; + if (timer_pos->position >= record.timers_cnt) { + if (exact) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } else { + goto try_next; + } + } + + timer = record.timers[timer_pos->position]; + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* index */ + (*field)->set_notnull(); + (*field)->store((long)index); + break; + case 1: /* request_id */ + (*field)->set_notnull(); + (*field)->store((long)timer_pos->request_id); + break; + case 2: /* hit_count */ + (*field)->set_notnull(); + (*field)->store(timer.hit_count); + break; + case 3: /* value */ + (*field)->set_notnull(); + (*field)->store(timeval_to_float(timer.value)); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + + if (new_index) { + *new_index = index + 1; + } + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::timers_fetch_row_by_request_id(unsigned char *buf, size_t index, size_t *new_index) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + pinba_pool *p = &D->request_pool; + pinba_timer_record timer; + pinba_stats_record record; + + DBUG_ENTER("ha_pinba::timers_fetch_row_by_request_id"); + + pthread_rwlock_rdlock(&D->collector_lock); + + if (new_index) { + *new_index = index; + } + + if (index == p->in || index < 0 || index >= (unsigned int)D->settings.request_pool_size || p->in == p->out) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } + + record = REQ_POOL(p)[index]; + + if (this_index[active_index].position >= record.timers_cnt || this_index[active_index].position < 0) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + timer = record.timers[this_index[active_index].position]; + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* index */ + (*field)->set_notnull(); + (*field)->store((long)timer.index); + break; + case 1: /* request_id */ + (*field)->set_notnull(); + (*field)->store((long)index); + break; + case 2: /* hit_count */ + (*field)->set_notnull(); + (*field)->store(timer.hit_count); + break; + case 3: /* value */ + (*field)->set_notnull(); + (*field)->store(timeval_to_float(timer.value)); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + + /* XXX this smells funny */ + if (new_index && (size_t)this_index[active_index].position == (size_t)(record.timers_cnt - 1)) { + *new_index = index + 1; + this_index[active_index].position = -1; + } + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::tags_fetch_row(unsigned char *buf, size_t index, size_t *new_index) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + pinba_tag *tag; + + DBUG_ENTER("ha_pinba::tags_fetch_row"); + + pthread_rwlock_rdlock(&D->collector_lock); + + if (new_index) { + *new_index = index; + } + + tag = pinba_tag_get_by_id(index); + if (!tag) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* id */ + (*field)->set_notnull(); + (*field)->store((long)index); + break; + case 1: /* name */ + (*field)->set_notnull(); + (*field)->store(tag->name, tag->name_len, &my_charset_bin); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + + if (new_index) { + *new_index = index + 1; + } + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::tags_fetch_row_by_name(unsigned char* buf, const unsigned char *name, uint name_len) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + pinba_tag *tag; + + DBUG_ENTER("ha_pinba::tags_fetch_row_by_name"); + + pthread_rwlock_rdlock(&D->collector_lock); + + tag = pinba_tag_get_by_name(name); + if (!tag) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* id */ + (*field)->set_notnull(); + (*field)->store((long)tag->id); + break; + case 1: /* name */ + (*field)->set_notnull(); + (*field)->store(tag->name, tag->name_len, &my_charset_bin); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::tag_values_fetch_next(unsigned char *buf, size_t *index, size_t *position) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + pinba_pool *timer_pool = &D->timer_pool; + pinba_pool *p = &D->request_pool; + pinba_timer_position *timer_pos; + pinba_timer_record *timer; + pinba_stats_record *record; + + DBUG_ENTER("ha_pinba::tag_values_fetch_row"); + + pthread_rwlock_rdlock(&D->collector_lock); + +retry_next: + + if (*index == (timer_pool->size - 1)) { + *index = 0; + } + + if (*index == timer_pool->in || *index < 0 || *index >= (unsigned int)timer_pool->size || timer_pool->in == timer_pool->out) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } + + timer_pos = TIMER_POOL(timer_pool) + *index; + + if (timer_pos->request_id < 0 || timer_pos->request_id >= p->size) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } + + record = REQ_POOL(p) + timer_pos->request_id; + + if (timer_pos->position >= record->timers_cnt) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } + + timer = record->timers + timer_pos->position; + + if (*position >= timer->tag_num) { + (*position) = 0; + (*index)++; + goto retry_next; + } + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* timer_id */ + (*field)->set_notnull(); + (*field)->store((long)timer->index); + break; + case 1: /* timer_id */ + (*field)->set_notnull(); + (*field)->store((long)timer->tag_ids[*position]); + break; + case 2: /* name */ + (*field)->set_notnull(); + (*field)->store(timer->tag_values[*position]->str, timer->tag_values[*position]->len, &my_charset_bin); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + + (*position)++; + + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::tag_values_fetch_by_timer_id(unsigned char *buf) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + pinba_pool *timer_pool = &D->timer_pool; + pinba_pool *p = &D->request_pool; + pinba_timer_position *timer_pos; + pinba_timer_record *timer; + pinba_stats_record *record; + + DBUG_ENTER("ha_pinba::tag_values_fetch_by_timer_id"); + + pthread_rwlock_rdlock(&D->collector_lock); + + if (this_index[0].ival == (timer_pool->size - 1)) { + this_index[0].ival = 0; + } + + if (this_index[0].ival == timer_pool->in || this_index[0].ival < 0 || this_index[0].ival >= (unsigned int)timer_pool->size || timer_pool->in == timer_pool->out) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } + + timer_pos = TIMER_POOL(timer_pool) + this_index[0].ival; + + if (timer_pos->request_id < 0 || timer_pos->request_id >= p->size) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } + + record = REQ_POOL(p) + timer_pos->request_id; + + if (timer_pos->position >= record->timers_cnt) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_KEY_NOT_FOUND); + } + + timer = record->timers + timer_pos->position; + + if (this_index[0].position >= timer->tag_num) { + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* timer_id */ + (*field)->set_notnull(); + (*field)->store((long)timer->index); + break; + case 1: /* timer_id */ + (*field)->set_notnull(); + (*field)->store((long)timer->tag_ids[this_index[0].position]); + break; + case 2: /* name */ + (*field)->set_notnull(); + (*field)->store(timer->tag_values[this_index[0].position]->str, timer->tag_values[this_index[0].position]->len, &my_charset_bin); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + + pthread_rwlock_unlock(&D->collector_lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::report1_fetch_row(unsigned char *buf) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + struct pinba_report1_data *data; + PPvoid_t ppvalue; + uint8_t index[PINBA_MAX_LINE_LEN] = {0}; + pinba_report *report = &D->base_reports[PINBA_BASE_REPORT1]; + + DBUG_ENTER("ha_pinba::report1_fetch_row"); + + if (this_index[0].position == 0 || this_index[0].str.val == NULL) { + pthread_rwlock_rdlock(&report->lock); + ppvalue = JudySLFirst(report->results, index, NULL); + report->time_interval = pinba_get_time_interval(); + } else { + pthread_rwlock_rdlock(&report->lock); + + strcpy((char *)index, (char *)this_index[0].str.val); + ppvalue = JudySLNext(report->results, index, NULL); + free(this_index[0].str.val); + this_index[0].str.val = NULL; + } + + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + this_index[0].str.val = (unsigned char *)strdup((char *)index); + this_index[0].position++; + + data = (struct pinba_report1_data *)*ppvalue; + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* req_count */ + (*field)->set_notnull(); + (*field)->store((long)data->req_count); + break; + case 1: /* req_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_count/(float)report->time_interval); + break; + case 2: /* req_time_total */ + (*field)->set_notnull(); + (*field)->store(data->req_time_total); + break; + case 3: /* req_time_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->req_time_total/report->time_total); + break; + case 4: /* req_time_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_time_total/(float)report->time_interval); + break; + case 5: /* ru_utime_total */ + (*field)->set_notnull(); + (*field)->store(data->ru_utime_total); + break; + case 6: /* ru_utime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_utime_total/(float)report->ru_utime_total); + break; + case 7: /* ru_utime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_utime_total/(float)report->time_interval); + break; + case 8: /* ru_stime_total */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)data->req_count); + break; + case 9: /* ru_stime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_stime_total/(float)report->ru_stime_total); + break; + case 10: /* ru_stime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)report->time_interval); + break; + case 11: /* traffic_total */ + (*field)->set_notnull(); + (*field)->store(data->kbytes_total); + break; + case 12: /* traffic_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->kbytes_total/report->kbytes_total); + break; + case 13: /* traffic_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->kbytes_total/(float)report->time_interval); + break; + case 14: /* script_name */ + (*field)->set_notnull(); + (*field)->store((const char *)index, strlen((const char *)index), &my_charset_bin); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::report2_fetch_row(unsigned char *buf) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + struct pinba_report2_data *data; + PPvoid_t ppvalue; + uint8_t index[PINBA_MAX_LINE_LEN] = {0}; + pinba_report *report = &D->base_reports[PINBA_BASE_REPORT2]; + + DBUG_ENTER("ha_pinba::report2_fetch_row"); + + if (this_index[0].position == 0 || this_index[0].str.val == NULL) { + pthread_rwlock_rdlock(&report->lock); + ppvalue = JudySLFirst(report->results, index, NULL); + report->time_interval = pinba_get_time_interval(); + } else { + pthread_rwlock_rdlock(&report->lock); + strcpy((char *)index, (char *)this_index[0].str.val); + ppvalue = JudySLNext(report->results, index, NULL); + free(this_index[0].str.val); + this_index[0].str.val = NULL; + } + + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + this_index[0].str.val = (unsigned char *)strdup((char *)index); + this_index[0].position++; + + data = (struct pinba_report2_data *)*ppvalue; + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* req_count */ + (*field)->set_notnull(); + (*field)->store((long)data->req_count); + break; + case 1: /* req_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_count/(float)report->time_interval); + break; + case 2: /* req_time_total */ + (*field)->set_notnull(); + (*field)->store(data->req_time_total); + break; + case 3: /* req_time_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->req_time_total/(float)report->time_total); + break; + case 4: /* req_time_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_time_total/(float)report->time_interval); + break; + case 5: /* ru_utime_total */ + (*field)->set_notnull(); + (*field)->store(data->ru_utime_total); + break; + case 6: /* ru_utime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_utime_total/(float)report->ru_utime_total); + break; + case 7: /* ru_utime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_utime_total/(float)report->time_interval); + break; + case 8: /* ru_stime_total */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)data->req_count); + break; + case 9: /* ru_stime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_stime_total/(float)report->ru_stime_total); + break; + case 10: /* ru_stime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)report->time_interval); + break; + case 11: /* traffic_total */ + (*field)->set_notnull(); + (*field)->store(data->kbytes_total); + break; + case 12: /* traffic_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->kbytes_total/(float)report->kbytes_total); + break; + case 13: /* traffic_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->kbytes_total/(float)report->time_interval); + break; + case 14: /* server_name */ + (*field)->set_notnull(); + (*field)->store((const char *)index, strlen((const char *)index), &my_charset_bin); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::report3_fetch_row(unsigned char *buf) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + struct pinba_report3_data *data; + PPvoid_t ppvalue; + uint8_t index[PINBA_MAX_LINE_LEN] = {0}; + pinba_report *report = &D->base_reports[PINBA_BASE_REPORT3]; + + DBUG_ENTER("ha_pinba::report3_fetch_row"); + + if (this_index[0].position == 0 || this_index[0].str.val == NULL) { + pthread_rwlock_rdlock(&report->lock); + ppvalue = JudySLFirst(report->results, index, NULL); + report->time_interval = pinba_get_time_interval(); + } else { + pthread_rwlock_rdlock(&report->lock); + strcpy((char *)index, (char *)this_index[0].str.val); + ppvalue = JudySLNext(report->results, index, NULL); + free(this_index[0].str.val); + this_index[0].str.val = NULL; + } + + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + this_index[0].str.val = (unsigned char *)strdup((char *)index); + this_index[0].position++; + + data = (struct pinba_report3_data *)*ppvalue; + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* req_count */ + (*field)->set_notnull(); + (*field)->store((long)data->req_count); + break; + case 1: /* req_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_count/(float)report->time_interval); + break; + case 2: /* req_time_total */ + (*field)->set_notnull(); + (*field)->store(data->req_time_total); + break; + case 3: /* req_time_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->req_time_total/(float)report->time_total); + break; + case 4: /* req_time_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_time_total/(float)report->time_interval); + break; + case 5: /* ru_utime_total */ + (*field)->set_notnull(); + (*field)->store(data->ru_utime_total); + break; + case 6: /* ru_utime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_utime_total/(float)report->ru_utime_total); + break; + case 7: /* ru_utime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_utime_total/(float)report->time_interval); + break; + case 8: /* ru_stime_total */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)data->req_count); + break; + case 9: /* ru_stime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_stime_total/(float)report->ru_stime_total); + break; + case 10: /* ru_stime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)report->time_interval); + break; + case 11: /* traffic_total */ + (*field)->set_notnull(); + (*field)->store(data->kbytes_total); + break; + case 12: /* traffic_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->kbytes_total/(float)report->kbytes_total); + break; + case 13: /* traffic_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->kbytes_total/(float)report->time_interval); + break; + case 14: /* hostname */ + (*field)->set_notnull(); + (*field)->store((const char *)index, strlen((const char *)index), &my_charset_bin); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::report4_fetch_row(unsigned char *buf) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + struct pinba_report4_data *data; + PPvoid_t ppvalue; + uint8_t index[PINBA_MAX_LINE_LEN] = {0}; + pinba_report *report = &D->base_reports[PINBA_BASE_REPORT4]; + + DBUG_ENTER("ha_pinba::report4_fetch_row"); + + if (this_index[0].position == 0 || this_index[0].str.val == NULL) { + pthread_rwlock_rdlock(&report->lock); + ppvalue = JudySLFirst(report->results, index, NULL); + report->time_interval = pinba_get_time_interval(); + } else { + pthread_rwlock_rdlock(&report->lock); + strcpy((char *)index, (char *)this_index[0].str.val); + ppvalue = JudySLNext(report->results, index, NULL); + free(this_index[0].str.val); + this_index[0].str.val = NULL; + } + + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + this_index[0].str.val = (unsigned char *)strdup((char *)index); + this_index[0].position++; + + data = (struct pinba_report4_data *)*ppvalue; + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* req_count */ + (*field)->set_notnull(); + (*field)->store((long)data->req_count); + break; + case 1: /* req_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_count/(float)report->time_interval); + break; + case 2: /* req_time_total */ + (*field)->set_notnull(); + (*field)->store(data->req_time_total); + break; + case 3: /* req_time_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->req_time_total/(float)report->time_total); + break; + case 4: /* req_time_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_time_total/(float)report->time_interval); + break; + case 5: /* ru_utime_total */ + (*field)->set_notnull(); + (*field)->store(data->ru_utime_total); + break; + case 6: /* ru_utime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_utime_total/(float)report->ru_utime_total); + break; + case 7: /* ru_utime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_utime_total/(float)report->time_interval); + break; + case 8: /* ru_stime_total */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)data->req_count); + break; + case 9: /* ru_stime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_stime_total/(float)report->ru_stime_total); + break; + case 10: /* ru_stime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)report->time_interval); + break; + case 11: /* traffic_total */ + (*field)->set_notnull(); + (*field)->store(data->kbytes_total); + break; + case 12: /* traffic_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->kbytes_total/report->kbytes_total); + break; + case 13: /* traffic_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->kbytes_total/(float)report->time_interval); + break; + case 14: /* server_name */ + (*field)->set_notnull(); + (*field)->store((const char *)data->server_name, strlen((const char *)data->server_name), &my_charset_bin); + break; + case 15: /* script_name */ + (*field)->set_notnull(); + (*field)->store((const char *)data->script_name, strlen((const char *)data->script_name), &my_charset_bin); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::report5_fetch_row(unsigned char *buf) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + struct pinba_report5_data *data; + PPvoid_t ppvalue; + uint8_t index[PINBA_MAX_LINE_LEN] = {0}; + pinba_report *report = &D->base_reports[PINBA_BASE_REPORT5]; + + DBUG_ENTER("ha_pinba::report5_fetch_row"); + + if (this_index[0].position == 0 || this_index[0].str.val == NULL) { + pthread_rwlock_rdlock(&report->lock); + ppvalue = JudySLFirst(report->results, index, NULL); + report->time_interval = pinba_get_time_interval(); + } else { + pthread_rwlock_rdlock(&report->lock); + strcpy((char *)index, (char *)this_index[0].str.val); + ppvalue = JudySLNext(report->results, index, NULL); + free(this_index[0].str.val); + this_index[0].str.val = NULL; + } + + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + this_index[0].str.val = (unsigned char *)strdup((char *)index); + this_index[0].position++; + + data = (struct pinba_report5_data *)*ppvalue; + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* req_count */ + (*field)->set_notnull(); + (*field)->store((long)data->req_count); + break; + case 1: /* req_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_count/(float)report->time_interval); + break; + case 2: /* req_time_total */ + (*field)->set_notnull(); + (*field)->store(data->req_time_total); + break; + case 3: /* req_time_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->req_time_total/report->time_total); + break; + case 4: /* req_time_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_time_total/(float)report->time_interval); + break; + case 5: /* ru_utime_total */ + (*field)->set_notnull(); + (*field)->store(data->ru_utime_total); + break; + case 6: /* ru_utime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_utime_total/(float)report->ru_utime_total); + break; + case 7: /* ru_utime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_utime_total/(float)report->time_interval); + break; + case 8: /* ru_stime_total */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)data->req_count); + break; + case 9: /* ru_stime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_stime_total/(float)report->ru_stime_total); + break; + case 10: /* ru_stime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)report->time_interval); + break; + case 11: /* traffic_total */ + (*field)->set_notnull(); + (*field)->store(data->kbytes_total); + break; + case 12: /* traffic_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->kbytes_total/report->kbytes_total); + break; + case 13: /* traffic_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->kbytes_total/(float)report->time_interval); + break; + case 14: /* hostname */ + (*field)->set_notnull(); + (*field)->store((const char *)data->hostname, strlen((const char *)data->hostname), &my_charset_bin); + break; + case 15: /* script_name */ + (*field)->set_notnull(); + (*field)->store((const char *)data->script_name, strlen((const char *)data->script_name), &my_charset_bin); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::report6_fetch_row(unsigned char *buf) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + struct pinba_report6_data *data; + PPvoid_t ppvalue; + uint8_t index[PINBA_MAX_LINE_LEN] = {0}; + pinba_report *report = &D->base_reports[PINBA_BASE_REPORT6]; + + DBUG_ENTER("ha_pinba::report6_fetch_row"); + + if (this_index[0].position == 0 || this_index[0].str.val == NULL) { + pthread_rwlock_rdlock(&report->lock); + ppvalue = JudySLFirst(report->results, index, NULL); + report->time_interval = pinba_get_time_interval(); + } else { + pthread_rwlock_rdlock(&report->lock); + strcpy((char *)index, (char *)this_index[0].str.val); + ppvalue = JudySLNext(report->results, index, NULL); + free(this_index[0].str.val); + this_index[0].str.val = NULL; + } + + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + this_index[0].str.val = (unsigned char *)strdup((char *)index); + this_index[0].position++; + + data = (struct pinba_report6_data *)*ppvalue; + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* req_count */ + (*field)->set_notnull(); + (*field)->store((long)data->req_count); + break; + case 1: /* req_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_count/(float)report->time_interval); + break; + case 2: /* req_time_total */ + (*field)->set_notnull(); + (*field)->store(data->req_time_total); + break; + case 3: /* req_time_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->req_time_total/report->time_total); + break; + case 4: /* req_time_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_time_total/(float)report->time_interval); + break; + case 5: /* ru_utime_total */ + (*field)->set_notnull(); + (*field)->store(data->ru_utime_total); + break; + case 6: /* ru_utime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_utime_total/(float)report->ru_utime_total); + break; + case 7: /* ru_utime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_utime_total/(float)report->time_interval); + break; + case 8: /* ru_stime_total */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)data->req_count); + break; + case 9: /* ru_stime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_stime_total/(float)report->ru_stime_total); + break; + case 10: /* ru_stime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)report->time_interval); + break; + case 11: /* traffic_total */ + (*field)->set_notnull(); + (*field)->store(data->kbytes_total); + break; + case 12: /* traffic_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->kbytes_total/report->kbytes_total); + break; + case 13: /* traffic_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->kbytes_total/(float)report->time_interval); + break; + case 14: /* hostname */ + (*field)->set_notnull(); + (*field)->store((const char *)data->hostname, strlen((const char *)data->hostname), &my_charset_bin); + break; + case 15: /* server_name */ + (*field)->set_notnull(); + (*field)->store((const char *)data->server_name, strlen((const char *)data->server_name), &my_charset_bin); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::report7_fetch_row(unsigned char *buf) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + struct pinba_report7_data *data; + PPvoid_t ppvalue; + uint8_t index[PINBA_MAX_LINE_LEN] = {0}; + pinba_report *report = &D->base_reports[PINBA_BASE_REPORT7]; + + DBUG_ENTER("ha_pinba::report7_fetch_row"); + + if (this_index[0].position == 0 || this_index[0].str.val == NULL) { + pthread_rwlock_rdlock(&report->lock); + ppvalue = JudySLFirst(report->results, index, NULL); + report->time_interval = pinba_get_time_interval(); + } else { + pthread_rwlock_rdlock(&report->lock); + strcpy((char *)index, (char *)this_index[0].str.val); + ppvalue = JudySLNext(report->results, index, NULL); + free(this_index[0].str.val); + this_index[0].str.val = NULL; + } + + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + this_index[0].str.val = (unsigned char *)strdup((char *)index); + this_index[0].position++; + + data = (struct pinba_report7_data *)*ppvalue; + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* req_count */ + (*field)->set_notnull(); + (*field)->store((long)data->req_count); + break; + case 1: /* req_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_count/(float)report->time_interval); + break; + case 2: /* req_time_total */ + (*field)->set_notnull(); + (*field)->store(data->req_time_total); + break; + case 3: /* req_time_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->req_time_total/report->time_total); + break; + case 4: /* req_time_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_time_total/(float)report->time_interval); + break; + case 5: /* ru_utime_total */ + (*field)->set_notnull(); + (*field)->store(data->ru_utime_total); + break; + case 6: /* ru_utime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_utime_total/(float)report->ru_utime_total); + break; + case 7: /* ru_utime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_utime_total/(float)report->time_interval); + break; + case 8: /* ru_stime_total */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)data->req_count); + break; + case 9: /* ru_stime_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->ru_stime_total/(float)report->ru_stime_total); + break; + case 10: /* ru_stime_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->ru_stime_total/(float)report->time_interval); + break; + case 11: /* traffic_total */ + (*field)->set_notnull(); + (*field)->store(data->kbytes_total); + break; + case 12: /* traffic_percent */ + (*field)->set_notnull(); + (*field)->store(100.0 * (float)data->kbytes_total/report->kbytes_total); + break; + case 13: /* traffic_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->kbytes_total/(float)report->time_interval); + break; + case 14: /* hostname */ + (*field)->set_notnull(); + (*field)->store((const char *)data->hostname, strlen((const char *)data->hostname), &my_charset_bin); + break; + case 15: /* server_name */ + (*field)->set_notnull(); + (*field)->store((const char *)data->server_name, strlen((const char *)data->server_name), &my_charset_bin); + break; + case 16: /* script_name */ + (*field)->set_notnull(); + (*field)->store((const char *)data->script_name, strlen((const char *)data->script_name), &my_charset_bin); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::info_fetch_row(unsigned char *buf) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + pinba_report *report = &D->base_reports[PINBA_BASE_REPORT_INFO]; + + DBUG_ENTER("ha_pinba::info_fetch_row"); + + pthread_rwlock_rdlock(&report->lock); + if (this_index[0].position == 0) { + report->time_interval = pinba_get_time_interval(); + } else { + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + this_index[0].position++; + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* req_count */ + (*field)->set_notnull(); + (*field)->store((long)report->results_cnt); + break; + case 1: /* req_time_total */ + (*field)->set_notnull(); + (*field)->store(pinba_round((float)report->time_total, 1000)); + break; + case 2: /* ru_utime_total */ + (*field)->set_notnull(); + (*field)->store(pinba_round((float)report->ru_utime_total, 1000)); + break; + case 3: /* ru_stime_total */ + (*field)->set_notnull(); + (*field)->store(pinba_round((float)report->ru_stime_total, 1000)); + break; + case 4: /* time_interval */ + (*field)->set_notnull(); + (*field)->store((long)report->time_interval); + break; + case 5: /* kbytes_total */ + (*field)->set_notnull(); + (*field)->store(pinba_round(report->kbytes_total, 1000)); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::tag_info_fetch_row(unsigned char *buf) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + struct pinba_tag_info_data *data; + pinba_tag_report *report; + PPvoid_t ppvalue; + uint8_t index[PINBA_TAG_VALUE_SIZE] = {0}; + + DBUG_ENTER("ha_pinba::tag_info_fetch_row"); + + if (!share->params || share->params[0] == '\0') { + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } + + if (this_index[0].position == 0) { + time_t now = time(NULL); + + report = pinba_get_tag_report(PINBA_TAG_REPORT_INFO, share->params[0], NULL); + if (!report) { + pthread_rwlock_unlock(&D->tag_reports_lock); + pthread_rwlock_wrlock(&D->tag_reports_lock); + report = pinba_regenerate_tag_info(share->params[0], strlen(share->params[0])); + } + + if (!report) { + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + pthread_rwlock_wrlock(&report->lock); + report->last_requested = now; + ppvalue = JudySLFirst(report->results, index, NULL); + report->time_interval = pinba_get_time_interval(); + } else { + report = pinba_get_tag_report(PINBA_TAG_REPORT_INFO, share->params[0], NULL); + if (!report) { + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + pthread_rwlock_wrlock(&report->lock); + strcpy((char *)index, (char *)this_index[0].str.val); + ppvalue = JudySLNext(report->results, index, NULL); + free(this_index[0].str.val); + this_index[0].str.val = NULL; + } + + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + this_index[0].str.val = (unsigned char *)strdup((char *)index); + this_index[0].position++; + + data = (struct pinba_tag_info_data *)*ppvalue; + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* tag_value */ + (*field)->set_notnull(); + (*field)->store((const char *)index, strlen((const char *)index), &my_charset_bin); + break; + case 1: /* req_count */ + (*field)->set_notnull(); + (*field)->store((long)data->req_count); + break; + case 2: /* req_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_count/(float)report->time_interval); + break; + case 3: /* hit_count */ + (*field)->set_notnull(); + (*field)->store((long)data->hit_count); + break; + case 4: /* hit_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->hit_count/(float)report->time_interval); + break; + case 5: /* timer_value */ + (*field)->set_notnull(); + (*field)->store(timeval_to_float(data->timer_value)); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::tag2_info_fetch_row(unsigned char *buf) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + struct pinba_tag2_info_data *data; + pinba_tag_report *report; + PPvoid_t ppvalue; + uint8_t index[PINBA_TAG_VALUE_SIZE + 1 + PINBA_TAG_VALUE_SIZE] = {0}; + + DBUG_ENTER("ha_pinba::tag2_info_fetch_row"); + + if (!share->params || share->params[0] == '\0') { + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } + + if (this_index[0].position == 0) { + time_t now = time(NULL); + + report = pinba_get_tag_report(PINBA_TAG2_REPORT_INFO, share->params[0], share->params[1]); + if (!report) { + pthread_rwlock_rdlock(&D->collector_lock); + report = pinba_regenerate_tag2_info(share->params[0], strlen(share->params[0]), share->params[1], strlen(share->params[1])); + pthread_rwlock_unlock(&D->collector_lock); + } + if (!report) { + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + pthread_rwlock_rdlock(&report->lock); + report->last_requested = now; + ppvalue = JudySLFirst(report->results, index, NULL); + report->time_interval = pinba_get_time_interval(); + } else { + report = pinba_get_tag_report(PINBA_TAG2_REPORT_INFO, share->params[0], share->params[1]); + if (!report) { + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + pthread_rwlock_rdlock(&report->lock); + strcpy((char *)index, (char *)this_index[0].str.val); + ppvalue = JudySLNext(report->results, index, NULL); + free(this_index[0].str.val); + this_index[0].str.val = NULL; + } + + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + this_index[0].str.val = (unsigned char *)strdup((char *)index); + this_index[0].position++; + + data = (struct pinba_tag2_info_data *)*ppvalue; + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* tag1_value */ + (*field)->set_notnull(); + (*field)->store((const char *)data->tag1_value, strlen((const char *)data->tag1_value), &my_charset_bin); + break; + case 1: /* tag2_value */ + (*field)->set_notnull(); + (*field)->store((const char *)data->tag2_value, strlen((const char *)data->tag2_value), &my_charset_bin); + break; + case 2: /* req_count */ + (*field)->set_notnull(); + (*field)->store((long)data->req_count); + break; + case 3: /* req_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_count/(float)report->time_interval); + break; + case 4: /* hit_count */ + (*field)->set_notnull(); + (*field)->store((long)data->hit_count); + break; + case 5: /* hit_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->hit_count/(float)report->time_interval); + break; + case 6: /* timer_value */ + (*field)->set_notnull(); + (*field)->store(timeval_to_float(data->timer_value)); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::tag_report_fetch_row(unsigned char *buf) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + struct pinba_tag_report_data *data; + pinba_tag_report *report; + PPvoid_t ppvalue; + uint8_t index[PINBA_SCRIPT_NAME_SIZE + 1 + PINBA_TAG_VALUE_SIZE] = {0}; + + DBUG_ENTER("ha_pinba::tag_report_fetch_row"); + + if (!share->params || share->params[0] == '\0') { + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } + + if (this_index[0].position == 0) { + time_t now = time(NULL); + + report = pinba_get_tag_report(PINBA_TAG_REPORT, share->params[0], NULL); + if (!report) { + pthread_rwlock_unlock(&D->tag_reports_lock); + pthread_rwlock_wrlock(&D->tag_reports_lock); + report = pinba_regenerate_tag_report(share->params[0], strlen(share->params[0])); + } + + if (!report) { + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + pthread_rwlock_rdlock(&report->lock); + report->last_requested = now; + ppvalue = JudySLFirst(report->results, index, NULL); + report->time_interval = pinba_get_time_interval(); + } else { + report = pinba_get_tag_report(PINBA_TAG_REPORT, share->params[0], NULL); + if (!report) { + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + pthread_rwlock_rdlock(&report->lock); + strcpy((char *)index, (char *)this_index[0].str.val); + ppvalue = JudySLNext(report->results, index, NULL); + free(this_index[0].str.val); + this_index[0].str.val = NULL; + } + + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + this_index[0].str.val = (unsigned char *)strdup((char *)index); + this_index[0].position++; + + data = (struct pinba_tag_report_data *)*ppvalue; + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* script_name */ + (*field)->set_notnull(); + (*field)->store((const char *)data->script_name, strlen((const char *)data->script_name), &my_charset_bin); + break; + case 1: /* tag_value */ + (*field)->set_notnull(); + (*field)->store((const char *)data->tag_value, strlen((const char *)data->tag_value), &my_charset_bin); + break; + case 2: /* req_count */ + (*field)->set_notnull(); + (*field)->store((long)data->req_count); + break; + case 3: /* req_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_count/(float)report->time_interval); + break; + case 4: /* hit_count */ + (*field)->set_notnull(); + (*field)->store((long)data->hit_count); + break; + case 5: /* hit_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->hit_count/(float)report->time_interval); + break; + case 6: /* timer_value */ + (*field)->set_notnull(); + (*field)->store(timeval_to_float(data->timer_value)); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(0); +} +/* }}} */ + +inline int ha_pinba::tag2_report_fetch_row(unsigned char *buf) /* {{{ */ +{ + Field **field; + my_bitmap_map *old_map; + struct pinba_tag2_report_data *data; + pinba_tag_report *report; + PPvoid_t ppvalue; + uint8_t index[PINBA_SCRIPT_NAME_SIZE + 1 + PINBA_TAG_VALUE_SIZE + 1 + PINBA_TAG_VALUE_SIZE] = {0}; + + DBUG_ENTER("ha_pinba::tag2_report_fetch_row"); + + if (!share->params || share->params[0] == '\0') { + DBUG_RETURN(HA_ERR_INTERNAL_ERROR); + } + + if (this_index[0].position == 0) { + time_t now = time(NULL); + + report = pinba_get_tag_report(PINBA_TAG2_REPORT, share->params[0], share->params[1]); + if (!report) { + pthread_rwlock_rdlock(&D->collector_lock); + report = pinba_regenerate_tag2_report(share->params[0], strlen(share->params[0]), share->params[1], strlen(share->params[1])); + pthread_rwlock_unlock(&D->collector_lock); + } + if (!report) { + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + pthread_rwlock_rdlock(&report->lock); + report->last_requested = now; + ppvalue = JudySLFirst(report->results, index, NULL); + report->time_interval = pinba_get_time_interval(); + } else { + report = pinba_get_tag_report(PINBA_TAG2_REPORT, share->params[0], share->params[1]); + if (!report) { + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + pthread_rwlock_rdlock(&report->lock); + strcpy((char *)index, (char *)this_index[0].str.val); + ppvalue = JudySLNext(report->results, index, NULL); + free(this_index[0].str.val); + this_index[0].str.val = NULL; + } + + if (!ppvalue || ppvalue == PPJERR) { + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(HA_ERR_END_OF_FILE); + } + + this_index[0].str.val = (unsigned char *)strdup((char *)index); + this_index[0].position++; + + data = (struct pinba_tag2_report_data *)*ppvalue; + + old_map = dbug_tmp_use_all_columns(table, table->write_set); + + for (field = table->field; *field; field++) { + if (bitmap_is_set(table->read_set, (*field)->field_index)) { + switch((*field)->field_index) { + case 0: /* script_name */ + (*field)->set_notnull(); + (*field)->store((const char *)data->script_name, strlen((const char *)data->script_name), &my_charset_bin); + break; + case 1: /* tag1_value */ + (*field)->set_notnull(); + (*field)->store((const char *)data->tag1_value, strlen((const char *)data->tag1_value), &my_charset_bin); + break; + case 2: /* tag2_value */ + (*field)->set_notnull(); + (*field)->store((const char *)data->tag2_value, strlen((const char *)data->tag2_value), &my_charset_bin); + break; + case 3: /* req_count */ + (*field)->set_notnull(); + (*field)->store((long)data->req_count); + break; + case 4: /* req_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->req_count/(float)report->time_interval); + break; + case 5: /* hit_count */ + (*field)->set_notnull(); + (*field)->store((long)data->hit_count); + break; + case 6: /* hit_per_sec */ + (*field)->set_notnull(); + (*field)->store((float)data->hit_count/(float)report->time_interval); + break; + case 7: /* timer_value */ + (*field)->set_notnull(); + (*field)->store(timeval_to_float(data->timer_value)); + break; + default: + (*field)->set_null(); + break; + } + } + } + dbug_tmp_restore_column_map(table->write_set, old_map); + pthread_rwlock_unlock(&report->lock); + DBUG_RETURN(0); +} +/* }}} */ + +/* }}} */ + +void ha_pinba::position(const unsigned char *record) /* {{{ */ +{ + DBUG_ENTER("ha_pinba::position"); + DBUG_VOID_RETURN; +} +/* }}} */ + +int ha_pinba::create(const char *name, TABLE *table_arg, HA_CREATE_INFO *create_info) /* {{{ */ +{ + DBUG_ENTER("ha_pinba::create"); + + if (pinba_get_table_type(table_arg) == PINBA_TABLE_UNKNOWN) { + DBUG_RETURN(HA_WRONG_CREATE_OPTION); + } + + if (pinba_parse_params(table_arg, NULL, NULL) < 0) { + DBUG_RETURN(HA_WRONG_CREATE_OPTION); + } + + DBUG_RETURN(0); +} +/* }}} */ + +int ha_pinba::delete_all_rows() /* {{{ */ +{ + DBUG_ENTER("ha_example::delete_all_rows"); + + switch (share->table_type) { + case PINBA_TABLE_REQUEST: + /* we only support 'delete from request' */ + break; + default: + DBUG_RETURN(HA_ERR_WRONG_COMMAND); + } + + pthread_rwlock_wrlock(&D->collector_lock); + /* destroy & reinitialize the request pool */ + pinba_pool_destroy(&D->request_pool); + pinba_pool_init(&D->request_pool, D->request_pool.size, D->request_pool.element_size, D->request_pool.dtor); + pthread_rwlock_unlock(&D->collector_lock); + + DBUG_RETURN(0); +} +/* }}} */ + +int ha_pinba::info(uint flag) /* {{{ */ +{ + pinba_pool *p = &D->request_pool; + DBUG_ENTER("ha_pinba::info"); + + switch(share->table_type) { + case PINBA_TABLE_REQUEST: + pthread_rwlock_rdlock(&D->collector_lock); + stats.records = pinba_pool_num_records(p); + pthread_rwlock_unlock(&D->collector_lock); + break; + case PINBA_TABLE_TIMER: + pthread_rwlock_rdlock(&D->collector_lock); + stats.records = D->timers_cnt; + pthread_rwlock_unlock(&D->collector_lock); + break; + case PINBA_TABLE_TAG: + pthread_rwlock_rdlock(&D->collector_lock); + stats.records = JudyLCount(D->tag.table, 0, -1, NULL); + pthread_rwlock_unlock(&D->collector_lock); + break; + case PINBA_TABLE_TIMERTAG: + pthread_rwlock_rdlock(&D->collector_lock); + stats.records = D->timertags_cnt; + pthread_rwlock_unlock(&D->collector_lock); + break; + case PINBA_TABLE_INFO: + /* this is always true, no point to bother regenerating the report.. */ + stats.records = 1; + break; + case PINBA_TABLE_REPORT1: + stats.records = D->base_reports[PINBA_BASE_REPORT1].results_cnt; + break; + case PINBA_TABLE_REPORT2: + stats.records = D->base_reports[PINBA_BASE_REPORT2].results_cnt; + break; + case PINBA_TABLE_REPORT3: + stats.records = D->base_reports[PINBA_BASE_REPORT3].results_cnt; + break; + case PINBA_TABLE_REPORT4: + stats.records = D->base_reports[PINBA_BASE_REPORT4].results_cnt; + break; + case PINBA_TABLE_REPORT5: + stats.records = D->base_reports[PINBA_BASE_REPORT5].results_cnt; + break; + case PINBA_TABLE_REPORT6: + stats.records = D->base_reports[PINBA_BASE_REPORT6].results_cnt; + break; + case PINBA_TABLE_REPORT7: + stats.records = D->base_reports[PINBA_BASE_REPORT7].results_cnt; + break; + case PINBA_TABLE_TAG_INFO: + { + pinba_tag_report *report; + + pthread_rwlock_rdlock(&D->collector_lock); + pthread_rwlock_wrlock(&D->tag_reports_lock); + report = pinba_get_tag_report(PINBA_TAG_REPORT_INFO, share->params[0], NULL); + + if (!report) { + report = pinba_regenerate_tag_info(share->params[0], strlen(share->params[0])); + } + + stats.records = 0; + if (report) { + stats.records = report->results_cnt; + } + pthread_rwlock_unlock(&D->tag_reports_lock); + pthread_rwlock_unlock(&D->collector_lock); + } + break; + case PINBA_TABLE_TAG2_INFO: + { + pinba_tag_report *report; + + pthread_rwlock_rdlock(&D->collector_lock); + pthread_rwlock_wrlock(&D->tag_reports_lock); + report = pinba_get_tag_report(PINBA_TAG2_REPORT_INFO, share->params[0], share->params[1]); + + if (!report) { + report = pinba_regenerate_tag2_info(share->params[0], strlen(share->params[0]), share->params[1], strlen(share->params[1])); + } + + stats.records = 0; + if (report) { + stats.records = report->results_cnt; + } + pthread_rwlock_unlock(&D->tag_reports_lock); + pthread_rwlock_unlock(&D->collector_lock); + } + break; + case PINBA_TABLE_TAG_REPORT: + { + pinba_tag_report *report; + + pthread_rwlock_rdlock(&D->collector_lock); + pthread_rwlock_wrlock(&D->tag_reports_lock); + report = pinba_get_tag_report(PINBA_TAG_REPORT, share->params[0], NULL); + + if (!report) { + report = pinba_regenerate_tag_report(share->params[0], strlen(share->params[0])); + } + + stats.records = 0; + if (report) { + stats.records = report->results_cnt; + } + pthread_rwlock_unlock(&D->tag_reports_lock); + pthread_rwlock_unlock(&D->collector_lock); + } + break; + case PINBA_TABLE_TAG2_REPORT: + { + pinba_tag_report *report; + + pthread_rwlock_rdlock(&D->collector_lock); + pthread_rwlock_wrlock(&D->tag_reports_lock); + report = pinba_get_tag_report(PINBA_TAG2_REPORT, share->params[0], share->params[1]); + + if (!report) { + report = pinba_regenerate_tag2_report(share->params[0], strlen(share->params[0]), share->params[1], strlen(share->params[1])); + } + + stats.records = 0; + if (report) { + stats.records = report->results_cnt; + } + pthread_rwlock_unlock(&D->tag_reports_lock); + pthread_rwlock_unlock(&D->collector_lock); + } + break; + default: + stats.records = 2; /* dummy */ + break; + } + DBUG_RETURN(0); +} +/* }}} */ + +THR_LOCK_DATA **ha_pinba::store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type) /* {{{ */ +{ + if (lock_type != TL_IGNORE && lock.type == TL_UNLOCK) { + lock.type = lock_type; + } + *to++ = &lock; + return to; +} +/* }}} */ + +/* conf variables {{{ */ + +static MYSQL_SYSVAR_INT(port, + port_var, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "UDP port to listen at", + NULL, + NULL, + 30002, + 0, + 65536, + 0); + +static MYSQL_SYSVAR_STR(address, + address_var, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "IP address to listen at (leave it empty if you want to listen at any IP)", + NULL, + NULL, + NULL); + +static MYSQL_SYSVAR_INT(temp_pool_size, + temp_pool_size_var, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "Temporary pool size", + NULL, + NULL, + 10000, + 10, + INT_MAX, + 0); + +static MYSQL_SYSVAR_INT(request_pool_size, + request_pool_size_var, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "Request pool size", + NULL, + NULL, + 1000000, + 10, + INT_MAX, + 0); + +static MYSQL_SYSVAR_INT(stats_history, + stats_history_var, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "Request stats history (seconds)", + NULL, + NULL, + 900, /* 15 * 60 sec */ + 1, + INT_MAX, + 0); + +static MYSQL_SYSVAR_INT(stats_gathering_period, + stats_gathering_period_var, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "Request stats gathering period (microseconds)", + NULL, + NULL, + 10000, + 10, + INT_MAX, + 0); + +static MYSQL_SYSVAR_INT(tag_report_timeout, + tag_report_timeout_var, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "Tag report timeout (in seconds)", + NULL, + NULL, + 600, + -1, + INT_MAX, + 0); + +static MYSQL_SYSVAR_BOOL(show_protobuf_errors, + show_protobuf_errors_var, + PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY, + "Show protobuf errors and warnings", + NULL, + NULL, + FALSE); + +static struct st_mysql_sys_var* system_variables[]= { + MYSQL_SYSVAR(port), + MYSQL_SYSVAR(address), + MYSQL_SYSVAR(temp_pool_size), + MYSQL_SYSVAR(request_pool_size), + MYSQL_SYSVAR(stats_history), + MYSQL_SYSVAR(stats_gathering_period), + MYSQL_SYSVAR(tag_report_timeout), + MYSQL_SYSVAR(show_protobuf_errors), + NULL +}; +/* }}} */ + +struct st_mysql_storage_engine pinba_storage_engine = +{ MYSQL_HANDLERTON_INTERFACE_VERSION }; + +mysql_declare_plugin(pinba) /* {{{ */ +{ + MYSQL_STORAGE_ENGINE_PLUGIN, + &pinba_storage_engine, + "PINBA", + "Antony Dovgal", + "Pinba engine", + PLUGIN_LICENSE_GPL, + pinba_engine_init, /* Plugin Init */ + pinba_engine_shutdown, /* Plugin Deinit */ + 0x0006, + NULL, /* status variables */ + system_variables, /* system variables */ + NULL /* config options */ +} +mysql_declare_plugin_end; +/* }}} */ + +/* + * vim600: sw=4 ts=4 fdm=marker + */ diff --git a/src/ha_pinba.h b/src/ha_pinba.h new file mode 100644 index 0000000..6dbc020 --- /dev/null +++ b/src/ha_pinba.h @@ -0,0 +1,173 @@ +/* Copyright (c) 2007-2009 Antony Dovgal + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* $Id: ha_pinba.h,v 1.31.6.5 2009/03/23 15:21:20 tony Exp $ */ + +#ifdef USE_PRAGMA_INTERFACE +#pragma interface /* gcc class implementation */ +#endif + +#define PINBA_MAX_KEYS 2 + +enum { + PINBA_TABLE_UNKNOWN, + PINBA_TABLE_REQUEST, + PINBA_TABLE_TIMER, + PINBA_TABLE_TIMERTAG, + PINBA_TABLE_TAG, + PINBA_TABLE_INFO, + PINBA_TABLE_REPORT1, /* group by script_name */ + PINBA_TABLE_REPORT2, /* group by virtual host */ + PINBA_TABLE_REPORT3, /* group by hostname */ + PINBA_TABLE_REPORT4, /* group by virtual host, script_name */ + PINBA_TABLE_REPORT5, /* group by hostname, script_name */ + PINBA_TABLE_REPORT6, /* group by hostname, virtual_host */ + PINBA_TABLE_REPORT7, /* group by hostname, virtual_host and script_name */ + PINBA_TABLE_TAG_INFO, /* tag report grouped by custom tag */ + PINBA_TABLE_TAG2_INFO, /* tag report grouped by 2 custom tags */ + PINBA_TABLE_TAG_REPORT, /* tag report grouped by script_name and custom tag */ + PINBA_TABLE_TAG2_REPORT /* tag report grouped by script_name and 2 custom tags */ +}; + +typedef struct pinba_index_st { /* {{{ */ + union { + size_t ival; + struct { + unsigned char *val; + uint len; + } str; + }; + size_t position; +} pinba_index_st; +/* }}} */ + +/* this thing is SHAREd between threads! */ +typedef struct pinba_share_st { /* {{{ */ + char *table_name; + uint table_name_length; + uint use_count; + THR_LOCK lock; + unsigned char table_type; + char **params; + int params_num; +} PINBA_SHARE; +/* }}} */ + +/* + Class definition for the storage engine. + New instance of the class is created for each new thread. + */ +class ha_pinba: public handler +{ + THR_LOCK_DATA lock; /* MySQL lock */ + PINBA_SHARE *share; /* Shared lock info */ + char key_built_buffer[2048]; + size_t current_key_length; + char *current_key; + unsigned char *rec_buff; + size_t alloced_rec_buff_length; + size_t rec_buff_length; + pinba_index_st this_index[PINBA_MAX_KEYS]; + + int read_row_by_key(unsigned char *buf, uint active_index, const unsigned char *key, uint key_len, int exact); + int read_next_row(unsigned char *buf, uint active_index); + int read_index_first(unsigned char *buf, uint active_index); + + inline int requests_fetch_row(unsigned char *buf, size_t index, size_t *new_index); + inline int timers_fetch_row(unsigned char *buf, size_t index, size_t *new_index, int exact); + inline int timers_fetch_row_by_request_id(unsigned char*, size_t index, size_t*new_index); + + inline int tags_fetch_row(unsigned char *buf, size_t index, size_t *new_index); + inline int tags_fetch_row_by_name(unsigned char*, const unsigned char *name, uint name_len); + + inline int tag_values_fetch_next(unsigned char *buf, size_t *index, size_t *position); + inline int tag_values_fetch_by_timer_id(unsigned char *buf); + + inline int info_fetch_row(unsigned char *buf); + inline int report1_fetch_row(unsigned char *buf); + inline int report2_fetch_row(unsigned char *buf); + inline int report3_fetch_row(unsigned char *buf); + inline int report4_fetch_row(unsigned char *buf); + inline int report5_fetch_row(unsigned char *buf); + inline int report6_fetch_row(unsigned char *buf); + inline int report7_fetch_row(unsigned char *buf); + inline int tag_info_fetch_row(unsigned char *buf); + inline int tag2_info_fetch_row(unsigned char *buf); + inline int tag_report_fetch_row(unsigned char *buf); + inline int tag2_report_fetch_row(unsigned char *buf); + + public: + ha_pinba(handlerton *hton, TABLE_SHARE *table_arg); + ~ha_pinba() + { + } + + const char *table_type() const { + return "PINBA"; + } + + const char **bas_ext() const; + ulonglong table_flags() const + { + return (HA_NO_AUTO_INCREMENT|HA_BINLOG_ROW_CAPABLE|HA_NO_TRANSACTIONS|HA_STATS_RECORDS_IS_EXACT); + } + + ulong index_flags(uint inx, uint part, bool all_parts) const + { + return HA_READ_NEXT | HA_READ_PREV | HA_KEYREAD_ONLY; + } + + + const char *index_type(uint inx) + { + return ("HASH"); + } + + uint max_supported_record_length() const { return HA_MAX_REC_LENGTH; } + uint max_supported_keys() const { return PINBA_MAX_KEYS; } + uint max_supported_key_parts() const { return 1; } + uint max_supported_key_length() const { return 256; } + + /* methods */ + + int open(const char *name, int mode, uint test_if_locked); //required + int close(void); //required + int rnd_init(bool scan); //required + int rnd_next(unsigned char *buf); //required + int rnd_pos(unsigned char * buf, unsigned char *pos); //required + int rnd_end(); + int index_init(uint keynr, bool sorted); + int index_read(unsigned char * buf, const unsigned char * key, uint key_len, enum ha_rkey_function find_flag); + int index_next(unsigned char * buf); + int index_prev(unsigned char * buf); + /* + Even though the docs say index_first() is not required, 'SELECT * FROM WHERE > N' + is not going to work without it. See mysql_ha_read() in sql_handler.cc, line ~548. + */ + int index_first(unsigned char * buf); + + void position(const unsigned char *record); //required + int info(uint); //required + + int create(const char *name, TABLE *form, HA_CREATE_INFO *create_info); //required + int delete_all_rows(); + + THR_LOCK_DATA **store_lock(THD *thd, THR_LOCK_DATA **to, enum thr_lock_type lock_type); //required +}; + +/* + * vim600: sw=4 ts=4 fdm=marker + */ diff --git a/src/main.cc b/src/main.cc new file mode 100644 index 0000000..41a5ebe --- /dev/null +++ b/src/main.cc @@ -0,0 +1,330 @@ +/* Copyright (c) 2007-2009 Antony Dovgal + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* $Id: main.cc,v 1.1.2.14 2009/04/16 11:53:34 tony Exp $ */ + +#include "pinba.h" + +int pinba_collector_init(pinba_daemon_settings settings) /* {{{ */ +{ + int i; + + if (settings.port < 0 || settings.port >= 65536) { + pinba_error(P_ERROR, "port number is invalid (%d)", settings.port); + return P_FAILURE; + } + + if (settings.temp_pool_size < 10) { + pinba_error(P_ERROR, "temp_pool_size is too small (%zd)", settings.temp_pool_size); + return P_FAILURE; + } + + if (settings.request_pool_size < 10) { + pinba_error(P_ERROR, "request_pool_size is too small (%zd)", settings.request_pool_size); + return P_FAILURE; + } + + if (settings.show_protobuf_errors == 0) { + google::protobuf::SetLogHandler(NULL); + } + + pinba_debug("initializing collector"); + + D = (pinba_daemon *)calloc(1, sizeof(pinba_daemon)); + + D->base = event_base_new(); + + pthread_rwlock_init(&D->collector_lock, 0); + pthread_rwlock_init(&D->temp_lock, 0); + + pthread_rwlock_init(&D->tag_reports_lock, 0); + + if (pinba_pool_init(&D->temp_pool, settings.temp_pool_size + 1, sizeof(pinba_tmp_stats_record), pinba_temp_pool_dtor) != P_SUCCESS) { + return P_FAILURE; + } + + for (i = 0; i < settings.temp_pool_size + 1; i++) { + pinba_tmp_stats_record *tmp_record = TMP_POOL(&D->temp_pool) + i; + new (&(tmp_record->request)) Pinba::Request; + } + + if (pinba_pool_init(&D->request_pool, settings.request_pool_size + 1, sizeof(pinba_stats_record), pinba_request_pool_dtor) != P_SUCCESS) { + return P_FAILURE; + } + + if (pinba_pool_init(&D->timer_pool, PINBA_TIMER_POOL_GROW_SIZE, sizeof(pinba_timer_position), NULL) != P_SUCCESS) { + return P_FAILURE; + } + + D->timers_cnt = 0; + D->timertags_cnt = 0; + + D->settings = settings; + + for (i = 0; i < PINBA_BASE_REPORT_LAST; i++) { + pthread_rwlock_init(&(D->base_reports[i].lock), 0); + } + + return P_SUCCESS; +} +/* }}} */ + +void pinba_collector_shutdown(void) /* {{{ */ +{ + int i; + Word_t id; + pinba_word *word; + pinba_tag *tag; + PPvoid_t ppvalue; + + pinba_debug("shutting down.."); + pthread_rwlock_wrlock(&D->collector_lock); + pthread_rwlock_wrlock(&D->temp_lock); + + pinba_socket_free(D->collector_socket); + + + pinba_debug("shutting down with %ld (of %ld) elements in the pool", pinba_pool_num_records(&D->request_pool), D->request_pool.size); + pinba_debug("shutting down with %ld (of %ld) elements in the temp pool", pinba_pool_num_records(&D->temp_pool), D->temp_pool.size); + pinba_debug("shutting down with %ld (of %ld) elements in the timer pool", pinba_pool_num_records(&D->timer_pool), D->timer_pool.size); + + pinba_pool_destroy(&D->request_pool); + pinba_pool_destroy(&D->temp_pool); + pinba_pool_destroy(&D->timer_pool); + + pinba_debug("shutting down with %ld elements in tag.table", JudyLCount(D->tag.table, 0, -1, NULL)); + pinba_debug("shutting down with %ld elements in tag.name_index", JudyLCount(D->tag.name_index, 0, -1, NULL)); + + pthread_rwlock_unlock(&D->collector_lock); + pthread_rwlock_destroy(&D->collector_lock); + + pthread_rwlock_unlock(&D->temp_lock); + pthread_rwlock_destroy(&D->temp_lock); + + pinba_tag_reports_destroy(1); + JudySLFreeArray(&D->tag_reports, NULL); + + pthread_rwlock_unlock(&D->tag_reports_lock); + pthread_rwlock_destroy(&D->tag_reports_lock); + + pinba_reports_destroy(); + + for (i = 0; i < PINBA_BASE_REPORT_LAST; i++) { + pthread_rwlock_unlock(&D->base_reports[i].lock); + pthread_rwlock_destroy(&D->base_reports[i].lock); + } + + for (id = 0; id < D->dict.count; id++) { + word = D->dict.table[id]; + free(word->str); + free(word); + } + + id = 0; + for (ppvalue = JudyLFirst(D->tag.table, &id, NULL); ppvalue && ppvalue != PPJERR; ppvalue = JudyLNext(D->tag.table, &id, NULL)) { + tag = (pinba_tag *)*ppvalue; + free(tag); + } + + free(D->dict.table); + JudyLFreeArray(&D->tag.table, NULL); + JudySLFreeArray(&D->tag.name_index, NULL); + JudySLFreeArray(&D->dict.word_index, NULL); + + event_base_free(D->base); + free(D); + D = NULL; + + pinba_debug("collector shut down"); +} +/* }}} */ + +void *pinba_collector_main(void *arg) /* {{{ */ +{ + pinba_debug("starting up collector thread"); + + D->collector_socket = pinba_socket_open(D->settings.address, D->settings.port); + if (!D->collector_socket) { + return NULL; + } + + event_base_dispatch(D->base); + + /* unreachable */ + return NULL; +} +/* }}} */ + +char *pinba_error_ex(int return_error, int type, const char *file, int line, const char *format, ...) /* {{{ */ +{ + va_list args; + const char *type_name; + char *tmp; + int errormsg_len = 0; + char tmp_format[PINBA_ERR_BUFFER/2]; + char errormsg[PINBA_ERR_BUFFER]; + + switch (type) { + case P_DEBUG_DUMP: + type_name = "debug dump"; + break; + case P_DEBUG: + type_name = "debug"; + break; + case P_NOTICE: + type_name = "notice"; + break; + case P_WARNING: + type_name = "warning"; + break; + case P_ERROR: + type_name = "error"; + break; + default: + type_name = "unknown error"; + break; + } + + snprintf(tmp_format, PINBA_ERR_BUFFER/2, "[PINBA] %s: %s:%d %s", type_name, file, line, format); + + va_start(args, format); + errormsg_len = vsnprintf(errormsg, PINBA_ERR_BUFFER, tmp_format, args); + va_end(args); + + if (!return_error) { + fprintf(stderr, "%s\n", errormsg); + fflush(stderr); + return NULL; + } + tmp = strdup(errormsg); + return tmp; +} +/* }}} */ + +void pinba_udp_read_callback_fn(int sock, short event, void *arg) /* {{{ */ +{ + if (event & EV_READ) { + int ret; + unsigned char buf[PINBA_UDP_BUFFER_SIZE]; + struct sockaddr_in from; + socklen_t fromlen = sizeof(struct sockaddr_in); + + ret = recvfrom(sock, buf, PINBA_UDP_BUFFER_SIZE-1, MSG_DONTWAIT, (sockaddr *)&from, &fromlen); + if (ret > 0) { + /* pinba_debug("GOT DATA (%d bytes, errno: %d, error: %s)", ret, errno, errno ? strerror(errno) : ""); */ + if (pinba_process_stats_packet(buf, ret) != P_SUCCESS) { + pinba_debug("failed to parse data received from %s", inet_ntoa(from.sin_addr)); + } + } else if (ret < 0) { + if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) { + /* pinba_debug("read() failed: %s (%d)", strerror(errno), errno); */ + return; + } + pinba_error(P_WARNING, "recv() failed: %s (%d)", strerror(errno), errno); + } else { + pinba_error(P_WARNING, "recv() returned 0"); + } + } +} +/* }}} */ + +void pinba_socket_free(pinba_socket *socket) /* {{{ */ +{ + if (!socket) { + return; + } + + if (socket->listen_sock >= 0) { + close(socket->listen_sock); + socket->listen_sock = -1; + } + + if (socket->accept_event) { + event_del(socket->accept_event); + free(socket->accept_event); + socket->accept_event = NULL; + } + + free(socket); +} +/* }}} */ + +pinba_socket *pinba_socket_open(char *ip, int listen_port) /* {{{ */ +{ + struct sockaddr_in addr; + pinba_socket *s; + int sfd, flags, yes = 1; + + if ((sfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { + pinba_error(P_ERROR, "socket() failed: %s (%d)", strerror(errno), errno); + return NULL; + } + + if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 || fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) { + close(sfd); + return NULL; + } + + if(setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1) { + close(sfd); + return NULL; + } + + s = (pinba_socket *)calloc(1, sizeof(pinba_socket)); + if (!s) { + return NULL; + } + s->listen_sock = sfd; + + memset(&addr, 0, sizeof(addr)); + + addr.sin_family = AF_INET; + addr.sin_port = htons(listen_port); + addr.sin_addr.s_addr = htonl(INADDR_ANY); + + if (ip && *ip) { + struct in_addr tmp; + + if (inet_aton(ip, &tmp)) { + addr.sin_addr.s_addr = tmp.s_addr; + } else { + pinba_error(P_WARNING, "inet_aton(%s) failed, listening on ANY IP-address", ip); + } + } + + if (bind(s->listen_sock, (struct sockaddr *)&addr, sizeof(addr))) { + pinba_socket_free(s); + pinba_error(P_ERROR, "bind() failed: %s (%d)", strerror(errno), errno); + return NULL; + } + + s->accept_event = (struct event *)calloc(1, sizeof(struct event)); + if (!s->accept_event) { + pinba_error(P_ERROR, "calloc() failed: %s (%d)", strerror(errno), errno); + pinba_socket_free(s); + return NULL; + } + + event_set(s->accept_event, s->listen_sock, EV_READ | EV_PERSIST, pinba_udp_read_callback_fn, s); + event_base_set(D->base, s->accept_event); + event_add(s->accept_event, NULL); + return s; +} +/* }}} */ + +/* + * vim600: sw=4 ts=4 fdm=marker + */ diff --git a/src/pinba-pb.cc b/src/pinba-pb.cc new file mode 100644 index 0000000..30cb098 --- /dev/null +++ b/src/pinba-pb.cc @@ -0,0 +1,729 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! + +#include "pinba-pb.h" +#include +#include +#include +#include + +namespace Pinba { + +namespace { + +const ::google::protobuf::Descriptor* Request_descriptor_ = NULL; +const ::google::protobuf::internal::GeneratedMessageReflection* + Request_reflection_ = NULL; + +} // namespace + + +void protobuf_BuildDesc_pinba_2eproto_AssignGlobalDescriptors(const ::google::protobuf::FileDescriptor* file) { + Request_descriptor_ = file->message_type(0); + Request::default_instance_ = new Request(); + static const int Request_offsets_[15] = { + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, hostname_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, server_name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, script_name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, request_count_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, document_size_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, memory_peak_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, request_time_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, ru_utime_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, ru_stime_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, timer_hit_count_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, timer_value_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, timer_tag_count_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, timer_tag_name_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, timer_tag_value_), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, dictionary_), + }; + Request_reflection_ = + new ::google::protobuf::internal::GeneratedMessageReflection( + Request_descriptor_, + Request::default_instance_, + Request_offsets_, + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, _has_bits_[0]), + GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Request, _unknown_fields_), + -1, + ::google::protobuf::DescriptorPool::generated_pool(), + sizeof(Request)); + ::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage( + Request_descriptor_, Request::default_instance_); + Request::default_instance_->InitAsDefaultInstance(); +} + +void protobuf_BuildDesc_pinba_2eproto() { + static bool already_here = false; + if (already_here) return; + already_here = true; + GOOGLE_PROTOBUF_VERIFY_VERSION; + ::google::protobuf::DescriptorPool* pool = + ::google::protobuf::DescriptorPool::internal_generated_pool(); + + pool->InternalBuildGeneratedFile( + "\n\013pinba.proto\022\005Pinba\"\316\002\n\007Request\022\020\n\010host" + "name\030\001 \002(\t\022\023\n\013server_name\030\002 \002(\t\022\023\n\013scrip" + "t_name\030\003 \002(\t\022\025\n\rrequest_count\030\004 \002(\r\022\025\n\rd" + "ocument_size\030\005 \002(\r\022\023\n\013memory_peak\030\006 \002(\r\022" + "\024\n\014request_time\030\007 \002(\002\022\020\n\010ru_utime\030\010 \002(\002\022" + "\020\n\010ru_stime\030\t \002(\002\022\027\n\017timer_hit_count\030\n \003" + "(\r\022\023\n\013timer_value\030\013 \003(\002\022\027\n\017timer_tag_cou" + "nt\030\014 \003(\r\022\026\n\016timer_tag_name\030\r \003(\r\022\027\n\017time" + "r_tag_value\030\016 \003(\r\022\022\n\ndictionary\030\017 \003(\tB\002H" + "\001", 361, + &protobuf_BuildDesc_pinba_2eproto_AssignGlobalDescriptors); +} + +// Force BuildDescriptors() to be called at static initialization time. +struct StaticDescriptorInitializer_pinba_2eproto { + StaticDescriptorInitializer_pinba_2eproto() { + protobuf_BuildDesc_pinba_2eproto(); + } +} static_descriptor_initializer_pinba_2eproto_; + + +// =================================================================== + +const ::std::string Request::_default_hostname_; +const ::std::string Request::_default_server_name_; +const ::std::string Request::_default_script_name_; + + + + + + + + + + + + +Request::Request() + : ::google::protobuf::Message(), + _cached_size_(0), + hostname_(const_cast< ::std::string*>(&_default_hostname_)), + server_name_(const_cast< ::std::string*>(&_default_server_name_)), + script_name_(const_cast< ::std::string*>(&_default_script_name_)), + request_count_(0u), + document_size_(0u), + memory_peak_(0u), + request_time_(0), + ru_utime_(0), + ru_stime_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); +} + +void Request::InitAsDefaultInstance() {} + +Request::Request(const Request& from) + : ::google::protobuf::Message(), + _cached_size_(0), + hostname_(const_cast< ::std::string*>(&_default_hostname_)), + server_name_(const_cast< ::std::string*>(&_default_server_name_)), + script_name_(const_cast< ::std::string*>(&_default_script_name_)), + request_count_(0u), + document_size_(0u), + memory_peak_(0u), + request_time_(0), + ru_utime_(0), + ru_stime_(0) { + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + MergeFrom(from); +} + +Request::~Request() { + if (hostname_ != &_default_hostname_) { + delete hostname_; + } + if (server_name_ != &_default_server_name_) { + delete server_name_; + } + if (script_name_ != &_default_script_name_) { + delete script_name_; + } + if (this != default_instance_) { + } +} + +const ::google::protobuf::Descriptor* Request::descriptor() { + if (Request_descriptor_ == NULL) protobuf_BuildDesc_pinba_2eproto(); + return Request_descriptor_; +} + +const Request& Request::default_instance() { + if (default_instance_ == NULL) protobuf_BuildDesc_pinba_2eproto(); + return *default_instance_; +} + +Request* Request::default_instance_ = NULL; + +Request* Request::New() const { + return new Request; +} + +void Request::Clear() { + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (_has_bit(0)) { + if (hostname_ != &_default_hostname_) { + hostname_->clear(); + } + } + if (_has_bit(1)) { + if (server_name_ != &_default_server_name_) { + server_name_->clear(); + } + } + if (_has_bit(2)) { + if (script_name_ != &_default_script_name_) { + script_name_->clear(); + } + } + request_count_ = 0u; + document_size_ = 0u; + memory_peak_ = 0u; + request_time_ = 0; + ru_utime_ = 0; + } + if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) { + ru_stime_ = 0; + } + timer_hit_count_.Clear(); + timer_value_.Clear(); + timer_tag_count_.Clear(); + timer_tag_name_.Clear(); + timer_tag_value_.Clear(); + dictionary_.Clear(); + ::memset(_has_bits_, 0, sizeof(_has_bits_)); + mutable_unknown_fields()->Clear(); +} + +bool Request::MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input) { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + ::google::protobuf::uint32 tag; + while ((tag = input->ReadTag()) != 0) { + switch (::google::protobuf::internal::WireFormat::GetTagFieldNumber(tag)) { + // required string hostname = 1; + case 1: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_hostname())); + if (input->ExpectTag(18)) goto parse_server_name; + break; + } + + // required string server_name = 2; + case 2: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_server_name: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_server_name())); + if (input->ExpectTag(26)) goto parse_script_name; + break; + } + + // required string script_name = 3; + case 3: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_script_name: + DO_(::google::protobuf::internal::WireFormat::ReadString(input, mutable_script_name())); + if (input->ExpectTag(32)) goto parse_request_count; + break; + } + + // required uint32 request_count = 4; + case 4: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_request_count: + DO_(::google::protobuf::internal::WireFormat::ReadUInt32( + input, &request_count_)); + _set_bit(3); + if (input->ExpectTag(40)) goto parse_document_size; + break; + } + + // required uint32 document_size = 5; + case 5: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_document_size: + DO_(::google::protobuf::internal::WireFormat::ReadUInt32( + input, &document_size_)); + _set_bit(4); + if (input->ExpectTag(48)) goto parse_memory_peak; + break; + } + + // required uint32 memory_peak = 6; + case 6: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_memory_peak: + DO_(::google::protobuf::internal::WireFormat::ReadUInt32( + input, &memory_peak_)); + _set_bit(5); + if (input->ExpectTag(61)) goto parse_request_time; + break; + } + + // required float request_time = 7; + case 7: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_FIXED32) { + goto handle_uninterpreted; + } + parse_request_time: + DO_(::google::protobuf::internal::WireFormat::ReadFloat( + input, &request_time_)); + _set_bit(6); + if (input->ExpectTag(69)) goto parse_ru_utime; + break; + } + + // required float ru_utime = 8; + case 8: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_FIXED32) { + goto handle_uninterpreted; + } + parse_ru_utime: + DO_(::google::protobuf::internal::WireFormat::ReadFloat( + input, &ru_utime_)); + _set_bit(7); + if (input->ExpectTag(77)) goto parse_ru_stime; + break; + } + + // required float ru_stime = 9; + case 9: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_FIXED32) { + goto handle_uninterpreted; + } + parse_ru_stime: + DO_(::google::protobuf::internal::WireFormat::ReadFloat( + input, &ru_stime_)); + _set_bit(8); + if (input->ExpectTag(80)) goto parse_timer_hit_count; + break; + } + + // repeated uint32 timer_hit_count = 10; + case 10: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_timer_hit_count: + ::google::protobuf::uint32 value; + DO_(::google::protobuf::internal::WireFormat::ReadUInt32(input, &value)); + add_timer_hit_count(value); + if (input->ExpectTag(80)) goto parse_timer_hit_count; + if (input->ExpectTag(93)) goto parse_timer_value; + break; + } + + // repeated float timer_value = 11; + case 11: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_FIXED32) { + goto handle_uninterpreted; + } + parse_timer_value: + float value; + DO_(::google::protobuf::internal::WireFormat::ReadFloat(input, &value)); + add_timer_value(value); + if (input->ExpectTag(93)) goto parse_timer_value; + if (input->ExpectTag(96)) goto parse_timer_tag_count; + break; + } + + // repeated uint32 timer_tag_count = 12; + case 12: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_timer_tag_count: + ::google::protobuf::uint32 value; + DO_(::google::protobuf::internal::WireFormat::ReadUInt32(input, &value)); + add_timer_tag_count(value); + if (input->ExpectTag(96)) goto parse_timer_tag_count; + if (input->ExpectTag(104)) goto parse_timer_tag_name; + break; + } + + // repeated uint32 timer_tag_name = 13; + case 13: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_timer_tag_name: + ::google::protobuf::uint32 value; + DO_(::google::protobuf::internal::WireFormat::ReadUInt32(input, &value)); + add_timer_tag_name(value); + if (input->ExpectTag(104)) goto parse_timer_tag_name; + if (input->ExpectTag(112)) goto parse_timer_tag_value; + break; + } + + // repeated uint32 timer_tag_value = 14; + case 14: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_VARINT) { + goto handle_uninterpreted; + } + parse_timer_tag_value: + ::google::protobuf::uint32 value; + DO_(::google::protobuf::internal::WireFormat::ReadUInt32(input, &value)); + add_timer_tag_value(value); + if (input->ExpectTag(112)) goto parse_timer_tag_value; + if (input->ExpectTag(122)) goto parse_dictionary; + break; + } + + // repeated string dictionary = 15; + case 15: { + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) != + ::google::protobuf::internal::WireFormat::WIRETYPE_LENGTH_DELIMITED) { + goto handle_uninterpreted; + } + parse_dictionary: + DO_(::google::protobuf::internal::WireFormat::ReadString( + input, add_dictionary())); + if (input->ExpectTag(122)) goto parse_dictionary; + if (input->ExpectAtEnd()) return true; + break; + } + + default: { + handle_uninterpreted: + if (::google::protobuf::internal::WireFormat::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormat::WIRETYPE_END_GROUP) { + return true; + } + DO_(::google::protobuf::internal::WireFormat::SkipField( + input, tag, mutable_unknown_fields())); + break; + } + } + } + return true; +#undef DO_ +} + +bool Request::SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const { +#define DO_(EXPRESSION) if (!(EXPRESSION)) return false + // required string hostname = 1; + if (_has_bit(0)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(1, this->hostname(), output)); + } + + // required string server_name = 2; + if (_has_bit(1)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(2, this->server_name(), output)); + } + + // required string script_name = 3; + if (_has_bit(2)) { + DO_(::google::protobuf::internal::WireFormat::WriteString(3, this->script_name(), output)); + } + + // required uint32 request_count = 4; + if (_has_bit(3)) { + DO_(::google::protobuf::internal::WireFormat::WriteUInt32(4, this->request_count(), output)); + } + + // required uint32 document_size = 5; + if (_has_bit(4)) { + DO_(::google::protobuf::internal::WireFormat::WriteUInt32(5, this->document_size(), output)); + } + + // required uint32 memory_peak = 6; + if (_has_bit(5)) { + DO_(::google::protobuf::internal::WireFormat::WriteUInt32(6, this->memory_peak(), output)); + } + + // required float request_time = 7; + if (_has_bit(6)) { + DO_(::google::protobuf::internal::WireFormat::WriteFloat(7, this->request_time(), output)); + } + + // required float ru_utime = 8; + if (_has_bit(7)) { + DO_(::google::protobuf::internal::WireFormat::WriteFloat(8, this->ru_utime(), output)); + } + + // required float ru_stime = 9; + if (_has_bit(8)) { + DO_(::google::protobuf::internal::WireFormat::WriteFloat(9, this->ru_stime(), output)); + } + + // repeated uint32 timer_hit_count = 10; + for (int i = 0; i < timer_hit_count_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteUInt32(10, this->timer_hit_count(i), output)); + } + + // repeated float timer_value = 11; + for (int i = 0; i < timer_value_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteFloat(11, this->timer_value(i), output)); + } + + // repeated uint32 timer_tag_count = 12; + for (int i = 0; i < timer_tag_count_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteUInt32(12, this->timer_tag_count(i), output)); + } + + // repeated uint32 timer_tag_name = 13; + for (int i = 0; i < timer_tag_name_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteUInt32(13, this->timer_tag_name(i), output)); + } + + // repeated uint32 timer_tag_value = 14; + for (int i = 0; i < timer_tag_value_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteUInt32(14, this->timer_tag_value(i), output)); + } + + // repeated string dictionary = 15; + for (int i = 0; i < dictionary_.size(); i++) { + DO_(::google::protobuf::internal::WireFormat::WriteString(15, this->dictionary(i), output)); + } + + if (!unknown_fields().empty()) { + DO_(::google::protobuf::internal::WireFormat::SerializeUnknownFields( + unknown_fields(), output)); + } + return true; +#undef DO_ +} + +int Request::ByteSize() const { + int total_size = 0; + + if (_has_bits_[0 / 32] & (0xffu << (0 % 32))) { + // required string hostname = 1; + if (has_hostname()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->hostname()); + } + + // required string server_name = 2; + if (has_server_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->server_name()); + } + + // required string script_name = 3; + if (has_script_name()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::StringSize(this->script_name()); + } + + // required uint32 request_count = 4; + if (has_request_count()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::UInt32Size( + this->request_count()); + } + + // required uint32 document_size = 5; + if (has_document_size()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::UInt32Size( + this->document_size()); + } + + // required uint32 memory_peak = 6; + if (has_memory_peak()) { + total_size += 1 + + ::google::protobuf::internal::WireFormat::UInt32Size( + this->memory_peak()); + } + + // required float request_time = 7; + if (has_request_time()) { + total_size += 1 + 4; + } + + // required float ru_utime = 8; + if (has_ru_utime()) { + total_size += 1 + 4; + } + + } + if (_has_bits_[8 / 32] & (0xffu << (8 % 32))) { + // required float ru_stime = 9; + if (has_ru_stime()) { + total_size += 1 + 4; + } + + } + // repeated uint32 timer_hit_count = 10; + total_size += 1 * timer_hit_count_size(); + for (int i = 0; i < timer_hit_count_size(); i++) { + total_size += ::google::protobuf::internal::WireFormat::UInt32Size( + this->timer_hit_count(i)); + } + + // repeated float timer_value = 11; + total_size += (1 + 4) * timer_value_size(); + + // repeated uint32 timer_tag_count = 12; + total_size += 1 * timer_tag_count_size(); + for (int i = 0; i < timer_tag_count_size(); i++) { + total_size += ::google::protobuf::internal::WireFormat::UInt32Size( + this->timer_tag_count(i)); + } + + // repeated uint32 timer_tag_name = 13; + total_size += 1 * timer_tag_name_size(); + for (int i = 0; i < timer_tag_name_size(); i++) { + total_size += ::google::protobuf::internal::WireFormat::UInt32Size( + this->timer_tag_name(i)); + } + + // repeated uint32 timer_tag_value = 14; + total_size += 1 * timer_tag_value_size(); + for (int i = 0; i < timer_tag_value_size(); i++) { + total_size += ::google::protobuf::internal::WireFormat::UInt32Size( + this->timer_tag_value(i)); + } + + // repeated string dictionary = 15; + total_size += 1 * dictionary_size(); + for (int i = 0; i < dictionary_size(); i++) { + total_size += ::google::protobuf::internal::WireFormat::StringSize( + this->dictionary(i)); + } + + if (!unknown_fields().empty()) { + total_size += + ::google::protobuf::internal::WireFormat::ComputeUnknownFieldsSize( + unknown_fields()); + } + _cached_size_ = total_size; + return total_size; +} + +void Request::MergeFrom(const ::google::protobuf::Message& from) { + GOOGLE_CHECK_NE(&from, this); + const Request* source = + ::google::protobuf::internal::dynamic_cast_if_available( + &from); + if (source == NULL) { + ::google::protobuf::internal::ReflectionOps::Merge(from, this); + } else { + MergeFrom(*source); + } +} + +void Request::MergeFrom(const Request& from) { + GOOGLE_CHECK_NE(&from, this); + timer_hit_count_.MergeFrom(from.timer_hit_count_); + timer_value_.MergeFrom(from.timer_value_); + timer_tag_count_.MergeFrom(from.timer_tag_count_); + timer_tag_name_.MergeFrom(from.timer_tag_name_); + timer_tag_value_.MergeFrom(from.timer_tag_value_); + dictionary_.MergeFrom(from.dictionary_); + if (from._has_bits_[0 / 32] & (0xffu << (0 % 32))) { + if (from._has_bit(0)) { + set_hostname(from.hostname()); + } + if (from._has_bit(1)) { + set_server_name(from.server_name()); + } + if (from._has_bit(2)) { + set_script_name(from.script_name()); + } + if (from._has_bit(3)) { + set_request_count(from.request_count()); + } + if (from._has_bit(4)) { + set_document_size(from.document_size()); + } + if (from._has_bit(5)) { + set_memory_peak(from.memory_peak()); + } + if (from._has_bit(6)) { + set_request_time(from.request_time()); + } + if (from._has_bit(7)) { + set_ru_utime(from.ru_utime()); + } + } + if (from._has_bits_[8 / 32] & (0xffu << (8 % 32))) { + if (from._has_bit(8)) { + set_ru_stime(from.ru_stime()); + } + } + mutable_unknown_fields()->MergeFrom(from.unknown_fields()); +} + +void Request::CopyFrom(const ::google::protobuf::Message& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Request::CopyFrom(const Request& from) { + if (&from == this) return; + Clear(); + MergeFrom(from); +} + +void Request::Swap(Request* other) { + if (other != this) { + std::swap(hostname_, other->hostname_); + std::swap(server_name_, other->server_name_); + std::swap(script_name_, other->script_name_); + std::swap(request_count_, other->request_count_); + std::swap(document_size_, other->document_size_); + std::swap(memory_peak_, other->memory_peak_); + std::swap(request_time_, other->request_time_); + std::swap(ru_utime_, other->ru_utime_); + std::swap(ru_stime_, other->ru_stime_); + timer_hit_count_.Swap(&other->timer_hit_count_); + timer_value_.Swap(&other->timer_value_); + timer_tag_count_.Swap(&other->timer_tag_count_); + timer_tag_name_.Swap(&other->timer_tag_name_); + timer_tag_value_.Swap(&other->timer_tag_value_); + dictionary_.Swap(&other->dictionary_); + std::swap(_has_bits_[0], other->_has_bits_[0]); + _unknown_fields_.Swap(&other->_unknown_fields_); + std::swap(_cached_size_, other->_cached_size_); + } +} + +bool Request::IsInitialized() const { + if ((_has_bits_[0] & 0x000001ff) != 0x000001ff) return false; + + return true; +} + +const ::google::protobuf::Descriptor* Request::GetDescriptor() const { + return descriptor(); +} + +const ::google::protobuf::Reflection* Request::GetReflection() const { + if (Request_reflection_ == NULL) protobuf_BuildDesc_pinba_2eproto(); + return Request_reflection_; +} + +} // namespace Pinba diff --git a/src/pinba-pb.h b/src/pinba-pb.h new file mode 100644 index 0000000..6b66099 --- /dev/null +++ b/src/pinba-pb.h @@ -0,0 +1,620 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! + +#ifndef PROTOBUF_pinba_2eproto__INCLUDED +#define PROTOBUF_pinba_2eproto__INCLUDED + +#include + +#include + +#if GOOGLE_PROTOBUF_VERSION < 2000003 +#error This file was generated by a newer version of protoc which is +#error incompatible with your Protocol Buffer headers. Please update +#error your headers. +#endif +#if 2000003 < GOOGLE_PROTOBUF_MIN_PROTOC_VERSION +#error This file was generated by an older version of protoc which is +#error incompatible with your Protocol Buffer headers. Please +#error regenerate this file with a newer version of protoc. +#endif + +#include +#include +#include + +namespace Pinba { + +// Internal implementation detail -- do not call these. +void protobuf_BuildDesc_pinba_2eproto(); +void protobuf_BuildDesc_pinba_2eproto_AssignGlobalDescriptors( + ::google::protobuf::FileDescriptor* file); + +class Request; + +// =================================================================== + +class Request : public ::google::protobuf::Message { + public: + Request(); + virtual ~Request(); + + Request(const Request& from); + + inline Request& operator=(const Request& from) { + CopyFrom(from); + return *this; + } + + inline const ::google::protobuf::UnknownFieldSet& unknown_fields() const { + return _unknown_fields_; + } + + inline ::google::protobuf::UnknownFieldSet* mutable_unknown_fields() { + return &_unknown_fields_; + } + + static const ::google::protobuf::Descriptor* descriptor(); + static const Request& default_instance(); + void Swap(Request* other); + + // implements Message ---------------------------------------------- + + Request* New() const; + void CopyFrom(const ::google::protobuf::Message& from); + void MergeFrom(const ::google::protobuf::Message& from); + void CopyFrom(const Request& from); + void MergeFrom(const Request& from); + void Clear(); + bool IsInitialized() const; + int ByteSize() const; + + bool MergePartialFromCodedStream( + ::google::protobuf::io::CodedInputStream* input); + bool SerializeWithCachedSizes( + ::google::protobuf::io::CodedOutputStream* output) const; + int GetCachedSize() const { return _cached_size_; } + private: + void SetCachedSize(int size) const { _cached_size_ = size; } + public: + + const ::google::protobuf::Descriptor* GetDescriptor() const; + const ::google::protobuf::Reflection* GetReflection() const; + + // nested types ---------------------------------------------------- + + // accessors ------------------------------------------------------- + + // required string hostname = 1; + inline bool has_hostname() const; + inline void clear_hostname(); + inline const ::std::string& hostname() const; + inline void set_hostname(const ::std::string& value); + inline void set_hostname(const char* value); + inline ::std::string* mutable_hostname(); + + // required string server_name = 2; + inline bool has_server_name() const; + inline void clear_server_name(); + inline const ::std::string& server_name() const; + inline void set_server_name(const ::std::string& value); + inline void set_server_name(const char* value); + inline ::std::string* mutable_server_name(); + + // required string script_name = 3; + inline bool has_script_name() const; + inline void clear_script_name(); + inline const ::std::string& script_name() const; + inline void set_script_name(const ::std::string& value); + inline void set_script_name(const char* value); + inline ::std::string* mutable_script_name(); + + // required uint32 request_count = 4; + inline bool has_request_count() const; + inline void clear_request_count(); + inline ::google::protobuf::uint32 request_count() const; + inline void set_request_count(::google::protobuf::uint32 value); + + // required uint32 document_size = 5; + inline bool has_document_size() const; + inline void clear_document_size(); + inline ::google::protobuf::uint32 document_size() const; + inline void set_document_size(::google::protobuf::uint32 value); + + // required uint32 memory_peak = 6; + inline bool has_memory_peak() const; + inline void clear_memory_peak(); + inline ::google::protobuf::uint32 memory_peak() const; + inline void set_memory_peak(::google::protobuf::uint32 value); + + // required float request_time = 7; + inline bool has_request_time() const; + inline void clear_request_time(); + inline float request_time() const; + inline void set_request_time(float value); + + // required float ru_utime = 8; + inline bool has_ru_utime() const; + inline void clear_ru_utime(); + inline float ru_utime() const; + inline void set_ru_utime(float value); + + // required float ru_stime = 9; + inline bool has_ru_stime() const; + inline void clear_ru_stime(); + inline float ru_stime() const; + inline void set_ru_stime(float value); + + // repeated uint32 timer_hit_count = 10; + inline int timer_hit_count_size() const; + inline void clear_timer_hit_count(); + inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >& timer_hit_count() const; + inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >* mutable_timer_hit_count(); + inline ::google::protobuf::uint32 timer_hit_count(int index) const; + inline void set_timer_hit_count(int index, ::google::protobuf::uint32 value); + inline void add_timer_hit_count(::google::protobuf::uint32 value); + + // repeated float timer_value = 11; + inline int timer_value_size() const; + inline void clear_timer_value(); + inline const ::google::protobuf::RepeatedField< float >& timer_value() const; + inline ::google::protobuf::RepeatedField< float >* mutable_timer_value(); + inline float timer_value(int index) const; + inline void set_timer_value(int index, float value); + inline void add_timer_value(float value); + + // repeated uint32 timer_tag_count = 12; + inline int timer_tag_count_size() const; + inline void clear_timer_tag_count(); + inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >& timer_tag_count() const; + inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >* mutable_timer_tag_count(); + inline ::google::protobuf::uint32 timer_tag_count(int index) const; + inline void set_timer_tag_count(int index, ::google::protobuf::uint32 value); + inline void add_timer_tag_count(::google::protobuf::uint32 value); + + // repeated uint32 timer_tag_name = 13; + inline int timer_tag_name_size() const; + inline void clear_timer_tag_name(); + inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >& timer_tag_name() const; + inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >* mutable_timer_tag_name(); + inline ::google::protobuf::uint32 timer_tag_name(int index) const; + inline void set_timer_tag_name(int index, ::google::protobuf::uint32 value); + inline void add_timer_tag_name(::google::protobuf::uint32 value); + + // repeated uint32 timer_tag_value = 14; + inline int timer_tag_value_size() const; + inline void clear_timer_tag_value(); + inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >& timer_tag_value() const; + inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >* mutable_timer_tag_value(); + inline ::google::protobuf::uint32 timer_tag_value(int index) const; + inline void set_timer_tag_value(int index, ::google::protobuf::uint32 value); + inline void add_timer_tag_value(::google::protobuf::uint32 value); + + // repeated string dictionary = 15; + inline int dictionary_size() const; + inline void clear_dictionary(); + inline const ::google::protobuf::RepeatedPtrField< ::std::string>& dictionary() const; + inline ::google::protobuf::RepeatedPtrField< ::std::string>* mutable_dictionary(); + inline const ::std::string& dictionary(int index) const; + inline ::std::string* mutable_dictionary(int index); + inline void set_dictionary(int index, const ::std::string& value); + inline void set_dictionary(int index, const char* value); + inline ::std::string* add_dictionary(); + inline void add_dictionary(const ::std::string& value); + inline void add_dictionary(const char* value); + + private: + ::google::protobuf::UnknownFieldSet _unknown_fields_; + mutable int _cached_size_; + + ::std::string* hostname_; + static const ::std::string _default_hostname_; + ::std::string* server_name_; + static const ::std::string _default_server_name_; + ::std::string* script_name_; + static const ::std::string _default_script_name_; + ::google::protobuf::uint32 request_count_; + ::google::protobuf::uint32 document_size_; + ::google::protobuf::uint32 memory_peak_; + float request_time_; + float ru_utime_; + float ru_stime_; + ::google::protobuf::RepeatedField< ::google::protobuf::uint32 > timer_hit_count_; + ::google::protobuf::RepeatedField< float > timer_value_; + ::google::protobuf::RepeatedField< ::google::protobuf::uint32 > timer_tag_count_; + ::google::protobuf::RepeatedField< ::google::protobuf::uint32 > timer_tag_name_; + ::google::protobuf::RepeatedField< ::google::protobuf::uint32 > timer_tag_value_; + ::google::protobuf::RepeatedPtrField< ::std::string> dictionary_; + friend void protobuf_BuildDesc_pinba_2eproto_AssignGlobalDescriptors( + const ::google::protobuf::FileDescriptor* file); + ::google::protobuf::uint32 _has_bits_[(15 + 31) / 32]; + + // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? + inline bool _has_bit(int index) const { + return (_has_bits_[index / 32] & (1u << (index % 32))) != 0; + } + inline void _set_bit(int index) { + _has_bits_[index / 32] |= (1u << (index % 32)); + } + inline void _clear_bit(int index) { + _has_bits_[index / 32] &= ~(1u << (index % 32)); + } + + void InitAsDefaultInstance(); + static Request* default_instance_; +}; +// =================================================================== + + +// =================================================================== + + +// =================================================================== + +// Request + +// required string hostname = 1; +inline bool Request::has_hostname() const { + return _has_bit(0); +} +inline void Request::clear_hostname() { + if (hostname_ != &_default_hostname_) { + hostname_->clear(); + } + _clear_bit(0); +} +inline const ::std::string& Request::hostname() const { + return *hostname_; +} +inline void Request::set_hostname(const ::std::string& value) { + _set_bit(0); + if (hostname_ == &_default_hostname_) { + hostname_ = new ::std::string; + } + hostname_->assign(value); +} +inline void Request::set_hostname(const char* value) { + _set_bit(0); + if (hostname_ == &_default_hostname_) { + hostname_ = new ::std::string; + } + hostname_->assign(value); +} +inline ::std::string* Request::mutable_hostname() { + _set_bit(0); + if (hostname_ == &_default_hostname_) { + hostname_ = new ::std::string; + } + return hostname_; +} + +// required string server_name = 2; +inline bool Request::has_server_name() const { + return _has_bit(1); +} +inline void Request::clear_server_name() { + if (server_name_ != &_default_server_name_) { + server_name_->clear(); + } + _clear_bit(1); +} +inline const ::std::string& Request::server_name() const { + return *server_name_; +} +inline void Request::set_server_name(const ::std::string& value) { + _set_bit(1); + if (server_name_ == &_default_server_name_) { + server_name_ = new ::std::string; + } + server_name_->assign(value); +} +inline void Request::set_server_name(const char* value) { + _set_bit(1); + if (server_name_ == &_default_server_name_) { + server_name_ = new ::std::string; + } + server_name_->assign(value); +} +inline ::std::string* Request::mutable_server_name() { + _set_bit(1); + if (server_name_ == &_default_server_name_) { + server_name_ = new ::std::string; + } + return server_name_; +} + +// required string script_name = 3; +inline bool Request::has_script_name() const { + return _has_bit(2); +} +inline void Request::clear_script_name() { + if (script_name_ != &_default_script_name_) { + script_name_->clear(); + } + _clear_bit(2); +} +inline const ::std::string& Request::script_name() const { + return *script_name_; +} +inline void Request::set_script_name(const ::std::string& value) { + _set_bit(2); + if (script_name_ == &_default_script_name_) { + script_name_ = new ::std::string; + } + script_name_->assign(value); +} +inline void Request::set_script_name(const char* value) { + _set_bit(2); + if (script_name_ == &_default_script_name_) { + script_name_ = new ::std::string; + } + script_name_->assign(value); +} +inline ::std::string* Request::mutable_script_name() { + _set_bit(2); + if (script_name_ == &_default_script_name_) { + script_name_ = new ::std::string; + } + return script_name_; +} + +// required uint32 request_count = 4; +inline bool Request::has_request_count() const { + return _has_bit(3); +} +inline void Request::clear_request_count() { + request_count_ = 0u; + _clear_bit(3); +} +inline ::google::protobuf::uint32 Request::request_count() const { + return request_count_; +} +inline void Request::set_request_count(::google::protobuf::uint32 value) { + _set_bit(3); + request_count_ = value; +} + +// required uint32 document_size = 5; +inline bool Request::has_document_size() const { + return _has_bit(4); +} +inline void Request::clear_document_size() { + document_size_ = 0u; + _clear_bit(4); +} +inline ::google::protobuf::uint32 Request::document_size() const { + return document_size_; +} +inline void Request::set_document_size(::google::protobuf::uint32 value) { + _set_bit(4); + document_size_ = value; +} + +// required uint32 memory_peak = 6; +inline bool Request::has_memory_peak() const { + return _has_bit(5); +} +inline void Request::clear_memory_peak() { + memory_peak_ = 0u; + _clear_bit(5); +} +inline ::google::protobuf::uint32 Request::memory_peak() const { + return memory_peak_; +} +inline void Request::set_memory_peak(::google::protobuf::uint32 value) { + _set_bit(5); + memory_peak_ = value; +} + +// required float request_time = 7; +inline bool Request::has_request_time() const { + return _has_bit(6); +} +inline void Request::clear_request_time() { + request_time_ = 0; + _clear_bit(6); +} +inline float Request::request_time() const { + return request_time_; +} +inline void Request::set_request_time(float value) { + _set_bit(6); + request_time_ = value; +} + +// required float ru_utime = 8; +inline bool Request::has_ru_utime() const { + return _has_bit(7); +} +inline void Request::clear_ru_utime() { + ru_utime_ = 0; + _clear_bit(7); +} +inline float Request::ru_utime() const { + return ru_utime_; +} +inline void Request::set_ru_utime(float value) { + _set_bit(7); + ru_utime_ = value; +} + +// required float ru_stime = 9; +inline bool Request::has_ru_stime() const { + return _has_bit(8); +} +inline void Request::clear_ru_stime() { + ru_stime_ = 0; + _clear_bit(8); +} +inline float Request::ru_stime() const { + return ru_stime_; +} +inline void Request::set_ru_stime(float value) { + _set_bit(8); + ru_stime_ = value; +} + +// repeated uint32 timer_hit_count = 10; +inline int Request::timer_hit_count_size() const { + return timer_hit_count_.size(); +} +inline void Request::clear_timer_hit_count() { + timer_hit_count_.Clear(); +} +inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >& +Request::timer_hit_count() const { + return timer_hit_count_; +} +inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >* +Request::mutable_timer_hit_count() { + return &timer_hit_count_; +} +inline ::google::protobuf::uint32 Request::timer_hit_count(int index) const { + return timer_hit_count_.Get(index); +} +inline void Request::set_timer_hit_count(int index, ::google::protobuf::uint32 value) { + timer_hit_count_.Set(index, value); +} +inline void Request::add_timer_hit_count(::google::protobuf::uint32 value) { + timer_hit_count_.Add(value); +} + +// repeated float timer_value = 11; +inline int Request::timer_value_size() const { + return timer_value_.size(); +} +inline void Request::clear_timer_value() { + timer_value_.Clear(); +} +inline const ::google::protobuf::RepeatedField< float >& +Request::timer_value() const { + return timer_value_; +} +inline ::google::protobuf::RepeatedField< float >* +Request::mutable_timer_value() { + return &timer_value_; +} +inline float Request::timer_value(int index) const { + return timer_value_.Get(index); +} +inline void Request::set_timer_value(int index, float value) { + timer_value_.Set(index, value); +} +inline void Request::add_timer_value(float value) { + timer_value_.Add(value); +} + +// repeated uint32 timer_tag_count = 12; +inline int Request::timer_tag_count_size() const { + return timer_tag_count_.size(); +} +inline void Request::clear_timer_tag_count() { + timer_tag_count_.Clear(); +} +inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >& +Request::timer_tag_count() const { + return timer_tag_count_; +} +inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >* +Request::mutable_timer_tag_count() { + return &timer_tag_count_; +} +inline ::google::protobuf::uint32 Request::timer_tag_count(int index) const { + return timer_tag_count_.Get(index); +} +inline void Request::set_timer_tag_count(int index, ::google::protobuf::uint32 value) { + timer_tag_count_.Set(index, value); +} +inline void Request::add_timer_tag_count(::google::protobuf::uint32 value) { + timer_tag_count_.Add(value); +} + +// repeated uint32 timer_tag_name = 13; +inline int Request::timer_tag_name_size() const { + return timer_tag_name_.size(); +} +inline void Request::clear_timer_tag_name() { + timer_tag_name_.Clear(); +} +inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >& +Request::timer_tag_name() const { + return timer_tag_name_; +} +inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >* +Request::mutable_timer_tag_name() { + return &timer_tag_name_; +} +inline ::google::protobuf::uint32 Request::timer_tag_name(int index) const { + return timer_tag_name_.Get(index); +} +inline void Request::set_timer_tag_name(int index, ::google::protobuf::uint32 value) { + timer_tag_name_.Set(index, value); +} +inline void Request::add_timer_tag_name(::google::protobuf::uint32 value) { + timer_tag_name_.Add(value); +} + +// repeated uint32 timer_tag_value = 14; +inline int Request::timer_tag_value_size() const { + return timer_tag_value_.size(); +} +inline void Request::clear_timer_tag_value() { + timer_tag_value_.Clear(); +} +inline const ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >& +Request::timer_tag_value() const { + return timer_tag_value_; +} +inline ::google::protobuf::RepeatedField< ::google::protobuf::uint32 >* +Request::mutable_timer_tag_value() { + return &timer_tag_value_; +} +inline ::google::protobuf::uint32 Request::timer_tag_value(int index) const { + return timer_tag_value_.Get(index); +} +inline void Request::set_timer_tag_value(int index, ::google::protobuf::uint32 value) { + timer_tag_value_.Set(index, value); +} +inline void Request::add_timer_tag_value(::google::protobuf::uint32 value) { + timer_tag_value_.Add(value); +} + +// repeated string dictionary = 15; +inline int Request::dictionary_size() const { + return dictionary_.size(); +} +inline void Request::clear_dictionary() { + dictionary_.Clear(); +} +inline const ::google::protobuf::RepeatedPtrField< ::std::string>& +Request::dictionary() const { + return dictionary_; +} +inline ::google::protobuf::RepeatedPtrField< ::std::string>* +Request::mutable_dictionary() { + return &dictionary_; +} +inline const ::std::string& Request::dictionary(int index) const { + return dictionary_.Get(index); +} +inline ::std::string* Request::mutable_dictionary(int index) { + return dictionary_.Mutable(index); +} +inline void Request::set_dictionary(int index, const ::std::string& value) { + dictionary_.Mutable(index)->assign(value); +} +inline void Request::set_dictionary(int index, const char* value) { + dictionary_.Mutable(index)->assign(value); +} +inline ::std::string* Request::add_dictionary() { + return dictionary_.Add(); +} +inline void Request::add_dictionary(const ::std::string& value) { + dictionary_.Add()->assign(value); +} +inline void Request::add_dictionary(const char* value) { + dictionary_.Add()->assign(value); +} + + +} // namespace Pinba +#endif // PROTOBUF_pinba_2eproto__INCLUDED diff --git a/src/pinba.h b/src/pinba.h new file mode 100644 index 0000000..ed73d6c --- /dev/null +++ b/src/pinba.h @@ -0,0 +1,190 @@ +/* Copyright (c) 2007-2009 Antony Dovgal + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* $Id: pinba.h,v 1.30.4.3.2.13 2009/04/16 11:53:34 tony Exp $ */ + +#ifndef PINBA_H +#define PINBA_H + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +extern "C" { +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +} + +#include "pinba-pb.h" +#include "pinba_config.h" +#include "pinba_types.h" + +#define PINBA_DEBUG + +#undef P_SUCCESS +#undef P_FAILURE +#define P_SUCCESS 0 +#define P_FAILURE -1 + + +#define P_ERROR (1<<0L) +#define P_WARNING (1<<1L) +#define P_NOTICE (1<<2L) +#define P_DEBUG (1<<3L) +#define P_DEBUG_DUMP (1<<4L) + +char *pinba_error_ex(int return_error, int type, const char *file, int line, const char *format, ...); + +#ifdef PINBA_DEBUG +#define pinba_debug(...) pinba_error_ex(0, P_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#else +#define pinba_debug(...) +#endif + +#define pinba_error(type, ...) pinba_error_ex(0, type, __FILE__, __LINE__, __VA_ARGS__) +#define pinba_error_get(type, ...) pinba_error_ex(1, type, __FILE__, __LINE__, __VA_ARGS__) +extern pinba_daemon *D; + +#ifdef __GNUC__ +#define LIKELY(x) __builtin_expect((x),1) +#define UNLIKELY(x) __builtin_expect((x),0) +#else +#define LIKELY(x) x +#define UNLIKELY(x) x +#endif + +void *pinba_collector_main(void *arg); +void *pinba_stats_main(void *arg); +int pinba_collector_init(pinba_daemon_settings settings); +void pinba_collector_shutdown(); + +int pinba_process_stats_packet(const unsigned char *buffer, int buffer_len); + +void pinba_udp_read_callback_fn(int sock, short event, void *arg); +void pinba_socket_free(pinba_socket *socket); +pinba_socket *pinba_socket_open(char *ip, int listen_port); + +void pinba_tag_dtor(pinba_tag *tag); +int pinba_tag_put(const unsigned char *name); +pinba_tag *pinba_tag_get_by_name(const unsigned char *name); +pinba_tag *pinba_tag_get_by_name_next(unsigned char *name); +pinba_tag *pinba_tag_get_by_id(size_t id); +void pinba_tag_delete_by_name(const unsigned char *name); +void pinba_tag_delete_by_id(size_t id); + +int pinba_tag_values_put(pinba_timer_record *timer, unsigned int *values, int *values_lens, int values_num); +void pinba_tag_value_delete(size_t timer_id); + +void pinba_update_reports_add(const pinba_stats_record *record); +void pinba_update_reports_delete(const pinba_stats_record *record); +void pinba_update_tag_reports_add(int request_id, const pinba_stats_record *record); +void pinba_update_tag_reports_delete(int request_id, const pinba_stats_record *record); +void pinba_reports_destroy(void); +void pinba_tag_reports_destroy(int force); + +/* go over all new records in the pool */ +#define pool_traverse_forward(i, pool) \ + for (i = (pool)->out; i != (pool)->in; i = (i == (pool)->size - 1) ? 0 : i + 1) + +/* go over all records in the pool */ +#define pool_traverse_backward(i, pool) \ + for (i = ((pool)->in > 0) ? (pool)->in - 1 : 0; \ + i != ((pool)->out ? (pool)->out : ((pool)->in ? ((pool)->size - 1) : 0)); \ + i = (i == 0) ? ((pool)->size - 1) : i - 1) + +#define TMP_POOL(pool) ((pinba_tmp_stats_record *)((pool)->data)) +#define REQ_POOL(pool) ((pinba_stats_record *)((pool)->data)) +#define TIMER_POOL(pool) ((pinba_timer_position *)((pool)->data)) +#define POOL_DATA(pool) ((void **)((pool)->data)) + +#define memcpy_static(buf, str, str_len, result_len) \ +do { \ + if ((int)(sizeof(buf) - 1) < str_len) { \ + /* truncate the string */ \ + memcpy(buf, str, sizeof(buf) - 1); \ + buf[sizeof(buf) - 1] = '\0'; \ + result_len = sizeof(buf) - 1; \ + } else { \ + memcpy(buf, str, str_len); \ + buf[str_len] = '\0'; \ + result_len = str_len; \ + } \ +} while(0) + +#define memcat_static(buf, plus, str, str_len, result_len) \ +do { \ + register int __n = sizeof(buf); \ + \ + if ((plus) >= __n) { \ + break; \ + } \ + \ + if ((__n - (plus) - 1) < str_len) { \ + /* truncate the string */ \ + memcpy(buf + (plus), str, __n - (plus)); \ + buf[__n - 1] = '\0'; \ + result_len = __n - 1; \ + } else { \ + memcpy(buf + (plus), str, str_len); \ + buf[(plus) + str_len] = '\0'; \ + result_len = (plus) + str_len; \ + } \ +} while(0) + +size_t pinba_pool_num_records(pinba_pool *p); +int pinba_pool_init(pinba_pool *p, size_t size, size_t element_size, pool_dtor_func_t dtor); +void pinba_pool_destroy(pinba_pool *p); + + +/* utility macros */ + +#define timeval_to_float(tv) (float)tv.tv_sec + (float)tv.tv_usec / 1000000.0 + +static inline pinba_timeval float_to_timeval(double f) /* {{{ */ +{ + pinba_timeval t; + double fraction, integral; + + fraction = modf(f, &integral); + t.tv_sec = (int)integral; + t.tv_usec = (int)(fraction*1000000); + return t; +} +/* }}} */ + +#define pinba_pool_is_full(pool) ((pool->in < pool->out) ? pool->size - (pool->out - pool->in) : (pool->in - pool->out)) == (pool->size - 1) + +void pinba_temp_pool_dtor(void *pool); +void pinba_request_pool_dtor(void *pool); + +#endif /* PINBA_H */ + +/* + * vim600: sw=4 ts=4 fdm=marker + */ diff --git a/src/pinba_types.h b/src/pinba_types.h new file mode 100644 index 0000000..5c5cef6 --- /dev/null +++ b/src/pinba_types.h @@ -0,0 +1,325 @@ +/* Copyright (c) 2007-2009 Antony Dovgal + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* $Id: pinba_types.h,v 1.38.4.3.2.14 2009/04/16 15:49:27 tony Exp $ */ + +#ifndef PINBA_TYPES_H +#define PINBA_TYPES_H + +/* max index string length */ +#define PINBA_MAX_LINE_LEN 4096 + +/* these must not be greater than 255! */ +#define PINBA_HOSTNAME_SIZE 17 +#define PINBA_SERVER_NAME_SIZE 33 +#define PINBA_SCRIPT_NAME_SIZE 129 + +#define PINBA_TAG_NAME_SIZE 65 +#define PINBA_TAG_VALUE_SIZE 65 + +#define PINBA_ERR_BUFFER 2048 + +#define PINBA_UDP_BUFFER_SIZE 65536 + +#define PINBA_DICTIONARY_GROW_SIZE 32 +#define PINBA_TIMER_POOL_GROW_SIZE 262144 +#define PINBA_TIMER_POOL_SHRINK_SIZE PINBA_TIMER_POOL_GROW_SIZE*5 + +enum { + PINBA_BASE_REPORT_INFO, + PINBA_BASE_REPORT1, + PINBA_BASE_REPORT2, + PINBA_BASE_REPORT3, + PINBA_BASE_REPORT4, + PINBA_BASE_REPORT5, + PINBA_BASE_REPORT6, + PINBA_BASE_REPORT7, + PINBA_BASE_REPORT_LAST +}; + +enum { + PINBA_TAG_REPORT_INFO, + PINBA_TAG2_REPORT_INFO, + PINBA_TAG_REPORT, + PINBA_TAG2_REPORT, + PINBA_TAG_REPORT_LAST +}; + +typedef struct _pinba_socket { /* {{{ */ + int listen_sock; + struct event *accept_event; +} pinba_socket; +/* }}} */ + +typedef struct _pinba_timeval { /* {{{ */ + int tv_sec; + int tv_usec; +} pinba_timeval; +/* }}} */ + +typedef struct _pinba_word { /* {{{ */ + char *str; + unsigned char len; +} pinba_word; +/* }}} */ + +typedef struct _pinba_timer_record { /* {{{ */ + pinba_timeval value; + int *tag_ids; + pinba_word **tag_values; + unsigned short tag_num; + int hit_count; + int index; +} pinba_timer_record; +/* }}} */ + +typedef struct _pinba_timer_position { /* {{{ */ + unsigned int request_id; + unsigned short position; +} pinba_timer_position; +/* }}} */ + +typedef struct _pinba_tmp_stats_record { /* {{{ */ + Pinba::Request request; + time_t time; +} pinba_tmp_stats_record; +/* }}} */ + +typedef struct _pinba_stats_record { /* {{{ */ + struct { + char script_name[PINBA_SCRIPT_NAME_SIZE]; + char server_name[PINBA_SERVER_NAME_SIZE]; + char hostname[PINBA_HOSTNAME_SIZE]; + pinba_timeval req_time; + pinba_timeval ru_utime; + pinba_timeval ru_stime; + unsigned char script_name_len; + unsigned char server_name_len; + unsigned char hostname_len; + unsigned int req_count; + float doc_size; + float mem_peak_usage; + } data; + pinba_timer_record *timers; + time_t time; + unsigned short timers_cnt; +} pinba_stats_record; +/* }}} */ + +typedef void (*pool_dtor_func_t)(void *pool); + +typedef struct _pinba_pool { /* {{{ */ + size_t size; + size_t element_size; + pool_dtor_func_t dtor; + size_t in; + size_t out; + void **data; +} pinba_pool; +/* }}} */ + +typedef struct _pinba_tag { /* {{{ */ + size_t id; + char name[PINBA_TAG_NAME_SIZE]; + unsigned char name_len; +} pinba_tag; +/* }}} */ + +typedef struct _pinba_report { /* {{{ */ + time_t time_interval; + size_t results_cnt; + Pvoid_t results; + double time_total; + double kbytes_total; + double ru_utime_total; + double ru_stime_total; + pthread_rwlock_t lock; +} pinba_report; +/* }}} */ + +typedef struct _pinba_tag_report { /* {{{ */ + char tag1[PINBA_TAG_NAME_SIZE]; + char tag2[PINBA_TAG_NAME_SIZE]; + int tag1_id; + int tag2_id; + time_t time_interval; + time_t last_requested; + size_t results_cnt; + Pvoid_t results; + int type; + pthread_rwlock_t lock; +} pinba_tag_report; +/* }}} */ + +typedef struct _pinba_daemon_settings { /* {{{ */ + int port; + int stats_history; + int stats_gathering_period; + int request_pool_size; + int temp_pool_size; + int tag_report_timeout; + int show_protobuf_errors; + char *address; +} pinba_daemon_settings; +/* }}} */ + +typedef struct _pinba_daemon { /* {{{ */ + pthread_rwlock_t collector_lock; + pthread_rwlock_t temp_lock; + pinba_socket *collector_socket; + struct event_base *base; + pinba_pool temp_pool; + pinba_pool request_pool; + struct { + pinba_word **table; + Pvoid_t word_index; + size_t count; + size_t size; + } dict; + pinba_pool timer_pool; + size_t timers_cnt; + size_t timertags_cnt; + struct { + Pvoid_t table; /* ID -> NAME */ + Pvoid_t name_index; /* NAME -> */ + } tag; + pinba_daemon_settings settings; + pinba_report base_reports[PINBA_BASE_REPORT_LAST]; + Pvoid_t tag_reports; + pthread_rwlock_t tag_reports_lock; +} pinba_daemon; +/* }}} */ + +struct pinba_report1_data { /* {{{ */ + size_t req_count; + double req_time_total; + double ru_utime_total; + double ru_stime_total; + double kbytes_total; +}; +/* }}} */ + +struct pinba_report2_data { /* {{{ */ + size_t req_count; + double req_time_total; + double ru_utime_total; + double ru_stime_total; + double kbytes_total; +}; +/* }}} */ + +struct pinba_report3_data { /* {{{ */ + size_t req_count; + double req_time_total; + double ru_utime_total; + double ru_stime_total; + double kbytes_total; +}; +/* }}} */ + +struct pinba_report4_data { /* {{{ */ + size_t req_count; + double req_time_total; + double ru_utime_total; + double ru_stime_total; + double kbytes_total; + char server_name[PINBA_SERVER_NAME_SIZE]; + char script_name[PINBA_SCRIPT_NAME_SIZE]; +}; +/* }}} */ + +struct pinba_report5_data { /* {{{ */ + size_t req_count; + double req_time_total; + double ru_utime_total; + double ru_stime_total; + double kbytes_total; + char hostname[PINBA_HOSTNAME_SIZE]; + char script_name[PINBA_SCRIPT_NAME_SIZE]; +}; +/* }}} */ + +struct pinba_report6_data { /* {{{ */ + size_t req_count; + double req_time_total; + double ru_utime_total; + double ru_stime_total; + double kbytes_total; + char hostname[PINBA_HOSTNAME_SIZE]; + char server_name[PINBA_SERVER_NAME_SIZE]; +}; +/* }}} */ + +struct pinba_report7_data { /* {{{ */ + size_t req_count; + double req_time_total; + double ru_utime_total; + double ru_stime_total; + double kbytes_total; + char hostname[PINBA_HOSTNAME_SIZE]; + char server_name[PINBA_SERVER_NAME_SIZE]; + char script_name[PINBA_SCRIPT_NAME_SIZE]; +}; +/* }}} */ + +struct pinba_tag_info_data { /* {{{ */ + size_t req_count; + size_t hit_count; + pinba_timeval timer_value; + int prev_add_request_id; + int prev_del_request_id; +}; +/* }}} */ + +struct pinba_tag2_info_data { /* {{{ */ + size_t req_count; + size_t hit_count; + pinba_timeval timer_value; + char tag1_value[PINBA_TAG_VALUE_SIZE]; + char tag2_value[PINBA_TAG_VALUE_SIZE]; + int prev_add_request_id; + int prev_del_request_id; +}; +/* }}} */ + +struct pinba_tag_report_data { /* {{{ */ + size_t req_count; + size_t hit_count; + pinba_timeval timer_value; + char script_name[PINBA_SCRIPT_NAME_SIZE]; + char tag_value[PINBA_TAG_VALUE_SIZE]; + int prev_add_request_id; + int prev_del_request_id; +}; +/* }}} */ + +struct pinba_tag2_report_data { /* {{{ */ + size_t req_count; + size_t hit_count; + pinba_timeval timer_value; + char script_name[PINBA_SCRIPT_NAME_SIZE]; + char tag1_value[PINBA_TAG_VALUE_SIZE]; + char tag2_value[PINBA_TAG_VALUE_SIZE]; + int prev_add_request_id; + int prev_del_request_id; +}; +/* }}} */ + +#endif /* PINBA_TYPES_H */ + +/* + * vim600: sw=4 ts=4 fdm=marker + */ diff --git a/src/pool.cc b/src/pool.cc new file mode 100644 index 0000000..8f7ae12 --- /dev/null +++ b/src/pool.cc @@ -0,0 +1,534 @@ +/* Copyright (c) 2007-2009 Antony Dovgal + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* $Id: pool.cc,v 1.1.2.23 2009/04/17 11:50:01 tony Exp $ */ + +#include "pinba.h" +#include +using namespace std; + +/* generic pool functions */ + +size_t pinba_pool_num_records(pinba_pool *p) /* {{{ */ +{ + size_t result; + + if (p->in == p->out) { + return 0; + } else if (p->in > p->out) { + result = p->in - p->out; + } else { + result = p->size - (p->out - p->in); + } + + return result; +} +/* }}} */ + +static inline int pinba_pool_grow(pinba_pool *p, size_t more) /* {{{ */ +{ + size_t old_size = p->size; + + p->size += more; /* +more elements*/ + + if (p->size <= 0) { + return P_FAILURE; + } + + p->data = (void **)realloc(p->data, p->size * p->element_size); + + if (!p->data) { + return P_FAILURE; + } + + memmove((char *)p->data + p->in*p->element_size + more*p->element_size, (char *)p->data + p->in*p->element_size, (old_size - p->in) * p->element_size); + /* initialize memory */ + memset((char *)p->data + p->in*p->element_size, 0, more * p->element_size); + + if (p->out > p->in) { + /* we inserted new records between the head and the tail, adjust the head of the pool */ + p->out += more; + } + return P_SUCCESS; +} +/* }}} */ + +static inline int pinba_pool_shrink(pinba_pool *p, size_t less) /* {{{ */ +{ + size_t old_size = p->size; + + if (old_size <= less) { + return P_FAILURE; + } + + pinba_debug("shrinking pool (in: %ld, out: %ld, taken: %ld, empty: %ld) from %ld to %ld", p->in, p->out, pinba_pool_num_records(p), p->size - p->in, old_size, p->size - less); + + p->size -= less; /* -less elements*/ + p->data = (void **)realloc(p->data, p->size * p->element_size); + + if (!p->data) { + return P_FAILURE; + } + return P_SUCCESS; +} +/* }}} */ + +int pinba_pool_init(pinba_pool *p, size_t size, size_t element_size, pool_dtor_func_t dtor) /* {{{ */ +{ + memset(p, 0, sizeof(pinba_pool)); + p->element_size = element_size; + p->dtor = dtor; + return pinba_pool_grow(p, size); +} +/* }}} */ + +void pinba_pool_destroy(pinba_pool *p) /* {{{ */ +{ + if (p->data) { + if (p->dtor) { + p->dtor(p); + } + + free(p->data); + p->data = NULL; + } +} +/* }}} */ + +/* stats pool functions */ + +static inline void pinba_stats_record_dtor(int request_id, pinba_stats_record *record) /* {{{ */ +{ + int i; + pinba_pool *timer_pool = &D->timer_pool; + + pinba_update_reports_delete(record); + pinba_update_tag_reports_delete(request_id, record); + + record->time = 0; + + if (record->timers_cnt > 0) { + pinba_timer_record *timer = record->timers; + + for (i = 0; i < record->timers_cnt; i++) { + if (UNLIKELY(timer_pool->out == (timer_pool->size - 1))) { + size_t empty_records; + + timer_pool->out = 0; + empty_records = timer_pool->size - timer_pool->in; + + if (empty_records > PINBA_TIMER_POOL_SHRINK_SIZE) { + pinba_pool_shrink(timer_pool, PINBA_TIMER_POOL_GROW_SIZE * (empty_records / PINBA_TIMER_POOL_GROW_SIZE)); + } + } else { + timer_pool->out++; + } + + D->timertags_cnt -= timer->tag_num; + D->timers_cnt--; + + free(timer->tag_values); + free(timer->tag_ids); + timer++; + } + free(record->timers); + record->timers_cnt = 0; + } +} +/* }}} */ + +void pinba_temp_pool_dtor(void *pool) /* {{{ */ +{ + pinba_pool *p = (pinba_pool *)pool; + unsigned int i; + pinba_tmp_stats_record *tmp_record; + + for (i = 0; i < p->size; i++) { + tmp_record = TMP_POOL(p) + i; + tmp_record->time = 0; + tmp_record->request.~Request(); + } +} +/* }}} */ + +void pinba_request_pool_dtor(void *pool) /* {{{ */ +{ + pinba_pool *p = (pinba_pool *)pool; + unsigned int i; + pinba_stats_record *record; + + if (pinba_pool_num_records(p) > 0) { + pool_traverse_forward(i, p) { + record = REQ_POOL(p) + i; + pinba_stats_record_dtor(i, record); + } + } +} +/* }}} */ + +int timer_pool_add(pinba_timer_position *position) /* {{{ */ +{ + int id; + pinba_pool *timer_pool = &D->timer_pool; + + if (pinba_pool_is_full(timer_pool)) { /* got maximum */ + pinba_debug("growing from %ld to %ld; in: %ld, out: %ld", timer_pool->size, timer_pool->size + PINBA_TIMER_POOL_GROW_SIZE, timer_pool->in, timer_pool->out); + pinba_pool_grow(timer_pool, PINBA_TIMER_POOL_GROW_SIZE); + /* adjust counters */ + } + + id = timer_pool->in; + TIMER_POOL(timer_pool)[timer_pool->in] = *position; + + if (UNLIKELY(timer_pool->in == (timer_pool->size - 1))) { + timer_pool->in = 0; + } else { + timer_pool->in++; + } + + return id; +} +/* }}} */ + +inline void pinba_request_pool_delete_old(time_t from) /* {{{ */ +{ + unsigned int i; + pinba_pool *p = &D->request_pool; + pinba_stats_record *record; + + pool_traverse_forward(i, p) { + record = REQ_POOL(p) + i; + + if (record->time <= from) { + pinba_stats_record_dtor(i, record); + + if (UNLIKELY(p->out == (p->size - 1))) { + p->out = 0; + } else { + p->out++; + } + } else { + /* all the data is newer after this point, so we stop here */ + break; + } + } +} +/* }}} */ + +inline void pinba_merge_pools(void) /* {{{ */ +{ + int timer_id; + unsigned int k; + Word_t tag_id; + pinba_pool *temp_pool = &D->temp_pool; + pinba_pool *request_pool = &D->request_pool; + Pinba::Request *request; + pinba_tmp_stats_record *tmp_record; + pinba_stats_record *record; + pinba_timer_record *timer; + pinba_timer_position pos; + pinba_word *word_ptr; + float timer_value; + unsigned int i, j, timers_cnt, timer_tag_cnt, timer_hit_cnt, dict_size, tag_value, tag_name; + unsigned ti, tt; + PPvoid_t ppvalue; + Word_t word_id; + string *str; + pinba_tag *tag; + int res; + + /* we start with the last record, which should be already empty at the moment */ + + pool_traverse_forward(k, temp_pool) { + record = REQ_POOL(request_pool) + request_pool->in; + tmp_record = TMP_POOL(temp_pool) + k; + + memset(record, 0, sizeof(*record)); + record->time = tmp_record->time; + request = &tmp_record->request; + + memcpy_static(record->data.script_name, request->script_name().c_str(), request->script_name().size(), record->data.script_name_len); + memcpy_static(record->data.server_name, request->server_name().c_str(), request->server_name().size(), record->data.server_name_len); + memcpy_static(record->data.hostname, request->hostname().c_str(), request->hostname().size(), record->data.hostname_len); + record->data.req_time = float_to_timeval((double)request->request_time()); + record->data.ru_utime = float_to_timeval((double)request->ru_utime()); + record->data.ru_stime = float_to_timeval((double)request->ru_stime()); + record->data.ru_stime = float_to_timeval((double)request->ru_stime()); + record->data.req_count = request->request_count(); + record->data.doc_size = (float)request->document_size() / 1024; /* Kbytes*/ + record->data.mem_peak_usage = (float)request->memory_peak() / 1024; /* Kbytes */ + + timers_cnt = request->timer_hit_count_size(); + if (timers_cnt != (unsigned int)request->timer_value_size() || timers_cnt != (unsigned int)request->timer_tag_count_size()) { + pinba_debug("internal error: timer_hit_count_size != timer_value_size || timer_hit_count_size != timer_tag_count_size"); + continue; + } + + dict_size = request->dictionary_size(); + if (dict_size == 0 && timers_cnt > 0) { + pinba_debug("internal error: dict_size == 0, but timers_cnt > 0"); + continue; + } + + ti = tt = 0; + + if (timers_cnt > 0) { + + record->timers_cnt = 0; + record->timers = (pinba_timer_record *)calloc(timers_cnt, sizeof(pinba_timer_record)); + + if (!record->timers) { + pinba_debug("internal error: failed to allocate timers array"); + continue; + } + + /* add timers to the timers hash */ + for (i = 0; i < timers_cnt; i++, ti++) { + timer_value = request->timer_value(ti); + timer_tag_cnt = request->timer_tag_count(ti); + timer_hit_cnt = request->timer_hit_count(ti); + + if (timer_value < 0) { + pinba_debug("internal error: timer.value is negative"); + continue; + } + + if (timer_hit_cnt < 0) { + pinba_debug("internal error: timer.hit_count is negative"); + continue; + } + + pos.request_id = request_pool->in; + pos.position = i; + + timer_id = timer_pool_add(&pos); + + timer = &record->timers[i]; + timer->index = timer_id; + timer->tag_ids = (int *)malloc(sizeof(int) * timer_tag_cnt); + timer->tag_values = (pinba_word **)malloc(sizeof(pinba_word *) * timer_tag_cnt); + + if (!timer->tag_ids) { + pinba_debug("out of memory when allocating tag_ids"); + continue; + } + + timer->tag_num = 0; + + for (j = 0; j < timer_tag_cnt; j++, tt++) { + + tag_value = request->timer_tag_value(tt); + tag_name = request->timer_tag_name(tt); + + timer->tag_values[j] = NULL; + + if (LIKELY(tag_value < dict_size && tag_name < dict_size && tag_value >= 0 && tag_name >= 0)) { + str = request->mutable_dictionary(tag_value); + } else { + pinba_debug("tag_value >= dict_size || tag_name >= dict_size"); + continue; + } + + ppvalue = JudySLGet(D->dict.word_index, (uint8_t *)str->c_str(), NULL); + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + pinba_word *word; + int len; + + word = (pinba_word *)malloc(sizeof(*word)); + + /* insert */ + word_id = D->dict.count; + if (word_id == D->dict.size) { + D->dict.table = (pinba_word **)realloc(D->dict.table, sizeof(pinba_word *) * (D->dict.size + PINBA_DICTIONARY_GROW_SIZE)); + D->dict.size += PINBA_DICTIONARY_GROW_SIZE; + } + + D->dict.table[word_id] = word; + + len = str->size(); + word->len = (len >= PINBA_TAG_VALUE_SIZE) ? PINBA_TAG_VALUE_SIZE - 1 : len; + word->str = strndup(str->c_str(), word->len); + word_ptr = word; + + ppvalue = JudySLIns(&D->dict.word_index, (uint8_t *)str->c_str(), NULL); + if (!ppvalue || ppvalue == PPJERR) { + /* well.. too bad.. */ + free(D->dict.table[word_id]); + pinba_debug("failed to insert new value into word_index"); + continue; + } + + *ppvalue = (void *)word_id; + D->dict.count++; + } else { + word_id = (Word_t)*ppvalue; + if (LIKELY(word_id >= 0 && word_id < D->dict.count)) { + word_ptr = D->dict.table[word_id]; + } else { + pinba_debug("invalid word_id"); + continue; + } + } + + timer->tag_values[j] = word_ptr; + + str = request->mutable_dictionary(tag_name); + + ppvalue = JudySLGet(D->tag.name_index, (uint8_t *)str->c_str(), NULL); + + if (UNLIKELY(!ppvalue || ppvalue == PPJERR)) { + /* doesn't exist, create */ + int dummy; + + tag_id = 0; + + /* get the first empty ID */ + res = JudyLFirstEmpty(D->tag.table, &tag_id, NULL); + if (res < 0) { + pinba_debug("no empty indexes in tag.table"); + continue; + } + + tag = (pinba_tag *)malloc(sizeof(pinba_tag)); + if (!tag) { + pinba_debug("failed to allocate tag"); + continue; + } + + tag->id = tag_id; + memcpy_static(tag->name, str->c_str(), str->size(), dummy); + tag->name_len = str->size(); + + /* add the tag to the table */ + ppvalue = JudyLIns(&D->tag.table, tag_id, NULL); + if (!ppvalue || ppvalue == PJERR) { + free(tag); + pinba_debug("failed to insert tag into tag.table"); + continue; + } + *ppvalue = tag; + + /* add the tag to the index */ + ppvalue = JudySLIns(&D->tag.name_index, (uint8_t *)str->c_str(), NULL); + if (UNLIKELY(ppvalue == PJERR)) { + JudyLDel(&D->tag.table, tag_id, NULL); + free(tag); + pinba_debug("failed to insert tag into tag.name_index"); + continue; + } else { + *ppvalue = tag; + } + } else { + tag = (pinba_tag *)*ppvalue; + tag_id = tag->id; + } + + timer->tag_ids[j] = tag_id; + timer->tag_num++; + D->timertags_cnt++; + } + + timer->value = float_to_timeval(timer_value); + timer->hit_count = timer_hit_cnt; + D->timers_cnt++; + record->timers_cnt++; + } + } + + pinba_update_reports_add(record); + pinba_update_tag_reports_add(request_pool->in, record); + + if (UNLIKELY(request_pool->in == (request_pool->size - 1))) { + request_pool->in = 0; + } else { + request_pool->in++; + } + + /* reached the end of the pool, start throwing out old entries */ + if (request_pool->in == request_pool->out) { + pinba_stats_record *tmp_record = REQ_POOL(request_pool) + request_pool->in; + + pinba_stats_record_dtor(request_pool->in, tmp_record); + + if (UNLIKELY(request_pool->out == (request_pool->size - 1))) { + request_pool->out = 0; + } else { + request_pool->out++; + } + } + } + temp_pool->in = temp_pool->out = 0; +} +/* }}} */ + +void *pinba_stats_main(void *arg) /* {{{ */ +{ + struct timeval launch; + + pinba_debug("starting up stats thread"); + + gettimeofday(&launch, 0); + + for (;;) { + struct timeval tv1; + + pthread_rwlock_wrlock(&D->collector_lock); + /* make sure we don't store any OLD data */ + pinba_request_pool_delete_old(launch.tv_sec - D->settings.stats_history); + + pthread_rwlock_rdlock(&D->temp_lock); + if (UNLIKELY(pinba_pool_num_records(&D->temp_pool) == 0)) { + pthread_rwlock_unlock(&D->temp_lock); + pthread_rwlock_unlock(&D->collector_lock); + } else { + pthread_rwlock_unlock(&D->temp_lock); + pthread_rwlock_wrlock(&D->temp_lock); + pinba_merge_pools(); + if (D->settings.tag_report_timeout != -1) { + pinba_tag_reports_destroy(0); + } + pthread_rwlock_unlock(&D->temp_lock); + pthread_rwlock_unlock(&D->collector_lock); + } + + launch.tv_sec += D->settings.stats_gathering_period / 1000000; + launch.tv_usec += D->settings.stats_gathering_period % 1000000; + + if (launch.tv_usec > 1000000) { + launch.tv_usec -= 1000000; + launch.tv_sec++; + } + + gettimeofday(&tv1, 0); + timersub(&launch, &tv1, &tv1); + + if (LIKELY(tv1.tv_sec >= 0 && tv1.tv_usec >= 0)) { + usleep(tv1.tv_sec * 1000000 + tv1.tv_usec); + } else { /* we were locked too long: run right now, but re-schedule next launch */ + gettimeofday(&launch, 0); + tv1.tv_sec = D->settings.stats_gathering_period / 1000000; + tv1.tv_usec = D->settings.stats_gathering_period % 1000000; + timeradd(&launch, &tv1, &launch); + } + } + /* not reachable */ + return NULL; +} +/* }}} */ + +/* + * vim600: sw=4 ts=4 fdm=marker + */ diff --git a/src/tags.cc b/src/tags.cc new file mode 100644 index 0000000..23d746b --- /dev/null +++ b/src/tags.cc @@ -0,0 +1,93 @@ +/* Copyright (c) 2007-2009 Antony Dovgal + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +/* $Id: tags.cc,v 1.1.2.5 2009/03/23 15:21:20 tony Exp $ */ + +#include "pinba.h" + +void pinba_tag_dtor(pinba_tag *tag) /* {{{ */ +{ + JudyLDel(&D->tag.table, tag->id, NULL); + JudySLDel(&D->tag.name_index, (uint8_t *)tag->name, NULL); + + free(tag); +} +/* }}} */ + +pinba_tag *pinba_tag_get_by_name(const unsigned char *name) /* {{{ */ +{ + pinba_tag *tag; + PPvoid_t ppvalue; + + ppvalue = JudySLGet(D->tag.name_index, (uint8_t *)name, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return NULL; + } + + tag = (pinba_tag *)*ppvalue; + return tag; +} +/* }}} */ + +pinba_tag *pinba_tag_get_by_name_next(unsigned char *name) /* {{{ */ +{ + pinba_tag *tag; + PPvoid_t ppvalue; + + ppvalue = JudySLNext(D->tag.name_index, (uint8_t *)name, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return NULL; + } + + tag = (pinba_tag *)*ppvalue; + return tag; +} +/* }}} */ + +pinba_tag *pinba_tag_get_by_id(size_t id) /* {{{ */ +{ + pinba_tag *tag; + PPvoid_t ppvalue; + + ppvalue = JudyLGet(D->tag.table, (Word_t)id, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return NULL; + } + + tag = (pinba_tag *)*ppvalue; + return tag; +} +/* }}} */ + +void pinba_tag_delete_by_name(const unsigned char *name) /* {{{ */ +{ + pinba_tag *tag; + PPvoid_t ppvalue; + + ppvalue = JudySLGet(D->tag.name_index, (uint8_t *)name, NULL); + if (!ppvalue || ppvalue == PPJERR) { + return; + } + + tag = (pinba_tag *)*ppvalue; + pinba_tag_dtor(tag); +} +/* }}} */ + +/* + * + * vim600: sw=4 ts=4 fdm=marker + */ diff --git a/test.sql b/test.sql new file mode 100644 index 0000000..996bb22 --- /dev/null +++ b/test.sql @@ -0,0 +1,50 @@ +select * from request limit 10; +select * from timer limit 10; +select * from timertag limit 10; +select * from tag limit 20; + +select * from request where id > 0 limit 1; +select * from timer where id > 0 limit 1; +select * from timertag where timer_id > 0 limit 1; +select * from timertag where tag_id > 0 limit 1; +select * from tag where id > 0 limit 1; + +select * from request where id >= 0 limit 1; +select * from timer where id >= 0 limit 1; +select * from timertag where timer_id >= 0 limit 1; +select * from timertag where tag_id >= 0 limit 1; +select * from tag where id >= 0 limit 1; + +select * from request where id in (0,1,2,100,1000) limit 1; +select * from timer where id in (0,1,2,100,1000) limit 1; +select * from timertag where timer_id in (0,1,2,100,1000) limit 1; +select * from timertag where tag_id in (0,1,2,100,1000) limit 1; +select * from tag where id in (0,1,2,100,1000) limit 1; + +select * from request where script_name != "" order by script_name asc limit 2; +select * from request where hostname != "" order by hostname asc limit 2; +select * from request where req_time > 1 order by req_time asc limit 2; +select * from request where timers_cnt != 0 order by req_time desc limit 2; +select distinct(script_name) from request limit 10; +select distinct(hostname) from request limit 10; +select distinct(server_name) from request limit 10; + +select * from timer where id = 0; +select * from timer where value > 0.3 order by value desc limit 2; +select * from timer where value between 0 and 0.5 order by value desc limit 2; +select * from timer where value not between 0 and 0.5 order by value desc limit 2; +select distinct(id) from timer limit 10; + +select * from tag where id = 0; +select * from tag where id != 0 limit 1; +select * from tag where id < 1; +select * from tag where name != "" limit 2; +select * from tag where name like "gr%"; +select distinct(name) from tag limit 10; + +select * from timertag where timer_id = 0 limit 2; +select * from timertag where tag_id = 0 limit 2; +select * from timertag where tag_id = 0 limit 2; +select * from timertag where value != "" limit 3; +select * from timertag where timer_id < 100 and tag_id = 0 limit 10; +select distinct(value) from timertag;