/*
 * pmap implementation for busybox
 *
 * Copyright (C) 2010 Nokia Corporation. All rights reserved.
 * Written by Alexander Shishkin <virtuoso@slind.org>
 *
 * Licensed under GPLv2 or later, see the LICENSE file in this source tree
 * for details.
 */

//config:config PMAP
//config:       bool "pmap"
//config:       default y
//config:       help
//config:         Display processes' memory mappings.

//applet:IF_PMAP(APPLET(pmap, BB_DIR_USR_BIN, BB_SUID_DROP))
//kbuild:lib-$(CONFIG_PMAP) += pmap.o

//usage:#define pmap_trivial_usage
//usage:       "[-xq] PID"
//usage:#define pmap_full_usage "\n\n"
//usage:       "Display detailed process memory usage"
//usage:     "\n"
//usage:     "\n	-x	Show details"
//usage:     "\n	-q	Quiet"

#include "libbb.h"

#if ULONG_MAX == 0xffffffff
# define TABS "\t"
# define AFMT "8"
# define DASHES ""
#else
# define TABS "\t\t"
# define AFMT "16"
# define DASHES "--------"
#endif

enum {
	OPT_x = 1 << 0,
	OPT_q = 1 << 1,
};

static void print_smaprec(struct smaprec *currec, void *data)
{
	unsigned opt = (uintptr_t)data;

	printf("%0" AFMT "lx ", currec->smap_start);

	if (opt & OPT_x)
		printf("%7lu %7lu %7lu %7lu ",
			currec->smap_size,
			currec->smap_pss,
			currec->private_dirty,
			currec->smap_swap);
	else
		printf("%7luK", currec->smap_size);

	printf(" %.4s  %s\n", currec->smap_mode, currec->smap_name);
}

static int procps_get_maps(pid_t pid, unsigned opt)
{
	struct smaprec total;
	int ret;
	char buf[256];

	read_cmdline(buf, sizeof(buf), pid, "no such process");
	printf("%u: %s\n", (int)pid, buf);

	if (!(opt & OPT_q) && (opt & OPT_x))
		puts("Address" TABS "  Kbytes     PSS   Dirty    Swap  Mode  Mapping");

	memset(&total, 0, sizeof(total));

	ret = procps_read_smaps(pid, &total, print_smaprec, (void*)(uintptr_t)opt);
	if (ret)
		return ret;

	if (!(opt & OPT_q)) {
		if (opt & OPT_x)
			printf("--------" DASHES "  ------  ------  ------  ------\n"
				"total" TABS " %7lu %7lu %7lu %7lu\n",
				total.smap_size, total.smap_pss, total.private_dirty, total.smap_swap);
		else
			printf("mapped: %luK\n", total.smap_size);
	}

	return 0;
}

int pmap_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int pmap_main(int argc UNUSED_PARAM, char **argv)
{
	unsigned opts;
	int ret;

	opts = getopt32(argv, "xq");
	argv += optind;

	ret = 0;
	while (*argv) {
		pid_t pid = xatoi_positive(*argv++);
		/* GNU pmap returns 42 if any of the pids failed */
		if (procps_get_maps(pid, opts) != 0)
			ret = 42;
	}

	return ret;
}
