linux/tools/perf/util/strlist.c
Arnaldo Carvalho de Melo 612e4022c6 perf strlist: Remove dont_dupstr logic, used only once
Ian Rogers noticed that 678ed6b707 ("perf strlist: Don't write to
const memory") breaks the 'Remove thread map' 'perf test' entry, because
it keeps pointers to the temporary string introduced to avoid touching
the const memory.

This is because the thread_map__new_by_[pt]id_str() were the only
methods using the slist->dont_dupstr knob to keep pointers to the
original const string list, as it uses strtol to parse numbers and it
stops at the comma.

As this is the only case of dont_dupstr use, dupstr being the default,
and it gets in the way of getting rid of the last const-correctness,
remove this knob, with it:

  $ perf test 37
  37: Remove thread map   : Ok
  $

Fixes: 678ed6b707 ("perf strlist: Don't write to const memory")
Reported-by: Ian Rogers <irogers@google.com>
Tested-by: Ian Rogers <irogers@google.com>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2026-01-27 21:19:24 -03:00

203 lines
4 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* (c) 2009 Arnaldo Carvalho de Melo <acme@redhat.com>
*/
#include "strlist.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <linux/zalloc.h>
static
struct rb_node *strlist__node_new(struct rblist *rblist __maybe_unused, const void *entry)
{
const char *s = entry;
struct rb_node *rc = NULL;
struct str_node *snode = malloc(sizeof(*snode));
if (snode != NULL) {
snode->s = strdup(s);
if (snode->s == NULL)
goto out_delete;
rc = &snode->rb_node;
}
return rc;
out_delete:
free(snode);
return NULL;
}
static void str_node__delete(struct str_node *snode)
{
zfree((char **)&snode->s);
free(snode);
}
static
void strlist__node_delete(struct rblist *rblist __maybe_unused, struct rb_node *rb_node)
{
struct str_node *snode = container_of(rb_node, struct str_node, rb_node);
str_node__delete(snode);
}
static int strlist__node_cmp(struct rb_node *rb_node, const void *entry)
{
const char *str = entry;
struct str_node *snode = container_of(rb_node, struct str_node, rb_node);
return strcmp(snode->s, str);
}
int strlist__add(struct strlist *slist, const char *new_entry)
{
return rblist__add_node(&slist->rblist, new_entry);
}
int strlist__load(struct strlist *slist, const char *filename)
{
char entry[1024];
int err;
FILE *fp = fopen(filename, "r");
if (fp == NULL)
return -errno;
while (fgets(entry, sizeof(entry), fp) != NULL) {
const size_t len = strlen(entry);
if (len == 0)
continue;
entry[len - 1] = '\0';
err = strlist__add(slist, entry);
if (err != 0)
goto out;
}
err = 0;
out:
fclose(fp);
return err;
}
void strlist__remove(struct strlist *slist, struct str_node *snode)
{
rblist__remove_node(&slist->rblist, &snode->rb_node);
}
struct str_node *strlist__find(struct strlist *slist, const char *entry)
{
struct str_node *snode = NULL;
struct rb_node *rb_node = rblist__find(&slist->rblist, entry);
if (rb_node)
snode = container_of(rb_node, struct str_node, rb_node);
return snode;
}
static int strlist__parse_list_entry(struct strlist *slist, const char *s,
const char *subst_dir)
{
int err;
char *subst = NULL;
if (strncmp(s, "file://", 7) == 0)
return strlist__load(slist, s + 7);
if (subst_dir) {
err = -ENOMEM;
if (asprintf(&subst, "%s/%s", subst_dir, s) < 0)
goto out;
if (access(subst, F_OK) == 0) {
err = strlist__load(slist, subst);
goto out;
}
if (slist->file_only) {
err = -ENOENT;
goto out;
}
}
err = strlist__add(slist, s);
out:
free(subst);
return err;
}
static int strlist__parse_list(struct strlist *slist, const char *list, const char *subst_dir)
{
char *sep, *s = strdup(list), *sdup = s;
int err;
if (s == NULL)
return -ENOMEM;
while ((sep = strchr(s, ',')) != NULL) {
*sep = '\0';
err = strlist__parse_list_entry(slist, s, subst_dir);
if (err != 0)
return err;
s = sep + 1;
}
err = *s ? strlist__parse_list_entry(slist, s, subst_dir) : 0;
free(sdup);
return err;
}
struct strlist *strlist__new(const char *list, const struct strlist_config *config)
{
struct strlist *slist = malloc(sizeof(*slist));
if (slist != NULL) {
bool file_only = false;
const char *dirname = NULL;
if (config) {
dirname = config->dirname;
file_only = config->file_only;
}
rblist__init(&slist->rblist);
slist->rblist.node_cmp = strlist__node_cmp;
slist->rblist.node_new = strlist__node_new;
slist->rblist.node_delete = strlist__node_delete;
slist->file_only = file_only;
if (list && strlist__parse_list(slist, list, dirname) != 0)
goto out_error;
}
return slist;
out_error:
free(slist);
return NULL;
}
void strlist__delete(struct strlist *slist)
{
if (slist != NULL)
rblist__delete(&slist->rblist);
}
struct str_node *strlist__entry(const struct strlist *slist, unsigned int idx)
{
struct str_node *snode = NULL;
struct rb_node *rb_node;
rb_node = rblist__entry(&slist->rblist, idx);
if (rb_node)
snode = container_of(rb_node, struct str_node, rb_node);
return snode;
}