18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <inttypes.h>
38c2ecf20Sopenharmony_ci#include <stdio.h>
48c2ecf20Sopenharmony_ci#include <stdlib.h>
58c2ecf20Sopenharmony_ci#include <string.h>
68c2ecf20Sopenharmony_ci#include <errno.h>
78c2ecf20Sopenharmony_ci#include <linux/zalloc.h>
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include "values.h"
108c2ecf20Sopenharmony_ci#include "debug.h"
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ciint perf_read_values_init(struct perf_read_values *values)
138c2ecf20Sopenharmony_ci{
148c2ecf20Sopenharmony_ci	values->threads_max = 16;
158c2ecf20Sopenharmony_ci	values->pid = malloc(values->threads_max * sizeof(*values->pid));
168c2ecf20Sopenharmony_ci	values->tid = malloc(values->threads_max * sizeof(*values->tid));
178c2ecf20Sopenharmony_ci	values->value = zalloc(values->threads_max * sizeof(*values->value));
188c2ecf20Sopenharmony_ci	if (!values->pid || !values->tid || !values->value) {
198c2ecf20Sopenharmony_ci		pr_debug("failed to allocate read_values threads arrays");
208c2ecf20Sopenharmony_ci		goto out_free_pid;
218c2ecf20Sopenharmony_ci	}
228c2ecf20Sopenharmony_ci	values->threads = 0;
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci	values->counters_max = 16;
258c2ecf20Sopenharmony_ci	values->counterrawid = malloc(values->counters_max
268c2ecf20Sopenharmony_ci				      * sizeof(*values->counterrawid));
278c2ecf20Sopenharmony_ci	values->countername = malloc(values->counters_max
288c2ecf20Sopenharmony_ci				     * sizeof(*values->countername));
298c2ecf20Sopenharmony_ci	if (!values->counterrawid || !values->countername) {
308c2ecf20Sopenharmony_ci		pr_debug("failed to allocate read_values counters arrays");
318c2ecf20Sopenharmony_ci		goto out_free_counter;
328c2ecf20Sopenharmony_ci	}
338c2ecf20Sopenharmony_ci	values->counters = 0;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return 0;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ciout_free_counter:
388c2ecf20Sopenharmony_ci	zfree(&values->counterrawid);
398c2ecf20Sopenharmony_ci	zfree(&values->countername);
408c2ecf20Sopenharmony_ciout_free_pid:
418c2ecf20Sopenharmony_ci	zfree(&values->pid);
428c2ecf20Sopenharmony_ci	zfree(&values->tid);
438c2ecf20Sopenharmony_ci	zfree(&values->value);
448c2ecf20Sopenharmony_ci	return -ENOMEM;
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_civoid perf_read_values_destroy(struct perf_read_values *values)
488c2ecf20Sopenharmony_ci{
498c2ecf20Sopenharmony_ci	int i;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	if (!values->threads_max || !values->counters_max)
528c2ecf20Sopenharmony_ci		return;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	for (i = 0; i < values->threads; i++)
558c2ecf20Sopenharmony_ci		zfree(&values->value[i]);
568c2ecf20Sopenharmony_ci	zfree(&values->value);
578c2ecf20Sopenharmony_ci	zfree(&values->pid);
588c2ecf20Sopenharmony_ci	zfree(&values->tid);
598c2ecf20Sopenharmony_ci	zfree(&values->counterrawid);
608c2ecf20Sopenharmony_ci	for (i = 0; i < values->counters; i++)
618c2ecf20Sopenharmony_ci		zfree(&values->countername[i]);
628c2ecf20Sopenharmony_ci	zfree(&values->countername);
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int perf_read_values__enlarge_threads(struct perf_read_values *values)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	int nthreads_max = values->threads_max * 2;
688c2ecf20Sopenharmony_ci	void *npid = realloc(values->pid, nthreads_max * sizeof(*values->pid)),
698c2ecf20Sopenharmony_ci	     *ntid = realloc(values->tid, nthreads_max * sizeof(*values->tid)),
708c2ecf20Sopenharmony_ci	     *nvalue = realloc(values->value, nthreads_max * sizeof(*values->value));
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci	if (!npid || !ntid || !nvalue)
738c2ecf20Sopenharmony_ci		goto out_err;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	values->threads_max = nthreads_max;
768c2ecf20Sopenharmony_ci	values->pid = npid;
778c2ecf20Sopenharmony_ci	values->tid = ntid;
788c2ecf20Sopenharmony_ci	values->value = nvalue;
798c2ecf20Sopenharmony_ci	return 0;
808c2ecf20Sopenharmony_ciout_err:
818c2ecf20Sopenharmony_ci	free(npid);
828c2ecf20Sopenharmony_ci	free(ntid);
838c2ecf20Sopenharmony_ci	free(nvalue);
848c2ecf20Sopenharmony_ci	pr_debug("failed to enlarge read_values threads arrays");
858c2ecf20Sopenharmony_ci	return -ENOMEM;
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic int perf_read_values__findnew_thread(struct perf_read_values *values,
898c2ecf20Sopenharmony_ci					    u32 pid, u32 tid)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	int i;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	for (i = 0; i < values->threads; i++)
948c2ecf20Sopenharmony_ci		if (values->pid[i] == pid && values->tid[i] == tid)
958c2ecf20Sopenharmony_ci			return i;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (values->threads == values->threads_max) {
988c2ecf20Sopenharmony_ci		i = perf_read_values__enlarge_threads(values);
998c2ecf20Sopenharmony_ci		if (i < 0)
1008c2ecf20Sopenharmony_ci			return i;
1018c2ecf20Sopenharmony_ci	}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	i = values->threads;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	values->value[i] = zalloc(values->counters_max * sizeof(**values->value));
1068c2ecf20Sopenharmony_ci	if (!values->value[i]) {
1078c2ecf20Sopenharmony_ci		pr_debug("failed to allocate read_values counters array");
1088c2ecf20Sopenharmony_ci		return -ENOMEM;
1098c2ecf20Sopenharmony_ci	}
1108c2ecf20Sopenharmony_ci	values->pid[i] = pid;
1118c2ecf20Sopenharmony_ci	values->tid[i] = tid;
1128c2ecf20Sopenharmony_ci	values->threads = i + 1;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return i;
1158c2ecf20Sopenharmony_ci}
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic int perf_read_values__enlarge_counters(struct perf_read_values *values)
1188c2ecf20Sopenharmony_ci{
1198c2ecf20Sopenharmony_ci	char **countername;
1208c2ecf20Sopenharmony_ci	int i, counters_max = values->counters_max * 2;
1218c2ecf20Sopenharmony_ci	u64 *counterrawid = realloc(values->counterrawid, counters_max * sizeof(*values->counterrawid));
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	if (!counterrawid) {
1248c2ecf20Sopenharmony_ci		pr_debug("failed to enlarge read_values rawid array");
1258c2ecf20Sopenharmony_ci		goto out_enomem;
1268c2ecf20Sopenharmony_ci	}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	countername = realloc(values->countername, counters_max * sizeof(*values->countername));
1298c2ecf20Sopenharmony_ci	if (!countername) {
1308c2ecf20Sopenharmony_ci		pr_debug("failed to enlarge read_values rawid array");
1318c2ecf20Sopenharmony_ci		goto out_free_rawid;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	for (i = 0; i < values->threads; i++) {
1358c2ecf20Sopenharmony_ci		u64 *value = realloc(values->value[i], counters_max * sizeof(**values->value));
1368c2ecf20Sopenharmony_ci		int j;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		if (!value) {
1398c2ecf20Sopenharmony_ci			pr_debug("failed to enlarge read_values ->values array");
1408c2ecf20Sopenharmony_ci			goto out_free_name;
1418c2ecf20Sopenharmony_ci		}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		for (j = values->counters_max; j < counters_max; j++)
1448c2ecf20Sopenharmony_ci			value[j] = 0;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci		values->value[i] = value;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	values->counters_max = counters_max;
1508c2ecf20Sopenharmony_ci	values->counterrawid = counterrawid;
1518c2ecf20Sopenharmony_ci	values->countername  = countername;
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return 0;
1548c2ecf20Sopenharmony_ciout_free_name:
1558c2ecf20Sopenharmony_ci	free(countername);
1568c2ecf20Sopenharmony_ciout_free_rawid:
1578c2ecf20Sopenharmony_ci	free(counterrawid);
1588c2ecf20Sopenharmony_ciout_enomem:
1598c2ecf20Sopenharmony_ci	return -ENOMEM;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int perf_read_values__findnew_counter(struct perf_read_values *values,
1638c2ecf20Sopenharmony_ci					     u64 rawid, const char *name)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	int i;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	for (i = 0; i < values->counters; i++)
1688c2ecf20Sopenharmony_ci		if (values->counterrawid[i] == rawid)
1698c2ecf20Sopenharmony_ci			return i;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	if (values->counters == values->counters_max) {
1728c2ecf20Sopenharmony_ci		i = perf_read_values__enlarge_counters(values);
1738c2ecf20Sopenharmony_ci		if (i)
1748c2ecf20Sopenharmony_ci			return i;
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	i = values->counters++;
1788c2ecf20Sopenharmony_ci	values->counterrawid[i] = rawid;
1798c2ecf20Sopenharmony_ci	values->countername[i] = strdup(name);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	return i;
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ciint perf_read_values_add_value(struct perf_read_values *values,
1858c2ecf20Sopenharmony_ci				u32 pid, u32 tid,
1868c2ecf20Sopenharmony_ci				u64 rawid, const char *name, u64 value)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	int tindex, cindex;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	tindex = perf_read_values__findnew_thread(values, pid, tid);
1918c2ecf20Sopenharmony_ci	if (tindex < 0)
1928c2ecf20Sopenharmony_ci		return tindex;
1938c2ecf20Sopenharmony_ci	cindex = perf_read_values__findnew_counter(values, rawid, name);
1948c2ecf20Sopenharmony_ci	if (cindex < 0)
1958c2ecf20Sopenharmony_ci		return cindex;
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci	values->value[tindex][cindex] += value;
1988c2ecf20Sopenharmony_ci	return 0;
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic void perf_read_values__display_pretty(FILE *fp,
2028c2ecf20Sopenharmony_ci					     struct perf_read_values *values)
2038c2ecf20Sopenharmony_ci{
2048c2ecf20Sopenharmony_ci	int i, j;
2058c2ecf20Sopenharmony_ci	int pidwidth, tidwidth;
2068c2ecf20Sopenharmony_ci	int *counterwidth;
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	counterwidth = malloc(values->counters * sizeof(*counterwidth));
2098c2ecf20Sopenharmony_ci	if (!counterwidth) {
2108c2ecf20Sopenharmony_ci		fprintf(fp, "INTERNAL ERROR: Failed to allocate counterwidth array\n");
2118c2ecf20Sopenharmony_ci		return;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci	tidwidth = 3;
2148c2ecf20Sopenharmony_ci	pidwidth = 3;
2158c2ecf20Sopenharmony_ci	for (j = 0; j < values->counters; j++)
2168c2ecf20Sopenharmony_ci		counterwidth[j] = strlen(values->countername[j]);
2178c2ecf20Sopenharmony_ci	for (i = 0; i < values->threads; i++) {
2188c2ecf20Sopenharmony_ci		int width;
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_ci		width = snprintf(NULL, 0, "%d", values->pid[i]);
2218c2ecf20Sopenharmony_ci		if (width > pidwidth)
2228c2ecf20Sopenharmony_ci			pidwidth = width;
2238c2ecf20Sopenharmony_ci		width = snprintf(NULL, 0, "%d", values->tid[i]);
2248c2ecf20Sopenharmony_ci		if (width > tidwidth)
2258c2ecf20Sopenharmony_ci			tidwidth = width;
2268c2ecf20Sopenharmony_ci		for (j = 0; j < values->counters; j++) {
2278c2ecf20Sopenharmony_ci			width = snprintf(NULL, 0, "%" PRIu64, values->value[i][j]);
2288c2ecf20Sopenharmony_ci			if (width > counterwidth[j])
2298c2ecf20Sopenharmony_ci				counterwidth[j] = width;
2308c2ecf20Sopenharmony_ci		}
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	fprintf(fp, "# %*s  %*s", pidwidth, "PID", tidwidth, "TID");
2348c2ecf20Sopenharmony_ci	for (j = 0; j < values->counters; j++)
2358c2ecf20Sopenharmony_ci		fprintf(fp, "  %*s", counterwidth[j], values->countername[j]);
2368c2ecf20Sopenharmony_ci	fprintf(fp, "\n");
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci	for (i = 0; i < values->threads; i++) {
2398c2ecf20Sopenharmony_ci		fprintf(fp, "  %*d  %*d", pidwidth, values->pid[i],
2408c2ecf20Sopenharmony_ci			tidwidth, values->tid[i]);
2418c2ecf20Sopenharmony_ci		for (j = 0; j < values->counters; j++)
2428c2ecf20Sopenharmony_ci			fprintf(fp, "  %*" PRIu64,
2438c2ecf20Sopenharmony_ci				counterwidth[j], values->value[i][j]);
2448c2ecf20Sopenharmony_ci		fprintf(fp, "\n");
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci	free(counterwidth);
2478c2ecf20Sopenharmony_ci}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_cistatic void perf_read_values__display_raw(FILE *fp,
2508c2ecf20Sopenharmony_ci					  struct perf_read_values *values)
2518c2ecf20Sopenharmony_ci{
2528c2ecf20Sopenharmony_ci	int width, pidwidth, tidwidth, namewidth, rawwidth, countwidth;
2538c2ecf20Sopenharmony_ci	int i, j;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	tidwidth = 3; /* TID */
2568c2ecf20Sopenharmony_ci	pidwidth = 3; /* PID */
2578c2ecf20Sopenharmony_ci	namewidth = 4; /* "Name" */
2588c2ecf20Sopenharmony_ci	rawwidth = 3; /* "Raw" */
2598c2ecf20Sopenharmony_ci	countwidth = 5; /* "Count" */
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	for (i = 0; i < values->threads; i++) {
2628c2ecf20Sopenharmony_ci		width = snprintf(NULL, 0, "%d", values->pid[i]);
2638c2ecf20Sopenharmony_ci		if (width > pidwidth)
2648c2ecf20Sopenharmony_ci			pidwidth = width;
2658c2ecf20Sopenharmony_ci		width = snprintf(NULL, 0, "%d", values->tid[i]);
2668c2ecf20Sopenharmony_ci		if (width > tidwidth)
2678c2ecf20Sopenharmony_ci			tidwidth = width;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci	for (j = 0; j < values->counters; j++) {
2708c2ecf20Sopenharmony_ci		width = strlen(values->countername[j]);
2718c2ecf20Sopenharmony_ci		if (width > namewidth)
2728c2ecf20Sopenharmony_ci			namewidth = width;
2738c2ecf20Sopenharmony_ci		width = snprintf(NULL, 0, "%" PRIx64, values->counterrawid[j]);
2748c2ecf20Sopenharmony_ci		if (width > rawwidth)
2758c2ecf20Sopenharmony_ci			rawwidth = width;
2768c2ecf20Sopenharmony_ci	}
2778c2ecf20Sopenharmony_ci	for (i = 0; i < values->threads; i++) {
2788c2ecf20Sopenharmony_ci		for (j = 0; j < values->counters; j++) {
2798c2ecf20Sopenharmony_ci			width = snprintf(NULL, 0, "%" PRIu64, values->value[i][j]);
2808c2ecf20Sopenharmony_ci			if (width > countwidth)
2818c2ecf20Sopenharmony_ci				countwidth = width;
2828c2ecf20Sopenharmony_ci		}
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	fprintf(fp, "# %*s  %*s  %*s  %*s  %*s\n",
2868c2ecf20Sopenharmony_ci		pidwidth, "PID", tidwidth, "TID",
2878c2ecf20Sopenharmony_ci		namewidth, "Name", rawwidth, "Raw",
2888c2ecf20Sopenharmony_ci		countwidth, "Count");
2898c2ecf20Sopenharmony_ci	for (i = 0; i < values->threads; i++)
2908c2ecf20Sopenharmony_ci		for (j = 0; j < values->counters; j++)
2918c2ecf20Sopenharmony_ci			fprintf(fp, "  %*d  %*d  %*s  %*" PRIx64 "  %*" PRIu64,
2928c2ecf20Sopenharmony_ci				pidwidth, values->pid[i],
2938c2ecf20Sopenharmony_ci				tidwidth, values->tid[i],
2948c2ecf20Sopenharmony_ci				namewidth, values->countername[j],
2958c2ecf20Sopenharmony_ci				rawwidth, values->counterrawid[j],
2968c2ecf20Sopenharmony_ci				countwidth, values->value[i][j]);
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_civoid perf_read_values_display(FILE *fp, struct perf_read_values *values, int raw)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	if (raw)
3028c2ecf20Sopenharmony_ci		perf_read_values__display_raw(fp, values);
3038c2ecf20Sopenharmony_ci	else
3048c2ecf20Sopenharmony_ci		perf_read_values__display_pretty(fp, values);
3058c2ecf20Sopenharmony_ci}
306