Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

  • 7e636a7
  • /
  • tools
  • /
  • mm
  • /
  • page_owner_sort.c
Raw File Download
Permalinks

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
content badge Iframe embedding
swh:1:cnt:99798894b8790544b3dd83c222c9237af32b94a7
directory badge Iframe embedding
swh:1:dir:1c702732f3a61e539848aff6f6d025423be7db82
Citations

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
page_owner_sort.c
// SPDX-License-Identifier: GPL-2.0
/*
 * User-space helper to sort the output of /sys/kernel/debug/page_owner
 *
 * Example use:
 * cat /sys/kernel/debug/page_owner > page_owner_full.txt
 * ./page_owner_sort page_owner_full.txt sorted_page_owner.txt
 * Or sort by total memory:
 * ./page_owner_sort -m page_owner_full.txt sorted_page_owner.txt
 *
 * See Documentation/mm/page_owner.rst
*/

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <regex.h>
#include <errno.h>
#include <linux/types.h>
#include <getopt.h>

#define bool int
#define true 1
#define false 0
#define TASK_COMM_LEN 16

struct block_list {
	char *txt;
	char *comm; // task command name
	char *stacktrace;
	__u64 ts_nsec;
	__u64 free_ts_nsec;
	int len;
	int num;
	int page_num;
	pid_t pid;
	pid_t tgid;
	int allocator;
};
enum FILTER_BIT {
	FILTER_UNRELEASE = 1<<1,
	FILTER_PID = 1<<2,
	FILTER_TGID = 1<<3,
	FILTER_COMM = 1<<4
};
enum CULL_BIT {
	CULL_UNRELEASE = 1<<1,
	CULL_PID = 1<<2,
	CULL_TGID = 1<<3,
	CULL_COMM = 1<<4,
	CULL_STACKTRACE = 1<<5,
	CULL_ALLOCATOR = 1<<6
};
enum ALLOCATOR_BIT {
	ALLOCATOR_CMA = 1<<1,
	ALLOCATOR_SLAB = 1<<2,
	ALLOCATOR_VMALLOC = 1<<3,
	ALLOCATOR_OTHERS = 1<<4
};
enum ARG_TYPE {
	ARG_TXT, ARG_COMM, ARG_STACKTRACE, ARG_ALLOC_TS, ARG_FREE_TS,
	ARG_CULL_TIME, ARG_PAGE_NUM, ARG_PID, ARG_TGID, ARG_UNKNOWN, ARG_FREE,
	ARG_ALLOCATOR
};
enum SORT_ORDER {
	SORT_ASC = 1,
	SORT_DESC = -1,
};
struct filter_condition {
	pid_t *pids;
	pid_t *tgids;
	char **comms;
	int pids_size;
	int tgids_size;
	int comms_size;
};
struct sort_condition {
	int (**cmps)(const void *, const void *);
	int *signs;
	int size;
};
static struct filter_condition fc;
static struct sort_condition sc;
static regex_t order_pattern;
static regex_t pid_pattern;
static regex_t tgid_pattern;
static regex_t comm_pattern;
static regex_t ts_nsec_pattern;
static regex_t free_ts_nsec_pattern;
static struct block_list *list;
static int list_size;
static int max_size;
static int cull;
static int filter;
static bool debug_on;

static void set_single_cmp(int (*cmp)(const void *, const void *), int sign);

int read_block(char *buf, char *ext_buf, int buf_size, FILE *fin)
{
	char *curr = buf, *const buf_end = buf + buf_size;

	while (buf_end - curr > 1 && fgets(curr, buf_end - curr, fin)) {
		if (*curr == '\n') { /* empty line */
			return curr - buf;
		}
		if (!strncmp(curr, "PFN", 3)) {
			strcpy(ext_buf, curr);
			continue;
		}
		curr += strlen(curr);
	}

	return -1; /* EOF or no space left in buf. */
}

static int compare_txt(const void *p1, const void *p2)
{
	const struct block_list *l1 = p1, *l2 = p2;

	return strcmp(l1->txt, l2->txt);
}

static int compare_stacktrace(const void *p1, const void *p2)
{
	const struct block_list *l1 = p1, *l2 = p2;

	return strcmp(l1->stacktrace, l2->stacktrace);
}

static int compare_num(const void *p1, const void *p2)
{
	const struct block_list *l1 = p1, *l2 = p2;

	return l1->num - l2->num;
}

static int compare_page_num(const void *p1, const void *p2)
{
	const struct block_list *l1 = p1, *l2 = p2;

	return l1->page_num - l2->page_num;
}

static int compare_pid(const void *p1, const void *p2)
{
	const struct block_list *l1 = p1, *l2 = p2;

	return l1->pid - l2->pid;
}

static int compare_tgid(const void *p1, const void *p2)
{
	const struct block_list *l1 = p1, *l2 = p2;

	return l1->tgid - l2->tgid;
}

static int compare_allocator(const void *p1, const void *p2)
{
	const struct block_list *l1 = p1, *l2 = p2;

	return l1->allocator - l2->allocator;
}

static int compare_comm(const void *p1, const void *p2)
{
	const struct block_list *l1 = p1, *l2 = p2;

	return strcmp(l1->comm, l2->comm);
}

static int compare_ts(const void *p1, const void *p2)
{
	const struct block_list *l1 = p1, *l2 = p2;

	return l1->ts_nsec < l2->ts_nsec ? -1 : 1;
}

static int compare_free_ts(const void *p1, const void *p2)
{
	const struct block_list *l1 = p1, *l2 = p2;

	return l1->free_ts_nsec < l2->free_ts_nsec ? -1 : 1;
}

static int compare_release(const void *p1, const void *p2)
{
	const struct block_list *l1 = p1, *l2 = p2;

	if (!l1->free_ts_nsec && !l2->free_ts_nsec)
		return 0;
	if (l1->free_ts_nsec && l2->free_ts_nsec)
		return 0;
	return l1->free_ts_nsec ? 1 : -1;
}

static int compare_cull_condition(const void *p1, const void *p2)
{
	if (cull == 0)
		return compare_txt(p1, p2);
	if ((cull & CULL_STACKTRACE) && compare_stacktrace(p1, p2))
		return compare_stacktrace(p1, p2);
	if ((cull & CULL_PID) && compare_pid(p1, p2))
		return compare_pid(p1, p2);
	if ((cull & CULL_TGID) && compare_tgid(p1, p2))
		return compare_tgid(p1, p2);
	if ((cull & CULL_COMM) && compare_comm(p1, p2))
		return compare_comm(p1, p2);
	if ((cull & CULL_UNRELEASE) && compare_release(p1, p2))
		return compare_release(p1, p2);
	if ((cull & CULL_ALLOCATOR) && compare_allocator(p1, p2))
		return compare_allocator(p1, p2);
	return 0;
}

static int compare_sort_condition(const void *p1, const void *p2)
{
	int cmp = 0;

	for (int i = 0; i < sc.size; ++i)
		if (cmp == 0)
			cmp = sc.signs[i] * sc.cmps[i](p1, p2);
	return cmp;
}

static int search_pattern(regex_t *pattern, char *pattern_str, char *buf)
{
	int err, val_len;
	regmatch_t pmatch[2];

	err = regexec(pattern, buf, 2, pmatch, REG_NOTBOL);
	if (err != 0 || pmatch[1].rm_so == -1) {
		if (debug_on)
			fprintf(stderr, "no matching pattern in %s\n", buf);
		return -1;
	}
	val_len = pmatch[1].rm_eo - pmatch[1].rm_so;

	memcpy(pattern_str, buf + pmatch[1].rm_so, val_len);

	return 0;
}

static bool check_regcomp(regex_t *pattern, const char *regex)
{
	int err;

	err = regcomp(pattern, regex, REG_EXTENDED | REG_NEWLINE);
	if (err != 0 || pattern->re_nsub != 1) {
		fprintf(stderr, "Invalid pattern %s code %d\n", regex, err);
		return false;
	}
	return true;
}

static char **explode(char sep, const char *str, int *size)
{
	int count = 0, len = strlen(str);
	int lastindex = -1, j = 0;

	for (int i = 0; i < len; i++)
		if (str[i] == sep)
			count++;
	char **ret = calloc(++count, sizeof(char *));

	for (int i = 0; i < len; i++) {
		if (str[i] == sep) {
			ret[j] = calloc(i - lastindex, sizeof(char));
			memcpy(ret[j++], str + lastindex + 1, i - lastindex - 1);
			lastindex = i;
		}
	}
	if (lastindex <= len - 1) {
		ret[j] = calloc(len - lastindex, sizeof(char));
		memcpy(ret[j++], str + lastindex + 1, strlen(str) - 1 - lastindex);
	}
	*size = j;
	return ret;
}

static void free_explode(char **arr, int size)
{
	for (int i = 0; i < size; i++)
		free(arr[i]);
	free(arr);
}

# define FIELD_BUFF 25

static int get_page_num(char *buf)
{
	int order_val;
	char order_str[FIELD_BUFF] = {0};
	char *endptr;

	search_pattern(&order_pattern, order_str, buf);
	errno = 0;
	order_val = strtol(order_str, &endptr, 10);
	if (order_val > 64 || errno != 0 || endptr == order_str || *endptr != '\0') {
		if (debug_on)
			fprintf(stderr, "wrong order in follow buf:\n%s\n", buf);
		return 0;
	}

	return 1 << order_val;
}

static pid_t get_pid(char *buf)
{
	pid_t pid;
	char pid_str[FIELD_BUFF] = {0};
	char *endptr;

	search_pattern(&pid_pattern, pid_str, buf);
	errno = 0;
	pid = strtol(pid_str, &endptr, 10);
	if (errno != 0 || endptr == pid_str || *endptr != '\0') {
		if (debug_on)
			fprintf(stderr, "wrong/invalid pid in follow buf:\n%s\n", buf);
		return -1;
	}

	return pid;

}

static pid_t get_tgid(char *buf)
{
	pid_t tgid;
	char tgid_str[FIELD_BUFF] = {0};
	char *endptr;

	search_pattern(&tgid_pattern, tgid_str, buf);
	errno = 0;
	tgid = strtol(tgid_str, &endptr, 10);
	if (errno != 0 || endptr == tgid_str || *endptr != '\0') {
		if (debug_on)
			fprintf(stderr, "wrong/invalid tgid in follow buf:\n%s\n", buf);
		return -1;
	}

	return tgid;

}

static __u64 get_ts_nsec(char *buf)
{
	__u64 ts_nsec;
	char ts_nsec_str[FIELD_BUFF] = {0};
	char *endptr;

	search_pattern(&ts_nsec_pattern, ts_nsec_str, buf);
	errno = 0;
	ts_nsec = strtoull(ts_nsec_str, &endptr, 10);
	if (errno != 0 || endptr == ts_nsec_str || *endptr != '\0') {
		if (debug_on)
			fprintf(stderr, "wrong ts_nsec in follow buf:\n%s\n", buf);
		return -1;
	}

	return ts_nsec;
}

static __u64 get_free_ts_nsec(char *buf)
{
	__u64 free_ts_nsec;
	char free_ts_nsec_str[FIELD_BUFF] = {0};
	char *endptr;

	search_pattern(&free_ts_nsec_pattern, free_ts_nsec_str, buf);
	errno = 0;
	free_ts_nsec = strtoull(free_ts_nsec_str, &endptr, 10);
	if (errno != 0 || endptr == free_ts_nsec_str || *endptr != '\0') {
		if (debug_on)
			fprintf(stderr, "wrong free_ts_nsec in follow buf:\n%s\n", buf);
		return -1;
	}

	return free_ts_nsec;
}

static char *get_comm(char *buf)
{
	char *comm_str = malloc(TASK_COMM_LEN);

	memset(comm_str, 0, TASK_COMM_LEN);

	search_pattern(&comm_pattern, comm_str, buf);
	errno = 0;
	if (errno != 0) {
		if (debug_on)
			fprintf(stderr, "wrong comm in follow buf:\n%s\n", buf);
		return NULL;
	}

	return comm_str;
}

static int get_arg_type(const char *arg)
{
	if (!strcmp(arg, "pid") || !strcmp(arg, "p"))
		return ARG_PID;
	else if (!strcmp(arg, "tgid") || !strcmp(arg, "tg"))
		return ARG_TGID;
	else if (!strcmp(arg, "name") || !strcmp(arg, "n"))
		return  ARG_COMM;
	else if (!strcmp(arg, "stacktrace") || !strcmp(arg, "st"))
		return ARG_STACKTRACE;
	else if (!strcmp(arg, "free") || !strcmp(arg, "f"))
		return ARG_FREE;
	else if (!strcmp(arg, "txt") || !strcmp(arg, "T"))
		return ARG_TXT;
	else if (!strcmp(arg, "free_ts") || !strcmp(arg, "ft"))
		return ARG_FREE_TS;
	else if (!strcmp(arg, "alloc_ts") || !strcmp(arg, "at"))
		return ARG_ALLOC_TS;
	else if (!strcmp(arg, "allocator") || !strcmp(arg, "ator"))
		return ARG_ALLOCATOR;
	else {
		return ARG_UNKNOWN;
	}
}

static int get_allocator(const char *buf, const char *migrate_info)
{
	char *tmp, *first_line, *second_line;
	int allocator = 0;

	if (strstr(migrate_info, "CMA"))
		allocator |= ALLOCATOR_CMA;
	if (strstr(migrate_info, "slab"))
		allocator |= ALLOCATOR_SLAB;
	tmp = strstr(buf, "__vmalloc_node_range");
	if (tmp) {
		second_line = tmp;
		while (*tmp != '\n')
			tmp--;
		tmp--;
		while (*tmp != '\n')
			tmp--;
		first_line = ++tmp;
		tmp = strstr(tmp, "alloc_pages");
		if (tmp && first_line <= tmp && tmp < second_line)
			allocator |= ALLOCATOR_VMALLOC;
	}
	if (allocator == 0)
		allocator = ALLOCATOR_OTHERS;
	return allocator;
}

static bool match_num_list(int num, int *list, int list_size)
{
	for (int i = 0; i < list_size; ++i)
		if (list[i] == num)
			return true;
	return false;
}

static bool match_str_list(const char *str, char **list, int list_size)
{
	for (int i = 0; i < list_size; ++i)
		if (!strcmp(list[i], str))
			return true;
	return false;
}

static bool is_need(char *buf)
{
	__u64 ts_nsec, free_ts_nsec;

	ts_nsec = get_ts_nsec(buf);
	free_ts_nsec = get_free_ts_nsec(buf);

	if ((filter & FILTER_UNRELEASE) && free_ts_nsec != 0 && ts_nsec < free_ts_nsec)
		return false;
	if ((filter & FILTER_PID) && !match_num_list(get_pid(buf), fc.pids, fc.pids_size))
		return false;
	if ((filter & FILTER_TGID) &&
		!match_num_list(get_tgid(buf), fc.tgids, fc.tgids_size))
		return false;

	char *comm = get_comm(buf);

	if ((filter & FILTER_COMM) &&
	!match_str_list(comm, fc.comms, fc.comms_size)) {
		free(comm);
		return false;
	}
	free(comm);
	return true;
}

static bool add_list(char *buf, int len, char *ext_buf)
{
	if (list_size != 0 &&
		len == list[list_size-1].len &&
		memcmp(buf, list[list_size-1].txt, len) == 0) {
		list[list_size-1].num++;
		list[list_size-1].page_num += get_page_num(buf);
		return true;
	}
	if (list_size == max_size) {
		fprintf(stderr, "max_size too small??\n");
		return false;
	}
	if (!is_need(buf))
		return true;
	list[list_size].pid = get_pid(buf);
	list[list_size].tgid = get_tgid(buf);
	list[list_size].comm = get_comm(buf);
	list[list_size].txt = malloc(len+1);
	if (!list[list_size].txt) {
		fprintf(stderr, "Out of memory\n");
		return false;
	}
	memcpy(list[list_size].txt, buf, len);
	list[list_size].txt[len] = 0;
	list[list_size].len = len;
	list[list_size].num = 1;
	list[list_size].page_num = get_page_num(buf);

	list[list_size].stacktrace = strchr(list[list_size].txt, '\n') ?: "";
	if (*list[list_size].stacktrace == '\n')
		list[list_size].stacktrace++;
	list[list_size].ts_nsec = get_ts_nsec(buf);
	list[list_size].free_ts_nsec = get_free_ts_nsec(buf);
	list[list_size].allocator = get_allocator(buf, ext_buf);
	list_size++;
	if (list_size % 1000 == 0) {
		printf("loaded %d\r", list_size);
		fflush(stdout);
	}
	return true;
}

static bool parse_cull_args(const char *arg_str)
{
	int size = 0;
	char **args = explode(',', arg_str, &size);

	for (int i = 0; i < size; ++i) {
		int arg_type = get_arg_type(args[i]);

		if (arg_type == ARG_PID)
			cull |= CULL_PID;
		else if (arg_type == ARG_TGID)
			cull |= CULL_TGID;
		else if (arg_type == ARG_COMM)
			cull |= CULL_COMM;
		else if (arg_type == ARG_STACKTRACE)
			cull |= CULL_STACKTRACE;
		else if (arg_type == ARG_FREE)
			cull |= CULL_UNRELEASE;
		else if (arg_type == ARG_ALLOCATOR)
			cull |= CULL_ALLOCATOR;
		else {
			free_explode(args, size);
			return false;
		}
	}
	free_explode(args, size);
	if (sc.size == 0)
		set_single_cmp(compare_num, SORT_DESC);
	return true;
}

static void set_single_cmp(int (*cmp)(const void *, const void *), int sign)
{
	if (sc.signs == NULL || sc.size < 1)
		sc.signs = calloc(1, sizeof(int));
	sc.signs[0] = sign;
	if (sc.cmps == NULL || sc.size < 1)
		sc.cmps = calloc(1, sizeof(int *));
	sc.cmps[0] = cmp;
	sc.size = 1;
}

static bool parse_sort_args(const char *arg_str)
{
	int size = 0;

	if (sc.size != 0) { /* reset sort_condition */
		free(sc.signs);
		free(sc.cmps);
		size = 0;
	}

	char **args = explode(',', arg_str, &size);

	sc.signs = calloc(size, sizeof(int));
	sc.cmps = calloc(size, sizeof(int *));
	for (int i = 0; i < size; ++i) {
		int offset = 0;

		sc.signs[i] = SORT_ASC;
		if (args[i][0] == '-' || args[i][0] == '+') {
			if (args[i][0] == '-')
				sc.signs[i] = SORT_DESC;
			offset = 1;
		}

		int arg_type = get_arg_type(args[i]+offset);

		if (arg_type == ARG_PID)
			sc.cmps[i] = compare_pid;
		else if (arg_type == ARG_TGID)
			sc.cmps[i] = compare_tgid;
		else if (arg_type == ARG_COMM)
			sc.cmps[i] = compare_comm;
		else if (arg_type == ARG_STACKTRACE)
			sc.cmps[i] = compare_stacktrace;
		else if (arg_type == ARG_ALLOC_TS)
			sc.cmps[i] = compare_ts;
		else if (arg_type == ARG_FREE_TS)
			sc.cmps[i] = compare_free_ts;
		else if (arg_type == ARG_TXT)
			sc.cmps[i] = compare_txt;
		else if (arg_type == ARG_ALLOCATOR)
			sc.cmps[i] = compare_allocator;
		else {
			free_explode(args, size);
			sc.size = 0;
			return false;
		}
	}
	sc.size = size;
	free_explode(args, size);
	return true;
}

static int *parse_nums_list(char *arg_str, int *list_size)
{
	int size = 0;
	char **args = explode(',', arg_str, &size);
	int *list = calloc(size, sizeof(int));

	errno = 0;
	for (int i = 0; i < size; ++i) {
		char *endptr = NULL;

		list[i] = strtol(args[i], &endptr, 10);
		if (errno != 0 || endptr == args[i] || *endptr != '\0') {
			free(list);
			return NULL;
		}
	}
	*list_size = size;
	free_explode(args, size);
	return list;
}

static void print_allocator(FILE *out, int allocator)
{
	fprintf(out, "allocated by ");
	if (allocator & ALLOCATOR_CMA)
		fprintf(out, "CMA ");
	if (allocator & ALLOCATOR_SLAB)
		fprintf(out, "SLAB ");
	if (allocator & ALLOCATOR_VMALLOC)
		fprintf(out, "VMALLOC ");
	if (allocator & ALLOCATOR_OTHERS)
		fprintf(out, "OTHERS ");
}

#define BUF_SIZE	(128 * 1024)

static void usage(void)
{
	printf("Usage: ./page_owner_sort [OPTIONS] <input> <output>\n"
		"-m\t\tSort by total memory.\n"
		"-s\t\tSort by the stack trace.\n"
		"-t\t\tSort by times (default).\n"
		"-p\t\tSort by pid.\n"
		"-P\t\tSort by tgid.\n"
		"-n\t\tSort by task command name.\n"
		"-a\t\tSort by memory allocate time.\n"
		"-r\t\tSort by memory release time.\n"
		"-f\t\tFilter out the information of blocks whose memory has been released.\n"
		"-d\t\tPrint debug information.\n"
		"--pid <pidlist>\tSelect by pid. This selects the information of blocks whose process ID numbers appear in <pidlist>.\n"
		"--tgid <tgidlist>\tSelect by tgid. This selects the information of blocks whose Thread Group ID numbers appear in <tgidlist>.\n"
		"--name <cmdlist>\n\t\tSelect by command name. This selects the information of blocks whose command name appears in <cmdlist>.\n"
		"--cull <rules>\tCull by user-defined rules.<rules> is a single argument in the form of a comma-separated list with some common fields predefined\n"
		"--sort <order>\tSpecify sort order as: [+|-]key[,[+|-]key[,...]]\n"
	);
}

int main(int argc, char **argv)
{
	FILE *fin, *fout;
	char *buf, *ext_buf;
	int i, count;
	struct stat st;
	int opt;
	struct option longopts[] = {
		{ "pid", required_argument, NULL, 1 },
		{ "tgid", required_argument, NULL, 2 },
		{ "name", required_argument, NULL, 3 },
		{ "cull",  required_argument, NULL, 4 },
		{ "sort",  required_argument, NULL, 5 },
		{ 0, 0, 0, 0},
	};

	while ((opt = getopt_long(argc, argv, "adfmnprstP", longopts, NULL)) != -1)
		switch (opt) {
		case 'a':
			set_single_cmp(compare_ts, SORT_ASC);
			break;
		case 'd':
			debug_on = true;
			break;
		case 'f':
			filter = filter | FILTER_UNRELEASE;
			break;
		case 'm':
			set_single_cmp(compare_page_num, SORT_DESC);
			break;
		case 'p':
			set_single_cmp(compare_pid, SORT_ASC);
			break;
		case 'r':
			set_single_cmp(compare_free_ts, SORT_ASC);
			break;
		case 's':
			set_single_cmp(compare_stacktrace, SORT_ASC);
			break;
		case 't':
			set_single_cmp(compare_num, SORT_DESC);
			break;
		case 'P':
			set_single_cmp(compare_tgid, SORT_ASC);
			break;
		case 'n':
			set_single_cmp(compare_comm, SORT_ASC);
			break;
		case 1:
			filter = filter | FILTER_PID;
			fc.pids = parse_nums_list(optarg, &fc.pids_size);
			if (fc.pids == NULL) {
				fprintf(stderr, "wrong/invalid pid in from the command line:%s\n",
						optarg);
				exit(1);
			}
			break;
		case 2:
			filter = filter | FILTER_TGID;
			fc.tgids = parse_nums_list(optarg, &fc.tgids_size);
			if (fc.tgids == NULL) {
				fprintf(stderr, "wrong/invalid tgid in from the command line:%s\n",
						optarg);
				exit(1);
			}
			break;
		case 3:
			filter = filter | FILTER_COMM;
			fc.comms = explode(',', optarg, &fc.comms_size);
			break;
		case 4:
			if (!parse_cull_args(optarg)) {
				fprintf(stderr, "wrong argument after --cull option:%s\n",
						optarg);
				exit(1);
			}
			break;
		case 5:
			if (!parse_sort_args(optarg)) {
				fprintf(stderr, "wrong argument after --sort option:%s\n",
						optarg);
				exit(1);
			}
			break;
		default:
			usage();
			exit(1);
		}

	if (optind >= (argc - 1)) {
		usage();
		exit(1);
	}

	fin = fopen(argv[optind], "r");
	fout = fopen(argv[optind + 1], "w");
	if (!fin || !fout) {
		usage();
		perror("open: ");
		exit(1);
	}

	if (!check_regcomp(&order_pattern, "order\\s*([0-9]*),"))
		goto out_order;
	if (!check_regcomp(&pid_pattern, "pid\\s*([0-9]*),"))
		goto out_pid;
	if (!check_regcomp(&tgid_pattern, "tgid\\s*([0-9]*) "))
		goto out_tgid;
	if (!check_regcomp(&comm_pattern, "tgid\\s*[0-9]*\\s*\\((.*)\\),\\s*ts"))
		goto out_comm;
	if (!check_regcomp(&ts_nsec_pattern, "ts\\s*([0-9]*)\\s*ns,"))
		goto out_ts;
	if (!check_regcomp(&free_ts_nsec_pattern, "free_ts\\s*([0-9]*)\\s*ns"))
		goto out_free_ts;

	fstat(fileno(fin), &st);
	max_size = st.st_size / 100; /* hack ... */

	list = malloc(max_size * sizeof(*list));
	buf = malloc(BUF_SIZE);
	ext_buf = malloc(BUF_SIZE);
	if (!list || !buf || !ext_buf) {
		fprintf(stderr, "Out of memory\n");
		goto out_free;
	}

	for ( ; ; ) {
		int buf_len = read_block(buf, ext_buf, BUF_SIZE, fin);

		if (buf_len < 0)
			break;
		if (!add_list(buf, buf_len, ext_buf))
			goto out_free;
	}

	printf("loaded %d\n", list_size);

	printf("sorting ....\n");

	qsort(list, list_size, sizeof(list[0]), compare_cull_condition);

	printf("culling\n");

	for (i = count = 0; i < list_size; i++) {
		if (count == 0 ||
		    compare_cull_condition((void *)(&list[count-1]), (void *)(&list[i])) != 0) {
			list[count++] = list[i];
		} else {
			list[count-1].num += list[i].num;
			list[count-1].page_num += list[i].page_num;
		}
	}

	qsort(list, count, sizeof(list[0]), compare_sort_condition);

	for (i = 0; i < count; i++) {
		if (cull == 0) {
			fprintf(fout, "%d times, %d pages, ", list[i].num, list[i].page_num);
			print_allocator(fout, list[i].allocator);
			fprintf(fout, ":\n%s\n", list[i].txt);
		}
		else {
			fprintf(fout, "%d times, %d pages",
					list[i].num, list[i].page_num);
			if (cull & CULL_PID || filter & FILTER_PID)
				fprintf(fout, ", PID %d", list[i].pid);
			if (cull & CULL_TGID || filter & FILTER_TGID)
				fprintf(fout, ", TGID %d", list[i].tgid);
			if (cull & CULL_COMM || filter & FILTER_COMM)
				fprintf(fout, ", task_comm_name: %s", list[i].comm);
			if (cull & CULL_ALLOCATOR) {
				fprintf(fout, ", ");
				print_allocator(fout, list[i].allocator);
			}
			if (cull & CULL_UNRELEASE)
				fprintf(fout, " (%s)",
						list[i].free_ts_nsec ? "UNRELEASED" : "RELEASED");
			if (cull & CULL_STACKTRACE)
				fprintf(fout, ":\n%s", list[i].stacktrace);
			fprintf(fout, "\n");
		}
	}

out_free:
	if (ext_buf)
		free(ext_buf);
	if (buf)
		free(buf);
	if (list)
		free(list);
out_free_ts:
	regfree(&free_ts_nsec_pattern);
out_ts:
	regfree(&ts_nsec_pattern);
out_comm:
	regfree(&comm_pattern);
out_tgid:
	regfree(&tgid_pattern);
out_pid:
	regfree(&pid_pattern);
out_order:
	regfree(&order_pattern);

	return 0;
}

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Contact— JavaScript license information— Web API

back to top