pax_global_header00006660000000000000000000000064133271347040014516gustar00rootroot0000000000000052 comment=e47d5e71d8ae0640695995550595e7ec107bfbb0 pg_stat_kcache-REL2_1_1/000077500000000000000000000000001332713470400151635ustar00rootroot00000000000000pg_stat_kcache-REL2_1_1/.gitignore000066400000000000000000000000631332713470400171520ustar00rootroot00000000000000*.o *.a *.so *.pc *~ pg_stat_kcache-*.zip results/ pg_stat_kcache-REL2_1_1/CHANGELOG.md000066400000000000000000000035571332713470400170060ustar00rootroot00000000000000## 2.1.1 (2018-07-28) **Bugfix**: - Fix usage increase, used to keep the most frequent entries in memory (Julien Rouhaud) **Miscellaneous**: - Allow PG_CONFIG value to be found on command-line (edechaux) - Warn users about incorrect GUC (Julien Rouhaud) - Add debian packaging (Julien Rouhaud) ## 2.1.0 (2018-07-17) **NOTE**: This version requires a change to the on-disk format. After installing the new version restarting PostgreSQL, any previously accumulated statistics will be reset. - Add support for architecture that don't provide getrusage(2), such as windows. Only user time and system time will be available on such platforms (Julien Rouhaud). - Expose more fields of getrusage(2). Depending on the platform, some of these fields are not maintained (Julien Rouhaud). - Add a workaround for sampling problems with getrusage(), new parameter pg_stat_kcache.linux_hz is added. By default, this parameter is discovered at server startup (Ronan Dunklau). - Add compatibility with PostgreSQL 11 (Thomas Reiss) - Fix issue when concurrently created entries for the same user, db and queryid could lost some execution counters (Mael Rimbault) - Do not install docs anymore (Ronan Dunklau) ## 2.0.3 (2016-10-03) - Add PG 9.6 compatibility - Fix issues in shared memory estimation, which could prevent starting postgres or reduce the amount of possible locks (thanks to Jean-Sébastien BACQ for the report) - Add hint of possible reasons pgss.max could not be retrieved, which could prevent starting postgres ## 2.0.2 (2015-03-17) - Fix another bug with 32 bits builds (thanks to Alain Delorme for reporting it) ## 2.0.1 (2015-03-16) - Fix a bug with 32 bits builds (thanks to Alain Delorme for reporting it) ## 2.0 (2015-01-30) - Handle stats per database, user, query ## 1.0 (2014-02-26) - Initial release pg_stat_kcache-REL2_1_1/CONTRIBUTORS.md000066400000000000000000000002051332713470400174370ustar00rootroot00000000000000 * Thomas Reiss * Julien Rouhaud * Alain Delorme * Guillaume Lelarge * Jean-Sébastien Bacq * Maël Rimbault * edechaux pg_stat_kcache-REL2_1_1/LICENSE000066400000000000000000000017411332713470400161730ustar00rootroot00000000000000Copyright (c) 2014-2017, Dalibo Copyright (c) 2018, The PoWA-team Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. pg_stat_kcache-REL2_1_1/META.json000066400000000000000000000021331332713470400166030ustar00rootroot00000000000000{ "name": "pg_stat_kcache", "abstract": "An extension gathering CPU and disk acess statistics", "version": "__VERSION__", "maintainer": "Julien Rouhaud ", "license": "postgresql", "release_status": "stable", "provides": { "pg_stat_kcache": { "abstract": "An extension gathering CPU and disk acess statistics", "file": "pg_stat_kcache.sql", "docfile": "README.rst", "version": "__VERSION__" } }, "resources": { "bugtracker": { "web": "http://github.com/powa-team/pg_stat_kcache/issues/" }, "repository": { "url": "git://github.com/powa-team/pg_stat_kcache.git", "web": "http://github.com/powa-team/pg_stat_kcache/", "type": "git" } }, "prereqs": { "runtime": { "requires": { "PostgreSQL": "9.4.0" } } }, "generated_by": "Julien Rouhaud", "meta-spec": { "version": "1.0.0", "url": "http://pgxn.org/meta/spec.txt" }, "tags": [ "monitoring", "CPU", "disk" ] } pg_stat_kcache-REL2_1_1/Makefile000066400000000000000000000016231332713470400166250ustar00rootroot00000000000000EXTENSION = pg_stat_kcache EXTVERSION = $(shell grep default_version $(EXTENSION).control | sed -e "s/default_version[[:space:]]*=[[:space:]]*'\([^']*\)'/\1/") TESTS = $(wildcard test/sql/*.sql) REGRESS = $(patsubst test/sql/%.sql,%,$(TESTS)) REGRESS_OPTS = --inputdir=test PG_CONFIG ?= pg_config MODULE_big = pg_stat_kcache OBJS = pg_stat_kcache.o all: release-zip: all git archive --format zip --prefix=pg_stat_kcache-${EXTVERSION}/ --output ./pg_stat_kcache-${EXTVERSION}.zip HEAD unzip ./pg_stat_kcache-$(EXTVERSION).zip rm ./pg_stat_kcache-$(EXTVERSION).zip rm ./pg_stat_kcache-$(EXTVERSION)/.gitignore sed -i -e "s/__VERSION__/$(EXTVERSION)/g" ./pg_stat_kcache-$(EXTVERSION)/META.json zip -r ./pg_stat_kcache-$(EXTVERSION).zip ./pg_stat_kcache-$(EXTVERSION)/ rm ./pg_stat_kcache-$(EXTVERSION) -rf DATA = $(wildcard *--*.sql) PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS) pg_stat_kcache-REL2_1_1/README.rst000066400000000000000000000300421332713470400166510ustar00rootroot00000000000000pg_stat_kcache ============== Features -------- Gathers statistics about real reads and writes done by the filesystem layer. It is provided in the form of an extension for PostgreSQL >= 9.4., and requires pg_stat_statements extension to be installed. PostgreSQL 9.4 or more is required as previous version of provided pg_stat_statements didn't expose the queryid field. Installation ============ Compiling --------- The module can be built using the standard PGXS infrastructure. For this to work, the ``pg_config`` program must be available in your $PATH. Instruction to install follows:: git clone https://github.com/powa-team/pg_stat_kcache.git cd pg_stat_kcache make make install PostgreSQL setup ---------------- The extension is now available. But, as it requires some shared memory to hold its counters, the module must be loaded at PostgreSQL startup. Thus, you must add the module to ``shared_preload_libraries`` in your ``postgresql.conf``. You need a server restart to take the change into account. As this extension depends on pg_stat_statements, it also need to be added to ``shared_preload_libraries``. Add the following parameters into you ``postgresql.conf``:: # postgresql.conf shared_preload_libraries = 'pg_stat_statements,pg_stat_kcache' Once your PostgreSQL cluster is restarted, you can install the extension in every database where you need to access the statistics:: mydb=# CREATE EXTENSION pg_stat_kcache; Usage ===== pg_stat_kcache create several objects. pg_stat_kcache view ------------------- +-------------+-------------------+-----------------------------------------------------+ | Name | Type | Description | +=============+===================+=====================================================+ | datname | name | Name of the database | +-------------+-------------------+-----------------------------------------------------+ | user_time | double precision | User CPU time used | +-------------+-------------------+-----------------------------------------------------+ | system_time | double precision | System CPU time used | +-------------+-------------------+-----------------------------------------------------+ | minflts | bigint | Number of page reclaims (soft page faults) | +-------------+-------------------+-----------------------------------------------------+ | majflts | bigint | Number of page faults (hard page faults) | +-------------+-------------------+-----------------------------------------------------+ | nswaps | bigint | Number of swaps | +-------------+-------------------+-----------------------------------------------------+ | reads | bigint | Number of bytes read by the filesystem layer | +-------------+-------------------+-----------------------------------------------------+ | reads_blks | bigint | Number of 8K blocks read by the filesystem layer | +-------------+-------------------+-----------------------------------------------------+ | writes | bigint | Number of butes written by the filesystem layer | +-------------+-------------------+-----------------------------------------------------+ | writes_blks | bigint | Number of 8K blocks written by the filesystem layer | +-------------+-------------------+-----------------------------------------------------+ | msgsnds | bigint | Number of IPC messages sent | +-------------+-------------------+-----------------------------------------------------+ | msgrcvs | bigint | Number of IPC messages received | +-------------+-------------------+-----------------------------------------------------+ | nsignals | bigint | Number of signals received | +-------------+-------------------+-----------------------------------------------------+ | nvcsws | bigint | Number of voluntary context switches | +-------------+-------------------+-----------------------------------------------------+ | nivcsws | bigint | Number of involuntary context switches | +-------------+-------------------+-----------------------------------------------------+ pg_stat_kcache_detail view -------------------------- +-------------+-------------------+-----------------------------------------------------+ | Name | Type | Description | +=============+===================+=====================================================+ | query | text | Query text | +-------------+-------------------+-----------------------------------------------------+ | datname | name | Database name | +-------------+-------------------+-----------------------------------------------------+ | rolname | name | Role name | +-------------+-------------------+-----------------------------------------------------+ | user_time | double precision | User CPU time used | +-------------+-------------------+-----------------------------------------------------+ | system_time | double precision | System CPU time used | +-------------+-------------------+-----------------------------------------------------+ | minflts | bigint | Number of page reclaims (soft page faults) | +-------------+-------------------+-----------------------------------------------------+ | majflts | bigint | Number of page faults (hard page faults) | +-------------+-------------------+-----------------------------------------------------+ | nswaps | bigint | Number of swaps | +-------------+-------------------+-----------------------------------------------------+ | reads | bigint | Number of bytes read by the filesystem layer | +-------------+-------------------+-----------------------------------------------------+ | reads_blks | bigint | Number of 8K blocks read by the filesystem layer | +-------------+-------------------+-----------------------------------------------------+ | writes | bigint | Number of bytes written by the filesystem layer | +-------------+-------------------+-----------------------------------------------------+ | writes_blks | bigint | Number of 8K blocks written by the filesystem layer | +-------------+-------------------+-----------------------------------------------------+ | msgsnds | bigint | Number of IPC messages sent | +-------------+-------------------+-----------------------------------------------------+ | msgrcvs | bigint | Number of IPC messages received | +-------------+-------------------+-----------------------------------------------------+ | nsignals | bigint | Number of signals received | +-------------+-------------------+-----------------------------------------------------+ | nvcsws | bigint | Number of voluntary context switches | +-------------+-------------------+-----------------------------------------------------+ | nivcsws | bigint | Number of involuntary context switches | +-------------+-------------------+-----------------------------------------------------+ pg_stat_kcache_reset function ----------------------------- Resets the statistics gathered by pg_stat_kcache. Can be called by superusers:: pg_stat_kcache_reset() pg_stat_kcache function ----------------------- This function is a set-returning functions that dumps the containt of the counters of the shared memory structure. This function is used by the pg_stat_kcache view. The function can be called by any user:: SELECT * FROM pg_stat_kcache(); It provides the following columns: +-------------+-------------------+--------------------------------------------------+ | Name | Type | Description | +=============+===================+==================================================+ | queryid | bigint | pg_stat_statements' query identifier | +-------------+-------------------+--------------------------------------------------+ | userid | oid | Database OID | +-------------+-------------------+--------------------------------------------------+ | dbid | oid | Database OID | +-------------+-------------------+--------------------------------------------------+ | user_time | double precision | User CPU time used | +-------------+-------------------+--------------------------------------------------+ | system_time | double precision | System CPU time use | +-------------+-------------------+--------------------------------------------------+ | minflts | bigint | Number of page reclaims (soft page faults) | +-------------+-------------------+--------------------------------------------------+ | majflts | bigint | Number of page faults (hard page faults) | +-------------+-------------------+--------------------------------------------------+ | nswaps | bigint | Number of swaps | +-------------+-------------------+--------------------------------------------------+ | reads | bigint | Number of bytes read by the filesystem layer | +-------------+-------------------+--------------------------------------------------+ | writes | bigint | Number of bytes written by the filesystem layer | +-------------+-------------------+--------------------------------------------------+ | msgsnds | bigint | Number of IPC messages sent | +-------------+-------------------+--------------------------------------------------+ | msgrcvs | bigint | Number of IPC messages received | +-------------+-------------------+--------------------------------------------------+ | nsignals | bigint | Number of signals received | +-------------+-------------------+--------------------------------------------------+ | nvcsws | bigint | Number of voluntary context switches | +-------------+-------------------+--------------------------------------------------+ | nivcsws | bigint | Number of involuntary context switches | +-------------+-------------------+--------------------------------------------------+ Bugs and limitations ==================== No known bugs. We assume that a kernel block is 512 bytes. This is true for Linux, but may not be the case for another Unix implementation. See: http://lkml.indiana.edu/hypermail/linux/kernel/0703.2/0937.html On platforms without a native getrusage(2), all fields except `user_time` and `system_time` will be NULL. On platforms with a native getrusage(2), some of the fields may not be maintained. This is a platform dependent behavior, please refer to your platform getrusage(2) manual page for more details. Authors ======= pg_stat_kcache is an original development from Thomas Reiss, with large portions of code inspired from pg_stat_plans. Julien Rouhaud also contributed some parts of the extension. Thanks goes to Peter Geoghegan for providing much inspiration with pg_stat_plans so we could write this extension quite straightforward. License ======= pg_stat_kcache is free software distributed under the PostgreSQL license. Copyright (c) 2014-2017, Dalibo Copyright (c) 2018, The PoWA-team pg_stat_kcache-REL2_1_1/debian/000077500000000000000000000000001332713470400164055ustar00rootroot00000000000000pg_stat_kcache-REL2_1_1/debian/changelog000066400000000000000000000002171332713470400202570ustar00rootroot00000000000000pg-stat-kcache (2.1.1-1) unstable; urgency=low * Initial release. -- Julien Rouhaud Sun, 22 Jul 2018 19:19:36 +0100 pg_stat_kcache-REL2_1_1/debian/compat000066400000000000000000000000021332713470400176030ustar00rootroot000000000000009 pg_stat_kcache-REL2_1_1/debian/control000066400000000000000000000045011332713470400200100ustar00rootroot00000000000000Source: pg-stat-kcache Section: database Priority: optional Maintainer: Julien Rouhaud Standards-Version: 4.1.3 Build-Depends: debhelper (>=9~), postgresql-server-dev-all (>= 141~) Homepage: https://powa.readthedocs.io/ Vcs-Browser: https://github.com/powa-team/pg_stat_kcache Vcs-Git: https://github.com/powa-team/pg_stat_kcache.git Package: postgresql-9.4-pg-stat-kcache Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, postgresql-9.4, postgresql-contrib-9.4 Description: PostgreSQL extension to gather per-query kernel statistics. Statistics gathered are reads and writes done out of the operating system cache, user and system CPU usage, context switches and all the other meaningful metrics returned by getrusage(2). All those counters are aggregated per postgres role, database and normalized query identifier. Package: postgresql-9.5-pg-stat-kcache Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, postgresql-9.5, postgresql-contrib-9.5 Description: PostgreSQL extension to gather per-query kernel statistics. Statistics gathered are reads and writes done out of the operating system cache, user and system CPU usage, context switches and all the other meaningful metrics returned by getrusage(2). All those counters are aggregated per postgres role, database and normalized query identifier. Package: postgresql-9.6-pg-stat-kcache Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, postgresql-9.6, postgresql-contrib-9.6 Description: PostgreSQL extension to gather per-query kernel statistics. Statistics gathered are reads and writes done out of the operating system cache, user and system CPU usage, context switches and all the other meaningful metrics returned by getrusage(2). All those counters are aggregated per postgres role, database and normalized query identifier. Package: postgresql-10-pg-stat-kcache Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, postgresql-10, postgresql-contrib-10 Description: PostgreSQL extension to gather per-query kernel statistics. Statistics gathered are reads and writes done out of the operating system cache, user and system CPU usage, context switches and all the other meaningful metrics returned by getrusage(2). All those counters are aggregated per postgres role, database and normalized query identifier. pg_stat_kcache-REL2_1_1/debian/control.in000066400000000000000000000015551332713470400204230ustar00rootroot00000000000000Source: pg-stat-kcache Section: database Priority: optional Maintainer: Julien Rouhaud Standards-Version: 4.1.3 Build-Depends: debhelper (>=9~), postgresql-server-dev-all (>= 141~) Homepage: https://powa.readthedocs.io/ Vcs-Browser: https://github.com/powa-team/pg_stat_kcache Vcs-Git: https://github.com/powa-team/pg_stat_kcache.git Package: postgresql-PGVERSION-pg-stat-kcache Architecture: any Depends: ${misc:Depends}, ${shlibs:Depends}, postgresql-PGVERSION, postgresql-contrib-PGVERSION Description: PostgreSQL extension to gather per-query kernel statistics. Statistics gathered are reads and writes done out of the operating system cache, user and system CPU usage, context switches and all the other meaningful metrics returned by getrusage(2). All those counters are aggregated per postgres role, database and normalized query identifier. pg_stat_kcache-REL2_1_1/debian/copyright000066400000000000000000000022541332713470400203430ustar00rootroot00000000000000Portions Copyright (c) 2014-2017, Dalibo Portions Copyright (c) 2018, The PoWA-team Permission to use, copy, modify, and distribute this software and its documentation for any purpose, without fee, and without a written agreement is hereby granted, provided that the above copyright notice and this paragraph and the following two paragraphs appear in all copies. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. Contributors to pg_stat_kcache: * Thomas Reiss * Julien Rouhaud * Guillaume Lelarge * Ronan Dunklau * Maël Rimbault * Alain Delorme * Jean-Sébastien Bacq * edechaux pg_stat_kcache-REL2_1_1/debian/pgversions000066400000000000000000000000051332713470400205220ustar00rootroot000000000000009.4+ pg_stat_kcache-REL2_1_1/debian/rules000077500000000000000000000014111332713470400174620ustar00rootroot00000000000000#!/usr/bin/make -f PKGVER = $(shell dpkg-parsechangelog | awk -F '[:-]' '/^Version:/ { print substr($$2, 2) }') EXCLUDE = --exclude-vcs --exclude=debian include /usr/share/postgresql-common/pgxs_debian_control.mk override_dh_auto_build: # do nothing override_dh_auto_test: # nothing to do here, upstream tests used, see debian/tests/* override_dh_auto_install: # build all supported versions +pg_buildext loop postgresql-%v-pg-stat-kcache override_dh_installdocs: dh_installdocs --all CONTRIBUTORS.md README.rst rm -rvf debian/*/usr/share/doc/postgresql-doc-* override_dh_installchangelogs: dh_installchangelogs CHANGELOG.md upstream orig: debian/control clean cd .. && tar czf pg-stat-kcache_$(PKGVER).orig.tar.gz $(EXCLUDE) pg-stat-kcache-$(PKGVER) %: dh $@ pg_stat_kcache-REL2_1_1/debian/source/000077500000000000000000000000001332713470400177055ustar00rootroot00000000000000pg_stat_kcache-REL2_1_1/debian/source/format000066400000000000000000000000041332713470400211120ustar00rootroot000000000000001.0 pg_stat_kcache-REL2_1_1/debian/tests/000077500000000000000000000000001332713470400175475ustar00rootroot00000000000000pg_stat_kcache-REL2_1_1/debian/tests/control000066400000000000000000000002641332713470400211540ustar00rootroot00000000000000Depends: @, postgresql-contrib-9.4, postgresql-contrib-9.5, postgresql-contrib-9.6, postgresql-contrib-10, postgresql-server-dev-all Tests: installcheck Restrictions: allow-stderr pg_stat_kcache-REL2_1_1/debian/tests/control.in000066400000000000000000000001631332713470400215570ustar00rootroot00000000000000Depends: @, postgresql-contrib-PGVERSION, postgresql-server-dev-all Tests: installcheck Restrictions: allow-stderr pg_stat_kcache-REL2_1_1/debian/tests/installcheck000077500000000000000000000001551332713470400221420ustar00rootroot00000000000000#!/bin/sh set -eu pg_buildext -o "shared_preload_libraries=pg_stat_statements,pg_stat_kcache" installcheck pg_stat_kcache-REL2_1_1/debian/watch000066400000000000000000000001731332713470400174370ustar00rootroot00000000000000version=3 opts="uversionmangle=s/_/./g" \ https://github.com/powa-team/pg_stat_kcache/releases .*/archive/REL(.*).tar.gz pg_stat_kcache-REL2_1_1/expected/000077500000000000000000000000001332713470400167645ustar00rootroot00000000000000pg_stat_kcache-REL2_1_1/expected/pgsk.out000066400000000000000000000021011332713470400204530ustar00rootroot00000000000000CREATE EXTENSION pg_stat_statements; CREATE EXTENSION pg_stat_kcache; -- dummy query SELECT 1 AS dummy; dummy ------- 1 (1 row) SELECT count(*) FROM pg_stat_kcache WHERE datname = current_database(); count ------- 1 (1 row) SELECT count(*) FROM pg_stat_kcache_detail WHERE datname = current_database() AND (query = 'SELECT $1 AS dummy' OR query = 'SELECT ? AS dummy;'); count ------- 1 (1 row) SELECT reads, reads_blks, writes, writes_blks FROM pg_stat_kcache_detail WHERE datname = current_database() AND (query = 'SELECT $1 AS dummy' OR query = 'SELECT ? AS dummy;'); reads | reads_blks | writes | writes_blks -------+------------+--------+------------- 0 | 0 | 0 | 0 (1 row) -- dummy table CREATE TABLE test AS SELECT i FROM generate_series(1, 1000) i; -- dummy query again SELECT count(*) FROM test; count ------- 1000 (1 row) SELECT user_time + system_time > 0 AS cpu_time_ok FROM pg_stat_kcache_detail WHERE datname = current_database() AND query LIKE 'SELECT count(*) FROM test%'; cpu_time_ok ------------- t (1 row) pg_stat_kcache-REL2_1_1/pg_stat_kcache--2.1.0--2.1.1.sql000066400000000000000000000005171332713470400220710ustar00rootroot00000000000000-- This program is open source, licensed under the PostgreSQL License. -- For license terms, see the LICENSE file. -- -- Copyright (c) 2014-2017, Dalibo -- Copyright (c) 2018, The PoWA-team -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "ALTER EXTENSION pg_stat_kcache" to load this file. \quit pg_stat_kcache-REL2_1_1/pg_stat_kcache--2.1.0.sql000066400000000000000000000057111332713470400213600ustar00rootroot00000000000000-- This program is open source, licensed under the PostgreSQL License. -- For license terms, see the LICENSE file. -- -- Copyright (c) 2014-2017, Dalibo -- Copyright (c) 2018, The PoWA-team -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION pg_stat_kcache" to load this file. \quit SET client_encoding = 'UTF8'; CREATE FUNCTION pg_stat_kcache( OUT queryid bigint, OUT userid oid, OUT dbid oid, OUT reads bigint, /* total reads, in bytes */ OUT writes bigint, /* total writes, in bytes */ OUT user_time double precision, /* total user CPU time used */ OUT system_time double precision, /* total system CPU time used */ OUT minflts bigint, /* total page reclaims (soft page faults) */ OUT majflts bigint, /* total page faults (hard page faults) */ OUT nswaps bigint, /* total swaps */ OUT msgsnds bigint, /* total IPC messages sent */ OUT msgrcvs bigint, /* total IPC messages received */ OUT nsignals bigint, /* total signals received */ OUT nvcsws bigint, /* total voluntary context switches */ OUT nivcsws bigint /* total involuntary context switches */ ) RETURNS SETOF record LANGUAGE c COST 1000 AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_2_1'; GRANT ALL ON FUNCTION pg_stat_kcache() TO public; CREATE FUNCTION pg_stat_kcache_reset() RETURNS void LANGUAGE c COST 1000 AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_reset'; REVOKE ALL ON FUNCTION pg_stat_kcache_reset() FROM public; CREATE VIEW pg_stat_kcache_detail AS SELECT s.query, d.datname, r.rolname, k.user_time, k.system_time, k.minflts, k.majflts, k.nswaps, k.reads AS reads, k.reads/(current_setting('block_size')::integer) AS reads_blks, k.writes AS writes, k.writes/(current_setting('block_size')::integer) AS writes_blks, k.msgsnds, k.msgrcvs, k.nsignals, k.nvcsws, k.nivcsws FROM pg_stat_kcache() k JOIN pg_stat_statements s ON k.queryid = s.queryid AND k.dbid = s.dbid AND k.userid = s.userid JOIN pg_database d ON d.oid = s.dbid JOIN pg_roles r ON r.oid = s.userid; GRANT SELECT ON pg_stat_kcache_detail TO public; CREATE VIEW pg_stat_kcache AS SELECT datname, SUM(user_time) AS user_time, SUM(system_time) AS system_time, SUM(minflts) AS minflts, SUM(majflts) AS majflts, SUM(nswaps) AS nswaps, SUM(reads) AS reads, SUM(reads_blks) AS reads_blks, SUM(writes) AS writes, SUM(writes_blks) AS writes_blks, SUM(msgsnds) AS msgsnds, SUM(msgrcvs) AS msgrcvs, SUM(nsignals) AS nsignals, SUM(nvcsws) AS nvcsws, SUM(nivcsws) AS nivcsws FROM pg_stat_kcache_detail GROUP BY datname; GRANT SELECT ON pg_stat_kcache TO public; pg_stat_kcache-REL2_1_1/pg_stat_kcache--2.1.1.sql000066400000000000000000000057111332713470400213610ustar00rootroot00000000000000-- This program is open source, licensed under the PostgreSQL License. -- For license terms, see the LICENSE file. -- -- Copyright (c) 2014-2017, Dalibo -- Copyright (c) 2018, The PoWA-team -- complain if script is sourced in psql, rather than via CREATE EXTENSION \echo Use "CREATE EXTENSION pg_stat_kcache" to load this file. \quit SET client_encoding = 'UTF8'; CREATE FUNCTION pg_stat_kcache( OUT queryid bigint, OUT userid oid, OUT dbid oid, OUT reads bigint, /* total reads, in bytes */ OUT writes bigint, /* total writes, in bytes */ OUT user_time double precision, /* total user CPU time used */ OUT system_time double precision, /* total system CPU time used */ OUT minflts bigint, /* total page reclaims (soft page faults) */ OUT majflts bigint, /* total page faults (hard page faults) */ OUT nswaps bigint, /* total swaps */ OUT msgsnds bigint, /* total IPC messages sent */ OUT msgrcvs bigint, /* total IPC messages received */ OUT nsignals bigint, /* total signals received */ OUT nvcsws bigint, /* total voluntary context switches */ OUT nivcsws bigint /* total involuntary context switches */ ) RETURNS SETOF record LANGUAGE c COST 1000 AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_2_1'; GRANT ALL ON FUNCTION pg_stat_kcache() TO public; CREATE FUNCTION pg_stat_kcache_reset() RETURNS void LANGUAGE c COST 1000 AS '$libdir/pg_stat_kcache', 'pg_stat_kcache_reset'; REVOKE ALL ON FUNCTION pg_stat_kcache_reset() FROM public; CREATE VIEW pg_stat_kcache_detail AS SELECT s.query, d.datname, r.rolname, k.user_time, k.system_time, k.minflts, k.majflts, k.nswaps, k.reads AS reads, k.reads/(current_setting('block_size')::integer) AS reads_blks, k.writes AS writes, k.writes/(current_setting('block_size')::integer) AS writes_blks, k.msgsnds, k.msgrcvs, k.nsignals, k.nvcsws, k.nivcsws FROM pg_stat_kcache() k JOIN pg_stat_statements s ON k.queryid = s.queryid AND k.dbid = s.dbid AND k.userid = s.userid JOIN pg_database d ON d.oid = s.dbid JOIN pg_roles r ON r.oid = s.userid; GRANT SELECT ON pg_stat_kcache_detail TO public; CREATE VIEW pg_stat_kcache AS SELECT datname, SUM(user_time) AS user_time, SUM(system_time) AS system_time, SUM(minflts) AS minflts, SUM(majflts) AS majflts, SUM(nswaps) AS nswaps, SUM(reads) AS reads, SUM(reads_blks) AS reads_blks, SUM(writes) AS writes, SUM(writes_blks) AS writes_blks, SUM(msgsnds) AS msgsnds, SUM(msgrcvs) AS msgrcvs, SUM(nsignals) AS nsignals, SUM(nvcsws) AS nvcsws, SUM(nivcsws) AS nivcsws FROM pg_stat_kcache_detail GROUP BY datname; GRANT SELECT ON pg_stat_kcache TO public; pg_stat_kcache-REL2_1_1/pg_stat_kcache.c000066400000000000000000000555561332713470400203060ustar00rootroot00000000000000/*---------------- * pg_stat_kcache * * Provides basic statistics about real I/O done by the filesystem layer. * This way, calculating a real hit-ratio is doable. Also provides basis * statistics about CPU usage. * * Large portions of code freely inspired by pg_stat_plans. Thanks to Peter * Geoghegan for this great extension. * * This program is open source, licensed under the PostgreSQL license. * For license terms, see the LICENSE file. * */ #include "postgres.h" #include #ifdef HAVE_SYS_RESOURCE_H #include #include #endif #ifndef HAVE_GETRUSAGE #include "rusagestub.h" #endif #include "access/hash.h" #include "executor/executor.h" #include "funcapi.h" #include "miscadmin.h" #include "pgstat.h" #include "storage/fd.h" #include "storage/ipc.h" #include "storage/spin.h" #include "utils/builtins.h" #include "utils/guc.h" PG_MODULE_MAGIC; #if PG_VERSION_NUM >= 90300 #define PGSK_DUMP_FILE "pg_stat/pg_stat_kcache.stat" #else #define PGSK_DUMP_FILE "global/pg_stat_kcache.stat" #endif /* In PostgreSQL 11, queryid becomes a uint64 internally. */ #if PG_VERSION_NUM >= 110000 typedef uint64 pgsk_queryid; #else typedef uint32 pgsk_queryid; #endif #define PG_STAT_KCACHE_COLS_V2_0 7 #define PG_STAT_KCACHE_COLS_V2_1 15 #define PG_STAT_KCACHE_COLS 15 /* maximum of above */ #define USAGE_INCREASE (1.0) #define USAGE_DECREASE_FACTOR (0.99) /* decreased every pgsk_entry_dealloc */ #define USAGE_DEALLOC_PERCENT 5 /* free this % of entries at once */ #define USAGE_INIT (1.0) /* including initial planning */ /* ru_inblock block size is 512 bytes with Linux * see http://lkml.indiana.edu/hypermail/linux/kernel/0703.2/0937.html */ #define RUSAGE_BLOCK_SIZE 512 /* Size of a block for getrusage() */ #define TIMEVAL_DIFF(start, end) ((double) end.tv_sec + (double) end.tv_usec / 1000000.0) \ - ((double) start.tv_sec + (double) start.tv_usec / 1000000.0) /* * Extension version number, for supporting older extension versions' objects */ typedef enum pgskVersion { PGSK_V2_0 = 0, PGSK_V2_1 } pgskVersion; static const uint32 PGSK_FILE_HEADER = 0x0d756e10; static struct rusage rusage_start; /* * Current getrusage counters. * * For platform without getrusage support, we rely on postgres implementation * defined in rusagestub.h, which only supports user and system time. */ typedef struct pgskCounters { double usage; /* usage factor */ /* These fields are always used */ float8 utime; /* CPU user time */ float8 stime; /* CPU system time */ #ifdef HAVE_GETRUSAGE /* These fields are only used for platform with HAVE_GETRUSAGE defined */ int64 minflts; /* page reclaims (soft page faults) */ int64 majflts; /* page faults (hard page faults) */ int64 nswaps; /* page faults (hard page faults) */ int64 reads; /* Physical block reads */ int64 writes; /* Physical block writes */ int64 msgsnds; /* IPC messages sent */ int64 msgrcvs; /* IPC messages received */ int64 nsignals; /* signals received */ int64 nvcsws; /* voluntary context witches */ int64 nivcsws; /* unvoluntary context witches */ #endif } pgskCounters; static int pgsk_max = 0; /* max #queries to store. pg_stat_statements.max is used */ /* * Hashtable key that defines the identity of a hashtable entry. We use the * same hash as pg_stat_statements */ typedef struct pgskHashKey { Oid userid; /* user OID */ Oid dbid; /* database OID */ pgsk_queryid queryid; /* query identifier */ } pgskHashKey; /* * Statistics per database */ typedef struct pgskEntry { pgskHashKey key; /* hash key of entry - MUST BE FIRST */ pgskCounters counters; /* statistics for this query */ slock_t mutex; /* protects the counters only */ } pgskEntry; /* * Global shared state */ typedef struct pgskSharedState { LWLockId lock; /* protects hashtable search/modification */ } pgskSharedState; /* saved hook address in case of unload */ static shmem_startup_hook_type prev_shmem_startup_hook = NULL; static ExecutorStart_hook_type prev_ExecutorStart = NULL; static ExecutorEnd_hook_type prev_ExecutorEnd = NULL; /* Links to shared memory state */ static pgskSharedState *pgsk = NULL; static HTAB *pgsk_hash = NULL; /*--- Functions --- */ void _PG_init(void); void _PG_fini(void); extern PGDLLEXPORT Datum pg_stat_kcache_reset(PG_FUNCTION_ARGS); extern PGDLLEXPORT Datum pg_stat_kcache(PG_FUNCTION_ARGS); extern PGDLLEXPORT Datum pg_stat_kcache_2_1(PG_FUNCTION_ARGS); PG_FUNCTION_INFO_V1(pg_stat_kcache_reset); PG_FUNCTION_INFO_V1(pg_stat_kcache); PG_FUNCTION_INFO_V1(pg_stat_kcache_2_1); static void pg_stat_kcache_internal(FunctionCallInfo fcinfo, pgskVersion api_version); static void pgsk_setmax(void); static Size pgsk_memsize(void); static void pgsk_shmem_startup(void); static void pgsk_shmem_shutdown(int code, Datum arg); static void pgsk_ExecutorStart(QueryDesc *queryDesc, int eflags); static void pgsk_ExecutorEnd(QueryDesc *queryDesc); static pgskEntry *pgsk_entry_alloc(pgskHashKey *key); static void pgsk_entry_dealloc(void); static void pgsk_entry_reset(void); static void pgsk_entry_store(pgsk_queryid queryId, pgskCounters counters); static uint32 pgsk_hash_fn(const void *key, Size keysize); static int pgsk_match_fn(const void *key1, const void *key2, Size keysize); static bool pgsk_assign_linux_hz_check_hook(int *newval, void **extra, GucSource source); static int pgsk_linux_hz; void _PG_init(void) { if (!process_shared_preload_libraries_in_progress) { elog(ERROR, "This module can only be loaded via shared_preload_libraries"); return; } DefineCustomIntVariable("pg_stat_kcache.linux_hz", "Inform pg_stat_kcache of the linux CONFIG_HZ config option", "This is used by pg_stat_kcache to compensate for sampling errors " "in getrusage due to the kernel adhering to its ticks. The default value, -1, " "tries to guess it at startup. ", &pgsk_linux_hz, -1, -1, INT_MAX, PGC_USERSET, 0, pgsk_assign_linux_hz_check_hook, NULL, NULL); EmitWarningsOnPlaceholders("pg_stat_kcache"); /* set pgsk_max if needed */ pgsk_setmax(); RequestAddinShmemSpace(pgsk_memsize()); #if PG_VERSION_NUM >= 90600 RequestNamedLWLockTranche("pg_stat_kcache", 1); #else RequestAddinLWLocks(1); #endif /* Install hook */ prev_shmem_startup_hook = shmem_startup_hook; shmem_startup_hook = pgsk_shmem_startup; prev_ExecutorStart = ExecutorStart_hook; ExecutorStart_hook = pgsk_ExecutorStart; prev_ExecutorEnd = ExecutorEnd_hook; ExecutorEnd_hook = pgsk_ExecutorEnd; } void _PG_fini(void) { /* uninstall hook */ ExecutorStart_hook = prev_ExecutorStart; ExecutorEnd_hook = prev_ExecutorEnd; shmem_startup_hook = prev_shmem_startup_hook; } static bool pgsk_assign_linux_hz_check_hook(int *newval, void **extra, GucSource source) { int val = *newval; struct rusage myrusage; struct timeval previous_value; /* In that case we try to guess it */ if (val == -1) { elog(LOG, "Auto detecting pg_stat_kcache.linux_hz parameter..."); getrusage(RUSAGE_SELF, &myrusage); previous_value = myrusage.ru_utime; while (myrusage.ru_utime.tv_usec == previous_value.tv_usec && myrusage.ru_utime.tv_sec == previous_value.tv_sec) { getrusage(RUSAGE_SELF, &myrusage); } *newval = (int) (1 / ((myrusage.ru_utime.tv_sec - previous_value.tv_sec) + (myrusage.ru_utime.tv_usec - previous_value.tv_usec) / 1000000.)); elog(LOG, "pg_stat_kcache.linux_hz is set to %d", *newval); } return true; } static void pgsk_shmem_startup(void) { bool found; HASHCTL info; FILE *file; int i; uint32 header; int32 num; pgskEntry *buffer = NULL; if (prev_shmem_startup_hook) prev_shmem_startup_hook(); /* reset in case this is a restart within the postmaster */ pgsk = NULL; /* Create or attach to the shared memory state */ LWLockAcquire(AddinShmemInitLock, LW_EXCLUSIVE); /* global access lock */ pgsk = ShmemInitStruct("pg_stat_kcache", sizeof(pgskSharedState), &found); if (!found) { /* First time through ... */ #if PG_VERSION_NUM >= 90600 pgsk->lock = &(GetNamedLWLockTranche("pg_stat_kcache"))->lock; #else pgsk->lock = LWLockAssign(); #endif } /* set pgsk_max if needed */ pgsk_setmax(); memset(&info, 0, sizeof(info)); info.keysize = sizeof(pgskHashKey); info.entrysize = sizeof(pgskEntry); info.hash = pgsk_hash_fn; info.match = pgsk_match_fn; /* allocate stats shared memory hash */ pgsk_hash = ShmemInitHash("pg_stat_kcache hash", pgsk_max, pgsk_max, &info, HASH_ELEM | HASH_FUNCTION | HASH_COMPARE); LWLockRelease(AddinShmemInitLock); if (!IsUnderPostmaster) on_shmem_exit(pgsk_shmem_shutdown, (Datum) 0); /* * Done if some other process already completed our initialization. */ if (found) return; /* Load stat file, don't care about locking */ file = AllocateFile(PGSK_DUMP_FILE, PG_BINARY_R); if (file == NULL) { if (errno == ENOENT) return; /* ignore not-found error */ goto error; } /* check is header is valid */ if (fread(&header, sizeof(uint32), 1, file) != 1 || header != PGSK_FILE_HEADER) goto error; /* get number of entries */ if (fread(&num, sizeof(int32), 1, file) != 1) goto error; for (i = 0; i < num ; i++) { pgskEntry temp; pgskEntry *entry; if (fread(&temp, sizeof(pgskEntry), 1, file) != 1) goto error; entry = pgsk_entry_alloc(&temp.key); /* copy in the actual stats */ entry->counters = temp.counters; /* don't initialize spinlock, already done */ } FreeFile(file); /* * Remove the file so it's not included in backups/replication slaves, * etc. A new file will be written on next shutdown. */ unlink(PGSK_DUMP_FILE); return; error: ereport(LOG, (errcode_for_file_access(), errmsg("could not read pg_stat_kcache file \"%s\": %m", PGSK_DUMP_FILE))); if (buffer) pfree(buffer); if (file) FreeFile(file); /* delete bogus file, don't care of errors in this case */ unlink(PGSK_DUMP_FILE); } /* * shmem_shutdown hook: dump statistics into file. * */ static void pgsk_shmem_shutdown(int code, Datum arg) { FILE *file; HASH_SEQ_STATUS hash_seq; int32 num_entries; pgskEntry *entry; /* Don't try to dump during a crash. */ if (code) return; if (!pgsk) return; file = AllocateFile(PGSK_DUMP_FILE ".tmp", PG_BINARY_W); if (file == NULL) goto error; if (fwrite(&PGSK_FILE_HEADER, sizeof(uint32), 1, file) != 1) goto error; num_entries = hash_get_num_entries(pgsk_hash); if (fwrite(&num_entries, sizeof(int32), 1, file) != 1) goto error; hash_seq_init(&hash_seq, pgsk_hash); while ((entry = hash_seq_search(&hash_seq)) != NULL) { if (fwrite(entry, sizeof(pgskEntry), 1, file) != 1) { /* note: we assume hash_seq_term won't change errno */ hash_seq_term(&hash_seq); goto error; } } if (FreeFile(file)) { file = NULL; goto error; } /* * Rename file inplace */ if (rename(PGSK_DUMP_FILE ".tmp", PGSK_DUMP_FILE) != 0) ereport(LOG, (errcode_for_file_access(), errmsg("could not rename pg_stat_kcache file \"%s\": %m", PGSK_DUMP_FILE ".tmp"))); return; error: ereport(LOG, (errcode_for_file_access(), errmsg("could not read pg_stat_kcache file \"%s\": %m", PGSK_DUMP_FILE))); if (file) FreeFile(file); unlink(PGSK_DUMP_FILE); } /* * Retrieve pg_stat_statement.max GUC value and store it into pgsk_max, since * we want to store the same number of entries as pg_stat_statements. Don't do * anything if pgsk_max is already set. */ static void pgsk_setmax(void) { const char *pgss_max; const char *name = "pg_stat_statements.max"; if (pgsk_max != 0) return; pgss_max = GetConfigOption(name, true, false); /* * Retrieving pg_stat_statements.max can fail if pgss is loaded after pgsk * in shared_preload_libraries. Hint user in case this happens. */ if (!pgss_max) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("unrecognized configuration parameter \"%s\"", name), errhint("make sure pg_stat_statements is loaded,\n" "and make sure pg_stat_kcache is present after pg_stat_statements" " in the shared_preload_libraries setting"))); pgsk_max = atoi(pgss_max); } static Size pgsk_memsize(void) { Size size; Assert(pgsk_max != 0); size = MAXALIGN(sizeof(pgskSharedState)); size = add_size(size, hash_estimate_size(pgsk_max, sizeof(pgskEntry))); return size; } /* * support functions */ static void pgsk_entry_store(pgsk_queryid queryId, pgskCounters counters) { volatile pgskEntry *e; pgskHashKey key; pgskEntry *entry; /* Safety check... */ if (!pgsk || !pgsk_hash) return; /* Set up key for hashtable search */ key.userid = GetUserId(); key.dbid = MyDatabaseId; key.queryid = queryId; /* Lookup the hash table entry with shared lock. */ LWLockAcquire(pgsk->lock, LW_SHARED); entry = (pgskEntry *) hash_search(pgsk_hash, &key, HASH_FIND, NULL); /* Create new entry, if not present */ if (!entry) { /* Need exclusive lock to make a new hashtable entry - promote */ LWLockRelease(pgsk->lock); LWLockAcquire(pgsk->lock, LW_EXCLUSIVE); /* OK to create a new hashtable entry */ entry = pgsk_entry_alloc(&key); } /* * Grab the spinlock while updating the counters (see comment about * locking rules at the head of the file) */ e = (volatile pgskEntry *) entry; SpinLockAcquire(&e->mutex); e->counters.usage += USAGE_INCREASE; e->counters.utime += counters.utime; e->counters.stime += counters.stime; #ifdef HAVE_GETRUSAGE e->counters.minflts += counters.minflts; e->counters.majflts += counters.majflts; e->counters.nswaps += counters.nswaps; e->counters.reads += counters.reads; e->counters.writes += counters.writes; e->counters.msgsnds += counters.msgsnds; e->counters.msgrcvs += counters.msgrcvs; e->counters.nsignals += counters.nsignals; e->counters.nvcsws += counters.nvcsws; e->counters.nivcsws += counters.nivcsws; #endif SpinLockRelease(&e->mutex); LWLockRelease(pgsk->lock); } /* * Allocate a new hashtable entry. * caller must hold an exclusive lock on pgsk->lock */ static pgskEntry *pgsk_entry_alloc(pgskHashKey *key) { pgskEntry *entry; bool found; /* Make space if needed */ while (hash_get_num_entries(pgsk_hash) >= pgsk_max) pgsk_entry_dealloc(); /* Find or create an entry with desired hash code */ entry = (pgskEntry *) hash_search(pgsk_hash, key, HASH_ENTER, &found); if (!found) { /* New entry, initialize it */ /* reset the statistics */ memset(&entry->counters, 0, sizeof(pgskCounters)); /* set the appropriate initial usage count */ entry->counters.usage = USAGE_INIT; /* re-initialize the mutex each time ... we assume no one using it */ SpinLockInit(&entry->mutex); } return entry; } /* * qsort comparator for sorting into increasing usage order */ static int entry_cmp(const void *lhs, const void *rhs) { double l_usage = (*(pgskEntry *const *) lhs)->counters.usage; double r_usage = (*(pgskEntry *const *) rhs)->counters.usage; if (l_usage < r_usage) return -1; else if (l_usage > r_usage) return +1; else return 0; } /* * Deallocate least used entries. * Caller must hold an exclusive lock on pgsk->lock. */ static void pgsk_entry_dealloc(void) { HASH_SEQ_STATUS hash_seq; pgskEntry **entries; pgskEntry *entry; int nvictims; int i; /* * Sort entries by usage and deallocate USAGE_DEALLOC_PERCENT of them. * While we're scanning the table, apply the decay factor to the usage * values. */ entries = palloc(hash_get_num_entries(pgsk_hash) * sizeof(pgskEntry *)); i = 0; hash_seq_init(&hash_seq, pgsk_hash); while ((entry = hash_seq_search(&hash_seq)) != NULL) { entries[i++] = entry; entry->counters.usage *= USAGE_DECREASE_FACTOR; } qsort(entries, i, sizeof(pgskEntry *), entry_cmp); nvictims = Max(10, i * USAGE_DEALLOC_PERCENT / 100); nvictims = Min(nvictims, i); for (i = 0; i < nvictims; i++) { hash_search(pgsk_hash, &entries[i]->key, HASH_REMOVE, NULL); } pfree(entries); } static void pgsk_entry_reset(void) { HASH_SEQ_STATUS hash_seq; pgskEntry *entry; LWLockAcquire(pgsk->lock, LW_EXCLUSIVE); hash_seq_init(&hash_seq, pgsk_hash); while ((entry = hash_seq_search(&hash_seq)) != NULL) { hash_search(pgsk_hash, &entry->key, HASH_REMOVE, NULL); } LWLockRelease(pgsk->lock); } /* * Hooks */ static void pgsk_ExecutorStart (QueryDesc *queryDesc, int eflags) { /* capture kernel usage stats in rusage_start */ getrusage(RUSAGE_SELF, &rusage_start); /* give control back to PostgreSQL */ if (prev_ExecutorStart) prev_ExecutorStart(queryDesc, eflags); else standard_ExecutorStart(queryDesc, eflags); } static void pgsk_ExecutorEnd (QueryDesc *queryDesc) { pgsk_queryid queryId; struct rusage rusage_end; pgskCounters counters; /* capture kernel usage stats in rusage_end */ getrusage(RUSAGE_SELF, &rusage_end); queryId = queryDesc->plannedstmt->queryId; /* Compute CPU time delta */ counters.utime = TIMEVAL_DIFF(rusage_start.ru_utime, rusage_end.ru_utime); counters.stime = TIMEVAL_DIFF(rusage_start.ru_stime, rusage_end.ru_stime); if (queryDesc->totaltime) { /* Make sure stats accumulation is done */ InstrEndLoop(queryDesc->totaltime); /* * We only consider values greater than 3 * linux tick, otherwise the * bias is too big */ if (queryDesc->totaltime->total < (3. / pgsk_linux_hz)) { counters.stime = 0; counters.utime = queryDesc->totaltime->total; } } #ifdef HAVE_GETRUSAGE /* Compute the rest of the counters */ counters.minflts = rusage_end.ru_minflt - rusage_start.ru_minflt; counters.majflts = rusage_end.ru_majflt - rusage_start.ru_majflt; counters.nswaps = rusage_end.ru_nswap - rusage_start.ru_nswap; counters.reads = rusage_end.ru_inblock - rusage_start.ru_inblock; counters.writes = rusage_end.ru_oublock - rusage_start.ru_oublock; counters.msgsnds = rusage_end.ru_msgsnd - rusage_start.ru_msgsnd; counters.msgrcvs = rusage_end.ru_msgrcv - rusage_start.ru_msgrcv; counters.nsignals = rusage_end.ru_nsignals - rusage_start.ru_nsignals; counters.nvcsws = rusage_end.ru_nvcsw - rusage_start.ru_nvcsw; counters.nivcsws = rusage_end.ru_nivcsw - rusage_start.ru_nivcsw; #endif /* store current number of block reads and writes */ pgsk_entry_store(queryId, counters); /* give control back to PostgreSQL */ if (prev_ExecutorEnd) prev_ExecutorEnd(queryDesc); else standard_ExecutorEnd(queryDesc); } /* * Calculate hash value for a key */ static uint32 pgsk_hash_fn(const void *key, Size keysize) { const pgskHashKey *k = (const pgskHashKey *) key; return hash_uint32((uint32) k->userid) ^ hash_uint32((uint32) k->dbid) ^ hash_uint32((uint32) k->queryid); } /* * Compare two keys - zero means match */ static int pgsk_match_fn(const void *key1, const void *key2, Size keysize) { const pgskHashKey *k1 = (const pgskHashKey *) key1; const pgskHashKey *k2 = (const pgskHashKey *) key2; if (k1->userid == k2->userid && k1->dbid == k2->dbid && k1->queryid == k2->queryid) return 0; else return 1; } /* * Reset statistics. */ PGDLLEXPORT Datum pg_stat_kcache_reset(PG_FUNCTION_ARGS) { if (!pgsk) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("pg_stat_kcache must be loaded via shared_preload_libraries"))); pgsk_entry_reset(); PG_RETURN_VOID(); } PGDLLEXPORT Datum pg_stat_kcache(PG_FUNCTION_ARGS) { pg_stat_kcache_internal(fcinfo, PGSK_V2_0); return (Datum) 0; } PGDLLEXPORT Datum pg_stat_kcache_2_1(PG_FUNCTION_ARGS) { pg_stat_kcache_internal(fcinfo, PGSK_V2_1); return (Datum) 0; } static void pg_stat_kcache_internal(FunctionCallInfo fcinfo, pgskVersion api_version) { ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; MemoryContext per_query_ctx; MemoryContext oldcontext; TupleDesc tupdesc; Tuplestorestate *tupstore; HASH_SEQ_STATUS hash_seq; pgskEntry *entry; if (!pgsk) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("pg_stat_kcache must be loaded via shared_preload_libraries"))); /* check to see if caller supports us returning a tuplestore */ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); if (!(rsinfo->allowedModes & SFRM_Materialize)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("materialize mode required, but it is not " \ "allowed in this context"))); /* Switch into long-lived context to construct returned data structures */ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; oldcontext = MemoryContextSwitchTo(per_query_ctx); /* Build a tuple descriptor for our result type */ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) elog(ERROR, "return type must be a row type"); tupstore = tuplestore_begin_heap(true, false, work_mem); rsinfo->returnMode = SFRM_Materialize; rsinfo->setResult = tupstore; rsinfo->setDesc = tupdesc; MemoryContextSwitchTo(oldcontext); LWLockAcquire(pgsk->lock, LW_SHARED); hash_seq_init(&hash_seq, pgsk_hash); while ((entry = hash_seq_search(&hash_seq)) != NULL) { Datum values[PG_STAT_KCACHE_COLS]; bool nulls[PG_STAT_KCACHE_COLS]; pgskCounters tmp; int i = 0; #ifdef HAVE_GETRUSAGE int64 reads, writes; #endif memset(values, 0, sizeof(values)); memset(nulls, 0, sizeof(nulls)); /* copy counters to a local variable to keep locking time short */ { volatile pgskEntry *e = (volatile pgskEntry *) entry; SpinLockAcquire(&e->mutex); tmp = e->counters; SpinLockRelease(&e->mutex); } values[i++] = Int64GetDatum(entry->key.queryid); values[i++] = ObjectIdGetDatum(entry->key.userid); values[i++] = ObjectIdGetDatum(entry->key.dbid); #ifdef HAVE_GETRUSAGE reads = tmp.reads * RUSAGE_BLOCK_SIZE; writes = tmp.writes * RUSAGE_BLOCK_SIZE; values[i++] = Int64GetDatumFast(reads); values[i++] = Int64GetDatumFast(writes); #else nulls[i++] = true; /* reads */ nulls[i++] = true; /* writes */ #endif values[i++] = Float8GetDatumFast(tmp.utime); values[i++] = Float8GetDatumFast(tmp.stime); if (api_version >= PGSK_V2_1) { #ifdef HAVE_GETRUSAGE values[i++] = Int64GetDatumFast(tmp.minflts); values[i++] = Int64GetDatumFast(tmp.majflts); values[i++] = Int64GetDatumFast(tmp.nswaps); values[i++] = Int64GetDatumFast(tmp.msgsnds); values[i++] = Int64GetDatumFast(tmp.msgrcvs); values[i++] = Int64GetDatumFast(tmp.nsignals); values[i++] = Int64GetDatumFast(tmp.nvcsws); values[i++] = Int64GetDatumFast(tmp.nivcsws); #else nulls[i++] = true; /* minflts */ nulls[i++] = true; /* majflts */ nulls[i++] = true; /* nswaps */ nulls[i++] = true; /* msgsnds */ nulls[i++] = true; /* msgrcvs */ nulls[i++] = true; /* nsignals */ nulls[i++] = true; /* nvcsws */ nulls[i++] = true; /* nivcsws */ #endif } Assert(i == (api_version == PGSK_V2_0 ? PG_STAT_KCACHE_COLS_V2_0 : api_version == PGSK_V2_1 ? PG_STAT_KCACHE_COLS_V2_1 : -1 /* fail if you forget to update this assert */ )); tuplestore_putvalues(tupstore, tupdesc, values, nulls); } LWLockRelease(pgsk->lock); /* clean up and return the tuplestore */ tuplestore_donestoring(tupstore); } pg_stat_kcache-REL2_1_1/pg_stat_kcache.control000066400000000000000000000002741332713470400215270ustar00rootroot00000000000000# pg_stat_kcache extension comment = 'Kernel statistics gathering' default_version = '2.1.1' requires = 'pg_stat_statements' module_pathname = '$libdir/pg_stat_kcache' relocatable = true pg_stat_kcache-REL2_1_1/test/000077500000000000000000000000001332713470400161425ustar00rootroot00000000000000pg_stat_kcache-REL2_1_1/test/sql/000077500000000000000000000000001332713470400167415ustar00rootroot00000000000000pg_stat_kcache-REL2_1_1/test/sql/pgsk.sql000066400000000000000000000014241332713470400204270ustar00rootroot00000000000000CREATE EXTENSION pg_stat_statements; CREATE EXTENSION pg_stat_kcache; -- dummy query SELECT 1 AS dummy; SELECT count(*) FROM pg_stat_kcache WHERE datname = current_database(); SELECT count(*) FROM pg_stat_kcache_detail WHERE datname = current_database() AND (query = 'SELECT $1 AS dummy' OR query = 'SELECT ? AS dummy;'); SELECT reads, reads_blks, writes, writes_blks FROM pg_stat_kcache_detail WHERE datname = current_database() AND (query = 'SELECT $1 AS dummy' OR query = 'SELECT ? AS dummy;'); -- dummy table CREATE TABLE test AS SELECT i FROM generate_series(1, 1000) i; -- dummy query again SELECT count(*) FROM test; SELECT user_time + system_time > 0 AS cpu_time_ok FROM pg_stat_kcache_detail WHERE datname = current_database() AND query LIKE 'SELECT count(*) FROM test%';