File pg_top-4.1.1.obscpio of Package pg_top
07070100000000000081A400000000000000000000000166622DE800000082000000000000000000000000000000000000002200000000pg_top-4.1.1/.containerfileignore**/*.sw?
builds/
cmake_install.cmake
CMakeCache.txt
Containerfile*
.containerignore
.git
.gitignore
install_manifest.txt
.vscode
07070100000001000081A400000000000000000000000166622DE80000012E000000000000000000000000000000000000001800000000pg_top-4.1.1/.gitignore*.sw?
*.o
*.in
.deps
config.h
!config.h.in
depcomp
pg_top
pg_top.1
tags
*.BAK
cscope.in.out
cscope.out
cscope.po.out
# cmake
/builds
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
install_manifest.txt
CPackConfig.cmake
CPackSourceConfig.cmake
_CPack_Packages/
*.tar.bz2
*.tar.gz
*.tar.xz
*.tar.Z
07070100000002000081A400000000000000000000000166622DE80000270B000000000000000000000000000000000000001C00000000pg_top-4.1.1/CMakeLists.txtcmake_minimum_required(VERSION 3.1.0)
include(CheckFunctionExists)
include(CheckLibraryExists)
include(CheckIncludeFiles)
include(CheckCSourceCompiles)
project(pg_top C)
set(PROJECT_VERSION_MAJOR 4)
set(PROJECT_VERSION_MINOR 1)
set(PROJECT_VERSION_PATCH 1)
set(PROJECT_VERSION
${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH})
set(CMAKE_C_STANDARD 99)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_C_FLAGS_DEBUG "-Wall")
# Handle cmake -D command line options
if (NOT DEFINED ENABLE_COLOR)
set(ENABLE_COLOR 1)
elseif(ENABLE_COLOR EQUAL 0)
unset(ENABLE_COLOR)
else()
set(ENABLE_COLOR "${ENABLE_COLOR}")
endif (NOT DEFINED ENABLE_COLOR)
# Determine which machine module to build
execute_process(
COMMAND uname -s
OUTPUT_VARIABLE MACHINE
OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(TOLOWER "${MACHINE}" MACHINE)
message("-- machine - ${MACHINE}")
execute_process(
COMMAND uname -m
OUTPUT_VARIABLE ARCH
OUTPUT_STRIP_TRAILING_WHITESPACE
)
message("-- arch - ${ARCH}")
# Some machines modules need additional information.
execute_process(
COMMAND uname -r
OUTPUT_VARIABLE OSREV
OUTPUT_STRIP_TRAILING_WHITESPACE
)
string(REGEX MATCH "^([0-9]*)" OSMAJOR "${OSREV}")
string(REPLACE "." "" OSREV "${OSREV}")
string(REPLACE "-" "" OSREV "${OSREV}")
string(REPLACE "_" "" OSREV "${OSREV}")
# Get PostgreSQL include and library directories.
execute_process(
COMMAND pg_config --includedir
OUTPUT_VARIABLE PGINCLUDEDIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
message("-- pg_config --includedir - ${PGINCLUDEDIR}")
if(PGINCLUDEDIR)
set(PGINCLUDE "-I${PGINCLUDEDIR}")
endif(PGINCLUDEDIR)
execute_process(
COMMAND pg_config --libdir
OUTPUT_VARIABLE PGLIBDIR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
message("-- pg_config --libdir - ${PGLIBDIR}")
# Check for include files.
check_include_files("stdlib.h;stdarg.h;string.h;float.h" STDC_HEADERS)
check_include_files("string.h" HAVE_STRING_H)
check_include_files("strings.h" HAVE_STRINGS_H)
check_include_files("sys/time.h;time.h" TIME_WITH_SYS_TIME)
check_include_files("sys/time.h" HAVE_SYS_TIME_H)
check_include_files("sys/resource.h" HAVE_SYS_RESOURCE_H)
check_include_files("unistd.h" HAVE_UNISTD_H)
# Check for library functions.
check_function_exists(getopt HAVE_GETOPT)
check_function_exists(memcpy HAVE_MEMCPY)
check_function_exists(setpriority HAVE_SETPRIORITY)
check_function_exists(sigaction HAVE_SIGACTION)
check_function_exists(sighold HAVE_SIGHOLD)
check_function_exists(sigprocmask HAVE_SIGPROCMASK)
check_function_exists(sigrelse HAVE_SIGRELSE)
check_function_exists(snprintf HAVE_SNPRINTF)
check_function_exists(strchr HAVE_STRCHR)
check_function_exists(strerror HAVE_STRERROR)
# Test return type of signal().
check_c_source_compiles("
#include <sys/types.h>
#include <signal.h>
int main () { return *(signal (0, 0)) (0) == 1; return 0; }
" SIGNAL_RETURN)
if(SIGNAL_RETURN)
set(RETSIGTYPE "int")
else()
set(RETSIGTYPE "void")
endif(SIGNAL_RETURN)
# Test whether time_t is defined.
check_c_source_compiles("
#include <sys/types.h>
int main () { time_t a; return 0; }
" TIME_T_DEFINED)
if(NOT TIME_T_DEFINED)
set(time_t "long")
endif(NOT TIME_T_DEFINED)
# Build config.h header.
configure_file(
${CMAKE_SOURCE_DIR}/config.h.in
${CMAKE_BINARY_DIR}/config.h
)
# Build manual page with appropriate machine specific notes.
configure_file(
${CMAKE_SOURCE_DIR}/pg_top.1.rst.in
${CMAKE_BINARY_DIR}/pg_top.1.rst
)
if(EXISTS "${CMAKE_SOURCE_DIR}/machine/m_${MACHINE}.man.rst")
execute_process(
COMMAND cat ${CMAKE_SOURCE_DIR}/machine/m_${MACHINE}.man.rst
OUTPUT_VARIABLE MANUAL_SUPPLIMENT
)
file(APPEND ${CMAKE_BINARY_DIR}/pg_top.1.rst "${MANUAL_SUPPLIMENT}")
endif(EXISTS "${CMAKE_SOURCE_DIR}/machine/m_${MACHINE}.man.rst")
file(APPEND ${CMAKE_BINARY_DIR}/pg_top.1 "${MANUAL_SUPPLIMENT}")
execute_process(
COMMAND rst2man.py ${CMAKE_BINARY_DIR}/pg_top.1.rst
OUTPUT_FILE ${CMAKE_BINARY_DIR}/pg_top.1
)
# Set appropriate compile flags.
set_source_files_properties(
color.c
commands.c
display.c
pg.c
pg_top.c
screen.c
sprompt.c
utils.c
PROPERTIES COMPILE_FLAGS "${PGINCLUDE}"
)
set_source_files_properties(
version.c
PROPERTIES COMPILE_FLAGS
"-DPACKAGE_VERSION=\\\"${PROJECT_VERSION}\\\" ${PGINCLUDE}"
)
set_source_files_properties(
machine/m_common.c
machine/m_remote.c
machine/m_${MACHINE}.c
PROPERTIES COMPILE_FLAGS "-I${CMAKE_HOME_DIRECTORY} ${PGINCLUDE}"
)
if (NOT ${HAVE_GETOPT})
set (EXTRAFILES "${EXTRAFILES} getopt.c")
endif (NOT ${HAVE_GETOPT})
add_executable(
${PROJECT_NAME}
color.c
commands.c
display.c
screen.c
sprompt.c
pg.c
pg_top.c
utils.c
version.c
machine/m_remote.c
machine/m_common.c
machine/m_${MACHINE}.c
${EXTRAFILES}
)
# Determine appropriate linker flags.
find_library(LIBPQ pq PATHS ${PGLIBDIR})
if(LIBPQ)
target_link_libraries(${PROJECT_NAME} ${LIBPQ})
endif(LIBPQ)
find_library(LIBM m)
if(LIBM)
target_link_libraries(${PROJECT_NAME} ${LIBM})
endif(LIBM)
find_library(LIBDL dl)
if(LIBDL)
target_link_libraries(${PROJECT_NAME} ${LIBDL})
endif(LIBDL)
find_library(LIBINTL intl)
if(LIBINTL)
target_link_libraries(${PROJECT_NAME} ${LIBINTL})
endif(LIBINTL)
find_library(LIBTINFO tinfo)
if(LIBTINFO)
target_link_libraries(${PROJECT_NAME} ${LIBTINFO})
endif(LIBTINFO)
find_library(LIBTERMCAP termcap)
if(LIBTERMCAP)
target_link_libraries(${PROJECT_NAME} ${LIBTERMCAP})
else(LIBTERMCAP)
find_library(LIBCURSES curses)
if(LIBCURSES)
target_link_libraries(${PROJECT_NAME} ${LIBCURSES})
else(LIBCURSES)
find_library(LIBNCURSES ncurses REQUIRED)
if(LIBNCURSES)
target_link_libraries(${PROJECT_NAME} ${LIBNCURSES})
endif(LIBNCURSES)
endif(LIBCURSES)
endif(LIBTERMCAP)
find_library(LIBKSTAT kstat)
if(LIBKSTAT)
target_link_libraries(${PROJECT_NAME} ${LIBKSTAT})
endif(LIBKSTAT)
find_library(LIBMACH mach)
if(LIBMACH)
target_link_libraries(${PROJECT_NAME} ${LIBMACH})
endif(LIBMACH)
find_library(LIBBSD bsd REQUIRED)
if(LIBBSD)
target_link_libraries(${PROJECT_NAME} ${LIBBSD})
endif(LIBBSD)
# FreeBSD specific libraries
if(${MACHINE} STREQUAL freebsd)
find_library(LIBKVM kvm)
if(LIBKVM)
target_link_libraries(${PROJECT_NAME} ${LIBKVM})
endif(LIBKVM)
endif(${MACHINE} STREQUAL freebsd)
install(
PROGRAMS
${CMAKE_BINARY_DIR}/${PROJECT_NAME}
DESTINATION "bin"
)
install(
FILES
${CMAKE_BINARY_DIR}/pg_top.1
DESTINATION "share/man/man1"
)
# CPack rules for creating distribution files.
include(InstallRequiredSystemLibraries)
set(CPACK_CMAKE_GENERATOR "Unix Makefiles")
set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "pg_top is 'top' for PostgreSQL")
set(CPACK_PACKAGE_VENDOR "pg_top")
set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_SOURCE_DIR}/README.rst")
set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_SOURCE_DIR}/LICENSE")
set(CPACK_PACKAGE_VERSION_MAJOR "${PROJECT_VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${PROJECT_VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${PROJECT_VERSION_PATCH}")
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${PROJECT_VERSION}")
set(
CPACK_SOURCE_IGNORE_FILES
"\\\\.bz2\\$;\\\\.gz\\$;\\\\.xz\\$;\\\\.Z\\$;\\\\.zip\\$"
)
set(
CPACK_SOURCE_IGNORE_FILES
"${CPACK_SOURCE_IGNORE_FILES};/\\\\.gitignore\\$"
)
set(
CPACK_SOURCE_IGNORE_FILES
"${CPACK_SOURCE_IGNORE_FILES};/CMakeCache\\\\.txt\\$"
)
set(
CPACK_SOURCE_IGNORE_FILES
"${CPACK_SOURCE_IGNORE_FILES};/CMakeFiles/"
)
set(
CPACK_SOURCE_IGNORE_FILES
"${CPACK_SOURCE_IGNORE_FILES};/CMakeCache\\\\.txt\\$"
)
set(
CPACK_SOURCE_IGNORE_FILES
"${CPACK_SOURCE_IGNORE_FILES};/cmake_install\\\\.cmake\\$"
)
set(
CPACK_SOURCE_IGNORE_FILES
"${CPACK_SOURCE_IGNORE_FILES};/config\\\\.h\\$"
)
set(
CPACK_SOURCE_IGNORE_FILES
"${CPACK_SOURCE_IGNORE_FILES};/CPackConfig\\\\.cmake\\$"
)
set(
CPACK_SOURCE_IGNORE_FILES
"${CPACK_SOURCE_IGNORE_FILES};/\\\\_CPack\\\\_Packages/"
)
set(
CPACK_SOURCE_IGNORE_FILES
"${CPACK_SOURCE_IGNORE_FILES};/CPackSourceConfig\\\\.cmake\\$"
)
set(
CPACK_SOURCE_IGNORE_FILES
"${CPACK_SOURCE_IGNORE_FILES};/Makefile\\$"
)
set(
CPACK_SOURCE_IGNORE_FILES
"${CPACK_SOURCE_IGNORE_FILES};/pg_top\\\\.1\\$"
)
set(
CPACK_SOURCE_IGNORE_FILES
"${CPACK_SOURCE_IGNORE_FILES};/sigdesc\\\\.h\\$"
)
include(CPack)
set(OUTPUT "${PROJECT_VERSION}")
add_custom_target(appimage
COMMAND cp -aL ${CMAKE_SOURCE_DIR}/appimage/activate
${CMAKE_SOURCE_DIR}/appimage/AppRun
${CMAKE_SOURCE_DIR}/appimage/pg_top.desktop
${CMAKE_SOURCE_DIR}/appimage/pg_top.svg
AppDir
COMMAND mkdir -p AppDir/usr/share/metainfo
COMMAND cp -aL ${CMAKE_SOURCE_DIR}/appimage/pg_top.appdata.xml
AppDir/usr/share/metainfo
COMMAND mkdir -p AppDir/usr/lib
COMMAND cp -aL `ldd pg_top | grep libpq\\\\\\\\. | cut -d \" \" -f 3`
`ldd pg_top | grep libtinfo\\\\\\\\. | cut -d \" \" -f 3`
`ldd pg_top | grep libbsd\\\\\\\\. | cut -d \" \" -f 3`
`ldd pg_top | grep libssl\\\\\\\\. | cut -d \" \" -f 3`
`ldd pg_top | grep libcrypto\\\\\\\\. | cut -d \" \" -f 3`
`ldd pg_top | grep libgssapi_krb5\\\\\\\\. | cut -d \" \" -f 3`
`ldd pg_top | grep libkrb5\\\\\\\\. | cut -d \" \" -f 3`
`ldd pg_top | grep libk5crypto\\\\\\\\. | cut -d \" \" -f 3`
`ldd pg_top | grep libkrb5support\\\\\\\\. | cut -d \" \" -f 3`
`ldd pg_top | grep libkeyutils\\\\\\\\. | cut -d \" \" -f 3`
`ldd pg_top | grep libselinux\\\\\\\\. | cut -d \" \" -f 3`
AppDir/usr/lib
COMMAND VERSION=${PROJECT_VERSION} /usr/local/squashfs-root/AppRun
--comp=xz AppDir
)
07070100000003000081A400000000000000000000000166622DE8000009E1000000000000000000000000000000000000002400000000pg_top-4.1.1/Containerfile.appimage# Build an environment for creating an AppImage.
# Use a distro with an old libc to maximize support on as many linux distros
# and architectures as possible.
FROM ubuntu:14.04
ARG ARCH
RUN apt-get -qq -y update && \
apt-get -qq -y dist-upgrade && \
apt-get -qq -y install bison \
bzip2 \
curl \
file \
flex \
gcc \
libc6-dev \
libbsd-dev \
libncurses5-dev \
make \
wget \
xz-utils && \
apt-get -qq -y clean && \
apt-get -qq -y autoclean
WORKDIR /usr/local
ARG APPIMAGEPATH="AppImage/AppImageKit/releases/download/13"
ARG APPIMAGEFILENAME="appimagetool-${ARCH}.AppImage"
ARG APPIMAGEURL="https://github.com/${APPIMAGEPATH}/${APPIMAGEFILENAME}"
RUN curl -OL ${APPIMAGEURL} && \
chmod +x appimagetool-${ARCH}.AppImage && \
./appimagetool-${ARCH}.AppImage --appimage-extract && \
chmod 0755 squashfs-root && \
chmod 0755 squashfs-root/usr && \
chmod 0755 squashfs-root/usr/bin && \
chmod 0755 squashfs-root/usr/lib && \
chmod 0755 squashfs-root/usr/lib/appimagekit && \
chmod 0755 squashfs-root/usr/share
ARG OPENSSLVER="1.1.1s"
RUN wget -O /tmp/openssl-${OPENSSLVER}.tar.gz -q --no-check-certificate \
https://www.openssl.org/source/openssl-${OPENSSLVER}.tar.gz && \
tar -C /usr/local/src -xf /tmp/openssl-${OPENSSLVER}.tar.gz
WORKDIR /usr/local/src/openssl-${OPENSSLVER}
RUN ./config -fPIC shared && \
make -s -j$(nproc) && \
make -s install
# PostgreSQL 10 is the first release supporting multiple hosts in the
# connection string.
ARG PGVER="10.23"
ARG PGFILENAME="postgresql-${PGVER}.tar.bz2"
ARG PGURL="https://ftp.postgresql.org/pub/source/v${PGVER}/${PGFILENAME}"
RUN wget -O /tmp/postgresql-${PGVER}.tar.bz2 -q --no-check-certificate \
${PGURL}
RUN tar -C /usr/local/src -xf /tmp/postgresql-${PGVER}.tar.bz2
WORKDIR /usr/local/src/postgresql-${PGVER}
RUN ./configure --silent --without-ldap --without-readline --without-zlib \
--without-gssapi --with-openssl --prefix=/usr && \
make -s -j $(nproc) install && \
ldconfig
ARG CMAKEVER="3.1.3"
RUN curl -o /tmp/cmake-${CMAKEVER}-linux-${ARCH}.tar.gz -SsOL \
https://github.com/Kitware/CMake/releases/download/v${CMAKEVER}/cmake-${CMAKEVER}-linux-${ARCH}.tar.gz && \
tar -C /usr --strip-components=1 \
-xf /tmp/cmake-${CMAKEVER}-linux-${ARCH}.tar.gz
07070100000004000081A400000000000000000000000166622DE800000F44000000000000000000000000000000000000001900000000pg_top-4.1.1/HISTORY.rstRelease Notes
=============
2024-06-06 v4.1.1
-----------------
* Fix remote query display to query in a single line
* Remove requirement for unused libelf
2023-07-01 v4.1.0
-----------------
* Set CMake minimum version to 2.8.12
* Add container files for creating an AppImage using AppImageKit v13
* Update AppImage notes
* Fix string truncation warnings in byte and kilobyte pretty functions
* Replace reallocarray() with POSIX realloc()
* Fix escaping in CMakeLists.txt AppImage target
* Removed building of sigdesc.h, since kill commands was removed in v4.0.0
* Fix CMake handling of building getopt.c when not found
* Bump CMake minimum to 3.1.0
2020-08-05 v4.0.0
-----------------
* Replace autoconf with cmake
* Remove table stats monitoring, use pg_systat instead
https://pg_systat.gitlab.io/
* Remove index stats monitoring, use pg_systat instead
https://pg_systat.gitlab.io/
* Remove kill and renice command, and stop displaying nice priority
* Show backend state instead of operating system state
* Show database username instead of operating system username
* Make connections with password persistent and clear connection memory in
these scenarios
* Fix sorting when specified from the command line and in interactive mode
* Add display for top replication processes using 'R' command line argument or
'R' command
* Add 'a' command to switch to top 'activity' processes, the default, while
removing the 'I' for the top I/O display as a toggle
* Show swapping activity
* Simplify top I/O display
* Remove toggle for display raw I/O statistics
* Fix bug for specifing number of displays to show on the command line without
-x flag
* Fix view of locks held by process. Separate table and index locks. Also
showing schema.
* Handle longer pids on Linux
* Updated for FreeBSD 12.
* Updated for OpenBSD 6.7.
2013-07-31 v3.7.0
-----------------
* Added support for monitoring databases on remote systems.
* Added support for monitoring i/o statistics on Linux.
* Updated for changed introduced in PostgreSQL 9.2.
* Updated for OpenBSD 5.2.
* Updated for FreeBSD 9.1.
* Updated for OS X Mountain Lion (10.8).
* Add monitoring for database activity
* Add monitoring for disk activity
* Add monitoring for disk space
* Add long options
2008-05-03 v3.6.2
-----------------
* Add 'A' command to re-run SQL statement and show actual execution plan
(EXPLAIN ANALYZE) of a running query.
* Fixed 'E' command (EXPLAIN) to be UPDATE and INSERT safe.
* Updated the automake file so other targets like 'make dist' and 'make
distdir' work.
* Fixed a bug so user table statistics can be sorted.
* Added a 't' command so that user table and index statistics can display
either cumulative or differential statistics.
* Fixed support for OS X, tested on v10.4.x, v10.5.x.
* Added support for OpenBSD, tested on v4.2.
* Rename 'ptop' to 'pg_top' to fit PostgreSQL naming conventions and avoid
naming conflict with free pascal's source formatter 'ptop'.
* Recognize PGDATABASE, PGHOST, PGUSER, and PGPORT environment variables.
2008-03-05 v3.6.1
-----------------
* Add -h command line option to specify a socket file when connected to the
database..
* Use the same -p PORT, -U USER, and -d DBNAME options as other PostgreSQL
programs.
* Change unixtop's original -d to -x, and -U to -z.
* Add 'X' command to view user index statistics.
* Add 'R' command to view user table statistics.
* Add support for Solaris 10.
* Add support for FreeBSD.
* Add 'E' command to re-determine and show execution plan of a running SQL
statement.
* Add parameters to specify database connection information.
* Add 'L' command to show locks held by a process.
* Add 'Q' command to show current query of a process.
* Rename 'top' to 'ptop'.
* Add support for Linux.
* Configure support for PostgreSQL libpq client libraries.
* Remove old_modules directory.
* Update RES calculation for Linux 2.6.x.
07070100000005000081A400000000000000000000000166622DE8000002D1000000000000000000000000000000000000001900000000pg_top-4.1.1/INSTALL.rstpg_top
======
Installation
------------
Configuring
~~~~~~~~~~~
::
cmake [options] CMakeLists.txt
options:
-DCMAKE_INSTALL_PREFIX=PREFIX Install files in PREFIX. Default is
'/usr/local'.
-DENABLE_COLOR=0 Default on. Include code that allows for the
use of color in the output display. Use
-DENABLE_COLOR=0 if you do not want this
feature compiled in to the code. The configure
script also recognizes the spelling "colour".
Installing
~~~~~~~~~~
::
make install
Uninstalling
~~~~~~~~~~~~
::
xargs rm < install_manifest.txt
07070100000006000081A400000000000000000000000166622DE8000005EC000000000000000000000000000000000000001500000000pg_top-4.1.1/LICENSECopyright (c) 2005, William LeFebvre
Copyright (c) 2007-2013, Mark Wong
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer. Redistributions in binary
* form must reproduce the above copyright notice, this list of conditions and
* the following disclaimer in the documentation and/or other materials provided
* with the distribution. Neither the name of Mark Wong nor the names
* of its contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
07070100000007000081A400000000000000000000000166622DE8000002AD000000000000000000000000000000000000001C00000000pg_top-4.1.1/Makefile.cmake.PHONY: appimage clean default debug package release
default:
@echo "targets: appimage (Linux only), clean, debug, package, release"
appimage:
cmake -H. -Bbuilds/appimage -DCMAKE_INSTALL_PREFIX=/usr
cd builds/appimage && make
cd builds/appimage && sed -i -e 's#/usr#././#g' pg_top
cd builds/appimage && make install DESTDIR=AppDir
cd builds/appimage && make appimage
clean:
-rm -rf builds
debug:
cmake -H. -Bbuilds/debug -DCMAKE_BUILD_TYPE=Debug
cd builds/debug && make
package:
git checkout-index --prefix=builds/source/ -a
cmake -Hbuilds/source -Bbuilds/source
cd builds/source && make package_source
release:
cmake -H. -Bbuilds/release
cd builds/release && make
07070100000008000081A400000000000000000000000166622DE80000213B000000000000000000000000000000000000001500000000pg_top-4.1.1/PortingInstructions for porting pg_top to other architectures.
This is still a preliminary document. Suggestions for improvement are
most welcome.
Before you embark on a port, please send me a mail message telling me
what platform you are porting pg_top to. There are three reasons for
this: (1) I may already have a port, (2) module naming needs to be
centralized, (3) I want to loosely track the various porting efforts.
You do not need to wait for an "okay", but I do want to know that you
are working on it. And of course, once it is finished, please send me
the module files so that I can add them to the main distribution!
----------
There is one set of functions which extract all the information that
pg_top needs for display. These functions are collected in to one file.
To make pg_top work on a different architecture simply requires a
different implementation of these functions. The functions for a
given architecture "foo" are stored in a file called "m_foo.c". The
Configure script looks for these files and lets the configurer choose
one of them. This file is called a "module". The idea is that making
pg_top work on a different machine only requires one additional file and
does not require changes to any existing files.
A module template is included in the distribution, called "m-template".
To write your own module, it is a good idea to start with this template.
If you architecture is similar to one for which a module already
exists, then you can start with that module instead. If you do so,
remember to change the "AUTHOR" section at the top!
The first comment in a module contains information which is extracted
and used by Configure. This information is marked with words in all
capitals (such as "SYNOPSIS:" and "LIBS:"). Go look at m-template: it
is fairly self-explanatory. The text after "LIBS:" (on the same line)
is extracted and included in the LIBS definition of the Makefile so
that extra libraries which may be necessary on some machines (such as
"-lkvm") can be specified in the module. The text after "CFLAGS:"
(on the same line) is extracted and included as flags in the "CFLAGS"
definition of the Makefile (thus in every compilation step). This is
used for rare circumstances only: please don't abuse this hook.
Some operating systems have idiosyncrasies which will affect the form
and/or content of the information pg_top displays. You may wish to
document such anomalies in the pg_top man page. This can be done by adding
a file called m_{modulename}.man (where {modulename} is replaced with
the name of the module). Configure will automatically add this file to
the end of the man page. See m_sunos4.man for an example.
A module is concerned with two structures:
The statics struct is filled in by machine_init. Each item is a
pointer to a list of character pointers. The list is terminated
with a null pointer.
struct statics
{
char **procstate_names; /* process state names */
char **cpustate_names; /* cpu state names */
char **memory_names; /* memory information names */
};
The system_info struct is filled in by get_system_info and
get_process_info.
struct system_info
{
int last_pid; /* last pid assigned (0 means non-sequential assignment) */
double load_avg[NUM_AVERAGES]; /* see below */
int p_total; /* total number of processes */
int p_active; /* number of procs considered "active" */
int *procstates; /* array of process state counters */
int *cpustates; /* array of cpustate counters */
int *memory; /* memory information */
};
The io_info struct is filled in by get_io_info.
struct io_info {
int64_t reads;
int64_t readsectors;
int64_t writes;
int64_t writesectors;
};
The last three pointers each point to an array of integers. The
length of the array is determined by the length of the corresponding
_names array in the statics structure. Furthermore, if an entry in a
_names array is the empty string ("") then the corresponding value in
the value array will be skipped over. The display routine displays,
for example, the string procstate_names[0] then the number
procstates[0], then procstate_names[1], procstates[1], etc. until
procstate_names[N] == NULL. This allows for a tremendous amount of
flexibility in labeling the displayed values.
"procstates" and "memory" are displayed as straight integer values.
Values in "cpustates" are displayed as a percentage * 10. For
example, the (integer) value 105 is displayed as 10.5%.
These routines must be defined by the machine dependent module.
int machine_init(struct statics *)
returns 0 on success and -1 on failure,
prints error messages
char *format_header(char *)
Returns a string which should be used as the header for the
process display area. The argument is a string used to label
the username column (either "USERNAME" or "UID") and is always
8 characters in length.
void get_system_info(struct system_info *)
caddr_t get_process_info(struct system_info *, int, int, int (*func)())
returns a handle to use with format_next_process
char *format_next_process(caddr_t, char *(*func)())
returns string which describes next process
int proc_compare(caddr_t, caddr_t)
qsort comparison function
void get_io_info(struct io_info *)
get_process_info is called immediately after get_system_info. In
fact, the two functions could be rolled in to one. The reason they
are not is mostly historical.
The file "machine.h" contains definitions which are useful to modules
and to pg_top.c (such as the structure definitions). You SHOULD NOT need
to change it when porting to a new platform.
Porting to a new platform should NOT require any changes to existing
files. You should only need to add m_ files. If you feel you need a
change in one of the existing files, please contact me so that we can
discuss the details. I want to keep such changes as general as
possible.
--------
Changes were made to the module interface between 3.5 and 3.6. Here are
the changes that need to be made to port a 3.5 module to 3.6:
The array that stores memory statistics and is passed back in the system
information structure as "memory" must now be an array of (signed) longs.
This was done to more easily accomodate systems that have gigabytes of
memory. Since the numbers are supposed to be kilobytes, a long can still
represent up to 2 terabytes. Look for "int memory_stats[X]" (where "X"
is some arbitrary number) and change it to "long memory_stats[X]". If
the module support reporting swap information on a separate line, then
its "swap_stats" array also needs to be an array of longs.
The argument to proc_owner should be an int, as in "int pid". When it is
used in proc_owner it should be cast as necessary. Many operating systems
will require it to be cast to a pid_t before being compared to the appropriate
element in the proc structure.
In the function format_next_process, the last argument in the main call
to sprintf is the string that contains the command for the process.
Make sure that this last argument is enclosed in a call to "printable".
For example: "printable(MPP(pp, p_comm))".
The third argument to "get_process_info" needs to be changed to an integer,
typically "int compare_index". The call to qsort in get_process_info may
be guarded by "if (compare != NULL)". If it is, remove the if statement.
The other changes to get_process_info depends on whether or not the module
supports multiple sort orders.
To support multiple keys:
Create an array int (*proc_compares[])() and assign to it the list of
comparison functions, NULL terminated. For example:
int (*proc_compares[])() = {
compare_cpu,
compare_size,
compare_res,
compare_time,
NULL };
In get_process_info there is a call to qsort which uses one of the
functions in proc_compares. It should be changed so that its fourth
argument is "proc_compares[compare_index]".
If the module contains the function "proc_compare", it should be removed.
There should also be a NULL-terminated array of strings which list the names
for the sort keys, for example:
char *ordernames[] =
{"cpu", "size", "res", "time", NULL};
To indicate that this module supports multiple sort keys, add the following
line in machine_init:
statics->order_names = ordernames;
If there is no support for multiple keys:
Leave statics->order_names alone and call the comparison function of
your choice in get_process_info, ignoring the third argument.
07070100000009000081A400000000000000000000000166622DE800000583000000000000000000000000000000000000002100000000pg_top-4.1.1/README.appimage.rstAppImages are only for Linux based systems:
https://appimage.org/
The AppImageKit AppImage can be downloaded from:
https://github.com/AppImage/AppImageKit/releases
It is recommended to build AppImages on older distributions:
https://docs.appimage.org/introduction/concepts.html#build-on-old-systems-run-on-newer-systems
At the time of this document, CentOS 7 is the one of the oldest supported Linux
distributions with the oldest libc version.
Use a custom configured PostgreSQL build with minimal options enabled to reduce
library dependency support. Part of this reason is to make it easier to
include libraries with compatible licences. Part of the reason is to be able
to build libpq with minimal development dependencies for PostgreSQL, since the
purpose is just to build a pg_top AppImage. At least version PostgreSQL 10
should be used so that PGHOST can be defined in the AppRun script to search
multiple potential unixsock directories if no PGHOST is defined in the user's
environment.
At the time of this document, PostgreSQL 10 was configured with the following
options::
./configure --without-ldap --without-readline --without-zlib \
--without-gssapi --with-openssl
Don't forget you may have to set both PATH and LD_LIBRARY_PATH appropriately
depending on where the custom build of PostgreSQL is installed.
See the README.rst in the container/ directory for an example.
0707010000000A000081A400000000000000000000000166622DE80000067D000000000000000000000000000000000000001800000000pg_top-4.1.1/README.rstpg_top
======
pg_top is 'top' for PostgreSQL. It is derived from Unix Top. Similar to top,
pg_top allows you to monitor PostgreSQL processes. It also allows you to:
* View currently running SQL statement of a process.
* View query plan of a currently running SELECT statement.
* View locks held by a process.
* View I/O statistics per process.
* View replication statistics for downstream nodes.
To compile and install "pg_top", read the file "INSTALL.rst" and follow the
directions and advice contained therein.
If you make any kind of change to "pg_top" that you feel would be
beneficial to others who use this program, or if you find and fix a bug,
please submit the change to the pg_top issue tracker:
https://gitlab.com/pg_top/pg_top/issues
In order to monitor a remote database, the pg_proctab extension needs to be
created on the database to be monitored. Any operating system that pg_proctab
supports can be monitored remotely on any operating system. See details for
pg_proctab here:
https://gitlab.com/pg_proctab/pg_proctab
Availability
------------
Project home page:
https://pg_top.gitlab.io/
If you have git, you can download the source code::
git clone git@gitlab.com:pg_top/pg_top.git
Logo
----
The logo is just a pipe (|) and a capital V using the free Ampad Brush true
type font.
Gratitude
---------
Selena Deckelmann & Gabrielle Roth, and the beer & free wi-fi at County Cork
pub in Portland, OR, USA.
License
-------
pg_top is distributed free of charge under the same terms as the BSD
license. For an official statement, please refer to the file "LICENSE"
which should be included with the source distribution.
0707010000000B000081A400000000000000000000000166622DE80000034D000000000000000000000000000000000000001200000000pg_top-4.1.1/TODO* Test on more platforms. Development has primarily been on Linux and some
attempts have been made on FreeBSD, OpenBSD, and OS X.
* Display status in the executor using pstack/dtract/gstack.
* Display elapsed time of currently running query.
* Display SQL statement type currently executing, i.e. SELECT, UPDATE,
VACUUM, etc.
* Display summary statistics for average query cpu time, average query elapsed
time, etc.
* Display summary statistics for locks such as total number of granted locks,
total number of ungranted locks, etc.
* Make new TPS and i/o activity and disk space usage work on remote
connections.
* Add warning when user running pg_top does not have OS privileges to view i/o
statistics. /proc/*/io may only be readable by process owner.
* Add warning when user i/o stats are not avaialble from OS. (Linux)
0707010000000C000081A400000000000000000000000166622DE8000004CD000000000000000000000000000000000000001100000000pg_top-4.1.1/Y2Kpg_top and the Year 2000
The software package pg_top will not be affected by years numbering
between 2000 and 2037. No portion of the pg_top code stores dates on
disk. All date processing in pg_top is performed with functions from the
Unix C library and Unix kernel. The specific functions are: time(2)
and ctime(3S). These functions deal exclusively with conventional
Unix time values (number of seconds since Midnight January 1, 1970
GMT) and produce strings with a 4-digit year. At no point in the code
for pg_top are the last two digits used to represent a year.
Top and the Year 2038
In the year 2038 pg_top will fail to represent the time of day correctly
on 32-bit Unix operating systems. This is due to a limitation in the
way Unix represents time. Top will only work on systems whose kernel
call "time" and C library call "ctime" have been adjusted to represent
time with a value greater than 32 bits. The exact date and time of
this failure is 3:14:08 January 19, 2038 GMT. Note that this failure
will only affect the display of the current time in the output from
pg_top.
THERE IS ABSOLUTELY NO WARRANTY PROVIDED WITH THIS SOFTWARE.
Please see the contents of the file "LICENSE" for further
information.
0707010000000D000041ED00000000000000000000000266622DE800000000000000000000000000000000000000000000001600000000pg_top-4.1.1/appimage0707010000000E000081ED00000000000000000000000166622DE8000000B1000000000000000000000000000000000000001D00000000pg_top-4.1.1/appimage/AppRun#!/bin/sh
export LD_LIBRARY_PATH="${APPDIR}/usr/lib:${LD_LIBRARY_PATH}"
if [ "x$PGHOST" = "x" ]; then
export PGHOST="/tmp,/var/run/postgresql"
fi
${APPDIR}/usr/bin/pg_top $@
0707010000000F000081A400000000000000000000000166622DE8000003E4000000000000000000000000000000000000001F00000000pg_top-4.1.1/appimage/activateif [ "${BASH}" = "" ]; then
echo "sourcing activate currently requires a bash shell"
return
fi
APPIMAGE_NAME="pg_top"
deactivate()
{
PATH="${APPIMAGE_OLD_PATH}"
export PATH
unset APPIMAGE_OLD_PATH
if [ "${APPIMAGE_OLD_LD_LIBRARY_PATH}" = "" ]; then
unset LD_LIBRARY_PATH
else
LD_LIBRARY_PATH="${APPIMAGE_OLD_LD_LIBRARY_PATH}"
export LD_LIBRARY_PATH
fi
unset APPIMAGE_OLD_LD_LIBRARY_PATH
PS1="${APPIMAGE_OLD_PS1}"
export PS1
unset APPIMAGE_OLD_PS1
}
APPIMAGE_ENV="$(dirname "$(realpath "${BASH_SOURCE[0]}")")"
APPIMAGE_OLD_PS1="${PS1}"
export APPIMAGE_OLD_PS1
APPIMAGE_OLD_PATH="${PATH}"
export APPIMAGE_OLD_PATH
APPIMAGE_OLD_LD_LIBRARY_PATH="${LD_LIBRARY_PATH}"
export APPIMAGE_OLD_LD_LIBRARY_PATH
PATH="${APPIMAGE_ENV}/usr/bin:${PATH}"
export PATH
if [ "${LD_LIBRARY_PATH}" = "" ]; then
LD_LIBRARY_PATH="${APPIMAGE_ENV}/usr/lib"
else
LD_LIBRARY_PATH="${APPIMAGE_ENV}/usr/lib:${LD_LIBRARY_PATH}"
fi
export LD_LIBRARY_PATH
PS1="<${APPIMAGE_NAME}> ${PS1}"
export PS1
07070100000010000081A400000000000000000000000166622DE800000406000000000000000000000000000000000000002900000000pg_top-4.1.1/appimage/pg_top.appdata.xml<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2019 PostgreSQL Global Development Group-->
<component type="desktop-application">
<id>org.postgresql.pg_top</id>
<metadata_license>PostgreSQL</metadata_license>
<project_license>PostgreSQL</project_license>
<name>pg_top</name>
<summary>'top' for PostgreSQL</summary>
<description>
<p>
pg_top is a terminal program used to view top PostgreSQL processes.
</p>
</description>
<launchable type="desktop-id">org.postgresql.pg_top</launchable>
<screenshots>
<screenshot type="default">
<caption>Screenshot</caption>
<image>https://pg_top.gitlab.io/pg_top-2.png</image>
</screenshot>
</screenshots>
<url type="homepage">https://pg_top.gitlab.io/</url>
<project_group>PostgreSQL</project_group>
<provides>
<binary>pg_top</binary>
</provides>
<releases>
<release version="4.0.0" date="2020-10-8">
<description>
<p>4.0.0 release.</p>
</description>
</release>
</releases>
</component>
07070100000011000081A400000000000000000000000166622DE800000070000000000000000000000000000000000000002500000000pg_top-4.1.1/appimage/pg_top.desktop[Desktop Entry]
Name=pg_top
Exec=pg_top
Icon=pg_top
Type=Application
Categories=Utility;Database;
Terminal=true
07070100000012000081A400000000000000000000000166622DE800000B87000000000000000000000000000000000000002100000000pg_top-4.1.1/appimage/pg_top.svg<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="512"
height="512"
viewBox="0 0 135.46666 135.46666"
version="1.1"
id="svg8"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
sodipodi:docname="pg_top.svg"
inkscape:export-filename="pg_top.png"
inkscape:export-xdpi="96.000008"
inkscape:export-ydpi="96.000008">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#336791"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="1"
inkscape:pageshadow="2"
inkscape:zoom="1.6679688"
inkscape:cx="151.38174"
inkscape:cy="256"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
inkscape:pagecheckerboard="true"
inkscape:window-width="2554"
inkscape:window-height="1028"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="0" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-161.53335)">
<flowRoot
xml:space="preserve"
id="flowRoot3713"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="matrix(0.52916751,0,0,0.52916652,-13.638613,165.01507)"><flowRegion
id="flowRegion3715"><rect
id="rect3717"
width="197.2459"
height="185.25526"
x="55.15691"
y="68.946136" /></flowRegion><flowPara
id="flowPara3719"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:192px;font-family:'Ampad Brush';-inkscape-font-specification:'Ampad Brush';text-align:center;text-anchor:middle;fill:#ffffff">|</flowPara><flowPara
id="flowPara3721"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:192px;font-family:'Ampad Brush';-inkscape-font-specification:'Ampad Brush';text-align:center;text-anchor:middle;fill:#ffffff">V</flowPara></flowRoot> </g>
</svg>
07070100000013000081A400000000000000000000000166622DE8000000C7000000000000000000000000000000000000001700000000pg_top-4.1.1/boolean.h#ifndef _BOOLEAN_H_
#define _BOOLEAN_H_
/* My favorite names for boolean values */
#define No 0
#define Yes 1
#define Maybe 2 /* tri-state boolean, actually */
#endif /* _BOOLEAN_H_ */
07070100000014000081A400000000000000000000000166622DE8000009DE000000000000000000000000000000000000001100000000pg_top-4.1.1/c.h/*-------------------------------------------------------------------------
*
* c.h
* Fundamental C definitions. This is included by every .c file in
* PostgreSQL (via either postgres.h or postgres_fe.h, as appropriate).
*
* Note that the definitions here are not intended to be exposed to clients
* of the frontend interface libraries --- so we don't worry much about
* polluting the namespace with lots of stuff...
*
*
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/c.h,v 1.214.2.1 2007/01/11 02:40:12 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
*----------------------------------------------------------------
* TABLE OF CONTENTS
*
* When adding stuff to this file, please try to put stuff
* into the relevant section, or add new sections as appropriate.
*
* section description
* ------- ------------------------------------------------
* 2) bool, true, false, TRUE, FALSE, NULL
*
* NOTE: since this file is included by both frontend and backend modules, it's
* almost certainly wrong to put an "extern" declaration here. typedefs and
* macros are the kind of thing that might go here.
*
*----------------------------------------------------------------
*/
#ifndef C_H
#define C_H
/*
* We have to include stdlib.h here because it defines many of these macros
* on some platforms, and we only want our definitions used if stdlib.h doesn't
* have its own. The same goes for stddef and stdarg if present.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define _(x) gettext((x))
#ifdef ENABLE_NLS
#include <libintl.h>
#else
#define gettext(x) (x)
#endif
/* ----------------------------------------------------------------
* Section 2: bool, true, false, TRUE, FALSE, NULL
* ----------------------------------------------------------------
*/
/*
* bool
* Boolean value, either true or false.
*
* XXX for C++ compilers, we assume the compiler has a compatible
* built-in definition of bool.
*/
#ifndef __cplusplus
#ifndef bool
typedef char bool;
#endif
#ifndef true
#define true ((bool) 1)
#endif
#ifndef false
#define false ((bool) 0)
#endif
#endif /* not C++ */
typedef bool *BoolPtr;
#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif
/* /port compatibility functions */
#include "port.h"
#endif /* C_H */
07070100000015000081A400000000000000000000000166622DE800001796000000000000000000000000000000000000001500000000pg_top-4.1.1/color.c/*
* Top users/processes display for Unix
* Version 3
*
* This program may be freely redistributed,
* but this entire comment MUST remain intact.
*/
/*
* This file handles color definitions and access for augmenting
* the output with ansi color sequences.
*
* The definition of a color setting is as follows, separated by
* colons:
*
* tag=minimum,maximum#code
*
* "tag" is the name of the value to display with color.
*
* "minimum" and "maximum" are positive integer values defining a range:
* when the value is within this range it will be shown with the
* specified color. A missing value indicates that no check should be
* made (i.e.: ",25" is n <= 25; "25,50" is 25 <= n <= 50; and "50,"
* is 50 <= n).
*
* "code" is the ansi sequence that defines the color to use with the
* escape sequence "[m". Semi-colons are allowed in this string to
* combine attributes.
*/
#include "os.h"
#include "message.h"
#include "display.h"
typedef struct color_entry
{
char *tag;
int min;
int max;
char color;
struct color_entry *next;
struct color_entry *tagnext;
} color_entry;
static color_entry * entries = NULL;
static color_entry * *bytag = NULL;
static char **bytag_names = NULL;
static int totaltags = 0;
static int tagcnt = 0;
static char **color_ansi = NULL;
static int num_color_ansi = 0;
static int max_color_ansi = 0;
#define COLOR_ANSI_SLOTS 20
static int
color_slot(char *str)
{
int i;
for (i = 0; i < num_color_ansi; i++)
{
if (strcmp(color_ansi[i], str) == 0)
{
return i;
}
}
/* need a new slot */
if (num_color_ansi >= max_color_ansi)
{
max_color_ansi += COLOR_ANSI_SLOTS;
color_ansi = (char **) realloc(color_ansi, max_color_ansi * sizeof(char *));
}
color_ansi[num_color_ansi] = strdup(str);
return num_color_ansi++;
}
/*
* int color_env_parse(char *env)
*
* Parse a color specification "env" (such as one found in the environment) and
* add them to the list of entries. Always returns 0. Should only be called
* once.
*/
int
color_env_parse(char *env)
{
char *p;
char *min;
char *max;
char *str;
int len;
color_entry *ce;
/* initialization */
color_ansi = (char **) malloc(COLOR_ANSI_SLOTS * sizeof(char *));
max_color_ansi = COLOR_ANSI_SLOTS;
/* color slot 0 is always "0" */
color_slot("0");
if (env != NULL)
{
p = strtok(env, ":");
while (p != NULL)
{
if ((min = strchr(p, '=')) != NULL &&
(max = strchr(min, ',')) != NULL &&
(str = strchr(max, '#')) != NULL)
{
ce = (color_entry *) malloc(sizeof(color_entry));
len = min - p;
ce->tag = (char *) malloc(len + 1);
strncpy(ce->tag, p, len);
ce->tag[len] = '\0';
ce->min = atoi(++min);
ce->max = atoi(++max);
ce->color = color_slot(++str);
ce->next = entries;
entries = ce;
}
else
{
if (min != NULL)
{
len = min - p;
}
else
{
len = strlen(p);
}
display_error_message(" %.*s: bad color entry", len, p);
}
p = strtok(NULL, ":");
}
}
return 0;
}
/*
* int color_tag(char *tag)
*
* Declare "tag" as a color tag. Return a tag index to use when testing
* a valuse against the tests for this tag. Should not be called before
* color_env_parse.
*/
int
color_tag(char *tag)
{
color_entry *entryp;
color_entry *tp;
if (tag == NULL || *tag == '\0')
{
return -1;
}
if (bytag == NULL)
{
totaltags = 10;
bytag = (color_entry * *) malloc(totaltags * sizeof(color_entry *));
bytag_names = (char **) malloc(totaltags * sizeof(char *));
}
if (tagcnt >= totaltags)
{
totaltags *= 2;
bytag = (color_entry * *) realloc(bytag, totaltags * sizeof(color_entry *));
bytag_names = (char **) realloc(bytag_names, totaltags * sizeof(char *));
}
entryp = entries;
tp = NULL;
while (entryp != NULL)
{
if (strcmp(entryp->tag, tag) == 0)
{
entryp->tagnext = tp;
tp = entryp;
}
entryp = entryp->next;
}
bytag[tagcnt] = tp;
bytag_names[tagcnt] = strdup(tag);
return (tagcnt++);
}
/*
* int color_test(int tagidx, int value)
*
* Test "value" against tests for tag "tagidx", a number previously returned
* by color_tag. Return the correct color number to use when highlighting.
* If there is no match, return 0 (color 0).
*/
int
color_test(int tagidx, int value)
{
color_entry *ce;
/* sanity check */
if (tagidx < 0 || tagidx >= tagcnt)
{
return 0;
}
ce = bytag[tagidx];
while (ce != NULL)
{
if ((!ce->min || ce->min <= value) &&
(!ce->max || ce->max >= value))
{
return ce->color;
}
ce = ce->tagnext;
}
return 0;
}
/*
* char *color_set(int color)
*
* Return ANSI string to set the terminal for color number "color".
*/
char *
color_set(int color)
{
static char v[32];
v[0] = '\0';
if (color >=0 && color <num_color_ansi)
{
snprintf(v, sizeof(v), "\033[%sm", color_ansi[color]);
}
return v;
}
void
color_dump(FILE *f)
{
color_entry *ep;
int i;
int col;
int len;
fputs("These color tags are available:", f);
col = 81;
for (i = 0; i < tagcnt; i++)
{
len = strlen(bytag_names[i]) + 1;
if (len + col > 79)
{
fputs("\n ", f);
col = 2;
}
fprintf(f, " %s", bytag_names[i]);
col += len;
}
fputs("\n\nTop color settings:\n", f);
for (i = 0; i < tagcnt; i++)
{
ep = bytag[i];
while (ep != NULL)
{
fprintf(f, " %s (%d-", ep->tag, ep->min);
if (ep->max != 0)
{
fprintf(f, "%d", ep->max);
}
fprintf(f, "): ansi color %s, %sSample Text",
color_ansi[(int) ep->color],
color_set(ep->color));
fprintf(f, "%s\n", color_set(0));
ep = ep->tagnext;
}
}
}
void
color_debug(FILE *f)
{
color_entry *ep;
int i;
printf("color debug dump\n");
ep = entries;
while (ep != NULL)
{
printf("%s(%d,%d): slot %d, ansi %s, %sSample Text",
ep->tag, ep->min, ep->max, ep->color, color_ansi[(int) ep->color],
color_set(ep->color));
printf("%s\n", color_set(0));
ep = ep->next;
}
printf("\ntags:");
for (i = 0; i < tagcnt; i++)
{
printf(" %s", bytag_names[i]);
}
printf("\n");
}
07070100000016000081A400000000000000000000000166622DE80000034A000000000000000000000000000000000000001500000000pg_top-4.1.1/color.h/*
* Top - a top users display for Unix
*
* Definition of the color interface.
*/
#ifndef _COLOR_H_
#define _COLOR_H_
int color_env_parse(char *env);
int color_tag(char *tag);
int color_test(int tagidx, int value);
char *color_set(int color);
void color_dump(FILE *f);
/*
* These color tag names are currently in use
* (or reserved for future use):
*
* cpu, size, res, time, 1min, 5min, 15min, host
*/
/*
* Valid ANSI values for colors are:
*
* 0 Reset all attributes
* 1 Bright
* 2 Dim
* 4 Underscore
* 5 Blink
* 7 Reverse
* 8 Hidden
*
* Foreground Colours
* 30 Black
* 31 Red
* 32 Green
* 33 Yellow
* 34 Blue
* 35 Magenta
* 36 Cyan
* 37 White
*
* Background Colours
* 40 Black
* 41 Red
* 42 Green
* 43 Yellow
* 44 Blue
* 45 Magenta
* 46 Cyan
* 47 White
*/
#endif /* _COLOR_H_ */
07070100000017000081A400000000000000000000000166622DE800003BED000000000000000000000000000000000000001800000000pg_top-4.1.1/commands.c/*
* Top users/processes display for Unix
* Version 3
*
* This program may be freely redistributed,
* but this entire comment MUST remain intact.
*
* Copyright (c) 1984, 1989, William LeFebvre, Rice University
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
* Copyright (c) 2007-2019, Mark Wong
*/
/*
* This file contains the routines that implement some of the interactive
* mode commands. Note that some of the commands are implemented in-line
* in "main". This is necessary because they change the global state of
* "top" (i.e.: changing the number of processes to display).
*/
#include "os.h"
#include <ctype.h>
#include <signal.h>
#include <errno.h>
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#include <unistd.h>
#include "pg_top.h"
#include "boolean.h"
#include "utils.h"
#include "version.h"
#include "machine.h"
#include "help.h"
#include "display.h"
#include "pg.h"
#include "commands.h"
#include "screen.h"
extern int errno;
extern char *copyright;
/* imported from screen.c */
extern int overstrike;
extern int max_topn;
#define BEGIN "BEGIN;"
#define ROLLBACK "ROLLBACK;"
struct cmd cmd_map[] = {
{'\014', cmd_redraw},
{'#', cmd_number},
{' ', cmd_update},
{'?', cmd_help},
{'A', cmd_explain_analyze},
{'a', cmd_activity},
{'c', cmd_cmdline},
#ifdef ENABLE_COLOR
{'C', cmd_color},
#endif /* ENABLE_COLOR */
{'d', cmd_displays},
{'E', cmd_explain},
{'h', cmd_help},
{'i', cmd_idletog},
{'I', cmd_io},
{'L', cmd_locks},
{'n', cmd_number},
{'o', cmd_order},
{'q', cmd_quit},
{'R', cmd_replication},
{'Q', cmd_current_query},
{'s', cmd_delay},
{'u', cmd_user},
{'\0', NULL},
};
int
cmd_activity(struct pg_top_context *pgtctx)
{
pgtctx->mode = MODE_PROCESSES;
pgtctx->header_text =
pgtctx->header_options[pgtctx->mode_remote][pgtctx->mode];
reset_display(pgtctx);
return No;
}
#ifdef ENABLE_COLOR
int
cmd_color(struct pg_top_context *pgtctx)
{
reset_display(pgtctx);
if (pgtctx->color_on)
{
pgtctx->color_on = 0;
display_resize(); /* To realloc screenbuf */
new_message(MT_standout | MT_delayed, " Color off");
}
else
{
if (!smart_terminal)
{
new_message(MT_standout | MT_delayed,
" Sorry, cannot do colors on this terminal type");
}
else
{
pgtctx->color_on = 1;
new_message(MT_standout | MT_delayed, " Color on");
}
}
return No;
}
#endif /* ENABLE_COLOR */
int
cmd_cmdline(struct pg_top_context *pgtctx)
{
if (pgtctx->statics.flags.fullcmds)
{
pgtctx->ps.fullcmd = (pgtctx->ps.fullcmd + 1) % 3;
switch (pgtctx->ps.fullcmd)
{
case 2:
new_message(MT_standout | MT_delayed, " Displaying current query.");
break;
case 1:
new_message(MT_standout | MT_delayed,
" Displaying full command lines.");
break;
case 0:
default:
new_message(MT_standout | MT_delayed,
" Not displaying full command lines.");
}
}
else
{
new_message(MT_standout, " Full command display not supported.");
/* no_command = Yes; */
}
putchar('\r');
return No;
}
int
cmd_current_query(struct pg_top_context *pgtctx)
{
int newval;
char tempbuf1[50];
new_message(MT_standout, "Current query of process: ");
newval = readline(tempbuf1, 8, Yes);
reset_display(pgtctx);
display_pagerstart();
show_current_query(&pgtctx->conninfo, newval);
display_pagerend();
return No;
}
int
cmd_delay(struct pg_top_context *pgtctx)
{
int i;
char tempbuf[50];
new_message(MT_standout, "Seconds to delay: ");
if ((i = readline(tempbuf, 8, Yes)) > -1)
{
if ((pgtctx->delay = i) == 0 && getuid() != 0)
{
pgtctx->delay = 1;
}
}
clear_message();
return No;
}
int
cmd_displays(struct pg_top_context *pgtctx)
{
int i;
char tempbuf[50];
new_message(MT_standout, "Displays to show (currently %s): ",
pgtctx->displays == -1 ? "infinite" : itoa(pgtctx->displays));
if ((i = readline(tempbuf, 10, Yes)) > 0)
{
pgtctx->displays = i;
}
else if (i == 0)
{
quit(0);
}
clear_message();
return No;
}
int
cmd_explain(struct pg_top_context *pgtctx)
{
int newval;
char tempbuf1[50];
new_message(MT_standout, "Re-determine execution plan: ");
newval = readline(tempbuf1, 8, Yes);
reset_display(pgtctx);
display_pagerstart();
show_explain(&pgtctx->conninfo, newval, EXPLAIN);
display_pagerend();
return No;
}
int
cmd_explain_analyze(struct pg_top_context *pgtctx)
{
int newval;
char tempbuf1[50];
new_message(MT_standout, "Re-run SQL for analysis: ");
newval = readline(tempbuf1, 8, Yes);
reset_display(pgtctx);
display_pagerstart();
show_explain(&pgtctx->conninfo, newval, EXPLAIN_ANALYZE);
display_pagerend();
return No;
}
int
cmd_help(struct pg_top_context *pgtctx)
{
reset_display(pgtctx);
display_pagerstart();
show_help(&pgtctx->statics);
display_pagerend();
return No;
}
int
cmd_idletog(struct pg_top_context *pgtctx)
{
pgtctx->ps.idle = !pgtctx->ps.idle;
new_message(MT_standout | MT_delayed, " %sisplaying idle processes.",
pgtctx->ps.idle ? "D" : "Not d");
putchar('\r');
return No;
}
int
cmd_io(struct pg_top_context *pgtctx)
{
pgtctx->mode = MODE_IO_STATS;
pgtctx->header_text =
pgtctx->header_options[pgtctx->mode_remote][pgtctx->mode];
reset_display(pgtctx);
return No;
}
int
cmd_locks(struct pg_top_context *pgtctx)
{
int newval;
char tempbuf1[50];
new_message(MT_standout, "Show locks held by process: ");
newval = readline(tempbuf1, 8, Yes);
reset_display(pgtctx);
display_pagerstart();
show_locks(&pgtctx->conninfo, newval);
display_pagerend();
return No;
}
int
cmd_number(struct pg_top_context *pgtctx)
{
int newval;
char tempbuf[50];
new_message(MT_standout, "Number of processes to show: ");
newval = readline(tempbuf, 8, Yes);
if (newval > -1)
{
if (newval > max_topn)
{
new_message(MT_standout | MT_delayed,
" This terminal can only display %d processes.",
max_topn);
putchar('\r');
}
if (newval == 0)
{
/* inhibit the header */
display_header(No);
}
else if (newval > pgtctx->topn && pgtctx->topn == 0)
{
/* redraw the header */
display_header(Yes);
pgtctx->d_header = i_header;
}
pgtctx->topn = newval;
}
return No;
}
int
cmd_quit(struct pg_top_context *pgtctx)
{
quit(0);
/* NOT REACHED */
return No;
}
int
cmd_replication(struct pg_top_context *pgtctx)
{
pgtctx->mode = MODE_REPLICATION;
pgtctx->header_text =
pgtctx->header_options[pgtctx->mode_remote][pgtctx->mode];
reset_display(pgtctx);
return No;
}
int
cmd_order(struct pg_top_context *pgtctx)
{
int i;
int no_command = No;
char tempbuf[50];
if (pgtctx->statics.order_names == NULL)
{
new_message(MT_standout, " Ordering not supported.");
putchar('\r');
no_command = Yes;
}
else
{
new_message(MT_standout, "Order to sort: ");
if (readline(tempbuf, sizeof(tempbuf), No) > 0)
{
i = string_index(tempbuf, pgtctx->statics.order_names);
if (i == -1)
{
new_message(MT_standout, " %s: unrecognized sorting order",
tempbuf);
no_command = Yes;
}
else
{
pgtctx->order_index = i;
}
putchar('\r');
}
else
{
clear_message();
}
}
return no_command;
}
int
cmd_order_cpu(struct pg_top_context *pgtctx)
{
int i;
if ((i = string_index("cpu", pgtctx->statics.order_names)) == -1)
{
new_message(MT_standout, " Unrecognized sorting order");
putchar('\r');
/* no_command = Yes; */
}
else
{
pgtctx->order_index = i;
}
return No;
}
int
cmd_order_mem(struct pg_top_context *pgtctx)
{
int i;
if ((i = string_index("size", pgtctx->statics.order_names)) == -1)
{
new_message(MT_standout, " Unrecognized sorting order");
putchar('\r');
return Yes;
}
else
{
pgtctx->order_index = i;
}
return No;
}
int
cmd_redraw(struct pg_top_context *pgtctx)
{
reset_display(pgtctx);
return No;
}
int
cmd_update(struct pg_top_context *pgtctx)
{
/* go home for visual feedback */
go_home();
fflush(stdout);
return No;
}
int
cmd_user(struct pg_top_context *pgtctx)
{
new_message(MT_standout, "Username to show: ");
if (readline(pgtctx->ps.usename, sizeof(pgtctx->ps.usename), No) > 0)
{
putchar('\r');
}
else
{
clear_message();
}
return No;
}
int
execute_command(struct pg_top_context *pgtctx, char ch)
{
struct cmd *cmap;
cmap = cmd_map;
while (cmap->func != NULL)
{
if (cmap->ch == ch)
{
return (cmap->func) (pgtctx);
}
++cmap;
}
return No;
}
/*
* str_adderr(str, len, err) - add an explanation of error "err" to
* the string "str".
*/
int
str_adderr(char *str, int len, int err)
{
register char *msg;
register int msglen;
msg = err == 0 ? "Not a number" : errmsg(err);
msglen = strlen(msg) + 2;
if (len <= msglen)
{
return (0);
}
(void) strcat(str, ": ");
(void) strcat(str, msg);
return (len - msglen);
}
/*
* str_addarg(str, len, arg, first) - add the string argument "arg" to
* the string "str". This is the first in the group when "first"
* is set (indicating that a comma should NOT be added to the front).
*/
int
str_addarg(char *str, int len, char *arg, int first)
{
register int arglen;
arglen = strlen(arg);
if (!first)
{
arglen += 2;
}
if (len <= arglen)
{
return (0);
}
if (!first)
{
(void) strcat(str, ", ");
}
(void) strcat(str, arg);
return (len - arglen);
}
/*
* show_help() - display the help screen; invoked in response to
* either 'h' or '?'.
*/
void
show_help(struct statics *stp)
{
static char *fullhelp;
char *p = NULL;
if (fullhelp == NULL)
{
/* set it up first time thru */
if (stp->order_names != NULL)
{
p = string_list(stp->order_names);
}
if (p == NULL)
{
p = "not supported";
}
fullhelp = (char *) malloc(strlen(help_text) + strlen(p) + 2);
sprintf(fullhelp, help_text, p);
}
display_pager("pg_top version ");
display_pager(version_string());
display_pager(", ");
display_pager(copyright);
display_pager("\n");
display_pager(fullhelp);
}
/*
* Utility routines that help with some of the commands.
*/
char *
next_field(char *str)
{
if ((str = strchr(str, ' ')) == NULL)
{
return (NULL);
}
*str = '\0';
while (*++str == ' ') /* loop */ ;
/* if there is nothing left of the string, return NULL */
/* This fix is dedicated to Greg Earle */
return (*str == '\0' ? NULL : str);
}
int
scanint(char *str, int *intp)
{
register int val = 0;
register char ch;
/* if there is nothing left of the string, flag it as an error */
/* This fix is dedicated to Greg Earle */
if (*str == '\0')
{
return (-1);
}
while ((ch = *str++) != '\0')
{
if (isdigit(ch))
{
val = val * 10 + (ch - '0');
}
else if (isspace(ch))
{
break;
}
else
{
return (-1);
}
}
*intp = val;
return (0);
}
void
show_current_query(struct pg_conninfo_ctx *conninfo, int procpid)
{
int i;
int rows;
char info[64];
PGresult *pgresult = NULL;
sprintf(info, "Current query for procpid %d:\n\n", procpid);
display_pager(info);
/* Get the currently running query. */
connect_to_db(conninfo);
if (conninfo->connection != NULL)
{
pgresult = pg_query(conninfo->connection, procpid);
rows = PQntuples(pgresult);
}
else
{
rows = 0;
}
for (i = 0; i < rows; i++)
{
display_pager(PQgetvalue(pgresult, i, 0));
}
display_pager("\n\n");
if (pgresult != NULL)
PQclear(pgresult);
disconnect_from_db(conninfo);
}
void
show_explain(struct pg_conninfo_ctx *conninfo, int procpid, int analyze)
{
int i,
j;
int rows,
r;
char sql[4096];
char info[1024];
PGresult *pgresult_query = NULL;
PGresult *pgresult_explain = NULL;
sprintf(info,
"Current query plan for procpid %d:\n\n Statement:\n\n",
procpid);
display_pager(info);
/* Get the currently running query. */
connect_to_db(conninfo);
if (conninfo->connection != NULL)
{
pgresult_query = pg_query(conninfo->connection, procpid);
rows = PQntuples(pgresult_query);
}
else
{
rows = 0;
}
for (i = 0; i < rows; i++)
{
/* Display the query before the query plan. */
display_pager(PQgetvalue(pgresult_query, i, 0));
/* Execute the EXPLAIN. */
if (analyze == EXPLAIN_ANALYZE)
{
sprintf(sql, "EXPLAIN (ANALYZE, VERBOSE, BUFFERS)\n%s",
PQgetvalue(pgresult_query, i, 0));
}
else
{
sprintf(sql, "EXPLAIN\n%s", PQgetvalue(pgresult_query, i, 0));
}
PQexec(conninfo->connection, BEGIN);
pgresult_explain = PQexec(conninfo->connection, sql);
PQexec(conninfo->connection, ROLLBACK);
r = PQntuples(pgresult_explain);
/* This will display an error if the EXPLAIN fails. */
display_pager("\n\nQuery Plan:\n\n");
display_pager(PQresultErrorMessage(pgresult_explain));
for (j = 0; j < r; j++)
{
display_pager(PQgetvalue(pgresult_explain, j, 0));
display_pager("\n");
}
if (pgresult_explain != NULL)
PQclear(pgresult_explain);
}
display_pager("\n\n");
if (pgresult_query != NULL)
PQclear(pgresult_query);
disconnect_from_db(conninfo);
}
void
show_locks(struct pg_conninfo_ctx *conninfo, int procpid)
{
int i,
j,
k;
int rows;
char info[64];
int width[7] = {1, 8, 6, 5, 5, 4, 7};
PGresult *pgresult = NULL;
char header_format[1024];
char line_format[1024];
char prefix[21]; /* Should hold any 64 bit integer. */
char line[1024];
sprintf(info, "Locks held by procpid %d:\n\n", procpid);
display_pager(info);
/* Get the locks helf by the process. */
connect_to_db(conninfo);
if (conninfo->connection == NULL)
{
disconnect_from_db(conninfo);
return;
}
pgresult = pg_locks(conninfo->connection, procpid);
rows = PQntuples(pgresult);
/* Determine column sizes. */
sprintf(prefix, "%d", rows);
width[0] = strlen(prefix);
for (i = 0; i < rows; i++)
{
if (strlen(PQgetvalue(pgresult, i, 0)) > width[1])
width[1] = strlen(PQgetvalue(pgresult, i, 0));
if (strlen(PQgetvalue(pgresult, i, 1)) > width[2])
width[2] = strlen(PQgetvalue(pgresult, i, 1));
if (strlen(PQgetvalue(pgresult, i, 2)) > width[3])
width[3] = strlen(PQgetvalue(pgresult, i, 2));
if (strlen(PQgetvalue(pgresult, i, 3)) > width[4])
width[4] = strlen(PQgetvalue(pgresult, i, 3));
if (strlen(PQgetvalue(pgresult, i, 4)) > width[5])
width[5] = strlen(PQgetvalue(pgresult, i, 4));
if (strlen(PQgetvalue(pgresult, i, 5)) > width[6])
width[6] = strlen(PQgetvalue(pgresult, i, 5));
}
sprintf(header_format,
"%%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds\n",
width[0], width[1], width[2], width[3], width[4], width[5],
width[6]);
sprintf(line_format,
"%%%dd | %%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds\n",
width[0], width[1], width[2], width[3], width[4], width[5],
width[6]);
/* Display the header. */
sprintf(line, header_format, "", "database", "schema", "table", "index",
"type", "granted");
display_pager(line);
for (i = 0, k = 0; i < 7; i++)
{
for (j = 0; j < width[i]; j++, k++)
{
line[k] = '-';
}
line[k++] = '-';
line[k++] = '+';
line[k++] = '-';
}
line[k - 3] = '\n';
line[k - 2] = '\0';
display_pager(line);
/* Display data. */
for (i = 0; i < rows; i++)
{
sprintf(line, line_format, i + 1, PQgetvalue(pgresult, i, 0),
PQgetvalue(pgresult, i, 1), PQgetvalue(pgresult, i, 2),
PQgetvalue(pgresult, i, 3), PQgetvalue(pgresult, i, 4),
PQgetvalue(pgresult, i, 5));
display_pager(line);
}
display_pager("\n");
PQclear(pgresult);
disconnect_from_db(conninfo);
}
07070100000018000081A400000000000000000000000166622DE80000061B000000000000000000000000000000000000001800000000pg_top-4.1.1/commands.h/*
* call specifications for commands.c
*
* Copyright (c) 2007-2019, Mark Wong
*/
#ifndef _COMMANDS_H_
#define _COMMANDS_H_
#include "pg_top.h"
struct cmd
{
int ch;
int (*func) (struct pg_top_context *);
};
#define EXPLAIN 0
#define EXPLAIN_ANALYZE 1
int cmd_activity(struct pg_top_context *);
#ifdef ENABLE_COLOR
int cmd_color(struct pg_top_context *);
#endif /* ENABLE_COLOR */
int cmd_cmdline(struct pg_top_context *);
int cmd_current_query(struct pg_top_context *);
int cmd_delay(struct pg_top_context *);
int cmd_displays(struct pg_top_context *);
int cmd_explain(struct pg_top_context *);
int cmd_explain_analyze(struct pg_top_context *);
int cmd_help(struct pg_top_context *);
int cmd_idletog(struct pg_top_context *);
int cmd_indexes(struct pg_top_context *);
int cmd_io(struct pg_top_context *);
int cmd_locks(struct pg_top_context *);
int cmd_number(struct pg_top_context *);
int cmd_quit(struct pg_top_context *);
int cmd_replication(struct pg_top_context *);
int cmd_order(struct pg_top_context *);
int cmd_redraw(struct pg_top_context *);
int cmd_statements(struct pg_top_context *);
int cmd_update(struct pg_top_context *);
int cmd_user(struct pg_top_context *);
int execute_command(struct pg_top_context *, char);
void show_help(struct statics *);
int scanint(char *str, int *intp);
void show_current_query(struct pg_conninfo_ctx *, int);
void show_explain(struct pg_conninfo_ctx *, int, int);
void show_locks(struct pg_conninfo_ctx *, int);
#endif /* _COMMANDS_H_ */
07070100000019000081A400000000000000000000000166622DE8000002D0000000000000000000000000000000000000001900000000pg_top-4.1.1/config.h.in#ifndef _CONFIG_H_
#define _CONFIG_H_
#cmakedefine ENABLE_COLOR 1
#cmakedefine HAVE_GETOPT 1
#cmakedefine HAVE_MEMCPY 1
#cmakedefine HAVE_SETPRIORITY 1
#cmakedefine HAVE_SIGACTION 1
#cmakedefine HAVE_SIGHOLD 1
#cmakedefine HAVE_SIGPROCMASK 1
#cmakedefine HAVE_SIGRELSE 1
#cmakedefine HAVE_SNPRINTF 1
#cmakedefine HAVE_STRCHR 1
#cmakedefine HAVE_STRERROR 1
#cmakedefine HAVE_STRING_H 1
#cmakedefine HAVE_STRINGS_H 1
#cmakedefine HAVE_SYS_TIME_H 1
#cmakedefine HAVE_SYS_RESOURCE_H 1
#cmakedefine HAVE_UNISTD_H 1
#cmakedefine OSMAJOR @OSMAJOR@
#cmakedefine OSREV @OSREV@
#cmakedefine STDC_HEADERS 1
#cmakedefine TIME_WITH_SYS_TIME 1
#cmakedefine RETSIGTYPE @RETSIGTYPE@
#cmakedefine time_t @time_t@
#endif /* _CONFIG_H_ */
0707010000001A000081A400000000000000000000000166622DE800007CBE000000000000000000000000000000000000001700000000pg_top-4.1.1/display.c/*
* Top users/processes display for Unix
* Version 3
*
* This program may be freely redistributed,
* but this entire comment MUST remain intact.
*
* Copyright (c) 1984, 1989, William LeFebvre, Rice University
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
*/
/*
* This file contains the routines that display information on the screen.
* Each section of the screen has two routines: one for initially writing
* all constant and dynamic text, and one for only updating the text that
* changes. The prefix "i_" is used on all the "initial" routines and the
* prefix "u_" is used for all the "updating" routines.
*
* ASSUMPTIONS:
* None of the "i_" routines use any of the termcap capabilities.
* In this way, those routines can be safely used on terminals that
* have minimal (or nonexistant) terminal capabilities.
*
* The routines are called in this order: *_loadave, i_timeofday,
* *_procstates, *_cpustates, *_memory, *_message, *_header,
* *_process, u_endscreen.
*/
#include "os.h"
#include <ctype.h>
#include <stdarg.h>
#include <unistd.h>
#include "pg_top.h"
#include "machine.h"
#include "screen.h" /* interface to screen package */
#include "layout.h" /* defines for screen position layout */
#include "display.h"
#include "boolean.h"
#include "utils.h"
#ifdef ENABLE_COLOR
#include "color.h"
#endif
#define CURSOR_COST 8
/* imported from screen.c */
extern int overstrike;
static int lmpid = -1;
static int display_width = MAX_COLS;
/* cursor positions of key points on the screen are maintained here */
/* layout.h has static definitions, but we may change our minds on some
of the positions as we make decisions about what needs to be displayed */
static int x_lastpid = X_LASTPID;
static int y_lastpid = Y_LASTPID;
static int x_loadave = X_LOADAVE;
static int y_loadave = Y_LOADAVE;
static int x_minibar = X_MINIBAR;
static int y_minibar = Y_MINIBAR;
static int x_uptime = X_UPTIME;
static int y_uptime = Y_UPTIME;
static int x_procstate = X_PROCSTATE;
static int y_procstate = Y_PROCSTATE;
static int x_cpustates = X_CPUSTATES;
static int y_cpustates = Y_CPUSTATES;
static int x_mem = X_MEM;
static int y_mem = Y_MEM;
static int x_swap = -1;
static int y_swap = -1;
static int y_message = Y_MESSAGE;
static int x_db = X_DB;
static int y_db = Y_DB;
static int x_io = X_IO;
static int y_io = Y_IO;
static int x_header = X_HEADER;
static int y_header = Y_HEADER;
static int x_idlecursor = X_IDLECURSOR;
static int y_idlecursor = Y_IDLECURSOR;
static int y_procs = Y_PROCS;
/* buffer and colormask that describes the content of the screen */
/* these are singly dimensioned arrays -- the row boundaries are
determined on the fly.
*/
static char *screenbuf = NULL;
static char *colorbuf = NULL;
static char scratchbuf[MAX_COLS];
static int bufsize = 0;
/* lineindex tells us where the beginning of a line is in the buffer */
#define lineindex(l) ((l)*MAX_COLS)
/* screen's cursor */
static int curr_x,
curr_y;
static int curr_color;
/* virtual cursor */
static int virt_x,
virt_y;
static char **procstate_names;
static char **cpustate_names;
static char **memory_names;
static char **swap_names;
static int num_procstates;
static int num_cpustates;
static int num_memory;
static int num_swap;
static int *lprocstates;
static int *lcpustates;
static int *cpustate_columns;
static int cpustate_total_length;
static enum
{
OFF, ON, ERASE
} header_status = ON;
#ifdef ENABLE_COLOR
static int load_cidx[3];
static int header_cidx;
static int *cpustate_cidx;
static int *memory_cidx;
static int *swap_cidx;
#endif
static int header_color = 0;
/* internal support routines */
/*
* static int string_count(char **pp)
*
* Pointer "pp" points to an array of string pointers, which is
* terminated by a NULL. Return the number of string pointers in
* this array.
*/
static int
string_count(char **pp)
{
register int cnt = 0;
if (pp != NULL)
{
while (*pp++ != NULL)
{
cnt++;
}
}
return (cnt);
}
void
display_clear()
{
#ifdef DEBUG
dprintf("display_clear\n");
#endif /* DEBUG */
clear();
memzero(screenbuf, bufsize);
memzero(colorbuf, bufsize);
curr_x = curr_y = 0;
}
/*
* void display_move(int x, int y)
*
* Efficiently move the cursor to x, y. This assumes the cursor is
* currently located at curr_x, curr_y, and will only use cursor
* addressing when it is less expensive than overstriking what's
* already on the screen.
*/
void
display_move(int x, int y)
{
char buff[128];
char *p;
char *bufp;
char *colorp;
int cnt = 0;
int color = curr_color;
#ifdef DEBUG
dprintf("display_move(%d, %d): curr_x %d, curr_y %d\n", x, y, curr_x, curr_y);
#endif /* DEBUG */
/* are we in a position to do this without cursor addressing? */
if (curr_y < y || (curr_y == y && curr_x <= x))
{
/*
* start buffering up what it would take to move there by rewriting
* what's on the screen
*/
cnt = CURSOR_COST;
p = buff;
/* one newline for every line */
while (cnt > 0 && curr_y < y)
{
if (color !=0)
{
p = strecpy(p, color_set(0));
color = 0;
cnt -= 5;
}
*p++ = '\n';
curr_y++;
curr_x = 0;
cnt--;
}
/* write whats in the screenbuf */
bufp = &screenbuf[lineindex(curr_y) + curr_x];
colorp = &colorbuf[lineindex(curr_y) + curr_x];
while (cnt > 0 && curr_x < x)
{
if (color !=*colorp)
{
color = *colorp;
p = strecpy(p, color_set(color));
cnt -= 5;
}
if ((*p = *bufp) == '\0')
{
/* somwhere on screen we haven't been before */
*p = *bufp = ' ';
}
p++;
bufp++;
colorp++;
curr_x++;
cnt--;
}
}
/* move the cursor */
if (cnt > 0)
{
/* screen rewrite is cheaper */
*p = '\0';
fputs(buff, stdout);
curr_color = color;
}
else
{
Move_to(x, y);
}
/* update our position */
curr_x = x;
curr_y = y;
}
/*
* display_write(int x, int y, int newcolor, int eol, char *new)
*
* Optimized write to the display. This writes characters to the
* screen in a way that optimizes the number of characters actually
* sent, by comparing what is being written to what is already on
* the screen (according to screenbuf and colorbuf). The string to
* write is "new", the first character of "new" should appear at
* screen position x, y. If x is -1 then "new" begins wherever the
* cursor is currently positioned. The string is written with color
* "newcolor". If "eol" is true then the remainder of the line is
* cleared. It is expected that "new" will have no newlines and no
* escape sequences.
*/
void
display_write(int x, int y, int newcolor, int eol, char *new)
{
char *bufp;
char *colorp;
int ch;
int diff;
#ifdef DEBUG
dprintf("display_write(%d, %d, %d, %d, \"%s\")\n",
x, y, newcolor, eol, new);
#endif /* DEBUG */
/* dumb terminal handling here */
if (!smart_terminal)
{
if (x != -1)
{
/* make sure we are on the right line */
while (curr_y < y)
{
putchar('\n');
curr_y++;
curr_x = 0;
}
/* make sure we are on the right column */
while (curr_x < x)
{
putchar(' ');
curr_x++;
}
}
/* write */
if (new != NULL)
{
fputs(new, stdout);
curr_x += strlen(new);
}
return;
}
/* adjust for "here" */
if (x == -1)
{
x = virt_x;
y = virt_y;
}
else
{
virt_x = x;
virt_y = y;
}
/* a pointer to where we start */
bufp = &screenbuf[lineindex(y) + x];
colorp = &colorbuf[lineindex(y) + x];
/* main loop */
while (new != NULL && (ch = *new++) != '\0')
{
/* if either character or color are different, an update is needed */
/* but only when the screen is wide enough */
if (x < display_width && (ch != *bufp || newcolor != *colorp))
{
/* check cursor */
if (y != curr_y || x != curr_x)
{
/* have to move the cursor */
display_move(x, y);
}
/* write character */
if (curr_color != newcolor)
{
fputs(color_set(newcolor), stdout);
curr_color = newcolor;
}
putchar(ch);
*bufp = ch;
*colorp = curr_color;
curr_x++;
}
/* move */
x++;
virt_x++;
bufp++;
colorp++;
}
/* eol handling */
if (eol && *bufp != '\0')
{
#ifdef DEBUG
dprintf("display_write: clear-eol (bufp = \"%s\")\n", bufp);
#endif /* DEBUG */
/* make sure we are color 0 */
if (curr_color != 0)
{
fputs(color_set(0), stdout);
curr_color = 0;
}
/* make sure we are at the end */
if (x != curr_x || y != curr_y)
{
Move_to(x, y);
curr_x = x;
curr_y = y;
}
/* clear to end */
clear_eol(strlen(bufp));
/* clear out whats left of this line's buffer */
diff = display_width - x;
if (diff > 0)
{
memzero(bufp, diff);
memzero(colorp, diff);
}
}
}
void
display_fmt(int x, int y, int newcolor, int eol, char *fmt,...)
{
va_list argp;
va_start(argp, fmt);
vsnprintf(scratchbuf, MAX_COLS, fmt, argp);
display_write(x, y, newcolor, eol, scratchbuf);
}
void
display_cte()
{
int len;
int y;
char *p;
int need_clear = 0;
/* is there anything out there that needs to be cleared? */
p = &screenbuf[lineindex(virt_y) + virt_x];
if (*p != '\0')
{
need_clear = 1;
}
else
{
/* this line is clear, what about the rest? */
y = virt_y;
while (++y < screen_length)
{
if (screenbuf[lineindex(y)] != '\0')
{
need_clear = 1;
break;
}
}
}
if (need_clear)
{
#ifdef DEBUG
dprintf("display_cte: clearing\n");
#endif /* DEBUG */
/* different method when there's no clear_to_end */
if (clear_to_end)
{
display_move(virt_x, virt_y);
putcap(clear_to_end);
}
else
{
if (++virt_y < screen_length)
{
display_move(0, virt_y);
virt_x = 0;
while (virt_y < screen_length)
{
p = &screenbuf[lineindex(virt_y)];
len = strlen(p);
if (len > 0)
{
clear_eol(len);
}
virt_y++;
}
}
}
/* clear the screenbuf */
len = lineindex(virt_y) + virt_x;
memzero(&screenbuf[len], bufsize - len);
memzero(&colorbuf[len], bufsize - len);
}
}
static void
summary_format(int x, int y, int *numbers, char **names)
{
register int num;
register char *thisname;
register char *lastname = NULL;
/* format each number followed by its string */
while ((thisname = *names++) != NULL)
{
/* get the number to format */
num = *numbers++;
/* display only non-zero numbers */
if (num != 0)
{
/* write the previous name */
if (lastname != NULL)
{
display_write(-1, -1, 0, 0, lastname);
}
/* write this number if positive */
if (num > 0)
{
display_write(x, y, 0, 0, itoa(num));
}
/* defer writing this name */
lastname = thisname;
/* next iteration will not start at x, y */
x = y = -1;
}
}
/*
* if the last string has a separator on the end, it has to be written
* with care
*/
if (lastname != NULL && (num = strlen(lastname)) > 1 &&
lastname[num - 2] == ',' && lastname[num - 1] == ' ')
{
display_fmt(-1, -1, 0, 1, "%.*s", num - 2, lastname);
}
else
{
display_write(-1, -1, 0, 1, lastname);
}
}
static void
summary_format_memory(int x, int y, long *numbers, char **names, int *cidx)
{
register long num;
register int color;
register char *thisname;
register char *lastname = NULL;
/* format each number followed by its string */
while ((thisname = *names++) != NULL)
{
/* get the number to format */
num = *numbers++;
color = 0;
/* display non-negative numbers */
if (num >= 0)
{
/* write the previous name */
if (lastname != NULL)
{
display_write(-1, -1, 0, 0, lastname);
}
/* defer writing this name */
lastname = thisname;
#ifdef ENABLE_COLOR
/* choose a color */
color = color_test(*cidx++, num);
#endif
/* is this number in kilobytes? */
if (thisname[0] == 'K')
{
display_write(x, y, color, 0, format_k(num));
lastname++;
}
else
{
display_write(x, y, color, 0, itoa((int) num));
}
/* next iteration will not start at x, y */
x = y = -1;
}
}
/*
* if the last string has a separator on the end, it has to be written
* with care
*/
if (lastname == NULL)
{
/*
* Don't show anything if the data doesn't exist.
*/
return;
}
else if ((num = strlen(lastname)) > 1 &&
lastname[num - 2] == ',' && lastname[num - 1] == ' ')
{
display_fmt(-1, -1, 0, 1, "%.*s", num - 2, lastname);
}
else
{
display_write(-1, -1, 0, 1, lastname);
}
}
/*
* int display_resize()
*
* Reallocate buffer space needed by the display package to accomodate
* a new screen size. Must be called whenever the screen's size has
* changed. Returns the number of lines available for displaying
* processes or -1 if there was a problem allocating space.
*/
int
display_resize()
{
register int lines;
register int newsize;
/* calculate the current dimensions */
/* if operating in "dumb" mode, we only need one line */
lines = smart_terminal ? screen_length : 1;
/*
* we don't want more than MAX_COLS columns, since the machine-dependent
* modules make static allocations based on MAX_COLS and we don't want to
* run off the end of their buffers
*/
display_width = screen_width;
if (display_width >= MAX_COLS)
{
display_width = MAX_COLS - 1;
}
/* see how much space we need */
newsize = lines * (MAX_COLS + 1);
/* reallocate only if we need more than we already have */
if (newsize > bufsize)
{
/* deallocate any previous buffer that may have been there */
if (screenbuf != NULL)
{
free(screenbuf);
}
if (colorbuf != NULL)
{
free(colorbuf);
}
/* allocate space for the screen and color buffers */
bufsize = newsize;
screenbuf = (char *) calloc(bufsize, sizeof(char));
colorbuf = (char *) calloc(bufsize, sizeof(char));
if (screenbuf == NULL || colorbuf == NULL)
{
/* oops! */
return (-1);
}
}
else
{
/* just clear them out */
memzero(screenbuf, bufsize);
memzero(colorbuf, bufsize);
}
/* adjust total lines on screen to lines available for procs */
lines -= y_procs;
/* return number of lines available */
/* for dumb terminals, pretend like we can show any amount */
return (smart_terminal ? lines : Largest);
}
/*
* int display_init(struct statics *statics)
*
* Initialize the display system based on information in the statics
* structure. Returns the number of lines available for displaying
* processes or -1 if there was an error.
*/
int
display_init(struct statics *statics)
{
register int lines;
register char **pp;
register char *p;
register int *ip;
register int i;
/*
* certain things may influence the screen layout, so look at those first
*/
/* a swap line shifts parts of the display down one */
swap_names = statics->swap_names;
if ((num_swap = string_count(swap_names)) > 0)
{
/* adjust screen placements */
y_message++;
y_header++;
y_idlecursor++;
y_procs++;
x_swap = X_SWAP;
y_swap = Y_SWAP;
}
/* call resize to do the dirty work */
lines = display_resize();
/* only do the rest if we need to */
if (lines > -1)
{
/* save pointers and allocate space for names */
procstate_names = statics->procstate_names;
num_procstates = string_count(procstate_names);
lprocstates = (int *) malloc(num_procstates * sizeof(int));
cpustate_names = statics->cpustate_names;
num_cpustates = string_count(cpustate_names);
lcpustates = (int *) malloc(num_cpustates * sizeof(int));
cpustate_columns = (int *) malloc(num_cpustates * sizeof(int));
memory_names = statics->memory_names;
num_memory = string_count(memory_names);
/* calculate starting columns where needed */
cpustate_total_length = 0;
pp = cpustate_names;
ip = cpustate_columns;
while (*pp != NULL)
{
*ip++ = cpustate_total_length;
if ((i = strlen(*pp++)) > 0)
{
cpustate_total_length += i + 8;
}
}
}
#ifdef ENABLE_COLOR
/* set up color tags for loadavg */
load_cidx[0] = color_tag("1min");
load_cidx[1] = color_tag("5min");
load_cidx[2] = color_tag("15min");
/* find header color */
header_cidx = color_tag("header");
header_color = color_test(header_cidx, 0);
/* color tags for cpu states */
cpustate_cidx = (int *) malloc(num_cpustates * sizeof(int));
i = 0;
p = strecpy(scratchbuf, "cpu.");
while (i < num_cpustates)
{
strcpy(p, cpustate_names[i]);
cpustate_cidx[i++] = color_tag(scratchbuf);
}
/* color tags for memory */
memory_cidx = (int *) malloc(num_memory * sizeof(int));
i = 0;
p = strecpy(scratchbuf, "memory.");
while (i < num_memory)
{
strcpy(p, homogenize(memory_names[i] + 1));
memory_cidx[i++] = color_tag(scratchbuf);
}
/* color tags for swap */
swap_cidx = (int *) malloc(num_swap * sizeof(int));
i = 0;
p = strecpy(scratchbuf, "swap.");
while (i < num_swap)
{
strcpy(p, homogenize(swap_names[i] + 1));
swap_cidx[i++] = color_tag(scratchbuf);
}
#endif
/* return number of lines available (or error) */
return (lines);
}
static void
pr_loadavg(double avg, int i)
{
int color = 0;
#ifdef ENABLE_COLOR
color = color_test(load_cidx[i], (int) (avg * 100));
#endif
display_fmt(x_loadave + X_LOADAVEWIDTH * i, y_loadave, color, 0,
avg < 10.0 ? " %5.2f" : " %5.1f", avg);
display_write(-1, -1, 0, 0, (i < 2 ? "," : ";"));
}
void
i_loadave(int mpid, double *avenrun)
{
register int i;
/* i_loadave also clears the screen, since it is first */
display_clear();
/* mpid == -1 implies this system doesn't have an _mpid */
if (mpid != -1)
{
display_fmt(0, 0, 0, 0,
"last pid: %5d; load avg:", mpid);
x_loadave = X_LOADAVE;
}
else
{
display_write(0, 0, 0, 0, "load averages:");
x_loadave = X_LOADAVE - X_LASTPIDWIDTH;
}
for (i = 0; i < 3; i++)
{
pr_loadavg(avenrun[i], i);
}
lmpid = mpid;
}
void
u_loadave(int mpid, double *avenrun)
{
register int i;
if (mpid != -1)
{
/* change screen only when value has really changed */
if (mpid != lmpid)
{
display_fmt(x_lastpid, y_lastpid, 0, 0,
"%5d", mpid);
lmpid = mpid;
}
}
/* display new load averages */
for (i = 0; i < 3; i++)
{
pr_loadavg(avenrun[i], i);
}
}
static char minibar_buffer[64];
#define MINIBAR_WIDTH 20
void
i_minibar(int (*formatter) (char *, int))
{
(void) ((*formatter) (minibar_buffer, MINIBAR_WIDTH));
display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
}
void
u_minibar(int (*formatter) (char *, int))
{
(void) ((*formatter) (minibar_buffer, MINIBAR_WIDTH));
display_write(x_minibar, y_minibar, 0, 0, minibar_buffer);
}
static int uptime_days;
static int uptime_hours;
static int uptime_mins;
static int uptime_secs;
void
i_uptime(time_t *bt, time_t *tod)
{
time_t uptime;
if (*bt != -1)
{
uptime = *tod - *bt;
uptime += 30;
uptime_days = uptime / 86400;
uptime %= 86400;
uptime_hours = uptime / 3600;
uptime %= 3600;
uptime_mins = uptime / 60;
uptime_secs = uptime % 60;
/*
* Display the uptime.
*/
display_fmt(x_uptime, y_uptime, 0, 0,
" up %d+%02d:%02d:%02d",
uptime_days, uptime_hours, uptime_mins, uptime_secs);
}
}
void
u_uptime(time_t *bt, time_t *tod)
{
i_uptime(bt, tod);
}
void
i_timeofday(time_t *tod)
{
/*
* Display the current time. "ctime" always returns a string that looks
* like this:
*
* Sun Sep 16 01:03:52 1973 012345678901234567890123 1 2
*
* We want indices 11 thru 18 (length 8).
*/
display_fmt((smart_terminal ? screen_width : 79) - 8, 0, 0, 1,
"%-8.8s", &(ctime(tod)[11]));
}
static int ltotal = 0;
/*
* *_procstates(total, brkdn, names) - print the process summary line
*/
void
i_procstates(int total, int *brkdn)
{
/* write current number of processes and remember the value */
display_fmt(0, y_procstate, 0, 0,
"%d processes: ", total);
ltotal = total;
/* remember where the summary starts */
x_procstate = virt_x;
/* format and print the process state summary */
summary_format(-1, -1, brkdn, procstate_names);
/* save the numbers for next time */
memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
}
void
u_procstates(int total, int *brkdn)
{
/* update number of processes only if it has changed */
if (ltotal != total)
{
display_fmt(0, y_procstate, 0, 0,
"%d", total);
/* if number of digits differs, rewrite the label */
if (digits(total) != digits(ltotal))
{
display_write(-1, -1, 0, 0, " processes: ");
x_procstate = virt_x;
}
/* save new total */
ltotal = total;
}
/* see if any of the state numbers has changed */
if (memcmp(lprocstates, brkdn, num_procstates * sizeof(int)) != 0)
{
/* format and update the line */
summary_format(x_procstate, y_procstate, brkdn, procstate_names);
memcpy(lprocstates, brkdn, num_procstates * sizeof(int));
}
}
/*
* *_cpustates(states, names) - print the cpu state percentages
*/
/* cpustates_tag() calculates the correct tag to use to label the line */
char *
cpustates_tag()
{
register char *use;
static char *short_tag = "CPU: ";
static char *long_tag = "CPU states: ";
/*
* if length + strlen(long_tag) >= screen_width, then we have to use the
* shorter tag (we subtract 2 to account for ": ")
*/
if (cpustate_total_length + (int) strlen(long_tag) - 2 >= screen_width)
{
use = short_tag;
}
else
{
use = long_tag;
}
/* set x_cpustates accordingly then return result */
x_cpustates = strlen(use);
return (use);
}
void
i_cpustates(int64_t * states)
{
int value;
char **names;
char *thisname;
int *colp;
int color = 0;
#ifdef ENABLE_COLOR
int *cidx = cpustate_cidx;
#endif
/* initialize */
names = cpustate_names;
colp = cpustate_columns;
/* print tag */
display_write(0, y_cpustates, 0, 0, cpustates_tag());
/* now walk thru the names and print the line */
while ((thisname = *names++) != NULL)
{
if (*thisname != '\0')
{
/* retrieve the value and remember it */
value = *states;
#ifdef ENABLE_COLOR
/* determine color number to use */
color = color_test(*cidx++, value / 10);
#endif
/* if percentage is >= 1000, print it as 100% */
display_fmt(x_cpustates + *colp, y_cpustates,
color, 0,
(value >= 1000 ? "%4.0f%% %s" : "%4.1f%% %s"),
((float) value) / 10.,
thisname);
if (*names != NULL)
display_write(-1, -1, 0, 0, ",");
}
/* increment */
colp++;
states++;
}
/* copy over values into "last" array */
memcpy(lcpustates, states, num_cpustates * sizeof(int));
}
void
u_cpustates(int64_t * states)
{
int value;
char **names = cpustate_names;
char *thisname;
int *lp;
int *colp;
int color = 0;
#ifdef ENABLE_COLOR
int *cidx = cpustate_cidx;
#endif
lp = lcpustates;
colp = cpustate_columns;
/* we could be much more optimal about this */
while ((thisname = *names++) != NULL)
{
if (*thisname != '\0')
{
/* did the value change since last time? */
if (*lp != *states)
{
/* yes, change it */
/* retrieve value and remember it */
value = *states;
#ifdef ENABLE_COLOR
/* determine color number to use */
color = color_test(*cidx, value / 10);
#endif
/* if percentage is >= 1000, print it as 100% */
display_fmt(x_cpustates + *colp, y_cpustates,
color, 0,
(value >= 1000 ? "%4.0f%% %s" : "%4.1f%% %s"),
((float) value) / 10.,
thisname);
if (*names != NULL)
display_write(-1, -1, 0, 0, ",");
/* remember it for next time */
*lp = value;
}
#ifdef ENABLE_COLOR
cidx++;
#endif
}
/* increment and move on */
lp++;
states++;
colp++;
}
}
void
z_cpustates()
{
register int i = 0;
register char **names = cpustate_names;
register char *thisname;
register int *lp;
/* print tag */
display_write(0, y_cpustates, 0, 0, cpustates_tag());
while ((thisname = *names++) != NULL)
{
if (*thisname != '\0')
{
display_fmt(-1, -1, 0, 0, "%s %% %s", i++ == 0 ? "" : ", ",
thisname);
}
}
/* fill the "last" array with all -1s, to insure correct updating */
lp = lcpustates;
i = num_cpustates;
while (--i >= 0)
{
*lp++ = -1;
}
}
/*
* *_memory(stats) - print "Memory: " followed by the memory summary string
*
* Assumptions: cursor is on "lastline", the previous line
*/
void
i_memory(long *stats)
{
display_write(0, y_mem, 0, 0, "Memory: ");
/* format and print the memory summary */
summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
}
void
u_memory(long *stats)
{
/* format the new line */
summary_format_memory(x_mem, y_mem, stats, memory_names, memory_cidx);
}
/*
* *_swap(stats) - print "Swap: " followed by the swap summary string
*
* Assumptions: cursor is on "lastline", the previous line
*
* These functions only print something when num_swap > 0
*/
void
i_swap(long *stats)
{
if (num_swap > 0)
{
/* print the tag */
display_write(0, y_swap, 0, 0, "Swap: ");
/* format and print the swap summary */
summary_format_memory(x_swap, y_swap, stats, swap_names,
swap_cidx);
}
}
void
u_swap(long *stats)
{
if (num_swap > 0)
{
/* format the new line */
summary_format_memory(x_swap, y_swap, stats, swap_names,
swap_cidx);
}
}
/*
* *_db(db_info) - print "DB activity: " followed by database activity
*/
void
i_db(struct db_info *db_info)
{
u_db(db_info);
}
void
u_db(struct db_info *db_info)
{
char buf[128];
display_write(x_db, y_db, 0, 0, "DB activity: ");
snprintf(buf, sizeof(buf),
"%3ld tps, %2ld rollbs/s, %3ld buffer r/s, %2ld hit%%, %6ld row r/s, %4ld row w/s ",
db_info->numXact, db_info->numRollback,
db_info->numBlockRead,
db_info->numBlockRead + db_info->numBlockHit > 0 ?
((int64_t)(db_info->numBlockHit * 100 /
(db_info->numBlockRead + db_info->numBlockHit))) : 0,
db_info->numTupleFetched,
db_info->numTupleAltered);
display_write(-1, -1, 0, 0, buf);
}
/*
* *_io(io_info) - print "DB I/O: " followed by IO summary
*/
void
i_io(struct io_info *io_info)
{
char buf[128];
display_write(x_io, y_io, 0, 0, "DB I/O: ");
snprintf(buf, sizeof(buf),
"%5ld reads/s, %5ld KB/s, %5ld writes/s, %5ld KB/s ",
io_info->reads,
io_info->readsectors / 2,
io_info->writes,
io_info->writesectors / 2);
display_write(-1, -1, 0, 0, buf);
}
void
u_io(struct io_info *io_info)
{
i_io(io_info);
}
/*
* *_message() - print the next pending message line, or erase the one
* that is there.
*
* Note that u_message is (currently) the same as i_message.
*
* Assumptions: lastline is consistent
*/
/*
* i_message is funny because it gets its message asynchronously (with
* respect to screen updates).
*/
static char next_msg[MAX_COLS + 8];
static int msglen = 0;
/* Invariant: msglen is always the length of the message currently displayed
on the screen (even when next_msg doesn't contain that message). */
void
i_message()
{
if (smart_terminal)
{
if (next_msg[0] != '\0')
{
display_move(0, y_message);
standout(next_msg);
msglen = strlen(next_msg);
next_msg[0] = '\0';
}
else if (msglen > 0)
{
display_move(0, y_message);
(void) clear_eol(msglen);
msglen = 0;
}
}
}
void
u_message()
{
i_message();
}
static int header_length;
/*
* *_header(text) - print the header for the process area
*
* Assumptions: cursor is on the previous line and lastline is consistent
*/
void
i_header(char *text)
{
if (text == NULL)
return;
header_length = strlen(text);
if (header_status == ON)
{
display_write(x_header, y_header, header_color, 1, text);
}
else if (header_status == ERASE)
{
header_status = OFF;
}
}
/*ARGSUSED*/
void
u_header(char *text)
{
if (header_status == ERASE)
{
display_write(x_header, y_header, header_color, 1, "");
header_status = OFF;
}
}
/*
* *_process(line, thisline) - print one process line
*
* Assumptions: lastline is consistent
*/
void
i_process(int line, char *thisline)
{
if (thisline == NULL)
return;
/* truncate the line to conform to our current screen width */
if (strlen(thisline) > display_width)
thisline[display_width] = '\0';
/* write the line out */
display_write(0, y_procs + line, 0, 1, thisline);
}
void
u_process(int line, char *newline)
{
i_process(line, newline);
}
void
u_endscreen(int hi)
{
if (smart_terminal)
{
/* clear-to-end the display */
display_cte();
/* move the cursor to a pleasant place */
/* does this need to be a display_move??? */
Move_to(x_idlecursor, y_idlecursor);
}
else
{
/* separate this display from the next with some vertical room */
fputs("\n\n", stdout);
}
}
void
display_header(int t)
{
if (t)
{
header_status = ON;
}
else if (header_status == ON)
{
header_status = ERASE;
}
}
void
new_message_v(int type, char *msgfmt, va_list ap)
{
register int i;
/* first, format the message */
(void) vsnprintf(next_msg, sizeof(next_msg), msgfmt, ap);
if (msglen > 0)
{
/* message there already -- can we clear it? */
if (!overstrike)
{
/* yes -- write it and clear to end */
i = strlen(next_msg);
if ((type & MT_delayed) == 0)
{
if ((type & MT_standout) != 0)
standout(next_msg);
else
fputs(next_msg, stdout);
(void) clear_eol(msglen - i);
msglen = i;
next_msg[0] = '\0';
}
}
}
else
{
if ((type & MT_delayed) == 0)
{
if ((type & MT_standout) != 0)
standout(next_msg);
else
fputs(next_msg, stdout);
msglen = strlen(next_msg);
next_msg[0] = '\0';
}
}
}
void
new_message(int type, char *msgfmt,...)
{
va_list ap;
va_start(ap, msgfmt);
new_message_v(type, msgfmt, ap);
va_end(ap);
}
void
display_error_message(char *msgfmt,...)
{
va_list ap;
va_start(ap, msgfmt);
new_message_v(MT_standout | MT_delayed, msgfmt, ap);
va_end(ap);
}
void
clear_message()
{
#ifdef DEBUG
dprintf("clear_message: msglen = %d, x = %d, y = %d\n", msglen, curr_x, curr_y);
#endif /* DEBUG */
if (clear_eol(msglen) == 1)
{
putchar('\r');
}
}
int
readline(char *buffer, int size, int numeric)
{
register char *ptr = buffer;
register char ch;
register char cnt = 0;
register char maxcnt = 0;
/* allow room for null terminator */
size -= 1;
/* read loop */
while ((fflush(stdout), read(0, ptr, 1) > 0))
{
/* newline or return means we are done */
if ((ch = *ptr) == '\n' || ch == '\r')
{
break;
}
/* handle special editing characters */
if (ch == ch_kill)
{
/* kill line -- account for overstriking */
if (overstrike)
{
msglen += maxcnt;
}
/* return null string */
*buffer = '\0';
putchar('\r');
return (-1);
}
else if (ch == ch_erase)
{
/* erase previous character */
if (cnt <= 0)
{
/* none to erase! */
putchar('\7');
}
else
{
fputs("\b \b", stdout);
ptr--;
cnt--;
}
}
/* check for character validity and buffer overflow */
else if (cnt == size || (numeric &&!isdigit(ch)) ||
!isprint(ch))
{
/* not legal */
putchar('\7');
}
else
{
/* echo it and store it in the buffer */
putchar(ch);
ptr++;
cnt++;
if (cnt > maxcnt)
{
maxcnt = cnt;
}
}
}
/* all done -- null terminate the string */
*ptr = '\0';
/* account for the extra characters in the message area */
/* (if terminal overstrikes, remember the furthest they went) */
msglen += overstrike ? maxcnt : cnt;
/* return either inputted number or string length */
putchar('\r');
return (cnt == 0 ? -1 : numeric ? atoi(buffer) : cnt);
}
void
display_pagerstart()
{
display_clear();
}
void
display_pagerend()
{
char ch;
standout("Hit any key to continue: ");
fflush(stdout);
(void) read(0, &ch, 1);
}
void
display_pager(char *data)
{
int ch;
char readch;
while ((ch = *data++) != '\0')
{
putchar(ch);
if (ch == '\n')
{
if (++curr_y >= screen_length - 1)
{
standout("...More...");
fflush(stdout);
(void) read(0, &readch, 1);
putchar('\r');
switch (readch)
{
case '\r':
case '\n':
curr_y--;
break;
case 'q':
return;
default:
curr_y = 0;
}
}
}
}
}
0707010000001B000081A400000000000000000000000166622DE8000005EC000000000000000000000000000000000000001700000000pg_top-4.1.1/display.h/* interface declaration for display.c */
#ifndef _DISPLAY_H
#define _DISPLAY_H
#include "machine.h"
/* "type" argument for new_message function */
#define MT_standout 1
#define MT_delayed 2
int display_resize();
int display_init(struct statics *statics);
void i_loadave(int mpid, double *avenrun);
void u_loadave(int mpid, double *avenrun);
void i_minibar(int (*) (char *, int));
void u_minibar(int (*) (char *, int));
void i_uptime(time_t *bt, time_t *tod);
void u_uptime(time_t *bt, time_t *tod);
void i_timeofday(time_t *tod);
void i_procstates(int total, int *brkdn);
void u_procstates(int total, int *brkdn);
void i_cpustates(int64_t * states);
void u_cpustates(int64_t * states);
void z_cpustates();
void i_memory(long *stats);
void u_memory(long *stats);
void i_swap(long *stats);
void u_swap(long *stats);
void i_db(struct db_info *db_info);
void u_db(struct db_info *db_info);
void i_io(struct io_info *io_info);
void u_io(struct io_info *io_info);
void i_message();
void u_message();
void i_header(char *text);
void u_header(char *text);
void i_process(int line, char *thisline);
void u_process(int line, char *newline);
void u_endscreen(int hi);
void display_header(int t);
void new_message(int type, char *msgfmt,...);
void display_error_message(char *msgfmt,...);
void clear_message();
int readline(char *buffer, int size, int numeric);
void display_pagerstart();
void display_pagerend();
void display_pager(char *data);
#endif /* _DISPLAY_H */
0707010000001C000081A400000000000000000000000166622DE8000006D6000000000000000000000000000000000000001600000000pg_top-4.1.1/getopt.c/*
* "getopt" routine customized for top.
*/
/*
* Many modern-day Unix implementations already have this function
* in libc. The standard "getopt" is perfectly sufficient for top's
* needs. If such a function exists in libc then you certainly don't
* need to compile this one in. To prevent this function from being
* compiled, define "HAVE_GETOPT". This is usually done in the "CFLAGS"
* line of the corresponding machine module.
*/
#include <config.h>
#ifndef HAVE_GETOPT
/*LINTLIBRARY*/
#include "os.h"
#ifndef NULL
#define NULL 0
#endif
#ifndef EOF
#define EOF (-1)
#endif
#define ERR(s, c) if(opterr){\
char errbuf[2];\
errbuf[0] = c; errbuf[1] = '\n';\
(void) write(2, argv[0], strlen(argv[0]));\
(void) write(2, s, strlen(s));\
(void) write(2, errbuf, 2);}
int opterr = 1;
int optind = 1;
int optopt;
char *optarg;
int
getopt(int argc, char **argv, char *opts)
{
static int sp = 1;
register int c;
register char *cp;
if (sp == 1)
{
if (optind >= argc ||
argv[optind][0] != '-' || argv[optind][1] == '\0')
return (EOF);
else if (strcmp(argv[optind], "--") == 0)
{
optind++;
return (EOF);
}
}
optopt = c = argv[optind][sp];
if (c == ':' || (cp = strchr(opts, c)) == NULL)
{
ERR(": unknown option, -", c);
if (argv[optind][++sp] == '\0')
{
optind++;
sp = 1;
}
return ('?');
}
if (*++cp == ':')
{
if (argv[optind][sp + 1] != '\0')
optarg = &argv[optind++][sp + 1];
else if (++optind >= argc)
{
ERR(": argument missing for -", c);
sp = 1;
return ('?');
}
else
optarg = argv[optind++];
sp = 1;
}
else
{
if (argv[optind][++sp] == '\0')
{
sp = 1;
optind++;
}
optarg = NULL;
}
return (c);
}
#endif /* HAVE_GETOPT */
0707010000001D000081A400000000000000000000000166622DE80000047F000000000000000000000000000000000000001400000000pg_top-4.1.1/help.h/* Simple help text displayed by "show_help" */
#ifndef _HELP_H_
#define _HELP_H_
char *help_text = "\n\
A top users display for PostgreSQL\n\
\n\
These single-character commands are available:\n\
\n\
^L - redraw screen\n\
<sp> - update screen\n\
A - EXPLAIN ANALYZE (UPDATE/DELETE safe)\n\
a - show PostgreSQL activity\n\
C - toggle the use of color\n\
E - show execution plan (UPDATE/DELETE safe)\n\
I - show I/O statistics per process (Linux only)\n\
L - show locks held by a process\n\
R - show PostgreSQL replication subscriptions\n\
Q - show current query of a process\n\
c - toggle the display of process commands\n\
d - change number of displays to show\n\
h or ? - help; show this text\n\
i - toggle the displaying of idle processes\n\
n or # - change number of processes to display\n\
o - specify sort order (%s)\n\
q - quit\n\
s - change number of seconds to delay between updates\n\
u - display processes for only one user (+ selects all users)\n\
\n\
Not all commands are available on all systems.\n\
";
#endif /* _HELP_H_ */
0707010000001E000081A400000000000000000000000166622DE80000039F000000000000000000000000000000000000001600000000pg_top-4.1.1/layout.h/*
* Top - a top users display for Unix
*
* This file defines the default locations on the screen for various parts
* of the display. These definitions are used by the routines in "display.c"
* for cursor addressing.
*/
#ifndef _LAYOUT_H_
#define _LAYOUT_H_
#define X_LASTPID 10
#define Y_LASTPID 0
#define X_LASTPIDWIDTH 13
#define X_LOADAVE 27
#define Y_LOADAVE 0
#define X_LOADAVEWIDTH 7
#define X_MINIBAR 50
#define Y_MINIBAR 0
#define X_UPTIME 53
#define Y_UPTIME 0
#define X_PROCSTATE 15
#define Y_PROCSTATE 1
#define X_BRKDN 15
#define Y_BRKDN 1
#define X_CPUSTATES 0
#define Y_CPUSTATES 2
#define X_MEM 8
#define Y_MEM 3
#define X_DB 0
#define Y_DB 4
#define X_IO 0
#define Y_IO 5
#define X_SWAP 6
#define Y_SWAP 6
#define Y_MESSAGE 6
#define X_HEADER 0
#define Y_HEADER 7
#define X_IDLECURSOR 0
#define Y_IDLECURSOR 6
#define Y_PROCS 8
#endif /* _LAYOUT_H_ */
0707010000001F000081A400000000000000000000000166622DE800000678000000000000000000000000000000000000001700000000pg_top-4.1.1/loadavg.h/*
* Top - a top users display for Berkeley Unix
*
* Defines required to access load average figures.
*
* This include file sets up everything we need to access the load average
* values in the kernel in a machine independent way. First, it sets the
* typedef "load_avg" to be either double or long (depending on what is
* needed), then it defines these macros appropriately:
*
* loaddouble(la) - convert load_avg to double.
* intload(i) - convert integer to load_avg.
*/
#ifndef _LOADAVG_H_
#define _LOADAVG_H_
/*
* We assume that if FSCALE is defined, then avenrun and ccpu are type long.
* If your machine is an exception (mips, perhaps?) then make adjustments
* here.
*
* Defined types: load_avg for load averages, pctcpu for cpu percentages.
*/
#if defined(mips) && !defined(NetBSD) && !defined(OpenBSD)
#include <sys/fixpoint.h>
#if defined(FBITS) && !defined(FSCALE)
#define FSCALE (1 << FBITS) /* mips */
#endif
#endif
#ifdef FSCALE
#define FIXED_LOADAVG FSCALE
#define FIXED_PCTCPU FSCALE
#endif
#ifdef ibm032
#undef FIXED_LOADAVG
#undef FIXED_PCTCPU
#define FIXED_PCTCPU PCT_SCALE
#endif
#ifdef FIXED_PCTCPU
typedef long pctcpu;
#define pctdouble(p) ((double)(p) / FIXED_PCTCPU)
#else
typedef double pctcpu;
#define pctdouble(p) (p)
#endif
#ifdef FIXED_LOADAVG
#if __FreeBSD__ == 9
typedef __uint32_t load_avg;
#else
typedef fixpt_t load_avg;
#endif /* __FreeBSD__ == 9 */
#define loaddouble(la) ((double)(la) / FIXED_LOADAVG)
#define intload(i) ((int)((i) * FIXED_LOADAVG))
#else
typedef double load_avg;
#define loaddouble(la) (la)
#define intload(i) ((double)(i))
#endif
#endif /* _LOADAVG_H_ */
07070100000020000041ED00000000000000000000000266622DE800000000000000000000000000000000000000000000001500000000pg_top-4.1.1/machine07070100000021000081A400000000000000000000000166622DE800000EC9000000000000000000000000000000000000001700000000pg_top-4.1.1/machine.h/*
* This file defines the interface between top and the machine-dependent
* module. It is NOT machine dependent and should not need to be changed
* for any specific machine.
*
* Copyright (c) 2007-2019, Mark Wong
*/
#ifndef _MACHINE_H_
#define _MACHINE_H_
#include <time.h>
#include <sys/types.h>
#include "pg.h"
#include "pg_config_manual.h"
/*
#ifdef CLK_TCK
# define HZ CLK_TCK
# else
# define HZ 60
# endif
*/
#ifndef HZ
#define HZ sysconf(_SC_CLK_TCK)
#endif
/* Display modes. */
enum DisplayMode
{
MODE_PROCESSES,
MODE_IO_STATS,
MODE_REPLICATION,
MODE_TYPES /* number of modes */
};
/* Maximum number of columns allowed for display */
#define MAX_COLS 255
/*
* The entire display is based on these next numbers being defined as is.
*/
#define NUM_AVERAGES 3
#define NPROCSTATES 7
/*
* The statics struct is filled in by machine_init. Fields marked as
* "optional" are not filled in by every module.
*/
struct statics
{
char **procstate_names;
char **cpustate_names;
char **memory_names;
char **swap_names; /* optional */
char **order_names; /* optional */
char **color_names; /* optional */
time_t boottime; /* optional */
int ncpus;
struct
{
unsigned int fullcmds:1;
unsigned int idle:1;
unsigned int warmup:1;
} flags;
};
/*
* the system_info struct is filled in by a machine dependent routine.
*/
#ifdef p_active /* uw7 define macro p_active */
#define P_ACTIVE p_pactive
#else
#define P_ACTIVE p_active
#endif
struct system_info
{
int last_pid;
double load_avg[NUM_AVERAGES];
int p_total;
int P_ACTIVE; /* number of procs considered "active" */
int *procstates;
int64_t *cpustates;
long *memory;
long *swap;
};
/* cpu_states is an array of percentages * 10. For example,
the (integer) value 105 is 10.5% (or .105).
*/
/*
* Database activity information
*/
struct db_info {
int numDb;
int64_t numXact;
int64_t numRollback;
int64_t numBlockRead;
int64_t numBlockHit;
int64_t numTupleFetched;
int64_t numTupleAltered;
int64_t numConflict;
};
/*
* Info on reads/writes happening on disk.
* On Linux, this can be obtained from /proc/diskstats.
*/
struct io_info {
int64_t reads;
int64_t readsectors;
int64_t writes;
int64_t writesectors;
};
/*
* the process_select struct tells get_process_info what processes we
* are interested in seeing
*/
struct process_select
{
int idle; /* show idle processes */
int fullcmd; /* show full command */
char *command; /* only this command (unless == NULL) */
char usename[NAMEDATALEN + 1]; /* only this postgres usename */
};
/* routines defined by the machine dependent module */
int machine_init(struct statics *);
void get_system_info(struct system_info *);
caddr_t get_process_info(struct system_info *, struct process_select *, int,
struct pg_conninfo_ctx *, int);
#ifdef __linux__
caddr_t get_process_info(struct system_info *, struct process_select *, int,
struct pg_conninfo_ctx *, int);
#else
caddr_t get_process_info(struct system_info *, struct process_select *, int,
char *);
#endif /* __linux__ */
void get_database_info(struct db_info *, struct pg_conninfo_ctx *);
void get_io_info(struct io_info *);
char *format_header(char *);
#if defined(__linux__) || defined (__FreeBSD__)
char *format_next_io(caddr_t);
#endif /* defined(__linux__) || defined (__FreeBSD__) */
char *format_next_process(caddr_t);
char *format_next_replication(caddr_t);
uid_t proc_owner(pid_t);
void update_state(int *pgstate, char *state);
void update_str(char **, char *);
extern int mode_stats;
extern char *backendstatenames[];
extern char *procstatenames[];
extern char fmt_header_io[];
extern char fmt_header_replication[];
#endif /* _MACHINE_H_ */
07070100000022000081A400000000000000000000000166622DE800004106000000000000000000000000000000000000001F00000000pg_top-4.1.1/machine/m_aix43.c/*
* pg_top - a top PostgreSQL users display for Unix
*
* SYNOPSIS: PowerPC running AIX 4.2 or higher
*
* DESCRIPTION:
* This is the machine-dependent module for AIX 4.2 and higher
* It is currenlty only tested on PowerPC architectures.
*
* TERMCAP: -lcurses
*
* CFLAGS: -DORDER -DHAVE_GETOPT
*
* LIBS: -bD:0x18000000
*
* AUTHOR: Joep Vesseur <joep@fwi.uva.nl>
*
* PATCHES: Antoine Tabary <tabary@bruyeres.cea.fr>
*/
#include "config.h"
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <nlist.h>
#include <sys/sysinfo.h>
#include <procinfo.h>
#include <sys/proc.h>
#include <sys/times.h>
#include <sys/param.h>
#include <pwd.h>
#include "pg_top.h"
#include "machine.h"
#include "utils.h"
#define PROCRESS(p) (((p)->pi_trss + (p)->pi_drss)*4)
#define PROCSIZE(p) (((p)->pi_tsize/1024+(p)->pi_dvm)*4)
#define PROCTIME(pi) (pi->pi_ru.ru_utime.tv_sec + pi->pi_ru.ru_stime.tv_sec)
/*
* structure definition taken from 'monitor' by Jussi Maki (jmaki@hut.fi)
*/
struct vmker
{
uint n0,
n1,
n2,
n3,
n4,
n5,
n6,
n7,
n8;
uint totalmem;
uint badmem; /* this is used in RS/6000 model 220 */
uint freemem;
uint n12;
uint numperm; /* this seems to keep other than text and data
* segment usage; name taken from
* /usr/lpp/bos/samples/vmtune.c */
uint totalvmem,
freevmem;
uint n15,
n16,
n17,
n18,
n19;
};
#define KMEM "/dev/kmem"
/* Indices in the nlist array */
#define X_AVENRUN 0
#define X_SYSINFO 1
#define X_VMKER 2
#define X_PROC 3
#define X_V 4
static struct nlist nlst[] = {
{"avenrun", 0, 0, 0, 0, 0}, /* 0 */
{"sysinfo", 0, 0, 0, 0, 0}, /* 1 */
{"vmker", 0, 0, 0, 0, 0}, /* 2 */
{"proc", 0, 0, 0, 0, 0}, /* 3 */
{"v", 0, 0, 0, 0, 0}, /* 4 */
{NULL, 0, 0, 0, 0, 0}
};
/* get_process_info returns handle. definition is here */
struct handle
{
struct procsinfo **next_proc;
int remaining;
};
/*
* These definitions control the format of the per-process area
*/
static char header[] =
" PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND";
/* 0123456 -- field to fill in starts at header+6 */
#define UNAME_START 7
#define Proc_format \
"%6d %-8.8s %3d %4d %5d%c %4d%c %-5s %6s %5.2f%% %5.2f%% %.14s%s"
/* these are for detailing the process states */
int process_states[9];
char *procstatenames[] = {
" none, ", " sleeping, ", " state2, ", " runnable, ",
" idle, ", " zombie, ", " stopped, ", " running, ", " swapped, ",
NULL
};
/* these are for detailing the cpu states */
int cpu_states[4];
char *cpustatenames[] = {
"idle", "user", "kernel", "wait",
NULL
};
/* these are for detailing the memory statistics */
long memory_stats[4];
char *memorynames[] = {
"K Total, ", "K Free, ", "K Buffers", NULL
};
#define M_REAL 0
#define M_REALFREE 1
#define M_BUFFERS 2
long swap_stats[3];
char *swapnames[] = {
"K Total, ", "K Free", NULL
};
#define M_VIRTUAL 0
#define M_VIRTFREE 1
char *state_abbrev[] = {
"", "sleep", "", "", "sleep", "zomb", "stop", "run", "swap"
};
/* sorting orders. first is default */
char *ordernames[] = {"cpu", "size", "res", "time", "pri", NULL};
/* compare routines */
int compare_cpu(), compare_size(), compare_res(), compare_time(),
compare_prio();
int (*proc_compares[]) () =
{
compare_cpu,
compare_size,
compare_res,
compare_time,
compare_prio,
NULL
};
/* useful externals */
extern int errno;
extern char *sys_errlist[];
long lseek();
long time();
long percentages();
/* useful globals */
int kmem; /* file descriptor */
/* offsets in kernel */
static unsigned long avenrun_offset;
static unsigned long sysinfo_offset;
static unsigned long vmker_offset;
static unsigned long proc_offset;
static unsigned long v_offset;
/* used for calculating cpu state percentages */
static long cp_time[CPU_NTIMES];
static long cp_old[CPU_NTIMES];
static long cp_diff[CPU_NTIMES];
/* the runqueue length is a cumulative value. keep old value */
long old_runque;
/* process info */
struct var v_info; /* to determine nprocs */
int nprocs; /* maximum nr of procs in proctab */
int ncpus; /* nr of cpus installed */
int ptsize; /* size of process table in bytes */
struct proc *p_proc; /* a copy of the process table */
struct procsinfo *p_info; /* needed for vm and ru info */
struct procsinfo **pref; /* processes selected for display */
int pref_len; /* number of processes selected */
/* needed to calculate WCPU */
unsigned long curtime;
/*
* Initialize globals, get kernel offsets and stuff...
*/
machine_init(struct statics *statics)
{
time_t uptime,
now;
struct tms tbuf;
if ((kmem = open(KMEM, O_RDONLY)) == -1)
{
perror(KMEM);
return -1;
}
/* get kernel symbol offsets */
if (knlist(nlst, 5, sizeof(struct nlist)) != 0)
{
perror("knlist");
return -1;
}
avenrun_offset = nlst[X_AVENRUN].n_value;
sysinfo_offset = nlst[X_SYSINFO].n_value;
vmker_offset = nlst[X_VMKER].n_value;
proc_offset = nlst[X_PROC].n_value;
v_offset = nlst[X_V].n_value;
getkval(v_offset, (caddr_t) & v_info, sizeof v_info, "v");
ncpus = v_info.v_ncpus; /* number of cpus */
nprocs = PROCMASK(PIDMAX);
if (nprocs > 1024)
nprocs = 1024;
ptsize = nprocs * sizeof(struct proc);
p_proc = (struct proc *) malloc(ptsize);
p_info = (struct procsinfo *) malloc(nprocs * sizeof(struct procsinfo));
pref = (struct procsinfo **) malloc(nprocs * sizeof(struct procsinfo *));
if (!p_proc || !p_info || !pref)
{
fprintf(stderr, "pg_top: not enough memory\n");
return -1;
}
/* set boot time */
now = time(NULL);
uptime = times(&tbuf) / HZ;
statics->boottime = now - uptime;
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
statics->order_names = ordernames;
statics->swap_names = swapnames;
return (0);
}
char *
format_header(char *uname_field)
{
register char *ptr;
ptr = header + UNAME_START;
while (*uname_field != '\0')
{
*ptr++ = *uname_field++;
}
return (header);
}
void
get_system_info(struct system_info *si)
{
int load_avg[3];
struct sysinfo s_info;
struct vmker m_info;
int i;
double total = 0;
/* get the load avarage array */
getkval(avenrun_offset, (caddr_t) load_avg, sizeof load_avg, "avenrun");
/* get the sysinfo structure */
getkval(sysinfo_offset, (caddr_t) & s_info, sizeof s_info, "sysinfo");
/* get vmker structure */
getkval(vmker_offset, (caddr_t) & m_info, sizeof m_info, "vmker");
/* convert load avarages to doubles */
for (i = 0; i < 3; i++)
si->load_avg[i] = (double) load_avg[i] / 65536.0;
/* calculate cpu state in percentages */
for (i = 0; i < CPU_NTIMES; i++)
{
cp_old[i] = cp_time[i];
cp_time[i] = s_info.cpu[i];
cp_diff[i] = cp_time[i] - cp_old[i];
total += cp_diff[i];
}
total = total / 1000.0; /* pg_top itself will correct this */
for (i = 0; i < CPU_NTIMES; i++)
{
cpu_states[i] = cp_diff[i] / total;
}
/* calculate memory statistics, scale 4K pages to megabytes */
#define PAGE_TO_MB(a) ((a)*4/1024)
memory_stats[M_REAL] = PAGE_TO_MB(m_info.totalmem);
memory_stats[M_REALFREE] = PAGE_TO_MB(m_info.freemem);
memory_stats[M_BUFFERS] = PAGE_TO_MB(m_info.numperm);
swap_stats[M_VIRTUAL] = PAGE_TO_MB(m_info.totalvmem);
swap_stats[M_VIRTFREE] = PAGE_TO_MB(m_info.freevmem);
/* runnable processes */
process_states[0] = s_info.runque - old_runque;
old_runque = s_info.runque;
si->cpustates = cpu_states;
si->memory = memory_stats;
si->swap = swap_stats;
}
static struct handle handle;
caddr_t
get_process_info(struct system_info *si, struct process_select *sel, int compare_index)
{
int i,
nproc;
int ptsize_util;
int active_procs = 0,
total_procs = 0;
struct procsinfo *pp,
**p_pref = pref;
unsigned long pctcpu;
pid_t procsindex = 0;
struct proc *p;
si->procstates = process_states;
curtime = time(0);
/* get the procsinfo structures of all running processes */
nproc = getprocs(p_info, sizeof(struct procsinfo), NULL, 0,
&procsindex, nprocs);
if (nproc < 0)
{
perror("getprocs");
quit(1);
}
/* the swapper has no cmd-line attached */
strcpy(p_info[0].pi_comm, "swapper");
/* get proc table */
ptsize_util = (PROCMASK(p_info[nproc - 1].pi_pid) + 1) * sizeof(struct proc);
getkval(proc_offset, (caddr_t) p_proc, ptsize_util, "proc");
memset(process_states, 0, sizeof process_states);
/*
* build a list of pointers to processes to show. walk through the list of
* procsinfo structures instead of the proc table since the mapping of
* procsinfo -> proctable is easy, the other way around is cumbersome
*/
for (pp = p_info, i = 0; i < nproc; pp++, i++)
{
p = &p_proc[PROCMASK(pp->pi_pid)];
/*
* AIX marks all runnable processes as ACTIVE. We want to know which
* processes are sleeping, so check used cpu ticks and adjust status
* field accordingly
*/
if (p->p_stat == SACTIVE && p->p_cpticks == 0)
p->p_stat = SIDL;
if (pp->pi_state && (sel->system || ((pp->pi_flags & SKPROC) == 0)))
{
total_procs++;
process_states[p->p_stat]++;
if ((pp->pi_state != SZOMB) &&
(sel->idle || p->p_cpticks != 0 || (p->p_stat == SACTIVE))
&& (sel->uid == -1 || pp->pi_uid == (uid_t) sel->uid))
{
*p_pref++ = pp;
active_procs++;
}
}
}
/*
* the pref array now holds pointers to the procsinfo structures in the
* p_info array that were selected for display
*/
/* sort if requested */
if (si->p_active)
qsort((char *) pref, active_procs, sizeof(struct procsinfo *),
proc_compares[compare_index]);
si->last_pid = -1; /* no way to figure out last used pid */
si->p_total = total_procs;
si->p_active = pref_len = active_procs;
handle.next_proc = pref;
handle.remaining = active_procs;
return ((caddr_t) & handle);
}
char fmt[MAX_COLS]; /* static area where result is built */
/* define what weighted cpu is. use definition of %CPU from 'man ps(1)' */
#define weighted_cpu(pp) (PROCTIME(pp) == 0 ? 0.0 : \
(((PROCTIME(pp)*100.0)/(curtime-pi->pi_start)/ncpus)))
#define double_pctcpu(p) ((double)p->p_pctcpu/(double)FLT_MODULO)
char *
format_next_process(caddr_t handle, char *(*get_userid) ())
{
register struct handle *hp;
register struct procsinfo *pi;
register struct proc *p;
char *uname;
long cpu_time;
int proc_size,
proc_ress;
char size_unit = 'K';
char ress_unit = 'K';
hp = (struct handle *) handle;
if (hp->remaining == 0)
{ /* safe guard */
fmt[0] = '\0';
return fmt;
}
pi = *(hp->next_proc++);
hp->remaining--;
p = &p_proc[PROCMASK(pi->pi_pid)];
cpu_time = PROCTIME(pi);
/* we disply sizes up to 10M in KiloBytes, beyond 10M in MegaBytes */
if ((proc_size = (pi->pi_tsize / 1024 + pi->pi_dvm) * 4) > 10240)
{
proc_size /= 1024;
size_unit = 'M';
}
if ((proc_ress = (pi->pi_trss + pi->pi_drss) * 4) > 10240)
{
proc_ress /= 1024;
ress_unit = 'M';
}
sprintf(fmt, Proc_format,
pi->pi_pid, /* PID */
(*get_userid) (pi->pi_uid), /* login name */
getpriority(PRIO_PROCESS, pi->pi_pid),
EXTRACT_NICE(p), /* fixed or vari */
proc_size, /* size */
size_unit, /* K or M */
proc_ress, /* resident */
ress_unit, /* K or M */
state_abbrev[p->p_stat], /* process state */
format_time(cpu_time), /* time used */
weighted_cpu(pi), /* WCPU */
100.0 * double_pctcpu(p), /* CPU */
printable(pi->pi_comm), /* COMM */
(pi->pi_flags & SKPROC) == 0 ? "" : " (sys)" /* kernel process? */
);
return (fmt);
}
/*
* getkval(offset, ptr, size, refstr) - get a value out of the kernel.
* "offset" is the byte offset into the kernel for the desired value,
* "ptr" points to a buffer into which the value is retrieved,
* "size" is the size of the buffer (and the object to retrieve),
* "refstr" is a reference string used when printing error meessages,
* if "refstr" starts with a '!', then a failure on read will not
* be fatal (this may seem like a silly way to do things, but I
* really didn't want the overhead of another argument).
*
*/
int
getkval(unsigned long offset, caddr_t ptr, int size, char *refstr)
{
int upper_2gb = 0;
/*
* reads above 2Gb are done by seeking to offset%2Gb, and supplying 1
* (opposed to 0) as fourth parameter to readx (see 'man kmem')
*/
if (offset > 1 << 31)
{
upper_2gb = 1;
offset &= 0x7fffffff;
}
if (lseek(kmem, offset, SEEK_SET) != offset)
{
fprintf(stderr, "pg_top: lseek failed\n");
quit(2);
}
if (readx(kmem, ptr, size, upper_2gb) != size)
{
if (*refstr == '!')
return 0;
else
{
fprintf(stderr, "pg_top: kvm_read for %s: %s\n", refstr,
sys_errlist[errno]);
quit(2);
}
}
return 1;
}
/* comparison routine for qsort */
/*
* The following code is taken from the solaris module and adjusted
* for AIX -- JV .
*/
#define ORDERKEY_PCTCPU \
if (lresult = p2->p_pctcpu - p1->p_pctcpu, \
(result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
#define ORDERKEY_CPTICKS \
if ((result = PROCTIME(pi2) - PROCTIME(pi1)) == 0)
#define ORDERKEY_STATE \
if ((result = sorted_state[p2->p_stat] \
- sorted_state[p1->p_stat]) == 0)
/* Nice values directly reflect the process' priority, and are always >0 ;-) */
#define ORDERKEY_PRIO \
if ((result = EXTRACT_NICE(p1) - EXTRACT_NICE(p2)) == 0)
#define ORDERKEY_RSSIZE \
if ((result = PROCRESS(pi2) - PROCRESS(pi1)) == 0)
#define ORDERKEY_MEM \
if ((result = PROCSIZE(pi2) - PROCSIZE(pi1)) == 0)
static unsigned char sorted_state[] =
{
0, /* not used */
0,
0,
0,
3, /* sleep */
1, /* zombie */
4, /* stop */
6, /* run */
2, /* swap */
};
/* compare_cpu - the comparison function for sorting by cpu percentage */
int
compare_cpu(struct procsinfo **ppi1, struct procsinfo **ppi2)
{
register struct procsinfo *pi1 = *ppi1,
*pi2 = *ppi2;
register struct proc *p1;
register struct proc *p2;
register int result;
register long lresult;
p1 = &p_proc[PROCMASK(pi1->pi_pid)];
p2 = &p_proc[PROCMASK(pi2->pi_pid)];
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return result;
}
/* compare_size - the comparison function for sorting by total memory usage */
int
compare_size(struct procsinfo **ppi1, struct procsinfo **ppi2)
{
register struct procsinfo *pi1 = *ppi1,
*pi2 = *ppi2;
register struct proc *p1;
register struct proc *p2;
register int result;
register long lresult;
p1 = &p_proc[PROCMASK(pi1->pi_pid)];
p2 = &p_proc[PROCMASK(pi2->pi_pid)];
ORDERKEY_MEM
ORDERKEY_RSSIZE
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return result;
}
/* compare_res - the comparison function for sorting by resident set size */
int
compare_res(struct procsinfo **ppi1, struct procsinfo **ppi2)
{
register struct procsinfo *pi1 = *ppi1,
*pi2 = *ppi2;
register struct proc *p1;
register struct proc *p2;
register int result;
register long lresult;
p1 = &p_proc[PROCMASK(pi1->pi_pid)];
p2 = &p_proc[PROCMASK(pi2->pi_pid)];
ORDERKEY_RSSIZE
ORDERKEY_MEM
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return result;
}
/* compare_time - the comparison function for sorting by total cpu time */
int
compare_time(struct procsinfo **ppi1, struct procsinfo **ppi2)
{
register struct procsinfo *pi1 = *ppi1,
*pi2 = *ppi2;
register struct proc *p1;
register struct proc *p2;
register int result;
register long lresult;
p1 = &p_proc[PROCMASK(pi1->pi_pid)];
p2 = &p_proc[PROCMASK(pi2->pi_pid)];
ORDERKEY_CPTICKS
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return result;
}
/* compare_prio - the comparison function for sorting by cpu percentage */
int
compare_prio(struct procsinfo **ppi1, struct procsinfo **ppi2)
{
register struct procsinfo *pi1 = *ppi1,
*pi2 = *ppi2;
register struct proc *p1;
register struct proc *p2;
register int result;
register long lresult;
p1 = &p_proc[PROCMASK(pi1->pi_pid)];
p2 = &p_proc[PROCMASK(pi2->pi_pid)];
ORDERKEY_PRIO
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return result;
}
int
proc_owner(int pid)
{
int uid;
register struct procsinfo **prefp = pref;
register int cnt = pref_len;
while (--cnt >= 0)
{
if ((*prefp)->pi_pid == pid)
return (*prefp)->pi_uid;
prefp++;
}
return (-1);
}
void
get_io_info(struct io_info *io_info)
{
/* Not supported yet */
memset(io_info, 0, sizeof(*io_info));
}
07070100000023000081A400000000000000000000000166622DE800004B36000000000000000000000000000000000000001E00000000pg_top-4.1.1/machine/m_aix5.c/*
* pg_top - a top PostgreSQL users display for Unix
*
* SYNOPSIS: PowerPC running AIX 5.1 or higher
*
* DESCRIPTION:
* This is the machine-dependent module for AIX 5.1 and higher (may work on
* older releases too). It is currently only tested on PowerPC
* architectures.
*
* TERMCAP: -lcurses
*
* CFLAGS: -DORDER -DHAVE_GETOPT -DHAVE_STRERROR -DMAXPROCS=10240
*
* LIBS: -lperfstat
*
* AUTHOR: Joep Vesseur <joep@fwi.uva.nl>
*
* PATCHES: Antoine Tabary <tabary@bruyeres.cea.fr>, Dan Nelson <dnelson@allantgroup.com>
*/
#define MAXPROCS 10240
#include "config.h"
#include <time.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>
#include <nlist.h>
#include <procinfo.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/sysinfo.h>
#include <sys/sysconfig.h>
#include <pwd.h>
#include <errno.h>
#include <libperfstat.h>
#include "pg_top.h"
#include "machine.h"
#include "utils.h"
#define PROCRESS(p) (((p)->pi_trss + (p)->pi_drss)*4)
#define PROCSIZE(p) (((p)->pi_tsize/1024+(p)->pi_dvm)*4)
#define PROCTIME(pi) (pi->pi_ru.ru_utime.tv_sec + pi->pi_ru.ru_stime.tv_sec)
#ifdef OLD
/*
* structure definition taken from 'monitor' by Jussi Maki (jmaki@hut.fi)
*/
struct vmker
{
uint n0,
n1,
n2,
n3,
n4,
n5,
n6,
n7,
n8;
uint totalmem;
uint badmem; /* this is used in RS/6000 model 220 */
uint freemem;
uint n12;
uint numperm; /* this seems to keep other than text and data
* segment usage; name taken from
* /usr/lpp/bos/samples/vmtune.c */
uint totalvmem,
freevmem;
uint n15,
n16,
n17,
n18,
n19;
};
#define KMEM "/dev/kmem"
/* Indices in the nlist array */
#define X_AVENRUN 0
#define X_SYSINFO 1
#define X_VMKER 2
#define X_V 3
static struct nlist nlst[] = {
{"avenrun", 0, 0, 0, 0, 0}, /* 0 */
{"sysinfo", 0, 0, 0, 0, 0}, /* 1 */
{"vmker", 0, 0, 0, 0, 0}, /* 2 */
{"v", 0, 0, 0, 0, 0}, /* 3 */
{NULL, 0, 0, 0, 0, 0}
};
#endif
/* get_process_info returns handle. definition is here */
struct handle
{
struct procentry64 **next_proc;
int remaining;
};
/*
* These definitions control the format of the per-process area
*/
static char header[] =
" PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND";
/* 0123456 -- field to fill in starts at header+6 */
#define UNAME_START 7
#define Proc_format \
"%6d %-8.8s %3d %4d %5d%c %4d%c %-5s %6s %5.2f%% %5.2f%% %.14s%s"
/* these are for detailing the process states */
int process_states[9];
char *procstatenames[] = {
" none, ", " sleeping, ", " state2, ", " runnable, ",
" idle, ", " zombie, ", " stopped, ", " running, ", " swapped, ",
NULL
};
/* these are for detailing the cpu states */
int cpu_states[CPU_NTIMES];
char *cpustatenames[] = {
"idle", "user", "kernel", "wait",
NULL
};
/* these are for detailing the memory statistics */
long memory_stats[7];
char *memorynames[] = {
"K total, ", "K buf, ", "K sys, ", "K free", NULL
};
#define M_REAL 0
#define M_BUFFERS 1
#define M_SYSTEM 2
#define M_REALFREE 3
long swap_stats[3];
char *swapnames[] = {
"K total, ", "K free", NULL
};
#define M_VIRTUAL 0
#define M_VIRTFREE 1
char *state_abbrev[] = {
NULL, NULL, NULL, NULL, "idle", "zomb", "stop", "run", "swap"
};
/* sorting orders. first is default */
char *ordernames[] = {"cpu", "size", "res", "time", "pri", NULL};
/* compare routines */
int compare_cpu(), compare_size(), compare_res(), compare_time(),
compare_prio();
int (*proc_compares[]) () =
{
compare_cpu,
compare_size,
compare_res,
compare_time,
compare_prio,
NULL
};
/* useful externals */
long percentages(int cnt, int *out, long *new, long *old, long *diffs);
char *format_time(long seconds);
#ifdef OLD
/* useful globals */
int kmem; /* file descriptor */
/* offsets in kernel */
static unsigned long avenrun_offset;
static unsigned long sysinfo_offset;
static unsigned long vmker_offset;
static unsigned long v_offset;
#endif
/* used for calculating cpu state percentages */
static long cp_time[CPU_NTIMES];
static long cp_old[CPU_NTIMES];
static long cp_diff[CPU_NTIMES];
/* the runqueue length is a cumulative value. keep old value */
long old_runque;
/* process info */
struct kernvars v_info; /* to determine nprocs */
int nprocs; /* maximum nr of procs in proctab */
int ncpus; /* nr of cpus installed */
struct procentry64 *p_info; /* needed for vm and ru info */
struct procentry64 **pref; /* processes selected for display */
struct timeval64 *cpu_proc,
*old_cpu_proc; /* total cpu used by each process */
int pref_len; /* number of processes selected */
/* needed to calculate WCPU */
unsigned long curtime;
/* needed to calculate CPU */
struct timeval curtimeval;
struct timeval lasttimeval;
#ifdef OLD
int getkval(unsigned long offset, caddr_t ptr, int size, char *refstr);
#endif
void *
xmalloc(long size)
{
void *p = malloc(size);
if (!p)
{
fprintf(stderr, "Could not allocate %ld bytes: %s\n", size, strerror(errno));
exit(1);
}
return p;
}
/*
* Initialize globals, get kernel offsets and stuff...
*/
int
machine_init(statics)
struct statics *statics;
{
#ifdef OLD
if ((kmem = open(KMEM, O_RDONLY)) == -1)
{
perror(KMEM);
return -1;
}
/* get kernel symbol offsets */
if (knlist(nlst, 4, sizeof(struct nlist)) != 0)
{
perror("knlist");
return -1;
}
avenrun_offset = nlst[X_AVENRUN].n_value;
sysinfo_offset = nlst[X_SYSINFO].n_value;
vmker_offset = nlst[X_VMKER].n_value;
v_offset = nlst[X_V].n_value;
getkval(v_offset, (caddr_t) & v_info, sizeof v_info, "v");
#else
sysconfig(SYS_GETPARMS, &v_info, sizeof v_info);
#endif
ncpus = v_info.v_ncpus; /* number of cpus */
/* procentry64 is 4912 bytes, and PROCMASK(PIDMAX) is 262144. That'd
require 1.2gb for the p_info array, which is way overkill. Raise
MAXPROCS if you have more than 10240 active processes in the system.
*/
#if 0
nprocs = PROCMASK(PIDMAX);
#else
nprocs = MAXPROCS;
#endif
cpu_proc = (struct timeval64 *) xmalloc(PROCMASK(PIDMAX) * sizeof(struct timeval64));
old_cpu_proc = (struct timeval64 *) xmalloc(PROCMASK(PIDMAX) * sizeof(struct timeval64));
p_info = (struct procentry64 *) xmalloc(nprocs * sizeof(struct procentry64));
pref = (struct procentry64 **) xmalloc(nprocs * sizeof(struct procentry64 *));
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
statics->swap_names = swapnames;
statics->order_names = ordernames;
return (0);
}
char *format_header(uname_field)
register char *uname_field;
{
register char *ptr;
ptr = header + UNAME_START;
while (*uname_field != '\0')
{
*ptr++ = *uname_field++;
}
return (header);
}
void
get_system_info(si)
struct system_info *si;
{
#ifdef OLD
long long load_avg[3];
struct sysinfo64 s_info;
struct vmker m_info;
#else
perfstat_memory_total_t m_info1;
perfstat_cpu_total_t s_info1;
#endif
int i;
int total = 0;
#ifdef OLD
/* get the load avarage array */
getkval(avenrun_offset, (caddr_t) load_avg, sizeof load_avg, "avenrun");
/* get the sysinfo structure */
getkval(sysinfo_offset, (caddr_t) & s_info, sizeof s_info, "sysinfo64");
/* get vmker structure */
getkval(vmker_offset, (caddr_t) & m_info, sizeof m_info, "vmker");
#else
/* cpu stats */
perfstat_cpu_total(NULL, &s_info1, sizeof s_info1, 1);
/* memory stats */
perfstat_memory_total(NULL, &m_info1, sizeof m_info1, 1);
#endif
#ifdef OLD
/* convert load avarages to doubles */
for (i = 0; i < 3; i++)
si->load_avg[i] = (double) load_avg[i] / 65536.0;
/* calculate cpu state in percentages */
for (i = 0; i < CPU_NTIMES; i++)
{
cp_old[i] = cp_time[i];
cp_time[i] = s_info.cpu[i];
cp_diff[i] = cp_time[i] - cp_old[i];
total += cp_diff[i];
}
#else
/* convert load avarages to doubles */
for (i = 0; i < 3; i++)
si->load_avg[i] = (double) s_info1.loadavg[i] / (1 << SBITS);
/* calculate cpu state in percentages */
for (i = 0; i < CPU_NTIMES; i++)
{
cp_old[i] = cp_time[i];
cp_time[i] = (i == CPU_IDLE ? s_info1.idle :
i == CPU_USER ? s_info1.user :
i == CPU_KERNEL ? s_info1.sys :
i == CPU_WAIT ? s_info1.wait : 0);
cp_diff[i] = cp_time[i] - cp_old[i];
total += cp_diff[i];
}
#endif
for (i = 0; i < CPU_NTIMES; i++)
{
cpu_states[i] = 1000 * cp_diff[i] / total;
}
/* calculate memory statistics, scale 4K pages */
#ifdef OLD
#define PAGE_TO_MB(a) ((a)*4/1024)
memory_stats[M_TOTAL] = PAGE_TO_MB(m_info.totalmem + m_info.totalvmem);
memory_stats[M_REAL] = PAGE_TO_MB(m_info.totalmem);
memory_stats[M_REALFREE] = PAGE_TO_MB(m_info.freemem);
memory_stats[M_BUFFERS] = PAGE_TO_MB(m_info.numperm);
swap_stats[M_VIRTUAL] = PAGE_TO_MB(m_info.totalvmem);
swap_stats[M_VIRTFREE] = PAGE_TO_MB(m_info.freevmem);
#else
#define PAGE_TO_KB(a) ((a)*4)
memory_stats[M_REAL] = PAGE_TO_KB(m_info1.real_total);
memory_stats[M_BUFFERS] = PAGE_TO_KB(m_info1.numperm);
#ifdef _AIXVERSION_520
memory_stats[M_SYSTEM] = PAGE_TO_KB(m_info1.real_system);
#endif
memory_stats[M_REALFREE] = PAGE_TO_KB(m_info1.real_free);
swap_stats[M_VIRTUAL] = PAGE_TO_KB(m_info1.pgsp_total);
swap_stats[M_VIRTFREE] = PAGE_TO_KB(m_info1.pgsp_free);
#endif
/* runnable processes */
#ifdef OLD
process_states[0] = s_info.runque - old_runque;
old_runque = s_info.runque;
#else
process_states[0] = s_info1.runque - old_runque;
old_runque = s_info1.runque;
#endif
si->cpustates = cpu_states;
si->memory = memory_stats;
si->swap = swap_stats;
}
static struct handle handle;
caddr_t
get_process_info(si, sel, compare_index)
struct system_info *si;
struct process_select *sel;
int compare_index;
{
int i,
nproc;
int active_procs = 0,
total_procs = 0;
struct procentry64 *pp,
**p_pref = pref;
struct timeval64 *cpu_proc_temp;
double timediff;
pid_t procsindex = 0;
si->procstates = process_states;
curtime = time(0);
lasttimeval = curtimeval;
gettimeofday(&curtimeval, NULL);
/* get the procentry64 structures of all running processes */
nproc = getprocs64(p_info, sizeof(struct procentry64), NULL, 0,
&procsindex, nprocs);
if (nproc < 0)
{
perror("getprocs64");
quit(1);
}
/* the swapper has no cmd-line attached */
strcpy(p_info[0].pi_comm, "swapper");
if (lasttimeval.tv_sec)
{
timediff = (curtimeval.tv_sec - lasttimeval.tv_sec) +
1.0 * (curtimeval.tv_usec - lasttimeval.tv_usec) / uS_PER_SECOND;
}
/*
* The pi_cpu value is wildly inaccurate. The maximum value is 120, but
* when the scheduling timer fires, the field is zeroed for all processes
* and ramps up over a short period of time. Instead of using this weird
* number, manually calculate an accurate value from the rusage data.
* Store this run's rusage in cpu_proc[pid], and subtract from
* old_cpu_proc.
*/
for (pp = p_info, i = 0; i < nproc; pp++, i++)
{
pid_t pid = PROCMASK(pp->pi_pid);
/* total system and user time into cpu_proc */
cpu_proc[pid] = pp->pi_ru.ru_utime;
cpu_proc[pid].tv_sec += pp->pi_ru.ru_stime.tv_sec;
cpu_proc[pid].tv_usec += pp->pi_ru.ru_stime.tv_usec;
if (cpu_proc[pid].tv_usec > NS_PER_SEC)
{
cpu_proc[pid].tv_sec++;
cpu_proc[pid].tv_usec -= NS_PER_SEC;
}
/*
* If this process was around during the previous update, calculate a
* true %CPU. If not, convert the kernel's cpu value from its 120-max
* value to a 10000-max one.
*/
if (old_cpu_proc[pid].tv_sec == 0 && old_cpu_proc[pid].tv_usec == 0)
pp->pi_cpu = pp->pi_cpu * 10000 / 120;
else
pp->pi_cpu = ((cpu_proc[pid].tv_sec - old_cpu_proc[pid].tv_sec) +
1.0 * (cpu_proc[pid].tv_usec - old_cpu_proc[pid].tv_usec) / NS_PER_SEC) / timediff * 10000;
}
/*
* remember our current values as old_cpu_proc, and zero out cpu_proc for
* the next update cycle
*/
memset(old_cpu_proc, 0, sizeof(struct timeval64) * nprocs);
cpu_proc_temp = cpu_proc;
cpu_proc = old_cpu_proc;
old_cpu_proc = cpu_proc_temp;
memset(process_states, 0, sizeof process_states);
/* build a list of pointers to processes to show. */
for (pp = p_info, i = 0; i < nproc; pp++, i++)
{
/*
* AIX marks all runnable processes as ACTIVE. We want to know which
* processes are sleeping, so check used cpu and adjust status field
* accordingly
*/
if (pp->pi_state == SACTIVE && pp->pi_cpu == 0)
pp->pi_state = SIDL;
if (pp->pi_state && (sel->system || ((pp->pi_flags & SKPROC) == 0)))
{
total_procs++;
process_states[pp->pi_state]++;
if ((pp->pi_state != SZOMB) &&
(sel->idle || pp->pi_cpu != 0 || (pp->pi_state == SACTIVE))
&& (sel->uid == -1 || pp->pi_uid == (uid_t) sel->uid))
{
*p_pref++ = pp;
active_procs++;
}
}
}
/*
* the pref array now holds pointers to the procentry64 structures in the
* p_info array that were selected for display
*/
/* sort if requested */
if (proc_compares[compare_index] != NULL)
qsort((char *) pref, active_procs, sizeof(struct procentry64 *),
proc_compares[compare_index]);
si->last_pid = -1; /* no way to figure out last used pid */
si->p_total = total_procs;
si->p_active = pref_len = active_procs;
handle.next_proc = pref;
handle.remaining = active_procs;
return ((caddr_t) & handle);
}
char fmt[128]; /* static area where result is built */
/* define what weighted cpu is. use definition of %CPU from 'man ps(1)' */
#define weighted_cpu(pp) (PROCTIME(pp) == 0 ? 0.0 : \
(((PROCTIME(pp)*100.0)/(curtime-pi->pi_start))))
char *format_next_process(handle, get_userid)
caddr_t handle;
char *(*get_userid) ();
{
register struct handle *hp;
register struct procentry64 *pi;
long cpu_time;
int proc_size,
proc_ress;
char size_unit = 'K';
char ress_unit = 'K';
hp = (struct handle *) handle;
if (hp->remaining == 0)
{ /* safe guard */
fmt[0] = '\0';
return fmt;
}
pi = *(hp->next_proc++);
hp->remaining--;
cpu_time = PROCTIME(pi);
/* we disply sizes up to 10M in KiloBytes, beyond 10M in MegaBytes */
if ((proc_size = (pi->pi_tsize / 1024 + pi->pi_dvm) * 4) > 10240)
{
proc_size /= 1024;
size_unit = 'M';
}
if ((proc_ress = (pi->pi_trss + pi->pi_drss) * 4) > 10240)
{
proc_ress /= 1024;
ress_unit = 'M';
}
sprintf(fmt, Proc_format,
pi->pi_pid, /* PID */
(*get_userid) (pi->pi_uid), /* login name */
pi->pi_nice, /* fixed or vari */
getpriority(PRIO_PROCESS, pi->pi_pid),
proc_size, /* size */
size_unit, /* K or M */
proc_ress, /* resident */
ress_unit, /* K or M */
state_abbrev[pi->pi_state], /* process state */
format_time(cpu_time), /* time used */
weighted_cpu(pi), /* WCPU */
pi->pi_cpu / 100.0, /* CPU */
printable(pi->pi_comm), /* COMM */
(pi->pi_flags & SKPROC) == 0 ? "" : " (sys)" /* kernel process? */
);
return (fmt);
}
#ifdef OLD
/*
* getkval(offset, ptr, size, refstr) - get a value out of the kernel.
* "offset" is the byte offset into the kernel for the desired value,
* "ptr" points to a buffer into which the value is retrieved,
* "size" is the size of the buffer (and the object to retrieve),
* "refstr" is a reference string used when printing error meessages,
* if "refstr" starts with a '!', then a failure on read will not
* be fatal (this may seem like a silly way to do things, but I
* really didn't want the overhead of another argument).
*
*/
int
getkval(offset, ptr, size, refstr)
unsigned long offset;
caddr_t ptr;
int size;
char *refstr;
{
int upper_2gb = 0;
/*
* reads above 2Gb are done by seeking to offset%2Gb, and supplying 1
* (opposed to 0) as fourth parameter to readx (see 'man kmem')
*/
if (offset > 1 << 31)
{
upper_2gb = 1;
offset &= 0x7fffffff;
}
if (lseek(kmem, offset, SEEK_SET) != offset)
{
fprintf(stderr, "pg_top: lseek failed\n");
quit(2);
}
if (readx(kmem, ptr, size, upper_2gb) != size)
{
if (*refstr == '!')
return 0;
else
{
fprintf(stderr, "pg_top: kvm_read for %s: %s\n", refstr,
sys_errlist[errno]);
quit(2);
}
}
return 1;
}
#endif
/* comparison routine for qsort */
/*
* The following code is taken from the solaris module and adjusted
* for AIX -- JV .
*/
#define ORDERKEY_PCTCPU \
if ((result = pi2->pi_cpu - pi1->pi_cpu) == 0)
#define ORDERKEY_CPTICKS \
if ((result = PROCTIME(pi2) - PROCTIME(pi1)) == 0)
#define ORDERKEY_STATE \
if ((result = sorted_state[pi2->pi_state] \
- sorted_state[pi1->pi_state]) == 0)
/* Nice values directly reflect the process' priority, and are always >0 ;-) */
#define ORDERKEY_PRIO \
if ((result = pi1->pi_nice - pi2->pi_nice) == 0)
#define ORDERKEY_RSSIZE \
if ((result = PROCRESS(pi2) - PROCRESS(pi1)) == 0)
#define ORDERKEY_MEM \
if ((result = PROCSIZE(pi2) - PROCSIZE(pi1)) == 0)
static unsigned char sorted_state[] =
{
0, /* not used */
0,
0,
0,
3, /* sleep */
1, /* zombie */
4, /* stop */
6, /* run */
2, /* swap */
};
/* compare_cpu - the comparison function for sorting by cpu percentage */
int
compare_cpu(ppi1, ppi2)
struct procentry64 **ppi1;
struct procentry64 **ppi2;
{
register struct procentry64 *pi1 = *ppi1,
*pi2 = *ppi2;
register int result;
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return result;
}
/* compare_size - the comparison function for sorting by total memory usage */
int
compare_size(ppi1, ppi2)
struct procentry64 **ppi1;
struct procentry64 **ppi2;
{
register struct procentry64 *pi1 = *ppi1,
*pi2 = *ppi2;
register int result;
ORDERKEY_MEM
ORDERKEY_RSSIZE
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return result;
}
/* compare_res - the comparison function for sorting by resident set size */
int
compare_res(ppi1, ppi2)
struct procentry64 **ppi1;
struct procentry64 **ppi2;
{
register struct procentry64 *pi1 = *ppi1,
*pi2 = *ppi2;
register int result;
ORDERKEY_RSSIZE
ORDERKEY_MEM
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return result;
}
/* compare_time - the comparison function for sorting by total cpu time */
int
compare_time(ppi1, ppi2)
struct procentry64 **ppi1;
struct procentry64 **ppi2;
{
register struct procentry64 *pi1 = *ppi1,
*pi2 = *ppi2;
register int result;
ORDERKEY_CPTICKS
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return result;
}
/* compare_prio - the comparison function for sorting by cpu percentage */
int
compare_prio(ppi1, ppi2)
struct procentry64 **ppi1;
struct procentry64 **ppi2;
{
register struct procentry64 *pi1 = *ppi1,
*pi2 = *ppi2;
register int result;
ORDERKEY_PRIO
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return result;
}
int
proc_owner(pid)
int pid;
{
register struct procentry64 **prefp = pref;
register int cnt = pref_len;
while (--cnt >= 0)
{
if ((*prefp)->pi_pid == pid)
return (*prefp)->pi_uid;
prefp++;
}
return (-1);
}
void
get_io_info(struct io_info *io_info)
{
/* Not supported yet */
memset(io_info, 0, sizeof(*io_info));
}
07070100000024000081A400000000000000000000000166622DE8000011B9000000000000000000000000000000000000002000000000pg_top-4.1.1/machine/m_common.c/*
* machine/m_common.c
*
* Functionalities common to all the platforms.
*
* Copyright (c) 2013 VMware, Inc. All Rights Reserved.
*/
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <libpq-fe.h>
#include "machine.h"
/* Query to fetch information about database activity */
#define QUERY_STAT_DB \
"SELECT datid, datname, numbackends, xact_commit, xact_rollback, \n" \
" blks_read, blks_hit, tup_returned, tup_fetched, \n" \
" tup_inserted, tup_updated, tup_deleted, conflicts \n" \
"FROM pg_stat_database;"
char *backendstatenames[] =
{
"", "idle", "active", "idltxn", "fast", "abort", "disabl", NULL
};
char *procstatenames[] =
{
" other background task(s), ", " idle, ", " active, ", " idle txn, ",
" fastpath, ", " aborted, ", " disabled, ", NULL
};
char fmt_header_replication[] =
" PID USERNAME APPLICATION CLIENT STATE PRIMARY SENT WRITE FLUSH REPLAY SLAG WLAG FLAG RLAG";
/*
* Get database info via the above QUERY_STAT_DB info.
* Returns rate info on the various statistics by comparing current
* values with previous values.
*/
void
get_database_info(struct db_info *db_info, struct pg_conninfo_ctx *conninfo)
{
struct timeval thistime;
double timediff;
int i;
int rows;
PGresult *pgresult = NULL;
struct db_info cur_info;
static struct timeval lasttime;
static struct db_info last_db_info;
/* calculate the time difference since our last check */
gettimeofday(&thistime, 0);
if (lasttime.tv_sec)
timediff = ((thistime.tv_sec - lasttime.tv_sec) +
(thistime.tv_usec - lasttime.tv_usec) * 1e-6);
else
timediff = 0;
lasttime = thistime;
rows = 0;
connect_to_db(conninfo);
if (conninfo->connection != NULL)
{
pgresult = PQexec(conninfo->connection, QUERY_STAT_DB);
if (PQresultStatus(pgresult) == PGRES_TUPLES_OK)
rows = PQntuples(pgresult);
}
if (rows == 0)
{
/* Database probably stopped, clear current and last */
memset(&last_db_info, 0, sizeof(last_db_info));
}
memset(&cur_info, 0, sizeof(cur_info));
for (i = 0; i < rows; i++)
{
PQgetvalue(pgresult, i, 2);
/* Count all databases, even with no active backends */
cur_info.numDb++;
cur_info.numXact += atoi(PQgetvalue(pgresult, i, 3));
cur_info.numRollback += atoi(PQgetvalue(pgresult, i, 4));
cur_info.numBlockRead += atoi(PQgetvalue(pgresult, i, 5));
cur_info.numBlockHit += atoi(PQgetvalue(pgresult, i, 6));
cur_info.numTupleFetched += atoi(PQgetvalue(pgresult, i, 8));
cur_info.numTupleAltered += atoi(PQgetvalue(pgresult, i, 9)) +
atoi(PQgetvalue(pgresult, i, 10)) +
atoi(PQgetvalue(pgresult, i, 11));
cur_info.numConflict += atoi(PQgetvalue(pgresult, i, 12));
}
if (pgresult != NULL)
PQclear(pgresult);
disconnect_from_db(conninfo);
if (timediff <= 0)
{
last_db_info = cur_info;
memset(db_info, 0, sizeof(*db_info));
return;
}
/* Compute the rate information */
db_info->numDb = cur_info.numDb;
db_info->numXact = (double)(cur_info.numXact - last_db_info.numXact) / timediff;
db_info->numRollback = (double)(cur_info.numRollback - last_db_info.numRollback) / timediff;
db_info->numBlockRead = (double)(cur_info.numBlockRead - last_db_info.numBlockRead) / timediff;
db_info->numBlockHit = (double)(cur_info.numBlockHit - last_db_info.numBlockHit) / timediff;
db_info->numTupleFetched = (double)(cur_info.numTupleFetched - last_db_info.numTupleFetched) / timediff;
db_info->numTupleAltered = (double)(cur_info.numTupleAltered - last_db_info.numTupleAltered) / timediff;
db_info->numConflict = (double)(cur_info.numConflict - last_db_info.numConflict) / timediff;
last_db_info = cur_info;
}
void
update_state(int *pgstate, char *state)
{
/*
* pgstate is always cleared to 0 when the node is created, so it will be
* to STATE_UNDEFINED if there is no match when comparing the state
*/
if (strcmp(state, "idle") == 0)
*pgstate = STATE_IDLE;
else if (strcmp(state, "active") == 0)
*pgstate = STATE_RUNNING;
else if (strcmp(state, "idle in transaction") == 0)
*pgstate = STATE_IDLEINTRANSACTION;
else if (strcmp(state, "fastpath function call") == 0)
*pgstate = STATE_FASTPATH;
else if (strcmp(state, "idle in transaction (aborted)") == 0)
*pgstate = STATE_IDLEINTRANSACTION_ABORTED;
else if (strcmp(state, "disabled") == 0)
*pgstate = STATE_DISABLED;
}
void
update_str(char **old, char *new)
{
if (*old == NULL)
*old = strdup(new);
else if (strcmp(*old, new) != 0)
{
free(*old);
*old = strdup(new);
}
}
07070100000025000081A400000000000000000000000166622DE800007118000000000000000000000000000000000000002100000000pg_top-4.1.1/machine/m_freebsd.c/*
* pg_top - a top PostgreSQL users display for Unix
*
* SYNOPSIS: For FreeBSD-2.x, 3.x, 4.x, and 5.x
*
* DESCRIPTION:
* Originally written for BSD4.4 system by Christos Zoulas.
* Ported to FreeBSD 2.x by Steven Wallace && Wolfram Schneider
* Order support hacked in from top-3.5beta6/machine/m_aix41.c
* by Monte Mitzelfelt
* Ported to FreeBSD 5.x by William LeFebvre
*
* AUTHOR: Christos Zoulas <christos@ee.cornell.edu>
* Steven Wallace <swallace@freebsd.org>
* Wolfram Schneider <wosch@FreeBSD.org>
*/
#include <sys/time.h>
#include <sys/types.h>
#include <sys/signal.h>
#include <sys/param.h>
#include <err.h>
#include "config.h"
#include <stdio.h>
#include <string.h>
#include <nlist.h>
#include <math.h>
#include <kvm.h>
#include <pwd.h>
#include <sys/errno.h>
#include <sys/sysctl.h>
#include <sys/dkstat.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/vmmeter.h>
#include <sys/resource.h>
#include <sys/rtprio.h>
#include <sys/tree.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
/* Swap */
#include <stdlib.h>
#include <sys/conf.h>
#include <osreldate.h> /* for changes in kernel structures */
#include "pg_top.h"
#include "machine.h"
#include "utils.h"
/* declarations for load_avg */
#include "loadavg.h"
#define GETSYSCTL(name, var) getsysctl(name, &(var), sizeof(var))
static int getkval __P((unsigned long, int *, int, char *));
extern char *printable __P((char *));
static void getsysctl(const char *name, void *ptr, size_t len);
int swapmode __P((int *retavail, int *retfree));
static int maxcpu;
static int maxid;
static int ncpus;
static u_long cpumask;
static long *times;
static long *pcpu_cp_time;
static long *pcpu_cp_old;
static long *pcpu_cp_diff;
static int64_t * pcpu_cpu_states;
static int smpmode;
static int namelength;
static int cmdlength;
/* get_process_info passes back a handle. This is what it looks like: */
struct handle
{
struct kinfo_proc **next_proc; /* points to next valid proc pointer */
int remaining; /* number of pointers remaining */
};
struct pg_proc
{
RB_ENTRY(pg_proc) entry;
pid_t pid;
/* This will be the previous copy of ki_rusage. */
struct rusage ki_rusage;
char *name;
char *usename;
int pgstate;
unsigned long xtime;
unsigned long qtime;
unsigned int locks;
/* Replication data */
char *application_name;
char *client_addr;
char *repstate;
char *primary;
char *sent;
char *write;
char *flush;
char *replay;
long long sent_lag;
long long write_lag;
long long flush_lag;
long long replay_lag;
};
int topproccmp(struct pg_proc *, struct pg_proc *);
RB_HEAD(pgproc, pg_proc) head_proc = RB_INITIALIZER(&head_proc);
RB_PROTOTYPE(pgproc, pg_proc, entry, topproccmp)
RB_GENERATE(pgproc, pg_proc, entry, topproccmp)
/* macros to access process information */
#if OSMAJOR <= 4
#define PP(pp, field) ((pp)->kp_proc . p_##field)
#define EP(pp, field) ((pp)->kp_eproc . e_##field)
#define VP(pp, field) ((pp)->kp_eproc.e_vm . vm_##field)
#define PRUID(pp) ((pp)->kp_eproc.e_pcred.p_ruid)
#else
#define PP(pp, field) ((pp)->ki_##field)
#define VP(pp, field) ((pp)->ki_##field)
#define PRUID(pp) ((pp)->ki_ruid)
#endif
#define RU(pp) (&(pp)->ki_rusage)
/* what we consider to be process size: */
#if OSMAJOR <= 4
#define PROCSIZE(pp) (VP((pp), map.size) / 1024)
#else
#define PROCSIZE(pp) (((pp)->ki_size) / 1024)
#endif
/* for 5.x and higher we show thread count */
#if OSMAJOR >= 5
#define SHOW_THREADS
#endif
/* definitions for indices in the nlist array */
static struct nlist nlst[] = {
#define X_CCPU 0
{"_ccpu"},
#define X_CP_TIME 1
{"_cp_time"},
#define X_AVENRUN 2
{"_averunnable"},
#define X_BUFSPACE 3
{"_bufspace"}, /* K in buffer cache */
#define X_CNT 4
{"_cnt"}, /* struct vmmeter cnt */
/* Last pid */
#define X_LASTPID 5
{"_nextpid"},
#define X_BOOTTIME 6
{"_boottime"},
{0}
};
/*
* These definitions control the format of the per-process area
*/
static char header[] =
" PID %-*.*s SIZE RES STATE XTIME QTIME CPU LOCKS COMMAND";
/* process state names for the "STATE" column of the display */
/* the extra nulls in the string "run" are for adding a slash and
the processor number when needed */
char *state_abbrev[] =
{
"", "START", "RUN\0\0\0", "SLEEP", "STOP", "ZOMB",
};
char fmt_header_io[] =
"PID USERNAME VCSW IVCSW READ WRITE FAULT TOTAL COMMAND";
static kvm_t * kd;
/* values that we stash away in _init and use in later routines */
static double logcpu;
/* these are retrieved from the kernel in _init */
static load_avg ccpu;
/* these are offsets obtained via nlist and used in the get_ functions */
static unsigned long cp_time_offset;
static unsigned long avenrun_offset;
static unsigned long lastpid_offset;
static int lastpid;
static unsigned long cnt_offset;
static unsigned long bufspace_offset;
/* these are for calculating cpu state percentages */
static int64_t cp_time[CPUSTATES];
static int64_t cp_old[CPUSTATES];
static int64_t cp_diff[CPUSTATES];
/* these are for detailing the process states */
int process_states[6];
/* these are for detailing the cpu states */
int64_t cpu_states[CPUSTATES];
char *cpustatenames[] = {
"user", "nice", "system", "interrupt", "idle", NULL
};
/* these are for detailing the memory statistics */
long memory_stats[7];
char *memorynames[] = {
"K Active, ", "K Inact, ", "K Wired, ", "K Cache, ", "K Buf, ", "K Free",
NULL
};
long swap_stats[7];
char *swapnames[] = {
/* 0 1 2 3 4 5 */
"K Total, ", "K Used, ", "K Free, ", "% Inuse, ", "K In, ", "K Out",
NULL
};
/* these are for keeping track of the proc array */
static int nproc;
static int onproc = -1;
static int pref_len;
static struct kinfo_proc *pbase;
static struct kinfo_proc **pref;
/* these are for getting the memory statistics */
static int pageshift; /* log base 2 of the pagesize */
/* define pagetok in terms of pageshift */
#define pagetok(size) ((size) << pageshift)
/* useful externals */
long percentages();
/* sorting orders. first is default */
char *ordernames[] = {"cpu", "size", "res", "time", "pri", NULL};
/* compare routines */
int proc_compare(), compare_size(), compare_res(), compare_time(), compare_prio();
int (*proc_compares[]) () =
{
proc_compare,
compare_size,
compare_res,
compare_time,
compare_prio,
NULL
};
int
machine_init(struct statics *statics)
{
register int pagesize;
size_t size;
struct passwd *pw;
int i,
j,
empty;
size = sizeof(smpmode);
if ((sysctlbyname("machdep.smp_active", &smpmode, &size, NULL, 0) != 0 &&
sysctlbyname("smp.smp_active", &smpmode, &size, NULL, 0) != 0) ||
size != sizeof(smpmode))
smpmode = 0;
while ((pw = getpwent()) != NULL)
{
if (strlen(pw->pw_name) > namelength)
namelength = strlen(pw->pw_name);
}
if (namelength < 8)
namelength = 8;
if (smpmode && namelength > 13)
namelength = 13;
else if (namelength > 15)
namelength = 15;
/*
* Silence kvm_open in the event that the pid from the database is gone
* before we ask the operating system about it.
*/
if ((kd = kvm_open(NULL, "/dev/null", NULL, O_RDONLY, NULL)) == NULL)
return -1;
/* get number of cpus */
GETSYSCTL("kern.ccpu", ccpu);
/* stash away certain offsets for later use */
cp_time_offset = nlst[X_CP_TIME].n_value;
avenrun_offset = nlst[X_AVENRUN].n_value;
lastpid_offset = nlst[X_LASTPID].n_value;
cnt_offset = nlst[X_CNT].n_value;
bufspace_offset = nlst[X_BUFSPACE].n_value;
/* this is used in calculating WCPU -- calculate it ahead of time */
logcpu = log(loaddouble(ccpu));
pbase = NULL;
pref = NULL;
nproc = 0;
onproc = -1;
/* get the page size with "getpagesize" and calculate pageshift from it */
pagesize = getpagesize();
pageshift = 0;
while (pagesize > 1)
{
pageshift++;
pagesize >>= 1;
}
/* we only need the amount of log(2)1024 for our conversion */
pageshift -= LOG1024;
/* fill in the statics information */
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
statics->swap_names = swapnames;
statics->order_names = ordernames;
statics->flags.fullcmds = 1;
/* Allocate state for per-CPU stats. */
cpumask = 0;
ncpus = 0;
GETSYSCTL("kern.smp.maxcpus", maxcpu);
size = sizeof(long) * maxcpu * CPUSTATES;
times = malloc(size);
if (times == NULL)
err(1, "malloc %zd bytes", size);
if (sysctlbyname("kern.cp_times", times, &size, NULL, 0) == -1)
err(1, "sysctlbyname kern.cp_times");
pcpu_cp_time = calloc(1, size);
maxid = (size / CPUSTATES / sizeof(long)) - 1;
for (i = 0; i <= maxid; i++)
{
empty = 1;
for (j = 0; empty && j < CPUSTATES; j++)
{
if (times[i * CPUSTATES + j] != 0)
empty = 0;
}
if (!empty)
{
cpumask |= (1ul << i);
ncpus++;
}
}
size = sizeof(long) * ncpus * CPUSTATES;
pcpu_cp_old = calloc(1, size);
pcpu_cp_diff = calloc(1, size);
pcpu_cpu_states = calloc(1, size);
statics->ncpus = ncpus;
/* all done! */
return (0);
}
char *
format_header(char *uname_field)
{
static char Header[128];
snprintf(Header, sizeof(Header), header, namelength, namelength,
uname_field);
cmdlength = 80 - strlen(Header) + 6;
return Header;
}
static int swappgsin = -1;
static int swappgsout = -1;
extern struct timeval timeout;
void
get_system_info(struct system_info *si)
{
struct loadavg sysload;
size_t size;
int i,
j;
/* get the CPU stats */
size = (maxid + 1) * CPUSTATES * sizeof(long);
if (sysctlbyname("kern.cp_times", pcpu_cp_time, &size, NULL, 0) == -1)
err(1, "sysctlbyname kern.cp_times");
GETSYSCTL("kern.cp_time", cp_time);
GETSYSCTL("vm.loadavg", sysload);
GETSYSCTL("kern.lastpid", lastpid);
/* convert load averages to doubles */
for (i = 0; i < 3; i++)
si->load_avg[i] = (double) sysload.ldavg[i] / sysload.fscale;
/* convert cp_time counts to percentages */
for (i = j = 0; i <= maxid; i++)
{
if ((cpumask & (1ul << i)) == 0)
continue;
percentages(CPUSTATES, &pcpu_cpu_states[j * CPUSTATES],
&pcpu_cp_time[j * CPUSTATES],
&pcpu_cp_old[j * CPUSTATES],
&pcpu_cp_diff[j * CPUSTATES]);
j++;
}
percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
/* sum memory & swap statistics */
{
static unsigned int swap_delay = 0;
static int swapavail = 0;
static int swapfree = 0;
static int bufspace = 0;
static int nspgsin,
nspgsout;
/*
* Use this temporary int array because we use longs for the other
* patforms.
*/
int tmp_memory_stats[7];
GETSYSCTL("vfs.bufspace", bufspace);
GETSYSCTL("vm.stats.vm.v_active_count", tmp_memory_stats[0]);
GETSYSCTL("vm.stats.vm.v_inactive_count", tmp_memory_stats[1]);
GETSYSCTL("vm.stats.vm.v_wire_count", tmp_memory_stats[2]);
GETSYSCTL("vm.stats.vm.v_cache_count", tmp_memory_stats[3]);
GETSYSCTL("vm.stats.vm.v_free_count", tmp_memory_stats[5]);
GETSYSCTL("vm.stats.vm.v_swappgsin", nspgsin);
GETSYSCTL("vm.stats.vm.v_swappgsout", nspgsout);
/* convert memory stats to Kbytes */
memory_stats[0] = pagetok(tmp_memory_stats[0]);
memory_stats[1] = pagetok(tmp_memory_stats[1]);
memory_stats[2] = pagetok(tmp_memory_stats[2]);
memory_stats[3] = pagetok(tmp_memory_stats[3]);
memory_stats[4] = bufspace / 1024;
memory_stats[5] = pagetok(tmp_memory_stats[5]);
memory_stats[6] = -1;
/* first interval */
if (swappgsin < 0)
{
swap_stats[4] = 0;
swap_stats[5] = 0;
}
/* compute differences between old and new swap statistic */
else
{
swap_stats[4] = pagetok(((nspgsin - swappgsin)));
swap_stats[5] = pagetok(((nspgsout - swappgsout)));
}
swappgsin = nspgsin;
swappgsout = nspgsout;
/* call CPU heavy swapmode() only for changes */
if (swap_stats[4] > 0 || swap_stats[5] > 0 || swap_delay == 0)
{
swap_stats[3] = swapmode(&swapavail, &swapfree);
swap_stats[0] = swapavail;
swap_stats[1] = swapavail - swapfree;
swap_stats[2] = swapfree;
}
swap_delay = 1;
swap_stats[6] = -1;
}
/* set arrays and strings */
si->cpustates = cpu_states;
si->memory = memory_stats;
si->swap = swap_stats;
if (lastpid > 0)
{
si->last_pid = lastpid;
}
else
{
si->last_pid = -1;
}
}
static struct handle handle;
static int show_fullcmd;
caddr_t
get_process_info(struct system_info *si,
struct process_select *sel,
int compare_index,
struct pg_conninfo_ctx *conninfo,
int mode)
{
register int i;
register int total_procs;
register int active_procs;
register struct kinfo_proc **prefp;
register struct kinfo_proc *pp;
/* these are copied out of sel for speed */
int show_idle;
int show_self;
int show_system = 0;
PGresult *pgresult = NULL;
struct pg_proc *n, *p;
nproc = 0;
connect_to_db(conninfo);
if (conninfo->connection != NULL)
{
if (mode == MODE_REPLICATION)
{
pgresult = pg_replication(conninfo->connection);
}
else
{
pgresult = pg_processes(conninfo->connection);
}
nproc = PQntuples(pgresult);
if (nproc > onproc)
pbase = (struct kinfo_proc *)
realloc(pbase, sizeof(struct kinfo_proc) * nproc);
pgresult = pg_processes(conninfo->connection);
}
if (nproc > onproc)
pref = (struct kinfo_proc **) realloc(pref, sizeof(struct kinfo_proc *)
* (onproc = nproc));
if (pref == NULL)
{
(void) fprintf(stderr, "pg_top: Out of memory.\n");
quit(23);
}
/* get a pointer to the states summary array */
si->procstates = process_states;
/* set up flags which define what we are going to select */
show_idle = sel->idle;
show_self = 0;
show_fullcmd = sel->fullcmd;
/* count up process states and get pointers to interesting procs */
total_procs = 0;
active_procs = 0;
memset((char *) process_states, 0, sizeof(process_states));
prefp = pref;
for (pp = pbase, i = 0; i < nproc; pp++, i++)
{
struct kinfo_proc *junk2;
int junk;
junk2 = kvm_getprocs(kd, KERN_PROC_PID,
atoi(PQgetvalue(pgresult, i, 0)), &junk);
if (junk2 == NULL)
{
continue;
}
/*
* FIXME: This memcpy is so not elegant and the reason why I'm doing
* it...
*/
memcpy(&pbase[i], &junk2[0], sizeof(struct kinfo_proc));
/*
* Place pointers to each valid proc structure in pref[]. Process
* slots that are actually in use have a non-zero status field.
* Processes with P_SYSTEM set are system processes---these get
* ignored unless show_sysprocs is set.
*/
if (PP(pp, stat) != 0 &&
(show_self != PP(pp, pid)) &&
(show_system || ((PP(pp, flag) & P_SYSTEM) == 0)))
{
total_procs++;
process_states[(unsigned char) PP(pp, stat)]++;
if ((PP(pp, stat) != SZOMB) &&
(show_idle || (PP(pp, pctcpu) != 0) ||
(PP(pp, stat) == SRUN)))
{
*prefp++ = pp;
active_procs++;
}
}
n = malloc(sizeof(struct pg_proc));
if (n == NULL)
{
fprintf(stderr, "malloc error\n");
if (pgresult != NULL)
PQclear(pgresult);
disconnect_from_db(conninfo);
exit(1);
}
memset(n, 0, sizeof(struct pg_proc));
n->pid = atoi(PQgetvalue(pgresult, i, 0));
p = RB_INSERT(pgproc, &head_proc, n);
if (p != NULL)
{
free(n);
n = p;
memcpy(RU(n), RU(&junk2[0]), sizeof(struct rusage));
}
if (mode == MODE_REPLICATION)
{
update_str(&n->usename, PQgetvalue(pgresult, i, REP_USENAME));
update_str(&n->application_name,
PQgetvalue(pgresult, i, REP_APPLICATION_NAME));
update_str(&n->client_addr,
PQgetvalue(pgresult, i, REP_CLIENT_ADDR));
update_str(&n->repstate, PQgetvalue(pgresult, i, REP_STATE));
update_str(&n->primary,
PQgetvalue(pgresult, i, REP_WAL_INSERT));
update_str(&n->sent, PQgetvalue(pgresult, i, REP_SENT));
update_str(&n->write, PQgetvalue(pgresult, i, REP_WRITE));
update_str(&n->flush, PQgetvalue(pgresult, i, REP_FLUSH));
update_str(&n->replay, PQgetvalue(pgresult, i, REP_REPLAY));
n->sent_lag = atol(PQgetvalue(pgresult, i, REP_SENT_LAG));
n->write_lag = atol(PQgetvalue(pgresult, i, REP_WRITE_LAG));
n->flush_lag = atol(PQgetvalue(pgresult, i, REP_FLUSH_LAG));
n->replay_lag = atol(PQgetvalue(pgresult, i, REP_REPLAY_LAG));
}
else
{
update_str(&n->name, PQgetvalue(pgresult, i, PROC_QUERY));
printable(n->name);
update_state(&n->pgstate, PQgetvalue(pgresult, i, PROC_STATE));
update_str(&n->usename, PQgetvalue(pgresult, i, PROC_USENAME));
n->xtime = atol(PQgetvalue(pgresult, i, PROC_XSTART));
n->qtime = atol(PQgetvalue(pgresult, i, PROC_QSTART));
n->locks = atoi(PQgetvalue(pgresult, i, PROC_LOCKS));
}
}
if (pgresult != NULL)
PQclear(pgresult);
disconnect_from_db(conninfo);
/* if requested, sort the "interesting" processes */
if (compare_index >= 0 && active_procs)
qsort((char *) pref, active_procs, sizeof(struct kinfo_proc *),
proc_compares[compare_index]);
/* remember active and total counts */
si->p_total = total_procs;
si->p_active = pref_len = active_procs;
/* pass back a handle */
handle.next_proc = pref;
handle.remaining = active_procs;
return ((caddr_t) & handle);
}
char fmt[MAX_COLS]; /* static area where result is built */
char cmd[MAX_COLS];
char *
format_next_io(caddr_t handle)
{
register struct kinfo_proc *pp;
struct handle *hp;
struct pg_proc n, *p = NULL;
/* find and remember the next proc structure */
hp = (struct handle *) handle;
pp = *(hp->next_proc++);
hp->remaining--;
memset(&n, 0, sizeof(struct pg_proc));
n.pid = PP(pp, pid);
p = RB_FIND(pgproc, &head_proc, &n);
snprintf(fmt, sizeof(fmt),
"%5d %-*.*s %6ld %6ld %6ld %6ld %6ld %6ld %s",
PP(pp, pid),
namelength, namelength,
p->usename,
RU(pp)->ru_nvcsw - RU(p)->ru_nvcsw,
RU(pp)->ru_nivcsw - RU(p)->ru_nivcsw,
RU(pp)->ru_inblock - RU(p)->ru_inblock,
RU(pp)->ru_oublock - RU(p)->ru_oublock,
RU(pp)->ru_majflt - RU(p)->ru_majflt,
(RU(pp)->ru_inblock - RU(p)->ru_inblock) +
(RU(pp)->ru_oublock - RU(p)->ru_oublock) +
(RU(pp)->ru_majflt - RU(p)->ru_majflt),
p->name);
return fmt;
}
char *
format_next_process(caddr_t handle)
{
register struct kinfo_proc *pp;
register long cputime;
register double pct;
struct handle *hp;
char status[16];
int state;
struct pg_proc n, *pr = NULL;
/* find and remember the next proc structure */
hp = (struct handle *) handle;
pp = *(hp->next_proc++);
hp->remaining--;
/* get the process's command name in to "cmd" */
if (show_fullcmd)
{
struct pargs pargs;
int len;
/* get the pargs structure */
getkval((unsigned long) PP(pp, args), (int *) &pargs,
sizeof(pargs), "!pargs");
/* determine workable length */
if ((len = pargs.ar_length) >= MAX_COLS)
{
len = MAX_COLS - 1;
}
/* get the string from that */
getkval((unsigned long) PP(pp, args) +
sizeof(pargs.ar_ref) +
sizeof(pargs.ar_length),
(int *) cmd, len, "!cmdline");
}
#if OSMAJOR <= 4
else if ((PP(pp, flag) & P_INMEM) == 0)
#else
else if ((PP(pp, sflag) & PS_INMEM) == 0)
#endif
{
/* Print swapped processes as <pname> */
char *p;
cmd[0] = '<';
p = strecpy(cmd + 1, PP(pp, comm));
*p++ = '>';
*p = '\0';
}
else
{
/* take it straight out of p_comm */
strncpy(cmd, PP(pp, comm), MAX_COLS - 1);
}
/*
* Convert the process's runtime from microseconds to seconds. This time
* includes the interrupt time although that is not wanted here. ps(1) is
* similarly sloppy.
*/
cputime = (PP(pp, runtime) + 500000) / 1000000;
/* calculate the base for cpu percentages */
pct = pctdouble(PP(pp, pctcpu));
/* generate "STATE" field */
switch (state = PP(pp, stat))
{
case SRUN:
if (smpmode && PP(pp, oncpu) != 0xff)
snprintf(status, sizeof(status), "CPU%d", PP(pp, oncpu));
else
strcpy(status, "RUN");
break;
case SSLEEP:
if (PP(pp, wmesg) != NULL)
{
#if OSMAJOR <= 4
snprintf(status, sizeof(status), "%.6s", EP(pp, wmesg));
#else
snprintf(status, sizeof(status), "%.6s", PP(pp, wmesg));
#endif
break;
}
/* fall through */
default:
if (state >= 0 &&
state < sizeof(state_abbrev) / sizeof(*state_abbrev))
snprintf(status, sizeof(status), "%.6s", state_abbrev[(unsigned char) state]);
else
snprintf(status, sizeof(status), "?%5d", state);
break;
}
memset(&n, 0, sizeof(struct pg_proc));
n.pid = PP(pp, pid);
pr = RB_FIND(pgproc, &head_proc, &n);
/* format this entry */
snprintf(fmt, sizeof(fmt),
"%5d %-*.*s %7s %6s %-6.6s %5s %5s %5.2f%% %5d %s",
PP(pp, pid),
namelength, namelength,
pr->usename,
format_k(PROCSIZE(pp)),
format_k(pagetok(VP(pp, rssize))),
backendstatenames[pr->pgstate],
format_time(pr->xtime),
format_time(pr->qtime),
100.0 * pct,
pr->locks,
pr->name);
/* return the result */
return (fmt);
}
char *
format_next_replication(caddr_t handle)
{
static char fmt[MAX_COLS]; /* static area where result is built */
register struct kinfo_proc *pp;
struct handle *hp;
struct pg_proc n, *p = NULL;
/* find and remember the next proc structure */
hp = (struct handle *) handle;
pp = *(hp->next_proc++);
hp->remaining--;
memset(&n, 0, sizeof(struct pg_proc));
n.pid = PP(pp, pid);
p = RB_FIND(pgproc, &head_proc, &n);
snprintf(fmt, sizeof(fmt),
"%7d %-8.8s %-11.11s %15s %-9.9s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %5s %5s %5s %5s",
p->pid,
p->usename,
p->application_name,
p->client_addr,
p->repstate,
p->primary,
p->sent,
p->write,
p->flush,
p->replay,
format_b(p->sent_lag),
format_b(p->write_lag),
format_b(p->flush_lag),
format_b(p->replay_lag));
/* return the result */
return (fmt);
}
/*
* getkval(offset, ptr, size, refstr) - get a value out of the kernel.
* "offset" is the byte offset into the kernel for the desired value,
* "ptr" points to a buffer into which the value is retrieved,
* "size" is the size of the buffer (and the object to retrieve),
* "refstr" is a reference string used when printing error meessages,
* if "refstr" starts with a '!', then a failure on read will not
* be fatal (this may seem like a silly way to do things, but I
* really didn't want the overhead of another argument).
*
*/
static int
getkval(unsigned long offset, int *ptr, int size, char *refstr)
{
if (kvm_read(kd, offset, (char *) ptr, size) != size)
{
if (*refstr == '!')
{
return (0);
}
else
{
fprintf(stderr, "pg_top: kvm_read for %s: %s\n",
refstr, strerror(errno));
quit(23);
}
}
return (1);
}
/* comparison routines for qsort */
/*
* proc_compare - comparison function for "qsort"
* Compares the resource consumption of two processes using five
* distinct keys. The keys (in descending order of importance) are:
* percent cpu, cpu ticks, state, resident set size, total virtual
* memory usage. The process states are ordered as follows (from least
* to most important): WAIT, zombie, sleep, stop, start, run. The
* array declaration below maps a process state index into a number
* that reflects this ordering.
*/
static unsigned char sorted_state[] =
{
0, /* not used */
3, /* sleep */
1, /* ABANDONED (WAIT) */
6, /* run */
5, /* start */
2, /* zombie */
4 /* stop */
};
#define ORDERKEY_PCTCPU \
if (lresult = (long) PP(p2, pctcpu) - (long) PP(p1, pctcpu), \
(result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
#define ORDERKEY_CPTICKS \
if ((result = PP(p2, runtime) > PP(p1, runtime) ? 1 : \
PP(p2, runtime) < PP(p1, runtime) ? -1 : 0) == 0)
#define ORDERKEY_STATE \
if ((result = sorted_state[(unsigned char) PP(p2, stat)] - \
sorted_state[(unsigned char) PP(p1, stat)]) == 0)
#if OSMAJOR <= 4
#define ORDERKEY_PRIO \
if ((result = PP(p2, priority) - PP(p1, priority)) == 0)
#else
#define ORDERKEY_PRIO \
if ((result = PP(p2, pri.pri_user) - PP(p1, pri.pri_user)) == 0)
#endif
#define ORDERKEY_RSSIZE \
if ((result = VP(p2, rssize) - VP(p1, rssize)) == 0)
#define ORDERKEY_MEM \
if ( (result = PROCSIZE(p2) - PROCSIZE(p1)) == 0 )
/* compare_cpu - the comparison function for sorting by cpu percentage */
int
proc_compare(struct proc **pp1, struct proc **pp2)
{
register struct kinfo_proc *p1;
register struct kinfo_proc *p2;
register int result;
register pctcpu lresult;
/* remove one level of indirection */
p1 = *(struct kinfo_proc **) pp1;
p2 = *(struct kinfo_proc **) pp2;
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
/* compare_size - the comparison function for sorting by total memory usage */
int
compare_size(struct proc **pp1, struct proc **pp2)
{
register struct kinfo_proc *p1;
register struct kinfo_proc *p2;
register int result;
register pctcpu lresult;
/* remove one level of indirection */
p1 = *(struct kinfo_proc **) pp1;
p2 = *(struct kinfo_proc **) pp2;
ORDERKEY_MEM
ORDERKEY_RSSIZE
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
/* compare_res - the comparison function for sorting by resident set size */
int
compare_res(struct proc **pp1, struct proc **pp2)
{
register struct kinfo_proc *p1;
register struct kinfo_proc *p2;
register int result;
register pctcpu lresult;
/* remove one level of indirection */
p1 = *(struct kinfo_proc **) pp1;
p2 = *(struct kinfo_proc **) pp2;
ORDERKEY_RSSIZE
ORDERKEY_MEM
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
/* compare_time - the comparison function for sorting by total cpu time */
int
compare_time(struct proc **pp1, struct proc **pp2)
{
register struct kinfo_proc *p1;
register struct kinfo_proc *p2;
register int result;
register pctcpu lresult;
/* remove one level of indirection */
p1 = *(struct kinfo_proc **) pp1;
p2 = *(struct kinfo_proc **) pp2;
ORDERKEY_CPTICKS
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
/* compare_prio - the comparison function for sorting by cpu percentage */
int
compare_prio(struct proc **pp1, struct proc **pp2)
{
register struct kinfo_proc *p1;
register struct kinfo_proc *p2;
register int result;
register pctcpu lresult;
/* remove one level of indirection */
p1 = *(struct kinfo_proc **) pp1;
p2 = *(struct kinfo_proc **) pp2;
ORDERKEY_PRIO
ORDERKEY_CPTICKS
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
/*
* proc_owner(pid) - returns the uid that owns process "pid", or -1 if
* the process does not exist.
* It is EXTREMLY IMPORTANT that this function work correctly.
* If pg_top runs setuid root (as in SVR4), then this function
* is the only thing that stands in the way of a serious
* security problem. It validates requests for the "kill"
* and "renice" commands.
*/
uid_t
proc_owner(pid_t pid)
{
register int cnt;
register struct kinfo_proc **prefp;
register struct kinfo_proc *pp;
prefp = pref;
cnt = pref_len;
while (--cnt >= 0)
{
pp = *prefp++;
if (PP(pp, pid) == (pid_t) pid)
{
return ((int) PRUID(pp));
}
}
return (-1);
}
static void
getsysctl(const char *name, void *ptr, size_t len)
{
size_t nlen = len;
if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1)
{
fprintf(stderr, "pg_top: sysctl(%s...) failed: %s\n", name,
strerror(errno));
quit(23);
}
if (nlen != len)
{
fprintf(stderr, "pg_top: sysctl(%s...) expected %lu, got %lu\n",
name, (unsigned long) len, (unsigned long) nlen);
quit(23);
}
}
/*
* swapmode is based on a program called swapinfo written
* by Kevin Lahey <kml@rokkaku.atl.ga.us>.
*/
#define SVAR(var) __STRING(var) /* to force expansion */
#define KGET(idx, var) \
KGET1(idx, &var, sizeof(var), SVAR(var))
#define KGET1(idx, p, s, msg) \
KGET2(nlst[idx].n_value, p, s, msg)
#define KGET2(addr, p, s, msg) \
if (kvm_read(kd, (u_long)(addr), p, s) != s) { \
warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \
return (0); \
}
#define KGETRET(addr, p, s, msg) \
if (kvm_read(kd, (u_long)(addr), p, s) != s) { \
warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \
return (0); \
}
int
swapmode(int *retavail, int *retfree)
{
int n;
int pagesize = getpagesize();
struct kvm_swap swapary[1];
*retavail = 0;
*retfree = 0;
#define CONVERT(v) ((quad_t)(v) * pagesize / 1024)
n = kvm_getswapinfo(kd, swapary, 1, 0);
if (n < 0 || swapary[0].ksw_total == 0)
return (0);
*retavail = CONVERT(swapary[0].ksw_total);
*retfree = CONVERT(swapary[0].ksw_total - swapary[0].ksw_used);
n = (int) ((double) swapary[0].ksw_used * 100.0 /
(double) swapary[0].ksw_total);
return (n);
}
int
topproccmp(struct pg_proc *e1, struct pg_proc *e2)
{
return (e1->pid < e2->pid ? -1 : e1->pid > e2->pid);
}
void
get_io_info(struct io_info *io_info)
{
/* Not supported yet */
memset(io_info, 0, sizeof(*io_info));
}
07070100000026000081A400000000000000000000000166622DE8000002CF000000000000000000000000000000000000002700000000pg_top-4.1.1/machine/m_freebsd.man.rstFreeBSD 4.0 NOTES
=================
Last pid is compiler depended.
$ strings /kernel | grep _nextpid
DESCRIPTION OF MEMORY
=====================
Memory: 10M Act 1208K Inact 3220K Wired 132K Free 25% Swap, 2924Kin 2604Kout
:K:: Kilobyte
:M:: Megabyte
:%:: 1/100
:Act:: number of pages active
:Incat:: number of pages inactive
:Wired:: number of pages wired down
:Free:: number of pages free
:Swap:: swap usage
:Kin:: kilobytes swap pager pages paged in (last interval)
:Kout:: kilobytes swap pager pages paged out (last interval)
See /usr/include/sys/vmmeter.h and /sys/vm/vm_meter.c.
Christos Zoulas, Steven Wallace, Wolfram Schneider, Monte Mitzelfelt.
This module was retrofitted from FreeBSD 9.1 sources.
07070100000027000081A400000000000000000000000166622DE80000823A000000000000000000000000000000000000001F00000000pg_top-4.1.1/machine/m_linux.c/*
* pg_top - a top PostgreSQL users display for Unix
*
* SYNOPSIS: Linux 1.2.x, 1.3.x, 2.x, using the /proc filesystem
*
* DESCRIPTION:
* This is the machine-dependent module for Linux 1.2.x, 1.3.x or 2.x.
*
* LIBS:
*
* CFLAGS: -DHAVE_GETOPT -DHAVE_STRERROR -DORDER
*
* TERMCAP: -lcurses
*
* AUTHOR: Richard Henderson <rth@tamu.edu>
* Order support added by Alexey Klimkin <kad@klon.tme.mcst.ru>
* Ported to 2.4 by William LeFebvre
*
* Portions Copyright (c) 2013 VMware, Inc. All Rights Reserved.
*/
#define _GNU_SOURCE
#include "config.h"
#include <sys/types.h>
#include <time.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <bsd/stdlib.h>
#include <bsd/sys/tree.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/vfs.h>
#include <sys/param.h> /* for HZ */
#if 0
#include <linux/proc_fs.h> /* for PROC_SUPER_MAGIC */
#else
#define PROC_SUPER_MAGIC 0x9fa0
#endif
#define BUFFERLEN 255
#define GET_VALUE(v) \
p = strchr(p, ':'); \
++p; \
++p; \
q = strchr(p, '\n'); \
len = q - p; \
if (len >= BUFFERLEN) \
{ \
printf("ERROR - value is larger than the buffer: %d\n", __LINE__); \
exit(1); \
} \
strncpy(value, p, len); \
value[len] = '\0'; \
v = atoll(value);
#include "machine.h"
#include "utils.h"
#define PROCFS "/proc"
extern char *myname;
/*=PROCESS INFORMATION==================================================*/
struct top_proc
{
RB_ENTRY(top_proc) entry;
pid_t pid;
/* index for which element is current in data arrays */
int index;
/* Data from /proc/<pid>/stat. */
char *name;
char *usename;
unsigned long size,
rss; /* in k */
int state;
int pgstate;
unsigned long time;
unsigned long start_time;
unsigned long xtime;
unsigned long qtime;
unsigned int locks;
double pcpu;
/* Data from /proc/<pid>/io. */
long long iops[2]; /* syscr + syscw */
long long syscr[2];
long long syscw[2];
long long read_bytes[2];
long long write_bytes[2];
/* Replication data */
char *application_name;
char *client_addr;
char *repstate;
char *primary;
char *sent;
char *write;
char *flush;
char *replay;
long long sent_lag;
long long write_lag;
long long flush_lag;
long long replay_lag;
};
int topproccmp(struct top_proc *, struct top_proc *);
RB_HEAD(pgproc, top_proc) head_proc = RB_INITIALIZER(&head_proc);
RB_PROTOTYPE(pgproc, top_proc, entry, topproccmp)
RB_GENERATE(pgproc, top_proc, entry, topproccmp)
double timediff;
/*=STATE IDENT STRINGS==================================================*/
#define NCPUSTATES 5
static char *cpustatenames[NCPUSTATES + 1] =
{
"user", "nice", "system", "idle", "iowait",
NULL
};
static int show_iowait = 0;
#define MEMUSED 0
#define MEMFREE 1
#define MEMSHARED 2
#define MEMBUFFERS 3
#define MEMCACHED 4
#define NMEMSTATS 5
static char *memorynames[NMEMSTATS + 1] =
{
"K used, ", "K free, ", "K shared, ", "K buffers, ", "K cached",
NULL
};
enum swap
{
SWAPUSED,
SWAPFREE,
SWAPCACHED,
SWAPIN,
SWAPOUT,
NSWAPSTATS
};
static char *swapnames[NSWAPSTATS + 1] =
{
"K used, ", "K free, ", "K cached, ", "K in, ", "K out", NULL
};
struct swap_t
{
int index;
long long in[2];
long long out[2];
} swap_activity;
static char fmt_header[] =
" PID X SIZE RES STATE XTIME QTIME %CPU LOCKS COMMAND";
char fmt_header_io[] =
" PID IOPS IORPS IOWPS READS WRITES COMMAND";
/* these are names given to allowed sorting orders -- first is default */
static char *ordernames[] =
{
"cpu", "size", "res", "xtime", "qtime", "iops", "iorps", "iowps", "reads",
"writes", "locks", "command", "flag", "rlag", "slag", "wlag", NULL
};
/* forward definitions for comparison functions */
static int compare_cmd(const void *, const void *);
static int compare_cpu(const void *, const void *);
static int compare_iops(const void *, const void *);
static int compare_lag_flush(const void *, const void *);
static int compare_lag_replay(const void *, const void *);
static int compare_lag_sent(const void *, const void *);
static int compare_lag_write(const void *, const void *);
static int compare_locks(const void *, const void *);
static int compare_qtime(const void *, const void *);
static int compare_reads(const void *, const void *);
static int compare_res(const void *, const void *);
static int compare_size(const void *, const void *);
static int compare_syscr(const void *, const void *);
static int compare_syscw(const void *, const void *);
static int compare_writes(const void *, const void *);
static int compare_xtime(const void *, const void *);
int (*proc_compares[]) () =
{
compare_cpu,
compare_size,
compare_res,
compare_xtime,
compare_qtime,
compare_iops,
compare_syscr,
compare_syscw,
compare_reads,
compare_writes,
compare_locks,
compare_cmd,
compare_lag_flush,
compare_lag_replay,
compare_lag_sent,
compare_lag_write,
NULL
};
/*=SYSTEM STATE INFO====================================================*/
/* these are for calculating cpu state percentages */
static int64_t cp_time[NCPUSTATES];
static int64_t cp_old[NCPUSTATES];
static int64_t cp_diff[NCPUSTATES];
/* for calculating the exponential average */
static struct timeval lasttime;
/* these are for keeping track of processes */
#define INITIAL_ACTIVE_SIZE (256)
#define PROCBLOCK_SIZE (32)
static struct top_proc *pgtable;
static int proc_index;
static time_t boottime = -1;
/* these are for passing data back to the machine independant portion */
static int64_t cpu_states[NCPUSTATES];
static int process_states[NPROCSTATES];
static long memory_stats[NMEMSTATS];
static long swap_stats[NSWAPSTATS];
/* usefull macros */
#define bytetok(x) (((x) + 512) >> 10)
#define pagetok(x) ((x) * sysconf(_SC_PAGESIZE) >> 10)
/*======================================================================*/
static inline long long
diff_stat(long long value[2], int index)
{
return value[index] - value[(index + 1) % 2];
}
static inline char *
skip_ws(const char *p)
{
while (isspace(*p))
p++;
return (char *) p;
}
static inline char *
skip_token(const char *p)
{
while (isspace(*p))
p++;
while (*p && !isspace(*p))
p++;
return (char *) p;
}
int
topproccmp(struct top_proc *e1, struct top_proc *e2)
{
return (e1->pid < e2->pid ? -1 : e1->pid > e2->pid);
}
static void
xfrm_cmdline(char *p, int len)
{
while (--len > 0)
{
if (*p == '\0')
{
*p = ' ';
}
p++;
}
}
int
machine_init(struct statics *statics)
{
/* make sure the proc filesystem is mounted */
{
struct statfs sb;
if (statfs(PROCFS, &sb) < 0 || sb.f_type != PROC_SUPER_MAGIC)
{
fprintf(stderr, "%s: proc filesystem not mounted on " PROCFS "\n",
myname);
return -1;
}
}
/* chdir to the proc filesystem to make things easier */
chdir(PROCFS);
/* a few preliminary checks */
{
int fd;
char buff[128];
char *p;
int cnt = 0;
unsigned long uptime;
struct timeval tv;
/* get a boottime */
if ((fd = open("uptime", 0)) != -1)
{
if (read(fd, buff, sizeof(buff)) > 0)
{
uptime = strtoul(buff, &p, 10);
gettimeofday(&tv, 0);
boottime = tv.tv_sec - uptime;
}
close(fd);
}
/* see how many states we get from stat */
if ((fd = open("stat", 0)) != -1)
{
if (read(fd, buff, sizeof(buff)) > 0)
{
if ((p = strchr(buff, '\n')) != NULL)
{
*p = '\0';
p = buff;
cnt = 0;
while (*p != '\0')
{
if (*p++ == ' ')
{
cnt++;
}
}
}
}
close(fd);
}
if (cnt > 5)
{
/* we have iowait */
show_iowait = 1;
}
}
/* if we aren't showing iowait, then we have to tweak cpustatenames */
if (!show_iowait)
{
cpustatenames[4] = NULL;
}
/* fill in the statics information */
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
statics->swap_names = swapnames;
statics->order_names = ordernames;
statics->boottime = boottime;
statics->flags.fullcmds = 1;
statics->flags.warmup = 1;
/* all done! */
return 0;
}
void
get_system_info(struct system_info *info)
{
char buffer[4096 + 1];
int fd,
len;
char *p;
/* get load averages */
if ((fd = open("loadavg", O_RDONLY)) != -1)
{
if ((len = read(fd, buffer, sizeof(buffer) - 1)) > 0)
{
buffer[len] = '\0';
info->load_avg[0] = strtod(buffer, &p);
info->load_avg[1] = strtod(p, &p);
info->load_avg[2] = strtod(p, &p);
p = skip_token(p); /* skip running/tasks */
p = skip_ws(p);
if (*p)
{
info->last_pid = atoi(p);
}
else
{
info->last_pid = -1;
}
}
close(fd);
}
/* get the cpu time info */
if ((fd = open("stat", O_RDONLY)) != -1)
{
if ((len = read(fd, buffer, sizeof(buffer) - 1)) > 0)
{
buffer[len] = '\0';
p = skip_token(buffer); /* "cpu" */
cp_time[0] = strtoul(p, &p, 0);
cp_time[1] = strtoul(p, &p, 0);
cp_time[2] = strtoul(p, &p, 0);
cp_time[3] = strtoul(p, &p, 0);
if (show_iowait)
{
cp_time[4] = strtoul(p, &p, 0);
}
/* convert cp_time counts to percentages */
percentages(NCPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
}
close(fd);
}
/* get system wide memory usage */
if ((fd = open("meminfo", O_RDONLY)) != -1)
{
char *p;
int mem = 0;
int swap = 0;
unsigned long memtotal = 0;
unsigned long memfree = 0;
unsigned long swaptotal = 0;
if ((len = read(fd, buffer, sizeof(buffer) - 1)) > 0)
{
buffer[len] = '\0';
p = buffer - 1;
/* iterate thru the lines */
while (p != NULL)
{
p++;
if (p[0] == ' ' || p[0] == '\t')
{
/* skip */
}
else if (strncmp(p, "Mem:", 4) == 0)
{
p = skip_token(p); /* "Mem:" */
p = skip_token(p); /* total memory */
memory_stats[MEMUSED] = strtoul(p, &p, 10);
memory_stats[MEMFREE] = strtoul(p, &p, 10);
memory_stats[MEMSHARED] = strtoul(p, &p, 10);
memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
memory_stats[MEMCACHED] = strtoul(p, &p, 10);
memory_stats[MEMUSED] = bytetok(memory_stats[MEMUSED]);
memory_stats[MEMFREE] = bytetok(memory_stats[MEMFREE]);
memory_stats[MEMSHARED] = bytetok(memory_stats[MEMSHARED]);
memory_stats[MEMBUFFERS] =
bytetok(memory_stats[MEMBUFFERS]);
memory_stats[MEMCACHED] = bytetok(memory_stats[MEMCACHED]);
mem = 1;
}
else if (strncmp(p, "Swap:", 5) == 0)
{
p = skip_token(p); /* "Swap:" */
p = skip_token(p); /* total swap */
swap_stats[SWAPUSED] = strtoul(p, &p, 10);
swap_stats[SWAPFREE] = strtoul(p, &p, 10);
swap_stats[SWAPUSED] = bytetok(swap_stats[SWAPUSED]);
swap_stats[SWAPFREE] = bytetok(swap_stats[SWAPFREE]);
swap = 1;
}
else if (!mem && strncmp(p, "MemTotal:", 9) == 0)
{
p = skip_token(p);
memtotal = strtoul(p, &p, 10);
}
else if (!mem && memtotal > 0 && strncmp(p, "MemFree:", 8) == 0)
{
p = skip_token(p);
memfree = strtoul(p, &p, 10);
memory_stats[MEMUSED] = memtotal - memfree;
memory_stats[MEMFREE] = memfree;
}
else if (!mem && strncmp(p, "MemShared:", 10) == 0)
{
p = skip_token(p);
memory_stats[MEMSHARED] = strtoul(p, &p, 10);
}
else if (!mem && strncmp(p, "Buffers:", 8) == 0)
{
p = skip_token(p);
memory_stats[MEMBUFFERS] = strtoul(p, &p, 10);
}
else if (!mem && strncmp(p, "Cached:", 7) == 0)
{
p = skip_token(p);
memory_stats[MEMCACHED] = strtoul(p, &p, 10);
}
else if (!swap && strncmp(p, "SwapTotal:", 10) == 0)
{
p = skip_token(p);
swaptotal = strtoul(p, &p, 10);
}
else if (!swap && swaptotal > 0 && strncmp(p, "SwapFree:", 9) == 0)
{
p = skip_token(p);
memfree = strtoul(p, &p, 10);
swap_stats[SWAPUSED] = swaptotal - memfree;
swap_stats[SWAPFREE] = memfree;
}
else if (!mem && strncmp(p, "SwapCached:", 11) == 0)
{
p = skip_token(p);
swap_stats[SWAPCACHED] = strtoul(p, &p, 10);
}
/* move to the next line */
p = strchr(p, '\n');
}
}
close(fd);
}
/* get swap activity */
if ((fd = open("vmstat", O_RDONLY)) != -1)
{
unsigned long swpin = -1;
unsigned long swpout = -1;
if ((len = read(fd, buffer, sizeof(buffer) - 1)) > 0)
{
buffer[len] = '\0';
p = buffer - 1;
while (p != NULL)
{
p++;
if (swpin == -1 && strncmp(p, "pswpin", 6) == 0)
{
p = skip_token(p);
swpin = strtoul(p, &p, 10);
}
else if (swpout == -1 && strncmp(p, "pswpout", 7) == 0)
{
p = skip_token(p);
swpout = strtoul(p, &p, 10);
}
if (swpin != -1 && swpout != -1)
{
swap_activity.in[swap_activity.index] = swpin;
swap_activity.out[swap_activity.index] = swpout;
swap_stats[SWAPIN] = diff_stat(swap_activity.in,
swap_activity.index);
swap_stats[SWAPOUT] = diff_stat(swap_activity.out,
swap_activity.index);
swap_activity.index = (swap_activity.index + 1) % 2;
break;
}
/* move to the next line */
p = strchr(p, '\n');
}
}
close(fd);
}
else
{
swap_activity.in[swap_activity.index] = -1;
swap_activity.out[swap_activity.index] = -1;
}
/* set arrays and strings */
info->cpustates = cpu_states;
info->memory = memory_stats;
info->swap = swap_stats;
}
static void
read_one_proc_stat(struct top_proc *proc, struct process_select *sel)
{
char buffer[4096],
*p,
*q;
int fd,
len;
int fullcmd;
char value[BUFFERLEN + 1];
long long tmp;
/* if anything goes wrong, we return with proc->state == 0 */
proc->state = 0;
/* full cmd handling */
fullcmd = sel->fullcmd;
if (fullcmd == 1)
{
sprintf(buffer, "%d/cmdline", proc->pid);
if ((fd = open(buffer, O_RDONLY)) != -1)
{
/* read command line data */
/* (theres no sense in reading more than we can fit) */
if ((len = read(fd, buffer, MAX_COLS)) > 1)
{
buffer[len] = '\0';
xfrm_cmdline(buffer, len);
update_str(&proc->name, buffer);
printable(proc->name);
}
else
{
fullcmd = 0;
}
close(fd);
}
else
{
fullcmd = 0;
}
}
/* grab the proc stat info in one go */
sprintf(buffer, "%d/stat", proc->pid);
fd = open(buffer, O_RDONLY);
len = read(fd, buffer, sizeof(buffer) - 1);
close(fd);
buffer[len] = '\0';
/* parse out the status, described in 'man proc' */
/* skip pid and locate command, which is in parentheses */
if ((p = strchr(buffer, '(')) == NULL)
{
return;
}
if ((q = strrchr(++p, ')')) == NULL)
{
return;
}
/* set the procname */
*q = '\0';
if (!fullcmd)
{
update_str(&proc->name, p);
printable(proc->name);
}
/* scan the rest of the line */
p = q + 1;
p = skip_ws(p);
switch (*p++) /* state */
{
case 'R':
proc->state = 1;
break;
case 'S':
proc->state = 2;
break;
case 'D':
proc->state = 3;
break;
case 'Z':
proc->state = 4;
break;
case 'T':
proc->state = 5;
break;
case 'W':
proc->state = 6;
break;
case '\0':
return;
}
p = skip_token(p); /* skip ppid */
p = skip_token(p); /* skip pgrp */
p = skip_token(p); /* skip session */
p = skip_token(p); /* skip tty nr */
p = skip_token(p); /* skip tty pgrp */
p = skip_token(p); /* skip flags */
p = skip_token(p); /* skip min flt */
p = skip_token(p); /* skip cmin flt */
p = skip_token(p); /* skip maj flt */
p = skip_token(p); /* skip cmaj flt */
proc->time = strtoul(p, &p, 10); /* utime */
proc->time += strtoul(p, &p, 10); /* stime */
p = skip_token(p); /* skip cutime */
p = skip_token(p); /* skip cstime */
p = skip_token(p); /* skip priority */
p = skip_token(p); /* skip nice */
p = skip_token(p); /* skip num_threads */
p = skip_token(p); /* skip itrealvalue, 0 */
proc->start_time = strtoul(p, &p, 10); /* start_time */
proc->size = bytetok(strtoul(p, &p, 10)); /* vsize */
proc->rss = pagetok(strtoul(p, &p, 10)); /* rss */
#if 0
/* for the record, here are the rest of the fields */
p = skip_token(p); /* skip rlim */
p = skip_token(p); /* skip start_code */
p = skip_token(p); /* skip end_code */
p = skip_token(p); /* skip start_stack */
p = skip_token(p); /* skip esp */
p = skip_token(p); /* skip eip */
p = skip_token(p); /* skip signal */
p = skip_token(p); /* skip sigblocked */
p = skip_token(p); /* skip sigignore */
p = skip_token(p); /* skip sigcatch */
p = skip_token(p); /* skip wchan */
p = skip_token(p); /* skip nswap, not maintained */
p = skip_token(p); /* exit signal */
p = skip_token(p); /* processor */
p = skip_token(p); /* rt_priority */
p = skip_token(p); /* policy */
p = skip_token(p); /* delayacct_blkio_ticks */
#endif
/* Get the io stats. */
sprintf(buffer, "%d/io", proc->pid);
fd = open(buffer, O_RDONLY);
if (fd == -1)
{
/*
* CONFIG_TASK_IO_ACCOUNTING is not enabled in the Linux kernel or
* this version of Linux may not support collecting i/o statistics per
* pid.
*/
return;
}
len = read(fd, buffer, sizeof(buffer) - 1);
close(fd);
buffer[len] = '\0';
p = buffer;
p = skip_token(p); /* rchar */
p = skip_token(p); /* wchar */
GET_VALUE(tmp); /* syscr */
proc->syscr[proc->index] = tmp;
proc->iops[proc->index] = tmp;
GET_VALUE(tmp); /* syscw */
proc->syscw[proc->index] = tmp;
proc->iops[proc->index] += tmp;
GET_VALUE(tmp); /* read_bytes */
proc->read_bytes[proc->index] = tmp;
GET_VALUE(tmp); /* write_bytes */
proc->write_bytes[proc->index] = tmp;
GET_VALUE(tmp); /* cancelled_write_bytes */
proc->write_bytes[proc->index] -= tmp;
}
caddr_t
get_process_info(struct system_info *si,
struct process_select *sel,
int compare_index, struct pg_conninfo_ctx *conninfo, int mode)
{
struct timeval thistime;
double tickdiff;
/* calculate the time difference since our last check */
gettimeofday(&thistime, 0);
if (lasttime.tv_sec)
{
timediff = ((thistime.tv_sec - lasttime.tv_sec) +
(thistime.tv_usec - lasttime.tv_usec) * 1e-6);
}
else
{
timediff = 0;
}
lasttime = thistime;
tickdiff = timediff * HZ; /* convert to ticks */
/* read the process information */
{
int total_procs = 0;
int active_procs = 0;
int show_idle = sel->idle;
int i;
int rows;
PGresult *pgresult = NULL;
struct top_proc *n,
*p;
memset(process_states, 0, sizeof(process_states));
connect_to_db(conninfo);
if (conninfo->connection != NULL)
{
if (mode == MODE_REPLICATION)
{
pgresult = pg_replication(conninfo->connection);
}
else
{
pgresult = pg_processes(conninfo->connection);
}
rows = PQntuples(pgresult);
}
else
{
rows = 0;
}
if (rows > 0)
{
p = realloc(pgtable, sizeof(struct top_proc) * rows);
if (p == NULL)
{
fprintf(stderr, "realloc error\n");
if (pgresult != NULL)
PQclear(pgresult);
disconnect_from_db(conninfo);
exit(1);
}
pgtable = p;
}
for (i = 0; i < rows; i++)
{
unsigned long otime;
n = malloc(sizeof(struct top_proc));
if (n == NULL)
{
fprintf(stderr, "malloc error\n");
if (pgresult != NULL)
PQclear(pgresult);
disconnect_from_db(conninfo);
exit(1);
}
memset(n, 0, sizeof(struct top_proc));
n->pid = atoi(PQgetvalue(pgresult, i, 0));
p = RB_INSERT(pgproc, &head_proc, n);
if (p != NULL)
{
free(n);
n = p;
}
else
{
n->time = 0;
}
otime = n->time;
if (mode == MODE_REPLICATION)
{
update_str(&n->usename, PQgetvalue(pgresult, i, REP_USENAME));
update_str(&n->application_name,
PQgetvalue(pgresult, i, REP_APPLICATION_NAME));
update_str(&n->client_addr,
PQgetvalue(pgresult, i, REP_CLIENT_ADDR));
update_str(&n->repstate, PQgetvalue(pgresult, i, REP_STATE));
update_str(&n->primary,
PQgetvalue(pgresult, i, REP_WAL_INSERT));
update_str(&n->sent, PQgetvalue(pgresult, i, REP_SENT));
update_str(&n->write, PQgetvalue(pgresult, i, REP_WRITE));
update_str(&n->flush, PQgetvalue(pgresult, i, REP_FLUSH));
update_str(&n->replay, PQgetvalue(pgresult, i, REP_REPLAY));
n->sent_lag = atol(PQgetvalue(pgresult, i, REP_SENT_LAG));
n->write_lag = atol(PQgetvalue(pgresult, i, REP_WRITE_LAG));
n->flush_lag = atol(PQgetvalue(pgresult, i, REP_FLUSH_LAG));
n->replay_lag = atol(PQgetvalue(pgresult, i, REP_REPLAY_LAG));
memcpy(&pgtable[active_procs++], n, sizeof(struct top_proc));
}
else
{
read_one_proc_stat(n, sel);
if (sel->fullcmd == 2)
{
update_str(&n->name, PQgetvalue(pgresult, i, PROC_QUERY));
printable(n->name);
}
update_state(&n->pgstate, PQgetvalue(pgresult, i, PROC_STATE));
update_str(&n->usename, PQgetvalue(pgresult, i, PROC_USENAME));
n->xtime = atol(PQgetvalue(pgresult, i, PROC_XSTART));
n->qtime = atol(PQgetvalue(pgresult, i, PROC_QSTART));
n->locks = atoi(PQgetvalue(pgresult, i, PROC_LOCKS));
process_states[n->pgstate]++;
if (tickdiff > 0.0)
{
if ((n->pcpu = (n->time - otime) / tickdiff) < 0.0001)
{
n->pcpu = 0;
}
}
if ((show_idle || n->pgstate != STATE_IDLE) &&
(sel->usename[0] == '\0' ||
strcmp(n->usename, sel->usename) == 0))
memcpy(&pgtable[active_procs++], n,
sizeof(struct top_proc));
}
n->index = (n->index + 1) % 2;
total_procs++;
}
if (pgresult != NULL)
PQclear(pgresult);
disconnect_from_db(conninfo);
si->p_active = active_procs;
si->p_total = total_procs;
si->procstates = process_states;
}
/* if requested, sort the "active" procs */
if (compare_index >= 0 && si->p_active)
{
qsort(pgtable, si->p_active, sizeof(struct top_proc),
proc_compares[compare_index]);
}
/* don't even pretend that the return value thing here isn't bogus */
proc_index = 0;
return (caddr_t) 0;
}
char *
format_header(char *uname_field)
{
int uname_len = strlen(uname_field);
if (uname_len > 8)
uname_len = 8;
memcpy(strchr(fmt_header, 'X'), uname_field, uname_len);
return fmt_header;
}
char *
format_next_io(caddr_t handle)
{
static char fmt[MAX_COLS]; /* static area where result is built */
struct top_proc *p = &pgtable[proc_index++];
snprintf(fmt, sizeof(fmt),
"%7d %7.0f %7.0f %7.0f %5s %6s %s",
p->pid,
diff_stat(p->iops, p->index) / timediff,
diff_stat(p->syscr, p->index) / timediff,
diff_stat(p->syscw, p->index) / timediff,
format_b(diff_stat(p->read_bytes, p->index) / timediff),
format_b(diff_stat(p->write_bytes, p->index) / timediff),
p->name);
return (fmt);
}
char *
format_next_process(caddr_t handle)
{
static char fmt[MAX_COLS]; /* static area where result is built */
struct top_proc *p = &pgtable[proc_index++];
snprintf(fmt, sizeof(fmt),
"%7d %-10.8s %5s %5s %-6s %5s %5s %5.1f %5d %s",
p->pid,
p->usename,
format_k(p->size),
format_k(p->rss),
backendstatenames[p->pgstate],
format_time(p->xtime),
format_time(p->qtime),
p->pcpu * 100.0,
p->locks,
p->name);
/* return the result */
return (fmt);
}
char *
format_next_replication(caddr_t handle)
{
static char fmt[MAX_COLS]; /* static area where result is built */
struct top_proc *p = &pgtable[proc_index++];
snprintf(fmt, sizeof(fmt),
"%7d %-8.8s %-11.11s %15s %-9.9s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %5s %5s %5s %5s",
p->pid,
p->usename,
p->application_name,
p->client_addr,
p->repstate,
p->primary,
p->sent,
p->write,
p->flush,
p->replay,
format_b(p->sent_lag),
format_b(p->write_lag),
format_b(p->flush_lag),
format_b(p->replay_lag));
/* return the result */
return (fmt);
}
/* comparison routines for qsort */
/*
* There are currently four possible comparison routines. main selects
* one of these by indexing in to the array proc_compares.
*
* Possible keys are defined as macros below. Currently these keys are
* defined: percent cpu, cpu ticks, process state, resident set size,
* total virtual memory usage. The process states are ordered as follows
* (from least to most important): WAIT, zombie, sleep, stop, start, run.
* The array declaration below maps a process state index into a number
* that reflects this ordering.
*/
/* First, the possible comparison keys. These are defined in such a way
that they can be merely listed in the source code to define the actual
desired ordering.
*/
#define ORDERKEY_IOPS if ((result = diff_stat(p2->iops, p2->index) - \
diff_stat(p1->iops, p1->index)) == 0)
#define ORDERKEY_LAG_FLUSH if ((result = p2->flush_lag - p1->flush_lag) == 0)
#define ORDERKEY_LAG_REPLAY if ((result = p2->replay_lag - \
p1->replay_lag) == 0)
#define ORDERKEY_LAG_SENT if ((result = p2->sent_lag - p1->sent_lag) == 0)
#define ORDERKEY_LAG_WRITE if ((result = p2->write_lag - p1->write_lag) == 0)
#define ORDERKEY_LOCKS if ((result = p2->locks - p1->locks) == 0)
#define ORDERKEY_MEM if ((result = p2->size - p1->size) == 0)
#define ORDERKEY_NAME if ((result = strcmp(p1->name, p2->name)) == 0)
#define ORDERKEY_PCTCPU if ((result = (int)(p2->pcpu - p1->pcpu)) == 0)
#define ORDERKEY_QTIME if ((result = p2->qtime - p1->qtime) == 0)
#define ORDERKEY_READS if ((result = diff_stat(p2->read_bytes, p2->index) - \
diff_stat(p1->read_bytes, p1->index)) == 0)
#define ORDERKEY_RSSIZE if ((result = p2->rss - p1->rss) == 0)
#define ORDERKEY_STATE if ((result = p1->pgstate < p2->pgstate))
#define ORDERKEY_SYSCR if ((result = diff_stat(p2->syscr, p2->index) - \
diff_stat(p1->syscr, p1->index)) == 0)
#define ORDERKEY_SYSCW if ((result = diff_stat(p2->syscw, p2->index) - \
diff_stat(p1->syscw, p1->index)) == 0)
#define ORDERKEY_WRITES if ((result = diff_stat(p2->write_bytes, p2->index) - \
diff_stat(p1->write_bytes, p1->index)) == 0)
#define ORDERKEY_XTIME if ((result = p2->xtime - p1->xtime) == 0)
/* compare_cmd - the comparison function for sorting by command name */
static int
compare_cmd(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_NAME
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
/* compare_cpu - the comparison function for sorting by cpu percentage */
static int
compare_cpu(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
/* compare_iops - the comparison function for sorting by iops */
static int
compare_iops(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_IOPS
ORDERKEY_SYSCW
ORDERKEY_SYSCR
ORDERKEY_READS
ORDERKEY_WRITES
ORDERKEY_NAME
;
return (result);
}
static int
compare_lag_flush(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_LAG_FLUSH
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
static int
compare_lag_replay(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_LAG_REPLAY
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
static int
compare_lag_sent(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_LAG_SENT
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
static int
compare_lag_write(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_LAG_WRITE
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
/*
* compare_locks - the comparison function for sorting by total locks ancquired
*/
static int
compare_locks(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_LOCKS
ORDERKEY_QTIME
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
/* compare_qtime - the comparison function for sorting by total cpu qtime */
static int
compare_qtime(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_QTIME
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
static int
compare_reads(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_READS
ORDERKEY_SYSCR
ORDERKEY_IOPS
ORDERKEY_SYSCW
ORDERKEY_WRITES
ORDERKEY_NAME
;
return (result);
}
/* compare_res - the comparison function for sorting by resident set size */
static int
compare_res(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_RSSIZE
ORDERKEY_MEM
ORDERKEY_PCTCPU
ORDERKEY_STATE
;
return (result);
}
/* compare_size - the comparison function for sorting by total memory usage */
static int
compare_size(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_MEM
ORDERKEY_RSSIZE
ORDERKEY_PCTCPU
ORDERKEY_STATE
;
return (result);
}
static int
compare_syscr(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_SYSCR
ORDERKEY_IOPS
ORDERKEY_SYSCW
ORDERKEY_READS
ORDERKEY_WRITES
ORDERKEY_NAME
;
return (result);
}
static int
compare_syscw(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_SYSCW
ORDERKEY_IOPS
ORDERKEY_SYSCR
ORDERKEY_READS
ORDERKEY_WRITES
ORDERKEY_NAME
;
return (result);
}
/* compare_xtime - the comparison function for sorting by total cpu xtime */
static int
compare_xtime(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_XTIME
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
static int
compare_writes(const void *v1, const void *v2)
{
struct top_proc *p1 = (struct top_proc *) v1;
struct top_proc *p2 = (struct top_proc *) v2;
int result;
ORDERKEY_WRITES
ORDERKEY_IOPS
ORDERKEY_SYSCR
ORDERKEY_SYSCW
ORDERKEY_READS
ORDERKEY_NAME
;
return (result);
}
/*
* Get IO information for the SCSI devices in the system. Returns
* read/write IOs per second and bandwidth by comparing current values
* with previous values.
*/
void
get_io_info(struct io_info *io_info)
{
struct timeval thistime;
double timediff;
static struct timeval lasttime;
struct io_info cur_info;
static struct io_info last_io_info;
FILE *fp;
char line[256];
int major, minor;
char dev_name[32];
int64_t reads, readsectors, skip, writes, writesectors;
int i;
/* calculate the time difference since our last check */
gettimeofday(&thistime, 0);
if (lasttime.tv_sec)
timediff = ((thistime.tv_sec - lasttime.tv_sec) +
(thistime.tv_usec - lasttime.tv_usec) * 1e-6);
else
timediff = 0;
lasttime = thistime;
fp = fopen("/proc/diskstats", "r");
if (fp == NULL)
{
memset(io_info, 0, sizeof(*io_info));
return;
}
memset(&cur_info, 0, sizeof(cur_info));
while (fgets(line, 256, fp) != NULL)
{
i = sscanf(line, "%d %d %31s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
&major, &minor, dev_name,
&reads, &skip, &readsectors, &skip,
&writes, &skip, &writesectors, &skip,
&skip, &skip, &skip);
if (i != 14)
continue;
/* Total up full scsi devices (not partitions) */
if (major == 8 && (minor % 16) == 0)
{
cur_info.reads += reads;
cur_info.readsectors += readsectors;
cur_info.writes += writes;
cur_info.writesectors += writesectors;
}
}
fclose(fp);
if (timediff == 0)
{
last_io_info = cur_info;
memset(io_info, 0, sizeof(*io_info));
return;
}
/* Compute the rate information */
io_info->reads = (double)(cur_info.reads - last_io_info.reads) / timediff;
io_info->readsectors = (double)(cur_info.readsectors - last_io_info.readsectors) / timediff;
io_info->writes = (double)(cur_info.writes - last_io_info.writes) / timediff;
io_info->writesectors = (double)(cur_info.writesectors - last_io_info.writesectors) / timediff;
last_io_info = cur_info;
}
07070100000028000081A400000000000000000000000166622DE80000016A000000000000000000000000000000000000002500000000pg_top-4.1.1/machine/m_linux.man.rstLINUX NOTES
===========
The Linux port was written by Richard Henderson <rth@tamu.edu>.
The CPU% calculation was brazenly stolen from the Solaris 2
port and should be attributed to one of the many names listed
in its man page.
The order support was stolen from SUNOS 5 port by
Alexey Klimkin <kad@klon.tme.mcst.ru>
Made to work under 2.4 by William LeFebvre.
07070100000029000081A400000000000000000000000166622DE800005061000000000000000000000000000000000000002000000000pg_top-4.1.1/machine/m_macosx.c/*
* m_macosx.c
*
* AUTHOR: Andrew S. Townley
* based on m_bsd44.c and m_next32.c
* by Christos Zoulas and Tim Pugh
* CREATED: Tue Aug 11 01:51:35 CDT 1998
* SYNOPSIS: MacOS X Server (Rhapsody Developer Release 2)
* DESCRIPTION:
* MacOS X Server (Rhapsody Developer Release 2)
*
* CFLAGS: -DHAVE_STRERROR
* TERMCAP: none
* MATH: none
*/
/*
* normal stuff
*/
#include "config.h"
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include "os.h"
#include "pg_top.h"
#include "machine.h"
#include "utils.h"
/*
* MacOS kernel stuff
*/
#include <fcntl.h>
#include <sys/dkstat.h>
#include <sys/sysctl.h>
#include <mach/message.h>
#include <mach/vm_statistics.h>
#include <mach/mach.h>
#include <mach/host_info.h>
/* for new sysctl calls */
#include <sys/types.h>
#include <sys/stat.h>
#define VMUNIX "/mach_kernel"
/* #define MEM "/dev/mem" */
#define SWAP NULL
#define NUM_AVERAGES 3
#define LOG1024 10
#define PP(pp, field) ((pp)->kp_proc . field)
#define EP(pp, field) ((pp)->kp_eproc . field)
#define VP(pp, field) ((pp)->kp_eproc.e_vm . field)
#define MPP(mp, field) (PP((mp)->kproc, field))
#define MEP(mp, field) (EP((mp)->kproc, field))
#define MVP(mp, field) (VP((mp)->kproc, field))
#define TP(mp, field) ((mp)->task_info . field)
#define RP(mp, field) ((mp)->thread_summary . field)
/* define what weighted cpu is */
#define weighted_cpu(pct, s) (s == 0 ? 0.0 : \
((pct) / (1.0 - exp(s * logcpu))))
/* what we consider to be process size: */
#ifdef notdef
#define PROCSIZE(pp) (VP((pp), vm_tsize) + VP((pp), vm_dsize) + VP((pp), vm_ssize))
#endif
#define PROCSIZE(pp) (EP(pp, e_xsize))
#define TASKSIZE(t) (TP(t, virtual_size) + TP(t, resident_size))
/* what we consider to be resident set size: */
#ifdef notdef
#define RSSIZE(pp) (MVP((pp), vm_rssize))
#endif
#define RSSIZE(pp) (MEP((pp), e_xrssize))
#define pctdouble(p) ((double)(p) / FSCALE)
/*
* globals
*/
/* static kvm_t *kd = NULL; */
static int nproc;
static int onproc = -1;
static int pref_len;
static int maxmem;
static char fmt[MAX_COLS];
/* static double logcpu = 1.0; */
/* process array stuff */
static struct kinfo_proc *kproc_list = NULL;
static struct macos_proc *proc_list = NULL;
static struct macos_proc **proc_ref = NULL;
static int process_states[7];
static struct handle handle;
static struct kinfo_proc *pbase;
/*
* The mach information hopefully will not be necessary
* when the kvm_* interfaces are supported completely.
*
* Since we're only concerned with task and thread info
* for 'interesting' processes, we're going to only allocate
* as many task and thread structures as needed.
*/
/* static struct task_basic_info *task_list = NULL; */
/* memory statistics */
static int pageshift = 0;
static int pagesize = 0;
#define pagetok(size) ((size) << pageshift)
static int swappgsin = -1;
static int swappgsout = -1;
static vm_statistics_data_t vm_stats;
static long memory_stats[7];
/* CPU state percentages */
host_cpu_load_info_data_t cpuload;
static int64_t cp_time[CPU_STATE_MAX];
static int64_t cp_old[CPU_STATE_MAX];
static int64_t cp_diff[CPU_STATE_MAX];
static int64_t cpu_states[CPU_STATE_MAX];
/*
* types
*/
typedef long pctcpu;
/*
* We need to declare a hybrid structure which will store all
* of the stuff we care about for each process.
*/
#define FULLCMDLEN 1024
struct macos_proc
{
struct kinfo_proc *kproc;
task_t the_task;
struct task_basic_info task_info;
unsigned int thread_count;
struct thread_basic_info thread_summary;
char fullcmd[FULLCMDLEN + 1];
};
static int show_fullcmd;
struct handle
{
struct macos_proc **next_proc;
int remaining;
};
static char header[] =
" PID X PRI THRD SIZE RES STATE TIME MEM CPU COMMAND";
/* 0123456 -- field to fill in starts at header+6 */
#define UNAME_START 6
#define Proc_format \
"%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %.180s"
int proc_compare(const void *, const void *);
int get_fullcmd(int, char *);
/*
* puke()
*
* This function is used to report errors to stderr.
*/
static void
puke(const char *fmt,...)
{
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
fputc('\n', stderr);
fflush(stderr);
}
/*
* load_thread_info()
*
* This function will attempt to load the thread summary info
* for a Mach task. The task is located as part of the macos_proc
* structure.
*
* returns the kern_return_t value of any failed call or KERN_SUCCESS
* if everything works.
*/
int
load_thread_info(struct macos_proc *mp)
{
register kern_return_t rc = 0;
register int i = 0;
register int t_utime = 0;
register int t_stime = 0;
register int t_cpu = 0;
register task_t the_task = mp->the_task;
thread_array_t thread_list = NULL;
/*
* We need to load all of the threads for the given task so we can get the
* performance data from them.
*/
mp->thread_count = 0;
rc = task_threads(the_task, &thread_list, &(mp->thread_count));
if (rc != KERN_SUCCESS)
{
return (rc);
}
/*
* now, for each of the threads, we need to sum the stats so we can
* present the whole thing to the caller.
*/
for (i = 0; i < mp->thread_count; i++)
{
struct thread_basic_info t_info;
unsigned int icount = THREAD_BASIC_INFO_COUNT;
kern_return_t rc = 0;
rc = thread_info(thread_list[i], THREAD_BASIC_INFO,
(thread_info_t) & t_info, &icount);
if (rc != KERN_SUCCESS)
{
puke("error: unable to load thread info for task (%s); rc = %d",
strerror(errno), rc);
return (rc);
}
t_utime += t_info.user_time.seconds;
t_stime += t_info.system_time.seconds;
t_cpu += t_info.cpu_usage;
}
vm_deallocate(mach_task_self(), (vm_address_t) thread_list, sizeof(thread_array_t) * (mp->thread_count));
/*
* Now, we load the values in the structure above.
*/
RP(mp, user_time).seconds = t_utime;
RP(mp, system_time).seconds = t_stime;
RP(mp, cpu_usage) = t_cpu;
return (KERN_SUCCESS);
}
/*
* prototypes for functions which pg_top needs
*/
char *printable();
/*
* definitions for offsets
*/
#define X_NPROC 0
#define X_HZ 1
#define X_MAXMEM 2
#define NLIST_LAST 3
static char *procstates[] =
{
"",
" starting, ",
" running, ",
" sleeping, ",
" stopped, ",
" zombie, ",
" swapped ",
NULL
};
static char *cpustates[] =
{
"user",
"system",
"idle",
"nice",
NULL
};
static char *state_abbrev[] =
{
"",
"start",
"run\0\0\0",
"sleep",
"stop",
"zomb"
};
static char *memnames[] =
{
"K Tot, ",
"K Free, ",
"K Act, ",
"K Inact, ",
"K Wired, ",
"K in, ",
"K out ",
NULL
};
/*
* format_header()
*
* This function is used to add the username into the
* header information.
*/
char *
format_header(register char *uname_field)
{
register char *ptr;
ptr = header + UNAME_START;
while (*uname_field != '\0')
*ptr++ = *uname_field++;
return (header);
}
char *
format_next_io(caddr_t handle, char *(*getuserid) ())
{
return (fmt);
}
/*
* format_next_process()
*
* This function actually is responsible for the formatting of
* each row which is displayed.
*/
char cmd[MAX_COLS];
char *
format_next_process(caddr_t handle, char *(*getuserid) ())
{
register struct macos_proc *pp;
register long cputime;
register double pct;
struct handle *hp;
char *command; /* text outputted to describe the command */
int show_cmd_local = show_fullcmd;
/*
* we need to keep track of the next proc structure.
*/
hp = (struct handle *) handle;
pp = *(hp->next_proc++);
hp->remaining--;
/*
* get the process structure and take care of the cputime
*/
if ((MPP(pp, p_flag) & P_INMEM) == 0)
{
/* we want to print swapped processes as <pname> */
char *comm = MPP(pp, p_comm);
#define COMSIZ sizeof(MPP(pp, p_comm))
char buf[COMSIZ];
strncpy(buf, comm, COMSIZ);
comm[0] = '<';
strncpy(&comm[1], buf, COMSIZ - 2);
comm[COMSIZ - 2] = '\0';
strncat(comm, ">", COMSIZ - 1);
comm[COMSIZ - 1] = '\0';
command = comm;
}
/*
* count the cpu time, but ignore the interrupts
*
* At the present time (DR2 8/1998), MacOS X doesn't correctly report this
* information through the kinfo_proc structure. We need to get it from
* the task threads.
*
* cputime = PP(pp, p_rtime).tv_sec;
*/
cputime = RP(pp, user_time).seconds + RP(pp, system_time).seconds;
/*
* calculate the base cpu percentages
*
* Again, at the present time, MacOS X doesn't report this information
* through the kinfo_proc. We need to talk to the threads.
*/
pct = (double) (RP(pp, cpu_usage)) / TH_USAGE_SCALE;
/* get the process's command name in to "cmd" */
if (show_fullcmd)
if (get_fullcmd(MPP(pp, p_pid), pp->fullcmd) < 0)
show_cmd_local = 0; /* Don't show if full command not found. */
/*
* format the entry
*/
/*
* In the final version, I would expect this to work correctly, but it
* seems that not all of the fields in the proc structure are being used.
*
* For now, we'll attempt to get some of the things we need from the mach
* task info.
*/
sprintf(fmt,
Proc_format,
MPP(pp, p_pid),
(*getuserid) (MEP(pp, e_pcred.p_ruid)),
0,
pp->thread_count,
format_k(TASKSIZE(pp) / 1024),
format_k(pagetok(RSSIZE(pp))),
state_abbrev[(u_char) MPP(pp, p_stat)],
format_time(cputime),
100.0 * TP(pp, resident_size) / maxmem,
100.0 * pct,
(show_cmd_local == 0 ? command : pp->fullcmd));
return (fmt);
}
int
get_fullcmd(int pid, char *fullcmd)
{
char *args,
*namePtr,
*stringPtr,
*cp;
size_t size = 0;
int mib[4],
maxarg,
numArgs,
c = 0;
mib[0] = CTL_KERN;
mib[1] = KERN_ARGMAX;
size = sizeof(maxarg);
if (sysctl(mib, 2, &maxarg, &size, NULL, 0) == -1)
return -1;
args = (char *) malloc(maxarg);
if (args == NULL)
return -2;
mib[0] = CTL_KERN;
mib[1] = KERN_PROCARGS2;
mib[2] = pid;
if (mib[1] < 1)
return -3;
size = (size_t) maxarg;
if (sysctl(mib, 3, args, &size, NULL, 0) == -1)
return -4;
memcpy(&numArgs, args, sizeof(numArgs));
/* Skip exec_patch. */
for (cp = args + sizeof(numArgs); cp < &args[size]; cp++)
if (*cp == '\0')
break;
if (cp == &args[size])
return -5;
/* Skip trailing '\0' characters. */
for (; cp < &args[size]; cp++)
if (*cp == '\0')
break;
if (cp == &args[size])
return -6;
stringPtr = cp;
/* Convert all '\0' to ' ' in the process arguments portion. */
for (namePtr = NULL; c < numArgs && cp < &args[size]; cp++)
{
if (*cp == '\0')
{
c++;
if (namePtr != NULL)
*namePtr = ' ';
namePtr = cp;
}
}
/* Convert all '\0' to ' ' in the process environment settings portion. */
for (; cp < &args[size]; cp++)
{
if (*cp == '\0')
{
if (namePtr != NULL)
{
if (&namePtr[1] == cp)
break;
*namePtr = ' ';
}
namePtr = cp;
}
}
if (namePtr == NULL || namePtr == stringPtr)
return -7;
/* Get rid of leading whitespace. */
while (stringPtr[0] == ' ' && stringPtr[0] != '\0')
++stringPtr;
strncpy(fullcmd, stringPtr, (size_t) FULLCMDLEN);
return 1;
}
/*
* get_process_info()
*
* This function returns information about the processes
* on the system.
*/
caddr_t
get_process_info(struct system_info *si, struct process_select *sel, int x,
const char *values[])
{
register int i;
register int total_procs;
register int active_procs;
register struct macos_proc **prefp;
register struct macos_proc *pp;
register struct kinfo_proc *pp2;
/*
* these are copied out of sel for speed
*/
int show_idle;
int show_system;
int show_uid;
int show_command;
/* begin mucking */
/* kproc_list = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); */
PGconn *pgconn;
PGresult *pgresult = NULL;
nproc = 0;
pgconn = connect_to_db(values);
if (pgconn != NULL)
{
pgresult = pg_processes(pgconn);
nproc = PQntuples(pgresult);
pbase = (struct kinfo_proc *) malloc(sizeof(struct kinfo_proc *));
}
PQfinish(pgconn);
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
size_t len = nproc;
struct kinfo_proc *buffer;
buffer = (struct kinfo_proc *) malloc(len * sizeof(struct kinfo_proc));
for (i = 0; i < nproc; i++)
{
size_t size = sizeof(struct kinfo_proc);
mib[3] = atoi(PQgetvalue(pgresult, i, 0));
if (sysctl(mib, sizeof(mib) / sizeof(int), &buffer[i], &size, NULL,
0) == -1)
{
perror("sysctl atoi loop");
return "1";
}
}
kproc_list = buffer;
len = nproc;
/* end selena's messing about */
if (nproc > onproc)
{
proc_list = (struct macos_proc *) realloc(proc_list,
sizeof(struct macos_proc) * nproc);
proc_ref = (struct macos_proc **) realloc(proc_ref,
sizeof(struct macos_proc *) * (onproc = nproc));
}
if (proc_ref == NULL || proc_list == NULL || kproc_list == NULL)
{
puke("error: out of memory (%s)", strerror(errno));
return (NULL);
}
/*
* now, our task is to build the array of information we need to function
* correctly. This involves setting a pointer to each real kinfo_proc
* structure returned by kvm_getprocs() in addition to getting the mach
* information for each of those processes.
*/
for (pp2 = kproc_list, i = 0; i < nproc; pp2++, i++)
{
/*
* first, we set the pointer to the reference in the kproc list.
*/
proc_list[i].kproc = pp2;
/*
* then, we load all of the task info for the process
*/
if (PP(pp2, p_stat) != SZOMB)
{
load_thread_info(&proc_list[i]);
}
}
/* get a pointer to the states summary array */
si->procstates = process_states;
/* set up flags which define what we are going to select */
show_idle = sel->idle;
show_uid = sel->uid != -1;
show_command = sel->command != NULL;
show_fullcmd = sel->fullcmd;
/* count up process states and get pointers to interesting procs */
total_procs = 0;
active_procs = 0;
memset((char *) process_states, 0, sizeof(process_states));
prefp = proc_ref;
for (pp = proc_list, i = 0; i < nproc; pp++, i++)
{
/*
* Place pointers to each valid proc structure in proc_ref[]. Process
* slots that are actually in use have a non-zero status field.
* Processes with P_SYSTEM set are system processes---these get
* ignored unless show_sysprocs is set.
*/
if (MPP(pp, p_stat) != 0 &&
(show_system || ((MPP(pp, p_flag) & P_SYSTEM) == 0)))
{
total_procs++;
process_states[(unsigned char) MPP(pp, p_stat)]++;
if ((MPP(pp, p_stat) != SZOMB) &&
(show_idle || (MPP(pp, p_pctcpu) != 0) ||
(MPP(pp, p_stat) == SRUN)) &&
(!show_uid || MEP(pp, e_pcred.p_ruid) == (uid_t) sel->uid))
{
*prefp++ = pp;
active_procs++;
}
}
}
/*
* if requested, sort the "interesting" processes
*/
qsort((char *) proc_ref, active_procs, sizeof(struct macos_proc *),
proc_compare);
/* remember active and total counts */
si->p_total = total_procs;
si->p_active = pref_len = active_procs;
/* pass back a handle */
handle.next_proc = proc_ref;
handle.remaining = active_procs;
return ((caddr_t) & handle);
}
/*
* get_system_info()
*
* This function is responsible for geting the periodic
* system information snapshot.
*/
void
get_system_info(struct system_info *si)
{
register long total;
register int i;
unsigned int count = HOST_CPU_LOAD_INFO_COUNT;
double avg[3];
if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO,
(host_info_t) & cpuload, &count) == KERN_SUCCESS)
{
for (i = 0; i < CPU_STATE_MAX; i++)
{
cp_time[i] = cpuload.cpu_ticks[i];
}
}
#ifdef MAX_VERBOSE
/*
* print out the entries
*/
for (i = 0; i < CPU_STATE_MAX; i++)
printf("cp_time[%d] = %d\n", i, cp_time[i]);
fflush(stdout);
#endif /* MAX_VERBOSE */
/*
* get the load averages
*/
getloadavg(avg, sizeof(avg));
si->load_avg[0] = avg[0];
si->load_avg[1] = avg[1];
si->load_avg[2] = avg[2];
#ifdef MAX_VERBOSE
printf("%-30s%03.2f, %03.2f, %03.2f\n",
"load averages:",
si->load_avg[0],
si->load_avg[1],
si->load_avg[2]);
#endif /* MAX_VERBOSE */
total = percentages(CPU_STATE_MAX, cpu_states, cp_time, cp_old, cp_diff);
/*
* get the memory statistics
*/
{
kern_return_t status;
count = HOST_VM_INFO_COUNT;
status = host_statistics(mach_host_self(), HOST_VM_INFO,
(host_info_t) & vm_stats, &count);
if (status != KERN_SUCCESS)
{
puke("error: vm_statistics() failed (%s)", strerror(errno));
return;
}
/*
* we already have the total memory, we just need to get it in the
* right format.
*/
pagesize = 1; /* temporary fix to div by 0 errors */
memory_stats[0] = pagetok(maxmem / pagesize);
memory_stats[1] = pagetok(vm_stats.free_count);
memory_stats[2] = pagetok(vm_stats.active_count);
memory_stats[3] = pagetok(vm_stats.inactive_count);
memory_stats[4] = pagetok(vm_stats.wire_count);
if (swappgsin < 0)
{
memory_stats[5] = 1;
memory_stats[6] = 1;
}
else
{
memory_stats[5] = pagetok(((vm_stats.pageins - swappgsin)));
memory_stats[6] = pagetok(((vm_stats.pageouts - swappgsout)));
}
swappgsin = vm_stats.pageins;
swappgsout = vm_stats.pageouts;
}
si->cpustates = cpu_states;
si->memory = memory_stats;
si->last_pid = -1;
return;
}
/*
* machine_init()
*
* This function is responsible for filling in the values of the
* statics structure.
*/
int
machine_init(struct statics *stat)
{
size_t size;
size = sizeof(maxmem);
sysctlbyname("hw.physmem", &maxmem, &size, NULL, 0);
size = sizeof(nproc);
sysctlbyname("kern.maxproc", &nproc, &size, NULL, 0);
#ifdef MAX_VERBOSE
printf("%-30s%10d\n", "total system memory:", maxmem);
#endif /* MAX_VERBOSE */
/*
* calculate the pageshift from the system page size
*/
pagesize = getpagesize();
pageshift = 0;
while ((pagesize >>= 1) > 0)
pageshift++;
pageshift -= LOG1024;
/*
* fill in the statics information
*/
stat->procstate_names = procstates;
stat->cpustate_names = cpustates;
stat->memory_names = memnames;
stat->flags.fullcmds = 1;
return (0);
}
/* comparison routine for qsort */
/*
* proc_compare - comparison function for "qsort"
* Compares the resource consumption of two processes using five
* distinct keys. The keys (in descending order of importance) are:
* percent cpu, cpu ticks, state, resident set size, total virtual
* memory usage. The process states are ordered as follows (from least
* to most important): WAIT, zombie, sleep, stop, start, run. The
* array declaration below maps a process state index into a number
* that reflects this ordering.
*/
static unsigned char sorted_state[] =
{
0, /* not used */
3, /* sleep */
1, /* ABANDONED (WAIT) */
6, /* run */
5, /* start */
2, /* zombie */
4 /* stop */
};
int
proc_compare(const void *pp1, const void *pp2)
{
register struct macos_proc *p1;
register struct macos_proc *p2;
register int result;
register pctcpu lresult;
/* remove one level of indirection */
p1 = *(struct macos_proc **) pp1;
p2 = *(struct macos_proc **) pp2;
/* compare percent cpu (pctcpu) */
if ((lresult = RP(p2, cpu_usage) - RP(p1, cpu_usage)) == 0)
{
/* use cpticks to break the tie */
if ((result = MPP(p2, p_cpticks) - MPP(p1, p_cpticks)) == 0)
{
/* use process state to break the tie */
if ((result = sorted_state[(unsigned char) MPP(p2, p_stat)] -
sorted_state[(unsigned char) MPP(p1, p_stat)]) == 0)
{
/* use priority to break the tie */
if ((result = MPP(p2, p_priority) - MPP(p1, p_priority)) == 0)
{
/* use resident set size (rssize) to break the tie */
if ((result = RSSIZE(p2) - RSSIZE(p1)) == 0)
{
/* use total memory to break the tie */
result = PROCSIZE(p2->kproc) - PROCSIZE(p1->kproc);
}
}
}
}
}
else
{
result = lresult < 0 ? -1 : 1;
}
return (result);
}
/*
* proc_owner(pid) - returns the uid that owns process "pid", or -1 if
* the process does not exist.
* It is EXTREMLY IMPORTANT that this function work correctly.
* If pg_top runs setuid root (as in SVR4), then this function
* is the only thing that stands in the way of a serious
* security problem. It validates requests for the "kill"
* and "renice" commands.
*/
uid_t
proc_owner(pid_t pid)
{
register int cnt;
register struct macos_proc **prefp;
register struct macos_proc *pp;
prefp = proc_ref;
cnt = pref_len;
while (--cnt >= 0)
{
pp = *prefp++;
if (MPP(pp, p_pid) == (pid_t) pid)
{
return ((int) MEP(pp, e_pcred.p_ruid));
}
}
return (-1);
}
0707010000002A000081A400000000000000000000000166622DE800000431000000000000000000000000000000000000002600000000pg_top-4.1.1/machine/m_macosx.man.rstMacOS X NOTES
=============
The display is pretty close to the recommended display and also that
of a normal 4.4 BSD system. The NICE column has been changed to be
the number of threads for each process. The SIZE column reflects the
total size of the process (resident + non-resident) while the RES
column shows only the resident size. The STATE column uses
information taken from the kinfo_proc structure p_pstat member. It
will accurately display the state of stopped and zombie processes, but
I am not really sure about the other states. Finally, the MEM column
is included which displays the percent of total memory per the ps
command.
The MacOS X module was written by Andrew S. Townley <atownley@primenet.com>.
Many thanks to William LeFebvre who is the original author
of the top utility and to Mike Rhee who showed the utility
to me in the first place. Thanks also to Christos Zoulas
who wrote the 4.4 BSD implementation of the machine module.
I also got some pointers from the NEXTSTEP 3.2 and OSF/1
versions by Tim Pugh and Anthony Baxter, respectively.
0707010000002B000081A400000000000000000000000166622DE800005050000000000000000000000000000000000000002000000000pg_top-4.1.1/machine/m_netbsd.c/* $NetBSD: m_netbsd15.c,v 1.16 2002/03/23 01:28:11 thorpej Exp $ */
/*
* pg_top - a top PostgreSQL users display for Unix
*
* SYNOPSIS: For a NetBSD-1.5 (or later) system
*
* DESCRIPTION:
* Originally written for BSD4.4 system by Christos Zoulas.
* Based on the FreeBSD 2.0 version by Steven Wallace and Wolfram Schneider.
* NetBSD-1.0 port by Arne Helme. Process ordering by Luke Mewburn.
* NetBSD-1.3 port by Luke Mewburn, based on code by Matthew Green.
* NetBSD-1.4/UVM port by matthew green.
* NetBSD-1.5 port by Simon Burge.
* NetBSD-1.6/UBC port by Tomas Svensson.
* NetBSD-3.0/kernel threads port Simon Burge.
* -
* This is the machine-dependent module for NetBSD-1.5 and later
* works for:
* NetBSD-1.6
* NetBSD-2.0
* NetBSD-3.0 (when released)
* NetBSD-3.99.10 (current development version)
* and should work for:
* NetBSD-1.5
* -
* Doesn't include separate CPU states line per cpu on multiprocessor
* systems like the NetBSD version of pg_top, but that requires some
* recent kernel support. This module forsakes that functionality as
* a tradeoff for working on older versions of NetBSD.
* -
* pg_top does not need to be installed setuid or setgid with this module.
*
* LIBS: -lkvm
*
* AUTHORS: Christos Zoulas <christos@ee.cornell.edu>
* Steven Wallace <swallace@freebsd.org>
* Wolfram Schneider <wosch@cs.tu-berlin.de>
* Arne Helme <arne@acm.org>
* Luke Mewburn <lukem@netbsd.org>
* matthew green <mrg@eterna.com.au>
* Simon Burge <simonb@netbsd.org>
* Tomas Svensson <ts@unix1.net>
*
*
* $Id: m_netbsd15.c,v 1.16 2002/03/23 01:28:11 thorpej Exp $
*/
#include <sys/param.h>
#include <sys/sysctl.h>
#include <sys/sched.h>
#include <sys/swap.h>
#include <uvm/uvm_extern.h>
#include <err.h>
#include <errno.h>
#include <kvm.h>
#include <math.h>
#include <nlist.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "os.h"
#include "pg_top.h"
#include "machine.h"
#include "utils.h"
#include "display.h"
#include "loadavg.h"
void percentages64 __P((int, int *, u_int64_t *, u_int64_t *, u_int64_t *));
/* get_process_info passes back a handle. This is what it looks like: */
struct handle
{
struct kinfo_proc2 **next_proc; /* points to next valid proc pointer */
int remaining; /* number of pointers remaining */
};
/* define what weighted cpu is. */
#define weighted_cpu(pct, pp) ((pp)->p_swtime == 0 ? 0.0 : \
((pct) / (1.0 - exp((pp)->p_swtime * logcpu))))
/* what we consider to be process size: */
#define PROCSIZE(pp) \
((pp)->p_vm_tsize + (pp)->p_vm_dsize + (pp)->p_vm_ssize)
/*
* These definitions control the format of the per-process area
*/
static char header[] =
" PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND";
/* 0123456 -- field to fill in starts at header+6 */
#define UNAME_START 6
#define Proc_format \
"%5d %-8.8s %3d %4d%7s %5s %-8.8s%7s %5.2f%% %5.2f%% %.12s"
/*
* Process state names for the "STATE" column of the display.
*/
const char *state_abbrev[] = {
"", "START", "RUN", "SLEEP", "STOP", "ZOMB", "DEAD", "CPU"
};
static kvm_t * kd;
/* these are retrieved from the kernel in _init */
static double logcpu;
static int hz;
static int ccpu;
/* these are for calculating cpu state percentages */
static u_int64_t cp_time[CPUSTATES];
static u_int64_t cp_old[CPUSTATES];
static u_int64_t cp_diff[CPUSTATES];
/* these are for detailing the process states */
int process_states[8];
char *procstatenames[] = {
"", " starting, ", " runnable, ", " sleeping, ", " stopped, ",
" zombie, ", " dead, ", " on processor, ",
NULL
};
/* these are for detailing the cpu states */
int cpu_states[CPUSTATES];
char *cpustatenames[] = {
"user", "nice", "system", "interrupt", "idle", NULL
};
/* these are for detailing the memory statistics */
long memory_stats[7];
char *memorynames[] = {
"K Act, ", "K Inact, ", "K Wired, ", "K Exec, ", "K File, ",
"K Free, ",
NULL
};
long swap_stats[4];
char *swapnames[] = {
"K Total, ", "K Used, ", "K Free, ",
NULL
};
/* these are names given to allowed sorting orders -- first is default */
char *ordernames[] = {
"cpu",
"pri",
"res",
"size",
"state",
"time",
NULL
};
/* forward definitions for comparison functions */
static int compare_cpu __P((struct proc **, struct proc **));
static int compare_prio __P((struct proc **, struct proc **));
static int compare_res __P((struct proc **, struct proc **));
static int compare_size __P((struct proc **, struct proc **));
static int compare_state __P((struct proc **, struct proc **));
static int compare_time __P((struct proc **, struct proc **));
int (*proc_compares[])
__P((struct proc **, struct proc **)) =
{
compare_cpu,
compare_prio,
compare_res,
compare_size,
compare_state,
compare_time,
NULL
};
/* these are for keeping track of the proc array */
static int nproc;
static int onproc = -1;
static int pref_len;
static struct kinfo_proc2 *pbase;
static struct kinfo_proc2 **pref;
/* these are for getting the memory statistics */
static int pageshift; /* log base 2 of the pagesize */
/* define pagetok in terms of pageshift */
#define pagetok(size) ((size) << pageshift)
/* Pre/post lwp process states */
#ifndef SDEAD
#define SDEAD LSDEAD
#define SRUN LSRUN
#define SONPROC LSONPROC
#define SSLEEP LSSLEEP
#define P_INMEM L_INMEM
#endif
int
machine_init(statics)
struct statics *statics;
{
int pagesize;
int mib[2];
size_t size;
struct clockinfo clockinfo;
if ((kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, "kvm_open")) == NULL)
return -1;
mib[0] = CTL_KERN;
mib[1] = KERN_CCPU;
size = sizeof(ccpu);
if (sysctl(mib, 2, &ccpu, &size, NULL, 0) == -1)
{
fprintf(stderr, "pg_top: sysctl kern.ccpu failed: %s\n",
strerror(errno));
return (-1);
}
mib[0] = CTL_KERN;
mib[1] = KERN_CLOCKRATE;
size = sizeof(clockinfo);
if (sysctl(mib, 2, &clockinfo, &size, NULL, 0) == -1)
{
fprintf(stderr, "pg_top: sysctl kern.clockrate failed: %s\n",
strerror(errno));
return (-1);
}
hz = clockinfo.stathz;
/* this is used in calculating WCPU -- calculate it ahead of time */
logcpu = log(loaddouble(ccpu));
pbase = NULL;
pref = NULL;
nproc = 0;
onproc = -1;
/* get the page size with "getpagesize" and calculate pageshift from it */
pagesize = getpagesize();
pageshift = 0;
while (pagesize > 1)
{
pageshift++;
pagesize >>= 1;
}
/* we only need the amount of log(2)1024 for our conversion */
pageshift -= LOG1024;
/* fill in the statics information */
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
statics->swap_names = swapnames;
statics->order_names = ordernames;
/* all done! */
return (0);
}
char *format_header(uname_field)
char *uname_field;
{
char *ptr;
ptr = header + UNAME_START;
while (*uname_field != '\0')
{
*ptr++ = *uname_field++;
}
return (header);
}
void
get_system_info(si)
struct system_info *si;
{
size_t ssize;
int mib[2];
struct uvmexp_sysctl uvmexp;
struct swapent *sep,
*seporig;
u_int64_t totalsize,
totalinuse;
int size,
inuse,
ncounted;
int rnswap,
nswap;
mib[0] = CTL_KERN;
mib[1] = KERN_CP_TIME;
ssize = sizeof(cp_time);
if (sysctl(mib, 2, cp_time, &ssize, NULL, 0) < 0)
{
fprintf(stderr, "pg_top: sysctl kern.cp_time failed: %s\n",
strerror(errno));
quit(23);
}
if (getloadavg(si->load_avg, NUM_AVERAGES) < 0)
{
int i;
warn("can't getloadavg");
for (i = 0; i < NUM_AVERAGES; i++)
si->load_avg[i] = 0.0;
}
/* convert cp_time counts to percentages */
percentages64(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
mib[0] = CTL_VM;
mib[1] = VM_UVMEXP2;
ssize = sizeof(uvmexp);
if (sysctl(mib, 2, &uvmexp, &ssize, NULL, 0) < 0)
{
fprintf(stderr, "pg_top: sysctl vm.uvmexp2 failed: %s\n",
strerror(errno));
quit(23);
}
/* convert memory stats to Kbytes */
memory_stats[0] = pagetok(uvmexp.active);
memory_stats[1] = pagetok(uvmexp.inactive);
memory_stats[2] = pagetok(uvmexp.wired);
memory_stats[3] = pagetok(uvmexp.execpages);
memory_stats[4] = pagetok(uvmexp.filepages);
memory_stats[5] = pagetok(uvmexp.free);
swap_stats[0] = swap_stats[1] = swap_stats[2] = 0;
seporig = NULL;
do
{
nswap = swapctl(SWAP_NSWAP, 0, 0);
if (nswap < 1)
break;
/*
* Use seporig to keep track of the malloc'd memory base, as sep will
* be incremented in the for loop below.
*/
seporig = sep = (struct swapent *) malloc(nswap * sizeof(*sep));
if (sep == NULL)
break;
rnswap = swapctl(SWAP_STATS, (void *) sep, nswap);
if (nswap != rnswap)
break;
totalsize = totalinuse = ncounted = 0;
for (; rnswap-- > 0; sep++)
{
ncounted++;
size = sep->se_nblks;
inuse = sep->se_inuse;
totalsize += size;
totalinuse += inuse;
}
swap_stats[0] = dbtob(totalsize) / 1024;
swap_stats[1] = dbtob(totalinuse) / 1024;
swap_stats[2] = dbtob(totalsize) / 1024 - swap_stats[1];
/*
* Free here, before we malloc again in the next iteration of this
* loop.
*/
if (seporig)
{
free(seporig);
seporig = NULL;
}
} while (0);
/*
* Catch the case where we malloc'd, but then exited the loop due to nswap
* != rnswap.
*/
if (seporig)
free(seporig);
memory_stats[6] = -1;
swap_stats[3] = -1;
/* set arrays and strings */
si->cpustates = cpu_states;
si->memory = memory_stats;
si->swap = swap_stats;
si->last_pid = -1;
}
caddr_t
get_process_info(si, sel, compare_index)
struct system_info *si;
struct process_select *sel;
int compare_index;
{
int i;
int total_procs;
int active_procs;
struct kinfo_proc2 **prefp;
struct kinfo_proc2 *pp;
/* these are copied out of sel for speed */
int show_idle;
int show_system;
int show_uid;
int show_command;
static struct handle handle;
pbase = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(struct kinfo_proc2), &nproc);
if (nproc > onproc)
pref = (struct kinfo_proc2 **) realloc(pref,
sizeof(struct kinfo_proc2 *) * (onproc = nproc));
if (pref == NULL || pbase == NULL)
{
(void) fprintf(stderr, "pg_top: Out of memory.\n");
quit(23);
}
/* get a pointer to the states summary array */
si->procstates = process_states;
/* set up flags which define what we are going to select */
show_idle = sel->idle;
show_system = sel->system;
show_uid = sel->uid != -1;
show_command = sel->command != NULL;
/* count up process states and get pointers to interesting procs */
total_procs = 0;
active_procs = 0;
memset((char *) process_states, 0, sizeof(process_states));
prefp = pref;
for (pp = pbase, i = 0; i < nproc; pp++, i++)
{
/*
* Place pointers to each valid proc structure in pref[]. Process
* slots that are actually in use have a non-zero status field.
* Processes with P_SYSTEM set are system processes---these get
* ignored unless show_sysprocs is set.
*/
if (pp->p_stat != 0 && (show_system || ((pp->p_flag & P_SYSTEM) == 0)))
{
total_procs++;
process_states[(unsigned char) pp->p_stat]++;
if (pp->p_stat != SZOMB && pp->p_stat != SDEAD &&
(show_idle || (pp->p_pctcpu != 0) ||
(pp->p_stat == SRUN || pp->p_stat == SONPROC)) &&
(!show_uid || pp->p_ruid == (uid_t) sel->uid))
{
*prefp++ = pp;
active_procs++;
}
}
}
/* if requested, sort the "interesting" processes */
qsort((char *) pref, active_procs, sizeof(struct kinfo_proc2 *),
proc_compares[compare_index]);
/* remember active and total counts */
si->p_total = total_procs;
si->p_active = pref_len = active_procs;
/* pass back a handle */
handle.next_proc = pref;
handle.remaining = active_procs;
return ((caddr_t) & handle);
}
char *format_next_process(handle, get_userid)
caddr_t handle;
char *(*get_userid) __P((int));
{
struct kinfo_proc2 *pp;
long cputime;
double pct;
struct handle *hp;
const char *statep;
#ifdef KI_NOCPU
char state[10];
#endif
char wmesg[KI_WMESGLEN + 1];
static char fmt[128]; /* static area where result is built */
char *pretty = "";
/* find and remember the next proc structure */
hp = (struct handle *) handle;
pp = *(hp->next_proc++);
hp->remaining--;
/* get the process's user struct and set cputime */
if ((pp->p_flag & P_INMEM) == 0)
pretty = "<>";
else if ((pp->p_flag & P_SYSTEM) != 0)
pretty = "[]";
if (pretty[0] != '\0')
{
/*
* Print swapped processes as <pname> and system processes as [pname]
*/
char *comm = pp->p_comm;
#define COMSIZ sizeof(pp->p_comm)
char buf[COMSIZ];
(void) strncpy(buf, comm, COMSIZ);
comm[0] = pretty[0];
(void) strncpy(&comm[1], buf, COMSIZ - 2);
comm[COMSIZ - 2] = '\0';
(void) strncat(comm, &pretty[1], COMSIZ - 1);
comm[COMSIZ - 1] = '\0';
}
#if 0
/* This does not produce the correct results */
cputime = pp->p_uticks + pp->p_sticks + pp->p_iticks;
#else
cputime = pp->p_rtime_sec; /* This does not count interrupts */
#endif
/* calculate the base for cpu percentages */
pct = pctdouble(pp->p_pctcpu);
if (pp->p_stat == SSLEEP)
{
strlcpy(wmesg, pp->p_wmesg, sizeof(wmesg));
statep = wmesg;
}
else
statep = state_abbrev[(unsigned) pp->p_stat];
#ifdef KI_NOCPU
/* Post-1.5 change: add cpu number if appropriate */
if (pp->p_cpuid != KI_NOCPU)
{
switch (pp->p_stat)
{
case SONPROC:
case SRUN:
case SSLEEP:
snprintf(state, sizeof(state), "%.6s/%lld",
statep, (long long) pp->p_cpuid);
statep = state;
break;
}
}
#endif
/* format this entry */
sprintf(fmt,
Proc_format,
pp->p_pid,
(*get_userid) (pp->p_ruid),
pp->p_priority - PZERO,
pp->p_nice - NZERO,
format_k(pagetok(PROCSIZE(pp))),
format_k(pagetok(pp->p_vm_rssize)),
statep,
format_time(cputime),
100.0 * weighted_cpu(pct, pp),
100.0 * pct,
printable(pp->p_comm));
/* return the result */
return (fmt);
}
/* comparison routines for qsort */
/*
* There are currently four possible comparison routines. main selects
* one of these by indexing in to the array proc_compares.
*
* Possible keys are defined as macros below. Currently these keys are
* defined: percent cpu, cpu ticks, process state, resident set size,
* total virtual memory usage. The process states are ordered as follows
* (from least to most important): WAIT, zombie, sleep, stop, start, run.
* The array declaration below maps a process state index into a number
* that reflects this ordering.
*/
/*
* First, the possible comparison keys. These are defined in such a way
* that they can be merely listed in the source code to define the actual
* desired ordering.
*/
#define ORDERKEY_PCTCPU \
if (lresult = (pctcpu)(p2)->p_pctcpu - (pctcpu)(p1)->p_pctcpu,\
(result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
#define ORDERKEY_CPTICKS \
if (lresult = (pctcpu)(p2)->p_rtime_sec \
- (pctcpu)(p1)->p_rtime_sec,\
(result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
#define ORDERKEY_STATE \
if ((result = sorted_state[(int)(p2)->p_stat] - \
sorted_state[(int)(p1)->p_stat] ) == 0)
#define ORDERKEY_PRIO \
if ((result = (p2)->p_priority - (p1)->p_priority) == 0)
#define ORDERKEY_RSSIZE \
if ((result = p2->p_vm_rssize - p1->p_vm_rssize) == 0)
#define ORDERKEY_MEM \
if ((result = (PROCSIZE(p2) - PROCSIZE(p1))) == 0)
/*
* Now the array that maps process state to a weight.
* The order of the elements should match those in state_abbrev[]
*/
static int sorted_state[] = {
0, /* (not used) ? */
6, /* "start" SIDL */
4, /* "run" SRUN */
3, /* "sleep" SSLEEP */
3, /* "stop" SSTOP */
2, /* "dead" SDEAD */
1, /* "zomb" SZOMB */
5, /* "onproc" SONPROC */
};
/* compare_cpu - the comparison function for sorting by cpu percentage */
static int
compare_cpu(pp1, pp2)
struct proc **pp1,
**pp2;
{
struct kinfo_proc2 *p1;
struct kinfo_proc2 *p2;
int result;
pctcpu lresult;
/* remove one level of indirection */
p1 = *(struct kinfo_proc2 **) pp1;
p2 = *(struct kinfo_proc2 **) pp2;
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
/* compare_prio - the comparison function for sorting by process priority */
static int
compare_prio(pp1, pp2)
struct proc **pp1,
**pp2;
{
struct kinfo_proc2 *p1;
struct kinfo_proc2 *p2;
int result;
pctcpu lresult;
/* remove one level of indirection */
p1 = *(struct kinfo_proc2 **) pp1;
p2 = *(struct kinfo_proc2 **) pp2;
ORDERKEY_PRIO
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
/* compare_res - the comparison function for sorting by resident set size */
static int
compare_res(pp1, pp2)
struct proc **pp1,
**pp2;
{
struct kinfo_proc2 *p1;
struct kinfo_proc2 *p2;
int result;
pctcpu lresult;
/* remove one level of indirection */
p1 = *(struct kinfo_proc2 **) pp1;
p2 = *(struct kinfo_proc2 **) pp2;
ORDERKEY_RSSIZE
ORDERKEY_MEM
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
/* compare_size - the comparison function for sorting by total memory usage */
static int
compare_size(pp1, pp2)
struct proc **pp1,
**pp2;
{
struct kinfo_proc2 *p1;
struct kinfo_proc2 *p2;
int result;
pctcpu lresult;
/* remove one level of indirection */
p1 = *(struct kinfo_proc2 **) pp1;
p2 = *(struct kinfo_proc2 **) pp2;
ORDERKEY_MEM
ORDERKEY_RSSIZE
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
/* compare_state - the comparison function for sorting by process state */
static int
compare_state(pp1, pp2)
struct proc **pp1,
**pp2;
{
struct kinfo_proc2 *p1;
struct kinfo_proc2 *p2;
int result;
pctcpu lresult;
/* remove one level of indirection */
p1 = *(struct kinfo_proc2 **) pp1;
p2 = *(struct kinfo_proc2 **) pp2;
ORDERKEY_STATE
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_PRIO
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
/* compare_time - the comparison function for sorting by total cpu time */
static int
compare_time(pp1, pp2)
struct proc **pp1,
**pp2;
{
struct kinfo_proc2 *p1;
struct kinfo_proc2 *p2;
int result;
pctcpu lresult;
/* remove one level of indirection */
p1 = *(struct kinfo_proc2 **) pp1;
p2 = *(struct kinfo_proc2 **) pp2;
ORDERKEY_CPTICKS
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
/*
* proc_owner(pid) - returns the uid that owns process "pid", or -1 if
* the process does not exist.
* It is EXTREMLY IMPORTANT that this function work correctly.
* If pg_top runs setuid root (as in SVR4), then this function
* is the only thing that stands in the way of a serious
* security problem. It validates requests for the "kill"
* and "renice" commands.
*/
int
proc_owner(pid)
int pid;
{
int cnt;
struct kinfo_proc2 **prefp;
struct kinfo_proc2 *pp;
prefp = pref;
cnt = pref_len;
while (--cnt >= 0)
{
pp = *prefp++;
if (pp->p_pid == (pid_t) pid)
return (pp->p_ruid);
}
return (-1);
}
/*
* percentages(cnt, out, new, old, diffs) - calculate percentage change
* between array "old" and "new", putting the percentages i "out".
* "cnt" is size of each array and "diffs" is used for scratch space.
* The array "old" is updated on each call.
* The routine assumes modulo arithmetic. This function is especially
* useful on BSD mchines for calculating cpu state percentages.
*/
void
percentages64(cnt, out, new, old, diffs)
int cnt;
int *out;
u_int64_t *new;
u_int64_t *old;
u_int64_t *diffs;
{
int i;
u_int64_t change;
u_int64_t total_change;
u_int64_t *dp;
u_int64_t half_total;
/* initialization */
total_change = 0;
dp = diffs;
/* calculate changes for each state and the overall change */
for (i = 0; i < cnt; i++)
{
/*
* Don't worry about wrapping - even at hz=1GHz, a u_int64_t will last
* at least 544 years.
*/
change = *new - *old;
total_change += (*dp++ = change);
*old++ = *new++;
}
/* avoid divide by zero potential */
if (total_change == 0)
total_change = 1;
/* calculate percentages based on overall change, rounding up */
half_total = total_change / 2;
for (i = 0; i < cnt; i++)
*out++ = (int) ((*diffs++ * 1000 + half_total) / total_change);
}
void
get_io_info(struct io_info *io_info)
{
/* Not supported yet */
memset(io_info, 0, sizeof(*io_info));
}
0707010000002C000081A400000000000000000000000166622DE8000000C9000000000000000000000000000000000000002600000000pg_top-4.1.1/machine/m_netbsd.man.rstNetBSD NOTES
============
This module has been tested on NetBSD 1.6, NetBSD 2.0 and NetBSD 3.0.
It should also work on NetBSD 1.5, and probably any newer releases of
NetBSD with little or no changes.
0707010000002D000081A400000000000000000000000166622DE8000055C4000000000000000000000000000000000000002100000000pg_top-4.1.1/machine/m_openbsd.c/*-
* Copyright (c) 1994 Thorsten Lockert <tholo@sigmasoft.com>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* AUTHOR: Thorsten Lockert <tholo@sigmasoft.com>
* Adapted from BSD4.4 by Christos Zoulas <christos@ee.cornell.edu>
* Patch for process wait display by Jarl F. Greipsland <jarle@idt.unit.no>
* Patch for -DORDER by Kenneth Stailey <kstailey@disclosure.com>
* Patch for new swapctl(2) by Tobias Weingartner <weingart@openbsd.org>
* Adapted for pg_top by Mark Wong <markwkm@gmail.com>
*/
#include <sys/types.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/swap.h>
#include <sys/sysctl.h>
#include <sys/tree.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <err.h>
#include <errno.h>
#include "pg_top.h"
#include "display.h"
#include "machine.h"
#include "utils.h"
#include "loadavg.h"
static long swapmode(long *, long *);
static char *format_comm(struct kinfo_proc *);
/* get_process_info passes back a handle. This is what it looks like: */
struct handle
{
struct kinfo_proc **next_proc; /* points to next valid proc pointer */
int remaining; /* number of pointers remaining */
};
struct pg_proc
{
RB_ENTRY(pg_proc) entry;
pid_t pid;
char *name;
char *usename;
int pgstate;
unsigned long xtime;
unsigned long qtime;
unsigned int locks;
/* Replication data */
char *application_name;
char *client_addr;
char *repstate;
char *primary;
char *sent;
char *write;
char *flush;
char *replay;
long long sent_lag;
long long write_lag;
long long flush_lag;
long long replay_lag;
};
int topproccmp(struct pg_proc *, struct pg_proc *);
RB_HEAD(pgproc, pg_proc) head_proc = RB_INITIALIZER(&head_proc);
RB_PROTOTYPE(pgproc, pg_proc, entry, topproccmp)
RB_GENERATE(pgproc, pg_proc, entry, topproccmp)
/* what we consider to be process size: */
#define PROCSIZE(pp) ((pp)->p_vm_tsize + (pp)->p_vm_dsize + (pp)->p_vm_ssize)
/*
* These definitions control the format of the per-process area
*/
static char header[] =
" PID X SIZE RES STATE XTIME QTIME %CPU LOCKS COMMAND";
/* 0123456 -- field to fill in starts at header+6 */
#define UNAME_START 6
#define Proc_format \
"%5d %-8.8s %5s %5s %-8s %5s %5s %5.2f %5d %.50s"
/* process state names for the "STATE" column of the display */
/*
* the extra nulls in the string "run" are for adding a slash and the
* processor number when needed
*/
char *state_abbrev[] = {
"", "start", "run", "sleep", "stop", "zomb", "dead", "onproc"
};
/* these are for calculating cpu state percentages */
static int64_t * *cp_time;
static int64_t * *cp_old;
static int64_t * *cp_diff;
/* these are for detailing the process states */
int process_states[6];
/* these are for detailing the cpu states */
int64_t *cpu_states;
char *cpustatenames[] = {
"user", "nice", "system", "interrupt", "idle", NULL
};
/* these are for detailing the memory statistics */
long memory_stats[8];
char *memorynames[] = {
"Real: ", "K/", "K act/tot ", "Free: ", "K ",
"Swap: ", "K/", "K used/tot",
NULL
};
/* these are names given to allowed sorting orders -- first is default */
char *ordernames[] = {
"cpu", "size", "res", "time", "pri", NULL
};
/* compare routines */
static int compare_cpu(), compare_size(), compare_res(), compare_time(), compare_prio();
int (*proc_compares[]) () =
{
compare_cpu,
compare_size,
compare_res,
compare_time,
compare_prio,
NULL
};
/* these are for keeping track of the proc array */
static int nproc;
static int onproc = -1;
static int pref_len;
static struct kinfo_proc *pbase;
static struct kinfo_proc **pref;
/* these are for getting the memory statistics */
static int pageshift; /* log base 2 of the pagesize */
/* define pagetok in terms of pageshift */
#define pagetok(size) ((size) << pageshift)
int ncpu;
unsigned int maxslp;
int
machine_init(struct statics *statics)
{
size_t size = sizeof(ncpu);
int mib[2],
pagesize,
cpu;
mib[0] = CTL_HW;
mib[1] = HW_NCPU;
if (sysctl(mib, 2, &ncpu, &size, NULL, 0) == -1)
return (-1);
cpu_states = calloc(ncpu, CPUSTATES * sizeof(int64_t));
if (cpu_states == NULL)
err(1, NULL);
cp_time = calloc(ncpu, sizeof(int64_t *));
cp_old = calloc(ncpu, sizeof(int64_t *));
cp_diff = calloc(ncpu, sizeof(int64_t *));
if (cp_time == NULL || cp_old == NULL || cp_diff == NULL)
err(1, NULL);
for (cpu = 0; cpu < ncpu; cpu++)
{
cp_time[cpu] = calloc(CPUSTATES, sizeof(int64_t));
cp_old[cpu] = calloc(CPUSTATES, sizeof(int64_t));
cp_diff[cpu] = calloc(CPUSTATES, sizeof(int64_t));
if (cp_time[cpu] == NULL || cp_old[cpu] == NULL ||
cp_diff[cpu] == NULL)
err(1, NULL);
}
pbase = NULL;
pref = NULL;
onproc = -1;
nproc = 0;
/*
* get the page size with "getpagesize" and calculate pageshift from it
*/
pagesize = getpagesize();
pageshift = 0;
while (pagesize > 1)
{
pageshift++;
pagesize >>= 1;
}
/* we only need the amount of log(2)1024 for our conversion */
pageshift -= LOG1024;
/* fill in the statics information */
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
statics->order_names = ordernames;
return (0);
}
char *
format_header(char *uname_field)
{
char *ptr;
ptr = header + UNAME_START;
while (*uname_field != '\0')
*ptr++ = *uname_field++;
return (header);
}
void
get_system_info(struct system_info *si)
{
static int sysload_mib[] = {CTL_VM, VM_LOADAVG};
static int uvmexp_mib[] = {CTL_VM, VM_UVMEXP};
struct loadavg sysload;
struct uvmexp uvmexp;
double *infoloadp;
size_t size;
int i;
int64_t *tmpstate;
/*
* Can't track down the exact issue, but I think it has something to do
* with pg_top being the only process connected to the database, that its
* pid is gone before data is extracted from the process table. So assume
* that there's nothing worth getting from the process table unless there
* is more than 1 process.
*/
if (nproc > 1)
{
if (ncpu > 1)
{
int cp_time_mib[] = {CTL_KERN, KERN_CPTIME2, 0};
size = CPUSTATES * sizeof(int64_t);
for (i = 0; i < ncpu; i++)
{
cp_time_mib[2] = i;
tmpstate = cpu_states + (CPUSTATES * i);
if (sysctl(cp_time_mib, 3, cp_time[i], &size, NULL, 0) < 0)
warn("sysctl kern.cp_time2 failed");
/* convert cp_time2 counts to percentages */
else
(void) percentages(CPUSTATES, tmpstate, cp_time[i],
cp_old[i], cp_diff[i]);
}
}
else
{
int cp_time_mib[] = {CTL_KERN, KERN_CPTIME};
long cp_time_tmp[CPUSTATES];
size = sizeof(cp_time_tmp);
if (sysctl(cp_time_mib, 2, cp_time_tmp, &size, NULL, 0) < 0)
warn("sysctl kern.cp_time failed");
else
{
for (i = 0; i < CPUSTATES; i++)
cp_time[0][i] = cp_time_tmp[i];
/* convert cp_time counts to percentages */
(void) percentages(CPUSTATES, cpu_states, cp_time[0],
cp_old[0], cp_diff[0]);
}
}
}
size = sizeof(sysload);
if (sysctl(sysload_mib, 2, &sysload, &size, NULL, 0) < 0)
warn("sysctl failed");
infoloadp = si->load_avg;
for (i = 0; i < 3; i++)
*infoloadp++ = ((double) sysload.ldavg[i]) / sysload.fscale;
/* get total -- systemwide main memory usage structure */
size = sizeof(uvmexp);
if (sysctl(uvmexp_mib, 2, &uvmexp, &size, NULL, 0) < 0)
{
warn("sysctl failed");
bzero(&uvmexp, sizeof(uvmexp));
}
/* convert memory stats to Kbytes */
memory_stats[0] = -1;
memory_stats[1] = pagetok(uvmexp.active);
memory_stats[2] = pagetok(uvmexp.npages - uvmexp.free);
memory_stats[3] = -1;
memory_stats[4] = pagetok(uvmexp.free);
memory_stats[5] = -1;
if (!swapmode(&memory_stats[6], &memory_stats[7]))
{
memory_stats[6] = 0;
memory_stats[7] = 0;
}
/* set arrays and strings */
si->cpustates = cpu_states;
si->memory = memory_stats;
si->last_pid = -1;
}
static struct handle handle;
caddr_t
get_process_info(struct system_info *si, struct process_select *sel,
int compare_index, struct pg_conninfo_ctx *conninfo, int mode)
{
int show_idle,
show_cmd;
int total_procs,
active_procs;
struct kinfo_proc **prefp,
*pp;
int mib[6];
size_t size;
int i;
PGresult *pgresult = NULL;
struct pg_proc *n, *p;
size = (size_t) sizeof(struct kinfo_proc);
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PID;
mib[4] = sizeof(struct kinfo_proc);
mib[5] = 1;
nproc = 0;
connect_to_db(conninfo);
if (conninfo->connection != NULL)
{
if (mode == MODE_REPLICATION)
{
pgresult = pg_replication(conninfo->connection);
}
else
{
pgresult = pg_processes(conninfo->connection);
}
nproc = PQntuples(pgresult);
if (nproc > onproc)
pbase = (struct kinfo_proc *)
realloc(pbase, sizeof(struct kinfo_proc) * nproc);
}
if (nproc > onproc)
pref = (struct kinfo_proc **) realloc(pref,
sizeof(struct kinfo_proc *) * (onproc = nproc));
if (pref == NULL)
{
warnx("Out of memory.");
quit(23);
}
/* get a pointer to the states summary array */
si->procstates = process_states;
/* set up flags which define what we are going to select */
show_idle = sel->idle;
show_cmd = sel->command != NULL;
/* count up process states and get pointers to interesting procs */
total_procs = 0;
active_procs = 0;
memset((char *) process_states, 0, sizeof(process_states));
prefp = pref;
i = 0;
for (pp = pbase; pp < &pbase[nproc]; pp++)
{
mib[3] = atoi(PQgetvalue(pgresult, i, 0));
if (sysctl(mib, 6, &pbase[i], &size, NULL, 0) != 0)
{
/*
* It appears that when pg_top is the only process accessing the
* database, the pg_top connection might be gone from the process
* table before we get it from the operating system. If sysctl
* throws any error, assume that is the case and adjust pbase
* accordingly.
*/
--nproc;
continue;
}
/*
* Place pointers to each valid proc structure in pref[]. Process
* slots that are actually in use have a non-zero status field.
* Processes with P_SYSTEM set are system processes---these get
* ignored unless show_system is set.
*/
if (pp->p_stat != 0 &&
((pp->p_flag & P_SYSTEM) == 0) &&
((pp->p_flag & P_THREAD) == 0))
{
total_procs++;
process_states[(unsigned char) pp->p_stat]++;
if (pp->p_stat != SZOMB &&
(show_idle || pp->p_pctcpu != 0 ||
pp->p_stat == SRUN) &&
(!show_cmd || strstr(pp->p_comm,
sel->command)))
{
*prefp++ = pp;
active_procs++;
}
}
n = malloc(sizeof(struct pg_proc));
if (n == NULL)
{
fprintf(stderr, "malloc error\n");
if (pgresult != NULL)
PQclear(pgresult);
disconnect_from_db(conninfo);
exit(1);
}
memset(n, 0, sizeof(struct pg_proc));
n->pid = atoi(PQgetvalue(pgresult, i, 0));
p = RB_INSERT(pgproc, &head_proc, n);
if (p != NULL)
{
free(n);
n = p;
}
if (mode == MODE_REPLICATION)
{
update_str(&n->usename, PQgetvalue(pgresult, i, REP_USENAME));
update_str(&n->application_name,
PQgetvalue(pgresult, i, REP_APPLICATION_NAME));
update_str(&n->client_addr,
PQgetvalue(pgresult, i, REP_CLIENT_ADDR));
update_str(&n->repstate, PQgetvalue(pgresult, i, REP_STATE));
update_str(&n->primary,
PQgetvalue(pgresult, i, REP_WAL_INSERT));
update_str(&n->sent, PQgetvalue(pgresult, i, REP_SENT));
update_str(&n->write, PQgetvalue(pgresult, i, REP_WRITE));
update_str(&n->flush, PQgetvalue(pgresult, i, REP_FLUSH));
update_str(&n->replay, PQgetvalue(pgresult, i, REP_REPLAY));
n->sent_lag = atol(PQgetvalue(pgresult, i, REP_SENT_LAG));
n->write_lag = atol(PQgetvalue(pgresult, i, REP_WRITE_LAG));
n->flush_lag = atol(PQgetvalue(pgresult, i, REP_FLUSH_LAG));
n->replay_lag = atol(PQgetvalue(pgresult, i, REP_REPLAY_LAG));
}
else
{
update_str(&n->name, PQgetvalue(pgresult, i, PROC_QUERY));
printable(n->name);
update_state(&n->pgstate, PQgetvalue(pgresult, i, PROC_STATE));
update_str(&n->usename, PQgetvalue(pgresult, i, PROC_USENAME));
n->xtime = atol(PQgetvalue(pgresult, i, PROC_XSTART));
n->qtime = atol(PQgetvalue(pgresult, i, PROC_QSTART));
n->locks = atoi(PQgetvalue(pgresult, i, PROC_LOCKS));
}
++i;
}
/* if requested, sort the "interesting" processes */
if (compare_index >= 0)
qsort((char *) pref, active_procs,
sizeof(struct kinfo_proc *), proc_compares[compare_index]);
/* remember active and total counts */
si->p_total = total_procs;
si->p_active = pref_len = active_procs;
/* pass back a handle */
handle.next_proc = pref;
handle.remaining = active_procs;
return ((caddr_t) & handle);
}
char fmt[MAX_COLS]; /* static area where result is built */
static char *
format_comm(struct kinfo_proc *kp)
{
#define ARG_SIZE 60
static char **s,
buf[ARG_SIZE];
size_t siz = 100;
char **p;
int mib[4];
for (;; siz *= 2)
{
if ((s = realloc(s, siz)) == NULL)
err(1, NULL);
mib[0] = CTL_KERN;
mib[1] = KERN_PROC_ARGS;
mib[2] = kp->p_pid;
mib[3] = KERN_PROC_ARGV;
if (sysctl(mib, 4, s, &siz, NULL, 0) == 0)
break;
if (errno != ENOMEM)
return (kp->p_comm);
}
buf[0] = '\0';
for (p = s; *p != NULL; p++)
{
if (p != s)
strlcat(buf, " ", sizeof(buf));
strlcat(buf, *p, sizeof(buf));
}
if (buf[0] == '\0')
return (kp->p_comm);
return (buf);
}
char *
format_next_process(caddr_t handle)
{
char *p_wait;
struct kinfo_proc *pp;
struct handle *hp;
int cputime;
double pct;
struct pg_proc n, *p = NULL;
/* find and remember the next proc structure */
hp = (struct handle *) handle;
pp = *(hp->next_proc++);
hp->remaining--;
memset(&n, 0, sizeof(struct pg_proc));
n.pid = pp->p_pid;
p = RB_FIND(pgproc, &head_proc, &n);
cputime = pp->p_rtime_sec + ((pp->p_rtime_usec + 500000) / 1000000);
/* calculate the base for cpu percentages */
pct = pctdouble(pp->p_pctcpu);
if (pp->p_wmesg[0])
p_wait = pp->p_wmesg;
else
p_wait = "-";
/* format this entry */
snprintf(fmt, sizeof fmt, Proc_format,
pp->p_pid, p->usename,
format_k(pagetok(PROCSIZE(pp))),
format_k(pagetok(pp->p_vm_rssize)),
backendstatenames[p->pgstate],
format_time(p->xtime),
format_time(p->qtime),
100.0 * pct,
p->locks,
printable(format_comm(pp)));
/* return the result */
return (fmt);
}
char *
format_next_replication(caddr_t handle)
{
static char fmt[MAX_COLS]; /* static area where result is built */
register struct kinfo_proc *pp;
struct handle *hp;
struct pg_proc n, *p = NULL;
/* find and remember the next proc structure */
hp = (struct handle *) handle;
pp = *(hp->next_proc++);
hp->remaining--;
memset(&n, 0, sizeof(struct pg_proc));
n.pid = pp->p_pid;
p = RB_FIND(pgproc, &head_proc, &n);
snprintf(fmt, sizeof(fmt),
"%7d %-8.8s %-11.11s %15s %-9.9s %-10.10s %-10.10s %-10.10s %-10.10s %-10.10s %5s %5s %5s %5s",
p->pid,
p->usename,
p->application_name,
p->client_addr,
p->repstate,
p->primary,
p->sent,
p->write,
p->flush,
p->replay,
format_b(p->sent_lag),
format_b(p->write_lag),
format_b(p->flush_lag),
format_b(p->replay_lag));
/* return the result */
return (fmt);
}
/* comparison routine for qsort */
static unsigned char sorted_state[] =
{
0, /* not used */
4, /* start */
5, /* run */
2, /* sleep */
3, /* stop */
1 /* zombie */
};
/*
* proc_compares - comparison functions for "qsort"
*/
/*
* First, the possible comparison keys. These are defined in such a way
* that they can be merely listed in the source code to define the actual
* desired ordering.
*/
#define ORDERKEY_PCTCPU \
if (lresult = (pctcpu)p2->p_pctcpu - (pctcpu)p1->p_pctcpu, \
(result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0)
#define ORDERKEY_CPUTIME \
if ((result = p2->p_rtime_sec - p1->p_rtime_sec) == 0) \
if ((result = p2->p_rtime_usec - p1->p_rtime_usec) == 0)
#define ORDERKEY_STATE \
if ((result = sorted_state[(unsigned char)p2->p_stat] - \
sorted_state[(unsigned char)p1->p_stat]) == 0)
#define ORDERKEY_PRIO \
if ((result = p2->p_priority - p1->p_priority) == 0)
#define ORDERKEY_RSSIZE \
if ((result = p2->p_vm_rssize - p1->p_vm_rssize) == 0)
#define ORDERKEY_MEM \
if ((result = PROCSIZE(p2) - PROCSIZE(p1)) == 0)
/* compare_cpu - the comparison function for sorting by cpu percentage */
static int
compare_cpu(const void *v1, const void *v2)
{
struct proc **pp1 = (struct proc **) v1;
struct proc **pp2 = (struct proc **) v2;
struct kinfo_proc *p1,
*p2;
pctcpu lresult;
int result;
/* remove one level of indirection */
p1 = *(struct kinfo_proc **) pp1;
p2 = *(struct kinfo_proc **) pp2;
ORDERKEY_PCTCPU
ORDERKEY_CPUTIME
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
/* compare_size - the comparison function for sorting by total memory usage */
static int
compare_size(const void *v1, const void *v2)
{
struct proc **pp1 = (struct proc **) v1;
struct proc **pp2 = (struct proc **) v2;
struct kinfo_proc *p1,
*p2;
pctcpu lresult;
int result;
/* remove one level of indirection */
p1 = *(struct kinfo_proc **) pp1;
p2 = *(struct kinfo_proc **) pp2;
ORDERKEY_MEM
ORDERKEY_RSSIZE
ORDERKEY_PCTCPU
ORDERKEY_CPUTIME
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
/* compare_res - the comparison function for sorting by resident set size */
static int
compare_res(const void *v1, const void *v2)
{
struct proc **pp1 = (struct proc **) v1;
struct proc **pp2 = (struct proc **) v2;
struct kinfo_proc *p1,
*p2;
pctcpu lresult;
int result;
/* remove one level of indirection */
p1 = *(struct kinfo_proc **) pp1;
p2 = *(struct kinfo_proc **) pp2;
ORDERKEY_RSSIZE
ORDERKEY_MEM
ORDERKEY_PCTCPU
ORDERKEY_CPUTIME
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
/* compare_time - the comparison function for sorting by CPU time */
static int
compare_time(const void *v1, const void *v2)
{
struct proc **pp1 = (struct proc **) v1;
struct proc **pp2 = (struct proc **) v2;
struct kinfo_proc *p1,
*p2;
pctcpu lresult;
int result;
/* remove one level of indirection */
p1 = *(struct kinfo_proc **) pp1;
p2 = *(struct kinfo_proc **) pp2;
ORDERKEY_CPUTIME
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
/* compare_prio - the comparison function for sorting by CPU time */
static int
compare_prio(const void *v1, const void *v2)
{
struct proc **pp1 = (struct proc **) v1;
struct proc **pp2 = (struct proc **) v2;
struct kinfo_proc *p1,
*p2;
pctcpu lresult;
int result;
/* remove one level of indirection */
p1 = *(struct kinfo_proc **) pp1;
p2 = *(struct kinfo_proc **) pp2;
ORDERKEY_PRIO
ORDERKEY_PCTCPU
ORDERKEY_CPUTIME
ORDERKEY_STATE
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
/*
* proc_owner(pid) - returns the uid that owns process "pid", or -1 if
* the process does not exist.
* It is EXTREMELY IMPORTANT that this function work correctly.
* If pg_top runs setuid root (as in SVR4), then this function
* is the only thing that stands in the way of a serious
* security problem. It validates requests for the "kill"
* and "renice" commands.
*/
uid_t
proc_owner(pid_t pid)
{
struct kinfo_proc **prefp,
*pp;
int cnt;
prefp = pref;
cnt = pref_len;
while (--cnt >= 0)
{
pp = *prefp++;
if (pp->p_pid == pid)
return ((uid_t) pp->p_ruid);
}
return (uid_t) (-1);
}
/*
* swapmode is rewritten by Tobias Weingartner <weingart@openbsd.org>
* to be based on the new swapctl(2) system call.
*/
static long
swapmode(long *used, long *total)
{
struct swapent *swdev;
int nswap,
rnswap,
i;
nswap = swapctl(SWAP_NSWAP, 0, 0);
if (nswap == 0)
return 0;
swdev = calloc(nswap, sizeof(*swdev));
if (swdev == NULL)
return 0;
rnswap = swapctl(SWAP_STATS, swdev, nswap);
if (rnswap == -1)
{
free(swdev);
return 0;
}
/* if rnswap != nswap, then what? */
/* Total things up */
*total = *used = 0;
for (i = 0; i < nswap; i++)
{
if (swdev[i].se_flags & SWF_ENABLE)
{
*used += (swdev[i].se_inuse / (1024 / DEV_BSIZE));
*total += (swdev[i].se_nblks / (1024 / DEV_BSIZE));
}
}
free(swdev);
return 1;
}
int
topproccmp(struct pg_proc *e1, struct pg_proc *e2)
{
return (e1->pid < e2->pid ? -1 : e1->pid > e2->pid);
}
void
get_io_info(struct io_info *io_info)
{
/* Not supported yet */
memset(io_info, 0, sizeof(*io_info));
}
0707010000002E000081A400000000000000000000000166622DE800006A0C000000000000000000000000000000000000002000000000pg_top-4.1.1/machine/m_remote.c/*
* Copyright (c) 2008-2009, Mark Wong
*/
#ifdef __linux__
#define _GNU_SOURCE
#endif /* __linux__ */
#include <stdlib.h>
#if defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/tree.h>
#endif /* __FreeBSD__*/
#ifdef __linux__
#include <bsd/stdlib.h>
#include <bsd/sys/tree.h>
#endif /* __linux__ */
#include <string.h>
#include <sys/time.h>
#include <unistd.h>
#include <libpq-fe.h>
#include "pg.h"
#include "remote.h"
#include "utils.h"
#define QUERY_CPUTIME \
"SELECT user, nice, system, idle, iowait\n" \
"FROM pg_cputime()"
#define QUERY_LOADAVG \
"SELECT load1, load5, load15, last_pid\n" \
"FROM pg_loadavg()"
#define QUERY_MEMUSAGE \
"SELECT memused, memfree, memshared, membuffers, memcached,\n" \
" swapused, swapfree, swapcached\n" \
"FROM pg_memusage()"
#define QUERY_PROCTAB \
"WITH lock_activity AS\n" \
"(\n" \
" SELECT pid, count(*) AS lock_count\n" \
" FROM pg_locks\n" \
" WHERE relation IS NOT NULL\n" \
" GROUP BY pid\n" \
")\n" \
"SELECT a.pid, comm, fullcomm, a.state, utime, stime,\n" \
" starttime, vsize, rss, usename, rchar, wchar,\n" \
" syscr, syscw, reads, writes, cwrites, b.state,\n" \
" extract(EPOCH FROM age(clock_timestamp(),\n" \
" xact_start))::BIGINT,\n" \
" extract(EPOCH FROM age(clock_timestamp(),\n" \
" query_start))::BIGINT,\n" \
" coalesce(lock_count, 0) AS lock_count\n" \
"FROM pg_proctab() a LEFT OUTER JOIN pg_stat_activity b\n" \
" ON a.pid = b.pid\n" \
" LEFT OUTER JOIN lock_activity c\n" \
" ON a.pid = c.pid;"
#define QUERY_PROCTAB_QUERY \
"WITH lock_activity AS\n" \
"(\n" \
" SELECT pid, count(*) AS lock_count\n" \
" FROM pg_locks\n" \
" GROUP BY pid\n" \
")\n" \
"SELECT a.pid, comm, query, a.state, utime, stime,\n" \
" starttime, vsize, rss, usename, rchar, wchar,\n" \
" syscr, syscw, reads, writes, cwrites, b.state,\n" \
" extract(EPOCH FROM age(clock_timestamp(),\n" \
" xact_start))::BIGINT,\n" \
" extract(EPOCH FROM age(clock_timestamp(),\n" \
" query_start))::BIGINT,\n" \
" coalesce(lock_count, 0) AS lock_count\n" \
"FROM pg_proctab() a LEFT OUTER JOIN pg_stat_activity b\n" \
" ON a.pid = b.pid\n" \
" LEFT OUTER JOIN lock_activity c\n" \
" ON a.pid = c.pid;"
#define QUERY_PG_PROC \
"SELECT COUNT(*)\n" \
"FROM pg_catalog.pg_proc\n" \
"WHERE proname = '%s'"
enum column_cputime
{
c_cpu_user, c_cpu_nice, c_cpu_system, c_cpu_idle,
c_cpu_iowait
};
enum column_loadavg
{
c_load1, c_load5, c_load15, c_last_pid
};
enum column_memusage
{
c_memused, c_memfree, c_memshared, c_membuffers,
c_memcached, c_swapused, c_swapfree, c_swapcached
};
enum column_proctab
{
c_pid, c_comm, c_fullcomm, c_state, c_utime, c_stime,
c_starttime, c_vsize, c_rss, c_username,
c_rchar, c_wchar, c_syscr, c_syscw, c_reads, c_writes, c_cwrites,
c_pgstate, c_xtime, c_qtime, c_locks
};
#define bytetok(x) (((x) + 512) >> 10)
#define INITIAL_ACTIVE_SIZE (256)
#define PROCBLOCK_SIZE (32)
#define NCPUSTATES 5
#define NMEMSTATS 5
#define NSWAPSTATS 3
#define MEMUSED 0
#define MEMFREE 1
#define MEMSHARED 2
#define MEMBUFFERS 3
#define MEMCACHED 4
#define NMEMSTATS 5
#define SWAPUSED 0
#define SWAPFREE 1
#define SWAPCACHED 2
struct top_proc_r
{
RB_ENTRY(top_proc_r) entry;
pid_t pid;
char *name;
char *usename;
unsigned long size;
unsigned long rss; /* in k */
int state;
int pgstate;
unsigned long time;
unsigned long start_time;
unsigned long xtime;
unsigned long qtime;
unsigned int locks;
double pcpu;
/* The change in the previous values and current values. */
long long rchar_diff;
long long wchar_diff;
long long syscr_diff;
long long syscw_diff;
long long read_bytes_diff;
long long write_bytes_diff;
long long cancelled_write_bytes_diff;
/* The absolute values. */
long long rchar;
long long wchar;
long long syscr;
long long syscw;
long long read_bytes;
long long write_bytes;
long long cancelled_write_bytes;
/* Replication data */
char *application_name;
char *client_addr;
char *repstate;
char *primary;
char *sent;
char *write;
char *flush;
char *replay;
long long sent_lag;
long long write_lag;
long long flush_lag;
long long replay_lag;
};
static time_t boottime = -1;
static struct top_proc_r *pgrtable;
static int proc_r_index;
int topprocrcmp(struct top_proc_r *, struct top_proc_r *);
RB_HEAD(pgprocr, top_proc_r) head_proc_r = RB_INITIALIZER(&head_proc_r);
RB_PROTOTYPE(pgprocr, top_proc_r, entry, topprocrcmp)
RB_GENERATE(pgprocr, top_proc_r, entry, topprocrcmp)
static char *cpustatenames[NCPUSTATES + 1] =
{
"user", "nice", "system", "idle", "iowait", NULL
};
static char *memorynames[NMEMSTATS + 1] =
{
"K used, ", "K free, ", "K shared, ", "K buffers, ", "K cached", NULL
};
/* these are names given to allowed sorting orders -- first is default */
static char *ordernames[] =
{
"cpu", "size", "res", "xtime", "qtime", "rchar", "wchar", "syscr",
"syscw", "reads", "writes", "cwrites", "locks", "command", "flag",
"rlag", "slag", "wlag", NULL
};
static char *swapnames[NSWAPSTATS + 1] =
{
"K used, ", "K free, ", "K cached", NULL
};
static char fmt_header[] =
" PID X SIZE RES STATE XTIME QTIME %CPU LOCKS COMMAND";
char fmt_header_io_r[] =
" PID RCHAR WCHAR SYSCR SYSCW READS WRITES CWRITES COMMAND";
char fmt_header_replication_r[] =
" PID USERNAME APPLICATION CLIENT STATE PRIMARY SENT WRITE FLUSH REPLAY SLAG WLAG FLAG RLAG";
/* Now the array that maps process state to a weight. */
unsigned char sort_state_r[] =
{
0, /* empty */
6, /* run */
3, /* sleep */
5, /* disk wait */
1, /* zombie */
2, /* stop */
4 /* swap */
};
static int64_t cpu_states[NCPUSTATES];
static long memory_stats[NMEMSTATS];
static int process_states[NPROCSTATES];
static long swap_stats[NSWAPSTATS];
static struct timeval lasttime;
static int64_t cp_time[NCPUSTATES];
static int64_t cp_old[NCPUSTATES];
static int64_t cp_diff[NCPUSTATES];
#define ORDERKEY_PCTCPU if ((result = (int)(p2->pcpu - p1->pcpu)) == 0)
#define ORDERKEY_STATE if ((result = p1->pgstate < p2->pgstate))
#define ORDERKEY_RSSIZE if ((result = p2->rss - p1->rss) == 0)
#define ORDERKEY_LAG_FLUSH if ((result = p2->flush_lag - p1->flush_lag) == 0)
#define ORDERKEY_LAG_REPLAY if ((result = p2->replay_lag - \
p1->replay_lag) == 0)
#define ORDERKEY_LAG_SENT if ((result = p2->sent_lag - p1->sent_lag) == 0)
#define ORDERKEY_LAG_WRITE if ((result = p2->write_lag - p1->write_lag) == 0)
#define ORDERKEY_MEM if ((result = p2->size - p1->size) == 0)
#define ORDERKEY_NAME if ((result = strcmp(p1->name, p2->name)) == 0)
#define ORDERKEY_RCHAR if ((result = p1->rchar - p2->rchar) == 0)
#define ORDERKEY_WCHAR if ((result = p1->wchar - p2->wchar) == 0)
#define ORDERKEY_SYSCR if ((result = p1->syscr - p2->syscr) == 0)
#define ORDERKEY_SYSCW if ((result = p1->syscw - p2->syscw) == 0)
#define ORDERKEY_READS if ((result = p1->read_bytes - p2->read_bytes) == 0)
#define ORDERKEY_WRITES if ((result = p1->write_bytes - p2->write_bytes) == 0)
#define ORDERKEY_CWRITES if ((result = p1->cancelled_write_bytes - p2->cancelled_write_bytes) == 0)
#define ORDERKEY_XTIME if ((result = p2->xtime - p1->xtime) == 0)
#define ORDERKEY_QTIME if ((result = p2->qtime - p1->qtime) == 0)
#define ORDERKEY_LOCKS if ((result = p2->locks - p1->locks) == 0)
int check_for_function(PGconn *, char *);
static int compare_cmd_r(const void *, const void *);
static int compare_cpu_r(const void *, const void *);
static int compare_cwrites_r(const void *, const void *);
static int compare_lag_flush(const void *, const void *);
static int compare_lag_replay(const void *, const void *);
static int compare_lag_sent(const void *, const void *);
static int compare_lag_write(const void *, const void *);
static int compare_locks_r(const void *, const void *);
static int compare_qtime_r(const void *, const void *);
static int compare_rchar_r(const void *, const void *);
static int compare_reads_r(const void *, const void *);
static int compare_res_r(const void *, const void *);
static int compare_size_r(const void *, const void *);
static int compare_syscr_r(const void *, const void *);
static int compare_syscw_r(const void *, const void *);
static int compare_wchar_r(const void *, const void *);
static int compare_writes_r(const void *, const void *);
static int compare_xtime_r(const void *, const void *);
int
check_for_function(PGconn *pgconn, char *procname)
{
PGresult *pgresult = NULL;
int rows = 0;
int count;
char sql[128];
sprintf(sql, QUERY_PG_PROC, procname);
pgresult = PQexec(pgconn, sql);
rows = PQntuples(pgresult);
/* Don't need to clean up on error, the program will exit shortly after. */
if (rows == 0)
{
fprintf(stderr, "Error executing '%s'.\n", sql);
return -1;
}
count = atoi(PQgetvalue(pgresult, 0, 0));
if (count == 0)
{
fprintf(stderr, "Stored function '%s' is missing.\n", procname);
return -1;
}
if (pgresult != NULL)
PQclear(pgresult);
return 0;
}
int (*proc_compares_r[]) () =
{
compare_cpu_r,
compare_size_r,
compare_res_r,
compare_xtime_r,
compare_qtime_r,
compare_rchar_r,
compare_wchar_r,
compare_syscr_r,
compare_syscw_r,
compare_reads_r,
compare_writes_r,
compare_cwrites_r,
compare_locks_r,
compare_cmd_r,
compare_lag_flush,
compare_lag_replay,
compare_lag_sent,
compare_lag_write,
NULL
};
/* The comparison function for sorting by command name. */
static int
compare_cmd_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_NAME
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
/* compare_cpu_r - the comparison function for sorting by cpu percentage */
static int
compare_cpu_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
static int
compare_cwrites_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_CWRITES
ORDERKEY_RCHAR
ORDERKEY_WCHAR
ORDERKEY_SYSCR
ORDERKEY_SYSCW
ORDERKEY_READS
ORDERKEY_WRITES
ORDERKEY_NAME
;
return (result);
}
static int
compare_lag_flush(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_LAG_FLUSH
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
static int
compare_lag_replay(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_LAG_REPLAY
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
static int
compare_lag_sent(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_LAG_SENT
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
static int
compare_lag_write(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_LAG_WRITE
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
/*
* compare_locks_r - the comparison function for sorting by total locks
* acquired
*/
int
compare_locks_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_LOCKS
ORDERKEY_QTIME
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
/* compare_qtime_r - the comparison function for sorting by total cpu qtime */
static int
compare_qtime_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_QTIME
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
/* The comparison function for sorting by resident set size. */
static int
compare_res_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_RSSIZE
ORDERKEY_MEM
ORDERKEY_PCTCPU
ORDERKEY_STATE
;
return (result);
}
/* The comparison function for sorting by total memory usage. */
static int
compare_rchar_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_RCHAR
ORDERKEY_WCHAR
ORDERKEY_SYSCR
ORDERKEY_SYSCW
ORDERKEY_READS
ORDERKEY_WRITES
ORDERKEY_CWRITES
ORDERKEY_NAME
;
return (result);
}
static int
compare_reads_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_READS
ORDERKEY_RCHAR
ORDERKEY_WCHAR
ORDERKEY_SYSCR
ORDERKEY_SYSCW
ORDERKEY_WRITES
ORDERKEY_CWRITES
ORDERKEY_NAME
;
return (result);
}
static int
compare_size_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_MEM
ORDERKEY_RSSIZE
ORDERKEY_PCTCPU
ORDERKEY_STATE
;
return (result);
}
static int
compare_syscr_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_SYSCR
ORDERKEY_RCHAR
ORDERKEY_WCHAR
ORDERKEY_SYSCW
ORDERKEY_READS
ORDERKEY_WRITES
ORDERKEY_CWRITES
ORDERKEY_NAME
;
return (result);
}
static int
compare_syscw_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_SYSCW
ORDERKEY_RCHAR
ORDERKEY_WCHAR
ORDERKEY_SYSCR
ORDERKEY_READS
ORDERKEY_WRITES
ORDERKEY_CWRITES
ORDERKEY_NAME
;
return (result);
}
/* compare_xtime_r - the comparison function for sorting by total cpu xtime */
static int
compare_xtime_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_XTIME
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
static int
compare_wchar_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_WCHAR
ORDERKEY_RCHAR
ORDERKEY_SYSCR
ORDERKEY_SYSCW
ORDERKEY_READS
ORDERKEY_WRITES
ORDERKEY_CWRITES
ORDERKEY_NAME
;
return (result);
}
static int
compare_writes_r(const void *v1, const void *v2)
{
struct top_proc_r *p1 = (struct top_proc_r *) v1;
struct top_proc_r *p2 = (struct top_proc_r *) v2;
int result;
ORDERKEY_WRITES
ORDERKEY_RCHAR
ORDERKEY_WCHAR
ORDERKEY_SYSCR
ORDERKEY_SYSCW
ORDERKEY_READS
ORDERKEY_CWRITES
ORDERKEY_NAME
;
return (result);
}
char *
format_header_r(char *uname_field)
{
int uname_len = strlen(uname_field);
if (uname_len > 8)
uname_len = 8;
memcpy(strchr(fmt_header, 'X'), uname_field, uname_len);
return fmt_header;
}
char *
format_next_io_r(caddr_t handler)
{
static char fmt[MAX_COLS]; /* static area where result is built */
struct top_proc_r *p = &pgrtable[proc_r_index++];
snprintf(fmt, sizeof(fmt),
"%7d %5s %5s %7lld %7lld %5s %6s %7s %s",
(int) p->pid,
format_b(p->rchar_diff),
format_b(p->wchar_diff),
p->syscr_diff,
p->syscw_diff,
format_b(p->read_bytes_diff),
format_b(p->write_bytes_diff),
format_b(p->cancelled_write_bytes_diff),
p->name);
return (fmt);
}
char *
format_next_process_r(caddr_t handler)
{
static char fmt[MAX_COLS]; /* static area where result is built */
struct top_proc_r *p = &pgrtable[proc_r_index++];
snprintf(fmt, sizeof(fmt),
"%7d %-8.8s %5s %5s %-6s %5s %5s %5.1f %5d %s",
(int) p->pid, /* Some OS's need to cast pid_t to int. */
p->usename,
format_k(p->size),
format_k(p->rss),
backendstatenames[p->pgstate],
format_time(p->xtime),
format_time(p->qtime),
p->pcpu * 100.0,
p->locks,
p->name);
return (fmt);
}
char *
format_next_replication_r(caddr_t handle)
{
static char fmt[MAX_COLS]; /* static area where result is built */
struct top_proc_r *p = &pgrtable[proc_r_index++];
snprintf(fmt, sizeof(fmt),
"%7d %-8.8s %-11.11s %15s %-9.9s %9s %9s %9s %9s %9s %5s %5s %5s %5s",
p->pid,
p->usename,
p->application_name,
p->client_addr,
p->repstate,
p->primary,
p->sent,
p->write,
p->flush,
p->replay,
format_b(p->sent_lag),
format_b(p->write_lag),
format_b(p->flush_lag),
format_b(p->replay_lag));
/* return the result */
return (fmt);
}
void
get_system_info_r(struct system_info *info, struct pg_conninfo_ctx *conninfo)
{
PGresult *pgresult = NULL;
int rows = 0;
connect_to_db(conninfo);
if (conninfo->connection != NULL)
{
pgresult = PQexec(conninfo->connection, QUERY_LOADAVG);
rows = PQntuples(pgresult);
}
/* Get load averages. */
if (rows > 0)
{
info->load_avg[0] = atof(PQgetvalue(pgresult, 0, c_load1));
info->load_avg[1] = atof(PQgetvalue(pgresult, 0, c_load5));
info->load_avg[2] = atof(PQgetvalue(pgresult, 0, c_load15));
info->last_pid = atoi(PQgetvalue(pgresult, 0, c_last_pid));
}
else
{
info->load_avg[0] = 0;
info->load_avg[1] = 0;
info->load_avg[2] = 0;
info->last_pid = 0;
}
/* Get processor time info. */
if (conninfo->connection != NULL)
{
pgresult = PQexec(conninfo->connection, QUERY_CPUTIME);
rows = PQntuples(pgresult);
}
if (rows > 0)
{
cp_time[0] = atol(PQgetvalue(pgresult, 0, c_cpu_user));
cp_time[1] = atol(PQgetvalue(pgresult, 0, c_cpu_nice));
cp_time[2] = atol(PQgetvalue(pgresult, 0, c_cpu_system));
cp_time[3] = atol(PQgetvalue(pgresult, 0, c_cpu_idle));
cp_time[4] = atol(PQgetvalue(pgresult, 0, c_cpu_iowait));
/* convert cp_time counts to percentages */
percentages(NCPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
}
else
{
cpu_states[0] = 0;
cpu_states[1] = 0;
cpu_states[2] = 0;
cpu_states[3] = 0;
cpu_states[4] = 0;
}
/* Get system wide memory usage. */
if (conninfo->connection != NULL)
{
pgresult = PQexec(conninfo->connection, QUERY_MEMUSAGE);
rows = PQntuples(pgresult);
}
if (rows > 0)
{
memory_stats[MEMUSED] = atol(PQgetvalue(pgresult, 0, c_memused));
memory_stats[MEMFREE] = atol(PQgetvalue(pgresult, 0, c_memfree));
memory_stats[MEMSHARED] = atol(PQgetvalue(pgresult, 0, c_memshared));
memory_stats[MEMBUFFERS] = atol(PQgetvalue(pgresult, 0, c_membuffers));
memory_stats[MEMCACHED] = atol(PQgetvalue(pgresult, 0, c_memcached));
swap_stats[SWAPUSED] = atol(PQgetvalue(pgresult, 0, c_swapused));
swap_stats[SWAPFREE] = atol(PQgetvalue(pgresult, 0, c_swapfree));
swap_stats[SWAPCACHED] = atol(PQgetvalue(pgresult, 0, c_swapcached));
}
else
{
memory_stats[MEMUSED] = 0;
memory_stats[MEMFREE] = 0;
memory_stats[MEMSHARED] = 0;
memory_stats[MEMBUFFERS] = 0;
memory_stats[MEMCACHED] = 0;
swap_stats[SWAPUSED] = 0;
swap_stats[SWAPFREE] = 0;
swap_stats[SWAPCACHED] = 0;
}
info->cpustates = cpu_states;
info->memory = memory_stats;
info->swap = swap_stats;
if (pgresult != NULL)
PQclear(pgresult);
disconnect_from_db(conninfo);
}
caddr_t
get_process_info_r(struct system_info *si, struct process_select *sel,
int compare_index, struct pg_conninfo_ctx *conninfo, int mode)
{
int i;
PGresult *pgresult = NULL;
int rows;
struct timeval thistime;
double timediff;
int active_procs = 0;
int total_procs = 0;
int show_idle = sel->idle;
struct top_proc_r *n,
*p;
memset(process_states, 0, sizeof(process_states));
/* Calculate the time difference since our last check. */
gettimeofday(&thistime, 0);
if (lasttime.tv_sec)
{
timediff = ((thistime.tv_sec - lasttime.tv_sec) +
(thistime.tv_usec - lasttime.tv_usec) * 1e-6);
}
else
{
timediff = 0;
}
lasttime = thistime;
timediff *= HZ; /* Convert to ticks. */
connect_to_db(conninfo);
if (conninfo->connection != NULL)
{
switch (mode)
{
case MODE_REPLICATION:
pgresult = pg_replication(conninfo->connection);
break;
default:
if (sel->fullcmd == 2)
{
pgresult = PQexec(conninfo->connection, QUERY_PROCTAB_QUERY);
}
else
{
pgresult = PQexec(conninfo->connection, QUERY_PROCTAB);
}
}
rows = PQntuples(pgresult);
}
else
{
rows = 0;
}
if (rows > 0)
{
p = realloc(pgrtable, sizeof(struct top_proc_r) * rows);
if (p == NULL)
{
fprintf(stderr, "realloc error\n");
if (pgresult != NULL)
PQclear(pgresult);
disconnect_from_db(conninfo);
exit(1);
}
pgrtable = p;
}
for (i = 0; i < rows; i++)
{
unsigned long otime;
long long value;
n = malloc(sizeof(struct top_proc_r));
if (n == NULL)
{
fprintf(stderr, "malloc error\n");
if (pgresult != NULL)
PQclear(pgresult);
disconnect_from_db(conninfo);
exit(1);
}
memset(n, 0, sizeof(struct top_proc_r));
n->pid = atoi(PQgetvalue(pgresult, i, c_pid));
p = RB_INSERT(pgprocr, &head_proc_r, n);
if (p != NULL)
{
free(n);
n = p;
}
else
{
n->time = 0;
}
otime = n->time;
switch (mode)
{
case MODE_REPLICATION:
update_str(&n->usename, PQgetvalue(pgresult, i, 1));
update_str(&n->application_name, PQgetvalue(pgresult, i, 2));
update_str(&n->client_addr, PQgetvalue(pgresult, i, 3));
update_str(&n->repstate, PQgetvalue(pgresult, i, 4));
update_str(&n->primary, PQgetvalue(pgresult, i, 5));
update_str(&n->sent, PQgetvalue(pgresult, i, 6));
update_str(&n->write, PQgetvalue(pgresult, i, 7));
update_str(&n->flush, PQgetvalue(pgresult, i, 8));
update_str(&n->replay, PQgetvalue(pgresult, i, 9));
n->sent_lag = atol(PQgetvalue(pgresult, i, 10));
n->write_lag = atol(PQgetvalue(pgresult, i, 11));
n->flush_lag = atol(PQgetvalue(pgresult, i, 12));
n->replay_lag = atol(PQgetvalue(pgresult, i, 13));
memcpy(&pgrtable[active_procs++], n, sizeof(struct top_proc_r));
break;
default:
if (sel->fullcmd && PQgetvalue(pgresult, i, c_fullcomm))
update_str(&n->name, printable(PQgetvalue(pgresult, i, c_fullcomm)));
else
update_str(&n->name, printable(PQgetvalue(pgresult, i, c_comm)));
switch (PQgetvalue(pgresult, i, c_state)[0])
{
case 'R':
n->state = 1;
break;
case 'S':
n->state = 2;
break;
case 'D':
n->state = 3;
break;
case 'Z':
n->state = 4;
break;
case 'T':
n->state = 5;
break;
case 'W':
n->state = 6;
break;
case '\0':
continue;
}
update_state(&n->pgstate, PQgetvalue(pgresult, i, c_pgstate));
n->time = (unsigned long) atol(PQgetvalue(pgresult, i, c_utime));
n->time += (unsigned long) atol(PQgetvalue(pgresult, i, c_stime));
n->start_time = (unsigned long)
atol(PQgetvalue(pgresult, i, c_starttime));
n->size = bytetok((unsigned long)
atol(PQgetvalue(pgresult, i, c_vsize)));
n->rss = bytetok((unsigned long)
atol(PQgetvalue(pgresult, i, c_rss)));
update_str(&n->usename, PQgetvalue(pgresult, i, c_username));
n->xtime = atol(PQgetvalue(pgresult, i, c_xtime));
n->qtime = atol(PQgetvalue(pgresult, i, c_qtime));
n->locks = atol(PQgetvalue(pgresult, i, c_locks));
value = atoll(PQgetvalue(pgresult, i, c_rchar));
n->rchar_diff = value - n->rchar;
n->rchar = value;
value = atoll(PQgetvalue(pgresult, i, c_wchar));
n->wchar_diff = value - n->wchar;
n->wchar = value;
value = atoll(PQgetvalue(pgresult, i, c_syscr));
n->syscr_diff = value - n->syscr;
n->syscr = value;
value = atoll(PQgetvalue(pgresult, i, c_syscw));
n->syscw_diff = value - n->syscw;
n->syscw = value;
value = atoll(PQgetvalue(pgresult, i, c_reads));
n->read_bytes_diff = value - n->read_bytes;
n->read_bytes = value;
value = atoll(PQgetvalue(pgresult, i, c_writes));
n->write_bytes_diff = value - n->write_bytes;
n->write_bytes = value;
value = atoll(PQgetvalue(pgresult, i, c_cwrites));
n->cancelled_write_bytes_diff = value - n->cancelled_write_bytes;
n->cancelled_write_bytes = value;
++total_procs;
++process_states[n->pgstate];
if (timediff > 0.0)
{
if ((n->pcpu = (n->time - otime) / timediff) < 0.0001)
n->pcpu = 0;
}
if ((show_idle || n->pgstate != STATE_IDLE) &&
(sel->usename[0] == '\0' ||
strcmp(n->usename, sel->usename) == 0))
memcpy(&pgrtable[active_procs++], n, sizeof(struct top_proc_r));
}
}
if (pgresult != NULL)
PQclear(pgresult);
disconnect_from_db(conninfo);
si->p_active = active_procs;
si->p_total = total_procs;
si->procstates = process_states;
/* Sort the "active" procs if specified. */
if (compare_index >= 0 && si->p_active)
qsort(pgrtable, si->p_active, sizeof(struct top_proc_r),
proc_compares_r[compare_index]);
/* don't even pretend that the return value thing here isn't bogus */
proc_r_index = 0;
return 0;
}
int
machine_init_r(struct statics *statics, struct pg_conninfo_ctx *conninfo)
{
/* Make sure the remote system has the stored function installed. */
connect_to_db(conninfo);
if (conninfo->connection == NULL)
{
fprintf(stderr, "Cannot connect to database.\n");
return -1;
}
if (check_for_function(conninfo->connection, "pg_cputime") != 0)
return -1;
if (check_for_function(conninfo->connection, "pg_loadavg") != 0)
return -1;
if (check_for_function(conninfo->connection, "pg_memusage") != 0)
return -1;
if (check_for_function(conninfo->connection, "pg_proctab") != 0)
return -1;
disconnect_from_db(conninfo);
/* fill in the statics information */
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
statics->swap_names = swapnames;
statics->order_names = ordernames;
statics->boottime = boottime;
statics->flags.fullcmds = 1;
statics->flags.warmup = 1;
return 0;
}
int
topprocrcmp(struct top_proc_r *e1, struct top_proc_r *e2)
{
return (e1->pid < e2->pid ? -1 : e1->pid > e2->pid);
}
0707010000002F000081A400000000000000000000000166622DE80000532E000000000000000000000000000000000000001E00000000pg_top-4.1.1/machine/m_sco5.c/*
* pg_top - a top PostgreSQL users display for Unix
*
* SYNOPSIS: SCO UNIX OpenServer5
*
* DESCRIPTION:
* This is the machine-dependent module for SCO OpenServer5.
* Originally written for BSD4.3 system by Christos Zoulas.
* Modified to m_sco.c (3.2v4.2) by Gregory Shilin <shilin@onyx.co.il>
* Modified to m_sco5.c (3.2v5.*) by Mike Hopkirk <hops@sco.com>
* Works for:
* SCO UNIX 3.2v5.*
*
* CFLAGS: -DHAVE_GETOPT -DORDER
*
* AUTHOR: Mike Hopkirk (hops@sco.com)
* hops 10-Jul-98 - added sort fields
* 17-Jul-98 - add philiph's chopped cmd string support
* (define NO_COMMAND_ARGS to enable )
* 09-Dec-98 - provide RSS calculation
* 15-Mar-2000 - Fix broken lines and cleanup sysinfo access w macros
*/
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <nlist.h>
#include <math.h>
#include <signal.h>
#include <string.h>
#include <sys/dir.h>
#include <sys/immu.h>
#include <sys/region.h>
#include <sys/proc.h>
#include <sys/user.h>
#include <sys/sysinfo.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/var.h>
#include <sys/sysi86.h>
#include "pg_top.h"
#include "machine.h"
#include "utils.h"
#include "loadavg.h"
/*
typedef unsigned long ulong;
typedef unsigned int uint;
typedef unsigned short ushort;
*/
typedef unsigned char uchar;
#define VMUNIX "/unix"
#define KMEM "/dev/kmem"
#define MEM "/dev/mem"
#define SI_ACTIVE(p) p->p_active
#define SI_TOTAL(p) p->p_total
/* get_process_info passes back a handle. This is what it looks like: */
struct handle
{
struct proc **next_proc; /* points to next valid proc pointer */
int remaining; /* number of pointers remaining */
};
/* define what weighted cpu is */
#define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \
((pct) / (1.0 - exp((pp)->p_time * logcpu))))
#define bytetok(bytes) ((bytes) >> 10)
/* what we consider to be process size: */
#define PROCSIZE(up) bytetok(ctob((up)->u_tsize + (up)->u_dsize+(up)->u_ssize))
/* definitions for indices in the nlist array */
#define X_V 0 /* System configuration information */
#define X_PROC 1 /* process tables */
#define X_FREEMEM 2 /* current free memory */
#define X_AVAILRMEM 3 /* available resident (not swappable) mem in
* pages */
#define X_AVAILSMEM 4 /* available swappable memory in pages */
#define X_MAXMEM 5 /* maximum available free memory in clicks */
#define X_PHYSMEM 6 /* physical memory in clicks */
#define X_NSWAP 7 /* size of swap space in blocks */
#define X_HZ 8 /* ticks/second of the clock */
#define X_MPID 9 /* last process id */
#define X_SYSINFO 10 /* system information (cpu states) */
#define X_CUR_CPU 11
static struct nlist nlst[] = {
{"v"}, /* 0 */
{"proc"}, /* 1 */
{"freemem"}, /* 2 */
{"availrmem"}, /* 3 */
{"availsmem"}, /* 4 */
{"maxmem"}, /* 5 */
{"physmem"}, /* 6 */
{"nswap"}, /* 7 */
{"Hz"}, /* 8 */
{"mpid"}, /* 9 */
{"sysinfo"}, /* 10 */
{"cur_cpu"}, /* 11 */
{NULL}
};
/*
* These definitions control the format of the per-process area
*/
static char header[] =
" PID X PRI NICE SIZE RES STATE TIME COMMAND";
/* 0123456 -- field to fill in starts at header+6 */
#define UNAME_START 6
#define Proc_format \
"%5d %-8.8s %3d %4d %5s %5dK %-5s %6s %.28s"
static int kmem,
mem;
static double logcpu;
/* these are retrieved from the kernel in _init */
static int Hz;
static struct var v;
static ulong proca;
static load_avg cur_cpu;
/* these are for detailing the process states */
int process_states[8];
char *procstatenames[] = {
"", " sleeping, ", " running, ", " zombie, ", " stopped, ",
" created, ", " onproc, ", " xswapped, ",
NULL
};
/* process state names for the "STATE" column of the display */
char *state_abbrev[] = {
"", "sleep", "run", "zomb", "stop", "create", "onpr", "swap"
};
/* these are for calculating cpu state percentages */
#define CPUSTATES 5 /* definition from struct sysinfo */
static time_t cp_time[CPUSTATES];
static time_t cp_old[CPUSTATES];
static time_t cp_diff[CPUSTATES];
/* these are for detailing the cpu states */
int cpu_states[CPUSTATES];
char *cpustatenames[] = {
"idle", "user", "system", "wait", "sxbrk",
NULL
};
/* these are for detailing the memory statistics */
unsigned long memory_stats[6];
char *memorynames[] = {
"K phys, ", "K max, ", "K free, ", "K lck, ", "K unlck, ",
"K swap,", NULL
};
/* these are for keeping track of the proc array */
static int bytes;
static int pref_len;
static struct proc *pbase;
static struct proc **pref;
/* forward definitions for comparison functions */
int proc_compare();
int compare_cpu();
int compare_size();
int compare_time();
int (*proc_compares[]) () =
{
proc_compare, /* state, pri, time, size */
compare_cpu, /* cpu, time, state, pri, size */
compare_size, /* size, cpu, time, state pri */
compare_time, /* time, cpu, state, pri, size */
/* compare_res, /* res, cpu, time, state pri */
NULL
};
/* these are names given to allowed sorting orders -- first is default */
char *ordernames[] = {
"state", "cpu", "size", "time", NULL
}; /* hops */
/* useful externals */
extern int errno;
extern char *sys_errlist[];
long time();
long percentages();
int
machine_init(struct statics *statics)
{
ulong ptr;
if ((kmem = open(KMEM, O_RDONLY)) == -1)
{
perror(KMEM);
return -1;
}
if ((mem = open(MEM, O_RDONLY)) == -1)
{
perror(MEM);
return -1;
}
/* get the list of symbols we want to access in the kernel */
if (nlist(VMUNIX, nlst) == -1)
{
fprintf(stderr, "pg_top: nlist failed\n");
return -1;
}
/* make sure they were all found */
/*
* ZZ if (check_nlist(nlst) > 0) return -1;
*/
proca = nlst[X_PROC].n_value;
/* get the symbol values out of kmem */
(void) getkval(nlst[X_CUR_CPU].n_value, (int *) (&cur_cpu), sizeof(cur_cpu),
nlst[X_CUR_CPU].n_name);
(void) getkval(nlst[X_HZ].n_value, (int *) (&Hz), sizeof(Hz),
nlst[X_HZ].n_name);
(void) getkval(nlst[X_V].n_value, (int *) (&v), sizeof(v),
nlst[X_V].n_name);
/* this is used in calculating WCPU -- calculate it ahead of time */
logcpu = log(fabs(loaddouble(cur_cpu)));
/* allocate space for proc structure array and array of pointers */
bytes = v.v_proc * sizeof(struct proc);
pbase = (struct proc *) malloc(bytes);
pref = (struct proc **) malloc(v.v_proc * sizeof(struct proc *));
if (pbase == (struct proc *) NULL || pref == (struct proc **) NULL)
{
fprintf(stderr, "pg_top: cannot allocate sufficient memory\n");
return -1;
}
/* fill in the statics information */
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
statics->order_names = ordernames; /* hops */
return 0;
}
char *
format_header(register char *uname_field)
{
register char *ptr;
ptr = header + UNAME_START;
while (*uname_field != '\0')
{
*ptr++ = *uname_field++;
}
return (header);
}
/* philiph - get run ave fm /dev/table info */
static int
tab_avenrun(double runave[])
{
FILE *fp = fopen("/dev/table/avenrun", "r");
int i;
for (i = 0; i < 3; i++)
runave[i] = -1.0;
if (fp == NULL)
return -1;
else
{
short rawave[3];
if (fread(rawave, sizeof(short), 3, fp) != 3)
{
fclose(fp);
return -1;
}
else
{
int i;
for (i = 0; i < 3; i++)
runave[i] = (double) (rawave[i] / 256.0);
fclose(fp);
return 0;
}
}
}
struct pregion *
get_pregion(void *ptr)
{
static struct pregion preg;
long addr = (long) ptr;
(void) getkval(addr, (struct pregion *) (&preg),
sizeof(struct pregion), "pregion");
return &preg;
}
struct region *
get_region(void *ptr)
{
static struct region reg;
long addr = (long) ptr;
(void) getkval(addr, (struct region *) (®),
sizeof(struct region), "region");
return ®
}
static unsigned char shareable[RT_VM86 + 1]; /* 1 if shareable */
/*
* sum private referenced pages,
* treat shared pages depending on value of TREAT_SHARABLE_PAGES macro
* undefined : ignore (don't account for - default)
* 1: divide among # of references
* 2: accumulate as if private
*/
/* #define TREAT_SHAREABLE_PAGES 1 */
static long
proc_residentsize(struct proc *pp)
{
struct pregion *prp;
struct region *rp;
long rtot = 0;
long stot = 0;
long s1tot = 0;
/* init shareable region array */
if (shareable[RT_STEXT] == 0)
shareable[RT_STEXT] = shareable[RT_SHMEM] = shareable[RT_MAPFILE] = 1
;
prp = pp->p_region;
if (prp == 0)
return 0;
for (; prp && (prp = get_pregion((void *) (prp))) &&
prp->p_reg && (rp = get_region((void *) (prp->p_reg)));
prp = prp->p_next)
{
if (shareable[rp->r_type]) /* account for shared pgs separately */
{
stot += (rp->r_nvalid / rp->r_refcnt);
s1tot += rp->r_nvalid;
}
else
rtot += rp->r_nvalid;
}
#if defined(TREAT_SHAREABLE_PAGES) && TREAT_SHAREABLE_PAGES == 1
rtot += stot; /* accumulate and spread over users */
#endif
#if defined(TREAT_SHAREABLE_PAGES) && TREAT_SHAREABLE_PAGES == 1
rtot += s1tot; /* accumulate as if private */
#endif
return rtot * NBPP / 1024;;
}
void
get_system_info(struct system_info *si)
{
long total;
/* get process id of the last process */
(void) getkval(nlst[X_MPID].n_value, &(si->last_pid),
sizeof(si->last_pid),
nlst[X_MPID].n_name);
/* get the cp_time array */
(void) getkval(nlst[X_SYSINFO].n_value, (int *) cp_time, sizeof(cp_time),
nlst[X_SYSINFO].n_name);
/* convert cp_time counts to persentages */
total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
/* sum memory statistics */
(void) getkval(nlst[X_PHYSMEM].n_value, &memory_stats[0],
sizeof(memory_stats[0]), nlst[X_PHYSMEM].n_name);
(void) getkval(nlst[X_MAXMEM].n_value, &memory_stats[1],
sizeof(memory_stats[1]), nlst[X_MAXMEM].n_name);
(void) getkval(nlst[X_FREEMEM].n_value, &memory_stats[2],
sizeof(memory_stats[2]), nlst[X_FREEMEM].n_name);
(void) getkval(nlst[X_AVAILRMEM].n_value, &memory_stats[3],
sizeof(memory_stats[3]), nlst[X_AVAILRMEM].n_name);
(void) getkval(nlst[X_AVAILSMEM].n_value, &memory_stats[4],
sizeof(memory_stats[4]), nlst[X_AVAILSMEM].n_name);
(void) getkval(nlst[X_NSWAP].n_value, &memory_stats[5],
sizeof(memory_stats[5]), nlst[X_NSWAP].n_name);
memory_stats[0] = bytetok(ctob(memory_stats[0])); /* clicks -> bytes */
memory_stats[1] = bytetok(ctob(memory_stats[1])); /* clicks -> bytes */
memory_stats[2] = bytetok(ctob(memory_stats[2])); /* clicks -> bytes */
memory_stats[3] = bytetok(memory_stats[3] * NBPP); /* # bytes per page */
memory_stats[4] = bytetok(memory_stats[4] * NBPP); /* # bytes per page */
memory_stats[5] = bytetok(memory_stats[5] * NBPSCTR); /* # bytes per sector */
/* set arrays and strings */
/*
* Note: we keep memory_stats as an unsigned long to avoid sign extension
* problems when shifting in bytetok. But the module interface requires an
* array of signed longs. So we just cast the pointer here and hope for
* the best. --wnl
*/
si->cpustates = cpu_states;
si->memory = (long *) memory_stats;
tab_avenrun(si->load_avg); /* philiph */
}
static struct handle handle;
caddr_t
get_process_info(struct system_info *si,
struct process_select *sel,
int idx)
{
register int i;
register int total_procs;
register int active_procs;
register struct proc **prefp;
register struct proc *pp;
/* set up flags of what we are going to select */
/* these are copied out of sel for simplicity */
int show_idle = sel->idle;
int show_system = sel->system;
int show_uid = sel->uid != -1;
int show_command = sel->command != NULL;
/* read all the proc structures in one fell swoop */
(void) getkval(proca, (int *) pbase, bytes, "proc array");
/* get a pointer to the states summary array */
si->procstates = process_states;
/* count up process states and get pointers to interesting procs */
total_procs = active_procs = 0;
memset((char *) process_states, 0, sizeof(process_states));
prefp = pref;
for (pp = pbase, i = 0; i < v.v_proc; pp++, i++)
{
/*
* Place pointers to each valid proc structure in pref[]. Process
* slots that are actually in use have a non-zero status field.
* Processes with SSYS set are system processes -- these are ignored
* unless show_system is set.
*/
if (pp->p_stat && (show_system || ((pp->p_flag & SSYS) == 0)))
{
total_procs++;
process_states[pp->p_stat]++;
if ((pp->p_stat != SZOMB) &&
(show_idle || (pp->p_stat == SRUN) || (pp->p_stat == SONPROC)) &&
(!show_uid || pp->p_uid == (ushort) sel->uid))
{
*prefp++ = pp;
active_procs++;
}
}
}
/* if requested, sort the "interesting" processes */
qsort((char *) pref, active_procs, sizeof(struct proc *), proc_compares[idx]);
/* remember active and total counts */
SI_TOTAL(si) = total_procs;
SI_ACTIVE(si) = pref_len = active_procs;
/* pass back a handle */
handle.next_proc = pref;
handle.remaining = active_procs;
return ((caddr_t) & handle);
}
char fmt[128]; /* static area where result is built */
char *
format_next_process(caddr_t handle, char *(*get_userid) ())
{
register struct proc *pp;
register time_t cputime;
register double pct;
int where;
struct user u;
struct handle *hp;
char command[29];
char *process;
char *process2;
/* find and remember the next proc structure */
hp = (struct handle *) handle;
pp = *(hp->next_proc++);
hp->remaining--;
/* get the process's user struct and set cputime */
if ((where = sysi86(RDUBLK, pp->p_pid, &u, sizeof(struct user))) != -1)
where = (pp->p_flag & SLOAD) ? 0 : 1;
if (where == -1)
{
strcpy(command, "<swapped>");
cputime = 0;
}
else
{
/* set u_comm for system processes */
if (u.u_comm[0] == '\0')
{
if (pp->p_pid == 0)
strcpy(command, "Swapper");
else if (pp->p_pid == 2)
strcpy(command, "Pager");
else if (pp->p_pid == 3)
strcpy(command, "Sync'er");
}
else if (where == 1)
{
/* print swapped processes as <pname> */
register char *s1;
u.u_psargs[28 - 3] = '\0';
strcpy(command, "<");
strcat(command, strtok(u.u_psargs, " "));
strcat(command, ">");
while (s1 = (char *) strtok(NULL, " "))
strcat(command, s1);
}
else
{
sprintf(command, "%s", u.u_psargs);
}
cputime = u.u_utime + u.u_stime;
/* cputime = pp->p_utime + pp->p_stime; */
}
/* calculate the base for cpu percentages */
pct = pctdouble(pp->p_cpu);
/*
* psargs gives the absolute path of the process... strip it to only the
* command - [Changes by D. Currie & M. Muldner Aitt NS Canada]
*/
process = printable(command);
#if NO_COMMAND_ARGS
strtok(process, " ");
#endif
process2 = strrchr(process, '/');
if (process2)
{
process = process2;
process++;
}
/* format this entry */
sprintf(fmt,
Proc_format,
pp->p_pid,
(*get_userid) (pp->p_uid),
pp->p_pri - PZERO,
pp->p_nice - NZERO,
format_k(PROCSIZE(&u)), /* same as pp->p_size * 4 */
proc_residentsize(pp),
state_abbrev[pp->p_stat],
format_time(cputime / Hz),
printable(process));
return (fmt);
}
/*
* Checks the nlist to see if any symbols were not found.
* For every symbol that was not found, a one-line message
* is printed to stderr. The routine returns the number of
* symbols NOT founded.
*/
int
check_nlist(register struct nlist *nlst)
{
register int i = 0;
while (nlst->n_name)
{
if (nlst->n_type == 0)
{
fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
i++;
}
nlst++;
}
return i;
}
/*
* getkval(offset, ptr, size, refstr) - get a value out of the kernel.
* "offset" is the byte offset into the kernel for the desired value,
* "ptr" points to a buffer into which the value is retrieved,
* "size" is the size of the buffer (and the object to retrieve),
* "refstr" is a reference string used when printing error meessages,
* if "refstr" starts with a '!', then a failure on read will not
* be fatal (this may seem like a silly way to do things, but I
* really didn't want the overhead of another argument).
*
*/
int
getkval(unsigned long offset, int *ptr, int size, char *refstr)
{
if (lseek(kmem, (long) offset, SEEK_SET) == -1)
{
if (*refstr == '!')
refstr++;
fprintf(stderr, "%s: lseek to %s: %s\n", KMEM,
refstr, errmsg(errno));
quit(23);
}
if (read(kmem, (char *) ptr, size) == -1)
{
if (*refstr == '!')
return 0;
fprintf(stderr, "%s: reading %s: %s\n", KMEM,
refstr, errmsg(errno));
quit(23);
}
return (1);
}
/* comparison routine for qsort */
/* NOTE: this is specific to the BSD proc structure, but it should
give you a good place to start. */
/*
* proc_compare - comparison function for "qsort"
* Compares the resource consumption of two processes using five
* distinct keys. The keys (in descending order of importance) are:
* percent cpu, cpu ticks, state, resident set size, total virtual
* memory usage. The process states are ordered as follows (from least
* to most important): WAIT, zombie, sleep, stop, start, run. The
* array declaration below maps a process state index into a number
* that reflects this ordering.
*/
static unsigned char sorted_state[] =
{
0, /* not used */
5, /* sleep */
6, /* run */
2, /* zombie */
4, /* stop */
1, /* start */
7, /* onpr */
3, /* swap */
};
int
proc_compare(struct proc **pp1, struct proc **pp2)
{
register struct proc *p1;
register struct proc *p2;
register int result;
register ulong lresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/* use process state to break the tie */
if ((result = sorted_state[p2->p_stat] -
sorted_state[p1->p_stat]) == 0)
{
/* use priority to break the tie */
if ((result = p2->p_pri - p1->p_pri) == 0)
{
/* use time to break the tie */
if ((result = (p2->p_utime + p2->p_stime) -
(p1->p_utime + p1->p_stime)) == 0)
{
/* use resident set size (rssize) to break the tie */
if ((result = p2->p_size - p1->p_size) == 0)
{
result = 0;
}
}
}
}
return (result);
}
/* returns uid of owner of process pid */
int
proc_owner(int pid)
{
register int cnt;
register struct proc **prefp;
register struct proc *pp;
prefp = pref;
cnt = pref_len;
while (--cnt >= 0)
{
if ((pp = *prefp++)->p_pid == (short) pid)
return ((int) pp->p_uid);
}
return (-1);
}
#if 0
int
setpriority(int dummy, int who, int nicewal)
{
errno = 1;
return -1;
}
#endif
/* sigblock is not POSIX conformant */
sigset_t
sigblock(sigset_t mask)
{
sigset_t oset;
sigemptyset(&oset);
sigprocmask(SIG_BLOCK, &mask, &oset);
return oset;
}
/* sigsetmask is not POSIX conformant */
sigsetmask(sigset_t mask)
{
sigset_t oset;
sigemptyset(&oset);
sigprocmask(SIG_SETMASK, &mask, &oset);
return oset;
}
/* ---------------- hops - comparison/ordering support ---------------- */
#define ORDERKEY_PCTCPU if (dresult = pctdouble(p2->p_cpu) - pctdouble(p1->p_cpu),\
(result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
#define ORDERKEY_MEMSIZE if ((result = (p2->p_size - p1->p_size)) == 0)
#define ORDERKEY_CPTIME if ((result = (long)(p2->p_utime + p2->p_stime) -\
(long)(p1->p_utime + p1->p_stime)) == 0)
#define ORDERKEY_STATE if ((result = (sorted_state[p2->p_stat] - \
sorted_state[p1->p_stat])) == 0)
#define ORDERKEY_PRIO if ((result = p2->p_pri - p1->p_pri) == 0)
int
compare_cpu(struct proc **pp1, struct proc **pp2)
{
register struct proc *p1;
register struct proc *p2;
register int result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_PCTCPU
ORDERKEY_CPTIME
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_MEMSIZE
;
return (result);
}
/* compare_size - the comparison function for sorting by process size */
int
compare_size(struct proc **pp1, struct proc **pp2)
{
register struct proc *p1;
register struct proc *p2;
register int result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_MEMSIZE
ORDERKEY_PCTCPU
ORDERKEY_CPTIME
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
/* compare_res - the comparison function for sorting by resident set size */
/* TODO: add shadow proc struct updating usr + sys times and RSS for use
* in comparison rtns, implement compare_res rtn as per compare_size()
*/
/* compare_time - the comparison function for sorting by total cpu time */
/* This is giving wrong results since its using the proc structure vals not
* the u struct vals we display above
* TODO: add shadow proc struct updating usr + sys times and RSS for use
* in comparison rtns
*/
int
compare_time(struct proc **pp1, struct proc **pp2)
{
register struct proc *p1;
register struct proc *p2;
register int result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_CPTIME
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_MEMSIZE
;
return (result);
}
void
get_io_info(struct io_info *io_info)
{
/* Not supported yet */
memset(io_info, 0, sizeof(*io_info));
}
07070100000030000081A400000000000000000000000166622DE800000145000000000000000000000000000000000000002400000000pg_top-4.1.1/machine/m_sco5.man.rstSCO UNIX NOTES
==============
The SCO OpenServer5 port is a modification of the SCO Unix port
done by Mike Hopkirk (hops@sco.com).
OpenServer5 is a more normal Unix although the proc variables are still
somewhat funky. No easy access to RSS memory and CPUTICKS.
Added support for ordering and enabled use of setpriority().
07070100000031000081A400000000000000000000000166622DE80000A2D2000000000000000000000000000000000000002000000000pg_top-4.1.1/machine/m_sunos5.c/*
* pg_top - a top PostgreSQL users display for Unix
*
* SYNOPSIS: Any Sun running SunOS 5.x (Solaris 2.x)
*
* DESCRIPTION:
* This is the machine-dependent module for SunOS 5.x (Solaris 2).
* There is some support for MP architectures.
* This makes pg_top work on all revisions of SunOS 5 from 5.0
* through 5.9 (otherwise known as Solaris 9). It has not been
* tested on SunOS 5.10.
*
* LIBS: -lelf -lkvm -lkstat
*
* CFLAGS: -DHAVE_GETOPT -DORDER -DHAVE_STRERROR -DUSE_SIZE_T
*
*
* AUTHORS: Torsten Kasch <torsten@techfak.uni-bielefeld.de>
* Robert Boucher <boucher@sofkin.ca>
* CONTRIBUTORS: Marc Cohen <marc@aai.com>
* Charles Hedrick <hedrick@geneva.rutgers.edu>
* William L. Jones <jones@chpc>
* Petri Kutvonen <kutvonen@cs.helsinki.fi>
* Casper Dik <casper.dik@sun.com>
* Tim Pugh <tpugh@oce.orst.edu>
* Mark Wong <markwkm@gmail.com>
*/
#define _KMEMUSER
#include "config.h"
#if (OSREV == 551)
#undef OSREV
#define OSREV 55
#endif
/* kernels 5.4 and above track pctcpu in the proc structure,
but the results are less than desirable, so we continue to
pretend that they don't and just calculate it on our own
*/
#if (OSREV >= 54)
/* #define PROC_HAS_PCTCPU */
#endif
#define USE_NEW_PROC
#if defined(USE_NEW_PROC) && OSREV >= 56
#define _STRUCTURED_PROC 1
#define prpsinfo psinfo
#include <sys/procfs.h>
#define pr_fill pr_nlwp
/* These require an ANSI C compiler "Reisser cpp" doesn't like this */
#define pr_state pr_lwp.pr_state
#define pr_oldpri pr_lwp.pr_oldpri
#define pr_nice pr_lwp.pr_nice
#define pr_pri pr_lwp.pr_pri
#define pr_onpro pr_lwp.pr_onpro
#define ZOMBIE(p) ((p)->pr_nlwp == 0)
#define SIZE_K(p) (long)((p)->pr_size)
#define RSS_K(p) (long)((p)->pr_rssize)
#else
#undef USE_NEW_PROC
#define ZOMBIE(p) ((p)->pr_zomb)
#define SIZE_K(p) (long)((p)->pr_bysize/1024)
#define RSS_K(p) (long)((p)->pr_byrssize/1024)
#define pr_onpro pr_filler[5]
#endif
#include "pg_top.h"
#include "machine.h"
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <nlist.h>
#include <string.h>
#include <kvm.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/signal.h>
#include <sys/fault.h>
#include <sys/sysinfo.h>
#include <sys/sysmacros.h>
#include <sys/syscall.h>
#include <sys/user.h>
#include <sys/proc.h>
#include <sys/procfs.h>
#include <sys/vm.h>
#include <sys/var.h>
#include <sys/cpuvar.h>
#include <sys/file.h>
#include <sys/time.h>
#include <sys/priocntl.h>
#include <sys/tspriocntl.h>
#include <sys/processor.h>
#include <sys/swap.h>
#include <vm/anon.h>
#include <math.h>
#include <utmpx.h>
#include "utils.h"
#if OSREV >= 53
#define USE_KSTAT
#endif
#ifdef USE_KSTAT
#include <kstat.h>
/*
* Some kstats are fixed at 32 bits, these will be specified as ui32; some
* are "natural" size (32 bit on 32 bit Solaris, 64 on 64 bit Solaris
* we'll make those unsigned long)
* Older Solaris doesn't define KSTAT_DATA_UINT32, those are always 32 bit.
*/
#ifndef KSTAT_DATA_UINT32
#define ui32 ul
#endif
#endif
#define UNIX "/dev/ksyms"
#define KMEM "/dev/kmem"
#define PROCFS "/proc"
#define CPUSTATES 5
#ifndef PRIO_MIN
#define PRIO_MIN -20
#endif
#ifndef PRIO_MAX
#define PRIO_MAX 20
#endif
#ifndef FSCALE
#define FSHIFT 8 /* bits to right of fixed binary point */
#define FSCALE (1<<FSHIFT)
#endif /* FSCALE */
#define loaddouble(la) ((double)(la) / FSCALE)
#define dbl_align(x) (((unsigned long)(x)+(sizeof(double)-1)) & \
~(sizeof(double)-1))
#ifdef PROC_HAS_PCTCPU
/*
* snarfed from <sys/procfs.h>: The following percent numbers are 16-bit
* binary fractions [0 .. 1] with the binary point to the right of the
* high-order bit (one == 0x8000)
*/
#define percent_cpu(pp) (((double)pp->pr_pctcpu)/0x8000*100)
#define weighted_cpu(pp) (*(double *)dbl_align(pp->pr_filler))
#else
#define percent_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[0]))
#define weighted_cpu(pp) (*(double *)dbl_align(&pp->pr_filler[2]))
#endif
/* definitions for indices in the nlist array */
#define X_V 0
#define X_MPID 1
#define X_ANONINFO 2
#define X_MAXMEM 3
#define X_FREEMEM 4
#define X_AVENRUN 5
#define X_CPU 6
#define X_NPROC 7
#define X_NCPUS 8
static struct nlist nlst[] =
{
{"v"}, /* 0 */ /* replaced by dynamic allocation */
{"mpid"}, /* 1 */
#if OSREV >= 56
/* this structure really has some extra fields, but the first three match */
{"k_anoninfo"}, /* 2 */
#else
{"anoninfo"}, /* 2 */
#endif
{"maxmem"}, /* 3 */ /* use sysconf */
{"freemem"}, /* 4 */ /* available from kstat >= 2.5 */
{"avenrun"}, /* 5 */ /* available from kstat */
{"cpu"}, /* 6 */ /* available from kstat */
{"nproc"}, /* 7 */ /* available from kstat */
{"ncpus"}, /* 8 */ /* available from kstat */
{0}
};
static unsigned long avenrun_offset;
static unsigned long mpid_offset;
#ifdef USE_KSTAT
static kstat_ctl_t * kc = NULL;
static kid_t kcid = 0;
#else
static unsigned long *cpu_offset;
#endif
static unsigned long nproc_offset;
static unsigned long freemem_offset;
static unsigned long maxmem_offset;
static unsigned long anoninfo_offset;
static void reallocproc(int n);
static int maxprocs = 0;
/* get_process_info passes back a handle. This is what it looks like: */
struct handle
{
struct prpsinfo **next_proc; /* points to next valid proc pointer */
int remaining; /* number of pointers remaining */
};
/*
* Structure for keeping track of CPU times from last time around
* the program. We keep these things in a hash table, which is
* recreated at every cycle.
*/
struct oldproc
{
pid_t oldpid;
double oldtime;
double oldpct;
};
int oldprocs; /* size of table */
#define HASH(x) ((x << 1) % oldprocs)
/*
* GCC assumes that all doubles are aligned. Unfortunately it
* doesn't round up the structure size to be a multiple of 8.
* Thus we'll get a coredump when going through array. The
* following is a size rounded up to 8.
*/
#define PRPSINFOSIZE dbl_align(sizeof(struct prpsinfo))
/*
* These definitions control the format of the per-process area
*/
#if OSREV >= 58
static char header[] =
" PID X LWP PRI NICE SIZE RES STATE TIME CPU COMMAND";
/* 0123456 -- field to fill in starts at header+6 */
#define UNAME_START 7
#define Proc_format \
"%6d %-8.8s %3d %3d %4d %5s %5s %-6s %6s %5s%% %s"
#else
static char header[] =
" PID X LWP PRI NICE SIZE RES STATE TIME CPU COMMAND";
/* 0123456 -- field to fill in starts at header+6 */
#define UNAME_START 6
#define Proc_format \
"%5d %-8.8s %3d %3d %4d %5s %5s %-6s %6s %5s%% %s"
#endif
/* process state names for the "STATE" column of the display */
char *state_abbrev[] =
{"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};
int process_states[8];
char *procstatenames[] =
{
"", " sleeping, ", " running, ", " zombie, ", " stopped, ",
" starting, ", " on cpu, ", " swapped, ",
NULL
};
int64_t cpu_states[CPUSTATES];
char *cpustatenames[] =
{"idle", "user", "kernel", "iowait", "swap", NULL};
#define CPUSTATE_IOWAIT 3
#define CPUSTATE_SWAP 4
/* these are for detailing the memory statistics */
long memory_stats[5];
char *memorynames[] =
{"K phys mem, ", "K free mem, ", "K total swap, ", "K free swap", NULL};
#define MEMORY_TOTALMEM 0
#define MEMORY_FREEMEM 1
#define MEMORY_TOTALSWAP 2
#define MEMORY_FREESWAP 3
/* these are names given to allowed sorting orders -- first is default */
char *ordernames[] = {"cpu", "size", "res", "time", NULL};
char *ordernames_io[] = {NULL};
/* forward definitions for comparison functions */
int compare_cpu();
int compare_size();
int compare_res();
int compare_time();
int (*proc_compares[]) () =
{
compare_cpu,
compare_size,
compare_res,
compare_time,
NULL
};
kvm_t *kd;
static DIR *procdir;
static int nproc;
/* "cpucount" is used to store the value for the kernel variable "ncpus".
But since <sys/cpuvar.h> actually defines a variable "ncpus" we need
to use a different name here. --wnl */
static int cpucount;
/* these are for keeping track of the proc array */
static struct prpsinfo *pbase;
static struct prpsinfo **pref;
static struct oldproc *oldbase;
/* pagetok function is really a pointer to an appropriate function */
static int pageshift;
static long (*p_pagetok) ();
#define pagetok(size) ((*p_pagetok)(size))
/* useful externals */
extern char *myname;
extern int check_nlist();
extern int gettimeofday();
extern void perror();
extern void getptable(struct prpsinfo *, PGresult *);
extern void quit();
extern int nlist();
/* p_pagetok points to one of the following, depending on which
direction data has to be shifted: */
long
pagetok_none(long size)
{
return (size);
}
long
pagetok_left(long size)
{
return (size << pageshift);
}
long
pagetok_right(long size)
{
return (size >> pageshift);
}
/*
* getkval(offset, ptr, size, refstr) - get a value out of the kernel.
* "offset" is the byte offset into the kernel for the desired value,
* "ptr" points to a buffer into which the value is retrieved,
* "size" is the size of the buffer (and the object to retrieve),
* "refstr" is a reference string used when printing error meessages,
* if "refstr" starts with a '!', then a failure on read will not
* be fatal (this may seem like a silly way to do things, but I
* really didn't want the overhead of another argument).
*
*/
int
getkval(unsigned long offset,
int *ptr,
int size,
char *refstr)
{
#ifdef DEBUG
dprintf("getkval(%08x, %08x, %d, %s)\n", offset, ptr, size, refstr);
#endif /* DEBUG */
if (kvm_read(kd, offset, (char *) ptr, size) != size)
{
#ifdef DEBUG
dprintf("getkval: read failed\n");
#endif /* DEBUG */
if (*refstr == '!')
{
return (0);
}
else
{
fprintf(stderr, "pg_top: kvm_read for %s: %s\n", refstr, strerror(errno));
quit(23);
}
}
#ifdef DEBUG
dprintf("getkval read %d (%08x)\n", *ptr);
#endif /* DEBUG */
return (1);
}
int
machine_init(struct statics *statics)
{
struct utmpx ut;
struct utmpx *up;
int i;
char *p;
#ifndef USE_KSTAT
int offset;
#endif
/*
* There's a buffer overflow bug in curses that can be exploited when we
* run as root. By making sure that TERMINFO is set to something this bug
* is avoided. This code thanks to Casper
*/
if ((p = getenv("TERMINFO")) == NULL || *p == '\0')
{
putenv("TERMINFO=/usr/share/lib/terminfo/");
}
/* perform the kvm_open - suppress error here */
if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL)
{
/* save the error message: we may need it later */
p = strerror(errno);
}
#ifdef DEBUG
dprintf("kvm_open: fd %d\n", kd);
#endif /* DEBUG */
/*
* turn off super group/user privs - but beware; we might want the privs
* back later and we still have a fd to /dev/kmem open so we can't use
* setgid()/setuid() as that would allow a debugger to attach to this
* process. CD
*/
setegid(getgid());
seteuid(getuid()); /* super user not needed for NEW_PROC */
#ifdef USE_KSTAT
if ((kc = kstat_open()) == NULL)
{
fprintf(stderr, "Unable to open kstat.\n");
return (-1);
}
kcid = kc->kc_chain_id;
#ifdef DEBUG
dprintf("kstat_open: chain %d\n", kcid);
#endif /* DEBUG */
#endif
/* fill in the statics information */
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
statics->order_names = ordernames;
statics->flags.fullcmds = 1;
statics->flags.warmup = 1;
/* get boot time */
ut.ut_type = BOOT_TIME;
if ((up = getutxid(&ut)) != NULL)
{
statics->boottime = up->ut_tv.tv_sec;
}
endutxent();
/* if the kvm_open succeeded, get the nlist */
if (kd)
{
if (kvm_nlist(kd, nlst) < 0)
{
perror("kvm_nlist");
return (-1);
}
if (check_nlist(nlst) != 0)
return (-1);
}
#ifndef USE_KSTAT
/*
* if KSTAT is not available to us and we can't open /dev/kmem, this is a
* serious problem.
*/
else
{
/* Print the error message here */
(void) fprintf(stderr, "kvm_open: %s\n", p);
return (-1);
}
#endif
/* stash away certain offsets for later use */
mpid_offset = nlst[X_MPID].n_value;
nproc_offset = nlst[X_NPROC].n_value;
avenrun_offset = nlst[X_AVENRUN].n_value;
anoninfo_offset = nlst[X_ANONINFO].n_value;
freemem_offset = nlst[X_FREEMEM].n_value;
maxmem_offset = nlst[X_MAXMEM].n_value;
#ifndef USE_KSTAT
(void) getkval(nlst[X_NCPUS].n_value, (int *) (&cpucount),
sizeof(cpucount), "ncpus");
cpu_offset = (unsigned long *) malloc(cpucount * sizeof(unsigned long));
for (i = offset = 0; i < cpucount; offset += sizeof(unsigned long))
{
(void) getkval(nlst[X_CPU].n_value + offset,
(int *) (&cpu_offset[i]), sizeof(unsigned long),
nlst[X_CPU].n_name);
if (cpu_offset[i] != 0)
i++;
}
#endif
/* calculate pageshift value */
i = sysconf(_SC_PAGESIZE);
pageshift = 0;
while ((i >>= 1) > 0)
{
pageshift++;
}
/* calculate an amount to shift to K values */
/* remember that log base 2 of 1024 is 10 (i.e.: 2^10 = 1024) */
pageshift -= 10;
/*
* now determine which pageshift function is appropriate for the result
* (have to because x << y is undefined for y < 0)
*/
if (pageshift > 0)
{
/* this is the most likely */
p_pagetok = pagetok_left;
}
else if (pageshift == 0)
{
p_pagetok = pagetok_none;
}
else
{
p_pagetok = pagetok_right;
pageshift = -pageshift;
}
if (!(procdir = opendir(PROCFS)))
{
(void) fprintf(stderr, "Unable to open %s\n", PROCFS);
return (-1);
}
if (chdir(PROCFS))
{ /* handy for later on when we're reading it */
(void) fprintf(stderr, "Unable to chdir to %s\n", PROCFS);
return (-1);
}
/* all done! */
return (0);
}
char *
format_header(register char *uname_field)
{
register char *ptr;
ptr = header + UNAME_START;
while (*uname_field != '\0')
*ptr++ = *uname_field++;
return (header);
}
#ifdef USE_KSTAT
long
kstat_data_value_l(kstat_named_t * kn)
{
switch (kn->data_type)
{
case KSTAT_DATA_INT32:
return ((long) (kn->value.i32));
case KSTAT_DATA_UINT32:
return ((long) (kn->value.ui32));
case KSTAT_DATA_INT64:
return ((long) (kn->value.i64));
case KSTAT_DATA_UINT64:
return ((long) (kn->value.ui64));
}
return 0;
}
int
kstat_safe_retrieve(kstat_t * *ksp,
char *module, int instance, char *name, void *buf)
{
kstat_t *ks;
kid_t new_kcid;
int changed;
#ifdef DEBUG
dprintf("kstat_safe_retrieve(%08x -> %08x, %s, %d, %s, %08x)\n",
ksp, *ksp, module, instance, name, buf);
#endif /* DEBUG */
ks = *ksp;
do
{
changed = 0;
/* if we dont already have the kstat, retrieve it */
if (ks == NULL)
{
if ((ks = kstat_lookup(kc, module, instance, name)) == NULL)
{
return (-1);
}
*ksp = ks;
}
/* attempt to read it */
new_kcid = kstat_read(kc, ks, buf);
/*
* chance for an infinite loop here if kstat_read keeps returning -1
*/
/* if the chain changed, update it */
if (new_kcid != kcid)
{
#ifdef DEBUG
dprintf("kstat_safe_retrieve: chain changed to %d...updating\n",
new_kcid);
#endif /* DEBUG */
changed = 1;
kcid = kstat_chain_update(kc);
}
} while (changed);
return (0);
}
/*
* int kstat_safe_namematch(int num, kstat_t *ksp, char *name, void *buf)
*
* Safe scan of kstat chain for names starting with "name". Matches
* are copied in to "ksp", and kstat_read is called on each match using
* "buf" as a buffer of length "size". The actual number of records
* found is returned. Up to "num" kstats are copied in to "ksp", but
* no more. If any kstat_read indicates that the chain has changed, then
* the whole process is restarted.
*/
int
kstat_safe_namematch(int num, kstat_t * *ksparg, char *name, void *buf, int size)
{
kstat_t *ks;
kstat_t **ksp;
kid_t new_kcid;
int namelen;
int count;
int changed;
char *cbuf;
#ifdef DEBUG
dprintf("kstat_safe_namematch(%d, %08x, %s, %08x, %d)\n",
num, ksp, name, buf, size);
#endif /* DEBUG */
namelen = strlen(name);
do
{
/* initialize before the scan */
cbuf = (char *) buf;
ksp = ksparg;
count = 0;
changed = 0;
/* scan the chain for matching kstats */
for (ks = kc->kc_chain; ks != NULL; ks = ks->ks_next)
{
if (strncmp(ks->ks_name, name, namelen) == 0)
{
/* this kstat matches: save it if there is room */
if (count++ < num)
{
/* read the kstat */
new_kcid = kstat_read(kc, ks, cbuf);
/* if the chain changed, update it */
if (new_kcid != kcid)
{
#ifdef DEBUG
dprintf("kstat_safe_namematch: chain changed to %d...updating\n",
new_kcid);
#endif /* DEBUG */
changed = 1;
kcid = kstat_chain_update(kc);
/* there's no sense in continuing the scan */
/* so break out of the for loop */
break;
}
/* move to the next buffers */
cbuf += size;
*ksp++ = ks;
}
}
}
} while (changed);
#ifdef DEBUG
dprintf("kstat_safe_namematch returns %d\n", count);
#endif /* DEBUG */
return count;
}
static kstat_t * ks_system_misc = NULL;
#endif /* USE_KSTAT */
int
get_avenrun(int avenrun[3])
{
#ifdef USE_KSTAT
int status;
kstat_named_t *kn;
#ifdef DEBUG
dprintf("get_avenrun(%08x)\n", avenrun);
#endif /* DEBUG */
if ((status = kstat_safe_retrieve(&ks_system_misc,
"unix", 0, "system_misc", NULL)) == 0)
{
if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_1min")) != NULL)
{
avenrun[0] = kn->value.ui32;
}
if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_5min")) != NULL)
{
avenrun[1] = kn->value.ui32;
}
if ((kn = kstat_data_lookup(ks_system_misc, "avenrun_15min")) != NULL)
{
avenrun[2] = kn->value.ui32;
}
}
#ifdef DEBUG
dprintf("get_avenrun returns %d\n", status);
#endif /* DEBUG */
return (status);
#else /* !USE_KSTAT */
(void) getkval(avenrun_offset, (int *) avenrun, sizeof(int[3]), "avenrun");
return 0;
#endif /* USE_KSTAT */
}
int
get_ncpus()
{
#ifdef USE_KSTAT
kstat_named_t *kn;
int ret = -1;
if ((kn = kstat_data_lookup(ks_system_misc, "ncpus")) != NULL)
{
ret = (int) (kn->value.ui32);
}
return ret;
#else
int ret;
(void) getkval(nlst[X_NCPUS].n_value, (int *) (&ret), sizeof(ret), "ncpus");
return ret;
#endif
}
int
get_nproc()
{
#ifdef USE_KSTAT
kstat_named_t *kn;
int ret = -1;
if ((kn = kstat_data_lookup(ks_system_misc, "nproc")) != NULL)
{
ret = (int) (kn->value.ui32);
}
return ret;
#else
int ret;
(void) getkval(nproc_offset, (int *) (&ret), sizeof(ret), "nproc");
return ret;
#endif
}
int64_t
(*get_cpustats(int *cnt, int64_t(*cp_stats)[CPUSTATES]))[CPUSTATES]
{
#ifdef USE_KSTAT
static kstat_t * *cpu_ks = NULL;
static cpu_stat_t * cpu_stat = NULL;
static unsigned int nelems = 0;
cpu_stat_t *cpu_stat_p;
int i,
cpu_num;
int64_t(*cp_stats_p)[CPUSTATES];
#ifdef DEBUG
dprintf("get_cpustats(%d -> %d, %08x)\n", cnt, *cnt, cp_stats);
#endif /* DEBUG */
while (nelems > 0 ?
(cpu_num = kstat_safe_namematch(nelems,
cpu_ks,
"cpu_stat",
cpu_stat,
sizeof(cpu_stat_t))) > nelems :
(cpu_num = get_ncpus()) > 0)
{
/* reallocate the arrays */
#ifdef DEBUG
dprintf("realloc from %d to %d\n", nelems, cpu_num);
#endif /* DEBUG */
nelems = cpu_num;
if (cpu_ks != NULL)
{
free(cpu_ks);
}
cpu_ks = (kstat_t * *) calloc(nelems, sizeof(kstat_t *));
if (cpu_stat != NULL)
{
free(cpu_stat);
}
cpu_stat = (cpu_stat_t *) malloc(nelems * sizeof(cpu_stat_t));
}
/* do we have more cpus than our caller? */
if (cpu_num > *cnt)
{
/* yes, so realloc their array, too */
#ifdef DEBUG
dprintf("realloc array from %d to %d\n", *cnt, cpu_num);
#endif /* DEBUG */
*cnt = cpu_num;
cp_stats = (int64_t(*)[CPUSTATES]) realloc(cp_stats,
cpu_num * sizeof(int64_t) * CPUSTATES);
}
cpu_stat_p = cpu_stat;
cp_stats_p = cp_stats;
for (i = 0; i < cpu_num; i++)
{
#ifdef DEBUG
dprintf("cpu %d %08x: idle %u, user %u, syscall %u\n", i, cpu_stat_p,
cpu_stat_p->cpu_sysinfo.cpu[0],
cpu_stat_p->cpu_sysinfo.cpu[1],
cpu_stat_p->cpu_sysinfo.syscall);
#endif /* DEBUG */
(*cp_stats_p)[CPU_IDLE] = (int64_t) cpu_stat_p->cpu_sysinfo.cpu[CPU_IDLE];
(*cp_stats_p)[CPU_USER] = (int64_t) cpu_stat_p->cpu_sysinfo.cpu[CPU_USER];
(*cp_stats_p)[CPU_KERNEL] = (int64_t) cpu_stat_p->cpu_sysinfo.cpu[CPU_KERNEL];
(*cp_stats_p)[CPUSTATE_IOWAIT] = (int64_t) cpu_stat_p->cpu_sysinfo.wait[W_IO] +
cpu_stat_p->cpu_sysinfo.wait[W_PIO];
(*cp_stats_p)[CPUSTATE_SWAP] = (int64_t) cpu_stat_p->cpu_sysinfo.wait[W_SWAP];
cp_stats_p++;
cpu_stat_p++;
}
cpucount = cpu_num;
#ifdef DEBUG
dprintf("get_cpustats sees %d cpus and returns %08x\n", cpucount, cp_stats);
#endif /* DEBUG */
return (cp_stats);
#else /* !USE_KSTAT */
int i;
struct cpu cpu;
int64_t(*cp_stats_p)[CPUSTATES];
/* do we have more cpus than our caller? */
if (cpucount > *cnt)
{
/* yes, so realloc their array, too */
#ifdef DEBUG
dprintf("realloc array from %d to %d\n", *cnt, cpucount);
#endif /* DEBUG */
*cnt = cpucount;
cp_stats = (int64_t(*)[CPUSTATES]) realloc(cp_stats,
cpucount * sizeof(int64_t) * CPUSTATES);
}
cp_stats_p = cp_stats;
for (i = 0; i < cpucount; i++)
{
if (cpu_offset[i] != 0)
{
/* get struct cpu for this processor */
(void) getkval(cpu_offset[i], (int *) (&cpu), sizeof(struct cpu), "cpu");
(*cp_stats_p)[CPU_IDLE] = (int64_t) cpu.cpu_stat.cpu_sysinfo.cpu[CPU_IDLE];
(*cp_stats_p)[CPU_USER] = (int64_t) cpu.cpu_stat.cpu_sysinfo.cpu[CPU_USER];
(*cp_stats_p)[CPU_KERNEL] = (int64_t) cpu.cpu_stat.cpu_sysinfo.cpu[CPU_KERNEL];
(*cp_stats_p)[CPUSTATE_IOWAIT] = (int64_t) cpu.cpu_stat.cpu_sysinfo.wait[W_IO] +
cpu.cpu_stat.cpu_sysinfo.wait[W_PIO];
(*cp_stats_p)[CPUSTATE_SWAP] = (int64_t) cpu.cpu_stat.cpu_sysinfo.wait[W_SWAP];
cp_stats_p++;
}
}
return (cp_stats);
#endif /* USE_KSTAT */
}
/*
* void get_meminfo(long *total, long *fr)
*
* Get information about the system's physical memory. Pass back values
* for total available and amount of memory that is free (in kilobytes).
* It returns 0 on success and -1 on any kind of failure.
*/
int
get_meminfo(long *total, long *fr)
{
long freemem;
static kstat_t * ks = NULL;
kstat_named_t *kn;
/* total comes from sysconf */
*total = pagetok(sysconf(_SC_PHYS_PAGES));
/* free comes from the kernel's freemem or from kstat */
/*
* prefer kmem for this because kstat unix:0:system_pages can be slow on
* systems with lots of memory
*/
if (kd)
{
(void) getkval(freemem_offset, (int *) (&freemem), sizeof(freemem),
"freemem");
}
else
{
#ifdef USE_KSTAT
/* only need to grab kstat chain once */
if (ks == NULL)
{
ks = kstat_lookup(kc, "unix", 0, "system_pages");
}
if (ks != NULL &&
kstat_read(kc, ks, 0) != -1 &&
(kn = kstat_data_lookup(ks, "freemem")) != NULL)
{
freemem = kstat_data_value_l(kn);
}
else
{
freemem = -1;
}
#else
freemem = -1;
#endif
}
*fr = freemem == -1 ? -1 : pagetok(freemem);
return (0);
}
/*
* void get_swapinfo(long *total, long *fr)
*
* Get information about the system's swap. Pass back values for
* total swap available and amount of swap that is free (in kilobytes).
* It returns 0 on success and -1 on any kind of failure.
*/
int
get_swapinfo(long *total, long *fr)
{
register int cnt,
i;
register long t,
f;
struct swaptable *swt;
struct swapent *ste;
static char path[256];
/* preset values to 0 just in case we have to return early */
*total = 0;
*fr = 0;
/* get total number of swap entries */
if ((cnt = swapctl(SC_GETNSWP, 0)) == -1)
{
return (-1);
}
/* allocate enough space to hold count + n swapents */
swt = (struct swaptable *) malloc(sizeof(int) +
cnt * sizeof(struct swapent));
if (swt == NULL)
{
return (-1);
}
swt->swt_n = cnt;
/*
* fill in ste_path pointers: we don't care about the paths, so we point
* them all to the same buffer
*/
ste = &(swt->swt_ent[0]);
i = cnt;
while (--i >= 0)
{
ste++->ste_path = path;
}
/* grab all swap info */
if (swapctl(SC_LIST, swt) == -1)
{
return (-1);
}
/* walk thru the structs and sum up the fields */
t = f = 0;
ste = &(swt->swt_ent[0]);
i = cnt;
while (--i >= 0)
{
/* dont count slots being deleted */
if (!(ste->ste_flags & ST_INDEL) &&
!(ste->ste_flags & ST_DOINGDEL))
{
t += ste->ste_pages;
f += ste->ste_free;
}
ste++;
}
/* fill in the results */
*total = pagetok(t);
*fr = pagetok(f);
free(swt);
/* good to go */
return (0);
}
void
get_system_info(struct system_info *si)
{
int avenrun[3];
static int64_t cp_time[CPUSTATES];
static int64_t cp_old[CPUSTATES];
static int64_t cp_diff[CPUSTATES];
static int64_t(*cp_stats)[CPUSTATES] = NULL;
static int cpus;
register int j,
i;
/* get important information */
get_avenrun(avenrun);
/* get the cpu statistics arrays */
cp_stats = get_cpustats(&cpus, cp_stats);
/* zero the cp_time array */
for (j = 0; j < CPUSTATES; j++)
{
cp_time[j] = 0;
}
/* sum stats in to a single array */
for (i = 0; i < cpus; i++)
{
for (j = 0; j < CPUSTATES; j++)
{
cp_time[j] += cp_stats[i][j];
}
}
/* convert cp_time counts to percentages */
(void) percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff);
/* get mpid -- process id of last process */
if (kd)
(void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "mpid");
else
si->last_pid = -1;
/* convert load averages to doubles */
for (i = 0; i < 3; i++)
si->load_avg[i] = loaddouble(avenrun[i]);
/* get physical memory data */
if (get_meminfo(&(memory_stats[MEMORY_TOTALMEM]),
&(memory_stats[MEMORY_FREEMEM])) == -1)
{
memory_stats[MEMORY_TOTALMEM] = memory_stats[MEMORY_FREEMEM] = -1;
}
/* get swap data */
if (get_swapinfo(&(memory_stats[MEMORY_TOTALSWAP]),
&(memory_stats[MEMORY_FREESWAP])) == -1)
{
memory_stats[MEMORY_TOTALSWAP] = memory_stats[MEMORY_FREESWAP] = -1;
}
/* set arrays and strings */
si->cpustates = cpu_states;
si->memory = memory_stats;
#ifdef DEBUG
dprintf("get_system_info returns\n");
#endif /* DEBUG */
}
static struct handle handle;
static int show_fullcmd;
caddr_t
get_process_info(
struct system_info *si,
struct process_select *sel,
int compare_index,
const char *values[],
int mode)
{
register int i;
register int total_procs;
register int active_procs;
register struct prpsinfo **prefp;
register struct prpsinfo *pp;
/* these are copied out of sel for speed */
int show_idle;
int show_system;
int show_uid;
PGconn *pgconn;
PGresult *pgresult = NULL;
/* allocate enough space for twice our current needs */
nproc = 20;
pgconn = connect_to_db(values);
if (pgconn != NULL)
{
pgresult = pg_processes(pgconn);
nproc = PQntuples(pgresult);
if (nproc > maxprocs)
{
reallocproc(2 * nproc);
}
/* read all the proc structures */
getptable(pbase, pgresult);
PQclear(pgresult);
PQfinish(pgconn);
}
else
nproc = 0;
/* get a pointer to the states summary array */
si->procstates = process_states;
/* set up flags which define what we are going to select */
show_idle = sel->idle;
show_uid = sel->uid != -1;
show_fullcmd = sel->fullcmd;
/* count up process states and get pointers to interesting procs */
total_procs = 0;
active_procs = 0;
(void) memset(process_states, 0, sizeof(process_states));
prefp = pref;
for (pp = pbase, i = 0; i < nproc;
i++, pp = (struct prpsinfo *) ((char *) pp + PRPSINFOSIZE))
{
/*
* Place pointers to each valid proc structure in pref[]. Process
* slots that are actually in use have a non-zero status field.
* Processes with SSYS set are system processes---these get ignored
* unless show_sysprocs is set.
*/
if (pp->pr_state != 0 &&
(show_system || ((pp->pr_flag & SSYS) == 0)))
{
total_procs++;
process_states[(int) pp->pr_state]++;
if ((!ZOMBIE(pp)) &&
(show_idle || percent_cpu(pp) || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) &&
(!show_uid || pp->pr_uid == (uid_t) sel->uid))
{
*prefp++ = pp;
active_procs++;
}
}
}
/* if requested, sort the "interesting" processes */
qsort((char *) pref, active_procs, sizeof(struct prpsinfo *),
proc_compares[compare_index]);
/* remember active and total counts */
si->p_total = total_procs;
si->p_active = active_procs;
/* pass back a handle */
handle.next_proc = pref;
handle.remaining = active_procs;
return ((caddr_t) & handle);
}
char fmt[MAX_COLS]; /* static area where result is built */
char *
format_next_io(caddr_t handle, char *(*get_userid) ())
{
fmt[0] = '\0';
return (fmt);
}
char *
format_next_process(
caddr_t handle,
char *(*get_userid) ())
{
register struct prpsinfo *pp;
struct handle *hp;
register long cputime;
register double pctcpu;
char sb[10];
/* find and remember the next proc structure */
hp = (struct handle *) handle;
pp = *(hp->next_proc++);
hp->remaining--;
/* get the cpu usage and calculate the cpu percentages */
cputime = pp->pr_time.tv_sec;
pctcpu = percent_cpu(pp) / cpucount;
if (pp->pr_state == SONPROC && cpucount > 1)
sprintf(sb, "cpu/%-2d", pp->pr_onpro); /* XXX large #s may overflow
* colums */
else
*sb = '\0';
/* format this entry */
#ifdef HAVE_SNPRINTF
snprintf(fmt, sizeof(fmt),
#else
sprintf(fmt,
#endif
Proc_format,
(int) pp->pr_pid,
(*get_userid) (pp->pr_uid),
(u_short) pp->pr_fill < 999 ? (u_short) pp->pr_fill : 999,
pp->pr_pri,
pp->pr_nice - NZERO,
format_k(SIZE_K(pp)),
format_k(RSS_K(pp)),
*sb ? sb : state_abbrev[(int) pp->pr_state],
format_time(cputime),
format_percent(pctcpu),
printable(show_fullcmd ? pp->pr_psargs : pp->pr_fname));
/* return the result */
return (fmt);
}
/*
* check_nlist(nlst) - checks the nlist to see if any symbols were not
* found. For every symbol that was not found, a one-line
* message is printed to stderr. The routine returns the
* number of symbols NOT found.
*/
int
check_nlist(register struct nlist *nlst)
{
register int i;
/* check to see if we got ALL the symbols we requested */
/* this will write one line to stderr for every symbol not found */
i = 0;
while (nlst->n_name != NULL)
{
if (nlst->n_type == 0)
{
/* this one wasn't found */
fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
i = 1;
}
nlst++;
}
return (i);
}
/* comparison routines for qsort */
/*
* There are currently four possible comparison routines. main selects
* one of these by indexing in to the array proc_compares.
*
* Possible keys are defined as macros below. Currently these keys are
* defined: percent cpu, cpu ticks, process state, resident set size,
* total virtual memory usage. The process states are ordered as follows
* (from least to most important): WAIT, zombie, sleep, stop, start, run.
* The array declaration below maps a process state index into a number
* that reflects this ordering.
*/
/* First, the possible comparison keys. These are defined in such a way
that they can be merely listed in the source code to define the actual
desired ordering.
*/
#define ORDERKEY_PCTCPU if (dresult = percent_cpu (p2) - percent_cpu (p1),\
(result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0)
#define ORDERKEY_CPTICKS if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
#define ORDERKEY_STATE if ((result = \
(long) (sorted_state[(int) p2->pr_state] - \
sorted_state[(int) p1->pr_state])) == 0)
#define ORDERKEY_PRIO if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)
#define ORDERKEY_RSSIZE if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
#define ORDERKEY_MEM if ((result = (p2->pr_size - p1->pr_size)) == 0)
/* Now the array that maps process state to a weight */
unsigned char sorted_state[] =
{
0, /* not used */
3, /* sleep */
6, /* run */
2, /* zombie */
4, /* stop */
5, /* start */
7, /* run on a processor */
1 /* being swapped (WAIT) */
};
/* compare_cpu - the comparison function for sorting by cpu percentage */
int
compare_cpu(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
/* compare_size - the comparison function for sorting by total memory usage */
int
compare_size(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_MEM
ORDERKEY_RSSIZE
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
/* compare_res - the comparison function for sorting by resident set size */
int
compare_res(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_RSSIZE
ORDERKEY_MEM
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
/* compare_time - the comparison function for sorting by total cpu time */
int
compare_time(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_CPTICKS
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
/*
get process table
V.4 only has a linked list of processes so we want to follow that
linked list, get all the process structures, and put them in our own
table
*/
void
getptable(struct prpsinfo *baseptr, PGresult *pgresult)
{
struct prpsinfo *currproc; /* pointer to current proc structure */
#ifndef USE_NEW_PROC
struct prstatus prstatus; /* for additional information */
#endif
int numprocs = 0;
int i;
struct oldproc *op;
static struct timeval lasttime =
{0, 0};
struct timeval thistime;
double timediff;
double alpha,
beta;
struct oldproc *endbase;
gettimeofday(&thistime, NULL);
/*
* To avoid divides, we keep times in nanoseconds. This is scaled by 1e7
* rather than 1e9 so that when we divide we get percent.
*/
if (lasttime.tv_sec)
timediff = ((double) thistime.tv_sec * 1.0e7 +
((double) thistime.tv_usec * 10.0)) -
((double) lasttime.tv_sec * 1.0e7 +
((double) lasttime.tv_usec * 10.0));
else
timediff = 1.0e7;
/*
* constants for exponential average. avg = alpha * new + beta * avg The
* goal is 50% decay in 30 sec. However if the sample period is greater
* than 30 sec, there's not a lot we can do.
*/
if (timediff < 30.0e7)
{
alpha = 0.5 * (timediff / 30.0e7);
beta = 1.0 - alpha;
}
else
{
alpha = 0.5;
beta = 0.5;
}
endbase = oldbase + oldprocs;
currproc = baseptr;
/* before reading /proc files, turn on root privs */
/* (we don't care if this fails since it will be caught later) */
#ifndef USE_NEW_PROC
seteuid(0);
#endif
for (i = 0; i < nproc; i++)
{
int fd;
char buf[30];
char *procpid = PQgetvalue(pgresult, i, 0);
#ifdef USE_NEW_PROC
#ifdef HAVE_SNPRINTF
snprintf(buf, sizeof(buf), "%s/%s/psinfo", PROCFS, procpid);
#else
sprintf(buf, "%s/psinfo", direntp->d_name);
#endif
if ((fd = open(buf, O_RDONLY)) < 0)
continue;
if (read(fd, currproc, sizeof(psinfo_t)) != sizeof(psinfo_t))
{
(void) close(fd);
continue;
}
#else
if ((fd = open(direntp->d_name, O_RDONLY)) < 0)
continue;
if (ioctl(fd, PIOCPSINFO, currproc) < 0)
{
(void) close(fd);
continue;
}
if (ioctl(fd, PIOCSTATUS, &prstatus) < 0)
{
/* not a show stopper -- just fill in the needed values */
currproc->pr_fill = 0;
currproc->pr_onpro = 0;
}
else
{
/* copy over the values we need from prstatus */
currproc->pr_fill = (short) prstatus.pr_nlwp;
currproc->pr_onpro = prstatus.pr_processor;
}
#endif
/*
* SVr4 doesn't keep track of CPU% in the kernel, so we have to do our
* own. See if we've heard of this process before. If so, compute %
* based on CPU since last time. NOTE: Solaris 2.4 and higher do
* maintain CPU% in prpsinfo.
*/
op = oldbase + HASH(currproc->pr_pid);
while (1)
{
if (op->oldpid == -1) /* not there */
break;
if (op->oldpid == currproc->pr_pid)
{ /* found old data */
#ifndef PROC_HAS_PCTCPU
percent_cpu(currproc) =
((currproc->pr_time.tv_sec * 1.0e9 +
currproc->pr_time.tv_nsec)
- op->oldtime) / timediff;
#endif
weighted_cpu(currproc) =
op->oldpct * beta + percent_cpu(currproc) * alpha;
break;
}
op++; /* try next entry in hash table */
if (op == endbase) /* table wrapped around */
op = oldbase;
}
/* Otherwise, it's new, so use all of its CPU time */
if (op->oldpid == -1)
{
#ifdef PROC_HAS_PCTCPU
weighted_cpu(currproc) =
percent_cpu(currproc);
#else
if (lasttime.tv_sec)
{
percent_cpu(currproc) =
(currproc->pr_time.tv_sec * 1.0e9 +
currproc->pr_time.tv_nsec) / timediff;
weighted_cpu(currproc) =
percent_cpu(currproc);
}
else
{ /* first screen -- no difference is possible */
percent_cpu(currproc) = 0.0;
weighted_cpu(currproc) = 0.0;
}
#endif
}
numprocs++;
currproc = (struct prpsinfo *) ((char *) currproc + PRPSINFOSIZE);
(void) close(fd);
/* Atypical place for growth */
if (numprocs >= maxprocs)
{
reallocproc(2 * numprocs);
currproc = (struct prpsinfo *)
((char *) baseptr + PRPSINFOSIZE * numprocs);
}
}
#ifndef USE_NEW_PROC
/* turn off root privs */
seteuid(getuid());
#endif
if (nproc != numprocs)
nproc = numprocs;
/*
* Save current CPU time for next time around For the moment recreate the
* hash table each time, as the code is easier that way.
*/
oldprocs = 2 * nproc;
endbase = oldbase + oldprocs;
for (op = oldbase; op < endbase; op++)
op->oldpid = -1;
for (i = 0, currproc = baseptr;
i < nproc;
i++, currproc = (struct prpsinfo *) ((char *) currproc + PRPSINFOSIZE))
{
/* find an empty spot */
op = oldbase + HASH(currproc->pr_pid);
while (1)
{
if (op->oldpid == -1)
break;
op++;
if (op == endbase)
op = oldbase;
}
op->oldpid = currproc->pr_pid;
op->oldtime = (currproc->pr_time.tv_sec * 1.0e9 +
currproc->pr_time.tv_nsec);
op->oldpct = weighted_cpu(currproc);
}
lasttime = thistime;
}
/*
* proc_owner(pid) - returns the uid that owns process "pid", or -1 if
* the process does not exist.
* It is EXTREMLY IMPORTANT that this function work correctly.
* If pg_top runs setuid root (as in SVR4), then this function
* is the only thing that stands in the way of a serious
* security problem. It validates requests for the "kill"
* and "renice" commands.
*/
uid_t
proc_owner(pid_t pid)
{
register struct prpsinfo *p;
int i;
for (i = 0, p = pbase; i < nproc;
i++, p = (struct prpsinfo *) ((char *) p + PRPSINFOSIZE))
{
if (p->pr_pid == (pid_t) pid)
return ((int) p->pr_uid);
}
return (-1);
}
/* older revisions don't supply a setpriority */
#if (OSREV < 55)
int
setpriority(int dummy, int who, int niceval)
{
int scale;
int prio;
pcinfo_t pcinfo;
pcparms_t pcparms;
tsparms_t *tsparms;
strcpy(pcinfo.pc_clname, "TS");
if (priocntl(0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
return (-1);
prio = niceval;
if (prio > PRIO_MAX)
prio = PRIO_MAX;
else if (prio < PRIO_MIN)
prio = PRIO_MIN;
tsparms = (tsparms_t *) pcparms.pc_clparms;
scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
pcparms.pc_cid = pcinfo.pc_cid;
if (priocntl(P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
return (-1);
return (0);
}
#endif
/*
* When we reach a proc limit, we need to realloc the stuff.
*/
static void
reallocproc(int n)
{
int bytes;
struct oldproc *op,
*endbase;
if (n < maxprocs)
return;
#ifdef DEBUG
dprintf("reallocproc(%d): reallocating from %d\n", n, maxprocs);
#endif /* DEBUG */
maxprocs = n;
/* allocate space for proc structure array and array of pointers */
bytes = maxprocs * PRPSINFOSIZE;
pbase = (struct prpsinfo *) realloc(pbase, bytes);
pref = (struct prpsinfo **) realloc(pref,
maxprocs * sizeof(struct prpsinfo *));
oldbase = (struct oldproc *) realloc(oldbase,
2 * maxprocs * sizeof(struct oldproc));
/* Just in case ... */
if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL
|| oldbase == (struct oldproc *) NULL)
{
fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
quit(1);
}
/*
* We're growing from 0 to some number, only then we need to init the
* oldproc stuff
*/
if (!oldprocs)
{
oldprocs = 2 * maxprocs;
endbase = oldbase + oldprocs;
for (op = oldbase; op < endbase; op++)
op->oldpid = -1;
}
}
void
get_io_info(struct io_info *io_info)
{
/* Not supported yet */
memset(io_info, 0, sizeof(*io_info));
}
07070100000032000081A400000000000000000000000166622DE8000007A3000000000000000000000000000000000000002600000000pg_top-4.1.1/machine/m_sunos5.man.rstSUNOS 5 NOTES
=============
CPU percentage is calculated as a fraction of total available computing
resources. Hence on a multiprocessor machine a single threaded process
can never consume cpu time in excess of 1 divided by the number of processors.
For example, on a 4 processor machine, a single threaded process will
never show a cpu percentage higher than 25%. The CPU percentage column
will always total approximately 100, regardless of the number of processors.
The memory summary line displays the following: "phys mem" is the total
amount of physical memory that can be allocated for use by processes
(it does not include memory reserved for the kernel's use), "free mem" is
the amount of unallocated physical memory, "total swap" is the amount
of swap area on disk that is being used, "free swap" is the amount of
swap area on disk that is still available. Unlike previous versions of
*pg_top*, The swap figures will differ from the summary output of *swap (1M)*
since the latter includes physical memory as well.
The column "THR" indicates the number of execution threads in the process.
In BSD Unix, process priority was represented internally as a signed
offset from a zero value with an unsigned value. The "zero" value
was usually something like 20, allowing for a range of priorities
from -20 to 20. As implemented on SunOS 5, older versions of top
continued to interpret process priority in this manner, even though
it was no longer correct. Starting with top version 3.5, this was
changed to agree with the rest of the system.
The SunOS 5 (Solaris 2) port was originally written by Torsten Kasch,
<torsten@techfak.uni-bielefeld.de>. Many contributions have been
provided by Casper Dik <Casper.Dik@sun.com>.
Support for multi-cpu, calculation of CPU% and memory stats provided by
Robert Boucher <boucher@sofkin.ca>, Marc Cohen <marc@aai.com>,
Charles Hedrick <hedrick@geneva.rutgers.edu>, and
William L. Jones <jones@chpc>.
07070100000033000081A400000000000000000000000166622DE800004994000000000000000000000000000000000000001E00000000pg_top-4.1.1/machine/m_svr4.c/*
* pg_top - a top PostgreSQL users display for Unix
*
* SYNOPSIS: Intel based System V Release 4
*
* DESCRIPTION:
* System V release 4.0.x for i486
* System V release 4 for Okidata M88100
* System V release 4 for NCR 3000 series OS Rel 1.00 to 2.02
* System V release 4 for NCR 3000 series OS Rel 02.03.00 and above
* and probably other svr4 ports
*
* LIBS: -lelf
*
* AUTHORS: Andrew Herbert <andrew@werple.apana.org.au>
* Robert Boucher <boucher@sofkin.ca>
* Ported to System 3000 Release 2.03 by:
* Jeff Janvrin <jeff.janvrinColumbiaSC.NCR.COM>
*/
#include "pg_top.h"
#include "machine.h"
#include "utils.h"
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <nlist.h>
#include <string.h>
#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/procfs.h>
#include <sys/sysinfo.h>
#include <sys/sysmacros.h>
#include <sys/vmmeter.h>
#include <vm/anon.h>
#include <sys/priocntl.h>
#include <sys/rtpriocntl.h>
#include <sys/tspriocntl.h>
#include <sys/procset.h>
#include <sys/var.h>
#define UNIX "/stand/unix"
#define KMEM "/dev/kmem"
#define PROCFS "/proc"
#define CPUSTATES 5
#ifndef PRIO_MAX
#define PRIO_MAX 20
#endif
#ifndef PRIO_MIN
#define PRIO_MIN -20
#endif
#ifndef FSCALE
#define FSHIFT 8 /* bits to right of fixed binary point */
#define FSCALE (1<<FSHIFT)
#endif
#define loaddouble(x) ((double)(x) / FSCALE)
#define percent_cpu(x) ((double)(x)->pr_cpu / FSCALE)
#define weighted_cpu(pct, pp) ( ((pp)->pr_time.tv_sec) == 0 ? 0.0 : \
((pp)->pr_cpu) / ((pp)->pr_time.tv_sec) )
#define pagetok(size) ctob(size) >> LOG1024
/* definitions for the index in the nlist array */
#define X_AVENRUN 0
#define X_MPID 1
#define X_V 2
#define X_NPROC 3
#define X_ANONINFO 4
#define X_TOTAL 5
#define X_SYSINFO 6
static struct nlist nlst[] =
{
{"avenrun"}, /* 0 */
{"mpid"}, /* 1 */
{"v"}, /* 2 */
{"nproc"}, /* 3 */
{"anoninfo"}, /* 4 */
{"total"}, /* 5 */
{"sysinfo"}, /* 6 */
{NULL}
};
static unsigned long avenrun_offset;
static unsigned long mpid_offset;
static unsigned long nproc_offset;
static unsigned long anoninfo_offset;
static unsigned long total_offset;
static unsigned long sysinfo_offset;
/* get_process_info passes back a handle. This is what it looks like: */
struct handle
{
struct prpsinfo **next_proc; /* points to next valid proc pointer */
int remaining; /* number of pointers remaining */
};
/*
* These definitions control the format of the per-process area
*/
static char header[] =
" PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND";
/* 0123456 -- field to fill in starts at header+6 */
#define UNAME_START 6
#define Proc_format \
"%5d %-8.8s %3d %4d %5s %5s %-5s %6s %3d.0%% %5.2f%% %.16s"
char *state_abbrev[] =
{"", "sleep", "run", "zombie", "stop", "start", "cpu", "swap"};
int process_states[8];
char *procstatenames[] =
{
"", " sleeping, ", " running, ", " zombie, ", " stopped, ",
" starting, ", " on cpu, ", " swapped, ",
NULL
};
int cpu_states[CPUSTATES];
char *cpustatenames[] =
{"idle", "user", "kernel", "wait", "swap", NULL};
/* these are for detailing the memory statistics */
long memory_stats[5];
char *memorynames[] =
{"K real, ", "K active, ", "K free, ", "K swap, ", "K free swap", NULL};
/* forward reference for qsort comparison function */
int proc_compare();
static int kmem = -1;
static int nproc;
static int bytes;
static int use_stats = 0;
static struct prpsinfo *pbase;
static struct prpsinfo **pref;
static DIR *proc_dir;
/* useful externals */
extern int errno;
extern char *sys_errlist[];
extern char *myname;
extern int check_nlist();
extern int getkval();
extern void perror();
extern void getptable();
extern void quit();
extern int nlist();
int
machine_init(struct statics *statics)
{
static struct var v;
/* fill in the statics information */
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
/* get the list of symbols we want to access in the kernel */
if (nlist(UNIX, nlst))
{
(void) fprintf(stderr, "Unable to nlist %s\n", UNIX);
return (-1);
}
/* make sure they were all found */
if (check_nlist(nlst) > 0)
return (-1);
/* open kernel memory */
if ((kmem = open(KMEM, O_RDONLY)) == -1)
{
perror(KMEM);
return (-1);
}
/* get the symbol values out of kmem */
/* NPROC Tuning parameter for max number of processes */
(void) getkval(nlst[X_V].n_value, &v, sizeof(struct var), nlst[X_V].n_name);
nproc = v.v_proc;
/* stash away certain offsets for later use */
mpid_offset = nlst[X_MPID].n_value;
nproc_offset = nlst[X_NPROC].n_value;
avenrun_offset = nlst[X_AVENRUN].n_value;
anoninfo_offset = nlst[X_ANONINFO].n_value;
total_offset = nlst[X_TOTAL].n_value;
/* JJ this may need to be changed */
sysinfo_offset = nlst[X_SYSINFO].n_value;
/* allocate space for proc structure array and array of pointers */
bytes = nproc * sizeof(struct prpsinfo);
pbase = (struct prpsinfo *) malloc(bytes);
pref = (struct prpsinfo **) malloc(nproc * sizeof(struct prpsinfo *));
/* Just in case ... */
if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL)
{
(void) fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
return (-1);
}
if (!(proc_dir = opendir(PROCFS)))
{
(void) fprintf(stderr, "Unable to open %s\n", PROCFS);
return (-1);
}
if (chdir(PROCFS))
{ /* handy for later on when we're reading it */
(void) fprintf(stderr, "Unable to chdir to %s\n", PROCFS);
return (-1);
}
/* all done! */
return (0);
}
char *
format_header(char *uname_field)
{
register char *ptr;
ptr = header + UNAME_START;
while (*uname_field != '\0')
*ptr++ = *uname_field++;
return (header);
}
void
get_system_info(struct system_info *si)
{
long avenrun[3];
struct sysinfo sysinfo;
static struct sysinfo *mpinfo = NULL; /* array, per-processor sysinfo
* structures. */
struct vmtotal total;
struct anoninfo anoninfo;
static long cp_old[CPUSTATES];
static long cp_diff[CPUSTATES]; /* for cpu state percentages */
static int num_cpus;
static int fd_cpu = 0;
register int i;
if (use_stats == 1)
{
if (fd_cpu == 0)
{
if ((fd_cpu = open("/stats/cpuinfo", O_RDONLY)) == -1)
{
(void) fprintf(stderr, "%s: Open of /stats/cpuinfo failed\n", myname);
quit(2);
}
if (read(fd_cpu, &num_cpus, sizeof(int)) != sizeof(int))
{
(void) fprintf(stderr, "%s: Read of /stats/cpuinfo failed\n", myname);
quit(2);
}
close(fd_cpu);
}
if (mpinfo == NULL)
{
mpinfo = (struct sysinfo *) calloc(num_cpus, sizeof(mpinfo[0]));
if (mpinfo == NULL)
{
(void) fprintf(stderr, "%s: can't allocate space for per-processor sysinfos\n", myname);
quit(12);
}
}
/* Read the per cpu sysinfo structures into mpinfo struct. */
read_sysinfos(num_cpus, mpinfo);
/* Add up all of the percpu sysinfos to get global sysinfo */
sysinfo_data(num_cpus, &sysinfo, mpinfo);
}
else
{
(void) getkval(sysinfo_offset, &sysinfo, sizeof(struct sysinfo), "sysinfo");
}
/* convert cp_time counts to percentages */
(void) percentages(CPUSTATES, cpu_states, sysinfo.cpu, cp_old, cp_diff);
/* get mpid -- process id of last process */
(void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid),
"mpid");
/* get load average array */
(void) getkval(avenrun_offset, (int *) avenrun, sizeof(avenrun), "avenrun");
/* convert load averages to doubles */
for (i = 0; i < 3; i++)
si->load_avg[i] = loaddouble(avenrun[i]);
/* get total -- systemwide main memory usage structure */
(void) getkval(total_offset, (int *) (&total), sizeof(total), "total");
/* convert memory stats to Kbytes */
memory_stats[0] = pagetok(total.t_rm);
memory_stats[1] = pagetok(total.t_arm);
memory_stats[2] = pagetok(total.t_free);
(void) getkval(anoninfo_offset, (int *) (&anoninfo), sizeof(anoninfo),
"anoninfo");
memory_stats[3] = pagetok(anoninfo.ani_max - anoninfo.ani_free);
memory_stats[4] = pagetok(anoninfo.ani_max - anoninfo.ani_resv);
/* set arrays and strings */
si->cpustates = cpu_states;
si->memory = memory_stats;
}
static struct handle handle;
caddr_t
get_process_info(
struct system_info *si,
struct process_select *sel,
int x)
{
register int i;
register int total_procs;
register int active_procs;
register struct prpsinfo **prefp;
register struct prpsinfo *pp;
/* these are copied out of sel for speed */
int show_idle;
int show_system;
int show_uid;
/* Get current number of processes */
(void) getkval(nproc_offset, (int *) (&nproc), sizeof(nproc), "nproc");
/* read all the proc structures */
getptable(pbase);
/* get a pointer to the states summary array */
si->procstates = process_states;
/* set up flags which define what we are going to select */
show_idle = sel->idle;
show_system = sel->system;
show_uid = sel->uid != -1;
/* count up process states and get pointers to interesting procs */
total_procs = 0;
active_procs = 0;
(void) memset(process_states, 0, sizeof(process_states));
prefp = pref;
for (pp = pbase, i = 0; i < nproc; pp++, i++)
{
/*
* Place pointers to each valid proc structure in pref[]. Process
* slots that are actually in use have a non-zero status field.
* Processes with SSYS set are system processes---these get ignored
* unless show_sysprocs is set.
*/
if (pp->pr_state != 0 &&
(show_system || ((pp->pr_flag & SSYS) == 0)))
{
total_procs++;
process_states[pp->pr_state]++;
if ((!pp->pr_zomb) &&
(show_idle || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) &&
(!show_uid || pp->pr_uid == (uid_t) sel->uid))
{
*prefp++ = pp;
active_procs++;
}
}
}
/* if requested, sort the "interesting" processes */
qsort((char *) pref, active_procs, sizeof(struct prpsinfo *), proc_compare);
/* remember active and total counts */
si->p_total = total_procs;
si->p_active = active_procs;
/* pass back a handle */
handle.next_proc = pref;
handle.remaining = active_procs;
return ((caddr_t) & handle);
}
char fmt[MAX_COLS]; /* static area where result is built */
char *
format_next_process(
caddr_t handle,
char *(*get_userid) ())
{
register struct prpsinfo *pp;
struct handle *hp;
register long cputime;
register double pctcpu;
/* find and remember the next proc structure */
hp = (struct handle *) handle;
pp = *(hp->next_proc++);
hp->remaining--;
/* get the cpu usage and calculate the cpu percentages */
cputime = pp->pr_time.tv_sec;
pctcpu = percent_cpu(pp);
/* format this entry */
(void) sprintf(fmt,
Proc_format,
pp->pr_pid,
(*get_userid) (pp->pr_uid),
pp->pr_pri - PZERO,
pp->pr_nice - NZERO,
format_k(pagetok(pp->pr_size)),
format_k(pagetok(pp->pr_rssize)),
state_abbrev[pp->pr_state],
format_time(cputime),
(pp->pr_cpu & 0377),
100.0 * pctcpu,
printable(pp->pr_fname));
/* return the result */
return (fmt);
}
/*
* check_nlist(nlst) - checks the nlist to see if any symbols were not
* found. For every symbol that was not found, a one-line
* message is printed to stderr. The routine returns the
* number of symbols NOT found.
*/
int
check_nlist(register struct nlist *nlst)
{
register int i;
struct stat stat_buf;
/* check to see if we got ALL the symbols we requested */
/* this will write one line to stderr for every symbol not found */
i = 0;
while (nlst->n_name != NULL)
{
if (nlst->n_type == 0)
{
if (strcmp("sysinfo", nlst->n_name) == 0)
{
/* check to see if /stats file system exists. If so, */
/* ignore error. */
if (!((stat("/stats/sysinfo", &stat_buf) == 0) &&
(stat_buf.st_mode & S_IFREG)))
{
(void) fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
i = 1;
}
else
{
use_stats = 1;
}
}
else
{
/* this one wasn't found */
(void) fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
i = 1;
}
}
nlst++;
}
return (i);
}
/*
* getkval(offset, ptr, size, refstr) - get a value out of the kernel.
* "offset" is the byte offset into the kernel for the desired value,
* "ptr" points to a buffer into which the value is retrieved,
* "size" is the size of the buffer (and the object to retrieve),
* "refstr" is a reference string used when printing error meessages,
* if "refstr" starts with a '!', then a failure on read will not
* be fatal (this may seem like a silly way to do things, but I
* really didn't want the overhead of another argument).
*
*/
int
getkval(
unsigned long offset,
int *ptr,
int size,
char *refstr)
{
#ifdef MIPS
if (lseek(kmem, (long) (offset & 0x7fffffff), 0) == -1)
#else
if (lseek(kmem, (long) offset, 0) == -1)
#endif
{
if (*refstr == '!')
refstr++;
(void) fprintf(stderr, "%s: lseek to %s: %s\n",
myname, refstr, sys_errlist[errno]);
quit(22);
}
if (read(kmem, (char *) ptr, size) == -1)
if (*refstr == '!')
/* we lost the race with the kernel, process isn't in memory */
return (0);
else
{
(void) fprintf(stderr, "%s: reading %s: %s\n",
myname, refstr, sys_errlist[errno]);
quit(23);
}
return (1);
}
/* comparison routine for qsort */
/*
* proc_compare - comparison function for "qsort"
* Compares the resource consumption of two processes using five
* distinct keys. The keys (in descending order of importance) are:
* percent cpu, cpu ticks, state, resident set size, total virtual
* memory usage. The process states are ordered as follows (from least
* to most important): WAIT, zombie, sleep, stop, start, run. The
* array declaration below maps a process state index into a number
* that reflects this ordering.
*/
unsigned char sorted_state[] =
{
0, /* not used */
3, /* sleep */
6, /* run */
2, /* zombie */
4, /* stop */
5, /* start */
7, /* run on a processor */
1 /* being swapped (WAIT) */
};
int
proc_compare(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/* compare percent cpu (pctcpu) */
if ((result = (long) (p2->pr_cpu - p1->pr_cpu)) == 0)
{
/* use cpticks to break the tie */
if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
{
/* use process state to break the tie */
if ((result = (long) (sorted_state[p2->pr_state] -
sorted_state[p1->pr_state])) == 0)
{
/* use priority to break the tie */
if ((result = p2->pr_oldpri - p1->pr_oldpri) == 0)
{
/* use resident set size (rssize) to break the tie */
if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
{
/* use total memory to break the tie */
result = (p2->pr_size - p1->pr_size);
}
}
}
}
}
return (result);
}
/*
get process table
*/
void
getptable(struct prpsinfo *baseptr)
{
struct prpsinfo *currproc; /* pointer to current proc structure */
int numprocs = 0;
struct dirent *direntp;
for (rewinddir(proc_dir); direntp = readdir(proc_dir);)
{
int fd;
if ((fd = open(direntp->d_name, O_RDONLY)) < 0)
continue;
currproc = &baseptr[numprocs];
if (ioctl(fd, PIOCPSINFO, currproc) < 0)
{
(void) close(fd);
continue;
}
numprocs++;
(void) close(fd);
}
if (nproc != numprocs)
nproc = numprocs;
}
/* return the owner of the specified process, for use in commands.c as we're
running setuid root */
int
proc_owner(int pid)
{
register struct prpsinfo *p;
int i;
for (i = 0, p = pbase; i < nproc; i++, p++)
if (p->pr_pid == (pid_t) pid)
return (p->pr_uid);
return (-1);
}
#ifndef HAVE_SETPRIORITY
int
setpriority(int dummy, int who, int niceval)
{
int scale;
int prio;
pcinfo_t pcinfo;
pcparms_t pcparms;
tsparms_t *tsparms;
strcpy(pcinfo.pc_clname, "TS");
if (priocntl(0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
return (-1);
prio = niceval;
if (prio > PRIO_MAX)
prio = PRIO_MAX;
else if (prio < PRIO_MIN)
prio = PRIO_MIN;
tsparms = (tsparms_t *) pcparms.pc_clparms;
scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
pcparms.pc_cid = pcinfo.pc_cid;
if (priocntl(P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
return (-1);
return (0);
}
#endif
/****************************************************************
* read_sysinfos() - *
* Read all of the CPU specific sysinfo sturctures in from *
* the /stats file system. *
****************************************************************/
read_sysinfos(num_cpus, buf)
int num_cpus;
struct sysinfo *buf;
{
static int fd1 = 0; /* file descriptor for /stats/sysinfo */
int read_sz;
/* Open /stats/sysinfo one time only and leave it open */
if (fd1 == 0)
{
if ((fd1 = open("/stats/sysinfo", O_RDONLY)) == -1)
(void) fprintf(stderr, "%s: Open of /stats/sysinfo failed\n", myname);
}
/* reset the read pointer to the beginning of the file */
if (lseek(fd1, 0L, SEEK_SET) == -1)
(void) fprintf(stderr, "%s: lseek to beginning of /stats/sysinfo failed\n", myname);
read_sz = num_cpus * sizeof(buf[0]);
if (read(fd1, buf, read_sz) != read_sz)
(void) fprintf(stderr, "%s: Read of /stats/sysinfo failed\n", myname);
}
/****************************************************************
* sysinfo_data() - *
* Add up all of the CPU specific sysinfo sturctures to *
* make the GLOBAL sysinfo. *
****************************************************************/
sysinfo_data(num_cpus, global_si, percpu_si)
int num_cpus;
struct sysinfo *global_si;
struct sysinfo *percpu_si;
{
struct sysinfo *percpu_p;
int cpu,
i,
*global,
*src;
/* null out the global statistics from last sample */
memset(global_si, 0, sizeof(struct sysinfo));
percpu_p = (struct sysinfo *) percpu_si;
for (cpu = 0; cpu < num_cpus; cpu++)
{
global = (int *) global_si;
src = (int *) percpu_p;
/* assume sysinfo ends on an int boundary */
/*
* Currently, all of the struct sysinfo members are the same size as
* an int. If that changes, we may not be able to do this. But this
* should be safe.
*/
for (i = 0; i < sizeof(struct sysinfo) / sizeof(int); i++)
{
*global +++= *src++;
}
percpu_p++;
}
}
void
get_io_info(struct io_info *io_info)
{
/* Not supported yet */
memset(io_info, 0, sizeof(*io_info));
}
07070100000034000081A400000000000000000000000166622DE800000150000000000000000000000000000000000000002400000000pg_top-4.1.1/machine/m_svr4.man.rstSVR4 CREDITS
============
The SVR4 port was initially written by Andrew Herbert. He was guided by a SVR4
port of top version 2.1 which was done by Andy Crump (andyc@bucky.intel.com).
Robert Boucher (boucher@sofkin.ca) adapted it to top version 3.1.
Ported to System 3000 Release 2.03 by
Jeff Janvrin (jeff.janvrinColumbiaSC.NCR.COM)
07070100000035000081A400000000000000000000000166622DE800007E92000000000000000000000000000000000000001E00000000pg_top-4.1.1/machine/m_svr5.c/*
* pg_top - a top PostgreSQL users display for Unix
*
* SYNOPSIS: For Intel based System V Release 5 (Unixware7)
*
* DESCRIPTION:
* System V release 5 for i[3456]86
* Works for:
* i586-sco-sysv5uw7 i386 SCO UNIX_SVR5 (UnixWare 7)
*
* LIBS: -lelf -lmas
*
* CFLAGS: -DHAVE_GETOPT -DORDER
*
* AUTHORS: Mike Hopkirk <hops@sco.com>
* David Cutter <dpc@grail.com>
* Andrew Herbert <andrew@werple.apana.org.au>
* Robert Boucher <boucher@sofkin.ca>
*/
/* build config
* SHOW_NICE - process nice fields don't seem to be being updated so changed
* default to display # of threads in use instead.
* define this to display nice fields (values always 0)
* #define SHOW_NICE 1
*/
#define _KMEMUSER
#define prpsinfo psinfo
#include <sys/procfs.h>
#define pr_state pr_lwp.pr_state
#define pr_nice pr_lwp.pr_nice
#define pr_pri pr_lwp.pr_pri
#define pr_onpro pr_lwp.pr_onpro
#define ZOMBIE(p) ((p)->pr_nlwp == 0)
#define SIZE_K(p) pagetok((p)->pr_size)
#define RSS_K(p) pagetok((p)->pr_rssize)
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <dirent.h>
#include <nlist.h>
#include <string.h>
#include <sys/types.h>
#include <sys/param.h>
#include <sys/proc.h>
#include <sys/sysmacros.h>
#include <vm/anon.h>
#include <sys/priocntl.h>
#include <sys/tspriocntl.h>
#include <sys/var.h>
#include "pg_top.h"
#include "machine.h"
#include "utils.h"
#define UNIX "/stand/unix"
#define KMEM "/dev/kmem"
#define PROCFS "/proc"
#define CPUSTATES 5
#ifndef PRIO_MAX
#define PRIO_MAX 20
#endif
#ifndef PRIO_MIN
#define PRIO_MIN -20
#endif
#ifndef FSCALE
#define FSHIFT 8 /* bits to right of fixed binary point */
#define FSCALE (1<<FSHIFT)
#endif
#define loaddouble(x) ((double)x/FSCALE)
#define pagetok(size) ((size) * pagesz) >> LOG1024
/* definitions for the index in the nlist array */
#define X_AVENRUN 0
#define X_V 1
#define X_MPID 2
static struct nlist nlst[] =
{
{"avenrun"}, /* 0 */
{"v"}, /* 1 */
{"nextpid"}, /* 2 */
{NULL}
};
static unsigned long avenrun_offset;
static unsigned long mpid_offset;
static unsigned int pagesz;
static void reallocproc(int n);
static int maxprocs;
/* get_process_info passes back a handle. This is what it looks like: */
struct handle
{
struct prpsinfo **next_proc; /* points to next valid proc pointer */
int remaining; /* number of pointers remaining */
};
/*
* These definitions control the format of the per-process area
*/
static char header[] =
#ifdef SHOW_NICE
" PID X PRI NICE SIZE RES STATE TIME CPU COMMAND";
#else
" PID X PRI THR SIZE RES STATE TIME CPU COMMAND";
#endif
/* 0123456 -- field to fill in starts at header+6 */
#define UNAME_START 6
#define Proc_format \
"%5d %-8.8s %3d %4d %5s %5s %-5s %6s %8.4f%% %.16s"
char *state_abbrev[] =
{"oncpu", "run", "sleep", "stop", "idle", "zombie"};
#define sZOMB 5
int process_states[8];
char *procstatenames[] =
{
" on cpu, ", " running, ", " sleeping, ", " stopped, ",
" idling ", " zombie, ",
NULL
};
int cpu_states[CPUSTATES];
char *cpustatenames[] =
{"idle", "user", "kernel", "wait", NULL};
/* these are for detailing the memory statistics */
long memory_stats[5];
char *memorynames[] =
{"K phys, ", "K used, ", "K free, ", "K swapUsed, ", "K swapFree", NULL};
/* these are names given to allowed sorting orders -- first is default */
char *ordernames[] = {
"state", "cpu", "size", "res", "time", "pid", "uid", "rpid", "ruid", NULL
};
/* forward definitions for comparison functions */
int proc_compare();
int compare_cpu();
int compare_size();
int compare_res();
int compare_time();
int compare_pid();
int compare_uid();
int compare_rpid();
int compare_ruid();
int (*proc_compares[]) () =
{
proc_compare,
compare_cpu,
compare_size,
compare_res,
compare_time,
compare_pid,
compare_uid,
compare_rpid,
compare_ruid,
NULL
};
static int kmem = -1;
static int nproc;
static int bytes;
static struct prpsinfo *pbase;
static struct prpsinfo **pref;
static DIR *procdir;
/* useful externals */
extern int errno;
extern char *sys_errlist[];
extern char *myname;
extern long percentages();
extern int check_nlist();
extern int getkval();
extern void perror();
extern void getptable();
extern void quit();
extern int nlist();
/* fwd dcls */
static int kmet_init(void);
static int get_cpustates(int *new);
int
machine_init(struct statics *statics)
{
static struct var v;
int i;
/* fill in the statics information */
statics->procstate_names = procstatenames;
statics->cpustate_names = cpustatenames;
statics->memory_names = memorynames;
statics->order_names = ordernames;
/* get the list of symbols we want to access in the kernel */
if (nlist(UNIX, nlst))
{
(void) fprintf(stderr, "Unable to nlist %s\n", UNIX);
return (-1);
}
/* make sure they were all found */
if (check_nlist(nlst) > 0)
return (-1);
/* open kernel memory */
if ((kmem = open(KMEM, O_RDONLY)) == -1)
{
perror(KMEM);
return (-1);
}
v.v_proc = 200; /* arbitrary default */
/* get the symbol values out of kmem */
/* NPROC Tuning parameter for max number of processes */
(void) getkval(nlst[X_V].n_value, &v, sizeof(struct var), nlst[X_V].n_name);
nproc = v.v_proc;
maxprocs = nproc;
/* stash away certain offsets for later use */
mpid_offset = nlst[X_MPID].n_value;
avenrun_offset = nlst[X_AVENRUN].n_value;
/* allocate space for proc structure array and array of pointers */
bytes = nproc * sizeof(struct prpsinfo);
pbase = (struct prpsinfo *) malloc(bytes);
pref = (struct prpsinfo **) malloc(nproc * sizeof(struct prpsinfo *));
pagesz = sysconf(_SC_PAGESIZE);
/* Just in case ... */
if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL)
{
(void) fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
return (-1);
}
if (!(procdir = opendir(PROCFS)))
{
(void) fprintf(stderr, "Unable to open %s\n", PROCFS);
return (-1);
}
if (chdir(PROCFS))
{ /* handy for later on when we're reading it */
(void) fprintf(stderr, "Unable to chdir to %s\n", PROCFS);
return (-1);
}
kmet_init();
/* all done! */
return (0);
}
char *
format_header(char *uname_field)
{
register char *ptr;
ptr = header + UNAME_START;
while (*uname_field != '\0')
*ptr++ = *uname_field++;
return (header);
}
void
get_system_info(struct system_info *si)
{
long avenrun[3];
long mem;
static time_t cp_old[CPUSTATES];
static time_t cp_diff[CPUSTATES]; /* for cpu state percentages */
register int i;
static long swap_total;
static long swap_free;
int new_states[CPUSTATES];
get_cpustates(new_states);
/* convert cp_time counts to percentages */
(void) percentages(CPUSTATES, cpu_states, new_states, cp_old, cp_diff);
si->last_pid = -1;
/*
* get mpid -- process id of last process svr5 is nextpid - next pid to be
* assigned (already incremented)
*/
(void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid),
"nextpid");
(si->last_pid)--; /* so we shld decrement for display */
/* get load average array */
(void) getkval(avenrun_offset, (int *) avenrun, sizeof(avenrun), "avenrun");
/* convert load averages to doubles */
for (i = 0; i < 3; i++)
si->load_avg[i] = loaddouble(avenrun[i]);
mem = sysconf(_SC_TOTAL_MEMORY); /* physical mem */
memory_stats[0] = pagetok(mem);
mem = kmet_get_freemem(); /* free mem */
memory_stats[2] = pagetok(mem);
/* mem = sysconf(_SC_GENERAL_MEMORY); */
memory_stats[1] = memory_stats[0] - memory_stats[2]; /* active */
get_swapinfo(&swap_total, &swap_free);
memory_stats[3] = pagetok(swap_total - swap_free);
memory_stats[4] = pagetok(swap_free);
/* set arrays and strings */
si->cpustates = cpu_states;
si->memory = memory_stats;
}
static struct handle handle;
caddr_t
get_process_info(
struct system_info *si,
struct process_select *sel,
int idx)
{
register int i;
register int total_procs;
register int active_procs;
register struct prpsinfo **prefp;
register struct prpsinfo *pp;
/* these are copied out of sel for speed */
int show_idle;
int show_system;
int show_uid;
/* Get current number of processes */
/* read all the proc structures */
getptable(pbase);
/* get a pointer to the states summary array */
si->procstates = process_states;
/* set up flags which define what we are going to select */
show_idle = sel->idle;
show_system = sel->system;
show_uid = sel->uid != -1;
nproc = kmet_get_nproc();
/* count up process states and get pointers to interesting procs */
total_procs = 0;
active_procs = 0;
(void) memset(process_states, 0, sizeof(process_states));
prefp = pref;
for (pp = pbase, i = 0; i < nproc; pp++, i++)
{
/*
* Place pointers to each valid proc structure in pref[]. Process
* slots that are actually in use have a non-zero status field.
* Processes with PR_ISSYS set are system processes---these get
* ignored unless show_sysprocs is set.
*/
if ((pp->pr_state >= SONPROC && pp->pr_state <= SIDL) &&
(show_system || ((pp->pr_flag & PR_ISSYS) == 0)))
{
total_procs++;
process_states[pp->pr_state]++;
if ((!ZOMBIE(pp)) &&
(show_idle || (pp->pr_state == SRUN) || (pp->pr_state == SONPROC)) &&
(!show_uid || pp->pr_uid == (uid_t) sel->uid))
{
*prefp++ = pp;
active_procs++;
}
if (ZOMBIE(pp))
process_states[sZOMB]++; /* invented */
}
}
/* if requested, sort the "interesting" processes */
qsort((char *) pref, active_procs, sizeof(struct prpsinfo *),
proc_compares[idx]);
/* remember active and total counts */
si->p_total = total_procs;
si->P_ACTIVE = active_procs;
/* pass back a handle */
handle.next_proc = pref;
handle.remaining = active_procs;
return ((caddr_t) & handle);
}
/*
* cpu percentage calculation is as fm ps.c
* seems to be ratio of (sys+user time used)/(elapsed time)
* i.e percent of cpu utilised when on cpu
*/
static double
percent_cpu(struct prpsinfo *pp)
{
static time_t tim = 0L;
time_t starttime;
time_t ctime;
time_t etime;
/* if (tim == 0L) */
tim = time((time_t *) 0);
starttime = pp->pr_start.tv_sec;
if (pp->pr_start.tv_nsec > 500000000)
starttime++;
etime = (tim - starttime);
ctime = pp->pr_time.tv_sec;
if (pp->pr_time.tv_nsec > 500000000)
ctime++;
if (etime)
{
/* return (float)(ctime * 100) / (unsigned)etime; */
/*
* this was ocasionally giving vals >100 for some unknown reason so
* the below normalises it
*/
double pct;
pct = (float) (ctime * 100) / (unsigned) etime;
return (pct < 100.0) ? pct : 100.00;
}
return 0.00;
}
char fmt[MAX_COLS]; /* static area where result is built */
char *
format_next_process(
caddr_t handle,
char *(*get_userid) ())
{
register struct prpsinfo *pp;
struct handle *hp;
register long cputime;
register double pctcpu;
/* find and remember the next proc structure */
hp = (struct handle *) handle;
pp = *(hp->next_proc++);
hp->remaining--;
/* get the cpu usage and calculate the cpu percentages */
cputime = pp->pr_time.tv_sec;
pctcpu = percent_cpu(pp);
/* format this entry */
(void) sprintf(fmt,
Proc_format,
pp->pr_pid,
(*get_userid) (pp->pr_uid),
pp->pr_pri,
#ifdef SHOW_NICE
pp->pr_nice,
#else
(u_short) pp->pr_nlwp < 999 ? (u_short) pp->pr_nlwp : 999,
#endif
format_k(SIZE_K(pp)),
format_k(RSS_K(pp)),
(ZOMBIE(pp)) ? state_abbrev[sZOMB]
: state_abbrev[pp->pr_state],
format_time(cputime),
/* 100.0 * */ pctcpu,
printable(pp->pr_fname));
/* return the result */
return (fmt);
}
/*
* check_nlist(nlst) - checks the nlist to see if any symbols were not
* found. For every symbol that was not found, a one-line
* message is printed to stderr. The routine returns the
* number of symbols NOT found.
*/
int
check_nlist(register struct nlist *nlst)
{
register int i;
/* check to see if we got ALL the symbols we requested */
/* this will write one line to stderr for every symbol not found */
i = 0;
while (nlst->n_name != NULL)
{
if (nlst->n_value == 0)
{
/* this one wasn't found */
(void) fprintf(stderr, "kernel: no symbol named `%s'\n", nlst->n_name);
i = 1;
}
nlst++;
}
return (i);
}
/*
* getkval(offset, ptr, size, refstr) - get a value out of the kernel.
* "offset" is the byte offset into the kernel for the desired value,
* "ptr" points to a buffer into which the value is retrieved,
* "size" is the size of the buffer (and the object to retrieve),
* "refstr" is a reference string used when printing error meessages,
* if "refstr" starts with a '!', then a failure on read will not
* be fatal (this may seem like a silly way to do things, but I
* really didn't want the overhead of another argument).
*
*/
int
getkval(
unsigned long offset,
int *ptr,
int size,
char *refstr)
{
if (lseek(kmem, (long) offset, 0) == -1)
{
if (*refstr == '!')
refstr++;
(void) fprintf(stderr, "%s: lseek to %s: %s\n",
myname, refstr, sys_errlist[errno]);
quit(22);
}
if (read(kmem, (char *) ptr, size) == -1)
if (*refstr == '!')
/* we lost the race with the kernel, process isn't in memory */
return (0);
else
{
(void) fprintf(stderr, "%s: reading %s: %s\n",
myname, refstr, sys_errlist[errno]);
quit(23);
}
return (1);
}
/* ----------------- comparison routines for qsort ---------------- */
/* First, the possible comparison keys. These are defined in such a way
that they can be merely listed in the source code to define the actual
desired ordering.
*/
#define ORDERKEY_PCTCPU if (dresult = percent_cpu (p2) - percent_cpu (p1),\
(result = dresult > 0.0 ? 1 : \
dresult < 0.0 ? -1 : 0) == 0)
#define ORDERKEY_CPTICKS if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
#define ORDERKEY_STATE if ((result = (long) (sorted_state[p2->pr_state] - \
sorted_state[p1->pr_state])) == 0)
#define ORDERKEY_PRIO if ((result = p2->pr_pri - p1->pr_pri) == 0)
#define ORDERKEY_RSSIZE if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
#define ORDERKEY_MEM if ((result = (p2->pr_size - p1->pr_size)) == 0)
#define ORDERKEY_PID if ((result = (p2->pr_pid - p1->pr_pid)) == 0)
#define ORDERKEY_UID if ((result = (p2->pr_uid - p1->pr_uid)) == 0)
#define ORDERKEY_RPID if ((result = (p1->pr_pid - p2->pr_pid)) == 0)
#define ORDERKEY_RUID if ((result = (p1->pr_uid - p2->pr_uid)) == 0)
/* states enum {SONPROC, SRUN, SSLEEP, SSTOP, SIDL} */
unsigned char sorted_state[] =
{
7, /* onproc */
6, /* run */
5, /* sleep */
4, /* stop */
3, /* idle */
2, /* zombie */
0, /* unused */
0 /* unused */
};
#if 0
/*
* proc_compare - original singleton comparison function for "qsort"
* Compares the resource consumption of two processes using five
* distinct keys. The keys (in descending order of importance) are:
* percent cpu, cpu ticks, state, resident set size, total virtual
* memory usage. The process states are ordered as follows (from least
* to most important): WAIT, zombie, sleep, stop, start, run. The
* array declaration below maps a process state index into a number
* that reflects this ordering.
*/
/* default comparison rtn */
int
original_proc_compare(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
/* compare percent cpu (pctcpu) */
dresult = percent_cpu(p2) - percent_cpu(p1);
result = dresult > 0.0 ? 1 :
dresult < 0.0 ? -1 : 0;
if (result)
{
/* use cpticks to break the tie */
if ((result = p2->pr_time.tv_sec - p1->pr_time.tv_sec) == 0)
{
/* use process state to break the tie */
if ((result = (long) (sorted_state[p2->pr_state] -
sorted_state[p1->pr_state])) == 0)
{
/* use priority to break the tie */
if ((result = p2->pr_pri - p1->pr_pri) == 0)
{
/* use resident set size (rssize) to break the tie */
if ((result = p2->pr_rssize - p1->pr_rssize) == 0)
{
/* use total memory to break the tie */
result = (p2->pr_size - p1->pr_size);
}
}
}
}
}
return (result);
}
#endif /* original comparison rtn */
/* compare_state - comparison function for sorting by state,pri,time,size */
int
proc_compare(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_CPTICKS
ORDERKEY_RSSIZE
ORDERKEY_MEM
ORDERKEY_PCTCPU
;
return (result);
}
/* compare_cpu - the comparison function for sorting by cpu % (deflt) */
int
compare_cpu(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_RSSIZE
ORDERKEY_MEM
;
return (result);
}
/* compare_size - the comparison function for sorting by total memory usage */
int
compare_size(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_MEM
ORDERKEY_RSSIZE
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
/* compare_res - the comparison function for sorting by resident set size */
int
compare_res(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_RSSIZE
ORDERKEY_MEM
ORDERKEY_PCTCPU
ORDERKEY_CPTICKS
ORDERKEY_STATE
ORDERKEY_PRIO
;
return (result);
}
/* compare_time - the comparison function for sorting by total cpu time */
int
compare_time(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_CPTICKS
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
/* compare_pid - the comparison function for sorting by pid */
int
compare_pid(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_PID
ORDERKEY_CPTICKS
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
/* compare_uid - the comparison function for sorting by user ID */
int
compare_uid(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_UID
ORDERKEY_CPTICKS
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
/* compare_rpid - the comparison function for sorting by pid ascending */
int
compare_rpid(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_RPID
ORDERKEY_CPTICKS
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
/* compare_uid - the comparison function for sorting by user ID ascending */
int
compare_ruid(
struct prpsinfo **pp1,
struct prpsinfo **pp2)
{
register struct prpsinfo *p1;
register struct prpsinfo *p2;
register long result;
double dresult;
/* remove one level of indirection */
p1 = *pp1;
p2 = *pp2;
ORDERKEY_RUID
ORDERKEY_CPTICKS
ORDERKEY_PCTCPU
ORDERKEY_STATE
ORDERKEY_PRIO
ORDERKEY_MEM
ORDERKEY_RSSIZE
;
return (result);
}
/* ---------------- helper rtns ---------------- */
/*
* get process table
*/
void
getptable(struct prpsinfo *baseptr)
{
struct prpsinfo *currproc; /* pointer to current proc structure */
int numprocs = 0;
struct dirent *direntp;
currproc = baseptr;
for (rewinddir(procdir); direntp = readdir(procdir);)
{
int fd;
char buf[30];
sprintf(buf, "%s/psinfo", direntp->d_name);
if ((fd = open(buf, O_RDONLY)) < 0)
continue;
if (read(fd, currproc, sizeof(psinfo_t)) != sizeof(psinfo_t))
{
(void) close(fd);
continue;
}
numprocs++;
currproc++;
(void) close(fd);
/* Atypical place for growth */
if (numprocs >= maxprocs)
{
reallocproc(2 * numprocs);
currproc = (struct prpsinfo *)
((char *) baseptr + sizeof(psinfo_t) * numprocs);
}
}
if (nproc != numprocs)
nproc = numprocs;
}
/* return the owner of the specified process, for use in commands.c as we're
running setuid root */
int
proc_owner(int pid)
{
register struct prpsinfo *p;
int i;
for (i = 0, p = pbase; i < nproc; i++, p++)
if (p->pr_pid == (pid_t) pid)
return ((int) (p->pr_uid));
return (-1);
}
int
setpriority(int dummy, int who, int niceval)
{
int scale;
int prio;
pcinfo_t pcinfo;
pcparms_t pcparms;
tsparms_t *tsparms;
strcpy(pcinfo.pc_clname, "TS");
if (priocntl(0, 0, PC_GETCID, (caddr_t) & pcinfo) == -1)
return (-1);
prio = niceval;
if (prio > PRIO_MAX)
prio = PRIO_MAX;
else if (prio < PRIO_MIN)
prio = PRIO_MIN;
tsparms = (tsparms_t *) pcparms.pc_clparms;
scale = ((tsinfo_t *) pcinfo.pc_clinfo)->ts_maxupri;
tsparms->ts_uprilim = tsparms->ts_upri = -(scale * prio) / 20;
pcparms.pc_cid = pcinfo.pc_cid;
if (priocntl(P_PID, who, PC_SETPARMS, (caddr_t) & pcparms) == -1)
return (-1);
return (0);
}
get_swapinfo(long *total, long *fr)
{
register int cnt,
i;
register long t,
f;
struct swaptable *swt;
struct swapent *ste;
static char path[256];
/* get total number of swap entries */
cnt = swapctl(SC_GETNSWP, 0);
/* allocate enough space to hold count + n swapents */
swt = (struct swaptable *) malloc(sizeof(int) +
cnt * sizeof(struct swapent));
if (swt == NULL)
{
*total = 0;
*fr = 0;
return;
}
swt->swt_n = cnt;
/*
* fill in ste_path pointers: we don't care about the paths, so we point
* them all to the same buffer
*/
ste = &(swt->swt_ent[0]);
i = cnt;
while (--i >= 0)
{
ste++->ste_path = path;
}
/* grab all swap info */
swapctl(SC_LIST, swt);
/* walk thru the structs and sum up the fields */
t = f = 0;
ste = &(swt->swt_ent[0]);
i = cnt;
while (--i >= 0)
{
/* dont count slots being deleted */
if (!(ste->ste_flags & ST_INDEL))
{
t += ste->ste_pages;
f += ste->ste_free;
}
ste++;
}
/* fill in the results */
*total = t;
*fr = f;
free(swt);
}
/*
* When we reach a proc limit, we need to realloc the stuff.
*/
static void
reallocproc(int n)
{
int bytes;
struct oldproc *op,
*endbase;
if (n < maxprocs)
return;
maxprocs = n;
/* allocate space for proc structure array and array of pointers */
bytes = maxprocs * sizeof(psinfo_t);
pbase = (struct prpsinfo *) realloc(pbase, bytes);
pref = (struct prpsinfo **) realloc(pref,
maxprocs * sizeof(struct prpsinfo *));
/* Just in case ... */
if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL)
{
fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
quit(1);
}
}
/* ---------------------------------------------------------------- */
/* Access kernel Metrics
* SVR5 uses metreg inteface to Kernel statistics (metrics)
* see /usr/include/mas.h, /usr/include/metreg.h
*/
#include <sys/mman.h>
#include <sys/dl.h>
#include <mas.h>
#include <metreg.h>
static int md; /* metric descriptor handle */
static uint32 ncpu; /* number of processors in system */
/* fwd dcls */
static uint32 kmet_get_cpu(int type, char *desc);
static void kmet_verify(
uint32 md, metid_t id, units_t units, type_t mettype,
uint32 metsz, uint32 nobj, uint32 nlocs, resource_t res_id,
uint32 ressz);
static int
get_cpustates(int *new)
{
new[0] = (int) kmet_get_cpu(MPC_CPU_IDLE, "idle");
new[1] = (int) kmet_get_cpu(MPC_CPU_USR, "usr");
new[2] = (int) kmet_get_cpu(MPC_CPU_SYS, "sys");
new[3] = (int) kmet_get_cpu(MPC_CPU_WIO, "wio");
}
/* initialises kernel metrics access and gets #cpus */
static int
kmet_init()
{
uint32 *ncpu_p;
/* open (and map in) the metric access file and assoc data structures */
if ((md = mas_open(MAS_FILE, MAS_MMAP_ACCESS)) < 0)
{
(void) fprintf(stderr, "mas_open failed\n");
mas_perror();
quit(10);
}
/* verify the NCPU metric is everything we expect */
kmet_verify(md, NCPU, CPUS, CONFIGURABLE, sizeof(short),
1, 1, MAS_SYSTEM, sizeof(uint32));
/* get the number of cpu's on the system */
if ((ncpu_p = (uint32 *) mas_get_met(md, NCPU, 0)) == NULL)
{
(void) fprintf(stderr, "mas_get_met of ncpu failed\n");
mas_perror();
quit(12);
}
ncpu = (uint32) (*(short *) ncpu_p);
/*
* check that MPC_CPU_IDLE is of the form we expect ( paranoically we
* should check the rest as well but ... )
*/
kmet_verify(md, MPC_CPU_IDLE, TIX, PROFILE, sizeof(uint32),
1, ncpu, NCPU, sizeof(short));
kmet_verify(md, PROCUSE, PROCESSES, COUNT, sizeof(uint32),
1, 1, MAS_SYSTEM, sizeof(uint32));
nproc = kmet_get_nproc();
return 0;
}
/* done with kernel metrics access */
static int
kmet_done()
{
if (mas_close(md) < 0)
{
(void) fprintf(stderr, "mas_close failed\n");
mas_perror();
quit(14);
}
}
static uint32
kmet_get_cpu(int type, char *desc)
{
int i;
uint32 r = 0,
rtot = 0;
for (i = 0; i < ncpu; i++)
{
r = *(uint32 *) mas_get_met(md, (metid_t) type, 0);
if (!r)
{
(void) fprintf(stderr, "mas_get_met of %s failed\n", desc);
mas_perror();
quit(12);
}
rtot += r; /* sum them for multi cpus */
}
return rtot /* /ncpu */ ;
}
static int
kmet_get_freemem()
{
dl_t *fm_p,
fm,
fmc,
denom;
time_t td1;
static time_t td0;
static dl_t fm_old;
td1 = time(NULL);
if ((fm_p = (dl_t *) mas_get_met(md, FREEMEM, 0)) == NULL)
{
(void) fprintf(stderr, "mas_get_met of freemem failed\n");
mas_perror();
quit(12);
}
fm = *fm_p;
denom.dl_hop = 0;
denom.dl_lop = (long) (td1 - td0);
td0 = td1;
/*
* calculate the freemem difference divided by the time diff giving the
* freemem in that time sample (new - old) / (time_between_samples)
*/
fmc = lsub(fm, fm_old);
fm_old = fm;
fmc = ldivide(fmc, denom);
return fmc.dl_lop;
}
/*
* return # of processes currently executing on system
*/
static int
kmet_get_nproc()
{
uint32 *p;
if ((p = (uint32 *) mas_get_met(md, PROCUSE, 0)) == NULL)
{
(void) fprintf(stderr, "mas_get_met of procuse failed\n");
mas_perror();
quit(11);
}
nproc = (int) *p;
}
/*
* Function: kmet_verify
* renamed from mas_usrtime example verify_met() fm Doug Souders
*
* Description: Verify the registration data associated with this metric
* match what are expected. Cautious consumer applications
* should do this sort of verification before using metrics.
*/
static void
kmet_verify(
uint32 md, /* metric descriptor */
metid_t id, /* metric id number */
units_t units, /* expected units of metric */
type_t mettype, /* expected type of metric */
uint32 metsz, /* expected object size of metric */
uint32 nobj, /* expected number of array elements */
uint32 nlocs, /* expected number of instances */
resource_t res_id, /* expected resource id number */
uint32 ressz /* expected resource object size */
)
{
char *name; /* the name of the metric */
units_t *units_p; /* the units of the metric */
type_t *mettype_p; /* type field of the metric */
uint32 *objsz_p; /* size of each element in met */
uint32 *nobj_p; /* num of elements >1 then array */
uint32 *nlocs_p; /* total number of instances */
uint32 *status_p; /* status word (update|avail) */
resource_t *resource_p; /* the resource list of the met */
uint32 *resval_p; /* pointer to resource */
uint32 *ressz_p; /* size of the resource met */
if (!(name = mas_get_met_name(md, id)))
{
(void) fprintf(stderr, "mas_get_met_name failed\n");
mas_perror();
quit(11);
}
if (!(status_p = mas_get_met_status(md, id)))
{
(void) fprintf(stderr, "mas_get_met_status of %s failed\n",
name);
mas_perror();
quit(11);
}
if (*status_p != MAS_AVAILABLE)
{
(void) fprintf(stderr, "unexpected status word for %s\n"
"- expected %u got %u\n",
name, MAS_AVAILABLE, *status_p);
quit(11);
}
if (!(units_p = mas_get_met_units(md, id)))
{
(void) fprintf(stderr, "mas_get_met_units of %s failed\n",
name);
mas_perror();
quit(11);
}
if (units != *units_p)
{
(void) fprintf(stderr, "unexpected units for %s\n"
"- expected %u got %u\n",
name, units, *units_p);
quit(11);
}
if (!(mettype_p = mas_get_met_type(md, id)))
{
(void) fprintf(stderr, "mas_get_met_type of %s failed\n",
name);
mas_perror();
quit(11);
}
if (mettype != *mettype_p)
{
(void) fprintf(stderr, "unexpected metric type for %s\n"
"- expected %u got %u\n",
name, mettype, *mettype_p);
quit(11);
}
if (!(objsz_p = mas_get_met_objsz(md, id)))
{
(void) fprintf(stderr, "mas_get_met_objsz of %s failed\n", name);
mas_perror();
quit(11);
}
if (*objsz_p != metsz)
{
(void) fprintf(stderr, "unexpected object size for %s\n"
"- expected %u got %u\n",
name, metsz, *objsz_p);
quit(11);
}
if (!(nobj_p = mas_get_met_nobj(md, id)))
{
(void) fprintf(stderr, "mas_get_met_nobj of %s failed\n", name);
mas_perror();
quit(11);
}
if (nobj != *nobj_p)
{
(void) fprintf(stderr, "unexpected number of objects for %s\n"
"- expected %u got %u\n",
name, nobj, *nobj_p);
quit(11);
}
/* get the number of instances that libmas thinks it knows about */
if (!(nlocs_p = mas_get_met_nlocs(md, id)))
{
(void) fprintf(stderr, "mas_get_met_nlocs of %s failed\n", name);
mas_perror();
quit(11);
}
if (nlocs != *nlocs_p)
{
(void) fprintf(stderr, "unexpected number of instances for %s"
" - expected %u got %u\n",
name, nlocs, *nlocs_p);
quit(11);
}
/* get the resource list for the metric */
if (!(resource_p = mas_get_met_resources(md, id)))
{
(void) fprintf(stderr, "mas_get_met_resources of %s failed\n", name);
mas_perror();
quit(11);
}
if (*resource_p != res_id)
{
(void) fprintf(stderr, "unexpected resource id for %s\n"
"- expected %u got %u\n",
name, res_id, *resource_p);
quit(11);
}
/* get the size of the resource */
if (!(ressz_p = mas_get_met_objsz(md, (metid_t) (*resource_p))))
{
(void) fprintf(stderr, "mas_get_met_objsz of resource failed\n");
mas_perror();
quit(11);
}
if (*ressz_p != ressz)
{
(void) fprintf(stderr, "unexpected resource size for %s\n"
"- expected %u got %u\n",
name, ressz, *ressz_p);
quit(11);
}
/*
* get the address of the resource
*/
if (!(resval_p = (uint32 *) mas_get_met(md, *resource_p, 0)))
{
(void) fprintf(stderr, "mas_get_met of resource failed\n");
mas_perror();
quit(11);
}
if (ressz == sizeof(short))
{
if ((uint32) (*(short *) resval_p) != nlocs)
{
(void) fprintf(stderr, "unexpected resource value for %s\n"
"- expected %u got %u\n",
name, nlocs, (uint32) (*(short *) resval_p));
quit(11);
}
}
else
{ /* assume size of uint32 */
if (*resval_p != nlocs)
{
(void) fprintf(stderr, "unexpected resource value for %s\n"
"- expected %u got %u\n",
name, nlocs, *resval_p);
quit(11);
}
}
return;
}
void
get_io_info(struct io_info *io_info)
{
/* Not supported yet */
memset(io_info, 0, sizeof(*io_info));
}
07070100000036000081A400000000000000000000000166622DE8000000A4000000000000000000000000000000000000002400000000pg_top-4.1.1/machine/m_svr5.man.rstSVR5 CREDITS
============
The SVR5 port was generated by Mike Hopkirk from the SVR42 port by David Cutter
with lots of help from Kurt Gollhardt and Doug Souders
07070100000037000081A400000000000000000000000166622DE80000016F000000000000000000000000000000000000001700000000pg_top-4.1.1/message.h/* interface declaration for display messages */
/* This is a small subset of the interface from display.c that
just contains the calls for displaying messages. Do not include
this and display.h at the same time. */
#ifndef _MESSAGE_H_
#define _MESSAGE_H_
void new_message(int type, char *msgfmt,...);
void clear_message();
#endif /* _MESSAGE_H_ */
07070100000038000081A400000000000000000000000166622DE8000005E7000000000000000000000000000000000000001200000000pg_top-4.1.1/os.h#ifndef _OS_H_
#define _OS_H_
#include "config.h"
#include <sys/types.h>
#include <sys/param.h>
#include <stdio.h>
#if TIME_WITH_SYS_TIME
#include <sys/time.h>
#include <time.h>
#else
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <time.h>
#endif
#endif
#if STDC_HEADERS
#include <string.h>
#include <stdlib.h>
#define setbuffer(f, b, s) setvbuf((f), (b), (b) ? _IOFBF : _IONBF, (s))
#define memzero(a, b) memset((a), 0, (b))
#else /* !STDC_HEADERS */
#ifndef HAVE_STRCHR
#define strchr(a, b) index((a), (b))
#define strrchr(a, b) rindex((a), (b))
#endif /* HAVE_STRCHR */
#ifdef HAVE_MEMCPY
#define memzero(a, b) memset((a), 0, (b))
#else
#define memcpy(a, b, c) bcopy((b), (a), (c))
#define memzero(a, b) bzero((a), (b))
#define memcmp(a, b, c) bcmp((a), (b), (c))
#endif /* HAVE_MEMCPY */
#ifdef HAVE_STRINGS_H
#include <strings.h>
#else
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#endif
char *getenv();
#if !defined (__macosx)
caddr_t malloc();
*/
#endif
#endif /* STDC_HEADERS */
/* we must have both sighold and sigrelse to use them */
#if defined(HAVE_SIGHOLD) && !defined(HAVE_SIGRELSE)
#undef HAVE_SIGHOLD
#endif
/* include unistd.h on Solaris, to get fewer
* warrnigs if compiling with Sun's Studio compilers.
* This should be included on anything that has it, but
* I don't have time to figure out if that will cause
* other problems. --wnl
*/
#if defined (__sun) && defined (__SVR4)
#include <unistd.h>
#endif
#endif /* _OS_H_ */
07070100000039000081A400000000000000000000000166622DE800001832000000000000000000000000000000000000001200000000pg_top-4.1.1/pg.c/* Copyright (c) 2007-2019, Mark Wong */
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include "display.h"
#include "pg.h"
#include "pg_top.h"
#define QUERY_PROCESSES \
"WITH lock_activity AS\n" \
"(\n" \
" SELECT pid, count(*) AS lock_count\n" \
" FROM pg_locks\n" \
" WHERE relation IS NOT NULL\n" \
" GROUP BY pid\n" \
")\n" \
"SELECT a.pid, query, state, usename,\n" \
" extract(EPOCH FROM age(clock_timestamp(),\n" \
" xact_start))::BIGINT,\n" \
" extract(EPOCH FROM age(clock_timestamp(),\n" \
" query_start))::BIGINT,\n" \
" coalesce(lock_count, 0) AS lock_count\n" \
"FROM pg_stat_activity a LEFT OUTER JOIN lock_activity b\n" \
" ON a.pid = b.pid;"
#define QUERY_PROCESSES_9_1 \
"SELECT procpid, current_query\n" \
"FROM pg_stat_activity;"
#define CURRENT_QUERY \
"SELECT query\n" \
"FROM pg_stat_activity\n" \
"WHERE pid = %d;"
#define CURRENT_QUERY_9_1 \
"SELECT current_query\n" \
"FROM pg_stat_activity\n" \
"WHERE procpid = %d;"
#define REPLICATION \
"SELECT pid, usename, application_name, client_addr, state,\n" \
" pg_current_wal_insert_lsn() AS primary,\n" \
" sent_lsn, write_lsn, flush_lsn,\n" \
" replay_lsn, \n" \
" pg_wal_lsn_diff(pg_current_wal_insert_lsn(),\n" \
" sent_lsn) as sent_lag,\n" \
" pg_wal_lsn_diff(pg_current_wal_insert_lsn(),\n" \
" write_lsn) as write_lag,\n" \
" pg_wal_lsn_diff(pg_current_wal_insert_lsn(),\n" \
" flush_lsn) as flush_lag,\n" \
" pg_wal_lsn_diff(pg_current_wal_insert_lsn(),\n" \
" replay_lsn) as replay_lag\n" \
" FROM pg_stat_replication;"
#define REPLICATION_9_6 \
"SELECT pid, usename, application_name, client_addr, state,\n" \
" pg_current_xlog_insert_location() AS primary,\n" \
" sent_location, write_location, flush_location,\n" \
" replay_location, \n" \
" pg_xlog_location_diff(pg_current_xlog_insert_location(),\n" \
" sent_location) as sent_lag,\n" \
" pg_xlog_location_diff(pg_current_xlog_insert_location(),\n" \
" write_location) as write_lag,\n" \
" pg_xlog_location_diff(pg_current_xlog_insert_location(),\n" \
" flush_location) as flush_lag,\n" \
" pg_xlog_location_diff(pg_current_xlog_insert_location(),\n" \
" replay_location) as replay_lag\n" \
" FROM pg_stat_replication;"
#define GET_LOCKS \
"SELECT datname, nspname, r.relname, i.relname, mode, granted\n" \
"FROM pg_stat_activity, pg_locks\n" \
"LEFT OUTER JOIN pg_class r \n" \
" ON relation = r.oid\n"\
" AND r.relkind = 'r'\n" \
"LEFT OUTER JOIN pg_class i \n" \
" ON relation = i.oid\n"\
" AND i.relkind = 'i'\n" \
"LEFT OUTER JOIN pg_namespace nsp\n" \
" ON coalesce(r.relnamespace, i.relnamespace) = nsp.oid\n" \
"WHERE pg_stat_activity.pid = %d\n" \
" AND pg_stat_activity.pid = pg_locks.pid\n" \
" AND relation IS NOT NULL;"
#define GET_LOCKS_9_1 \
"SELECT datname, nspname, r.relname, i.relname, mode, granted\n" \
"FROM pg_stat_activity, pg_locks\n" \
"LEFT OUTER JOIN pg_class r \n" \
" ON relation = r.oid\n"\
" AND r.relkind = 'r'\n" \
"LEFT OUTER JOIN pg_class i \n" \
" ON relation = i.oid\n"\
" AND i.relkind = 'i'\n" \
"LEFT OUTER JOIN pg_namespace nsp\n" \
" ON coalesce(r.relnamespace, i.relnamespace) = nsp.oid\n" \
"WHERE procpid = %d\n" \
" AND procpid = pid\n" \
" AND relation IS NOT NULL;"
int pg_version(PGconn *);
void
connect_to_db(struct pg_conninfo_ctx *conninfo)
{
int i;
const char *keywords[6] = {"host", "port", "user", "password", "dbname",
NULL};
if (conninfo->persistent && PQsocket(conninfo->connection) >= 0)
return;
conninfo->connection = PQconnectdbParams(keywords, conninfo->values, 1);
if (PQstatus(conninfo->connection) != CONNECTION_OK)
{
new_message(MT_standout | MT_delayed, " %s",
PQerrorMessage(conninfo->connection));
PQfinish(conninfo->connection);
conninfo->connection = NULL;
return;
}
if (conninfo->persistent)
for (i = 0; i < 5; i++)
if (conninfo->values[i] != NULL)
free((void *) conninfo->values[i]);
PQexec(conninfo->connection,
"SET SESSION CHARACTERISTICS AS TRANSACTION ISOLATION LEVEL " \
"READ UNCOMMITTED;");
}
void
disconnect_from_db(struct pg_conninfo_ctx *conninfo)
{
if (conninfo->persistent)
return;
PQfinish(conninfo->connection);
}
PGresult *
pg_locks(PGconn *pgconn, int procpid)
{
char *sql;
PGresult *pgresult;
if (pg_version(pgconn) >= 902)
{
sql = (char *) malloc(strlen(GET_LOCKS) + 7);
sprintf(sql, GET_LOCKS, procpid);
}
else
{
sql = (char *) malloc(strlen(GET_LOCKS) + 7);
sprintf(sql, GET_LOCKS_9_1, procpid);
}
pgresult = PQexec(pgconn, sql);
free(sql);
return pgresult;
}
PGresult *
pg_processes(PGconn *pgconn)
{
PGresult *pgresult;
PQexec(pgconn, "BEGIN;");
PQexec(pgconn, "SET statement_timeout = '2s';");
if (pg_version(pgconn) >= 902)
{
pgresult = PQexec(pgconn, QUERY_PROCESSES);
}
else
{
pgresult = PQexec(pgconn, QUERY_PROCESSES_9_1);
}
PQexec(pgconn, "ROLLBACK;;");
return pgresult;
}
PGresult *
pg_replication(PGconn *pgconn)
{
PGresult *pgresult;
PQexec(pgconn, "BEGIN;");
PQexec(pgconn, "SET statement_timeout = '2s';");
if (pg_version(pgconn) >= 1000) {
pgresult = PQexec(pgconn, REPLICATION);
} else {
pgresult = PQexec(pgconn, REPLICATION_9_6);
}
PQexec(pgconn, "ROLLBACK;");
return pgresult;
}
PGresult *
pg_query(PGconn *pgconn, int procpid)
{
char *sql;
PGresult *pgresult;
if (pg_version(pgconn) >= 902)
{
sql = (char *) malloc(strlen(CURRENT_QUERY) + 7);
sprintf(sql, CURRENT_QUERY, procpid);
}
else
{
sql = (char *) malloc(strlen(CURRENT_QUERY_9_1) + 7);
sprintf(sql, CURRENT_QUERY_9_1, procpid);
}
pgresult = PQexec(pgconn, sql);
free(sql);
return pgresult;
}
int
pg_version(PGconn *pgconn)
{
return PQserverVersion(pgconn) / 100;
}
0707010000003A000081A400000000000000000000000166622DE8000003E3000000000000000000000000000000000000001200000000pg_top-4.1.1/pg.h/* Copyright (c) 2007-2019, Mark Wong */
#ifndef _PG_H_
#define _PG_H_
#include <libpq-fe.h>
struct pg_conninfo_ctx
{
PGconn *connection;
int persistent;
const char *values[6];
};
void connect_to_db(struct pg_conninfo_ctx *);
void disconnect_from_db(struct pg_conninfo_ctx *);
PGresult *pg_locks(PGconn *, int);
PGresult *pg_processes(PGconn *);
PGresult *pg_replication(PGconn *);
PGresult *pg_query(PGconn *, int);
enum BackendState
{
STATE_UNDEFINED,
STATE_IDLE,
STATE_RUNNING,
STATE_IDLEINTRANSACTION,
STATE_FASTPATH,
STATE_IDLEINTRANSACTION_ABORTED,
STATE_DISABLED
};
enum pg_stat_activity
{
PROC_PID = 0,
PROC_QUERY,
PROC_STATE,
PROC_USENAME,
PROC_XSTART,
PROC_QSTART,
PROC_LOCKS
};
enum pg_stat_replication
{
REP_PID = 0,
REP_USENAME,
REP_APPLICATION_NAME,
REP_CLIENT_ADDR,
REP_STATE,
REP_WAL_INSERT,
REP_SENT,
REP_WRITE,
REP_FLUSH,
REP_REPLAY,
REP_SENT_LAG,
REP_WRITE_LAG,
REP_FLUSH_LAG,
REP_REPLAY_LAG
};
#endif /* _PG_H_ */
0707010000003B000081A400000000000000000000000166622DE800003B1A000000000000000000000000000000000000001D00000000pg_top-4.1.1/pg_top.1.rst.in========
pg_top
========
---------------------------------------------------------------------
display and update information about the top cpu PostgreSQL processes
---------------------------------------------------------------------
:Author: William LeFebvre, Mark Wong
:Manual section: 1
:Version: 4.0.0
SYNOPSIS
========
**pg_top** [option...] [number]
DESCRIPTION
===========
*pg_top* displays the top processes on the system and periodically updates this
information. Raw cpu percentage is used to rank the processes. If *number* is
given, then the top *number* processes will be displayed instead of the
default.
*pg_top* makes a distinction between terminals that support advanced
capabilities and those that do not. This distinction affects the choice of
defaults for certain options. In the remainder of this document, an
"intelligent" terminal is one that supports cursor addressing, clear screen,
and clear to end of line. Conversely, a \*(lqdumb\*(rq terminal is one that
does not support such features. If the output of *pg_top* is redirected to a
file, it acts as if it were being run on a dumb terminal.
OPTIONS
=======
-b, --batch Use "batch" mode. In this mode, all input from the terminal is
ignored. Interrupt characters (such as ^C and ^\e) still have an
effect. This is the default on a dumb terminal, or when the
output is not a terminal.
-C, --color-mode Turn off the use of color in the display.
-c, --show-command Show the command name for each process. Default is to show
the full command line. This option is not supported on
all platforms.
-h HOST, --host=HOST Specifies the host name of the machine on which the server is
running. If the value begins with a slash, it is used as the
directory for the Unix domain socket. The default is taken
from the PGHOST environment variable, if set.
-I, --hide-idle Do not display idle processes. By default, pg_top displays
both active and idle processes.
-i, --interactive Use "lqinteractive" mode. In this mode, any input is
immediately read for processing. See the section on
"Interactive Mode" for an explanation of which keys perform
what functions. After the command is processed, the screen
will immediately be updated, even if the command was not
understood. This mode is the default when standard output
is an intelligent terminal.
-n, --non-interactive Use "non-interactive" mode. This is identical to
"batch" mode.
-o FIELD, --order-field=FIELD Sort the process display area on the specified
field. The field name is the name of the
column as seen in the output, but in lower
case. Likely values are "cpu", "size", "res",
"xtime" and "qtime", but may vary on different
operating systems. Note that not all operating
systems support this option.
-p PORT, --port=PORT Specifies the TCP port or local Unix domain socket file
extension on which the server is listening for
connections. Defaults to the PGPORT environment
variable, if set.
-R Display WAL sender processes' replication activity to connected standby
servers. Only directly connected standbys are listed; no information is
available about downstream standby servers.
-r, --remote-mode Monitor a remote database where the database is on a system
other than where pg_top is running from. *pg_top* will
monitor a remote database if it has the pg_proctab
extension installed.
-s TIME, --set-delay=TIME Set the delay between screen updates to *TIME*
seconds. The default delay between updates is 5
seconds.
-T, --show-tags List all available color tags and the current set of tests
used for color highlighting, then exit.
-U USERNAME, --username=USERNAME PostgreSQL database user name to connect as.
-V, --version Write version number information to stderr then exit
immediately. No other processing takes place when this option
is used. To see current revision information while *pg_top* is
running, use the help command "?".
-W, --password Forces *pg_top* to prompt for a password before connecting to
a database. *pg_top* will also keep the database connection
open while running, and will clear the database connection
from memory for security.
-X Display I/O activity per process. This depends on whether the platform
*pg_top* is run on supports getting I/O statistics per process, or whether
the database system that pg_proctab is installed on supports getting I/O
statistics when *pg_top* attempts to get operating system statistics
remotely.
-x COUNT, --set-display=COUNT Show only *COUNT* displays, then exit. A
display is considered to be one update of the
screen. This option allows the user to select
the number of displays he wants to see before
*pg_top* automatically exits. For intelligent
terminals, no upper limit is set. The default
is 1 for dumb terminals.
-z USERNAME, --show-username=USERNAME Show only those processes owned by
*USERNAME*. This option currently only
accepts PostgreSQL database user names.
Both *COUNT* and *NUMBER* fields can be specified as "infinite", indicating
that they can stretch as far as possible. This is accomplished by using any
proper prefix of the keywords "infinity", "maximum, or "all". The default for
*COUNT* on an intelligent terminal is, in fact, *infinity*.
The environment variable **PG_TOP** is examined for options before the command
line is scanned. This enables a user to set his or her own defaults. The
number of processes to display can also be specified in the environment
variable **PG_TOP**. The options **-C**, **-I**, and **\-u** are actually
toggles. A second specification of any of these options will negate the first.
Thus a user who has the environment variable **PG_TOP** set to "-I" may use the
command "top -I" to see idle processes.
INTERACTIVE MODE
================
When *pg_top* is running in "interactive mode", it reads commands from the
terminal and acts upon them accordingly. In this mode, the terminal is put in
"BREAK", so that a character will be processed as soon as it is typed. Almost
always, a key will be pressed when *pg_top* is between displays; that is, while
it is waiting for *TIME* seconds to elapse. If this is the case, the command
will be processed and the display will be updated immediately thereafter
(reflecting any changes that the command may have specified). This happens
even if the command was incorrect. If a key is pressed while *pg_top* is in
the middle of updating the display, it will finish the update and then process
the command. Some commands require additional information, and the user will
be prompted accordingly. While typing this information in, the user's erase
and kill keys (as set up by the command *stty*) are recognized, and a newline
terminates the input.
These commands are currently recognized (^L refers to control-L):
:^L: Redraw the screen.
:A: Display the actual query plan (EXPLAIN ANALYZE) of the currently running
SQL statement by re-running the SQL statement (prompt for process id.)
:a: Display the top PostgreSQL processor activity. (default)
:C: Toggle the use of color in the display.
:c: Toggle the display of the full command line.
:d: Change the number of displays to show (prompt for new number). Remember
that the next display counts as one, so typing **d1** will make *pg_top*
show one final display and then immediately exit.
:h or ?: Display a summary of the commands (help screen). Version information
is included in this display.
:E: Display re-determined execution plan (EXPLAIN) of the SQL statement by a
backend process (prompt for process id.)
:i: Toggle the display of idle processes.
:L: Display the currently held locks by a backend process (prompt for process
id.)
:n or #: Change the number of processes to display (prompt for new number).
:o: Change the order in which the display is sorted. This command is not
available on all systems. The sort key names when viewing processes vary
from system to system but usually include: "cpu", "res", "size", "xtime"
and "qtime". The default is unsorted. See the interactive help for
available sort key names.
:Q: Display the currently running query of a backend process (prompt for
process id.)
:q: Quit *pg_top*.
:s: Change the number of seconds to delay between displays (prompt for new
number).
:u: Display only processes owned by a specific username (prompt for username).
If the username specified is simply \*(lq+\*(rq, then processes belonging
to all users will be displayed.
THE DISPLAY
===========
The actual display varies depending on the specific variant of Unix that the
machine is running. This description may not exactly match what is seen by
pg_top running on this particular machine. Differences are listed at the end
of this manual entry.
The top few lines of the display show general information about the state of
the system, including the last process id assigned to a process (on most
systems), the three load averages, the current time, the number of existing
processes, the number of processes in each state (sleeping, running, starting,
zombies, and stopped), and a percentage of time spent in each of the processor
states (user, nice, system, and idle). It also includes information about
physical and virtual memory allocation.
The remainder of the screen displays information about individual processes.
This display is similar in spirit to *ps(1)* but it is not exactly the same.
The columns displayed by *pg_top* will differ slightly between operating
systems. Generally, the following display are available:
ACTIVITY DISPLAY
================
:PID: The process id.
:USERNAME: Username of the process's owner (if **-u** is specified, a UID
column will be substituted for USERNAME).
:SIZE: Total size of the process (text, data, and stack) given in kilobytes.
:RES: Resident memory: current amount of process memory that resides in
physical memory, given in kilobytes.
:STATE: Current backend state (typically one of "idle", "active", "idltxn",
"fast", "disable", or "stop".
:XTIME: Elapsed time since the current transactions started.
:QTIME: Elapsed time since the current query started.
:%CPU: Percentage of available cpu time used by this process.
:LOCKS: Number of locks granted to this process.
:COMMAND: Name of the command that the process is currently running.
I/O DISPLAY (Linux only)
========================
:PID: The process id.
:IOPS: Count the number of read and write I/O operations per second.
:IORPS: Count the number of read I/O operations per second.
:IOWPS: Count the number of write I/O operations per second.
:READS: Number of bytes read from storage.
:WRITES: Number of bytes written to storage.
:COMMAND: Name of the command that the process is currently running.
REPLICATION DISPLAY
===================
:PID: The process id.
:USERNAME: Name of the user logged into this WAL sender process
:APPLICATION: Name of the application that is connected to this WAL sender
:CLIENT: IP address of the client connected to this WAL sender
:STATE: Current WAL sender state
:PRIMARY: Current transaction log insert location on primary node
:SENT: Last write-ahead log location sent on this connection
:WRITE: Last write-ahead log location written to disk
:FLUSH: Last write-ahead log location flushed to disk
:REPLAY: Last write-ahead log location replayed into the database
:SLAG: Size of write-ahead log location remaining to be sent
:WLAG: Size of write-ahead log location remaining to be written to disk
:FLAG: Size of write-ahead log location remaining to be flushed to disk
:RLAG: Size of write-ahead log location remaining to be replayed into the
database
COLOR
=====
*pg_top* supports the use of ANSI color in its output. By default, color is
available but not used. The environment variable **PG_TOPCOLORS** specifies
colors to use and conditions for which they should be used. At the present
time, only numbers in the summary display area can be colored. In a future
version it will be possible to highlight numbers in the process display area as
well. The environment variable is the only way to specify color: there is no
equivalent command line option. Note that the environment variable
**PG_TOPCOLOURS** is also understood. The British spelling takes precedence.
The use of color only works on terminals that understand and process ANSI color
escape sequences.
The environment variable is a sequence of color specifications, separated by
colons. Each specification takes the form tag=min,max#code where *tag* is the
name of the value to check, *min* and *max* specify a range for the value, and
*code* is an ANSI color code. Multiple color codes can be listed and separated
with semi-colons. A missing *min* implies the lowest possible value (usually
0) and a missing *max* implies infinity. The comma must always be present. When
specifying numbers for load averages, they should be multiplied by 100. For
example, the specification **1min=500,1000#31** indicates that a 1 minute load
average between 5 and 10 should be displayed in red. Color attributes can be
combined. For example, the specification **5min=1000,#37;41** indicates that a
5 minute load average higher than 10 should be displayed with white characters
on a red background. A special tag named *header* is used to control the color
of the header for process display. It should be specified with no lower and
upper limits, specifically **header=,#** followed by the ANSI color code.
You can see a list of color codes recognized by this installation of pg_top
with the **-T** option. This will also show the current set of tests used for
color highligting, as specified in the environment.
ENVIRONMENT
===========
**PG_TOP** user-configurable defaults for options. **PG_TOPCOLORS**
color specification
BUGS
====
As with *ps(1)*, things can change while *pg_top* is collecting information for
an update. The picture it gives is only a close approximation to reality.
SEE ALSO
========
*ps(1),* *stty(1),* *mem(4)*
@MAN_SUPPLEMENT@
0707010000003C000081A400000000000000000000000166622DE800006141000000000000000000000000000000000000001600000000pg_top-4.1.1/pg_top.cchar *copyright =
"Copyright (c) 1984 through 2007, William LeFebvre";
/*
* Top users/processes display for Unix
*
* This program may be freely redistributed,
* but this entire comment MUST remain intact.
*
* Copyright (c) 1984, 1989, William LeFebvre, Rice University
* Copyright (c) 1989 - 1994, William LeFebvre, Northwestern University
* Copyright (c) 1994, 1995, William LeFebvre, Argonne National Laboratory
* Copyright (c) 1996, William LeFebvre, Group sys Consulting
* Copyright (c) 2007-2019, Mark Wong
*/
/*
* See the file "HISTORY" for information on version-to-version changes.
*/
/*
* This file contains "main" and other high-level routines.
*/
/*
* The following preprocessor variables, when defined, are used to
* distinguish between different Unix implementations:
*
* FD_SET - macros FD_SET and FD_ZERO are used when defined
*/
#include "os.h"
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
#include <unistd.h>
#include <getopt.h>
/* determine which type of signal functions to use */
#ifdef HAVE_SIGACTION
#undef HAVE_SIGHOLD
#else
#if !defined(HAVE_SIGHOLD) || !defined(HAVE_SIGRELSE)
#define BSD_SIGNALS
#endif
#endif
/* includes specific to top */
#include "pg_top.h"
#include "remote.h"
#include "commands.h"
#include "display.h" /* interface to display package */
#include "screen.h" /* interface to screen package */
#include "boolean.h"
#include "utils.h"
#include "version.h"
#ifdef ENABLE_COLOR
#include "color.h"
#endif
#include "port.h"
/* Size of the stdio buffer given to stdout */
#define Buffersize 2048
/* The buffer that stdio will use */
char stdoutbuf[Buffersize];
/* build signal masks */
#ifndef sigmask
#define sigmask(s) (1 << ((s) - 1))
#endif
/* for getopt: */
extern int optind;
extern char *optarg;
/* imported from screen.c */
extern int overstrike;
/* values which need to be accessed by signal handlers */
int max_topn; /* maximum displayable processes */
/* miscellaneous things */
char *myname = "pg_top";
jmp_buf jmp_int;
/* internal variables */
static const char *progname = "pg_top";
void process_commands(struct pg_top_context *);
static void usage(const char *progname);
/* List of all the options available */
static struct option long_options[] = {
{"batch", no_argument, NULL, 'b'},
{"show-command", no_argument, NULL, 'c'},
{"color-mode", no_argument, NULL, 'C'},
{"interactive", no_argument, NULL, 'i'},
{"hide-idle", no_argument, NULL, 'I'},
{"non-interactive", no_argument, NULL, 'n'},
{"order-field", required_argument, NULL, 'o'},
{"remote-mode", no_argument, NULL, 'r'},
{"set-delay", required_argument, NULL, 's'},
{"show-tags", no_argument, NULL, 'T'},
{"version", no_argument, NULL, 'V'},
{"set-display", required_argument, NULL, 'x'},
{"show-username", required_argument, NULL, 'z'},
{"help", no_argument, NULL, '?'},
{"dbname", required_argument, NULL, 'd'},
{"host", required_argument, NULL, 'h'},
{"port", required_argument, NULL, 'p'},
{"username", required_argument, NULL, 'U'},
{"password", no_argument, NULL, 'W'},
{NULL, 0, NULL, 0}
};
/* pointers to display routines */
void (*d_loadave) (int, double *) = i_loadave;
void (*d_minibar) (
int (*) (char *, int)) = i_minibar;
void (*d_uptime) (time_t *, time_t *) = i_uptime;
void (*d_procstates) (int, int *) = i_procstates;
void (*d_cpustates) (int64_t *) = i_cpustates;
void (*d_memory) (long *) = i_memory;
void (*d_swap) (long *) = i_swap;
void (*d_db) (struct db_info *) = i_db;
void (*d_io) (struct io_info *) = i_io;
void (*d_message) () = i_message;
void (*d_process) (int, char *) = i_process;
/*
* usage - print help message with details about commands
*/
static void
usage(const char *progname)
{
printf("%s monitors a PostgreSQL database cluster.\n\n", progname);
printf("Usage:\n");
printf(" %s [OPTION]... [COUNT]\n", progname);
printf("\nGeneral options:\n");
printf(" -b, --batch use batch mode\n");
printf(" -c, --show-command display command name of each process\n");
printf(" -C, --color-mode turn off color mode\n");
printf(" -i, --interactive use interactive mode\n");
printf(" -I, --hide-idle hide idle processes\n");
printf(" -n, --non-interactive use non-interactive mode\n");
printf(" -o, --order-field=FIELD select sort order\n");
printf(" -r, --remote-mode activate remote mode\n");
printf(" -R display replication stats\n");
printf(" -s, --set-delay=SECOND set delay between screen updates\n");
printf(" -T, --show-tags show color tags\n");
printf(" -V, --version output version information, then exit\n");
printf(" -x, --set-display=COUNT set maximum number of displays\n");
printf(" exit once this number is reached\n");
printf(" -X display i/o stats\n");
printf(" -z, --show-username=NAME display only processes owned by given\n");
printf(" username\n");
printf(" -?, --help show this help, then exit\n");
printf("\nConnection options:\n");
printf(" -d, --dbname=DBNAME database to connect to\n");
printf(" -h, --host=HOSTNAME database server host or socket directory\n");
printf(" -p, --port=PORT database server port\n");
printf(" -U, --username=USERNAME user name to connect as\n");
printf(" -W, --password force password prompt, and persistent connection\n");
}
RETSIGTYPE
onalrm(int i) /* SIGALRM handler */
{
/* this is only used in batch mode to break out of the pause() */
/* return; */
}
void
do_display(struct pg_top_context *pgtctx)
{
register int i = 0;
register int active_procs;
caddr_t processes;
time_t curr_time;
static struct ext_decl exts = {NULL, NULL};
/* get the current stats and processes */
if (pgtctx->mode_remote == 0)
{
get_system_info(&pgtctx->system_info);
processes = get_process_info(&pgtctx->system_info, &pgtctx->ps,
pgtctx->order_index, &pgtctx->conninfo,
pgtctx->mode);
}
else
{
get_system_info_r(&pgtctx->system_info, &pgtctx->conninfo);
processes = get_process_info_r(&pgtctx->system_info, &pgtctx->ps,
pgtctx->order_index, &pgtctx->conninfo, pgtctx->mode);
}
/* Get database activity information */
get_database_info(&pgtctx->db_info, &pgtctx->conninfo);
/* Get database I/O information */
get_io_info(&pgtctx->io_info);
/* display the load averages */
(*d_loadave) (pgtctx->system_info.last_pid, pgtctx->system_info.load_avg);
/* this method of getting the time SHOULD be fairly portable */
time(&curr_time);
/* if we have a minibar extension, use it, otherwise show uptime */
if (exts.f_minibar != NULL)
{
(*d_minibar) (exts.f_minibar);
}
else
{
(*d_uptime) (&pgtctx->statics.boottime, &curr_time);
}
/* display the current time */
i_timeofday(&curr_time);
/* display process state breakdown */
(*d_procstates) (pgtctx->system_info.p_total, pgtctx->system_info.procstates);
/* display the cpu state percentage breakdown */
if (pgtctx->dostates) /* but not the first time */
{
(*d_cpustates) (pgtctx->system_info.cpustates);
}
else
{
/* we'll do it next time */
if (smart_terminal)
{
z_cpustates();
}
pgtctx->dostates = Yes;
}
/* display memory stats */
(*d_memory) (pgtctx->system_info.memory);
/* display database activity */
(*d_db) (&pgtctx->db_info);
/* display database I/O */
(*d_io) (&pgtctx->io_info);
/* display swap stats */
(*d_swap) (pgtctx->system_info.swap);
/* handle message area */
(*d_message) ();
/* update the header area */
pgtctx->d_header(pgtctx->header_text);
if (pgtctx->topn > 0)
{
/* determine number of processes to actually display */
/*
* this number will be the smallest of: active processes, number user
* requested, number current screen accomodates
*/
active_procs = pgtctx->system_info.P_ACTIVE;
if (active_procs > pgtctx->topn)
{
active_procs = pgtctx->topn;
}
if (active_procs > max_topn)
{
active_procs = max_topn;
}
/* Now show the top "n" processes or other statistics. */
switch (pgtctx->mode)
{
#if defined(__linux__) || defined(__FreeBSD__)
case MODE_IO_STATS:
for (i = 0; i < active_procs; i++)
{
if (pgtctx->mode_remote == 0)
(*d_process) (i, format_next_io(processes));
else
(*d_process) (i, format_next_io_r(processes));
}
break;
#endif /* defined(__linux__) || defined(__FreeBSD__) */
case MODE_REPLICATION:
for (i = 0; i < active_procs; i++)
{
if (pgtctx->mode_remote == 0)
(*d_process) (i, format_next_replication(processes));
else
(*d_process) (i, format_next_replication_r(processes));
}
break;
case MODE_PROCESSES:
default:
for (i = 0; i < active_procs; i++)
{
if (pgtctx->mode_remote == 0)
(*d_process) (i, format_next_process(processes));
else
(*d_process) (i, format_next_process_r(processes));
}
}
}
else
{
i = 0;
}
/* do end-screen processing */
u_endscreen(i);
/* now, flush the output buffer */
if (fflush(stdout) != 0)
{
new_message(MT_standout, " Write error on stdout");
putchar('\r');
quit(1);
/* NOTREACHED */
}
/* only do the rest if we have more displays to show */
if (pgtctx->displays)
{
/* switch out for new display on smart terminals */
if (smart_terminal)
{
if (overstrike)
{
reset_display(pgtctx);
}
else
{
d_loadave = u_loadave;
d_minibar = u_minibar;
d_uptime = u_uptime;
d_procstates = u_procstates;
d_cpustates = u_cpustates;
d_memory = u_memory;
d_db = u_db;
d_io = u_io;
d_swap = u_swap;
d_message = u_message;
pgtctx->d_header = u_header;
d_process = u_process;
}
}
if (!pgtctx->interactive)
{
/* set up alarm */
(void) signal(SIGALRM, onalrm);
(void) alarm((unsigned) pgtctx->delay);
/* wait for the rest of it .... */
pause();
}
else
process_commands(pgtctx);
}
}
void
process_arguments(struct pg_top_context *pgtctx, int ac, char **av)
{
int i;
int option_index;
while ((i = getopt_long(ac, av, "CDITbcinRrVh:s:d:U:o:Wp:Xx:z:",
long_options, &option_index)) != EOF)
{
switch (i)
{
#ifdef ENABLE_COLOR
case 'C':
pgtctx->color_on = !pgtctx->color_on;
break;
#endif
case 'D':
debug_set(1);
break;
case 'V': /* show version number */
printf("pg_top %s\n", version_string());
exit(0);
break;
case 'z': /* display only username's processes */
strncpy(pgtctx->ps.usename, optarg, NAMEDATALEN);
break;
case 'I': /* show idle processes */
pgtctx->ps.idle = !pgtctx->ps.idle;
break;
case 'T': /* show color tags */
pgtctx->show_tags = 1;
break;
case 'i': /* go interactive regardless */
pgtctx->interactive = Yes;
break;
case 'c':
pgtctx->ps.fullcmd = No;
break;
case 'n': /* batch, or non-interactive */
case 'b':
pgtctx->interactive = No;
break;
case 'x': /* number of displays to show */
if ((i = atoiwi(optarg)) == Invalid || i == 0)
{
new_message(MT_standout | MT_delayed,
" Bad display count (ignored)");
}
else
{
pgtctx->displays = i;
}
break;
case 's':
if ((pgtctx->delay = atoi(optarg)) < 0 ||
(pgtctx->delay == 0 && getuid() != 0))
{
new_message(MT_standout | MT_delayed,
" Bad seconds delay (ignored)");
pgtctx->delay = Default_DELAY;
}
break;
case 'o': /* select sort order */
pgtctx->order_name = optarg;
break;
case 'p': /* database port */
if ((i = atoiwi(optarg)) == Invalid || i == 0)
{
new_message(MT_standout | MT_delayed,
" Bad port number (ignored)");
}
else
{
pgtctx->conninfo.values[PG_PORT] = strdup(optarg);
}
break;
case 'W': /* prompt for database password */
pgtctx->conninfo.persistent = 1;
pgtctx->conninfo.values[PG_PASSWORD] =
simple_prompt("Password: ", 1000, 0);
break;
case 'U': /* database user name */
pgtctx->conninfo.values[PG_USER] = strdup(optarg);
break;
case 'd': /* database name */
pgtctx->conninfo.values[PG_DBNAME] = strdup(optarg);
break;
case 'h': /* socket location */
pgtctx->conninfo.values[PG_HOST] = strdup(optarg);
break;
case 'R': /* replication mode */
pgtctx->mode = MODE_REPLICATION;
break;
case 'r': /* remote mode */
pgtctx->mode_remote = 1;
break;
case 'X': /* i/O mode */
pgtctx->mode = MODE_IO_STATS;
break;
default:
fprintf(stderr, "Try \"%s --help\" for more information.\n",
progname);
exit(1);
}
}
}
void
process_commands(struct pg_top_context *pgtctx)
{
int no_command;
fd_set readfds;
char ch;
do
{
no_command = No;
/* set up arguments for select with timeout */
FD_ZERO(&readfds);
FD_SET(0, &readfds); /* for standard input */
pgtctx->timeout.tv_sec = pgtctx->delay;
pgtctx->timeout.tv_usec = 0;
/* wait for either input or the end of the delay period */
if (select(32, &readfds, (fd_set *) NULL, (fd_set *) NULL,
&pgtctx->timeout) > 0)
{
/* something to read -- clear the message area first */
clear_message();
/* now read it and convert to command strchr */
/* (use "change" as a temporary to hold strchr) */
if (read(0, &ch, 1) != 1)
{
/* read error: either 0 or -1 */
new_message(MT_standout, " Read error on stdin");
putchar('\r');
quit(1);
/* NOTREACHED */
}
no_command = execute_command(pgtctx, ch);
/* flush out stuff that may have been written */
fflush(stdout);
}
} while (no_command);
}
/*
* reset_display() - reset all the display routine pointers so that entire
* screen will get redrawn.
*/
void
reset_display(struct pg_top_context *pgtctx)
{
d_loadave = i_loadave;
d_minibar = i_minibar;
d_uptime = i_uptime;
d_procstates = i_procstates;
d_cpustates = i_cpustates;
d_memory = i_memory;
d_swap = i_swap;
d_db = i_db;
d_io = i_io;
d_message = i_message;
pgtctx->d_header = i_header;
d_process = i_process;
}
/*
* signal handlers
*/
void
set_signal(int sig, RETSIGTYPE(*handler) (int))
{
#ifdef HAVE_SIGACTION
struct sigaction action;
action.sa_handler = handler;
action.sa_flags = 0;
(void) sigaction(sig, &action, NULL);
#else
(void) signal(sig, handler);
#endif
}
RETSIGTYPE
leave(int i) /* exit under normal conditions -- INT handler */
{
end_screen();
exit(0);
}
RETSIGTYPE
tstop(int i) /* SIGTSTP handler */
{
#ifdef HAVE_SIGACTION
sigset_t set;
#endif
/* move to the lower left */
end_screen();
fflush(stdout);
/* default the signal handler action */
set_signal(SIGTSTP, SIG_DFL);
/* unblock the TSTP signal */
#ifdef HAVE_SIGACTION
sigemptyset(&set);
sigaddset(&set, SIGTSTP);
sigprocmask(SIG_UNBLOCK, &set, NULL);
#endif
#ifdef HAVE_SIGHOLD
sigrelse(SIGTSTP);
#endif
#ifdef BSD_SIGNALS
(void) sigsetmask(sigblock(0) & ~(sigmask(SIGTSTP)));
#endif
/* send ourselves a TSTP to stop the process */
(void) kill(0, SIGTSTP);
/* reset the signal handler */
set_signal(SIGTSTP, tstop);
/* reinit screen */
reinit_screen();
/* jump to appropriate place */
longjmp(jmp_int, 1);
/* NOTREACHED */
}
#ifdef SIGWINCH
RETSIGTYPE
winch(int i) /* SIGWINCH handler */
{
/* reascertain the screen dimensions */
get_screensize();
/* tell display to resize */
max_topn = display_resize();
#ifndef HAVE_SIGACTION
/* reset the signal handler */
set_signal(SIGWINCH, winch);
#endif
/* jump to appropriate place */
longjmp(jmp_int, 1);
}
#endif
void
quit(int status) /* exit under duress */
{
end_screen();
exit(status);
/* NOTREACHED */
}
int
main(int argc, char *argv[])
{
register int i;
struct pg_top_context pgtctx;
#ifdef BSD_SIGNALS
int old_sigmask; /* only used for BSD-style signals */
#endif /* BSD_SIGNALS */
char *uname_field = "USERNAME";
char *env_top;
char **preset_argv;
int preset_argc = 0;
char **av;
int ac;
#ifndef FD_SET
/* FD_SET and friends are not present: fake it */
typedef int fd_set;
#define FD_ZERO(x) (*(x) = 0)
#define FD_SET(f, x) (*(x) = 1<<f)
#endif
#ifdef HAVE_SIGPROCMASK
sigset_t signalset;
#endif
/* initialize some selection options */
memset(&pgtctx, 0, sizeof(struct pg_top_context));
#ifdef ENABLE_COLOR
pgtctx.color_on = 1;
#endif
pgtctx.d_header = i_header;
pgtctx.delay = Default_DELAY;
pgtctx.displays = 0; /* indicates unspecified */
pgtctx.dostates = No;
pgtctx.do_unames = Yes;
pgtctx.interactive = Maybe;
pgtctx.mode = MODE_PROCESSES;
pgtctx.mode_remote = No;
pgtctx.order_index = -1;
pgtctx.ps.idle = Yes;
pgtctx.ps.fullcmd = Yes;
pgtctx.ps.command = NULL;
pgtctx.ps.usename[0] = '\0';
pgtctx.show_tags = No;
pgtctx.topn = 0;
pgtctx.conninfo.connection = NULL;
pgtctx.conninfo.persistent = 0;
/* Show help or version number if necessary */
if (argc > 1)
{
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
{
usage(progname);
exit(0);
}
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
{
printf("pg_top %s\n", version_string());
exit(0);
}
}
/* set the buffer for stdout */
#ifdef HAVE_SETVBUF
setvbuf(stdout, stdoutbuf, _IOFBF, BUFFERSIZE);
#else
#ifdef HAVE_SETBUFFER
setbuffer(stdout, stdoutbuf, Buffersize);
#endif /* HAVE_SETBUFFER */
#endif /* HAVE_SETVBUF */
/* get our name */
if (argc > 0)
{
if ((myname = strrchr(argv[0], '/')) == 0)
{
myname = argv[0];
}
else
{
myname++;
}
}
/* get preset options from the environment */
if ((env_top = getenv("PG_TOP")) != NULL)
{
av = preset_argv = argparse(env_top, &preset_argc);
ac = preset_argc;
/*
* set the dummy argument to an explanatory message, in case getopt
* encounters a bad argument
*/
preset_argv[0] = "while processing environment";
}
/* process options */
do
{
/* if we're done doing the presets, then process the real arguments */
if (preset_argc == 0)
{
ac = argc;
av = argv;
/* this should keep getopt happy... */
optind = 1;
}
process_arguments(&pgtctx, ac, av);
/* get count of top processes to display (if any) */
if (optind < ac && *av[optind])
{
if ((i = atoiwi(av[optind])) == Invalid)
{
new_message(MT_standout | MT_delayed,
" Process count not a number (ignored)");
}
else
{
pgtctx.displays = i;
}
}
/* tricky: remember old value of preset_argc & set preset_argc = 0 */
i = preset_argc;
preset_argc = 0;
/* repeat only if we really did the preset arguments */
} while (i != 0);
/* set constants for username/uid display correctly */
if (!pgtctx.do_unames)
{
uname_field = " UID ";
}
/*
* in order to support forward compatability, we have to ensure that the
* entire statics structure is set to a known value before we call
* machine_init. This way fields that a module does not know about will
* retain their default values
*/
memzero((void *) &pgtctx.statics, sizeof(pgtctx.statics));
pgtctx.statics.boottime = -1;
#ifdef ENABLE_COLOR
/* If colour has been turned on read in the settings. */
env_top = getenv("PG_TOPCOLOURS");
if (!env_top)
{
env_top = getenv("PG_TOPCOLORS");
}
/* must do something about error messages */
color_env_parse(env_top);
#endif
/* call the platform-specific init */
if (pgtctx.mode_remote == 0)
i = machine_init(&pgtctx.statics);
else
i = machine_init_r(&pgtctx.statics, &pgtctx.conninfo);
if (i == -1)
exit(1);
/* determine sorting order index, if necessary */
if (pgtctx.order_name != NULL)
{
if (pgtctx.statics.order_names == NULL)
{
new_message(MT_standout | MT_delayed,
" This platform does not support arbitrary ordering");
}
else if ((pgtctx.order_index = string_index(pgtctx.order_name,
pgtctx.statics.order_names)) == -1)
{
char **pp;
fprintf(stderr, "%s: '%s' is not a recognized sorting order.\n",
myname, pgtctx.order_name);
fprintf(stderr, "\tTry one of these:");
pp = pgtctx.statics.order_names;
while (*pp != NULL)
{
fprintf(stderr, " %s", *pp++);
}
fputc('\n', stderr);
exit(1);
}
}
#ifdef WITH_EXT
/* initialize extensions */
init_ext(&exts);
#endif
/* initialize termcap */
init_termcap(pgtctx.interactive);
/* 0 corresponds to machine headers definitions */
pgtctx.header_options[0][MODE_PROCESSES] = format_header(uname_field);
#if defined(__linux__) || defined(__FreeBSD__)
pgtctx.header_options[0][MODE_IO_STATS] = fmt_header_io;
#endif /* defined(__linux__) || defined(__FreeBSD__) */
pgtctx.header_options[0][MODE_REPLICATION] = fmt_header_replication;
/* 1 corresponds to headers definitions when remotely connecting to pg */
pgtctx.header_options[1][MODE_PROCESSES] = format_header_r(uname_field);
pgtctx.header_options[1][MODE_IO_STATS] = fmt_header_io_r;
pgtctx.header_options[1][MODE_REPLICATION] = fmt_header_replication_r;
/* get the string to use for the process area header */
pgtctx.header_text =
pgtctx.header_options[pgtctx.mode_remote][pgtctx.mode];
#ifdef ENABLE_COLOR
/* Disable colours on non-smart terminals */
if (!smart_terminal)
{
pgtctx.color_on = 0;
}
#endif
/* initialize display interface */
if ((max_topn = display_init(&pgtctx.statics)) == -1)
{
fprintf(stderr, "%s: can't allocate sufficient memory\n", myname);
exit(4);
}
/* handle request for color tags */
if (pgtctx.show_tags)
{
color_dump(stdout);
exit(0);
}
/*
* Set topn based on the current screensize when starting up if it was not
* specified on the command line.
*/
if (pgtctx.topn == 0)
{
get_screensize();
pgtctx.topn = display_resize();
}
/* print warning if user requested more processes than we can display */
if (pgtctx.topn > max_topn)
{
new_message(MT_standout | MT_delayed,
" This terminal can only display %d processes.",
max_topn);
}
/* set header display accordingly */
display_header(pgtctx.topn > 0);
/* determine interactive state */
if (pgtctx.interactive == Maybe)
{
pgtctx.interactive = smart_terminal;
}
/* if # of displays not specified, fill it in */
if (pgtctx.displays == 0)
{
pgtctx.displays = smart_terminal ? Infinity : 1;
}
/* hold interrupt signals while setting up the screen and the handlers */
#ifdef HAVE_SIGPROCMASK
sigemptyset(&signalset);
sigaddset(&signalset, SIGINT);
sigaddset(&signalset, SIGQUIT);
sigaddset(&signalset, SIGTSTP);
#ifdef SIGWINCH
sigaddset(&signalset, SIGWINCH);
#endif
sigprocmask(SIG_BLOCK, &signalset, NULL);
#endif
#ifdef HAVE_SIGHOLD
sighold(SIGINT);
sighold(SIGQUIT);
sighold(SIGTSTP);
#ifdef SIGWINCH
sighold(SIGWINCH);
#endif
#endif
#ifdef BSD_SIGNALS
#ifdef SIGWINCH
old_sigmask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) |
sigmask(SIGTSTP) | sigmask(SIGWINCH));
#else
old_sigmask = sigblock(sigmask(SIGINT) | sigmask(SIGQUIT) |
sigmask(SIGTSTP));
#endif
#endif
init_screen();
(void) set_signal(SIGINT, leave);
(void) set_signal(SIGQUIT, leave);
(void) set_signal(SIGTSTP, tstop);
#ifdef SIGWINCH
(void) set_signal(SIGWINCH, winch);
#endif
/* setup the jump buffer for stops */
if (setjmp(jmp_int) != 0)
{
/* control ends up here after an interrupt */
reset_display(&pgtctx);
}
/*
* Ready to release the signals. This will also happen (needlessly) after
* a longjmp, but that's okay.
*/
#ifdef HAVE_SIGPROCMASK
sigprocmask(SIG_UNBLOCK, &signalset, NULL);
#endif
#ifdef HAVE_SIGHOLD
sigrelse(SIGINT);
sigrelse(SIGQUIT);
sigrelse(SIGTSTP);
#ifdef SIGWINCH
sigrelse(SIGWINCH);
#endif
#endif
#ifdef BSD_SIGNALS
(void) sigsetmask(old_sigmask);
#endif
/* some systems require a warmup */
if (pgtctx.statics.flags.warmup)
{
if (pgtctx.mode_remote == 0)
{
get_system_info(&pgtctx.system_info);
(void) get_process_info(&pgtctx.system_info, &pgtctx.ps, -1,
&pgtctx.conninfo, pgtctx.mode);
}
else
{
get_system_info_r(&pgtctx.system_info, &pgtctx.conninfo);
(void) get_process_info_r(&pgtctx.system_info, &pgtctx.ps, -1,
&pgtctx.conninfo, -1);
}
/* Get database activity information */
get_database_info(&pgtctx.db_info, &pgtctx.conninfo);
/* Get database I/O information */
get_io_info(&pgtctx.io_info);
pgtctx.timeout.tv_sec = 1;
pgtctx.timeout.tv_usec = 0;
select(0, NULL, NULL, NULL, &pgtctx.timeout);
/* if we've warmed up, then we can show good states too */
pgtctx.dostates = Yes;
}
/*
* main loop -- repeat while display count is positive or while it
* indicates infinity (by being -1)
*/
while ((pgtctx.displays == -1) || (pgtctx.displays-- > 0))
{
do_display(&pgtctx);
}
quit(0);
/* NOTREACHED */
return 0;
}
0707010000003D000081A400000000000000000000000166622DE800000AEA000000000000000000000000000000000000001600000000pg_top-4.1.1/pg_top.h/*
* Top - a top users display for Berkeley Unix
*
* General (global) definitions
*
* Copyright (c) 2007-2019, Mark Wong
*/
#ifndef _PG_TOP_H_
#define _PG_TOP_H_
#include "machine.h"
#include "os.h"
/* Log base 2 of 1024 is 10 (2^10 == 1024) */
#define LOG1024 10
/* Special atoi routine returns either a non-negative number or one of: */
#define Infinity -1
#define Invalid -2
/* maximum number we can have */
#define Largest 0x7fffffff
struct ext_decl
{
int (*f_minibar) (char *, int);
int (*f_display) (char *, int);
};
/*
* Definitions for things that might vary between installations.
*/
/*
* "Table_size" defines the size of the hash tables used to map uid to
* username. Things will work best if the number is a prime number.
* We use a number that should be suitable for most installations.
*/
#ifndef Table_size
#define Table_size 8191
#endif
/*
* "Nominal_TOPN" is used as the default TOPN when Default_TOPN is Infinity
* and the output is a dumb terminal. If we didn't do this, then
* installations who use a default TOPN of Infinity will get every
* process in the system when running top on a dumb terminal (or redirected
* to a file). Note that Nominal_TOPN is a default: it can still be
* overridden on the command line, even with the value "infinity".
*/
#ifndef Nominal_TOPN
#define Nominal_TOPN 40
#endif
#ifndef Default_DELAY
#define Default_DELAY 5
#endif
/*
* If the local system's getpwnam interface uses random access to retrieve
* a record (i.e.: 4.3 systems, Sun "yellow pages"), then defining
* RANDOM_PW will take advantage of that fact. If RANDOM_PW is defined,
* then getpwnam is used and the result is cached. If not, then getpwent
* is used to read and cache the password entries sequentially until the
* desired one is found.
*
* We initially set RANDOM_PW to something which is controllable by the
* Configure script. Then if its value is 0, we undef it.
*/
#define RANDOM_PW 1
#if RANDOM_PW == 0
#undef RANDOM_PW
#endif
enum pgparams
{
PG_HOST,
PG_PORT,
PG_USER,
PG_PASSWORD,
PG_DBNAME
};
struct pg_top_context
{
#ifdef ENABLE_COLOR
int color_on;
#endif
int delay;
int displays;
void (*d_header) (char *);
char do_unames;
char dostates;
char *header_options[2][MODE_TYPES];
char *header_text;
char interactive;
int mode;
int mode_remote; /* Mode for monitoring a remote database
* system. */
int order_index;
char *order_name;
struct db_info db_info;
struct io_info io_info;
struct process_select ps;
char show_tags;
struct statics statics;
struct system_info system_info;
struct timeval timeout;
int topn;
struct pg_conninfo_ctx conninfo;
};
void quit(int);
void reset_display(struct pg_top_context *);
#endif /* _PG_TOP_H_ */
0707010000003E000081A400000000000000000000000166622DE80000036B000000000000000000000000000000000000001400000000pg_top-4.1.1/port.h/*-------------------------------------------------------------------------
*
* port.h
* Header for src/port/ compatibility functions.
*
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/port.h,v 1.106.2.1 2007/01/11 02:40:12 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef _PORT_H_
#define _PORT_H_
#include <stdbool.h>
#if defined(WIN32) && !defined(__CYGWIN__)
#define DEVNULL "nul"
/* "con" does not work from the Msys 1.0.10 console (part of MinGW). */
#define DEVTTY "con"
#else
#define DEVNULL "/dev/null"
#define DEVTTY "/dev/tty"
#endif
/* Portable prompt handling */
extern char *simple_prompt(const char *prompt, int maxlen, bool echo);
#endif /* _PORT_H_ */
0707010000003F000081A400000000000000000000000166622DE800000273000000000000000000000000000000000000001600000000pg_top-4.1.1/remote.h/*
* Copyright (c) 2008-2019, Mark Wong
*/
#ifndef _REMOTE_H_
#define _REMOTE_H_
#include "machine.h"
int machine_init_r(struct statics *, struct pg_conninfo_ctx *);
void get_system_info_r(struct system_info *, struct pg_conninfo_ctx *);
caddr_t get_process_info_r(struct system_info *, struct process_select *, int,
struct pg_conninfo_ctx *, int);
char *format_header_r(char *);
char *format_next_io_r(caddr_t);
char *format_next_process_r(caddr_t);
char *format_next_replication_r(caddr_t);
extern char fmt_header_io_r[];
extern char fmt_header_replication_r[];
#endif /* _REMOTE_H_ */
07070100000040000081A400000000000000000000000166622DE800002215000000000000000000000000000000000000001600000000pg_top-4.1.1/screen.c/*
* Top users/processes display for Unix
* Version 3
*
* This program may be freely redistributed,
* but this entire comment MUST remain intact.
*
* Copyright (c) 1984, 1989, William LeFebvre, Rice University
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
*/
/* This file contains the routines that interface to termcap and stty/gtty.
*
* Paul Vixie, February 1987: converted to use ioctl() instead of stty/gtty.
*
* I put in code to turn on the TOSTOP bit while top was running, but I
* didn't really like the results. If you desire it, turn on the
* preprocessor variable "TOStop". --wnl
*/
#include "os.h"
#include "pg_top.h"
#include <sys/ioctl.h>
#ifdef CBREAK
#include <sgtty.h>
#define SGTTY
#else
#include <termios.h>
#endif /* CBREAK */
#ifndef TAB3
#ifdef OXTABS
#define TAB3 OXTABS
#else
#define TAB3 0
#endif /* OXTABS */
#endif
#include "screen.h"
#include "boolean.h"
extern char *myname;
int overstrike;
int screen_length;
int screen_width;
char ch_erase;
char ch_kill;
char smart_terminal;
char PC;
char termcap_buf[1024];
char string_buffer[1024];
char home[16];
char lower_left[15];
char *clear_line;
char *clear_screen;
char *clear_to_end;
char *cursor_motion;
char *start_standout;
char *end_standout;
char *terminal_init;
char *terminal_end;
#ifdef SGTTY
static struct sgttyb old_settings;
static struct sgttyb new_settings;
#endif
static struct termios old_settings;
static struct termios new_settings;
static char is_a_terminal = No;
#ifdef TOStop
static int old_lword;
static int new_lword;
#endif
#define STDIN 0
#define STDOUT 1
#define STDERR 2
/* This has to be defined as a subroutine for tputs (instead of a macro) */
int
putstdout(int ch)
{
return putchar((unsigned int) ch);
}
void
get_screensize()
{
#ifdef TIOCGWINSZ
struct winsize ws;
if (ioctl(1, TIOCGWINSZ, &ws) != -1)
{
if (ws.ws_row != 0)
{
screen_length = ws.ws_row;
}
if (ws.ws_col != 0)
{
screen_width = ws.ws_col - 1;
}
}
#else
#ifdef TIOCGSIZE
struct ttysize ts;
if (ioctl(1, TIOCGSIZE, &ts) != -1)
{
if (ts.ts_lines != 0)
{
screen_length = ts.ts_lines;
}
if (ts.ts_cols != 0)
{
screen_width = ts.ts_cols - 1;
}
}
#endif /* TIOCGSIZE */
#endif /* TIOCGWINSZ */
char *lower_left_motion = "";
/* get_screensize() can be called from main() without cursor_motion */
/* having been set, so we protect against that possibility. */
if (smart_terminal == Yes)
{
/* We need to account for the fact that tgoto() might return NULL. */
lower_left_motion = tgoto(cursor_motion, 0, screen_length - 1);
if (lower_left_motion == NULL)
{
lower_left_motion = "";
}
}
(void) strncpy(lower_left, lower_left_motion, 15);
lower_left[14] = '\0';
}
void
init_termcap(int interactive)
{
char *bufptr;
char *PCptr;
char *term_name;
int status;
/* set defaults in case we aren't smart */
screen_width = MAX_COLS;
screen_length = 0;
if (!interactive)
{
/* pretend we have a dumb terminal */
smart_terminal = No;
return;
}
/* assume we have a smart terminal until proven otherwise */
smart_terminal = Yes;
/* get the terminal name */
term_name = getenv("TERM");
/* if there is no TERM, assume it's a dumb terminal */
/* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */
if (term_name == NULL)
{
smart_terminal = No;
return;
}
/* now get the termcap entry */
if ((status = tgetent(termcap_buf, term_name)) != 1)
{
if (status == -1)
{
fprintf(stderr, "%s: can't open termcap file\n", myname);
}
else
{
fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n",
myname, term_name);
}
/* pretend it's dumb and proceed */
smart_terminal = No;
return;
}
/* "hardcopy" immediately indicates a very stupid terminal */
if (tgetflag("hc"))
{
smart_terminal = No;
return;
}
/* set up common terminal capabilities */
if ((screen_length = tgetnum("li")) <= 0)
{
screen_length = smart_terminal = 0;
return;
}
/* screen_width is a little different */
if ((screen_width = tgetnum("co")) == -1)
{
screen_width = 79;
}
else
{
screen_width -= 1;
}
/* terminals that overstrike need special attention */
overstrike = tgetflag("os");
/* initialize the pointer into the termcap string buffer */
bufptr = string_buffer;
/* get "ce", clear to end */
if (!overstrike)
{
clear_line = tgetstr("ce", &bufptr);
}
/* get necessary capabilities */
if ((clear_screen = tgetstr("cl", &bufptr)) == NULL ||
(cursor_motion = tgetstr("cm", &bufptr)) == NULL)
{
smart_terminal = No;
return;
}
/* get some more sophisticated stuff -- these are optional */
clear_to_end = tgetstr("cd", &bufptr);
terminal_init = tgetstr("ti", &bufptr);
terminal_end = tgetstr("te", &bufptr);
start_standout = tgetstr("so", &bufptr);
end_standout = tgetstr("se", &bufptr);
/* pad character */
PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
/* set convenience strings */
/* We need to account for the fact that tgoto() might return NULL. */
char *home_motion = tgoto(cursor_motion, 0, 0);
if (home_motion == NULL)
{
home_motion = "";
}
(void) strncpy(home, home_motion, 15);
home[15] = '\0';
/* (lower_left is set in get_screensize) */
/* get the actual screen size with an ioctl, if needed */
/*
* This may change screen_width and screen_length, and it always sets
* lower_left.
*/
get_screensize();
/* if stdout is not a terminal, pretend we are a dumb terminal */
#ifdef SGTTY
if (ioctl(STDOUT, TIOCGETP, &old_settings) == -1)
{
smart_terminal = No;
}
#endif
if (tcgetattr(STDOUT, &old_settings) == -1)
{
smart_terminal = No;
}
}
void
init_screen()
{
/* get the old settings for safe keeping */
#ifdef SGTTY
if (ioctl(STDOUT, TIOCGETP, &old_settings) != -1)
{
/* copy the settings so we can modify them */
new_settings = old_settings;
/* turn on CBREAK and turn off character echo and tab expansion */
new_settings.sg_flags |= CBREAK;
new_settings.sg_flags &= ~(ECHO | XTABS);
(void) ioctl(STDOUT, TIOCSETP, &new_settings);
/* remember the erase and kill characters */
ch_erase = old_settings.sg_erase;
ch_kill = old_settings.sg_kill;
#ifdef TOStop
/* get the local mode word */
(void) ioctl(STDOUT, TIOCLGET, &old_lword);
/* modify it */
new_lword = old_lword | LTOSTOP;
(void) ioctl(STDOUT, TIOCLSET, &new_lword);
#endif
/* remember that it really is a terminal */
is_a_terminal = Yes;
/* send the termcap initialization string */
putcap(terminal_init);
}
#endif
if (tcgetattr(STDOUT, &old_settings) != -1)
{
/* copy the settings so we can modify them */
new_settings = old_settings;
/* turn off ICANON, character echo and tab expansion */
new_settings.c_lflag &= ~(ICANON | ECHO);
new_settings.c_oflag &= ~(TAB3);
new_settings.c_cc[VMIN] = 1;
new_settings.c_cc[VTIME] = 0;
(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
/* remember the erase and kill characters */
ch_erase = old_settings.c_cc[VERASE];
ch_kill = old_settings.c_cc[VKILL];
/* remember that it really is a terminal */
is_a_terminal = Yes;
/* send the termcap initialization string */
putcap(terminal_init);
}
if (!is_a_terminal)
{
/* not a terminal at all---consider it dumb */
smart_terminal = No;
}
}
void
end_screen()
{
/* move to the lower left, clear the line and send "te" */
if (smart_terminal)
{
putcap(lower_left);
putcap(clear_line);
fflush(stdout);
putcap(terminal_end);
}
/* if we have settings to reset, then do so */
if (is_a_terminal)
{
#ifdef SGTTY
(void) ioctl(STDOUT, TIOCSETP, &old_settings);
#ifdef TOStop
(void) ioctl(STDOUT, TIOCLSET, &old_lword);
#endif
#endif
(void) tcsetattr(STDOUT, TCSADRAIN, &old_settings);
}
}
void
reinit_screen()
{
/* install our settings if it is a terminal */
if (is_a_terminal)
{
#ifdef SGTTY
(void) ioctl(STDOUT, TIOCSETP, &new_settings);
#ifdef TOStop
(void) ioctl(STDOUT, TIOCLSET, &new_lword);
#endif
#endif
(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
}
/* send init string */
if (smart_terminal)
{
putcap(terminal_init);
}
}
void
standout(char *msg)
{
if (smart_terminal)
{
putcap(start_standout);
fputs(msg, stdout);
putcap(end_standout);
}
else
{
fputs(msg, stdout);
}
}
void
clear()
{
if (smart_terminal)
{
putcap(clear_screen);
}
}
int
clear_eol(int len)
{
if (smart_terminal && !overstrike && len > 0)
{
if (clear_line)
{
putcap(clear_line);
return (0);
}
else
{
while (len-- > 0)
{
putchar(' ');
}
return (1);
}
}
return (-1);
}
void
go_home()
{
if (smart_terminal)
{
putcap(home);
}
}
07070100000041000081A400000000000000000000000166622DE8000005A3000000000000000000000000000000000000001600000000pg_top-4.1.1/screen.h/*
* top - a top users display for Unix 4.2
*
* This file contains all the definitions necessary to use the hand-written
* screen package in "screen.c"
*/
#ifndef _SCREEN_H_
#define _SCREEN_H_
/* includes for termcap */
#ifdef HAVE_TERMCAP_H
#include <termcap.h>
#else
int tputs(const char *, int, int (*) (int));
char *tgoto(const char *, int, int);
int tgetent(const char *, char *);
int tgetflag(const char *);
int tgetnum(const char *);
char *tgetstr(const char *, char **);
#endif
#define TCputs(str) tputs(str, 1, putstdout)
#define putcap(str) (void)((str) != NULL ? TCputs(str) : 0)
#define Move_to(x, y) TCputs(tgoto(cursor_motion, x, y))
extern char ch_erase; /* set to the user's erase character */
extern char ch_kill; /* set to the user's kill character */
extern char smart_terminal; /* set if the terminal has sufficient termcap
* capabilities for normal operation */
/* These are some termcap strings for use outside of "screen.c" */
extern char *cursor_motion;
extern char *clear_line;
extern char *clear_to_end;
/* rows and columns on the screen according to termcap */
extern int screen_length;
extern int screen_width;
int putstdout(int);
void get_screensize();
void init_termcap(int interactive);
void init_screen();
void end_screen();
void reinit_screen();
void standout(char *msg);
void clear();
int clear_eol(int len);
void go_home();
#endif /* _SCREEN_H_ */
07070100000042000081A400000000000000000000000166622DE80000097F000000000000000000000000000000000000001700000000pg_top-4.1.1/sprompt.c/*-------------------------------------------------------------------------
*
* sprompt.c
* simple_prompt() routine
*
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/port/sprompt.c,v 1.18 2006/10/04 00:30:14 momjian Exp $
*
*-------------------------------------------------------------------------
*/
/*
* simple_prompt
*
* Generalized function especially intended for reading in usernames and
* password interactively. Reads from /dev/tty or stdin/stderr.
*
* prompt: The prompt to print
* maxlen: How many characters to accept
* echo: Set to false if you want to hide what is entered (for passwords)
*
* Returns a malloc()'ed string with the input (w/o trailing newline).
*/
#include "c.h"
#include <termios.h>
extern char *simple_prompt(const char *prompt, int maxlen, bool echo);
char *
simple_prompt(const char *prompt, int maxlen, bool echo)
{
int length;
char *destination;
FILE *termin,
*termout;
struct termios t_orig,
t;
destination = (char *) malloc(maxlen + 1);
if (!destination)
return NULL;
/*
* Do not try to collapse these into one "w+" mode file. Doesn't work on
* some platforms (eg, HPUX 10.20).
*/
termin = fopen(DEVTTY, "r");
termout = fopen(DEVTTY, "w");
if (!termin || !termout
)
{
if (termin)
fclose(termin);
if (termout)
fclose(termout);
termin = stdin;
termout = stderr;
}
if (!echo)
{
tcgetattr(fileno(termin), &t);
t_orig = t;
t.c_lflag &= ~ECHO;
tcsetattr(fileno(termin), TCSAFLUSH, &t);
}
if (prompt)
{
fputs(_(prompt), termout);
fflush(termout);
}
if (fgets(destination, maxlen + 1, termin) == NULL)
destination[0] = '\0';
length = strlen(destination);
if (length > 0 && destination[length - 1] != '\n')
{
/* eat rest of the line */
char buf[128];
int buflen;
do
{
if (fgets(buf, sizeof(buf), termin) == NULL)
break;
buflen = strlen(buf);
} while (buflen > 0 && buf[buflen - 1] != '\n');
}
if (length > 0 && destination[length - 1] == '\n')
/* remove trailing newline */
destination[length - 1] = '\0';
if (!echo)
{
tcsetattr(fileno(termin), TCSAFLUSH, &t_orig);
fputs("\n", termout);
fflush(termout);
}
if (termin != stdin)
{
fclose(termin);
fclose(termout);
}
return destination;
}
07070100000043000041ED00000000000000000000000266622DE800000000000000000000000000000000000000000000001300000000pg_top-4.1.1/tools07070100000044000081A400000000000000000000000166622DE8000000D5000000000000000000000000000000000000001E00000000pg_top-4.1.1/tools/README.rstThis is a collection of scripts to use Podman for specific tasks:
* build-appimage - Create an AppImage for pg_top.
* build-appimage-container - Build a container image to use for creating an
AppImage.
07070100000045000081ED00000000000000000000000166622DE800000301000000000000000000000000000000000000002200000000pg_top-4.1.1/tools/build-appimage#!/bin/sh
if ! which podman > /dev/null 2>&1; then
echo "podman is not in your path"
exit 1
fi
CONTAINER_DIR=$(realpath "$(dirname "$0")")
CONTAINER_TAG="pg_top-appimage"
# Use the return code from `podman inspect` to determine if the container image
# needs to be created.
if ! podman inspect $CONTAINER_TAG > /dev/null 2>&1; then
"${CONTAINER_DIR}/build-appimage-container" || exit 1
fi
podman run \
--rm \
-v "${CONTAINER_DIR}/..:/usr/local/src/pg_top:rw,Z" \
-w /usr/local/src/pg_top \
$CONTAINER_TAG \
make -f Makefile.cmake appimage
podman run \
--rm \
-v "${CONTAINER_DIR}/..:/usr/local/src/pg_top:rw,Z" \
-w /usr/local/src/pg_top \
-u root \
$CONTAINER_TAG \
chown -R root:root builds/appimage/
07070100000046000081ED00000000000000000000000166622DE8000001A2000000000000000000000000000000000000002C00000000pg_top-4.1.1/tools/build-appimage-container#!/bin/sh
if ! which podman > /dev/null 2>&1; then
echo "podman is not in your path"
exit 1
fi
CONTAINER_DIR=$(realpath "$(dirname "$0")")
CONTAINER_TAG="pg_top-appimage"
ARCH="$(uname -m)"
if [ "${ARCH}" = "arm64" ]; then
ARCH="aarch64"
fi
cd "$CONTAINER_DIR/.." && \
podman build \
--isolation=chroot \
--build-arg ARCH="${ARCH}" \
-t "${CONTAINER_TAG}" \
-f Containerfile.appimage \
.
07070100000047000081A400000000000000000000000166622DE800003470000000000000000000000000000000000000001500000000pg_top-4.1.1/utils.c/*
* Top users/processes display for Unix
* Version 3
*
* This program may be freely redistributed,
* but this entire comment MUST remain intact.
*
* Copyright (c) 1984, 1989, William LeFebvre, Rice University
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
*/
/*
* This file contains various handy utilities used by top.
*/
#include "os.h"
#include <ctype.h>
#ifdef HAVE_STDARG_H
#include <stdarg.h>
#else
#undef DEBUG
#endif
#include "pg_top.h"
#include "utils.h"
static int
alldigits(char *s)
{
int ch;
while ((ch = *s++) != '\0')
{
if (!isdigit(ch))
{
return 0;
}
}
return 1;
}
int
atoiwi(char *str)
{
register int len;
len = strlen(str);
if (len != 0)
{
if (strncmp(str, "infinity", len) == 0 ||
strncmp(str, "all", len) == 0 ||
strncmp(str, "maximum", len) == 0)
{
return (Infinity);
}
else if (alldigits(str))
{
return (atoi(str));
}
else
{
return (Invalid);
}
}
return (0);
}
/*
* itoa - convert integer (decimal) to ascii string for positive numbers
* only (we don't bother with negative numbers since we know we
* don't use them).
*/
/*
* How do we know that 16 will suffice? Because the biggest number that we
* will ever convert will be 2^32-1, which is 10 digits.
*/
char *
itoa(int val)
{
register char *ptr;
static char buffer[16]; /* result is built here */
/*
* 16 is sufficient since the largest number we will ever convert will be
* 2^32-1, which is 10 digits.
*/
ptr = buffer + sizeof(buffer);
*--ptr = '\0';
if (val == 0)
{
*--ptr = '0';
}
else
while (val != 0)
{
*--ptr = (val % 10) + '0';
val /= 10;
}
return (ptr);
}
/*
* itoa7(val) - like itoa, except the number is right justified in a 7
* character field. This code is a duplication of itoa instead of
* a front end to a more general routine for efficiency.
*/
char *
itoa7(uid_t val)
{
register char *ptr;
static char buffer[16]; /* result is built here */
/*
* 16 is sufficient since the largest number we will ever convert will be
* 2^32-1, which is 10 digits.
*/
ptr = buffer + sizeof(buffer);
*--ptr = '\0';
if (val == 0)
{
*--ptr = '0';
}
else
while (val != 0)
{
*--ptr = (val % 10) + '0';
val /= 10;
}
while (ptr > buffer + sizeof(buffer) - 7)
{
*--ptr = ' ';
}
return (ptr);
}
/*
* digits(val) - return number of decimal digits in val. Only works for
* positive numbers. If val < 0 then digits(val) == 0, but
* digits(0) == 1.
*/
int
digits(int val)
{
register int cnt = 0;
if (val == 0)
{
return 1;
}
while (val > 0)
{
cnt++;
val /= 10;
}
return (cnt);
}
/*
* printable(char *str) - make the string pointed to by "str" into one that is
* printable (i.e.: all ascii), by converting all non-printable
* characters into '?'. Replacements are done in place and a pointer
* to the original buffer is returned.
*/
char *
printable(char *str)
{
register char *ptr;
register char ch;
ptr = str;
while ((ch = *ptr) != '\0')
{
if (!isprint(ch))
{
*ptr = '?';
}
ptr++;
}
return (str);
}
/*
* strecpy(to, from) - copy string "from" into "to" and return a pointer
* to the END of the string "to".
*/
char *
strecpy(char *to, char *from)
{
while ((*to++ = *from++) != '\0');
return (--to);
}
/*
* char *
* homogenize(char *str)
*
* Remove unwanted characters from "str" and make everything lower case.
* Newly allocated string is returned: the original is not altered.
*/
char *
homogenize(char *str)
{
char *ans;
char *fr;
char *to;
int ch;
to = fr = ans = strdup(str);
while ((ch = *fr++) != '\0')
{
if (isalnum(ch))
{
*to++ = tolower(ch);
}
}
*to = '\0';
return ans;
}
/*
* string_index(string, array) - find string in array and return index
*/
int
string_index(char *string, char **array)
{
register int i = 0;
while (*array != NULL)
{
if (strcmp(string, *array) == 0)
{
return (i);
}
array++;
i++;
}
return (-1);
}
/*
* char *string_list(char **strings)
*
* Create a comma-separated list of the strings in the NULL-terminated
* "strings". Returned string is malloc-ed and should be freed when the
* caller is done. Note that this is not an efficient function.
*/
char *
string_list(char **strings)
{
int cnt = 0;
char **pp;
char *p;
char *result = NULL;
char *resp = NULL;
pp = strings;
while ((p = *pp++) != NULL)
{
cnt += strlen(p) + 2;
}
if (cnt > 0)
{
resp = result = (char *) malloc(cnt);
pp = strings;
while ((p = *pp++) != NULL)
{
resp = strecpy(resp, p);
if (*pp != NULL)
{
resp = strecpy(resp, ", ");
}
}
}
return result;
}
/*
* argparse(line, cntp) - parse arguments in string "line", separating them
* out into an argv-like array, and setting *cntp to the number of
* arguments encountered. This is a simple parser that doesn't understand
* squat about quotes.
*/
char **
argparse(char *line, int *cntp)
{
register char *from;
register char *to;
register int cnt;
register int ch;
int length;
int lastch;
register char **argv;
char **argarray;
char *args;
/*
* unfortunately, the only real way to do this is to go thru the input
* string twice.
*/
/* step thru the string counting the white space sections */
from = line;
lastch = cnt = length = 0;
while ((ch = *from++) != '\0')
{
length++;
if (ch == ' ' && lastch != ' ')
{
cnt++;
}
lastch = ch;
}
/*
* add three to the count: one for the initial "dummy" argument, one for
* the last argument and one for NULL
*/
cnt += 3;
/* allocate a char * array to hold the pointers */
argarray = (char **) malloc(cnt * sizeof(char *));
/* allocate another array to hold the strings themselves */
args = (char *) malloc(length + 2);
/* initialization for main loop */
from = line;
to = args;
argv = argarray;
lastch = '\0';
/* create a dummy argument to keep getopt happy */
*argv++ = to;
*to++ = '\0';
cnt = 2;
/* now build argv while copying characters */
*argv++ = to;
while ((ch = *from++) != '\0')
{
if (ch != ' ')
{
if (lastch == ' ')
{
*to++ = '\0';
*argv++ = to;
cnt++;
}
*to++ = ch;
}
lastch = ch;
}
*to++ = '\0';
/* set cntp and return the allocated array */
*cntp = cnt;
return (argarray);
}
/*
* percentages(cnt, out, new, old, diffs) - calculate percentage change
* between array "old" and "new", putting the percentages i "out".
* "cnt" is size of each array and "diffs" is used for scratch space.
* The array "old" is updated on each call.
* The routine assumes modulo arithmetic. This function is especially
* useful on BSD mchines for calculating cpu state percentages.
*/
long
percentages(int cnt, int64_t * out, int64_t * new, int64_t * old,
int64_t * diffs)
{
register int i;
register int64_t change;
register int64_t total_change;
register int64_t * dp;
int64_t half_total;
/* initialization */
total_change = 0;
dp = diffs;
/* calculate changes for each state and the overall change */
for (i = 0; i < cnt; i++)
{
if ((change = *new - *old) < 0)
{
/* this only happens when the counter wraps */
change = (int64_t) ((int64_t) * new - (int64_t) * old);
}
total_change += (*dp++ = change);
*old++ = *new++;
}
/* avoid divide by zero potential */
if (total_change == 0)
{
total_change = 1;
}
/* calculate percentages based on overall change, rounding up */
half_total = total_change / 2l;
for (i = 0; i < cnt; i++)
{
*out++ = (int64_t) ((*diffs++ * 1000 + half_total) / total_change);
}
/* return the total in case the caller wants to use it */
return (total_change);
}
/*
* errmsg(errnum) - return an error message string appropriate to the
* error number "errnum". This is a substitute for the System V
* function "strerror". There appears to be no reliable way to
* determine if "strerror" exists at compile time, so I make do
* by providing something of similar functionality. For those
* systems that have strerror and NOT errlist, define
* -DHAVE_STRERROR in the module file and this function will
* use strerror.
*/
/* externs referenced by errmsg */
#ifndef HAVE_STRERROR
#if !HAVE_DECL_SYS_ERRLIST
extern char *sys_errlist[];
#endif
extern int sys_nerr;
#endif
char *
errmsg(int errnum)
{
#ifdef HAVE_STRERROR
char *msg = strerror(errnum);
if (msg != NULL)
{
return msg;
}
#else
if (errnum > 0 && errnum < sys_nerr)
{
return ((char *) (sys_errlist[errnum]));
}
#endif
return ("No error");
}
/* format_percent(v) - format a double as a percentage in a manner that
* does not exceed 5 characters (excluding any trailing
* percent sign). Since it is possible for the value
* to exceed 100%, we format such values with no fractional
* component to fit within the 5 characters.
*/
char *
format_percent(double v)
{
static char result[10];
/* enumerate the possibilities */
if (v < 0 || v >= 100000.)
{
/* we dont want to try extreme values */
strcpy(result, " ???");
}
else if (v > 99.99)
{
sprintf(result, "%5.0f", v);
}
else
{
sprintf(result, "%5.2f", v);
}
return result;
}
/* format_time(seconds) - format number of seconds into a suitable
* display that will fit within 6 characters. Note that this
* routine builds its string in a static area. If it needs
* to be called more than once without overwriting previous data,
* then we will need to adopt a technique similar to the
* one used for format_k.
*/
/* Explanation:
We want to keep the output within 6 characters. For low values we use
the format mm:ss. For values that exceed 999:59, we switch to a format
that displays hours and fractions: hhh.tH. For values that exceed
999.9, we use hhhh.t and drop the "H" designator. For values that
exceed 9999.9, we use "???".
*/
char *
format_time(long seconds)
{
static char result[10];
/* sanity protection */
if (seconds < 0 || seconds > (99999l * 360l))
{
strcpy(result, " ???");
}
else if (seconds >= (1000l * 60l))
{
/* alternate (slow) method displaying hours and tenths */
sprintf(result, "%5.1fH", (double) seconds / (double) (60l * 60l));
/*
* It is possible that the sprintf took more than 6 characters. If so,
* then the "H" appears as result[6]. If not, then there is a \0 in
* result[6]. Either way, it is safe to step on.
*/
result[6] = '\0';
}
else
{
/* standard method produces MMM:SS */
/* we avoid printf as must as possible to make this quick */
sprintf(result, "%3ld:%02ld", seconds / 60l, seconds % 60l);
}
return (result);
}
#define NUM_STRINGS 8
/*
* format_b(amt) - format a byte memory value, returning a string
* suitable for display. Returns a pointer to a static
* area that changes each call. "amt" is converted to a
* string with a trailing "B". If "amt" is 10000 or greater,
* then it is formatted as megabytes (rounded) with a
* trailing "K". And so on...
*/
char *
format_b(long long amt)
{
static char retarray[NUM_STRINGS][32];
static int index = 0;
register char *ret;
register char tag = 'B';
ret = retarray[index];
index = (index + 1) % NUM_STRINGS;
if (amt >= 10000)
{
amt = (amt + 512) / 1024;
tag = 'K';
if (amt >= 10000)
{
amt = (amt + 512) / 1024;
tag = 'B';
if (amt >= 10000)
{
amt = (amt + 512) / 1024;
tag = 'G';
}
}
}
snprintf(ret, sizeof(retarray[index]) - 1, "%lld%c", amt, tag);
return (ret);
}
/*
* format_k(amt) - format a kilobyte memory value, returning a string
* suitable for display. Returns a pointer to a static
* area that changes each call. "amt" is converted to a
* string with a trailing "K". If "amt" is 10000 or greater,
* then it is formatted as megabytes (rounded) with a
* trailing "M".
*/
/*
* Compromise time. We need to return a string, but we don't want the
* caller to have to worry about freeing a dynamically allocated string.
* Unfortunately, we can't just return a pointer to a static area as one
* of the common uses of this function is in a large call to sprintf where
* it might get invoked several times. Our compromise is to maintain an
* array of strings and cycle thru them with each invocation. We make the
* array large enough to handle the above mentioned case. The constant
* NUM_STRINGS defines the number of strings in this array: we can tolerate
* up to NUM_STRINGS calls before we start overwriting old information.
* Keeping NUM_STRINGS a power of two will allow an intelligent optimizer
* to convert the modulo operation into something quicker. What a hack!
*/
char *
format_k(long amt)
{
static char retarray[NUM_STRINGS][32];
static int index = 0;
register char *ret;
register char tag = 'K';
ret = retarray[index];
index = (index + 1) % NUM_STRINGS;
if (amt >= 10000)
{
amt = (amt + 512) / 1024;
tag = 'M';
if (amt >= 10000)
{
amt = (amt + 512) / 1024;
tag = 'G';
}
}
snprintf(ret, sizeof(retarray[index]) - 1, "%ld%c", amt, tag);
return (ret);
}
static int debug_on = 0;
#ifdef DEBUG
FILE *debugfile;
#endif
void
debug_set(int i)
{
debug_on = i;
#ifdef DEBUG
debugfile = fopen("/tmp/top.debug", "w");
#endif
}
#ifdef DEBUG
void
xdprintf(char *fmt,...)
{
va_list argp;
va_start(argp, fmt);
if (debug_on)
{
vfprintf(debugfile, fmt, argp);
fflush(stdout);
}
va_end(argp);
}
#endif
07070100000048000081A400000000000000000000000166622DE800000402000000000000000000000000000000000000001500000000pg_top-4.1.1/utils.h/*
* Top users/processes display for Unix
* Version 3
*
* This program may be freely redistributed,
* but this entire comment MUST remain intact.
*
* Copyright (c) 1984, 1989, William LeFebvre, Rice University
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
*/
#ifndef _UTILS_H_
#define _UTILS_H_
/* prototypes for functions found in utils.c */
int atoiwi(char *);
char *itoa(int);
char *itoa7(uid_t);
int digits(int);
char *printable(char *);
char *strecpy(char *, char *);
char *homogenize(char *);
int string_index(char *, char **);
char **argparse(char *, int *);
long percentages(int, int64_t *, int64_t *, int64_t *, int64_t *);
char *errmsg(int);
char *format_percent(double);
char *format_time(long);
char *format_b(long long);
char *format_k(long);
char *string_list(char **);
void debug_set(int);
#ifdef DEBUG
#define dprintf xdprintf
void xdprintf(char *fmt,...);
#else
#define dprintf if (0)
#endif
#endif /* _UTILS_H_ */
07070100000049000081A400000000000000000000000166622DE80000018B000000000000000000000000000000000000001700000000pg_top-4.1.1/version.c/*
* Top users/processes display for Unix
* Version 3
*
* This program may be freely redistributed,
* but this entire comment MUST remain intact.
*
* Copyright (c) 1984, 1989, William LeFebvre, Rice University
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
*/
#include "config.h"
#include "pg_top.h"
char *
version_string()
{
return (PACKAGE_VERSION);
}
0707010000004A000081A400000000000000000000000166622DE80000018F000000000000000000000000000000000000001700000000pg_top-4.1.1/version.h/*
* Top users/processes display for Unix
* Version 3
*
* This program may be freely redistributed,
* but this entire comment MUST remain intact.
*
* Copyright (c) 1984, 1989, William LeFebvre, Rice University
* Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University
*/
#ifndef _VERSION_H_
#define _VERSION_H_
char *version_string();
#endif /* _VERSION_H_ */
07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!1005 blocks