From 021330cd7a0a2f9cfb8513bddacea43618ac214e Mon Sep 17 00:00:00 2001 From: Antony Dovgal Date: Mon, 4 May 2009 15:58:20 +0400 Subject: [PATCH] initial commit --- .gitignore | 39 + COPYING | 352 +++++ Makefile.am | 3 + README | 93 ++ build.mk | 38 + buildconf.sh | 23 + configure.in | 416 +++++ default_tables.sql | 202 +++ pinba.proto | 21 + protobuf.supp | 7 + src/.gitignore | 19 + src/Makefile.am | 12 + src/data.cc | 1436 ++++++++++++++++++ src/ha_pinba.cc | 3630 ++++++++++++++++++++++++++++++++++++++++++++ src/ha_pinba.h | 173 +++ src/main.cc | 330 ++++ src/pinba-pb.cc | 729 +++++++++ src/pinba-pb.h | 620 ++++++++ src/pinba.h | 190 +++ src/pinba_types.h | 325 ++++ src/pool.cc | 534 +++++++ src/tags.cc | 93 ++ test.sql | 50 + 23 files changed, 9335 insertions(+) create mode 100644 .gitignore create mode 100644 COPYING create mode 100644 Makefile.am create mode 100644 README create mode 100644 build.mk create mode 100755 buildconf.sh create mode 100644 configure.in create mode 100644 default_tables.sql create mode 100644 pinba.proto create mode 100644 protobuf.supp create mode 100644 src/.gitignore create mode 100644 src/Makefile.am create mode 100644 src/data.cc create mode 100644 src/ha_pinba.cc create mode 100644 src/ha_pinba.h create mode 100644 src/main.cc create mode 100644 src/pinba-pb.cc create mode 100644 src/pinba-pb.h create mode 100644 src/pinba.h create mode 100644 src/pinba_types.h create mode 100644 src/pool.cc create mode 100644 src/tags.cc create mode 100644 test.sql 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;