pax_global_header00006660000000000000000000000064127104647240014521gustar00rootroot0000000000000052 comment=bbf1f127651ae80f82334b2ea6b21f0317a43d84 pgtop/000077500000000000000000000000001271046472400122165ustar00rootroot00000000000000pgtop/.gitignore000066400000000000000000000002721271046472400142070ustar00rootroot00000000000000*.sw? *.o *.in *.tar.gz *.tar.bz2 .deps Makefile aclocal.m4 autom4te.cache config.h config.log config.status configure depcomp install-sh missing pg_top pg_top.1 sigdesc.h stamp-h1 tags pgtop/.hgignore000066400000000000000000000004271271046472400140240ustar00rootroot00000000000000.hgignore Makefile autom4te.cache/output.0 autom4te.cache/requests autom4te.cache/traces.0 color.o commands.o config.h config.log config.status configure cscope.out display.o m_freebsd.o m_linux.o pg.o screen.o sigdesc.h sprompt.o ptop ptop.1 ptop.o username.o utils.o version.o pgtop/.hgtags000066400000000000000000000016751271046472400135050ustar00rootroot00000000000000df86e76181bdb55c4d57c83ffee7a725298b0715 v3.6.1-pre1 2b652d67766f1237d011a8407d74abb3bebb4744 v3.6.1-pre2 68151730ae5082df38cd8c11984e1a8ec220b7d3 v3.6.1-pre3 6b022ce8cde37eee7abbc49f2997ed7f53b418a6 v3.6.1-pre4 8ddf0dc8ab400b62bb5d9421b2e919077acf2833 v3.6.1-pre5 df892c21fba98e5091eea25e575b8fda76e5baeb v3.6.1-pre6 f2642c619034cbd5b2bf0da4190c49fb67205d3b v3.6.1-pre7 ccb5f6e191d92ec28c1e72c56a129d71d8c1fce9 v3.6.1-pre8 0ff27fb6296cd7409cf25f0643948e2db525cf2f v3.6.1-pre9 d5e7f34f35580d0b7269318e8b28e36b204dbec3 v3.6.1-beta1 7749cfe715574c95951240f3935b2ab5cb94a761 v3.6.1-beta2 3fb794a103750fdf06c7ed87c3a67e32eff3890a v3.6.1-beta3 f790022562107de9be002ceecde4a3b95852b6bc v3.6.1-beta4 38871dc12f7fac79970dbf0fda537898de24748a v3.6.1 fb93596f083d18fbf59f2280090b2ce0975bee28 v3.6.2-beta1 4ec94ee976474ead21345b663c57e42c380d630a v3.6.2-beta2 8a90ed8ffdd2aca755130864dd02f7c61701f0e8 v3.6.2-beta3 40305117300adb4157e69880135607ec9ce59107 v3.6.2-beta4 pgtop/FAQ000066400000000000000000000062651271046472400125610ustar00rootroot00000000000000 pg_top Version 3.7.0 Frequently Asked Questions and their Answers GENERAL 1. What is pg_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 user table statistics. * View user index statistics. 2. Where do I get the latest version of pg_top? If you have git, you can checkout the latest at: git clone git://git.postgresql.org/git/pg_top.git 3. Is there a web page for pg_top? http://ptop.projects.postgresql.org/ 4. Is there a mailing list or on-line bulletin board for pg_top? Subscribe or browse the archives of ptop-hackers at: http://lists.pgfoundry.org/mailman/listinfo/ptop-hackers 5. What about Y2K/Year 2038 compliance? A full statement concerning top and the year 2000 can be found in the file "Y2K" included with the distribution. 6. Will there be another major release of pg_top? Will there be a pg_top version 4? We hope so! Depends on how much fun we have. :) 7. We just upgraded our operating system to a new version and pg_top broke. What should we do? Recompile it. pg_top is very sensitive to changes in internal kernel data structures. It is not uncommon for a new version of the operating system to include changes to kernel data structures. RUNNING 8. I just finished compiling top and it works fine for root, but when I try to run it as a regular user it either complains about files it can't open or it doesn't display all the information it should. Did I do something wrong? Well, you're just not done. On many operating systems today, access to many of the kernel memory devices and other system files is restricted to either root or a particular group. The configure script figures this out (usually) and makes sure that the "install" rule in the Makefile will install top so that anyone can run it successfully. However, you have to *install* it first. Do this with the command "make install". 9. pg_top is (not) displaying idle processes and I don't (do) want it to. This default has only changed about a dozen times, and I finally got tired of people whining about it. Go read the manual page for the current version and pay special attention to the description of the "PG_TOP" environment variable. 10. The cpu state percentages are all wrong, indicating that my machine is using 95% system time when it is clearly idle. What's wrong? This can happen if you compiled with gcc using the wrong include files. See the previous question. STILL STUCK 11. I'm still stuck. To whom do I report problems with top? If after reading all of this file and checking everything you can you are still stuck, then please refer to the mailing to ask further questions: http://lists.pgfoundry.org/mailman/listinfo/ptop-hackers pgtop/HISTORY000066400000000000000000000043061271046472400133050ustar00rootroot00000000000000Release Notes Release 3.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 Release 3.6.2 Changes * 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. Release 3.6.1 Changes * 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. pgtop/INSTALL000066400000000000000000000035151271046472400132530ustar00rootroot00000000000000 pg_top Version 3.6.1 Mark Wong and a cast of ... a few INSTALLATION Configuration and installation of pg_top is easy. pg_top version 3.6 comes with a configure script generated by gnu autoconf. After unpacking the tar file, simply run "./configure". The script will automatically perform a series of checks on the system and determine what settings are appropriate for the Makefile and certain include files. Once configure completes, simply type "make install" and pg_top will be compiled and installed. By default, the installation location is /usr/local/bin. You can change the destination location with the --prefix option to configure. pg_config must be in your path in order to install pg_top. In addition to the standard options, pg_top's configure script supports the following: --with-module=name Force the use of a particular module. Modules are located in the subdirectory "machine". A module's name is derived from the file's basename without the leading "m_". --with-ext=name Compile with the extension "name", found in the subdirectory "ext". At the present time, there are no extensions in the standard distribution. --enable-debug --disable-debug Default off. Include debugging output in the compilation, which can be seen with the -D switch. --enable-color --disable-color Default on. Include code that allows for the use of color in the output display. Use --disable-color if you do not want this feature compiled in to the code. The configure script also recognizes the spelling "colour". --enable-kill --disable-kill Default on. Include code that allows for renicing and sending signals to processes from within pg_top (the 'kill' and 'renice' commands). Use --disable-kill if you do not want this feature compiled in to the code. pgtop/LICENSE000066400000000000000000000027541271046472400132330ustar00rootroot00000000000000Copyright (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. pgtop/Makefile.am000066400000000000000000000031621271046472400142540ustar00rootroot00000000000000AUTOMAKE_OPTIONS = foreign AM_CPPFLAGS = $(DBCFLAGS) AM_LDFLAGS = $(DBLDFLAGS) BUILT_SOURCES = sigdesc.h CLEANFILES = sigdesc.h sigdesc.h: sigconv.awk $(SIGNAL_H) $(AWK) -f $(srcdir)/sigconv.awk $(SIGNAL_H) > sigdesc.h bin_PROGRAMS = pg_top pg_top_SOURCES = color.c commands.c display.c getopt.c screen.c sprompt.c \ pg.c pg_top.c username.c utils.c version.c machine/m_remote.c \ machine/m_common.c EXTRA_pg_top_SOURCES = \ machine/m_aix43.c \ machine/m_aix5.c \ machine/m_decosf1.c \ machine/m_freebsd.c \ machine/m_hpux10.c \ machine/m_hpux7.c \ machine/m_hpux9.c \ machine/m_irix5.c \ machine/m_irixsgi.c \ machine/m_linux.c \ machine/m_linuxthr.c \ machine/m_macosx.c \ machine/m_netbsd.c \ machine/m_openbsd.c \ machine/m_sco5.c \ machine/m_sunos4.c \ machine/m_sunos5.c \ machine/m_svr4.c \ machine/m_svr5.c pg_top_LDADD = @OBJ@ pg_top_DEPENDENCIES = @OBJ@ man_MANS = pg_top.1 EXTRA_DIST = \ FAQ \ HISTORY \ INSTALL \ LICENSE \ Porting \ README \ TODO \ Y2K \ boolean.h \ c.h \ color.h \ commands.h \ configure \ display.h \ help.h \ layout.h \ loadavg.h \ machine.h \ message.h \ os.h \ pg.h \ pg_trace.h \ port.h \ pg_top.h \ remote.h \ screen.h \ sigconv.awk \ sigdesc.h \ username.h \ utils.h \ version.h \ machine/m_decosf1.man \ machine/m_freebsd.man \ machine/m_linux.man \ machine/m_netbsd.man \ machine/m_sco5.man \ machine/m_svr4.man \ machine/m_svr5.man \ machine/m_hpux9.man \ machine/m_hpux10.man \ machine/m_linuxthr.man \ machine/m_sunos5.man \ machine/m_macosx.man \ machine/m_sunos4.man pgtop/Porting000066400000000000000000000233471271046472400135740ustar00rootroot00000000000000Instructions 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 disk_info struct is filled in by get_disk_info. struct disk_info { int64_t size; int64_t avail; }; 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 uid_t proc_owner(pid_t) Returns the uid owner of the process specified by the pid argument. This function is VERY IMPORTANT. If it fails to do its job, then pg_top may pose a security risk. void get_io_info(struct io_info *) void get_disk_info(struct disk_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. Top relies on the existence of a function called "setpriority" to change a process's priority. This exists as a kernel call on most 4.3 BSD derived Unixes. If neither your operating system nor your C library supplies such a function, then you will need to add one to the module. It is defined as follows: int setpriority (int dummy, int who, int niceval) For the purposes of pg_top, the first argument is meaningless. The second is the pid and the third is the new nice value. This function should behave just like a kernel call, setting errno and returning -1 in case of an error. This function MUST check to make sure that a non-root user does not specify a nice value less than the process's current value. If it detects such a condition, it should set errno to EACCES and return -1. Other possible ERRNO values: ESRCH when pid "who" does not exist, EPERM when the invoker is not root and not the same as the process owner. Note that pg_top checks process ownership and should never call setpriority when the invoker's uid is not root and not the same as the process's owner uid. 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. pgtop/README000066400000000000000000000042751271046472400131060ustar00rootroot00000000000000 pg_top Version 3.7.0 Mark Wong and a cast of ... a few 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 user table statistics. * View user index statistics. CAVEAT: version 3 of pg_top has internal commands that kill and renice processes. Although I have taken steps to insure that pg_top makes appropriate checks with these commands, I cannot guarantee that these internal commands are totally secure. IF YOU INSTALL pg_top SET-USER-ID TO ROOT, YOU DO SO AT YOUR OWN RISK! I realize that some operating systems will require pg_top to run setuid root, and I will do everything I can to make sure that pg_top is a secure setuid program. To compile and install "pg_top", read the file "INSTALL" 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 send the change to the pg_top mailing list. 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_protab here: http://pgxn.org/dist/pg_proctab/ Be sure to read the FAQ enclosed with the distrubution. It contains answers to the most commonly asked questions about the configuration, installation, and operation of pg_top. AVAILABILITY Project home page: http://ptop.projects.postgresql.org/ If you have git, you can download the source code: git clone git://git.postgresql.org/git/pg_top.git 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. AUTHOR Mark Wong pgtop/TODO000066400000000000000000000032541271046472400127120ustar00rootroot00000000000000* 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. * Renamed number of processs to be number of connections since that is more accurate: 5 processes: 1 running, 4 sleeping to 5 connections: 1 running, 4 sleeping * Display summary statistics for connections such as total number of idles connections, etc. * Display summary statistics for locks such as total number of granted locks, total number of ungranted locks, etc. * Display summary statistics for listener information such as total number of entries in pg_listener, distinct number of entries in pg_listener, etc. * Display screen database statistics: SELECT datname, SUM(numbackends) AS numbackends, SUM(blks_read) AS reads, SUM(tup_fetched) AS fetches, SUM(tup_inserte + tup_update + tup_deleted) AS alterations FROM pg_stat_database GROUP BY datname HAVING SUM(numbackends) > 0 ORDER BY (SUM(blks_read) + SUM(tup_fetched) + SUM(tup_inserted + tup_updated + tup_deleted)) DESC LIMIT 5; datname | numbackends | reads | fetches | alterations -------------+-------------+-------+---------+------------- pgbenchtest | 1 | 3085 | 321558 | 485894 * Display the size of each database. * Make new TPS and i/o activity and disk space usage work on remote connections. pgtop/Y2K000066400000000000000000000023151271046472400125470ustar00rootroot00000000000000pg_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. pgtop/autogen.sh000077500000000000000000000001551271046472400142200ustar00rootroot00000000000000#!/bin/sh aclocal || exit 1 autoheader || exit 1 automake --add-missing --copy || exit 1 autoconf || exit 1 pgtop/boolean.h000066400000000000000000000003031271046472400140020ustar00rootroot00000000000000#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_ */ pgtop/c.h000066400000000000000000000546331271046472400126240ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * 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 * ------- ------------------------------------------------ * 0) pg_config.h and standard system headers * 1) hacks to cope with non-ANSI C compilers * 2) bool, true, false, TRUE, FALSE, NULL * 3) standard system types * 4) IsValid macros for system types * 5) offsetof, lengthof, endof, alignment * 6) widely useful macros * 7) random stuff * 8) system-specific hacks * * 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 "pg_config.h" #include "pg_config_manual.h" /* must be after pg_config.h */ #if !defined(WIN32) && !defined(__CYGWIN__) /* win32 will include further * down */ #include "pg_config_os.h" /* must be before any system header files */ #endif #include "postgres_ext.h" #include "pg_trace.h" #if _MSC_VER >= 1400 #define errcode __msvc_errcode #include #undef errcode #endif #include #include #include #include #include #ifdef HAVE_STRINGS_H #include #endif #include #include #if defined(WIN32) || defined(__CYGWIN__) #include /* ensure O_BINARY is available */ #endif #ifdef HAVE_SUPPORTDEFS_H #include #endif #if defined(WIN32) || defined(__CYGWIN__) /* We have to redefine some system functions after they are included above. */ #include "pg_config_os.h" #endif /* Must be before gettext() games below */ #include #define _(x) gettext((x)) #ifdef ENABLE_NLS #include #else #define gettext(x) (x) #endif /* * Use this to mark strings to be translated by gettext, in places where * you don't want an actual function call to occur (eg, constant tables). */ #define gettext_noop(x) (x) /* ---------------------------------------------------------------- * Section 1: hacks to cope with non-ANSI C compilers * * type prefixes (const, signed, volatile, inline) are handled in pg_config.h. * ---------------------------------------------------------------- */ /* * CppAsString * Convert the argument to a string, using the C preprocessor. * CppConcat * Concatenate two arguments together, using the C preprocessor. * * Note: the standard Autoconf macro AC_C_STRINGIZE actually only checks * whether #identifier works, but if we have that we likely have ## too. */ #if defined(HAVE_STRINGIZE) #define CppAsString(identifier) #identifier #define CppConcat(x, y) x##y #else /* !HAVE_STRINGIZE */ #define CppAsString(identifier) "identifier" /* * CppIdentity -- On Reiser based cpp's this is used to concatenate * two tokens. That is * CppIdentity(A)B ==> AB * We renamed it to _private_CppIdentity because it should not * be referenced outside this file. On other cpp's it * produces A B. */ #define _priv_CppIdentity(x)x #define CppConcat(x, y) _priv_CppIdentity(x)y #endif /* !HAVE_STRINGIZE */ /* * dummyret is used to set return values in macros that use ?: to make * assignments. gcc wants these to be void, other compilers like char */ #ifdef __GNUC__ /* GNU cc */ #define dummyret void #else #define dummyret char #endif #ifndef __GNUC__ #define __attribute__(_arg_) #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 /* * NULL * Null pointer. */ #ifndef NULL #define NULL ((void *) 0) #endif /* ---------------------------------------------------------------- * Section 3: standard system types * ---------------------------------------------------------------- */ /* * Pointer * Variable holding address of any memory resident object. * * XXX Pointer arithmetic is done with this, so it can't be void * * under "true" ANSI compilers. */ typedef char *Pointer; /* * intN * Signed integer, EXACTLY N BITS IN SIZE, * used for numerical computations and the * frontend/backend protocol. */ #ifndef HAVE_INT8 typedef signed char int8; /* == 8 bits */ typedef signed short int16; /* == 16 bits */ typedef signed int int32; /* == 32 bits */ #endif /* not HAVE_INT8 */ /* * uintN * Unsigned integer, EXACTLY N BITS IN SIZE, * used for numerical computations and the * frontend/backend protocol. */ #ifndef HAVE_UINT8 typedef unsigned char uint8; /* == 8 bits */ typedef unsigned short uint16; /* == 16 bits */ typedef unsigned int uint32; /* == 32 bits */ #endif /* not HAVE_UINT8 */ /* * bitsN * Unit of bitwise operation, AT LEAST N BITS IN SIZE. */ typedef uint8 bits8; /* >= 8 bits */ typedef uint16 bits16; /* >= 16 bits */ typedef uint32 bits32; /* >= 32 bits */ /* * floatN * Floating point number, AT LEAST N BITS IN SIZE, * used for numerical computations. * * Since sizeof(floatN) may be > sizeof(char *), always pass * floatN by reference. * * XXX: these typedefs are now deprecated in favor of float4 and float8. * They will eventually go away. */ typedef float float32data; typedef double float64data; typedef float *float32; typedef double *float64; /* * 64-bit integers */ #ifdef HAVE_LONG_INT_64 /* Plain "long int" fits, use it */ #ifndef HAVE_INT64 typedef long int int64; #endif #ifndef HAVE_UINT64 typedef unsigned long int uint64; #endif #elif defined(HAVE_LONG_LONG_INT_64) /* We have working support for "long long int", use that */ #ifndef HAVE_INT64 typedef long long int int64; #endif #ifndef HAVE_UINT64 typedef unsigned long long int uint64; #endif #else /* not HAVE_LONG_INT_64 and not * HAVE_LONG_LONG_INT_64 */ /* Won't actually work, but fall back to long int so that code compiles */ #ifndef HAVE_INT64 typedef long int int64; #endif #ifndef HAVE_UINT64 typedef unsigned long int uint64; #endif #define INT64_IS_BUSTED #endif /* not HAVE_LONG_INT_64 and not * HAVE_LONG_LONG_INT_64 */ /* Decide if we need to decorate 64-bit constants */ #ifdef HAVE_LL_CONSTANTS #define INT64CONST(x) ((int64) x##LL) #define UINT64CONST(x) ((uint64) x##ULL) #else #define INT64CONST(x) ((int64) x) #define UINT64CONST(x) ((uint64) x) #endif /* Select timestamp representation (float8 or int64) */ #if defined(USE_INTEGER_DATETIMES) && !defined(INT64_IS_BUSTED) #define HAVE_INT64_TIMESTAMP #endif /* sig_atomic_t is required by ANSI C, but may be missing on old platforms */ #ifndef HAVE_SIG_ATOMIC_T typedef int sig_atomic_t; #endif /* * Size * Size of any memory resident object, as returned by sizeof. */ typedef size_t Size; /* * Index * Index into any memory resident array. * * Note: * Indices are non negative. */ typedef unsigned int Index; /* * Offset * Offset into any memory resident array. * * Note: * This differs from an Index in that an Index is always * non negative, whereas Offset may be negative. */ typedef signed int Offset; /* * Common Postgres datatype names (as used in the catalogs) */ typedef int16 int2; typedef int32 int4; typedef float float4; typedef double float8; /* * Oid, RegProcedure, TransactionId, SubTransactionId, MultiXactId, * CommandId */ /* typedef Oid is in postgres_ext.h */ /* * regproc is the type name used in the include/catalog headers, but * RegProcedure is the preferred name in C code. */ typedef Oid regproc; typedef regproc RegProcedure; typedef uint32 TransactionId; typedef uint32 SubTransactionId; #define InvalidSubTransactionId ((SubTransactionId) 0) #define TopSubTransactionId ((SubTransactionId) 1) /* MultiXactId must be equivalent to TransactionId, to fit in t_xmax */ typedef TransactionId MultiXactId; typedef uint32 MultiXactOffset; typedef uint32 CommandId; #define FirstCommandId ((CommandId) 0) /* * Array indexing support */ #define MAXDIM 6 typedef struct { int indx[MAXDIM]; } IntArray; /* ---------------- * Variable-length datatypes all share the 'struct varlena' header. * * NOTE: for TOASTable types, this is an oversimplification, since the value * may be compressed or moved out-of-line. However datatype-specific routines * are mostly content to deal with de-TOASTed values only, and of course * client-side routines should never see a TOASTed value. See postgres.h for * details of the TOASTed form. * ---------------- */ struct varlena { int32 vl_len; char vl_dat[1]; }; #define VARHDRSZ ((int32) sizeof(int32)) /* * These widely-used datatypes are just a varlena header and the data bytes. * There is no terminating null or anything like that --- the data length is * always VARSIZE(ptr) - VARHDRSZ. */ typedef struct varlena bytea; typedef struct varlena text; typedef struct varlena BpChar; /* blank-padded char, ie SQL char(n) */ typedef struct varlena VarChar; /* var-length char, ie SQL varchar(n) */ /* * Specialized array types. These are physically laid out just the same * as regular arrays (so that the regular array subscripting code works * with them). They exist as distinct types mostly for historical reasons: * they have nonstandard I/O behavior which we don't want to change for fear * of breaking applications that look at the system catalogs. There is also * an implementation issue for oidvector: it's part of the primary key for * pg_proc, and we can't use the normal btree array support routines for that * without circularity. */ typedef struct { int32 size; /* these fields must match ArrayType! */ int ndim; /* always 1 for int2vector */ int32 dataoffset; /* always 0 for int2vector */ Oid elemtype; int dim1; int lbound1; int2 values[1]; /* VARIABLE LENGTH ARRAY */ } int2vector; /* VARIABLE LENGTH STRUCT */ typedef struct { int32 size; /* these fields must match ArrayType! */ int ndim; /* always 1 for oidvector */ int32 dataoffset; /* always 0 for oidvector */ Oid elemtype; int dim1; int lbound1; Oid values[1]; /* VARIABLE LENGTH ARRAY */ } oidvector; /* VARIABLE LENGTH STRUCT */ /* * We want NameData to have length NAMEDATALEN and int alignment, * because that's how the data type 'name' is defined in pg_type. * Use a union to make sure the compiler agrees. Note that NAMEDATALEN * must be a multiple of sizeof(int), else sizeof(NameData) will probably * not come out equal to NAMEDATALEN. */ typedef union nameData { char data[NAMEDATALEN]; int alignmentDummy; } NameData; typedef NameData *Name; #define NameStr(name) ((name).data) /* * Support macros for escaping strings. escape_backslash should be TRUE * if generating a non-standard-conforming string. Prefixing a string * with ESCAPE_STRING_SYNTAX guarantees it is non-standard-conforming. * Beware of multiple evaluation of the "ch" argument! */ #define SQL_STR_DOUBLE(ch, escape_backslash) \ ((ch) == '\'' || ((ch) == '\\' && (escape_backslash))) #define ESCAPE_STRING_SYNTAX 'E' /* ---------------------------------------------------------------- * Section 4: IsValid macros for system types * ---------------------------------------------------------------- */ /* * BoolIsValid * True iff bool is valid. */ #define BoolIsValid(boolean) ((boolean) == false || (boolean) == true) /* * PointerIsValid * True iff pointer is valid. */ #define PointerIsValid(pointer) ((void*)(pointer) != NULL) /* * PointerIsAligned * True iff pointer is properly aligned to point to the given type. */ #define PointerIsAligned(pointer, type) \ (((long)(pointer) % (sizeof (type))) == 0) #define OidIsValid(objectId) ((bool) ((objectId) != InvalidOid)) #define RegProcedureIsValid(p) OidIsValid(p) /* ---------------------------------------------------------------- * Section 5: offsetof, lengthof, endof, alignment * ---------------------------------------------------------------- */ /* * offsetof * Offset of a structure/union field within that structure/union. * * XXX This is supposed to be part of stddef.h, but isn't on * some systems (like SunOS 4). */ #ifndef offsetof #define offsetof(type, field) ((long) &((type *)0)->field) #endif /* offsetof */ /* * lengthof * Number of elements in an array. */ #define lengthof(array) (sizeof (array) / sizeof ((array)[0])) /* * endof * Address of the element one past the last in an array. */ #define endof(array) (&(array)[lengthof(array)]) /* ---------------- * Alignment macros: align a length or address appropriately for a given type. * * There used to be some incredibly crufty platform-dependent hackery here, * but now we rely on the configure script to get the info for us. Much nicer. * * NOTE: TYPEALIGN will not work if ALIGNVAL is not a power of 2. * That case seems extremely unlikely to occur in practice, however. * ---------------- */ #define TYPEALIGN(ALIGNVAL,LEN) \ (((long) (LEN) + ((ALIGNVAL) - 1)) & ~((long) ((ALIGNVAL) - 1))) #define SHORTALIGN(LEN) TYPEALIGN(ALIGNOF_SHORT, (LEN)) #define INTALIGN(LEN) TYPEALIGN(ALIGNOF_INT, (LEN)) #define LONGALIGN(LEN) TYPEALIGN(ALIGNOF_LONG, (LEN)) #define DOUBLEALIGN(LEN) TYPEALIGN(ALIGNOF_DOUBLE, (LEN)) #define MAXALIGN(LEN) TYPEALIGN(MAXIMUM_ALIGNOF, (LEN)) /* MAXALIGN covers only built-in types, not buffers */ #define BUFFERALIGN(LEN) TYPEALIGN(ALIGNOF_BUFFER, (LEN)) /* ---------------------------------------------------------------- * Section 6: widely useful macros * ---------------------------------------------------------------- */ /* * Max * Return the maximum of two numbers. */ #define Max(x, y) ((x) > (y) ? (x) : (y)) /* * Min * Return the minimum of two numbers. */ #define Min(x, y) ((x) < (y) ? (x) : (y)) /* * Abs * Return the absolute value of the argument. */ #define Abs(x) ((x) >= 0 ? (x) : -(x)) /* * StrNCpy * Like standard library function strncpy(), except that result string * is guaranteed to be null-terminated --- that is, at most N-1 bytes * of the source string will be kept. * Also, the macro returns no result (too hard to do that without * evaluating the arguments multiple times, which seems worse). * * BTW: when you need to copy a non-null-terminated string (like a text * datum) and add a null, do not do it with StrNCpy(..., len+1). That * might seem to work, but it fetches one byte more than there is in the * text object. One fine day you'll have a SIGSEGV because there isn't * another byte before the end of memory. Don't laugh, we've had real * live bug reports from real live users over exactly this mistake. * Do it honestly with "memcpy(dst,src,len); dst[len] = '\0';", instead. */ #define StrNCpy(dst,src,len) \ do \ { \ char * _dst = (dst); \ Size _len = (len); \ \ if (_len > 0) \ { \ strncpy(_dst, (src), _len); \ _dst[_len-1] = '\0'; \ } \ } while (0) /* Get a bit mask of the bits set in non-long aligned addresses */ #define LONG_ALIGN_MASK (sizeof(long) - 1) /* * MemSet * Exactly the same as standard library function memset(), but considerably * faster for zeroing small word-aligned structures (such as parsetree nodes). * This has to be a macro because the main point is to avoid function-call * overhead. However, we have also found that the loop is faster than * native libc memset() on some platforms, even those with assembler * memset() functions. More research needs to be done, perhaps with * MEMSET_LOOP_LIMIT tests in configure. */ #define MemSet(start, val, len) \ do \ { \ /* must be void* because we don't know if it is integer aligned yet */ \ void *_vstart = (void *) (start); \ int _val = (val); \ Size _len = (len); \ \ if ((((long) _vstart) & LONG_ALIGN_MASK) == 0 && \ (_len & LONG_ALIGN_MASK) == 0 && \ _val == 0 && \ _len <= MEMSET_LOOP_LIMIT && \ /* \ * If MEMSET_LOOP_LIMIT == 0, optimizer should find \ * the whole "if" false at compile time. \ */ \ MEMSET_LOOP_LIMIT != 0) \ { \ long *_start = (long *) _vstart; \ long *_stop = (long *) ((char *) _start + _len); \ while (_start < _stop) \ *_start++ = 0; \ } \ else \ memset(_vstart, _val, _len); \ } while (0) /* * MemSetAligned is the same as MemSet except it omits the test to see if * "start" is word-aligned. This is okay to use if the caller knows a-priori * that the pointer is suitably aligned (typically, because he just got it * from palloc(), which always delivers a max-aligned pointer). */ #define MemSetAligned(start, val, len) \ do \ { \ long *_start = (long *) (start); \ int _val = (val); \ Size _len = (len); \ \ if ((_len & LONG_ALIGN_MASK) == 0 && \ _val == 0 && \ _len <= MEMSET_LOOP_LIMIT && \ MEMSET_LOOP_LIMIT != 0) \ { \ long *_stop = (long *) ((char *) _start + _len); \ while (_start < _stop) \ *_start++ = 0; \ } \ else \ memset(_start, _val, _len); \ } while (0) /* * MemSetTest/MemSetLoop are a variant version that allow all the tests in * MemSet to be done at compile time in cases where "val" and "len" are * constants *and* we know the "start" pointer must be word-aligned. * If MemSetTest succeeds, then it is okay to use MemSetLoop, otherwise use * MemSetAligned. Beware of multiple evaluations of the arguments when using * this approach. */ #define MemSetTest(val, len) \ ( ((len) & LONG_ALIGN_MASK) == 0 && \ (len) <= MEMSET_LOOP_LIMIT && \ MEMSET_LOOP_LIMIT != 0 && \ (val) == 0 ) #define MemSetLoop(start, val, len) \ do \ { \ long * _start = (long *) (start); \ long * _stop = (long *) ((char *) _start + (Size) (len)); \ \ while (_start < _stop) \ *_start++ = 0; \ } while (0) /* ---------------------------------------------------------------- * Section 7: random stuff * ---------------------------------------------------------------- */ /* msb for char */ #define HIGHBIT (0x80) #define IS_HIGHBIT_SET(ch) ((unsigned char)(ch) & HIGHBIT) #define STATUS_OK (0) #define STATUS_ERROR (-1) #define STATUS_EOF (-2) #define STATUS_FOUND (1) #define STATUS_WAITING (2) /* ---------------------------------------------------------------- * Section 8: system-specific hacks * * This should be limited to things that absolutely have to be * included in every source file. The port-specific header file * is usually a better place for this sort of thing. * ---------------------------------------------------------------- */ /* * NOTE: this is also used for opening text files. * WIN32 treats Control-Z as EOF in files opened in text mode. * Therefore, we open files in binary mode on Win32 so we can read * literal control-Z. The other affect is that we see CRLF, but * that is OK because we can already handle those cleanly. */ #if defined(WIN32) || defined(__CYGWIN__) #define PG_BINARY O_BINARY #define PG_BINARY_R "rb" #define PG_BINARY_W "wb" #else #define PG_BINARY 0 #define PG_BINARY_R "r" #define PG_BINARY_W "w" #endif #if defined(sun) && defined(__sparc__) && !defined(__SVR4) #include #endif /* These are for things that are one way on Unix and another on NT */ #define NULL_DEV "/dev/null" /* * Provide prototypes for routines not present in a particular machine's * standard C library. */ #if !HAVE_DECL_SNPRINTF extern int snprintf(char *str, size_t count, const char *fmt,...) /* This extension allows gcc to check the format string */ __attribute__((format(printf, 3, 4))); #endif #if !HAVE_DECL_VSNPRINTF extern int vsnprintf(char *str, size_t count, const char *fmt, va_list args); #endif #if !defined(HAVE_MEMMOVE) && !defined(memmove) #define memmove(d, s, c) bcopy(s, d, c) #endif #ifndef DLLIMPORT #define DLLIMPORT /* no special DLL markers on most ports */ #endif /* * The following is used as the arg list for signal handlers. Any ports * that take something other than an int argument should override this in * their pg_config_os.h file. Note that variable names are required * because it is used in both the prototypes as well as the definitions. * Note also the long name. We expect that this won't collide with * other names causing compiler warnings. */ #ifndef SIGNAL_ARGS #define SIGNAL_ARGS int postgres_signal_arg #endif /* * When there is no sigsetjmp, its functionality is provided by plain * setjmp. Incidentally, nothing provides setjmp's functionality in * that case. */ #ifndef HAVE_SIGSETJMP #define sigjmp_buf jmp_buf #define sigsetjmp(x,y) setjmp(x) #define siglongjmp longjmp #endif #if defined(HAVE_FDATASYNC) && !HAVE_DECL_FDATASYNC extern int fdatasync(int fildes); #endif /* If strtoq() exists, rename it to the more standard strtoll() */ #if defined(HAVE_LONG_LONG_INT_64) && !defined(HAVE_STRTOLL) && defined(HAVE_STRTOQ) #define strtoll strtoq #define HAVE_STRTOLL 1 #endif /* If strtouq() exists, rename it to the more standard strtoull() */ #if defined(HAVE_LONG_LONG_INT_64) && !defined(HAVE_STRTOULL) && defined(HAVE_STRTOUQ) #define strtoull strtouq #define HAVE_STRTOULL 1 #endif /* EXEC_BACKEND defines */ #ifdef EXEC_BACKEND #define NON_EXEC_STATIC #else #define NON_EXEC_STATIC static #endif /* /port compatibility functions */ #include "port.h" #endif /* C_H */ pgtop/color.c000066400000000000000000000136331271046472400135060ustar00rootroot00000000000000/* * 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"); } pgtop/color.h000066400000000000000000000015071271046472400135100ustar00rootroot00000000000000/* * 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_ */ pgtop/commands.c000066400000000000000000000644041271046472400141730ustar00rootroot00000000000000/* * 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 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 #include #include #ifdef HAVE_SYS_RESOURCE_H #include #endif #include #include "sigdesc.h" /* generated automatically */ #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" #include "username.h" extern int errno; extern char *copyright; /* imported from screen.c */ extern int overstrike; extern int max_topn; /* * Some of the commands make system calls that could generate errors. * These errors are collected up in an array of structures for later * contemplation and display. Such routines return a string containing an * error message, or NULL if no errors occurred. We need an upper limit on * the number of errors, so we arbitrarily choose 20. */ #define ERRMAX 20 struct errs /* structure for a system-call error */ { int errnum; /* value of errno (that is, the actual error) */ char *arg; /* argument that caused the error */ }; static struct errs errs[ERRMAX]; static int errcnt; static char *err_toomany = " too many errors occurred"; static char *err_listem = " Many errors occurred. Press `e' to display the list of errors."; char header_index_stats[43] = " I_SCANS I_READS I_FETCHES INDEXRELNAME"; char header_io_stats[64] = " PID RCHAR WCHAR SYSCR SYSCW READS WRITES CWRITES COMMAND"; char header_statements[46] = " CALLS CALLS% TOTAL_TIME AVG_TIME QUERY"; char header_table_stats[78] = "SEQ_SCANS SEQ_READS I_SCANS I_FETCHES INSERTS UPDATES DELETES RELNAME"; /* These macros get used to reset and log the errors */ #define ERR_RESET errcnt = 0 #define ERROR(p, e) if (errcnt >= ERRMAX) \ { \ return(err_toomany); \ } \ else \ { \ errs[errcnt].arg = (p); \ errs[errcnt++].errnum = (e); \ } #define BEGIN "BEGIN;" #define ROLLBACK "ROLLBACK;" struct cmd cmd_map[] = { {'\014', cmd_redraw}, {'#', cmd_number}, {' ', cmd_update}, {'?', cmd_help}, {'A', cmd_explain_analyze}, {'c', cmd_cmdline}, #ifdef ENABLE_COLOR {'C', cmd_color}, #endif /* ENABLE_COLOR */ {'d', cmd_displays}, {'e', cmd_errors}, {'E', cmd_explain}, {'h', cmd_help}, {'i', cmd_idletog}, {'I', cmd_io}, #ifdef ENABLE_KILL {'k', cmd_kill}, #endif /* ENABLE_KILL */ {'L', cmd_locks}, {'n', cmd_number}, {'M', cmd_order_mem}, {'N', cmd_order_pid}, {'o', cmd_order}, {'P', cmd_order_cpu}, {'q', cmd_quit}, {'Q', cmd_current_query}, #ifdef ENABLE_KILL {'r', cmd_renice}, #endif /* ENABLE_KILL */ {'R', cmd_tables}, {'s', cmd_delay}, {'S', cmd_statements}, {'t', cmd_toggle}, {'T', cmd_order_time}, {'u', cmd_user}, {'X', cmd_indexes}, {'\0', NULL}, }; #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_errors(struct pg_top_context *pgtctx) { char ch; if (error_count() == 0) { new_message(MT_standout, " Currently no errors to report."); putchar('\r'); return Yes; } else { reset_display(pgtctx); clear(); show_errors(); standout("Hit any key to continue: "); fflush(stdout); (void) read(0, &ch, 1); } 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_indexes(struct pg_top_context *pgtctx) { if (pgtctx->mode == MODE_INDEX_STATS) { pgtctx->mode = MODE_PROCESSES; pgtctx->header_text = pgtctx->header_processes; } else { pgtctx->mode = MODE_INDEX_STATS; pgtctx->header_text = header_index_stats; } /* Reset display to show changed header text. */ reset_display(pgtctx); return No; } int cmd_io(struct pg_top_context *pgtctx) { if (pgtctx->mode == MODE_IO_STATS) { pgtctx->mode = MODE_PROCESSES; pgtctx->header_text = pgtctx->header_processes; } else { pgtctx->mode = MODE_IO_STATS; pgtctx->header_text = header_io_stats; } reset_display(pgtctx); return No; } #ifdef ENABLE_KILL int cmd_kill(struct pg_top_context *pgtctx) { char *errmsg; char tempbuf[50]; if (pgtctx->mode_remote == Yes) { new_message(MT_standout, "Cannot kill when accessing a remote database."); putchar('\r'); return Yes; } new_message(0, "kill "); if (readline(tempbuf, sizeof(tempbuf), No) > 0) { if ((errmsg = kill_procs(tempbuf)) != NULL) { new_message(MT_standout, "%s", errmsg); putchar('\r'); return Yes; } } else { clear_message(); } return No; } #endif /* ENABLE_KILL */ 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_order(struct pg_top_context *pgtctx) { int i; int no_command = No; char tempbuf[50]; switch (pgtctx->mode) { case MODE_INDEX_STATS: new_message(MT_standout, "Order to sort: "); if (readline(tempbuf, sizeof(tempbuf), No) > 0) { if ((i = string_index(tempbuf, index_ordernames)) == -1) { new_message(MT_standout, " %s: unrecognized sorting order", tempbuf); no_command = Yes; } else { pgtctx->index_order_index = i; } putchar('\r'); } else { clear_message(); } break; case MODE_TABLE_STATS: new_message(MT_standout, "Order to sort: "); if (readline(tempbuf, sizeof(tempbuf), No) > 0) { if ((i = string_index(tempbuf, table_ordernames)) == -1) { new_message(MT_standout, " %s: unrecognized sorting order", tempbuf); no_command = Yes; } else { pgtctx->table_order_index = i; } putchar('\r'); } else { clear_message(); } break; case MODE_IO_STATS: new_message(MT_standout, "Order to sort: "); if (readline(tempbuf, sizeof(tempbuf), No) > 0) { if ((i = string_index(tempbuf, pgtctx->statics.order_names_io)) == -1) { new_message(MT_standout, " %s: unrecognized sorting order", tempbuf); no_command = Yes; } else { pgtctx->io_order_index = i; } } else { clear_message(); } break; case MODE_STATEMENTS: new_message(MT_standout, "Order to sort: "); if (readline(tempbuf, sizeof(tempbuf), No) > 0) { if ((i = string_index(tempbuf, statement_ordernames)) == -1) { new_message(MT_standout, " %s: unrecognized sorting order", tempbuf); no_command = Yes; } else { pgtctx->statement_order_index = i; } } else { clear_message(); } break; case MODE_PROCESSES: default: 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) == -1; 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_order_pid(struct pg_top_context *pgtctx) { int i; if ((i = string_index("pid", 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_order_time(struct pg_top_context *pgtctx) { int i; if ((i = string_index("time", 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; } #ifdef ENABLE_KILL int cmd_renice(struct pg_top_context *pgtctx) { char *errmsg; char tempbuf[50]; if (pgtctx->mode_remote == Yes) { new_message(MT_standout, "Cannot renice when accessing a remote database."); putchar('\r'); return Yes; } new_message(0, "renice "); if (readline(tempbuf, sizeof(tempbuf), No) > 0) { if ((errmsg = renice_procs(tempbuf)) != NULL) { new_message(MT_standout, "%s", errmsg); putchar('\r'); /* no_command = Yes; */ } } else { clear_message(); } return No; } #endif /* ENABLE_KILL */ int cmd_statements(struct pg_top_context *pgtctx) { if (pgtctx->mode == MODE_STATEMENTS) { pgtctx->mode = MODE_PROCESSES; pgtctx->header_text = pgtctx->header_processes; } else { pgtctx->mode = MODE_STATEMENTS; pgtctx->header_text = header_statements; } reset_display(pgtctx); return No; } int cmd_tables(struct pg_top_context *pgtctx) { if (pgtctx->mode == MODE_TABLE_STATS) { pgtctx->mode = MODE_PROCESSES; pgtctx->header_text = pgtctx->header_processes; } else { pgtctx->mode = MODE_TABLE_STATS; pgtctx->header_text = header_table_stats; } reset_display(pgtctx); return No; } int cmd_toggle(struct pg_top_context *pgtctx) { if (mode_stats == STATS_DIFF) { mode_stats = STATS_CUMULATIVE; new_message(MT_standout | MT_delayed, " Displaying cumulative statistics."); putchar('\r'); } else { mode_stats = STATS_DIFF; new_message(MT_standout | MT_delayed, " Displaying differential statistics."); putchar('\r'); } 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) { int i; int no_command = No; char tempbuf[50]; new_message(MT_standout, "Username to show: "); if (readline(tempbuf, sizeof(tempbuf), No) > 0) { if (tempbuf[0] == '+' && tempbuf[1] == '\0') { pgtctx->ps.uid = -1; } else if ((i = userid(tempbuf)) == -1) { new_message(MT_standout, " %s: unknown user", tempbuf); no_command = Yes; } else { pgtctx->ps.uid = i; } putchar('\r'); } else { clear_message(); } return no_command; } /* * err_compar(p1, p2) - comparison routine used by "qsort" * for sorting errors. */ int err_compar(const void *p1, const void *p2) { register int result; if ((result = ((struct errs *) p1)->errnum - ((struct errs *) p2)->errnum) == 0) { return (strcmp(((struct errs *) p1)->arg, ((struct errs *) p2)->arg)); } return (result); } 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); } /* * err_string() - return an appropriate error string. This is what the * command will return for displaying. If no errors were logged, then * return NULL. The maximum length of the error string is defined by * "STRMAX". */ #define STRMAX 80 char * err_string() { register struct errs *errp; register int cnt = 0; register int first = Yes; register int currerr = -1; int stringlen; /* characters still available in "string" */ static char string[STRMAX]; /* if there are no errors, return NULL */ if (errcnt == 0) { return (NULL); } /* sort the errors */ qsort((char *) errs, errcnt, sizeof(struct errs), err_compar); /* need a space at the front of the error string */ string[0] = ' '; string[1] = '\0'; stringlen = STRMAX - 2; /* loop thru the sorted list, building an error string */ while (cnt < errcnt) { errp = &(errs[cnt++]); if (errp->errnum != currerr) { if (currerr != -1) { if ((stringlen = str_adderr(string, stringlen, currerr)) < 2) { return (err_listem); } (void) strcat(string, "; "); /* we know there's more */ } currerr = errp->errnum; first = Yes; } if ((stringlen = str_addarg(string, stringlen, errp->arg, first)) == 0) { return (err_listem); } first = No; } /* add final message */ stringlen = str_adderr(string, stringlen, currerr); /* return the error string */ return (stringlen == 0 ? err_listem : string); } /* * 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; char *q = 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"; } if (stp->order_names != NULL) { q = string_list(stp->order_names_io); } if (q == NULL) { q = "not supported"; } fullhelp = (char *) malloc(strlen(help_text) + strlen(p) + strlen(q) + 2); sprintf(fullhelp, help_text, p, q); } 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); } /* * error_count() - return the number of errors currently logged. */ int error_count() { return (errcnt); } /* * show_errors() - display on stdout the current log of errors. */ void show_errors() { register int cnt = 0; register struct errs *errp = errs; printf("%d error%s:\n\n", errcnt, errcnt == 1 ? "" : "s"); while (cnt++ < errcnt) { printf("%5s: %s\n", errp->arg, errp->errnum == 0 ? "Not a number" : errmsg(errp->errnum)); errp++; } } /* * kill_procs(str) - send signals to processes, much like the "kill" * command does; invoked in response to 'k'. */ char * kill_procs(char *str) { register char *nptr; int signum = SIGTERM; /* default */ int procnum; struct sigdesc *sigp; int uid; /* reset error array */ ERR_RESET; /* remember our uid */ uid = getuid(); /* skip over leading white space */ while (isspace(*str)) str++; if (str[0] == '-') { /* explicit signal specified */ if ((nptr = next_field(str)) == NULL) { return (" kill: no processes specified"); } if (isdigit(str[1])) { (void) scanint(str + 1, &signum); if (signum <= 0 || signum >= NSIG) { return (" invalid signal number"); } } else { /* translate the name into a number */ for (sigp = sigdesc; sigp->name != NULL; sigp++) { if (strcmp(sigp->name, str + 1) == 0) { signum = sigp->number; break; } } /* was it ever found */ if (sigp->name == NULL) { return (" bad signal name"); } } /* put the new pointer in place */ str = nptr; } /* loop thru the string, killing processes */ do { if (scanint(str, &procnum) == -1) { ERROR(str, 0); } else { /* check process owner if we're not root */ if (uid && (uid != proc_owner(procnum))) { ERROR(str, EACCES); } /* go in for the kill */ else if (kill(procnum, signum) == -1) { /* chalk up an error */ ERROR(str, errno); } } } while ((str = next_field(str)) != NULL); /* return appropriate error string */ return (err_string()); } /* * renice_procs(str) - change the "nice" of processes, much like the * "renice" command does; invoked in response to 'r'. */ char * renice_procs(char *str) { register char negate; int prio; int procnum; int uid; ERR_RESET; uid = getuid(); /* allow for negative priority values */ if ((negate = (*str == '-')) != 0) { /* move past the minus sign */ str++; } /* use procnum as a temporary holding place and get the number */ procnum = scanint(str, &prio); /* negate if necessary */ if (negate) { prio = -prio; } #if defined(PRIO_MIN) && defined(PRIO_MAX) /* check for validity */ if (procnum == -1 || prio < PRIO_MIN || prio > PRIO_MAX) { return (" bad priority value"); } #endif /* move to the first process number */ if ((str = next_field(str)) == NULL) { return (" no processes specified"); } #ifdef HAVE_SETPRIORITY /* loop thru the process numbers, renicing each one */ do { if (scanint(str, &procnum) == -1) { ERROR(str, 0); } /* check process owner if we're not root */ else if (uid && (uid != proc_owner(procnum))) { ERROR(str, EACCES); } else if (setpriority(PRIO_PROCESS, procnum, prio) == -1) { ERROR(str, errno); } } while ((str = next_field(str)) != NULL); /* return appropriate error string */ return (err_string()); #else return (" operation not supported"); #endif } void show_current_query(char *conninfo, int procpid) { int i; int rows; char info[64]; PGconn *pgconn; PGresult *pgresult = NULL; sprintf(info, "Current query for procpid %d:\n\n", procpid); display_pager(info); /* Get the currently running query. */ pgconn = connect_to_db(conninfo); if (pgconn != NULL) { pgresult = pg_query(pgconn, 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); PQfinish(pgconn); } void show_explain(char *conninfo, int procpid, int analyze) { int i, j; int rows, r; char sql[4096]; char info[1024]; PGconn *pgconn; 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. */ pgconn = connect_to_db(conninfo); if (pgconn != NULL) { pgresult_query = pg_query(pgconn, 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\n%s", PQgetvalue(pgresult_query, i, 0)); } else { sprintf(sql, "EXPLAIN\n%s", PQgetvalue(pgresult_query, i, 0)); } PQexec(pgconn, BEGIN); pgresult_explain = PQexec(pgconn, sql); PQexec(pgconn, 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); PQfinish(pgconn); } void show_locks(char *conninfo, int procpid) { int i, j, k; int rows; char info[64]; int width[5] = {1, 8, 5, 4, 7}; PGconn *pgconn; 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. */ pgconn = connect_to_db(conninfo); if (pgconn == NULL) { PQfinish(pgconn); return; } pgresult = pg_locks(pgconn, 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)); } sprintf(header_format, "%%-%ds | %%-%ds | %%-%ds | %%-%ds | %%-%ds\n", width[0], width[1], width[2], width[3], width[4]); sprintf(line_format, "%%%dd | %%-%ds | %%-%ds | %%-%ds | %%-%ds\n", width[0], width[1], width[2], width[3], width[4]); /* Display the header. */ sprintf(line, header_format, "", "database", "table", "type", "granted"); display_pager(line); for (i = 0, k = 0; i < 5; 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)); display_pager(line); } display_pager("\n"); PQclear(pgresult); PQfinish(pgconn); } pgtop/commands.h000066400000000000000000000035251271046472400141750ustar00rootroot00000000000000/* call specifications for commands.c */ #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 #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_errors(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 *); #ifdef ENABLE_KILL int cmd_kill(struct pg_top_context *); #endif /* ENABLE_KILL */ int cmd_locks(struct pg_top_context *); int cmd_number(struct pg_top_context *); int cmd_quit(struct pg_top_context *); int cmd_order(struct pg_top_context *); int cmd_order_cpu(struct pg_top_context *); int cmd_order_mem(struct pg_top_context *); int cmd_order_pid(struct pg_top_context *); int cmd_order_time(struct pg_top_context *); int cmd_redraw(struct pg_top_context *); #ifdef ENABLE_KILL int cmd_renice(struct pg_top_context *); #endif /* ENABLE_KILL */ int cmd_statements(struct pg_top_context *); int cmd_tables(struct pg_top_context *); int cmd_toggle(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); int error_count(); void show_errors(); char *kill_procs(char *str); char *renice_procs(char *str); void show_current_query(char *, int); void show_explain(char *, int, int); void show_locks(char *, int); #endif /* _COMMANDS_H_ */ pgtop/config.guess000077500000000000000000001220651271046472400145440ustar00rootroot00000000000000#! /bin/sh # Attempt to guess a canonical system name. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003 Free Software Foundation, Inc. timestamp='2003-07-02' # This file is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Originally written by Per Bothner . # Please send patches to . Submit a context # diff and a properly formatted ChangeLog entry. # # This script attempts to guess a canonical system name similar to # config.sub. If it succeeds, it prints the system name on stdout, and # exits with 0. Otherwise, it exits with 1. # # The plan is that this can be called by configure scripts if you # don't specify an explicit build system type. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] Output the configuration name of the system \`$me' is run on. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.guess ($timestamp) Originally written by Per Bothner. Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit 0 ;; --version | -v ) echo "$version" ; exit 0 ;; --help | --h* | -h ) echo "$usage"; exit 0 ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" >&2 exit 1 ;; * ) break ;; esac done if test $# != 0; then echo "$me: too many arguments$help" >&2 exit 1 fi trap 'exit 1' 1 2 15 # CC_FOR_BUILD -- compiler used by this script. Note that the use of a # compiler to aid in system detection is discouraged as it requires # temporary files to be created and, as you can see below, it is a # headache to deal with in a portable fashion. # Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still # use `HOST_CC' if defined, but it is deprecated. # Portable tmp directory creation inspired by the Autoconf team. set_cc_for_build=' trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; : ${TMPDIR=/tmp} ; { tmp=`(umask 077 && mktemp -d -q "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; dummy=$tmp/dummy ; tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; case $CC_FOR_BUILD,$HOST_CC,$CC in ,,) echo "int x;" > $dummy.c ; for c in cc gcc c89 c99 ; do if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then CC_FOR_BUILD="$c"; break ; fi ; done ; if test x"$CC_FOR_BUILD" = x ; then CC_FOR_BUILD=no_compiler_found ; fi ;; ,,*) CC_FOR_BUILD=$CC ;; ,*,*) CC_FOR_BUILD=$HOST_CC ;; esac ;' # This is needed to find uname on a Pyramid OSx when run in the BSD universe. # (ghazi@noc.rutgers.edu 1994-08-24) if (test -f /.attbin/uname) >/dev/null 2>&1 ; then PATH=$PATH:/.attbin ; export PATH fi UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown # Note: order is significant - the case branches are not exclusive. case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in *:NetBSD:*:*) # NetBSD (nbsd) targets should (where applicable) match one or # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently # switched to ELF, *-*-netbsd* would select the old # object file format. This provides both forward # compatibility and a consistent mechanism for selecting the # object file format. # # Note: NetBSD doesn't particularly care about the vendor # portion of the name. We always set it to "unknown". sysctl="sysctl -n hw.machine_arch" UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ /usr/sbin/$sysctl 2>/dev/null || echo unknown)` case "${UNAME_MACHINE_ARCH}" in armeb) machine=armeb-unknown ;; arm*) machine=arm-unknown ;; sh3el) machine=shl-unknown ;; sh3eb) machine=sh-unknown ;; *) machine=${UNAME_MACHINE_ARCH}-unknown ;; esac # The Operating System including object format, if it has switched # to ELF recently, or will in the future. case "${UNAME_MACHINE_ARCH}" in arm*|i386|m68k|ns32k|sh3*|sparc|vax) eval $set_cc_for_build if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ | grep __ELF__ >/dev/null then # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). # Return netbsd for either. FIX? os=netbsd else os=netbsdelf fi ;; *) os=netbsd ;; esac # The OS release # Debian GNU/NetBSD machines have a different userland, and # thus, need a distinct triplet. However, they do not need # kernel version information, so it can be replaced with a # suitable tag, in the style of linux-gnu. case "${UNAME_VERSION}" in Debian*) release='-gnu' ;; *) release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` ;; esac # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: # contains redundant information, the shorter form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. echo "${machine}-${os}${release}" exit 0 ;; amiga:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; arc:OpenBSD:*:*) echo mipsel-unknown-openbsd${UNAME_RELEASE} exit 0 ;; hp300:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; mac68k:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; macppc:OpenBSD:*:*) echo powerpc-unknown-openbsd${UNAME_RELEASE} exit 0 ;; mvme68k:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; mvme88k:OpenBSD:*:*) echo m88k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; mvmeppc:OpenBSD:*:*) echo powerpc-unknown-openbsd${UNAME_RELEASE} exit 0 ;; pmax:OpenBSD:*:*) echo mipsel-unknown-openbsd${UNAME_RELEASE} exit 0 ;; sgi:OpenBSD:*:*) echo mipseb-unknown-openbsd${UNAME_RELEASE} exit 0 ;; sun3:OpenBSD:*:*) echo m68k-unknown-openbsd${UNAME_RELEASE} exit 0 ;; wgrisc:OpenBSD:*:*) echo mipsel-unknown-openbsd${UNAME_RELEASE} exit 0 ;; *:OpenBSD:*:*) echo ${UNAME_MACHINE}-unknown-openbsd${UNAME_RELEASE} exit 0 ;; alpha:OSF1:*:*) if test $UNAME_RELEASE = "V4.0"; then UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` fi # According to Compaq, /usr/sbin/psrinfo has been available on # OSF/1 and Tru64 systems produced since 1995. I hope that # covers most systems running today. This code pipes the CPU # types through head -n 1, so we only detect the type of CPU 0. ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` case "$ALPHA_CPU_TYPE" in "EV4 (21064)") UNAME_MACHINE="alpha" ;; "EV4.5 (21064)") UNAME_MACHINE="alpha" ;; "LCA4 (21066/21068)") UNAME_MACHINE="alpha" ;; "EV5 (21164)") UNAME_MACHINE="alphaev5" ;; "EV5.6 (21164A)") UNAME_MACHINE="alphaev56" ;; "EV5.6 (21164PC)") UNAME_MACHINE="alphapca56" ;; "EV5.7 (21164PC)") UNAME_MACHINE="alphapca57" ;; "EV6 (21264)") UNAME_MACHINE="alphaev6" ;; "EV6.7 (21264A)") UNAME_MACHINE="alphaev67" ;; "EV6.8CB (21264C)") UNAME_MACHINE="alphaev68" ;; "EV6.8AL (21264B)") UNAME_MACHINE="alphaev68" ;; "EV6.8CX (21264D)") UNAME_MACHINE="alphaev68" ;; "EV6.9A (21264/EV69A)") UNAME_MACHINE="alphaev69" ;; "EV7 (21364)") UNAME_MACHINE="alphaev7" ;; "EV7.9 (21364A)") UNAME_MACHINE="alphaev79" ;; esac # A Vn.n version is a released version. # A Tn.n version is a released field test version. # A Xn.n version is an unreleased experimental baselevel. # 1.2 uses "1.2" for uname -r. echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[VTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` exit 0 ;; Alpha*:OpenVMS:*:*) echo alpha-hp-vms exit 0 ;; Alpha\ *:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # Should we change UNAME_MACHINE based on the output of uname instead # of the specific Alpha model? echo alpha-pc-interix exit 0 ;; 21064:Windows_NT:50:3) echo alpha-dec-winnt3.5 exit 0 ;; Amiga*:UNIX_System_V:4.0:*) echo m68k-unknown-sysv4 exit 0;; *:[Aa]miga[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-amigaos exit 0 ;; *:[Mm]orph[Oo][Ss]:*:*) echo ${UNAME_MACHINE}-unknown-morphos exit 0 ;; *:OS/390:*:*) echo i370-ibm-openedition exit 0 ;; arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) echo arm-acorn-riscix${UNAME_RELEASE} exit 0;; SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) echo hppa1.1-hitachi-hiuxmpp exit 0;; Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. if test "`(/bin/universe) 2>/dev/null`" = att ; then echo pyramid-pyramid-sysv3 else echo pyramid-pyramid-bsd fi exit 0 ;; NILE*:*:*:dcosx) echo pyramid-pyramid-svr4 exit 0 ;; DRS?6000:unix:4.0:6*) echo sparc-icl-nx6 exit 0 ;; DRS?6000:UNIX_SV:4.2*:7*) case `/usr/bin/uname -p` in sparc) echo sparc-icl-nx7 && exit 0 ;; esac ;; sun4H:SunOS:5.*:*) echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; i86pc:SunOS:5.*:*) echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; sun4*:SunOS:6*:*) # According to config.sub, this is the proper way to canonicalize # SunOS6. Hard to guess exactly what SunOS6 will be like, but # it's likely to be more like Solaris than SunOS4. echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; sun4*:SunOS:*:*) case "`/usr/bin/arch -k`" in Series*|S4*) UNAME_RELEASE=`uname -v` ;; esac # Japanese Language versions have a version number like `4.1.3-JL'. echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` exit 0 ;; sun3*:SunOS:*:*) echo m68k-sun-sunos${UNAME_RELEASE} exit 0 ;; sun*:*:4.2BSD:*) UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 case "`/bin/arch`" in sun3) echo m68k-sun-sunos${UNAME_RELEASE} ;; sun4) echo sparc-sun-sunos${UNAME_RELEASE} ;; esac exit 0 ;; aushp:SunOS:*:*) echo sparc-auspex-sunos${UNAME_RELEASE} exit 0 ;; # The situation for MiNT is a little confusing. The machine name # can be virtually everything (everything which is not # "atarist" or "atariste" at least should have a processor # > m68000). The system name ranges from "MiNT" over "FreeMiNT" # to the lowercase version "mint" (or "freemint"). Finally # the system name "TOS" denotes a system which is actually not # MiNT. But MiNT is downward compatible to TOS, so this should # be no problem. atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit 0 ;; atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit 0 ;; *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) echo m68k-atari-mint${UNAME_RELEASE} exit 0 ;; milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) echo m68k-milan-mint${UNAME_RELEASE} exit 0 ;; hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) echo m68k-hades-mint${UNAME_RELEASE} exit 0 ;; *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) echo m68k-unknown-mint${UNAME_RELEASE} exit 0 ;; powerpc:machten:*:*) echo powerpc-apple-machten${UNAME_RELEASE} exit 0 ;; RISC*:Mach:*:*) echo mips-dec-mach_bsd4.3 exit 0 ;; RISC*:ULTRIX:*:*) echo mips-dec-ultrix${UNAME_RELEASE} exit 0 ;; VAX*:ULTRIX*:*:*) echo vax-dec-ultrix${UNAME_RELEASE} exit 0 ;; 2020:CLIX:*:* | 2430:CLIX:*:*) echo clipper-intergraph-clix${UNAME_RELEASE} exit 0 ;; mips:*:*:UMIPS | mips:*:*:RISCos) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #ifdef __cplusplus #include /* for printf() prototype */ int main (int argc, char *argv[]) { #else int main (argc, argv) int argc; char *argv[]; { #endif #if defined (host_mips) && defined (MIPSEB) #if defined (SYSTYPE_SYSV) printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_SVR4) printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); #endif #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); #endif #endif exit (-1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c \ && $dummy `echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` \ && exit 0 echo mips-mips-riscos${UNAME_RELEASE} exit 0 ;; Motorola:PowerMAX_OS:*:*) echo powerpc-motorola-powermax exit 0 ;; Motorola:*:4.3:PL8-*) echo powerpc-harris-powermax exit 0 ;; Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) echo powerpc-harris-powermax exit 0 ;; Night_Hawk:Power_UNIX:*:*) echo powerpc-harris-powerunix exit 0 ;; m88k:CX/UX:7*:*) echo m88k-harris-cxux7 exit 0 ;; m88k:*:4*:R4*) echo m88k-motorola-sysv4 exit 0 ;; m88k:*:3*:R3*) echo m88k-motorola-sysv3 exit 0 ;; AViiON:dgux:*:*) # DG/UX returns AViiON for all architectures UNAME_PROCESSOR=`/usr/bin/uname -p` if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] then if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ [ ${TARGET_BINARY_INTERFACE}x = x ] then echo m88k-dg-dgux${UNAME_RELEASE} else echo m88k-dg-dguxbcs${UNAME_RELEASE} fi else echo i586-dg-dgux${UNAME_RELEASE} fi exit 0 ;; M88*:DolphinOS:*:*) # DolphinOS (SVR3) echo m88k-dolphin-sysv3 exit 0 ;; M88*:*:R3*:*) # Delta 88k system running SVR3 echo m88k-motorola-sysv3 exit 0 ;; XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) echo m88k-tektronix-sysv3 exit 0 ;; Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) echo m68k-tektronix-bsd exit 0 ;; *:IRIX*:*:*) echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` exit 0 ;; ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id exit 0 ;; # Note that: echo "'`uname -s`'" gives 'AIX ' i*86:AIX:*:*) echo i386-ibm-aix exit 0 ;; ia64:AIX:*:*) if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} exit 0 ;; *:AIX:2:3) if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include main() { if (!__power_pc()) exit(1); puts("powerpc-ibm-aix3.2.5"); exit(0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 echo rs6000-ibm-aix3.2.5 elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then echo rs6000-ibm-aix3.2.4 else echo rs6000-ibm-aix3.2 fi exit 0 ;; *:AIX:*:[45]) IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then IBM_ARCH=rs6000 else IBM_ARCH=powerpc fi if [ -x /usr/bin/oslevel ] ; then IBM_REV=`/usr/bin/oslevel` else IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} fi echo ${IBM_ARCH}-ibm-aix${IBM_REV} exit 0 ;; *:AIX:*:*) echo rs6000-ibm-aix exit 0 ;; ibmrt:4.4BSD:*|romp-ibm:BSD:*) echo romp-ibm-bsd4.4 exit 0 ;; ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to exit 0 ;; # report: romp-ibm BSD 4.3 *:BOSX:*:*) echo rs6000-bull-bosx exit 0 ;; DPX/2?00:B.O.S.:*:*) echo m68k-bull-sysv3 exit 0 ;; 9000/[34]??:4.3bsd:1.*:*) echo m68k-hp-bsd exit 0 ;; hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) echo m68k-hp-bsd4.4 exit 0 ;; 9000/[34678]??:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` case "${UNAME_MACHINE}" in 9000/31? ) HP_ARCH=m68000 ;; 9000/[34]?? ) HP_ARCH=m68k ;; 9000/[678][0-9][0-9]) if [ -x /usr/bin/getconf ]; then sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` case "${sc_cpu_version}" in 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 532) # CPU_PA_RISC2_0 case "${sc_kernel_bits}" in 32) HP_ARCH="hppa2.0n" ;; 64) HP_ARCH="hppa2.0w" ;; '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 esac ;; esac fi if [ "${HP_ARCH}" = "" ]; then eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #define _HPUX_SOURCE #include #include int main () { #if defined(_SC_KERNEL_BITS) long bits = sysconf(_SC_KERNEL_BITS); #endif long cpu = sysconf (_SC_CPU_VERSION); switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0"); break; case CPU_PA_RISC1_1: puts ("hppa1.1"); break; case CPU_PA_RISC2_0: #if defined(_SC_KERNEL_BITS) switch (bits) { case 64: puts ("hppa2.0w"); break; case 32: puts ("hppa2.0n"); break; default: puts ("hppa2.0"); break; } break; #else /* !defined(_SC_KERNEL_BITS) */ puts ("hppa2.0"); break; #endif default: puts ("hppa1.0"); break; } exit (0); } EOF (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` test -z "$HP_ARCH" && HP_ARCH=hppa fi ;; esac if [ ${HP_ARCH} = "hppa2.0w" ] then # avoid double evaluation of $set_cc_for_build test -n "$CC_FOR_BUILD" || eval $set_cc_for_build if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E -) | grep __LP64__ >/dev/null then HP_ARCH="hppa2.0w" else HP_ARCH="hppa64" fi fi echo ${HP_ARCH}-hp-hpux${HPUX_REV} exit 0 ;; ia64:HP-UX:*:*) HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` echo ia64-hp-hpux${HPUX_REV} exit 0 ;; 3050*:HI-UX:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include int main () { long cpu = sysconf (_SC_CPU_VERSION); /* The order matters, because CPU_IS_HP_MC68K erroneously returns true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct results, however. */ if (CPU_IS_PA_RISC (cpu)) { switch (cpu) { case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; default: puts ("hppa-hitachi-hiuxwe2"); break; } } else if (CPU_IS_HP_MC68K (cpu)) puts ("m68k-hitachi-hiuxwe2"); else puts ("unknown-hitachi-hiuxwe2"); exit (0); } EOF $CC_FOR_BUILD -o $dummy $dummy.c && $dummy && exit 0 echo unknown-hitachi-hiuxwe2 exit 0 ;; 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) echo hppa1.1-hp-bsd exit 0 ;; 9000/8??:4.3bsd:*:*) echo hppa1.0-hp-bsd exit 0 ;; *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) echo hppa1.0-hp-mpeix exit 0 ;; hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) echo hppa1.1-hp-osf exit 0 ;; hp8??:OSF1:*:*) echo hppa1.0-hp-osf exit 0 ;; i*86:OSF1:*:*) if [ -x /usr/sbin/sysversion ] ; then echo ${UNAME_MACHINE}-unknown-osf1mk else echo ${UNAME_MACHINE}-unknown-osf1 fi exit 0 ;; parisc*:Lites*:*:*) echo hppa1.1-hp-lites exit 0 ;; C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) echo c1-convex-bsd exit 0 ;; C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit 0 ;; C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) echo c34-convex-bsd exit 0 ;; C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) echo c38-convex-bsd exit 0 ;; C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) echo c4-convex-bsd exit 0 ;; CRAY*Y-MP:*:*:*) echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; CRAY*[A-Z]90:*:*:*) echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ -e 's/\.[^.]*$/.X/' exit 0 ;; CRAY*TS:*:*:*) echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; CRAY*T3E:*:*:*) echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; CRAY*SV1:*:*:*) echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; *:UNICOS/mp:*:*) echo nv1-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' exit 0 ;; F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" exit 0 ;; i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} exit 0 ;; sparc*:BSD/OS:*:*) echo sparc-unknown-bsdi${UNAME_RELEASE} exit 0 ;; *:BSD/OS:*:*) echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} exit 0 ;; *:FreeBSD:*:*|*:GNU/FreeBSD:*:*) # Determine whether the default compiler uses glibc. eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include #if __GLIBC__ >= 2 LIBC=gnu #else LIBC= #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` # GNU/FreeBSD systems have a "k" prefix to indicate we are using # FreeBSD's kernel, but not the complete OS. case ${LIBC} in gnu) kernel_only='k' ;; esac echo ${UNAME_MACHINE}-unknown-${kernel_only}freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`${LIBC:+-$LIBC} exit 0 ;; i*:CYGWIN*:*) echo ${UNAME_MACHINE}-pc-cygwin exit 0 ;; i*:MINGW*:*) echo ${UNAME_MACHINE}-pc-mingw32 exit 0 ;; i*:PW*:*) echo ${UNAME_MACHINE}-pc-pw32 exit 0 ;; x86:Interix*:[34]*) echo i586-pc-interix${UNAME_RELEASE}|sed -e 's/\..*//' exit 0 ;; [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) echo i${UNAME_MACHINE}-pc-mks exit 0 ;; i*:Windows_NT*:* | Pentium*:Windows_NT*:*) # How do we know it's Interix rather than the generic POSIX subsystem? # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we # UNAME_MACHINE based on the output of uname instead of i386? echo i586-pc-interix exit 0 ;; i*:UWIN*:*) echo ${UNAME_MACHINE}-pc-uwin exit 0 ;; p*:CYGWIN*:*) echo powerpcle-unknown-cygwin exit 0 ;; prep*:SunOS:5.*:*) echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` exit 0 ;; *:GNU:*:*) echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` exit 0 ;; i*86:Minix:*:*) echo ${UNAME_MACHINE}-pc-minix exit 0 ;; arm*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; cris:Linux:*:*) echo cris-axis-linux-gnu exit 0 ;; ia64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; m68*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; mips:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef mips #undef mipsel #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=mipsel #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=mips #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 ;; mips64:Linux:*:*) eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #undef CPU #undef mips64 #undef mips64el #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) CPU=mips64el #else #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) CPU=mips64 #else CPU= #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^CPU=` test x"${CPU}" != x && echo "${CPU}-unknown-linux-gnu" && exit 0 ;; ppc:Linux:*:*) echo powerpc-unknown-linux-gnu exit 0 ;; ppc64:Linux:*:*) echo powerpc64-unknown-linux-gnu exit 0 ;; alpha:Linux:*:*) case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in EV5) UNAME_MACHINE=alphaev5 ;; EV56) UNAME_MACHINE=alphaev56 ;; PCA56) UNAME_MACHINE=alphapca56 ;; PCA57) UNAME_MACHINE=alphapca56 ;; EV6) UNAME_MACHINE=alphaev6 ;; EV67) UNAME_MACHINE=alphaev67 ;; EV68*) UNAME_MACHINE=alphaev68 ;; esac objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} exit 0 ;; parisc:Linux:*:* | hppa:Linux:*:*) # Look for CPU level case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in PA7*) echo hppa1.1-unknown-linux-gnu ;; PA8*) echo hppa2.0-unknown-linux-gnu ;; *) echo hppa-unknown-linux-gnu ;; esac exit 0 ;; parisc64:Linux:*:* | hppa64:Linux:*:*) echo hppa64-unknown-linux-gnu exit 0 ;; s390:Linux:*:* | s390x:Linux:*:*) echo ${UNAME_MACHINE}-ibm-linux exit 0 ;; sh64*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; sh*:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; sparc:Linux:*:* | sparc64:Linux:*:*) echo ${UNAME_MACHINE}-unknown-linux-gnu exit 0 ;; x86_64:Linux:*:*) echo x86_64-unknown-linux-gnu exit 0 ;; i*86:Linux:*:*) # The BFD linker knows what the default object file format is, so # first see if it will tell us. cd to the root directory to prevent # problems with other programs or directories called `ld' in the path. # Set LC_ALL=C to ensure ld outputs messages in English. ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ | sed -ne '/supported targets:/!d s/[ ][ ]*/ /g s/.*supported targets: *// s/ .*// p'` case "$ld_supported_targets" in elf32-i386) TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" ;; a.out-i386-linux) echo "${UNAME_MACHINE}-pc-linux-gnuaout" exit 0 ;; coff-i386) echo "${UNAME_MACHINE}-pc-linux-gnucoff" exit 0 ;; "") # Either a pre-BFD a.out linker (linux-gnuoldld) or # one that does not give us useful --help. echo "${UNAME_MACHINE}-pc-linux-gnuoldld" exit 0 ;; esac # Determine whether the default compiler is a.out or elf eval $set_cc_for_build sed 's/^ //' << EOF >$dummy.c #include #ifdef __ELF__ # ifdef __GLIBC__ # if __GLIBC__ >= 2 LIBC=gnu # else LIBC=gnulibc1 # endif # else LIBC=gnulibc1 # endif #else #ifdef __INTEL_COMPILER LIBC=gnu #else LIBC=gnuaout #endif #endif EOF eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep ^LIBC=` test x"${LIBC}" != x && echo "${UNAME_MACHINE}-pc-linux-${LIBC}" && exit 0 test x"${TENTATIVE}" != x && echo "${TENTATIVE}" && exit 0 ;; i*86:DYNIX/ptx:4*:*) # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. # earlier versions are messed up and put the nodename in both # sysname and nodename. echo i386-sequent-sysv4 exit 0 ;; i*86:UNIX_SV:4.2MP:2.*) # Unixware is an offshoot of SVR4, but it has its own version # number series starting with 2... # I am not positive that other SVR4 systems won't match this, # I just have to hope. -- rms. # Use sysv4.2uw... so that sysv4* matches it. echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} exit 0 ;; i*86:OS/2:*:*) # If we were able to find `uname', then EMX Unix compatibility # is probably installed. echo ${UNAME_MACHINE}-pc-os2-emx exit 0 ;; i*86:XTS-300:*:STOP) echo ${UNAME_MACHINE}-unknown-stop exit 0 ;; i*86:atheos:*:*) echo ${UNAME_MACHINE}-unknown-atheos exit 0 ;; i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) echo i386-unknown-lynxos${UNAME_RELEASE} exit 0 ;; i*86:*DOS:*:*) echo ${UNAME_MACHINE}-pc-msdosdjgpp exit 0 ;; i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} else echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} fi exit 0 ;; i*86:*:5:[78]*) case `/bin/uname -X | grep "^Machine"` in *486*) UNAME_MACHINE=i486 ;; *Pentium) UNAME_MACHINE=i586 ;; *Pent*|*Celeron) UNAME_MACHINE=i686 ;; esac echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} exit 0 ;; i*86:*:3.2:*) if test -f /usr/options/cb.name; then UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ && UNAME_MACHINE=i586 (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ && UNAME_MACHINE=i686 (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ && UNAME_MACHINE=i686 echo ${UNAME_MACHINE}-pc-sco$UNAME_REL else echo ${UNAME_MACHINE}-pc-sysv32 fi exit 0 ;; pc:*:*:*) # Left here for compatibility: # uname -m prints for DJGPP always 'pc', but it prints nothing about # the processor, so we play safe by assuming i386. echo i386-pc-msdosdjgpp exit 0 ;; Intel:Mach:3*:*) echo i386-pc-mach3 exit 0 ;; paragon:*:*:*) echo i860-intel-osf1 exit 0 ;; i860:*:4.*:*) # i860-SVR4 if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 else # Add other i860-SVR4 vendors below as they are discovered. echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 fi exit 0 ;; mini*:CTIX:SYS*5:*) # "miniframe" echo m68010-convergent-sysv exit 0 ;; mc68k:UNIX:SYSTEM5:3.51m) echo m68k-convergent-sysv exit 0 ;; M680?0:D-NIX:5.3:*) echo m68k-diab-dnix exit 0 ;; M68*:*:R3V[567]*:*) test -r /sysV68 && echo 'm68k-motorola-sysv' && exit 0 ;; 3[34]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0) OS_REL='' test -r /etc/.relid \ && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && echo i486-ncr-sysv4.3${OS_REL} && exit 0 /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ && echo i586-ncr-sysv4.3${OS_REL} && exit 0 ;; 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ && echo i486-ncr-sysv4 && exit 0 ;; m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) echo m68k-unknown-lynxos${UNAME_RELEASE} exit 0 ;; mc68030:UNIX_System_V:4.*:*) echo m68k-atari-sysv4 exit 0 ;; TSUNAMI:LynxOS:2.*:*) echo sparc-unknown-lynxos${UNAME_RELEASE} exit 0 ;; rs6000:LynxOS:2.*:*) echo rs6000-unknown-lynxos${UNAME_RELEASE} exit 0 ;; PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) echo powerpc-unknown-lynxos${UNAME_RELEASE} exit 0 ;; SM[BE]S:UNIX_SV:*:*) echo mips-dde-sysv${UNAME_RELEASE} exit 0 ;; RM*:ReliantUNIX-*:*:*) echo mips-sni-sysv4 exit 0 ;; RM*:SINIX-*:*:*) echo mips-sni-sysv4 exit 0 ;; *:SINIX-*:*:*) if uname -p 2>/dev/null >/dev/null ; then UNAME_MACHINE=`(uname -p) 2>/dev/null` echo ${UNAME_MACHINE}-sni-sysv4 else echo ns32k-sni-sysv fi exit 0 ;; PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort # says echo i586-unisys-sysv4 exit 0 ;; *:UNIX_System_V:4*:FTX*) # From Gerald Hewes . # How about differentiating between stratus architectures? -djm echo hppa1.1-stratus-sysv4 exit 0 ;; *:*:*:FTX*) # From seanf@swdc.stratus.com. echo i860-stratus-sysv4 exit 0 ;; *:VOS:*:*) # From Paul.Green@stratus.com. echo hppa1.1-stratus-vos exit 0 ;; mc68*:A/UX:*:*) echo m68k-apple-aux${UNAME_RELEASE} exit 0 ;; news*:NEWS-OS:6*:*) echo mips-sony-newsos6 exit 0 ;; R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) if [ -d /usr/nec ]; then echo mips-nec-sysv${UNAME_RELEASE} else echo mips-unknown-sysv${UNAME_RELEASE} fi exit 0 ;; BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. echo powerpc-be-beos exit 0 ;; BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. echo powerpc-apple-beos exit 0 ;; BePC:BeOS:*:*) # BeOS running on Intel PC compatible. echo i586-pc-beos exit 0 ;; SX-4:SUPER-UX:*:*) echo sx4-nec-superux${UNAME_RELEASE} exit 0 ;; SX-5:SUPER-UX:*:*) echo sx5-nec-superux${UNAME_RELEASE} exit 0 ;; SX-6:SUPER-UX:*:*) echo sx6-nec-superux${UNAME_RELEASE} exit 0 ;; Power*:Rhapsody:*:*) echo powerpc-apple-rhapsody${UNAME_RELEASE} exit 0 ;; *:Rhapsody:*:*) echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} exit 0 ;; *:Darwin:*:*) case `uname -p` in *86) UNAME_PROCESSOR=i686 ;; powerpc) UNAME_PROCESSOR=powerpc ;; esac echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} exit 0 ;; *:procnto*:*:* | *:QNX:[0123456789]*:*) UNAME_PROCESSOR=`uname -p` if test "$UNAME_PROCESSOR" = "x86"; then UNAME_PROCESSOR=i386 UNAME_MACHINE=pc fi echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} exit 0 ;; *:QNX:*:4*) echo i386-pc-qnx exit 0 ;; NSR-[DGKLNPTVW]:NONSTOP_KERNEL:*:*) echo nsr-tandem-nsk${UNAME_RELEASE} exit 0 ;; *:NonStop-UX:*:*) echo mips-compaq-nonstopux exit 0 ;; BS2000:POSIX*:*:*) echo bs2000-siemens-sysv exit 0 ;; DS/*:UNIX_System_V:*:*) echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} exit 0 ;; *:Plan9:*:*) # "uname -m" is not consistent, so use $cputype instead. 386 # is converted to i386 for consistency with other x86 # operating systems. if test "$cputype" = "386"; then UNAME_MACHINE=i386 else UNAME_MACHINE="$cputype" fi echo ${UNAME_MACHINE}-unknown-plan9 exit 0 ;; *:TOPS-10:*:*) echo pdp10-unknown-tops10 exit 0 ;; *:TENEX:*:*) echo pdp10-unknown-tenex exit 0 ;; KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) echo pdp10-dec-tops20 exit 0 ;; XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) echo pdp10-xkl-tops20 exit 0 ;; *:TOPS-20:*:*) echo pdp10-unknown-tops20 exit 0 ;; *:ITS:*:*) echo pdp10-unknown-its exit 0 ;; SEI:*:*:SEIUX) echo mips-sei-seiux${UNAME_RELEASE} exit 0 ;; esac #echo '(No uname command or uname output not recognized.)' 1>&2 #echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 eval $set_cc_for_build cat >$dummy.c < # include #endif main () { #if defined (sony) #if defined (MIPSEB) /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, I don't know.... */ printf ("mips-sony-bsd\n"); exit (0); #else #include printf ("m68k-sony-newsos%s\n", #ifdef NEWSOS4 "4" #else "" #endif ); exit (0); #endif #endif #if defined (__arm) && defined (__acorn) && defined (__unix) printf ("arm-acorn-riscix"); exit (0); #endif #if defined (hp300) && !defined (hpux) printf ("m68k-hp-bsd\n"); exit (0); #endif #if defined (NeXT) #if !defined (__ARCHITECTURE__) #define __ARCHITECTURE__ "m68k" #endif int version; version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; if (version < 4) printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); else printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); exit (0); #endif #if defined (MULTIMAX) || defined (n16) #if defined (UMAXV) printf ("ns32k-encore-sysv\n"); exit (0); #else #if defined (CMU) printf ("ns32k-encore-mach\n"); exit (0); #else printf ("ns32k-encore-bsd\n"); exit (0); #endif #endif #endif #if defined (__386BSD__) printf ("i386-pc-bsd\n"); exit (0); #endif #if defined (sequent) #if defined (i386) printf ("i386-sequent-dynix\n"); exit (0); #endif #if defined (ns32000) printf ("ns32k-sequent-dynix\n"); exit (0); #endif #endif #if defined (_SEQUENT_) struct utsname un; uname(&un); if (strncmp(un.version, "V2", 2) == 0) { printf ("i386-sequent-ptx2\n"); exit (0); } if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ printf ("i386-sequent-ptx1\n"); exit (0); } printf ("i386-sequent-ptx\n"); exit (0); #endif #if defined (vax) # if !defined (ultrix) # include # if defined (BSD) # if BSD == 43 printf ("vax-dec-bsd4.3\n"); exit (0); # else # if BSD == 199006 printf ("vax-dec-bsd4.3reno\n"); exit (0); # else printf ("vax-dec-bsd\n"); exit (0); # endif # endif # else printf ("vax-dec-bsd\n"); exit (0); # endif # else printf ("vax-dec-ultrix\n"); exit (0); # endif #endif #if defined (alliant) && defined (i860) printf ("i860-alliant-bsd\n"); exit (0); #endif exit (1); } EOF $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && $dummy && exit 0 # Apollos put the system type in the environment. test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit 0; } # Convex versions that predate uname can use getsysinfo(1) if [ -x /usr/convex/getsysinfo ] then case `getsysinfo -f cpu_type` in c1*) echo c1-convex-bsd exit 0 ;; c2*) if getsysinfo -f scalar_acc then echo c32-convex-bsd else echo c2-convex-bsd fi exit 0 ;; c34*) echo c34-convex-bsd exit 0 ;; c38*) echo c38-convex-bsd exit 0 ;; c4*) echo c4-convex-bsd exit 0 ;; esac fi cat >&2 < in order to provide the needed information to handle your system. config.guess timestamp = $timestamp uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` /bin/uname -X = `(/bin/uname -X) 2>/dev/null` hostinfo = `(hostinfo) 2>/dev/null` /bin/universe = `(/bin/universe) 2>/dev/null` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` /bin/arch = `(/bin/arch) 2>/dev/null` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` UNAME_MACHINE = ${UNAME_MACHINE} UNAME_RELEASE = ${UNAME_RELEASE} UNAME_SYSTEM = ${UNAME_SYSTEM} UNAME_VERSION = ${UNAME_VERSION} EOF exit 1 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: pgtop/config.sub000077500000000000000000000731531271046472400142120ustar00rootroot00000000000000#! /bin/sh # Configuration validation subroutine script. # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, # 2000, 2001, 2002, 2003 Free Software Foundation, Inc. timestamp='2003-07-04' # This file is (in principle) common to ALL GNU software. # The presence of a machine in this file suggests that SOME GNU software # can handle that machine. It does not imply ALL GNU software can. # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. # As a special exception to the GNU General Public License, if you # distribute this file as part of a program that contains a # configuration script generated by Autoconf, you may include it under # the same distribution terms that you use for the rest of that program. # Please send patches to . Submit a context # diff and a properly formatted ChangeLog entry. # # Configuration subroutine to validate and canonicalize a configuration type. # Supply the specified configuration type as an argument. # If it is invalid, we print an error message on stderr and exit with code 1. # Otherwise, we print the canonical config type on stdout and succeed. # This file is supposed to be the same for all GNU packages # and recognize all the CPU types, system types and aliases # that are meaningful with *any* GNU software. # Each package is responsible for reporting which valid configurations # it does not support. The user should be able to distinguish # a failure to support a valid configuration from a meaningless # configuration. # The goal of this file is to map all the various variations of a given # machine specification into a single specification in the form: # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM # or in some cases, the newer four-part form: # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM # It is wrong to echo any other type of specification. me=`echo "$0" | sed -e 's,.*/,,'` usage="\ Usage: $0 [OPTION] CPU-MFR-OPSYS $0 [OPTION] ALIAS Canonicalize a configuration name. Operation modes: -h, --help print this help, then exit -t, --time-stamp print date of last modification, then exit -v, --version print version number, then exit Report bugs and patches to ." version="\ GNU config.sub ($timestamp) Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." help=" Try \`$me --help' for more information." # Parse command line while test $# -gt 0 ; do case $1 in --time-stamp | --time* | -t ) echo "$timestamp" ; exit 0 ;; --version | -v ) echo "$version" ; exit 0 ;; --help | --h* | -h ) echo "$usage"; exit 0 ;; -- ) # Stop option processing shift; break ;; - ) # Use stdin as input. break ;; -* ) echo "$me: invalid option $1$help" exit 1 ;; *local*) # First pass through any local machine types. echo $1 exit 0;; * ) break ;; esac done case $# in 0) echo "$me: missing argument$help" >&2 exit 1;; 1) ;; *) echo "$me: too many arguments$help" >&2 exit 1;; esac # Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). # Here we must recognize all the valid KERNEL-OS combinations. maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` case $maybe_os in nto-qnx* | linux-gnu* | kfreebsd*-gnu* | netbsd*-gnu* | storm-chaos* | os2-emx* | rtmk-nova*) os=-$maybe_os basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` ;; *) basic_machine=`echo $1 | sed 's/-[^-]*$//'` if [ $basic_machine != $1 ] then os=`echo $1 | sed 's/.*-/-/'` else os=; fi ;; esac ### Let's recognize common machines as not being operating systems so ### that things like config.sub decstation-3100 work. We also ### recognize some manufacturers as not being operating systems, so we ### can provide default operating systems below. case $os in -sun*os*) # Prevent following clause from handling this invalid input. ;; -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ -apple | -axis) os= basic_machine=$1 ;; -sim | -cisco | -oki | -wec | -winbond) os= basic_machine=$1 ;; -scout) ;; -wrs) os=-vxworks basic_machine=$1 ;; -chorusos*) os=-chorusos basic_machine=$1 ;; -chorusrdb) os=-chorusrdb basic_machine=$1 ;; -hiux*) os=-hiuxwe2 ;; -sco5) os=-sco3.2v5 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco4) os=-sco3.2v4 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2.[4-9]*) os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco3.2v[4-9]*) # Don't forget version if it is 3.2v4 or newer. basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -sco*) os=-sco3.2v2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -udk*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -isc) os=-isc2.2 basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -clix*) basic_machine=clipper-intergraph ;; -isc*) basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` ;; -lynx*) os=-lynxos ;; -ptx*) basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` ;; -windowsnt*) os=`echo $os | sed -e 's/windowsnt/winnt/'` ;; -psos*) os=-psos ;; -mint | -mint[0-9]*) basic_machine=m68k-atari os=-mint ;; esac # Decode aliases for certain CPU-COMPANY combinations. case $basic_machine in # Recognize the basic CPU types without company name. # Some are omitted here because they have special meanings below. 1750a | 580 \ | a29k \ | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr \ | c4x | clipper \ | d10v | d30v | dlx | dsp16xx \ | fr30 | frv \ | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ | i370 | i860 | i960 | ia64 \ | ip2k \ | m32r | m68000 | m68k | m88k | mcore \ | mips | mipsbe | mipseb | mipsel | mipsle \ | mips16 \ | mips64 | mips64el \ | mips64vr | mips64vrel \ | mips64orion | mips64orionel \ | mips64vr4100 | mips64vr4100el \ | mips64vr4300 | mips64vr4300el \ | mips64vr5000 | mips64vr5000el \ | mipsisa32 | mipsisa32el \ | mipsisa32r2 | mipsisa32r2el \ | mipsisa64 | mipsisa64el \ | mipsisa64sb1 | mipsisa64sb1el \ | mipsisa64sr71k | mipsisa64sr71kel \ | mipstx39 | mipstx39el \ | mn10200 | mn10300 \ | msp430 \ | ns16k | ns32k \ | openrisc | or32 \ | pdp10 | pdp11 | pj | pjl \ | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ | pyramid \ | sh | sh[1234] | sh[23]e | sh[34]eb | shbe | shle | sh[1234]le | sh3ele \ | sh64 | sh64le \ | sparc | sparc64 | sparc86x | sparclet | sparclite | sparcv9 | sparcv9b \ | strongarm \ | tahoe | thumb | tic4x | tic80 | tron \ | v850 | v850e \ | we32k \ | x86 | xscale | xstormy16 | xtensa \ | z8k) basic_machine=$basic_machine-unknown ;; m6811 | m68hc11 | m6812 | m68hc12) # Motorola 68HC11/12. basic_machine=$basic_machine-unknown os=-none ;; m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) ;; # We use `pc' rather than `unknown' # because (1) that's what they normally are, and # (2) the word "unknown" tends to confuse beginning users. i*86 | x86_64) basic_machine=$basic_machine-pc ;; # Object if more than one company name word. *-*-*) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; # Recognize the basic CPU types with company name. 580-* \ | a29k-* \ | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ | alphapca5[67]-* | alpha64pca5[67]-* | amd64-* | arc-* \ | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ | avr-* \ | bs2000-* \ | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ | clipper-* | cydra-* \ | d10v-* | d30v-* | dlx-* \ | elxsi-* \ | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ | h8300-* | h8500-* \ | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ | i*86-* | i860-* | i960-* | ia64-* \ | ip2k-* \ | m32r-* \ | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ | m88110-* | m88k-* | mcore-* \ | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ | mips16-* \ | mips64-* | mips64el-* \ | mips64vr-* | mips64vrel-* \ | mips64orion-* | mips64orionel-* \ | mips64vr4100-* | mips64vr4100el-* \ | mips64vr4300-* | mips64vr4300el-* \ | mips64vr5000-* | mips64vr5000el-* \ | mipsisa32-* | mipsisa32el-* \ | mipsisa32r2-* | mipsisa32r2el-* \ | mipsisa64-* | mipsisa64el-* \ | mipsisa64sb1-* | mipsisa64sb1el-* \ | mipsisa64sr71k-* | mipsisa64sr71kel-* \ | mipstx39-* | mipstx39el-* \ | msp430-* \ | none-* | np1-* | nv1-* | ns16k-* | ns32k-* \ | orion-* \ | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ | pyramid-* \ | romp-* | rs6000-* \ | sh-* | sh[1234]-* | sh[23]e-* | sh[34]eb-* | shbe-* \ | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ | sparc-* | sparc64-* | sparc86x-* | sparclet-* | sparclite-* \ | sparcv9-* | sparcv9b-* | strongarm-* | sv1-* | sx?-* \ | tahoe-* | thumb-* \ | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ | tron-* \ | v850-* | v850e-* | vax-* \ | we32k-* \ | x86-* | x86_64-* | xps100-* | xscale-* | xstormy16-* \ | xtensa-* \ | ymp-* \ | z8k-*) ;; # Recognize the various machine names and aliases which stand # for a CPU type and a company and sometimes even an OS. 386bsd) basic_machine=i386-unknown os=-bsd ;; 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) basic_machine=m68000-att ;; 3b*) basic_machine=we32k-att ;; a29khif) basic_machine=a29k-amd os=-udi ;; adobe68k) basic_machine=m68010-adobe os=-scout ;; alliant | fx80) basic_machine=fx80-alliant ;; altos | altos3068) basic_machine=m68k-altos ;; am29k) basic_machine=a29k-none os=-bsd ;; amd64) basic_machine=x86_64-pc ;; amdahl) basic_machine=580-amdahl os=-sysv ;; amiga | amiga-*) basic_machine=m68k-unknown ;; amigaos | amigados) basic_machine=m68k-unknown os=-amigaos ;; amigaunix | amix) basic_machine=m68k-unknown os=-sysv4 ;; apollo68) basic_machine=m68k-apollo os=-sysv ;; apollo68bsd) basic_machine=m68k-apollo os=-bsd ;; aux) basic_machine=m68k-apple os=-aux ;; balance) basic_machine=ns32k-sequent os=-dynix ;; c90) basic_machine=c90-cray os=-unicos ;; convex-c1) basic_machine=c1-convex os=-bsd ;; convex-c2) basic_machine=c2-convex os=-bsd ;; convex-c32) basic_machine=c32-convex os=-bsd ;; convex-c34) basic_machine=c34-convex os=-bsd ;; convex-c38) basic_machine=c38-convex os=-bsd ;; cray | j90) basic_machine=j90-cray os=-unicos ;; crds | unos) basic_machine=m68k-crds ;; cris | cris-* | etrax*) basic_machine=cris-axis ;; da30 | da30-*) basic_machine=m68k-da30 ;; decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) basic_machine=mips-dec ;; decsystem10* | dec10*) basic_machine=pdp10-dec os=-tops10 ;; decsystem20* | dec20*) basic_machine=pdp10-dec os=-tops20 ;; delta | 3300 | motorola-3300 | motorola-delta \ | 3300-motorola | delta-motorola) basic_machine=m68k-motorola ;; delta88) basic_machine=m88k-motorola os=-sysv3 ;; dpx20 | dpx20-*) basic_machine=rs6000-bull os=-bosx ;; dpx2* | dpx2*-bull) basic_machine=m68k-bull os=-sysv3 ;; ebmon29k) basic_machine=a29k-amd os=-ebmon ;; elxsi) basic_machine=elxsi-elxsi os=-bsd ;; encore | umax | mmax) basic_machine=ns32k-encore ;; es1800 | OSE68k | ose68k | ose | OSE) basic_machine=m68k-ericsson os=-ose ;; fx2800) basic_machine=i860-alliant ;; genix) basic_machine=ns32k-ns ;; gmicro) basic_machine=tron-gmicro os=-sysv ;; go32) basic_machine=i386-pc os=-go32 ;; h3050r* | hiux*) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; h8300hms) basic_machine=h8300-hitachi os=-hms ;; h8300xray) basic_machine=h8300-hitachi os=-xray ;; h8500hms) basic_machine=h8500-hitachi os=-hms ;; harris) basic_machine=m88k-harris os=-sysv3 ;; hp300-*) basic_machine=m68k-hp ;; hp300bsd) basic_machine=m68k-hp os=-bsd ;; hp300hpux) basic_machine=m68k-hp os=-hpux ;; hp3k9[0-9][0-9] | hp9[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k2[0-9][0-9] | hp9k31[0-9]) basic_machine=m68000-hp ;; hp9k3[2-9][0-9]) basic_machine=m68k-hp ;; hp9k6[0-9][0-9] | hp6[0-9][0-9]) basic_machine=hppa1.0-hp ;; hp9k7[0-79][0-9] | hp7[0-79][0-9]) basic_machine=hppa1.1-hp ;; hp9k78[0-9] | hp78[0-9]) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) # FIXME: really hppa2.0-hp basic_machine=hppa1.1-hp ;; hp9k8[0-9][13679] | hp8[0-9][13679]) basic_machine=hppa1.1-hp ;; hp9k8[0-9][0-9] | hp8[0-9][0-9]) basic_machine=hppa1.0-hp ;; hppa-next) os=-nextstep3 ;; hppaosf) basic_machine=hppa1.1-hp os=-osf ;; hppro) basic_machine=hppa1.1-hp os=-proelf ;; i370-ibm* | ibm*) basic_machine=i370-ibm ;; # I'm not sure what "Sysv32" means. Should this be sysv3.2? i*86v32) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv32 ;; i*86v4*) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv4 ;; i*86v) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-sysv ;; i*86sol2) basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` os=-solaris2 ;; i386mach) basic_machine=i386-mach os=-mach ;; i386-vsta | vsta) basic_machine=i386-unknown os=-vsta ;; iris | iris4d) basic_machine=mips-sgi case $os in -irix*) ;; *) os=-irix4 ;; esac ;; isi68 | isi) basic_machine=m68k-isi os=-sysv ;; m88k-omron*) basic_machine=m88k-omron ;; magnum | m3230) basic_machine=mips-mips os=-sysv ;; merlin) basic_machine=ns32k-utek os=-sysv ;; mingw32) basic_machine=i386-pc os=-mingw32 ;; miniframe) basic_machine=m68000-convergent ;; *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) basic_machine=m68k-atari os=-mint ;; mips3*-*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` ;; mips3*) basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown ;; mmix*) basic_machine=mmix-knuth os=-mmixware ;; monitor) basic_machine=m68k-rom68k os=-coff ;; morphos) basic_machine=powerpc-unknown os=-morphos ;; msdos) basic_machine=i386-pc os=-msdos ;; mvs) basic_machine=i370-ibm os=-mvs ;; ncr3000) basic_machine=i486-ncr os=-sysv4 ;; netbsd386) basic_machine=i386-unknown os=-netbsd ;; netwinder) basic_machine=armv4l-rebel os=-linux ;; news | news700 | news800 | news900) basic_machine=m68k-sony os=-newsos ;; news1000) basic_machine=m68030-sony os=-newsos ;; news-3600 | risc-news) basic_machine=mips-sony os=-newsos ;; necv70) basic_machine=v70-nec os=-sysv ;; next | m*-next ) basic_machine=m68k-next case $os in -nextstep* ) ;; -ns2*) os=-nextstep2 ;; *) os=-nextstep3 ;; esac ;; nh3000) basic_machine=m68k-harris os=-cxux ;; nh[45]000) basic_machine=m88k-harris os=-cxux ;; nindy960) basic_machine=i960-intel os=-nindy ;; mon960) basic_machine=i960-intel os=-mon960 ;; nonstopux) basic_machine=mips-compaq os=-nonstopux ;; np1) basic_machine=np1-gould ;; nv1) basic_machine=nv1-cray os=-unicosmp ;; nsr-tandem) basic_machine=nsr-tandem ;; op50n-* | op60c-*) basic_machine=hppa1.1-oki os=-proelf ;; or32 | or32-*) basic_machine=or32-unknown os=-coff ;; OSE68000 | ose68000) basic_machine=m68000-ericsson os=-ose ;; os68k) basic_machine=m68k-none os=-os68k ;; pa-hitachi) basic_machine=hppa1.1-hitachi os=-hiuxwe2 ;; paragon) basic_machine=i860-intel os=-osf ;; pbd) basic_machine=sparc-tti ;; pbb) basic_machine=m68k-tti ;; pc532 | pc532-*) basic_machine=ns32k-pc532 ;; pentium | p5 | k5 | k6 | nexgen | viac3) basic_machine=i586-pc ;; pentiumpro | p6 | 6x86 | athlon | athlon_*) basic_machine=i686-pc ;; pentiumii | pentium2 | pentiumiii | pentium3) basic_machine=i686-pc ;; pentium4) basic_machine=i786-pc ;; pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumpro-* | p6-* | 6x86-* | athlon-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pentium4-*) basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` ;; pn) basic_machine=pn-gould ;; power) basic_machine=power-ibm ;; ppc) basic_machine=powerpc-unknown ;; ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppcle | powerpclittle | ppc-le | powerpc-little) basic_machine=powerpcle-unknown ;; ppcle-* | powerpclittle-*) basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64) basic_machine=powerpc64-unknown ;; ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ppc64le | powerpc64little | ppc64-le | powerpc64-little) basic_machine=powerpc64le-unknown ;; ppc64le-* | powerpc64little-*) basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` ;; ps2) basic_machine=i386-ibm ;; pw32) basic_machine=i586-unknown os=-pw32 ;; rom68k) basic_machine=m68k-rom68k os=-coff ;; rm[46]00) basic_machine=mips-siemens ;; rtpc | rtpc-*) basic_machine=romp-ibm ;; s390 | s390-*) basic_machine=s390-ibm ;; s390x | s390x-*) basic_machine=s390x-ibm ;; sa29200) basic_machine=a29k-amd os=-udi ;; sb1) basic_machine=mipsisa64sb1-unknown ;; sb1el) basic_machine=mipsisa64sb1el-unknown ;; sei) basic_machine=mips-sei os=-seiux ;; sequent) basic_machine=i386-sequent ;; sh) basic_machine=sh-hitachi os=-hms ;; sh64) basic_machine=sh64-unknown ;; sparclite-wrs | simso-wrs) basic_machine=sparclite-wrs os=-vxworks ;; sps7) basic_machine=m68k-bull os=-sysv2 ;; spur) basic_machine=spur-unknown ;; st2000) basic_machine=m68k-tandem ;; stratus) basic_machine=i860-stratus os=-sysv4 ;; sun2) basic_machine=m68000-sun ;; sun2os3) basic_machine=m68000-sun os=-sunos3 ;; sun2os4) basic_machine=m68000-sun os=-sunos4 ;; sun3os3) basic_machine=m68k-sun os=-sunos3 ;; sun3os4) basic_machine=m68k-sun os=-sunos4 ;; sun4os3) basic_machine=sparc-sun os=-sunos3 ;; sun4os4) basic_machine=sparc-sun os=-sunos4 ;; sun4sol2) basic_machine=sparc-sun os=-solaris2 ;; sun3 | sun3-*) basic_machine=m68k-sun ;; sun4) basic_machine=sparc-sun ;; sun386 | sun386i | roadrunner) basic_machine=i386-sun ;; sv1) basic_machine=sv1-cray os=-unicos ;; symmetry) basic_machine=i386-sequent os=-dynix ;; t3e) basic_machine=alphaev5-cray os=-unicos ;; t90) basic_machine=t90-cray os=-unicos ;; tic54x | c54x*) basic_machine=tic54x-unknown os=-coff ;; tic55x | c55x*) basic_machine=tic55x-unknown os=-coff ;; tic6x | c6x*) basic_machine=tic6x-unknown os=-coff ;; tx39) basic_machine=mipstx39-unknown ;; tx39el) basic_machine=mipstx39el-unknown ;; toad1) basic_machine=pdp10-xkl os=-tops20 ;; tower | tower-32) basic_machine=m68k-ncr ;; udi29k) basic_machine=a29k-amd os=-udi ;; ultra3) basic_machine=a29k-nyu os=-sym1 ;; v810 | necv810) basic_machine=v810-nec os=-none ;; vaxv) basic_machine=vax-dec os=-sysv ;; vms) basic_machine=vax-dec os=-vms ;; vpp*|vx|vx-*) basic_machine=f301-fujitsu ;; vxworks960) basic_machine=i960-wrs os=-vxworks ;; vxworks68) basic_machine=m68k-wrs os=-vxworks ;; vxworks29k) basic_machine=a29k-wrs os=-vxworks ;; w65*) basic_machine=w65-wdc os=-none ;; w89k-*) basic_machine=hppa1.1-winbond os=-proelf ;; xps | xps100) basic_machine=xps100-honeywell ;; ymp) basic_machine=ymp-cray os=-unicos ;; z8k-*-coff) basic_machine=z8k-unknown os=-sim ;; none) basic_machine=none-none os=-none ;; # Here we handle the default manufacturer of certain CPU types. It is in # some cases the only manufacturer, in others, it is the most popular. w89k) basic_machine=hppa1.1-winbond ;; op50n) basic_machine=hppa1.1-oki ;; op60c) basic_machine=hppa1.1-oki ;; romp) basic_machine=romp-ibm ;; rs6000) basic_machine=rs6000-ibm ;; vax) basic_machine=vax-dec ;; pdp10) # there are many clones, so DEC is not a safe bet basic_machine=pdp10-unknown ;; pdp11) basic_machine=pdp11-dec ;; we32k) basic_machine=we32k-att ;; sh3 | sh4 | sh[34]eb | sh[1234]le | sh[23]ele) basic_machine=sh-unknown ;; sh64) basic_machine=sh64-unknown ;; sparc | sparcv9 | sparcv9b) basic_machine=sparc-sun ;; cydra) basic_machine=cydra-cydrome ;; orion) basic_machine=orion-highlevel ;; orion105) basic_machine=clipper-highlevel ;; mac | mpw | mac-mpw) basic_machine=m68k-apple ;; pmac | pmac-mpw) basic_machine=powerpc-apple ;; *-unknown) # Make sure to match an already-canonicalized machine name. ;; *) echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 exit 1 ;; esac # Here we canonicalize certain aliases for manufacturers. case $basic_machine in *-digital*) basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` ;; *-commodore*) basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` ;; *) ;; esac # Decode manufacturer-specific aliases for certain operating systems. if [ x"$os" != x"" ] then case $os in # First match some system type aliases # that might get confused with valid system types. # -solaris* is a basic system type, with this one exception. -solaris1 | -solaris1.*) os=`echo $os | sed -e 's|solaris1|sunos4|'` ;; -solaris) os=-solaris2 ;; -svr4*) os=-sysv4 ;; -unixware*) os=-sysv4.2uw ;; -gnu/linux*) os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` ;; # First accept the basic system types. # The portable systems comes first. # Each alternative MUST END IN A *, to match a version number. # -sysv* is not here because it comes later, after sysvr4. -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ | -aos* \ | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ | -hiux* | -386bsd* | -netbsd* | -openbsd* | -kfreebsd* | -freebsd* | -riscix* \ | -lynxos* | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ | -chorusos* | -chorusrdb* \ | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ | -mingw32* | -linux-gnu* | -uxpv* | -beos* | -mpeix* | -udk* \ | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ | -powermax* | -dnix* | -nx6 | -nx7 | -sei*) # Remember, each alternative MUST END IN *, to match a version number. ;; -qnx*) case $basic_machine in x86-* | i*86-*) ;; *) os=-nto$os ;; esac ;; -nto-qnx*) ;; -nto*) os=`echo $os | sed -e 's|nto|nto-qnx|'` ;; -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ | -windows* | -osx | -abug | -netware* | -os9* | -beos* \ | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) ;; -mac*) os=`echo $os | sed -e 's|mac|macos|'` ;; -linux*) os=`echo $os | sed -e 's|linux|linux-gnu|'` ;; -sunos5*) os=`echo $os | sed -e 's|sunos5|solaris2|'` ;; -sunos6*) os=`echo $os | sed -e 's|sunos6|solaris3|'` ;; -opened*) os=-openedition ;; -wince*) os=-wince ;; -osfrose*) os=-osfrose ;; -osf*) os=-osf ;; -utek*) os=-bsd ;; -dynix*) os=-bsd ;; -acis*) os=-aos ;; -atheos*) os=-atheos ;; -386bsd) os=-bsd ;; -ctix* | -uts*) os=-sysv ;; -nova*) os=-rtmk-nova ;; -ns2 ) os=-nextstep2 ;; -nsk*) os=-nsk ;; # Preserve the version number of sinix5. -sinix5.*) os=`echo $os | sed -e 's|sinix|sysv|'` ;; -sinix*) os=-sysv4 ;; -triton*) os=-sysv3 ;; -oss*) os=-sysv3 ;; -svr4) os=-sysv4 ;; -svr3) os=-sysv3 ;; -sysvr4) os=-sysv4 ;; # This must come after -sysvr4. -sysv*) ;; -ose*) os=-ose ;; -es1800*) os=-ose ;; -xenix) os=-xenix ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) os=-mint ;; -aros*) os=-aros ;; -kaos*) os=-kaos ;; -none) ;; *) # Get rid of the `-' at the beginning of $os. os=`echo $os | sed 's/[^-]*-//'` echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 exit 1 ;; esac else # Here we handle the default operating systems that come with various machines. # The value should be what the vendor currently ships out the door with their # machine or put another way, the most popular os provided with the machine. # Note that if you're going to try to match "-MANUFACTURER" here (say, # "-sun"), then you have to tell the case statement up towards the top # that MANUFACTURER isn't an operating system. Otherwise, code above # will signal an error saying that MANUFACTURER isn't an operating # system, and we'll never get to this point. case $basic_machine in *-acorn) os=-riscix1.2 ;; arm*-rebel) os=-linux ;; arm*-semi) os=-aout ;; c4x-* | tic4x-*) os=-coff ;; # This must come before the *-dec entry. pdp10-*) os=-tops20 ;; pdp11-*) os=-none ;; *-dec | vax-*) os=-ultrix4.2 ;; m68*-apollo) os=-domain ;; i386-sun) os=-sunos4.0.2 ;; m68000-sun) os=-sunos3 # This also exists in the configure program, but was not the # default. # os=-sunos4 ;; m68*-cisco) os=-aout ;; mips*-cisco) os=-elf ;; mips*-*) os=-elf ;; or32-*) os=-coff ;; *-tti) # must be before sparc entry or we get the wrong os. os=-sysv3 ;; sparc-* | *-sun) os=-sunos4.1.1 ;; *-be) os=-beos ;; *-ibm) os=-aix ;; *-wec) os=-proelf ;; *-winbond) os=-proelf ;; *-oki) os=-proelf ;; *-hp) os=-hpux ;; *-hitachi) os=-hiux ;; i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) os=-sysv ;; *-cbm) os=-amigaos ;; *-dg) os=-dgux ;; *-dolphin) os=-sysv3 ;; m68k-ccur) os=-rtu ;; m88k-omron*) os=-luna ;; *-next ) os=-nextstep ;; *-sequent) os=-ptx ;; *-crds) os=-unos ;; *-ns) os=-genix ;; i370-*) os=-mvs ;; *-next) os=-nextstep3 ;; *-gould) os=-sysv ;; *-highlevel) os=-bsd ;; *-encore) os=-bsd ;; *-sgi) os=-irix ;; *-siemens) os=-sysv4 ;; *-masscomp) os=-rtu ;; f30[01]-fujitsu | f700-fujitsu) os=-uxpv ;; *-rom68k) os=-coff ;; *-*bug) os=-coff ;; *-apple) os=-macos ;; *-atari*) os=-mint ;; *) os=-none ;; esac fi # Here we handle the case where we know the os, and the CPU type, but not the # manufacturer. We pick the logical manufacturer. vendor=unknown case $basic_machine in *-unknown) case $os in -riscix*) vendor=acorn ;; -sunos*) vendor=sun ;; -aix*) vendor=ibm ;; -beos*) vendor=be ;; -hpux*) vendor=hp ;; -mpeix*) vendor=hp ;; -hiux*) vendor=hitachi ;; -unos*) vendor=crds ;; -dgux*) vendor=dg ;; -luna*) vendor=omron ;; -genix*) vendor=ns ;; -mvs* | -opened*) vendor=ibm ;; -ptx*) vendor=sequent ;; -vxsim* | -vxworks* | -windiss*) vendor=wrs ;; -aux*) vendor=apple ;; -hms*) vendor=hitachi ;; -mpw* | -macos*) vendor=apple ;; -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) vendor=atari ;; -vos*) vendor=stratus ;; esac basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` ;; esac echo $basic_machine$os exit 0 # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "timestamp='" # time-stamp-format: "%:y-%02m-%02d" # time-stamp-end: "'" # End: pgtop/configure.ac000077500000000000000000000177341271046472400145230ustar00rootroot00000000000000# configure.ac AC_PREREQ(2.59) AC_INIT(pg_top, 4.0.0devel, ptop-hackers@pgfoundry.org) echo "Configuring $PACKAGE_STRING" AC_CONFIG_HEADERS([config.h]) AC_CANONICAL_BUILD AC_CANONICAL_HOST AC_CANONICAL_TARGET AM_INIT_AUTOMAKE([1.7 -Wall subdir-objects]) # options processing AC_ARG_WITH(module, AC_HELP_STRING([--with-module=NAME], [use the platform module NAME]), [if test ! -f machine/m_$withval.c; then AC_MSG_ERROR([No such module $withval]); fi]) AC_ARG_WITH(ext, AC_HELP_STRING([--with-ext=EXT], [use the extension EXT]), [if test -f ext/$withval.c; then AC_DEFINE(WITH_EXT, 1, [Include code that utilizes extensions]) SRC="$SRC ext/$withval.c" OBJ="$OBJ $withval.o" else AC_MSG_ERROR([No such extension $withval]) fi]) AC_ARG_WITH(postgresql, [AC_HELP_STRING([--with-postgresql=DIR], [Default on. Set to the path of the PostgreSQL's installation.])], [pgsql_path=$withval]) AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug], [enable support for debugging output])) if test "x$enable_debug" = xyes; then AC_DEFINE(DEBUG, 1, [Support for debugging output]) fi AC_ARG_ENABLE(kill, AC_HELP_STRING([--disable-kill], [disable kill and renice commands]), ,[enable_kill=yes]) if test x$enable_kill = xyes; then AC_DEFINE(ENABLE_KILL, 1, [Enable kill and renice]) fi enable_color=yes AC_ARG_ENABLE(color, AC_HELP_STRING([--disable-color], [disable the use of color])) AC_ARG_ENABLE(colour, AC_HELP_STRING([--disable-colour], [synonym for --disable-color]), [enable_color=$enableval]) if test x$enable_color = xyes; then AC_DEFINE(ENABLE_COLOR, 1, [Enable color]) fi # check for needed programs AC_CHECK_PROGS(MAKE, make) AC_PROG_CC AC_PROG_AWK AC_PROG_INSTALL # system checks # we make the version number available as a C preprocessor definition AC_MSG_CHECKING(OS revision number) osrev=`uname -r | tr -cd ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789` if test "$osrev" != "unknown" ; then AC_DEFINE_UNQUOTED(OSREV, $osrev, [Define the OS revision.]) osmajor=`uname -r | sed 's/^\([[0-9]]*\).*$/\1/'` if test -n "$osmajor"; then AC_DEFINE_UNQUOTED(OSMAJOR, $osmajor, [Define the major OS revision number.]) fi else AC_DEFINE(OSREV, "") fi AC_MSG_RESULT($osrev) # checks for postgresql if test -z "$pgsql_path"; then AC_PATH_PROGS(PG_CONFIG, pg_config, no) else AC_PATH_PROGS(PG_CONFIG, pg_config, no, "$pgsql_path/bin") fi if test -z "$PG_CONFIG" || test ! -r "$PG_CONFIG"; then AC_MSG_ERROR([pg_config not found.]) fi AC_MSG_CHECKING([for PostgreSQL libraries]) BINDIR=`$PG_CONFIG --bindir` INCLUDEDIR=`$PG_CONFIG --includedir` DBCFLAGS="-I$INCLUDEDIR" LIBDIR=`$PG_CONFIG --libdir` DBLDFLAGS="-L$LIBDIR" LDFLAGS="$LDFLAGS $DBLDFLAGS" AC_SUBST(DBCFLAGS) AC_SUBST(DBLDFLAGS) # checks for libraries AC_SEARCH_LIBS(PQexec, pq) AC_SEARCH_LIBS(kstat_open, kstat) AC_SEARCH_LIBS(kvm_open, kvm) AC_SEARCH_LIBS(elf32_getphdr, elf) # -lmld -lmach AC_SEARCH_LIBS(vm_statistics, mach) AC_SEARCH_LIBS(tgetent, termcap curses ncurses) AC_SEARCH_LIBS(exp, m) AC_SEARCH_LIBS(libintl_gettext, intl) AC_SEARCH_LIBS(dlerror, dl) AC_SEARCH_LIBS(inet_aton, resolv, AC_DEFINE(HAVE_INET_ATON, 1, [inet_aton is already defined])) AC_SEARCH_LIBS(PQexec, pq) # check for libraries required by extension extlibs="" if test -n "$with_ext" -a -f "${srcdir}/ext/$with_ext.libs"; then AC_MSG_CHECKING(for libraries needed by extensions) for lib in `cat "${srcdir}/ext/$with_ext.libs"` do saveLIBS=$LIBS LIBS="$LIBS -l$lib" AC_TRY_LINK(, [exit(0);], [extlibs="$extlibs -l$lib"], ) LIBS=$saveLIBS done AC_MSG_RESULT($extlibs) LIBS="$LIBS$extlibs" fi # checks for header files AC_HEADER_STDC AC_CHECK_HEADERS([stdarg.h sys/resource.h sys/time.h], [], [ AC_MSG_ERROR([could not locate termcap or curses headers]) ]) AC_HEADER_TIME AC_MSG_CHECKING(for a good signal.h) SIGNAL_H="no" for f in /usr/include/signal.h /usr/include/sys/signal.h /usr/include/sys/iso/signal_iso.h /usr/include/bits/signum.h; do if grep SIGKILL $f >/dev/null 2>&1; then SIGNAL_H=$f break fi done AC_MSG_RESULT($SIGNAL_H) if test "$SIGNAL_H" = "no"; then SIGNAL_H="/dev/null" fi AC_SUBST(SIGNAL_H) # checks for typedefs, structures, and compiler characteristics. AC_CHECK_DECLS([sys_errlist]) AC_TYPE_SIGNAL AC_CHECK_TYPE(time_t, long) # Checks for library functions. AC_CHECK_FUNCS(getopt) AC_CHECK_FUNCS(memcpy) AC_CHECK_FUNCS(setpriority) AC_CHECK_FUNCS(strchr) AC_CHECK_FUNCS(strerror) AC_CHECK_FUNCS(snprintf) AC_CHECK_FUNCS(sighold) AC_CHECK_FUNCS(sigrelse) AC_CHECK_FUNCS(sigaction) AC_CHECK_FUNCS(sigprocmask) # determine correct user, group, and mode # these can be overridden later if need be AC_MSG_CHECKING(for correct ls options) lslong="ls -l" if test `$lslong -d . | wc -w` -lt 9; then lslong="ls -lg" fi AC_MSG_RESULT($lslong) # determine correct module AC_MSG_CHECKING(for a platform module) if test "$with_module"; then MODULE=$with_module else case $target_os in aix4.2*) MODULE=aix43;; aix4.3*) MODULE=aix43;; aix5*) MODULE=aix5;; dec-osf*) MODULE=decosf1;; osf1*) MODULE=decosf1;; osf4*) MODULE=decosf1;; osf5*) MODULE=decosf1;; freebsd*) MODULE=freebsd; USE_KMEM=1;; hpux7*) MODULE=hpux7;; hpux9*) MODULE=hpux9;; hpux10*) MODULE=hpux10;; hpux11*) MODULE=hpux10;; irix5*) MODULE=irix5;; irix6*) MODULE=irixsgi;; linux*) MODULE=linux; SET_MODE=755;; netbsd*) MODULE=netbsd; SET_MODE=755;; openbsd*) MODULE=openbsd;; solaris2*) MODULE=sunos5; SET_MODE=755;; sunos4*) MODULE=sunos4;; sysv4*) MODULE=svr4;; sysv5*) MODULE=svr5;; darwin*) MODULE=macosx;; *) echo "none" echo "Configure doesn't recognize this system and doesn't know" echo "what module to assign to it. Help the cause and run the" echo "following command to let the maintainers know about this" echo "deficiency! Thanks. Just cut and paste the following:" echo "uname -a | mail -s $target_os ptop-hackers@pgfoundry.org" echo "" AC_MSG_ERROR([System type $target_os unrecognized]) esac fi AC_MSG_RESULT($MODULE) SRC="$SRC machine/m_$MODULE.c" OBJ="$OBJ machine/m_$MODULE.o" AC_SUBST(SRC) AC_SUBST(OBJ) AC_MSG_CHECKING(for installation settings) # calculate appropriate settings OWNER="" GROUP="" MODE="" if test ! -n "$USE_KMEM" -a -d /proc; then # make sure we are installed so that we can read /proc rm -f conftest.txt if test -r /proc/0/psinfo; then # system uses solaris-style /proc $lslong /proc/0/psinfo >conftest.txt elif test -r /proc/1/stat; then # linux-style /proc? $lslong /proc/1/stat >conftest.txt else echo "-r--r--r-- 1 bin bin 32 Jan 1 12:00 /foo" >conftest.txt fi # set permissions so that we can read stuff in /proc if grep '^.......r..' conftest.txt >/dev/null; then # world readable MODE=755 elif grep '^....r.....' conftest.txt >/dev/null; then # group readable MODE=2711 GROUP=`awk ' { print $4 }'` else # probably only readable by root MODE=4711 OWNER=`awk ' { print $3 }'` fi elif test -c /dev/kmem; then $lslong -L /dev/kmem >conftest.txt if grep '^....r..r..' conftest.txt >/dev/null; then MODE=755 elif grep '^....r..-..' conftest.txt >/dev/null; then MODE=2755 GROUP=`$AWK ' { print $4 }' conftest.txt` fi else MODE=755 fi rm -f conftest.txt # let module settings override what we calculated OWNER=${SET_OWNER:-$OWNER} GROUP=${SET_GROUP:-$GROUP} MODE=${SET_MODE:-$MODE} # set only those things that require it result="" INSTALL_OPTS_PROG="" if test x$OWNER != x; then result="${result}owner=$OWNER, " INSTALL_OPTS_PROG="$INSTALL_OPTS_PROG -o $OWNER" fi if test x$GROUP != x; then result="${result}group=$GROUP, " INSTALL_OPTS_PROG="$INSTALL_OPTS_PROG -g $GROUP" fi result="${result}mode=$MODE" INSTALL_OPTS_PROG="$INSTALL_OPTS_PROG -m $MODE" AC_MSG_RESULT($result) # Define man page supplement MAN_SUPPLEMENT=machine/m_$MODULE.man AC_SUBST_FILE(MAN_SUPPLEMENT) AC_SUBST(MODULE) AC_SUBST(INSTALL_OPTS_PROG) # wrapup AC_CONFIG_FILES([Makefile]) AC_CONFIG_FILES([pg_top.1]) AC_OUTPUT pgtop/display.c000066400000000000000000000773031271046472400140410ustar00rootroot00000000000000/* * 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 * Portions Copyright (c) 2013 VMware, Inc. All Rights Reserved. */ /* * 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 #include #include #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_disk = X_DISK; static int y_disk = Y_DISK; 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 only non-zero 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%s" : "%4.1f%% %s%s"), ((float) value) / 10., thisname, *names != NULL ? ", " : ""); } /* 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" : "%4.1f"), ((double) value) / 10.); /* 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, 80, "%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, 80, "%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); } /* * *_disk(disk_info) - print "DB disk: " followed by summary of * the disk space where the data directory is located. */ void i_disk(struct disk_info *disk_info) { char buf[128]; display_write(x_disk, y_disk, 0, 0, "DB disk: "); snprintf(buf, 80, "%2.1f GB total, %2.1f GB free (%ld%% used)", (double)disk_info->size / (1024 * 1024 * 1024), (double)disk_info->avail / (1024 * 1024 * 1024), disk_info->size == 0 ? 100 : (disk_info->size - disk_info->avail) * 100 / disk_info->size); display_write(-1, -1, 0, 0, buf); } void u_disk(struct disk_info *disk_info) { i_disk(disk_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) { 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) { /* truncate the line to conform to our current screen 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; } } } } } pgtop/display.h000066400000000000000000000031631271046472400140370ustar00rootroot00000000000000/* interface declaration for display.c */ /* Portions Copyright (c) 2013 VMware, Inc. All Rights Reserved. */ #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_disk(struct disk_info *disk_info); void u_disk(struct disk_info *disk_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 pgtop/getopt.c000066400000000000000000000034311271046472400136650ustar00rootroot00000000000000/* * "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. */ #ifdef HAVE_CONFIG_H #include #endif /* HAVE_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){\ extern int write();\ 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 */ pgtop/help.h000066400000000000000000000036341271046472400133250ustar00rootroot00000000000000/* 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\ - update screen\n\ A - EXPLAIN ANALYZE (UPDATE/DELETE safe)\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\ M - sort by memory usage\n\ N - sort by pid\n\ P - sort by CPU usage\n\ R - show user table statistics\n\ S - show pg_stat_statements statistics\n\ Q - show current query of a process\n\ T - sort by time\n\ X - show user index statistics\n\ c - toggle the display of process commands\n\ d - change number of displays to show\n\ e - list errors generated by last \"kill\" or \"renice\" command\n\ h or ? - help; show this text\n\ i - toggle the displaying of idle processes\n\ k - kill processes; send a signal to a list of processes\n\ not avilable when connected to a remote database\n\ n or # - change number of processes to display\n\ o - specify sort order (%s)\n\ index stats (idx_scan, idx_tup_fetch, idx_tup_read)\n\ table stats (seq_scan, seq_tup_read, idx_scan, idx_tup_fetch,\n\ n_tup_ins, n_tup_upd, n_tup_del)\n\ i/o stats (%s)\n\ q - quit\n\ r - renice a process\n\ not avilable when connected to a remote database\n\ s - change number of seconds to delay between updates\n\ t - Toggle between cumulative or differential statistics when viewing\n\ user table or user index statistics.\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_ */ pgtop/layout.h000066400000000000000000000020051271046472400137010ustar00rootroot00000000000000/* * 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. * * Portions Copyright (c) 2013 VMware, Inc. All Rights Reserved. */ #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_DISK 0 #define Y_DISK 6 #define X_SWAP 6 #define Y_SWAP 7 #define Y_MESSAGE 7 #define X_HEADER 0 #define Y_HEADER 8 #define X_IDLECURSOR 0 #define Y_IDLECURSOR 7 #define Y_PROCS 9 #endif /* _LAYOUT_H_ */ pgtop/loadavg.h000066400000000000000000000031561271046472400140110ustar00rootroot00000000000000/* * 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 #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_ */ pgtop/machine.h000066400000000000000000000070161271046472400137770ustar00rootroot00000000000000/* * 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. * * Portions Copyright (c) 2013 VMware, Inc. All Rights Reserved. */ #ifndef _MACHINE_H_ #define _MACHINE_H_ #include #include #include "pg.h" /* #ifdef CLK_TCK # define HZ CLK_TCK # else # define HZ 60 # endif */ #ifndef HZ #define HZ sysconf(_SC_CLK_TCK) #endif /* Display modes. */ #define MODE_PROCESSES 0 #define MODE_TABLE_STATS 1 #define MODE_INDEX_STATS 2 #define MODE_IO_STATS 3 #define MODE_STATEMENTS 4 /* Display modes for table and index statistics. */ #define STATS_DIFF 0 #define STATS_CUMULATIVE 1 /* 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 /* * 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 **order_names_io; /* 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; }; /* * Database disk(s) info */ struct disk_info { int64_t size; int64_t avail; }; /* * 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 */ int uid; /* only this uid (unless uid == -1) */ char *command; /* only this command (unless == NULL) */ }; /* routines defined by the machine dependent module */ int machine_init(struct statics *); void get_system_info(struct system_info *); #ifdef __linux__ caddr_t get_process_info(struct system_info *, struct process_select *, int, char *, int); #else caddr_t get_process_info(struct system_info *, struct process_select *, int, char *); #endif /* __linux__ */ void get_disk_info(struct disk_info *, char *); void get_io_info(struct io_info *); void get_database_info(struct db_info *, char *); char *get_data_directory(char *); char *format_header(char *); char *format_next_io(caddr_t, char *(*) (uid_t)); char *format_next_process(caddr_t, char *(*) (uid_t)); uid_t proc_owner(pid_t); extern int mode_stats; #endif /* _MACHINE_H_ */ pgtop/machine/000077500000000000000000000000001271046472400136225ustar00rootroot00000000000000pgtop/machine/m_aix43.c000066400000000000000000000406411271046472400152370ustar00rootroot00000000000000/* * 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 * * PATCHES: Antoine Tabary */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #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)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_aix5.c000066400000000000000000000456261271046472400151650ustar00rootroot00000000000000/* * 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 * * PATCHES: Antoine Tabary , Dan Nelson */ #define MAXPROCS 10240 #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_common.c000066400000000000000000000101351271046472400155720ustar00rootroot00000000000000/* * machine/m_common.c * * Functionalities common to all the platforms. * * Copyright (c) 2013 VMware, Inc. All Rights Reserved. */ #include #include #include #include #include #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;" #define QUERY_DATA_DIRECTORY "SHOW data_directory;" /* Store data directory to avoid unnecessary requests to server */ static char *data_directory = NULL; /* * 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, char *conninfo) { struct timeval thistime; double timediff; int i; int rows; PGconn *pgconn; 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; pgconn = connect_to_db(conninfo); if (pgconn != NULL) { pgresult = PQexec(pgconn, 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); PQfinish(pgconn); 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; } /* * Obtain data directory of server if necessary. if this has already been * queried to server, return existing value. */ char * get_data_directory(char *conninfo) { PGconn *pgconn; PGresult *pgresult = NULL; int rows; /* Return existing value if any */ if (data_directory) return data_directory; /* No existing value, so query server */ rows = 0; pgconn = connect_to_db(conninfo); if (pgconn != NULL) { pgresult = PQexec(pgconn, QUERY_DATA_DIRECTORY); if (PQresultStatus(pgresult) == PGRES_TUPLES_OK) rows = PQntuples(pgresult); } if (rows != 0) { char *dir; dir = PQgetvalue(pgresult, 0, 0); if (dir != NULL) data_directory = strdup(dir); } /* Clean up */ if (pgresult != NULL) PQclear(pgresult); PQfinish(pgconn); return data_directory; } pgtop/machine/m_decosf1.c000066400000000000000000000612101271046472400156260ustar00rootroot00000000000000/* * pg_top - a top PostgreSQL users display for Unix * * SYNOPSIS: OSF/1, Digital Unix 4.0, Compaq Tru64 5.0 * * DESCRIPTION: * This is the machine-dependent module for DEC OSF/1 and its descendents * It is known to work on OSF/1 1.2, 1.3, 2.0-T3, 3.0, Digital Unix V4.0, * Digital Unix 5.0, and Tru64 5.0. * WARNING: if you use optimization with the standard "cc" compiler that * . comes with V3.0 the resulting executable may core dump. If * . this happens, recompile without optimization. * * LIBS: -lmld -lmach * * CFLAGS: -DHAVE_GETOPT -DORDER * * AUTHOR: Anthony Baxter, * Derived originally from m_ultrix, by David S. Comay , * although by now there is hardly any of the code from m_ultrix left. * Helped a lot by having the source for syd(1), by Claus Kalle, and * from several people at DEC who helped with providing information on * some of the less-documented bits of the kernel interface. * * Modified: 31-Oct-94, Pat Welch, tpw@physics.orst.edu * changed _mpid to pidtab for compatibility with OSF/1 version 3.0 * * Modified: 13-Dec-94, William LeFebvre, lefebvre@dis.anl.gov * removed used of pidtab (that was bogus) and changed things to * automatically detect the absence of _mpid in the nlist and * recover gracefully---this appears to be the only difference * with 3.0. * * Modified: 3-Mar-00, Rainer Orth * added support for sort ordering. */ /* * Theory of operation: * * Use Mach calls to build up a structure that contains all the sorts * of stuff normally found in a struct proc in a BSD system. Then * everything else uses this structure. This has major performance wins, * and also should work for future versions of the O/S. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* #include */ /* forward declarations, needed by included from */ struct rtentry; struct mbuf; #include #include #include #include #include /* for SYS_setpriority, in setpriority(), * below */ #include "pg_top.h" #include "machine.h" #include "utils.h" extern int errno, sys_nerr; extern char *sys_errlist[]; #define strerror(e) (((e) >= 0 && (e) < sys_nerr) ? sys_errlist[(e)] : "Unknown error") #define VMUNIX "/vmunix" #define KMEM "/dev/kmem" #define MEM "/dev/mem" /* get_process_info passes back a handle. This is what it looks like: */ struct handle { struct osf1_top_proc **next_proc; /* points to next valid proc pointer */ int remaining; /* number of pointers remaining */ }; /* declarations for load_avg */ #include "loadavg.h" /* definitions for indices in the nlist array */ #define X_MPID 0 static struct nlist nlst[] = { {"_mpid"}, /* 0 */ {0} }; /* Some versions of OSF/1 don't support reporting of the last PID. This flag indicates whether or not we are reporting the last PID. */ static int do_last_pid = 1; /* * These definitions control the format of the per-process area */ static char header[] = " PID X PRI NICE SIZE RES STATE TIME CPU COMMAND"; /* 01234567 -- field to fill in starts at header+7 */ #define UNAME_START 7 #define Proc_format \ "%6d %-8.8s %3d %4d %5s %5s %-5s %-6s %5.2f%% %s" /* 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. Although OSF/1 doesnt support * multiple processors yet, (and this module _certainly_ doesnt * support it, either, we may as well plan for the future. :-) */ char *state_abbrev[] = { "", "run\0\0\0", "WAIT", "sleep", "sleep", "stop", "halt", "???", "zomb" }; static int kmem, mem; /* values that we stash away in _init and use in later routines */ static double logcpu; /* these are retrieved from the kernel in _init */ static unsigned long proc; static int nproc; static load_avg ccpu; typedef long mtime_t; /* these are offsets obtained via nlist and used in the get_ functions */ static unsigned long mpid_offset; /* these are for detailing the process states */ int process_states[9]; char *procstatenames[] = { "", " running, ", " waiting, ", " sleeping, ", " idle, ", " stopped, ", " halted, ", "", " zombie", NULL }; /* these are for detailing the cpu states */ int cpu_states[5]; char *cpustatenames[] = { "user", "nice", "system", "wio", "idle", NULL }; long old_cpu_ticks[5]; /* these are for detailing the memory statistics */ long memory_stats[5]; char *memorynames[] = { "K active, ", "K inactive, ", "K total, ", "K free", NULL }; long swap_stats[3]; char *swapnames[] = { "K in use, ", "K total", NULL }; /* these are names given to allowed sorting orders -- first is default */ char *ordernames[] = {"cpu", "size", "res", "time", 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 }; /* 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) /* take a process, make it a mach task, and grab all the info out */ void do_threads_calculations(); /* * Because I dont feel like repeatedly grunging through the kernel with * Mach calls, and I also dont want the horrid performance hit this * would give, I read the stuff I need out, and put in into my own * structure, for later use. */ struct osf1_top_proc { size_t p_mach_virt_size; char p_mach_state; int p_flag; fixpt_t p_mach_pct_cpu; /* aka p_pctcpu */ int used_ticks; size_t process_size; pid_t p_pid; uid_t p_ruid; char p_pri; char p_nice; size_t p_rssize; char u_comm[PI_COMLEN + 1]; }; /* these are for keeping track of the proc array */ static int bytes; static int pref_len; static struct osf1_top_proc *pbase; static struct osf1_top_proc **pref; /* useful externals */ extern int errno; extern char *sys_errlist[]; long percentages(); machine_init(statics) struct statics *statics; { register int i = 0; register int pagesize; struct tbl_sysinfo sibuf; 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) { perror("TOP(nlist)"); return (-1); } if (nlst[X_MPID].n_type == 0) { /* this kernel has no _mpid, so go without */ do_last_pid = 0; } else { /* stash away mpid pointer for later use */ mpid_offset = nlst[X_MPID].n_value; } /* get the symbol values out of kmem */ nproc = table(TBL_PROCINFO, 0, (struct tbl_procinfo *) NULL, INT_MAX, 0); /* allocate space for proc structure array and array of pointers */ bytes = nproc * sizeof(struct osf1_top_proc); pbase = (struct osf1_top_proc *) malloc(bytes); pref = (struct osf1_top_proc **) malloc(nproc * sizeof(struct osf1_top_proc *)); /* Just in case ... */ if (pbase == (struct osf1_top_proc *) NULL || pref == (struct osf1_top_proc **) NULL) { fprintf(stderr, "pg_top: cannot allocate sufficient memory\n"); return (-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->order_names = ordernames; statics->swap_names = swapnames; /* initialise this, for calculating cpu time */ if (table(TBL_SYSINFO, 0, &sibuf, 1, sizeof(struct tbl_sysinfo)) < 0) { perror("TBL_SYSINFO"); return (-1); } old_cpu_ticks[0] = sibuf.si_user; old_cpu_ticks[1] = sibuf.si_nice; old_cpu_ticks[2] = sibuf.si_sys; old_cpu_ticks[3] = sibuf.wait; old_cpu_ticks[4] = sibuf.si_idle; /* all done! */ 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; { struct tbl_loadavg labuf; struct tbl_sysinfo sibuf; struct tbl_swapinfo swbuf; vm_statistics_data_t vmstats; int swap_pages = 0, swap_free = 0, i; long new_ticks[5], diff_ticks[5]; long delta_ticks; if (do_last_pid) { /* last pid assigned */ (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "_mpid"); } else { si->last_pid = -1; } /* get load averages */ if (table(TBL_LOADAVG, 0, &labuf, 1, sizeof(struct tbl_loadavg)) < 0) { perror("TBL_LOADAVG"); return; } if (labuf.tl_lscale) /* scaled */ for (i = 0; i < 3; i++) si->load_avg[i] = ((double) labuf.tl_avenrun.l[i] / (double) labuf.tl_lscale); else /* not scaled */ for (i = 0; i < 3; i++) si->load_avg[i] = labuf.tl_avenrun.d[i]; /* array of cpu state counters */ if (table(TBL_SYSINFO, 0, &sibuf, 1, sizeof(struct tbl_sysinfo)) < 0) { perror("TBL_SYSINFO"); return; } new_ticks[0] = sibuf.si_user; new_ticks[1] = sibuf.si_nice; new_ticks[2] = sibuf.si_sys; new_ticks[3] = sibuf.wait; new_ticks[4] = sibuf.si_idle; delta_ticks = 0; for (i = 0; i < 5; i++) { diff_ticks[i] = new_ticks[i] - old_cpu_ticks[i]; delta_ticks += diff_ticks[i]; old_cpu_ticks[i] = new_ticks[i]; } si->cpustates = cpu_states; if (delta_ticks) for (i = 0; i < 5; i++) si->cpustates[i] = (int) (((double) diff_ticks[i] / (double) delta_ticks) * 1000); /* memory information */ /* this is possibly bogus - we work out total # pages by */ /* adding up the free, active, inactive, wired down, and */ /* zero filled. Anyone who knows a better way, TELL ME! */ /* Change: dont use zero filled. */ (void) vm_statistics(task_self(), &vmstats); /* thanks DEC for the table() command. No thanks at all for */ /* omitting the man page for it from OSF/1 1.2, and failing */ /* to document SWAPINFO in the 1.3 man page. Lets hear it for */ /* include files. */ i = 0; while (table(TBL_SWAPINFO, i, &swbuf, 1, sizeof(struct tbl_swapinfo)) > 0) { swap_pages += swbuf.size; swap_free += swbuf.free; i++; } memory_stats[0] = pagetok(vmstats.active_count); memory_stats[1] = pagetok(vmstats.inactive_count); memory_stats[2] = pagetok((vmstats.free_count + vmstats.active_count + vmstats.inactive_count + vmstats.wire_count)); memory_stats[3] = pagetok(vmstats.free_count); swap_stats[0] = pagetok(swap_pages - swap_free); swap_stats[1] = pagetok(swap_pages); 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; { register int i; register int total_procs; register int active_procs; register struct osf1_top_proc **prefp; register struct osf1_top_proc *pp; struct tbl_procinfo p_i[8]; int j, k, r; /* these are copied out of sel for speed */ int show_idle; int show_uid; int show_command; /* 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; /* 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; pp = pbase; for (j = 0; j < nproc; j += 8) { r = table(TBL_PROCINFO, j, (struct tbl_procinfo *) p_i, 8, sizeof(struct tbl_procinfo)); for (k = 0; k < r; k++, pp++) { if (p_i[k].pi_pid == 0) { pp->p_pid = 0; } else { pp->p_pid = p_i[k].pi_pid; pp->p_ruid = p_i[k].pi_ruid; pp->p_flag = p_i[k].pi_flag; pp->p_nice = getpriority(PRIO_PROCESS, p_i[k].pi_pid); /* Load useful values into the proc structure */ do_threads_calculations(pp); /* * Place pointers to each valid proc structure in pref[]. * Process slots that are actually in use have a non-zero * status field. */ #ifdef DEBUG /* * Emit debug info about all processes before selection. */ fprintf(stderr, "pid = %d ruid = %d comm = %s p_mach_state = %d p_stat = %d p_flag = 0x%x\n", pp->p_pid, pp->p_ruid, p_i[k].pi_comm, pp->p_mach_state, p_i[k].pi_status, pp->p_flag); #endif if (pp->p_mach_state != 0) { total_procs++; process_states[pp->p_mach_state]++; if ((pp->p_mach_state != 8) && (show_idle || (pp->p_mach_pct_cpu != 0) || (pp->p_mach_state == 1)) && (!show_uid || pp->p_ruid == (uid_t) sel->uid)) { *prefp++ = pp; active_procs++; } } } } } /* if requested, sort the "interesting" processes */ if (proc_compares[compare_index] != NULL) { qsort((char *) pref, active_procs, sizeof(struct osf1_top_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 * format_next_process(handle, get_userid) caddr_t handle; char *(*get_userid) (); { register struct osf1_top_proc *pp; register long cputime; register double pct; struct user u; struct handle *hp; /* 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 (table(TBL_UAREA, pp->p_pid, &u, 1, sizeof(struct user)) < 0) { /* * whoops, it must have died between the read of the proc area and * now. Oh well, lets just dump some meaningless thing out to keep the * rest of the program happy */ sprintf(fmt, Proc_format, pp->p_pid, (*get_userid) (pp->p_ruid), 0, 0, "", "", "dead", "", 0.0, ""); return (fmt); } /* set u_comm for system processes */ if (u.u_comm[0] == '\0') { if (pp->p_pid == 0) { (void) strcpy(u.u_comm, "[idle]"); } else if (pp->p_pid == 2) { (void) strcpy(u.u_comm, "[execpt.hndlr]"); } } /* Check if process is in core */ if (!(pp->p_flag & SLOAD)) { /* * Print swapped processes as */ char buf[sizeof(u.u_comm)]; (void) strncpy(buf, u.u_comm, sizeof(u.u_comm)); u.u_comm[0] = '<'; (void) strncpy(&u.u_comm[1], buf, sizeof(u.u_comm) - 2); u.u_comm[sizeof(u.u_comm) - 2] = '\0'; (void) strncat(u.u_comm, ">", sizeof(u.u_comm) - 1); u.u_comm[sizeof(u.u_comm) - 1] = '\0'; } cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec; /* calculate the base for cpu percentages */ pct = pctdouble(pp->p_mach_pct_cpu); /* format this entry */ sprintf(fmt, Proc_format, pp->p_pid, (*get_userid) (pp->p_ruid), pp->p_pri, pp->p_nice, format_k(pp->p_mach_virt_size / 1024), format_k(pp->p_rssize / 1000), state_abbrev[pp->p_mach_state], format_time(cputime), 100.0 * ((double) pp->p_mach_pct_cpu / 10000.0), printable(u.u_comm)); /* 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). * */ getkval(offset, ptr, size, refstr) unsigned long offset; int *ptr; int size; char *refstr; { if (lseek(kmem, (long) offset, L_SET) == -1) { if (*refstr == '!') refstr++; (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM, refstr, strerror(errno)); quit(23); } if (read(kmem, (char *) ptr, size) == -1) { if (*refstr == '!') return (0); else { (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM, refstr, strerror(errno)); quit(23); } } return (1); } /* 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, zomb, ???, halt, idle, sleep, * stop, 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 = p2->p_mach_pct_cpu - p1->p_mach_pct_cpu,\ (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0) #define ORDERKEY_CPTICKS if ((result = p2->used_ticks - p1->used_ticks) == 0) #define ORDERKEY_STATE if ((result = sorted_state[p2->p_mach_state] - \ sorted_state[p1->p_mach_state]) == 0) #define ORDERKEY_PRIO if ((result = p2->p_pri - p1->p_pri) == 0) #define ORDERKEY_RSSIZE if ((result = p2->p_rssize - p1->p_rssize) == 0) #define ORDERKEY_MEM if ((result = p2->p_mach_virt_size - p1->p_mach_virt_size) == 0) /* Now the array that maps process state to a weight */ static unsigned char sorted_state[] = { 0, /* "" */ 8, /* "run" */ 1, /* "WAIT" */ 6, /* "sleep" */ 5, /* "idle" */ 7, /* "stop" */ 4, /* "halt" */ 3, /* "???" */ 2, /* "zomb" */ }; /* compare_cpu - the comparison function for sorting by cpu percentage */ compare_cpu(pp1, pp2) struct osf1_top_proc **pp1; struct osf1_top_proc **pp2; { register struct osf1_top_proc *p1; register struct osf1_top_proc *p2; register long result; register pctcpu lresult; /* 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 */ compare_size(pp1, pp2) struct osf1_top_proc **pp1; struct osf1_top_proc **pp2; { register struct osf1_top_proc *p1; register struct osf1_top_proc *p2; register long result; register pctcpu lresult; /* 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 */ compare_res(pp1, pp2) struct osf1_top_proc **pp1; struct osf1_top_proc **pp2; { register struct osf1_top_proc *p1; register struct osf1_top_proc *p2; register long result; register pctcpu lresult; /* 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 */ compare_time(pp1, pp2) struct osf1_top_proc **pp1; struct osf1_top_proc **pp2; { register struct osf1_top_proc *p1; register struct osf1_top_proc *p2; register long result; register pctcpu lresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_CPTICKS ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_PRIO 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. */ int proc_owner(pid) int pid; { register int cnt; register struct osf1_top_proc **prefp; register struct osf1_top_proc *pp; prefp = pref; cnt = pref_len; while (--cnt >= 0) { if ((pp = *prefp++)->p_pid == (pid_t) pid) { return ((int) pp->p_ruid); } } return (-1); } /* * We use the Mach interface, as well as the table(UAREA,,,) call to * get some more information, then put it into unused fields in our * copy of the proc structure, to make it faster and easier to get at * later. */ void do_threads_calculations(thisproc) struct osf1_top_proc *thisproc; { int j; task_t thistask; task_basic_info_data_t taskinfo; unsigned int taskinfo_l; thread_array_t threadarr; unsigned int threadarr_l; thread_basic_info_t threadinfo; thread_basic_info_data_t threadinfodata; unsigned int threadinfo_l; int task_tot_cpu = 0; /* total cpu usage of threads in a * task */ struct user u; thisproc->p_pri = 0; thisproc->p_rssize = 0; thisproc->p_mach_virt_size = 0; thisproc->p_mach_state = 0; thisproc->p_mach_pct_cpu = 0; if (task_by_unix_pid(task_self(), thisproc->p_pid, &thistask) != KERN_SUCCESS) { thisproc->p_mach_state = 8; /* (zombie) */ } else { taskinfo_l = TASK_BASIC_INFO_COUNT; if (task_info(thistask, TASK_BASIC_INFO, (task_info_t) & taskinfo, &taskinfo_l) != KERN_SUCCESS) { thisproc->p_mach_state = 8; /* (zombie) */ } else { int minim_state = 99, mcurp = 1000, mbasp = 1000, mslpt = 999; thisproc->p_rssize = taskinfo.resident_size; thisproc->p_mach_virt_size = taskinfo.virtual_size; if (task_threads(thistask, &threadarr, &threadarr_l) != KERN_SUCCESS) return; threadinfo = &threadinfodata; for (j = 0; j < threadarr_l; j++) { threadinfo_l = THREAD_BASIC_INFO_COUNT; if (thread_info(threadarr[j], THREAD_BASIC_INFO, (thread_info_t) threadinfo, &threadinfo_l) == KERN_SUCCESS) { task_tot_cpu += threadinfo->cpu_usage; if (minim_state > threadinfo->run_state) minim_state = threadinfo->run_state; if (mcurp > threadinfo->cur_priority) mcurp = threadinfo->cur_priority; if (mbasp > threadinfo->base_priority) mbasp = threadinfo->base_priority; if (mslpt > threadinfo->sleep_time) mslpt = threadinfo->sleep_time; } } switch (minim_state) { case TH_STATE_RUNNING: thisproc->p_mach_state = 1; break; case TH_STATE_UNINTERRUPTIBLE: thisproc->p_mach_state = 2; break; case TH_STATE_WAITING: thisproc->p_mach_state = (threadinfo->sleep_time > 20) ? 4 : 3; break; case TH_STATE_STOPPED: thisproc->p_mach_state = 5; break; case TH_STATE_HALTED: thisproc->p_mach_state = 6; break; default: thisproc->p_mach_state = 7; break; } thisproc->p_pri = mcurp; thisproc->p_mach_pct_cpu = (fixpt_t) (task_tot_cpu * 10); vm_deallocate(task_self(), (vm_address_t) threadarr, threadarr_l); } } if (table(TBL_UAREA, thisproc->p_pid, &u, 1, sizeof(struct user)) >= 0) { thisproc->used_ticks = (u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec); thisproc->process_size = u.u_tsize + u.u_dsize + u.u_ssize; } } /* The reason for this function is that the system call will let * someone lower their own processes priority (because pg_top is setuid :-( * Yes, using syscall() is a hack, if you can come up with something * better, then I'd be thrilled to hear it. I'm not holding my breath, * though. * Anthony. */ int setpriority(int dummy, int procnum, int niceval) { int uid, curprio; uid = getuid(); if ((curprio = getpriority(PRIO_PROCESS, procnum)) == -1) { return (-1); /* errno goes back to renice_process() */ } /* check for not-root - if so, dont allow users to decrease priority */ else if (uid && (niceval < curprio)) { errno = EACCES; return (-1); } return (syscall(SYS_setpriority, PRIO_PROCESS, procnum, niceval)); } void get_io_info(struct io_info *io_info) { /* Not supported yet */ memset(io_info, 0, sizeof(*io_info)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_decosf1.man000066400000000000000000000013511271046472400161570ustar00rootroot00000000000000.SH "DEC OSF/1 NOTES" Original author was Anthony Baxter, . Derived originally from m_ultrix, by David S. Comay , although by now there is hardly any of the code from m_ultrix left. Helped a lot by having the source for syd(1), by Claus Kalle, and from several people at DEC who helped with providing information on some of the less-documented bits of the kernel interface. Patches from Rainer Orth Theory of operation: Use Mach calls to build up a structure that contains all the sorts of stuff normally found in a struct proc in a BSD system. Then everything else uses this structure. This has major performance wins, and also should work for future versions of the O/S. pgtop/machine/m_freebsd.c000066400000000000000000000625711271046472400157270ustar00rootroot00000000000000/* * 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 * Steven Wallace * Wolfram Schneider */ #include #include #include #include #include #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif /* Swap */ #include #include #include /* for changes in kernel structures */ #include "pg_top.h" #include "machine.h" #include "utils.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 */ }; /* declarations for load_avg */ #include "loadavg.h" /* 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 /* 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 */ #ifdef SHOW_THREADS static char smp_header[] = " PID %-*.*s THR PRI NICE SIZE RES STATE C TIME CPU COMMAND"; #define smp_Proc_format \ "%5d %-*.*s %3d %3d %3d%7s %6s %-6.6s %1x%7s %5.2f%% %s" static char up_header[] = " PID %-*.*s THR PRI NICE SIZE RES STATE TIME CPU COMMAND"; #define up_Proc_format \ "%5d %-*.*s %3d %3d %3d%7s %6s %-6.6s%.0d%7s %5.2f%% %s" #else static char smp_header[] = " PID %-*.*s PRI NICE SIZE RES STATE C TIME WCPU CPU COMMAND"; #define smp_Proc_format \ "%5d %-*.*s %3d %3d%7s %6s %-6.6s %1x%7s %5.2f%% %5.2f%% %s" static char up_header[] = " PID %-*.*s PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND"; #define up_Proc_format \ "%5d %-*.*s %3d %3d%7s %6s %-6.6s%.0d%7s %5.2f%% %5.2f%% %s" /* define what weighted cpu is. */ #define weighted_cpu(pct, pp) (PP((pp), swtime) == 0 ? 0.0 : \ ((pct) / (1.0 - exp(PP((pp), swtime) * logcpu)))) #endif /* 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", }; 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]; char *procstatenames[] = { "", " starting, ", " running, ", " sleeping, ", " stopped, ", " zombie, ", NULL }; /* 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), smpmode ? smp_header : up_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, char *conninfo) { 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; int show_uid; PGconn *pgconn; PGresult *pgresult = NULL; nproc = 0; pgconn = connect_to_db(conninfo); if (pgconn != NULL) { pgresult = pg_processes(pgconn); nproc = PQntuples(pgresult); if (nproc > onproc) pbase = (struct kinfo_proc *) realloc(pbase, sizeof(struct kinfo_proc) * nproc); } PQfinish(pgconn); 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_uid = sel->uid != -1; 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 elegent and the reason why I'm donig * 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)) && (!show_uid || PRUID(pp) == (uid_t) sel->uid)) { *prefp++ = pp; active_procs++; } } } PQclear(pgresult); /* if requested, sort the "interesting" processes */ 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_process(caddr_t handle, char *(*get_userid) (uid_t)) { register struct kinfo_proc *pp; register long cputime; register double pct; struct handle *hp; char status[16]; int state; /* 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 */ 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) sprintf(status, "CPU%d", PP(pp, oncpu)); else strcpy(status, "RUN"); break; case SSLEEP: if (PP(pp, wmesg) != NULL) { #if OSMAJOR <= 4 sprintf(status, "%.6s", EP(pp, wmesg)); #else sprintf(status, "%.6s", PP(pp, wmesg)); #endif break; } /* fall through */ default: if (state >= 0 && state < sizeof(state_abbrev) / sizeof(*state_abbrev)) sprintf(status, "%.6s", state_abbrev[(unsigned char) state]); else sprintf(status, "?%5d", state); break; } /* format this entry */ sprintf(fmt, smpmode ? smp_Proc_format : up_Proc_format, PP(pp, pid), namelength, namelength, (*get_userid) (PRUID(pp)), #ifdef SHOW_THREADS PP(pp, numthreads), #endif #if OSMAJOR <= 4 PP(pp, priority) - PZERO, #else PP(pp, pri.pri_level) - PZERO, #endif /* * normal time -> nice value -20 - +20 real time 0 - 31 -> nice value * -52 - -21 idle time 0 - 31 -> nice value +21 - +52 */ #if OSMAJOR <= 4 (PP(pp, rtprio.type) == RTP_PRIO_NORMAL ? PP(pp, nice) - NZERO : (RTP_PRIO_IS_REALTIME(PP(pp, rtprio.type)) ? (PRIO_MIN - 1 - RTP_PRIO_MAX + PP(pp, rtprio.prio)) : (PRIO_MAX + 1 + PP(pp, rtprio.prio)))), #else (PP(pp, pri.pri_class) == PRI_TIMESHARE ? PP(pp, nice) - NZERO : (PRI_IS_REALTIME(PP(pp, pri.pri_class))) ? (PRIO_MIN - 1 - (PRI_MAX_REALTIME - PP(pp, pri.pri_level))) : (PRIO_MAX + 1 + PP(pp, pri.pri_level) - PRI_MIN_IDLE)), #endif format_k(PROCSIZE(pp)), format_k(pagetok(VP(pp, rssize))), status, smpmode ? PP(pp, lastcpu) : 0, format_time(cputime), #ifndef SHOW_THREADS 100.0 * weighted_cpu(pct, pp), #endif 100.0 * pct, printable(cmd)); /* 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 . */ #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); } void get_io_info(struct io_info *io_info) { /* Not supported yet */ memset(io_info, 0, sizeof(*io_info)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_freebsd.man000066400000000000000000000013741271046472400162520ustar00rootroot00000000000000.SH "FreeBSD 4.0 NOTES" Last pid is compiler depended. $ strings /kernel | grep _nextpid .SH "DESCRIPTION OF MEMORY" Memory: 10M Act 1208K Inact 3220K Wired 132K Free 25% Swap, 2924Kin 2604Kout .TP .B K: Kilobyte .TP .B M: Megabyte .TP .B %: 1/100 .TP .B Act: number of pages active .TP .B Incat: number of pages inactive .TP .B Wired: number of pages wired down .TP .B Free: number of pages free .TP .B Swap: swap usage .TP .B Kin: kilobytes swap pager pages paged in (last interval) .TP .B Kout: kilobytes swap pager pages paged out (last interval) .PP See /usr/include/sys/vmmeter.h and /sys/vm/vm_meter.c. .PP Christos Zoulas, Steven Wallace, Wolfram Schneider, Monte Mitzelfelt. .PP This module was retrofitted from FreeBSD 9.1 sources. pgtop/machine/m_hpux10.c000066400000000000000000000404761271046472400154420ustar00rootroot00000000000000/* * pg_top - a top PostgreSQL users display for Unix * * SYNOPSIS: any hp9000 running hpux version 10.x * * DESCRIPTION: * This is the machine-dependent module for HPUX 10/11 that uses pstat. * It has been tested on HP/UX 10.01, 10.20, and 11.00. It is presumed * to work also on 10.10. * Idle processes are marked by being either runnable or having a %CPU * of at least 0.1%. This fraction is defined by CPU_IDLE_THRESH and * can be adjusted at compile time. * * CFLAGS: -DHAVE_GETOPT * * LIBS: * * AUTHOR: John Haxby * AUTHOR: adapted from Rich Holland * AUTHOR: adapted from Kevin Schmidt */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pg_top.h" #include "machine.h" #include "utils.h" /* * The idle threshold (CPU_IDLE_THRESH) is an extension to the normal * idle process check. Basically, we regard a process as idle if it is * both asleep and using less that CPU_IDLE_THRESH percent cpu time. I * believe this makes the "i" option more useful, but if you don't, add * "-DCPU_IDLE_THRESH=0.0" to the CFLAGS. */ #ifndef CPU_IDLE_THRESH #define CPU_IDLE_THRESH 0.1 #endif #define P_RSSIZE(p) (p)->pst_rssize #define P_TSIZE(p) (p)->pst_tsize #define P_DSIZE(p) (p)->pst_dsize #define P_SSIZE(p) (p)->pst_ssize #define VMUNIX "/stand/vmunix" #define KMEM "/dev/kmem" #define MEM "/dev/mem" #ifdef DOSWAP #define SWAP "/dev/dmem" #endif /* what we consider to be process size: */ #define PROCSIZE(pp) (P_TSIZE(pp) + P_DSIZE(pp) + P_SSIZE(pp)) /* definitions for indices in the nlist array */ #define X_MPID 0 static struct nlist nlst[] = { {"mpid"}, {0} }; /* * These definitions control the format of the per-process area */ static char header[] = " TTY PID X PRI NICE SIZE RES STATE TIME CPU COMMAND"; /* 0123456789.12345 -- field to fill in starts at header+6 */ #define UNAME_START 15 #define Proc_format \ "%8.8s %5d %-8.8s %4d %4d %5s %5s %-5s %6s %5.2f%% %s" /* process state names for the "STATE" column of the display */ char *state_abbrev[] = { "", "sleep", "run", "stop", "zomb", "trans", "start" }; /* values that we stash away in _init and use in later routines */ static int kmem; static struct pst_status *pst; /* these are retrieved from the OS in _init */ static int nproc; static int ncpu = 0; /* these are offsets obtained via nlist and used in the get_ functions */ static unsigned long mpid_offset; /* these are for calculating cpu state percentages */ static long cp_time[PST_MAX_CPUSTATES]; static long cp_old[PST_MAX_CPUSTATES]; static long cp_diff[PST_MAX_CPUSTATES]; /* these are for detailing the process states */ int process_states[7]; char *procstatenames[] = { "", " sleeping, ", " running, ", " stopped, ", " zombie, ", " trans, ", " starting, ", NULL }; /* these are for detailing the cpu states */ int cpu_states[PST_MAX_CPUSTATES]; char *cpustatenames[] = { /* roll "swait" into "block" and "ssys" into "sys" */ "usr", "nice", "sys", "idle", "", "block", "\0swait", "intr", "\0ssys", NULL }; /* these are for detailing the memory statistics */ long memory_stats[8]; char *memorynames[] = { "Real: ", "K act, ", "K tot ", "Virtual: ", "K act, ", "K tot, ", "K free", NULL }; /* 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) /* Mapping TTY major/minor numbers is done through this structure */ struct ttymap { dev_t dev; char name[9]; }; static struct ttymap *ttynames = NULL; static int nttys = 0; static get_tty_names(); /* 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 */ 6, /* run */ 4, /* stop */ 2, /* zombie */ 5, /* start */ 1, /* other */ }; proc_compare(p1, p2) struct pst_status *p1; struct pst_status *p2; { int result; float lresult; /* compare percent cpu (pctcpu) */ if ((lresult = p2->pst_pctcpu - p1->pst_pctcpu) == 0) { /* use cpticks to break the tie */ if ((result = p2->pst_cpticks - p1->pst_cpticks) == 0) { /* use process state to break the tie */ if ((result = sorted_state[p2->pst_stat] - sorted_state[p1->pst_stat]) == 0) { /* use priority to break the tie */ if ((result = p2->pst_pri - p1->pst_pri) == 0) { /* use resident set size (rssize) to break the tie */ if ((result = P_RSSIZE(p2) - P_RSSIZE(p1)) == 0) { /* use total memory to break the tie */ result = PROCSIZE(p2) - PROCSIZE(p1); } } } } } else { result = lresult < 0 ? -1 : 1; } return (result); } machine_init(statics) struct statics *statics; { struct pst_static info; int i = 0; int pagesize; /* If we can get mpid from the kernel, we'll use it, otherwise */ /* we'll guess from the most recently started proces */ if ((kmem = open(KMEM, O_RDONLY)) < 0 || (nlist(VMUNIX, nlst)) < 0 || (nlst[X_MPID].n_type) == 0) mpid_offset = 0; else mpid_offset = nlst[X_MPID].n_value; if (pstat_getstatic(&info, sizeof(info), 1, 0) < 0) { perror("pstat_getstatic"); return -1; } /* * Allocate space for the per-process structures (pst_status). To make * life easier, simply allocate enough storage to hold all the process * information at once. This won't normally be a problem since machines * with lots of processes configured will also have lots of memory. */ nproc = info.max_proc; pst = (struct pst_status *) malloc(nproc * sizeof(struct pst_status)); if (pst == NULL) { fprintf(stderr, "out of memory\n"); return -1; } /* * Calculate pageshift -- the value needed to convert pages to Kbytes. * This will usually be 2. */ pageshift = 0; for (pagesize = info.page_size; pagesize > 1; pagesize >>= 1) pageshift += 1; pageshift -= LOG1024; /* get tty name information */ i = 0; get_tty_names("/dev", &i); /* fill in the statics information */ statics->procstate_names = procstatenames; statics->cpustate_names = cpustatenames; statics->memory_names = memorynames; /* all done! */ return (0); } char * format_header(uname_field) char *uname_field; { char *ptr = header + UNAME_START; while (*uname_field != '\0') *ptr++ = *uname_field++; return header; } void get_system_info(si) struct system_info *si; { static struct pst_dynamic dynamic; int i, n; long total; pstat_getdynamic(&dynamic, sizeof(dynamic), 1, 0); ncpu = dynamic.psd_proc_cnt; /* need this later */ /* Load average */ si->load_avg[0] = dynamic.psd_avg_1_min; si->load_avg[1] = dynamic.psd_avg_5_min; si->load_avg[2] = dynamic.psd_avg_15_min; /* * CPU times to avoid space problems, we roll SWAIT (kernel semaphore * block) into BLOCK (spin lock block) and SSYS (kernel process) into SYS * (system time) Ideally, all screens would be wider :-) */ dynamic.psd_cpu_time[CP_BLOCK] += dynamic.psd_cpu_time[CP_SWAIT]; dynamic.psd_cpu_time[CP_SWAIT] = 0; dynamic.psd_cpu_time[CP_SYS] += dynamic.psd_cpu_time[CP_SSYS]; dynamic.psd_cpu_time[CP_SSYS] = 0; for (i = 0; i < PST_MAX_CPUSTATES; i++) cp_time[i] = dynamic.psd_cpu_time[i]; percentages(PST_MAX_CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); si->cpustates = cpu_states; /* * VM statistics */ memory_stats[0] = -1; memory_stats[1] = pagetok(dynamic.psd_arm); memory_stats[2] = pagetok(dynamic.psd_rm); memory_stats[3] = -1; memory_stats[4] = pagetok(dynamic.psd_avm); memory_stats[5] = pagetok(dynamic.psd_vm); memory_stats[6] = pagetok(dynamic.psd_free); si->memory = memory_stats; /* * If we can get mpid from the kernel, then we will do so now. Otherwise * we'll guess at mpid from the most recently started process time. Note * that this requires us to get the pst array now rather than in * get_process_info(). We rely on get_system_info() being called before * get_system_info() for this to work reliably. */ for (i = 0; i < nproc; i++) pst[i].pst_pid = -1; n = pstat_getproc(pst, sizeof(*pst), nproc, 0); if (kmem >= 0 && mpid_offset > 0) (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "mpid"); else { static int last_start_time = 0; int pid = 0; for (i = 0; i < n; i++) { if (last_start_time <= pst[i].pst_start) { last_start_time = pst[i].pst_start; if (pid <= pst[i].pst_pid) pid = pst[i].pst_pid; } } if (pid != 0) si->last_pid = pid; } } caddr_t get_process_info(si, sel, compare_index) struct system_info *si; struct process_select *sel; int compare_index; { static int handle; int i, active, total; /* * Eliminate unwanted processes and tot up all the wanted processes by * state */ for (i = 0; i < sizeof(process_states) / sizeof(process_states[0]); i++) process_states[i] = 0; for (total = 0, active = 0, i = 0; pst[i].pst_pid >= 0; i++) { int state = pst[i].pst_stat; process_states[state] += 1; total += 1; if (!sel->system && (pst[i].pst_flag & PS_SYS)) { pst[i].pst_stat = -1; continue; } /* * If we are eliminating idle processes, then a process is regarded as * idle if it is in a short term sleep and not using much CPU, or * stopped, or simple dead. */ if (!sel->idle && (state == PS_SLEEP || state == PS_STOP || state == PS_ZOMBIE) && (state != PS_SLEEP && pst[i].pst_pctcpu < CPU_IDLE_THRESH / 100.0)) pst[i].pst_stat = -1; if (sel->uid > 0 && sel->uid != pst[i].pst_uid) pst[i].pst_stat = -1; if (sel->command != NULL && strncmp(sel->command, pst[i].pst_ucomm, strlen(pst[i].pst_ucomm)) != 0) pst[i].pst_stat = -1; if (pst[i].pst_stat >= 0) active += 1; } si->procstates = process_states; si->p_total = total; si->p_active = active; qsort((char *) pst, i, sizeof(*pst), proc_compare); /* handle is simply an index into the process structures */ handle = 0; return (caddr_t) & handle; } /* * Find the terminal name associated with a particular * major/minor number pair */ static char * term_name(term) struct psdev *term; { dev_t dev; int i; if (term->psd_major == -1 && term->psd_minor == -1) return "?"; dev = makedev(term->psd_major, term->psd_minor); for (i = 0; i < nttys && ttynames[i].name[0] != '\0'; i++) { if (dev == ttynames[i].dev) return ttynames[i].name; } return ""; } char * format_next_process(handle, get_userid) caddr_t handle; char *(*get_userid) (); { static char fmt[MAX_COLS]; /* static area where result is built */ char run[sizeof("runNN")]; int idx; struct pst_status *proc; char *state; int size; register long cputime; register double pct; int where; struct handle *hp; struct timeval time; struct timezone timezone; /* sanity check */ if (handle == NULL) return ""; idx = *((int *) handle); while (idx < nproc && pst[idx].pst_stat < 0) idx += 1; if (idx >= nproc || pst[idx].pst_stat < 0) return ""; proc = &pst[idx]; *((int *) handle) = idx + 1; /* set ucomm for system processes, although we shouldn't need to */ if (proc->pst_ucomm[0] == '\0') { if (proc->pst_pid == 0) strcpy(proc->pst_ucomm, "Swapper"); else if (proc->pst_pid == 2) strcpy(proc->pst_ucomm, "Pager"); } size = proc->pst_tsize + proc->pst_dsize + proc->pst_ssize; if (ncpu > 1 && proc->pst_stat == PS_RUN) { sprintf(run, "run%02d", proc->pst_procnum); state = run; } else if (proc->pst_stat == PS_SLEEP) { switch (proc->pst_pri + PTIMESHARE) { case PSWP: state = "SWP"; break; /* also PMEM */ case PRIRWLOCK: state = "RWLOCK"; break; case PRIBETA: state = "BETA"; break; case PRIALPHA: state = "ALPHA"; break; case PRISYNC: state = "SYNC"; break; case PINOD: state = "INOD"; break; case PRIBIO: state = "BIO"; break; case PLLIO: state = "LLIO"; break; /* also PRIUBA */ case PZERO: state = "ZERO"; break; case PPIPE: state = "pipe"; break; case PVFS: state = "vfs"; break; case PWAIT: state = "wait"; break; case PLOCK: state = "lock"; break; case PSLEP: state = "slep"; break; case PUSER: state = "user"; break; default: if (proc->pst_pri < PZERO - PTIMESHARE) state = "SLEEP"; else state = "sleep"; } } else state = state_abbrev[proc->pst_stat]; /* format this entry */ sprintf(fmt, Proc_format, term_name(&proc->pst_term), proc->pst_pid, (*get_userid) (proc->pst_uid), proc->pst_pri, proc->pst_nice - NZERO, format_k(size), format_k(proc->pst_rssize), state, format_time(proc->pst_utime + proc->pst_stime), 100.0 * proc->pst_pctcpu, printable(proc->pst_ucomm)); /* 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). * */ getkval(offset, ptr, size, refstr) unsigned long offset; int *ptr; int size; char *refstr; { if (lseek(kmem, (long) offset, SEEK_SET) == -1) { if (*refstr == '!') refstr++; (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM, refstr, strerror(errno)); quit(23); } if (read(kmem, (char *) ptr, size) == -1) { if (*refstr == '!') return (0); else { (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM, refstr, strerror(errno)); quit(23); } } return (1); } void (* signal(sig, func)) () int sig; void (*func) (); { struct sigaction act; struct sigaction oact; memset(&act, 0, sizeof(act)); act.sa_handler = func; if (sigaction(sig, &act, &oact) < 0) return BADSIG; return oact.sa_handler; } /* * 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 i; for (i = 0; i < nproc; i++) { if (pst[i].pst_pid == pid) return pst[i].pst_uid; } return -1; } static get_tty_names(dir, m) char *dir; int *m; { char name[MAXPATHLEN + 1]; struct dirent **namelist; int i, n; if ((n = scandir(dir, &namelist, NULL, NULL)) < 0) return; if (ttynames == NULL) { nttys = n; ttynames = malloc(n * sizeof(*ttynames)); } else { nttys += n; ttynames = realloc(ttynames, nttys * sizeof(*ttynames)); } for (i = 0; i < n; i++) { struct stat statbuf; char *str = namelist[i]->d_name; if (*str == '.') continue; sprintf(name, "%s/%s", dir, str); if (stat(name, &statbuf) < 0) continue; if (!isalpha(*str)) str = name + sizeof("/dev"); if (S_ISCHR(statbuf.st_mode)) { ttynames[*m].dev = statbuf.st_rdev; strncpy(ttynames[*m].name, str, 8); ttynames[*m].name[9] = '\0'; *m += 1; } else if (S_ISDIR(statbuf.st_mode)) get_tty_names(name, m); } if (*m < nttys) ttynames[*m].name[0] = '\0'; free(namelist); } void get_io_info(struct io_info *io_info) { /* Not supported yet */ memset(io_info, 0, sizeof(*io_info)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_hpux10.man000066400000000000000000000014401271046472400157570ustar00rootroot00000000000000.SH "HPUX 10 INFORMATION" The process information layout has changed slightly since previous versions. The CPU percentage column reports weighted cpu as calculated directly by the kernel. The WCPU column is no longer present in the output and a TTY column has been added to indicate the name of the process's controlling terminal. The definition of an idle process has been relaxed to include those processes that have only just gone to sleep. This version of pg_top does not display a per-cpu breakdown of processor state. Perhaps a later version will add this sophistication across all platforms. The HP/UX 10 port has greatly benefitted from the diligent efforts of the following individuals: John Haxby , Rich Holland , and . pgtop/machine/m_hpux7.c000066400000000000000000000456321271046472400153670ustar00rootroot00000000000000/* * pg_top - a top PostgreSQL users display for Unix * * SYNOPSIS: any hp9000 running hpux version 7 or earlier * * DESCRIPTION: * This is the machine-dependent module for Hpux 6.5 and 7.0. * This makes pg_top work on the following systems: * hp9000s300 * hp9000s700 * hp9000s800 * * LIBS: * * AUTHOR: Christos Zoulas */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pg_top.h" #include "machine.h" #include "utils.h" #define VMUNIX "/hp-ux" #define KMEM "/dev/kmem" #define MEM "/dev/mem" #ifdef DOSWAP #define SWAP "/dev/swap" #endif /* 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 */ }; /* declarations for load_avg */ #include "loadavg.h" /* define what weighted cpu is. */ #define weighted_cpu(pct, pp) ((pp)->p_time == 0 ? 0.0 : \ ((pct) / (1.0 - exp((pp)->p_time * logcpu)))) /* what we consider to be process size: */ #define PROCSIZE(pp) ((pp)->p_tsize + (pp)->p_dsize + (pp)->p_ssize) /* definitions for indices in the nlist array */ #define X_AVENRUN 0 #define X_CCPU 1 #define X_NPROC 2 #define X_PROC 3 #define X_TOTAL 4 #define X_CP_TIME 5 #ifdef hp9000s300 #define X_USRPTMAP 6 #define X_USRPT 7 #else #define X_MPID 6 #define X_HZ 7 #endif #ifdef hp9000s800 #define X_NPIDS 8 #define X_UBASE 9 #endif static struct nlist nlst[] = { {"_avenrun"}, /* 0 */ {"_ccpu"}, /* 1 */ {"_nproc"}, /* 2 */ {"_proc"}, /* 3 */ {"_total"}, /* 4 */ {"_cp_time"}, /* 5 */ #ifdef hp9000s300 {"_Usrptmap"}, /* 6 */ {"_usrpt"}, /* 7 */ #else {"_mpid"}, /* 6 */ {"_hz"}, /* 7 */ #endif #ifdef hp9000s800 {"_npids"}, /* 8 */ {"_ubase"}, /* 9 */ #endif {0} }; /* * 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 %5.2f%% %5.2f%% %s" /* 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[] = { "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop" }; static int kmem, mem; #ifdef DOSWAP static int swap; #endif /* values that we stash away in _init and use in later routines */ static double logcpu; /* these are retrieved from the kernel in _init */ static unsigned long proc; static int nproc; static long hz; static load_avg ccpu; static int ncpu = 0; /* these are offsets obtained via nlist and used in the get_ functions */ #ifndef hp9000s300 static unsigned long mpid_offset; #endif #ifdef hp9000s300 static struct pte *Usrptmap, *usrpt; #endif #ifdef hp9000s800 static int npids; char *ubase; #endif static unsigned long avenrun_offset; static unsigned long total_offset; static unsigned long cp_time_offset; /* these are for calculating cpu state percentages */ static long cp_time[CPUSTATES]; static long cp_old[CPUSTATES]; static long cp_diff[CPUSTATES]; /* these are for detailing the process states */ int process_states[7]; char *procstatenames[] = { "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ", " zombie, ", " stopped, ", NULL }; /* these are for detailing the cpu states */ #ifdef hp9000s300 int cpu_states[9]; #endif #ifdef hp9000s800 int cpu_states[5]; #endif char *cpustatenames[] = { #ifdef hp9000s300 "usr", "nice", "sys", "idle", "", "", "", "intr", "ker", #endif #ifdef hp9000s800 "user", "nice", "system", "idle", "wait", #endif NULL }; /* these are for detailing the memory statistics */ long memory_stats[8]; char *memorynames[] = { "Real: ", "K active, ", "K total ", "Virtual: ", "K active, ", "K total, ", "K free", 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; /* 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 */ extern int errno; extern char *sys_errlist[]; long lseek(); long time(); machine_init(statics) struct statics *statics; { register int i = 0; register int pagesize; if ((kmem = open(KMEM, O_RDONLY)) == -1) { perror(KMEM); return (-1); } if ((mem = open(MEM, O_RDONLY)) == -1) { perror(MEM); return (-1); } #ifdef DOSWAP if ((swap = open(SWAP, O_RDONLY)) == -1) { perror(SWAP); return (-1); } #endif #ifdef hp9000s800 /* 800 names don't have leading underscores */ for (i = 0; nlst[i].n_name; nlst[i++].n_name++) continue; #endif /* get the list of symbols we want to access in the kernel */ (void) nlist(VMUNIX, nlst); if (nlst[0].n_type == 0) { fprintf(stderr, "pg_top: nlist failed\n"); return (-1); } /* make sure they were all found */ if (i > 0 && check_nlist(nlst) > 0) { return (-1); } /* get the symbol values out of kmem */ (void) getkval(nlst[X_PROC].n_value, (int *) (&proc), sizeof(proc), nlst[X_PROC].n_name); (void) getkval(nlst[X_NPROC].n_value, &nproc, sizeof(nproc), nlst[X_NPROC].n_name); #ifndef hp9000s300 (void) getkval(nlst[X_HZ].n_value, (int *) (&hz), sizeof(hz), nlst[X_HZ].n_name); #else hz = HZ; #endif (void) getkval(nlst[X_CCPU].n_value, (int *) (&ccpu), sizeof(ccpu), nlst[X_CCPU].n_name); #ifdef hp9000s800 (void) getkval(nlst[X_NPIDS].n_value, (int *) (&npids), sizeof(npids), nlst[X_NPIDS].n_name); #endif /* stash away certain offsets for later use */ #ifdef hp9000s800 #ifndef UAREA ubase = nlst[X_UBASE].n_value; #else ubase = UAREA; #endif #endif #ifdef hp9000s300 Usrptmap = (struct pte *) nlst[X_USRPTMAP].n_value; usrpt = (struct pte *) nlst[X_USRPT].n_value; #endif #ifndef hp9000s300 mpid_offset = nlst[X_MPID].n_value; #endif avenrun_offset = nlst[X_AVENRUN].n_value; total_offset = nlst[X_TOTAL].n_value; cp_time_offset = nlst[X_CP_TIME].n_value; /* this is used in calculating WCPU -- calculate it ahead of time */ logcpu = log(loaddouble(ccpu)); /* allocate space for proc structure array and array of pointers */ bytes = nproc * sizeof(struct proc); pbase = (struct proc *) malloc(bytes); pref = (struct proc **) malloc(nproc * sizeof(struct proc *)); /* Just in case ... */ if (pbase == (struct proc *) NULL || pref == (struct proc **) NULL) { fprintf(stderr, "pg_top: can't allocate sufficient memory\n"); return (-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; /* all done! */ 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; { load_avg avenrun[3]; long total; /* get the cp_time array */ (void) getkval(cp_time_offset, (int *) cp_time, sizeof(cp_time), "_cp_time"); /* get load average array */ (void) getkval(avenrun_offset, (int *) avenrun, sizeof(avenrun), "_avenrun"); #ifndef hp9000s300 /* get mpid -- process id of last process */ (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "_mpid"); #else si->last_pid = -1; #endif /* convert load averages to doubles */ { register int i; register double *infoloadp; register load_avg *sysloadp; infoloadp = si->load_avg; sysloadp = avenrun; for (i = 0; i < 3; i++) { *infoloadp++ = loaddouble(*sysloadp++); } } /* convert cp_time counts to percentages */ total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); /* sum memory statistics */ { struct vmtotal total; /* get total -- systemwide main memory usage structure */ (void) getkval(total_offset, (int *) (&total), sizeof(total), "_total"); /* convert memory stats to Kbytes */ memory_stats[0] = -1; memory_stats[1] = pagetok(total.t_arm); memory_stats[2] = pagetok(total.t_rm); memory_stats[3] = -1; memory_stats[4] = pagetok(total.t_avm); memory_stats[5] = pagetok(total.t_vm); memory_stats[6] = pagetok(total.t_free); } /* set arrays and strings */ si->cpustates = cpu_states; si->memory = memory_stats; } static struct handle handle; caddr_t get_process_info(si, sel, i) struct system_info *si; struct process_select *sel; int i; { register int i; register int total_procs; register int active_procs; register struct proc **prefp; register struct proc *pp; /* these are copied out of sel for speed */ int show_idle; int show_system; int show_uid; int show_command; /* read all the proc structures in one fell swoop */ (void) getkval(proc, (int *) pbase, bytes, "proc array"); /* 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 SSYS set are system processes---these get ignored * unless show_sysprocs is set. */ if (pp->p_stat != 0 && (show_system || ((pp->p_flag & SSYS) == 0))) { total_procs++; process_states[pp->p_stat]++; if ((pp->p_stat != SZOMB) && (show_idle || (pp->p_pctcpu != 0) || (pp->p_stat == SRUN)) && (!show_uid || pp->p_uid == (uid_t) sel->uid)) { *prefp++ = pp; active_procs++; } } } /* if requested, sort the "interesting" processes */ if (compare != NULL) { qsort((char *) pref, active_procs, sizeof(struct 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 = pref; handle.remaining = active_procs; return ((caddr_t) & handle); } char fmt[MAX_COLS]; /* static area where result is built */ char * format_next_process(handle, get_userid) caddr_t handle; char *(*get_userid) (); { register struct proc *pp; register long cputime; register double pct; int where; struct user u; struct handle *hp; /* 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 */ where = getu(pp, &u); if (where == -1) { (void) strcpy(u.u_comm, ""); cputime = 0; } else { /* set u_comm for system processes */ if (u.u_comm[0] == '\0') { if (pp->p_pid == 0) { (void) strcpy(u.u_comm, "Swapper"); } else if (pp->p_pid == 2) { (void) strcpy(u.u_comm, "Pager"); } } if (where == 1) { /* * Print swapped processes as */ char buf[sizeof(u.u_comm)]; (void) strncpy(buf, u.u_comm, sizeof(u.u_comm)); u.u_comm[0] = '<'; (void) strncpy(&u.u_comm[1], buf, sizeof(u.u_comm) - 2); u.u_comm[sizeof(u.u_comm) - 2] = '\0'; (void) strncat(u.u_comm, ">", sizeof(u.u_comm) - 1); u.u_comm[sizeof(u.u_comm) - 1] = '\0'; } cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec; } /* calculate the base for cpu percentages */ pct = pctdouble(pp->p_pctcpu); /* 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(pagetok(PROCSIZE(pp))), format_k(pagetok(pp->p_rssize)), state_abbrev[pp->p_stat], format_time(cputime), 100.0 * weighted_cpu(pct, pp), 100.0 * pct, printable(u.u_comm)); /* return the result */ return (fmt); } /* * getu(p, u) - get the user structure for the process whose proc structure * is pointed to by p. The user structure is put in the buffer pointed * to by u. Return 0 if successful, -1 on failure (such as the process * being swapped out). */ #define USERSIZE sizeof(struct user) getu(p, u) register struct proc *p; struct user *u; { struct pte uptes[UPAGES]; register caddr_t upage; register struct pte *pte; register nbytes, n; /* * Check if the process is currently loaded or swapped out. The way we * get the u area is totally different for the two cases. For this * application, we just don't bother if the process is swapped out. */ if ((p->p_flag & SLOAD) == 0) { #ifdef DOSWAP if (lseek(swap, (long) dtob(p->p_swaddr), 0) == -1) { perror("lseek(swap)"); return (-1); } if (read(swap, (char *) u, USERSIZE) != USERSIZE) { perror("read(swap)"); return (-1); } return (1); #else return (-1); #endif } /* * Process is currently in memory, we hope! */ if (!getkval((unsigned long) p->p_addr, (int *) uptes, sizeof(uptes), "!p->p_addr")) { #ifdef DEBUG perror("getkval(uptes)"); #endif /* we can't seem to get to it, so pretend it's swapped out */ return (-1); } upage = (caddr_t) u; pte = uptes; for (nbytes = USERSIZE; nbytes > 0; nbytes -= NBPG) { (void) lseek(mem, (long) (pte++->pg_pfnum * NBPG), 0); #ifdef DEBUG perror("lseek(mem)"); #endif n = MIN(nbytes, NBPG); if (read(mem, upage, n) != n) { #ifdef DEBUG perror("read(mem)"); #endif /* we can't seem to get to it, so pretend it's swapped out */ return (-1); } upage += n; } return (0); } /* * 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(nlst) 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); } /* * 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). * */ getkval(offset, ptr, size, refstr) unsigned long offset; int *ptr; int size; char *refstr; { if (lseek(kmem, (long) offset, L_SET) == -1) { if (*refstr == '!') refstr++; (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM, refstr, strerror(errno)); quit(23); } if (read(kmem, (char *) ptr, size) == -1) { if (*refstr == '!') return (0); else { (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM, refstr, strerror(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. */ static unsigned char sorted_state[] = { 0, /* not used */ 3, /* sleep */ 1, /* ABANDONED (WAIT) */ 6, /* run */ 5, /* start */ 2, /* zombie */ 4 /* stop */ }; proc_compare(pp1, pp2) struct proc **pp1; struct proc **pp2; { register struct proc *p1; register struct proc *p2; register int result; register pctcpu lresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; /* compare percent cpu (pctcpu) */ if ((lresult = p2->p_pctcpu - p1->p_pctcpu) == 0) { /* use cpticks to break the tie */ if ((result = p2->p_cpticks - p1->p_cpticks) == 0) { /* 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 resident set size (rssize) to break the tie */ if ((result = p2->p_rssize - p1->p_rssize) == 0) { /* use total memory to break the tie */ result = PROCSIZE(p2) - PROCSIZE(p1); } } } } } else { result = lresult < 0 ? -1 : 1; } return (result); } void (* signal(sig, func)) () int sig; void (*func) (); { struct sigvec osv, sv; /* * XXX: we should block the signal we are playing with, in case we get * interrupted in here. */ if (sigvector(sig, NULL, &osv) == -1) return BADSIG; sv = osv; sv.sv_handler = func; #ifdef SV_BSDSIG sv.sv_flags |= SV_BSDSIG; #endif if (sigvector(sig, &sv, NULL) == -1) return BADSIG; return osv.sv_handler; } int getpagesize() { return 1 << PGSHIFT; } int setpriority(a, b, c) { errno = ENOSYS; return -1; } /* * 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; { register int cnt; register struct proc **prefp; register struct proc *pp; prefp = pref; cnt = pref_len; while (--cnt >= 0) { if ((pp = *prefp++)->p_pid == (pid_t) pid) { return ((int) pp->p_uid); } } return (-1); } void get_io_info(struct io_info *io_info) { /* Not supported yet */ memset(io_info, 0, sizeof(*io_info)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_hpux9.c000066400000000000000000000457431271046472400153740ustar00rootroot00000000000000/* * pg_top - a top PostgreSQL users display for Unix * * SYNOPSIS: any hp9000 running hpux version 9 * * DESCRIPTION: * This is the machine-dependent module for HPUX 9. * This makes pg_top work on (at least) the following systems: * hp9000s800 * hp9000s700 * This may make pg_top work on the following, but we aren't sure: * hp9000s300 * * LIBS: * * CFLAGS: -DHAVE_GETOPT * * AUTHOR: Kevin Schmidt * adapted from Christos Zoulas */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef hpux #define P_RSSIZE(p) (p)->p_rssize #define P_TSIZE(p) (p)->p_tsize #define P_DSIZE(p) (p)->p_dsize #define P_SSIZE(p) (p)->p_ssize #else #include #define __PST2P(p, field) \ ((p)->p_upreg ? ((struct pst_status *) (p)->p_upreg)->field : 0) #define P_RSSIZE(p) __PST2P(p, pst_rssize) #define P_TSIZE(p) __PST2P(p, pst_tsize) #define P_DSIZE(p) __PST2P(p, pst_dsize) #define P_SSIZE(p) __PST2P(p, pst_ssize) #ifdef __hp9000s700 #define p_percentcpu(p) ((p)->p_pctcpu) #define p_time_exact(p) ((p)->p_time) #else /* The following 4 #defines are per HPUX-9.0's */ #define PCT_NORM 9 /* log2(PCT_BASE) */ #define PCT_BASE (1<p_fractioncpu/(float)(PCT_BASE*HZ)) #define p_time_exact(p) (time.tv_sec-((p)->p_swaptime)) #endif /* __hp9000s700 */ #endif /* hpux */ #include "pg_top.h" #include "machine.h" #include "utils.h" #define VMUNIX "/hp-ux" #define KMEM "/dev/kmem" #define MEM "/dev/mem" #ifdef DOSWAP #define SWAP "/dev/dmem" #endif /* 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 */ }; /* declarations for load_avg */ #include "loadavg.h" /* define what weighted cpu is. */ #define weighted_cpu(pct, pp) ((p_time_exact(pp)) == 0 ? 0.0 : \ ((pct) / (1.0 - exp((p_time_exact(pp)) * logcpu)))) /* what we consider to be process size: */ #define PROCSIZE(pp) (P_TSIZE(pp) + P_DSIZE(pp) + P_SSIZE(pp)) /* definitions for indices in the nlist array */ #define X_AVENRUN 0 #define X_CCPU 1 #define X_NPROC 2 #define X_PROC 3 #define X_TOTAL 4 #define X_CP_TIME 5 #define X_MPID 6 /* * Steinar Haug from University of Trondheim, NORWAY pointed out that * the HP 9000 system 800 doesn't have _hz defined in the kernel. He * provided a patch to work around this. We've improved on this patch * here and set the constant X_HZ only when _hz is available in the * kernel. Code in this module that uses X_HZ is surrounded with * appropriate ifdefs. */ #ifndef hp9000s300 #define X_HZ 7 #endif static struct nlist nlst[] = { {"_avenrun"}, /* 0 */ {"_cexp"}, /* 1 */ {"_nproc"}, /* 2 */ {"_proc"}, /* 3 */ {"_total"}, /* 4 */ {"_cp_time"}, /* 5 */ {"_mpid"}, /* 6 */ #ifdef X_HZ {"_hz"}, /* 7 */ #endif {0} }; /* * 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 %5.2f%% %5.2f%% %s" /* 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[] = { "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop" }; static int kmem; /* values that we stash away in _init and use in later routines */ static double logcpu; /* these are retrieved from the kernel in _init */ static unsigned long proc; static int nproc; static long hz; static load_avg ccpu; static int ncpu = 0; /* these are offsets obtained via nlist and used in the get_ functions */ static unsigned long mpid_offset; static unsigned long avenrun_offset; static unsigned long total_offset; static unsigned long cp_time_offset; /* these are for calculating cpu state percentages */ static long cp_time[CPUSTATES]; static long cp_old[CPUSTATES]; static long cp_diff[CPUSTATES]; /* these are for detailing the process states */ int process_states[7]; char *procstatenames[] = { "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ", " zombie, ", " stopped, ", NULL }; /* these are for detailing the cpu states */ int cpu_states[9]; char *cpustatenames[] = { "usr", "nice", "sys", "idle", "", "", "", "intr", "ker", NULL }; /* these are for detailing the memory statistics */ long memory_stats[8]; char *memorynames[] = { "Real: ", "K act, ", "K tot ", "Virtual: ", "K act, ", "K tot, ", "K free", 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; static struct pst_status *pst; /* 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 */ extern int errno; extern char *sys_errlist[]; long lseek(); long time(); machine_init(statics) struct statics *statics; { register int i = 0; register int pagesize; if ((kmem = open(KMEM, O_RDONLY)) == -1) { perror(KMEM); return (-1); } #ifdef hp9000s800 /* 800 names don't have leading underscores */ for (i = 0; nlst[i].n_name; nlst[i++].n_name++) continue; #endif /* get the list of symbols we want to access in the kernel */ (void) nlist(VMUNIX, nlst); if (nlst[0].n_type == 0) { fprintf(stderr, "pg_top: nlist failed\n"); return (-1); } /* make sure they were all found */ if (check_nlist(nlst) > 0) { return (-1); } /* get the symbol values out of kmem */ (void) getkval(nlst[X_PROC].n_value, (int *) (&proc), sizeof(proc), nlst[X_PROC].n_name); (void) getkval(nlst[X_NPROC].n_value, &nproc, sizeof(nproc), nlst[X_NPROC].n_name); (void) getkval(nlst[X_CCPU].n_value, (int *) (&ccpu), sizeof(ccpu), nlst[X_CCPU].n_name); #ifdef X_HZ (void) getkval(nlst[X_HZ].n_value, (int *) (&hz), sizeof(hz), nlst[X_HZ].n_name); #else hz = HZ; #endif /* stash away certain offsets for later use */ mpid_offset = nlst[X_MPID].n_value; avenrun_offset = nlst[X_AVENRUN].n_value; total_offset = nlst[X_TOTAL].n_value; cp_time_offset = nlst[X_CP_TIME].n_value; /* this is used in calculating WCPU -- calculate it ahead of time */ logcpu = log(loaddouble(ccpu)); /* allocate space for proc structure array and array of pointers */ bytes = nproc * sizeof(struct proc); pbase = (struct proc *) malloc(bytes); pref = (struct proc **) malloc(nproc * sizeof(struct proc *)); pst = (struct pst_status *) malloc(nproc * sizeof(struct pst_status)); /* Just in case ... */ if (pbase == (struct proc *) NULL || pref == (struct proc **) NULL) { fprintf(stderr, "pg_top: can't allocate sufficient memory\n"); return (-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; /* all done! */ 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; { load_avg avenrun[3]; long total; /* get the cp_time array */ (void) getkval(cp_time_offset, (int *) cp_time, sizeof(cp_time), "_cp_time"); /* get load average array */ (void) getkval(avenrun_offset, (int *) avenrun, sizeof(avenrun), "_avenrun"); /* get mpid -- process id of last process */ (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "_mpid"); /* convert load averages to doubles */ { register int i; register double *infoloadp; register load_avg *sysloadp; infoloadp = si->load_avg; sysloadp = avenrun; for (i = 0; i < 3; i++) { *infoloadp++ = loaddouble(*sysloadp++); } } /* convert cp_time counts to percentages */ total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); /* sum memory statistics */ { struct vmtotal total; /* get total -- systemwide main memory usage structure */ (void) getkval(total_offset, (int *) (&total), sizeof(total), "_total"); /* convert memory stats to Kbytes */ memory_stats[0] = -1; memory_stats[1] = pagetok(total.t_arm); memory_stats[2] = pagetok(total.t_rm); memory_stats[3] = -1; memory_stats[4] = pagetok(total.t_avm); memory_stats[5] = pagetok(total.t_vm); memory_stats[6] = pagetok(total.t_free); } /* set arrays and strings */ si->cpustates = cpu_states; si->memory = memory_stats; } static struct handle handle; caddr_t get_process_info(si, sel, i) struct system_info *si; struct process_select *sel; int i; { register int i; register int total_procs; register int active_procs; register struct proc **prefp; register struct proc *pp; /* these are copied out of sel for speed */ int show_idle; int show_system; int show_uid; int show_command; /* read all the proc structures in one fell swoop */ (void) getkval(proc, (int *) pbase, bytes, "proc array"); for (i = 0; i < nproc; ++i) { if (pstat(PSTAT_PROC, &pst[i], sizeof(pst[i]), 0, pbase[i].p_pid) != 1) pbase[i].p_upreg = (preg_t *) 0; else pbase[i].p_upreg = (preg_t *) & pst[i]; pbase[i].p_nice = pst[i].pst_nice; pbase[i].p_cpticks = pst[i].pst_cpticks; } /* 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 SSYS set are system processes---these get ignored * unless show_sysprocs is set. */ if (pp->p_stat != 0 && (show_system || ((pp->p_flag & SSYS) == 0))) { total_procs++; process_states[pp->p_stat]++; /* * idle processes can be selectively ignored: a process is * considered idle when cpticks is zero AND it is not in the run * state. Zombies are always ignored. We also skip over * processes that have been excluded via a uid selection */ if ((pp->p_stat != SZOMB) && (show_idle || (pp->p_cpticks != 0) || (pp->p_stat == SRUN)) && (!show_uid || pp->p_uid == (uid_t) sel->uid)) { *prefp++ = pp; active_procs++; } } } /* if requested, sort the "interesting" processes */ if (compare != NULL) { qsort((char *) pref, active_procs, sizeof(struct 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 = pref; handle.remaining = active_procs; return ((caddr_t) & handle); } char fmt[MAX_COLS]; /* static area where result is built */ char * format_next_process(handle, get_userid) caddr_t handle; char *(*get_userid) (); { register struct proc *pp; register long cputime; register double pct; int where; struct user u; struct handle *hp; struct timeval time; struct timezone timezone; /* 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 */ where = getu(pp, &u); if (where == -1) { (void) strcpy(u.u_comm, ""); cputime = 0; } else { /* set u_comm for system processes */ if (u.u_comm[0] == '\0') { if (pp->p_pid == 0) { (void) strcpy(u.u_comm, "Swapper"); } else if (pp->p_pid == 2) { (void) strcpy(u.u_comm, "Pager"); } } if (where == 1) { /* * Print swapped processes as */ char buf[sizeof(u.u_comm)]; (void) strncpy(buf, u.u_comm, sizeof(u.u_comm)); u.u_comm[0] = '<'; (void) strncpy(&u.u_comm[1], buf, sizeof(u.u_comm) - 2); u.u_comm[sizeof(u.u_comm) - 2] = '\0'; (void) strncat(u.u_comm, ">", sizeof(u.u_comm) - 1); u.u_comm[sizeof(u.u_comm) - 1] = '\0'; } cputime = __PST2P(pp, pst_cptickstotal) / hz; } /* calculate the base for cpu percentages */ pct = pctdouble(p_percentcpu(pp)); /* get time used for calculation in weighted_cpu */ gettimeofday(&time, &timezone); /* 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(pagetok(PROCSIZE(pp))), format_k(pagetok(P_RSSIZE(pp))), state_abbrev[pp->p_stat], format_time(cputime), 100.0 * weighted_cpu(pct, pp), 100.0 * pct, printable(u.u_comm)); /* return the result */ return (fmt); } /* * getu(p, u) - get the user structure for the process whose proc structure * is pointed to by p. The user structure is put in the buffer pointed * to by u. Return 0 if successful, -1 on failure (such as the process * being swapped out). */ getu(p, u) register struct proc *p; struct user *u; { struct pst_status *ps; char *s, *c; int i; if ((ps = (struct pst_status *) p->p_upreg) == NULL) return -1; memset(u, 0, sizeof(struct user)); c = ps->pst_cmd; ps->pst_cmd[PST_CLEN - 1] = '\0'; /* paranoia */ s = strtok(ps->pst_cmd, "\t \n"); if (c = strrchr(s, '/')) c++; else c = s; if (*c == '-') c++; i = 0; for (; i < MAXCOMLEN; i++) { if (*c == '\0' || *c == ' ' || *c == '/') break; u->u_comm[i] = *c++; } #ifndef DOSWAP return ((p->p_flag & SLOAD) == 0 ? 1 : 0); #endif return (0); } /* * 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(nlst) 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); } /* * 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). * */ getkval(offset, ptr, size, refstr) unsigned long offset; int *ptr; int size; char *refstr; { if (lseek(kmem, (long) offset, L_SET) == -1) { if (*refstr == '!') refstr++; (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM, refstr, strerror(errno)); quit(23); } if (read(kmem, (char *) ptr, size) == -1) { if (*refstr == '!') return (0); else { (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM, refstr, strerror(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. */ static unsigned char sorted_state[] = { 0, /* not used */ 3, /* sleep */ 1, /* ABANDONED (WAIT) */ 6, /* run */ 5, /* start */ 2, /* zombie */ 4 /* stop */ }; proc_compare(pp1, pp2) struct proc **pp1; struct proc **pp2; { register struct proc *p1; register struct proc *p2; register int result; register pctcpu lresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; /* compare percent cpu (pctcpu) */ if ((lresult = p_percentcpu(p2) - p_percentcpu(p1)) == 0) { /* use cpticks to break the tie */ if ((result = p2->p_cpticks - p1->p_cpticks) == 0) { /* 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 resident set size (rssize) to break the tie */ if ((result = P_RSSIZE(p2) - P_RSSIZE(p1)) == 0) { /* use total memory to break the tie */ result = PROCSIZE(p2) - PROCSIZE(p1); } } } } } else { result = lresult < 0 ? -1 : 1; } return (result); } void (* signal(sig, func)) () int sig; void (*func) (); { struct sigvec osv, sv; /* * XXX: we should block the signal we are playing with, in case we get * interrupted in here. */ if (sigvector(sig, NULL, &osv) == -1) return BADSIG; sv = osv; sv.sv_handler = func; #ifdef SV_BSDSIG sv.sv_flags |= SV_BSDSIG; #endif if (sigvector(sig, &sv, NULL) == -1) return BADSIG; return osv.sv_handler; } int getpagesize() { return 1 << PGSHIFT; } int setpriority(a, b, c) { errno = ENOSYS; return -1; } /* * 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; { register int cnt; register struct proc **prefp; register struct proc *pp; prefp = pref; cnt = pref_len; while (--cnt >= 0) { if ((pp = *prefp++)->p_pid == (pid_t) pid) { return ((int) pp->p_uid); } } return (-1); } void get_io_info(struct io_info *io_info) { /* Not supported yet */ memset(io_info, 0, sizeof(*io_info)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_hpux9.man000066400000000000000000000007171271046472400157150ustar00rootroot00000000000000.SH "HPUX 9 INFORMATION" Under HP/UX 9, the kernel symbol _ccpu was eliminated. The author believe that _cexp is a suitable substitute, but cannot be positive. This seems to be confirmed by the fact that information produced using this assumption correlates well with that produced by HP's version of top. This port was adapted from the port for HP/UX version 8 (written by Christos Zoulas). The adaptation was performed by Kevin Schmidt . pgtop/machine/m_irix5.c000066400000000000000000000442221271046472400153460ustar00rootroot00000000000000/* * pg_top - a top PostgreSQL users display for Unix * * SYNOPSIS: any uniprocessor, 32 bit SGI machine running IRIX 5.3 * * DESCRIPTION: * This is the machine-dependent module for IRIX 5.3. * It has been tested on Indys running 5.3 and Indigos running 5.3XFS * * LIBS: -lmld * CFLAGS: -DHAVE_GETOPT * * AUTHOR: Sandeep Cariapa * This is not a supported product of Silicon Graphics, Inc. * Please do not call SGI for support. * */ #define _KMEMUSER #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pg_top.h" #include "machine.h" #ifdef IRIX64 #define nlist nlist64 #define lseek lseek64 #define off_t off64_t #endif #define UNIX "/unix" #define KMEM "/dev/kmem" #define CPUSTATES 6 #ifndef FSCALE #define FSHIFT 8 /* bits to right of fixed binary point */ #define FSCALE (1<pr_fill) #define weighted_cpu(pp) (*(double *)&pp->pr_fill[2]) static int pagesize; #define pagetok(size) ((size)*pagesize) static int numcpus; /* * 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 %5.2f%% %5.2f%% %.16s" /* these are for detailing the process states */ char *state_abbrev[] = {"", "sleep", "run\0\0\0", "zombie", "stop", "idle", "", "swap"}; int process_states[8]; char *procstatenames[] = { "", " sleeping, ", " running, ", " zombie, ", " stopped, ", " idle, ", "", " swapped, ", NULL }; /* these are for detailing the cpu states */ int cpu_states[CPUSTATES]; char *cpustatenames[] = { "idle", "usr", "ker", "wait", "swp", "intr", NULL }; /* these are for detailing the memory statistics */ long memory_stats[5]; char *memorynames[] = { "K max, ", "K avail, ", "K free, ", "K swap, ", "K free swap", NULL }; /* useful externals */ extern int errno; extern char *myname; extern char *sys_errlist[]; extern char *format_k(); extern char *format_time(); extern long percentages(); /* forward references */ int proc_compare(void *pp1, void *pp2); #define X_AVENRUN 0 #define X_NPROC 1 #define X_FREEMEM 2 #define X_MAXMEM 3 #define X_AVAILRMEM 4 #define X_MPID 5 static struct nlist nlst[] = { {"avenrun"}, /* 0. Array containing the 3 load averages. */ {"nproc"}, /* 1. Kernel parameter: Max number of * processes. */ {"freemem"}, /* 2. Amount of free memory in system. */ {"maxmem"}, /* 3. Maximum amount of memory usable by * system. */ {"availrmem"}, /* 4. Available real memory. */ #ifndef IRIX64 {"mpid"}, /* 5. PID of last process. */ #endif {0} }; static unsigned long avenrun_offset; static unsigned long nproc_offset; static unsigned long freemem_offset; static unsigned long maxmem_offset; static unsigned long availrmem_offset; static unsigned long mpid_offset; double load[3]; char fmt[MAX_COLS]; static int kmem; static int nproc; static int bytes; static struct prpsinfo *pbase; static struct prpsinfo **pref; static DIR *procdir; /* 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 */ }; static struct handle handle; void getptable(); /* * 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; }; static int oldprocs; /* size of table */ static struct oldproc *oldbase; #define HASH(x) ((x << 1) % oldprocs) #define PRPSINFOSIZE (sizeof(struct prpsinfo)) int machine_init(statics) struct statics *statics; { struct oldproc *op, *endbase; if ((kmem = open(KMEM, O_RDONLY)) == -1) { perror(KMEM); return (-1); } /* get the list of symbols we want to access in the kernel */ (void) nlist(UNIX, nlst); if (nlst[0].n_type == 0) { fprintf(stderr, "%s: nlist failed\n", myname); return (-1); } /* Check if we got all of 'em. */ if (check_nlist(nlst) > 0) { return (-1); } avenrun_offset = nlst[X_AVENRUN].n_value; nproc_offset = nlst[X_NPROC].n_value; freemem_offset = nlst[X_FREEMEM].n_value; maxmem_offset = nlst[X_MAXMEM].n_value; availrmem_offset = nlst[X_AVAILRMEM].n_value; #ifndef IRIX64 mpid_offset = nlst[X_MPID].n_value; #endif /* * Got to do this first so that we can map real estate for the process * array. */ (void) getkval(nproc_offset, (int *) (&nproc), sizeof(nproc), "nproc"); /* 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 *)); oldbase = (struct oldproc *) malloc(2 * nproc * sizeof(struct oldproc)); /* Just in case ... */ if (pbase == (struct prpsinfo *) NULL || pref == (struct prpsinfo **) NULL || oldbase == (struct oldproc *) NULL) { (void) fprintf(stderr, "%s: can't allocate sufficient memory\n", myname); return (-1); } oldprocs = 2 * nproc; endbase = oldbase + oldprocs; for (op = oldbase; op < endbase; op++) { op->oldpid = -1; } if (!(procdir = opendir(_PATH_PROCFSPI))) { (void) fprintf(stderr, "Unable to open %s\n", _PATH_PROCFSPI); return (-1); } if (chdir(_PATH_PROCFSPI)) { /* handy for later on when we're reading it */ (void) fprintf(stderr, "Unable to chdir to %s\n", _PATH_PROCFSPI); return (-1); } statics->procstate_names = procstatenames; statics->cpustate_names = cpustatenames; statics->memory_names = memorynames; pagesize = getpagesize() / 1024; /* all done! */ 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; { register int i; int avenrun[3]; static int freemem; static int maxmem; static int availrmem; struct sysinfo sysinfo; static long cp_new[CPUSTATES]; static long cp_old[CPUSTATES]; static long cp_diff[CPUSTATES]; /* for cpu state percentages */ off_t fswap; /* current free swap in blocks */ off_t tswap; /* total swap in blocks */ (void) getkval(avenrun_offset, (int *) avenrun, sizeof(avenrun), "avenrun"); for (i = 0; i < 3; i++) { si->load_avg[i] = loaddouble(avenrun[i]); si->load_avg[i] = si->load_avg[i] / 1024.0; } (void) getkval(freemem_offset, (int *) (&freemem), sizeof(freemem), "freemem"); (void) getkval(maxmem_offset, (int *) (&maxmem), sizeof(maxmem), "maxmem"); (void) getkval(availrmem_offset, (int *) (&availrmem), sizeof(availrmem), "availrmem"); #ifdef IRIX64 si->last_pid = 0; #else (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "mpid"); #endif swapctl(SC_GETFREESWAP, &fswap); swapctl(SC_GETSWAPTOT, &tswap); memory_stats[0] = pagetok(maxmem); memory_stats[1] = pagetok(availrmem); memory_stats[2] = pagetok(freemem); memory_stats[3] = tswap / 2; memory_stats[4] = fswap / 2; /* * use sysmp() to get current sysinfo usage. Can run into all kinds of * problems if you try to nlist this kernel variable. */ if (sysmp(MP_SAGET, MPSA_SINFO, &sysinfo, sizeof(struct sysinfo)) == -1) { perror("sysmp"); return; } /* copy sysinfo.cpu to an array of longs, as expected by percentages() */ for (i = 0; i < CPUSTATES; i++) { cp_new[i] = sysinfo.cpu[i]; } (void) percentages(CPUSTATES, cpu_states, cp_new, cp_old, cp_diff); si->cpustates = cpu_states; si->memory = memory_stats; numcpus = sysmp(MP_NPROCS); /* add a slash to the "run" state abbreviation */ if (numcpus > 1) { state_abbrev[SRUN][3] = '/'; } return; } caddr_t get_process_info(si, sel, x) 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; /* 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_system 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)) && (!show_uid || pp->pr_uid == (uid_t) sel->uid)) { *prefp++ = pp; active_procs++; } } } /* if requested, sort the "interesting" processes */ if (compare != NULL) 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 * format_next_process(handle, get_userid) 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); if (numcpus > 1) { if (pp->pr_sonproc < 0) state_abbrev[SRUN][4] = '*'; else state_abbrev[SRUN][4] = pp->pr_sonproc + '0'; } /* format this entry */ 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), weighted_cpu(pp), pctcpu, pp->pr_fname); /* 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). * */ int getkval(offset, ptr, size, refstr) off_t offset; int *ptr; int size; char *refstr; { if (lseek(kmem, offset, SEEK_SET) == -1) { if (*refstr == '!') refstr++; (void) fprintf(stderr, "%s: lseek to %s: %s\n", KMEM, refstr, strerror(errno)); quit(0); } if (read(kmem, (char *) ptr, size) == -1) { if (*refstr == '!') return (0); else { (void) fprintf(stderr, "%s: reading %s: %s\n", KMEM, refstr, strerror(errno)); quit(0); } } return (1); } /* * 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, idle, 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, /* idle */ 0, /* not used */ 1 /* being swapped (WAIT) */ }; int proc_compare(pp1, pp2) void *pp1; void *pp2; { register struct prpsinfo *p1; register struct prpsinfo *p2; register long result; /* remove one level of indirection */ p1 = *(struct prpsinfo **) pp1; p2 = *(struct prpsinfo **) 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); } /* return the owner of the specified process. */ int proc_owner(pid) int pid; { register struct prpsinfo *p; int i; for (i = 0, p = pbase; i < nproc; i++, p++) if (p->pr_pid == (oid_t) pid) return ((int) p->pr_uid); return (-1); } /* * 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(nlst) 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); } /* get process table */ void getptable(baseptr) struct prpsinfo *baseptr; { struct prpsinfo *currproc; /* pointer to current proc structure */ int numprocs = 0; int i; struct dirent *directp; struct oldproc *op; static struct timeval lasttime = {0L, 0L}; struct timeval thistime; struct timezone thiszone; double timediff; double alpha, beta; struct oldproc *endbase; gettimeofday(&thistime, &thiszone); /* * 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; for (rewinddir(procdir); directp = readdir(procdir);) { int fd; if ((fd = open(directp->d_name, O_RDONLY)) < 0) continue; currproc = &baseptr[numprocs]; if (ioctl(fd, PIOCPSINFO, currproc) < 0) { (void) close(fd); continue; } /* * 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. */ op = oldbase + HASH(currproc->pr_pid); while (1) { if (op->oldpid == -1) /* not there */ break; if (op->oldpid == currproc->pr_pid) { /* found old data */ percent_cpu(currproc) = ((currproc->pr_time.tv_sec * 1.0e9 + currproc->pr_time.tv_nsec) - op->oldtime) / timediff; 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) { 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; } } numprocs++; (void) close(fd); } 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; } void get_io_info(struct io_info *io_info) { /* Not supported yet */ memset(io_info, 0, sizeof(*io_info)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_irixsgi.c000066400000000000000000000652611271046472400157720ustar00rootroot00000000000000/* * pg_top - a top PostgreSQL users display for Unix * * SYNOPSIS: Any SGI machine running IRIX 6.2 and up * * DESCRIPTION: * This is the machine-dependent module for IRIX as supplied by * engineers at SGI. * * CFLAGS: -DHAVE_GETOPT -D_OLD_TERMIOS -DORDER * * AUTHOR: Sandeep Cariapa * AUTHOR: Larry McVoy * Sandeep did all the hard work; I ported to 6.2 and fixed up some formats. * AUTHOR: John Schimmel * He did the all irix merge. * AUTHOR: Ariel Faigon * Ported to Ficus/Kudzu (IRIX 6.4+). * Got rid of all nlist and different (elf64, elf32, COFF) kernel * dependencies * Various small fixes and enhancements: multiple CPUs, nicer formats. * Added -DORDER process display ordering * cleaned most -fullwarn'ings. * Need -D_OLD_TERMIOS when compiling on IRIX 6.4 to work on 6.2 systems * Support much bigger values in memory sizes (over Peta-byte) * AUTHOR: William LeFebvre * Converted to ANSI C and updated to new module interface */ #define _KMEMUSER #include "config.h" #include #include #include #include #include #include #include #include #include #include /* for < 6.4 NDPHIMAX et al. */ #include #include #include #include #include #include #include #include #include #include #include "pg_top.h" #include "machine.h" #include "utils.h" #define KMEM "/dev/kmem" typedef double load_avg; #define loaddouble(la) (la) #define intload(i) ((double)(i)) /* * 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; }; static int oldprocs; /* size of table */ static struct oldproc *oldbase; #define HASH(x) ((x << 1) % oldprocs) #define pagetok(pages) ((((uint64_t) pages) * pagesize) >> 10) /* * Ugly hack, save space and complexity of allocating and maintaining * parallel arrays to the prpsinfo array: use spare space (pr_fill area) * in prpsinfo structures to store %CPU calculated values */ #define D_align(addr) (((unsigned long)(addr) & ~0x0fU)) #define percent_cpu(pp) (* (double *) D_align(&((pp)->pr_fill[0]))) #define weighted_cpu(pp) (* (double *) D_align(&((pp)->pr_fill[4]))) /* Username field to fill in starts at: */ #define UNAME_START 16 /* * These definitions control the format of the per-process area */ static char header[] = " PID PGRP X PRI SIZE RES STATE TIME %WCPU %CPU COMMAND"; /* 012345678901234567890123456789012345678901234567890123456789012345678901234567 10 20 30 40 50 60 70 */ /* PID PGRP USER PRI SIZE RES STATE TIME %WCPU %CPU CMD */ #define Proc_format \ "%7d %7d %-8.8s %4.4s %6.6s %5.5s %-6.6s %6.6s %5.2f %5.2f %-.10s" /* * these are for detailing the cpu states * Data is taken from the sysinfo structure (see ) * We rely on the following values: * * #define CPU_IDLE 0 * #define CPU_USER 1 * #define CPU_KERNEL 2 * #define CPU_WAIT 3 * #define CPU_SXBRK 4 * #define CPU_INTR 5 */ #ifndef CPU_STATES /* defined only in 6.4 and up */ #define CPU_STATES 6 #endif int cpu_states[CPU_STATES]; char *cpustatenames[] = { "idle", "usr", "ker", "wait", "xbrk", "intr", NULL }; /* these are for detailing the memory statistics */ #define MEMSTATS 10 int memory_stats[MEMSTATS]; char *memorynames[] = { "K max, ", "K avail, ", "K free, ", "K swap, ", "K free swap", NULL }; char uname_str[40]; double load[3]; static char fmt[MAX_COLS + 2]; int numcpus; /* useful externals */ extern int errno; extern char *sys_errlist[]; extern char *myname; extern char *format_k(); extern char *format_time(); extern long percentages(); static int kmem; static unsigned long avenrun_offset; static float irix_ver; /* for easy numeric comparison */ static struct prpsinfo *pbase; static struct prpsinfo **pref; static struct oldproc *oldbase; static int oldprocs; /* size of table */ static DIR *procdir; static int ptable_size; /* allocated process table size */ static int nproc; /* estimated process table size */ static int pagesize; /* 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 */ }; static struct handle handle; void getptable(struct prpsinfo * baseptr); void size(int fd, struct prpsinfo * ps); extern char *ordernames[]; /* * Process states letters are mapped into numbers * 6.5 seems to have changed the semantics of prpsinfo.pr_state * so we rely, (like ps does) on the char value pr_sname. * The order we use here is what may be most interesting * to pg_top users: Most interesting state on pg_top, least on bottom. * 'S' (sleeping) is the most common case so I put it _after_ * zombie, even though it is more "active" than zombie. * * State letters and their meanings: * * R Process is running (may not have a processor yet) * I Process is in intermediate state of creation * X Process is waiting for memory * T Process is stopped * Z Process is terminated and parent not waiting (zombie) * S Process is sleeping, waiting for a resource */ /* abbreviated process states */ static char *state_abbrev[] = {"", "sleep", "zomb", "stop", "swap", "start", "ready", "run", NULL}; /* Same but a little "wordier", used in CPU activity summary */ int process_states[8]; /* per state counters */ char *procstatenames[] = { /* ready to run is considered running here */ "", " sleeping, ", " zombie, ", " stopped, ", " swapped, ", " starting, ", " ready, ", " running, ", NULL }; #define S_RUNNING 7 #define S_READY 6 #define S_STARTING 5 #define S_SWAPPED 4 #define S_STOPPED 3 #define S_ZOMBIE 2 #define S_SLEEPING 1 #define IS_ACTIVE(pp) \ (first_screen ? proc_state(pp) >= S_STARTING : percent_cpu(pp) > 0.0) /* * proc_state * map the pr_sname value to an integer. * used as an index into state_abbrev[] * as well as an "order" key */ static int proc_state(struct prpsinfo * pp) { char psname = pp->pr_sname; switch (psname) { case 'R': return (pp->pr_sonproc >= 0 && pp->pr_sonproc < numcpus) ? S_RUNNING /* on a processor */ : S_READY; case 'I': return S_STARTING; case 'X': return S_SWAPPED; case 'T': return S_STOPPED; case 'Z': return S_ZOMBIE; case 'S': return S_SLEEPING; default: return 0; } } /* * To avoid nlist'ing the kernel (with all the different kernel type * complexities), we estimate the size of the needed working process * table by scanning /proc/pinfo and taking the number of entries * multiplied by some reasonable factor. * Assume current dir is _PATH_PROCFSPI */ static int active_proc_count() { DIR *dirp; int pcnt; if ((dirp = opendir(".")) == NULL) { (void) fprintf(stderr, "%s: Unable to open %s\n", myname, _PATH_PROCFSPI); exit(1); } for (pcnt = 0; readdir(dirp) != NULL; pcnt++) ; closedir(dirp); return pcnt; } /* * allocate space for: * proc structure array * array of pointers to the above (used for sorting) * array for storing per-process old CPU usage */ void allocate_proc_tables() { int n_active = active_proc_count(); if (pbase != NULL) /* && n_active < ptable_size */ return; /* Need to realloc if we exceed, but factor should be enough */ nproc = n_active * 5; oldprocs = 2 * nproc; pbase = (struct prpsinfo *) malloc(nproc * sizeof(struct prpsinfo)); pref = (struct prpsinfo **) malloc(nproc * sizeof(struct prpsinfo *)); oldbase = (struct oldproc *) malloc(oldprocs * sizeof(struct oldproc)); ptable_size = nproc; if (pbase == NULL || pref == NULL || oldbase == NULL) { (void) fprintf(stderr, "%s: malloc: out of memory\n", myname); exit(1); } } int machine_init(struct statics * statics) { struct oldproc *op, *endbase; int pcnt = 0; struct utsname utsname; char tmpbuf[20]; uname(&utsname); irix_ver = (float) atof((const char *) utsname.release); strncpy(tmpbuf, utsname.release, 9); tmpbuf[9] = '\0'; sprintf(uname_str, "%s %-.14s %s %s", utsname.sysname, utsname.nodename, tmpbuf, utsname.machine); pagesize = getpagesize(); if ((kmem = open(KMEM, O_RDONLY)) == -1) { perror(KMEM); return -1; } if (chdir(_PATH_PROCFSPI)) { /* handy for later on when we're reading it */ (void) fprintf(stderr, "%s: Unable to chdir to %s\n", myname, _PATH_PROCFSPI); return -1; } if ((procdir = opendir(".")) == NULL) { (void) fprintf(stderr, "%s: Unable to open %s\n", myname, _PATH_PROCFSPI); return -1; } if ((avenrun_offset = sysmp(MP_KERNADDR, MPKA_AVENRUN)) == -1) { perror("sysmp(MP_KERNADDR, MPKA_AVENRUN)"); return -1; } allocate_proc_tables(); oldprocs = 2 * nproc; endbase = oldbase + oldprocs; for (op = oldbase; op < endbase; op++) { op->oldpid = -1; } statics->cpustate_names = cpustatenames; statics->memory_names = memorynames; statics->order_names = ordernames; statics->procstate_names = procstatenames; 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); } void get_system_info(struct system_info * si) { int i; int avenrun[3]; struct rminfo realmem; struct sysinfo sysinfo; static time_t cp_old[CPU_STATES]; static time_t cp_diff[CPU_STATES]; /* for cpu state percentages */ off_t fswap; /* current free swap in blocks */ off_t tswap; /* total swap in blocks */ (void) getkval(avenrun_offset, (int *) avenrun, sizeof(avenrun), "avenrun"); for (i = 0; i < 3; i++) { si->load_avg[i] = loaddouble(avenrun[i]); si->load_avg[i] /= 1024.0; } if ((numcpus = sysmp(MP_NPROCS)) == -1) { perror("sysmp(MP_NPROCS)"); return; } if (sysmp(MP_SAGET, MPSA_RMINFO, &realmem, sizeof(realmem)) == -1) { perror("sysmp(MP_SAGET,MPSA_RMINFO, ...)"); return; } swapctl(SC_GETFREESWAP, &fswap); swapctl(SC_GETSWAPTOT, &tswap); memory_stats[0] = pagetok(realmem.physmem); memory_stats[1] = pagetok(realmem.availrmem); memory_stats[2] = pagetok(realmem.freemem); memory_stats[3] = tswap / 2; memory_stats[4] = fswap / 2; if (sysmp(MP_SAGET, MPSA_SINFO, &sysinfo, sizeof(struct sysinfo)) == -1) { perror("sysmp(MP_SAGET,MPSA_SINFO)"); return; } (void) percentages(CPU_STATES, cpu_states, sysinfo.cpu, cp_old, cp_diff); si->cpustates = cpu_states; si->memory = memory_stats; si->last_pid = -1; return; } caddr_t get_process_info(struct system_info * si, struct process_select * sel, int compare_index) { int i, total_procs, active_procs; struct prpsinfo **prefp; struct prpsinfo *pp; int show_uid; static char first_screen = 1; /* 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_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_system is set. Ariel: IRIX 6.4 had to redefine "system * processes" They do not exist outside the kernel in new kernels. Now * defining as uid==0 and ppid==1 (init children) */ if (pp->pr_state && (sel->system || !(pp->pr_uid == 0 && pp->pr_ppid == 1))) { total_procs++; process_states[proc_state(pp)]++; /* * zombies are actually interesting (to avoid) although they are * not active, so I leave them displayed. */ if ( /* (! pp->pr_zomb) && */ (sel->idle || IS_ACTIVE(pp)) && (!show_uid || pp->pr_uid == (uid_t) sel->uid)) { *prefp++ = pp; active_procs++; } } } first_screen = 0; /* 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); } /* * Added cpu_id to running processes, add 'ready' (to run) state */ static char * format_state(struct prpsinfo * pp) { static char state_str[16]; int state = proc_state(pp); if (state == S_RUNNING) { /* * Alert: 6.2 (MP only?) binary incompatibility pp->pr_sonproc * apparently (?) has a different offset on 6.2 machines... I've seen * cases where a 6.4 compiled pg_top running on 6.2 printed a garbage * CPU-id. To be safe, I print the CPU-id only if it falls within * range [0..numcpus-1] */ sprintf(state_str, "run/%d", pp->pr_sonproc); return state_str; } /* default */ return state_abbrev[state]; } static char * format_prio(struct prpsinfo * pp) { static char prio_str[10]; if (irix_ver < 6.4) { /* * Note: this is _compiled_ on 6.x where x >= 4 but I would like it to * run on 6.2 6.3 as well (backward binary compatibility). Scheduling * is completely different between these IRIX versions and some * scheduling classes may even have different names. * * The solution: have more than one style of 'priority' depending on * the OS version. * * See npri(1) + nice(2) + realtime(5) for scheduling classes, and * priority values. */ if (pp->pr_pri <= NDPHIMIN) /* real time? */ sprintf(prio_str, "+%d", pp->pr_pri); else if (pp->pr_pri <= NDPNORMMIN) /* normal interactive */ sprintf(prio_str, "%d", pp->pr_pri); else /* batch: low prio */ sprintf(prio_str, "b%d", pp->pr_pri); } else { /* copied from Kostadis's code */ if (strcmp(pp->pr_clname, "RT") == 0) /* real time */ sprintf(prio_str, "+%d", pp->pr_pri); else if (strcmp(pp->pr_clname, "DL") == 0) /* unsupported ? */ sprintf(prio_str, "d%d", pp->pr_pri); else if (strcmp(pp->pr_clname, "GN") == 0) sprintf(prio_str, "g%d", pp->pr_pri); else if (strcmp(pp->pr_clname, "GB") == 0) sprintf(prio_str, "p%d", pp->pr_pri); else if (strcmp(pp->pr_clname, "WL") == 0) /* weightless */ return "w"; else if (strcmp(pp->pr_clname, "BC") == 0) return "bc"; /* batch critical */ else if (strcmp(pp->pr_clname, "B") == 0) return "b"; /* batch */ else sprintf(prio_str, "%d", pp->pr_pri); } return prio_str; } static double clip_percent(double pct) { if (pct < 0) { return 0.0; } else if (pct >= 100) { return 99.99; } return pct; } char * format_next_process(caddr_t handle, char *(*get_userid) ()) { struct prpsinfo *pp; struct handle *hp; long cputime; /* find and remember the next proc structure */ hp = (struct handle *) handle; pp = *(hp->next_proc++); hp->remaining--; /* get the process cpu usage since startup */ cputime = pp->pr_time.tv_sec; /* format this entry */ sprintf(fmt, Proc_format, pp->pr_pid, pp->pr_pgrp, (*get_userid) (pp->pr_uid), format_prio(pp), format_k(pagetok(pp->pr_size)), format_k(pagetok(pp->pr_rssize)), format_state(pp), format_time(cputime), clip_percent(weighted_cpu(pp)), clip_percent(percent_cpu(pp)), printable(pp->pr_fname)); /* 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). * */ int getkval(unsigned long offset, int *ptr, int size, char *refstr) { if (lseek(kmem, (long) offset, SEEK_SET) == -1) { if (*refstr == '!') refstr++; (void) fprintf(stderr, "%s: %s: lseek to %s: %s\n", myname, KMEM, refstr, strerror(errno)); exit(0); } if (read(kmem, (char *) ptr, size) == -1) { if (*refstr == '!') return (0); else { (void) fprintf(stderr, "%s: %s: reading %s: %s\n", myname, KMEM, refstr, strerror(errno)); exit(0); } } return (1); } /* * compare_K - comparison functions for "qsort" * Compares the resource consumption of two processes using five * distinct keys. The keys 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, idle, run. * Different comparison functions are used for different orderings. */ /* these are names given to allowed sorting orders -- first is default */ char *ordernames[] = { /* * Aliases for user convenience/friendliness: mem == size rss == res */ "cpu", "size", "mem", "res", "rss", "time", "state", "command", "prio", NULL }; /* forward definitions for comparison functions */ int compare_cpu(struct prpsinfo ** pp1, struct prpsinfo ** pp2); int compare_size(struct prpsinfo ** pp1, struct prpsinfo ** pp2); int compare_res(struct prpsinfo ** pp1, struct prpsinfo ** pp2); int compare_time(struct prpsinfo ** pp1, struct prpsinfo ** pp2); int compare_state(struct prpsinfo ** pp1, struct prpsinfo ** pp2); int compare_cmd(struct prpsinfo ** pp1, struct prpsinfo ** pp2); int compare_prio(struct prpsinfo ** pp1, struct prpsinfo ** pp2); int (*proc_compares[]) () = { compare_cpu, compare_size, compare_size, compare_res, compare_res, compare_time, compare_state, compare_cmd, compare_prio, NULL }; /* * The possible comparison expressions. 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 = proc_state(p2) - proc_state(p1)) == 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) #define ORDERKEY_CMD if ((result = strcmp(p1->pr_fname,p2->pr_fname)) == 0) int compare_cpu(struct prpsinfo ** pp1, struct prpsinfo ** pp2) { struct prpsinfo *p1, *p2; int result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; /* * order by various keys, resorting to the next one whenever there's a tie * in comparisons */ ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_RSSIZE ORDERKEY_MEM ; return (result); } int compare_size(struct prpsinfo ** pp1, struct prpsinfo ** pp2) { struct prpsinfo *p1, *p2; int result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; /* * order by various keys, resorting to the next one whenever there's a tie * in comparisons */ ORDERKEY_MEM ORDERKEY_RSSIZE ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ; return (result); } int compare_res(struct prpsinfo ** pp1, struct prpsinfo ** pp2) { struct prpsinfo *p1, *p2; int result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; /* * order by various keys, resorting to the next one whenever there's a tie * in comparisons */ ORDERKEY_RSSIZE ORDERKEY_MEM ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ; return (result); } int compare_time(struct prpsinfo ** pp1, struct prpsinfo ** pp2) { struct prpsinfo *p1, *p2; int result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; /* * order by various keys, resorting to the next one whenever there's a tie * in comparisons */ ORDERKEY_CPTICKS ORDERKEY_RSSIZE ORDERKEY_MEM ORDERKEY_PCTCPU ORDERKEY_STATE ORDERKEY_PRIO ; return (result); } int compare_cmd(struct prpsinfo ** pp1, struct prpsinfo ** pp2) { struct prpsinfo *p1, *p2; int result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; /* * order by various keys, resorting to the next one whenever there's a tie * in comparisons */ ORDERKEY_CMD ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_RSSIZE ; return (result); } int compare_state(struct prpsinfo ** pp1, struct prpsinfo ** pp2) { struct prpsinfo *p1, *p2; int result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; /* * order by various keys, resorting to the next one whenever there's a tie * in comparisons */ ORDERKEY_STATE ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_RSSIZE ; return (result); } int compare_prio(struct prpsinfo ** pp1, struct prpsinfo ** pp2) { struct prpsinfo *p1, *p2; int result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; /* * order by various keys, resorting to the next one whenever there's a tie * in comparisons */ ORDERKEY_PRIO ORDERKEY_PCTCPU ; return (result); } /* return the owner of the specified process. */ uid_t proc_owner(pid_t pid) { register struct prpsinfo *p; int i; for (i = 0, p = pbase; i < nproc; i++, p++) if (p->pr_pid == pid) return (p->pr_uid); return (-1); } #ifdef DO_MAPSIZE static void size(int fd, struct prpsinfo * ps) { prmap_sgi_arg_t maparg; struct prmap_sgi maps[256]; int nmaps; double sz; int i; maparg.pr_vaddr = (caddr_t) maps; maparg.pr_size = sizeof maps; if ((nmaps = ioctl(fd, PIOCMAP_SGI, &maparg)) == -1) { /* XXX - this will be confusing */ return; } for (i = 0, sz = 0; i < nmaps; ++i) { sz += (double) maps[i].pr_wsize / MA_WSIZE_FRAC; } ps->pr_rssize = (long) sz; } #endif /* get process table */ void getptable(struct prpsinfo * baseptr) { struct prpsinfo *currproc; /* ptr to current proc struct */ int i, numprocs; struct dirent *direntp; struct oldproc *op, *endbase; static struct timeval lasttime, thistime; static double timediff, alpha, beta; /* measure time between last call to getptable and current call */ 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. */ timediff = ((double) thistime.tv_sec * 1.0e7 - (double) lasttime.tv_sec * 1.0e7) + ((double) thistime.tv_usec * 10 - (double) lasttime.tv_usec * 10); /* * Under extreme load conditions, sca has experienced an assert(timediff > * 0) failure here. His guess is that sometimes timed resets the time * backwards and gettimeofday returns a lower number on a later call. To * be on the safe side I fix it here by setting timediff to some arbitrary * small value (in nanoseconds). */ if (timediff <= 0.0) timediff = 100.0; lasttime = thistime; /* prepare for next round */ /* * constants for exponential decaying 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 / 15.0e7); beta = 1.0 - alpha; } else { alpha = 0.5; beta = 0.5; } assert(alpha >= 0); assert(alpha <= 1); assert(beta >= 0); assert(beta <= 1); endbase = oldbase + oldprocs; currproc = baseptr; for (numprocs = 0, rewinddir(procdir); direntp = readdir(procdir);) { 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; } /* * 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. */ op = oldbase + HASH(currproc->pr_pid); for (;;) { if (op->oldpid == -1) /* not there */ break; if (op->oldpid == currproc->pr_pid) { /* found old data */ percent_cpu(currproc) = ((currproc->pr_time.tv_sec * 1.0e9 + currproc->pr_time.tv_nsec) - op->oldtime) / timediff; weighted_cpu(currproc) = op->oldpct * beta + percent_cpu(currproc) * alpha; break; } op++; /* try next entry in hash table */ if (op == endbase) /* table wrap around */ op = oldbase; } /* Otherwise, it's new, so use all of its CPU time */ if (op->oldpid == -1) { 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; } } #ifdef DO_MAPSIZE size(fd, currproc); #endif numprocs++; (void) close(fd); /* * Bug: in case process count grew so dramatically as to exceed to * table size. We give up on a full scan. the chances of this to * happen are extremely slim due to the big factor we're using. * getting nproc from nlist is not worth the headache. realloc * wouldn't work either because we have pointers to the proc table so * we cannot move it around. */ if (numprocs >= ptable_size) { fprintf(stderr, "preallocated proc table size (%d) exceeded, " "skipping some processes\n", ptable_size); break; } } 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++) { /* find an empty spot */ op = oldbase + HASH(currproc->pr_pid); for (;;) { 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); } } void get_io_info(struct io_info *io_info) { /* Not supported yet */ memset(io_info, 0, sizeof(*io_info)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_linux.c000066400000000000000000001116461271046472400154520ustar00rootroot00000000000000/* * 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 * Order support added by Alexey Klimkin * Ported to 2.4 by William LeFebvre * * Portions Copyright (c) 2013 VMware, Inc. All Rights Reserved. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for HZ */ #if 0 #include /* 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 { pid_t pid; /* Data from /proc//stat. */ uid_t uid; char *name; int pri, nice; unsigned long size, rss; /* in k */ int state; unsigned long time; unsigned long start_time; double pcpu, wcpu; /* Data from /proc//io. */ 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; struct top_proc *next; }; struct io_node { pid_t pid; /* The change in the previous values and current values. */ long long diff_rchar; long long diff_wchar; long long diff_syscr; long long diff_syscw; long long diff_read_bytes; long long diff_write_bytes; long long diff_cancelled_write_bytes; /* The previous values. */ long long old_rchar; long long old_wchar; long long old_syscr; long long old_syscw; long long old_read_bytes; long long old_write_bytes; long long old_cancelled_write_bytes; struct io_node *next; }; /*=STATE IDENT STRINGS==================================================*/ #define NPROCSTATES 7 static char *state_abbrev[NPROCSTATES + 1] = { "", "run", "sleep", "disk", "zomb", "stop", "swap", NULL }; static char *procstatenames[NPROCSTATES + 1] = { "", " running, ", " sleeping, ", " uninterruptable, ", " zombie, ", " stopped, ", " swapping, ", NULL }; #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 }; #define SWAPUSED 0 #define SWAPFREE 1 #define SWAPCACHED 2 #define NSWAPSTATS 3 static char *swapnames[NSWAPSTATS + 1] = { "K used, ", "K free, ", "K cached", NULL }; static char fmt_header[] = " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND"; /* these are names given to allowed sorting orders -- first is default */ static char *ordernames[] = {"cpu", "size", "res", "time", "command", NULL}; static char *ordernames_io[] = { "pid", "rchar", "wchar", "syscr", "syscw", "reads", "writes", "cwrites", "command", NULL }; /* forward definitions for comparison functions */ int compare_cpu(); int compare_size(); int compare_res(); int compare_time(); int compare_cmd(); int compare_pid(); int compare_rchar(); int compare_wchar(); int compare_syscr(); int compare_syscw(); int compare_reads(); int compare_writes(); int compare_cwrites(); int (*proc_compares[]) () = { compare_cpu, compare_size, compare_res, compare_time, compare_cmd, NULL }; int (*io_compares[]) () = { compare_pid, compare_rchar, compare_wchar, compare_syscr, compare_syscw, compare_reads, compare_writes, compare_cwrites, compare_cmd, 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 HASH_SIZE (1003) #define INITIAL_ACTIVE_SIZE (256) #define PROCBLOCK_SIZE (32) static struct top_proc *ptable[HASH_SIZE]; static struct top_proc **pactive; static struct top_proc **nextactive; static unsigned int activesize = 0; 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) #define HASH(x) (((x) * 1686629713U) % HASH_SIZE) /*======================================================================*/ struct io_node * new_io_node(pid_t); struct io_node * get_io_stats(struct io_node *, pid_t); struct io_node * insert_io_stats(struct io_node *, struct io_node *); void update_io_stats(struct io_node *, pid_t, long long, long long, long long, long long, long long, long long, long long); struct io_node * upsert_io_stats(struct io_node *, pid_t, long long, long long, long long, long long, long long, long long, long long); 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; } static void xfrm_cmdline(char *p, int len) { while (--len > 0) { if (*p == '\0') { *p = ' '; } p++; } } static void update_procname(struct top_proc * proc, char *cmd) { printable(cmd); if (proc->name == NULL) { proc->name = strdup(cmd); } else if (strcmp(proc->name, cmd) != 0) { free(proc->name); proc->name = strdup(cmd); } } /* * Process structures are allocated and freed as needed. Here we * keep big pools of them, adding more pool as needed. When a * top_proc structure is freed, it is added to a freelist and reused. */ static struct top_proc *freelist = NULL; static struct top_proc *procblock = NULL; static struct top_proc *procmax = NULL; static struct top_proc * new_proc() { struct top_proc *p; if (freelist) { p = freelist; freelist = freelist->next; } else if (procblock) { p = procblock; if (++procblock >= procmax) { procblock = NULL; } } else { p = procblock = (struct top_proc *) calloc(PROCBLOCK_SIZE, sizeof(struct top_proc)); procmax = procblock++ + PROCBLOCK_SIZE; } /* initialization */ if (p->name != NULL) { free(p->name); p->name = NULL; } return p; } static void free_proc(struct top_proc * proc) { proc->next = freelist; freelist = proc; } 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; 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->order_names_io = ordernames_io; statics->boottime = boottime; statics->flags.fullcmds = 1; statics->flags.warmup = 1; /* allocate needed space */ pactive = (struct top_proc **) malloc(sizeof(struct top_proc *) * INITIAL_ACTIVE_SIZE); activesize = INITIAL_ACTIVE_SIZE; /* make sure the hash table is empty */ memset(ptable, 0, HASH_SIZE * sizeof(struct top_proc *)); /* 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); } /* set arrays and strings */ info->cpustates = cpu_states; info->memory = memory_stats; info->swap = swap_stats; } static void read_one_proc_stat(pid_t pid, struct top_proc * proc, struct process_select * sel) { char buffer[4096], *p, *q; int fd, len; int fullcmd; char value[BUFFERLEN + 1]; /* 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", 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_procname(proc, buffer); } else { fullcmd = 0; } close(fd); } else { fullcmd = 0; } } /* grab the proc stat info in one go */ sprintf(buffer, "%d/stat", pid); fd = open(buffer, O_RDONLY); len = read(fd, buffer, sizeof(buffer) - 1); close(fd); buffer[len] = '\0'; proc->uid = (uid_t) proc_owner((int) pid); /* 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_procname(proc, p); } /* 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 */ proc->pri = strtol(p, &p, 10); /* priority */ proc->nice = strtol(p, &p, 10); /* 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", 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. Report 0's. */ proc->rchar = 0; proc->wchar = 0; proc->syscr = 0; proc->syscw = 0; proc->read_bytes = 0; proc->write_bytes = 0; proc->cancelled_write_bytes = 0; return; } len = read(fd, buffer, sizeof(buffer) - 1); close(fd); buffer[len] = '\0'; p = buffer; GET_VALUE(proc->rchar); GET_VALUE(proc->wchar); GET_VALUE(proc->syscr); GET_VALUE(proc->syscw); GET_VALUE(proc->read_bytes); GET_VALUE(proc->write_bytes); GET_VALUE(proc->cancelled_write_bytes); } caddr_t get_process_info(struct system_info * si, struct process_select * sel, int compare_index, char *conninfo, int mode) { struct timeval thistime; double timediff, alpha, beta; struct top_proc *proc; pid_t pid; unsigned long now; unsigned long elapsed; 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; /* round current time to a second */ now = (unsigned long) thistime.tv_sec; if (thistime.tv_usec >= 500000) { now++; } /* calculate constants for the exponental average */ if (timediff > 0.0 && timediff < 30.0) { alpha = 0.5 * (timediff / 30.0); beta = 1.0 - alpha; } else { alpha = beta = 0.5; } timediff *= HZ; /* convert to ticks */ /* mark all hash table entries as not seen */ for (i = 0; i < HASH_SIZE; ++i) { for (proc = ptable[i]; proc; proc = proc->next) { proc->state = 0; } } /* read the process information */ { int total_procs = 0; struct top_proc **active; int show_idle = sel->idle; int show_uid = sel->uid != -1; int i; int rows; PGconn *pgconn; PGresult *pgresult = NULL; memset(process_states, 0, sizeof(process_states)); pgconn = connect_to_db(conninfo); if (pgconn != NULL) { pgresult = pg_processes(pgconn); rows = PQntuples(pgresult); } else { rows = 0; } for (i = 0; i < rows; i++) { char *procpid = PQgetvalue(pgresult, i, 0); struct top_proc *pp; unsigned long otime; pid = atoi(procpid); /* look up hash table entry */ proc = pp = ptable[HASH(pid)]; while (proc && proc->pid != pid) { proc = proc->next; } /* if we came up empty, create a new entry */ if (proc == NULL) { proc = new_proc(); proc->pid = pid; proc->next = pp; ptable[HASH(pid)] = proc; proc->time = 0; proc->wcpu = 0; } otime = proc->time; read_one_proc_stat(pid, proc, sel); if (sel->fullcmd == 2) update_procname(proc, PQgetvalue(pgresult, i, 1)); if (proc->state == 0) continue; total_procs++; process_states[proc->state]++; if (timediff > 0.0) { if ((proc->pcpu = (proc->time - otime) / timediff) < 0.0001) { proc->pcpu = 0; } proc->wcpu = proc->pcpu * alpha + proc->wcpu * beta; } else if ((elapsed = (now - boottime) * HZ - proc->start_time) > 0) { /* * What's with the noop statement? if ((proc->pcpu = * (double)proc->time / (double)elapsed) < 0.0001) { * proc->pcpu; } */ proc->wcpu = proc->pcpu; } else { proc->wcpu = proc->pcpu = 0.0; } } if (pgresult != NULL) PQclear(pgresult); PQfinish(pgconn); /* make sure we have enough slots for the active procs */ if (activesize < total_procs) { pactive = (struct top_proc **) realloc(pactive, sizeof(struct top_proc *) * total_procs); activesize = total_procs; } /* set up the active procs and flush dead entries */ active = pactive; for (i = 0; i < HASH_SIZE; i++) { struct top_proc *last; struct top_proc *ptmp; last = NULL; proc = ptable[i]; while (proc != NULL) { if (proc->state == 0) { ptmp = proc; if (last) { proc = last->next = proc->next; } else { proc = ptable[i] = proc->next; } free_proc(ptmp); } else { if ((show_idle || proc->state == 1 || proc->pcpu) && (!show_uid || proc->uid == sel->uid)) { *active++ = proc; last = proc; } proc = proc->next; } } } si->p_active = active - pactive; si->p_total = total_procs; si->procstates = process_states; } /* if requested, sort the "active" procs */ if (si->p_active) { if (mode == MODE_IO_STATS) { qsort(pactive, si->p_active, sizeof(struct top_proc *), io_compares[compare_index]); } else { qsort(pactive, si->p_active, sizeof(struct top_proc *), proc_compares[compare_index]); } } /* don't even pretend that the return value thing here isn't bogus */ nextactive = pactive; 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, char *(*get_userid) (uid_t)) { static char fmt[MAX_COLS]; /* static area where result is built */ static struct io_node *head = NULL; struct top_proc *p = *nextactive++; struct io_node *node = NULL; head = upsert_io_stats(head, p->pid, p->rchar, p->wchar, p->syscr, p->syscw, p->read_bytes, p->write_bytes, p->cancelled_write_bytes); if (mode_stats == STATS_DIFF) { node = get_io_stats(head, p->pid); if (node == NULL) { snprintf(fmt, sizeof(fmt), "%5d %5d %5d %7d %7d %5d %6d %7d %s", p->pid, 0, 0, 0, 0, 0, 0, 0, p->name); } else { snprintf(fmt, sizeof(fmt), "%5d %5s %5s %7lld %7lld %5s %6s %7s %s", p->pid, format_b(node->diff_rchar), format_b(node->diff_wchar), node->diff_syscr, node->diff_syscw, format_b(node->diff_read_bytes), format_b(node->diff_write_bytes), format_b(node->diff_cancelled_write_bytes), p->name); } } else { snprintf(fmt, sizeof(fmt), "%5d %5s %5s %7lld %7lld %5s %6s %7s %s", p->pid, format_b(p->rchar), format_b(p->wchar), p->syscr, p->syscw, format_b(p->read_bytes), format_b(p->write_bytes), format_b(p->cancelled_write_bytes), p->name); } return (fmt); } char * format_next_process(caddr_t handle, char *(*get_userid) (uid_t)) { static char fmt[MAX_COLS]; /* static area where result is built */ struct top_proc *p = *nextactive++; snprintf(fmt, sizeof(fmt), "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %s", p->pid, (*get_userid) (p->uid), p->pri < -99 ? -99 : p->pri, p->nice, format_k(p->size), format_k(p->rss), state_abbrev[p->state], format_time(p->time / HZ), p->wcpu * 100.0, p->pcpu * 100.0, p->name); /* 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 (dresult = p2->pcpu - p1->pcpu,\ (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0) #define ORDERKEY_CPTICKS if ((result = (long)p2->time - (long)p1->time) == 0) #define ORDERKEY_STATE if ((result = (sort_state[p2->state] - \ sort_state[p1->state])) == 0) #define ORDERKEY_PRIO if ((result = p2->pri - p1->pri) == 0) #define ORDERKEY_RSSIZE if ((result = p2->rss - p1->rss) == 0) #define ORDERKEY_MEM if ((result = p2->size - p1->size) == 0) #define ORDERKEY_NAME if ((result = strcmp(p1->name, p2->name)) == 0) #define ORDERKEY_PID if ((result = p1->pid - p2->pid) == 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) /* Now the array that maps process state to a weight */ unsigned char sort_state[] = { 0, /* empty */ 6, /* run */ 3, /* sleep */ 5, /* disk wait */ 1, /* zombie */ 2, /* stop */ 4 /* swap */ }; /* compare_cpu - the comparison function for sorting by cpu percentage */ int compare_cpu( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *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 == 0 ? 0 : result < 0 ? -1 : 1; } /* compare_size - the comparison function for sorting by total memory usage */ int compare_size( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *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 == 0 ? 0 : result < 0 ? -1 : 1; } /* compare_res - the comparison function for sorting by resident set size */ int compare_res( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *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 == 0 ? 0 : result < 0 ? -1 : 1; } /* compare_time - the comparison function for sorting by total cpu time */ int compare_time( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *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 == 0 ? 0 : result < 0 ? -1 : 1; } /* compare_cmd - the comparison function for sorting by command name */ int compare_cmd( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_NAME ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_RSSIZE ORDERKEY_MEM ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_pid(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_WCHAR ORDERKEY_SYSCR ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_rchar(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_RCHAR ORDERKEY_PID ORDERKEY_WCHAR ORDERKEY_SYSCR ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_wchar(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_WCHAR ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_SYSCR ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_syscr(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_SYSCR ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_WCHAR ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_syscw(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_SYSCW ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_WCHAR ORDERKEY_SYSCR ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_reads(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_READS ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_WCHAR ORDERKEY_SYSCR ORDERKEY_SYSCW ORDERKEY_WRITES ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_writes(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_WRITES ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_WCHAR ORDERKEY_SYSCR ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_CWRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } int compare_cwrites(struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_CWRITES ORDERKEY_PID ORDERKEY_RCHAR ORDERKEY_WCHAR ORDERKEY_SYSCR ORDERKEY_SYSCW ORDERKEY_READS ORDERKEY_WRITES ORDERKEY_NAME ; return result == 0 ? 0 : result < 0 ? -1 : 1; } /* * 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) { struct stat sb; char buffer[32]; sprintf(buffer, "%d", pid); if (stat(buffer, &sb) < 0) return -1; else return (int) sb.st_uid; } struct io_node * new_io_node(pid_t pid) { struct io_node *node; node = (struct io_node *) malloc(sizeof(struct io_node)); bzero(node, sizeof(struct io_node)); node->pid = pid; node->next = NULL; return node; } struct io_node * get_io_stats(struct io_node *head, pid_t pid) { struct io_node *node = head; while (node != NULL) { if (node->pid == pid) { return node; } node = node->next; } return node; } struct io_node * insert_io_stats(struct io_node *head, struct io_node *node) { struct io_node *c = head; struct io_node *p = NULL; if (node->pid < head->pid) { node->next = head; head = node; return head; } c = head->next; p = head; while (c != NULL) { if (node->pid < c->pid) { node->next = c; p->next = node; return head; } p = c; c = c->next; } if (c == NULL) { p->next = node; } return head; } void update_io_stats(struct io_node *node, pid_t pid, 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) { /* Calculate difference between previous and current values. */ node->diff_rchar = rchar - node->old_rchar; node->diff_wchar = wchar - node->old_wchar; node->diff_syscr = syscr - node->old_syscr; node->diff_syscw = syscw - node->old_syscw; node->diff_read_bytes = read_bytes - node->old_read_bytes; node->diff_write_bytes = write_bytes - node->old_write_bytes; node->diff_cancelled_write_bytes = cancelled_write_bytes - node->old_cancelled_write_bytes; /* Save the current values as previous values. */ node->old_rchar = rchar; node->old_wchar = wchar; node->old_syscr = syscr; node->old_syscw = syscw; node->old_read_bytes = read_bytes; node->old_write_bytes = write_bytes; node->old_cancelled_write_bytes = cancelled_write_bytes; } struct io_node * upsert_io_stats(struct io_node *head, pid_t pid, 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) { struct io_node *c = head; /* List is empty, create a new node. */ if (head == NULL) { head = new_io_node(pid); update_io_stats(head, pid, rchar, wchar, syscr, syscw, read_bytes, write_bytes, cancelled_write_bytes); return head; } /* Check if this pid exists already. */ while (c != NULL) { if (c->pid == pid) { /* Found an existing node with same pid, update it. */ update_io_stats(c, pid, rchar, wchar, syscr, syscw, read_bytes, write_bytes, cancelled_write_bytes); return head; } c = c->next; } /* Didn't find pig. Create a new node, save the data and insert it. */ c = new_io_node(pid); update_io_stats(c, pid, rchar, wchar, syscr, syscw, read_bytes, write_bytes, cancelled_write_bytes); head = insert_io_stats(head, c); return head; } /* * 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; } /* * Get disk space information for the disk that contains the data * directory. */ void get_disk_info(struct disk_info *disk_info, char *data_directory) { struct statfs buf; /* If no data directory found, return nothing */ if (!data_directory) { memset(disk_info, 0, sizeof(*disk_info)); return; } /* Get statistics */ if (statfs(data_directory, &buf) == 0) { disk_info->size = (int64_t)buf.f_bsize * (int64_t)buf.f_blocks; disk_info->avail = (int64_t)buf.f_bsize * (int64_t)buf.f_bavail; } } pgtop/machine/m_linux.man000066400000000000000000000005431271046472400157740ustar00rootroot00000000000000.SH "LINUX NOTES" The Linux port was written by Richard Henderson . 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 Made to work under 2.4 by William LeFebvre. pgtop/machine/m_linuxthr.c000066400000000000000000000571001271046472400161620ustar00rootroot00000000000000/* * pg_top - a top PostgreSQL users display for Unix * * SYNOPSIS: 2.x with thread eliding * * DESCRIPTION: * This is the machine-dependent module for Linux 2.x that elides threads * from the output. * * CFLAGS: -DHAVE_GETOPT -DHAVE_STRERROR -DORDER * * TERMCAP: -lcurses * * AUTHOR: Richard Henderson * Order support added by Alexey Klimkin * Ported to 2.4 by William LeFebvre * Thread eliding by William LeFebvre */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* for HZ */ #include /* for PAGE_SHIFT */ #if 0 #include /* for PROC_SUPER_MAGIC */ #else #define PROC_SUPER_MAGIC 0x9fa0 #endif #include "pg_top.h" #include "machine.h" #include "utils.h" #define PROCFS "/proc" extern char *myname; /*=PROCESS INFORMATION==================================================*/ struct top_proc { pid_t pid; pid_t ppid; uid_t uid; char *name; int pri, nice; unsigned long size, rss; /* in k */ int state; unsigned long time; unsigned long start_time; unsigned long otime; unsigned long start_code; unsigned long end_code; unsigned long start_stack; unsigned int threads; double pcpu, wcpu; struct top_proc *next; }; /*=STATE IDENT STRINGS==================================================*/ #define NPROCSTATES 7 static char *state_abbrev[NPROCSTATES + 1] = { "", "run", "sleep", "disk", "zomb", "stop", "swap", NULL }; static char *procstatenames[NPROCSTATES + 1] = { "", " running, ", " sleeping, ", " uninterruptable, ", " zombie, ", " stopped, ", " swapping, ", NULL }; #define NCPUSTATES 4 static char *cpustatenames[NCPUSTATES + 1] = { "user", "nice", "system", "idle", NULL }; #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 }; #define SWAPUSED 0 #define SWAPFREE 1 #define SWAPCACHED 2 #define NSWAPSTATS 3 static char *swapnames[NSWAPSTATS + 1] = { "K used, ", "K free, ", "K cached", NULL }; static char fmt_header[] = " PID X THR PRI NICE SIZE RES STATE TIME CPU COMMAND"; /* these are names given to allowed sorting orders -- first is default */ char *ordernames[] = { "cpu", "size", "res", "time", "command", NULL }; /* forward definitions for comparison functions */ int compare_cpu(); int compare_size(); int compare_res(); int compare_time(); int compare_cmd(); int (*proc_compares[]) () = { compare_cpu, compare_size, compare_res, compare_time, compare_cmd, NULL }; /*=SYSTEM STATE INFO====================================================*/ /* these are for calculating cpu state percentages */ static long cp_time[NCPUSTATES]; static long cp_old[NCPUSTATES]; static long cp_diff[NCPUSTATES]; /* for calculating the exponential average */ static struct timeval lasttime; /* these are for keeping track of processes */ #define HASH_SIZE (1003) #define INITIAL_ACTIVE_SIZE (256) #define PROCBLOCK_SIZE (32) static struct top_proc *ptable[HASH_SIZE]; static struct top_proc **pactive; static struct top_proc **nextactive; static unsigned int activesize = 0; static time_t boottime = -1; /* these are for passing data back to the machine independant portion */ static int 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) << (PAGE_SHIFT - 10)) #define HASH(x) (((x) * 1686629713U) % HASH_SIZE) /*======================================================================*/ 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; } static void xfrm_cmdline(char *p, int len) { while (--len > 0) { if (*p == '\0') { *p = ' '; } p++; } } static void update_procname(struct top_proc * proc, char *cmd) { printable(cmd); if (proc->name == NULL) { proc->name = strdup(cmd); } else if (strcmp(proc->name, cmd) != 0) { free(proc->name); proc->name = strdup(cmd); } } /* * Process structures are allocated and freed as needed. Here we * keep big pools of them, adding more pool as needed. When a * top_proc structure is freed, it is added to a freelist and reused. */ static struct top_proc *freelist = NULL; static struct top_proc *procblock = NULL; static struct top_proc *procmax = NULL; static struct top_proc * new_proc() { struct top_proc *p; if (freelist) { p = freelist; freelist = freelist->next; } else if (procblock) { p = procblock; if (++procblock >= procmax) { procblock = NULL; } } else { p = procblock = (struct top_proc *) calloc(PROCBLOCK_SIZE, sizeof(struct top_proc)); procmax = procblock++ + PROCBLOCK_SIZE; } /* initialization */ if (p->name != NULL) { free(p->name); p->name = NULL; } return p; } static void free_proc(struct top_proc * proc) { proc->next = freelist; freelist = proc; } 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); /* get a boottime */ { int fd; char buff[64]; char *p; unsigned long uptime; struct timeval tv; 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); } } /* 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; /* allocate needed space */ pactive = (struct top_proc **) malloc(sizeof(struct top_proc *) * INITIAL_ACTIVE_SIZE); activesize = INITIAL_ACTIVE_SIZE; /* make sure the hash table is empty */ memset(ptable, 0, HASH_SIZE * sizeof(struct top_proc *)); /* 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); /* convert cp_time counts to percentages */ percentages(4, 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); } /* set arrays and strings */ info->cpustates = cpu_states; info->memory = memory_stats; info->swap = swap_stats; } static void read_one_proc_stat(pid_t pid, struct top_proc * proc, struct process_select * sel) { char buffer[4096], *p, *q; int fd, len; int fullcmd; /* if anything goes wrong, we return with proc->state == 0 */ proc->state = 0; /* full cmd handling */ fullcmd = sel->fullcmd; if (fullcmd) { sprintf(buffer, "%d/cmdline", 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_procname(proc, buffer); } else { fullcmd = 0; } close(fd); } else { fullcmd = 0; } } /* grab the proc stat info in one go */ sprintf(buffer, "%d/stat", pid); fd = open(buffer, O_RDONLY); len = read(fd, buffer, sizeof(buffer) - 1); close(fd); buffer[len] = '\0'; proc->uid = (uid_t) proc_owner((int) pid); /* parse out the status */ /* 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_procname(proc, p); } /* 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; } proc->ppid = strtoul(p, &p, 10); /* ppid */ p = skip_token(p); /* skip pgrp */ p = skip_token(p); /* skip session */ p = skip_token(p); /* skip tty */ 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 */ proc->pri = strtol(p, &p, 10); /* priority */ proc->nice = strtol(p, &p, 10); /* nice */ p = skip_token(p); /* skip timeout */ p = skip_token(p); /* skip it_real_val */ 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 */ p = skip_token(p); /* skip rlim */ proc->start_code = strtoul(p, &p, 10); /* start_code */ proc->end_code = strtoul(p, &p, 10); /* end_code */ proc->start_stack = strtoul(p, &p, 10); /* start_stack */ /* for the record, here are the rest of the fields */ #if 0 p = skip_token(p); /* skip sp */ p = skip_token(p); /* skip pc */ 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 */ #endif } caddr_t get_process_info(struct system_info * si, struct process_select * sel, int compare_index) { struct timeval thistime; double timediff, alpha, beta; struct top_proc *proc; pid_t pid; unsigned long now; unsigned long elapsed; 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; /* round current time to a second */ now = (unsigned long) thistime.tv_sec; if (thistime.tv_usec >= 500000) { now++; } /* calculate constants for the exponental average */ if (timediff > 0.0 && timediff < 30.0) { alpha = 0.5 * (timediff / 30.0); beta = 1.0 - alpha; } else alpha = beta = 0.5; timediff *= HZ; /* convert to ticks */ /* mark all hash table entries as not seen */ for (i = 0; i < HASH_SIZE; ++i) { for (proc = ptable[i]; proc; proc = proc->next) { proc->state = 0; } } /* read the process information */ { DIR *dir = opendir("."); struct dirent *ent; int total_procs = 0; struct top_proc **active; int show_idle = sel->idle; int show_uid = sel->uid != -1; memset(process_states, 0, sizeof(process_states)); while ((ent = readdir(dir)) != NULL) { struct top_proc *pp; if (!isdigit(ent->d_name[0])) continue; pid = atoi(ent->d_name); /* look up hash table entry */ proc = pp = ptable[HASH(pid)]; while (proc && proc->pid != pid) { proc = proc->next; } /* if we came up empty, create a new entry */ if (proc == NULL) { proc = new_proc(); proc->pid = pid; proc->next = pp; ptable[HASH(pid)] = proc; proc->time = proc->otime = 0; } read_one_proc_stat(pid, proc, sel); proc->threads = 1; if (proc->state == 0) continue; total_procs++; process_states[proc->state]++; /* calculate cpu percentage */ if (timediff > 0.0) { if ((proc->pcpu = (proc->time - proc->otime) / timediff) < 0.0001) { proc->pcpu = 0; } } else if ((elapsed = (now - boottime) * HZ - proc->start_time) > 0) { if ((proc->pcpu = (double) proc->time / (double) elapsed) < 0.0001) { proc->pcpu; } } else { proc->pcpu = 0.0; } /* remember time for next time */ proc->otime = proc->time; } closedir(dir); /* make sure we have enough slots for the active procs */ if (activesize < total_procs) { pactive = (struct top_proc **) realloc(pactive, sizeof(struct top_proc *) * total_procs); activesize = total_procs; } /* set up the active procs and flush dead entries */ /* also coalesce threads */ active = pactive; for (i = 0; i < HASH_SIZE; i++) { struct top_proc *last; struct top_proc *ptmp; struct top_proc *parent; last = NULL; proc = ptable[i]; while (proc != NULL) { if (proc->state == 0) { ptmp = proc; if (last) { proc = last->next = proc->next; } else { proc = ptable[i] = proc->next; } free_proc(ptmp); } else { /* look up hash table entry for parent */ parent = proc; do { pid = parent->ppid; parent = ptable[HASH(pid)]; while (parent && parent->pid != pid) { parent = parent->next; } } while (parent && parent->state == 0); /* does this look like a thread of its parent? */ if (parent && proc->size == parent->size && proc->rss == parent->rss && proc->start_code == parent->start_code && proc->end_code == parent->end_code && proc->start_stack == parent->start_stack) { /* yes it does: roll up the cumulative numbers */ parent->threads += proc->threads; parent->time += proc->time; parent->pcpu += proc->pcpu; /* mark this process as dead (undisplayable) */ proc->state = 0; } else if ((show_idle || proc->state == 1 || proc->pcpu) && (!show_uid || proc->uid == sel->uid)) { *active++ = proc; last = proc; } proc = proc->next; } } } si->p_active = active - pactive; si->p_total = total_procs; si->procstates = process_states; } /* if requested, sort the "active" procs */ if (si->p_active) qsort(pactive, si->p_active, sizeof(struct top_proc *), proc_compares[compare_index]); /* don't even pretend that the return value thing here isn't bogus */ nextactive = pactive; 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_process(caddr_t handle, char *(*get_userid) (int)) { static char fmt[MAX_COLS]; /* static area where result is built */ struct top_proc *p = *nextactive++; snprintf(fmt, sizeof(fmt), "%5d %-8.8s %3d %3d %4d %5s %5s %-5s %6s %5.2f%% %s", p->pid, (*get_userid) (p->uid), p->threads, p->pri < -99 ? -99 : p->pri, p->nice, format_k(p->size), format_k(p->rss), state_abbrev[p->state], format_time(p->time / HZ), p->pcpu * 100.0, p->name); /* 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 (dresult = p2->pcpu - p1->pcpu,\ (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0) #define ORDERKEY_CPTICKS if ((result = (long)p2->time - (long)p1->time) == 0) #define ORDERKEY_STATE if ((result = (sort_state[p2->state] - \ sort_state[p1->state])) == 0) #define ORDERKEY_PRIO if ((result = p2->pri - p1->pri) == 0) #define ORDERKEY_RSSIZE if ((result = p2->rss - p1->rss) == 0) #define ORDERKEY_MEM if ((result = p2->size - p1->size) == 0) #define ORDERKEY_NAME if ((result = strcmp(p1->name, p2->name)) == 0) /* Now the array that maps process state to a weight */ unsigned char sort_state[] = { 0, /* empty */ 6, /* run */ 3, /* sleep */ 5, /* disk wait */ 1, /* zombie */ 2, /* stop */ 4 /* swap */ }; /* compare_cpu - the comparison function for sorting by cpu percentage */ int compare_cpu( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *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 == 0 ? 0 : result < 0 ? -1 : 1; } /* compare_size - the comparison function for sorting by total memory usage */ int compare_size( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *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 == 0 ? 0 : result < 0 ? -1 : 1; } /* compare_res - the comparison function for sorting by resident set size */ int compare_res( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *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 == 0 ? 0 : result < 0 ? -1 : 1; } /* compare_time - the comparison function for sorting by total cpu time */ int compare_time( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *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 == 0 ? 0 : result < 0 ? -1 : 1; } /* compare_cmd - the comparison function for sorting by command name */ int compare_cmd( struct top_proc ** pp1, struct top_proc ** pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_NAME ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_RSSIZE ORDERKEY_MEM ; return result == 0 ? 0 : result < 0 ? -1 : 1; } /* * 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(int pid) { struct stat sb; char buffer[32]; sprintf(buffer, "%d", pid); if (stat(buffer, &sb) < 0) return -1; else return (int) sb.st_uid; } void get_io_info(struct io_info *io_info) { /* Not supported yet */ memset(io_info, 0, sizeof(*io_info)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_linuxthr.man000066400000000000000000000025761271046472400165220ustar00rootroot00000000000000.SH "LINUX NOTES" The Linux port was written by Richard Henderson . 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 the SunOS 5 port by Alexey Klimkin Made to work under 2.4 by William LeFebvre. This version of the Linux port includes automatic thread "eliding". In Linux, a thread is treated as another process sharing the memory space (as well as file table and other resources). Thus multiple threads appear as separate processes in most system utilities (see .IR clone (2)). This version of pg_top detects child thread processes and does not display them separately. Instead of displaying threads individually, an extra column "THR" shows the number of thread processes for a parent process. The cpu time and percentages are added to the parent. This gives a display much closer to other thread-capable Unix systems. However, threads are still counted as separate processes in the process summary line. A process is considered a thread of its parent if the following values are identical to its parent: address space size, resident set size, code start and end program counters, and stack start. This heuristic can mistake a recently forked child as a thread, until the child has either called exec or allocated space on its own. pgtop/machine/m_macosx.c000066400000000000000000000503461271046472400156040ustar00rootroot00000000000000/* * 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 #include #include #include #include #include #include "os.h" #include "pg_top.h" #include "machine.h" #include "utils.h" /* * MacOS kernel stuff */ #include #include #include #include #include #include #include /* for new sysctl calls */ #include #include #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 */ 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, char *conninfo) { 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(conninfo); 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); } void get_io_info(struct io_info *io_info) { /* Not supported yet */ memset(io_info, 0, sizeof(*io_info)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_macosx.man000066400000000000000000000020501271046472400161220ustar00rootroot00000000000000.SH "MacOS 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 . 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. pgtop/machine/m_netbsd.c000066400000000000000000000502401271046472400155620ustar00rootroot00000000000000/* $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 * Steven Wallace * Wolfram Schneider * Arne Helme * Luke Mewburn * matthew green * Simon Burge * Tomas Svensson * * * $Id: m_netbsd15.c,v 1.16 2002/03/23 01:28:11 thorpej Exp $ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 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)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_netbsd.man000066400000000000000000000003021271046472400161050ustar00rootroot00000000000000.SH "NetBSD 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. pgtop/machine/m_openbsd.c000066400000000000000000000451061271046472400157420ustar00rootroot00000000000000/*- * Copyright (c) 1994 Thorsten Lockert * 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 * Adapted from BSD4.4 by Christos Zoulas * Patch for process wait display by Jarl F. Greipsland * Patch for -DORDER by Kenneth Stailey * Patch for new swapctl(2) by Tobias Weingartner * Adapted for pg_top by Mark Wong */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pg_top.h" #include "display.h" #include "machine.h" #include "utils.h" #include "loadavg.h" static long swapmode(long *, long *); static char *state_abbr(struct kinfo_proc *); 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 */ }; /* 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 WAIT TIME 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 %-8s %-7.7s %6s %5.2f%% %.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[8]; char *procstatenames[] = { "", " starting, ", " running, ", " idle, ", " stopped, ", " zombie, ", " dead, ", " on processor, ", NULL }; /* 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 vmtotal_mib[] = {CTL_VM, VM_METER}; struct loadavg sysload; struct vmtotal vmtotal; 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(vmtotal); if (sysctl(vmtotal_mib, 2, &vmtotal, &size, NULL, 0) < 0) { warn("sysctl failed"); bzero(&vmtotal, sizeof(vmtotal)); } /* convert memory stats to Kbytes */ memory_stats[0] = -1; memory_stats[1] = pagetok(vmtotal.t_arm); memory_stats[2] = pagetok(vmtotal.t_rm); memory_stats[3] = -1; memory_stats[4] = pagetok(vmtotal.t_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, char *conninfo) { int show_idle, show_system, show_threads, show_uid, show_cmd; int total_procs, active_procs; struct kinfo_proc **prefp, *pp; int mib[6]; size_t size; int i; PGconn *pgconn; PGresult *pgresult = NULL; 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; pgconn = connect_to_db(conninfo); if (pgconn != NULL) { pgresult = pg_processes(pgconn); nproc = PQntuples(pgresult); pbase = (struct kinfo_proc *) realloc(pbase, sizeof(struct kinfo_proc *) * nproc); } PQfinish(pgconn); 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_uid = sel->uid != (uid_t)-1; 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. */ --i; --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 && (show_system || (pp->p_flag & P_SYSTEM) == 0) && (show_threads || (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_uid || pp->p_ruid == sel->uid) && (!show_cmd || strstr(pp->p_comm, sel->command))) { *prefp++ = pp; active_procs++; } } } /* 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 * state_abbr(struct kinfo_proc *pp) { static char buf[10]; if (ncpu > 1 && pp->p_cpuid != KI_NOCPU) snprintf(buf, sizeof buf, "%s/%llu", state_abbrev[(unsigned char)pp->p_stat], pp->p_cpuid); else snprintf(buf, sizeof buf, "%s", state_abbrev[(unsigned char)pp->p_stat]); return buf; } 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 *(*get_userid)(uid_t)) { char *p_wait; struct kinfo_proc *pp; struct handle *hp; int cputime; double pct; /* find and remember the next proc structure */ hp = (struct handle *) handle; pp = *(hp->next_proc++); hp->remaining--; 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, (*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)), (pp->p_stat == SSLEEP && pp->p_slptime > maxslp) ? "idle" : state_abbr(pp), p_wait, format_time(cputime), 100.0 * pct, printable(format_comm(pp))); /* 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 * 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; } void get_io_info(struct io_info *io_info) { /* Not supported yet */ memset(io_info, 0, sizeof(*io_info)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_remote.c000066400000000000000000000471521271046472400156060ustar00rootroot00000000000000/* * Copyright (c) 2008-2009, Mark Wong */ #include #include #include #include #include #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 \ "SELECT pid, comm, fullcomm, state, utime, stime, priority, nice,\n" \ " starttime, vsize, rss, uid, username, rchar, wchar,\n" \ " syscr, syscw, reads, writes, cwrites\n" \ "FROM pg_proctab()" #define QUERY_PROCTAB_QUERY \ "SELECT a.pid, comm, query, a.state, utime, stime, priority, nice,\n" \ " starttime, vsize, rss, uid, username, rchar, wchar,\n" \ " syscr, syscw, reads, writes, cwrites\n" \ "FROM pg_proctab() a LEFT OUTER JOIN pg_stat_activity b\n" \ " ON a.pid = b.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_priority, c_nice, c_starttime, c_vsize, c_rss, c_uid, c_username, c_rchar, c_wchar, c_syscr, c_syscw, c_reads, c_writes, c_cwrites }; #define HASH_SIZE (1003) #define HASH(x) (((x) * 1686629713U) % HASH_SIZE) #define bytetok(x) (((x) + 512) >> 10) #define INITIAL_ACTIVE_SIZE (256) #define PROCBLOCK_SIZE (32) #define NCPUSTATES 5 #define NMEMSTATS 5 #define NPROCSTATES 7 #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 { pid_t pid; uid_t uid; char *name; char *username; int pri; int nice; unsigned long size; unsigned long rss; /* in k */ int state; unsigned long time; unsigned long start_time; double pcpu; double wcpu; /* 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; struct top_proc *next; }; static unsigned int activesize = 0; static time_t boottime = -1; static struct top_proc **nextactive; static struct top_proc **pactive; static struct top_proc *freelist = NULL; static struct top_proc *procblock = NULL; static struct top_proc *procmax = NULL; static struct top_proc *ptable[HASH_SIZE]; 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", "time", "command", NULL}; static char *procstatenames[NPROCSTATES + 1] = { "", " running, ", " sleeping, ", " uninterruptable, ", " zombie, ", " stopped, ", " swapping, ", NULL }; static char *state_abbrev[NPROCSTATES + 1] = { "", "run", "sleep", "disk", "zomb", "stop", "swap", NULL }; static char *swapnames[NSWAPSTATS + 1] = { "K used, ", "K free, ", "K cached", NULL }; static char fmt_header[] = " PID X PRI NICE SIZE RES STATE TIME WCPU CPU COMMAND"; /* 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 (dresult = p2->pcpu - p1->pcpu,\ (result = dresult > 0.0 ? 1 : dresult < 0.0 ? -1 : 0) == 0) #define ORDERKEY_CPTICKS if ((result = (long)p2->time - (long)p1->time) == 0) #define ORDERKEY_STATE if ((result = (sort_state_r[p2->state] - \ sort_state_r[p1->state])) == 0) #define ORDERKEY_PRIO if ((result = p2->pri - p1->pri) == 0) #define ORDERKEY_RSSIZE if ((result = p2->rss - p1->rss) == 0) #define ORDERKEY_MEM if ((result = p2->size - p1->size) == 0) #define ORDERKEY_NAME if ((result = strcmp(p1->name, p2->name)) == 0) int check_for_function(PGconn *, char *); int compare_cpu_r(struct top_proc **, struct top_proc **); int compare_size_r(struct top_proc **, struct top_proc **); int compare_res_r(struct top_proc **, struct top_proc **); int compare_time_r(struct top_proc **, struct top_proc **); int compare_cmd_r(struct top_proc **, struct top_proc **); static void free_proc(struct top_proc *); static struct top_proc *new_proc(); 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_time_r, compare_cmd_r, NULL }; /* compare_cpu_r - the comparison function for sorting by cpu percentage */ int compare_cpu_r(struct top_proc **pp1, struct top_proc **pp2) { register struct top_proc *p1; register struct top_proc *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 == 0 ? 0 : result < 0 ? -1 : 1; } /* The comparison function for sorting by total memory usage. */ int compare_size_r(struct top_proc **pp1, struct top_proc **pp2) { register struct top_proc *p1; register struct top_proc *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 == 0 ? 0 : result < 0 ? -1 : 1; } /* The comparison function for sorting by resident set size. */ int compare_res_r(struct top_proc **pp1, struct top_proc **pp2) { register struct top_proc *p1; register struct top_proc *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 == 0 ? 0 : result < 0 ? -1 : 1; } /* The comparison function for sorting by total cpu time. */ int compare_time_r(struct top_proc **pp1, struct top_proc **pp2) { register struct top_proc *p1; register struct top_proc *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 == 0 ? 0 : result < 0 ? -1 : 1; } /* The comparison function for sorting by command name. */ int compare_cmd_r(struct top_proc ** pp1, struct top_proc **pp2) { register struct top_proc *p1; register struct top_proc *p2; register long result; double dresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_NAME ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ORDERKEY_RSSIZE ORDERKEY_MEM ; return result == 0 ? 0 : result < 0 ? -1 : 1; } 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 *p = *nextactive++; if (mode_stats == STATS_DIFF) snprintf(fmt, sizeof(fmt), "%5d %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); else snprintf(fmt, sizeof(fmt), "%5d %5s %5s %7lld %7lld %5s %6s %7s %s", (int) p->pid, format_b(p->rchar), format_b(p->wchar), p->syscr, p->syscw, format_b(p->read_bytes), format_b(p->write_bytes), format_b(p->cancelled_write_bytes), 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 *p = *nextactive++; snprintf(fmt, sizeof(fmt), "%5d %-8.8s %3d %4d %5s %5s %-5s %6s %5.2f%% %5.2f%% %s", (int) p->pid, /* Some OS's need to cast pid_t to int. */ p->username, p->pri < -99 ? -99 : p->pri, p->nice, format_k(p->size), format_k(p->rss), state_abbrev[p->state], format_time(p->time), p->wcpu * 100.0, p->pcpu * 100.0, p->name); return (fmt); } static void free_proc(struct top_proc *proc) { proc->next = freelist; freelist = proc; } void get_system_info_r(struct system_info *info, char *conninfo) { PGconn *pgconn; PGresult *pgresult = NULL; int rows = 0; pgconn = connect_to_db(conninfo); if (pgconn != NULL) { pgresult = PQexec(pgconn, 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 (pgconn != NULL) { pgresult = PQexec(pgconn, 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 (pgconn != NULL) { pgresult = PQexec(pgconn, 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); PQfinish(pgconn); } caddr_t get_process_info_r(struct system_info *si, struct process_select *sel, int compare_index, char *conninfo) { int i; struct top_proc *pp; struct top_proc *proc; struct top_proc **active; pid_t pid; PGconn *pgconn; PGresult *pgresult = NULL; int rows; struct timeval thistime; double timediff; double alpha; double beta; unsigned long now; unsigned long elapsed; int total_procs = 0; int show_idle = sel->idle; int show_uid = sel->uid != -1; 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; /* Round current time to a second. */ now = (unsigned long) thistime.tv_sec; if (thistime.tv_usec >= 500000) now++; /* Calculate constants for the exponental average. */ if (timediff > 0.0 && timediff < 30.0) { alpha = 0.5 * (timediff / 30.0); beta = 1.0 - alpha; } else { alpha = beta = 0.5; } timediff *= HZ; /* Convert to ticks. */ /* Mark all has table entries as not seen. */ for (i = 0; i < HASH_SIZE; ++i) for (proc = ptable[i]; proc; proc = proc->next) proc->state = 0; pgconn = connect_to_db(conninfo); if (pgconn != NULL) { if (sel->fullcmd == 2) { pgresult = PQexec(pgconn, QUERY_PROCTAB_QUERY); } else { pgresult = PQexec(pgconn, QUERY_PROCTAB); } rows = PQntuples(pgresult); } else { rows = 0; } for (i = 0; i < rows; i++) { unsigned long otime; long long value; pid = atoi(PQgetvalue(pgresult, i, c_pid)); /* Look up hash table entry. */ proc = pp = ptable[HASH(pid)]; while (proc && proc->pid != pid) proc = proc->next; /* Create a new entry if not found. */ if (proc == NULL) { proc = new_proc(); proc->pid = pid; proc->next = pp; ptable[HASH(pid)] = proc; proc->time = 0; proc->wcpu = 0; /* Never mark as owner because we are remote. */ proc->uid = -1; } otime = proc->time; if (sel->fullcmd && PQgetvalue(pgresult, i, c_fullcomm)) proc->name = strdup(PQgetvalue(pgresult, i, c_fullcomm)); else proc->name = strdup(PQgetvalue(pgresult, i, c_comm)); switch (PQgetvalue(pgresult, i, c_state)[0]) { 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': continue; } proc->time = (unsigned long) atol(PQgetvalue(pgresult, i, c_utime)); proc->time += (unsigned long) atol(PQgetvalue(pgresult, i, c_stime)); proc->pri = atol(PQgetvalue(pgresult, i, c_priority)); proc->nice = atol(PQgetvalue(pgresult, i, c_nice)); proc->start_time = (unsigned long) atol(PQgetvalue(pgresult, i, c_starttime)); proc->size = bytetok((unsigned long) atol(PQgetvalue(pgresult, i, c_vsize))); proc->rss = bytetok((unsigned long) atol(PQgetvalue(pgresult, i, c_rss))); proc->uid = atol(PQgetvalue(pgresult, i, c_uid)); proc->username = strdup(PQgetvalue(pgresult, i, c_username)); value = atoll(PQgetvalue(pgresult, i, c_rchar)); proc->rchar_diff = value - proc->rchar; proc->rchar = value; value = atoll(PQgetvalue(pgresult, i, c_wchar)); proc->wchar_diff = value - proc->wchar; proc->wchar = value; value = atoll(PQgetvalue(pgresult, i, c_syscr)); proc->syscr_diff = value - proc->syscr; proc->syscr = value; value = atoll(PQgetvalue(pgresult, i, c_syscw)); proc->syscw_diff = value - proc->syscw; proc->syscw = value; value = atoll(PQgetvalue(pgresult, i, c_reads)); proc->read_bytes_diff = value - proc->read_bytes; proc->read_bytes = value; value = atoll(PQgetvalue(pgresult, i, c_writes)); proc->write_bytes_diff = value - proc->write_bytes; proc->write_bytes = value; value = atoll(PQgetvalue(pgresult, i, c_cwrites)); proc->cancelled_write_bytes_diff = value - proc->cancelled_write_bytes; proc->cancelled_write_bytes = value; ++total_procs; ++process_states[proc->state]; if (timediff > 0.0) { if ((proc->pcpu = (proc->time - otime) / timediff) < 0.0001) proc->pcpu = 0; proc->wcpu = proc->pcpu * alpha + proc->wcpu * beta; } else if ((elapsed = (now - boottime) * HZ - proc->start_time) > 0) proc->wcpu = proc->pcpu; else proc->wcpu = proc->pcpu = 0.0; } if (pgresult != NULL) PQclear(pgresult); PQfinish(pgconn); /* Make sure we have enough slots for the active procs. */ if (activesize < total_procs) { pactive = (struct top_proc **) realloc(pactive, sizeof(struct top_proc *) * total_procs); activesize = total_procs; } /* Set up the active procs and flush dead entries. */ active = pactive; for (i = 0; i < HASH_SIZE; i++) { struct top_proc *last; struct top_proc *ptmp; last = NULL; proc = ptable[i]; while (proc != NULL) { if (proc->state == 0) { ptmp = proc; if (last) { proc = last->next = proc->next; } else { proc = ptable[i] = proc->next; } free_proc(ptmp); } else { if ((show_idle || proc->state == 1 || proc->pcpu) && (!show_uid || proc->uid == sel->uid)) { *active++ = proc; last = proc; } proc = proc->next; } } } si->p_active = active - pactive; si->p_total = total_procs; si->procstates = process_states; /* Sort the "active" procs if specified. */ if (si->p_active) qsort(pactive, si->p_active, sizeof(struct top_proc *), proc_compares_r[compare_index]); /* Don't even pretend that the return value thing here isn't bogus. */ nextactive = pactive; return 0; } int machine_init_r(struct statics *statics, char *conninfo) { PGconn *pgconn; /* Make sure the remote system has the stored function installed. */ pgconn = connect_to_db(conninfo); if (pgconn == NULL) { fprintf(stderr, "Cannot connect to database.\n"); return -1; } if (check_for_function(pgconn, "pg_cputime") != 0) return -1; if (check_for_function(pgconn, "pg_loadavg") != 0) return -1; if (check_for_function(pgconn, "pg_memusage") != 0) return -1; if (check_for_function(pgconn, "pg_proctab") != 0) return -1; PQfinish(pgconn); /* 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; /* allocate needed space */ pactive = (struct top_proc **) malloc(sizeof(struct top_proc *) * INITIAL_ACTIVE_SIZE); activesize = INITIAL_ACTIVE_SIZE; /* make sure the hash table is empty */ memset(ptable, 0, HASH_SIZE * sizeof(struct top_proc *)); return 0; } static struct top_proc * new_proc() { struct top_proc *p; if (freelist) { p = freelist; freelist = freelist->next; } else if (procblock) { p = procblock; if (++procblock >= procmax) procblock = NULL; } else { p = procblock = (struct top_proc *) calloc(PROCBLOCK_SIZE, sizeof(struct top_proc)); procmax = procblock++ + PROCBLOCK_SIZE; } /* initialization */ if (p->name != NULL) { free(p->name); p->name = NULL; } return p; } pgtop/machine/m_sco5.c000066400000000000000000000517141271046472400151630ustar00rootroot00000000000000/* * 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 * Modified to m_sco5.c (3.2v5.*) by Mike Hopkirk * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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, ""); 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 */ 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)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_sco5.man000066400000000000000000000004731271046472400155100ustar00rootroot00000000000000.SH "SCO 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(). pgtop/machine/m_sunos4.c000066400000000000000000000527101271046472400155420ustar00rootroot00000000000000/* * pg_top - a top PostgreSQL users display for Unix * * SYNOPSIS: any Sun running SunOS version 4.x * * DESCRIPTION: * This is the machine-dependent module for SunOS 4.x. * This makes pg_top work on the following systems: * SunOS 4.0 * SunOS 4.0.1 * SunOS 4.0.2 (including 386i architecture) * SunOS 4.0.3 * SunOS 4.1 * SunOS 4.1.1 * SunOS 4.1.2 (including MP architectures) * SunOS 4.1.3 (including MP architectures) * SunOS 4.1.3_U1 (including MP architectures) * SunOS 4.1.4 (including MP architectures) * Solbourne OS/MP PRIOR to 4.1A * * LIBS: -lkvm * * CFLAGS: -DHAVE_GETOPT -DORDER * * AUTHOR: William LeFebvre * Solbourne support by David MacKenzie */ /* * #ifdef MULTIPROCESSOR means Sun MP. * #ifdef solbourne is for Solbourne. */ #include "config.h" #include #include /* make sure param.h gets loaded with KERNEL defined to get PZERO & NZERO */ #define KERNEL #include #undef KERNEL #include #include #include #include #include #include #include #include #include #include #include #include #ifdef solbourne #include #endif /* Older versions of SunOS don't have a typedef for pid_t. Hopefully this will catch all those cases without causing other problems. */ #ifndef __sys_stdtypes_h typedef int pid_t; #endif #include "pg_top.h" #include "machine.h" #include "utils.h" /* declarations for load_avg */ #include "loadavg.h" /* 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)))) /* what we consider to be process size: */ #define PROCSIZE(pp) ((pp)->p_tsize + (pp)->p_dsize + (pp)->p_ssize) /* definitions for indices in the nlist array */ #define X_AVENRUN 0 #define X_CCPU 1 #define X_MPID 2 #define X_NPROC 3 #define X_PROC 4 #define X_TOTAL 5 #define X_CP_TIME 6 #define X_PAGES 7 #define X_EPAGES 8 static struct nlist nlst[] = { #ifdef i386 {"avenrun"}, /* 0 */ {"ccpu"}, /* 1 */ {"mpid"}, /* 2 */ {"nproc"}, /* 3 */ {"proc"}, /* 4 */ {"total"}, /* 5 */ {"cp_time"}, /* 6 */ {"pages"}, /* 7 */ {"epages"}, /* 8 */ #else {"_avenrun"}, /* 0 */ {"_ccpu"}, /* 1 */ {"_mpid"}, /* 2 */ {"_nproc"}, /* 3 */ {"_proc"}, /* 4 */ {"_total"}, /* 5 */ {"_cp_time"}, /* 6 */ {"_pages"}, /* 7 */ {"_epages"}, /* 8 */ #ifdef MULTIPROCESSOR {"_ncpu"}, #define X_NCPU 9 {"_xp_time"}, #define X_XP_TIME 10 #endif #endif {0} }; /* * 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 %5.2f%% %5.2f%% %s" /* 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[] = { "", "sleep", "WAIT", "run\0\0\0", "start", "zomb", "stop" }; /* values that we stash away in _init and use in later routines */ static double logcpu; kvm_t *kd; /* these are retrieved from the kernel in _init */ static unsigned long proc; static int nproc; static load_avg ccpu; static unsigned long pages; static unsigned long epages; static int ncpu = 0; /* these are offsets obtained via nlist and used in the get_ functions */ static unsigned long mpid_offset; static unsigned long avenrun_offset; static unsigned long total_offset; static unsigned long cp_time_offset; #ifdef MULTIPROCESSOR static unsigned long xp_time_offset; #endif /* these are for calculating cpu state percentages */ static long cp_time[CPUSTATES]; static long cp_old[CPUSTATES]; static long cp_diff[CPUSTATES]; #ifdef MULTIPROCESSOR static long xp_time[NCPU][XPSTATES]; /* for now we only accumulate spin time, but extending this to pick up other stuff in xp_time is trivial. */ static long xp_old[NCPU]; #endif /* these are for detailing the process states */ int process_states[7]; char *procstatenames[] = { "", " sleeping, ", " ABANDONED, ", " running, ", " starting, ", " zombie, ", " stopped, ", NULL }; /* these are for detailing the cpu states */ int cpu_states[5]; char *cpustatenames[] = { "user", "nice", "system", "idle", #ifdef MULTIPROCESSOR "spin", #define XCP_SPIN 4 #endif NULL }; /* these are for detailing the memory statistics */ long memory_stats[4]; char *memorynames[] = { "K available, ", "K in use, ", "K free, ", "K locked", NULL }; /* these are names given to allowed sorting orders -- first is default */ char *ordernames[] = {"cpu", "size", "res", NULL}; /* forward definitions for comparison functions */ int compare_cpu(); int compare_size(); int compare_res(); int (*proc_compares[]) () = { compare_cpu, compare_size, compare_res, 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; /* these are for getting the memory statistics */ static struct page *physpage; static int bytesize; static int count; static int pageshift; /* log base 2 of the pagesize */ /* define pagetok in terms of pageshift */ #define pagetok(size) ((size) << pageshift) /* useful externals */ extern int errno; extern char *sys_errlist[]; long lseek(); long time(); machine_init(statics) struct statics *statics; { register int i; register int pagesize; /* initialize the kernel interface */ if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "pg_top")) == NULL) { perror("kvm_open"); return (-1); } /* get the list of symbols we want to access in the kernel */ if ((i = kvm_nlist(kd, nlst)) < 0) { fprintf(stderr, "pg_top: nlist failed\n"); return (-1); } #ifdef MULTIPROCESSOR /* were ncpu and xp_time not found in the nlist? */ if (i > 0 && nlst[X_NCPU].n_type == 0 && nlst[X_XP_TIME].n_type == 0) { /* we were compiled on an MP system but we are not running on one */ /* so we will pretend this didn't happen and set ncpu = 1 */ i -= 2; ncpu = 1; } #endif #ifdef solbourne { unsigned int status, type; /* Get the number of CPUs on this system. */ syscall(SYS_getcpustatus, &status, &ncpu, &type); } #endif /* make sure they were all found */ if (i > 0 && check_nlist(nlst) > 0) { return (-1); } /* get the symbol values out of kmem */ (void) getkval(nlst[X_PROC].n_value, (int *) (&proc), sizeof(proc), nlst[X_PROC].n_name); (void) getkval(nlst[X_NPROC].n_value, &nproc, sizeof(nproc), nlst[X_NPROC].n_name); (void) getkval(nlst[X_CCPU].n_value, (int *) (&ccpu), sizeof(ccpu), nlst[X_CCPU].n_name); (void) getkval(nlst[X_PAGES].n_value, (int *) (&pages), sizeof(pages), nlst[X_PAGES].n_name); (void) getkval(nlst[X_EPAGES].n_value, (int *) (&epages), sizeof(epages), nlst[X_EPAGES].n_name); #ifdef MULTIPROCESSOR if (ncpu == 0) { /* if ncpu > 0 then we are not really on an MP system */ (void) getkval(nlst[X_NCPU].n_value, (int *) (&ncpu), sizeof(ncpu), nlst[X_NCPU].n_name); } #endif /* stash away certain offsets for later use */ mpid_offset = nlst[X_MPID].n_value; avenrun_offset = nlst[X_AVENRUN].n_value; total_offset = nlst[X_TOTAL].n_value; cp_time_offset = nlst[X_CP_TIME].n_value; #ifdef MULTIPROCESSOR xp_time_offset = nlst[X_XP_TIME].n_value; #endif /* this is used in calculating WCPU -- calculate it ahead of time */ logcpu = log(loaddouble(ccpu)); /* allocate space for proc structure array and array of pointers */ bytes = nproc * sizeof(struct proc); pbase = (struct proc *) malloc(bytes); pref = (struct proc **) malloc(nproc * sizeof(struct proc *)); /* Just in case ... */ if (pbase == (struct proc *) NULL || pref == (struct proc **) NULL) { fprintf(stderr, "pg_top: can't allocate sufficient memory\n"); return (-1); } /* allocate a table to hold all the page structs */ bytesize = epages - pages; count = bytesize / sizeof(struct page); physpage = (struct page *) malloc(epages - pages); if (physpage == NULL) { fprintf(stderr, "pg_top: can't allocate sufficient memory\n"); return (-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; #if defined(MULTIPROCESSOR) || defined(solbourne) /* add a slash to the "run" state abbreviation */ if (ncpu > 1) { state_abbrev[SRUN][3] = '/'; } #endif /* fill in the statics information */ statics->procstate_names = procstatenames; statics->cpustate_names = cpustatenames; statics->memory_names = memorynames; statics->order_names = ordernames; /* all done! */ 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; { load_avg avenrun[3]; long total; #ifdef MULTIPROCESSOR long half_total; #endif /* get the cp_time array */ (void) getkval(cp_time_offset, (int *) cp_time, sizeof(cp_time), "_cp_time"); #ifdef MULTIPROCESSOR /* get the xp_time array as well */ if (ncpu > 1) { (void) getkval(xp_time_offset, (int *) xp_time, sizeof(xp_time), "_xp_time"); } #endif /* get load average array */ (void) getkval(avenrun_offset, (int *) avenrun, sizeof(avenrun), "_avenrun"); /* get mpid -- process id of last process */ (void) getkval(mpid_offset, &(si->last_pid), sizeof(si->last_pid), "_mpid"); /* get the array of physpage descriptors */ (void) getkval(pages, (int *) physpage, bytesize, "array _page"); /* convert load averages to doubles */ { register int i; register double *infoloadp; register load_avg *sysloadp; infoloadp = si->load_avg; sysloadp = avenrun; for (i = 0; i < 3; i++) { *infoloadp++ = loaddouble(*sysloadp++); } } /* convert cp_time counts to percentages */ total = percentages(CPUSTATES, cpu_states, cp_time, cp_old, cp_diff); #ifdef MULTIPROCESSOR /* calculate spin time from all processors */ if (ncpu > 1) { register int c; register int i; register long sum; register long change; /* collect differences for each processor and add them */ sum = 0; for (i = 0; i < ncpu; i++) { c = xp_time[i][XP_SPIN]; change = c - xp_old[i]; if (change < 0) { /* counter wrapped */ change = (long) ((unsigned long) c - (unsigned long) xp_old[i]); } sum += change; xp_old[i] = c; } /* * NOTE: I am assuming that the ticks found in xp_time are already * included in the ticks accumulated in cp_time. To get an accurate * reflection, therefore, we have to subtract the spin time from the * system time and recompute those two percentages. */ half_total = total / 2l; cp_diff[CP_SYS] -= sum; cpu_states[CP_SYS] = (int) ((cp_diff[CP_SYS] * 1000 + half_total) / total); cpu_states[XCP_SPIN] = (int) ((sum * 1000 + half_total) / total); } #endif /* sum memory statistics */ { register struct page *pp; register int cnt; register int inuse; register int free; register int locked; /* bop thru the array counting page types */ pp = physpage; inuse = free = locked = 0; for (cnt = count; --cnt >= 0; pp++) { if (pp->p_free) free++; else if (pp->p_lock || pp->p_keepcnt > 0) locked++; else inuse++; } /* convert memory stats to Kbytes */ memory_stats[0] = pagetok(inuse + free); memory_stats[1] = pagetok(inuse); memory_stats[2] = pagetok(free); memory_stats[3] = pagetok(locked); } /* set arrays and strings */ si->cpustates = cpu_states; si->memory = memory_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; { register int i; register int total_procs; register int active_procs; register struct proc **prefp; register struct proc *pp; /* these are copied out of sel for speed */ int show_idle; int show_system; int show_uid; int show_command; /* read all the proc structures in one fell swoop */ (void) getkval(proc, (int *) pbase, bytes, "proc array"); /* 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; bzero((char *) process_states, 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->p_stat != 0 && (show_system || ((pp->p_flag & SSYS) == 0))) { total_procs++; process_states[pp->p_stat]++; if ((pp->p_stat != SZOMB) && (show_idle || (pp->p_pctcpu != 0) || (pp->p_stat == SRUN)) && (!show_uid || pp->p_uid == (uid_t) sel->uid)) { *prefp++ = pp; active_procs++; } } } /* if requested, sort the "interesting" processes */ qsort((char *) pref, active_procs, sizeof(struct 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 * format_next_process(handle, get_userid) caddr_t handle; char *(*get_userid) (); { register struct proc *pp; register long cputime; register double pct; struct user u; struct handle *hp; /* 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 (getu(pp, &u) == -1) { (void) strcpy(u.u_comm, ""); cputime = 0; } else { /* set u_comm for system processes */ if (u.u_comm[0] == '\0') { if (pp->p_pid == 0) { (void) strcpy(u.u_comm, "Swapper"); } else if (pp->p_pid == 2) { (void) strcpy(u.u_comm, "Pager"); } } cputime = u.u_ru.ru_utime.tv_sec + u.u_ru.ru_stime.tv_sec; } /* calculate the base for cpu percentages */ pct = pctdouble(pp->p_pctcpu); #ifdef MULTIPROCESSOR /* * If there is more than one cpu then add the processor number to the * "run/" string. Note that this will only show up if the process is in * the run state. Also note: when they start making Suns with more than * 9 processors this will break since the string will then be more than 5 * characters. */ if (ncpu > 1) { state_abbrev[SRUN][4] = (pp->p_cpuid & 0xf) + '0'; } #endif #ifdef solbourne if (ncpu > 1) { state_abbrev[SRUN][4] = (pp->p_lastcpu) + '0'; } #endif /* 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(pagetok(PROCSIZE(pp))), format_k(pagetok(pp->p_rssize)), state_abbrev[pp->p_stat], format_time(cputime), 100.0 * weighted_cpu(pct, pp), 100.0 * pct, printable(u.u_comm)); /* return the result */ return (fmt); } /* * getu(p, u) - get the user structure for the process whose proc structure * is pointed to by p. The user structure is put in the buffer pointed * to by u. Return 0 if successful, -1 on failure (such as the process * being swapped out). */ getu(p, u) register struct proc *p; struct user *u; { register struct user *lu; lu = kvm_getu(kd, p); if (lu == NULL) { return (-1); } else { *u = *lu; return (0); } } /* * 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(nlst) 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) { #ifdef i386 if (nlst->n_value == 0) #else if (nlst->n_type == 0) #endif { /* this one wasn't found */ 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). * */ getkval(offset, ptr, size, refstr) unsigned long offset; int *ptr; int size; char *refstr; { if (kvm_read(kd, offset, ptr, size) != size) { if (*refstr == '!') { return (0); } else { fprintf(stderr, "pg_top: kvm_read for %s: %s\n", refstr, sys_errlist[errno]); quit(23); /* NOTREACHED */ } } return (1); } /* 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 = p2->p_pctcpu - p1->p_pctcpu,\ (result = lresult > 0 ? 1 : lresult < 0 ? -1 : 0) == 0) #define ORDERKEY_CPTICKS if ((result = p2->p_cpticks - p1->p_cpticks) == 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) #define ORDERKEY_RSSIZE if ((result = p2->p_rssize - p1->p_rssize) == 0) #define ORDERKEY_MEM if ((result = PROCSIZE(p2) - PROCSIZE(p1)) == 0) /* Now the array that maps process state to a weight */ static unsigned char sorted_state[] = { 0, /* not used */ 3, /* sleep */ 1, /* ABANDONED (WAIT) */ 6, /* run */ 5, /* start */ 2, /* zombie */ 4 /* stop */ }; /* compare_cpu - the comparison function for sorting by cpu percentage */ compare_cpu(pp1, pp2) struct proc **pp1; struct proc **pp2; { register struct proc *p1; register struct proc *p2; register int result; register pctcpu lresult; /* 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 */ compare_size(pp1, pp2) struct proc **pp1; struct proc **pp2; { register struct proc *p1; register struct proc *p2; register int result; register pctcpu lresult; /* 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 */ compare_res(pp1, pp2) struct proc **pp1; struct proc **pp2; { register struct proc *p1; register struct proc *p2; register int result; register pctcpu lresult; /* remove one level of indirection */ p1 = *pp1; p2 = *pp2; ORDERKEY_RSSIZE ORDERKEY_MEM ORDERKEY_PCTCPU ORDERKEY_CPTICKS ORDERKEY_STATE ORDERKEY_PRIO ; 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; { register int cnt; register struct proc **prefp; register struct proc *pp; prefp = pref; cnt = pref_len; while (--cnt >= 0) { if ((pp = *prefp++)->p_pid == (pid_t) pid) { return ((int) pp->p_uid); } } return (-1); } void get_io_info(struct io_info *io_info) { /* Not supported yet */ memset(io_info, 0, sizeof(*io_info)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_sunos4.man000066400000000000000000000024041271046472400160660ustar00rootroot00000000000000.SH "SUNOS 4 DIFFERENCES" On multiprocessor machines, the amount of time the processors spend in a spin lock is displayed along with the other processor state percentages. The percentages shown for processor states are averages across all processors. A process in run state also has its current processor displayed in the STATE column, for example "run/2" indicates running on processor 2. There is an extra column in the process display indicating which processor each running process is assigned to. Information about physical memory is displayed on the memory status line, but information about virtual memory is not available. Due to incompatabilities in kernel data structures, a pg_top executable compiled on a Sun 4 multiprocessor architecture machine (sun4m) will not run correctly on a uniprocessor architecture machine (sun4), and vice versa. You will have to compile and maintain separate executables for these architectures. Yeah, I don't like it either. Some processes may show up with a resident set size (RES column) larger than total virtual memory size (SIZE column). This seems odd at first, but is a consequence of shared libraries: shared memory is counted as resident but is not counted in total size. The SunOS 4 port was written by William LeFebvre. pgtop/machine/m_sunos5.c000066400000000000000000001214041271046472400155400ustar00rootroot00000000000000/* * 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 * Robert Boucher * CONTRIBUTORS: Marc Cohen * Charles Hedrick * William L. Jones * Petri Kutvonen * Casper Dik * Tim Pugh * Mark Wong */ #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 #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" #if OSREV >= 53 #define USE_KSTAT #endif #ifdef USE_KSTAT #include /* * 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<: 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 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->order_names_io = ordernames_io; 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, char *conninfo, 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(conninfo); 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)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_sunos5.man000066400000000000000000000036401271046472400160720ustar00rootroot00000000000000.SH "SUNOS 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 .IR pg_top , The swap figures will differ from the summary output of .IR 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, . Many contributions have been provided by Casper Dik . Support for multi-cpu, calculation of CPU% and memory stats provided by Robert Boucher , Marc Cohen , Charles Hedrick , and William L. Jones . pgtop/machine/m_svr4.c000066400000000000000000000450551271046472400152110ustar00rootroot00000000000000/* * 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 * Robert Boucher * Ported to System 3000 Release 2.03 by: * Jeff Janvrin */ #include "pg_top.h" #include "machine.h" #include "utils.h" #include #include #include #include #include #include #include #include #if TIME_WITH_SYS_TIME #include #include #else #if HAVE_SYS_TIME_H #include #else #include #endif #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #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<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)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_svr4.man000066400000000000000000000005101271046472400155250ustar00rootroot00000000000000.SH "SVR4 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) pgtop/machine/m_svr5.c000066400000000000000000000774451271046472400152220ustar00rootroot00000000000000/* * 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 * David Cutter * Andrew Herbert * Robert Boucher */ /* 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 #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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<> 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 #include #include #include 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)); } void get_disk_info(struct disk_info *disk_info, char *data_directory) { /* Not supported yet */ memset(disk_info, 0, sizeof(*disk_info)); } pgtop/machine/m_svr5.man000066400000000000000000000002341271046472400155310ustar00rootroot00000000000000.SH "SVR5 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 pgtop/message.h000066400000000000000000000005531271046472400140160ustar00rootroot00000000000000/* 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_ */ pgtop/os.h000066400000000000000000000027301271046472400130120ustar00rootroot00000000000000#ifndef _OS_H_ #define _OS_H_ #include "config.h" #include #include #include #if TIME_WITH_SYS_TIME #include #include #else #if HAVE_SYS_TIME_H #include #else #include #endif #endif #if STDC_HEADERS #include #include #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 #else #ifdef HAVE_STRING_H #include #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 #endif #endif /* _OS_H_ */ pgtop/pg.c000066400000000000000000000555311271046472400130010ustar00rootroot00000000000000/* Copyright (c) 2007-2015, Mark Wong */ #include #include #include #include "display.h" #include "pg.h" #include "pg_top.h" #define QUERY_PROCESSES \ "SELECT pid, query\n" \ "FROM pg_stat_activity;" #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 GET_LOCKS \ "SELECT datname, relname, mode, granted\n" \ "FROM pg_stat_activity, pg_locks\n" \ "LEFT OUTER JOIN pg_class\n" \ "ON relation = pg_class.oid\n"\ "WHERE pg_stat_activity.pid = %d\n" \ " AND pg_stat_activity.pid = pg_locks.pid;" #define GET_LOCKS_9_1 \ "SELECT datname, relname, mode, granted\n" \ "FROM pg_stat_activity, pg_locks\n" \ "LEFT OUTER JOIN pg_class\n" \ "ON relation = pg_class.oid\n"\ "WHERE procpid = %d\n" \ " AND procpid = pid;" char *index_ordernames[] = { "idx_scan", "idx_tup_fetch", "idx_tup_read", NULL }; char *statement_ordernames[] = { "calls", "calls%", "total_time", "avg_time", NULL }; char *table_ordernames[] = { "seq_scan", "seq_tup_read", "idx_scan", "idx_tup_fetch", "n_tup_ins", "n_tup_upd", "n_tup_del", NULL }; int (*index_compares[]) () = { compare_idx_scan, compare_idx_tup_fetch, compare_idx_tup_read, NULL }; int (*table_compares[]) () = { compare_seq_scan, compare_seq_tup_read, compare_idx_scan_t, compare_idx_tup_fetch_t, compare_n_tup_ins, compare_n_tup_upd, compare_n_tup_del, NULL }; struct index_node { long long indexrelid; /* Index to the index name in the PGresult object. */ int name_index; /* The change in the previous values and current values. */ long long diff_idx_scan; long long diff_idx_tup_read; long long diff_idx_tup_fetch; /* The previous values. */ long long old_idx_scan; long long old_idx_tup_read; long long old_idx_tup_fetch; /* The value totals. */ long long total_idx_scan; long long total_idx_tup_read; long long total_idx_tup_fetch; struct index_node *next; }; struct table_node { long long relid; /* Index to the relation name in the PGresult object. */ int name_index; /* The change in the previous values and current values. */ long long diff_idx_scan; long long diff_idx_tup_fetch; long long diff_n_tup_del; long long diff_n_tup_ins; long long diff_n_tup_upd; long long diff_seq_scan; long long diff_seq_tup_read; /* The previous values. */ long long old_idx_scan; long long old_idx_tup_fetch; long long old_n_tup_del; long long old_n_tup_ins; long long old_n_tup_upd; long long old_seq_scan; long long old_seq_tup_read; /* The value totals. */ long long total_idx_scan; long long total_idx_tup_fetch; long long total_n_tup_del; long long total_n_tup_ins; long long total_n_tup_upd; long long total_seq_scan; long long total_seq_tup_read; struct table_node *next; }; struct index_node *get_index_stats(struct index_node *, long long); struct index_node *insert_index_stats(struct index_node *, struct index_node *); struct index_node *new_index_node(long long); void update_index_stats(struct index_node *, long long, long long, long long); struct index_node *upsert_index_stats(struct index_node *, long long, long long, long long, long long); struct table_node *get_table_stats(struct table_node *, long long); struct table_node *insert_table_stats(struct table_node *, struct table_node *); struct table_node *new_table_node(long long); void update_table_stats(struct table_node *, long long, long long, long long, long long, long long, long long, long long); struct table_node *upsert_table_stats(struct table_node *, long long, long long, long long, long long, long long, long long, long long, long long); float pg_version(PGconn *); int compare_idx_scan(const void *vp1, const void *vp2) { struct index_node **pp1 = (struct index_node **) vp1; struct index_node **pp2 = (struct index_node **) vp2; struct index_node *p1 = *pp1; struct index_node *p2 = *pp2; if (mode_stats == STATS_DIFF) { if (p1->diff_idx_scan < p2->diff_idx_scan) { return -1; } else if (p1->diff_idx_scan > p2->diff_idx_scan) { return 1; } return 0; } else { if (p1->total_idx_scan < p2->total_idx_scan) { return -1; } else if (p1->total_idx_scan > p2->total_idx_scan) { return 1; } return 0; } } int compare_idx_scan_t(const void *vp1, const void *vp2) { struct table_node **pp1 = (struct table_node **) vp1; struct table_node **pp2 = (struct table_node **) vp2; struct table_node *p1 = *pp1; struct table_node *p2 = *pp2; if (mode_stats == STATS_DIFF) { if (p1->diff_idx_scan < p2->diff_idx_scan) { return -1; } else if (p1->diff_idx_scan > p2->diff_idx_scan) { return 1; } return 0; } else { if (p1->total_idx_scan < p2->total_idx_scan) { return -1; } else if (p1->total_idx_scan > p2->total_idx_scan) { return 1; } return 0; } } int compare_idx_tup_fetch(const void *vp1, const void *vp2) { struct index_node **pp1 = (struct index_node **) vp1; struct index_node **pp2 = (struct index_node **) vp2; struct index_node *p1 = *pp1; struct index_node *p2 = *pp2; if (mode_stats == STATS_DIFF) { if (p1->diff_idx_tup_fetch < p2->diff_idx_tup_fetch) { return -1; } else if (p1->diff_idx_tup_fetch > p2->diff_idx_tup_fetch) { return 1; } return 0; } else { if (p1->total_idx_tup_fetch < p2->total_idx_tup_fetch) { return -1; } else if (p1->total_idx_tup_fetch > p2->total_idx_tup_fetch) { return 1; } return 0; } } int compare_idx_tup_fetch_t(const void *vp1, const void *vp2) { struct table_node **pp1 = (struct table_node **) vp1; struct table_node **pp2 = (struct table_node **) vp2; struct table_node *p1 = *pp1; struct table_node *p2 = *pp2; if (mode_stats == STATS_DIFF) { if (p1->diff_idx_tup_fetch < p2->diff_idx_tup_fetch) { return -1; } else if (p1->diff_idx_tup_fetch > p2->diff_idx_tup_fetch) { return 1; } return 0; } else { if (p1->total_idx_tup_fetch < p2->total_idx_tup_fetch) { return -1; } else if (p1->total_idx_tup_fetch > p2->total_idx_tup_fetch) { return 1; } return 0; } } int compare_idx_tup_read(const void *vp1, const void *vp2) { struct index_node **pp1 = (struct index_node **) vp1; struct index_node **pp2 = (struct index_node **) vp2; struct index_node *p1 = *pp1; struct index_node *p2 = *pp2; if (mode_stats == STATS_DIFF) { if (p1->diff_idx_tup_read < p2->diff_idx_tup_read) { return -1; } else if (p1->diff_idx_tup_read > p2->diff_idx_tup_read) { return 1; } return 0; } else { if (p1->total_idx_tup_read < p2->total_idx_tup_read) { return -1; } else if (p1->total_idx_tup_read > p2->total_idx_tup_read) { return 1; } return 0; } } int compare_n_tup_del(const void *vp1, const void *vp2) { struct table_node **pp1 = (struct table_node **) vp1; struct table_node **pp2 = (struct table_node **) vp2; struct table_node *p1 = *pp1; struct table_node *p2 = *pp2; if (mode_stats == STATS_DIFF) { if (p1->diff_n_tup_del < p2->diff_n_tup_del) { return -1; } else if (p1->diff_n_tup_del > p2->diff_n_tup_del) { return 1; } return 0; } else { if (p1->total_n_tup_del < p2->total_n_tup_del) { return -1; } else if (p1->total_n_tup_del > p2->total_n_tup_del) { return 1; } return 0; } } int compare_n_tup_ins(const void *vp1, const void *vp2) { struct table_node **pp1 = (struct table_node **) vp1; struct table_node **pp2 = (struct table_node **) vp2; struct table_node *p1 = *pp1; struct table_node *p2 = *pp2; if (mode_stats == STATS_DIFF) { if (p1->diff_n_tup_ins < p2->diff_n_tup_ins) { return -1; } else if (p1->diff_n_tup_ins > p2->diff_n_tup_ins) { return 1; } return 0; } else { if (p1->total_n_tup_ins < p2->total_n_tup_ins) { return -1; } else if (p1->total_n_tup_ins > p2->total_n_tup_ins) { return 1; } return 0; } } int compare_n_tup_upd(const void *vp1, const void *vp2) { struct table_node **pp1 = (struct table_node **) vp1; struct table_node **pp2 = (struct table_node **) vp2; struct table_node *p1 = *pp1; struct table_node *p2 = *pp2; if (mode_stats == STATS_DIFF) { if (p1->diff_n_tup_upd < p2->diff_n_tup_upd) { return -1; } else if (p1->diff_n_tup_upd > p2->diff_n_tup_upd) { return 1; } return 0; } else { if (p1->total_n_tup_upd < p2->total_n_tup_upd) { return -1; } else if (p1->total_n_tup_upd > p2->total_n_tup_upd) { return 1; } return 0; } } int compare_seq_scan(const void *vp1, const void *vp2) { struct table_node **pp1 = (struct table_node **) vp1; struct table_node **pp2 = (struct table_node **) vp2; struct table_node *p1 = *pp1; struct table_node *p2 = *pp2; if (mode_stats == STATS_DIFF) { if (p1->diff_seq_scan < p2->diff_seq_scan) { return -1; } else if (p1->diff_seq_scan > p2->diff_seq_scan) { return 1; } return 0; } else { if (p1->total_seq_scan < p2->total_seq_scan) { return -1; } else if (p1->total_seq_scan > p2->total_seq_scan) { return 1; } return 0; } } int compare_seq_tup_read(const void *vp1, const void *vp2) { struct table_node **pp1 = (struct table_node **) vp1; struct table_node **pp2 = (struct table_node **) vp2; struct table_node *p1 = *pp1; struct table_node *p2 = *pp2; if (mode_stats == STATS_DIFF) { if (p1->diff_seq_tup_read < p2->diff_seq_tup_read) { return -1; } else if (p1->diff_seq_tup_read > p2->diff_seq_tup_read) { return 1; } return 0; } else { if (p1->total_seq_tup_read < p2->total_seq_tup_read) { return -1; } else if (p1->total_seq_tup_read > p2->total_seq_tup_read) { return 1; } return 0; } } PGconn * connect_to_db(char *conninfo) { PGconn *pgconn = NULL; pgconn = PQconnectdb(conninfo); if (PQstatus(pgconn) != CONNECTION_OK) { new_message(MT_standout | MT_delayed, " %s", PQerrorMessage(pgconn)); PQfinish(pgconn); return NULL; } return pgconn; } struct index_node * get_index_stats(struct index_node * head, long long indexrelid) { struct index_node *c = head; while (c != NULL) { if (c->indexrelid == indexrelid) { break; } c = c->next; } return c; } struct table_node * get_table_stats(struct table_node * head, long long relid) { struct table_node *c = head; while (c != NULL) { if (c->relid == relid) { break; } c = c->next; } return c; } void pg_display_index_stats(char *conninfo, int compare_index, int max_topn) { int i; int rows; PGconn *pgconn; PGresult *pgresult = NULL; static char line[512]; static struct index_node *head = NULL; static struct index_node **procs = NULL; int max_lines; pgconn = connect_to_db(conninfo); if (pgconn != NULL) { pgresult = PQexec(pgconn, SELECT_INDEX_STATS); rows = PQntuples(pgresult); } else { PQfinish(pgconn); return; } PQfinish(pgconn); max_lines = rows < max_topn ? rows : max_topn; procs = (struct index_node **) realloc(procs, rows * sizeof(struct index_node *)); /* Calculate change in values. */ for (i = 0; i < rows; i++) { head = upsert_index_stats(head, atoll(PQgetvalue(pgresult, i, 0)), atoll(PQgetvalue(pgresult, i, 2)), atoll(PQgetvalue(pgresult, i, 3)), atoll(PQgetvalue(pgresult, i, 4))); } /* Sort stats. */ for (i = 0; i < rows; i++) { procs[i] = get_index_stats(head, atoll(PQgetvalue(pgresult, i, 0))); procs[i]->name_index = i; } qsort(procs, rows, sizeof(struct index_node *), index_compares[compare_index]); /* Display stats. */ for (i = rows - 1; i > rows - max_lines - 1; i--) { if (mode_stats == STATS_DIFF) { snprintf(line, sizeof(line), "%9lld %9lld %9lld %s", procs[i]->diff_idx_scan, procs[i]->diff_idx_tup_read, procs[i]->diff_idx_tup_fetch, PQgetvalue(pgresult, procs[i]->name_index, 1)); } else { snprintf(line, sizeof(line), "%9lld %9lld %9lld %s", procs[i]->total_idx_scan, procs[i]->total_idx_tup_read, procs[i]->total_idx_tup_fetch, PQgetvalue(pgresult, procs[i]->name_index, 1)); } u_process(rows - i - 1, line); } if (pgresult != NULL) PQclear(pgresult); } void pg_display_table_stats(char *conninfo, int compare_index, int max_topn) { int i; int rows; PGconn *pgconn; PGresult *pgresult = NULL; static char line[512]; static struct table_node *head = NULL; static struct table_node **procs = NULL; int max_lines; pgconn = connect_to_db(conninfo); if (pgconn != NULL) { pgresult = PQexec(pgconn, SELECT_TABLE_STATS); rows = PQntuples(pgresult); } else { PQfinish(pgconn); return; } PQfinish(pgconn); max_lines = rows < max_topn ? rows : max_topn; procs = (struct table_node **) realloc(procs, rows * sizeof(struct table_node *)); /* Calculate change in values. */ for (i = 0; i < rows; i++) { head = upsert_table_stats(head, atoll(PQgetvalue(pgresult, i, 0)), atoll(PQgetvalue(pgresult, i, 2)), atoll(PQgetvalue(pgresult, i, 3)), atoll(PQgetvalue(pgresult, i, 4)), atoll(PQgetvalue(pgresult, i, 5)), atoll(PQgetvalue(pgresult, i, 6)), atoll(PQgetvalue(pgresult, i, 7)), atoll(PQgetvalue(pgresult, i, 8))); } /* Sort stats. */ for (i = 0; i < rows; i++) { procs[i] = get_table_stats(head, atoll(PQgetvalue(pgresult, i, 0))); procs[i]->name_index = i; } qsort(procs, rows, sizeof(struct table_node *), table_compares[compare_index]); for (i = rows - 1; i > rows - max_lines - 1; i--) { if (mode_stats == STATS_DIFF) { snprintf(line, sizeof(line), "%9lld %9lld %9lld %9lld %9lld %9lld %9lld %s", procs[i]->diff_seq_scan, procs[i]->diff_seq_tup_read, procs[i]->diff_idx_scan, procs[i]->diff_idx_tup_fetch, procs[i]->diff_n_tup_ins, procs[i]->diff_n_tup_upd, procs[i]->diff_n_tup_del, PQgetvalue(pgresult, procs[i]->name_index, 1)); } else { snprintf(line, sizeof(line), "%9lld %9lld %9lld %9lld %9lld %9lld %9lld %s", procs[i]->total_seq_scan, procs[i]->total_seq_tup_read, procs[i]->total_idx_scan, procs[i]->total_idx_tup_fetch, procs[i]->total_n_tup_ins, procs[i]->total_n_tup_upd, procs[i]->total_n_tup_del, PQgetvalue(pgresult, procs[i]->name_index, 1)); } u_process(rows - i - 1, line); } if (pgresult != NULL) PQclear(pgresult); } int pg_display_statements(char *conninfo, int compare_index, int max_topn) { int i; int rows; PGconn *pgconn; PGresult *pgresult = NULL; static char line[512]; int max_lines; pgconn = connect_to_db(conninfo); if (pgconn != NULL) { pgresult = PQexec(pgconn, CHECK_FOR_STATEMENTS_X); if (PQntuples(pgresult) == 0) return 1; snprintf(line, sizeof(line), SELECT_STATEMENTS, compare_index + 1); pgresult = PQexec(pgconn, line); rows = PQntuples(pgresult); } else { PQfinish(pgconn); return 0; } PQfinish(pgconn); max_lines = rows < max_topn ? rows : max_topn; /* Display stats. */ for (i = rows - 1; i > rows - max_lines - 1; i--) { snprintf(line, sizeof(line), "%7s %6.1f %10s %8s %s", PQgetvalue(pgresult, i, 0), atof(PQgetvalue(pgresult, i, 1)), PQgetvalue(pgresult, i, 2), PQgetvalue(pgresult, i, 3), PQgetvalue(pgresult, i, 4)); u_process(rows - i - 1, line); } if (pgresult != NULL) PQclear(pgresult); return 0; } PGresult * pg_locks(PGconn *pgconn, int procpid) { char *sql; PGresult *pgresult; if (pg_version(pgconn) >= 9.2) { 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) >= 9.2) { pgresult = PQexec(pgconn, QUERY_PROCESSES); } else { pgresult = PQexec(pgconn, QUERY_PROCESSES_9_1); } PQexec(pgconn, "ROLLBACK;;"); return pgresult; } PGresult * pg_query(PGconn *pgconn, int procpid) { char *sql; PGresult *pgresult; if (pg_version(pgconn) >= 9.2) { 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; } /* Query the version string and just return the major.minor as a float. */ float pg_version(PGconn *pgconn) { PGresult *pgresult = NULL; char *version_string; float version; pgresult = PQexec(pgconn, "SHOW server_version;"); version_string = PQgetvalue(pgresult, 0, 0); sscanf(version_string, "%f%*s", &version); /* Deal with rounding problems by adding 0.01. */ version += 0.01; PQclear(pgresult); return version; } struct index_node * insert_index_stats(struct index_node * head, struct index_node * node) { struct index_node *c = head; struct index_node *p = NULL; /* Check the head of the list as a special case. */ if (node->indexrelid < head->indexrelid) { node->next = head; head = node; return head; } c = head->next; p = head; while (c != NULL) { if (node->indexrelid < c->indexrelid) { node->next = c; p->next = node; return head; } p = c; c = c->next; } /* * The node to be inserted has the highest indexrelid so it goes on the * end. */ if (c == NULL) { p->next = node; } return head; } struct table_node * insert_table_stats(struct table_node * head, struct table_node * node) { struct table_node *c = head; struct table_node *p = NULL; /* Check the head of the list as a special case. */ if (node->relid < head->relid) { node->next = head; head = node; return head; } c = head->next; p = head; while (c != NULL) { if (node->relid < c->relid) { node->next = c; p->next = node; return head; } p = c; c = c->next; } /* * The node to be inserted has the highest relid so it goes on the end. */ if (c == NULL) { p->next = node; } return head; } struct index_node * new_index_node(long long indexrelid) { struct index_node *node; node = (struct index_node *) malloc(sizeof(struct index_node)); bzero(node, sizeof(struct index_node)); node->indexrelid = indexrelid; node->next = NULL; return node; } struct table_node * new_table_node(long long relid) { struct table_node *node; node = (struct table_node *) malloc(sizeof(struct table_node)); bzero(node, sizeof(struct table_node)); node->relid = relid; node->next = NULL; return node; } void update_index_stats(struct index_node * node, long long idx_scan, long long idx_tup_read, long long idx_tup_fetch) { /* Add to the index totals */ node->total_idx_scan = idx_scan; node->total_idx_tup_read = idx_tup_read; node->total_idx_tup_fetch = idx_tup_fetch; /* Calculate difference between previous and current values. */ node->diff_idx_scan = idx_scan - node->old_idx_scan; node->diff_idx_tup_read = idx_tup_read - node->old_idx_tup_read; node->diff_idx_tup_fetch = idx_tup_fetch - node->old_idx_tup_fetch; /* Save the current values as previous values. */ node->old_idx_scan = idx_scan; node->old_idx_tup_read = idx_tup_read; node->old_idx_tup_fetch = idx_tup_fetch; } void update_table_stats(struct table_node * node, long long seq_scan, long long seq_tup_read, long long idx_scan, long long idx_tup_fetch, long long n_tup_ins, long long n_tup_upd, long long n_tup_del) { /* Add to the table totals */ node->total_idx_scan = idx_scan; node->total_idx_tup_fetch = idx_tup_fetch; node->total_n_tup_del = n_tup_del; node->total_n_tup_ins = n_tup_ins; node->total_n_tup_upd = n_tup_upd; node->total_seq_scan = seq_scan; node->total_seq_tup_read = seq_tup_read; /* Calculate difference between previous and current values. */ node->diff_idx_scan = idx_scan - node->old_idx_scan; node->diff_idx_tup_fetch = idx_tup_fetch - node->old_idx_tup_fetch; node->diff_n_tup_del = n_tup_del - node->old_n_tup_del; node->diff_n_tup_ins = n_tup_ins - node->old_n_tup_ins; node->diff_n_tup_upd = n_tup_upd - node->old_n_tup_upd; node->diff_seq_scan = seq_scan - node->old_seq_scan; node->diff_seq_tup_read = seq_tup_read - node->old_seq_tup_read; /* Save the current values as previous values. */ node->old_idx_scan = idx_scan; node->old_idx_tup_fetch = idx_tup_fetch; node->old_n_tup_del = n_tup_del; node->old_n_tup_ins = n_tup_ins; node->old_n_tup_upd = n_tup_upd; node->old_seq_scan = seq_scan; node->old_seq_tup_read = seq_tup_read; } /* * Determine if indexrelid exists in the list and update it if it does. * Otherwise Create a new node and insert it into the list. Sort this * list by indexrelid. */ struct index_node * upsert_index_stats(struct index_node * head, long long indexrelid, long long idx_scan, long long idx_tup_read, long long idx_tup_fetch) { struct index_node *c = head; /* List is empty, create a new node. */ if (head == NULL) { head = new_index_node(indexrelid); update_index_stats(head, idx_scan, idx_tup_read, idx_tup_fetch); return head; } /* Check if this indexrelid exists already. */ while (c != NULL) { if (c->indexrelid == indexrelid) { /* Found an existing node with same indexrelid, update it. */ update_index_stats(c, idx_scan, idx_tup_read, idx_tup_fetch); return head; } c = c->next; } /* * Didn't find indexrelid. Create a new node, save the data and insert * it. */ c = new_index_node(indexrelid); update_index_stats(c, idx_scan, idx_tup_read, idx_tup_fetch); head = insert_index_stats(head, c); return head; } /* * Determine if relid exists in the list and update it if it does. * Otherwise Create a new node and insert it into the list. Sort this * list by relid. */ struct table_node * upsert_table_stats(struct table_node * head, long long relid, long long seq_scan, long long seq_tup_read, long long idx_scan, long long idx_tup_fetch, long long n_tup_ins, long long n_tup_upd, long long n_tup_del) { struct table_node *c = head; /* List is empty, create a new node. */ if (head == NULL) { head = new_table_node(relid); update_table_stats(head, seq_scan, seq_tup_read, idx_scan, idx_tup_fetch, n_tup_ins, n_tup_upd, n_tup_del); return head; } /* Check if this relid exists already. */ while (c != NULL) { if (c->relid == relid) { /* Found an existing node with same relid, update it. */ update_table_stats(c, seq_scan, seq_tup_read, idx_scan, idx_tup_fetch, n_tup_ins, n_tup_upd, n_tup_del); return head; } c = c->next; } /* * Didn't find relid. Create a new node, save the data and insert it. */ c = new_table_node(relid); update_table_stats(c, seq_scan, seq_tup_read, idx_scan, idx_tup_fetch, n_tup_ins, n_tup_upd, n_tup_del); head = insert_table_stats(head, c); return head; } pgtop/pg.h000066400000000000000000000043321271046472400127770ustar00rootroot00000000000000/* Copyright (c) 2007-2015, Mark Wong */ #ifndef _PG_H_ #define _PG_H_ #include #define CHECK_FOR_STATEMENTS_X \ "SELECT 1\n" \ "FROM pg_extension\n" \ "WHERE extname = 'pg_stat_statements'" #define SELECT_INDEX_STATS \ "SELECT indexrelid, indexrelname, idx_scan, idx_tup_read,\n" \ " idx_tup_fetch\n" \ "FROM pg_stat_user_indexes\n" \ "ORDER BY indexrelname" #define SELECT_TABLE_STATS \ "SELECT relid, relname, seq_scan, seq_tup_read, idx_scan,\n" \ " idx_tup_fetch, n_tup_ins, n_tup_upd, n_tup_del\n" \ "FROM pg_stat_user_tables\n" \ "ORDER BY relname" #define SELECT_STATEMENTS \ "WITH aggs AS (\n" \ " SELECT sum(calls) AS calls_total\n" \ " FROM pg_stat_statements\n" \ ")\n" \ "SELECT calls,\n" \ " calls / calls_total AS calls_percentage,\n" \ " to_char(INTERVAL '1 milliseconds' * total_time,\n" \ " 'HH24:MI:SS.MS'),\n" \ " to_char(INTERVAL '1 milliseconds' * (total_time / calls),\n" \ " 'HH24:MI:SS.MS') AS average_time,\n" \ " regexp_replace(query, E'[\\n\\r]+', ' ', 'g' ) AS query\n" \ "FROM pg_stat_statements, aggs\n" \ "ORDER BY %d ASC" /* Table statistics comparison functions for qsort. */ int compare_idx_scan_t(const void *, const void *); int compare_idx_tup_fetch_t(const void *, const void *); int compare_n_tup_del(const void *, const void *); int compare_n_tup_ins(const void *, const void *); int compare_n_tup_upd(const void *, const void *); int compare_seq_scan(const void *, const void *); int compare_seq_tup_read(const void *, const void *); /* Index statistics comparison functions for qsort. */ int compare_idx_scan(const void *, const void *); int compare_idx_tup_fetch(const void *, const void *); int compare_idx_tup_read(const void *, const void *); PGconn *connect_to_db(char *); void pg_display_index_stats(char *, int, int); void pg_display_table_stats(char *, int, int); int pg_display_statements(char *, int, int); PGresult *pg_locks(PGconn *, int); PGresult *pg_processes(PGconn *); PGresult *pg_query(PGconn *, int); extern char *index_ordernames[]; extern char *statement_ordernames[]; extern char *table_ordernames[]; #endif /* _PG_H_ */ pgtop/pg_top.1.in000066400000000000000000000345651271046472400142120ustar00rootroot00000000000000.\" NOTE: changes to the manual page for "pg_top" should be made in the .\" file "pg_top.1.in" and NOT in the file "pg_top.1". .nr N 10 .nr D 5 .TH PG_TOP 1 Local .UC 4 .SH NAME pg_top \- display and update information about the top cpu PostgreSQL processes .SH SYNOPSIS .B pg_top [ OPTIONS ] [ NUMBER ] .SH DESCRIPTION .\" This defines appropriate quote strings for nroff and troff .ds lq \&" .ds rq \&" .if t .ds lq `` .if t .ds rq '' .\" Just in case these number registers aren't set yet... .if \nN==0 .nr N 10 .if \nD==0 .nr D 5 .I pg_top displays the top processes on the system and periodically updates this information. .if \nN==-1 \ \{\ If standard output is an intelligent terminal (see below) then as many processes as will fit on the terminal screen are displayed by default. Otherwise, a good number of them are shown (around 20). .\} Raw cpu percentage is used to rank the processes. If .I number is given, then the top .I number processes will be displayed instead of the default. .PP .I 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 \*(lqintelligent\*(rq 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 .I pg_top is redirected to a file, it acts as if it were being run on a dumb terminal. .SH OPTIONS .TP .B \-C, \-\-color-mode Turn off the use of color in the display. .TP .B \-I, \-\-hide-idle Do not display idle processes. By default, pg_top displays both active and idle processes. .TP .B \-T, \-\-show-tags List all available color tags and the current set of tests used for color highlighting, then exit. .TP .B \-W, \-\-password Forces pg_top to prompt for a password before connecting to a database. .TP .B \-b, \-\-batch Use \*(lqbatch\*(rq 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. .TP .B \-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. .TP .B \-i, \-\-interactive Use \*(lqinteractive\*(rq mode. In this mode, any input is immediately read for processing. See the section on \*(lqInteractive Mode\*(rq 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. .TP .B \-n, \-\-non-interactive Use \*(lqnon-interactive\*(rq mode. This is indentical to \*(lqbatch\*(rq mode. .TP .B \-q, \-\-quick-mode Renice .I pg_top to \-20 so that it will run faster. This can be used when the system is being very sluggish to improve the possibility of discovering the problem. This option can only be used by root. .TP .B \-r, \-\-remote-mode Monitor a remote database where the database is on a system other than where pg_top is running from. .I pg_top will monitor a remote database if it has the pg_proctab extension installed. .TP .B \-u, \-\-show-uid Do not take the time to map uid numbers to usernames. Normally, .I pg_top will read as much of the file \*(lq/etc/passwd\*(rq as is necessary to map all the user id numbers it encounters into login names. This option disables all that, while possibly decreasing execution time. The uid numbers are displayed instead of the names. .TP .B \-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 \*(lq?\*(rq. .TP \fB\-s \fR\fB\fITIME\fR\fR, \fB\-\-set-delay=\fR\fB\fITIME\fR\fR .I TIME Set the delay between screen updates to .I TIME seconds. The default delay between updates is \nD seconds. .TP \fB\-o \fR\fB\fIFIELD\fR\fR, \fB\-\-order-field=\fR\fB\fIFIELD\fR\fR 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 \*(lqcpu\*(rq, \*(lqsize\*(rq, \*(lqres\*(rq, and \*(lqtime\*(rq, but may vary on different operating systems. Note that not all operating systems support this option. .TP \fB\-x \fR\fB\fICOUNT\fR\fR, \fB\-\-set-display=\fR\fB\fICOUNT\fR\fR Show only .I 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 .I pg_top automatically exits. For intelligent terminals, no upper limit is set. The default is 1 for dumb terminals. .TP \fB\-z \fR\fB\fIUSERNAME\fR\fR, \fB\-\-show-username=\fR\fB\fIUSERNAME\fR\fR Show only those processes owned by .IR USERNAME . This option currently only accepts usernames and will not understand uid numbers. .TP \fB\-h \fR\fB\fIHOST\fR\fR, \fB\-\-host=\fR\fB\fIHOST\fR\fR 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. .TP \fB\-p \fR\fB\fIPORT\fR\fR, \fB\-\-port=\fR\fB\fIPORT\fR\fR 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. .TP \fB\-U \fR\fB\fIUSERNAME\fR\fR, \fB\-\-username=\fR\fB\fIUSERNAME\fR\fR User name to connect as. .TP .B \-W, \-\-password Force pg_top to prompt for a password before connecting to a database. .PP Both .I COUNT and .I NUMBER fields can be specified as \*(lqinfinite\*(rq, indicating that they can stretch as far as possible. This is accomplished by using any proper prefix of the keywords \*(lqinfinity\*(rq, \*(lqmaximum\*(rq, or \*(lqall\*(rq. The default for .I count on an intelligent terminal is, in fact, .BI infinity . .PP The environment variable .B 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 .BR PG_TOP . The options .BR \-C , .BR \-I , and .B \-u are actually toggles. A second specification of any of these options will negate the first. Thus a user who has the environment variable .B PG_TOP set to \*(lq\-I\*(rq may use the command \*(lqtop \-I\*(rq to see idle processes. .SH "INTERACTIVE MODE" When .I pg_top is running in \*(lqinteractive mode\*(rq, it reads commands from the terminal and acts upon them accordingly. In this mode, the terminal is put in \*(lqCBREAK\*(rq, so that a character will be processed as soon as it is typed. Almost always, a key will be pressed when .I pg_top is between displays; that is, while it is waiting for .I 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 .I 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 .IR stty ) are recognized, and a newline terminates the input. .PP These commands are currently recognized (^L refers to control-L): .TP .B ^L Redraw the screen. .TP .B A Display the actual query plan (EXPLAIN ANALYZE) of the currently running SQL statement by re-running the SQL statement (prompt for process id.) .TP .B C Toggle the use of color in the display. .TP .B c Toggle the display of the full command line. .TP .B d Change the number of displays to show (prompt for new number). Remember that the next display counts as one, so typing .B d1 will make .I pg_top show one final display and then immediately exit. .IP "\fBh\fP\ or\ \fB?\fP" Display a summary of the commands (help screen). Version information is included in this display. .TP .B E Display re-determined execution plan (EXPLAIN) of the SQL statement by a backend process (prompt for process id.) .TP .B e Display a list of system errors (if any) generated by the last .BR k ill or .BR r enice command. .TP .B i (or .BR I ) Toggle the display of idle processes. .TP .B k Send a signal (\*(lqkill\*(rq by default) to a list of processes. This acts similarly to the command .IR kill (1)). .TP .B L Display the currently held locks by a backend process (prompt for process id.) .TP .B M Order by memory utilization. .TP .B N Sort by process id. .TP .B n or # Change the number of processes to display (prompt for new number). .TP .B 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 fron system to system but usually include: \*(lqcpu\*(rq, \*(lqres\*(rq, \*(lqsize\*(rq, \*(lqtime\*(rq. The default is cpu. When viewing user table statistics: \*(lqseq_scan\*(rq, \*(lqseq_tup_read\*(rq, \*(lqidx_scan\*(rq, \*(lqidx_tup_fetch\*(rq, \*(lqn_tup_ins\*(rq, \*(lqn_tup_upd\*(rq, \*(lqn_tup_del\*(rq. The default is seq_scan. When viewing user index statistics: \*(lqidx_scan\*(rq, \*(lqidx_tup_fetch\*(rq, \*(lqidx_tup_read\*(rq. The default is idx_scan. .TP .B P Sort by processor utilization. .TP .B Q Display the currently running query of a backend process (prompt for process id.) .TP .B q Quit .IR pg_top. .TP .B R Display user table statistics. .TP .B r Change the priority (the \*(lqnice\*(rq) of a list of processes. This acts similarly to the command .IR renice (8)). .TP .B s Change the number of seconds to delay between displays (prompt for new number). .TP .B T Order by time. .TP .B t Toggle between cumulative or differential statistics when viewing user table or user index statistics. .TP .B 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. .TP .B X Display user index statistics. .SH "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. .PP 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. .PP The remainder of the screen displays information about individual processes. This display is similar in spirit to .IR ps (1) but it is not exactly the same. The columns displayed by pg_top will differ slightly between operating systems. Generally, the following fields are displayed: .TP .B PID The process id. .TP .B USERNAME Username of the process's owner (if .B \-u is specified, a UID column will be substituted for USERNAME). .TP .B PRI Current priority of the process. .TP .B NICE Nice amount in the range \-20 to 20, as established by the use of the command .IR nice . .TP .B SIZE Total size of the process (text, data, and stack) given in kilobytes. .TP .B RES Resident memory: current amount of process memory that resides in physical memory, given in kilobytes. .TP .B STATE Current state (typically one of \*(lqsleep\*(rq, \*(lqrun\*(rq, \*(lqidl\*(rq, \*(lqzomb\*(rq, or \*(lqstop\*(rq). .TP .B TIME Number of system and user cpu seconds that the process has used. .TP .B CPU Percentage of available cpu time used by this process. .TP .B COMMAND Name of the command that the process is currently running. .SH COLOR pg_top supports the use of ANSI color in its output. By default, color is available but not used. The environment variable .B TOPCOLORS specifies colors to use and conditions for which they should be used. At the present time, only numbers in the summay 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 .B 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. .PP The environment variable is a sequence of color specifications, separated by colons. Each specification takes the form tag=min,max#code where .I tag is the name of the value to check, .I min and .I max specify a range for the value, and .I code is an ANSI color code. Multiple color codes can be listed and separated with semi-colons. A missing .I min implies the lowest possible value (usually 0) and a missing .I 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 .B 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 .B 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 .I header is used to control the color of the header for process display. It should be specified with no lower and upper limits, specifically .B header=,# followed by the ANSI color code. .PP You can see a list of color codes recognized by this installation of pg_top with the .B \-T option. This will also show the current set of tests used for color highligting, as specified in the environment. .SH AUTHOR William LeFebvre Mark Wong .SH ENVIRONMENT .DT PG_TOP user-configurable defaults for options. PG_TOPCOLORS color specification .SH BUGS As with .IR ps (1), things can change while .I pg_top is collecting information for an update. The picture it gives is only a close approximation to reality. .SH "SEE ALSO" kill(1), ps(1), stty(1), mem(4), renice(8) @MAN_SUPPLEMENT@ pgtop/pg_top.c000066400000000000000000000637621271046472400136700ustar00rootroot00000000000000char *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-2015, Mark Wong * Portions Copyright (c) 2013 VMware, Inc. All Rights Reserved. */ /* * 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 #include #include #include #include /* 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 "username.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'}, {"quick-mode", no_argument, NULL, 'q'}, {"remote-mode", no_argument, NULL, 'r'}, {"set-delay", required_argument, NULL, 's'}, {"show-tags", no_argument, NULL, 'T'}, {"show-uid", no_argument, NULL, 'u'}, {"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_disk) (struct disk_info *) = i_disk; void (*d_message) () = i_message; void (*d_process) (int, char *) = i_process; /* * Mode for display cumulutive or differential stats when displaying table or * index statistics. */ int mode_stats = STATS_DIFF; /* * 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]... [NUMBER]\n", progname); printf("\nOptions:\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(" -q, --quick-mode modify schedule priority\n"); printf(" usable only by root\n"); printf(" -r, --remote-mode activate remote mode\n"); printf(" -s, --set-delay=SECOND set delay between screen updates\n"); printf(" -T, --show-tags show color tags\n"); printf(" -u, --show-uid show UID instead of username\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(" -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\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; register int active_procs; int tmp_index = 0; caddr_t processes; time_t curr_time; static struct ext_decl exts = {NULL, NULL}; switch (pgtctx->mode) { case MODE_IO_STATS: tmp_index = pgtctx->io_order_index; break; default: tmp_index = 0; } /* get the current stats and processes */ if (pgtctx->mode_remote == 0) { get_system_info(&pgtctx->system_info); #ifdef __linux__ processes = get_process_info(&pgtctx->system_info, &pgtctx->ps, tmp_index, pgtctx->conninfo, pgtctx->mode); #else processes = get_process_info(&pgtctx->system_info, &pgtctx->ps, tmp_index, pgtctx->conninfo); #endif /* __linux__ */ } 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); } /* Get database activity information */ get_database_info(&pgtctx->db_info, pgtctx->conninfo); /* Get database I/O information */ get_io_info(&pgtctx->io_info); /* display database disk info */ get_disk_info(&pgtctx->disk_info, get_data_directory(pgtctx->conninfo)); /* 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 database disk info */ (*d_disk) (&pgtctx->disk_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) { case MODE_STATEMENTS: if (pg_display_statements(pgtctx->conninfo, pgtctx->statement_order_index, max_topn) != 0) new_message(MT_standout | MT_delayed, " Extension pg_stat_statments not found"); break; case MODE_INDEX_STATS: pg_display_index_stats(pgtctx->conninfo, pgtctx->index_order_index, max_topn); break; case MODE_TABLE_STATS: pg_display_table_stats(pgtctx->conninfo, pgtctx->table_order_index, max_topn); break; #ifdef __linux__ case MODE_IO_STATS: for (i = 0; i < active_procs; i++) { if (pgtctx->mode_remote == 0) (*d_process) (i, format_next_io(processes, pgtctx->get_userid)); else (*d_process) (i, format_next_io_r(processes)); } break; #endif /* __linux__ */ case MODE_PROCESSES: default: for (i = 0; i < active_procs; i++) { if (pgtctx->mode_remote == 0) (*d_process) (i, format_next_process(processes, pgtctx->get_userid)); 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_disk = u_disk; 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; char *password_tmp; while ((i = getopt_long(ac, av, "CDITbcinqruVh:s:d:U:o:Wp:x: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 'u': /* toggle uid/username display */ pgtctx->do_unames = !pgtctx->do_unames; break; case 'z': /* display only username's processes */ if ((pgtctx->ps.uid = userid(optarg)) == -1) { fprintf(stderr, "%s: unknown user\n", optarg); exit(1); } 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 'q': /* be quick about it */ /* only allow this if user is really root */ if (getuid() == 0) { /* be very un-nice! */ (void) nice(-20); } else { new_message(MT_standout | MT_delayed, " Option -q can only be used by root"); } 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->dbport = i; } break; case 'W': /* prompt for database password */ password_tmp = simple_prompt("Password: ", 1000, 0); /* * get the password in the format we want for the connect string */ sprintf(pgtctx->password, "password=%s", password_tmp); break; case 'U': /* database user name */ sprintf(pgtctx->dbusername, "user=%s", optarg); break; case 'd': /* database name */ sprintf(pgtctx->dbname, "dbname=%s", optarg); break; case 'h': /* socket location */ sprintf(pgtctx->socket, "host=%s", optarg); break; case 'r': /* remote mode */ pgtctx->mode_remote = 1; 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_disk = i_disk; 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< 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 default values from the environment. */ env_top = getenv("PGDATABASE"); if (env_top != NULL) sprintf(pgtctx.dbname, "dbname=%s", getenv("PGDATABASE")); env_top = getenv("PGHOST"); if (env_top != NULL) sprintf(pgtctx.socket, "host=%s", getenv("PGHOST")); env_top = getenv("PGPORT"); if (env_top != NULL) pgtctx.dbport = atoi(getenv("PGPORT")); env_top = getenv("PGUSER"); if (env_top != NULL) sprintf(pgtctx.dbusername, "user=%s", getenv("PGUSER")); /* 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); /* connect to the database */ sprintf(pgtctx.conninfo, "port=%d %s %s %s %s", pgtctx.dbport, pgtctx.dbname, pgtctx.socket, pgtctx.dbusername, pgtctx.password); /* 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.topn = 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 "; pgtctx.get_userid = itoa7; } /* * 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); /* get the string to use for the process area header */ if (pgtctx.mode_remote == 0) pgtctx.header_text = pgtctx.header_processes = format_header(uname_field); else pgtctx.header_text = pgtctx.header_processes = format_header_r(uname_field); #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); #ifdef __linux__ (void) get_process_info(&pgtctx.system_info, &pgtctx.ps, 0, pgtctx.conninfo, pgtctx.mode); #else (void) get_process_info(&pgtctx.system_info, &pgtctx.ps, 0, pgtctx.conninfo); #endif /* __linux__ */ } else { get_system_info_r(&pgtctx.system_info, pgtctx.conninfo); (void) get_process_info_r(&pgtctx.system_info, &pgtctx.ps, 0, pgtctx.conninfo); } /* Get database activity information */ get_database_info(&pgtctx.db_info, pgtctx.conninfo); /* Get database I/O information */ get_io_info(&pgtctx.io_info); /* Get database disk information */ get_disk_info(&pgtctx.disk_info, get_data_directory(pgtctx.conninfo)); 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; } pgtop/pg_top.h000066400000000000000000000054421271046472400136640ustar00rootroot00000000000000/* * Top - a top users display for Berkeley Unix * * General (global) definitions */ #ifndef _PG_TOPP_H_ #define _PG_TOPP_H_ #include "machine.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 struct pg_top_context { #ifdef ENABLE_COLOR int color_on; #endif char conninfo[4096]; struct db_info db_info; char dbname[1024]; int dbport; char dbusername[1024]; int delay; struct disk_info disk_info; int displays; void (*d_header)(char *); char do_unames; char dostates; char *(*get_userid)(uid_t); char *header_text; char *header_processes; int index_order_index; char interactive; struct io_info io_info; int io_order_index; int mode; int mode_remote; /* Mode for monitoring a remote database system. */ int order_index; char *order_name; char password[1001]; struct process_select ps; char show_tags; char socket[1024]; int statement_order_index; struct statics statics; struct system_info system_info; struct timeval timeout; int table_order_index; int topn; }; void quit(int); void reset_display(struct pg_top_context *); #endif /* _PG_TOPP_H_ */ pgtop/pg_trace.h000066400000000000000000000030331271046472400141520ustar00rootroot00000000000000/* ---------- * pg_trace.h * * Definitions for the PostgreSQL tracing framework * * Copyright (c) 2006, PostgreSQL Global Development Group * * $PostgreSQL: pgsql/src/include/pg_trace.h,v 1.2 2006/10/04 00:30:06 momjian Exp $ * ---------- */ #ifndef PG_TRACE_H #define PG_TRACE_H #ifdef ENABLE_DTRACE #include /* * The PG_TRACE macros are mapped to the appropriate macros used by DTrace. * * Only one DTrace provider called "postgresql" will be used for PostgreSQL, * so the name is hard-coded here to avoid having to specify it in the * source code. */ #define PG_TRACE(name) \ DTRACE_PROBE(postgresql, name) #define PG_TRACE1(name, arg1) \ DTRACE_PROBE1(postgresql, name, arg1) #define PG_TRACE2(name, arg1, arg2) \ DTRACE_PROBE2(postgresql, name, arg1, arg2) #define PG_TRACE3(name, arg1, arg2, arg3) \ DTRACE_PROBE3(postgresql, name, arg1, arg2, arg3) #define PG_TRACE4(name, arg1, arg2, arg3, arg4) \ DTRACE_PROBE4(postgresql, name, arg1, arg2, arg3, arg4) #define PG_TRACE5(name, arg1, arg2, arg3, arg4, arg5) \ DTRACE_PROBE5(postgresql, name, arg1, arg2, arg3, arg4, arg5) #else /* not ENABLE_DTRACE */ /* * Unless DTrace is explicitly enabled with --enable-dtrace, the PG_TRACE * macros will expand to no-ops. */ #define PG_TRACE(name) #define PG_TRACE1(name, arg1) #define PG_TRACE2(name, arg1, arg2) #define PG_TRACE3(name, arg1, arg2, arg3) #define PG_TRACE4(name, arg1, arg2, arg3, arg4) #define PG_TRACE5(name, arg1, arg2, arg3, arg4, arg5) #endif /* not ENABLE_DTRACE */ #endif /* PG_TRACE_H */ pgtop/port.h000066400000000000000000000263431271046472400133630ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * 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 PG_PORT_H #define PG_PORT_H #include #include #include #define bool int /* non-blocking */ extern bool pg_set_noblock(int sock); extern bool pg_set_block(int sock); /* Portable path handling for Unix/Win32 (in path.c) */ extern char *first_dir_separator(const char *filename); extern char *last_dir_separator(const char *filename); extern char *first_path_separator(const char *pathlist); extern void join_path_components(char *ret_path, const char *head, const char *tail); extern void canonicalize_path(char *path); extern void make_native_path(char *path); extern bool path_contains_parent_reference(const char *path); extern bool path_is_prefix_of_path(const char *path1, const char *path2); extern const char *get_progname(const char *argv0); extern void get_share_path(const char *my_exec_path, char *ret_path); extern void get_etc_path(const char *my_exec_path, char *ret_path); extern void get_include_path(const char *my_exec_path, char *ret_path); extern void get_pkginclude_path(const char *my_exec_path, char *ret_path); extern void get_includeserver_path(const char *my_exec_path, char *ret_path); extern void get_lib_path(const char *my_exec_path, char *ret_path); extern void get_pkglib_path(const char *my_exec_path, char *ret_path); extern void get_locale_path(const char *my_exec_path, char *ret_path); extern void get_doc_path(const char *my_exec_path, char *ret_path); extern void get_man_path(const char *my_exec_path, char *ret_path); extern bool get_home_path(char *ret_path); extern void get_parent_directory(char *path); /* * is_absolute_path * * By making this a macro we avoid needing to include path.c in libpq. */ #ifndef WIN32 #define is_absolute_path(filename) \ ( \ ((filename)[0] == '/') \ ) #else #define is_absolute_path(filename) \ ( \ ((filename)[0] == '/') || \ (filename)[0] == '\\' || \ (isalpha((unsigned char) ((filename)[0])) && (filename)[1] == ':' && \ ((filename)[2] == '\\' || (filename)[2] == '/')) \ ) #endif /* Portable locale initialization (in exec.c) */ extern void set_pglocale_pgservice(const char *argv0, const char *app); /* Portable way to find binaries (in exec.c) */ extern int find_my_exec(const char *argv0, char *retpath); extern int find_other_exec(const char *argv0, const char *target, const char *versionstr, char *retpath); #if defined(WIN32) || defined(__CYGWIN__) #define EXE ".exe" #else #define EXE "" #endif #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 /* * Win32 needs double quotes at the beginning and end of system() * strings. If not, it gets confused with multiple quoted strings. * It also requires double-quotes around the executable name and * any files used for redirection. Other args can use single-quotes. * * Generated using Win32 "CMD /?": * * 1. If all of the following conditions are met, then quote characters * on the command line are preserved: * * - no /S switch * - exactly two quote characters * - no special characters between the two quote characters, where special * is one of: &<>()@^| * - there are one or more whitespace characters between the the two quote * characters * - the string between the two quote characters is the name of an * executable file. * * 2. Otherwise, old behavior is to see if the first character is a quote * character and if so, strip the leading character and remove the last * quote character on the command line, preserving any text after the last * quote character. */ #if defined(WIN32) && !defined(__CYGWIN__) #define SYSTEMQUOTE "\"" #else #define SYSTEMQUOTE "" #endif /* Portable delay handling */ extern void pg_usleep(long microsec); /* Portable SQL-like case-independent comparisons and conversions */ extern int pg_strcasecmp(const char *s1, const char *s2); extern int pg_strncasecmp(const char *s1, const char *s2, size_t n); extern unsigned char pg_toupper(unsigned char ch); extern unsigned char pg_tolower(unsigned char ch); #ifdef USE_REPL_SNPRINTF /* * Versions of libintl >= 0.13 try to replace printf() and friends with * macros to their own versions that understand the %$ format. We do the * same, so disable their macros, if they exist. */ #ifdef vsnprintf #undef vsnprintf #endif #ifdef snprintf #undef snprintf #endif #ifdef sprintf #undef sprintf #endif #ifdef vfprintf #undef vfprintf #endif #ifdef fprintf #undef fprintf #endif #ifdef printf #undef printf #endif extern int pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args); extern int pg_snprintf(char *str, size_t count, const char *fmt,...) /* This extension allows gcc to check the format string */ __attribute__((format(printf, 3, 4))); extern int pg_sprintf(char *str, const char *fmt,...) /* This extension allows gcc to check the format string */ __attribute__((format(printf, 2, 3))); extern int pg_vfprintf(FILE * stream, const char *fmt, va_list args); extern int pg_fprintf(FILE * stream, const char *fmt,...) /* This extension allows gcc to check the format string */ __attribute__((format(printf, 2, 3))); extern int pg_printf(const char *fmt,...) /* This extension allows gcc to check the format string */ __attribute__((format(printf, 1, 2))); /* * The GCC-specific code below prevents the __attribute__(... 'printf') * above from being replaced, and this is required because gcc doesn't * know anything about pg_printf. */ #ifdef __GNUC__ #define vsnprintf(...) pg_vsnprintf(__VA_ARGS__) #define snprintf(...) pg_snprintf(__VA_ARGS__) #define sprintf(...) pg_sprintf(__VA_ARGS__) #define vfprintf(...) pg_vfprintf(__VA_ARGS__) #define fprintf(...) pg_fprintf(__VA_ARGS__) #define printf(...) pg_printf(__VA_ARGS__) #else #define vsnprintf pg_vsnprintf #define snprintf pg_snprintf #define sprintf pg_sprintf #define vfprintf pg_vfprintf #define fprintf pg_fprintf #define printf pg_printf #endif #endif /* USE_REPL_SNPRINTF */ /* Portable prompt handling */ extern char *simple_prompt(const char *prompt, int maxlen, bool echo); /* * WIN32 doesn't allow descriptors returned by pipe() to be used in select(), * so for that platform we use socket() instead of pipe(). * There is some inconsistency here because sometimes we require pg*, like * pgpipe, but in other cases we define rename to pgrename just on Win32. */ #ifndef WIN32 /* * The function prototypes are not supplied because every C file * includes this file. */ #define pgpipe(a) pipe(a) #define piperead(a,b,c) read(a,b,c) #define pipewrite(a,b,c) write(a,b,c) #else extern int pgpipe(int handles[2]); extern int piperead(int s, char *buf, int len); #define pipewrite(a,b,c) send(a,b,c,0) #define PG_SIGNAL_COUNT 32 #define kill(pid,sig) pgkill(pid,sig) extern int pgkill(int pid, int sig); #endif extern int pclose_check(FILE * stream); /* Global variable holding time zone information. */ #ifndef __CYGWIN__ #define TIMEZONE_GLOBAL timezone #define TZNAME_GLOBAL tzname #else #define TIMEZONE_GLOBAL _timezone #define TZNAME_GLOBAL _tzname #endif #if defined(WIN32) || defined(__CYGWIN__) /* * Win32 doesn't have reliable rename/unlink during concurrent access, * and we need special code to do symlinks. */ extern int pgrename(const char *from, const char *to); extern int pgunlink(const char *path); /* Include this first so later includes don't see these defines */ #ifdef WIN32_ONLY_COMPILER #include #endif #define rename(from, to) pgrename(from, to) #define unlink(path) pgunlink(path) /* * Cygwin has its own symlinks which work on Win95/98/ME where * junction points don't, so use it instead. We have no way of * knowing what type of system Cygwin binaries will be run on. * Note: Some CYGWIN includes might #define WIN32. */ #if defined(WIN32) && !defined(__CYGWIN__) extern int pgsymlink(const char *oldpath, const char *newpath); #define symlink(oldpath, newpath) pgsymlink(oldpath, newpath) #endif #endif /* defined(WIN32) || defined(__CYGWIN__) */ extern void copydir(char *fromdir, char *todir, bool recurse); extern bool rmtree(char *path, bool rmtopdir); #if defined(WIN32) && !defined(__CYGWIN__) /* open() and fopen() replacements to allow deletion of open files and * passing of other special options. */ extern int pgwin32_open(const char *, int,...); extern FILE *pgwin32_fopen(const char *, const char *); #ifndef FRONTEND #define open(a,b,c) pgwin32_open(a,b,c) #define fopen(a,b) pgwin32_fopen(a,b) #endif #define popen(a,b) _popen(a,b) #define pclose(a) _pclose(a) /* Missing rand functions */ extern long lrand48(void); extern void srand48(long seed); /* Last parameter not used */ extern int gettimeofday(struct timeval * tp, struct timezone * tzp); #else /* !WIN32 */ /* * Win32 requires a special close for sockets and pipes, while on Unix * close() does them all. */ #define closesocket close #endif /* WIN32 */ /* * Default "extern" declarations or macro substitutes for library routines. * When necessary, these routines are provided by files in src/port/. */ #ifndef HAVE_CRYPT extern char *crypt(const char *key, const char *setting); #endif #if defined(bsdi) || defined(netbsd) extern int fseeko(FILE * stream, off_t offset, int whence); extern off_t ftello(FILE * stream); #endif #ifndef HAVE_FSEEKO #define fseeko(a, b, c) fseek(a, b, c) #define ftello(a) ftell(a) #endif #ifndef HAVE_GETOPT extern int getopt(int nargc, char *const * nargv, const char *ostr); #endif #ifndef HAVE_ISINF extern int isinf(double x); #endif #ifndef HAVE_RINT extern double rint(double x); #endif #ifndef HAVE_INET_ATON #include #include extern int inet_aton(const char *cp, struct in_addr * addr); #endif #ifndef HAVE_STRDUP /* extern char *strdup(const char *str); */ #endif #if !HAVE_DECL_STRLCPY extern size_t strlcpy(char *dst, const char *src, size_t siz); #endif #if !defined(HAVE_RANDOM) && !defined(__BORLANDC__) extern long random(void); #endif #ifndef HAVE_UNSETENV /* extern void unsetenv(const char *name); */ #endif #ifndef HAVE_SRANDOM /* extern void srandom(unsigned int seed); */ #endif /* thread.h */ extern char *pqStrerror(int errnum, char *strerrbuf, size_t buflen); #if !defined(WIN32) || defined(__CYGWIN__) extern int pqGetpwuid(uid_t uid, struct passwd * resultbuf, char *buffer, size_t buflen, struct passwd ** result); #endif extern int pqGethostbyname(const char *name, struct hostent * resultbuf, char *buffer, size_t buflen, struct hostent ** result, int *herrno); extern void pg_qsort(void *base, size_t nel, size_t elsize, int (*cmp) (const void *, const void *)); #define qsort(a,b,c,d) pg_qsort(a,b,c,d) typedef int (*qsort_arg_comparator) (const void *a, const void *b, void *arg); extern void qsort_arg(void *base, size_t nel, size_t elsize, qsort_arg_comparator cmp, void *arg); #endif /* PG_PORT_H */ pgtop/remote.h000066400000000000000000000006501271046472400136630ustar00rootroot00000000000000/* * Copyright (c) 2008-2009, Mark Wong */ #ifndef _REMOTE_H_ #define _REMOTE_H_ #include "machine.h" int machine_init_r(struct statics *, char *); void get_system_info_r(struct system_info *, char *); caddr_t get_process_info_r(struct system_info *, struct process_select *, int, char *); char *format_header_r(char *); char *format_next_io_r(caddr_t); char *format_next_process_r(caddr_t); #endif /* _REMOTE_H_ */ pgtop/screen.c000066400000000000000000000233641271046472400136510ustar00rootroot00000000000000/* * 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 #ifdef CBREAK #include #define SGTTY #else #ifdef TCGETA #define TERMIO #include #else #define TERMIOS #include #endif #endif #if defined(TERMIO) || defined(TERMIOS) #ifndef TAB3 #ifdef OXTABS #define TAB3 OXTABS #else #define TAB3 0 #endif #endif #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 *tgetstr(); char *tgoto(); char termcap_buf[1024]; char string_buffer[1024]; char home[15]; 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 #ifdef TERMIO static struct termio old_settings; static struct termio new_settings; #endif #ifdef TERMIOS static struct termios old_settings; static struct termios new_settings; #endif 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; char *getenv(); 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 #ifdef TERMIO if (ioctl(STDOUT, TCGETA, &old_settings) == -1) { smart_terminal = No; } #endif #ifdef TERMIOS if (tcgetattr(STDOUT, &old_settings) == -1) { smart_terminal = No; } #endif } 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 #ifdef TERMIO if (ioctl(STDOUT, TCGETA, &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) ioctl(STDOUT, TCSETA, &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); } #endif #ifdef TERMIOS 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); } #endif 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 #ifdef TERMIO (void) ioctl(STDOUT, TCSETA, &old_settings); #endif #ifdef TERMIOS (void) tcsetattr(STDOUT, TCSADRAIN, &old_settings); #endif } } 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 #ifdef TERMIO (void) ioctl(STDOUT, TCSETA, &new_settings); #endif #ifdef TERMIOS (void) tcsetattr(STDOUT, TCSADRAIN, &new_settings); #endif } /* 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); } } pgtop/screen.h000066400000000000000000000026371271046472400136560ustar00rootroot00000000000000/* * 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 #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_ */ pgtop/sigconv.awk000066400000000000000000000022741271046472400143770ustar00rootroot00000000000000BEGIN { nsig = 0; j = 0; print "/* This file was automatically generated */" print "/* by the awk script \"sigconv.awk\". */\n" print "struct sigdesc {" print " char *name;" print " int number;" print "};\n" print "struct sigdesc sigdesc[] = {" } /^#define[ \t][ \t]*SIG[A-Z]/ { j = sprintf("%d", $3); if (siglist[j] != "") next; str = $2; if (nsig < j) nsig = j; siglist[j] = sprintf("\"%s\",\t%2d", \ substr(str, 4), j); } /^#[ \t]*define[ \t][ \t]*SIG[A-Z]/ { j = sprintf("%d", $4); if (siglist[j] != "") next; str = $3; if (nsig < j) nsig = j; siglist[j] = sprintf("\"%s\",\t%2d", \ substr(str, 4), j); } /^#[ \t]*define[ \t][ \t]*_SIG[A-Z]/ { j = sprintf("%d", $4); if (siglist[j] != "") next; str = $3; if (nsig < j) nsig = j; siglist[j] = sprintf("\"%s\",\t%2d", \ substr(str, 5), j); } END { for (n = 1; n <= nsig; n++) if (siglist[n] != "") printf(" { %s },\n", siglist[n]); printf(" { NULL,\t 0 }\n};\n"); } pgtop/sprompt.c000066400000000000000000000062561271046472400140770ustar00rootroot00000000000000/*------------------------------------------------------------------------- * * 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" #ifdef HAVE_TERMIOS_H #include #endif 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; #ifdef HAVE_TERMIOS_H struct termios t_orig, t; #else #ifdef WIN32 HANDLE t = NULL; LPDWORD t_orig = NULL; #endif #endif 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 #ifdef WIN32 /* See DEVTTY comment for msys */ || (getenv("OSTYPE") && strcmp(getenv("OSTYPE"), "msys") == 0) #endif ) { if (termin) fclose(termin); if (termout) fclose(termout); termin = stdin; termout = stderr; } #ifdef HAVE_TERMIOS_H if (!echo) { tcgetattr(fileno(termin), &t); t_orig = t; t.c_lflag &= ~ECHO; tcsetattr(fileno(termin), TCSAFLUSH, &t); } #else #ifdef WIN32 if (!echo) { /* get a new handle to turn echo off */ t_orig = (LPDWORD) malloc(sizeof(DWORD)); t = GetStdHandle(STD_INPUT_HANDLE); /* save the old configuration first */ GetConsoleMode(t, t_orig); /* set to the new mode */ SetConsoleMode(t, ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT); } #endif #endif 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'; #ifdef HAVE_TERMIOS_H if (!echo) { tcsetattr(fileno(termin), TCSAFLUSH, &t_orig); fputs("\n", termout); fflush(termout); } #else #ifdef WIN32 if (!echo) { /* reset to the original console mode */ SetConsoleMode(t, *t_orig); fputs("\n", termout); fflush(termout); free(t_orig); } #endif #endif if (termin != stdin) { fclose(termin); fclose(termout); } return destination; } pgtop/username.c000066400000000000000000000111261271046472400142020ustar00rootroot00000000000000/* * 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 */ /* * Username translation code for top. * * These routines handle uid to username mapping. * They use a hashing table scheme to reduce reading overhead. * For the time being, these are very straightforward hashing routines. * Maybe someday I'll put in something better. But with the advent of * "random access" password files, it might not be worth the effort. * * Changes to these have been provided by John Gilmore (gnu@toad.com). * * The hash has been simplified in this release, to avoid the * table overflow problems of previous releases. If the value * at the initial hash location is not right, it is replaced * by the right value. Collisions will cause us to call getpw* * but hey, this is a cache, not the Library of Congress. * This makes the table size independent of the passwd file size. */ #include "os.h" #include #include "pg_top.h" #include "utils.h" struct hash_el { int uid; char name[9]; }; #define is_empty_hash(x) (hash_table[x].name[0] == 0) /* simple minded hashing function */ /* Uid "nobody" is -2 results in hashit(-2) = -2 which is out of bounds for the hash_table. Applied abs() function to fix. 2/16/96 tpugh */ #define hashit(i) (abs(i) % Table_size) /* K&R requires that statically declared tables be initialized to zero. */ /* We depend on that for hash_table and YOUR compiler had BETTER do it! */ struct hash_el hash_table[Table_size]; void init_hash() { /* * There used to be some steps we had to take to initialize things. We * don't need to do that anymore, but we will leave this stub in just in * case future changes require initialization steps. */ } int enter_user(int uid, char *name, int wecare) { register int hashindex; #ifdef DEBUG dprintf("enter_hash(%d, %s, %d)\n", uid, name, wecare); #endif hashindex = hashit(uid); if (!is_empty_hash(hashindex)) { if (!wecare) return 0; /* Don't clobber a slot for trash */ if (hash_table[hashindex].uid == uid) return (hashindex); /* Fortuitous find */ } /* empty or wrong slot -- fill it with new value */ hash_table[hashindex].uid = uid; (void) strncpy(hash_table[hashindex].name, name, 8); return (hashindex); } /* * Get a userid->name mapping from the system. * If the passwd database is hashed (#define RANDOM_PW), we * just handle this uid. Otherwise we scan the passwd file * and cache any entries we pass over while looking. */ int get_user(int uid) { struct passwd *pwd; #ifdef RANDOM_PW /* no performance penalty for using getpwuid makes it easy */ if ((pwd = getpwuid(uid)) != NULL) { return (enter_user(pwd->pw_uid, pwd->pw_name, 1)); } #else int from_start = 0; /* * If we just called getpwuid each time, things would be very slow since * that just iterates through the passwd file each time. So, we walk * through the file instead (using getpwent) and cache each entry as we * go. Once the right record is found, we cache it and return * immediately. The next time we come in, getpwent will get the next * record. In theory, we never have to read the passwd file a second time * (because we cache everything we read). But in practice, the cache may * not be large enough, so if we don't find it the first time we have to * scan the file a second time. This is not very efficient, but it will * do for now. */ while (from_start++ < 2) { while ((pwd = getpwent()) != NULL) { if (pwd->pw_uid == uid) { return (enter_user(pwd->pw_uid, pwd->pw_name, 1)); } (void) enter_user(pwd->pw_uid, pwd->pw_name, 0); } /* try again */ setpwent(); } #endif /* if we can't find the name at all, then use the uid as the name */ return (enter_user(uid, itoa7(uid), 1)); } char * username(uid_t uid) { register int hashindex; hashindex = hashit(uid); if (is_empty_hash(hashindex) || (hash_table[hashindex].uid != uid)) { /* not here or not right -- get it out of passwd */ hashindex = get_user(uid); } return (hash_table[hashindex].name); } int userid(char *username) { struct passwd *pwd; /* * Eventually we want this to enter everything in the hash table, but for * now we just do it simply and remember just the result. */ if ((pwd = getpwnam(username)) == NULL) { return (-1); } /* enter the result in the hash table */ enter_user(pwd->pw_uid, username, 1); /* return our result */ return (pwd->pw_uid); } pgtop/username.h000066400000000000000000000002671271046472400142130ustar00rootroot00000000000000/* interface for username.c */ #ifndef _USERNAME_H_ #define _USERNAME_H_ void init_hash(); char *username(uid_t uid); int userid(char *username); #endif /* _USERNAME_H_ */ pgtop/utils.c000066400000000000000000000321631271046472400135270ustar00rootroot00000000000000/* * 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 #ifdef HAVE_STDARG_H #include #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; 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][16]; 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][16]; 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 pgtop/utils.h000066400000000000000000000017761271046472400135420ustar00rootroot00000000000000/* * 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_ */ pgtop/version.c000066400000000000000000000006141271046472400140500ustar00rootroot00000000000000/* * 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); } pgtop/version.h000066400000000000000000000006131271046472400140540ustar00rootroot00000000000000/* * 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_ */