162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <inttypes.h>
362306a36Sopenharmony_ci#include <stdio.h>
462306a36Sopenharmony_ci#include <stdlib.h>
562306a36Sopenharmony_ci#include <string.h>
662306a36Sopenharmony_ci#include <errno.h>
762306a36Sopenharmony_ci#include <linux/zalloc.h>
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include "values.h"
1062306a36Sopenharmony_ci#include "debug.h"
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ciint perf_read_values_init(struct perf_read_values *values)
1362306a36Sopenharmony_ci{
1462306a36Sopenharmony_ci	values->threads_max = 16;
1562306a36Sopenharmony_ci	values->pid = malloc(values->threads_max * sizeof(*values->pid));
1662306a36Sopenharmony_ci	values->tid = malloc(values->threads_max * sizeof(*values->tid));
1762306a36Sopenharmony_ci	values->value = zalloc(values->threads_max * sizeof(*values->value));
1862306a36Sopenharmony_ci	if (!values->pid || !values->tid || !values->value) {
1962306a36Sopenharmony_ci		pr_debug("failed to allocate read_values threads arrays");
2062306a36Sopenharmony_ci		goto out_free_pid;
2162306a36Sopenharmony_ci	}
2262306a36Sopenharmony_ci	values->threads = 0;
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	values->counters_max = 16;
2562306a36Sopenharmony_ci	values->counterrawid = malloc(values->counters_max
2662306a36Sopenharmony_ci				      * sizeof(*values->counterrawid));
2762306a36Sopenharmony_ci	values->countername = malloc(values->counters_max
2862306a36Sopenharmony_ci				     * sizeof(*values->countername));
2962306a36Sopenharmony_ci	if (!values->counterrawid || !values->countername) {
3062306a36Sopenharmony_ci		pr_debug("failed to allocate read_values counters arrays");
3162306a36Sopenharmony_ci		goto out_free_counter;
3262306a36Sopenharmony_ci	}
3362306a36Sopenharmony_ci	values->counters = 0;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	return 0;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ciout_free_counter:
3862306a36Sopenharmony_ci	zfree(&values->counterrawid);
3962306a36Sopenharmony_ci	zfree(&values->countername);
4062306a36Sopenharmony_ciout_free_pid:
4162306a36Sopenharmony_ci	zfree(&values->pid);
4262306a36Sopenharmony_ci	zfree(&values->tid);
4362306a36Sopenharmony_ci	zfree(&values->value);
4462306a36Sopenharmony_ci	return -ENOMEM;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_civoid perf_read_values_destroy(struct perf_read_values *values)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	int i;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	if (!values->threads_max || !values->counters_max)
5262306a36Sopenharmony_ci		return;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	for (i = 0; i < values->threads; i++)
5562306a36Sopenharmony_ci		zfree(&values->value[i]);
5662306a36Sopenharmony_ci	zfree(&values->value);
5762306a36Sopenharmony_ci	zfree(&values->pid);
5862306a36Sopenharmony_ci	zfree(&values->tid);
5962306a36Sopenharmony_ci	zfree(&values->counterrawid);
6062306a36Sopenharmony_ci	for (i = 0; i < values->counters; i++)
6162306a36Sopenharmony_ci		zfree(&values->countername[i]);
6262306a36Sopenharmony_ci	zfree(&values->countername);
6362306a36Sopenharmony_ci}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int perf_read_values__enlarge_threads(struct perf_read_values *values)
6662306a36Sopenharmony_ci{
6762306a36Sopenharmony_ci	int nthreads_max = values->threads_max * 2;
6862306a36Sopenharmony_ci	void *npid = realloc(values->pid, nthreads_max * sizeof(*values->pid)),
6962306a36Sopenharmony_ci	     *ntid = realloc(values->tid, nthreads_max * sizeof(*values->tid)),
7062306a36Sopenharmony_ci	     *nvalue = realloc(values->value, nthreads_max * sizeof(*values->value));
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	if (!npid || !ntid || !nvalue)
7362306a36Sopenharmony_ci		goto out_err;
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	values->threads_max = nthreads_max;
7662306a36Sopenharmony_ci	values->pid = npid;
7762306a36Sopenharmony_ci	values->tid = ntid;
7862306a36Sopenharmony_ci	values->value = nvalue;
7962306a36Sopenharmony_ci	return 0;
8062306a36Sopenharmony_ciout_err:
8162306a36Sopenharmony_ci	free(npid);
8262306a36Sopenharmony_ci	free(ntid);
8362306a36Sopenharmony_ci	free(nvalue);
8462306a36Sopenharmony_ci	pr_debug("failed to enlarge read_values threads arrays");
8562306a36Sopenharmony_ci	return -ENOMEM;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int perf_read_values__findnew_thread(struct perf_read_values *values,
8962306a36Sopenharmony_ci					    u32 pid, u32 tid)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	int i;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	for (i = 0; i < values->threads; i++)
9462306a36Sopenharmony_ci		if (values->pid[i] == pid && values->tid[i] == tid)
9562306a36Sopenharmony_ci			return i;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (values->threads == values->threads_max) {
9862306a36Sopenharmony_ci		i = perf_read_values__enlarge_threads(values);
9962306a36Sopenharmony_ci		if (i < 0)
10062306a36Sopenharmony_ci			return i;
10162306a36Sopenharmony_ci	}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	i = values->threads;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	values->value[i] = zalloc(values->counters_max * sizeof(**values->value));
10662306a36Sopenharmony_ci	if (!values->value[i]) {
10762306a36Sopenharmony_ci		pr_debug("failed to allocate read_values counters array");
10862306a36Sopenharmony_ci		return -ENOMEM;
10962306a36Sopenharmony_ci	}
11062306a36Sopenharmony_ci	values->pid[i] = pid;
11162306a36Sopenharmony_ci	values->tid[i] = tid;
11262306a36Sopenharmony_ci	values->threads = i + 1;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	return i;
11562306a36Sopenharmony_ci}
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_cistatic int perf_read_values__enlarge_counters(struct perf_read_values *values)
11862306a36Sopenharmony_ci{
11962306a36Sopenharmony_ci	char **countername;
12062306a36Sopenharmony_ci	int i, counters_max = values->counters_max * 2;
12162306a36Sopenharmony_ci	u64 *counterrawid = realloc(values->counterrawid, counters_max * sizeof(*values->counterrawid));
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	if (!counterrawid) {
12462306a36Sopenharmony_ci		pr_debug("failed to enlarge read_values rawid array");
12562306a36Sopenharmony_ci		goto out_enomem;
12662306a36Sopenharmony_ci	}
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci	countername = realloc(values->countername, counters_max * sizeof(*values->countername));
12962306a36Sopenharmony_ci	if (!countername) {
13062306a36Sopenharmony_ci		pr_debug("failed to enlarge read_values rawid array");
13162306a36Sopenharmony_ci		goto out_free_rawid;
13262306a36Sopenharmony_ci	}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	for (i = 0; i < values->threads; i++) {
13562306a36Sopenharmony_ci		u64 *value = realloc(values->value[i], counters_max * sizeof(**values->value));
13662306a36Sopenharmony_ci		int j;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		if (!value) {
13962306a36Sopenharmony_ci			pr_debug("failed to enlarge read_values ->values array");
14062306a36Sopenharmony_ci			goto out_free_name;
14162306a36Sopenharmony_ci		}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		for (j = values->counters_max; j < counters_max; j++)
14462306a36Sopenharmony_ci			value[j] = 0;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci		values->value[i] = value;
14762306a36Sopenharmony_ci	}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	values->counters_max = counters_max;
15062306a36Sopenharmony_ci	values->counterrawid = counterrawid;
15162306a36Sopenharmony_ci	values->countername  = countername;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	return 0;
15462306a36Sopenharmony_ciout_free_name:
15562306a36Sopenharmony_ci	free(countername);
15662306a36Sopenharmony_ciout_free_rawid:
15762306a36Sopenharmony_ci	free(counterrawid);
15862306a36Sopenharmony_ciout_enomem:
15962306a36Sopenharmony_ci	return -ENOMEM;
16062306a36Sopenharmony_ci}
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_cistatic int perf_read_values__findnew_counter(struct perf_read_values *values,
16362306a36Sopenharmony_ci					     u64 rawid, const char *name)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	int i;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	for (i = 0; i < values->counters; i++)
16862306a36Sopenharmony_ci		if (values->counterrawid[i] == rawid)
16962306a36Sopenharmony_ci			return i;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	if (values->counters == values->counters_max) {
17262306a36Sopenharmony_ci		i = perf_read_values__enlarge_counters(values);
17362306a36Sopenharmony_ci		if (i)
17462306a36Sopenharmony_ci			return i;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	i = values->counters++;
17862306a36Sopenharmony_ci	values->counterrawid[i] = rawid;
17962306a36Sopenharmony_ci	values->countername[i] = strdup(name);
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	return i;
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ciint perf_read_values_add_value(struct perf_read_values *values,
18562306a36Sopenharmony_ci				u32 pid, u32 tid,
18662306a36Sopenharmony_ci				u64 rawid, const char *name, u64 value)
18762306a36Sopenharmony_ci{
18862306a36Sopenharmony_ci	int tindex, cindex;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	tindex = perf_read_values__findnew_thread(values, pid, tid);
19162306a36Sopenharmony_ci	if (tindex < 0)
19262306a36Sopenharmony_ci		return tindex;
19362306a36Sopenharmony_ci	cindex = perf_read_values__findnew_counter(values, rawid, name);
19462306a36Sopenharmony_ci	if (cindex < 0)
19562306a36Sopenharmony_ci		return cindex;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	values->value[tindex][cindex] += value;
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic void perf_read_values__display_pretty(FILE *fp,
20262306a36Sopenharmony_ci					     struct perf_read_values *values)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	int i, j;
20562306a36Sopenharmony_ci	int pidwidth, tidwidth;
20662306a36Sopenharmony_ci	int *counterwidth;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	counterwidth = malloc(values->counters * sizeof(*counterwidth));
20962306a36Sopenharmony_ci	if (!counterwidth) {
21062306a36Sopenharmony_ci		fprintf(fp, "INTERNAL ERROR: Failed to allocate counterwidth array\n");
21162306a36Sopenharmony_ci		return;
21262306a36Sopenharmony_ci	}
21362306a36Sopenharmony_ci	tidwidth = 3;
21462306a36Sopenharmony_ci	pidwidth = 3;
21562306a36Sopenharmony_ci	for (j = 0; j < values->counters; j++)
21662306a36Sopenharmony_ci		counterwidth[j] = strlen(values->countername[j]);
21762306a36Sopenharmony_ci	for (i = 0; i < values->threads; i++) {
21862306a36Sopenharmony_ci		int width;
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci		width = snprintf(NULL, 0, "%d", values->pid[i]);
22162306a36Sopenharmony_ci		if (width > pidwidth)
22262306a36Sopenharmony_ci			pidwidth = width;
22362306a36Sopenharmony_ci		width = snprintf(NULL, 0, "%d", values->tid[i]);
22462306a36Sopenharmony_ci		if (width > tidwidth)
22562306a36Sopenharmony_ci			tidwidth = width;
22662306a36Sopenharmony_ci		for (j = 0; j < values->counters; j++) {
22762306a36Sopenharmony_ci			width = snprintf(NULL, 0, "%" PRIu64, values->value[i][j]);
22862306a36Sopenharmony_ci			if (width > counterwidth[j])
22962306a36Sopenharmony_ci				counterwidth[j] = width;
23062306a36Sopenharmony_ci		}
23162306a36Sopenharmony_ci	}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	fprintf(fp, "# %*s  %*s", pidwidth, "PID", tidwidth, "TID");
23462306a36Sopenharmony_ci	for (j = 0; j < values->counters; j++)
23562306a36Sopenharmony_ci		fprintf(fp, "  %*s", counterwidth[j], values->countername[j]);
23662306a36Sopenharmony_ci	fprintf(fp, "\n");
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	for (i = 0; i < values->threads; i++) {
23962306a36Sopenharmony_ci		fprintf(fp, "  %*d  %*d", pidwidth, values->pid[i],
24062306a36Sopenharmony_ci			tidwidth, values->tid[i]);
24162306a36Sopenharmony_ci		for (j = 0; j < values->counters; j++)
24262306a36Sopenharmony_ci			fprintf(fp, "  %*" PRIu64,
24362306a36Sopenharmony_ci				counterwidth[j], values->value[i][j]);
24462306a36Sopenharmony_ci		fprintf(fp, "\n");
24562306a36Sopenharmony_ci	}
24662306a36Sopenharmony_ci	free(counterwidth);
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic void perf_read_values__display_raw(FILE *fp,
25062306a36Sopenharmony_ci					  struct perf_read_values *values)
25162306a36Sopenharmony_ci{
25262306a36Sopenharmony_ci	int width, pidwidth, tidwidth, namewidth, rawwidth, countwidth;
25362306a36Sopenharmony_ci	int i, j;
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	tidwidth = 3; /* TID */
25662306a36Sopenharmony_ci	pidwidth = 3; /* PID */
25762306a36Sopenharmony_ci	namewidth = 4; /* "Name" */
25862306a36Sopenharmony_ci	rawwidth = 3; /* "Raw" */
25962306a36Sopenharmony_ci	countwidth = 5; /* "Count" */
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	for (i = 0; i < values->threads; i++) {
26262306a36Sopenharmony_ci		width = snprintf(NULL, 0, "%d", values->pid[i]);
26362306a36Sopenharmony_ci		if (width > pidwidth)
26462306a36Sopenharmony_ci			pidwidth = width;
26562306a36Sopenharmony_ci		width = snprintf(NULL, 0, "%d", values->tid[i]);
26662306a36Sopenharmony_ci		if (width > tidwidth)
26762306a36Sopenharmony_ci			tidwidth = width;
26862306a36Sopenharmony_ci	}
26962306a36Sopenharmony_ci	for (j = 0; j < values->counters; j++) {
27062306a36Sopenharmony_ci		width = strlen(values->countername[j]);
27162306a36Sopenharmony_ci		if (width > namewidth)
27262306a36Sopenharmony_ci			namewidth = width;
27362306a36Sopenharmony_ci		width = snprintf(NULL, 0, "%" PRIx64, values->counterrawid[j]);
27462306a36Sopenharmony_ci		if (width > rawwidth)
27562306a36Sopenharmony_ci			rawwidth = width;
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci	for (i = 0; i < values->threads; i++) {
27862306a36Sopenharmony_ci		for (j = 0; j < values->counters; j++) {
27962306a36Sopenharmony_ci			width = snprintf(NULL, 0, "%" PRIu64, values->value[i][j]);
28062306a36Sopenharmony_ci			if (width > countwidth)
28162306a36Sopenharmony_ci				countwidth = width;
28262306a36Sopenharmony_ci		}
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	fprintf(fp, "# %*s  %*s  %*s  %*s  %*s\n",
28662306a36Sopenharmony_ci		pidwidth, "PID", tidwidth, "TID",
28762306a36Sopenharmony_ci		namewidth, "Name", rawwidth, "Raw",
28862306a36Sopenharmony_ci		countwidth, "Count");
28962306a36Sopenharmony_ci	for (i = 0; i < values->threads; i++)
29062306a36Sopenharmony_ci		for (j = 0; j < values->counters; j++)
29162306a36Sopenharmony_ci			fprintf(fp, "  %*d  %*d  %*s  %*" PRIx64 "  %*" PRIu64,
29262306a36Sopenharmony_ci				pidwidth, values->pid[i],
29362306a36Sopenharmony_ci				tidwidth, values->tid[i],
29462306a36Sopenharmony_ci				namewidth, values->countername[j],
29562306a36Sopenharmony_ci				rawwidth, values->counterrawid[j],
29662306a36Sopenharmony_ci				countwidth, values->value[i][j]);
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_civoid perf_read_values_display(FILE *fp, struct perf_read_values *values, int raw)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	if (raw)
30262306a36Sopenharmony_ci		perf_read_values__display_raw(fp, values);
30362306a36Sopenharmony_ci	else
30462306a36Sopenharmony_ci		perf_read_values__display_pretty(fp, values);
30562306a36Sopenharmony_ci}
306