Skip to content

Commit

Permalink
perf test pmu: Refactor format test and exposed test APIs
Browse files Browse the repository at this point in the history
In tests/pmu.c, make a common utility that creates a PMU in a mkdtemp
directory and uses regular PMU parsing logic to load that PMU. Formats
must still be eagerly loaded as by default the PMU code assumes devices
are going to be in sysfs.

In util/pmu.[ch], hide perf_pmu__format_parse but add the eager argument
to perf_pmu__lookup called by perf_pmus__add_test_pmu. Later patches
will eagerly load other non-sysfs files when eager loading is enabled.

In tests/pmu.c, rather than manually constructing a list of term
arguments, just use the term parsing code from a string.

Add more comments and debug logging.

Reviewed-by: Kan Liang <kan.liang@linux.intel.com>
Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@arm.com>
Cc: Jing Zhang <renyu.zj@linux.alibaba.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@amd.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Link: https://lore.kernel.org/r/20240502213507.2339733-4-irogers@google.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
  • Loading branch information
Ian Rogers authored and Arnaldo Carvalho de Melo committed May 3, 2024
1 parent 785623e commit aa1551f
Show file tree
Hide file tree
Showing 7 changed files with 177 additions and 179 deletions.
319 changes: 151 additions & 168 deletions tools/perf/tests/pmu.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,203 +2,186 @@
#include "parse-events.h"
#include "pmu.h"
#include "tests.h"
#include "debug.h"
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <linux/kernel.h>
#include <linux/limits.h>
#include <linux/zalloc.h>

/* Simulated format definitions. */
static struct test_format {
const char *name;
const char *value;
} test_formats[] = {
{ "krava01", "config:0-1,62-63\n", },
{ "krava02", "config:10-17\n", },
{ "krava03", "config:5\n", },
{ "krava11", "config1:0,2,4,6,8,20-28\n", },
{ "krava12", "config1:63\n", },
{ "krava13", "config1:45-47\n", },
{ "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },
{ "krava22", "config2:8,18,48,58\n", },
{ "krava23", "config2:28-29,38\n", },
};

/* Simulated users input. */
static struct parse_events_term test_terms[] = {
{
.config = "krava01",
.val.num = 15,
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
},
{
.config = "krava02",
.val.num = 170,
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
},
{
.config = "krava03",
.val.num = 1,
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
},
{
.config = "krava11",
.val.num = 27,
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
},
{
.config = "krava12",
.val.num = 1,
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
},
{
.config = "krava13",
.val.num = 2,
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
},
{
.config = "krava21",
.val.num = 119,
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
},
{
.config = "krava22",
.val.num = 11,
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
},
{
.config = "krava23",
.val.num = 2,
.type_val = PARSE_EVENTS__TERM_TYPE_NUM,
.type_term = PARSE_EVENTS__TERM_TYPE_USER,
},
};

/*
* Prepare format directory data, exported by kernel
* at /sys/bus/event_source/devices/<dev>/format.
*/
static char *test_format_dir_get(char *dir, size_t sz)
{
unsigned int i;

snprintf(dir, sz, "/tmp/perf-pmu-test-format-XXXXXX");
if (!mkdtemp(dir))
return NULL;

for (i = 0; i < ARRAY_SIZE(test_formats); i++) {
char name[PATH_MAX];
struct test_format *format = &test_formats[i];
FILE *file;

scnprintf(name, PATH_MAX, "%s/%s", dir, format->name);

file = fopen(name, "w");
if (!file)
return NULL;

if (1 != fwrite(format->value, strlen(format->value), 1, file))
break;
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>

fclose(file);
}

return dir;
}
/* Fake PMUs created in temp directory. */
static LIST_HEAD(test_pmus);

/* Cleanup format directory. */
static int test_format_dir_put(char *dir)
/* Cleanup test PMU directory. */
static int test_pmu_put(const char *dir, struct perf_pmu *pmu)
{
char buf[PATH_MAX + 20];
int ret;

snprintf(buf, sizeof(buf), "rm -f %s/*\n", dir);
if (system(buf))
return -1;
if (scnprintf(buf, sizeof(buf), "rm -fr %s", dir) < 0) {
pr_err("Failure to set up buffer for \"%s\"\n", dir);
return -EINVAL;
}
ret = system(buf);
if (ret)
pr_err("Failure to \"%s\"\n", buf);

snprintf(buf, sizeof(buf), "rmdir %s\n", dir);
return system(buf);
list_del(&pmu->list);
perf_pmu__delete(pmu);
return ret;
}

static void add_test_terms(struct parse_events_terms *terms)
/*
* Prepare test PMU directory data, normally exported by kernel at
* /sys/bus/event_source/devices/<pmu>/. Give as input a buffer to hold the file
* path, the result is PMU loaded using that directory.
*/
static struct perf_pmu *test_pmu_get(char *dir, size_t sz)
{
unsigned int i;
/* Simulated format definitions. */
const struct test_format {
const char *name;
const char *value;
} test_formats[] = {
{ "krava01", "config:0-1,62-63\n", },
{ "krava02", "config:10-17\n", },
{ "krava03", "config:5\n", },
{ "krava11", "config1:0,2,4,6,8,20-28\n", },
{ "krava12", "config1:63\n", },
{ "krava13", "config1:45-47\n", },
{ "krava21", "config2:0-3,10-13,20-23,30-33,40-43,50-53,60-63\n", },
{ "krava22", "config2:8,18,48,58\n", },
{ "krava23", "config2:28-29,38\n", },
};
char name[PATH_MAX];
int dirfd, file;
struct perf_pmu *pmu = NULL;
ssize_t len;

/* Create equivalent of sysfs mount point. */
scnprintf(dir, sz, "/tmp/perf-pmu-test-XXXXXX");
if (!mkdtemp(dir)) {
pr_err("mkdtemp failed\n");
dir[0] = '\0';
return NULL;
}
dirfd = open(dir, O_DIRECTORY);
if (dirfd < 0) {
pr_err("Failed to open test directory \"%s\"\n", dir);
goto err_out;
}

for (i = 0; i < ARRAY_SIZE(test_terms); i++) {
struct parse_events_term *clone;
/* Create the test PMU directory and give it a perf_event_attr type number. */
if (mkdirat(dirfd, "perf-pmu-test", 0755) < 0) {
pr_err("Failed to mkdir PMU directory\n");
goto err_out;
}
file = openat(dirfd, "perf-pmu-test/type", O_WRONLY | O_CREAT, 0600);
if (!file) {
pr_err("Failed to open for writing file \"type\"\n");
goto err_out;
}
len = strlen("9999");
if (write(file, "9999\n", len) < len) {
close(file);
pr_err("Failed to write to 'type' file\n");
goto err_out;
}
close(file);

parse_events_term__clone(&clone, &test_terms[i]);
list_add_tail(&clone->list, &terms->terms);
/* Create format directory and files. */
if (mkdirat(dirfd, "perf-pmu-test/format", 0755) < 0) {
pr_err("Failed to mkdir PMU format directory\n)");
goto err_out;
}
for (size_t i = 0; i < ARRAY_SIZE(test_formats); i++) {
const struct test_format *format = &test_formats[i];

if (scnprintf(name, PATH_MAX, "perf-pmu-test/format/%s", format->name) < 0) {
pr_err("Failure to set up path for \"%s\"\n", format->name);
goto err_out;
}
file = openat(dirfd, name, O_WRONLY | O_CREAT, 0600);
if (!file) {
pr_err("Failed to open for writing file \"%s\"\n", name);
goto err_out;
}

if (write(file, format->value, strlen(format->value)) < 0) {
pr_err("Failed to write to file \"%s\"\n", name);
close(file);
goto err_out;
}
close(file);
}

/* Make the PMU reading the files created above. */
pmu = perf_pmus__add_test_pmu(dirfd, "perf-pmu-test");
if (!pmu)
pr_err("Test PMU creation failed\n");

err_out:
if (!pmu)
test_pmu_put(dir, pmu);
if (dirfd >= 0)
close(dirfd);
return pmu;
}

static int test__pmu(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
static int test__pmu_format(struct test_suite *test __maybe_unused, int subtest __maybe_unused)
{
char dir[PATH_MAX];
char *format;
struct parse_events_terms terms;
struct perf_event_attr attr;
struct perf_pmu *pmu;
int fd;
int ret;
struct parse_events_terms terms;
int ret = TEST_FAIL;
struct perf_pmu *pmu = test_pmu_get(dir, sizeof(dir));

parse_events_terms__init(&terms);
add_test_terms(&terms);
pmu = zalloc(sizeof(*pmu));
if (!pmu) {
parse_events_terms__exit(&terms);
return -ENOMEM;
}
if (!pmu)
return TEST_FAIL;

INIT_LIST_HEAD(&pmu->format);
INIT_LIST_HEAD(&pmu->aliases);
INIT_LIST_HEAD(&pmu->caps);
format = test_format_dir_get(dir, sizeof(dir));
if (!format) {
free(pmu);
parse_events_terms__exit(&terms);
return -EINVAL;
parse_events_terms__init(&terms);
if (parse_events_terms(&terms,
"krava01=15,krava02=170,krava03=1,krava11=27,krava12=1,"
"krava13=2,krava21=119,krava22=11,krava23=2",
NULL)) {
pr_err("Term parsing failed\n");
goto err_out;
}

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

fd = open(format, O_DIRECTORY);
if (fd < 0) {
ret = fd;
goto out;
ret = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/false, /*err=*/NULL);
if (ret) {
pr_err("perf_pmu__config_terms failed");
goto err_out;
}

pmu->name = strdup("perf-pmu-test");
ret = perf_pmu__format_parse(pmu, fd, /*eager_load=*/true);
if (ret)
goto out;
if (attr.config != 0xc00000000002a823) {
pr_err("Unexpected config value %llx\n", attr.config);
goto err_out;
}
if (attr.config1 != 0x8000400000000145) {
pr_err("Unexpected config1 value %llx\n", attr.config1);
goto err_out;
}
if (attr.config2 != 0x0400000020041d07) {
pr_err("Unexpected config2 value %llx\n", attr.config2);
goto err_out;
}

ret = perf_pmu__config_terms(pmu, &attr, &terms, /*zero=*/false, /*err=*/NULL);
if (ret)
goto out;

ret = -EINVAL;
if (attr.config != 0xc00000000002a823)
goto out;
if (attr.config1 != 0x8000400000000145)
goto out;
if (attr.config2 != 0x0400000020041d07)
goto out;

ret = 0;
out:
test_format_dir_put(format);
perf_pmu__delete(pmu);
ret = TEST_OK;
err_out:
parse_events_terms__exit(&terms);
test_pmu_put(dir, pmu);
return ret;
}

DEFINE_SUITE("Parse perf pmu format", pmu);
static struct test_case tests__pmu[] = {
TEST_CASE("Parsing with PMU format directory", pmu_format),
{ .name = NULL, }
};

struct test_suite suite__pmu = {
.desc = "Sysfs PMU tests",
.test_cases = tests__pmu,
};
2 changes: 1 addition & 1 deletion tools/perf/util/parse-events.c
Original file line number Diff line number Diff line change
Expand Up @@ -2585,7 +2585,7 @@ int parse_events_term__term(struct parse_events_term **term,
}

int parse_events_term__clone(struct parse_events_term **new,
struct parse_events_term *term)
const struct parse_events_term *term)
{
char *str;
struct parse_events_term temp = *term;
Expand Down
2 changes: 1 addition & 1 deletion tools/perf/util/parse-events.h
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ int parse_events_term__term(struct parse_events_term **term,
enum parse_events__term_type term_rhs,
void *loc_term, void *loc_val);
int parse_events_term__clone(struct parse_events_term **new,
struct parse_events_term *term);
const struct parse_events_term *term);
void parse_events_term__delete(struct parse_events_term *term);

void parse_events_terms__delete(struct parse_events_terms *terms);
Expand Down
Loading

0 comments on commit aa1551f

Please sign in to comment.