From: Florian Forster Date: Thu, 28 May 2015 12:09:35 +0000 (+0200) Subject: Merge remote-tracking branch 'github/pr/1054' X-Git-Tag: collectd-5.6.0~709 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=711a4a302936537f6487a99d0ed8f292a8135817;hp=0e8adc990989f23bff5fe62e9e0b1e12a494935a;p=collectd.git Merge remote-tracking branch 'github/pr/1054' --- diff --git a/AUTHORS b/AUTHORS index 027ac969..3f63c3dc 100644 --- a/AUTHORS +++ b/AUTHORS @@ -128,6 +128,9 @@ Jeremy Katz Jérôme Renard - varnish plugin. +Jiri Tyr + - fhcount plugin. + Kevin Bowling - write_tsdb plugin for http://opentsdb.net/ @@ -280,6 +283,9 @@ Tommie Gannert Vincent Bernat - smart plugin. +Vincent Brillault + - turbostat plugin, based on Len Brown kernel tool + Vincent Stehlé - hddtemp plugin. diff --git a/ChangeLog b/ChangeLog index 28be899a..b0a997c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,282 @@ +2015-05-27, Version 5.5.0 + * Build system: Ability to make out-of-tree builds has been fixed. + Thanks to Vincent Bernat. #792 + * Build system, Disk and Users plugins: Detection and use of libstatgrab + ≧ 0.90 has been added. Thanks to Vincent Bernat. #445, #795, #806, + #807, #908 + * Build system, Memory, CPU, TCPConns and Processes plugins: Numerous + fixes related to OpenBSD support have been added. Thanks to Landry + Breuil. #777, #778, #779, #808 + * Build system: Plugins now only export "module_register()". Thanks to + Florian Forster. + * Build system: Various cleanups and improvements have been done. Thanks + to Marc Fournier. + * collectd: Numerous internal changes and improvements to the daemon and + the plugin API have been make. Thanks to Florian Forster, Pierre-Yves + Ritschard and Alex Petrov. #512, #727 + * collectd: Numerous spelling mistakes have been corrected in comments + and documentation and several error messages have been improved. + Thanks to Ruben Kerkhof, Abhinav Upadhyay, Olivier Bazoud, Pierre-Yves + Ritschard, Tim Smith, Moshe Zada, Katelyn Perry and Marc Fournier. + * collectd: Rules/Targets can now be appended to existing Filter Chains. + Thanks to Marc Falzon. #444 + * collectd: Failing Filter Chains destinations will now log the list of + available write targets. Thanks to Wilfried Goesgens. #650, #1043 + * collectd: Support for process signaling and management by upstart and + systemd has been implemented for the Linux platform. Thanks to + Pierre-Yves Ritschard and Marc Fournier. #798, #811, #814 + * collectd: The "CollectInternalStats" option has been added. Thanks to + Yves Mettier. #691 + * collectd: The daemon source code and dependencies have moved to the + "src/daemon/" directory. Thanks to Florian Forster. + * collectd: The new "MaxReadInterval" option allows to cap the + exponential retry interval of plugins read errors. Thanks to Alexey + Remizov and Florian Forster. #713 + * collectd: The "-P" command-line option now has precedence over the + "PIDFile" option. Thanks to Thomas D. #553 + * collection.cgi: Various data-source related adjustments have been + made. Thanks to Fabiano Pires and Sebastian Harl. + * libcollectdclient: Now propagates errors when signing / encrypting + network packets. Thanks to Florian Forster. + * Configuration: Support for unquoted IPv6 addresses has been added. + Thanks to Sebastian Harl. #489 + * Documentation: Various improvements have been done. Thanks to Florian + Forster and Marc Fournier. + * Examples: the sample C plugin has been updated to the current plugin + API. Thanks to Sebastian Harl. + * Licensing: The following components have been relicensed to the MIT + license: the Apple Sensors, Ascent, DBI, E-Mail, Entropy, GenericJMX, + gmond, LogFile, nginx, Notify Desktop, NTPd, NUT, olsrd, Perl, Ping, + PostgreSQL, Protocols, RouterOS, RRDCacheD, SNMP, StatsD, SysLog, + Table, Tail, UnixSock, vmem, VServer, Wireless, Write Riemann and XMMS + plugins, the core collectd daemon, the collectdmon, collectd-nagios + and collectd-tg utilities, all the Targets and Matches, liboconfig, + most of the "utils_*" files and the plugin API. + * Tests: A test suite has been added. Thanks to Florian Forster. + * Threshold: The hysteresis calculation has been made more reliable. + Thanks to Jan Kundrát. #581 + * Threshold: Various fixes and improvements have been made. Thanks to + Manuel Luis Sanmartín Rozada. #649, #644 + * AMQP plugin: The "ConnectionRetryDelay" option has been added, + allowing to delay reconnection. Thanks to Yoga Ramalingam and Marc + Fournier. #833 + * AMQP plugin: The "QueueDurable" and "QueueAutoDelete" options have + been added, giving control over queue creation and deletion. Thanks to + David Blundell and Marc Fournier. #623 + * Apache, Ascent, BIND, cURL, cURL-JSON, cURL-XML, nginx and Write HTTP + plugins: Customizing the "User-Agent" field is now possible at + compile-time. Thanks to Jeremy Katz. #440 + * Apache, Ascent, BIND, cURL, cURL-JSON, cURL-XML, nginx plugins: The + connection will be reset if it hasn't completed within the configured + "Interval". The new "Timeout" option gives control over this behavior. + Thanks to Jan Kundrát and Marc Fournier. #982, #983, #993 + * Apache, Ascent, cURL, cURL-JSON, cURL-XML, nginx, Write HTTP plugins: + Allow usernames and passwords to contain colons if built against + libcurl ≧ 7.19.1. Thanks to Marc Fournier. #695, #947 + * Apache plugin: The "SSLCiphers" option gives control over the + encryption algorithms to use with TLS connections. Thanks to Toni + Moreno. #946 + * Barometer plugin: This new plugin reads sensor data from various + Freescale and Bosch digital barometers. Thanks to Tomas Menzl. #69, + #693 + * Battery plugin: Reporting values as percentages and reporting degraded + batteries has been added. Thanks to Florian Forster. + * Battery plugin: Support for reading values from sysfs on Linux has + been added. Thanks to Andy Parkins, Nicholas Humfrey, Peter Wu and + Florian Forster. #725, #810, #998 + * Battery plugin: The value for current is no longer supplied unless the + battery provides this information. Thanks to Florian Forster. + * BIND plugin: Bind's XML v3 API is now supported; Thanks to Victor + Berger, Bruno Prémont and Michal Humpula. #742, #847 + * Ceph plugin: This new plugin collects statistics from the Ceph + distributed storage system. Thanks to Dan Ryder, Dennis Zou, Colin + McCabe, Sage Weil. #522, #598 + * ConnTrack plugin: Support for reporting values as percentages as well + as legacy conntrack files in "/proc" has been added. Thanks to + Pierre-Yves Ritschard. #497, #680 + * CPU plugin: The plugin is now able to report values as percentages and + aggregate values per-state and per-CPU. Thanks to Pierre-Yves + Ritschard, Florian Forster, Fabien Wernli, Nicholas Humfrey and + Wilfried Goesgens. #499, #516, #639 #734, #812, #802 + * cURL-JSON plugin: Extracting values from complex JSON structures has + been enhanced. Thanks to Jim Radford. #408, #411 + * cURL-JSON plugin: Intervals can now be configured on a per-URL basis. + Thanks to Stan Sawa. #685 + * cURL-JSON, cURL-XML, Write HTTP plugins: These plugins now also follow + HTTP redirects. Thanks to Marc Fournier. + * cURL, cURL-JSON, cURL-XML plugins: HTTP Digest authentication has been + implemented. Thanks to Frank Cornelis. #482 + * DBI, Oracle, PostgreSQL plugins: A "MetadataFrom" parameter has been + added which allows to set metadata from database columns. Thanks to + Mark Wong. #317, #321 + * DBI plugin: Querying several databases in parallel is now possible. + Thanks to Vincent Bernat. #453 + * Disk plugin: On the Linux platform, disk names can now get looked up + in udev with the "UdevNameAttr" option. Thanks to Patrick Mooney. #537 + * Disk plugin: This plugin now collects several additional I/O-related + metrics on the Linux platform. Thanks to Florian Forster and Michael + Schenck. #705, #759 + * DRBD plugin: This new plugin reads Linux's Distributed Replicated + Block Device (DRBD) statistics. Thanks to Tim Laszlo. #566, #700 + * Exec, UnixSock plugins: The "PUTNOTIF" command now allows to set + metadata on notifications. Thanks to John-John Tedro. #416 + * fhcount plugin: This new plugin reports the number of used file + handles. Thanks to Jiri Tyr. #1009 + * GenericJMX plugin: A Class Loader for "JMXConnectorFactory" has been + added, allowing the plugin to work with JBOSS > 7. Thanks to Alexandre + Moutot. #452 + * IPC plugin: This new plugin collects information related to shared + memory. Thanks to Andrés J. Díaz. #925 + * Java plugin: Now uses the hostname defined in the configuration file. + Thanks to Pierre-Yves Ritschard. #530, #681 + * Load plugin: The plugin is now able to report values as percentages. + Thanks to Vedran Bartonicek and Pierre-Yves Ritschard. #344, #498 + * Log Logstash plugin: This new plugin writes collectd logs and events + as Logstash JSON formatted events. Thanks to Pierre-Yves Ritschard. + #360 + * LVM plugin: The plugin collects thin pool data volumes size, and no + longer reports virtual volumes. Thanks to Benjamin Gilbert. #603 + * memcached plugin: "listen_disabled_num" are now also reported. Thanks + to Matt Cottingham. #622 + * Memory plugin: Slab memory reporting on the Linux platform has been + added. Thanks to Manuel CISSÉ and Marc Fournier. #560, #697 + * Memory plugin: The plugin is now able to report values as percentages. + Thanks to Jeremy Katz, Florian Forster and Manuel CISSÉ. #501, #511, + #559 + * Modbus plugin: Selecting between holding and input registers is now + possible. Thanks to Jan Vitek. #338 + * Modbus plugin: Support for accessing devices through an RS-485 serial + port has been added. Thanks to Eric Sandeen. + * Multimeter plugin: This plugin isn't built by default on the AIX + platform anymore. Thanks to Manuel Luis Sanmartin Rozada. #549, #684 + * MySQL and PostgreSQL plugins: Passing "127.0.0.1" as a host will now + result in the global Hostname being used in metric names. Thanks to + Jeremy Katz. #441 + * MySQL plugin: InnoDB, Select and Sort statistics collection has been + added. Thanks to Wilson Felipe, Marek Becka and Pierre-Yves Ritschard. + #248, #621, #699, #824 + * MySQL plugin: The "Alias" and "ConnectTimeout" options have been + added. Thanks to William Tisäter. + * Netlink plugin: Support for 64bit netlink counters has been added. + Thanks to Marek Becka. #435 + * Network plugin: The "ReconnectInterval" configuration option has been + added. Thanks to John Ferlito. #732 + * NFS plugin: Support for NFSv4.0 has been implemented. Thanks to Marek + Becka. #550 + * OneWire plugin: Support for more temperature-providing sensor families + has been added. Thanks to Tomasz Torcz. #672 + * OneWire plugin: Support for full OWFS path and more device families + has been implemented. Thanks to Tomas Menzl. #68 + * OpenLDAP plugin: This new plugin reads monitoring information from + OpenLDAP's "cn=Monitor" subtree. Thanks to Kimo Rosenbaum, Marc + Fournier and Nicholas Humfrey. #719 + * OpenVPN plugin: Support for OpenVPN 2.3.0 has been implemented. Thanks + to Ed Okerson. #252 + * OpenVZ plugin: Various improvements have been made, making the plugin + report values like the other collectd plugins do. Thanks to Chris + Lundquist. #264 + * Perl plugin: A new "listval_filter" method has been added, various + internal cleanups and improvements have been made and a test suite has + been added. Thanks to Matthias Bethke. #728 + * PostgreSQL plugin: The new "ExpireDelay" option allows skipping older + values pending write when the database slows down. Thanks to Stephen + O'Dor. #593 + * PowerDNS plugin: The plugin was updated for stats from pdns 3.4.3. + Thanks to Ruben Kerkhof. #965 + * Processes plugin: A memory-usage related optimization for low-profile + systems has been added. Thanks to Florian Forster. #652 + * Python plugin: Support for Python3 has been improved, "ModulePath" is + now prepended to "sys.path", and the "get_dataset()" function has been + added to the Python API. Thanks to Sven Trenkel and Patrick Browne. + #890, #751, #771 + * Redis and Write_Redis plugins: The support library has been switched + from credis to hiredis. Thanks to Andrés J. Díaz, Victor Seva, Marc + Fournier, Johan Bergström, Michael Spiegle and brianpkelly. #296, + #464, #475, #799, #1030 + * Redis plugin: Custom commands can now be used to fetch values stored + in Redis. Thanks to Pierre-Yves Ritschard. #816 + * Redis plugin: Support for passwords up to 512 characters long has been + added. Thanks to Jeremy Katz. #532 + * Sensors plugin: Support for lm_sensors' power sensors has been added. + Thanks to Jan Kundrát. #571 + * SMART plugin: This new plugin collects SMART statistics from disk + drives. Thanks to Vincent Bernat. #797 + * SNMP plugin: A blacklist/whitelist feature can now be used to filter + which OIDs to collect. Thanks to Christophe Courtaut. #414 + * SNMP plugin: SNMPv3 authentication and encryption support has been + implemented. Thanks to Michael Pilat. #362 + * SNMP plugin: Two error messages have been disambiguated. Thanks to + Sergey. #939, #952 + * Swap plugin: The plugin is now able to report values as percentages. + Thanks to Jeremy Katz and Florian Forster. #500, #510 + * Swap plugin: The plugin no longer fails on Linux systems where + "SwapCached" isn't exposed by the kernel. Thanks to Florian Forster. + #733 + * Tail plugin: "GaugeInc" and "GaugeAdd" options have been implemented. + Thanks to Andre Ferraz. #673 + * Tail plugin: Intervals can now be configured on a per-File basis. + Thanks to Tom Leaman. #446 + * TCPConns plugin: The "AllPortsSummary" option, allowing to summarize + all connections, has been added. Thanks to Marek Becka. #488 + * TCPConns plugin: Three metrics were renamed on the AIX platform, for + the sake of consistency. Thanks to Manuel Luis Sanmartín Rozada. #546 + * Turbostat plugin: This new plugin reads CPU frequency and C-state + residency on modern Intel turbo-capable processors. Thanks to Vincent + Brillault, Jean Delvare and Nicolas Iooss. #651 + * UnixSock plugin: The "GETTHRESHOLD" command has been re-added. Thanks + to Manuel Luis Sanmartín Rozada. #674 + * Varnish plugin: Varnish 4 support has been added, as well as as + monitoring metrics only available in Varnish 4. Thanks to Marc + Fournier. #618, #783 + * virt plugin: Guests memory usage is now also collected. Thanks to + Tiago Carvalho, jazzmes and Zollner Robert. + * virt plugin: It is now possible to chose between using guests' name or + UUID as plugin_instance. Thanks to Remi Ferrand. #385 + * virt plugin: The libvirt plugin has been renamed to virt. Thanks to + Florian Forster. + * Write Graphite plugin: When the connection to graphite fails, + reconnection attempts are now limited to once per second. Thanks to + Florian Forster. #625 + * Write HTTP plugin: Multi-instance support of this plugin has been + improved. The "" block has been deprecated in favor of + "". Thanks to Marc Fournier. #902 + * Write HTTP plugin: Several TLS-related configuration options have been + added. Thanks to Ingmar Runge. #666 + * Write HTTP plugin: The "LowSpeedLimit" and "Timeout" options allow to + reset slow/stalled network connections. Thanks to loginator17 and Marc + Fournier. #752, #985 + * Write HTTP plugin: The size of the payload posted to the HTTP server + can now be controlled with the "BufferSize" option. Thanks to Florian + Forster. #722 + * Write Kafka plugin: This new plugin sends data to Apache Kafka, a + distributed messaging queue. Thanks to Pierre-Yves Ritschard, + ciomaire, Vincent Bernat, Marc Fournier. #670, #694, #794, #853, #014 + * Write Log plugin: This new plugin dispatches collected values to the + configured log destination(s). Thanks to Pierre-Yves Ritschard. #886 + * Write Riemann plugin: Extra meta strings are now added as attributes + in notifications. Thanks to John-John Tedro. #417 + * Write Riemann plugin: Notification message are now sent to the Riemann + server via the description field. Thanks to Adrian Miron. #575 + * Write Riemann plugin: Support for custom attributes has been added. + Thanks to Pierre-Yves Ritschard. #459 + * Write Riemann plugin: Support had been implemented for sending events + to Riemann in batches (when using TCP), and is enabled by default. + Thanks to Pierre-Yves Ritschard. #800 + * Write Riemann plugin: The "EventServicePrefix" option has been added, + which adds a prefix to event service names. Thanks to Moshe Zada. #706 + * Write Riemann plugin: Threshold checks can now be passed down to the + Riemann server. Thanks to Pierre-Yves Ritschard. #518 + * Write Sensu plugin: This new plugin submits values to Sensu, a stream + processing and monitoring system. Thanks to Fabrice A. Marie and Marc + Fournier. #912, #1001, #1016 + * Write TSDB plugin: This new plugin sends data to OpenTSDB, a scalable + time series database. Thanks to Kevin Bowling, Florian Forster, Dallin + Young, Michael Schenck and Pierre-Yves Ritschard. #703, #772, #945 + * ZFS ARC plugin: Support for ZFS-on-Linux has been added. Thanks to + Marc Fournier and Wilfried Goesgens. #552 + * Zookeeper plugin: This new plugin reads data from the Apache Zookeeper + "MNTR" command. Thanks to Jeremy Katz. #826 + 2015-02-26, Version 5.4.2 * Build system: Numerous fixes. Thanks to Bjørn Nordbø, Jim Radford, KOMEDA Shinji, Lauri Tirkkonen, Manuel Luis Sanmartin Rozada, Marc diff --git a/README b/README index 12ab7c65..9604fda3 100644 --- a/README +++ b/README @@ -107,6 +107,9 @@ Features Values gathered by a custom program or script. See collectd-exec(5). + - fhcount + File handles statistics. + - filecount Count the number of files in directories. @@ -349,6 +352,10 @@ Features Reads the number of records and file size from a running Tokyo Tyrant server. + - turbostat + Reads CPU frequency and C-state residency on modern Intel + turbo-capable processors. + - uptime System uptime statistics. @@ -632,6 +639,12 @@ Prerequisites Used by the `smart' plugin. + * libcap (optional) + The `turbostat' plugin can optionally build Linux Capabilities support, + which avoids full privileges requirement (aka. running as root) to read + values. + + * libclntsh (optional) Used by the `oracle' plugin. diff --git a/bindings/java/Makefile.am b/bindings/java/Makefile.am index f8e936a8..8d2e49d7 100644 --- a/bindings/java/Makefile.am +++ b/bindings/java/Makefile.am @@ -24,7 +24,7 @@ EXTRA_DIST = org/collectd/api/CollectdConfigInterface.java \ org/collectd/java/GenericJMX.java \ org/collectd/java/JMXMemory.java -java-build-stamp: org/collectd/api/*.java org/collectd/java/*.java +java-build-stamp: $(srcdir)/org/collectd/api/*.java $(srcdir)/org/collectd/java/*.java $(JAVAC) -d "." "$(srcdir)/org/collectd/api"/*.java $(JAVAC) -d "." "$(srcdir)/org/collectd/java"/*.java mkdir -p .libs @@ -41,6 +41,11 @@ install-exec-local: java-build-stamp $(INSTALL) -m 644 .libs/generic-jmx.jar \ "$(DESTDIR)$(pkgdatadir)/java" +uninstall-local: + rm -f "$(DESTDIR)$(pkgdatadir)/java/collectd-api.jar" + rm -f "$(DESTDIR)$(pkgdatadir)/java/generic-jmx.jar" + rmdir "$(DESTDIR)$(pkgdatadir)/java" || true + clean-local: rm -f "org/collectd/api"/*.class rm -f "org/collectd/java"/*.class diff --git a/configure.ac b/configure.ac index d4e3301a..f713ce13 100644 --- a/configure.ac +++ b/configure.ac @@ -589,13 +589,44 @@ AC_CHECK_HEADERS(net/pfvar.h, have_termios_h="no" AC_CHECK_HEADERS(termios.h, [have_termios_h="yes"]) +# For the turbostat plugin +have_asm_msrindex_h="no" +AC_CHECK_HEADERS(asm/msr-index.h, [have_asm_msrindex_h="yes"]) + +if test "x$have_asm_msrindex_h" = "xyes" +then + AC_CACHE_CHECK([whether asm/msr-index.h has MSR_CORE_C3_RESIDENCY], + [c_cv_have_usable_asm_msrindex_h], + AC_COMPILE_IFELSE([AC_LANG_PROGRAM( +[[[ +#include +]]], +[[[ +int y = MSR_CORE_C3_RESIDENCY; +return(y); +]]] + )], + [c_cv_have_usable_asm_msrindex_h="yes"], + [c_cv_have_usable_asm_msrindex_h="no"], + ) + ) +fi + +have_cpuid_h="no" +AC_CHECK_HEADERS(cpuid.h, [have_cpuid_h="yes"]) + +AC_CHECK_HEADERS(sys/capability.h) # # Checks for typedefs, structures, and compiler characteristics. # AC_C_CONST +AC_C_INLINE +AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T AC_TYPE_UID_T +AC_TYPE_UINT32_T AC_HEADER_TIME # @@ -3729,6 +3760,7 @@ AC_ARG_WITH(librdkafka, [AS_HELP_STRING([--with-librdkafka@<:@=PREFIX@:>@], [Pat then with_librdkafka_cppflags="-I$withval/include" with_librdkafka_ldflags="-L$withval/lib" + with_librdkafka_rpath="$withval/lib" with_librdkafka="yes" else with_librdkafka="$withval" @@ -3740,6 +3772,9 @@ AC_ARG_WITH(librdkafka, [AS_HELP_STRING([--with-librdkafka@<:@=PREFIX@:>@], [Pat SAVE_CPPFLAGS="$CPPFLAGS" SAVE_LDFLAGS="$LDFLAGS" +CPPFLAGS="$CPPFLAGS $with_librdkafka_cppflags" +LDFLAGS="$LDFLAGS $with_librdkafka_ldflags" + if test "x$with_librdkafka" = "xyes" then AC_CHECK_HEADERS(librdkafka/rdkafka.h, [with_librdkafka="yes"], [with_librdkafka="no (librdkafka/rdkafka.h not found)"]) @@ -3755,7 +3790,12 @@ if test "x$with_librdkafka" = "xyes" then BUILD_WITH_LIBRDKAFKA_CPPFLAGS="$with_librdkafka_cppflags" BUILD_WITH_LIBRDKAFKA_LDFLAGS="$with_librdkafka_ldflags" - BUILD_WITH_LIBRDKAFKA_LIBS="-lrdkafka" + if test "x$with_librdkafka_rpath" != "x" + then + BUILD_WITH_LIBRDKAFKA_LIBS="-Wl,-rpath,$with_librdkafka_rpath -lrdkafka" + else + BUILD_WITH_LIBRDKAFKA_LIBS="-lrdkafka" + fi AC_SUBST(BUILD_WITH_LIBRDKAFKA_CPPFLAGS) AC_SUBST(BUILD_WITH_LIBRDKAFKA_LDFLAGS) AC_SUBST(BUILD_WITH_LIBRDKAFKA_LIBS) @@ -5146,6 +5186,7 @@ plugin_tape="no" plugin_tcpconns="no" plugin_ted="no" plugin_thermal="no" +plugin_turbostat="no" plugin_uptime="no" plugin_users="no" plugin_virt="no" @@ -5193,6 +5234,10 @@ then then plugin_ipvs="yes" fi + if test "x$c_cv_have_usable_asm_msrindex_h" = "xyes" && test "x$have_cpuid_h" = "xyes" + then + plugin_turbostat="yes" + fi fi if test "x$ac_system" = "xOpenBSD" @@ -5244,6 +5289,7 @@ fi if test "x$with_kstat" = "xyes" then plugin_nfs="yes" + plugin_processes="yes" plugin_uptime="yes" plugin_zfs_arc="yes" plugin_zone="yes" @@ -5507,6 +5553,7 @@ AC_PLUGIN([email], [yes], [EMail statistics]) AC_PLUGIN([entropy], [$plugin_entropy], [Entropy statistics]) AC_PLUGIN([ethstat], [$plugin_ethstat], [Stats from NIC driver]) AC_PLUGIN([exec], [yes], [Execution of external programs]) +AC_PLUGIN([fhcount], [yes], [File handles statistics]) AC_PLUGIN([filecount], [yes], [Count files in directories]) AC_PLUGIN([fscache], [$plugin_fscache], [fscache statistics]) AC_PLUGIN([gmond], [$with_libganglia], [Ganglia plugin]) @@ -5590,6 +5637,7 @@ AC_PLUGIN([ted], [$plugin_ted], [Read The Energy Detective values AC_PLUGIN([thermal], [$plugin_thermal], [Linux ACPI thermal zone statistics]) AC_PLUGIN([threshold], [yes], [Threshold checking plugin]) AC_PLUGIN([tokyotyrant], [$with_libtokyotyrant], [TokyoTyrant database statistics]) +AC_PLUGIN([turbostat], [$plugin_turbostat], [Advanced statistic on Intel cpu states]) AC_PLUGIN([unixsock], [yes], [Unixsock communication plugin]) AC_PLUGIN([uptime], [$plugin_uptime], [Uptime statistics]) AC_PLUGIN([users], [$plugin_users], [User statistics]) @@ -5884,6 +5932,7 @@ Configuration: entropy . . . . . . . $enable_entropy ethstat . . . . . . . $enable_ethstat exec . . . . . . . . $enable_exec + fhcount . . . . . . . $enable_fhcount filecount . . . . . . $enable_filecount fscache . . . . . . . $enable_fscache gmond . . . . . . . . $enable_gmond @@ -5966,6 +6015,7 @@ Configuration: thermal . . . . . . . $enable_thermal threshold . . . . . . $enable_threshold tokyotyrant . . . . . $enable_tokyotyrant + turbostat . . . . . . $enable_turbostat unixsock . . . . . . $enable_unixsock uptime . . . . . . . $enable_uptime users . . . . . . . . $enable_users diff --git a/contrib/redhat/collectd.spec b/contrib/redhat/collectd.spec index 20f6dd5f..9d491ff8 100644 --- a/contrib/redhat/collectd.spec +++ b/contrib/redhat/collectd.spec @@ -52,6 +52,7 @@ %{?el6:%global _has_iproute 1} %{?el6:%global _has_atasmart 1} %{?el6:%global _has_hiredis 1} +%{?el6:%global _has_asm_msr_index 1} %{?el7:%global _has_libyajl 1} %{?el7:%global _has_recent_libpcap 1} @@ -66,6 +67,7 @@ %{?el7:%global _has_iproute 1} %{?el7:%global _has_atasmart 1} %{?el7:%global _has_hiredis 1} +%{?el7:%global _has_asm_msr_index 1} # plugins enabled by default %define with_aggregation 0%{!?_without_aggregation:1} @@ -94,6 +96,7 @@ %define with_entropy 0%{!?_without_entropy:1} %define with_ethstat 0%{!?_without_ethstat:0%{?_has_recent_sockios_h}} %define with_exec 0%{!?_without_exec:1} +%define with_fhcount 0%{!?_without_fhcount:1} %define with_filecount 0%{!?_without_filecount:1} %define with_fscache 0%{!?_without_fscache:1} %define with_gmond 0%{!?_without_gmond:0%{?_has_recent_libganglia}} @@ -157,6 +160,7 @@ %define with_ted 0%{!?_without_ted:1} %define with_thermal 0%{!?_without_thermal:1} %define with_threshold 0%{!?_without_threshold:1} +%define with_turbostat 0%{!?_without_turbostat:0%{?_has_asm_msr_index}} %define with_unixsock 0%{!?_without_unixsock:1} %define with_uptime 0%{!?_without_uptime:1} %define with_users 0%{!?_without_users:1} @@ -213,14 +217,14 @@ Summary: statistics collection and monitoring daemon Name: collectd -Version: 5.4.2 +Version: 5.5.0 Release: 1%{?dist} URL: http://collectd.org Source: http://collectd.org/files/%{name}-%{version}.tar.bz2 License: GPLv2 Group: System Environment/Daemons BuildRoot: %{_tmppath}/%{name}-%{version}-root -BuildRequires: libgcrypt-devel, kernel-headers, libtool-ltdl-devel +BuildRequires: libgcrypt-devel, kernel-headers, libtool-ltdl-devel, libcap-devel Vendor: collectd development team %if 0%{?el7:1} @@ -999,6 +1003,12 @@ Collectd utilities %define _with_exec --disable-exec %endif +%if %{with_fhcount} +%define _with_fhcount --enable-fhcount +%else +%define _with_fhcount --disable-fhcount +%endif + %if %{with_filecount} %define _with_filecount --enable-filecount %else @@ -1441,6 +1451,12 @@ Collectd utilities %define _with_tokyotyrant --disable-tokyotyrant %endif +%if %{with_turbostat} +%define _with_turbostat --enable-turbostat +%else +%define _with_turbostat --disable-turbostat +%endif + %if %{with_unixsock} %define _with_unixsock --enable-unixsock %else @@ -1604,6 +1620,7 @@ Collectd utilities %{?_with_entropy} \ %{?_with_ethstat} \ %{?_with_exec} \ + %{?_with_fhcount} \ %{?_with_filecount} \ %{?_with_fscache} \ %{?_with_gmond} \ @@ -1685,6 +1702,7 @@ Collectd utilities %{?_with_ted} \ %{?_with_thermal} \ %{?_with_threshold} \ + %{?_with_turbostat} \ %{?_with_unixsock} \ %{?_with_uptime} \ %{?_with_users} \ @@ -1880,6 +1898,9 @@ fi %if %{with_exec} %{_libdir}/%{name}/exec.so %endif +%if %{with_fhcount} +%{_libdir}/%{name}/fhcount.so +%endif %if %{with_filecount} %{_libdir}/%{name}/filecount.so %endif @@ -1985,6 +2006,9 @@ fi %if %{with_threshold} %{_libdir}/%{name}/threshold.so %endif +%if %{with_turbostat} +%{_libdir}/%{name}/turbostat.so +%endif %if %{with_unixsock} %{_libdir}/%{name}/unixsock.so %endif @@ -2311,14 +2335,17 @@ fi %doc contrib/ %changelog -# * TODO 5.5.0-1 -# - New upstream version -# - New plugins enabled by default: ceph, drbd, log_logstash, write_tsdb, smart, openldap, redis, write_redis, zookeeper, write_log, write_sensu, ipc -# - New plugins disabled by default: barometer, write_kafka -# - Enable zfs_arc, now supported on Linux -# - Install disk plugin in a dedicated package, as it depends on libudev -# - use systemd on EL7, sysvinit on EL6 & EL5 -# - Install collectdctl, collectd-tg and collectd-nagios in collectd-utils.rpm +* Wed May 27 2015 Marc Fournier 5.5.0-1 +- New upstream version +- New plugins enabled by default: ceph, drbd, log_logstash, write_tsdb, smart, + openldap, redis, write_redis, zookeeper, write_log, write_sensu, ipc, + turbostat, fhcount +- New plugins disabled by default: barometer, write_kafka +- Enable zfs_arc, now supported on Linux +- Install disk plugin in a dedicated package, as it depends on libudev +- use systemd on EL7, sysvinit on EL6 & EL5 +- Install collectdctl, collectd-tg and collectd-nagios in collectd-utils.rpm +- Add build-dependency on libcap-devel * Mon Aug 19 2013 Marc Fournier 5.4.0-1 - New upstream version diff --git a/contrib/wiki2changelog.pl b/contrib/wiki2changelog.pl new file mode 100755 index 00000000..b8fc965b --- /dev/null +++ b/contrib/wiki2changelog.pl @@ -0,0 +1,75 @@ +#!/usr/bin/perl + +use strict; +use warnings; + +=head1 NAME + +wiki2changelog.pl + +=head1 DESCRIPTION + +This script takes the change log from one of the "Version x.y" pages in +collectd's wiki and converts it to the format used by the "ChangeLog" file. +This is usually done as part of the release process. + +=cut + +our $TextWidth = 80; + +sub format_entry +{ + my $in = shift; + my $out = ''; + + my $line = "\t*"; + my $line_len = 9; + + for (split (' ', $in)) { + my $word = $_; + my $word_len = 1 + length $word; + + if (($line_len + $word_len) > $TextWidth) { + $out .= "$line\n"; + $line = "\t "; + $line_len = 9; + } + + $line .= " $word"; + $line_len += $word_len; + } + + if ($line_len != 9) { + $out .= "$line\n"; + } + + return $out; +} + +while (<>) +{ + chomp; + my $line = $_; + + if ($line =~ m#^\* (.*)#) { + $line = $1; + } else { + next; + } + + $line =~ s#<#<#g; + $line =~ s#>#>#g; + $line =~ s# # #g; + $line =~ s#"#"#g; + + $line =~ s#\{\{Plugin\|([^}]+)\}\}#$1 plugin#g; + $line =~ s@\{\{Issue\|([^}]+)\}\}@#$1@g; + $line =~ s#\[\[[^|]+\|([^\]]+)\]\]#$1#g; + $line =~ s#\[\[([^|]+)\]\]#$1#g; + + $line =~ s#'''(.*?)'''#*$1*#g; + $line =~ s#''(.*?)''#$1#g; + $line =~ s#(.*?)#"$1"#gi; + + print format_entry($line); +} diff --git a/src/Makefile.am b/src/Makefile.am index 95e9f508..1282f225 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -23,6 +23,14 @@ AM_CPPFLAGS += -DPKGDATADIR='"${pkgdatadir}"' AUTOMAKE_OPTIONS = subdir-objects +noinst_LTLIBRARIES = libmount.la liblookup.la + +libmount_la_SOURCES = utils_mount.c utils_mount.h +libmount_la_LIBADD = daemon/libcommon.la + +liblookup_la_SOURCES = utils_vl_lookup.c utils_vl_lookup.h +liblookup_la_LIBADD = daemon/libavltree.la daemon/libcommon.la + sbin_PROGRAMS = collectdmon bin_PROGRAMS = collectd-nagios collectdctl collectd-tg @@ -55,10 +63,9 @@ endif collectdctl_LDADD += libcollectdclient/libcollectdclient.la collectdctl_DEPENDENCIES = libcollectdclient/libcollectdclient.la -collectd_tg_SOURCES = collectd-tg.c \ - daemon/utils_heap.c daemon/utils_heap.h +collectd_tg_SOURCES = collectd-tg.c collectd_tg_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src/libcollectdclient/collectd -I$(top_builddir)/src/libcollectdclient/collectd -collectd_tg_LDADD = +collectd_tg_LDADD = daemon/libheap.la if BUILD_WITH_LIBSOCKET collectd_tg_LDADD += -lsocket endif @@ -183,9 +190,9 @@ endif if BUILD_PLUGIN_CGROUPS pkglib_LTLIBRARIES += cgroups.la cgroups_la_SOURCES = cgroups.c \ - utils_ignorelist.c utils_ignorelist.h \ - utils_mount.c utils_mount.h + utils_ignorelist.c utils_ignorelist.h cgroups_la_LDFLAGS = $(PLUGIN_LDFLAGS) +cgroups_la_LIBADD = libmount.la endif if BUILD_PLUGIN_CONNTRACK @@ -283,9 +290,9 @@ endif if BUILD_PLUGIN_DF pkglib_LTLIBRARIES += df.la df_la_SOURCES = df.c \ - utils_ignorelist.c utils_ignorelist.h \ - utils_mount.c utils_mount.h + utils_ignorelist.c utils_ignorelist.h df_la_LDFLAGS = $(PLUGIN_LDFLAGS) +df_la_LIBADD = libmount.la endif if BUILD_PLUGIN_DISK @@ -359,6 +366,12 @@ ethstat_la_SOURCES = ethstat.c ethstat_la_LDFLAGS = $(PLUGIN_LDFLAGS) endif +if BUILD_PLUGIN_FHCOUNT +pkglib_LTLIBRARIES += fhcount.la +fhcount_la_SOURCES = fhcount.c +fhcount_la_LDFLAGS = $(PLUGIN_LDFLAGS) +endif + if BUILD_PLUGIN_FILECOUNT pkglib_LTLIBRARIES += filecount.la filecount_la_SOURCES = filecount.c @@ -1076,6 +1089,12 @@ tokyotyrant_la_LIBADD += -lsocket endif endif +if BUILD_PLUGIN_TURBOSTAT +pkglib_LTLIBRARIES += turbostat.la +turbostat_la_SOURCES = turbostat.c +turbostat_la_LDFLAGS = $(PLUGIN_LDFLAGS) +endif + if BUILD_PLUGIN_UNIXSOCK pkglib_LTLIBRARIES += unixsock.la unixsock_la_SOURCES = unixsock.c \ @@ -1188,6 +1207,7 @@ write_kafka_la_SOURCES = write_kafka.c \ utils_format_json.c utils_format_json.h \ utils_cmd_putval.c utils_cmd_putval.h \ utils_crc32.c utils_crc32.h +write_kafka_la_CPPFLAGS = $(AM_CPPFLAGS) $(BUILD_WITH_LIBRDKAFKA_CPPFLAGS) write_kafka_la_LDFLAGS = $(PLUGIN_LDFLAGS) $(BUILD_WITH_LIBRDKAFKA_LDFLAGS) write_kafka_la_LIBADD = $(BUILD_WITH_LIBRDKAFKA_LIBS) endif @@ -1367,49 +1387,12 @@ uninstall-hook: rm -f $(DESTDIR)$(sysconfdir)/collectd.conf rm -f $(DESTDIR)$(pkgdatadir)/postgresql_default.conf; -check_PROGRAMS = test_common test_utils_avltree test_utils_heap test_utils_mount test_utils_vl_lookup - -test_common_SOURCES = tests/test_common.c \ - daemon/common.h daemon/common.c \ - tests/macros.h \ - tests/mock/plugin.c \ - tests/mock/utils_cache.c \ - tests/mock/utils_time.c -test_common_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL) -test_common_LDFLAGS = -export-dynamic -test_common_LDADD = - -test_utils_avltree_SOURCES = tests/test_utils_avltree.c \ - daemon/utils_avltree.c daemon/utils_avltree.h -test_utils_avltree_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL) -test_utils_avltree_LDFLAGS = -export-dynamic -test_utils_avltree_LDADD = - -test_utils_heap_SOURCES = tests/test_utils_heap.c \ - daemon/utils_heap.c daemon/utils_heap.h -test_utils_heap_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL) -test_utils_heap_LDFLAGS = -export-dynamic -test_utils_heap_LDADD = - -test_utils_mount_SOURCES = tests/test_utils_mount.c \ - utils_mount.c utils_mount.h \ - daemon/common.c daemon/common.h \ - tests/mock/plugin.c \ - tests/mock/utils_cache.c \ - tests/mock/utils_time.c -test_utils_mount_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL) -test_utils_mount_LDFLAGS = -export-dynamic -test_utils_mount_LDADD = - -test_utils_vl_lookup_SOURCES = tests/test_utils_vl_lookup.c \ - utils_vl_lookup.h utils_vl_lookup.c \ - daemon/utils_avltree.c daemon/utils_avltree.h \ - daemon/common.c daemon/common.h \ - tests/mock/plugin.c \ - tests/mock/utils_cache.c \ - tests/mock/utils_time.c -test_utils_vl_lookup_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL) -test_utils_vl_lookup_LDFLAGS = -export-dynamic -test_utils_vl_lookup_LDADD = - -TESTS = test_common test_utils_avltree test_utils_heap test_utils_mount test_utils_vl_lookup +check_PROGRAMS = test_utils_mount test_utils_vl_lookup + +test_utils_mount_SOURCES = utils_mount_test.c testing.h +test_utils_mount_LDADD = libmount.la daemon/libplugin_mock.la + +test_utils_vl_lookup_SOURCES = utils_vl_lookup_test.c testing.h +test_utils_vl_lookup_LDADD = liblookup.la daemon/libplugin_mock.la + +TESTS = test_utils_mount test_utils_vl_lookup diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 4950e401..99f879cc 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -117,6 +117,7 @@ #@BUILD_PLUGIN_ENTROPY_TRUE@LoadPlugin entropy #@BUILD_PLUGIN_ETHSTAT_TRUE@LoadPlugin ethstat #@BUILD_PLUGIN_EXEC_TRUE@LoadPlugin exec +#@BUILD_PLUGIN_FHCOUNT_TRUE@LoadPlugin fhcount #@BUILD_PLUGIN_FILECOUNT_TRUE@LoadPlugin filecount #@BUILD_PLUGIN_FSCACHE_TRUE@LoadPlugin fscache #@BUILD_PLUGIN_GMOND_TRUE@LoadPlugin gmond @@ -183,6 +184,7 @@ #@BUILD_PLUGIN_TED_TRUE@LoadPlugin ted #@BUILD_PLUGIN_THERMAL_TRUE@LoadPlugin thermal #@BUILD_PLUGIN_TOKYOTYRANT_TRUE@LoadPlugin tokyotyrant +#@BUILD_PLUGIN_TURBOSTAT_TRUE@LoadPlugin turbostat #@BUILD_PLUGIN_UNIXSOCK_TRUE@LoadPlugin unixsock #@BUILD_PLUGIN_UPTIME_TRUE@LoadPlugin uptime #@BUILD_PLUGIN_USERS_TRUE@LoadPlugin users @@ -521,6 +523,11 @@ # NotificationExec "user:group" "/path/to/exec" # +# +# ValuesAbsolute true +# ValuesPercentage false +# + # # # Instance "foodir" @@ -1205,6 +1212,25 @@ # Port "1978" # +# +## None of the following option should be set manually +## This plugin automatically detect most optimal options +## Only set values here if: +## - The module ask you to +## - You want to disable the collection of some data +## - Your (intel) CPU is not supported (yet) by the module +## - The module generate a lot of errors 'MSR offset 0x... read failed' +## In the last two cases, please open a bug request +# +# TCCActivationTemp "100" +# CoreCstates "392" +# PackageCstates "396" +# SystemManagementInterrupt true +# DigitalTemperatureSensor true +# PackageThermalManagement true +# RunningAveragePowerLimit "7" +# + # # SocketFile "@prefix@/var/run/@PACKAGE_NAME@-unixsock" # SocketGroup "collectd" diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index a7a58167..0c8e1e20 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -2264,6 +2264,27 @@ expected from them. This is documented in great detail in L. =back +=head2 Plugin C + +The C plugin provides statistics about used, unused and total number of +file handles. + +The I provides the following configuration options: + +=over 4 + +=item B B|B + +Enables or disables reporting of file handles usage in absolute numbers, +e.g. file handles used. Defaults to B. + +=item B B|B + +Enables or disables reporting of file handles usage in percentages, e.g. +percent of file handles used. Defaults to B. + +=back + =head2 Plugin C The C plugin counts the number of files in a certain directory (and @@ -6587,6 +6608,79 @@ Default: B<1978> =back +=head2 Plugin C + +The I reads CPU frequency and C-state residency on modern +Intel processors by using the new Model Specific Registers. + +=over 4 + +=item B I + +Bitmask of the list of core C states supported by the processor. +This option should only be used if the automated detection fails. +Default value extracted from the cpu model and family. + +Currently supported C-states (by this plugin): 3, 6, 7 + +Example: (1<<3)+(1<<6)+(1<<7) = 392 for all states + +=item B I + +Bitmask of the list of pacages C states supported by the processor. +This option should only be used if the automated detection fails. +Default value extracted from the cpu model and family. + +Currently supported C-states (by this plugin): 2, 3, 6, 7, 8, 9, 10 + +Example: (1<<2)+(1<<3)+(1<<6)+(1<<7) = 396 for states 2, 3, 6 and 7 + +=item B I|I + +Boolean enabling the collection of the I/O System-Management Interrupt +counter'. This option should only be used if the automated detection +fails or if you want to disable this feature. + +=item B I|I + +Boolean enabling the collection of the temperature of each core. +This option should only be used if the automated detectionfails or +if you want to disable this feature. + +=item B I|I + +Boolean enabling the collection of the temperature of each package. +This option should only be used if the automated detectionfails or +if you want to disable this feature. + +=item B I + +Thermal Control Circuit Activation Temperature of the installed +CPU. This temperature is used when collecting the temperature of +cores or packages. This option should only be used if the automated +detection fails. Default value extracted from B + +=item B I + +Bitmask of the list of elements to be thermally monitored. This option +should only be used if the automated detection fails or if you want to +disable some collections. The different bits of this bitmask accepted +by this plugin are: + +=over 4 + +=item 0 ('1'): Package + +=item 1 ('2'): DRAM + +=item 2 ('4'): Cores + +=item 3 ('8'): Embedded graphic device + +=back + +=back + =head2 Plugin C =over 4 diff --git a/src/curl_xml.c b/src/curl_xml.c index 9049d990..689d5e15 100644 --- a/src/curl_xml.c +++ b/src/curl_xml.c @@ -343,6 +343,7 @@ static int cx_handle_single_value_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ /* free up object */ xmlXPathFreeObject (values_node_obj); + sfree (node_value); /* We have reached here which means that * we have got something to work */ @@ -387,7 +388,7 @@ static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ /* If the base xpath returns more than one block, the result is assumed to be * a table. The `Instance' option is not optional in this case. Check for the * condition and inform the user. */ - if (is_table) + if (is_table && (xpath->instance == NULL)) { WARNING ("curl_xml plugin: " "Base-XPath %s is a table (more than one result was returned), " @@ -440,8 +441,12 @@ static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ if (xpath->instance_prefix != NULL) { if (instance_node != NULL) + { + char *node_value = (char *) xmlNodeGetContent(instance_node->nodeTab[0]); ssnprintf (vl->type_instance, sizeof (vl->type_instance),"%s%s", - xpath->instance_prefix, (char *) xmlNodeGetContent(instance_node->nodeTab[0])); + xpath->instance_prefix, node_value); + sfree (node_value); + } else sstrncpy (vl->type_instance, xpath->instance_prefix, sizeof (vl->type_instance)); @@ -451,8 +456,11 @@ static int cx_handle_instance_xpath (xmlXPathContextPtr xpath_ctx, /* {{{ */ /* If instance_prefix and instance_node are NULL, then * don't set the type_instance */ if (instance_node != NULL) - sstrncpy (vl->type_instance, (char *) xmlNodeGetContent(instance_node->nodeTab[0]), - sizeof (vl->type_instance)); + { + char *node_value = (char *) xmlNodeGetContent(instance_node->nodeTab[0]); + sstrncpy (vl->type_instance, node_value, sizeof (vl->type_instance)); + sfree (node_value); + } } /* Free `instance_node_obj' this late, because `instance_node' points to diff --git a/src/daemon/Makefile.am b/src/daemon/Makefile.am index fc815543..7f826e34 100644 --- a/src/daemon/Makefile.am +++ b/src/daemon/Makefile.am @@ -17,16 +17,23 @@ AUTOMAKE_OPTIONS = subdir-objects sbin_PROGRAMS = collectd +noinst_LTLIBRARIES = libavltree.la libcommon.la libheap.la libplugin_mock.la + +libavltree_la_SOURCES = utils_avltree.c utils_avltree.h + +libcommon_la_SOURCES = common.c common.h + +libheap_la_SOURCES = utils_heap.c utils_heap.h + +libplugin_mock_la_SOURCES = plugin_mock.c utils_cache_mock.c utils_time_mock.c + collectd_SOURCES = collectd.c collectd.h \ - common.c common.h \ configfile.c configfile.h \ filter_chain.c filter_chain.h \ meta_data.c meta_data.h \ plugin.c plugin.h \ - utils_avltree.c utils_avltree.h \ utils_cache.c utils_cache.h \ utils_complain.c utils_complain.h \ - utils_heap.c utils_heap.h \ utils_llist.c utils_llist.h \ utils_random.c utils_random.h \ utils_tail_match.c utils_tail_match.h \ @@ -41,7 +48,7 @@ collectd_SOURCES = collectd.c collectd.h \ collectd_CPPFLAGS = $(AM_CPPFLAGS) $(LTDLINCL) collectd_CFLAGS = $(AM_CFLAGS) collectd_LDFLAGS = -export-dynamic -collectd_LDADD = -lm +collectd_LDADD = libavltree.la libcommon.la libheap.la -lm collectd_DEPENDENCIES = # Link to these libraries.. @@ -83,3 +90,15 @@ collectd_DEPENDENCIES += $(top_builddir)/src/liboconfig/liboconfig.la else collectd_LDADD += -loconfig endif + +check_PROGRAMS = test_common test_utils_avltree test_utils_heap +TESTS = test_common test_utils_avltree test_utils_heap + +test_common_SOURCES = common_test.c ../testing.h +test_common_LDADD = libcommon.la libplugin_mock.la + +test_utils_avltree_SOURCES = utils_avltree_test.c ../testing.h +test_utils_avltree_LDADD = libavltree.la + +test_utils_heap_SOURCES = utils_heap_test.c ../testing.h +test_utils_heap_LDADD = libheap.la diff --git a/src/daemon/collectd.h b/src/daemon/collectd.h index 6886c123..80b753c8 100644 --- a/src/daemon/collectd.h +++ b/src/daemon/collectd.h @@ -304,6 +304,10 @@ typedef int _Bool; # endif #endif +#ifndef GAUGE_FORMAT +# define GAUGE_FORMAT "%.15g" +#endif + /* Type for time as used by "utils_time.h" */ typedef uint64_t cdtime_t; diff --git a/src/daemon/common.c b/src/daemon/common.c index e2b618fb..e396b79d 100644 --- a/src/daemon/common.c +++ b/src/daemon/common.c @@ -968,18 +968,17 @@ int format_values (char *ret, size_t ret_len, /* {{{ */ for (i = 0; i < ds->ds_num; i++) { if (ds->ds[i].type == DS_TYPE_GAUGE) - BUFFER_ADD (":%f", vl->values[i].gauge); + BUFFER_ADD (":"GAUGE_FORMAT, vl->values[i].gauge); else if (store_rates) { if (rates == NULL) rates = uc_get_rate (ds, vl); if (rates == NULL) { - WARNING ("format_values: " - "uc_get_rate failed."); + WARNING ("format_values: uc_get_rate failed."); return (-1); } - BUFFER_ADD (":%g", rates[i]); + BUFFER_ADD (":"GAUGE_FORMAT, rates[i]); } else if (ds->ds[i].type == DS_TYPE_COUNTER) BUFFER_ADD (":%llu", vl->values[i].counter); @@ -989,7 +988,7 @@ int format_values (char *ret, size_t ret_len, /* {{{ */ BUFFER_ADD (":%"PRIu64, vl->values[i].absolute); else { - ERROR ("format_values plugin: Unknown data source type: %i", + ERROR ("format_values: Unknown data source type: %i", ds->ds[i].type); sfree (rates); return (-1); diff --git a/src/daemon/common_test.c b/src/daemon/common_test.c new file mode 100644 index 00000000..1fa8f324 --- /dev/null +++ b/src/daemon/common_test.c @@ -0,0 +1,246 @@ +/** + * collectd - src/tests/test_common.c + * Copyright (C) 2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +#include "testing.h" +#include "common.h" + +DEF_TEST(sstrncpy) +{ + char buffer[16] = ""; + char *ptr = &buffer[4]; + char *ret; + + buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff; + buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff; + + ret = sstrncpy (ptr, "foobar", 8); + OK(ret == ptr); + STREQ ("foobar", ptr); + OK(buffer[3] == buffer[12]); + + ret = sstrncpy (ptr, "abc", 8); + OK(ret == ptr); + STREQ ("abc", ptr); + OK(buffer[3] == buffer[12]); + + ret = sstrncpy (ptr, "collectd", 8); + OK(ret == ptr); + OK(ptr[7] == 0); + STREQ ("collect", ptr); + OK(buffer[3] == buffer[12]); + + return (0); +} + +DEF_TEST(ssnprintf) +{ + char buffer[16] = ""; + char *ptr = &buffer[4]; + int status; + + buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff; + buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff; + + status = ssnprintf (ptr, 8, "%i", 1337); + OK(status == 4); + STREQ ("1337", ptr); + + status = ssnprintf (ptr, 8, "%s", "collectd"); + OK(status == 8); + OK(ptr[7] == 0); + STREQ ("collect", ptr); + OK(buffer[3] == buffer[12]); + + return (0); +} + +DEF_TEST(sstrdup) +{ + char *ptr; + + ptr = sstrdup ("collectd"); + OK(ptr != NULL); + STREQ ("collectd", ptr); + + sfree(ptr); + OK(ptr == NULL); + + ptr = sstrdup (NULL); + OK(ptr == NULL); + + return (0); +} + +DEF_TEST(strsplit) +{ + char buffer[32]; + char *fields[8]; + int status; + + strncpy (buffer, "foo bar", sizeof (buffer)); + status = strsplit (buffer, fields, 8); + OK(status == 2); + STREQ ("foo", fields[0]); + STREQ ("bar", fields[1]); + + strncpy (buffer, "foo \t bar", sizeof (buffer)); + status = strsplit (buffer, fields, 8); + OK(status == 2); + STREQ ("foo", fields[0]); + STREQ ("bar", fields[1]); + + strncpy (buffer, "one two\tthree\rfour\nfive", sizeof (buffer)); + status = strsplit (buffer, fields, 8); + OK(status == 5); + STREQ ("one", fields[0]); + STREQ ("two", fields[1]); + STREQ ("three", fields[2]); + STREQ ("four", fields[3]); + STREQ ("five", fields[4]); + + strncpy (buffer, "\twith trailing\n", sizeof (buffer)); + status = strsplit (buffer, fields, 8); + OK(status == 2); + STREQ ("with", fields[0]); + STREQ ("trailing", fields[1]); + + strncpy (buffer, "1 2 3 4 5 6 7 8 9 10 11 12 13", sizeof (buffer)); + status = strsplit (buffer, fields, 8); + OK(status == 8); + STREQ ("7", fields[6]); + STREQ ("8", fields[7]); + + strncpy (buffer, "single", sizeof (buffer)); + status = strsplit (buffer, fields, 8); + OK(status == 1); + STREQ ("single", fields[0]); + + strncpy (buffer, "", sizeof (buffer)); + status = strsplit (buffer, fields, 8); + OK(status == 0); + + return (0); +} + +DEF_TEST(strjoin) +{ + char buffer[16]; + char *fields[4]; + int status; + + fields[0] = "foo"; + fields[1] = "bar"; + fields[2] = "baz"; + fields[3] = "qux"; + + status = strjoin (buffer, sizeof (buffer), fields, 2, "!"); + OK(status == 7); + STREQ ("foo!bar", buffer); + + status = strjoin (buffer, sizeof (buffer), fields, 1, "!"); + OK(status == 3); + STREQ ("foo", buffer); + + status = strjoin (buffer, sizeof (buffer), fields, 0, "!"); + OK(status < 0); + + status = strjoin (buffer, sizeof (buffer), fields, 2, "rcht"); + OK(status == 10); + STREQ ("foorchtbar", buffer); + + status = strjoin (buffer, sizeof (buffer), fields, 4, ""); + OK(status == 12); + STREQ ("foobarbazqux", buffer); + + status = strjoin (buffer, sizeof (buffer), fields, 4, "!"); + OK(status == 15); + STREQ ("foo!bar!baz!qux", buffer); + + fields[0] = "0123"; + fields[1] = "4567"; + fields[2] = "8901"; + fields[3] = "2345"; + status = strjoin (buffer, sizeof (buffer), fields, 4, "-"); + OK(status < 0); + + return (0); +} + +DEF_TEST(strunescape) +{ + char buffer[16]; + int status; + + strncpy (buffer, "foo\\tbar", sizeof (buffer)); + status = strunescape (buffer, sizeof (buffer)); + OK(status == 0); + STREQ ("foo\tbar", buffer); + + strncpy (buffer, "\\tfoo\\r\\n", sizeof (buffer)); + status = strunescape (buffer, sizeof (buffer)); + OK(status == 0); + STREQ ("\tfoo\r\n", buffer); + + strncpy (buffer, "With \\\"quotes\\\"", sizeof (buffer)); + status = strunescape (buffer, sizeof (buffer)); + OK(status == 0); + STREQ ("With \"quotes\"", buffer); + + /* Backslash before null byte */ + strncpy (buffer, "\\tbackslash end\\", sizeof (buffer)); + status = strunescape (buffer, sizeof (buffer)); + OK(status != 0); + STREQ ("\tbackslash end", buffer); + return (0); + + /* Backslash at buffer end */ + strncpy (buffer, "\\t3\\56", sizeof (buffer)); + status = strunescape (buffer, 4); + OK(status != 0); + OK(buffer[0] == '\t'); + OK(buffer[1] == '3'); + OK(buffer[2] == 0); + OK(buffer[3] == 0); + OK(buffer[4] == '5'); + OK(buffer[5] == '6'); + OK(buffer[6] == '7'); + + return (0); +} + +int main (void) +{ + RUN_TEST(sstrncpy); + RUN_TEST(ssnprintf); + RUN_TEST(sstrdup); + RUN_TEST(strsplit); + RUN_TEST(strjoin); + RUN_TEST(strunescape); + + END_TEST; +} + +/* vim: set sw=2 sts=2 et : */ diff --git a/src/daemon/filter_chain.c b/src/daemon/filter_chain.c index b93435fd..0fd4a73e 100644 --- a/src/daemon/filter_chain.c +++ b/src/daemon/filter_chain.c @@ -733,6 +733,8 @@ static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */ "all write plugins failed with status %i (ENOENT). " "Most likely this means you didn't load any write plugins.", status); + + plugin_log_available_writers (); } else if (status != 0) { @@ -763,6 +765,8 @@ static int fc_bit_write_invoke (const data_set_t *ds, /* {{{ */ "Filter subsystem: Built-in target `write': Dispatching value to " "the `%s' plugin failed with status %i.", plugin_list[i].plugin, status); + + plugin_log_available_writers (); } else { diff --git a/src/daemon/plugin.c b/src/daemon/plugin.c index 25bd37b8..3d364458 100644 --- a/src/daemon/plugin.c +++ b/src/daemon/plugin.c @@ -307,6 +307,56 @@ static int register_callback (llist_t **list, /* {{{ */ return (0); } /* }}} int register_callback */ +static void log_list_callbacks (llist_t **list, /* {{{ */ + const char *comment) +{ + char *str; + int len; + llentry_t *le; + int i; + int n; + char **keys; + + n = llist_size(*list); + if (n == 0) + { + INFO("%s [none]", comment); + return; + } + + keys = calloc(n, sizeof(char*)); + + if (keys == NULL) + { + ERROR("%s: failed to allocate memory for list of callbacks", + comment); + + return; + } + + for (le = llist_head (*list), i = 0, len = 0; + le != NULL; + le = le->next, i++) + { + keys[i] = le->key; + len += strlen(le->key) + 6; + } + str = malloc(len + 10); + if (str == NULL) + { + ERROR("%s: failed to allocate memory for list of callbacks", + comment); + } + else + { + *str = '\0'; + strjoin(str, len, keys, n, "', '"); + INFO("%s ['%s']", comment, str); + free(str); + } + free(keys); +} /* }}} void log_list_callbacks */ + static int create_register_callback (llist_t **list, /* {{{ */ const char *name, void *callback, user_data_t *ud) { @@ -1398,6 +1448,11 @@ int plugin_unregister_read (const char *name) /* {{{ */ return (0); } /* }}} int plugin_unregister_read */ +void plugin_log_available_writers (void) +{ + log_list_callbacks (&list_write, "Available write targets:"); +} + static int compare_read_func_group (llentry_t *e, void *ud) /* {{{ */ { read_func_t *rf = e->value; diff --git a/src/daemon/plugin.h b/src/daemon/plugin.h index 86a2d662..70a22326 100644 --- a/src/daemon/plugin.h +++ b/src/daemon/plugin.h @@ -322,6 +322,17 @@ int plugin_unregister_data_set (const char *name); int plugin_unregister_log (const char *name); int plugin_unregister_notification (const char *name); +/* + * NAME + * plugin_log_available_writers + * + * DESCRIPTION + * This function can be called to output a list of _all_ registered + * writers to the logfacility. + * Since some writers dynamically build their name it can be hard for + * the configuring person to know it. This function will fill this gap. + */ +void plugin_log_available_writers (); /* * NAME diff --git a/src/daemon/plugin_mock.c b/src/daemon/plugin_mock.c new file mode 100644 index 00000000..86528809 --- /dev/null +++ b/src/daemon/plugin_mock.c @@ -0,0 +1,41 @@ +/** + * collectd - src/tests/mock/plugin.c + * Copyright (C) 2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +#include "plugin.h" + +void plugin_log (int level, char const *format, ...) +{ + char buffer[1024]; + va_list ap; + + va_start (ap, format); + vsnprintf (buffer, sizeof (buffer), format, ap); + va_end (ap); + + printf ("plugin_log (%i, \"%s\");\n", level, buffer); +} + +/* vim: set sw=2 sts=2 et : */ diff --git a/src/daemon/utils_avltree_test.c b/src/daemon/utils_avltree_test.c new file mode 100644 index 00000000..2a8244c9 --- /dev/null +++ b/src/daemon/utils_avltree_test.c @@ -0,0 +1,82 @@ +/** + * collectd - src/tests/test_utils_avltree.c + * Copyright (C) 2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +#include "testing.h" +#include "collectd.h" +#include "utils_avltree.h" + +static int compare_total_count = 0; +#define RESET_COUNTS() do { compare_total_count = 0; } while (0) + +static int compare_callback (void const *v0, void const *v1) +{ + assert (v0 != NULL); + assert (v1 != NULL); + + compare_total_count++; + return (strcmp (v0, v1)); +} + +DEF_TEST(success) +{ + c_avl_tree_t *t; + char key_orig[] = "foo"; + char value_orig[] = "bar"; + char *key_ret = NULL; + char *value_ret = NULL; + + RESET_COUNTS (); + t = c_avl_create (compare_callback); + OK (t != NULL); + + OK (c_avl_insert (t, key_orig, value_orig) == 0); + OK (c_avl_size (t) == 1); + + /* Key already exists. */ + OK (c_avl_insert (t, "foo", "qux") > 0); + + OK (c_avl_get (t, "foo", (void *) &value_ret) == 0); + OK (value_ret == &value_orig[0]); + + key_ret = value_ret = NULL; + OK (c_avl_remove (t, "foo", (void *) &key_ret, (void *) &value_ret) == 0); + OK (key_ret == &key_orig[0]); + OK (value_ret == &value_orig[0]); + OK (c_avl_size (t) == 0); + + c_avl_destroy (t); + + return (0); +} + +int main (void) +{ + RUN_TEST(success); + + END_TEST; +} + +/* vim: set sw=2 sts=2 et : */ diff --git a/src/daemon/utils_cache_mock.c b/src/daemon/utils_cache_mock.c new file mode 100644 index 00000000..6c78d64d --- /dev/null +++ b/src/daemon/utils_cache_mock.c @@ -0,0 +1,32 @@ +/** + * collectd - src/tests/mock/utils_cache.c + * Copyright (C) 2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +#include "utils_cache.h" + +gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl) +{ + return (NULL); +} diff --git a/src/daemon/utils_heap_test.c b/src/daemon/utils_heap_test.c new file mode 100644 index 00000000..53d0fba8 --- /dev/null +++ b/src/daemon/utils_heap_test.c @@ -0,0 +1,85 @@ +/** + * collectd - src/tests/test_utils_heap.c + * Copyright (C) 2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +#include "testing.h" +#include "collectd.h" +#include "utils_heap.h" + +static int compare (void const *v0, void const *v1) +{ + int const *i0 = v0; + int const *i1 = v1; + + if ((*i0) < (*i1)) + return -1; + else if ((*i0) > (*i1)) + return 1; + else + return 0; +} + +DEF_TEST(simple) +{ + int values[] = { 9, 5, 6, 1, 3, 4, 0, 8, 2, 7 }; + int i; + c_heap_t *h; + + CHECK_NOT_NULL(h = c_heap_create (compare)); + for (i = 0; i < 10; i++) + CHECK_ZERO(c_heap_insert (h, &values[i])); + + for (i = 0; i < 5; i++) + { + int *ret = NULL; + CHECK_NOT_NULL(ret = c_heap_get_root(h)); + OK(*ret == i); + } + + CHECK_ZERO(c_heap_insert (h, &values[6] /* = 0 */)); + CHECK_ZERO(c_heap_insert (h, &values[3] /* = 1 */)); + CHECK_ZERO(c_heap_insert (h, &values[8] /* = 2 */)); + CHECK_ZERO(c_heap_insert (h, &values[4] /* = 3 */)); + CHECK_ZERO(c_heap_insert (h, &values[5] /* = 4 */)); + + for (i = 0; i < 10; i++) + { + int *ret = NULL; + CHECK_NOT_NULL(ret = c_heap_get_root(h)); + OK(*ret == i); + } + + c_heap_destroy(h); + return (0); +} + +int main (void) +{ + RUN_TEST(simple); + + END_TEST; +} + +/* vim: set sw=2 sts=2 et : */ diff --git a/src/daemon/utils_time_mock.c b/src/daemon/utils_time_mock.c new file mode 100644 index 00000000..5edfe6f9 --- /dev/null +++ b/src/daemon/utils_time_mock.c @@ -0,0 +1,33 @@ +/** + * collectd - src/tests/mock/utils_time.c + * Copyright (C) 2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +#include "utils_time.h" + +cdtime_t cdtime (void) +{ + return (0); +} + diff --git a/src/fhcount.c b/src/fhcount.c new file mode 100644 index 00000000..4c409b58 --- /dev/null +++ b/src/fhcount.c @@ -0,0 +1,139 @@ +/** + * + * collectd - src/fhcount.c + * Copyright (c) 2015, Jiri Tyr + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY + * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + **/ + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "configfile.h" + + +static const char *config_keys[] = { + "ValuesAbsolute", + "ValuesPercentage" +}; +static int config_keys_num = STATIC_ARRAY_SIZE(config_keys); + +static _Bool values_absolute = 1; +static _Bool values_percentage = 0; + + +static int fhcount_config(const char *key, const char *value) { + int ret = -1; + + if (strcasecmp(key, "ValuesAbsolute") == 0) { + if (IS_TRUE(value)) { + values_absolute = 1; + } else { + values_absolute = 0; + } + + ret = 0; + } else if (strcasecmp(key, "ValuesPercentage") == 0) { + if (IS_TRUE(value)) { + values_percentage = 1; + } else { + values_percentage = 0; + } + + ret = 0; + } + + return(ret); +} + + +static void fhcount_submit( + const char *type, const char *type_instance, gauge_t value) { + + value_t values[1]; + value_list_t vl = VALUE_LIST_INIT; + + values[0].gauge = value; + + vl.values = values; + vl.values_len = 1; + + // Compose the metric + sstrncpy(vl.host, hostname_g, sizeof(vl.host)); + sstrncpy(vl.plugin, "fhcount", sizeof(vl.plugin)); + sstrncpy(vl.type, type, sizeof(vl.type)); + sstrncpy(vl.type_instance, type_instance, sizeof(vl.type_instance)); + + // Dispatch the metric + plugin_dispatch_values(&vl); +} + + +static int fhcount_read(void) { + int numfields = 0; + int buffer_len = 60; + gauge_t used, unused, max; + int prc_used, prc_unused; + char *fields[3]; + char buffer[buffer_len]; + char errbuf[1024]; + FILE *fp; + + // Open file + fp = fopen("/proc/sys/fs/file-nr" , "r"); + if (fp == NULL) { + ERROR("fhcount: fopen: %s", sstrerror(errno, errbuf, sizeof(errbuf))); + return(EXIT_FAILURE); + } + if (fgets(buffer, buffer_len, fp) == NULL) { + ERROR("fhcount: fgets: %s", sstrerror(errno, errbuf, sizeof(errbuf))); + return(EXIT_FAILURE); + } + fclose(fp); + + // Tokenize string + numfields = strsplit(buffer, fields, STATIC_ARRAY_SIZE(fields)); + + if (numfields != 3) { + ERROR("fhcount: Line doesn't contain 3 fields"); + return(EXIT_FAILURE); + } + + // Define the values + strtogauge(fields[0], &used); + strtogauge(fields[1], &unused); + strtogauge(fields[2], &max); + prc_used = (gauge_t) used/max*100; + prc_unused = (gauge_t) unused/max*100; + + // Submit values + if (values_absolute) { + fhcount_submit("file_handles", "used", (gauge_t) used); + fhcount_submit("file_handles", "unused", (gauge_t) unused); + fhcount_submit("file_handles", "max", (gauge_t) max); + } + if (values_percentage) { + fhcount_submit("percent", "used", (gauge_t) prc_used); + fhcount_submit("percent", "unused", (gauge_t) prc_unused); + } + + return(0); +} + + +void module_register(void) { + plugin_register_config( + "fhcount", fhcount_config, config_keys, config_keys_num); + plugin_register_read("fhcount", fhcount_read); +} diff --git a/src/ipc.c b/src/ipc.c index 2d2db2ab..3763f248 100644 --- a/src/ipc.c +++ b/src/ipc.c @@ -111,7 +111,80 @@ static void ipc_submit_g (const char *plugin_instance, plugin_dispatch_values (&vl); } /* }}} */ -#if KERNEL_AIX +#if KERNEL_LINUX +static int ipc_read_sem (void) /* {{{ */ +{ + struct seminfo seminfo; + union semun arg; + int status; + + arg.array = (void *) &seminfo; + + status = semctl (/* id = */ 0, /* num = */ 0, SEM_INFO, arg); + if (status == -1) + { + char errbuf[1024]; + ERROR("ipc plugin: semctl(2) failed: %s. " + "Maybe the kernel is not configured for semaphores?", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + ipc_submit_g("sem", "count", "arrays", seminfo.semusz); + ipc_submit_g("sem", "count", "total", seminfo.semaem); + + return (0); +} /* }}} int ipc_read_sem */ + +static int ipc_read_shm (void) /* {{{ */ +{ + struct shm_info shm_info; + int status; + + status = shmctl (/* id = */ 0, SHM_INFO, (void *) &shm_info); + if (status == -1) + { + char errbuf[1024]; + ERROR("ipc plugin: shmctl(2) failed: %s. " + "Maybe the kernel is not configured for shared memory?", + sstrerror (errno, errbuf, sizeof (errbuf))); + return (-1); + } + + ipc_submit_g("shm", "segments", NULL, shm_info.used_ids); + ipc_submit_g("shm", "bytes", "total", shm_info.shm_tot * pagesize_g); + ipc_submit_g("shm", "bytes", "rss", shm_info.shm_rss * pagesize_g); + ipc_submit_g("shm", "bytes", "swapped", shm_info.shm_swp * pagesize_g); + return (0); +} +/* }}} int ipc_read_shm */ + +static int ipc_read_msg (void) /* {{{ */ +{ + struct msginfo msginfo; + + if ( msgctl(0, MSG_INFO, (struct msqid_ds *) (void *) &msginfo) < 0 ) + { + ERROR("Kernel is not configured for message queues"); + return (-1); + } + ipc_submit_g("msg", "count", "queues", msginfo.msgmni); + ipc_submit_g("msg", "count", "headers", msginfo.msgmap); + ipc_submit_g("msg", "count", "space", msginfo.msgtql); + + return (0); +} +/* }}} int ipc_read_msg */ + +static int ipc_init (void) /* {{{ */ +{ + pagesize_g = sysconf(_SC_PAGESIZE); + return (0); +} +/* }}} */ +/* #endif KERNEL_LINUX */ + +#elif KERNEL_AIX static caddr_t ipc_get_info (cid_t cid, int cmd, int version, int stsize, int *nmemb) /* {{{ */ { int size = 0; @@ -154,27 +227,9 @@ static caddr_t ipc_get_info (cid_t cid, int cmd, int version, int stsize, int *n return buff; } /* }}} */ -#endif /* KERNEL_AIX */ static int ipc_read_sem (void) /* {{{ */ { -#if KERNEL_LINUX - struct seminfo seminfo; - union semun arg; - - arg.array = (ushort *) (void *) &seminfo; - - if ( semctl(0, 0, SEM_INFO, arg) < 0 ) - { - ERROR("Kernel is not configured for semaphores"); - return (-1); - } - - ipc_submit_g("sem", "count", "arrays", seminfo.semusz); - ipc_submit_g("sem", "count", "total", seminfo.semaem); - -/* #endif KERNEL_LINUX */ -#elif KERNEL_AIX ipcinfo_sem_t *ipcinfo_sem; unsigned short sem_nsems=0; unsigned short sems=0; @@ -193,28 +248,12 @@ static int ipc_read_sem (void) /* {{{ */ ipc_submit_g("sem", "count", "arrays", sem_nsems); ipc_submit_g("sem", "count", "total", sems); -#endif /* KERNEL_AIX */ return (0); -} -/* }}} */ +} /* }}} int ipc_read_sem */ static int ipc_read_shm (void) /* {{{ */ { -#if KERNEL_LINUX - struct shm_info shm_info; - - if ( shmctl(0, SHM_INFO, (struct shmid_ds *) (void *) &shm_info) < 0 ) - { - ERROR("Kernel is not configured for shared memory"); - return (-1); - } - ipc_submit_g("shm", "segments", NULL, shm_info.used_ids); - ipc_submit_g("shm", "bytes", "total", shm_info.shm_tot * pagesize_g); - ipc_submit_g("shm", "bytes", "rss", shm_info.shm_rss * pagesize_g); - ipc_submit_g("shm", "bytes", "swapped", shm_info.shm_swp * pagesize_g); -/* #endif KERNEL_LINUX */ -#elif KERNEL_AIX ipcinfo_shm_t *ipcinfo_shm; ipcinfo_shm_t *pshm; unsigned int shm_segments=0; @@ -235,26 +274,12 @@ static int ipc_read_shm (void) /* {{{ */ ipc_submit_g("shm", "segments", NULL, shm_segments); ipc_submit_g("shm", "bytes", "total", shm_bytes); -#endif /* KERNEL_AIX */ return (0); } -/* }}} */ +/* }}} int ipc_read_shm */ static int ipc_read_msg (void) /* {{{ */ { -#if KERNEL_LINUX - struct msginfo msginfo; - - if ( msgctl(0, MSG_INFO, (struct msqid_ds *) (void *) &msginfo) < 0 ) - { - ERROR("Kernel is not configured for message queues"); - return (-1); - } - ipc_submit_g("msg", "count", "queues", msginfo.msgmni); - ipc_submit_g("msg", "count", "headers", msginfo.msgmap); - ipc_submit_g("msg", "count", "space", msginfo.msgtql); -/* #endif KERNEL_LINUX */ -#elif KERNEL_AIX ipcinfo_msg_t *ipcinfo_msg; uint32_t msg_used_space=0; uint32_t msg_alloc_queues=0; @@ -270,16 +295,17 @@ static int ipc_read_msg (void) /* {{{ */ msg_alloc_queues++; msg_used_space += ipcinfo_msg[i].msg_cbytes; msg_qnum += ipcinfo_msg[i].msg_qnum; - } + free(ipcinfo_msg); ipc_submit_g("msg", "count", "queues", msg_alloc_queues); ipc_submit_g("msg", "count", "headers", msg_qnum); ipc_submit_g("msg", "count", "space", msg_used_space); -#endif /* KERNEL_AIX */ + return (0); } /* }}} */ +#endif /* KERNEL_AIX */ static int ipc_read (void) /* {{{ */ { @@ -292,15 +318,6 @@ static int ipc_read (void) /* {{{ */ } /* }}} */ -#ifdef KERNEL_LINUX -static int ipc_init (void) /* {{{ */ -{ - pagesize_g = sysconf(_SC_PAGESIZE); - return (0); -} -/* }}} */ -#endif /* KERNEL_LINUX */ - void module_register (void) /* {{{ */ { #ifdef KERNEL_LINUX diff --git a/src/java.c b/src/java.c index d0423be9..2f1efbf1 100644 --- a/src/java.c +++ b/src/java.c @@ -3078,10 +3078,8 @@ static int cjni_init (void) /* {{{ */ if (config_block != NULL) { - cjni_config_perform (config_block); oconfig_free (config_block); - config_block = NULL; } if (jvm == NULL) diff --git a/src/libcollectdclient/network.c b/src/libcollectdclient/network.c index ddb3b5b9..c390a1ce 100644 --- a/src/libcollectdclient/network.c +++ b/src/libcollectdclient/network.c @@ -1,6 +1,6 @@ /** * collectd - src/libcollectdclient/network.c - * Copyright (C) 2005-2013 Florian Forster + * Copyright (C) 2005-2015 Florian Forster * Copyright (C) 2010 Max Henkel * * Permission is hereby granted, free of charge, to any person obtaining a @@ -219,7 +219,13 @@ static int server_send_buffer (lcc_server_t *srv) /* {{{ */ memset (buffer, 0, sizeof (buffer)); buffer_size = sizeof (buffer); - lcc_network_buffer_finalize (srv->buffer); + status = lcc_network_buffer_finalize (srv->buffer); + if (status != 0) + { + lcc_network_buffer_initialize (srv->buffer); + return (status); + } + status = lcc_network_buffer_get (srv->buffer, buffer, &buffer_size); lcc_network_buffer_initialize (srv->buffer); diff --git a/src/libcollectdclient/network_buffer.c b/src/libcollectdclient/network_buffer.c index 61c7c22e..343c285a 100644 --- a/src/libcollectdclient/network_buffer.c +++ b/src/libcollectdclient/network_buffer.c @@ -1,6 +1,6 @@ /** * collectd - src/libcollectdclient/network_buffer.c - * Copyright (C) 2010-2012 Florian octo Forster + * Copyright (C) 2010-2015 Florian octo Forster * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), @@ -792,9 +792,9 @@ int lcc_network_buffer_finalize (lcc_network_buffer_t *nb) /* {{{ */ #if HAVE_LIBGCRYPT if (nb->seclevel == SIGN) - nb_add_signature (nb); + return nb_add_signature (nb); else if (nb->seclevel == ENCRYPT) - nb_add_encryption (nb); + return nb_add_encryption (nb); #endif return (0); diff --git a/src/liboconfig/oconfig.c b/src/liboconfig/oconfig.c index 539c9d35..bf21b90e 100644 --- a/src/liboconfig/oconfig.c +++ b/src/liboconfig/oconfig.c @@ -195,7 +195,7 @@ oconfig_item_t *oconfig_clone (const oconfig_item_t *ci_orig) return (ci_copy); } /* oconfig_item_t *oconfig_clone */ -void oconfig_free (oconfig_item_t *ci) +void oconfig_free_all (oconfig_item_t *ci) { int i; @@ -214,12 +214,19 @@ void oconfig_free (oconfig_item_t *ci) free (ci->values); for (i = 0; i < ci->children_num; i++) - oconfig_free (ci->children + i); + oconfig_free_all (ci->children + i); if (ci->children != NULL) free (ci->children); } +void oconfig_free (oconfig_item_t *ci) +{ + oconfig_free_all (ci); + free (ci); + ci = NULL; +} + /* * vim:shiftwidth=2:tabstop=8:softtabstop=2:fdm=marker */ diff --git a/src/postgresql.c b/src/postgresql.c index 47c9c5c2..54c856d6 100644 --- a/src/postgresql.c +++ b/src/postgresql.c @@ -772,7 +772,7 @@ static char *values_to_sqlarray (const data_set_t *ds, const value_list_t *vl, if (ds->ds[i].type == DS_TYPE_GAUGE) status = ssnprintf (str_ptr, str_len, - ",%f", vl->values[i].gauge); + ","GAUGE_FORMAT, vl->values[i].gauge); else if (store_rates) { if (rates == NULL) rates = uc_get_rate (ds, vl); diff --git a/src/processes.c b/src/processes.c index 5a09b573..0649eab2 100644 --- a/src/processes.c +++ b/src/processes.c @@ -112,7 +112,22 @@ /* #endif HAVE_PROCINFO_H */ #elif KERNEL_SOLARIS +/* Hack: Avoid #error when building a 32-bit binary with + * _FILE_OFFSET_BITS=64. There is a reason for this #error, as one + * of the structures in uses an off_t, but that + * isn't relevant to our usage of procfs. */ +#if !defined(_LP64) && _FILE_OFFSET_BITS == 64 +# define SAVE_FOB_64 +# undef _FILE_OFFSET_BITS +#endif + # include + +#ifdef SAVE_FOB_64 +# define _FILE_OFFSET_BITS 64 +# undef SAVE_FOB_64 +#endif + # include /* #endif KERNEL_SOLARIS */ @@ -1199,17 +1214,17 @@ static int read_fork_rate () #endif /*KERNEL_LINUX */ #if KERNEL_SOLARIS -static const char *ps_get_cmdline (pid_t pid, /* {{{ */ +static const char *ps_get_cmdline (long pid, /* {{{ */ char *buffer, size_t buffer_size) { char path[PATH_MAX]; psinfo_t info; int status; - snprintf(path, sizeof (path), "/proc/%i/psinfo", pid); + snprintf(path, sizeof (path), "/proc/%li/psinfo", pid); status = read_file_contents (path, (void *) &info, sizeof (info)); - if (status != ((int) buffer_size)) + if (status != sizeof (info)) { ERROR ("processes plugin: Unexpected return value " "while reading \"%s\": " @@ -1230,7 +1245,7 @@ static const char *ps_get_cmdline (pid_t pid, /* {{{ */ * The values for input and ouput chars are calculated "by hand" * Added a few "solaris" specific process states as well */ -static int ps_read_process(int pid, procstat_t *ps, char *state) +static int ps_read_process(long pid, procstat_t *ps, char *state) { char filename[64]; char f_psinfo[64], f_usage[64]; @@ -1240,9 +1255,9 @@ static int ps_read_process(int pid, procstat_t *ps, char *state) psinfo_t *myInfo; prusage_t *myUsage; - snprintf(filename, sizeof (filename), "/proc/%i/status", pid); - snprintf(f_psinfo, sizeof (f_psinfo), "/proc/%i/psinfo", pid); - snprintf(f_usage, sizeof (f_usage), "/proc/%i/usage", pid); + snprintf(filename, sizeof (filename), "/proc/%li/status", pid); + snprintf(f_psinfo, sizeof (f_psinfo), "/proc/%li/psinfo", pid); + snprintf(f_usage, sizeof (f_usage), "/proc/%li/usage", pid); buffer = malloc(sizeof (pstatus_t)); @@ -2224,14 +2239,16 @@ static int ps_read (void) while ((ent = readdir(proc)) != NULL) { - int pid; + long pid; struct procstat ps; procstat_entry_t pse; + char *endptr; if (!isdigit ((int) ent->d_name[0])) continue; - if ((pid = atoi (ent->d_name)) < 1) + pid = strtol (ent->d_name, &endptr, 10); + if (*endptr != 0) /* value didn't completely parse as a number */ continue; status = ps_read_process (pid, &ps, &state); @@ -2281,8 +2298,7 @@ static int ps_read (void) ps_list_add (ps.name, - ps_get_cmdline ((pid_t) pid, - cmdline, sizeof (cmdline)), + ps_get_cmdline (pid, cmdline, sizeof (cmdline)), &pse); } /* while(readdir) */ closedir (proc); diff --git a/src/rrdtool.c b/src/rrdtool.c index c795e265..bebf468c 100644 --- a/src/rrdtool.c +++ b/src/rrdtool.c @@ -227,7 +227,7 @@ static int value_list_to_string_multiple (char *buffer, int buffer_len, ":%llu", vl->values[i].counter); else if (ds->ds[i].type == DS_TYPE_GAUGE) status = ssnprintf (buffer + offset, buffer_len - offset, - ":%lf", vl->values[i].gauge); + ":"GAUGE_FORMAT, vl->values[i].gauge); else if (ds->ds[i].type == DS_TYPE_DERIVE) status = ssnprintf (buffer + offset, buffer_len - offset, ":%"PRIi64, vl->values[i].derive); @@ -262,7 +262,7 @@ static int value_list_to_string (char *buffer, int buffer_len, (unsigned) tt, vl->values[0].derive); break; case DS_TYPE_GAUGE: - status = ssnprintf (buffer, buffer_len, "%u:%lf", + status = ssnprintf (buffer, buffer_len, "%u:"GAUGE_FORMAT, (unsigned) tt, vl->values[0].gauge); break; case DS_TYPE_COUNTER: diff --git a/src/target_notification.c b/src/target_notification.c index 5eaa4275..2e5ab3b2 100644 --- a/src/target_notification.c +++ b/src/target_notification.c @@ -256,12 +256,12 @@ static int tn_invoke (const data_set_t *ds, value_list_t *vl, /* {{{ */ /* If this is a gauge value, use the current value. */ if (ds->ds[i].type == DS_TYPE_GAUGE) ssnprintf (value_str, sizeof (value_str), - "%g", (double) vl->values[i].gauge); + GAUGE_FORMAT, (double) vl->values[i].gauge); /* If it's a counter, try to use the current rate. This may fail, if the * value has been renamed. */ else if (rates != NULL) ssnprintf (value_str, sizeof (value_str), - "%g", (double) rates[i]); + GAUGE_FORMAT, (double) rates[i]); /* Since we don't know any better, use the string `unknown'. */ else sstrncpy (value_str, "unknown", sizeof (value_str)); diff --git a/src/testing.h b/src/testing.h new file mode 100644 index 00000000..5df1b83a --- /dev/null +++ b/src/testing.h @@ -0,0 +1,67 @@ +/** + * collectd - src/tests/macros.h + * Copyright (C) 2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +static int fail_count__ = 0; +static int check_count__ = 0; + +#define DEF_TEST(func) static int test_##func () + +#define RUN_TEST(func) do { \ + int status; \ + printf ("Testing %s ...\n", #func); \ + status = test_ ## func (); \ + printf ("%s.\n", (status == 0) ? "Success" : "FAILURE"); \ + if (status != 0) { fail_count__++; } \ +} while (0) + +#define END_TEST exit ((fail_count__ == 0) ? 0 : 1); + +#define OK1(cond, text) do { \ + _Bool result = (cond); \ + printf ("%s %i - %s\n", result ? "ok" : "not ok", ++check_count__, text); \ +} while (0) +#define OK(cond) OK1(cond, #cond) + +#define STREQ(expect, actual) do { \ + if (strcmp (expect, actual) != 0) { \ + printf ("not ok %i - %s incorrect: expected \"%s\", got \"%s\"\n", \ + ++check_count__, #actual, expect, actual); \ + return (-1); \ + } \ + printf ("ok %i - %s evaluates to \"%s\"\n", ++check_count__, #actual, expect); \ +} while (0) + +#define CHECK_NOT_NULL(expr) do { \ + void *ptr_; \ + ptr_ = (expr); \ + OK1(ptr_ != NULL, #expr); \ +} while (0) + +#define CHECK_ZERO(expr) do { \ + long status_; \ + status_ = (long) (expr); \ + OK1(status_ == 0L, #expr); \ +} while (0) diff --git a/src/tests/common_test.c b/src/tests/common_test.c deleted file mode 100644 index f65fcab3..00000000 --- a/src/tests/common_test.c +++ /dev/null @@ -1,246 +0,0 @@ -/** - * collectd - src/tests/common_test.c - * Copyright (C) 2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "tests/macros.h" -#include "common.h" - -DEF_TEST(sstrncpy) -{ - char buffer[16] = ""; - char *ptr = &buffer[4]; - char *ret; - - buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff; - buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff; - - ret = sstrncpy (ptr, "foobar", 8); - OK(ret == ptr); - STREQ ("foobar", ptr); - OK(buffer[3] == buffer[12]); - - ret = sstrncpy (ptr, "abc", 8); - OK(ret == ptr); - STREQ ("abc", ptr); - OK(buffer[3] == buffer[12]); - - ret = sstrncpy (ptr, "collectd", 8); - OK(ret == ptr); - OK(ptr[7] == 0); - STREQ ("collect", ptr); - OK(buffer[3] == buffer[12]); - - return (0); -} - -DEF_TEST(ssnprintf) -{ - char buffer[16] = ""; - char *ptr = &buffer[4]; - int status; - - buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff; - buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff; - - status = ssnprintf (ptr, 8, "%i", 1337); - OK(status == 4); - STREQ ("1337", ptr); - - status = ssnprintf (ptr, 8, "%s", "collectd"); - OK(status == 8); - OK(ptr[7] == 0); - STREQ ("collect", ptr); - OK(buffer[3] == buffer[12]); - - return (0); -} - -DEF_TEST(sstrdup) -{ - char *ptr; - - ptr = sstrdup ("collectd"); - OK(ptr != NULL); - STREQ ("collectd", ptr); - - sfree(ptr); - OK(ptr == NULL); - - ptr = sstrdup (NULL); - OK(ptr == NULL); - - return (0); -} - -DEF_TEST(strsplit) -{ - char buffer[32]; - char *fields[8]; - int status; - - strncpy (buffer, "foo bar", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 2); - STREQ ("foo", fields[0]); - STREQ ("bar", fields[1]); - - strncpy (buffer, "foo \t bar", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 2); - STREQ ("foo", fields[0]); - STREQ ("bar", fields[1]); - - strncpy (buffer, "one two\tthree\rfour\nfive", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 5); - STREQ ("one", fields[0]); - STREQ ("two", fields[1]); - STREQ ("three", fields[2]); - STREQ ("four", fields[3]); - STREQ ("five", fields[4]); - - strncpy (buffer, "\twith trailing\n", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 2); - STREQ ("with", fields[0]); - STREQ ("trailing", fields[1]); - - strncpy (buffer, "1 2 3 4 5 6 7 8 9 10 11 12 13", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 8); - STREQ ("7", fields[6]); - STREQ ("8", fields[7]); - - strncpy (buffer, "single", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 1); - STREQ ("single", fields[0]); - - strncpy (buffer, "", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 0); - - return (0); -} - -DEF_TEST(strjoin) -{ - char buffer[16]; - char *fields[4]; - int status; - - fields[0] = "foo"; - fields[1] = "bar"; - fields[2] = "baz"; - fields[3] = "qux"; - - status = strjoin (buffer, sizeof (buffer), fields, 2, "!"); - OK(status == 7); - STREQ ("foo!bar", buffer); - - status = strjoin (buffer, sizeof (buffer), fields, 1, "!"); - OK(status == 3); - STREQ ("foo", buffer); - - status = strjoin (buffer, sizeof (buffer), fields, 0, "!"); - OK(status < 0); - - status = strjoin (buffer, sizeof (buffer), fields, 2, "rcht"); - OK(status == 10); - STREQ ("foorchtbar", buffer); - - status = strjoin (buffer, sizeof (buffer), fields, 4, ""); - OK(status == 12); - STREQ ("foobarbazqux", buffer); - - status = strjoin (buffer, sizeof (buffer), fields, 4, "!"); - OK(status == 15); - STREQ ("foo!bar!baz!qux", buffer); - - fields[0] = "0123"; - fields[1] = "4567"; - fields[2] = "8901"; - fields[3] = "2345"; - status = strjoin (buffer, sizeof (buffer), fields, 4, "-"); - OK(status < 0); - - return (0); -} - -DEF_TEST(strunescape) -{ - char buffer[16]; - int status; - - strncpy (buffer, "foo\\tbar", sizeof (buffer)); - status = strunescape (buffer, sizeof (buffer)); - OK(status == 0); - STREQ ("foo\tbar", buffer); - - strncpy (buffer, "\\tfoo\\r\\n", sizeof (buffer)); - status = strunescape (buffer, sizeof (buffer)); - OK(status == 0); - STREQ ("\tfoo\r\n", buffer); - - strncpy (buffer, "With \\\"quotes\\\"", sizeof (buffer)); - status = strunescape (buffer, sizeof (buffer)); - OK(status == 0); - STREQ ("With \"quotes\"", buffer); - - /* Backslash before null byte */ - strncpy (buffer, "\\tbackslash end\\", sizeof (buffer)); - status = strunescape (buffer, sizeof (buffer)); - OK(status != 0); - STREQ ("\tbackslash end", buffer); - return (0); - - /* Backslash at buffer end */ - strncpy (buffer, "\\t3\\56", sizeof (buffer)); - status = strunescape (buffer, 4); - OK(status != 0); - OK(buffer[0] == '\t'); - OK(buffer[1] == '3'); - OK(buffer[2] == 0); - OK(buffer[3] == 0); - OK(buffer[4] == '5'); - OK(buffer[5] == '6'); - OK(buffer[6] == '7'); - - return (0); -} - -int main (void) -{ - RUN_TEST(sstrncpy); - RUN_TEST(ssnprintf); - RUN_TEST(sstrdup); - RUN_TEST(strsplit); - RUN_TEST(strjoin); - RUN_TEST(strunescape); - - END_TEST; -} - -/* vim: set sw=2 sts=2 et : */ diff --git a/src/tests/macros.h b/src/tests/macros.h deleted file mode 100644 index 5df1b83a..00000000 --- a/src/tests/macros.h +++ /dev/null @@ -1,67 +0,0 @@ -/** - * collectd - src/tests/macros.h - * Copyright (C) 2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -static int fail_count__ = 0; -static int check_count__ = 0; - -#define DEF_TEST(func) static int test_##func () - -#define RUN_TEST(func) do { \ - int status; \ - printf ("Testing %s ...\n", #func); \ - status = test_ ## func (); \ - printf ("%s.\n", (status == 0) ? "Success" : "FAILURE"); \ - if (status != 0) { fail_count__++; } \ -} while (0) - -#define END_TEST exit ((fail_count__ == 0) ? 0 : 1); - -#define OK1(cond, text) do { \ - _Bool result = (cond); \ - printf ("%s %i - %s\n", result ? "ok" : "not ok", ++check_count__, text); \ -} while (0) -#define OK(cond) OK1(cond, #cond) - -#define STREQ(expect, actual) do { \ - if (strcmp (expect, actual) != 0) { \ - printf ("not ok %i - %s incorrect: expected \"%s\", got \"%s\"\n", \ - ++check_count__, #actual, expect, actual); \ - return (-1); \ - } \ - printf ("ok %i - %s evaluates to \"%s\"\n", ++check_count__, #actual, expect); \ -} while (0) - -#define CHECK_NOT_NULL(expr) do { \ - void *ptr_; \ - ptr_ = (expr); \ - OK1(ptr_ != NULL, #expr); \ -} while (0) - -#define CHECK_ZERO(expr) do { \ - long status_; \ - status_ = (long) (expr); \ - OK1(status_ == 0L, #expr); \ -} while (0) diff --git a/src/tests/mock/plugin.c b/src/tests/mock/plugin.c deleted file mode 100644 index 86528809..00000000 --- a/src/tests/mock/plugin.c +++ /dev/null @@ -1,41 +0,0 @@ -/** - * collectd - src/tests/mock/plugin.c - * Copyright (C) 2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "plugin.h" - -void plugin_log (int level, char const *format, ...) -{ - char buffer[1024]; - va_list ap; - - va_start (ap, format); - vsnprintf (buffer, sizeof (buffer), format, ap); - va_end (ap); - - printf ("plugin_log (%i, \"%s\");\n", level, buffer); -} - -/* vim: set sw=2 sts=2 et : */ diff --git a/src/tests/mock/utils_cache.c b/src/tests/mock/utils_cache.c deleted file mode 100644 index 6c78d64d..00000000 --- a/src/tests/mock/utils_cache.c +++ /dev/null @@ -1,32 +0,0 @@ -/** - * collectd - src/tests/mock/utils_cache.c - * Copyright (C) 2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "utils_cache.h" - -gauge_t *uc_get_rate (const data_set_t *ds, const value_list_t *vl) -{ - return (NULL); -} diff --git a/src/tests/mock/utils_time.c b/src/tests/mock/utils_time.c deleted file mode 100644 index 5edfe6f9..00000000 --- a/src/tests/mock/utils_time.c +++ /dev/null @@ -1,33 +0,0 @@ -/** - * collectd - src/tests/mock/utils_time.c - * Copyright (C) 2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "utils_time.h" - -cdtime_t cdtime (void) -{ - return (0); -} - diff --git a/src/tests/test_common.c b/src/tests/test_common.c deleted file mode 100644 index c2eec953..00000000 --- a/src/tests/test_common.c +++ /dev/null @@ -1,246 +0,0 @@ -/** - * collectd - src/tests/test_common.c - * Copyright (C) 2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "tests/macros.h" -#include "common.h" - -DEF_TEST(sstrncpy) -{ - char buffer[16] = ""; - char *ptr = &buffer[4]; - char *ret; - - buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff; - buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff; - - ret = sstrncpy (ptr, "foobar", 8); - OK(ret == ptr); - STREQ ("foobar", ptr); - OK(buffer[3] == buffer[12]); - - ret = sstrncpy (ptr, "abc", 8); - OK(ret == ptr); - STREQ ("abc", ptr); - OK(buffer[3] == buffer[12]); - - ret = sstrncpy (ptr, "collectd", 8); - OK(ret == ptr); - OK(ptr[7] == 0); - STREQ ("collect", ptr); - OK(buffer[3] == buffer[12]); - - return (0); -} - -DEF_TEST(ssnprintf) -{ - char buffer[16] = ""; - char *ptr = &buffer[4]; - int status; - - buffer[0] = buffer[1] = buffer[2] = buffer[3] = 0xff; - buffer[12] = buffer[13] = buffer[14] = buffer[15] = 0xff; - - status = ssnprintf (ptr, 8, "%i", 1337); - OK(status == 4); - STREQ ("1337", ptr); - - status = ssnprintf (ptr, 8, "%s", "collectd"); - OK(status == 8); - OK(ptr[7] == 0); - STREQ ("collect", ptr); - OK(buffer[3] == buffer[12]); - - return (0); -} - -DEF_TEST(sstrdup) -{ - char *ptr; - - ptr = sstrdup ("collectd"); - OK(ptr != NULL); - STREQ ("collectd", ptr); - - sfree(ptr); - OK(ptr == NULL); - - ptr = sstrdup (NULL); - OK(ptr == NULL); - - return (0); -} - -DEF_TEST(strsplit) -{ - char buffer[32]; - char *fields[8]; - int status; - - strncpy (buffer, "foo bar", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 2); - STREQ ("foo", fields[0]); - STREQ ("bar", fields[1]); - - strncpy (buffer, "foo \t bar", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 2); - STREQ ("foo", fields[0]); - STREQ ("bar", fields[1]); - - strncpy (buffer, "one two\tthree\rfour\nfive", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 5); - STREQ ("one", fields[0]); - STREQ ("two", fields[1]); - STREQ ("three", fields[2]); - STREQ ("four", fields[3]); - STREQ ("five", fields[4]); - - strncpy (buffer, "\twith trailing\n", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 2); - STREQ ("with", fields[0]); - STREQ ("trailing", fields[1]); - - strncpy (buffer, "1 2 3 4 5 6 7 8 9 10 11 12 13", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 8); - STREQ ("7", fields[6]); - STREQ ("8", fields[7]); - - strncpy (buffer, "single", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 1); - STREQ ("single", fields[0]); - - strncpy (buffer, "", sizeof (buffer)); - status = strsplit (buffer, fields, 8); - OK(status == 0); - - return (0); -} - -DEF_TEST(strjoin) -{ - char buffer[16]; - char *fields[4]; - int status; - - fields[0] = "foo"; - fields[1] = "bar"; - fields[2] = "baz"; - fields[3] = "qux"; - - status = strjoin (buffer, sizeof (buffer), fields, 2, "!"); - OK(status == 7); - STREQ ("foo!bar", buffer); - - status = strjoin (buffer, sizeof (buffer), fields, 1, "!"); - OK(status == 3); - STREQ ("foo", buffer); - - status = strjoin (buffer, sizeof (buffer), fields, 0, "!"); - OK(status < 0); - - status = strjoin (buffer, sizeof (buffer), fields, 2, "rcht"); - OK(status == 10); - STREQ ("foorchtbar", buffer); - - status = strjoin (buffer, sizeof (buffer), fields, 4, ""); - OK(status == 12); - STREQ ("foobarbazqux", buffer); - - status = strjoin (buffer, sizeof (buffer), fields, 4, "!"); - OK(status == 15); - STREQ ("foo!bar!baz!qux", buffer); - - fields[0] = "0123"; - fields[1] = "4567"; - fields[2] = "8901"; - fields[3] = "2345"; - status = strjoin (buffer, sizeof (buffer), fields, 4, "-"); - OK(status < 0); - - return (0); -} - -DEF_TEST(strunescape) -{ - char buffer[16]; - int status; - - strncpy (buffer, "foo\\tbar", sizeof (buffer)); - status = strunescape (buffer, sizeof (buffer)); - OK(status == 0); - STREQ ("foo\tbar", buffer); - - strncpy (buffer, "\\tfoo\\r\\n", sizeof (buffer)); - status = strunescape (buffer, sizeof (buffer)); - OK(status == 0); - STREQ ("\tfoo\r\n", buffer); - - strncpy (buffer, "With \\\"quotes\\\"", sizeof (buffer)); - status = strunescape (buffer, sizeof (buffer)); - OK(status == 0); - STREQ ("With \"quotes\"", buffer); - - /* Backslash before null byte */ - strncpy (buffer, "\\tbackslash end\\", sizeof (buffer)); - status = strunescape (buffer, sizeof (buffer)); - OK(status != 0); - STREQ ("\tbackslash end", buffer); - return (0); - - /* Backslash at buffer end */ - strncpy (buffer, "\\t3\\56", sizeof (buffer)); - status = strunescape (buffer, 4); - OK(status != 0); - OK(buffer[0] == '\t'); - OK(buffer[1] == '3'); - OK(buffer[2] == 0); - OK(buffer[3] == 0); - OK(buffer[4] == '5'); - OK(buffer[5] == '6'); - OK(buffer[6] == '7'); - - return (0); -} - -int main (void) -{ - RUN_TEST(sstrncpy); - RUN_TEST(ssnprintf); - RUN_TEST(sstrdup); - RUN_TEST(strsplit); - RUN_TEST(strjoin); - RUN_TEST(strunescape); - - END_TEST; -} - -/* vim: set sw=2 sts=2 et : */ diff --git a/src/tests/test_utils_avltree.c b/src/tests/test_utils_avltree.c deleted file mode 100644 index 00d14e16..00000000 --- a/src/tests/test_utils_avltree.c +++ /dev/null @@ -1,82 +0,0 @@ -/** - * collectd - src/tests/test_utils_avltree.c - * Copyright (C) 2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "tests/macros.h" -#include "collectd.h" -#include "utils_avltree.h" - -static int compare_total_count = 0; -#define RESET_COUNTS() do { compare_total_count = 0; } while (0) - -static int compare_callback (void const *v0, void const *v1) -{ - assert (v0 != NULL); - assert (v1 != NULL); - - compare_total_count++; - return (strcmp (v0, v1)); -} - -DEF_TEST(success) -{ - c_avl_tree_t *t; - char key_orig[] = "foo"; - char value_orig[] = "bar"; - char *key_ret = NULL; - char *value_ret = NULL; - - RESET_COUNTS (); - t = c_avl_create (compare_callback); - OK (t != NULL); - - OK (c_avl_insert (t, key_orig, value_orig) == 0); - OK (c_avl_size (t) == 1); - - /* Key already exists. */ - OK (c_avl_insert (t, "foo", "qux") > 0); - - OK (c_avl_get (t, "foo", (void *) &value_ret) == 0); - OK (value_ret == &value_orig[0]); - - key_ret = value_ret = NULL; - OK (c_avl_remove (t, "foo", (void *) &key_ret, (void *) &value_ret) == 0); - OK (key_ret == &key_orig[0]); - OK (value_ret == &value_orig[0]); - OK (c_avl_size (t) == 0); - - c_avl_destroy (t); - - return (0); -} - -int main (void) -{ - RUN_TEST(success); - - END_TEST; -} - -/* vim: set sw=2 sts=2 et : */ diff --git a/src/tests/test_utils_heap.c b/src/tests/test_utils_heap.c deleted file mode 100644 index 91c90e5d..00000000 --- a/src/tests/test_utils_heap.c +++ /dev/null @@ -1,85 +0,0 @@ -/** - * collectd - src/tests/test_utils_heap.c - * Copyright (C) 2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "collectd.h" -#include "tests/macros.h" -#include "utils_heap.h" - -static int compare (void const *v0, void const *v1) -{ - int const *i0 = v0; - int const *i1 = v1; - - if ((*i0) < (*i1)) - return -1; - else if ((*i0) > (*i1)) - return 1; - else - return 0; -} - -DEF_TEST(simple) -{ - int values[] = { 9, 5, 6, 1, 3, 4, 0, 8, 2, 7 }; - int i; - c_heap_t *h; - - CHECK_NOT_NULL(h = c_heap_create (compare)); - for (i = 0; i < 10; i++) - CHECK_ZERO(c_heap_insert (h, &values[i])); - - for (i = 0; i < 5; i++) - { - int *ret = NULL; - CHECK_NOT_NULL(ret = c_heap_get_root(h)); - OK(*ret == i); - } - - CHECK_ZERO(c_heap_insert (h, &values[6] /* = 0 */)); - CHECK_ZERO(c_heap_insert (h, &values[3] /* = 1 */)); - CHECK_ZERO(c_heap_insert (h, &values[8] /* = 2 */)); - CHECK_ZERO(c_heap_insert (h, &values[4] /* = 3 */)); - CHECK_ZERO(c_heap_insert (h, &values[5] /* = 4 */)); - - for (i = 0; i < 10; i++) - { - int *ret = NULL; - CHECK_NOT_NULL(ret = c_heap_get_root(h)); - OK(*ret == i); - } - - c_heap_destroy(h); - return (0); -} - -int main (void) -{ - RUN_TEST(simple); - - END_TEST; -} - -/* vim: set sw=2 sts=2 et : */ diff --git a/src/tests/test_utils_mount.c b/src/tests/test_utils_mount.c deleted file mode 100644 index ba6b99bb..00000000 --- a/src/tests/test_utils_mount.c +++ /dev/null @@ -1,101 +0,0 @@ -/** - * collectd - src/tests/test_utils_mount.c - * Copyright (C) 2013 Florian octo Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian octo Forster - */ - -#include "tests/macros.h" -#include "collectd.h" -#include "utils_mount.h" - -DEF_TEST(cu_mount_checkoption) -{ - char line_opts[] = "foo=one,bar=two,qux=three"; - char *foo = strstr (line_opts, "foo"); - char *bar = strstr (line_opts, "bar"); - char *qux = strstr (line_opts, "qux"); - - char line_bool[] = "one,two,three"; - char *one = strstr (line_bool, "one"); - char *two = strstr (line_bool, "two"); - char *three = strstr (line_bool, "three"); - - /* Normal operation */ - OK (foo == cu_mount_checkoption (line_opts, "foo", 0)); - OK (bar == cu_mount_checkoption (line_opts, "bar", 0)); - OK (qux == cu_mount_checkoption (line_opts, "qux", 0)); - OK (NULL == cu_mount_checkoption (line_opts, "unknown", 0)); - - OK (one == cu_mount_checkoption (line_bool, "one", 0)); - OK (two == cu_mount_checkoption (line_bool, "two", 0)); - OK (three == cu_mount_checkoption (line_bool, "three", 0)); - OK (NULL == cu_mount_checkoption (line_bool, "four", 0)); - - /* Shorter and longer parts */ - OK (foo == cu_mount_checkoption (line_opts, "fo", 0)); - OK (bar == cu_mount_checkoption (line_opts, "bar=", 0)); - OK (qux == cu_mount_checkoption (line_opts, "qux=thr", 0)); - - OK (one == cu_mount_checkoption (line_bool, "o", 0)); - OK (two == cu_mount_checkoption (line_bool, "tw", 0)); - OK (three == cu_mount_checkoption (line_bool, "thr", 0)); - - /* "full" flag */ - OK (one == cu_mount_checkoption (line_bool, "one", 1)); - OK (two == cu_mount_checkoption (line_bool, "two", 1)); - OK (three == cu_mount_checkoption (line_bool, "three", 1)); - OK (NULL == cu_mount_checkoption (line_bool, "four", 1)); - - OK (NULL == cu_mount_checkoption (line_bool, "o", 1)); - OK (NULL == cu_mount_checkoption (line_bool, "tw", 1)); - OK (NULL == cu_mount_checkoption (line_bool, "thr", 1)); - - return (0); -} -DEF_TEST(cu_mount_getoptionvalue) -{ - char line_opts[] = "foo=one,bar=two,qux=three"; - char line_bool[] = "one,two,three"; - - STREQ ("one", cu_mount_getoptionvalue (line_opts, "foo=")); - STREQ ("two", cu_mount_getoptionvalue (line_opts, "bar=")); - STREQ ("three", cu_mount_getoptionvalue (line_opts, "qux=")); - OK (NULL == cu_mount_getoptionvalue (line_opts, "unknown=")); - - STREQ ("", cu_mount_getoptionvalue (line_bool, "one")); - STREQ ("", cu_mount_getoptionvalue (line_bool, "two")); - STREQ ("", cu_mount_getoptionvalue (line_bool, "three")); - OK (NULL == cu_mount_getoptionvalue (line_bool, "four")); - - return (0); -} - -int main (void) -{ - RUN_TEST(cu_mount_checkoption); - RUN_TEST(cu_mount_getoptionvalue); - - END_TEST; -} - -/* vim: set sw=2 sts=2 et : */ diff --git a/src/tests/test_utils_vl_lookup.c b/src/tests/test_utils_vl_lookup.c deleted file mode 100644 index 842f3fd3..00000000 --- a/src/tests/test_utils_vl_lookup.c +++ /dev/null @@ -1,249 +0,0 @@ -/** - * collectd - src/tests/test_utils_vl_lookup.c - * Copyright (C) 2012 Florian Forster - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER - * DEALINGS IN THE SOFTWARE. - * - * Authors: - * Florian Forster - **/ - -#include "tests/macros.h" -#include "collectd.h" -#include "utils_vl_lookup.h" - -static _Bool expect_new_obj = 0; -static _Bool have_new_obj = 0; - -static identifier_t last_class_ident; -static identifier_t last_obj_ident; - -static data_source_t dsrc_test = { "value", DS_TYPE_DERIVE, 0.0, NAN }; -static data_set_t const ds_test = { "test", 1, &dsrc_test }; - -static data_source_t dsrc_unknown = { "value", DS_TYPE_DERIVE, 0.0, NAN }; -static data_set_t const ds_unknown = { "unknown", 1, &dsrc_unknown }; - -static int lookup_obj_callback (data_set_t const *ds, - value_list_t const *vl, - void *user_class, void *user_obj) -{ - identifier_t *class = user_class; - identifier_t *obj = user_obj; - - OK1(expect_new_obj == have_new_obj, - (expect_new_obj ? "New obj is created." : "Updating existing obj.")); - - memcpy (&last_class_ident, class, sizeof (last_class_ident)); - memcpy (&last_obj_ident, obj, sizeof (last_obj_ident)); - - if (strcmp (obj->plugin_instance, "failure") == 0) - return (-1); - - return (0); -} - -static void *lookup_class_callback (data_set_t const *ds, - value_list_t const *vl, void *user_class) -{ - identifier_t *class = user_class; - identifier_t *obj; - - OK(expect_new_obj); - - memcpy (&last_class_ident, class, sizeof (last_class_ident)); - - obj = malloc (sizeof (*obj)); - strncpy (obj->host, vl->host, sizeof (obj->host)); - strncpy (obj->plugin, vl->plugin, sizeof (obj->plugin)); - strncpy (obj->plugin_instance, vl->plugin_instance, sizeof (obj->plugin_instance)); - strncpy (obj->type, vl->type, sizeof (obj->type)); - strncpy (obj->type_instance, vl->type_instance, sizeof (obj->type_instance)); - - have_new_obj = 1; - - return ((void *) obj); -} - -static void checked_lookup_add (lookup_t *obj, /* {{{ */ - char const *host, - char const *plugin, char const *plugin_instance, - char const *type, char const *type_instance, - unsigned int group_by) -{ - identifier_t ident; - void *user_class; - - memset (&ident, 0, sizeof (ident)); - strncpy (ident.host, host, sizeof (ident.host)); - strncpy (ident.plugin, plugin, sizeof (ident.plugin)); - strncpy (ident.plugin_instance, plugin_instance, sizeof (ident.plugin_instance)); - strncpy (ident.type, type, sizeof (ident.type)); - strncpy (ident.type_instance, type_instance, sizeof (ident.type_instance)); - - user_class = malloc (sizeof (ident)); - memmove (user_class, &ident, sizeof (ident)); - - OK(lookup_add (obj, &ident, group_by, user_class) == 0); -} /* }}} void test_add */ - -static int checked_lookup_search (lookup_t *obj, - char const *host, - char const *plugin, char const *plugin_instance, - char const *type, char const *type_instance, - _Bool expect_new) -{ - int status; - value_list_t vl = VALUE_LIST_STATIC; - data_set_t const *ds = &ds_unknown; - - strncpy (vl.host, host, sizeof (vl.host)); - strncpy (vl.plugin, plugin, sizeof (vl.plugin)); - strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); - strncpy (vl.type, type, sizeof (vl.type)); - strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); - - if (strcmp (vl.type, "test") == 0) - ds = &ds_test; - - expect_new_obj = expect_new; - have_new_obj = 0; - - status = lookup_search (obj, ds, &vl); - return (status); -} - -static lookup_t *checked_lookup_create (void) -{ - lookup_t *obj = lookup_create ( - lookup_class_callback, - lookup_obj_callback, - (void *) free, - (void *) free); - OK(obj != NULL); - return (obj); -} - -DEF_TEST(group_by_specific_host) -{ - lookup_t *obj = checked_lookup_create (); - - checked_lookup_add (obj, "/.*/", "test", "", "test", "/.*/", LU_GROUP_BY_HOST); - checked_lookup_search (obj, "host0", "test", "", "test", "0", - /* expect new = */ 1); - checked_lookup_search (obj, "host0", "test", "", "test", "1", - /* expect new = */ 0); - checked_lookup_search (obj, "host1", "test", "", "test", "0", - /* expect new = */ 1); - checked_lookup_search (obj, "host1", "test", "", "test", "1", - /* expect new = */ 0); - - lookup_destroy (obj); - return (0); -} - -DEF_TEST(group_by_any_host) -{ - lookup_t *obj = checked_lookup_create (); - - checked_lookup_add (obj, "/.*/", "/.*/", "/.*/", "test", "/.*/", LU_GROUP_BY_HOST); - checked_lookup_search (obj, "host0", "plugin0", "", "test", "0", - /* expect new = */ 1); - checked_lookup_search (obj, "host0", "plugin0", "", "test", "1", - /* expect new = */ 0); - checked_lookup_search (obj, "host0", "plugin1", "", "test", "0", - /* expect new = */ 0); - checked_lookup_search (obj, "host0", "plugin1", "", "test", "1", - /* expect new = */ 0); - checked_lookup_search (obj, "host1", "plugin0", "", "test", "0", - /* expect new = */ 1); - checked_lookup_search (obj, "host1", "plugin0", "", "test", "1", - /* expect new = */ 0); - checked_lookup_search (obj, "host1", "plugin1", "", "test", "0", - /* expect new = */ 0); - checked_lookup_search (obj, "host1", "plugin1", "", "test", "1", - /* expect new = */ 0); - - lookup_destroy (obj); - return (0); -} - -DEF_TEST(multiple_lookups) -{ - lookup_t *obj = checked_lookup_create (); - int status; - - checked_lookup_add (obj, "/.*/", "plugin0", "", "test", "/.*/", LU_GROUP_BY_HOST); - checked_lookup_add (obj, "/.*/", "/.*/", "", "test", "ti0", LU_GROUP_BY_HOST); - - status = checked_lookup_search (obj, "host0", "plugin1", "", "test", "", - /* expect new = */ 0); - assert (status == 0); - status = checked_lookup_search (obj, "host0", "plugin0", "", "test", "", - /* expect new = */ 1); - assert (status == 1); - status = checked_lookup_search (obj, "host0", "plugin1", "", "test", "ti0", - /* expect new = */ 1); - assert (status == 1); - status = checked_lookup_search (obj, "host0", "plugin0", "", "test", "ti0", - /* expect new = */ 0); - assert (status == 2); - - lookup_destroy (obj); - return (0); -} - -DEF_TEST(regex) -{ - lookup_t *obj = checked_lookup_create (); - - checked_lookup_add (obj, "/^db[0-9]\\./", "cpu", "/.*/", "cpu", "/.*/", - LU_GROUP_BY_TYPE_INSTANCE); - checked_lookup_search (obj, "db0.example.com", "cpu", "0", "cpu", "user", - /* expect new = */ 1); - checked_lookup_search (obj, "db0.example.com", "cpu", "0", "cpu", "idle", - /* expect new = */ 1); - checked_lookup_search (obj, "db0.example.com", "cpu", "1", "cpu", "user", - /* expect new = */ 0); - checked_lookup_search (obj, "db0.example.com", "cpu", "1", "cpu", "idle", - /* expect new = */ 0); - checked_lookup_search (obj, "app0.example.com", "cpu", "0", "cpu", "user", - /* expect new = */ 0); - checked_lookup_search (obj, "app0.example.com", "cpu", "0", "cpu", "idle", - /* expect new = */ 0); - checked_lookup_search (obj, "db1.example.com", "cpu", "0", "cpu", "user", - /* expect new = */ 0); - checked_lookup_search (obj, "db1.example.com", "cpu", "0", "cpu", "idle", - /* expect new = */ 0); - checked_lookup_search (obj, "db1.example.com", "cpu", "0", "cpu", "system", - /* expect new = */ 1); - - lookup_destroy (obj); - return (0); -} - -int main (int argc, char **argv) /* {{{ */ -{ - RUN_TEST(group_by_specific_host); - RUN_TEST(group_by_any_host); - RUN_TEST(multiple_lookups); - RUN_TEST(regex); - - END_TEST; -} /* }}} int main */ diff --git a/src/turbostat.c b/src/turbostat.c new file mode 100644 index 00000000..80650dff --- /dev/null +++ b/src/turbostat.c @@ -0,0 +1,1611 @@ +/* + * turbostat -- Log CPU frequency and C-state residency + * on modern Intel turbo-capable processors for collectd. + * + * Based on the 'turbostat' tool of the Linux kernel, found at + * linux/tools/power/x86/turbostat/turbostat.c: + * ---- + * Copyright (c) 2013 Intel Corporation. + * Len Brown + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * ---- + * Ported to collectd by Vincent Brillault + */ + +/* + * _GNU_SOURCE is required because of the following functions: + * - CPU_ISSET_S + * - CPU_ZERO_S + * - CPU_SET_S + * - CPU_FREE + * - CPU_ALLOC + * - CPU_ALLOC_SIZE + */ +#define _GNU_SOURCE + +#include "collectd.h" +#include "common.h" +#include "plugin.h" +#include "utils_time.h" + +#include +#include +#ifdef HAVE_SYS_CAPABILITY_H +#include +#endif /* HAVE_SYS_CAPABILITY_H */ + +#define PLUGIN_NAME "turbostat" + +/* + * This tool uses the Model-Specific Registers (MSRs) present on Intel processors. + * The general description each of these registers, depending on the architecture, + * can be found in the Intel® 64 and IA-32 Architectures Software Developer Manual, + * Volume 3 Chapter 35. + */ + +/* + * If set, aperf_mperf_unstable disables a/mperf based stats. + * This includes: C0 & C1 states, frequency + * + * This value is automatically set if mperf or aperf go backward + */ +static _Bool aperf_mperf_unstable; + +/* + * Bitmask of the list of core C states supported by the processor. + * Currently supported C-states (by this plugin): 3, 6, 7 + */ +static unsigned int do_core_cstate; +static unsigned int config_core_cstate; +static _Bool apply_config_core_cstate; + +/* + * Bitmask of the list of pacages C states supported by the processor. + * Currently supported C-states (by this plugin): 2, 3, 6, 7, 8, 9, 10 + */ +static unsigned int do_pkg_cstate; +static unsigned int config_pkg_cstate; +static _Bool apply_config_pkg_cstate; + +/* + * Boolean indicating if the processor supports 'I/O System-Management Interrupt counter' + */ +static _Bool do_smi; +static _Bool config_smi; +static _Bool apply_config_smi; + +/* + * Boolean indicating if the processor supports 'Digital temperature sensor' + * This feature enables the monitoring of the temperature of each core + * + * This feature has two limitations: + * - if MSR_IA32_TEMPERATURE_TARGET is not supported, the absolute temperature might be wrong + * - Temperatures above the tcc_activation_temp are not recorded + */ +static _Bool do_dts; +static _Bool config_dts; +static _Bool apply_config_dts; + +/* + * Boolean indicating if the processor supports 'Package thermal management' + * This feature allows the monitoring of the temperature of each package + * + * This feature has two limitations: + * - if MSR_IA32_TEMPERATURE_TARGET is not supported, the absolute temperature might be wrong + * - Temperatures above the tcc_activation_temp are not recorded + */ +static _Bool do_ptm; +static _Bool config_ptm; +static _Bool apply_config_ptm; + +/* + * Thermal Control Circuit Activation Temperature as configured by the user. + * This override the automated detection via MSR_IA32_TEMPERATURE_TARGET + * and should only be used if the automated detection fails. + */ +static unsigned int tcc_activation_temp; + +static unsigned int do_rapl; +static unsigned int config_rapl; +static _Bool apply_config_rapl; +static double rapl_energy_units; + +#define RAPL_PKG (1 << 0) + /* 0x610 MSR_PKG_POWER_LIMIT */ + /* 0x611 MSR_PKG_ENERGY_STATUS */ +#define RAPL_DRAM (1 << 1) + /* 0x618 MSR_DRAM_POWER_LIMIT */ + /* 0x619 MSR_DRAM_ENERGY_STATUS */ + /* 0x61c MSR_DRAM_POWER_INFO */ +#define RAPL_CORES (1 << 2) + /* 0x638 MSR_PP0_POWER_LIMIT */ + /* 0x639 MSR_PP0_ENERGY_STATUS */ + +#define RAPL_GFX (1 << 3) + /* 0x640 MSR_PP1_POWER_LIMIT */ + /* 0x641 MSR_PP1_ENERGY_STATUS */ + /* 0x642 MSR_PP1_POLICY */ +#define TJMAX_DEFAULT 100 + +static cpu_set_t *cpu_present_set, *cpu_affinity_set, *cpu_saved_affinity_set; +static size_t cpu_present_setsize, cpu_affinity_setsize, cpu_saved_affinity_setsize; + +static struct thread_data { + unsigned long long tsc; + unsigned long long aperf; + unsigned long long mperf; + unsigned long long c1; + unsigned int smi_count; + unsigned int cpu_id; + unsigned int flags; +#define CPU_IS_FIRST_THREAD_IN_CORE 0x2 +#define CPU_IS_FIRST_CORE_IN_PACKAGE 0x4 +} *thread_delta, *thread_even, *thread_odd; + +static struct core_data { + unsigned long long c3; + unsigned long long c6; + unsigned long long c7; + unsigned int core_temp_c; + unsigned int core_id; +} *core_delta, *core_even, *core_odd; + +static struct pkg_data { + unsigned long long pc2; + unsigned long long pc3; + unsigned long long pc6; + unsigned long long pc7; + unsigned long long pc8; + unsigned long long pc9; + unsigned long long pc10; + unsigned int package_id; + uint32_t energy_pkg; /* MSR_PKG_ENERGY_STATUS */ + uint32_t energy_dram; /* MSR_DRAM_ENERGY_STATUS */ + uint32_t energy_cores; /* MSR_PP0_ENERGY_STATUS */ + uint32_t energy_gfx; /* MSR_PP1_ENERGY_STATUS */ + unsigned int tcc_activation_temp; + unsigned int pkg_temp_c; +} *package_delta, *package_even, *package_odd; + +#define DELTA_COUNTERS thread_delta, core_delta, package_delta +#define ODD_COUNTERS thread_odd, core_odd, package_odd +#define EVEN_COUNTERS thread_even, core_even, package_even +static _Bool is_even = 1; + +static _Bool allocated = 0; +static _Bool initialized = 0; + +#define GET_THREAD(thread_base, thread_no, core_no, pkg_no) \ + (thread_base + \ + (pkg_no) * topology.num_cores * topology.num_threads + \ + (core_no) * topology.num_threads + \ + (thread_no)) +#define GET_CORE(core_base, core_no, pkg_no) \ + (core_base + \ + (pkg_no) * topology.num_cores + \ + (core_no)) +#define GET_PKG(pkg_base, pkg_no) (pkg_base + pkg_no) + +struct cpu_topology { + unsigned int package_id; + unsigned int core_id; + _Bool first_core_in_package; + _Bool first_thread_in_core; +}; + +static struct topology { + unsigned int max_cpu_id; + unsigned int num_packages; + unsigned int num_cores; + unsigned int num_threads; + struct cpu_topology *cpus; +} topology; + +static cdtime_t time_even, time_odd, time_delta; + +static const char *config_keys[] = +{ + "CoreCstates", + "PackageCstates", + "SystemManagementInterrupt", + "DigitalTemperatureSensor", + "PackageThermalManagement", + "TCCActivationTemp", + "RunningAveragePowerLimit", +}; +static const int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +/***************************** + * MSR Manipulation helpers * + *****************************/ + +/* + * Open a MSR device for reading + * Can change the scheduling affinity of the current process if multiple_read is 1 + */ +static int __attribute__((warn_unused_result)) +open_msr(unsigned int cpu, _Bool multiple_read) +{ + char pathname[32]; + int fd; + + /* + * If we need to do multiple read, let's migrate to the CPU + * Otherwise, we would lose time calling functions on another CPU + * + * If we are not yet initialized (cpu_affinity_setsize = 0), + * we need to skip this optimisation. + */ + if (multiple_read && cpu_affinity_setsize) { + CPU_ZERO_S(cpu_affinity_setsize, cpu_affinity_set); + CPU_SET_S(cpu, cpu_affinity_setsize, cpu_affinity_set); + if (sched_setaffinity(0, cpu_affinity_setsize, cpu_affinity_set) == -1) { + ERROR("turbostat plugin: Could not migrate to CPU %d", cpu); + return -1; + } + } + + ssnprintf(pathname, sizeof(pathname), "/dev/cpu/%d/msr", cpu); + fd = open(pathname, O_RDONLY); + if (fd < 0) { + ERROR("turbostat plugin: failed to open %s", pathname); + return -1; + } + return fd; +} + +/* + * Read a single MSR from an open file descriptor + */ +static int __attribute__((warn_unused_result)) +read_msr(int fd, off_t offset, unsigned long long *msr) +{ + ssize_t retval; + + retval = pread(fd, msr, sizeof *msr, offset); + + if (retval != sizeof *msr) { + ERROR("turbostat plugin: MSR offset 0x%llx read failed", + (unsigned long long)offset); + return -1; + } + return 0; +} + +/* + * Open a MSR device for reading, read the value asked for and close it. + * This call will not affect the scheduling affinity of this thread. + */ +static ssize_t __attribute__((warn_unused_result)) +get_msr(unsigned int cpu, off_t offset, unsigned long long *msr) +{ + ssize_t retval; + int fd; + + fd = open_msr(cpu, 0); + if (fd < 0) + return fd; + retval = read_msr(fd, offset, msr); + close(fd); + return retval; +} + + +/******************************** + * Raw data acquisition (1 CPU) * + ********************************/ + +/* + * Read every data avalaible for a single CPU + * + * Core data is shared for all threads in one core: extracted only for the first thread + * Package data is shared for all core in one package: extracted only for the first thread of the first core + * + * Side effect: migrates to the targeted CPU + */ +static int __attribute__((warn_unused_result)) +get_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) +{ + unsigned int cpu = t->cpu_id; + unsigned long long msr; + int msr_fd; + int retval = 0; + + msr_fd = open_msr(cpu, 1); + if (msr_fd < 0) + return msr_fd; + +#define READ_MSR(msr, dst) \ +do { \ + if (read_msr(msr_fd, msr, dst)) { \ + ERROR("turbostat plugin: Unable to read " #msr); \ + retval = -1; \ + goto out; \ + } \ +} while (0) + + READ_MSR(MSR_IA32_TSC, &t->tsc); + + READ_MSR(MSR_IA32_APERF, &t->aperf); + READ_MSR(MSR_IA32_MPERF, &t->mperf); + + if (do_smi) { + READ_MSR(MSR_SMI_COUNT, &msr); + t->smi_count = msr & 0xFFFFFFFF; + } + + /* collect core counters only for 1st thread in core */ + if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) { + retval = 0; + goto out; + } + + if (do_core_cstate & (1 << 3)) + READ_MSR(MSR_CORE_C3_RESIDENCY, &c->c3); + if (do_core_cstate & (1 << 6)) + READ_MSR(MSR_CORE_C6_RESIDENCY, &c->c6); + if (do_core_cstate & (1 << 7)) + READ_MSR(MSR_CORE_C7_RESIDENCY, &c->c7); + + if (do_dts) { + READ_MSR(MSR_IA32_THERM_STATUS, &msr); + c->core_temp_c = p->tcc_activation_temp - ((msr >> 16) & 0x7F); + } + + /* collect package counters only for 1st core in package */ + if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) { + retval = 0; + goto out; + } + + if (do_pkg_cstate & (1 << 2)) + READ_MSR(MSR_PKG_C2_RESIDENCY, &p->pc2); + if (do_pkg_cstate & (1 << 3)) + READ_MSR(MSR_PKG_C3_RESIDENCY, &p->pc3); + if (do_pkg_cstate & (1 << 6)) + READ_MSR(MSR_PKG_C6_RESIDENCY, &p->pc6); + if (do_pkg_cstate & (1 << 7)) + READ_MSR(MSR_PKG_C7_RESIDENCY, &p->pc7); + if (do_pkg_cstate & (1 << 8)) + READ_MSR(MSR_PKG_C8_RESIDENCY, &p->pc8); + if (do_pkg_cstate & (1 << 9)) + READ_MSR(MSR_PKG_C9_RESIDENCY, &p->pc9); + if (do_pkg_cstate & (1 << 10)) + READ_MSR(MSR_PKG_C10_RESIDENCY, &p->pc10); + + if (do_rapl & RAPL_PKG) { + READ_MSR(MSR_PKG_ENERGY_STATUS, &msr); + p->energy_pkg = msr & 0xFFFFFFFF; + } + if (do_rapl & RAPL_CORES) { + READ_MSR(MSR_PP0_ENERGY_STATUS, &msr); + p->energy_cores = msr & 0xFFFFFFFF; + } + if (do_rapl & RAPL_DRAM) { + READ_MSR(MSR_DRAM_ENERGY_STATUS, &msr); + p->energy_dram = msr & 0xFFFFFFFF; + } + if (do_rapl & RAPL_GFX) { + READ_MSR(MSR_PP1_ENERGY_STATUS, &msr); + p->energy_gfx = msr & 0xFFFFFFFF; + } + if (do_ptm) { + READ_MSR(MSR_IA32_PACKAGE_THERM_STATUS, &msr); + p->pkg_temp_c = p->tcc_activation_temp - ((msr >> 16) & 0x7F); + } + +out: + close(msr_fd); + return retval; +} + + +/********************************** + * Evaluating the changes (1 CPU) * + **********************************/ + +/* + * Extract the evolution old->new in delta at a package level + * (some are not new-delta, e.g. temperature) + */ +static inline void +delta_package(struct pkg_data *delta, const struct pkg_data *new, const struct pkg_data *old) +{ + delta->pc2 = new->pc2 - old->pc2; + delta->pc3 = new->pc3 - old->pc3; + delta->pc6 = new->pc6 - old->pc6; + delta->pc7 = new->pc7 - old->pc7; + delta->pc8 = new->pc8 - old->pc8; + delta->pc9 = new->pc9 - old->pc9; + delta->pc10 = new->pc10 - old->pc10; + delta->pkg_temp_c = new->pkg_temp_c; + + delta->energy_pkg = new->energy_pkg - old->energy_pkg; + delta->energy_cores = new->energy_cores - old->energy_cores; + delta->energy_gfx = new->energy_gfx - old->energy_gfx; + delta->energy_dram = new->energy_dram - old->energy_dram; +} + +/* + * Extract the evolution old->new in delta at a core level + * (some are not new-delta, e.g. temperature) + */ +static inline void +delta_core(struct core_data *delta, const struct core_data *new, const struct core_data *old) +{ + delta->c3 = new->c3 - old->c3; + delta->c6 = new->c6 - old->c6; + delta->c7 = new->c7 - old->c7; + delta->core_temp_c = new->core_temp_c; +} + +/* + * Extract the evolution old->new in delta at a package level + * core_delta is required for c1 estimation (tsc - c0 - all core cstates) + */ +static inline int __attribute__((warn_unused_result)) +delta_thread(struct thread_data *delta, const struct thread_data *new, const struct thread_data *old, + const struct core_data *core_delta) +{ + delta->tsc = new->tsc - old->tsc; + + /* check for TSC < 1 Mcycles over interval */ + if (delta->tsc < (1000 * 1000)) { + WARNING("turbostat plugin: Insanely slow TSC rate, TSC stops " + "in idle? You can disable all c-states by booting with" + " 'idle=poll' or just the deep ones with" + " 'processor.max_cstate=1'"); + return -1; + } + + delta->c1 = new->c1 - old->c1; + + if ((new->aperf > old->aperf) && (new->mperf > old->mperf)) { + delta->aperf = new->aperf - old->aperf; + delta->mperf = new->mperf - old->mperf; + } else { + if (!aperf_mperf_unstable) { + WARNING("turbostat plugin: APERF or MPERF went " + "backwards. Frequency results do not cover " + "the entire interval. Fix this by running " + "Linux-2.6.30 or later."); + + aperf_mperf_unstable = 1; + } + } + + /* + * As counter collection is not atomic, + * it is possible for mperf's non-halted cycles + idle states + * to exceed TSC's all cycles: show c1 = 0% in that case. + */ + if ((delta->mperf + core_delta->c3 + core_delta->c6 + core_delta->c7) > delta->tsc) + delta->c1 = 0; + else { + /* normal case, derive c1 */ + delta->c1 = delta->tsc - delta->mperf - core_delta->c3 + - core_delta->c6 - core_delta->c7; + } + + if (delta->mperf == 0) { + WARNING("turbostat plugin: cpu%d MPERF 0!", old->cpu_id); + delta->mperf = 1; /* divide by 0 protection */ + } + + if (do_smi) + delta->smi_count = new->smi_count - old->smi_count; + + return 0; +} + +/********************************** + * Submitting the results (1 CPU) * + **********************************/ + +/* + * Submit one gauge value + */ +static void +turbostat_submit (const char *plugin_instance, + const char *type, const char *type_instance, + gauge_t value) +{ + value_list_t vl = VALUE_LIST_INIT; + value_t v; + + v.gauge = value; + vl.values = &v; + vl.values_len = 1; + sstrncpy (vl.host, hostname_g, sizeof (vl.host)); + sstrncpy (vl.plugin, PLUGIN_NAME, sizeof (vl.plugin)); + if (plugin_instance != NULL) + sstrncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); + sstrncpy (vl.type, type, sizeof (vl.type)); + if (type_instance != NULL) + sstrncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); + + plugin_dispatch_values (&vl); +} + +/* + * Submit every data for a single CPU + * + * Core data is shared for all threads in one core: submitted only for the first thread + * Package data is shared for all core in one package: submitted only for the first thread of the first core + */ +static int +submit_counters(struct thread_data *t, struct core_data *c, struct pkg_data *p) +{ + char name[DATA_MAX_NAME_LEN]; + double interval_float; + + interval_float = CDTIME_T_TO_DOUBLE(time_delta); + + ssnprintf(name, sizeof(name), "cpu%02d", t->cpu_id); + + if (!aperf_mperf_unstable) + turbostat_submit(name, "percent", "c0", 100.0 * t->mperf/t->tsc); + if (!aperf_mperf_unstable) + turbostat_submit(name, "percent", "c1", 100.0 * t->c1/t->tsc); + + turbostat_submit(name, "frequency", "average", 1.0 / 1000000 * t->aperf / interval_float); + + if ((!aperf_mperf_unstable) || (!(t->aperf > t->tsc || t->mperf > t->tsc))) + turbostat_submit(name, "frequency", "busy", 1.0 * t->tsc / 1000000 * t->aperf / t->mperf / interval_float); + + /* Sanity check (should stay stable) */ + turbostat_submit(name, "gauge", "TSC", 1.0 * t->tsc / 1000000 / interval_float); + + /* SMI */ + if (do_smi) + turbostat_submit(name, "count", NULL, t->smi_count); + + /* submit per-core data only for 1st thread in core */ + if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE)) + goto done; + + ssnprintf(name, sizeof(name), "core%02d", c->core_id); + + if (do_core_cstate & (1 << 3)) + turbostat_submit(name, "percent", "c3", 100.0 * c->c3/t->tsc); + if (do_core_cstate & (1 << 6)) + turbostat_submit(name, "percent", "c6", 100.0 * c->c6/t->tsc); + if (do_core_cstate & (1 << 7)) + turbostat_submit(name, "percent", "c7", 100.0 * c->c7/t->tsc); + + if (do_dts) + turbostat_submit(name, "temperature", NULL, c->core_temp_c); + + /* submit per-package data only for 1st core in package */ + if (!(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) + goto done; + + ssnprintf(name, sizeof(name), "pkg%02d", p->package_id); + + if (do_ptm) + turbostat_submit(name, "temperature", NULL, p->pkg_temp_c); + + if (do_pkg_cstate & (1 << 2)) + turbostat_submit(name, "percent", "pc2", 100.0 * p->pc2/t->tsc); + if (do_pkg_cstate & (1 << 3)) + turbostat_submit(name, "percent", "pc3", 100.0 * p->pc3/t->tsc); + if (do_pkg_cstate & (1 << 6)) + turbostat_submit(name, "percent", "pc6", 100.0 * p->pc6/t->tsc); + if (do_pkg_cstate & (1 << 7)) + turbostat_submit(name, "percent", "pc7", 100.0 * p->pc7/t->tsc); + if (do_pkg_cstate & (1 << 8)) + turbostat_submit(name, "percent", "pc8", 100.0 * p->pc8/t->tsc); + if (do_pkg_cstate & (1 << 9)) + turbostat_submit(name, "percent", "pc9", 100.0 * p->pc9/t->tsc); + if (do_pkg_cstate & (1 << 10)) + turbostat_submit(name, "percent", "pc10", 100.0 * p->pc10/t->tsc); + + if (do_rapl) { + if (do_rapl & RAPL_PKG) + turbostat_submit(name, "power", "pkg", p->energy_pkg * rapl_energy_units / interval_float); + if (do_rapl & RAPL_CORES) + turbostat_submit(name, "power", "cores", p->energy_cores * rapl_energy_units / interval_float); + if (do_rapl & RAPL_GFX) + turbostat_submit(name, "power", "GFX", p->energy_gfx * rapl_energy_units / interval_float); + if (do_rapl & RAPL_DRAM) + turbostat_submit(name, "power", "DRAM", p->energy_dram * rapl_energy_units / interval_float); + } +done: + return 0; +} + + +/********************************** + * Looping function over all CPUs * + **********************************/ + +/* + * Check if a given cpu id is in our compiled list of existing CPUs + */ +static int +cpu_is_not_present(unsigned int cpu) +{ + return !CPU_ISSET_S(cpu, cpu_present_setsize, cpu_present_set); +} + +/* + * Loop on all CPUs in topological order + * + * Skip non-present cpus + * Return the error code at the first error or 0 + */ +static int __attribute__((warn_unused_result)) +for_all_cpus(int (func)(struct thread_data *, struct core_data *, struct pkg_data *), + struct thread_data *thread_base, struct core_data *core_base, struct pkg_data *pkg_base) +{ + int retval; + unsigned int pkg_no, core_no, thread_no; + + for (pkg_no = 0; pkg_no < topology.num_packages; ++pkg_no) { + for (core_no = 0; core_no < topology.num_cores; ++core_no) { + for (thread_no = 0; thread_no < topology.num_threads; ++thread_no) { + struct thread_data *t; + struct core_data *c; + struct pkg_data *p; + + t = GET_THREAD(thread_base, thread_no, core_no, pkg_no); + + if (cpu_is_not_present(t->cpu_id)) + continue; + + c = GET_CORE(core_base, core_no, pkg_no); + p = GET_PKG(pkg_base, pkg_no); + + retval = func(t, c, p); + if (retval) + return retval; + } + } + } + return 0; +} + +/* + * Dedicated loop: Extract every data evolution for all CPU + * + * Skip non-present cpus + * Return the error code at the first error or 0 + * + * Core data is shared for all threads in one core: extracted only for the first thread + * Package data is shared for all core in one package: extracted only for the first thread of the first core + */ +static int __attribute__((warn_unused_result)) +for_all_cpus_delta(const struct thread_data *thread_new_base, const struct core_data *core_new_base, const struct pkg_data *pkg_new_base, + const struct thread_data *thread_old_base, const struct core_data *core_old_base, const struct pkg_data *pkg_old_base) +{ + int retval; + unsigned int pkg_no, core_no, thread_no; + + for (pkg_no = 0; pkg_no < topology.num_packages; ++pkg_no) { + for (core_no = 0; core_no < topology.num_cores; ++core_no) { + for (thread_no = 0; thread_no < topology.num_threads; ++thread_no) { + struct thread_data *t_delta; + const struct thread_data *t_old, *t_new; + struct core_data *c_delta; + + /* Get correct pointers for threads */ + t_delta = GET_THREAD(thread_delta, thread_no, core_no, pkg_no); + t_new = GET_THREAD(thread_new_base, thread_no, core_no, pkg_no); + t_old = GET_THREAD(thread_old_base, thread_no, core_no, pkg_no); + + /* Skip threads that disappeared */ + if (cpu_is_not_present(t_delta->cpu_id)) + continue; + + /* c_delta is always required for delta_thread */ + c_delta = GET_CORE(core_delta, core_no, pkg_no); + + /* calculate core delta only for 1st thread in core */ + if (t_new->flags & CPU_IS_FIRST_THREAD_IN_CORE) { + const struct core_data *c_old, *c_new; + + c_new = GET_CORE(core_new_base, core_no, pkg_no); + c_old = GET_CORE(core_old_base, core_no, pkg_no); + + delta_core(c_delta, c_new, c_old); + } + + /* Always calculate thread delta */ + retval = delta_thread(t_delta, t_new, t_old, c_delta); + if (retval) + return retval; + + /* calculate package delta only for 1st core in package */ + if (t_new->flags & CPU_IS_FIRST_CORE_IN_PACKAGE) { + struct pkg_data *p_delta; + const struct pkg_data *p_old, *p_new; + + p_delta = GET_PKG(package_delta, pkg_no); + p_new = GET_PKG(pkg_new_base, pkg_no); + p_old = GET_PKG(pkg_old_base, pkg_no); + + delta_package(p_delta, p_new, p_old); + } + } + } + } + return 0; +} + + +/*************** + * CPU Probing * + ***************/ + +/* + * MSR_IA32_TEMPERATURE_TARGET indicates the temperature where + * the Thermal Control Circuit (TCC) activates. + * This is usually equal to tjMax. + * + * Older processors do not have this MSR, so there we guess, + * but also allow conficuration over-ride with "TCCActivationTemp". + * + * Several MSR temperature values are in units of degrees-C + * below this value, including the Digital Thermal Sensor (DTS), + * Package Thermal Management Sensor (PTM), and thermal event thresholds. + */ +static int __attribute__((warn_unused_result)) +set_temperature_target(struct thread_data *t, struct core_data *c, struct pkg_data *p) +{ + unsigned long long msr; + unsigned int target_c_local; + + /* tcc_activation_temp is used only for dts or ptm */ + if (!(do_dts || do_ptm)) + return 0; + + /* this is a per-package concept */ + if (!(t->flags & CPU_IS_FIRST_THREAD_IN_CORE) || !(t->flags & CPU_IS_FIRST_CORE_IN_PACKAGE)) + return 0; + + if (tcc_activation_temp != 0) { + p->tcc_activation_temp = tcc_activation_temp; + return 0; + } + + if (get_msr(t->cpu_id, MSR_IA32_TEMPERATURE_TARGET, &msr)) + goto guess; + + target_c_local = (msr >> 16) & 0xFF; + + if (!target_c_local) + goto guess; + + p->tcc_activation_temp = target_c_local; + + return 0; + +guess: + p->tcc_activation_temp = TJMAX_DEFAULT; + WARNING("turbostat plugin: cpu%d: Guessing tjMax %d C," + " Please use TCCActivationTemp to specify it.", + t->cpu_id, p->tcc_activation_temp); + + return 0; +} + +/* + * Identify the functionality of the CPU + */ +static int __attribute__((warn_unused_result)) +probe_cpu() +{ + unsigned int eax, ebx, ecx, edx, max_level; + unsigned int fms, family, model; + + /* CPUID(0): + * - EAX: Maximum Input Value for Basic CPUID Information + * - EBX: "Genu" (0x756e6547) + * - EDX: "ineI" (0x49656e69) + * - ECX: "ntel" (0x6c65746e) + */ + max_level = ebx = ecx = edx = 0; + __get_cpuid(0, &max_level, &ebx, &ecx, &edx); + if (ebx != 0x756e6547 && edx != 0x49656e69 && ecx != 0x6c65746e) { + ERROR("turbostat plugin: Unsupported CPU (not Intel)"); + return -1; + } + + /* CPUID(1): + * - EAX: Version Information: Type, Family, Model, and Stepping ID + * + 4-7: Model ID + * + 8-11: Family ID + * + 12-13: Processor type + * + 16-19: Extended Model ID + * + 20-27: Extended Family ID + * - EDX: Feature Information: + * + 5: Support for MSR read/write operations + */ + fms = ebx = ecx = edx = 0; + __get_cpuid(1, &fms, &ebx, &ecx, &edx); + family = (fms >> 8) & 0xf; + model = (fms >> 4) & 0xf; + if (family == 0xf) + family += (fms >> 20) & 0xf; + if (family == 6 || family == 0xf) + model += ((fms >> 16) & 0xf) << 4; + if (!(edx & (1 << 5))) { + ERROR("turbostat plugin: Unsupported CPU (no MSR support)"); + return -1; + } + + /* + * CPUID(6): + * - EAX: + * + 0: Digital temperature sensor is supported if set + * + 6: Package thermal management is supported if set + * - ECX: + * + 0: Hardware Coordination Feedback Capability (Presence of IA32_MPERF and IA32_APERF). + * + 3: The processor supports performance-energy bias preference if set. + * It also implies the presence of a new architectural MSR called IA32_ENERGY_PERF_BIAS + * + * This check is valid for both Intel and AMD + */ + eax = ebx = ecx = edx = 0; + __get_cpuid(0x6, &eax, &ebx, &ecx, &edx); + do_dts = eax & (1 << 0); + do_ptm = eax & (1 << 6); + if (!(ecx & (1 << 0))) { + ERROR("turbostat plugin: Unsupported CPU (No APERF)"); + return -1; + } + + /* + * Enable or disable C states depending on the model and family + */ + if (family == 6) { + switch (model) { + /* Atom (partial) */ + case 0x27: + do_smi = 0; + do_core_cstate = 0; + do_pkg_cstate = (1 << 2) | (1 << 4) | (1 << 6); + break; + /* Silvermont */ + case 0x37: /* BYT */ + case 0x4D: /* AVN */ + do_smi = 1; + do_core_cstate = (1 << 1) | (1 << 6); + do_pkg_cstate = (1 << 6); + break; + /* Nehalem */ + case 0x1A: /* Core i7, Xeon 5500 series - Bloomfield, Gainstown NHM-EP */ + case 0x1E: /* Core i7 and i5 Processor - Clarksfield, Lynnfield, Jasper Forest */ + case 0x1F: /* Core i7 and i5 Processor - Nehalem */ + case 0x2E: /* Nehalem-EX Xeon - Beckton */ + do_smi = 1; + do_core_cstate = (1 << 3) | (1 << 6); + do_pkg_cstate = (1 << 3) | (1 << 6) | (1 << 7); + break; + /* Westmere */ + case 0x25: /* Westmere Client - Clarkdale, Arrandale */ + case 0x2C: /* Westmere EP - Gulftown */ + case 0x2F: /* Westmere-EX Xeon - Eagleton */ + do_smi = 1; + do_core_cstate = (1 << 3) | (1 << 6); + do_pkg_cstate = (1 << 3) | (1 << 6) | (1 << 7); + break; + /* Sandy Bridge */ + case 0x2A: /* SNB */ + case 0x2D: /* SNB Xeon */ + do_smi = 1; + do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7); + do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7); + break; + /* Ivy Bridge */ + case 0x3A: /* IVB */ + case 0x3E: /* IVB Xeon */ + do_smi = 1; + do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7); + do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7); + break; + /* Haswell Bridge */ + case 0x3C: /* HSW */ + case 0x3F: /* HSW */ + case 0x46: /* HSW */ + do_smi = 1; + do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7); + do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7); + break; + case 0x45: /* HSW */ + do_smi = 1; + do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7); + do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10); + break; + /* Broadwel */ + case 0x4F: /* BDW */ + case 0x56: /* BDX-DE */ + do_smi = 1; + do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7); + do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7); + break; + case 0x3D: /* BDW */ + do_smi = 1; + do_core_cstate = (1 << 3) | (1 << 6) | (1 << 7); + do_pkg_cstate = (1 << 2) | (1 << 3) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) | (1 << 10); + break; + default: + do_smi = 0; + do_core_cstate = 0; + do_pkg_cstate = 0; + break; + } + switch (model) { + case 0x2A: /* SNB */ + case 0x3A: /* IVB */ + case 0x3C: /* HSW */ + case 0x45: /* HSW */ + case 0x46: /* HSW */ + case 0x3D: /* BDW */ + do_rapl = RAPL_PKG | RAPL_CORES | RAPL_GFX; + break; + case 0x3F: /* HSX */ + case 0x4F: /* BDX */ + case 0x56: /* BDX-DE */ + do_rapl = RAPL_PKG | RAPL_DRAM ; + break; + case 0x2D: /* SNB Xeon */ + case 0x3E: /* IVB Xeon */ + do_rapl = RAPL_PKG | RAPL_CORES | RAPL_DRAM; + break; + case 0x37: /* BYT */ + case 0x4D: /* AVN */ + do_rapl = RAPL_PKG | RAPL_CORES; + break; + default: + do_rapl = 0; + } + } else { + ERROR("turbostat plugin: Unsupported CPU (family: %#x, " + "model: %#x)", family, model); + return -1; + } + + /* Override detected values with configuration */ + if (apply_config_core_cstate) + do_core_cstate = config_core_cstate; + if (apply_config_pkg_cstate) + do_pkg_cstate = config_pkg_cstate; + if (apply_config_smi) + do_smi = config_smi; + if (apply_config_dts) + do_dts = config_dts; + if (apply_config_ptm) + do_ptm = config_ptm; + if (apply_config_rapl) + do_rapl = config_rapl; + + if (do_rapl) { + unsigned long long msr; + if (get_msr(0, MSR_RAPL_POWER_UNIT, &msr)) + return 0; + + if (model == 0x37) + rapl_energy_units = 1.0 * (1 << (msr >> 8 & 0x1F)) / 1000000; + else + rapl_energy_units = 1.0 / (1 << (msr >> 8 & 0x1F)); + } + + return 0; +} + + +/******************** + * Topology Probing * + ********************/ + +/* + * Read a single int from a file. + */ +static int __attribute__ ((format(printf,1,2))) +parse_int_file(const char *fmt, ...) +{ + va_list args; + char path[PATH_MAX]; + FILE *filep; + int len, value; + + va_start(args, fmt); + len = vsnprintf(path, sizeof(path), fmt, args); + va_end(args); + if (len < 0 || len >= PATH_MAX) { + ERROR("turbostat plugin: path truncated: '%s'", path); + return -1; + } + + filep = fopen(path, "r"); + if (!filep) { + ERROR("turbostat plugin: Failed to open '%s'", path); + return -1; + } + if (fscanf(filep, "%d", &value) != 1) { + ERROR("turbostat plugin: Failed to parse number from '%s'", path); + return -1; + } + fclose(filep); + return value; +} + +static int +get_threads_on_core(unsigned int cpu) +{ + char path[80]; + FILE *filep; + int sib1, sib2; + int matches; + char character; + + ssnprintf(path, sizeof(path), "/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", cpu); + filep = fopen(path, "r"); + if (!filep) { + ERROR("turbostat plugin: Failed to open '%s'", path); + return -1; + } + /* + * file format: + * if a pair of number with a character between: 2 siblings (eg. 1-2, or 1,4) + * otherwinse 1 sibling (self). + */ + matches = fscanf(filep, "%d%c%d\n", &sib1, &character, &sib2); + + fclose(filep); + + if (matches == 3) + return 2; + else + return 1; +} + +/* + * run func(cpu) on every cpu in /proc/stat + * return max_cpu number + */ +static int __attribute__((warn_unused_result)) +for_all_proc_cpus(int (func)(unsigned int)) +{ + FILE *fp; + unsigned int cpu_num; + int retval; + + fp = fopen("/proc/stat", "r"); + if (!fp) { + ERROR("turbostat plugin: Failed to open /proc/stat"); + return -1; + } + + retval = fscanf(fp, "cpu %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n"); + if (retval != 0) { + ERROR("turbostat plugin: Failed to parse /proc/stat"); + fclose(fp); + return -1; + } + + while (1) { + retval = fscanf(fp, "cpu%u %*d %*d %*d %*d %*d %*d %*d %*d %*d %*d\n", &cpu_num); + if (retval != 1) + break; + + retval = func(cpu_num); + if (retval) { + fclose(fp); + return(retval); + } + } + fclose(fp); + return 0; +} + +/* + * Update the stored topology.max_cpu_id + */ +static int +update_max_cpu_id(unsigned int cpu) +{ + if (topology.max_cpu_id < cpu) + topology.max_cpu_id = cpu; + return 0; +} + +static int +mark_cpu_present(unsigned int cpu) +{ + CPU_SET_S(cpu, cpu_present_setsize, cpu_present_set); + return 0; +} + +static int __attribute__((warn_unused_result)) +allocate_cpu_set(cpu_set_t ** set, size_t * size) { + *set = CPU_ALLOC(topology.max_cpu_id + 1); + if (*set == NULL) { + ERROR("turbostat plugin: Unable to allocate CPU state"); + return -1; + } + *size = CPU_ALLOC_SIZE(topology.max_cpu_id + 1); + CPU_ZERO_S(*size, *set); + return 0; +} + +/* + * Build a local representation of the cpu distribution + */ +static int __attribute__((warn_unused_result)) +topology_probe() +{ + unsigned int i; + int ret; + unsigned int max_package_id, max_core_id, max_threads; + max_package_id = max_core_id = max_threads = 0; + + /* Clean topology */ + free(topology.cpus); + memset(&topology, 0, sizeof(topology)); + + ret = for_all_proc_cpus(update_max_cpu_id); + if (ret != 0) + goto err; + + topology.cpus = calloc(1, (topology.max_cpu_id + 1) * sizeof(struct cpu_topology)); + if (topology.cpus == NULL) { + ERROR("turbostat plugin: Unable to allocate memory for CPU topology"); + return -1; + } + + ret = allocate_cpu_set(&cpu_present_set, &cpu_present_setsize); + if (ret != 0) + goto err; + ret = allocate_cpu_set(&cpu_affinity_set, &cpu_affinity_setsize); + if (ret != 0) + goto err; + ret = allocate_cpu_set(&cpu_saved_affinity_set, &cpu_saved_affinity_setsize); + if (ret != 0) + goto err; + + ret = for_all_proc_cpus(mark_cpu_present); + if (ret != 0) + goto err; + + /* + * For online cpus + * find max_core_id, max_package_id + */ + for (i = 0; i <= topology.max_cpu_id; ++i) { + unsigned int num_threads; + struct cpu_topology *cpu = &topology.cpus[i]; + + if (cpu_is_not_present(i)) { + WARNING("turbostat plugin: cpu%d NOT PRESENT", i); + continue; + } + + ret = parse_int_file("/sys/devices/system/cpu/cpu%d/topology/physical_package_id", i); + if (ret < 0) + goto err; + else + cpu->package_id = (unsigned int) ret; + if (cpu->package_id > max_package_id) + max_package_id = cpu->package_id; + + ret = parse_int_file("/sys/devices/system/cpu/cpu%d/topology/core_id", i); + if (ret < 0) + goto err; + else + cpu->core_id = (unsigned int) ret; + if (cpu->core_id > max_core_id) + max_core_id = cpu->core_id; + ret = parse_int_file("/sys/devices/system/cpu/cpu%d/topology/core_siblings_list", i); + if (ret < 0) + goto err; + else if ((unsigned int) ret == i) + cpu->first_core_in_package = 1; + + ret = get_threads_on_core(i); + if (ret < 0) + goto err; + else + num_threads = (unsigned int) ret; + if (num_threads > max_threads) + max_threads = num_threads; + ret = parse_int_file("/sys/devices/system/cpu/cpu%d/topology/thread_siblings_list", i); + if (ret < 0) + goto err; + else if ((unsigned int) ret == i) + cpu->first_thread_in_core = 1; + + DEBUG("turbostat plugin: cpu %d pkg %d core %d\n", + i, cpu->package_id, cpu->core_id); + } + /* Num is max + 1 (need to count 0) */ + topology.num_packages = max_package_id + 1; + topology.num_cores = max_core_id + 1; + topology.num_threads = max_threads; + + return 0; +err: + free(topology.cpus); + return ret; +} + + +/************************ + * Main alloc/init/free * + ************************/ + +static int +allocate_counters(struct thread_data **threads, struct core_data **cores, struct pkg_data **packages) +{ + unsigned int i; + unsigned int total_threads, total_cores; + + total_threads = topology.num_threads * topology.num_cores * topology.num_packages; + *threads = calloc(total_threads, sizeof(struct thread_data)); + if (*threads == NULL) + goto err; + + for (i = 0; i < total_threads; ++i) + (*threads)[i].cpu_id = topology.max_cpu_id + 1; + + total_cores = topology.num_cores * topology.num_packages; + *cores = calloc(total_cores, sizeof(struct core_data)); + if (*cores == NULL) + goto err_clean_threads; + + *packages = calloc(topology.num_packages, sizeof(struct pkg_data)); + if (*packages == NULL) + goto err_clean_cores; + + return 0; + +err_clean_cores: + free(*cores); +err_clean_threads: + free(*threads); +err: + ERROR("turbostat plugin: Failled to allocate memory for counters"); + return -1; +} + +static void +init_counter(struct thread_data *thread_base, struct core_data *core_base, + struct pkg_data *pkg_base, unsigned int cpu_id) +{ + struct thread_data *t; + struct core_data *c; + struct pkg_data *p; + struct cpu_topology *cpu = &topology.cpus[cpu_id]; + + t = GET_THREAD(thread_base, !(cpu->first_thread_in_core), cpu->core_id, cpu->package_id); + c = GET_CORE(core_base, cpu->core_id, cpu->package_id); + p = GET_PKG(pkg_base, cpu->package_id); + + t->cpu_id = cpu_id; + if (cpu->first_thread_in_core) + t->flags |= CPU_IS_FIRST_THREAD_IN_CORE; + if (cpu->first_core_in_package) + t->flags |= CPU_IS_FIRST_CORE_IN_PACKAGE; + + c->core_id = cpu->core_id; + p->package_id = cpu->package_id; +} + +static void +initialize_counters(void) +{ + unsigned int cpu_id; + + for (cpu_id = 0; cpu_id <= topology.max_cpu_id; ++cpu_id) { + if (cpu_is_not_present(cpu_id)) + continue; + init_counter(EVEN_COUNTERS, cpu_id); + init_counter(ODD_COUNTERS, cpu_id); + init_counter(DELTA_COUNTERS, cpu_id); + } +} + + + +static void +free_all_buffers(void) +{ + allocated = 0; + initialized = 0; + + CPU_FREE(cpu_present_set); + cpu_present_set = NULL; + cpu_present_set = 0; + + CPU_FREE(cpu_affinity_set); + cpu_affinity_set = NULL; + cpu_affinity_setsize = 0; + + CPU_FREE(cpu_saved_affinity_set); + cpu_saved_affinity_set = NULL; + cpu_saved_affinity_setsize = 0; + + free(thread_even); + free(core_even); + free(package_even); + + thread_even = NULL; + core_even = NULL; + package_even = NULL; + + free(thread_odd); + free(core_odd); + free(package_odd); + + thread_odd = NULL; + core_odd = NULL; + package_odd = NULL; + + free(thread_delta); + free(core_delta); + free(package_delta); + + thread_delta = NULL; + core_delta = NULL; + package_delta = NULL; +} + + +/********************** + * Collectd functions * + **********************/ + +#define DO_OR_GOTO_ERR(something) \ +do { \ + ret = (something); \ + if (ret < 0) \ + goto err; \ +} while (0) + +static int setup_all_buffers(void) +{ + int ret; + + DO_OR_GOTO_ERR(topology_probe()); + DO_OR_GOTO_ERR(allocate_counters(&thread_even, &core_even, &package_even)); + DO_OR_GOTO_ERR(allocate_counters(&thread_odd, &core_odd, &package_odd)); + DO_OR_GOTO_ERR(allocate_counters(&thread_delta, &core_delta, &package_delta)); + initialize_counters(); + DO_OR_GOTO_ERR(for_all_cpus(set_temperature_target, EVEN_COUNTERS)); + DO_OR_GOTO_ERR(for_all_cpus(set_temperature_target, ODD_COUNTERS)); + + allocated = 1; + return 0; +err: + free_all_buffers(); + return ret; +} + +static int +turbostat_read(void) +{ + int ret; + + if (!allocated) { + if ((ret = setup_all_buffers()) < 0) + return ret; + } + + if (for_all_proc_cpus(cpu_is_not_present)) { + free_all_buffers(); + if ((ret = setup_all_buffers()) < 0) + return ret; + if (for_all_proc_cpus(cpu_is_not_present)) { + ERROR("turbostat plugin: CPU appeared just after " + "initialization"); + return -1; + } + } + + /* Saving the scheduling affinity, as it will be modified by get_counters */ + if (sched_getaffinity(0, cpu_saved_affinity_setsize, cpu_saved_affinity_set) != 0) { + ERROR("turbostat plugin: Unable to save the CPU affinity"); + return -1; + } + + if (!initialized) { + if ((ret = for_all_cpus(get_counters, EVEN_COUNTERS)) < 0) + goto out; + time_even = cdtime(); + is_even = 1; + initialized = 1; + ret = 0; + goto out; + } + + if (is_even) { + if ((ret = for_all_cpus(get_counters, ODD_COUNTERS)) < 0) + goto out; + time_odd = cdtime(); + is_even = 0; + time_delta = time_odd - time_even; + if ((ret = for_all_cpus_delta(ODD_COUNTERS, EVEN_COUNTERS)) < 0) + goto out; + if ((ret = for_all_cpus(submit_counters, DELTA_COUNTERS)) < 0) + goto out; + } else { + if ((ret = for_all_cpus(get_counters, EVEN_COUNTERS)) < 0) + goto out; + time_even = cdtime(); + is_even = 1; + time_delta = time_even - time_odd; + if ((ret = for_all_cpus_delta(EVEN_COUNTERS, ODD_COUNTERS)) < 0) + goto out; + if ((ret = for_all_cpus(submit_counters, DELTA_COUNTERS)) < 0) + goto out; + } + ret = 0; +out: + /* + * Let's restore the affinity + * This might fail if the number of CPU changed, but we can't do anything in that case.. + */ + (void)sched_setaffinity(0, cpu_saved_affinity_setsize, cpu_saved_affinity_set); + return ret; +} + +static int +check_permissions(void) +{ +#ifdef HAVE_SYS_CAPABILITY_H + struct __user_cap_header_struct cap_header_data; + cap_user_header_t cap_header = &cap_header_data; + struct __user_cap_data_struct cap_data_data; + cap_user_data_t cap_data = &cap_data_data; + int ret = 0; +#endif /* HAVE_SYS_CAPABILITY_H */ + + if (getuid() == 0) { + /* We have everything we need */ + return 0; +#ifndef HAVE_SYS_CAPABILITY_H + } else { + ERROR("turbostat plugin: Initialization failed: this plugin " + "requires collectd to run as root"); + return -1; + } +#else /* HAVE_SYS_CAPABILITY_H */ + } + + /* check for CAP_SYS_RAWIO */ + cap_header->pid = getpid(); + cap_header->version = _LINUX_CAPABILITY_VERSION; + if (capget(cap_header, cap_data) < 0) { + ERROR("turbostat plugin: capget failed"); + return -1; + } + + if ((cap_data->effective & (1 << CAP_SYS_RAWIO)) == 0) { + WARNING("turbostat plugin: Collectd doesn't have the " + "CAP_SYS_RAWIO capability. If you don't want to run " + "collectd as root, try running \"setcap " + "cap_sys_rawio=ep\" on collectd binary"); + ret = -1; + } + + if (euidaccess("/dev/cpu/0/msr", R_OK)) { + WARNING("turbostat plugin: Collectd cannot open" + "/dev/cpu/0/msr. If you don't want to run collectd as " + "root, you need to change the ownership (chown) and " + "permissions on /dev/cpu/*/msr to allow such access"); + ret = -1; + } + + if (ret != 0) + ERROR("turbostat plugin: Initialization failed: this plugin " + "requires collectd to either to run as root or give " + "collectd a special capability (CAP_SYS_RAWIO) and read " + "access to /dev/cpu/*/msr (see previous warnings)"); + return ret; +#endif /* HAVE_SYS_CAPABILITY_H */ +} + +static int +turbostat_init(void) +{ + struct stat sb; + int ret; + + if (stat("/dev/cpu/0/msr", &sb)) { + ERROR("turbostat plugin: Initialization failed: /dev/cpu/0/msr" + " does not exist while the CPU supports MSR. You may be " + "missing the corresponding kernel module, please try '# " + "modprobe msr'"); + return -1; + } + + DO_OR_GOTO_ERR(check_permissions()); + + DO_OR_GOTO_ERR(probe_cpu()); + + DO_OR_GOTO_ERR(setup_all_buffers()); + + plugin_register_read(PLUGIN_NAME, turbostat_read); + + return 0; +err: + free_all_buffers(); + return ret; +} + +static int +turbostat_config(const char *key, const char *value) +{ + long unsigned int tmp_val; + char *end; + + if (strcasecmp("CoreCstates", key) == 0) { + tmp_val = strtoul(value, &end, 0); + if (*end != '\0' || tmp_val > UINT_MAX) { + ERROR("turbostat plugin: Invalid CoreCstates '%s'", + value); + return -1; + } + config_core_cstate = (unsigned int) tmp_val; + apply_config_core_cstate = 1; + } else if (strcasecmp("PackageCstates", key) == 0) { + tmp_val = strtoul(value, &end, 0); + if (*end != '\0' || tmp_val > UINT_MAX) { + ERROR("turbostat plugin: Invalid PackageCstates '%s'", + value); + return -1; + } + config_pkg_cstate = (unsigned int) tmp_val; + apply_config_pkg_cstate = 1; + } else if (strcasecmp("SystemManagementInterrupt", key) == 0) { + config_smi = IS_TRUE(value); + apply_config_smi = 1; + } else if (strcasecmp("DigitalTemperatureSensor", key) == 0) { + config_dts = IS_TRUE(value); + apply_config_dts = 1; + } else if (strcasecmp("PackageThermalManagement", key) == 0) { + config_ptm = IS_TRUE(value); + apply_config_ptm = 1; + } else if (strcasecmp("RunningAveragePowerLimit", key) == 0) { + tmp_val = strtoul(value, &end, 0); + if (*end != '\0' || tmp_val > UINT_MAX) { + ERROR("turbostat plugin: Invalid RunningAveragePowerLimit '%s'", + value); + return -1; + } + config_rapl = (unsigned int) tmp_val; + apply_config_rapl = 1; + } else if (strcasecmp("TCCActivationTemp", key) == 0) { + tmp_val = strtoul(value, &end, 0); + if (*end != '\0' || tmp_val > UINT_MAX) { + ERROR("turbostat plugin: Invalid TCCActivationTemp '%s'", + value); + return -1; + } + tcc_activation_temp = (unsigned int) tmp_val; + } else { + ERROR("turbostat plugin: Invalid configuration option '%s'", + key); + return -1; + } + return 0; +} + +void module_register(void) +{ + plugin_register_init(PLUGIN_NAME, turbostat_init); + plugin_register_config(PLUGIN_NAME, turbostat_config, config_keys, config_keys_num); +} diff --git a/src/types.db b/src/types.db index 5474659b..38fb546c 100644 --- a/src/types.db +++ b/src/types.db @@ -68,6 +68,7 @@ email_size value:GAUGE:0:U entropy value:GAUGE:0:4294967295 expired_keys value:GAUGE:0:U fanspeed value:GAUGE:0:U +file_handles value:GAUGE:0:U file_size value:GAUGE:0:U files value:GAUGE:0:U flow value:GAUGE:0:U diff --git a/src/utils_format_graphite.c b/src/utils_format_graphite.c index 220258f4..023f7a46 100644 --- a/src/utils_format_graphite.c +++ b/src/utils_format_graphite.c @@ -60,7 +60,7 @@ static int gr_format_values (char *ret, size_t ret_len, } while (0) if (ds->ds[ds_num].type == DS_TYPE_GAUGE) - BUFFER_ADD ("%f", vl->values[ds_num].gauge); + BUFFER_ADD (GAUGE_FORMAT, vl->values[ds_num].gauge); else if (rates != NULL) BUFFER_ADD ("%f", rates[ds_num]); else if (ds->ds[ds_num].type == DS_TYPE_COUNTER) diff --git a/src/utils_format_json.c b/src/utils_format_json.c index 699c74e2..10a5343f 100644 --- a/src/utils_format_json.c +++ b/src/utils_format_json.c @@ -113,7 +113,7 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */ if (ds->ds[i].type == DS_TYPE_GAUGE) { if(isfinite (vl->values[i].gauge)) - BUFFER_ADD ("%g", vl->values[i].gauge); + BUFFER_ADD (JSON_GAUGE_FORMAT, vl->values[i].gauge); else BUFFER_ADD ("null"); } @@ -129,7 +129,7 @@ static int values_to_json (char *buffer, size_t buffer_size, /* {{{ */ } if(isfinite (rates[i])) - BUFFER_ADD ("%g", rates[i]); + BUFFER_ADD (JSON_GAUGE_FORMAT, rates[i]); else BUFFER_ADD ("null"); } diff --git a/src/utils_format_json.h b/src/utils_format_json.h index a56913d5..f1fbb6e7 100644 --- a/src/utils_format_json.h +++ b/src/utils_format_json.h @@ -30,6 +30,10 @@ #include "collectd.h" #include "plugin.h" +#ifndef JSON_GAUGE_FORMAT +# define JSON_GAUGE_FORMAT GAUGE_FORMAT +#endif + int format_json_initialize (char *buffer, size_t *ret_buffer_fill, size_t *ret_buffer_free); int format_json_value_list (char *buffer, diff --git a/src/utils_mount.c b/src/utils_mount.c index 3cede018..b63a81ad 100644 --- a/src/utils_mount.c +++ b/src/utils_mount.c @@ -21,20 +21,18 @@ * Niki W. Waibel **/ -#if HAVE_CONFIG_H -# include "config.h" -#endif +#include "collectd.h" +#include "utils_mount.h" + +#include "common.h" /* sstrncpy() et alii */ +#include "plugin.h" /* ERROR() macro */ -#include "common.h" #if HAVE_XFS_XQM_H # include #define XFS_SUPER_MAGIC_STR "XFSB" #define XFS_SUPER_MAGIC2_STR "BSFX" #endif -#include "plugin.h" -#include "utils_mount.h" - #if HAVE_GETVFSSTAT # if HAVE_SYS_TYPES_H # include diff --git a/src/utils_mount_test.c b/src/utils_mount_test.c new file mode 100644 index 00000000..c5ffbfbc --- /dev/null +++ b/src/utils_mount_test.c @@ -0,0 +1,101 @@ +/** + * collectd - src/tests/test_utils_mount.c + * Copyright (C) 2013 Florian octo Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian octo Forster + */ + +#include "testing.h" +#include "collectd.h" +#include "utils_mount.h" + +DEF_TEST(cu_mount_checkoption) +{ + char line_opts[] = "foo=one,bar=two,qux=three"; + char *foo = strstr (line_opts, "foo"); + char *bar = strstr (line_opts, "bar"); + char *qux = strstr (line_opts, "qux"); + + char line_bool[] = "one,two,three"; + char *one = strstr (line_bool, "one"); + char *two = strstr (line_bool, "two"); + char *three = strstr (line_bool, "three"); + + /* Normal operation */ + OK (foo == cu_mount_checkoption (line_opts, "foo", 0)); + OK (bar == cu_mount_checkoption (line_opts, "bar", 0)); + OK (qux == cu_mount_checkoption (line_opts, "qux", 0)); + OK (NULL == cu_mount_checkoption (line_opts, "unknown", 0)); + + OK (one == cu_mount_checkoption (line_bool, "one", 0)); + OK (two == cu_mount_checkoption (line_bool, "two", 0)); + OK (three == cu_mount_checkoption (line_bool, "three", 0)); + OK (NULL == cu_mount_checkoption (line_bool, "four", 0)); + + /* Shorter and longer parts */ + OK (foo == cu_mount_checkoption (line_opts, "fo", 0)); + OK (bar == cu_mount_checkoption (line_opts, "bar=", 0)); + OK (qux == cu_mount_checkoption (line_opts, "qux=thr", 0)); + + OK (one == cu_mount_checkoption (line_bool, "o", 0)); + OK (two == cu_mount_checkoption (line_bool, "tw", 0)); + OK (three == cu_mount_checkoption (line_bool, "thr", 0)); + + /* "full" flag */ + OK (one == cu_mount_checkoption (line_bool, "one", 1)); + OK (two == cu_mount_checkoption (line_bool, "two", 1)); + OK (three == cu_mount_checkoption (line_bool, "three", 1)); + OK (NULL == cu_mount_checkoption (line_bool, "four", 1)); + + OK (NULL == cu_mount_checkoption (line_bool, "o", 1)); + OK (NULL == cu_mount_checkoption (line_bool, "tw", 1)); + OK (NULL == cu_mount_checkoption (line_bool, "thr", 1)); + + return (0); +} +DEF_TEST(cu_mount_getoptionvalue) +{ + char line_opts[] = "foo=one,bar=two,qux=three"; + char line_bool[] = "one,two,three"; + + STREQ ("one", cu_mount_getoptionvalue (line_opts, "foo=")); + STREQ ("two", cu_mount_getoptionvalue (line_opts, "bar=")); + STREQ ("three", cu_mount_getoptionvalue (line_opts, "qux=")); + OK (NULL == cu_mount_getoptionvalue (line_opts, "unknown=")); + + STREQ ("", cu_mount_getoptionvalue (line_bool, "one")); + STREQ ("", cu_mount_getoptionvalue (line_bool, "two")); + STREQ ("", cu_mount_getoptionvalue (line_bool, "three")); + OK (NULL == cu_mount_getoptionvalue (line_bool, "four")); + + return (0); +} + +int main (void) +{ + RUN_TEST(cu_mount_checkoption); + RUN_TEST(cu_mount_getoptionvalue); + + END_TEST; +} + +/* vim: set sw=2 sts=2 et : */ diff --git a/src/utils_vl_lookup_test.c b/src/utils_vl_lookup_test.c new file mode 100644 index 00000000..6a2676a8 --- /dev/null +++ b/src/utils_vl_lookup_test.c @@ -0,0 +1,249 @@ +/** + * collectd - src/tests/test_utils_vl_lookup.c + * Copyright (C) 2012 Florian Forster + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Florian Forster + **/ + +#include "testing.h" +#include "collectd.h" +#include "utils_vl_lookup.h" + +static _Bool expect_new_obj = 0; +static _Bool have_new_obj = 0; + +static identifier_t last_class_ident; +static identifier_t last_obj_ident; + +static data_source_t dsrc_test = { "value", DS_TYPE_DERIVE, 0.0, NAN }; +static data_set_t const ds_test = { "test", 1, &dsrc_test }; + +static data_source_t dsrc_unknown = { "value", DS_TYPE_DERIVE, 0.0, NAN }; +static data_set_t const ds_unknown = { "unknown", 1, &dsrc_unknown }; + +static int lookup_obj_callback (data_set_t const *ds, + value_list_t const *vl, + void *user_class, void *user_obj) +{ + identifier_t *class = user_class; + identifier_t *obj = user_obj; + + OK1(expect_new_obj == have_new_obj, + (expect_new_obj ? "New obj is created." : "Updating existing obj.")); + + memcpy (&last_class_ident, class, sizeof (last_class_ident)); + memcpy (&last_obj_ident, obj, sizeof (last_obj_ident)); + + if (strcmp (obj->plugin_instance, "failure") == 0) + return (-1); + + return (0); +} + +static void *lookup_class_callback (data_set_t const *ds, + value_list_t const *vl, void *user_class) +{ + identifier_t *class = user_class; + identifier_t *obj; + + OK(expect_new_obj); + + memcpy (&last_class_ident, class, sizeof (last_class_ident)); + + obj = malloc (sizeof (*obj)); + strncpy (obj->host, vl->host, sizeof (obj->host)); + strncpy (obj->plugin, vl->plugin, sizeof (obj->plugin)); + strncpy (obj->plugin_instance, vl->plugin_instance, sizeof (obj->plugin_instance)); + strncpy (obj->type, vl->type, sizeof (obj->type)); + strncpy (obj->type_instance, vl->type_instance, sizeof (obj->type_instance)); + + have_new_obj = 1; + + return ((void *) obj); +} + +static void checked_lookup_add (lookup_t *obj, /* {{{ */ + char const *host, + char const *plugin, char const *plugin_instance, + char const *type, char const *type_instance, + unsigned int group_by) +{ + identifier_t ident; + void *user_class; + + memset (&ident, 0, sizeof (ident)); + strncpy (ident.host, host, sizeof (ident.host)); + strncpy (ident.plugin, plugin, sizeof (ident.plugin)); + strncpy (ident.plugin_instance, plugin_instance, sizeof (ident.plugin_instance)); + strncpy (ident.type, type, sizeof (ident.type)); + strncpy (ident.type_instance, type_instance, sizeof (ident.type_instance)); + + user_class = malloc (sizeof (ident)); + memmove (user_class, &ident, sizeof (ident)); + + OK(lookup_add (obj, &ident, group_by, user_class) == 0); +} /* }}} void test_add */ + +static int checked_lookup_search (lookup_t *obj, + char const *host, + char const *plugin, char const *plugin_instance, + char const *type, char const *type_instance, + _Bool expect_new) +{ + int status; + value_list_t vl = VALUE_LIST_STATIC; + data_set_t const *ds = &ds_unknown; + + strncpy (vl.host, host, sizeof (vl.host)); + strncpy (vl.plugin, plugin, sizeof (vl.plugin)); + strncpy (vl.plugin_instance, plugin_instance, sizeof (vl.plugin_instance)); + strncpy (vl.type, type, sizeof (vl.type)); + strncpy (vl.type_instance, type_instance, sizeof (vl.type_instance)); + + if (strcmp (vl.type, "test") == 0) + ds = &ds_test; + + expect_new_obj = expect_new; + have_new_obj = 0; + + status = lookup_search (obj, ds, &vl); + return (status); +} + +static lookup_t *checked_lookup_create (void) +{ + lookup_t *obj = lookup_create ( + lookup_class_callback, + lookup_obj_callback, + (void *) free, + (void *) free); + OK(obj != NULL); + return (obj); +} + +DEF_TEST(group_by_specific_host) +{ + lookup_t *obj = checked_lookup_create (); + + checked_lookup_add (obj, "/.*/", "test", "", "test", "/.*/", LU_GROUP_BY_HOST); + checked_lookup_search (obj, "host0", "test", "", "test", "0", + /* expect new = */ 1); + checked_lookup_search (obj, "host0", "test", "", "test", "1", + /* expect new = */ 0); + checked_lookup_search (obj, "host1", "test", "", "test", "0", + /* expect new = */ 1); + checked_lookup_search (obj, "host1", "test", "", "test", "1", + /* expect new = */ 0); + + lookup_destroy (obj); + return (0); +} + +DEF_TEST(group_by_any_host) +{ + lookup_t *obj = checked_lookup_create (); + + checked_lookup_add (obj, "/.*/", "/.*/", "/.*/", "test", "/.*/", LU_GROUP_BY_HOST); + checked_lookup_search (obj, "host0", "plugin0", "", "test", "0", + /* expect new = */ 1); + checked_lookup_search (obj, "host0", "plugin0", "", "test", "1", + /* expect new = */ 0); + checked_lookup_search (obj, "host0", "plugin1", "", "test", "0", + /* expect new = */ 0); + checked_lookup_search (obj, "host0", "plugin1", "", "test", "1", + /* expect new = */ 0); + checked_lookup_search (obj, "host1", "plugin0", "", "test", "0", + /* expect new = */ 1); + checked_lookup_search (obj, "host1", "plugin0", "", "test", "1", + /* expect new = */ 0); + checked_lookup_search (obj, "host1", "plugin1", "", "test", "0", + /* expect new = */ 0); + checked_lookup_search (obj, "host1", "plugin1", "", "test", "1", + /* expect new = */ 0); + + lookup_destroy (obj); + return (0); +} + +DEF_TEST(multiple_lookups) +{ + lookup_t *obj = checked_lookup_create (); + int status; + + checked_lookup_add (obj, "/.*/", "plugin0", "", "test", "/.*/", LU_GROUP_BY_HOST); + checked_lookup_add (obj, "/.*/", "/.*/", "", "test", "ti0", LU_GROUP_BY_HOST); + + status = checked_lookup_search (obj, "host0", "plugin1", "", "test", "", + /* expect new = */ 0); + assert (status == 0); + status = checked_lookup_search (obj, "host0", "plugin0", "", "test", "", + /* expect new = */ 1); + assert (status == 1); + status = checked_lookup_search (obj, "host0", "plugin1", "", "test", "ti0", + /* expect new = */ 1); + assert (status == 1); + status = checked_lookup_search (obj, "host0", "plugin0", "", "test", "ti0", + /* expect new = */ 0); + assert (status == 2); + + lookup_destroy (obj); + return (0); +} + +DEF_TEST(regex) +{ + lookup_t *obj = checked_lookup_create (); + + checked_lookup_add (obj, "/^db[0-9]\\./", "cpu", "/.*/", "cpu", "/.*/", + LU_GROUP_BY_TYPE_INSTANCE); + checked_lookup_search (obj, "db0.example.com", "cpu", "0", "cpu", "user", + /* expect new = */ 1); + checked_lookup_search (obj, "db0.example.com", "cpu", "0", "cpu", "idle", + /* expect new = */ 1); + checked_lookup_search (obj, "db0.example.com", "cpu", "1", "cpu", "user", + /* expect new = */ 0); + checked_lookup_search (obj, "db0.example.com", "cpu", "1", "cpu", "idle", + /* expect new = */ 0); + checked_lookup_search (obj, "app0.example.com", "cpu", "0", "cpu", "user", + /* expect new = */ 0); + checked_lookup_search (obj, "app0.example.com", "cpu", "0", "cpu", "idle", + /* expect new = */ 0); + checked_lookup_search (obj, "db1.example.com", "cpu", "0", "cpu", "user", + /* expect new = */ 0); + checked_lookup_search (obj, "db1.example.com", "cpu", "0", "cpu", "idle", + /* expect new = */ 0); + checked_lookup_search (obj, "db1.example.com", "cpu", "0", "cpu", "system", + /* expect new = */ 1); + + lookup_destroy (obj); + return (0); +} + +int main (int argc, char **argv) /* {{{ */ +{ + RUN_TEST(group_by_specific_host); + RUN_TEST(group_by_any_host); + RUN_TEST(multiple_lookups); + RUN_TEST(regex); + + END_TEST; +} /* }}} int main */ diff --git a/src/write_redis.c b/src/write_redis.c index b4c5e212..22e30abb 100644 --- a/src/write_redis.c +++ b/src/write_redis.c @@ -95,7 +95,7 @@ static int wr_write (const data_set_t *ds, /* {{{ */ if (ds->ds[i].type == DS_TYPE_COUNTER) APPEND ("%llu", vl->values[i].counter); else if (ds->ds[i].type == DS_TYPE_GAUGE) - APPEND ("%g", vl->values[i].gauge); + APPEND (GAUGE_FORMAT, vl->values[i].gauge); else if (ds->ds[i].type == DS_TYPE_DERIVE) APPEND ("%"PRIi64, vl->values[i].derive); else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) @@ -113,22 +113,30 @@ static int wr_write (const data_set_t *ds, /* {{{ */ node->conn = redisConnectWithTimeout ((char *)node->host, node->port, node->timeout); if (node->conn == NULL) { - ERROR ("write_redis plugin: Connecting to host \"%s\" (port %i) failed.", + ERROR ("write_redis plugin: Connecting to host \"%s\" (port %i) failed: Unkown reason", (node->host != NULL) ? node->host : "localhost", (node->port != 0) ? node->port : 6379); pthread_mutex_unlock (&node->lock); return (-1); } + else if (node->conn->err) + { + ERROR ("write_redis plugin: Connecting to host \"%s\" (port %i) failed: %s", + (node->host != NULL) ? node->host : "localhost", + (node->port != 0) ? node->port : 6379, + node->conn->errstr); + pthread_mutex_unlock (&node->lock); + return (-1); + } } - assert (node->conn != NULL); rr = redisCommand (node->conn, "ZADD %s %s %s", key, time, value); if (rr==NULL) - WARNING("ZADD command error. key:%s", key); + WARNING("ZADD command error. key:%s message:%s", key, node->conn->errstr); rr = redisCommand (node->conn, "SADD collectd/values %s", ident); if (rr==NULL) - WARNING("SADD command error. ident:%s", ident); + WARNING("SADD command error. ident:%s message:%s", ident, node->conn->errstr); pthread_mutex_unlock (&node->lock); diff --git a/src/write_sensu.c b/src/write_sensu.c index cb0c2fe2..7a3e4f40 100644 --- a/src/write_sensu.c +++ b/src/write_sensu.c @@ -468,16 +468,14 @@ static char *sensu_value_to_json(struct sensu_host const *host, /* {{{ */ // calculate the value and set to a string if (ds->ds[index].type == DS_TYPE_GAUGE) { - double tmp_v = (double) vl->values[index].gauge; - res = asprintf(&value_str, "%.8f", tmp_v); + res = asprintf(&value_str, GAUGE_FORMAT, vl->values[index].gauge); if (res == -1) { free(ret_str); ERROR("write_sensu plugin: Unable to alloc memory"); return NULL; } } else if (rates != NULL) { - double tmp_v = (double) rates[index]; - res = asprintf(&value_str, "%.8f", tmp_v); + res = asprintf(&value_str, GAUGE_FORMAT, rates[index]); if (res == -1) { free(ret_str); ERROR("write_sensu plugin: Unable to alloc memory"); diff --git a/src/write_tsdb.c b/src/write_tsdb.c index 9008a67b..27ea4738 100644 --- a/src/write_tsdb.c +++ b/src/write_tsdb.c @@ -308,7 +308,7 @@ static int wt_format_values(char *ret, size_t ret_len, } while (0) if (ds->ds[ds_num].type == DS_TYPE_GAUGE) - BUFFER_ADD("%f", vl->values[ds_num].gauge); + BUFFER_ADD(GAUGE_FORMAT, vl->values[ds_num].gauge); else if (store_rates) { if (rates == NULL) @@ -319,7 +319,7 @@ static int wt_format_values(char *ret, size_t ret_len, "uc_get_rate failed."); return -1; } - BUFFER_ADD("%f", rates[ds_num]); + BUFFER_ADD(GAUGE_FORMAT, rates[ds_num]); } else if (ds->ds[ds_num].type == DS_TYPE_COUNTER) BUFFER_ADD("%llu", vl->values[ds_num].counter); diff --git a/src/zfs_arc.c b/src/zfs_arc.c index b784ee3a..f0d23239 100644 --- a/src/zfs_arc.c +++ b/src/zfs_arc.c @@ -251,7 +251,14 @@ static int za_read (void) /* Sizes */ za_read_gauge (ksp, "size", "cache_size", "arc"); - za_read_gauge (ksp, "l2_size", "cache_size", "L2"); + + /* The "l2_size" value has disappeared from Solaris some time in + * early 2013, and has only reappeared recently in Solaris 11.2. + * Stop trying if we ever fail to read it, so we don't spam the log. + */ + static int l2_size_avail = 1; + if (l2_size_avail && za_read_gauge (ksp, "l2_size", "cache_size", "L2") != 0) + l2_size_avail = 0; /* Operations */ za_read_derive (ksp, "deleted", "cache_operation", "deleted"); diff --git a/version-gen.sh b/version-gen.sh index 460c6e9d..b09be8e7 100755 --- a/version-gen.sh +++ b/version-gen.sh @@ -1,6 +1,6 @@ #!/bin/sh -DEFAULT_VERSION="5.4.2.git" +DEFAULT_VERSION="5.5.0.git" VERSION="`git describe 2> /dev/null | grep collectd | sed -e 's/^collectd-//'`"