/*
 * pidstat: Report statistics for Linux tasks
 * (C) 2007-2025 by Sebastien GODARD (sysstat <at> orange.fr)
 *
 ***************************************************************************
 * This program is free software; you can redistribute it and/or modify it *
 * under the terms of the GNU General Public License as published  by  the *
 * Free Software Foundation; either version 2 of the License, or (at  your *
 * option) any later version.                                              *
 *                                                                         *
 * This program is distributed in the hope that it  will  be  useful,  but *
 * WITHOUT ANY WARRANTY; without the implied warranty  of  MERCHANTABILITY *
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License *
 * for more details.                                                       *
 *                                                                         *
 * You should have received a copy of the GNU General Public License along *
 * with this program; if not, write to the Free Software Foundation, Inc., *
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA              *
 ***************************************************************************
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <dirent.h>
#include <ctype.h>
#include <sys/types.h>
#include <pwd.h>
#include <sys/utsname.h>
#include <sys/wait.h>
#include <regex.h>

#ifdef HAVE_LINUX_SCHED_H
#include <linux/sched.h>
#endif

#include "version.h"
#include "pidstat.h"
#include "rd_stats.h"
#include "count.h"

#include <locale.h>	/* For setlocale() */
#ifdef USE_NLS
#include <libintl.h>
#define _(string) gettext(string)
#else
#define _(string) (string)
#endif

#ifdef USE_SCCSID
#define SCCSID "@(#)sysstat-" VERSION ": " __FILE__ " compiled " __DATE__ " " __TIME__
char *sccsid(void) { return (SCCSID); }
#endif

#ifdef TEST
extern int __env;
#endif

unsigned long long tot_jiffies[3] = {0, 0, 0};
unsigned long long uptime_cs[3] = {0, 0, 0};
struct st_pid *pid_list = NULL;

struct tm ps_tstamp[3];
char commstr[MAX_COMM_LEN];
char userstr[MAX_USER_LEN];
char procstr[MAX_COMM_LEN];

int cpu_nr = 0;			/* Nb of processors on the machine */
unsigned long tlmkb;		/* Total memory in kB */
long interval = -1;
long count = 0;
unsigned int pidflag = 0;	/* General flags */
unsigned int tskflag = 0;	/* TASK/CHILD stats */
unsigned int actflag = 0;	/* Activity flag */
uint64_t xflags = 0;		/* Extended flag for options used by multiple commands */

struct sigaction alrm_act, int_act, chld_act;
int signal_caught = 0;
int status = 0;

int dplaces_nr = -1;		/* Number of decimal places */

/*
 ***************************************************************************
 * Print usage and exit.
 *
 * IN:
 * @progname	Name of sysstat command
 ***************************************************************************
 */
void usage(char *progname)
{
	fprintf(stderr, _("Usage: %s [ options ] [ <interval> [ <count> ] ] [ -e <program> <args> ]\n"),
		progname);

	fprintf(stderr, _("Options are:\n"
			  "[ -d ] [ -H ] [ -h ] [ -I ] [ -l ] [ -R ] [ -r ] [ -s ] [ -t ] [ -U [ <username> ] ]\n"
			  "[ -u ] [ -V ] [ -v ] [ -w ] [ -C <command> ] [ -G <process_name> ]\n"
			  "[ -p { <pid> [,...] | SELF | ALL } ] [ -T { TASK | CHILD | ALL } ]\n"
			  "[ --dec={ 0 | 1 | 2 } ] [ --human ] [ -o JSON ]\n"));
	exit(1);
}

/*
 ***************************************************************************
 * SIGALRM signal handler. No need to reset the handler here.
 *
 * IN:
 * @sig	Signal number.
 ***************************************************************************
 */
void alarm_handler(int sig)
{
	alarm(interval);
}

/*
 ***************************************************************************
 * SIGINT, SIGTERM and SIGCHLD signals handler.
 *
 * IN:
 * @sig	Signal number.
 ***************************************************************************
 */
void int_handler(int sig)
{
	int status_code;

	signal_caught = TRUE;

	if (sig == SIGCHLD) {
		/*
		 * SIGCHLD tells the parent process that it can now
		 * get the exit status of the child via wait().
		 */
		wait(&status_code);
		status = WEXITSTATUS(status_code);
	}
}

/*
 ***************************************************************************
 * Free unused PID structures.
 *
 * IN
 * @plist	Pointer address on the start of the linked list.
 * @force	Set to TRUE if all PID structures shall be freed.
 ***************************************************************************
 */
void sfree_pid(struct st_pid **plist, int force)
{
	int i;
	struct st_pid *p;

	while (*plist != NULL) {

		p = *plist;
		if (!p->exist || force) {
			*plist = p->next;
			for (i = 0; i < 3; i++) {
				if (p->pstats[i]) {
					free(p->pstats[i]);
				}
			}
			free(p);
		}
		else {
			plist = &(p->next);
		}
	}
}

/*
 ***************************************************************************
 * Set every PID in list to nonexistent status.
 *
 * IN:
 * @plist	Pointer on the start of the linked list.
 ***************************************************************************
 */
void set_pid_nonexistent(struct st_pid *plist)
{
	while (plist != NULL) {
		plist->exist = FALSE;
		plist = plist->next;
	}
}

/*
 ***************************************************************************
 * Check flags and set default values.
 ***************************************************************************
 */
void check_flags(void)
{
	unsigned int act = 0;

	/* Display CPU usage for active tasks by default */
	if (!actflag) {
		actflag |= P_A_CPU;
	}

	if (!DISPLAY_PID(pidflag)) {
		/*
		 * If no PIDs nor -p ALL entered on the command line then
		 * only active PIDs will be displayed.
		 */
		pidflag |= P_D_ACTIVE_PID + P_D_ALL_PID;
	}

	if (!tskflag) {
		tskflag |= P_TASK;
	}

	/* Check that requested activities are available */
	if (DISPLAY_TASK_STATS(tskflag)) {
		act |= P_A_CPU + P_A_MEM + P_A_IO + P_A_CTXSW
		     + P_A_STACK + P_A_KTAB + P_A_RT;
	}
	if (DISPLAY_CHILD_STATS(tskflag)) {
		act |= P_A_CPU + P_A_MEM;
	}

	actflag &= act;

	if (!actflag) {
		fprintf(stderr, _("Requested activities not available\n"));
		exit(1);
	}
}

/*
 ***************************************************************************
 * Look for the PID in the list and store it if necessary.
 *       PID ->  PID -> TGID ->  TID ->  TID ->  TID ->  PID -> NULL
 * Eg.: 1234 -> 1289 -> 1356 -> 1356 -> 1361 -> 4678 -> 1376 -> NULL
 *
 * IN:
 * @plist	Pointer address on the start of the linked list.
 * @pid		PID number.
 * @tgid	If PID is a TID then @tgid is its TGID number. 0 otherwise.
 *
 * RETURNS:
 * Pointer on the st_pid structure in the list where the PID is located
 * (whether it was already in the list or if it has been added).
 * NULL if the PID is 0 or it is a TID and its TGID has not been found in
 * list.
 ***************************************************************************
 */
struct st_pid *add_list_pid(struct st_pid **plist, pid_t pid, pid_t tgid)
{
	struct st_pid *p, *ps, *tgid_p = NULL;
	int i;

	if (!pid)
		return NULL;

	if (!tgid) {
		/*
		 * Add a true PID to the list.
		 * Add it in ascending order, not taking into account
		 * other TIDs.
		 */
		while (*plist != NULL) {

			p = *plist;
			if (!p->tgid && (p->pid == pid))
				/* PID found in list */
				return p;

			if (!p->tgid && (p->pid > pid))
				/* Stop now to insert PID in list */
				break;

			plist = &(p->next);
		}
	}
	else {
		int tgid_found = FALSE;

		/*
		 * PID is a TID.
		 * It will be inserted in ascending order immediately
		 * following its TGID.
		 */
		while (*plist != NULL) {

			p = *plist;
			if (p->pid == tgid) {
				/* TGID found in list */
				tgid_found = TRUE;
				tgid_p = p;
				break;
			}

			plist = &(p->next);
		}
		if (!tgid_found)
			/* TGID not found: Stop now */
			return NULL;

		plist = &(p->next);
		while (*plist != NULL) {

			p = *plist;
			if ((p->tgid == tgid_p) && (p->pid == pid))
				/* TID found in list */
				return p;

			if ((p->tgid == tgid_p) && (p->pid > pid))
				/* Stop now to insert TID in list */
				break;
			if (p->tgid != tgid_p)
				/* End of TID list: insert TID here */
				break;

			plist = &(p->next);
		}
	}

	/* PID not found */
	ps = *plist;

	/* Add PID to the list */
	if ((*plist = (struct st_pid *) malloc(sizeof(struct st_pid))) == NULL) {
		perror("malloc");
		exit(4);
	}
	memset(*plist, 0, sizeof(struct st_pid));

	p = *plist;
	for (i = 0; i < 3; i++) {
		if ((p->pstats[i] = (struct pid_stats *) malloc(sizeof(struct pid_stats))) == NULL) {
			perror("malloc");
			exit(4);
		}
		memset(p->pstats[i], 0, PID_STATS_SIZE);
	}
	p->pid = pid;
	p->next = ps;
	if (tgid_p) {
		p->tgid = tgid_p;
	}

	return p;
}

/*
 ***************************************************************************
 * Get pointer on task's command string.
 * If this is a thread then return the short command name so that threads
 * can still be identified.
 *
 * IN:
 * @plist	Pointer address on the start of the linked list.
 ***************************************************************************
 */
char *get_tcmd(struct st_pid *plist)
{
	if (DISPLAY_CMDLINE(pidflag) && strlen(plist->cmdline) && !plist->tgid)
		/* Option "-l" used */
		return plist->cmdline;
	else
		return plist->comm;
}

/*
 ***************************************************************************
 * Display process command name or command line.
 *
 * IN:
 * @plist	Pointer address on the start of the linked list.
 ***************************************************************************
 */
void print_comm(struct st_pid *plist)
{
	char *p;

	/* Get pointer on task's command string */
	p = get_tcmd(plist);

	if (plist->tgid) {
		if (IS_PID_DISPLAYED(plist->tgid->flags)) {
			cprintf_s(IS_ZERO, "  |__%s", p);
		}
		else {
			/* Its TGID has not been displayed */
			cprintf_s(IS_STR, "  (%s)", plist->tgid->comm);
			cprintf_s(IS_ZERO, "__%s", p);

			/* We can now consider this has been the case */
			plist->tgid->flags |= F_PID_DISPLAYED;
		}
	}
	else {
		cprintf_s(IS_STR, "  %s", p);
	}
	printf("\n");
}

/*
 * **************************************************************************
 * Display process command name or command line in JSON format.
 *
 * IN:
 * @plist	Pointer address on the start of the linked list.
 ***************************************************************************
 */
void print_json_comm(struct st_pid *plist)
{
	char *p;

	/* Get pointer on task's command string */
	p = get_tcmd(plist);

	if (DISPLAY_TID(pidflag)) {
		/* This is a TID */
		printf("\"leader\": \"");
		if (plist->tgid) {
			/* Print thead group leader command then task one */
			printf("%s", plist->tgid->comm);
		} /* else this is a PID (TGID) */

		printf("\", ");
	}
	printf("\"cmd\": \"%s\"}", p);
}

/*
 ***************************************************************************
 * Read /proc/meminfo.
 ***************************************************************************
 */
void read_proc_meminfo(void)
{
	struct stats_memory st_mem;

	memset(&st_mem, 0, STATS_MEMORY_SIZE);
	read_meminfo(&st_mem);
	tlmkb = st_mem.tlmkb;
}

/*
 ***************************************************************************
 * Read stats from /proc/#[/task/##]/stat.
 *
 * IN:
 * @pid		Process whose stats are to be read.
 * @plist	Pointer on the linked list where PID is saved.
 * @tgid	If !=0, thread whose stats are to be read.
 * @curr	Index in array for current sample statistics.
 *
 * OUT:
 * @thread_nr	Number of threads of the process.
 *
 * RETURNS:
 * 0 if stats have been successfully read, and 1 otherwise.
 ***************************************************************************
 */
int read_proc_pid_stat(pid_t pid, struct st_pid *plist,
		       unsigned int *thread_nr, pid_t tgid, int curr)
{
	int fd, sz, rc, commsz;
	char filename[128];
	static char buffer[1024 + 1];
	char *start, *end;
	struct pid_stats *pst = plist->pstats[curr];

	if (tgid) {
		sprintf(filename, TASK_STAT, tgid, pid);
	}
	else {
		sprintf(filename, PID_STAT, pid);
	}

	if ((fd = open(filename, O_RDONLY)) < 0)
		/* No such process */
		return 1;

	sz = read(fd, buffer, 1024);
	close(fd);
	if (sz <= 0)
		return 1;
	buffer[sz] = '\0';

	if ((start = strchr(buffer, '(')) == NULL)
		return 1;
	start += 1;
	if ((end = strrchr(start, ')')) == NULL)
		return 1;
	commsz = end - start;
	if (commsz >= MAX_COMM_LEN)
		return 1;
	memcpy(plist->comm, start, commsz);
	plist->comm[commsz] = '\0';
	start = end + 2;

	rc = sscanf(start,
		    "%*s %*d %*d %*d %*d %*d %*u %llu %llu"
		    " %llu %llu %llu %llu %lld %lld %*d %*d %u %*u %*d %llu %llu"
		    " %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u"
		    " %*u %u %u %u %llu %llu %lld\n",
		    &pst->minflt, &pst->cminflt, &pst->majflt, &pst->cmajflt,
		    &pst->utime,  &pst->stime, &pst->cutime, &pst->cstime,
		    thread_nr, &pst->vsz, &pst->rss, &pst->processor,
		    &pst->priority, &pst->policy,
		    &pst->blkio_swapin_delays, &pst->gtime, &pst->cgtime);

	if (rc < 15)
		return 1;

	if (rc < 17) {
		/* gtime and cgtime fields are unavailable in file */
		pst->gtime = pst->cgtime = 0;
	}

	/* Convert to kB */
	pst->vsz >>= 10;
	pst->rss = PG_TO_KB(pst->rss);

	return 0;
}

/*
 ***************************************************************************
 * Read stats from /proc/#[/task/##]/schedstat.
 *
 * IN:
 * @pid		Process whose stats are to be read.
 * @plist	Pointer on the linked list where PID is saved.
 * @tgid	If != 0, thread whose stats are to be read.
 * @curr	Index in array for current sample statistics.
 *
 * RETURNS:
 * 0 if stats have been successfully read, and 1 otherwise.
 ***************************************************************************
 */
int read_proc_pid_sched(pid_t pid, struct st_pid *plist, pid_t tgid, int curr)
{
	int fd, rc = 0;
	char filename[128];
	static char buffer[1024 + 1];
	unsigned long long wtime = 0;
	struct pid_stats *pst = plist->pstats[curr];

	if (tgid) {
		sprintf(filename, TASK_SCHED, tgid, pid);
	}
	else {
		sprintf(filename, PID_SCHED, pid);
	}

	if ((fd = open(filename, O_RDONLY)) >= 0) {
		int sz;

		/* schedstat file found for process */
		sz = read(fd, buffer, 1024);
		close(fd);
		if (sz > 0) {
			buffer[sz] = '\0';

			rc = sscanf(buffer, "%*u %llu %*d\n", &wtime);
		}
	}

	/* Convert ns to jiffies */
	pst->wtime = wtime * HZ / 1000000000;

	if (rc < 1)
		return 1;

	return 0;
}

/*
 *****************************************************************************
 * Read stats from /proc/#[/task/##]/status.
 *
 * IN:
 * @pid		Process whose stats are to be read.
 * @plist	Pointer on the linked list where PID is saved.
 * @tgid	If != 0, thread whose stats are to be read.
 * @curr	Index in array for current sample statistics.
 *
 * RETURNS:
 * 0 if stats have been successfully read, and 1 otherwise.
 *****************************************************************************
 */
int read_proc_pid_status(pid_t pid, struct st_pid *plist, pid_t tgid, int curr)
{
	FILE *fp;
	char filename[128], line[256];
	struct pid_stats *pst = plist->pstats[curr];

	if (tgid) {
		sprintf(filename, TASK_STATUS, tgid, pid);
	}
	else {
		sprintf(filename, PID_STATUS, pid);
	}

	if ((fp = fopen(filename, "r")) == NULL)
		/* No such process */
		return 1;

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "Uid:", 4)) {
			sscanf(line + 5, "%u", &plist->uid);
		}
		else if (!strncmp(line, "Threads:", 8)) {
			sscanf(line + 9, "%u", &pst->threads);
		}
		else if (!strncmp(line, "voluntary_ctxt_switches:", 24)) {
			sscanf(line + 25, "%lu", &pst->nvcsw);
		}
		else if (!strncmp(line, "nonvoluntary_ctxt_switches:", 27)) {
			sscanf(line + 28, "%lu", &pst->nivcsw);
		}
	}

	fclose(fp);

	return 0;
}

/*
 *****************************************************************************
 * Read information from /proc/#[/task/##}/smaps.
 *
 * @pid		Process whose stats are to be read.
 * @plist	Pointer on the linked list where PID is saved.
 * @tgid	If != 0, thread whose stats are to be read.
 * @curr	Index in array for current sample statistics.
 *
 * RETURNS:
 * 0 if stats have been successfully read, and 1 otherwise.
 *****************************************************************************
 */
int read_proc_pid_smap(pid_t pid, struct st_pid *plist, pid_t tgid, int curr)
{
	FILE *fp;
	char filename[128], line[256];
	int state = 0;
	struct pid_stats *pst = plist->pstats[curr];

	if (tgid) {
		sprintf(filename, TASK_SMAP, tgid, pid);
	}
	else {
		sprintf(filename, PID_SMAP, pid);
	}

	if ((fp = fopen(filename, "rt")) == NULL)
		/* No such process */
		return 1;

	while ((state < 3) && (fgets(line, sizeof(line), fp) != NULL)) {

		switch (state) {
			case 0:
				if (strstr(line, "[stack]")) {
					state = 1;
				}
				break;
			case 1:
				if (strstr(line, "Size:")) {
					sscanf(line + sizeof("Size:"), "%lu", &pst->stack_size);
					state = 2;
				}
				break;
			case 2:
				if (strstr(line, "Referenced:")) {
					sscanf(line + sizeof("Referenced:"), "%lu", &pst->stack_ref);
					state = 3;
				}
				break;
		}
	}

	fclose(fp);

	return 0;
}

/*
 *****************************************************************************
 * Read process command line from /proc/#[/task/##]/cmdline.
 *
 * IN:
 * @pid		Process whose command line is to be read.
 * @plist	Pointer on the linked list where PID is saved.
 * @tgid	If != 0, thread whose stats are to be read.
 *
 * OUT:
 * @pst		Pointer on structure where command line has been saved.
 *
 * RETURNS:
 * 0 if command line has been successfully read (even if the /proc/.../cmdline
 * is just empty), and 1 otherwise (the process has terminated).
 *****************************************************************************
 */
int read_proc_pid_cmdline(pid_t pid, struct st_pid *plist, pid_t tgid)
{
	FILE *fp;
	char filename[128], line[MAX_CMDLINE_LEN];
	size_t len;
	int found = FALSE;

	if (tgid) {
		sprintf(filename, TASK_CMDLINE, tgid, pid);
	}
	else {
		sprintf(filename, PID_CMDLINE, pid);
	}

	if ((fp = fopen(filename, "r")) == NULL)
		/* No such process */
		return 1;

	memset(line, 0, MAX_CMDLINE_LEN);

	len = fread(line, 1, sizeof(line) - 1, fp);
	fclose(fp);

	if (len) {
		int i;

		for (i = len - 2; i >= 0; i--) {
			if (line[i]) {
				found = TRUE;
			}
			else if (found) {
				line[i] = ' ';
			}
		}
		snprintf(plist->cmdline, sizeof(plist->cmdline), "%s", line);
	}
	else {
		/* proc/.../cmdline was empty */
		plist->cmdline[0] = '\0';
	}
	return 0;
}

/*
 ***************************************************************************
 * Read stats from /proc/#[/task/##]/io.
 *
 * IN:
 * @pid		Process whose stats are to be read.
 * @plist	Pointer on the linked list where PID is saved.
 * @tgid	If != 0, thread whose stats are to be read.
 * @curr	Index in array for current sample statistics.
 *
 * RETURNS:
 * 0 if stats have been successfully read.
 * Also returns 0 if current process has terminated or if its io file
 * doesn't exist, but in this case, set process' F_NO_PID_IO flag to
 * indicate that I/O stats should no longer be read for it.
 ***************************************************************************
 */
int read_proc_pid_io(pid_t pid, struct st_pid *plist, pid_t tgid, int curr)
{
	FILE *fp;
	char filename[128], line[256];
	struct pid_stats *pst = plist->pstats[curr];

	if (tgid) {
		sprintf(filename, TASK_IO, tgid, pid);
	}
	else {
		sprintf(filename, PID_IO, pid);
	}

	if ((fp = fopen(filename, "r")) == NULL) {
		/* No such process... or file non existent! */
		plist->flags |= F_NO_PID_IO;
		/*
		 * Also returns 0 since io stats file doesn't necessarily exist,
		 * depending on the kernel version used.
		 */
		return 0;
	}

	while (fgets(line, sizeof(line), fp) != NULL) {

		if (!strncmp(line, "read_bytes:", 11)) {
			sscanf(line + 12, "%llu", &pst->read_bytes);
		}
		else if (!strncmp(line, "write_bytes:", 12)) {
			sscanf(line + 13, "%llu", &pst->write_bytes);
		}
		else if (!strncmp(line, "cancelled_write_bytes:", 22)) {
			sscanf(line + 23, "%llu", &pst->cancelled_write_bytes);
		}
	}

	fclose(fp);

	plist->flags &= ~F_NO_PID_IO;

	return 0;
}

/*
 ***************************************************************************
 * Count number of file descriptors in /proc/#[/task/##]/fd directory.
 *
 * IN:
 * @pid		Process whose stats are to be read.
 * @plist	Pointer on the linked list where PID is saved.
 * @tgid	If != 0, thread whose stats are to be read.
 * @curr	Index in array for current sample statistics.
 *
 * RETURNS:
 * 0 if stats have been successfully read.
 * Also returns 0 if current process has terminated or if we cannot read its
 * fd directory, but in this case, set process' F_NO_PID_FD flag to
 * indicate that fd directory couldn't be read.
 ***************************************************************************
 */
int read_proc_pid_fd(pid_t pid, struct st_pid *plist, pid_t tgid, int curr)
{
	DIR *dir;
	struct dirent *drp;
	char filename[128];
	struct pid_stats *pst = plist->pstats[curr];

	if (tgid) {
		sprintf(filename, TASK_FD, tgid, pid);
	}
	else {
		sprintf(filename, PID_FD, pid);
	}

	if ((dir = opendir(filename)) == NULL) {
		/* Cannot read fd directory */
		plist->flags |= F_NO_PID_FD;
		return 0;
	}

	pst->fd_nr = 0;

	/* Count number of entries if fd directory */
	while ((drp = readdir(dir)) != NULL) {
		if (isdigit(drp->d_name[0])) {
			(pst->fd_nr)++;
		}
	}

	closedir(dir);

	plist->flags &= ~F_NO_PID_FD;

	return 0;
}

/*
 ***************************************************************************
 * Read various stats for given PID.
 *
 * IN:
 * @pid		Process whose stats are to be read.
 * @plist	Pointer on the linked list where PID is saved.
 * @tgid	If !=0, thread whose stats are to be read.
 * @curr	Index in array for current sample statistics.
 *
 * OUT:
 * @thread_nr	Number of threads of the process.
 *
 * RETURNS:
 * 0 if stats have been successfully read, and 1 otherwise.
 ***************************************************************************
 */
int read_pid_stats(pid_t pid, struct st_pid *plist, unsigned int *thread_nr,
		   pid_t tgid, int curr)
{
	if (read_proc_pid_stat(pid, plist, thread_nr, tgid, curr))
		return 1;

	/*
	 * No need to test the return code here: Not finding
	 * the schedstat files shouldn't make pidstat stop.
	 */
	read_proc_pid_sched(pid, plist, tgid, curr);

	if (DISPLAY_CMDLINE(pidflag) && !plist->cmdline[0]) {
		if (read_proc_pid_cmdline(pid, plist, tgid))
			return 1;
	}

	if (read_proc_pid_status(pid, plist, tgid, curr))
		return 1;

	if (DISPLAY_STACK(actflag)) {
		if (read_proc_pid_smap(pid, plist, tgid, curr))
			return 1;
	}

	if (DISPLAY_KTAB(actflag)) {
		if (read_proc_pid_fd(pid, plist, tgid, curr))
			return 1;
	}

	if (DISPLAY_IO(actflag))
		/* Assume that /proc/#/task/#/io exists! */
		return (read_proc_pid_io(pid, plist, tgid, curr));

	return 0;
}

/*
 ***************************************************************************
 * Read stats for threads in /proc/#/task directory.
 *
 * IN:
 * @pid		Process number whose threads stats are to be read.
 * @plist	Pointer on the linked list where PID is saved.
 * @curr	Index in array for current sample statistics.
 ***************************************************************************
 */
void read_task_stats(pid_t pid, struct st_pid *plist, int curr)
{
	DIR *dir;
	pid_t tid;
	struct dirent *drp;
	char filename[128];
	unsigned int thr_nr;
	struct st_pid *tlist;

	/* Open /proc/#/task directory */
	sprintf(filename, PROC_TASK, pid);
	if ((dir = __opendir(filename)) == NULL)
		return;

	while ((drp = __readdir(dir)) != NULL) {

		if (!isdigit(drp->d_name[0])) {
			continue;
		}

		tid = atoi(drp->d_name);

		tlist = add_list_pid(&pid_list, tid, pid);
		if (!tlist)
			continue;
		tlist->exist = TRUE;

		if (read_pid_stats(tid, tlist, &thr_nr, pid, curr)) {
			/* Thread doesn't exist */
			tlist->exist = FALSE;
		}
	}

	__closedir(dir);
}

/*
 ***************************************************************************
 * Read various stats.
 *
 * IN:
 * @curr	Index in array for current sample statistics.
 ***************************************************************************
 */
void read_stats(int curr)
{
	unsigned int thr_nr;
	pid_t pid;
	struct st_pid *plist;
	struct stats_cpu *st_cpu;

	/*
	 * Allocate two structures for CPU statistics.
	 * No need to init them (done by read_stat_cpu() function).
	 */
	if ((st_cpu = (struct stats_cpu *) malloc(STATS_CPU_SIZE * 2)) == NULL) {
		perror("malloc");
		exit(4);
	}
	/* Read statistics for CPUs "all" */
	read_stat_cpu(st_cpu, 1);

	/*
	 * Compute the total number of jiffies spent by all processors.
	 * NB: Don't add cpu_guest/cpu_guest_nice because cpu_user/cpu_nice
	 * already include them.
	 */
	tot_jiffies[curr] = st_cpu->cpu_user + st_cpu->cpu_nice +
			    st_cpu->cpu_sys + st_cpu->cpu_idle +
			    st_cpu->cpu_iowait + st_cpu->cpu_hardirq +
			    st_cpu->cpu_steal + st_cpu->cpu_softirq;
	free(st_cpu);

	if (DISPLAY_ALL_PID(pidflag)) {
		DIR *dir;
		struct dirent *drp;

		/* Open /proc directory */
		if ((dir = __opendir(PROC)) == NULL) {
			perror("opendir");
			exit(4);
		}

		/* Get directory entries */
		while ((drp = __readdir(dir)) != NULL) {

			if (!isdigit(drp->d_name[0])) {
				continue;
			}

			pid = atoi(drp->d_name);

			plist = add_list_pid(&pid_list, pid, 0);
			if (!plist)
				continue;
			plist->exist = TRUE;

			if (read_pid_stats(pid, plist, &thr_nr, 0, curr)) {
				/* PID has terminated */
				plist->exist = FALSE;

			} else if (DISPLAY_TID(pidflag)) {
				/* Read stats for threads in task subdirectory */
				read_task_stats(pid, plist, curr);
			}
		}

		/* Close /proc directory */
		__closedir(dir);
	}

	else {
		/* Read stats for each PID in the list */
		for (plist = pid_list; plist != NULL; plist = plist->next) {

			if (plist->tgid)
				/*
				 * Ignore TIDs.
				 * The user can only enter PIDs on the command line.
				 * If there is a TID then this is because the user has
				 * used option -t, and the TID has been inserted in the
				 * list by read_task_stats() function below.
				 */
				continue;

			if (read_pid_stats(plist->pid, plist, &thr_nr, 0, curr)) {
				/* PID has terminated */
				plist->exist = FALSE;
			}
			else {
				plist->exist = TRUE;

				if (DISPLAY_TID(pidflag)) {
					read_task_stats(plist->pid, plist, curr);
				}
			}
		}
	}

	/* Free unused PID structures */
	sfree_pid(&pid_list, FALSE);
}

/*
 ***************************************************************************
 * Get current PID to display.
 * First, check that PID exists. *Then* check that it's an active process
 * and/or that the string (entered on the command line with option -C)
 * is found in command name, or that the process string (entered on the
 * command line with option -G) is found either in its command name (in case
 * PID is a process) or in command name of its thread leader (in case
 * PID is a thread).
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @activity	Current activity to display (CPU, memory...).
 * 		Can be more than one if stats are displayed on one line.
 * @pflag	Flag indicating whether stats are to be displayed for
 * 		individual tasks or for all their children.
 * @plist	Pointer on the linked list where PID is saved.
 *
 * RETURNS:
 *  0 if PID no longer exists.
 * -1 if PID exists but should not be displayed.
 *  1 if PID can be displayed.
 ***************************************************************************
 */
int get_pid_to_display(int prev, int curr, unsigned int activity, unsigned int pflag,
		       struct st_pid *plist)
{
	int rc;
	char *pc;
	regex_t regex;
	struct pid_stats *pstc = plist->pstats[curr], *pstp = plist->pstats[prev];

	if (!plist->exist)
		/* PID no longer exists */
		return 0;

	if (!plist->tgid) {
		/* This is group leader: Set it as not displayed by default */
		plist->flags &= ~F_PID_DISPLAYED;
	}

	if ((DISPLAY_ALL_PID(pidflag) || DISPLAY_TID(pidflag)) &&
		DISPLAY_ACTIVE_PID(pidflag)) {
		int isActive = FALSE;

		/* Check that it's an "active" process */
		if (DISPLAY_CPU(activity)) {
			/* User time already includes guest time */
			if ((pstc->utime != pstp->utime) ||
			    (pstc->stime != pstp->stime)) {
				isActive = TRUE;
			}
			else {
				/*
				 * Process is not active but if we are showing
				 * child stats then we need to look there.
				 */
				if (DISPLAY_CHILD_STATS(pflag)) {
					/* User time already includes guest time */
					if ((pstc->cutime != pstp->cutime) ||
					    (pstc->cstime != pstp->cstime)) {
						isActive = TRUE;
					}
				}
			}
		}

		if (DISPLAY_MEM(activity) && !isActive) {
			if ((pstc->minflt != pstp->minflt) ||
			    (pstc->majflt != pstp->majflt)) {
				isActive = TRUE;
			}
			else {
				if (DISPLAY_TASK_STATS(pflag)) {
					if ((pstc->vsz != pstp->vsz) ||
					    (pstc->rss != pstp->rss)) {
						isActive = TRUE;
					}
				}
				else if (DISPLAY_CHILD_STATS(pflag)) {
					if ((pstc->cminflt != pstp->cminflt) ||
					    (pstc->cmajflt != pstp->cmajflt)) {
						isActive = TRUE;
					}
				}
			}
		}

		if (DISPLAY_STACK(activity) && !isActive) {
			if ((pstc->stack_size != pstp->stack_size) ||
			    (pstc->stack_ref != pstp->stack_ref)) {
				isActive = TRUE;
			}
		}

		if (DISPLAY_IO(activity) && !isActive) {
			if (pstc->blkio_swapin_delays !=
			     pstp->blkio_swapin_delays) {
				isActive = TRUE;
			}
			if (!(NO_PID_IO(plist->flags)) && !isActive) {
				/* /proc/#/io file should exist to check I/O stats */
				if ((pstc->read_bytes  != pstp->read_bytes)  ||
				    (pstc->write_bytes != pstp->write_bytes) ||
				    (pstc->cancelled_write_bytes !=
				     pstp->cancelled_write_bytes)) {
					isActive = TRUE;
				}
			}
		}

		if (DISPLAY_CTXSW(activity) && !isActive) {
			if ((pstc->nvcsw  != pstp->nvcsw) ||
			    (pstc->nivcsw != pstp->nivcsw)) {
				isActive = TRUE;
			}
		}

		if (DISPLAY_RT(activity) && !isActive) {
			if ((pstc->priority != pstp->priority) ||
			    (pstc->policy != pstp->policy)) {
				isActive = TRUE;
			}
		}

		if (DISPLAY_KTAB(activity) && !isActive &&
			!(NO_PID_FD(plist->flags))) {	/* /proc/#/fd directory should be readable */
			if ((pstc->threads != pstp->threads) ||
			    (pstc->fd_nr != pstp->fd_nr)) {
				isActive = TRUE;
			}
		}

		/* If PID isn't active for any of the activities then return */
		if (!isActive)
			return -1;
	}

	if (COMMAND_STRING(pidflag)) {
		if (regcomp(&regex, commstr, REG_EXTENDED | REG_NOSUB) != 0)
			/* Error in preparing regex structure */
			return -1;

		pc = get_tcmd(plist);	/* Get pointer on task's command string */
		rc = regexec(&regex, pc, 0, NULL, 0);
		regfree(&regex);

		if (rc)
			/* regex pattern not found in command name */
			return -1;
	}

	if (PROCESS_STRING(pidflag)) {
		if (!plist->tgid) {
			/* This PID is a process ("thread group leader") */
			if (regcomp(&regex, procstr, REG_EXTENDED | REG_NOSUB) != 0)
				/* Error in preparing regex structure */
				return -1;

			pc = get_tcmd(plist);	/* Get pointer on task's command string */
			rc = regexec(&regex, pc, 0, NULL, 0);
			regfree(&regex);

			if (rc)
				/* regex pattern not found in command name */
				return -1;

		}
		else if (!IS_PID_DISPLAYED(plist->tgid->flags))
			/* This pid is a thread and is not part of a process to display */
			return -1;
	}

	if (USER_STRING(pidflag)) {
		struct passwd *pwdent;

		if ((pwdent = __getpwuid(plist->uid)) != NULL) {
			if (strcmp(pwdent->pw_name, userstr))
				/* This PID doesn't belong to user */
				return -1;
		}
	}

	plist->flags |= F_PID_DISPLAYED;
	return 1;
}

/*
 ***************************************************************************
 * Display UID/username, PID and TID.
 *
 * IN:
 * @plist	Pointer on the linked list where PID is saved.
 * @c		No-op character.
 ***************************************************************************
 */
void __print_line_id(struct st_pid *plist, char c)
{
	char format[32];
	struct passwd *pwdent;

	if (DISPLAY_USERNAME(pidflag) && ((pwdent = __getpwuid(plist->uid)) != NULL)) {
		cprintf_in(IS_STR, " %8s", pwdent->pw_name, 0);
	}
	else {
		cprintf_in(IS_INT, " %5d", "", plist->uid);
	}

	if (DISPLAY_TID(pidflag)) {
		if (plist->tgid) {
			/* This is a TID */
			if (IS_PID_DISPLAYED(plist->tgid->flags)) {
				/*
				 * Its TGID has previously been displayed.
				 * We can just display a dash for the TGID then
				 * the TID number, i.e. "   -     <TID> "
				 */
				sprintf(format, "         %c %%9u", c);
			}
			else {
				/*
				 * Its TGID didn't meet the conditions to be displayed.
				 * So we have to specify to which TGID the current TID is
				 * linked, i.e. " <TGID>   <TID> "
				 */
				strcpy(format, " %9u");
				cprintf_in(IS_INT, format, "", plist->tgid->pid);
			}
		}
		else {
			/* This is a PID (TGID) */
			sprintf(format, " %%9u         %c", c);
		}
	}
	else {
		strcpy(format, " %9u");
	}

	cprintf_in(IS_INT, format, "", plist->pid);
}

/*
 ***************************************************************************
 * Display timestamp, PID and TID.
 *
 * IN:
 * @timestamp	Current timestamp.
 * @plist	Pointer on the linked list where PID is saved.
 ***************************************************************************
 */
void print_line_id(char *timestamp, struct st_pid *plist)
{
	printf("%-11s", timestamp);
	__print_line_id(plist, '-');
}

/*
 * **************************************************************************
 * Display UID/username, PID and TID in JSON format.
 *
 * IN:
 * @tab		Number of tabs to print.
 * @plist	Pointer on the linked list where PID is saved.
 ***************************************************************************
 */
void print_json_line_id(int tab, struct st_pid *plist)
{
	char format[32];
	struct passwd *pwdent;

	xprintf0(tab, "{\"");

	if (DISPLAY_USERNAME(pidflag)) {
		printf("USER");
	}
	else {
		printf("UID");
	}
	printf("\": \"");

	if (DISPLAY_USERNAME(pidflag) && ((pwdent = __getpwuid(plist->uid)) != NULL)) {
		printf("%s", pwdent->pw_name);
	}
	else {
		printf("%d", plist->uid);
	}
	printf("\", \"");

	if (DISPLAY_TID(pidflag)) {
		printf("TGID");
	}
	else {
		printf("PID");
	}
	printf("\": \"");

	strcpy(format, "%u");
	if (DISPLAY_TID(pidflag)) {
		if (plist->tgid) {
			/* This is a TID */
			printf("%u\", \"TID\": \"", plist->tgid->pid);
		}
		else {
			/* This is a PID (TGID) */
			strcpy(format, "%u\", \"TID\": \"");
		}
	}

	printf(format, plist->pid);
	printf("\", ");
}

/*
 * **************************************************************************
 * Display CPU statistics data for current task.
 *
 * IN:
 * @disp_avg	TRUE if average stats are displayed.
 * @pstc	Pointer on current statistics data sample for current process.
 * @pstp	Pointer on previous statistics data sampe for current process.
 * @itv		Interval of time in 1/100th of a second.
 * @deltot_jiffies
 *		Number of jiffies spent on the interval by all processors.
 ***************************************************************************
 */
void write_plain_pid_task_cpu_data(int disp_avg,
				   struct pid_stats *pstc, struct pid_stats *pstp,
				   unsigned long long itv,
				   unsigned long long deltot_jiffies)
{
	cprintf_xpc(DISPLAY_UNIT(pidflag), XHIGH, 5, 7, 2,
		    (pstc->utime - pstc->gtime) < (pstp->utime - pstp->gtime) ||
		    (pstc->utime < pstc->gtime) || (pstp->utime < pstp->gtime) ?
		    0.0 :
		    SP_VALUE(pstp->utime - pstp->gtime,
			     pstc->utime - pstc->gtime, itv * HZ / 100),
		    SP_VALUE(pstp->stime, pstc->stime, itv * HZ / 100),
		    SP_VALUE(pstp->gtime, pstc->gtime, itv * HZ / 100),
		    SP_VALUE(pstp->wtime, pstc->wtime, itv * HZ / 100),
		    /* User time already includes guest time */
		    IRIX_MODE_OFF(pidflag) ?
		    SP_VALUE(pstp->utime + pstp->stime,
			     pstc->utime + pstc->stime, deltot_jiffies) :
			     SP_VALUE(pstp->utime + pstp->stime,
				      pstc->utime + pstc->stime, itv * HZ / 100));

	if (!disp_avg) {
		cprintf_in(IS_INT, "   %3d", "", pstc->processor);
	}
	else {
		cprintf_in(IS_STR, "%s", "     -", 0);
	}
}

/*
 * **************************************************************************
 * Display memory statistics data for current task.
 *
 * IN:
 * @disp_avg	TRUE if average stats are displayed.
 * @plist	Pointer on current process in list.
 * @pstc	Pointer on current statistics data sample for current process.
 * @pstp	Pointer on previous statistics data sampe for current process.
 * @itv		Interval of time in 1/100th of a second.
 ***************************************************************************
 */
void write_plain_pid_task_memory_data(int disp_avg, struct st_pid *plist,
				      struct pid_stats *pstc, struct pid_stats *pstp,
				      unsigned long long itv)
{
	cprintf_f(NO_UNIT, FALSE, 2, 9, 2,
		  S_VALUE(pstp->minflt, pstc->minflt, itv),
		  S_VALUE(pstp->majflt, pstc->majflt, itv));

	if (disp_avg) {
		cprintf_f(DISPLAY_UNIT(pidflag) ? UNIT_KILOBYTE : NO_UNIT, FALSE, 2, 7, 0,
			  (double) plist->total_vsz / plist->rt_asum_count,
			  (double) plist->total_rss / plist->rt_asum_count);

		cprintf_xpc(DISPLAY_UNIT(pidflag), XHIGH, 1, 6, 2,
			    tlmkb ?
			    SP_VALUE(0, plist->total_rss / plist->rt_asum_count, tlmkb)
			    : 0.0);
	}
	else {
		cprintf_u64(DISPLAY_UNIT(pidflag) ? UNIT_KILOBYTE : NO_UNIT, 2, 7,
			    (unsigned long long) pstc->vsz,
			    (unsigned long long) pstc->rss);

		cprintf_xpc(DISPLAY_UNIT(pidflag), XHIGH, 1, 6, 2,
			    tlmkb ? SP_VALUE(0, pstc->rss, tlmkb) : 0.0);
	}
}

/*
 * **************************************************************************
 * Display stack size statistics data for current task.
 *
 * IN:
 * @disp_avg	TRUE if average stats are displayed.
 * @plist	Pointer on current process in list.
 * @pstc	Pointer on current statistics data sample for current process.
 ***************************************************************************
 */
void write_plain_pid_stack_data(int disp_avg, struct st_pid *plist,
				struct pid_stats *pstc)
{
	if (disp_avg) {
		cprintf_f(DISPLAY_UNIT(pidflag) ? UNIT_KILOBYTE : NO_UNIT, FALSE, 2, 7, 0,
			  (double) plist->total_stack_size / plist->sk_asum_count,
			  (double) plist->total_stack_ref  / plist->sk_asum_count);
	}
	else {
		cprintf_u64(DISPLAY_UNIT(pidflag) ? UNIT_KILOBYTE : NO_UNIT, 2, 7,
			    (unsigned long long) pstc->stack_size,
			    (unsigned long long) pstc->stack_ref);
	}
}

/*
 * **************************************************************************
 * Display I/O statistics data for current task.
 *
 * IN:
 * @disp_avg	TRUE if average stats are displayed.
 * @plist	Pointer on current process in list.
 * @pstc	Pointer on current statistics data sample for current process.
 * @pstp	Pointer on previous statistics data sampe for current process.
 * @itv		Interval of time in 1/100th of a second.
 ***************************************************************************
 */
void write_plain_pid_io_data(int disp_avg, struct st_pid *plist,
			     struct pid_stats *pstc, struct pid_stats *pstp,
			     unsigned long long itv)
{
	char dstr[32];
	double rbytes, wbytes, cbytes;

	if (!NO_PID_IO(plist->flags)) {
		rbytes = S_VALUE(pstp->read_bytes,  pstc->read_bytes, itv);
		wbytes = S_VALUE(pstp->write_bytes, pstc->write_bytes, itv);
		cbytes = S_VALUE(pstp->cancelled_write_bytes,
				 pstc->cancelled_write_bytes, itv);
		if (!DISPLAY_UNIT(pidflag)) {
			rbytes /= 1024;
			wbytes /= 1024;
			cbytes /= 1024;
		}
		cprintf_f(DISPLAY_UNIT(pidflag) ? UNIT_BYTE : NO_UNIT, FALSE, 3, 9, 2,
			  rbytes, wbytes, cbytes);
	}
	else {
		/* I/O file not readable (permission denied or file non existent) */
		sprintf(dstr, " %9.2f %9.2f %9.2f", -1.0, -1.0, -1.0);
		cprintf_s(IS_ZERO, "%s", dstr);
	}
	/* I/O delays come from another file (/proc/#/stat) */
	if (disp_avg) {
		cprintf_f(NO_UNIT, FALSE, 1, 7, 0,
			  (double) (pstc->blkio_swapin_delays - pstp->blkio_swapin_delays) /
			  plist->delay_asum_count);
	}
	else {
		cprintf_u64(NO_UNIT, 1, 7,
			    (unsigned long long) (pstc->blkio_swapin_delays - pstp->blkio_swapin_delays));
	}
}

/*
 * **************************************************************************
 * Display context switches statistics data for current task.
 *
 * IN:
 * @pstc	Pointer on current statistics data sample for current process.
 * @pstp	Pointer on previous statistics data sampe for current process.
 * @itv		Interval of time in 1/100th of a second.
 ***************************************************************************
 */
void write_plain_pid_ctxswitch_data(struct pid_stats *pstc, struct pid_stats *pstp,
				    unsigned long long itv)
{
	cprintf_f(NO_UNIT, FALSE, 2, 9, 2,
		  S_VALUE(pstp->nvcsw,  pstc->nvcsw,  itv),
		  S_VALUE(pstp->nivcsw, pstc->nivcsw, itv));
}

/*
 * **************************************************************************
 * Display some kernel tables values for current task.
 *
 * IN:
 * @disp_avg	TRUE if average stats are displayed.
 * @plist	Pointer on current process in list.
 * @pstc	Pointer on current statistics data sample for current process.
 ***************************************************************************
 */
void write_plain_pid_ktab_data(int disp_avg, struct st_pid *plist,
			       struct pid_stats *pstc)
{
	if (disp_avg) {
		cprintf_f(NO_UNIT, FALSE, 2, 7, 0,
			  (double) plist->total_threads / plist->tf_asum_count,
			  NO_PID_FD(plist->flags) ?
			  -1.0 :
			  (double) plist->total_fd_nr / plist->tf_asum_count);
	}
	else {
		cprintf_u64(NO_UNIT, 1, 7,
			    (unsigned long long) pstc->threads);
		if (NO_PID_FD(plist->flags)) {
			/* /proc/#/fd directory not readable */
			cprintf_s(IS_ZERO, " %7s", "-1");
		}
		else {
			cprintf_u64(NO_UNIT, 1, 7,
				    (unsigned long long) pstc->fd_nr);
		}
	}
}

/*
 * **************************************************************************
 * Display scheduling priority and policy information for current task.
 *
 * IN:
 * @pstc	Pointer on current statistics data sample for current process.
 ***************************************************************************
 */
void write_plain_pid_rt_data(struct pid_stats *pstc)
{
	cprintf_u64(NO_UNIT, 1, 4,
		    (unsigned long long) pstc->priority);
	cprintf_s(IS_STR, " %6s", GET_POLICY(pstc->policy));
}

/*
 ***************************************************************************
 * Display all statistics for tasks in one line format.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample.
 * @itv		Interval of time in jiffies.
 * @deltot_jiffies
 *		Number of jiffies spent on the interval by all processors.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_pid_task_all_stats(int prev, int curr, int dis,
			     char *prev_string, char *curr_string,
			     unsigned long long itv,
			     unsigned long long deltot_jiffies)
{
	struct pid_stats *pstc, *pstp;
	struct st_pid *plist;
	int again = 0;

	if (dis) {
		PRINT_ID_HDR(prev_string, pidflag);
		if (DISPLAY_CPU(actflag)) {
			printf("    %%usr %%system  %%guest   %%wait    %%CPU   CPU");
		}
		if (DISPLAY_MEM(actflag)) {
			printf("  minflt/s  majflt/s     VSZ     RSS   %%MEM");
		}
		if (DISPLAY_STACK(actflag)) {
			printf(" StkSize  StkRef");
		}
		if (DISPLAY_IO(actflag)) {
			printf("   kB_rd/s   kB_wr/s kB_ccwr/s iodelay");
		}
		if (DISPLAY_CTXSW(actflag)) {
			printf("   cswch/s nvcswch/s");
		}
		if (DISPLAY_KTAB(actflag)) {
			printf(" threads   fd-nr");
		}
		if (DISPLAY_RT(actflag)) {
			printf(" prio policy");
		}
		printf("  Command\n");
	}

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if (get_pid_to_display(prev, curr, actflag, P_TASK, plist) <= 0)
			continue;

		print_line_id(curr_string, plist);

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		if (DISPLAY_CPU(actflag)) {
			write_plain_pid_task_cpu_data(FALSE, pstc, pstp, itv,
						      deltot_jiffies);
		}

		if (DISPLAY_MEM(actflag)) {
			write_plain_pid_task_memory_data(FALSE, plist,
							 pstc, pstp, itv);
		}

		if (DISPLAY_STACK(actflag)) {
			write_plain_pid_stack_data(FALSE, plist, pstc);
		}

		if (DISPLAY_IO(actflag)) {
			write_plain_pid_io_data(FALSE, plist, pstc, pstp, itv);
		}

		if (DISPLAY_CTXSW(actflag)) {
			write_plain_pid_ctxswitch_data(pstc, pstp, itv);
		}

		if (DISPLAY_KTAB(actflag)) {
			write_plain_pid_ktab_data(FALSE, plist, pstc);
		}

		if (DISPLAY_RT(actflag)) {
			write_plain_pid_rt_data(pstc);
		}

		print_comm(plist);
		again = 1;
	}

	return again;
}

/*
 * **************************************************************************
 * Display CPU statistics for the children of current task.
 *
 * IN:
 * @disp_avg	TRUE if average stats are displayed.
 * @plist	Pointer on current process in list.
 * @pstc	Pointer on current statistics data sample for current process.
 * @pstp	Pointer on previous statistics data sampe for current process.
 ***************************************************************************
 */
void write_plain_pid_child_cpu_data(int disp_avg, struct st_pid *plist,
				    struct pid_stats *pstc, struct pid_stats *pstp)
{
	if (disp_avg) {
		cprintf_f(NO_UNIT, FALSE, 3, 9, 0,
			  (pstc->utime + pstc->cutime - pstc->gtime - pstc->cgtime) <
			  (pstp->utime + pstp->cutime - pstp->gtime - pstp->cgtime) ||
			  (pstc->utime + pstc->cutime < pstc->gtime + pstc->cgtime) ||
			  (pstp->utime + pstp->cutime < pstp->gtime + pstp->cgtime) ?
			  0.0 :
			  (double) ((pstc->utime + pstc->cutime - pstc->gtime - pstc->cgtime) -
				    (pstp->utime + pstp->cutime - pstp->gtime - pstp->cgtime)) /
				   (HZ * plist->uc_asum_count) * 1000,
			  (double) ((pstc->stime + pstc->cstime) -
				    (pstp->stime + pstp->cstime)) /
				   (HZ * plist->uc_asum_count) * 1000,
			  (double) ((pstc->gtime + pstc->cgtime) -
				    (pstp->gtime + pstp->cgtime)) /
				   (HZ * plist->uc_asum_count) * 1000);
	}
	else {
		cprintf_f(NO_UNIT, FALSE, 3, 9, 0,
			  (pstc->utime + pstc->cutime - pstc->gtime - pstc->cgtime) <
			  (pstp->utime + pstp->cutime - pstp->gtime - pstp->cgtime) ||
			  (pstc->utime + pstc->cutime < pstc->gtime + pstc->cgtime) ||
			  (pstp->utime + pstp->cutime < pstp->gtime + pstp->cgtime) ?
			  0.0 :
			  (double) ((pstc->utime + pstc->cutime - pstc->gtime - pstc->cgtime) -
				    (pstp->utime + pstp->cutime - pstp->gtime - pstp->cgtime)) /
				   HZ * 1000,
			  (double) ((pstc->stime + pstc->cstime) -
				    (pstp->stime + pstp->cstime)) / HZ * 1000,
			  (double) ((pstc->gtime + pstc->cgtime) -
				    (pstp->gtime + pstp->cgtime)) / HZ * 1000);
	}
}

/*
 * **************************************************************************
 * Display memory statistics for the children of current task.
 *
 * IN:
 * @disp_avg	TRUE if average stats are displayed.
 * @plist	Pointer on current process in list.
 * @pstc	Pointer on current statistics data sample for current process.
 * @pstp	Pointer on previous statistics data sampe for current process.
 ***************************************************************************
 */
void write_plain_pid_child_memory_data(int disp_avg, struct st_pid *plist,
				       struct pid_stats *pstc, struct pid_stats *pstp)
{
	if (disp_avg) {
		cprintf_f(NO_UNIT, FALSE, 2, 9, 0,
			  (double) ((pstc->minflt + pstc->cminflt) -
				    (pstp->minflt + pstp->cminflt)) / plist->rc_asum_count,
			  (double) ((pstc->majflt + pstc->cmajflt) -
				    (pstp->majflt + pstp->cmajflt)) / plist->rc_asum_count);
	}
	else {
		cprintf_u64(NO_UNIT, 2, 9,
			    (unsigned long long) ((pstc->minflt + pstc->cminflt) - (pstp->minflt + pstp->cminflt)),
			    (unsigned long long) ((pstc->majflt + pstc->cmajflt) - (pstp->majflt + pstp->cmajflt)));
	}
}

/*
 ***************************************************************************
 * Display all statistics for tasks' children in one line format.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_pid_child_all_stats(int prev, int curr, int dis,
			      char *prev_string, char *curr_string)
{
	struct pid_stats *pstc, *pstp;
	struct st_pid *plist;
	int again = 0;

	if (dis) {
		PRINT_ID_HDR(prev_string, pidflag);
		if (DISPLAY_CPU(actflag))
			printf("    usr-ms system-ms  guest-ms");
		if (DISPLAY_MEM(actflag))
			printf(" minflt-nr majflt-nr");
		printf("  Command\n");
	}

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if (get_pid_to_display(prev, curr, actflag, P_CHILD, plist) <= 0)
			continue;

		print_line_id(curr_string, plist);

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		if (DISPLAY_CPU(actflag)) {
			write_plain_pid_child_cpu_data(FALSE, plist, pstc, pstp);
		}

		if (DISPLAY_MEM(actflag)) {
			write_plain_pid_child_memory_data(FALSE, plist, pstc, pstp);
		}

		print_comm(plist);
		again = 1;
	}

	return again;
}

/*
 ***************************************************************************
 * Display CPU statistics for tasks.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @itv		Interval of time in 1/100th of a second.
 * @deltot_jiffies
 *		Number of jiffies spent on the interval by all processors.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_plain_pid_task_cpu_stats(int prev, int curr, int dis, int disp_avg,
				   char *prev_string, char *curr_string,
				   unsigned long long itv,
				   unsigned long long deltot_jiffies)
{
	struct pid_stats *pstc, *pstp;
	struct st_pid *plist;
	int again = 0;

	if (dis) {
		PRINT_ID_HDR(prev_string, pidflag);
		printf("    %%usr %%system  %%guest   %%wait    %%CPU   CPU  Command\n");
	}

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if (get_pid_to_display(prev, curr, P_A_CPU, P_TASK, plist) <= 0)
			continue;

		print_line_id(curr_string, plist);

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		/* Write current process data */
		write_plain_pid_task_cpu_data(disp_avg, pstc, pstp, itv,
					      deltot_jiffies);

		print_comm(plist);
		again = 1;
	}

	return again;
}

/*
 * **************************************************************************
 * Display CPU statistics for tasks.
 *
 * IN:
 * @tab		Number of tabs to print.
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @itv		Interval of time in 1/100th of a second.
 * @deltot_jiffies
 *		Number of jiffies spent on the interval by all processors.
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma.
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any).
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_json_pid_task_cpu_stats(int tab, int prev, int curr, unsigned long long itv,
				  unsigned long long deltot_jiffies, int *follow)
{
	struct pid_stats *pstc, *pstp;
	struct st_pid *plist;
	int again = 0, next = FALSE;

	if (*follow) {
		printf(",\n");
	}
	xprintf(tab++, "\"task-cpu-load\": [");

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if (get_pid_to_display(prev, curr, P_A_CPU, P_TASK, plist) <= 0)
			continue;

		if (next) {
			printf(",\n");
		}
		next = TRUE;

		print_json_line_id(tab, plist);

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		printf("\"usr\": %.2f, \"system\": %.2f, \"guest\": %.2f, \"wait\": %.2f, "
		       "\"cpu\": %.2f, \"cpu_nr\": %d, ",
		       (pstc->utime - pstc->gtime) < (pstp->utime - pstp->gtime) ||
		       (pstc->utime < pstc->gtime) || (pstp->utime < pstp->gtime) ?
		       0.0 :
		       SP_VALUE(pstp->utime - pstp->gtime,
				pstc->utime - pstc->gtime, itv * HZ / 100),
		       SP_VALUE(pstp->stime, pstc->stime, itv * HZ / 100),
		       SP_VALUE(pstp->gtime, pstc->gtime, itv * HZ / 100),
		       SP_VALUE(pstp->wtime, pstc->wtime, itv * HZ / 100),
		       /* User time already includes guest time */
		       IRIX_MODE_OFF(pidflag) ?
		       SP_VALUE(pstp->utime + pstp->stime,
				pstc->utime + pstc->stime, deltot_jiffies) :
		       SP_VALUE(pstp->utime + pstp->stime,
				 pstc->utime + pstc->stime, itv * HZ / 100),
		       pstc->processor);

		print_json_comm(plist);
		again = 1;
	}

	printf("\n");
	xprintf0(--tab, "]");
	*follow = TRUE;

	return again;
}

/*
 * **************************************************************************
 * Display CPU statistics for tasks.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @itv		Interval of time in 1/100th of a second.
 * @deltot_jiffies
 *		Number of jiffies spent on the interval by all processors.
 * @tab		Number of tabs to print (JSON format only).
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma (JSON format only).
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any). JSON format only.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_pid_task_cpu_stats(int prev, int curr, int dis, int disp_avg,
			     char *prev_string, char *curr_string,
			     unsigned long long itv,
			     unsigned long long deltot_jiffies, int tab, int *follow)
{
	if (DISPLAY_JSON_OUTPUT(xflags)) {
		return write_json_pid_task_cpu_stats(tab, prev, curr, itv,
						     deltot_jiffies, follow);
	}
	else {
		return write_plain_pid_task_cpu_stats(prev, curr, dis, disp_avg,
						      prev_string, curr_string, itv,
						      deltot_jiffies);
	}
}

/*
 ***************************************************************************
 * Display CPU statistics for tasks' children.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_plain_pid_child_cpu_stats(int prev, int curr, int dis, int disp_avg,
				    char *prev_string, char *curr_string)
{
	struct pid_stats *pstc, *pstp;
	struct st_pid *plist;
	int rc, again = 0;

	if (dis) {
		PRINT_ID_HDR(prev_string, pidflag);
		printf("    usr-ms system-ms  guest-ms  Command\n");
	}

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if ((rc = get_pid_to_display(prev, curr, P_A_CPU, P_CHILD, plist)) == 0)
			/* PID no longer exists */
			continue;

		/* This will be used to compute average */
		if (!disp_avg) {
			plist->uc_asum_count += 1;
		}

		if (rc < 0)
			/* PID should not be displayed */
			continue;

		print_line_id(curr_string, plist);

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		/* Write data for children of current task */
		write_plain_pid_child_cpu_data(disp_avg, plist, pstc, pstp);

		print_comm(plist);
		again = 1;
	}

	return again;
}

/*
 * **************************************************************************
 * Display CPU statistics for tasks' children in JSON format.
 *
 * IN:
 * @tab		Number of tabs to print.
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma.
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any).
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_json_pid_child_cpu_stats(int tab, int prev, int curr, int *follow)
{
	struct pid_stats *pstc, *pstp;
	struct st_pid *plist;
	int rc, again = 0, next = FALSE;

	if (*follow) {
		printf(",\n");
	}
	xprintf(tab++, "\"child-cpu-load\": [");

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if ((rc = get_pid_to_display(prev, curr, P_A_CPU, P_CHILD, plist)) <= 0)
			/* PID no longer exists or should not be displayed */
			continue;

		if (next) {
			printf(",\n");
		}
		next = TRUE;

		print_json_line_id(tab, plist);

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		printf("\"usr-ms\": %.0f, \"system-ms\": %.0f, \"guest-ms\": %.0f, ",
		       (pstc->utime + pstc->cutime - pstc->gtime - pstc->cgtime) <
		       (pstp->utime + pstp->cutime - pstp->gtime - pstp->cgtime) ||
		       (pstc->utime + pstc->cutime < pstc->gtime + pstc->cgtime) ||
		       (pstp->utime + pstp->cutime < pstp->gtime + pstp->cgtime) ?
		       0.0 :
		       (double) ((pstc->utime + pstc->cutime - pstc->gtime - pstc->cgtime) -
		       (pstp->utime + pstp->cutime - pstp->gtime - pstp->cgtime)) /
		       HZ * 1000,
		       (double) ((pstc->stime + pstc->cstime) -
				 (pstp->stime + pstp->cstime)) / HZ * 1000,
		       (double) ((pstc->gtime + pstc->cgtime) -
				 (pstp->gtime + pstp->cgtime)) / HZ * 1000);

		print_json_comm(plist);
		again = 1;
	}

	printf("\n");
	xprintf0(--tab, "]");
	*follow = TRUE;

	return again;
}

/*
 * **************************************************************************
 * Display CPU statistics for tasks' children.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @tab		Number of tabs to print (JSON format only).
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma (JSON format only).
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any). JSON format only.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_pid_child_cpu_stats(int prev, int curr, int dis, int disp_avg,
			      char *prev_string, char *curr_string, int tab,
			      int *follow)
{
	if (DISPLAY_JSON_OUTPUT(xflags)) {
		return write_json_pid_child_cpu_stats(tab, prev, curr, follow);
	}
	else {
		return write_plain_pid_child_cpu_stats(prev, curr, dis, disp_avg,
						       prev_string, curr_string);
	}
}

/*
 ***************************************************************************
 * Display memory statistics for tasks.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @itv		Interval of time in 1/100th of a second.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_plain_pid_task_memory_stats(int prev, int curr, int dis, int disp_avg,
				      char *prev_string, char *curr_string,
				      unsigned long long itv)
{
	struct pid_stats *pstc, *pstp;
	struct st_pid *plist;
	int rc, again = 0;

	if (dis) {
		PRINT_ID_HDR(prev_string, pidflag);
		printf("  minflt/s  majflt/s     VSZ     RSS   %%MEM  Command\n");
	}

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if ((rc = get_pid_to_display(prev, curr, P_A_MEM, P_TASK, plist)) == 0)
			/* PID no longer exists */
			continue;

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		/* This will be used to compute average */
		if (!disp_avg) {
			plist->total_vsz += pstc->vsz;
			plist->total_rss += pstc->rss;
			plist->rt_asum_count += 1;
		}

		if (rc < 0)
			/* PID should not be displayed */
			continue;

		print_line_id(curr_string, plist);

		/* Write current process data */
		write_plain_pid_task_memory_data(disp_avg, plist,
						 pstc, pstp, itv);

		print_comm(plist);
		again = 1;
	}

	return again;
}

/*
 * **************************************************************************
 * Display memory statistics for tasks in JSON format.
 *
 * IN:
 * @tab		Number of tabs to print.
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @itv		Interval of time in 1/100th of a second.
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma.
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any).
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_json_pid_task_memory_stats(int tab, int prev, int curr,
				     unsigned long long itv, int *follow)
{
	struct pid_stats *pstc, *pstp;
	struct st_pid *plist;
	int rc, again = 0, next = FALSE;

	if (*follow) {
		printf(",\n");
	}
	xprintf(tab++, "\"task-memory\": [");

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if ((rc = get_pid_to_display(prev, curr, P_A_MEM, P_TASK, plist)) <= 0)
			/* PID no longer exists or should not be displayed */
			continue;

		if (next) {
			printf(",\n");
		}
		next = TRUE;

		print_json_line_id(tab, plist);

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		printf("\"minflt/s\": %.2f, \"majflt/s\": %.2f, "
		       "\"VSZ\": %llu, \"RSS\": %llu, \"MEM\": %.2f, ",
		       S_VALUE(pstp->minflt, pstc->minflt, itv),
		       S_VALUE(pstp->majflt, pstc->majflt, itv),
		       (unsigned long long) pstc->vsz,
		       (unsigned long long) pstc->rss,
		       tlmkb ? SP_VALUE(0, pstc->rss, tlmkb) : 0.0);

		print_json_comm(plist);
		again = 1;
	}

	printf("\n");
	xprintf0(--tab, "]");
	*follow = TRUE;

	return again;
}

/*
 * **************************************************************************
 * Display memory statistics for tasks.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @itv		Interval of time in 1/100th of a second.
 * @tab		Number of tabs to print (JSON format only).
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma (JSON format only).
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any). JSON format only.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_pid_task_memory_stats(int prev, int curr, int dis, int disp_avg,
				char *prev_string, char *curr_string,
				unsigned long long itv, int tab, int *follow)
{
	if (DISPLAY_JSON_OUTPUT(xflags)) {
		return write_json_pid_task_memory_stats(tab, prev, curr,
							itv, follow);
	}
	else {
		return write_plain_pid_task_memory_stats(prev, curr, dis, disp_avg,
							 prev_string, curr_string, itv);
	}
}

/*
 ***************************************************************************
 * Display memory statistics for tasks' children.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_plain_pid_child_memory_stats(int prev, int curr, int dis, int disp_avg,
				       char *prev_string, char *curr_string)
{
	struct pid_stats *pstc, *pstp;
	struct st_pid *plist;
	int rc, again = 0;

	if (dis) {
		PRINT_ID_HDR(prev_string, pidflag);
		printf(" minflt-nr majflt-nr  Command\n");
	}

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if ((rc = get_pid_to_display(prev, curr, P_A_MEM, P_CHILD, plist)) == 0)
			/* PID no longer exists */
			continue;

		/* This will be used to compute average */
		if (!disp_avg) {
			plist->rc_asum_count += 1;
		}

		if (rc < 0)
			/* PID should not be displayed */
			continue;

		print_line_id(curr_string, plist);

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		/* Write data for current task */
		write_plain_pid_child_memory_data(disp_avg, plist, pstc, pstp);

		print_comm(plist);
		again = 1;
	}

	return again;
}

/*
 * **************************************************************************
 * Display memory statistics for tasks' children in JSON format.
 *
 * IN:
 * @tab		Number of tabs to print.
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma.
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any).
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_json_pid_child_memory_stats(int tab, int prev, int curr, int *follow)
{
	struct pid_stats *pstc, *pstp;
	struct st_pid *plist;
	int rc, again = 0, next = FALSE;

	if (*follow) {
		printf(",\n");
	}
	xprintf(tab++, "\"child-memory\": [");

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if ((rc = get_pid_to_display(prev, curr, P_A_MEM, P_CHILD, plist)) <= 0)
			/* PID no longer exists or should not be displayed */
			continue;

		if (next) {
			printf(",\n");
		}
		next = TRUE;

		print_json_line_id(tab, plist);

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		printf("\"minflt-nr\": %llu, \"majflt-nr\": %llu, ",
		       (unsigned long long) ((pstc->minflt + pstc->cminflt) - (pstp->minflt + pstp->cminflt)),
		       (unsigned long long) ((pstc->majflt + pstc->cmajflt) - (pstp->majflt + pstp->cmajflt)));

		print_json_comm(plist);
		again = 1;
	}

	printf("\n");
	xprintf0(--tab, "]");
	*follow = TRUE;

	return again;
}

/*
 * **************************************************************************
 * Display memory statistics for tasks' children.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @tab		Number of tabs to print (JSON format only).
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma (JSON format only).
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any). JSON format only.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_pid_child_memory_stats(int prev, int curr, int dis, int disp_avg,
				 char *prev_string, char *curr_string, int tab,
				 int *follow)
{
	if (DISPLAY_JSON_OUTPUT(xflags)) {
		return write_json_pid_child_memory_stats(tab, prev, curr, follow);
	}
	else {
		return write_plain_pid_child_memory_stats(prev, curr, dis, disp_avg,
							  prev_string, curr_string);
	}
}

/*
 ***************************************************************************
 * Display stack size statistics for tasks.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_plain_pid_stack_stats(int prev, int curr, int dis, int disp_avg,
				char *prev_string, char *curr_string)
{
	struct pid_stats *pstc;
	struct st_pid *plist;
	int rc, again = 0;

	if (dis) {
		PRINT_ID_HDR(prev_string, pidflag);
		printf(" StkSize  StkRef  Command\n");
	}

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if ((rc = get_pid_to_display(prev, curr, P_A_STACK, P_NULL, plist)) == 0)
			/* PID no longer exists */
			continue;

		pstc = plist->pstats[curr];

		/* This will be used to compute average */
		if (!disp_avg) {
			plist->total_stack_size += pstc->stack_size;
			plist->total_stack_ref  += pstc->stack_ref;
			plist->sk_asum_count += 1;
		}

		if (rc < 0)
			/* PID should not be displayed */
			continue;

		print_line_id(curr_string, plist);

		/* Write current process data */
		write_plain_pid_stack_data(disp_avg, plist, pstc);

		print_comm(plist);
		again = 1;
	}

	return again;
}

/*
 * **************************************************************************
 * Display stack size statistics for tasks in JSON format.
 *
 * IN:
 * @tab		Number of tabs to print.
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma.
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any).
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_json_pid_stack_stats(int tab, int prev, int curr, int *follow)
{
	struct pid_stats *pstc;
	struct st_pid *plist;
	int rc, again = 0, next = FALSE;

	if (*follow) {
		printf(",\n");
	}
	xprintf(tab++, "\"stack\": [");

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if ((rc = get_pid_to_display(prev, curr, P_A_STACK, P_NULL, plist)) <= 0)
			/* PID no longer exists or should not be displayed */
			continue;

		if (next) {
			printf(",\n");
		}
		next = TRUE;

		print_json_line_id(tab, plist);

		pstc = plist->pstats[curr];

		printf("\"StkSize\": %llu, \"StkRef\": %llu, ",
		       (unsigned long long) pstc->stack_size,
		       (unsigned long long) pstc->stack_ref);

		print_json_comm(plist);
		again = 1;
	}

	printf("\n");
	xprintf0(--tab, "]");
	*follow = TRUE;

	return again;
}

/*
 * **************************************************************************
 * Display stack size statistics for tasks.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @tab		Number of tabs to print (JSON format only).
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma (JSON format only).
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any). JSON format only.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_pid_stack_stats(int prev, int curr, int dis, int disp_avg,
			  char *prev_string, char *curr_string, int tab, int *follow)
{
	if (DISPLAY_JSON_OUTPUT(xflags)) {
		return write_json_pid_stack_stats(tab, prev, curr, follow);
	}
	else {
		return write_plain_pid_stack_stats(prev, curr, dis, disp_avg,
						   prev_string, curr_string);
	}
}

/*
 ***************************************************************************
 * Display I/O statistics.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @itv		Interval of time in 1/100th of a second.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_plain_pid_io_stats(int prev, int curr, int dis, int disp_avg,
			     char *prev_string, char *curr_string,
			     unsigned long long itv)
{
	struct pid_stats *pstc, *pstp;
	struct st_pid *plist;
	int rc, again = 0;

	if (dis) {
		PRINT_ID_HDR(prev_string, pidflag);
		printf("   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command\n");
	}

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if ((rc = get_pid_to_display(prev, curr, P_A_IO, P_NULL, plist)) == 0)
			/* PID no longer exists */
			continue;

		/* This will be used to compute average delays */
		if (!disp_avg) {
			plist->delay_asum_count += 1;
		}

		if (rc < 0)
			/* PID should not be displayed */
			continue;

		print_line_id(curr_string, plist);

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		/* Write statistics for current task */
		write_plain_pid_io_data(disp_avg, plist, pstc, pstp, itv);

		print_comm(plist);
		again = 1;
	}

	return again;
}

/*
 * **************************************************************************
 * Display I/O statistics in JSON format.
 *
 * IN:
 * @tab		Number of tabs to print.
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @itv		Interval of time in 1/100th of a second.
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma.
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any).
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_json_pid_io_stats(int tab, int prev, int curr, unsigned long long itv,
			    int *follow)
{
	struct pid_stats *pstc, *pstp;
	struct st_pid *plist;
	int rc, again = 0,  next = FALSE;
	double rbytes, wbytes, cbytes;

	if (*follow) {
		printf(",\n");
	}
	xprintf(tab++, "\"io\": [");

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if ((rc = get_pid_to_display(prev, curr, P_A_IO, P_NULL, plist)) <= 0)
			/* PID no longer exists or should not be displayed */
			continue;

		if (next) {
			printf(",\n");
		}
		next = TRUE;

		print_json_line_id(tab, plist);

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		if (!NO_PID_IO(plist->flags)) {
			rbytes = S_VALUE(pstp->read_bytes,  pstc->read_bytes, itv);
			wbytes = S_VALUE(pstp->write_bytes, pstc->write_bytes, itv);
			cbytes = S_VALUE(pstp->cancelled_write_bytes,
					 pstc->cancelled_write_bytes, itv);
			printf("\"kB_rd/s\": %.2f, \"kB_wr/s\": %.2f, \"kB_ccwr/s\": %.2f, ",
			       rbytes / 1024,
			       wbytes / 1024,
			       cbytes / 1024);
		}
		else {
			/* I/O file not readable (permission denied or file non existent) */
			printf("\"kB_rd/s\": -1.00, \"kB_wr/s\": -1.00, \"kB_ccwr/s\": -1.00, ");
		}
		/* I/O delays come from another file (/proc/#/stat) */
		printf("\"iodelay\": %llu, ",
		       (unsigned long long) (pstc->blkio_swapin_delays - pstp->blkio_swapin_delays));

		print_json_comm(plist);
		again = 1;
	}

	printf("\n");
	xprintf0(--tab, "]");
	*follow = TRUE;

	return again;
}

/*
 * **************************************************************************
 * Display I/O statistics.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @itv		Interval of time in 1/100th of a second.
 * @tab		Number of tabs to print (JSON format only).
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma (JSON format only).
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any). JSON format only.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_pid_io_stats(int prev, int curr, int dis, int disp_avg,
		       char *prev_string, char *curr_string,
		       unsigned long long itv, int tab, int *follow)
{
	if (DISPLAY_JSON_OUTPUT(xflags)) {
		return write_json_pid_io_stats(tab, prev, curr, itv, follow);
	}
	else {
		return write_plain_pid_io_stats(prev, curr, dis, disp_avg,
						prev_string, curr_string, itv);
	}
}

/*
 ***************************************************************************
 * Display context switches statistics.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @itv		Interval of time in 1/100th of a second.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_plain_pid_ctxswitch_stats(int prev, int curr, int dis,
				    char *prev_string, char *curr_string,
				    unsigned long long itv)
{
	struct pid_stats *pstc, *pstp;

	struct st_pid *plist;
	int again = 0;

	if (dis) {
		PRINT_ID_HDR(prev_string, pidflag);
		printf("   cswch/s nvcswch/s  Command\n");
	}

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if (get_pid_to_display(prev, curr, P_A_CTXSW, P_NULL, plist) <= 0)
			continue;

		print_line_id(curr_string, plist);

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		/* Write statistics for current task */
		write_plain_pid_ctxswitch_data(pstc, pstp, itv);

		print_comm(plist);
		again = 1;
	}

	return again;
}

/*
 * **************************************************************************
 * Display context switches statistics in JSON format.
 *
 * IN:
 * @tab		Number of tabs to print.
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @itv		Interval of time in 1/100th of a second.
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma.
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any).
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_json_pid_ctxswitch_stats(int tab, int prev, int curr,
				   unsigned long long itv, int *follow)
{
	struct pid_stats *pstc, *pstp;
	struct st_pid *plist;
	int again = 0, next = FALSE;

	if (*follow) {
		printf(",\n");
	}
	xprintf(tab++, "\"context-switch\": [");

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if (get_pid_to_display(prev, curr, P_A_CTXSW, P_NULL, plist) <= 0)
			/* PID no longer exists or should not be displayed */
			continue;

		if (next) {
			printf(",\n");
		}
		next = TRUE;

		print_json_line_id(tab, plist);

		pstc = plist->pstats[curr];
		pstp = plist->pstats[prev];

		printf("\"cswch/s\": %.2f, \"nvcswch/s\": %.2f, ",
		       S_VALUE(pstp->nvcsw,  pstc->nvcsw,  itv),
		       S_VALUE(pstp->nivcsw, pstc->nivcsw, itv));

		print_json_comm(plist);
		again = 1;
	}

	printf("\n");
	xprintf0(--tab, "]");
	*follow = TRUE;

	return again;
}

/*
 * **************************************************************************
 * Display context switches statistics.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @itv		Interval of time in 1/100th of a second.
 * @tab		Number of tabs to print (JSON format only).
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma (JSON format only).
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any). JSON format only.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_pid_ctxswitch_stats(int prev, int curr, int dis,
			      char *prev_string, char *curr_string,
			      unsigned long long itv, int tab, int *follow)
{
	if (DISPLAY_JSON_OUTPUT(xflags)) {
		return write_json_pid_ctxswitch_stats(tab, prev, curr, itv, follow);
	}
	else {
		return write_plain_pid_ctxswitch_stats(prev, curr, dis,
						       prev_string, curr_string, itv);
	}
}

/*
 ***************************************************************************
 * Display some kernel tables values for tasks.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_plain_pid_ktab_stats(int prev, int curr, int dis, int disp_avg,
			       char *prev_string, char *curr_string)
{
	struct pid_stats *pstc;
	struct st_pid *plist;
	int rc, again = 0;

	if (dis) {
		PRINT_ID_HDR(prev_string, pidflag);
		printf(" threads   fd-nr");
		printf("  Command\n");
	}

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if ((rc = get_pid_to_display(prev, curr, P_A_KTAB, P_NULL, plist)) == 0)
			/* PID no longer exists */
			continue;

		pstc = plist->pstats[curr];

		/* This will be used to compute average */
		if (!disp_avg) {
			plist->total_threads += pstc->threads;
			plist->total_fd_nr   += pstc->fd_nr;
			plist->tf_asum_count += 1;
		}

		if (rc < 0)
			/* PID should not be displayed */
			continue;

		print_line_id(curr_string, plist);

		/* Write data for current task */
		write_plain_pid_ktab_data(disp_avg, plist, pstc);

		print_comm(plist);
		again = 1;
	}

	return again;
}

/*
 * **************************************************************************
 * Display some kernel tables values for tasks in JSON format.
 *
 * IN:
 * @tab		Number of tabs to print.
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma.
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any).
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_json_pid_ktab_stats(int tab, int prev, int curr, int *follow)
{
	struct pid_stats *pstc;
	struct st_pid *plist;
	int rc, again = 0, next = FALSE;

	if (*follow) {
		printf(",\n");
	}
	xprintf(tab++, "\"kernel\": [");

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if ((rc = get_pid_to_display(prev, curr, P_A_KTAB, P_NULL, plist)) <= 0)
			/* PID no longer exists or should not be displayed */
			continue;

		if (next) {
			printf(",\n");
		}
		next = TRUE;

		print_json_line_id(tab, plist);

		pstc = plist->pstats[curr];

		printf("\"threads\": %llu, \"fd-nr\": ",
		       (unsigned long long) pstc->threads);
		if (NO_PID_FD(plist->flags)) {
			printf("-1, ");
		}
		else {
			printf("%llu, ", (unsigned long long) pstc->fd_nr);
		}

		print_json_comm(plist);
		again = 1;
	}

	printf("\n");
	xprintf0(--tab, "]");
	*follow = TRUE;

	return again;
}

/*
 * **************************************************************************
 * Display some kernel tables values for tasks.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @tab		Number of tabs to print (JSON format only).
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma (JSON format only).
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any). JSON format only.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_pid_ktab_stats(int prev, int curr, int dis, int disp_avg,
			 char *prev_string, char *curr_string, int tab, int *follow)
{
	if (DISPLAY_JSON_OUTPUT(xflags)) {
		return write_json_pid_ktab_stats(tab, prev, curr, follow);
	}
	else {
		return write_plain_pid_ktab_stats(prev, curr, dis, disp_avg,
						  prev_string, curr_string);
	}
}

/*
 * **************************************************************************
 * Display scheduling priority and policy information.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_plain_pid_rt_stats(int prev, int curr, int dis,
			     char *prev_string, char *curr_string)
{
	struct pid_stats *pstc;
	struct st_pid *plist;
	int again = 0;

	if (dis) {
		PRINT_ID_HDR(prev_string, pidflag);
		printf(" prio policy  Command\n");
	}

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if (get_pid_to_display(prev, curr, P_A_RT, P_NULL, plist) <= 0)
			continue;

		print_line_id(curr_string, plist);

		pstc = plist->pstats[curr];

		/* Write data for current task */
		write_plain_pid_rt_data(pstc);

		print_comm(plist);
		again = 1;
	}

	return again;
}

/*
 * **************************************************************************
 * Display scheduling priority and policy information in JSON format.
 *
 * IN:
 * @tab		Number of tabs to print.
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma.
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any).
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_json_pid_rt_stats(int tab, int prev, int curr, int *follow)
{
	struct pid_stats *pstc;
	struct st_pid *plist;
	int again = 0, next = FALSE;

	if (*follow) {
		printf(",\n");
	}
	xprintf(tab++, "\"realtime\": [");

	for (plist = pid_list; plist != NULL; plist = plist->next) {

		if (get_pid_to_display(prev, curr, P_A_RT, P_NULL, plist) <= 0)
			/* PID no longer exists or should not be displayed */
			continue;

		if (next) {
			printf(",\n");
		}
		next = TRUE;

		print_json_line_id(tab, plist);

		pstc = plist->pstats[curr];

		printf("\"prio\": %llu, \"policy\": \"%s\", ",
		       (unsigned long long) pstc->priority,
		       GET_POLICY(pstc->policy));

		print_json_comm(plist);
		again = 1;
	}

	printf("\n");
	xprintf0(--tab, "]");
	*follow = TRUE;

	return again;
}

/*
 * **************************************************************************
 * Display scheduling priority and policy information.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 * @tab		Number of tabs to print (JSON format only).
 * @follow	True if a previous set of stats has been displayed and should
 *		be followed by a coma (JSON format only).
 *
 * OUT:
 * @follow	Set to TRUE to indicate that a coma should be displayed
 *		before next set of stats (if any). JSON format only.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_pid_rt_stats(int prev, int curr, int dis,
		       char *prev_string, char *curr_string, int tab, int *follow)
{
	if (DISPLAY_JSON_OUTPUT(xflags)) {
		return write_json_pid_rt_stats(tab, prev, curr, follow);
	}
	else {
		return write_plain_pid_rt_stats(prev, curr, dis,
						prev_string, curr_string);
	}
}

/*
 ***************************************************************************
 * Display statistics.
 *
 * IN:
 * @prev	Index in array where stats used as reference are.
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 * @disp_avg	TRUE if average stats are displayed.
 * @prev_string	String displayed at the beginning of a header line. This is
 * 		the timestamp of the previous sample, or "Average" when
 * 		displaying average stats.
 * @curr_string	String displayed at the beginning of current sample stats.
 * 		This is the timestamp of the current sample, or "Average"
 * 		when displaying average stats.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_stats_core(int prev, int curr, int dis, int disp_avg,
		     char *prev_string, char *curr_string)
{
	unsigned long long itv, deltot_jiffies;
	int again = 0, tab = 4, follow = FALSE;

	/* Test stdout */
	TEST_STDOUT(STDOUT_FILENO);

	/* Total number of jiffies spent on the interval */
	deltot_jiffies = get_interval(tot_jiffies[prev], tot_jiffies[curr]);

	if (DISPLAY_JSON_OUTPUT(xflags)) {
		xprintf(tab++, "{");
		xprintf(tab, "\"timestamp\": \"%s\",", curr_string);
	}

	itv = get_interval(uptime_cs[prev], uptime_cs[curr]);

	if (DISPLAY_ONELINE(pidflag)) {
		if (DISPLAY_TASK_STATS(tskflag)) {
			again += write_pid_task_all_stats(prev, curr, dis,
							  prev_string, curr_string,
							  itv, deltot_jiffies);
		}
		if (DISPLAY_CHILD_STATS(tskflag)) {
			again += write_pid_child_all_stats(prev, curr, dis,
							   prev_string, curr_string);
		}
	}
	else {
		/* Display CPU stats */
		if (DISPLAY_CPU(actflag)) {
			if (DISPLAY_TASK_STATS(tskflag)) {
				again += write_pid_task_cpu_stats(prev, curr, dis, disp_avg,
								  prev_string, curr_string,
								  itv, deltot_jiffies,
								  tab, &follow);
			}
			if (DISPLAY_CHILD_STATS(tskflag)) {
				again += write_pid_child_cpu_stats(prev, curr, dis, disp_avg,
								   prev_string, curr_string,
								   tab, &follow);
			}
		}

		/* Display memory stats */
		if (DISPLAY_MEM(actflag)) {
			if (DISPLAY_TASK_STATS(tskflag)) {
				again += write_pid_task_memory_stats(prev, curr, dis, disp_avg,
								     prev_string, curr_string,
								     itv, tab, &follow);
			}
			if (DISPLAY_CHILD_STATS(tskflag)) {
				again += write_pid_child_memory_stats(prev, curr, dis, disp_avg,
								      prev_string, curr_string,
								      tab, &follow);
			}
		}

		/* Display stack stats */
		if (DISPLAY_STACK(actflag)) {
			again += write_pid_stack_stats(prev, curr, dis, disp_avg,
						       prev_string, curr_string,
						       tab, &follow);
		}

		/* Display I/O stats */
		if (DISPLAY_IO(actflag)) {
			again += write_pid_io_stats(prev, curr, dis, disp_avg, prev_string,
						    curr_string, itv, tab, &follow);
		}

		/* Display context switches stats */
		if (DISPLAY_CTXSW(actflag)) {
			again += write_pid_ctxswitch_stats(prev, curr, dis, prev_string,
							   curr_string, itv,
							   tab, &follow);
		}

		/* Display kernel table stats */
		if (DISPLAY_KTAB(actflag)) {
			again += write_pid_ktab_stats(prev, curr, dis, disp_avg,
						      prev_string, curr_string,
						      tab, &follow);
		}

		/* Display scheduling priority and policy information */
		if (DISPLAY_RT(actflag)) {
			again += write_pid_rt_stats(prev, curr, dis, prev_string,
						    curr_string, tab, &follow);
		}
	}

	if (DISPLAY_JSON_OUTPUT(xflags)) {
		printf("\n");
		xprintf0(--tab, "}");
	}

	if (DISPLAY_ALL_PID(pidflag)) {
		again = 1;
	}

	return again;
}

/*
 ***************************************************************************
 * Print statistics average.
 *
 * IN:
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 ***************************************************************************
 */
void write_stats_avg(int curr, int dis)
{
	char string[16];

	snprintf(string, sizeof(string), "%s", _("Average:"));
	write_stats_core(2, curr, dis, TRUE, string, string);
}

/*
 ***************************************************************************
 * Get previous and current timestamps, then display statistics.
 *
 * IN:
 * @curr	Index in array for current sample statistics.
 * @dis		TRUE if a header line must be printed.
 *
 * RETURNS:
 * 0 if all the processes to display have terminated.
 * <> 0 if there are still some processes left to display.
 ***************************************************************************
 */
int write_stats(int curr, int dis)
{
	char cur_time[2][TIMESTAMP_LEN];

	/* Get previous timestamp */
	if (DISPLAY_ONELINE(pidflag)) {
		strcpy(cur_time[!curr], "# Time     ");
	}
	else if (PRINT_SEC_EPOCH(pidflag)) {
		snprintf(cur_time[!curr], sizeof(cur_time[!curr]), "%ld", (long) mktime(&ps_tstamp[!curr]));
	}
	else if (is_iso_time_fmt()) {
		strftime(cur_time[!curr], sizeof(cur_time[!curr]), "%H:%M:%S", &ps_tstamp[!curr]);
	}
	else {
		strftime(cur_time[!curr], sizeof(cur_time[!curr]), "%X", &ps_tstamp[!curr]);
	}

	/* Get current timestamp */
	if (PRINT_SEC_EPOCH(pidflag)) {
		snprintf(cur_time[curr], sizeof(cur_time[curr]), "%ld", (long) mktime(&ps_tstamp[curr]));
	}
	else if (is_iso_time_fmt()) {
		strftime(cur_time[curr], sizeof(cur_time[curr]), "%H:%M:%S", &ps_tstamp[curr]);
	}
	else {
		strftime(cur_time[curr], sizeof(cur_time[curr]), "%X", &ps_tstamp[curr]);
	}

	/* Make sure timestamp is null-terminated */
	cur_time[!curr][sizeof(cur_time[!curr]) - 1] = '\0';
	cur_time[curr][sizeof(cur_time[curr]) - 1] = '\0';

	return (write_stats_core(!curr, curr, dis, FALSE,
				 cur_time[!curr], cur_time[curr]));
}

/*
 ***************************************************************************
 * Main loop: Read and display PID stats.
 *
 * IN:
 * @dis_hdr	Set to TRUE if the header line must always be printed.
 * @rows	Number of rows of screen.
 ***************************************************************************
 */
void rw_pidstat_loop(int dis_hdr, int rows)
{
	int curr = 1, dis = 1;
	int again;
	unsigned long lines = rows;
	struct st_pid *plist;

	/* Read system uptime */
	read_uptime(&uptime_cs[0]);
	read_stats(0);

	if (DISPLAY_MEM(actflag)) {
		/* Get total memory */
		read_proc_meminfo();
	}

	if (!interval && !EXEC_PGM(pidflag)) {
		/* Display since boot time */
		ps_tstamp[1] = ps_tstamp[0];
		write_stats(0, DISP_HDR);
		if (DISPLAY_JSON_OUTPUT(xflags)) {
			printf("\n");
		}
		goto terminate;
	}

	/* Set a handler for SIGALRM */
	memset(&alrm_act, 0, sizeof(alrm_act));
	alrm_act.sa_handler = alarm_handler;
	sigaction(SIGALRM, &alrm_act, NULL);
	if (interval) {
		alarm(interval);
	}

	/* Save the first stats collected. Will be used to compute the average */
	ps_tstamp[2] = ps_tstamp[0];
	tot_jiffies[2] = tot_jiffies[0];
	uptime_cs[2] = uptime_cs[0];
	for (plist = pid_list; plist != NULL; plist = plist->next) {
		memcpy(plist->pstats[2], plist->pstats[0], PID_STATS_SIZE);
	}

	/* Set a handler for SIGINT and SIGTERM */
	memset(&int_act, 0, sizeof(int_act));
	int_act.sa_handler = int_handler;
	sigaction(SIGINT, &int_act, NULL);
	sigaction(SIGTERM, &int_act, NULL);

	/* Wait for SIGALRM (or possibly SIGINT) signal */
	__pause();

	if (signal_caught && interval)
		/*
		 * SIGINT/SIGTERM/SIGCHLD signals caught during first interval:
		 * Exit immediately.
		 */
		goto terminate;

	do {
		/* Every PID is potentially nonexistent */
		set_pid_nonexistent(pid_list);

		/* Get time */
		get_xtime(&ps_tstamp[curr], 0, LOCAL_TIME);

		/* Read system uptime (in 1/100th of a second) */
		read_uptime(&(uptime_cs[curr]));

		/* Read stats */
		read_stats(curr);

		if (!dis_hdr) {
			dis = lines / rows;
			if (dis) {
				lines %= rows;
			}
			lines++;
		}

		/* Print results */
		again = write_stats(curr, dis);

		if (!again)
			return;

		if (count > 0) {
			count--;
		}

		if (count) {

			__pause();

			if (signal_caught) {
				/*
				 * SIGINT/SIGTERM/SIGCHLD signals caught:
				 * Display average stats.
				 */
				count = 0;
			}
			else {
				if (DISPLAY_JSON_OUTPUT(xflags)) {
					printf(",");
				}
				curr ^= 1;
			}
		}
		if (signal_caught || DISPLAY_JSON_OUTPUT(xflags)) {
			printf("\n");
		}
	}
	while (count);

	/*
	 * The one line format uses a raw time value rather than time strings
	 * so the average doesn't really fit.
	 */
	if (!DISPLAY_ONELINE(pidflag) && !DISPLAY_JSON_OUTPUT(xflags))
	{
		/* Write stats average */
		write_stats_avg(curr, dis_hdr);
	}

terminate:
	if (DISPLAY_JSON_OUTPUT(xflags)) {
		printf("\t\t\t]\n\t\t}\n\t]\n}}\n");
	}
}

/*
 ***************************************************************************
 * Start a program that will be monitored by pidstat.
 *
 * IN:
 * @argc	Number of arguments.
 * @argv	Arguments values.
 *
 * RETURNS:
 * The PID of the program executed.
 ***************************************************************************
 */
pid_t exec_pgm(int argc, char **argv)
{
	pid_t child;
	char *args[argc + 1];
	int i;

	child = __fork();

	switch(child) {

		case -1:
			perror("fork");
			exit(4);
			break;

		case 0:
			/* Child */
			for (i = 0; i < argc; i++) {
				args[i] = argv[i];
			}
			args[argc] = NULL;

			execvp(args[0], args);
			perror("exec");
			exit(4);
			break;

		default:
			/*
			 * Parent.
			 * Set a handler for SIGCHLD (signal that will be received
			 * by pidstat when the child program terminates).
			 * The handler is the same as for SIGINT: Stop and display
			 * average statistics.
			 */
			memset(&chld_act, 0, sizeof(chld_act));
			chld_act.sa_handler = int_handler;
			sigaction(SIGCHLD, &chld_act, NULL);

			return child;
	}
}

/*
 ***************************************************************************
 * Main entry to the pidstat program.
 ***************************************************************************
 */
int main(int argc, char **argv)
{
	int opt = 1, dis_hdr = -1, pid_nr = 0;
	int i;
	unsigned int pid;
	struct utsname header;
	int rows = 23;
	char *t;

#ifdef USE_NLS
	/* Init National Language Support */
	init_nls();
#endif

	/* Init color strings */
	init_colors();

	/* Get HZ */
	get_HZ();

	/* Compute page shift in kB */
	get_kb_shift();

	/* Process args... */
	while (opt < argc) {

		if (!strcmp(argv[opt], "-e")) {
			if (!argv[++opt]) {
				usage(argv[0]);
			}
			pidflag |= P_D_PID + P_F_EXEC_PGM;
			add_list_pid(&pid_list, exec_pgm(argc - opt, argv + opt), 0);
			break;
		}

		else if (!strcmp(argv[opt], "-p")) {
			pidflag |= P_D_PID;
			if (!argv[++opt]) {
				usage(argv[0]);
			}

			for (t = strtok(argv[opt], ","); t; t = strtok(NULL, ","), pid_nr++) {
				if (!strcmp(t, K_ALL)) {
					pidflag |= P_D_ALL_PID;
					pid_nr++;
				}
				else if (!strcmp(t, K_SELF)) {
					add_list_pid(&pid_list, getpid(), 0);
				}
				else {
					if (strspn(t, DIGITS) != strlen(t)) {
						usage(argv[0]);
					}
					pid = atoi(t);
					if (pid < 1) {
						usage(argv[0]);
					}
					add_list_pid(&pid_list, pid, 0);
				}
			}
			opt++;
		}

		else if (!strcmp(argv[opt], "-C")) {
			if (!argv[++opt]) {
				usage(argv[0]);
			}
			snprintf(commstr, sizeof(commstr), "%s", argv[opt++]);
			pidflag |= P_F_COMMSTR;
			if (!strlen(commstr)) {
				usage(argv[0]);
			}
		}

		else if (!strcmp(argv[opt], "-G")) {
			if (!argv[++opt]) {
				usage(argv[0]);
			}
			snprintf(procstr, sizeof(procstr), "%s", argv[opt++]);
			pidflag |= P_F_PROCSTR;
			if (!strlen(procstr)) {
				usage(argv[0]);
			}
		}

		else if (!strcmp(argv[opt], "--human")) {
			pidflag |= P_D_UNIT;
			opt++;
		}

#ifdef TEST
		else if (!strncmp(argv[opt], "--getenv", 8)) {
			__env = TRUE;
			opt++;
		}
#endif

		else if (!strncmp(argv[opt], "--dec=", 6) && (strlen(argv[opt]) == 7)) {
			/* Check that the argument is a digit */
			if (!isdigit(argv[opt][6])) {
				usage(argv[0]);
			}

			/* Get number of decimal places */
			dplaces_nr = atoi(argv[opt] + 6);
			if ((dplaces_nr < 0) || (dplaces_nr > 2)) {
				usage(argv[0]);
			}
			opt++;
		}

		else if (!strcmp(argv[opt], "-T")) {
			if (!argv[++opt]) {
				usage(argv[0]);
			}
			if (tskflag) {
				dis_hdr++;
			}
			if (!strcmp(argv[opt], K_P_TASK)) {
				tskflag |= P_TASK;
			}
			else if (!strcmp(argv[opt], K_P_CHILD)) {
				tskflag |= P_CHILD;
			}
			else if (!strcmp(argv[opt], K_P_ALL)) {
				tskflag |= P_TASK + P_CHILD;
				dis_hdr++;
			}
			else {
				usage(argv[0]);
			}
			opt++;
		}

		/* Option used individually. See below for grouped option */
		else if (!strcmp(argv[opt], "-U")) {
			/* Display username instead of UID */
			pidflag |= P_D_USERNAME;
			if (argv[++opt] && (argv[opt][0] != '-') &&
			    (strspn(argv[opt], DIGITS) != strlen(argv[opt]))) {
				snprintf(userstr, sizeof(userstr), "%s", argv[opt++]);
				pidflag |= P_F_USERSTR;
				if (!strlen(userstr)) {
					usage(argv[0]);
				}
			}
		}

		else if (!strcmp(argv[opt], "-o")) {
			/* Select output format */
			if (argv[++opt] && !strcmp(argv[opt], K_JSON)) {
				xflags |= X_D_JSON_OUTPUT;
				opt++;
			}
			else {
				usage(argv[0]);
			}
		}

		else if (!strncmp(argv[opt], "-", 1)) {
			for (i = 1; *(argv[opt] + i); i++) {

				switch (*(argv[opt] + i)) {

				case 'd':
					/* Display I/O usage */
					actflag |= P_A_IO;
					dis_hdr++;
					break;

				case 'H':
					/* Display timestamps in sec since the epoch */
					pidflag |= P_D_SEC_EPOCH;
					break;

				case 'h':
					/* Display stats on one line */
					pidflag |= P_D_ONELINE;
					break;

				case 'I':
					/* IRIX mode off */
					pidflag |= P_F_IRIX_MODE;
					break;

				case 'l':
					/* Display whole command line */
					pidflag |= P_D_CMDLINE;
					break;

				case 'R':
					/* Display priority and policy info */
					actflag |= P_A_RT;
					dis_hdr++;
					break;

				case 'r':
					/* Display memory usage */
					actflag |= P_A_MEM;
					dis_hdr++;
					break;

				case 's':
					/* Display stack sizes */
					actflag |= P_A_STACK;
					dis_hdr++;
					break;

				case 't':
					/* Display stats for threads */
					pidflag |= P_D_TID;
					break;

				case 'U':
					/* When option is grouped, it cannot take an arg */
					pidflag |= P_D_USERNAME;
					break;

				case 'u':
					/* Display CPU usage */
					actflag |= P_A_CPU;
					dis_hdr++;
					break;

				case 'V':
					{
						const char *pidstat_env[] = {ENV_COLORS,
									     ENV_COLORS_SGR,
									     ENV_TIME_FMT};
#define PIDSTAT_ENV_NR	3
						/* Print environment contents, version number and exit */
						print_version(pidstat_env, PIDSTAT_ENV_NR);
						break;
					}

				case 'v':
					/* Display some kernel tables values */
					actflag |= P_A_KTAB;
					dis_hdr++;
					break;

				case 'w':
					/* Display context switches */
					actflag |= P_A_CTXSW;
					dis_hdr++;
					break;

				default:
					usage(argv[0]);
				}
			}
			opt++;
		}

		else if (interval < 0) {	/* Get interval */
			if (strspn(argv[opt], DIGITS) != strlen(argv[opt])) {
				usage(argv[0]);
			}
			interval = atol(argv[opt++]);
			if (interval < 0) {
				usage(argv[0]);
			}
			count = -1;
		}

		else if (count <= 0) {	/* Get count value */
			if ((strspn(argv[opt], DIGITS) != strlen(argv[opt])) ||
			    !interval) {
				usage(argv[0]);
			}
			count = atol(argv[opt++]);
			if (count < 1) {
				usage(argv[0]);
			}
		}
		else {
			usage(argv[0]);
		}
	}

	if (interval < 0) {
		/* Interval not set => display stats since boot time */
		interval = 0;
	}

	/* A non-zero value must be specified for interval when option -e is used */
	if (!interval && EXEC_PGM(pidflag))
		usage(argv[0]);

	if (!DISPLAY_PID(pidflag)) {
		dis_hdr = 1;
	}

	/* Check flags and set default values */
	check_flags();

	/* Count nb of proc */
	cpu_nr = get_cpu_nr(~0, FALSE);

	if (dis_hdr < 0) {
		dis_hdr = 0;
	}
	if (!dis_hdr) {
		if ((pid_nr > 1)) {
			dis_hdr = 1;
		}
		else {
			rows = get_win_height();
		}
	}

	/* Get time */
	get_xtime(&(ps_tstamp[0]), 0, LOCAL_TIME);

	if (DISPLAY_JSON_OUTPUT(xflags)) {
		/* Use a decimal point to make JSON code compliant with RFC7159 */
		setlocale(LC_NUMERIC, "C");
		/* Option "-h" cannot be used in JSON format */
		pidflag &= ~P_D_ONELINE;
	}

	/*
	 * Don't buffer data if redirected to a pipe.
	 * Note: With musl-c, the behavior of this function is undefined except
	 * when it is the first operation on the stream.
	 */
	setbuf(stdout, NULL);

	/* Get system name, release number and hostname */
	__uname(&header);
	print_gal_header(&(ps_tstamp[0]), header.sysname, header.release,
			 header.nodename, header.machine, cpu_nr,
			 DISPLAY_JSON_OUTPUT(xflags));

	/* Main loop */
	rw_pidstat_loop(dis_hdr, rows);

	/* Free structures */
	sfree_pid(&pid_list, TRUE);

	/*
	 * @status contains the exit code of the child process monitored with option -e,
	 * or 0 otherwise.
	 */
	return status;
}
