* [PATCH v1 01/26] perf threads: Fix uninitialized pointer put when thread removal misses
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 02/26] perf annotate: Fix sym_hist_entry memory leak when zeroing histograms Ian Rogers
` (24 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Unlike hashmap_insert(), hashmap_delete() leaves old_key and old_value
untouched when the requested key is not present in the map. In
threads__remove() and threads__remove_all_threads(), old_value was left
uninitialized and passed unconditionally to thread__put() without
checking hashmap__delete()'s return value. If the thread is not present
in the shard, thread__put() dereferences uninitialized stack memory.
Initialize old_value to NULL and only call thread__put() when
hashmap__delete() returns true.
Fixes: 412a2ff473ac ("perf threads: Switch from rbtree to hashmap")
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/threads.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/tools/perf/util/threads.c b/tools/perf/util/threads.c
index 6ca0b178fb6c..98cf7edaf748 100644
--- a/tools/perf/util/threads.c
+++ b/tools/perf/util/threads.c
@@ -142,10 +142,10 @@ void threads__remove_all_threads(struct threads *threads)
down_write(&table->lock);
__threads_table_entry__set_last_match(table, NULL);
hashmap__for_each_entry_safe(&table->shard, cur, tmp, bkt) {
- struct thread *old_value;
+ struct thread *old_value = NULL;
- hashmap__delete(&table->shard, cur->key, /*old_key=*/NULL, &old_value);
- thread__put(old_value);
+ if (hashmap__delete(&table->shard, cur->key, /*old_key=*/NULL, &old_value))
+ thread__put(old_value);
}
up_write(&table->lock);
}
@@ -154,14 +154,14 @@ void threads__remove_all_threads(struct threads *threads)
void threads__remove(struct threads *threads, struct thread *thread)
{
struct threads_table_entry *table = threads__table(threads, thread__tid(thread));
- struct thread *old_value;
+ struct thread *old_value = NULL;
down_write(&table->lock);
if (table->last_match && RC_CHK_EQUAL(table->last_match, thread))
__threads_table_entry__set_last_match(table, NULL);
- hashmap__delete(&table->shard, thread__tid(thread), /*old_key=*/NULL, &old_value);
- thread__put(old_value);
+ if (hashmap__delete(&table->shard, thread__tid(thread), /*old_key=*/NULL, &old_value))
+ thread__put(old_value);
up_write(&table->lock);
}
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 02/26] perf annotate: Fix sym_hist_entry memory leak when zeroing histograms
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
2026-11-13 21:48 ` [PATCH v1 01/26] perf threads: Fix uninitialized pointer put when thread removal misses Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 03/26] perf stat: Fix per_pkg_mask key leak on hashmap__add failure Ian Rogers
` (23 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
annotated_source__samples maps keys to heap-allocated struct
sym_hist_entry pointers, which annotated_source__delete() frees before
destroying the hashmap. However, symbol__annotate_zero_histograms()
called hashmap__clear(notes->src->samples) directly without freeing the
stored sym_hist_entry values, leaking every entry whenever histograms
are zeroed.
Iterate over notes->src->samples and free each entry before calling
hashmap__clear().
Fixes: d3e7cad6f36d ("perf annotate: Add a hashmap for symbol histogram")
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/annotate.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 86a4beec6528..9af1d6b72da9 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -164,7 +164,14 @@ void symbol__annotate_zero_histograms(struct symbol *sym)
if (notes->src != NULL) {
memset(notes->src->histograms, 0,
notes->src->nr_histograms * sizeof(*notes->src->histograms));
- hashmap__clear(notes->src->samples);
+ if (notes->src->samples) {
+ struct hashmap_entry *cur;
+ size_t bkt;
+
+ hashmap__for_each_entry(notes->src->samples, cur, bkt)
+ zfree(&cur->pvalue);
+ hashmap__clear(notes->src->samples);
+ }
}
if (notes->branch && notes->branch->cycles_hist) {
memset(notes->branch->cycles_hist, 0,
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 03/26] perf stat: Fix per_pkg_mask key leak on hashmap__add failure
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
2026-11-13 21:48 ` [PATCH v1 01/26] perf threads: Fix uninitialized pointer put when thread removal misses Ian Rogers
2026-11-13 21:48 ` [PATCH v1 02/26] perf annotate: Fix sym_hist_entry memory leak when zeroing histograms Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 04/26] perf build: Don't check difference of perf and libbpf hashmap Ian Rogers
` (22 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
In check_per_pkg(), key is allocated with malloc() before checking
whether the (die, socket) pair is already present in per_pkg_mask. If
hashmap__find() misses and hashmap__add() fails (e.g. -ENOMEM), key is
never freed.
Free key when hashmap__add() fails.
Fixes: 034f7ee130c1 ("perf stat: Fix wrong skipping for per-die aggregation")
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/stat.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index 6da3dbbde0e2..e7013449271d 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -363,8 +363,11 @@ static int check_per_pkg(struct evsel *counter, struct perf_counts_values *vals,
if (hashmap__find(mask, key, NULL)) {
*skip = true;
free(key);
- } else
+ } else {
ret = hashmap__add(mask, key, 1);
+ if (ret)
+ free(key);
+ }
return ret;
}
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 04/26] perf build: Don't check difference of perf and libbpf hashmap
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (2 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 03/26] perf stat: Fix per_pkg_mask key leak on hashmap__add failure Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 05/26] perf hashmap: Always build perf's hashmap and avoid libbpf symbol conflict Ian Rogers
` (21 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
We want to explore diverging the perf hashmap from libbpf's to fix a
few subtle bugs around strict aliasing (casting void ** to long *) and
use of ERR_PTR which is non-standard in the perf codebase and a source
of bugs such as:
commit d05073adda0f ("perf trace: Avoid an ERR_PTR in syscall_stats")
commit 96f202eab813 ("perf trace: Fix IS_ERR() vs NULL check bug")
commit 9f3c16a430e8 ("perf expr: Fix return value of ids__new()")
Signed-off-by: Ian Rogers <irogers@google.com>
---
| 4 ----
1 file changed, 4 deletions(-)
--git a/tools/perf/check-headers.sh b/tools/perf/check-headers.sh
index 531c0e0e84df..846a1b2fa5d8 100755
--- a/tools/perf/check-headers.sh
+++ b/tools/perf/check-headers.sh
@@ -214,10 +214,6 @@ do
beauty_check "$i" -B
done
-# check duplicated library files
-check_2 tools/perf/util/hashmap.h tools/lib/bpf/hashmap.h
-check_2 tools/perf/util/hashmap.c tools/lib/bpf/hashmap.c
-
# Files with larger differences
check_ignore_some_hunks lib/list_sort.c
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 05/26] perf hashmap: Always build perf's hashmap and avoid libbpf symbol conflict
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (3 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 04/26] perf build: Don't check difference of perf and libbpf hashmap Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 06/26] perf hashmap: Fix strict aliasing violations in hashmap Ian Rogers
` (20 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Previously tools/perf/util/hashmap.o was only compiled when libbpf was
disabled or linked dynamically; with static libbpf.a, perf linked
against libbpf's internal hashmap symbols.
To allow perf's hashmap implementation to diverge from libbpf's without
colliding with libbpf.a's global hashmap symbols or churning callers:
- Always build util/hashmap.o in tools/perf/util/Build.
- Rename the backing C functions in util/hashmap.c to __hashmap__*
(__hashmap__init, __hashmap__new, __hashmap__clear, __hashmap__free,
__hashmap__size, __hashmap__capacity, __hashmap__insert,
__hashmap__find, and __hashmap__delete, along with the internal
__hashmap__* helpers).
- Keep the existing struct hashmap and hashmap__* header wrappers in
util/hashmap.h forwarding to those functions, and update the single
direct hashmap_find() call in hwmon_pmu.c to hashmap__find().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/Build | 9 ----
tools/perf/util/hashmap.c | 70 +++++++++++++++----------------
tools/perf/util/hashmap.h | 83 ++++++++++++++++++++++++++-----------
tools/perf/util/hwmon_pmu.c | 2 +-
4 files changed, 94 insertions(+), 70 deletions(-)
diff --git a/tools/perf/util/Build b/tools/perf/util/Build
index 2c1f880c4c47..d9090f217301 100644
--- a/tools/perf/util/Build
+++ b/tools/perf/util/Build
@@ -197,16 +197,7 @@ perf-util-$(CONFIG_LIBELF) += symbol-elf.o
perf-util-$(CONFIG_LIBELF) += probe-file.o
perf-util-$(CONFIG_LIBELF) += probe-event.o
-ifdef CONFIG_LIBBPF_DYNAMIC
- hashmap := 1
-endif
-ifndef CONFIG_LIBBPF
- hashmap := 1
-endif
-
-ifdef hashmap
perf-util-y += hashmap.o
-endif
ifndef CONFIG_LIBELF
perf-util-y += symbol-minimal.o
diff --git a/tools/perf/util/hashmap.c b/tools/perf/util/hashmap.c
index 140ee4055676..b6f80a92f5dd 100644
--- a/tools/perf/util/hashmap.c
+++ b/tools/perf/util/hashmap.c
@@ -21,22 +21,22 @@
/* start with 4 buckets */
#define HASHMAP_MIN_CAP_BITS 2
-static void hashmap_add_entry(struct hashmap_entry **pprev,
- struct hashmap_entry *entry)
+static void __hashmap__add_entry(struct hashmap_entry **pprev,
+ struct hashmap_entry *entry)
{
entry->next = *pprev;
*pprev = entry;
}
-static void hashmap_del_entry(struct hashmap_entry **pprev,
- struct hashmap_entry *entry)
+static void __hashmap__del_entry(struct hashmap_entry **pprev,
+ struct hashmap_entry *entry)
{
*pprev = entry->next;
entry->next = NULL;
}
-void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn, void *ctx)
+void __hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn, void *ctx)
{
map->hash_fn = hash_fn;
map->equal_fn = equal_fn;
@@ -48,19 +48,19 @@ void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
map->sz = 0;
}
-struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn,
- void *ctx)
+struct hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn,
+ void *ctx)
{
struct hashmap *map = malloc(sizeof(struct hashmap));
if (!map)
return ERR_PTR(-ENOMEM);
- hashmap__init(map, hash_fn, equal_fn, ctx);
+ __hashmap__init(map, hash_fn, equal_fn, ctx);
return map;
}
-void hashmap__clear(struct hashmap *map)
+void __hashmap__clear(struct hashmap *map)
{
struct hashmap_entry *cur, *tmp;
size_t bkt;
@@ -73,32 +73,32 @@ void hashmap__clear(struct hashmap *map)
map->cap = map->cap_bits = map->sz = 0;
}
-void hashmap__free(struct hashmap *map)
+void __hashmap__free(struct hashmap *map)
{
if (IS_ERR_OR_NULL(map))
return;
- hashmap__clear(map);
+ __hashmap__clear(map);
free(map);
}
-size_t hashmap__size(const struct hashmap *map)
+size_t __hashmap__size(const struct hashmap *map)
{
return map->sz;
}
-size_t hashmap__capacity(const struct hashmap *map)
+size_t __hashmap__capacity(const struct hashmap *map)
{
return map->cap;
}
-static bool hashmap_needs_to_grow(struct hashmap *map)
+static bool __hashmap__needs_to_grow(struct hashmap *map)
{
/* grow if empty or more than 75% filled */
return (map->cap == 0) || ((map->sz + 1) * 4 / 3 > map->cap);
}
-static int hashmap_grow(struct hashmap *map)
+static int __hashmap__grow(struct hashmap *map)
{
struct hashmap_entry **new_buckets;
struct hashmap_entry *cur, *tmp;
@@ -116,7 +116,7 @@ static int hashmap_grow(struct hashmap *map)
hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
h = hash_bits(map->hash_fn(cur->key, map->ctx), new_cap_bits);
- hashmap_add_entry(&new_buckets[h], cur);
+ __hashmap__add_entry(&new_buckets[h], cur);
}
map->cap = new_cap;
@@ -127,10 +127,10 @@ static int hashmap_grow(struct hashmap *map)
return 0;
}
-static bool hashmap_find_entry(const struct hashmap *map,
- const long key, size_t hash,
- struct hashmap_entry ***pprev,
- struct hashmap_entry **entry)
+static bool __hashmap__find_entry(const struct hashmap *map,
+ const long key, size_t hash,
+ struct hashmap_entry ***pprev,
+ struct hashmap_entry **entry)
{
struct hashmap_entry *cur, **prev_ptr;
@@ -151,9 +151,9 @@ static bool hashmap_find_entry(const struct hashmap *map,
return false;
}
-int hashmap_insert(struct hashmap *map, long key, long value,
- enum hashmap_insert_strategy strategy,
- long *old_key, long *old_value)
+int __hashmap__insert(struct hashmap *map, long key, long value,
+ enum hashmap_insert_strategy strategy,
+ long *old_key, long *old_value)
{
struct hashmap_entry *entry;
size_t h;
@@ -166,7 +166,7 @@ int hashmap_insert(struct hashmap *map, long key, long value,
h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
if (strategy != HASHMAP_APPEND &&
- hashmap_find_entry(map, key, h, NULL, &entry)) {
+ __hashmap__find_entry(map, key, h, NULL, &entry)) {
if (old_key)
*old_key = entry->key;
if (old_value)
@@ -184,8 +184,8 @@ int hashmap_insert(struct hashmap *map, long key, long value,
if (strategy == HASHMAP_UPDATE)
return -ENOENT;
- if (hashmap_needs_to_grow(map)) {
- err = hashmap_grow(map);
+ if (__hashmap__needs_to_grow(map)) {
+ err = __hashmap__grow(map);
if (err)
return err;
h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
@@ -197,19 +197,19 @@ int hashmap_insert(struct hashmap *map, long key, long value,
entry->key = key;
entry->value = value;
- hashmap_add_entry(&map->buckets[h], entry);
+ __hashmap__add_entry(&map->buckets[h], entry);
map->sz++;
return 0;
}
-bool hashmap_find(const struct hashmap *map, long key, long *value)
+bool __hashmap__find(const struct hashmap *map, long key, long *value)
{
struct hashmap_entry *entry;
size_t h;
h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
- if (!hashmap_find_entry(map, key, h, NULL, &entry))
+ if (!__hashmap__find_entry(map, key, h, NULL, &entry))
return false;
if (value)
@@ -217,14 +217,14 @@ bool hashmap_find(const struct hashmap *map, long key, long *value)
return true;
}
-bool hashmap_delete(struct hashmap *map, long key,
- long *old_key, long *old_value)
+bool __hashmap__delete(struct hashmap *map, long key,
+ long *old_key, long *old_value)
{
struct hashmap_entry **pprev, *entry;
size_t h;
h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
- if (!hashmap_find_entry(map, key, h, &pprev, &entry))
+ if (!__hashmap__find_entry(map, key, h, &pprev, &entry))
return false;
if (old_key)
@@ -232,7 +232,7 @@ bool hashmap_delete(struct hashmap *map, long key,
if (old_value)
*old_value = entry->value;
- hashmap_del_entry(pprev, entry);
+ __hashmap__del_entry(pprev, entry);
free(entry);
map->sz--;
diff --git a/tools/perf/util/hashmap.h b/tools/perf/util/hashmap.h
index 0c4f155e8eb7..f8bc70a33026 100644
--- a/tools/perf/util/hashmap.h
+++ b/tools/perf/util/hashmap.h
@@ -5,8 +5,8 @@
*
* Copyright (c) 2019 Facebook
*/
-#ifndef __LIBBPF_HASHMAP_H
-#define __LIBBPF_HASHMAP_H
+#ifndef __PERF_UTIL_HASHMAP_H
+#define __PERF_UTIL_HASHMAP_H
#include <stdbool.h>
#include <stddef.h>
@@ -80,16 +80,49 @@ struct hashmap {
size_t sz;
};
-void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn, void *ctx);
-struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn,
- void *ctx);
-void hashmap__clear(struct hashmap *map);
-void hashmap__free(struct hashmap *map);
+void __hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn, void *ctx);
+struct hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn,
+ void *ctx);
+void __hashmap__clear(struct hashmap *map);
+void __hashmap__free(struct hashmap *map);
-size_t hashmap__size(const struct hashmap *map);
-size_t hashmap__capacity(const struct hashmap *map);
+size_t __hashmap__size(const struct hashmap *map);
+size_t __hashmap__capacity(const struct hashmap *map);
+
+static inline void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn, void *ctx)
+{
+ __hashmap__init(map, hash_fn, equal_fn, ctx);
+}
+
+static inline struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn,
+ void *ctx)
+{
+ return __hashmap__new(hash_fn, equal_fn, ctx);
+}
+
+static inline void hashmap__clear(struct hashmap *map)
+{
+ __hashmap__clear(map);
+}
+
+static inline void hashmap__free(struct hashmap *map)
+{
+ __hashmap__free(map);
+}
+
+static inline size_t hashmap__size(const struct hashmap *map)
+{
+ return __hashmap__size(map);
+}
+
+static inline size_t hashmap__capacity(const struct hashmap *map)
+{
+ return __hashmap__capacity(map);
+}
/*
* Hashmap insertion strategy:
@@ -126,14 +159,14 @@ enum hashmap_insert_strategy {
* through old_key and old_value to allow calling code do proper memory
* management.
*/
-int hashmap_insert(struct hashmap *map, long key, long value,
- enum hashmap_insert_strategy strategy,
- long *old_key, long *old_value);
+int __hashmap__insert(struct hashmap *map, long key, long value,
+ enum hashmap_insert_strategy strategy,
+ long *old_key, long *old_value);
#define hashmap__insert(map, key, value, strategy, old_key, old_value) \
- hashmap_insert((map), (long)(key), (long)(value), (strategy), \
- hashmap_cast_ptr(old_key), \
- hashmap_cast_ptr(old_value))
+ __hashmap__insert((map), (long)(key), (long)(value), (strategy), \
+ hashmap_cast_ptr(old_key), \
+ hashmap_cast_ptr(old_value))
#define hashmap__add(map, key, value) \
hashmap__insert((map), (key), (value), HASHMAP_ADD, NULL, NULL)
@@ -147,17 +180,17 @@ int hashmap_insert(struct hashmap *map, long key, long value,
#define hashmap__append(map, key, value) \
hashmap__insert((map), (key), (value), HASHMAP_APPEND, NULL, NULL)
-bool hashmap_delete(struct hashmap *map, long key, long *old_key, long *old_value);
+bool __hashmap__delete(struct hashmap *map, long key, long *old_key, long *old_value);
-#define hashmap__delete(map, key, old_key, old_value) \
- hashmap_delete((map), (long)(key), \
- hashmap_cast_ptr(old_key), \
- hashmap_cast_ptr(old_value))
+#define hashmap__delete(map, key, old_key, old_value) \
+ __hashmap__delete((map), (long)(key), \
+ hashmap_cast_ptr(old_key), \
+ hashmap_cast_ptr(old_value))
-bool hashmap_find(const struct hashmap *map, long key, long *value);
+bool __hashmap__find(const struct hashmap *map, long key, long *value);
#define hashmap__find(map, key, value) \
- hashmap_find((map), (long)(key), hashmap_cast_ptr(value))
+ __hashmap__find((map), (long)(key), hashmap_cast_ptr(value))
/*
* hashmap__for_each_entry - iterate over all entries in hashmap
@@ -205,4 +238,4 @@ bool hashmap_find(const struct hashmap *map, long key, long *value);
cur = tmp) \
if ((map)->equal_fn(cur->key, (_key), (map)->ctx))
-#endif /* __LIBBPF_HASHMAP_H */
+#endif /* __PERF_UTIL_HASHMAP_H */
diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c
index 96aeb379e169..c150fc0d7bd3 100644
--- a/tools/perf/util/hwmon_pmu.c
+++ b/tools/perf/util/hwmon_pmu.c
@@ -571,7 +571,7 @@ bool hwmon_pmu__have_event(struct perf_pmu *pmu, const char *name)
key.type = type;
key.num = number;
- if (hashmap_find(&hwm->events, key.type_and_num, /*value=*/NULL))
+ if (hashmap__find(&hwm->events, key.type_and_num, /*value=*/NULL))
return true;
if (key.num != -1)
return false;
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 06/26] perf hashmap: Fix strict aliasing violations in hashmap
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (4 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 05/26] perf hashmap: Always build perf's hashmap and avoid libbpf symbol conflict Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 07/26] perf hashmap: Remove errptr usage from hashmap Ian Rogers
` (19 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
The hashmap in perf util (copied from libbpf) contained strict
aliasing violations. Specifically, the hashmap_cast_ptr(p) macro was
casting pointers (such as void **) to long *, and these were
subsequently dereferenced in functions like __hashmap__insert(),
__hashmap__find(), and __hashmap__delete().
C's strict aliasing rules (C11 6.5/7) prohibit accessing an object
through an lvalue of an incompatible type. Dereferencing a long * to
write to a void * object is a violation, even if they share the same
size, as they are not compatible types. This can lead to undefined
behavior, especially with aggressive compiler optimizations.
Fix this by:
1. Updating __hashmap__insert(), __hashmap__find(), and
__hashmap__delete() to take void * for their output parameters
(old_key, old_value, and value), and zeroing old_key/old_value on
entry in __hashmap__delete() to match __hashmap__insert().
2. Modifying the implementation to use memcpy() and memset() for
accessing these output parameters. Accessing an object as an array of
characters (as done by memcpy) is a permitted exception to the
strict aliasing rules.
3. Updating the hashmap_cast_ptr(p) macro to return void *, ensuring
compatibility with the new function signatures while preserving the
static assertion that ensures the pointed-to type matches the size of
a long.
Input parameters (key and value) remain as long, as they involve value
conversion rather than incompatible pointer dereferencing, which is safe
under strict aliasing rules.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/hashmap.c | 26 ++++++++++++++++----------
tools/perf/util/hashmap.h | 8 ++++----
2 files changed, 20 insertions(+), 14 deletions(-)
diff --git a/tools/perf/util/hashmap.c b/tools/perf/util/hashmap.c
index b6f80a92f5dd..7fd87c7d72be 100644
--- a/tools/perf/util/hashmap.c
+++ b/tools/perf/util/hashmap.c
@@ -8,6 +8,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
+#include <string.h>
#include <errno.h>
#include <linux/err.h>
#include "hashmap.h"
@@ -153,24 +154,24 @@ static bool __hashmap__find_entry(const struct hashmap *map,
int __hashmap__insert(struct hashmap *map, long key, long value,
enum hashmap_insert_strategy strategy,
- long *old_key, long *old_value)
+ void *old_key, void *old_value)
{
struct hashmap_entry *entry;
size_t h;
int err;
if (old_key)
- *old_key = 0;
+ memset(old_key, 0, sizeof(long));
if (old_value)
- *old_value = 0;
+ memset(old_value, 0, sizeof(long));
h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
if (strategy != HASHMAP_APPEND &&
__hashmap__find_entry(map, key, h, NULL, &entry)) {
if (old_key)
- *old_key = entry->key;
+ memcpy(old_key, &entry->key, sizeof(long));
if (old_value)
- *old_value = entry->value;
+ memcpy(old_value, &entry->value, sizeof(long));
if (strategy == HASHMAP_SET || strategy == HASHMAP_UPDATE) {
entry->key = key;
@@ -203,7 +204,7 @@ int __hashmap__insert(struct hashmap *map, long key, long value,
return 0;
}
-bool __hashmap__find(const struct hashmap *map, long key, long *value)
+bool __hashmap__find(const struct hashmap *map, long key, void *value)
{
struct hashmap_entry *entry;
size_t h;
@@ -213,24 +214,29 @@ bool __hashmap__find(const struct hashmap *map, long key, long *value)
return false;
if (value)
- *value = entry->value;
+ memcpy(value, &entry->value, sizeof(long));
return true;
}
bool __hashmap__delete(struct hashmap *map, long key,
- long *old_key, long *old_value)
+ void *old_key, void *old_value)
{
struct hashmap_entry **pprev, *entry;
size_t h;
+ if (old_key)
+ memset(old_key, 0, sizeof(long));
+ if (old_value)
+ memset(old_value, 0, sizeof(long));
+
h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
if (!__hashmap__find_entry(map, key, h, &pprev, &entry))
return false;
if (old_key)
- *old_key = entry->key;
+ memcpy(old_key, &entry->key, sizeof(long));
if (old_value)
- *old_value = entry->value;
+ memcpy(old_value, &entry->value, sizeof(long));
__hashmap__del_entry(pprev, entry);
free(entry);
diff --git a/tools/perf/util/hashmap.h b/tools/perf/util/hashmap.h
index f8bc70a33026..4dcc2cf3ef1a 100644
--- a/tools/perf/util/hashmap.h
+++ b/tools/perf/util/hashmap.h
@@ -149,7 +149,7 @@ enum hashmap_insert_strategy {
_Static_assert((__builtin_constant_p((p)) ? (p) == NULL : 0) || \
sizeof(*(p)) == sizeof(long), \
#p " pointee should be a long-sized integer or a pointer"); \
- (long *)(p); \
+ (void *)(p); \
})
/*
@@ -161,7 +161,7 @@ enum hashmap_insert_strategy {
*/
int __hashmap__insert(struct hashmap *map, long key, long value,
enum hashmap_insert_strategy strategy,
- long *old_key, long *old_value);
+ void *old_key, void *old_value);
#define hashmap__insert(map, key, value, strategy, old_key, old_value) \
__hashmap__insert((map), (long)(key), (long)(value), (strategy), \
@@ -180,14 +180,14 @@ int __hashmap__insert(struct hashmap *map, long key, long value,
#define hashmap__append(map, key, value) \
hashmap__insert((map), (key), (value), HASHMAP_APPEND, NULL, NULL)
-bool __hashmap__delete(struct hashmap *map, long key, long *old_key, long *old_value);
+bool __hashmap__delete(struct hashmap *map, long key, void *old_key, void *old_value);
#define hashmap__delete(map, key, old_key, old_value) \
__hashmap__delete((map), (long)(key), \
hashmap_cast_ptr(old_key), \
hashmap_cast_ptr(old_value))
-bool __hashmap__find(const struct hashmap *map, long key, long *value);
+bool __hashmap__find(const struct hashmap *map, long key, void *value);
#define hashmap__find(map, key, value) \
__hashmap__find((map), (long)(key), hashmap_cast_ptr(value))
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 07/26] perf hashmap: Remove errptr usage from hashmap
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (5 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 06/26] perf hashmap: Fix strict aliasing violations in hashmap Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 08/26] perf hashmap: Introduce generic types and macros Ian Rogers
` (18 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
The hashmap implementation in tools/perf/util was using ERR_PTR to
return errors from hashmap__new(). This is non-standard for most of
the perf codebase which prefers NULL on error and setting errno. As
such it was a frequent source of bugs:
commit d05073adda0f ("perf trace: Avoid an ERR_PTR in syscall_stats")
commit 96f202eab813 ("perf trace: Fix IS_ERR() vs NULL check bug")
commit 9f3c16a430e8 ("perf expr: Fix return value of ids__new()")
commit 4d4d00dd321f ("perf tools: Update copy of libbpf's hashmap.c")
Remove the dependency on linux/err.h in hashmap.c. Update
__hashmap__new() to set errno = ENOMEM and return NULL on failure, and
update __hashmap__free() to check for NULL instead of using
IS_ERR_OR_NULL().
Update all callers of hashmap__new() to check for NULL (including
missing failure checks in fncache and
perf_pmu__create_placeholder_core_pmu()), and switch IS_ERR_OR_NULL()
checks on hashmap pointers to regular NULL checks.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-ftrace.c | 9 +--------
tools/perf/builtin-trace.c | 4 +---
tools/perf/ui/browsers/annotate.c | 4 ++--
tools/perf/util/annotate.c | 4 +---
tools/perf/util/expr.c | 9 ++-------
tools/perf/util/fncache.c | 10 +++++++---
tools/perf/util/hashmap.c | 9 +++++----
tools/perf/util/pmu.c | 6 ++++++
tools/perf/util/s390-sample-raw.c | 2 +-
tools/perf/util/stat.c | 2 +-
10 files changed, 27 insertions(+), 32 deletions(-)
diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c
index 7cdc9ae2fbbb..ac0107f5b891 100644
--- a/tools/perf/builtin-ftrace.c
+++ b/tools/perf/builtin-ftrace.c
@@ -1229,14 +1229,7 @@ static int prepare_func_profile(struct perf_ftrace *ftrace)
ftrace->graph_verbose = 0;
ftrace->profile_hash = hashmap__new(profile_hash, profile_equal, NULL);
- if (IS_ERR(ftrace->profile_hash)) {
- int err = PTR_ERR(ftrace->profile_hash);
-
- ftrace->profile_hash = NULL;
- return err;
- }
-
- return 0;
+ return ftrace->profile_hash ? 0 : -ENOMEM;
}
/* This is saved in a hashmap keyed by the function name */
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 4094141461f9..128efe1be8c3 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -1673,9 +1673,7 @@ static bool syscall_id_equal(long key1, long key2, void *ctx __maybe_unused)
static struct hashmap *alloc_syscall_stats(void)
{
- struct hashmap *result = hashmap__new(syscall_id_hash, syscall_id_equal, NULL);
-
- return IS_ERR(result) ? NULL : result;
+ return hashmap__new(syscall_id_hash, syscall_id_equal, NULL);
}
static void delete_syscall_stats(struct hashmap *syscall_stats)
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index cf36b5347f72..8b0c933caf2e 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -144,7 +144,7 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
if (!browser->navkeypressed)
ops.width += 1;
- if (!IS_ERR_OR_NULL(ab->type_hash))
+ if (ab->type_hash)
apd.type_hash = ab->type_hash;
annotation_line__write(al, notes, &ops, &apd);
@@ -1264,7 +1264,7 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
debuginfo__delete(browser.dbg);
- if (!IS_ERR_OR_NULL(browser.type_hash)) {
+ if (browser.type_hash) {
struct hashmap_entry *cur;
size_t bkt;
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index 9af1d6b72da9..d11add6424dc 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -137,10 +137,8 @@ static int annotated_source__alloc_histograms(struct annotated_source *src,
return -1;
src->samples = hashmap__new(sym_hist_hash, sym_hist_equal, NULL);
- if (IS_ERR(src->samples)) {
+ if (!src->samples)
zfree(&src->histograms);
- src->samples = NULL;
- }
return src->histograms ? 0 : -1;
}
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index f1164c4666b1..ac0243576def 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -66,12 +66,7 @@ static bool key_equal(long key1, long key2, void *ctx __maybe_unused)
struct hashmap *ids__new(void)
{
- struct hashmap *hash;
-
- hash = hashmap__new(key_hash, key_equal, NULL);
- if (IS_ERR(hash))
- return NULL;
- return hash;
+ return hashmap__new(key_hash, key_equal, NULL);
}
void ids__free(struct hashmap *ids)
@@ -306,7 +301,7 @@ struct expr_parse_ctx *expr__ctx_new(void)
return NULL;
ctx->ids = hashmap__new(key_hash, key_equal, NULL);
- if (IS_ERR(ctx->ids)) {
+ if (!ctx->ids) {
free(ctx);
return NULL;
}
diff --git a/tools/perf/util/fncache.c b/tools/perf/util/fncache.c
index bf9559c55c63..04ea150bc7cd 100644
--- a/tools/perf/util/fncache.c
+++ b/tools/perf/util/fncache.c
@@ -36,9 +36,10 @@ static struct hashmap *fncache__get(void)
static bool lookup_fncache(const char *name, bool *res)
{
+ struct hashmap *map = fncache__get();
long val;
- if (!hashmap__find(fncache__get(), name, &val))
+ if (!map || !hashmap__find(map, name, &val))
return false;
*res = (val != 0);
@@ -47,11 +48,14 @@ static bool lookup_fncache(const char *name, bool *res)
static void update_fncache(const char *name, bool res)
{
+ struct hashmap *map = fncache__get();
char *old_key = NULL, *key = strdup(name);
- if (key) {
- hashmap__set(fncache__get(), key, res, &old_key, /*old_value*/NULL);
+ if (map && key) {
+ hashmap__set(map, key, res, &old_key, /*old_value*/NULL);
free(old_key);
+ } else {
+ free(key);
}
}
diff --git a/tools/perf/util/hashmap.c b/tools/perf/util/hashmap.c
index 7fd87c7d72be..f2d8beeac27a 100644
--- a/tools/perf/util/hashmap.c
+++ b/tools/perf/util/hashmap.c
@@ -10,7 +10,6 @@
#include <stdio.h>
#include <string.h>
#include <errno.h>
-#include <linux/err.h>
#include "hashmap.h"
/* make sure libbpf doesn't use kernel-only integer typedefs */
@@ -55,8 +54,10 @@ struct hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
{
struct hashmap *map = malloc(sizeof(struct hashmap));
- if (!map)
- return ERR_PTR(-ENOMEM);
+ if (!map) {
+ errno = ENOMEM;
+ return NULL;
+ }
__hashmap__init(map, hash_fn, equal_fn, ctx);
return map;
}
@@ -76,7 +77,7 @@ void __hashmap__clear(struct hashmap *map)
void __hashmap__free(struct hashmap *map)
{
- if (IS_ERR_OR_NULL(map))
+ if (!map)
return;
__hashmap__clear(map);
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index 01753d5c69d1..a63fa0d9cfa5 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -1314,6 +1314,12 @@ struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pm
INIT_LIST_HEAD(&pmu->format);
pmu->aliases = hashmap__new(aliases__hash, aliases__equal, /*ctx=*/ NULL);
+ if (!pmu->aliases) {
+ perf_cpu_map__put(pmu->cpus);
+ free((char *)pmu->name);
+ free(pmu);
+ return NULL;
+ }
INIT_LIST_HEAD(&pmu->caps);
list_add_tail(&pmu->list, core_pmus);
return pmu;
diff --git a/tools/perf/util/s390-sample-raw.c b/tools/perf/util/s390-sample-raw.c
index aaf8838b2cb2..e0fac2aa15ed 100644
--- a/tools/perf/util/s390-sample-raw.c
+++ b/tools/perf/util/s390-sample-raw.c
@@ -202,7 +202,7 @@ static char *get_counter_name(int set, int nr, struct perf_pmu *pmu)
get_counter_name_hashmap_equal_fn,
/*ctx=*/NULL);
- if (!IS_ERR(tmp)) {
+ if (tmp) {
cache = tmp;
cache_pmu = pmu;
}
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index e7013449271d..097985da3dbb 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -326,7 +326,7 @@ static int check_per_pkg(struct evsel *counter, struct perf_counts_values *vals,
if (!mask) {
mask = hashmap__new(pkg_id_hash, pkg_id_equal, NULL);
- if (IS_ERR(mask))
+ if (!mask)
return -ENOMEM;
counter->per_pkg_mask = mask;
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 08/26] perf hashmap: Introduce generic types and macros
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (6 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 07/26] perf hashmap: Remove errptr usage from hashmap Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 09/26] perf fncache: Use generic hashmap for fncache Ian Rogers
` (17 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Add DECLARE_HASHMAP(name, key_type, value_type) and type-checked
hashmap__* wrapper macros around struct __hashmap while keeping legacy
struct hashmap compatible so callers can be migrated one hashmap at a
time:
- Encode key_type and value_type via phantom pointer members in a union
with struct __hashmap so wrapper structs preserve identical size and
alignment across 32-bit and 64-bit targets, with _Static_assert checks
that key_type and value_type fit in a long.
- Generate typed <name>__new() and NULL-safe <name>__free() helpers,
plus pointer-taking hashmap__init(), hashmap__clear(), hashmap__free(),
hashmap__size(), hashmap__capacity(), hashmap__insert(),
hashmap__add(), hashmap__set(), hashmap__update(), hashmap__append(),
hashmap__find(), and hashmap__delete() macros.
- Marshal output parameters (value_ptr, old_key, old_value) through
long-sized temporaries and assign via the declared types so sub-long
types (such as bool, int, or pid_t) are safe from stack buffer
overflows, endianness bugs, and strict-aliasing violations, while
allowing NULL output pointers and const-qualified lookup keys.
- Implement hashmap__for_each{,_safe}() and
hashmap__for_each_key{,_safe}() using single-loop __hashmap_iter and
__hashmap_key_iter helpers so 'break' exits the entire iteration,
bucket cursors are internal, and element deletion during iteration is
safe.
- Add a 'Generic hashmap' perf test suite exercising integer, string,
and sub-long bool/int32 maps, NULL frees, const lookups, early loop
breaks, and in-loop deletions.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/Build | 1 +
tools/perf/tests/builtin-test.c | 1 +
tools/perf/tests/hashmap.c | 192 +++++++++
tools/perf/tests/tests.h | 1 +
tools/perf/util/hashmap.c | 30 +-
tools/perf/util/hashmap.h | 675 ++++++++++++++++++++++++++------
6 files changed, 773 insertions(+), 127 deletions(-)
create mode 100644 tools/perf/tests/hashmap.c
diff --git a/tools/perf/tests/Build b/tools/perf/tests/Build
index 5eebb2ec5dde..2c2560613323 100644
--- a/tools/perf/tests/Build
+++ b/tools/perf/tests/Build
@@ -74,6 +74,7 @@ perf-test-y += hwmon_pmu.o
perf-test-y += tool_pmu.o
perf-test-y += subcmd-help.o
perf-test-y += kallsyms-split.o
+perf-test-y += hashmap.o
ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 arm arm64 powerpc riscv))
perf-test-$(CONFIG_DWARF_UNWIND) += dwarf-unwind.o
diff --git a/tools/perf/tests/builtin-test.c b/tools/perf/tests/builtin-test.c
index 327483f96401..24e7f8443e94 100644
--- a/tools/perf/tests/builtin-test.c
+++ b/tools/perf/tests/builtin-test.c
@@ -192,6 +192,7 @@ static struct test_suite *generic_tests[] = {
&suite__util,
&suite__subcmd_help,
&suite__kallsyms_split,
+ &suite__hashmap,
NULL,
};
diff --git a/tools/perf/tests/hashmap.c b/tools/perf/tests/hashmap.c
new file mode 100644
index 000000000000..5a4f39eb8c17
--- /dev/null
+++ b/tools/perf/tests/hashmap.c
@@ -0,0 +1,192 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "util/hashmap.h"
+#include "tests.h"
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+
+DECLARE_HASHMAP(int_map, long, long);
+DECLARE_HASHMAP(bool_map, char *, bool);
+DECLARE_HASHMAP(int32_map, int, const char *);
+
+static size_t int_hash(long key, void *ctx __maybe_unused)
+{
+ return (size_t)key;
+}
+
+static bool int_equal(long key1, long key2, void *ctx __maybe_unused)
+{
+ return key1 == key2;
+}
+
+static size_t str_hash_fn(long key, void *ctx __maybe_unused)
+{
+ return str_hash((const char *)key);
+}
+
+static bool str_equal_fn(long key1, long key2, void *ctx __maybe_unused)
+{
+ return strcmp((const char *)key1, (const char *)key2) == 0;
+}
+
+static int test_int_map(void)
+{
+ struct int_map imap;
+ long val = -1, old_k = -1, old_v = -1;
+ long k, v, sum = 0;
+
+ hashmap__init(&imap, int_hash, int_equal, NULL);
+ TEST_ASSERT_EQUAL("initial size", (int)hashmap__size(&imap), 0);
+
+ TEST_ASSERT_EQUAL("add 1", hashmap__add(&imap, 1L, 10L), 0);
+ TEST_ASSERT_EQUAL("add 2", hashmap__add(&imap, 2L, 20L), 0);
+ TEST_ASSERT_EQUAL("add dup", hashmap__add(&imap, 1L, 99L), -EEXIST);
+ TEST_ASSERT_EQUAL("size after add", (int)hashmap__size(&imap), 2);
+ TEST_ASSERT_VAL("capacity > 0", hashmap__capacity(&imap) >= 2);
+
+ TEST_ASSERT_VAL("find 1", hashmap__find(&imap, 1L, &val));
+ TEST_ASSERT_EQUAL("find 1 val", (int)val, 10);
+ TEST_ASSERT_VAL("find 2 NULL out", hashmap__find(&imap, 2L, NULL));
+ TEST_ASSERT_VAL("find 3 missing", !hashmap__find(&imap, 3L, &val));
+
+ /* Update missing vs existing */
+ TEST_ASSERT_EQUAL("update missing",
+ hashmap__update(&imap, 3L, 30L, &old_k, &old_v), -ENOENT);
+ TEST_ASSERT_EQUAL("update existing",
+ hashmap__update(&imap, 1L, 11L, &old_k, &old_v), 0);
+ TEST_ASSERT_EQUAL("update old_k", (int)old_k, 1);
+ TEST_ASSERT_EQUAL("update old_v", (int)old_v, 10);
+
+ /* Set new vs existing */
+ TEST_ASSERT_EQUAL("set new",
+ hashmap__set(&imap, 3L, 30L, &old_k, &old_v), 0);
+ TEST_ASSERT_EQUAL("set new old_k", (int)old_k, 0);
+ TEST_ASSERT_EQUAL("set new old_v", (int)old_v, 0);
+
+ /* Multimap append + for_each_key */
+ TEST_ASSERT_EQUAL("append 2", hashmap__append(&imap, 2L, 200L), 0);
+ sum = 0;
+ hashmap__for_each_key(&imap, 2L, v)
+ sum += v;
+ TEST_ASSERT_EQUAL("for_each_key sum", (int)sum, 220);
+
+ /* Delete one of the key=2 entries; verify missing delete zeroes outputs */
+ TEST_ASSERT_VAL("delete 2", hashmap__delete(&imap, 2L, &old_k, &old_v));
+ TEST_ASSERT_EQUAL("delete old_k", (int)old_k, 2);
+ TEST_ASSERT_VAL("delete missing", !hashmap__delete(&imap, 999L, &old_k, &old_v));
+ TEST_ASSERT_EQUAL("delete missing old_k zeroed", (int)old_k, 0);
+ TEST_ASSERT_EQUAL("delete missing old_v zeroed", (int)old_v, 0);
+
+ sum = 0;
+ hashmap__for_each(&imap, k, v)
+ sum += v;
+ TEST_ASSERT_EQUAL("for_each sum", (int)sum, 11 + 20 + 30);
+
+ hashmap__clear(&imap);
+ TEST_ASSERT_EQUAL("cleared size", (int)hashmap__size(&imap), 0);
+ return 0;
+}
+
+static int test_bool_and_str_map(void)
+{
+ struct bool_map *bmap;
+ char *k1, *k2, *k2_dup, *old_key = NULL;
+ char *iter_key;
+ const char *const_lookup = "alpha";
+ /* Guard bytes around bool to catch any >1-byte write overflow. */
+ struct {
+ unsigned char pre[8];
+ bool val;
+ unsigned char post[8];
+ } guard;
+ bool old_val = true;
+
+ /* Freeing NULL must be a safe no-op (including under UBSAN). */
+ bool_map__free(NULL);
+
+ bmap = bool_map__new(str_hash_fn, str_equal_fn, NULL);
+ TEST_ASSERT_VAL("bool_map__new", bmap != NULL);
+
+ k1 = strdup("alpha");
+ k2 = strdup("beta");
+ k2_dup = strdup("beta");
+ TEST_ASSERT_VAL("strdup", k1 && k2 && k2_dup);
+
+ TEST_ASSERT_EQUAL("add alpha", hashmap__add(bmap, k1, true), 0);
+ TEST_ASSERT_EQUAL("add beta", hashmap__add(bmap, k2, false), 0);
+
+ memset(&guard, 0x5a, sizeof(guard));
+ TEST_ASSERT_VAL("find const key", hashmap__find(bmap, const_lookup, &guard.val));
+ TEST_ASSERT_VAL("alpha is true", guard.val == true);
+ for (size_t i = 0; i < sizeof(guard.pre); i++) {
+ TEST_ASSERT_EQUAL("pre guard intact", guard.pre[i], 0x5a);
+ TEST_ASSERT_EQUAL("post guard intact", guard.post[i], 0x5a);
+ }
+
+ TEST_ASSERT_VAL("find beta", hashmap__find(bmap, "beta", &guard.val));
+ TEST_ASSERT_VAL("beta is false", guard.val == false);
+
+ TEST_ASSERT_EQUAL("set beta",
+ hashmap__set(bmap, k2_dup, true, &old_key, &old_val), 0);
+ TEST_ASSERT_VAL("old_key == k2", old_key == k2);
+ TEST_ASSERT_VAL("old_val == false", old_val == false);
+ free(old_key);
+
+ hashmap__for_each(bmap, iter_key, old_val)
+ free(iter_key);
+ bool_map__free(bmap);
+ return 0;
+}
+
+static int test_iter_break_and_safe_delete(void)
+{
+ struct int32_map map;
+ const struct int32_map *cmap = ↦
+ int k, count = 0;
+ const char *v;
+
+ hashmap__init(&map, int_hash, int_equal, NULL);
+
+ /* Insert enough entries (including negative keys) to span multiple buckets. */
+ for (int i = -10; i < 20; i++)
+ TEST_ASSERT_EQUAL("add int32", hashmap__add(&map, i, "entry"), 0);
+
+ TEST_ASSERT_VAL("find negative key on const map",
+ hashmap__find(cmap, -7, &v) && strcmp(v, "entry") == 0);
+
+ /* Verify 'break' exits the entire hashmap__for_each loop, not just one bucket. */
+ hashmap__for_each(cmap, k, v) {
+ count++;
+ if (count == 3)
+ break;
+ }
+ TEST_ASSERT_EQUAL("break stops outer iteration", count, 3);
+
+ /* Verify safe deletion during iteration. */
+ hashmap__for_each_safe(&map, k, v) {
+ if (k < 0)
+ TEST_ASSERT_VAL("delete negative", hashmap__delete(&map, k, NULL, NULL));
+ }
+ TEST_ASSERT_EQUAL("remaining non-negative", (int)hashmap__size(&map), 20);
+
+ hashmap__clear(&map);
+ return 0;
+}
+
+static int test__hashmap(struct test_suite *t __maybe_unused, int subtest __maybe_unused)
+{
+ int err;
+
+ err = test_int_map();
+ if (err)
+ return err;
+
+ err = test_bool_and_str_map();
+ if (err)
+ return err;
+
+ return test_iter_break_and_safe_delete();
+}
+
+DEFINE_SUITE("Generic hashmap", hashmap);
diff --git a/tools/perf/tests/tests.h b/tools/perf/tests/tests.h
index af822885890d..e9cbc0dcd778 100644
--- a/tools/perf/tests/tests.h
+++ b/tools/perf/tests/tests.h
@@ -203,6 +203,7 @@ DECLARE_SUITE(util);
DECLARE_SUITE(uncore_event_sorting);
DECLARE_SUITE(subcmd_help);
DECLARE_SUITE(kallsyms_split);
+DECLARE_SUITE(hashmap);
/*
* PowerPC and S390 do not support creation of instruction breakpoints using the
diff --git a/tools/perf/util/hashmap.c b/tools/perf/util/hashmap.c
index f2d8beeac27a..f6bfaeaee9d7 100644
--- a/tools/perf/util/hashmap.c
+++ b/tools/perf/util/hashmap.c
@@ -35,7 +35,7 @@ static void __hashmap__del_entry(struct hashmap_entry **pprev,
entry->next = NULL;
}
-void __hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
+void __hashmap__init(struct __hashmap *map, hashmap_hash_fn hash_fn,
hashmap_equal_fn equal_fn, void *ctx)
{
map->hash_fn = hash_fn;
@@ -48,11 +48,11 @@ void __hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
map->sz = 0;
}
-struct hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn,
- void *ctx)
+struct __hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn,
+ void *ctx)
{
- struct hashmap *map = malloc(sizeof(struct hashmap));
+ struct __hashmap *map = malloc(sizeof(*map));
if (!map) {
errno = ENOMEM;
@@ -62,7 +62,7 @@ struct hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
return map;
}
-void __hashmap__clear(struct hashmap *map)
+void __hashmap__clear(struct __hashmap *map)
{
struct hashmap_entry *cur, *tmp;
size_t bkt;
@@ -75,7 +75,7 @@ void __hashmap__clear(struct hashmap *map)
map->cap = map->cap_bits = map->sz = 0;
}
-void __hashmap__free(struct hashmap *map)
+void __hashmap__free(struct __hashmap *map)
{
if (!map)
return;
@@ -84,23 +84,23 @@ void __hashmap__free(struct hashmap *map)
free(map);
}
-size_t __hashmap__size(const struct hashmap *map)
+size_t __hashmap__size(const struct __hashmap *map)
{
return map->sz;
}
-size_t __hashmap__capacity(const struct hashmap *map)
+size_t __hashmap__capacity(const struct __hashmap *map)
{
return map->cap;
}
-static bool __hashmap__needs_to_grow(struct hashmap *map)
+static bool __hashmap__needs_to_grow(struct __hashmap *map)
{
/* grow if empty or more than 75% filled */
return (map->cap == 0) || ((map->sz + 1) * 4 / 3 > map->cap);
}
-static int __hashmap__grow(struct hashmap *map)
+static int __hashmap__grow(struct __hashmap *map)
{
struct hashmap_entry **new_buckets;
struct hashmap_entry *cur, *tmp;
@@ -129,7 +129,7 @@ static int __hashmap__grow(struct hashmap *map)
return 0;
}
-static bool __hashmap__find_entry(const struct hashmap *map,
+static bool __hashmap__find_entry(const struct __hashmap *map,
const long key, size_t hash,
struct hashmap_entry ***pprev,
struct hashmap_entry **entry)
@@ -153,7 +153,7 @@ static bool __hashmap__find_entry(const struct hashmap *map,
return false;
}
-int __hashmap__insert(struct hashmap *map, long key, long value,
+int __hashmap__insert(struct __hashmap *map, long key, long value,
enum hashmap_insert_strategy strategy,
void *old_key, void *old_value)
{
@@ -205,7 +205,7 @@ int __hashmap__insert(struct hashmap *map, long key, long value,
return 0;
}
-bool __hashmap__find(const struct hashmap *map, long key, void *value)
+bool __hashmap__find(const struct __hashmap *map, long key, void *value)
{
struct hashmap_entry *entry;
size_t h;
@@ -219,7 +219,7 @@ bool __hashmap__find(const struct hashmap *map, long key, void *value)
return true;
}
-bool __hashmap__delete(struct hashmap *map, long key,
+bool __hashmap__delete(struct __hashmap *map, long key,
void *old_key, void *old_value)
{
struct hashmap_entry **pprev, *entry;
diff --git a/tools/perf/util/hashmap.h b/tools/perf/util/hashmap.h
index 4dcc2cf3ef1a..bb85a65486be 100644
--- a/tools/perf/util/hashmap.h
+++ b/tools/perf/util/hashmap.h
@@ -8,10 +8,20 @@
#ifndef __PERF_UTIL_HASHMAP_H
#define __PERF_UTIL_HASHMAP_H
+#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
+#include <stdlib.h>
#include <limits.h>
+#include <linux/compiler.h>
+/**
+ * hash_bits() - Shuffle hash value and return requested number of upper bits.
+ * @h: Raw hash value.
+ * @bits: Number of bits to return (0 returns 0).
+ *
+ * Return: Multiplicative hash truncated to @bits bits.
+ */
static inline size_t hash_bits(size_t h, int bits)
{
/* shuffle bits and return requested number of upper bits */
@@ -28,7 +38,12 @@ static inline size_t hash_bits(size_t h, int bits)
#endif
}
-/* generic C-string hashing function */
+/**
+ * str_hash() - Generic C-string hashing function.
+ * @s: NUL-terminated string to hash.
+ *
+ * Return: Unshuffled hash value suitable for hash_bits().
+ */
static inline size_t str_hash(const char *s)
{
size_t h = 0;
@@ -40,22 +55,42 @@ static inline size_t str_hash(const char *s)
return h;
}
+/**
+ * typedef hashmap_hash_fn - Hash callback computing a key's hash value.
+ * @key: Key value cast to long.
+ * @ctx: Optional user context pointer passed at hashmap initialization.
+ *
+ * Return: Hash value for @key (shuffled internally via hash_bits()).
+ */
typedef size_t (*hashmap_hash_fn)(long key, void *ctx);
+
+/**
+ * typedef hashmap_equal_fn - Equality callback comparing two keys.
+ * @key1: First key value cast to long.
+ * @key2: Second key value cast to long.
+ * @ctx: Optional user context pointer passed at hashmap initialization.
+ *
+ * Return: true if @key1 and @key2 are equal, false otherwise.
+ */
typedef bool (*hashmap_equal_fn)(long key1, long key2, void *ctx);
/*
- * Hashmap interface is polymorphic, keys and values could be either
- * long-sized integers or pointers, this is achieved as follows:
- * - interface functions that operate on keys and values are hidden
- * behind auxiliary macros, e.g. hashmap_insert <-> hashmap__insert;
- * - these auxiliary macros cast the key and value parameters as
- * long or long *, so the user does not have to specify the casts explicitly;
- * - for pointer parameters (e.g. old_key) the size of the pointed
- * type is verified by hashmap_cast_ptr using _Static_assert;
- * - when iterating using hashmap__for_each_* forms
- * hasmap_entry->key should be used for integer keys and
- * hasmap_entry->pkey should be used for pointer keys,
- * same goes for values.
+ * Typed hashmap interface:
+ *
+ * Hashmaps are declared with DECLARE_HASHMAP(name, key_type, value_type),
+ * where key_type and value_type may be any integer, enum, bool, or pointer
+ * type with sizeof <= sizeof(long):
+ * - DECLARE_HASHMAP() defines 'struct <name>' along with '<name>__new()' and
+ * '<name>__free()' allocation helpers;
+ * - phantom pointer members (__key, __value) in a union with the backing
+ * struct __hashmap encode the declared key and value types at zero
+ * runtime space cost and without altering struct size or alignment;
+ * - hashmap__* macros type-check keys, values, output pointers, and iteration
+ * cursor variables against the declared types at compile time;
+ * - output parameters (value_ptr, old_key, old_value) are marshaled through
+ * long temporaries and assigned via the declared types so sub-long types
+ * (e.g. bool, int, pid_t) are safe from buffer overflows, endianness bugs,
+ * and strict-aliasing violations.
*/
struct hashmap_entry {
union {
@@ -69,7 +104,7 @@ struct hashmap_entry {
struct hashmap_entry *next;
};
-struct hashmap {
+struct __hashmap {
hashmap_hash_fn hash_fn;
hashmap_equal_fn equal_fn;
void *ctx;
@@ -80,148 +115,564 @@ struct hashmap {
size_t sz;
};
-void __hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn, void *ctx);
-struct hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn,
- void *ctx);
-void __hashmap__clear(struct hashmap *map);
-void __hashmap__free(struct hashmap *map);
+/* Legacy untyped hashmap, kept while migrating callers to DECLARE_HASHMAP. */
+struct hashmap {
+ union {
+ struct __hashmap map;
+ struct {
+ hashmap_hash_fn hash_fn;
+ hashmap_equal_fn equal_fn;
+ void *ctx;
+
+ struct hashmap_entry **buckets;
+ size_t cap;
+ size_t cap_bits;
+ size_t sz;
+ };
+ long *__key;
+ long *__value;
+ char __untyped;
+ };
+};
-size_t __hashmap__size(const struct hashmap *map);
-size_t __hashmap__capacity(const struct hashmap *map);
+void __hashmap__init(struct __hashmap *map, hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn, void *ctx);
+struct __hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
+ hashmap_equal_fn equal_fn,
+ void *ctx);
+void __hashmap__clear(struct __hashmap *map);
+void __hashmap__free(struct __hashmap *map);
-static inline void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn, void *ctx)
-{
- __hashmap__init(map, hash_fn, equal_fn, ctx);
-}
+size_t __hashmap__size(const struct __hashmap *map);
+size_t __hashmap__capacity(const struct __hashmap *map);
static inline struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
hashmap_equal_fn equal_fn,
void *ctx)
{
- return __hashmap__new(hash_fn, equal_fn, ctx);
+ struct hashmap *map = malloc(sizeof(*map));
+
+ if (!map) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ __hashmap__init(&map->map, hash_fn, equal_fn, ctx);
+ return map;
}
-static inline void hashmap__clear(struct hashmap *map)
+/**
+ * enum hashmap_insert_strategy - Strategy for hashmap__insert().
+ * @HASHMAP_ADD: Only add key/value if key doesn't exist yet; returns -EEXIST
+ * and optionally fills old_key/old_value if already present.
+ * @HASHMAP_SET: Add key/value pair if key doesn't exist yet; otherwise update
+ * key/value and optionally return previous key/value.
+ * @HASHMAP_UPDATE: Update key/value if key already exists; otherwise do
+ * nothing and return -ENOENT.
+ * @HASHMAP_APPEND: Always add key/value pair, even if key already exists.
+ * Turns the hashmap into a multimap allowing multiple values
+ * associated with the same key. Read via
+ * hashmap__for_each_key(); hashmap__find() returns the most
+ * recently inserted entry for the key.
+ */
+enum hashmap_insert_strategy {
+ HASHMAP_ADD,
+ HASHMAP_SET,
+ HASHMAP_UPDATE,
+ HASHMAP_APPEND,
+};
+
+int __hashmap__insert(struct __hashmap *map, long key, long value,
+ enum hashmap_insert_strategy strategy,
+ void *old_key, void *old_value);
+bool __hashmap__delete(struct __hashmap *map, long key,
+ void *old_key, void *old_value);
+bool __hashmap__find(const struct __hashmap *map, long key, void *value);
+
+/*
+ * Single-loop iterators used by hashmap__for_each* so 'break' exits the entire
+ * iteration and element deletion during iteration is safe.
+ */
+struct __hashmap_iter {
+ const struct __hashmap *map;
+ size_t bkt;
+ struct hashmap_entry *cur;
+ struct hashmap_entry *next;
+};
+
+static inline struct __hashmap_iter
+__hashmap_iter__init(const struct __hashmap *map)
{
- __hashmap__clear(map);
+ return (struct __hashmap_iter){
+ .map = map,
+ .bkt = 0,
+ .cur = NULL,
+ .next = (map && map->buckets && map->cap > 0) ? map->buckets[0] : NULL,
+ };
}
-static inline void hashmap__free(struct hashmap *map)
+static inline struct hashmap_entry *
+__hashmap_iter__advance(struct __hashmap_iter *it)
{
- __hashmap__free(map);
+ if (!it->map || !it->map->buckets)
+ return NULL;
+
+ while (!it->next) {
+ it->bkt++;
+ if (it->bkt >= it->map->cap) {
+ it->cur = NULL;
+ return NULL;
+ }
+ it->next = it->map->buckets[it->bkt];
+ }
+ it->cur = it->next;
+ it->next = it->cur->next;
+ return it->cur;
}
-static inline size_t hashmap__size(const struct hashmap *map)
+struct __hashmap_key_iter {
+ const struct __hashmap *map;
+ long key;
+ struct hashmap_entry *cur;
+ struct hashmap_entry *next;
+};
+
+static inline struct __hashmap_key_iter
+__hashmap_key_iter__init(const struct __hashmap *map, long key)
{
- return __hashmap__size(map);
+ struct hashmap_entry *first = NULL;
+
+ if (map && map->buckets) {
+ size_t h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
+
+ first = map->buckets[h];
+ }
+ return (struct __hashmap_key_iter){
+ .map = map,
+ .key = key,
+ .cur = NULL,
+ .next = first,
+ };
}
-static inline size_t hashmap__capacity(const struct hashmap *map)
+static inline struct hashmap_entry *
+__hashmap_key_iter__advance(struct __hashmap_key_iter *it)
{
- return __hashmap__capacity(map);
+ while (it->next) {
+ struct hashmap_entry *entry = it->next;
+
+ it->next = entry->next;
+ if (it->map->equal_fn(entry->key, it->key, it->map->ctx)) {
+ it->cur = entry;
+ return entry;
+ }
+ }
+ it->cur = NULL;
+ return NULL;
}
-/*
- * Hashmap insertion strategy:
- * - HASHMAP_ADD - only add key/value if key doesn't exist yet;
- * - HASHMAP_SET - add key/value pair if key doesn't exist yet; otherwise,
- * update value;
- * - HASHMAP_UPDATE - update value, if key already exists; otherwise, do
- * nothing and return -ENOENT;
- * - HASHMAP_APPEND - always add key/value pair, even if key already exists.
- * This turns hashmap into a multimap by allowing multiple values to be
- * associated with the same key. Most useful read API for such hashmap is
- * hashmap__for_each_key_entry() iteration. If hashmap__find() is still
- * used, it will return last inserted key/value entry (first in a bucket
- * chain).
+/**
+ * DECLARE_HASHMAP() - Declare a type-safe hashmap struct and alloc helpers.
+ * @name: Struct tag to define (creates 'struct @name', '@name__new()', and
+ * '@name__free()').
+ * @key_type: Type of keys stored in the map (sizeof(@key_type) <= sizeof(long)).
+ * @value_type: Type of values stored in the map
+ * (sizeof(@value_type) <= sizeof(long)).
+ *
+ * '@name__new(hash_fn, equal_fn, ctx)' allocates and initializes a new
+ * 'struct @name *', returning NULL with errno = ENOMEM on allocation failure.
+ * '@name__free(hmap)' clears all bucket storage and frees @hmap (NULL-safe;
+ * does not free caller-owned pointees referenced by keys or values).
*/
-enum hashmap_insert_strategy {
- HASHMAP_ADD,
- HASHMAP_SET,
- HASHMAP_UPDATE,
- HASHMAP_APPEND,
-};
-
-#define hashmap_cast_ptr(p) ({ \
- _Static_assert((__builtin_constant_p((p)) ? (p) == NULL : 0) || \
- sizeof(*(p)) == sizeof(long), \
- #p " pointee should be a long-sized integer or a pointer"); \
- (void *)(p); \
-})
+#define DECLARE_HASHMAP(name, key_type, value_type) \
+struct name { \
+ union { \
+ struct __hashmap map; \
+ key_type *__key; \
+ value_type *__value; \
+ int __untyped; \
+ }; \
+ _Static_assert(sizeof(key_type) <= sizeof(long), \
+ #name " key_type exceeds sizeof(long)"); \
+ _Static_assert(sizeof(value_type) <= sizeof(long), \
+ #name " value_type exceeds sizeof(long)"); \
+}; \
+static inline __maybe_unused struct name * \
+name##__new(hashmap_hash_fn hash_fn, hashmap_equal_fn equal_fn, void *ctx) \
+{ \
+ struct name *hmap = malloc(sizeof(*hmap)); \
+ \
+ if (!hmap) { \
+ errno = ENOMEM; \
+ return NULL; \
+ } \
+ __hashmap__init(&hmap->map, hash_fn, equal_fn, ctx); \
+ return hmap; \
+} \
+static inline __maybe_unused void name##__free(struct name *hmap) \
+{ \
+ if (!hmap) \
+ return; \
+ __hashmap__clear(&hmap->map); \
+ free(hmap); \
+}
/*
- * hashmap__insert() adds key/value entry w/ various semantics, depending on
- * provided strategy value. If a given key/value pair replaced already
- * existing key/value pair, both old key and old value will be returned
- * through old_key and old_value to allow calling code do proper memory
- * management.
+ * For pointer key types (T *), allow const-qualified lookup keys (const T *);
+ * for non-pointer key types, preserve the exact declared key_type.
*/
-int __hashmap__insert(struct hashmap *map, long key, long value,
- enum hashmap_insert_strategy strategy,
- void *old_key, void *old_value);
-
-#define hashmap__insert(map, key, value, strategy, old_key, old_value) \
- __hashmap__insert((map), (long)(key), (long)(value), (strategy), \
- hashmap_cast_ptr(old_key), \
- hashmap_cast_ptr(old_value))
-
-#define hashmap__add(map, key, value) \
- hashmap__insert((map), (key), (value), HASHMAP_ADD, NULL, NULL)
-
-#define hashmap__set(map, key, value, old_key, old_value) \
- hashmap__insert((map), (key), (value), HASHMAP_SET, (old_key), (old_value))
+#define ___hashmap__lookup_key_type(hm) \
+ typeof(__builtin_choose_expr( \
+ __builtin_classify_type(*(hm)->__key) == 5, \
+ (const typeof(*__builtin_choose_expr( \
+ __builtin_classify_type(*(hm)->__key) == 5, \
+ *(hm)->__key, (char *)NULL)) *)NULL, \
+ *(hm)->__key))
+
+#define ___hashmap__choose(hm, untyped_expr, typed_expr) \
+ __builtin_choose_expr(sizeof((hm)->__untyped) == 1, \
+ (untyped_expr), (typed_expr))
+
+#define ___hashmap__in_val(hm, expr) \
+ ___hashmap__choose(hm, (long)(expr), (expr))
+
+#define ___hashmap__check_untyped_ptr(hm, ptr) \
+ _Static_assert(sizeof((hm)->__untyped) != 1 || \
+ (__builtin_constant_p((ptr)) ? (ptr) == NULL : 0) || \
+ sizeof(*(ptr)) == sizeof(long), \
+ #ptr " pointee should be a long-sized integer or a pointer")
+
+/**
+ * hashmap__init() - Initialize an embedded/stack-allocated typed hashmap.
+ * @hmap: Pointer to a hashmap struct declared with DECLARE_HASHMAP().
+ * @hash_fn: Hash callback function.
+ * @equal_fn: Key equality callback function.
+ * @ctx: Optional user context pointer passed to @hash_fn and @equal_fn.
+ */
+#define hashmap__init(hmap, hash_fn, equal_fn, ctx) \
+ __hashmap__init(&(hmap)->map, (hash_fn), (equal_fn), (ctx))
-#define hashmap__update(map, key, value, old_key, old_value) \
- hashmap__insert((map), (key), (value), HASHMAP_UPDATE, (old_key), (old_value))
+/**
+ * hashmap__clear() - Remove all entries from a typed hashmap and free buckets.
+ * @hmap: Pointer to a typed hashmap.
+ *
+ * Does not free @hmap itself or caller-owned memory referenced by keys/values.
+ */
+#define hashmap__clear(hmap) \
+ __hashmap__clear(&(hmap)->map)
+
+#define ___hashmap__free(hmap, hm) \
+do { \
+ typeof(hmap) hm = (hmap); \
+ if (hm) { \
+ __hashmap__clear(&(hm)->map); \
+ free(hm); \
+ } \
+} while (0)
+
+/**
+ * hashmap__free() - Clear and free a heap-allocated typed hashmap.
+ * @hmap: Pointer to a typed hashmap allocated with <name>__new() (NULL-safe).
+ *
+ * Does not free caller-owned memory referenced by keys or values.
+ */
+#define hashmap__free(hmap) \
+ ___hashmap__free(hmap, __PASTE(__hm_, __COUNTER__))
-#define hashmap__append(map, key, value) \
- hashmap__insert((map), (key), (value), HASHMAP_APPEND, NULL, NULL)
+/**
+ * hashmap__size() - Return the number of entries currently in a hashmap.
+ * @hmap: Pointer to a typed hashmap (may be const).
+ *
+ * Return: Number of stored key/value entries.
+ */
+#define hashmap__size(hmap) \
+ __hashmap__size(&(hmap)->map)
-bool __hashmap__delete(struct hashmap *map, long key, void *old_key, void *old_value);
+/**
+ * hashmap__capacity() - Return the bucket count of a hashmap.
+ * @hmap: Pointer to a typed hashmap (may be const).
+ *
+ * Return: Number of allocated hash buckets.
+ */
+#define hashmap__capacity(hmap) \
+ __hashmap__capacity(&(hmap)->map)
+
+#define ___hashmap__insert(hmap, key, value, strategy, old_key, old_value, \
+ hm, k, v, okp, ovp, raw_ok, raw_ov, ok, ov, err) \
+({ \
+ typeof(hmap) hm = (hmap); \
+ typeof(*(hm)->__key) k = ___hashmap__in_val(hm, key); \
+ typeof(*(hm)->__value) v = ___hashmap__in_val(hm, value); \
+ long ok = 0, ov = 0; \
+ typeof((hm)->__key) okp = \
+ ___hashmap__choose(hm, (long *)NULL, (old_key)); \
+ typeof((hm)->__value) ovp = \
+ ___hashmap__choose(hm, (long *)NULL, (old_value)); \
+ void *raw_ok = ___hashmap__choose(hm, (void *)(old_key), \
+ (void *)(okp ? &ok : NULL)); \
+ void *raw_ov = ___hashmap__choose(hm, (void *)(old_value), \
+ (void *)(ovp ? &ov : NULL)); \
+ int err; \
+ ___hashmap__check_untyped_ptr(hm, old_key); \
+ ___hashmap__check_untyped_ptr(hm, old_value); \
+ err = __hashmap__insert(&(hm)->map, (long)k, (long)v, (strategy), \
+ raw_ok, raw_ov); \
+ if (okp) \
+ *okp = (typeof(*(hm)->__key))ok; \
+ if (ovp) \
+ *ovp = (typeof(*(hm)->__value))ov; \
+ err; \
+})
-#define hashmap__delete(map, key, old_key, old_value) \
- __hashmap__delete((map), (long)(key), \
- hashmap_cast_ptr(old_key), \
- hashmap_cast_ptr(old_value))
+#define _hashmap__insert(hmap, key, value, strategy, old_key, old_value, u) \
+ ___hashmap__insert(hmap, key, value, strategy, old_key, old_value, \
+ __PASTE(__hm_, u), __PASTE(__k_, u), __PASTE(__v_, u), \
+ __PASTE(__okp_, u), __PASTE(__ovp_, u), \
+ __PASTE(__rok_, u), __PASTE(__rov_, u), \
+ __PASTE(__ok_, u), __PASTE(__ov_, u), __PASTE(__err_, u))
+
+/**
+ * hashmap__insert() - Insert a key/value entry using the specified strategy.
+ * @hmap: Pointer to a typed hashmap.
+ * @key: Key to insert (must be compatible with declared key_type).
+ * @value: Value to insert (must be compatible with declared value_type).
+ * @strategy: Insertion strategy (enum hashmap_insert_strategy).
+ * @old_key: Optional pointer to key_type receiving previous key on match (or
+ * zeroed if no existing entry matched); may be NULL.
+ * @old_value: Optional pointer to value_type receiving previous value on match
+ * (or zeroed if no existing entry matched); may be NULL.
+ *
+ * If a given key/value pair replaced or matched an existing entry, both old
+ * key and old value are returned through @old_key and @old_value so calling
+ * code can perform proper memory management.
+ *
+ * Return: 0 on success, -EEXIST if @strategy is HASHMAP_ADD and @key exists,
+ * -ENOENT if @strategy is HASHMAP_UPDATE and @key is absent, or -ENOMEM on
+ * allocation failure.
+ */
+#define hashmap__insert(hmap, key, value, strategy, old_key, old_value) \
+ _hashmap__insert(hmap, key, value, strategy, old_key, old_value, __COUNTER__)
+
+/**
+ * hashmap__add() - Add a new key/value entry only if the key does not exist.
+ * @hmap: Pointer to a typed hashmap.
+ * @key: Key to insert.
+ * @value: Value to insert.
+ *
+ * Return: 0 on success, -EEXIST if @key already exists, or -ENOMEM.
+ */
+#define hashmap__add(hmap, key, value) \
+ hashmap__insert(hmap, key, value, HASHMAP_ADD, NULL, NULL)
+
+/**
+ * hashmap__set() - Add a key/value entry or replace an existing one.
+ * @hmap: Pointer to a typed hashmap.
+ * @key: Key to insert or replace.
+ * @value: Value to insert or replace.
+ * @old_key: Optional pointer to key_type receiving replaced key (or zeroed if
+ * newly inserted); may be NULL.
+ * @old_value: Optional pointer to value_type receiving replaced value (or
+ * zeroed if newly inserted); may be NULL.
+ *
+ * Return: 0 on success, or -ENOMEM on allocation failure.
+ */
+#define hashmap__set(hmap, key, value, old_key, old_value) \
+ hashmap__insert(hmap, key, value, HASHMAP_SET, old_key, old_value)
+
+/**
+ * hashmap__update() - Update value for an existing key only.
+ * @hmap: Pointer to a typed hashmap.
+ * @key: Key to look up and replace.
+ * @value: New value to store.
+ * @old_key: Optional pointer to key_type receiving replaced key; may be NULL.
+ * @old_value: Optional pointer to value_type receiving replaced value; may be
+ * NULL.
+ *
+ * Return: 0 on success, or -ENOENT if @key does not exist.
+ */
+#define hashmap__update(hmap, key, value, old_key, old_value) \
+ hashmap__insert(hmap, key, value, HASHMAP_UPDATE, old_key, old_value)
+
+/**
+ * hashmap__append() - Unconditionally append a key/value pair (multimap).
+ * @hmap: Pointer to a typed hashmap.
+ * @key: Key to append.
+ * @value: Value to append.
+ *
+ * Return: 0 on success, or -ENOMEM on allocation failure.
+ */
+#define hashmap__append(hmap, key, value) \
+ hashmap__insert(hmap, key, value, HASHMAP_APPEND, NULL, NULL)
+
+#define ___hashmap__find(hmap, key, value_ptr, hm, k, vp, raw_vp, v, found) \
+({ \
+ typeof(hmap) hm = (hmap); \
+ ___hashmap__lookup_key_type(hm) k = ___hashmap__in_val(hm, key); \
+ long v = 0; \
+ typeof((hm)->__value) vp = \
+ ___hashmap__choose(hm, (long *)NULL, (value_ptr)); \
+ void *raw_vp = ___hashmap__choose(hm, (void *)(value_ptr), \
+ (void *)(vp ? &v : NULL)); \
+ bool found; \
+ ___hashmap__check_untyped_ptr(hm, value_ptr); \
+ found = __hashmap__find(&(hm)->map, (long)k, raw_vp); \
+ if (found && vp) \
+ *vp = (typeof(*(hm)->__value))v; \
+ found; \
+})
-bool __hashmap__find(const struct hashmap *map, long key, void *value);
+#define _hashmap__find(hmap, key, value_ptr, u) \
+ ___hashmap__find(hmap, key, value_ptr, \
+ __PASTE(__hm_, u), __PASTE(__k_, u), __PASTE(__vp_, u), \
+ __PASTE(__rvp_, u), __PASTE(__v_, u), __PASTE(__found_, u))
+
+/**
+ * hashmap__find() - Look up a key in a typed hashmap.
+ * @hmap: Pointer to a typed hashmap (may be const).
+ * @key: Key to search for (const-qualified pointer keys are accepted).
+ * @value_ptr: Optional pointer to value_type receiving the found value; may be
+ * NULL to test membership only. Left unmodified if not found.
+ *
+ * Return: true if @key was found, false otherwise.
+ */
+#define hashmap__find(hmap, key, value_ptr) \
+ _hashmap__find(hmap, key, value_ptr, __COUNTER__)
+
+#define ___hashmap__delete(hmap, key, old_key, old_value, \
+ hm, k, okp, ovp, raw_ok, raw_ov, ok, ov, rem) \
+({ \
+ typeof(hmap) hm = (hmap); \
+ ___hashmap__lookup_key_type(hm) k = ___hashmap__in_val(hm, key); \
+ long ok = 0, ov = 0; \
+ typeof((hm)->__key) okp = \
+ ___hashmap__choose(hm, (long *)NULL, (old_key)); \
+ typeof((hm)->__value) ovp = \
+ ___hashmap__choose(hm, (long *)NULL, (old_value)); \
+ void *raw_ok = ___hashmap__choose(hm, (void *)(old_key), \
+ (void *)(okp ? &ok : NULL)); \
+ void *raw_ov = ___hashmap__choose(hm, (void *)(old_value), \
+ (void *)(ovp ? &ov : NULL)); \
+ bool rem; \
+ ___hashmap__check_untyped_ptr(hm, old_key); \
+ ___hashmap__check_untyped_ptr(hm, old_value); \
+ rem = __hashmap__delete(&(hm)->map, (long)k, raw_ok, raw_ov); \
+ if (okp) \
+ *okp = (typeof(*(hm)->__key))ok; \
+ if (ovp) \
+ *ovp = (typeof(*(hm)->__value))ov; \
+ rem; \
+})
-#define hashmap__find(map, key, value) \
- __hashmap__find((map), (long)(key), hashmap_cast_ptr(value))
+#define _hashmap__delete(hmap, key, old_key, old_value, u) \
+ ___hashmap__delete(hmap, key, old_key, old_value, \
+ __PASTE(__hm_, u), __PASTE(__k_, u), \
+ __PASTE(__okp_, u), __PASTE(__ovp_, u), \
+ __PASTE(__rok_, u), __PASTE(__rov_, u), \
+ __PASTE(__ok_, u), __PASTE(__ov_, u), __PASTE(__rem_, u))
+
+/**
+ * hashmap__delete() - Remove an entry matching @key from a typed hashmap.
+ * @hmap: Pointer to a typed hashmap.
+ * @key: Key to delete (const-qualified pointer keys are accepted).
+ * @old_key: Optional pointer to key_type receiving removed key (or zeroed if
+ * not found); may be NULL.
+ * @old_value: Optional pointer to value_type receiving removed value (or
+ * zeroed if not found); may be NULL.
+ *
+ * Return: true if an entry was removed, false if @key was not found.
+ */
+#define hashmap__delete(hmap, key, old_key, old_value) \
+ _hashmap__delete(hmap, key, old_key, old_value, __COUNTER__)
+
+#define ___hashmap__for_each(hmap, key_var, val_var, it, hm) \
+ for (struct __hashmap_iter it = ({ \
+ typeof(hmap) hm = (hmap); \
+ __hashmap_iter__init(hm ? &(hm)->map : NULL); \
+ }); \
+ __hashmap_iter__advance(&it) && ({ \
+ _Static_assert(sizeof(key_var) >= sizeof(*(hmap)->__key), \
+ "key_var narrower than key_type"); \
+ _Static_assert(sizeof(val_var) >= sizeof(*(hmap)->__value), \
+ "val_var narrower than value_type"); \
+ (key_var) = (typeof(*(hmap)->__key))it.cur->key; \
+ (val_var) = (typeof(*(hmap)->__value))it.cur->value; \
+ true; \
+ });)
+
+#define _hashmap__for_each(hmap, key_var, val_var, u) \
+ ___hashmap__for_each(hmap, key_var, val_var, \
+ __PASTE(__it_, u), __PASTE(__hm_, u))
+
+/**
+ * hashmap__for_each() - Iterate over all entries in a typed hashmap.
+ * @hmap: Pointer to a typed hashmap (may be const or NULL).
+ * @key_var: Loop cursor variable assigned each entry's key.
+ * @val_var: Loop cursor variable assigned each entry's value.
+ *
+ * Safe against deleting the current entry during iteration, and 'break' exits
+ * the entire loop across all buckets.
+ */
+#define hashmap__for_each(hmap, key_var, val_var) \
+ _hashmap__for_each(hmap, key_var, val_var, __COUNTER__)
+
+/**
+ * hashmap__for_each_safe() - Iterate over all entries in a typed hashmap, safe
+ * against removals.
+ * @hmap: Pointer to a typed hashmap (may be const or NULL).
+ * @key_var: Loop cursor variable assigned each entry's key.
+ * @val_var: Loop cursor variable assigned each entry's value.
+ */
+#define hashmap__for_each_safe(hmap, key_var, val_var) \
+ hashmap__for_each(hmap, key_var, val_var)
+
+#define ___hashmap__for_each_key(hmap, key, val_var, kit, hm, k) \
+ for (struct __hashmap_key_iter kit = ({ \
+ typeof(hmap) hm = (hmap); \
+ ___hashmap__lookup_key_type(hm) k = (key); \
+ __hashmap_key_iter__init(hm ? &(hm)->map : NULL, (long)k); \
+ }); \
+ __hashmap_key_iter__advance(&kit) && ({ \
+ _Static_assert(sizeof(val_var) >= sizeof(*(hmap)->__value), \
+ "val_var narrower than value_type"); \
+ (val_var) = (typeof(*(hmap)->__value))kit.cur->value; \
+ true; \
+ });)
+
+#define _hashmap__for_each_key(hmap, key, val_var, u) \
+ ___hashmap__for_each_key(hmap, key, val_var, __PASTE(__kit_, u), \
+ __PASTE(__hm_, u), __PASTE(__k_, u))
+
+/**
+ * hashmap__for_each_key() - Iterate over all values associated with @key.
+ * @hmap: Pointer to a typed hashmap (may be const or NULL).
+ * @key: Key to iterate entries for (const-qualified pointer keys accepted).
+ * @val_var: Loop cursor variable assigned each matching entry's value.
+ */
+#define hashmap__for_each_key(hmap, key, val_var) \
+ _hashmap__for_each_key(hmap, key, val_var, __COUNTER__)
+
+/**
+ * hashmap__for_each_key_safe() - Iterate over all values associated with @key,
+ * safe against removals.
+ * @hmap: Pointer to a typed hashmap (may be const or NULL).
+ * @key: Key to iterate entries for (const-qualified pointer keys accepted).
+ * @val_var: Loop cursor variable assigned each matching entry's value.
+ */
+#define hashmap__for_each_key_safe(hmap, key, val_var) \
+ hashmap__for_each_key(hmap, key, val_var)
/*
- * hashmap__for_each_entry - iterate over all entries in hashmap
- * @map: hashmap to iterate
- * @cur: struct hashmap_entry * used as a loop cursor
- * @bkt: integer used as a bucket loop cursor
+ * Legacy entry iterators for unconverted struct hashmap callers.
*/
#define hashmap__for_each_entry(map, cur, bkt) \
for (bkt = 0; bkt < (map)->cap; bkt++) \
for (cur = (map)->buckets[bkt]; cur; cur = cur->next)
-/*
- * hashmap__for_each_entry_safe - iterate over all entries in hashmap, safe
- * against removals
- * @map: hashmap to iterate
- * @cur: struct hashmap_entry * used as a loop cursor
- * @tmp: struct hashmap_entry * used as a temporary next cursor storage
- * @bkt: integer used as a bucket loop cursor
- */
#define hashmap__for_each_entry_safe(map, cur, tmp, bkt) \
for (bkt = 0; bkt < (map)->cap; bkt++) \
for (cur = (map)->buckets[bkt]; \
cur && ({tmp = cur->next; true; }); \
cur = tmp)
-/*
- * hashmap__for_each_key_entry - iterate over entries associated with given key
- * @map: hashmap to iterate
- * @cur: struct hashmap_entry * used as a loop cursor
- * @key: key to iterate entries for
- */
#define hashmap__for_each_key_entry(map, cur, _key) \
for (cur = (map)->buckets \
? (map)->buckets[hash_bits((map)->hash_fn((_key), (map)->ctx), (map)->cap_bits)] \
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 09/26] perf fncache: Use generic hashmap for fncache
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (7 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 08/26] perf hashmap: Introduce generic types and macros Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 10/26] perf ftrace: Use generic hashmap for profile_hash Ian Rogers
` (16 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert fncache in tools/perf/util/fncache.c to
DECLARE_HASHMAP(fncache_map, char *, bool) and use the type-checked
hashmap__* wrappers, eliminating the manual long temporary conversion on
bool lookups.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/fncache.c | 21 +++++++++------------
1 file changed, 9 insertions(+), 12 deletions(-)
diff --git a/tools/perf/util/fncache.c b/tools/perf/util/fncache.c
index 04ea150bc7cd..f00367b5f265 100644
--- a/tools/perf/util/fncache.c
+++ b/tools/perf/util/fncache.c
@@ -8,7 +8,9 @@
#include "fncache.h"
#include "hashmap.h"
-static struct hashmap *fncache;
+DECLARE_HASHMAP(fncache_map, char *, bool);
+
+static struct fncache_map *fncache;
static size_t fncache__hash(long key, void *ctx __maybe_unused)
{
@@ -22,10 +24,10 @@ static bool fncache__equal(long key1, long key2, void *ctx __maybe_unused)
static void fncache__init(void)
{
- fncache = hashmap__new(fncache__hash, fncache__equal, /*ctx=*/NULL);
+ fncache = fncache_map__new(fncache__hash, fncache__equal, /*ctx=*/NULL);
}
-static struct hashmap *fncache__get(void)
+static struct fncache_map *fncache__get(void)
{
static pthread_once_t fncache_once = PTHREAD_ONCE_INIT;
@@ -36,23 +38,18 @@ static struct hashmap *fncache__get(void)
static bool lookup_fncache(const char *name, bool *res)
{
- struct hashmap *map = fncache__get();
- long val;
-
- if (!map || !hashmap__find(map, name, &val))
- return false;
+ struct fncache_map *map = fncache__get();
- *res = (val != 0);
- return true;
+ return map && hashmap__find(map, name, res);
}
static void update_fncache(const char *name, bool res)
{
- struct hashmap *map = fncache__get();
+ struct fncache_map *map = fncache__get();
char *old_key = NULL, *key = strdup(name);
if (map && key) {
- hashmap__set(map, key, res, &old_key, /*old_value*/NULL);
+ hashmap__set(map, key, res, &old_key, /*old_value=*/NULL);
free(old_key);
} else {
free(key);
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 10/26] perf ftrace: Use generic hashmap for profile_hash
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (8 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 09/26] perf fncache: Use generic hashmap for fncache Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 11/26] perf trace: Use generic hashmap for syscall_stats Ian Rogers
` (15 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert perf_ftrace::profile_hash to DECLARE_HASHMAP(ftrace_profile_map,
char *, struct ftrace_profile_data *) and use the typed hashmap__* and
hashmap__for_each() wrappers.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-ftrace.c | 43 ++++++++++++++++++++++---------------
tools/perf/util/ftrace.h | 7 ++++--
2 files changed, 31 insertions(+), 19 deletions(-)
diff --git a/tools/perf/builtin-ftrace.c b/tools/perf/builtin-ftrace.c
index ac0107f5b891..02b4bf12451e 100644
--- a/tools/perf/builtin-ftrace.c
+++ b/tools/perf/builtin-ftrace.c
@@ -1228,7 +1228,7 @@ static int prepare_func_profile(struct perf_ftrace *ftrace)
ftrace->graph_tail = 1;
ftrace->graph_verbose = 0;
- ftrace->profile_hash = hashmap__new(profile_hash, profile_equal, NULL);
+ ftrace->profile_hash = ftrace_profile_map__new(profile_hash, profile_equal, NULL);
return ftrace->profile_hash ? 0 : -ENOMEM;
}
@@ -1347,17 +1347,22 @@ enum perf_ftrace_profile_sort_key {
static enum perf_ftrace_profile_sort_key profile_sort = PFP_SORT_TOTAL;
+struct ftrace_profile_entry {
+ char *name;
+ struct ftrace_profile_data *data;
+};
+
static int cmp_profile_data(const void *a, const void *b)
{
- const struct hashmap_entry *e1 = *(const struct hashmap_entry **)a;
- const struct hashmap_entry *e2 = *(const struct hashmap_entry **)b;
- struct ftrace_profile_data *p1 = e1->pvalue;
- struct ftrace_profile_data *p2 = e2->pvalue;
+ const struct ftrace_profile_entry *e1 = a;
+ const struct ftrace_profile_entry *e2 = b;
+ struct ftrace_profile_data *p1 = e1->data;
+ struct ftrace_profile_data *p2 = e2->data;
double v1, v2;
switch (profile_sort) {
case PFP_SORT_NAME:
- return strcmp(e1->pkey, e2->pkey);
+ return strcmp(e1->name, e2->name);
case PFP_SORT_AVG:
v1 = p1->st.mean;
v2 = p2->st.mean;
@@ -1386,8 +1391,10 @@ static int cmp_profile_data(const void *a, const void *b)
static void print_profile_result(struct perf_ftrace *ftrace)
{
- struct hashmap_entry *entry, **profile;
- size_t i, nr, bkt;
+ struct ftrace_profile_entry *profile;
+ size_t i, nr;
+ char *key;
+ struct ftrace_profile_data *val;
nr = hashmap__size(ftrace->profile_hash);
if (nr == 0)
@@ -1400,20 +1407,22 @@ static void print_profile_result(struct perf_ftrace *ftrace)
}
i = 0;
- hashmap__for_each_entry(ftrace->profile_hash, entry, bkt)
- profile[i++] = entry;
+ hashmap__for_each(ftrace->profile_hash, key, val) {
+ profile[i].name = key;
+ profile[i].data = val;
+ i++;
+ }
assert(i == nr);
- //cmp_profile_data(profile[0], profile[1]);
qsort(profile, nr, sizeof(*profile), cmp_profile_data);
printf("# %10s %10s %10s %10s %s\n",
"Total (us)", "Avg (us)", "Max (us)", "Count", "Function");
for (i = 0; i < nr; i++) {
- const char *name = profile[i]->pkey;
- struct ftrace_profile_data *p = profile[i]->pvalue;
+ const char *name = profile[i].name;
+ struct ftrace_profile_data *p = profile[i].data;
printf("%12.3f %10.3f %6"PRIu64".%03"PRIu64" %10.0f %s\n",
p->st.n * p->st.mean / 1000, p->st.mean / 1000,
@@ -1422,12 +1431,12 @@ static void print_profile_result(struct perf_ftrace *ftrace)
free(profile);
- hashmap__for_each_entry(ftrace->profile_hash, entry, bkt) {
- free((char *)entry->pkey);
- free(entry->pvalue);
+ hashmap__for_each(ftrace->profile_hash, key, val) {
+ free(key);
+ free(val);
}
- hashmap__free(ftrace->profile_hash);
+ ftrace_profile_map__free(ftrace->profile_hash);
ftrace->profile_hash = NULL;
}
diff --git a/tools/perf/util/ftrace.h b/tools/perf/util/ftrace.h
index 950f2efafad2..5ed52e66b8cb 100644
--- a/tools/perf/util/ftrace.h
+++ b/tools/perf/util/ftrace.h
@@ -3,12 +3,15 @@
#include <linux/list.h>
+#include "hashmap.h"
#include "target.h"
struct evlist;
-struct hashamp;
+struct ftrace_profile_data;
struct stats;
+DECLARE_HASHMAP(ftrace_profile_map, char *, struct ftrace_profile_data *);
+
struct perf_ftrace {
struct evlist *evlist;
struct target target;
@@ -18,7 +21,7 @@ struct perf_ftrace {
struct list_head graph_funcs;
struct list_head nograph_funcs;
struct list_head event_pair;
- struct hashmap *profile_hash;
+ struct ftrace_profile_map *profile_hash;
unsigned long percpu_buffer_size;
bool inherit;
bool use_nsec;
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 11/26] perf trace: Use generic hashmap for syscall_stats
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (9 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 10/26] perf ftrace: Use generic hashmap for profile_hash Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 12/26] perf trace: Use generic hashmap for bpf-trace-summary Ian Rogers
` (14 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert trace and thread_trace syscall_stats maps in builtin-trace.c to
DECLARE_HASHMAP(syscall_stats_map, long, struct syscall_stats *) and
replace direct struct hashmap_entry and map->sz accesses with
hashmap__for_each() and hashmap__size().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/builtin-trace.c | 43 ++++++++++++++++++++------------------
1 file changed, 23 insertions(+), 20 deletions(-)
diff --git a/tools/perf/builtin-trace.c b/tools/perf/builtin-trace.c
index 128efe1be8c3..dcd7890db8ac 100644
--- a/tools/perf/builtin-trace.c
+++ b/tools/perf/builtin-trace.c
@@ -141,6 +141,9 @@ struct syscall_fmt {
bool hexret;
};
+struct syscall_stats;
+DECLARE_HASHMAP(syscall_stats_map, long, struct syscall_stats *);
+
struct trace {
struct perf_env host_env;
struct perf_tool tool;
@@ -187,7 +190,7 @@ struct trace {
* into the key, but this would make the code inconsistent with the
* per-thread version.
*/
- struct hashmap *syscall_stats;
+ struct syscall_stats_map *syscall_stats;
double duration_filter;
double runtime_ms;
unsigned long pfmaj, pfmin;
@@ -1658,7 +1661,7 @@ struct thread_trace {
struct file *table;
} files;
- struct hashmap *syscall_stats;
+ struct syscall_stats_map *syscall_stats;
};
static size_t syscall_id_hash(long key, void *ctx __maybe_unused)
@@ -1671,22 +1674,22 @@ static bool syscall_id_equal(long key1, long key2, void *ctx __maybe_unused)
return key1 == key2;
}
-static struct hashmap *alloc_syscall_stats(void)
+static struct syscall_stats_map *alloc_syscall_stats(void)
{
- return hashmap__new(syscall_id_hash, syscall_id_equal, NULL);
+ return syscall_stats_map__new(syscall_id_hash, syscall_id_equal, NULL);
}
-static void delete_syscall_stats(struct hashmap *syscall_stats)
+static void delete_syscall_stats(struct syscall_stats_map *syscall_stats)
{
- struct hashmap_entry *pos;
- size_t bkt;
+ long key;
+ struct syscall_stats *val;
if (!syscall_stats)
return;
- hashmap__for_each_entry(syscall_stats, pos, bkt)
- zfree(&pos->pvalue);
- hashmap__free(syscall_stats);
+ hashmap__for_each(syscall_stats, key, val)
+ free(val);
+ syscall_stats_map__free(syscall_stats);
}
static struct thread_trace *thread_trace__new(struct trace *trace)
@@ -2865,7 +2868,7 @@ static void thread__update_stats(struct thread *thread, struct thread_trace *ttr
int id, struct perf_sample *sample, long err,
struct trace *trace)
{
- struct hashmap *syscall_stats = ttrace->syscall_stats;
+ struct syscall_stats_map *syscall_stats = ttrace->syscall_stats;
struct syscall_stats *stats = NULL;
u64 duration = 0;
@@ -5210,25 +5213,25 @@ static int entry_cmp(const void *e1, const void *e2)
return entry1->msecs > entry2->msecs ? -1 : 1;
}
-static struct syscall_entry *syscall__sort_stats(struct hashmap *syscall_stats)
+static struct syscall_entry *syscall__sort_stats(struct syscall_stats_map *syscall_stats)
{
struct syscall_entry *entry;
- struct hashmap_entry *pos;
- unsigned bkt, i, nr;
+ unsigned int i, nr;
+ long id;
+ struct syscall_stats *ss;
- nr = syscall_stats->sz;
+ nr = hashmap__size(syscall_stats);
entry = malloc(nr * sizeof(*entry));
if (entry == NULL)
return NULL;
i = 0;
- hashmap__for_each_entry(syscall_stats, pos, bkt) {
- struct syscall_stats *ss = pos->pvalue;
+ hashmap__for_each(syscall_stats, id, ss) {
struct stats *st = &ss->stats;
entry[i].stats = ss;
entry[i].msecs = (u64)st->n * (avg_stats(st) / NSEC_PER_MSEC);
- entry[i].syscall = pos->key;
+ entry[i].syscall = id;
i++;
}
assert(i == nr);
@@ -5238,7 +5241,7 @@ static struct syscall_entry *syscall__sort_stats(struct hashmap *syscall_stats)
}
static size_t syscall__dump_stats(struct trace *trace, int e_machine, FILE *fp,
- struct hashmap *syscall_stats)
+ struct syscall_stats_map *syscall_stats)
{
size_t printed = 0;
int lines = 0;
@@ -5255,7 +5258,7 @@ static size_t syscall__dump_stats(struct trace *trace, int e_machine, FILE *fp,
printed += fprintf(fp, " (msec) (msec) (msec) (msec) (%%)\n");
printed += fprintf(fp, " --------------- -------- ------ -------- --------- --------- --------- ------\n");
- for (size_t i = 0; i < syscall_stats->sz; i++) {
+ for (size_t i = 0; i < hashmap__size(syscall_stats); i++) {
struct syscall_entry *entry = &entries[i];
struct syscall_stats *stats = entry->stats;
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 12/26] perf trace: Use generic hashmap for bpf-trace-summary
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (10 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 11/26] perf trace: Use generic hashmap for syscall_stats Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 13/26] perf lock: Use generic hashmap for slab_hash Ian Rogers
` (13 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert the syscall summary hashmaps in util/bpf-trace-summary.c to
DECLARE_HASHMAP(syscall_data_map, long, struct syscall_data *) and use
hashmap__for_each() instead of struct hashmap_entry iteration.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/bpf-trace-summary.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/tools/perf/util/bpf-trace-summary.c b/tools/perf/util/bpf-trace-summary.c
index 9a31dbf06cbb..2cc42f19d223 100644
--- a/tools/perf/util/bpf-trace-summary.c
+++ b/tools/perf/util/bpf-trace-summary.c
@@ -113,6 +113,8 @@ struct syscall_data {
struct syscall_node *nodes;
};
+DECLARE_HASHMAP(syscall_data_map, long, struct syscall_data *);
+
static int datacmp(const void *a, const void *b)
{
const struct syscall_data * const *sa = a;
@@ -169,7 +171,7 @@ static int print_common_stats(struct syscall_data *data, int max_summary, FILE *
return printed;
}
-static int update_thread_stats(struct hashmap *hash, struct syscall_key *map_key,
+static int update_thread_stats(struct syscall_data_map *hash, struct syscall_key *map_key,
struct syscall_stats *map_data)
{
struct syscall_data *data;
@@ -233,7 +235,7 @@ static int print_thread_stats(struct syscall_data **data, int nr_data, int max_s
return printed;
}
-static int update_total_stats(struct hashmap *hash, struct syscall_key *map_key,
+static int update_total_stats(struct syscall_data_map *hash, struct syscall_key *map_key,
struct syscall_stats *map_data)
{
struct syscall_data *data;
@@ -305,7 +307,7 @@ static int print_total_stats(struct syscall_data **data, int nr_data, int max_su
return printed;
}
-static int update_cgroup_stats(struct hashmap *hash, struct syscall_key *map_key,
+static int update_cgroup_stats(struct syscall_data_map *hash, struct syscall_key *map_key,
struct syscall_stats *map_data)
{
struct syscall_data *data;
@@ -379,12 +381,12 @@ int trace_print_bpf_summary(FILE *fp, int max_summary)
struct bpf_map *map = skel->maps.syscall_stats_map;
struct syscall_key *prev_key, key;
struct syscall_data **data = NULL;
- struct hashmap schash;
- struct hashmap_entry *entry;
+ struct syscall_data_map schash;
+ long entry_key;
+ struct syscall_data *entry_val;
int nr_data = 0;
int printed = 0;
int i;
- size_t bkt;
hashmap__init(&schash, sc_node_hash, sc_node_equal, /*ctx=*/NULL);
@@ -420,8 +422,8 @@ int trace_print_bpf_summary(FILE *fp, int max_summary)
goto out;
i = 0;
- hashmap__for_each_entry(&schash, entry, bkt)
- data[i++] = entry->pvalue;
+ hashmap__for_each(&schash, entry_key, entry_val)
+ data[i++] = entry_val;
qsort(data, nr_data, sizeof(*data), datacmp);
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 13/26] perf lock: Use generic hashmap for slab_hash
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (11 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 12/26] perf trace: Use generic hashmap for bpf-trace-summary Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 14/26] perf stat: Use generic hashmap for per_pkg_mask Ian Rogers
` (12 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert slab_hash in util/bpf_lock_contention.c to
DECLARE_HASHMAP(slab_cache_map, u32, struct slab_cache_data *) and use
hashmap__for_each() during cleanup.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/bpf_lock_contention.c | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/tools/perf/util/bpf_lock_contention.c b/tools/perf/util/bpf_lock_contention.c
index c20bd075664e..2bd8e62d3265 100644
--- a/tools/perf/util/bpf_lock_contention.c
+++ b/tools/perf/util/bpf_lock_contention.c
@@ -20,9 +20,11 @@
#include "bpf_skel/lock_contention.skel.h"
#include "bpf_skel/lock_data.h"
+DECLARE_HASHMAP(slab_cache_map, u32, struct slab_cache_data *);
+
static struct lock_contention_bpf *skel;
static bool has_slab_iter;
-static struct hashmap slab_hash;
+static struct slab_cache_map slab_hash;
static size_t slab_cache_hash(long key, void *ctx __maybe_unused)
{
@@ -99,11 +101,11 @@ static void run_slab_cache_iter(void)
static void exit_slab_cache_iter(void)
{
- struct hashmap_entry *cur;
- unsigned bkt;
+ u32 id;
+ struct slab_cache_data *val;
- hashmap__for_each_entry(&slab_hash, cur, bkt)
- free(cur->pvalue);
+ hashmap__for_each(&slab_hash, id, val)
+ free(val);
hashmap__clear(&slab_hash);
}
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 14/26] perf stat: Use generic hashmap for per_pkg_mask
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (12 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 13/26] perf lock: Use generic hashmap for slab_hash Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 15/26] perf expr: Use generic hashmap for expr_ids Ian Rogers
` (11 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert evsel::per_pkg_mask in util/evsel.{c,h} and util/stat.c to
DECLARE_HASHMAP(per_pkg_mask_map, uint64_t *, bool) and use
hashmap__for_each() to free keys in evsel__free_per_pkg().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/evsel.c | 10 +++++-----
tools/perf/util/evsel.h | 6 ++++--
tools/perf/util/stat.c | 6 +++---
3 files changed, 12 insertions(+), 10 deletions(-)
diff --git a/tools/perf/util/evsel.c b/tools/perf/util/evsel.c
index 6d69f63b7fb1..a37d0d66fb45 100644
--- a/tools/perf/util/evsel.c
+++ b/tools/perf/util/evsel.c
@@ -2094,7 +2094,7 @@ static void evsel__exit(struct evsel *evsel)
zfree(&evsel->unit);
zfree(&evsel->metric_id);
evsel__zero_per_pkg(evsel);
- hashmap__free(evsel->per_pkg_mask);
+ per_pkg_mask_map__free(evsel->per_pkg_mask);
evsel->per_pkg_mask = NULL;
if (evsel__priv_destructor)
evsel__priv_destructor(evsel->priv);
@@ -4761,12 +4761,12 @@ int evsel__store_ids(struct evsel *evsel, struct evlist *evlist)
void evsel__zero_per_pkg(struct evsel *evsel)
{
- struct hashmap_entry *cur;
- size_t bkt;
+ uint64_t *key;
+ bool val;
if (evsel->per_pkg_mask) {
- hashmap__for_each_entry(evsel->per_pkg_mask, cur, bkt)
- zfree(&cur->pkey);
+ hashmap__for_each(evsel->per_pkg_mask, key, val)
+ free(key);
hashmap__clear(evsel->per_pkg_mask);
}
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h
index 53f3641b66e1..eeb2ece1490c 100644
--- a/tools/perf/util/evsel.h
+++ b/tools/perf/util/evsel.h
@@ -13,6 +13,7 @@
#include <internal/evsel.h>
#include <perf/evsel.h>
+#include "hashmap.h"
#include "symbol_conf.h"
struct bperf_follower_bpf;
@@ -20,7 +21,6 @@ struct bperf_leader_bpf;
struct bpf_counter_ops;
struct bpf_object;
struct cgroup;
-struct hashmap;
struct perf_counts;
struct perf_pmu;
struct perf_stat_config;
@@ -28,6 +28,8 @@ struct perf_stat_evsel;
struct target;
union perf_event;
+DECLARE_HASHMAP(per_pkg_mask_map, uint64_t *, bool);
+
typedef int (evsel__sb_cb_t)(union perf_event *event, void *data);
/** struct evsel - event selector
@@ -135,7 +137,7 @@ struct evsel {
bool fallenback_eacces;
bool fallenback_eopnotsupp;
u8 probe_type:3;
- struct hashmap *per_pkg_mask;
+ struct per_pkg_mask_map *per_pkg_mask;
int err;
int script_output_type;
struct {
diff --git a/tools/perf/util/stat.c b/tools/perf/util/stat.c
index 097985da3dbb..c502f4928f9d 100644
--- a/tools/perf/util/stat.c
+++ b/tools/perf/util/stat.c
@@ -310,7 +310,7 @@ static bool pkg_id_equal(long __key1, long __key2, void *ctx __maybe_unused)
static int check_per_pkg(struct evsel *counter, struct perf_counts_values *vals,
int cpu_map_idx, bool *skip)
{
- struct hashmap *mask = counter->per_pkg_mask;
+ struct per_pkg_mask_map *mask = counter->per_pkg_mask;
struct perf_cpu_map *cpus = evsel__cpus(counter);
struct perf_cpu cpu = perf_cpu_map__cpu(cpus, cpu_map_idx);
int s, d, ret = 0;
@@ -325,7 +325,7 @@ static int check_per_pkg(struct evsel *counter, struct perf_counts_values *vals,
return 0;
if (!mask) {
- mask = hashmap__new(pkg_id_hash, pkg_id_equal, NULL);
+ mask = per_pkg_mask_map__new(pkg_id_hash, pkg_id_equal, NULL);
if (!mask)
return -ENOMEM;
@@ -364,7 +364,7 @@ static int check_per_pkg(struct evsel *counter, struct perf_counts_values *vals,
*skip = true;
free(key);
} else {
- ret = hashmap__add(mask, key, 1);
+ ret = hashmap__add(mask, key, true);
if (ret)
free(key);
}
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 15/26] perf expr: Use generic hashmap for expr_ids
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (13 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 14/26] perf stat: Use generic hashmap for per_pkg_mask Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 16/26] perf pmu: Use generic hashmap for pmu aliases Ian Rogers
` (10 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert expr_parse_ctx::ids and ids__* helpers across util/expr.{c,h,y},
util/metricgroup.c, tests/expr.c, and tests/pmu-events.c to
DECLARE_HASHMAP(expr_ids_map, const char *, struct expr_id_data *).
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/tests/expr.c | 2 +-
tools/perf/tests/pmu-events.c | 16 +++---
tools/perf/util/expr.c | 99 +++++++++++++++++------------------
tools/perf/util/expr.h | 18 ++++---
tools/perf/util/expr.y | 2 +-
tools/perf/util/metricgroup.c | 28 +++++-----
6 files changed, 83 insertions(+), 82 deletions(-)
diff --git a/tools/perf/tests/expr.c b/tools/perf/tests/expr.c
index 726cf8d4da28..a2c7dc4d71c9 100644
--- a/tools/perf/tests/expr.c
+++ b/tools/perf/tests/expr.c
@@ -15,7 +15,7 @@
static int test_ids_union(void)
{
- struct hashmap *ids1, *ids2;
+ struct expr_ids_map *ids1, *ids2;
/* Empty union. */
ids1 = ids__new();
diff --git a/tools/perf/tests/pmu-events.c b/tools/perf/tests/pmu-events.c
index e6e4f4cf1fa2..42862501f0bf 100644
--- a/tools/perf/tests/pmu-events.c
+++ b/tools/perf/tests/pmu-events.c
@@ -964,10 +964,10 @@ static struct test_metric metrics[] = {
static int metric_parse_fake(const char *metric_name, const char *str)
{
struct expr_parse_ctx *ctx;
- struct hashmap_entry *cur;
+ const char *key;
+ struct expr_id_data *val;
double result;
int ret = -1;
- size_t bkt;
int i;
pr_debug("parsing '%s': '%s'\n", metric_name, str);
@@ -989,11 +989,11 @@ static int metric_parse_fake(const char *metric_name, const char *str)
* make them unique.
*/
i = 1;
- hashmap__for_each_entry(ctx->ids, cur, bkt)
- expr__add_id_val(ctx, strdup(cur->pkey), i++);
+ hashmap__for_each(ctx->ids, key, val)
+ expr__add_id_val(ctx, strdup(key), i++);
- hashmap__for_each_entry(ctx->ids, cur, bkt) {
- if (check_parse_fake(cur->pkey)) {
+ hashmap__for_each(ctx->ids, key, val) {
+ if (check_parse_fake(key)) {
pr_err("check_parse_fake failed\n");
goto out;
}
@@ -1006,8 +1006,8 @@ static int metric_parse_fake(const char *metric_name, const char *str)
* resolve divide by zero issues.
*/
i = 1024;
- hashmap__for_each_entry(ctx->ids, cur, bkt)
- expr__add_id_val(ctx, strdup(cur->pkey), i--);
+ hashmap__for_each(ctx->ids, key, val)
+ expr__add_id_val(ctx, strdup(key), i--);
if (expr__parse(&result, ctx, str)) {
pr_err("expr__parse failed for %s\n", metric_name);
/* The following have hard to avoid divide by zero. */
diff --git a/tools/perf/util/expr.c b/tools/perf/util/expr.c
index ac0243576def..d548ae5a4ec9 100644
--- a/tools/perf/util/expr.c
+++ b/tools/perf/util/expr.c
@@ -64,48 +64,46 @@ static bool key_equal(long key1, long key2, void *ctx __maybe_unused)
return !strcmp((const char *)key1, (const char *)key2);
}
-struct hashmap *ids__new(void)
+struct expr_ids_map *ids__new(void)
{
- return hashmap__new(key_hash, key_equal, NULL);
+ return expr_ids_map__new(key_hash, key_equal, NULL);
}
-void ids__free(struct hashmap *ids)
+void ids__free(struct expr_ids_map *ids)
{
- struct hashmap_entry *cur;
- size_t bkt;
+ const char *key;
+ struct expr_id_data *val;
if (ids == NULL)
return;
- hashmap__for_each_entry(ids, cur, bkt) {
- zfree(&cur->pkey);
- zfree(&cur->pvalue);
+ hashmap__for_each(ids, key, val) {
+ free((char *)key);
+ free(val);
}
- hashmap__free(ids);
+ expr_ids_map__free(ids);
}
-int ids__insert(struct hashmap *ids, const char *id)
+int ids__insert(struct expr_ids_map *ids, const char *id)
{
struct expr_id_data *data_ptr = NULL, *old_data = NULL;
- char *old_key = NULL;
+ const char *old_key = NULL;
int ret;
ret = hashmap__set(ids, id, data_ptr, &old_key, &old_data);
if (ret)
free(data_ptr);
- free(old_key);
+ free((char *)old_key);
free(old_data);
return ret;
}
-struct hashmap *ids__union(struct hashmap *ids1, struct hashmap *ids2)
+struct expr_ids_map *ids__union(struct expr_ids_map *ids1, struct expr_ids_map *ids2)
{
- size_t bkt;
- struct hashmap_entry *cur;
+ const char *key, *old_key = NULL;
+ struct expr_id_data *val, *old_data = NULL;
int ret;
- struct expr_id_data *old_data = NULL;
- char *old_key = NULL;
if (!ids1)
return ids2;
@@ -113,24 +111,24 @@ struct hashmap *ids__union(struct hashmap *ids1, struct hashmap *ids2)
if (!ids2)
return ids1;
- if (hashmap__size(ids1) < hashmap__size(ids2)) {
- struct hashmap *tmp = ids1;
+ if (hashmap__size(ids1) < hashmap__size(ids2)) {
+ struct expr_ids_map *tmp = ids1;
ids1 = ids2;
ids2 = tmp;
}
- hashmap__for_each_entry(ids2, cur, bkt) {
- ret = hashmap__set(ids1, cur->key, cur->value, &old_key, &old_data);
- free(old_key);
+ hashmap__for_each(ids2, key, val) {
+ ret = hashmap__set(ids1, key, val, &old_key, &old_data);
+ free((char *)old_key);
free(old_data);
if (ret) {
- hashmap__free(ids1);
- hashmap__free(ids2);
+ expr_ids_map__free(ids1);
+ expr_ids_map__free(ids2);
return NULL;
}
}
- hashmap__free(ids2);
+ expr_ids_map__free(ids2);
return ids1;
}
@@ -151,7 +149,7 @@ int expr__add_id_val_source_count_aggr_nr(struct expr_parse_ctx *ctx, const char
double val, int source_count, int aggr_nr)
{
struct expr_id_data *data_ptr = NULL, *old_data = NULL;
- char *old_key = NULL;
+ const char *old_key = NULL;
int ret;
data_ptr = malloc(sizeof(*data_ptr));
@@ -170,7 +168,7 @@ int expr__add_id_val_source_count_aggr_nr(struct expr_parse_ctx *ctx, const char
data_ptr->val.source_count += old_data->val.source_count;
data_ptr->val.aggr_nr += old_data->val.aggr_nr;
}
- free(old_key);
+ free((char *)old_key);
free(old_data);
return ret;
}
@@ -185,7 +183,7 @@ int expr__add_id_val_source_count(struct expr_parse_ctx *ctx, const char *id,
int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref)
{
struct expr_id_data *data_ptr = NULL, *old_data = NULL;
- char *old_key = NULL;
+ const char *old_key = NULL;
char *name;
int ret;
@@ -210,13 +208,15 @@ int expr__add_ref(struct expr_parse_ctx *ctx, struct metric_ref *ref)
data_ptr->kind = EXPR_ID_DATA__REF;
ret = hashmap__set(ctx->ids, name, data_ptr, &old_key, &old_data);
- if (ret)
+ if (ret) {
free(data_ptr);
+ free(name);
+ }
pr_debug2("adding ref metric %s: %s\n",
ref->metric_name, ref->metric_expr);
- free(old_key);
+ free((char *)old_key);
free(old_data);
return ret;
}
@@ -232,12 +232,11 @@ int expr__get_id(struct expr_parse_ctx *ctx, const char *id,
bool expr__subset_of_ids(struct expr_parse_ctx *haystack,
struct expr_parse_ctx *needles)
{
- struct hashmap_entry *cur;
- size_t bkt;
- struct expr_id_data *data;
+ const char *key;
+ struct expr_id_data *val, *data;
- hashmap__for_each_entry(needles->ids, cur, bkt) {
- if (expr__get_id(haystack, cur->pkey, &data))
+ hashmap__for_each(needles->ids, key, val) {
+ if (expr__get_id(haystack, key, &data))
return false;
}
return true;
@@ -285,10 +284,10 @@ int expr__resolve_id(struct expr_parse_ctx *ctx, const char *id,
void expr__del_id(struct expr_parse_ctx *ctx, const char *id)
{
struct expr_id_data *old_val = NULL;
- char *old_key = NULL;
+ const char *old_key = NULL;
hashmap__delete(ctx->ids, id, &old_key, &old_val);
- free(old_key);
+ free((char *)old_key);
free(old_val);
}
@@ -300,7 +299,7 @@ struct expr_parse_ctx *expr__ctx_new(void)
if (!ctx)
return NULL;
- ctx->ids = hashmap__new(key_hash, key_equal, NULL);
+ ctx->ids = expr_ids_map__new(key_hash, key_equal, NULL);
if (!ctx->ids) {
free(ctx);
return NULL;
@@ -311,30 +310,30 @@ struct expr_parse_ctx *expr__ctx_new(void)
void expr__ctx_clear(struct expr_parse_ctx *ctx)
{
- struct hashmap_entry *cur;
- size_t bkt;
+ const char *key;
+ struct expr_id_data *val;
- hashmap__for_each_entry(ctx->ids, cur, bkt) {
- zfree(&cur->pkey);
- zfree(&cur->pvalue);
+ hashmap__for_each(ctx->ids, key, val) {
+ free((char *)key);
+ free(val);
}
hashmap__clear(ctx->ids);
}
void expr__ctx_free(struct expr_parse_ctx *ctx)
{
- struct hashmap_entry *cur;
- size_t bkt;
+ const char *key;
+ struct expr_id_data *val;
if (!ctx)
return;
zfree(&ctx->sctx.user_requested_cpu_list);
- hashmap__for_each_entry(ctx->ids, cur, bkt) {
- zfree(&cur->pkey);
- zfree(&cur->pvalue);
+ hashmap__for_each(ctx->ids, key, val) {
+ free((char *)key);
+ free(val);
}
- hashmap__free(ctx->ids);
+ expr_ids_map__free(ctx->ids);
free(ctx);
}
@@ -438,7 +437,7 @@ double expr__has_event(const struct expr_parse_ctx *ctx, bool compute_ids, const
struct evlist *tmp;
double ret;
- if (hashmap__find(ctx->ids, id, /*value=*/NULL))
+ if (hashmap__find(ctx->ids, id, /*value_ptr=*/NULL))
return 1.0;
if (!compute_ids)
diff --git a/tools/perf/util/expr.h b/tools/perf/util/expr.h
index ed12e4007d2d..57edcb3f70ea 100644
--- a/tools/perf/util/expr.h
+++ b/tools/perf/util/expr.h
@@ -2,9 +2,13 @@
#ifndef PARSE_CTX_H
#define PARSE_CTX_H 1
-struct hashmap;
+#include "hashmap.h"
+
+struct expr_id_data;
struct metric_ref;
+DECLARE_HASHMAP(expr_ids_map, const char *, struct expr_id_data *);
+
struct expr_scanner_ctx {
char *user_requested_cpu_list;
int runtime;
@@ -13,20 +17,18 @@ struct expr_scanner_ctx {
};
struct expr_parse_ctx {
- struct hashmap *ids;
+ struct expr_ids_map *ids;
struct expr_scanner_ctx sctx;
};
-struct expr_id_data;
-
-struct hashmap *ids__new(void);
-void ids__free(struct hashmap *ids);
-int ids__insert(struct hashmap *ids, const char *id);
+struct expr_ids_map *ids__new(void);
+void ids__free(struct expr_ids_map *ids);
+int ids__insert(struct expr_ids_map *ids, const char *id);
/*
* Union two sets of ids (hashmaps) and construct a third, freeing ids1 and
* ids2.
*/
-struct hashmap *ids__union(struct hashmap *ids1, struct hashmap *ids2);
+struct expr_ids_map *ids__union(struct expr_ids_map *ids1, struct expr_ids_map *ids2);
struct expr_parse_ctx *expr__ctx_new(void);
void expr__ctx_clear(struct expr_parse_ctx *ctx);
diff --git a/tools/perf/util/expr.y b/tools/perf/util/expr.y
index e20f649354cf..d39ba27d75bc 100644
--- a/tools/perf/util/expr.y
+++ b/tools/perf/util/expr.y
@@ -29,7 +29,7 @@ int expr_lex(YYSTYPE * yylval_param , void *yyscanner);
* When creating ids, holds the working set of event ids. NULL
* implies the set is empty.
*/
- struct hashmap *ids;
+ struct expr_ids_map *ids;
/*
* The metric value. When not creating ids this is the value
* read from a counter, a constant or some computed value. When
diff --git a/tools/perf/util/metricgroup.c b/tools/perf/util/metricgroup.c
index 779fe56fe8d0..41887fad4968 100644
--- a/tools/perf/util/metricgroup.c
+++ b/tools/perf/util/metricgroup.c
@@ -289,7 +289,7 @@ static bool contains_metric_id(struct evsel **metric_events, int num_events,
* @metric_evlist: the list of perf events.
* @out_metric_events: holds the created metric events array.
*/
-static int setup_metric_events(const char *pmu, struct hashmap *ids,
+static int setup_metric_events(const char *pmu, struct expr_ids_map *ids,
struct evlist *metric_evlist,
struct evsel ***out_metric_events)
{
@@ -563,16 +563,16 @@ static int metricgroup__build_event_string(struct strbuf *events,
const char *modifier,
bool group_events)
{
- struct hashmap_entry *cur;
- size_t bkt;
+ const char *id;
+ struct expr_id_data *val;
bool no_group = true, has_tool_events = false;
bool tool_events[TOOL_PMU__EVENT_MAX] = {false};
int ret = 0;
#define RETURN_IF_NON_ZERO(x) do { if (x) return x; } while (0)
- hashmap__for_each_entry(ctx->ids, cur, bkt) {
- const char *sep, *rsep, *id = cur->pkey;
+ hashmap__for_each(ctx->ids, id, val) {
+ const char *sep, *rsep;
enum tool_pmu_event ev;
pr_debug("found event %s\n", id);
@@ -748,8 +748,8 @@ static int resolve_metric(struct list_head *metric_list,
const struct visited_metric *visited,
const struct pmu_metrics_table *table)
{
- struct hashmap_entry *cur;
- size_t bkt;
+ const char *key;
+ struct expr_id_data *val;
struct to_resolve {
/* The metric to resolve. */
struct pmu_metric pm;
@@ -765,10 +765,10 @@ static int resolve_metric(struct list_head *metric_list,
* Iterate all the parsed IDs and if there's a matching metric and it to
* the pending array.
*/
- hashmap__for_each_entry(root_metric->pctx->ids, cur, bkt) {
+ hashmap__for_each(root_metric->pctx->ids, key, val) {
struct pmu_metric pm;
- if (pmu_metrics_table__find_metric(table, pmu, cur->pkey,
+ if (pmu_metrics_table__find_metric(table, pmu, key,
metricgroup__find_metric_callback,
&pm) != PMU_METRICS__NOT_FOUND) {
pending = realloc(pending,
@@ -777,7 +777,7 @@ static int resolve_metric(struct list_head *metric_list,
return -ENOMEM;
memcpy(&pending[pending_cnt].pm, &pm, sizeof(pm));
- pending[pending_cnt].key = cur->pkey;
+ pending[pending_cnt].key = key;
pending_cnt++;
}
}
@@ -1229,8 +1229,8 @@ static void find_tool_events(const struct list_head *metric_list,
static int build_combined_expr_ctx(const struct list_head *metric_list,
struct expr_parse_ctx **combined)
{
- struct hashmap_entry *cur;
- size_t bkt;
+ const char *key;
+ struct expr_id_data *val;
struct metric *m;
char *dup;
int ret;
@@ -1241,8 +1241,8 @@ static int build_combined_expr_ctx(const struct list_head *metric_list,
list_for_each_entry(m, metric_list, nd) {
if (!m->group_events && !m->modifier) {
- hashmap__for_each_entry(m->pctx->ids, cur, bkt) {
- dup = strdup(cur->pkey);
+ hashmap__for_each(m->pctx->ids, key, val) {
+ dup = strdup(key);
if (!dup) {
ret = -ENOMEM;
goto err_out;
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 16/26] perf pmu: Use generic hashmap for pmu aliases
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (14 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 15/26] perf expr: Use generic hashmap for expr_ids Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 17/26] perf hwmon_pmu: Use generic hashmap for hwmon events Ian Rogers
` (9 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert perf_pmu::aliases in util/pmu.{c,h} to
DECLARE_HASHMAP(pmu_aliases_map, const char *, struct perf_pmu_alias *)
and replace hashmap__for_each_entry() loops with hashmap__for_each().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/pmu.c | 30 ++++++++++++++----------------
tools/perf/util/pmu.h | 7 +++++--
2 files changed, 19 insertions(+), 18 deletions(-)
diff --git a/tools/perf/util/pmu.c b/tools/perf/util/pmu.c
index a63fa0d9cfa5..cabc14260719 100644
--- a/tools/perf/util/pmu.c
+++ b/tools/perf/util/pmu.c
@@ -425,16 +425,16 @@ static void perf_pmu_free_alias(struct perf_pmu_alias *alias)
static void perf_pmu__del_aliases(struct perf_pmu *pmu)
{
- struct hashmap_entry *entry;
- size_t bkt;
+ const char *key;
+ struct perf_pmu_alias *val;
if (!pmu->aliases)
return;
- hashmap__for_each_entry(pmu->aliases, entry, bkt)
- perf_pmu_free_alias(entry->pvalue);
+ hashmap__for_each(pmu->aliases, key, val)
+ perf_pmu_free_alias(val);
- hashmap__free(pmu->aliases);
+ pmu_aliases_map__free(pmu->aliases);
pmu->aliases = NULL;
}
@@ -676,7 +676,7 @@ static int perf_pmu__new_alias(struct perf_pmu *pmu, const char *name,
break;
}
- hashmap__set(pmu->aliases, alias->name, alias, /*old_key=*/ NULL, &old_alias);
+ hashmap__set(pmu->aliases, alias->name, alias, /*old_key=*/NULL, &old_alias);
perf_pmu_free_alias(old_alias);
return 0;
}
@@ -1198,7 +1198,7 @@ int perf_pmu__init(struct perf_pmu *pmu, __u32 type, const char *name)
if (!pmu->name)
return -ENOMEM;
- pmu->aliases = hashmap__new(aliases__hash, aliases__equal, /*ctx=*/ NULL);
+ pmu->aliases = pmu_aliases_map__new(aliases__hash, aliases__equal, /*ctx=*/NULL);
if (!pmu->aliases)
return -ENOMEM;
@@ -1313,7 +1313,7 @@ struct perf_pmu *perf_pmu__create_placeholder_core_pmu(struct list_head *core_pm
pmu->cpus = cpu_map__online();
INIT_LIST_HEAD(&pmu->format);
- pmu->aliases = hashmap__new(aliases__hash, aliases__equal, /*ctx=*/ NULL);
+ pmu->aliases = pmu_aliases_map__new(aliases__hash, aliases__equal, /*ctx=*/NULL);
if (!pmu->aliases) {
perf_cpu_map__put(pmu->cpus);
free((char *)pmu->name);
@@ -2276,8 +2276,8 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
.event_type_desc = "Kernel PMU event",
};
int ret = 0;
- struct hashmap_entry *entry;
- size_t bkt;
+ const char *key;
+ struct perf_pmu_alias *event;
size_t size_rem;
if (perf_pmu__is_tracepoint(pmu))
@@ -2289,8 +2289,7 @@ int perf_pmu__for_each_event(struct perf_pmu *pmu, bool skip_duplicate_pmus,
pmu_aliases_parse(pmu);
pmu_add_cpu_aliases(pmu);
- hashmap__for_each_entry(pmu->aliases, entry, bkt) {
- struct perf_pmu_alias *event = entry->pvalue;
+ hashmap__for_each(pmu->aliases, key, event) {
size_t buf_used, pmu_name_len;
if (perf_pmu__is_tool(pmu) && tool_pmu__skip_event(event->name))
@@ -2809,16 +2808,15 @@ void perf_pmu__delete(struct perf_pmu *pmu)
const char *perf_pmu__name_from_config(struct perf_pmu *pmu, u64 config)
{
- struct hashmap_entry *entry;
- size_t bkt;
+ const char *key;
+ struct perf_pmu_alias *event;
if (!pmu)
return NULL;
pmu_aliases_parse(pmu);
pmu_add_cpu_aliases(pmu);
- hashmap__for_each_entry(pmu->aliases, entry, bkt) {
- struct perf_pmu_alias *event = entry->pvalue;
+ hashmap__for_each(pmu->aliases, key, event) {
struct perf_event_attr attr = {.config = 0,};
int ret = perf_pmu__parse_terms_to_attr(pmu, event->terms, &attr);
diff --git a/tools/perf/util/pmu.h b/tools/perf/util/pmu.h
index 2c8916ad073e..928aee2486e7 100644
--- a/tools/perf/util/pmu.h
+++ b/tools/perf/util/pmu.h
@@ -10,15 +10,18 @@
#include <stdio.h>
#include "parse-events.h"
#include "pmu-events/pmu-events.h"
+#include "hashmap.h"
#include "map_symbol.h"
#include "mem-events.h"
struct evsel_config_term;
-struct hashmap;
struct perf_cpu_map;
struct perf_env;
+struct perf_pmu_alias;
struct print_callbacks;
+DECLARE_HASHMAP(pmu_aliases_map, const char *, struct perf_pmu_alias *);
+
enum {
PERF_PMU_FORMAT_VALUE_CONFIG,
PERF_PMU_FORMAT_VALUE_CONFIG1,
@@ -143,7 +146,7 @@ struct perf_pmu {
* event read from <sysfs>/bus/event_source/devices/<name>/events/ or
* from json events in pmu-events.c.
*/
- struct hashmap *aliases;
+ struct pmu_aliases_map *aliases;
/**
* @events_table: The events table for json events in pmu-events.c.
*/
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 17/26] perf hwmon_pmu: Use generic hashmap for hwmon events
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (15 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 16/26] perf pmu: Use generic hashmap for pmu aliases Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 18/26] perf s390: Use generic hashmap for counter name cache Ian Rogers
` (8 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert hwmon_pmu::events in util/hwmon_pmu.c to
DECLARE_HASHMAP(hwmon_pmu_events_map, long, struct hwmon_pmu_event_value *)
and use hashmap__for_each{,_safe}().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/hwmon_pmu.c | 57 +++++++++++++++++--------------------
1 file changed, 26 insertions(+), 31 deletions(-)
diff --git a/tools/perf/util/hwmon_pmu.c b/tools/perf/util/hwmon_pmu.c
index c150fc0d7bd3..95049f5fead3 100644
--- a/tools/perf/util/hwmon_pmu.c
+++ b/tools/perf/util/hwmon_pmu.c
@@ -101,9 +101,12 @@ static const char *const hwmon_units[HWMON_TYPE_MAX] = {
"'C", /* temp */
};
+struct hwmon_pmu_event_value;
+DECLARE_HASHMAP(hwmon_pmu_events_map, long, struct hwmon_pmu_event_value *);
+
struct hwmon_pmu {
struct perf_pmu pmu;
- struct hashmap events;
+ struct hwmon_pmu_events_map events;
char *hwmon_dir;
};
@@ -237,8 +240,8 @@ static void fix_name(char *p)
static int hwmon_pmu__read_events(struct hwmon_pmu *pmu)
{
int err = 0;
- struct hashmap_entry *cur, *tmp;
- size_t bkt;
+ long key_val;
+ struct hwmon_pmu_event_value *value;
struct io_dirent64 *ent;
struct io_dir dir;
@@ -257,7 +260,6 @@ static int hwmon_pmu__read_events(struct hwmon_pmu *pmu)
enum hwmon_item item;
bool alarm;
union hwmon_pmu_event_key key = { .type_and_num = 0 };
- struct hwmon_pmu_event_value *value;
if (ent->d_type != DT_REG)
continue;
@@ -324,16 +326,15 @@ static int hwmon_pmu__read_events(struct hwmon_pmu *pmu)
if (hashmap__size(&pmu->events) == 0)
pr_debug2("hwmon_pmu: %s has no events\n", pmu->pmu.name);
- hashmap__for_each_entry_safe((&pmu->events), cur, tmp, bkt) {
+ hashmap__for_each_safe(&pmu->events, key_val, value) {
union hwmon_pmu_event_key key = {
- .type_and_num = cur->key,
+ .type_and_num = key_val,
};
- struct hwmon_pmu_event_value *value = cur->pvalue;
if (!test_bit(HWMON_ITEM_INPUT, value->items)) {
pr_debug("hwmon_pmu: %s removing event '%s%d' that has no input file\n",
pmu->pmu.name, hwmon_type_strs[key.type], key.num);
- hashmap__delete(&pmu->events, key.type_and_num, &key, &value);
+ hashmap__delete(&pmu->events, key_val, NULL, NULL);
zfree(&value->label);
zfree(&value->name);
free(value);
@@ -397,12 +398,10 @@ struct perf_pmu *hwmon_pmu__new(struct list_head *pmus, const char *hwmon_dir,
void hwmon_pmu__exit(struct perf_pmu *pmu)
{
struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
- struct hashmap_entry *cur, *tmp;
- size_t bkt;
-
- hashmap__for_each_entry_safe((&hwm->events), cur, tmp, bkt) {
- struct hwmon_pmu_event_value *value = cur->pvalue;
+ long key;
+ struct hwmon_pmu_event_value *value;
+ hashmap__for_each_safe(&hwm->events, key, value) {
zfree(&value->label);
zfree(&value->name);
free(value);
@@ -463,13 +462,13 @@ static size_t hwmon_pmu__describe_items(struct hwmon_pmu *hwm, char *out_buf, si
int hwmon_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callback cb)
{
struct hwmon_pmu *hwm = container_of(pmu, struct hwmon_pmu, pmu);
- struct hashmap_entry *cur;
- size_t bkt;
+ long key_val;
+ struct hwmon_pmu_event_value *value;
if (hwmon_pmu__read_events(hwm))
return false;
- hashmap__for_each_entry((&hwm->events), cur, bkt) {
+ hashmap__for_each(&hwm->events, key_val, value) {
static const char *const hwmon_scale_units[HWMON_TYPE_MAX] = {
NULL,
"0.001V", /* cpu */
@@ -500,9 +499,8 @@ int hwmon_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callb
char desc_buf[256];
char encoding_buf[128];
union hwmon_pmu_event_key key = {
- .type_and_num = cur->key,
+ .type_and_num = key_val,
};
- struct hwmon_pmu_event_value *value = cur->pvalue;
struct pmu_event_info info = {
.pmu = pmu,
.name = value->name,
@@ -537,7 +535,7 @@ int hwmon_pmu__for_each_event(struct perf_pmu *pmu, void *state, pmu_event_callb
key, value->alarm_items, /*is_alarm=*/true);
snprintf(encoding_buf, sizeof(encoding_buf), "%s/config=0x%lx/",
- pmu->name, cur->key);
+ pmu->name, key_val);
ret = cb(state, &info);
if (ret)
@@ -560,8 +558,8 @@ bool hwmon_pmu__have_event(struct perf_pmu *pmu, const char *name)
enum hwmon_type type;
int number;
union hwmon_pmu_event_key key = { .type_and_num = 0 };
- struct hashmap_entry *cur;
- size_t bkt;
+ long key_val;
+ struct hwmon_pmu_event_value *value;
if (!parse_hwmon_filename(name, &type, &number, /*item=*/NULL, /*is_alarm=*/NULL))
return false;
@@ -571,15 +569,13 @@ bool hwmon_pmu__have_event(struct perf_pmu *pmu, const char *name)
key.type = type;
key.num = number;
- if (hashmap__find(&hwm->events, key.type_and_num, /*value=*/NULL))
+ if (hashmap__find(&hwm->events, key.type_and_num, /*value_ptr=*/NULL))
return true;
if (key.num != -1)
return false;
/* Item is of form <type>_ which means we should match <type>_<label>. */
- hashmap__for_each_entry((&hwm->events), cur, bkt) {
- struct hwmon_pmu_event_value *value = cur->pvalue;
-
- key.type_and_num = cur->key;
+ hashmap__for_each(&hwm->events, key_val, value) {
+ key.type_and_num = key_val;
if (key.type == type && value->name && !strcasecmp(name, value->name))
return true;
}
@@ -602,15 +598,14 @@ static int hwmon_pmu__config_term(const struct hwmon_pmu *hwm,
* Item is of form <type>_ which means we should
* match <type>_<label>.
*/
- struct hashmap_entry *cur;
- size_t bkt;
+ long key_val;
+ struct hwmon_pmu_event_value *value;
attr->config = 0;
- hashmap__for_each_entry((&hwm->events), cur, bkt) {
+ hashmap__for_each(&hwm->events, key_val, value) {
union hwmon_pmu_event_key key = {
- .type_and_num = cur->key,
+ .type_and_num = key_val,
};
- struct hwmon_pmu_event_value *value = cur->pvalue;
if (key.type == type && value->name &&
!strcasecmp(term->config, value->name)) {
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 18/26] perf s390: Use generic hashmap for counter name cache
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (16 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 17/26] perf hwmon_pmu: Use generic hashmap for hwmon events Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 19/26] perf threads: Use generic hashmap for thread shards Ian Rogers
` (7 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert the counter name cache in util/s390-sample-raw.c to
DECLARE_HASHMAP(s390_counter_name_map, long, char *).
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/s390-sample-raw.c | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
diff --git a/tools/perf/util/s390-sample-raw.c b/tools/perf/util/s390-sample-raw.c
index e0fac2aa15ed..c9afc4bc4a39 100644
--- a/tools/perf/util/s390-sample-raw.c
+++ b/tools/perf/util/s390-sample-raw.c
@@ -170,6 +170,8 @@ static bool get_counter_name_hashmap_equal_fn(long key1, long key2, void *ctx __
return key1 == key2;
}
+DECLARE_HASHMAP(s390_counter_name_map, long, char *);
+
/* Scan the PMU and extract the logical name of a counter from the event. Input
* is the counter set and counter number with in the set. Construct the event
* number and use this as key. If they match return the name of this counter.
@@ -177,7 +179,7 @@ static bool get_counter_name_hashmap_equal_fn(long key1, long key2, void *ctx __
*/
static char *get_counter_name(int set, int nr, struct perf_pmu *pmu)
{
- static struct hashmap *cache;
+ static struct s390_counter_name_map *cache;
static struct perf_pmu *cache_pmu;
long cache_key = get_counterset_start(set) + nr;
struct get_counter_name_data data = {
@@ -198,7 +200,8 @@ static char *get_counter_name(int set, int nr, struct perf_pmu *pmu)
result = strdup(data.result ?: "<unknown>");
if (cache_pmu == NULL) {
- struct hashmap *tmp = hashmap__new(get_counter_name_hash_fn,
+ struct s390_counter_name_map *tmp =
+ s390_counter_name_map__new(get_counter_name_hash_fn,
get_counter_name_hashmap_equal_fn,
/*ctx=*/NULL);
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 19/26] perf threads: Use generic hashmap for thread shards
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (17 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 18/26] perf s390: Use generic hashmap for counter name cache Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 20/26] perf annotate: Use generic hashmap for sym_samples Ian Rogers
` (6 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert threads_table_entry::shard in util/threads.{c,h} to
DECLARE_HASHMAP(threads_map, pid_t, struct thread *) and replace
hashmap__for_each_entry{,_safe}() loops with hashmap__for_each{,_safe}().
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/threads.c | 16 ++++++++--------
tools/perf/util/threads.h | 4 +++-
2 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/tools/perf/util/threads.c b/tools/perf/util/threads.c
index 98cf7edaf748..c2c675d064b3 100644
--- a/tools/perf/util/threads.c
+++ b/tools/perf/util/threads.c
@@ -136,15 +136,15 @@ void threads__remove_all_threads(struct threads *threads)
{
for (int i = 0; i < THREADS__TABLE_SIZE; i++) {
struct threads_table_entry *table = &threads->table[i];
- struct hashmap_entry *cur, *tmp;
- size_t bkt;
+ pid_t tid;
+ struct thread *val;
down_write(&table->lock);
__threads_table_entry__set_last_match(table, NULL);
- hashmap__for_each_entry_safe(&table->shard, cur, tmp, bkt) {
+ hashmap__for_each_safe(&table->shard, tid, val) {
struct thread *old_value = NULL;
- if (hashmap__delete(&table->shard, cur->key, /*old_key=*/NULL, &old_value))
+ if (hashmap__delete(&table->shard, tid, /*old_key=*/NULL, &old_value))
thread__put(old_value);
}
up_write(&table->lock);
@@ -171,12 +171,12 @@ int threads__for_each_thread(struct threads *threads,
{
for (int i = 0; i < THREADS__TABLE_SIZE; i++) {
struct threads_table_entry *table = &threads->table[i];
- struct hashmap_entry *cur;
- size_t bkt;
+ pid_t tid;
+ struct thread *th;
down_read(&table->lock);
- hashmap__for_each_entry(&table->shard, cur, bkt) {
- int rc = fn((struct thread *)cur->pvalue, data);
+ hashmap__for_each(&table->shard, tid, th) {
+ int rc = fn(th, data);
if (rc != 0) {
up_read(&table->lock);
diff --git a/tools/perf/util/threads.h b/tools/perf/util/threads.h
index da68d2223f18..059d7a617e63 100644
--- a/tools/perf/util/threads.h
+++ b/tools/perf/util/threads.h
@@ -10,9 +10,11 @@ struct thread;
#define THREADS__TABLE_BITS 3
#define THREADS__TABLE_SIZE (1 << THREADS__TABLE_BITS)
+DECLARE_HASHMAP(threads_map, pid_t, struct thread *);
+
struct threads_table_entry {
/* Key is tid, value is struct thread. */
- struct hashmap shard;
+ struct threads_map shard;
struct rw_semaphore lock;
struct thread *last_match;
};
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 20/26] perf annotate: Use generic hashmap for sym_samples
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (18 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 19/26] perf threads: Use generic hashmap for thread shards Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 21/26] perf annotate: Use generic hashmap for browser type_hash Ian Rogers
` (5 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert annotated_source::samples in util/annotate.{c,h} to
DECLARE_HASHMAP(sym_samples_map, long, struct sym_hist_entry *) and use
hashmap__for_each() when freeing histogram entries.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/annotate.c | 20 ++++++++++----------
tools/perf/util/annotate.h | 5 ++++-
2 files changed, 14 insertions(+), 11 deletions(-)
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index d11add6424dc..c6325040452f 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -112,16 +112,16 @@ static struct annotated_source *annotated_source__new(void)
static __maybe_unused void annotated_source__delete(struct annotated_source *src)
{
- struct hashmap_entry *cur;
- size_t bkt;
+ long key;
+ struct sym_hist_entry *val;
if (src == NULL)
return;
if (src->samples) {
- hashmap__for_each_entry(src->samples, cur, bkt)
- zfree(&cur->pvalue);
- hashmap__free(src->samples);
+ hashmap__for_each(src->samples, key, val)
+ free(val);
+ sym_samples_map__free(src->samples);
}
zfree(&src->histograms);
free(src);
@@ -136,7 +136,7 @@ static int annotated_source__alloc_histograms(struct annotated_source *src,
if (src->histograms == NULL)
return -1;
- src->samples = hashmap__new(sym_hist_hash, sym_hist_equal, NULL);
+ src->samples = sym_samples_map__new(sym_hist_hash, sym_hist_equal, NULL);
if (!src->samples)
zfree(&src->histograms);
@@ -163,11 +163,11 @@ void symbol__annotate_zero_histograms(struct symbol *sym)
memset(notes->src->histograms, 0,
notes->src->nr_histograms * sizeof(*notes->src->histograms));
if (notes->src->samples) {
- struct hashmap_entry *cur;
- size_t bkt;
+ long key;
+ struct sym_hist_entry *val;
- hashmap__for_each_entry(notes->src->samples, cur, bkt)
- zfree(&cur->pvalue);
+ hashmap__for_each(notes->src->samples, key, val)
+ free(val);
hashmap__clear(notes->src->samples);
}
}
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 1b47c1156b9a..495c8602864c 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -312,10 +312,13 @@ struct cyc_hist {
* presented. It is deallocated right after symbol__{tui,tty,etc}_annotate
* returns.
*/
+struct sym_hist_entry;
+DECLARE_HASHMAP(sym_samples_map, long, struct sym_hist_entry *);
+
struct annotated_source {
struct list_head source;
struct sym_hist *histograms;
- struct hashmap *samples;
+ struct sym_samples_map *samples;
int nr_histograms;
int nr_events;
int nr_entries;
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 21/26] perf annotate: Use generic hashmap for browser type_hash
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (19 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 20/26] perf annotate: Use generic hashmap for sym_samples Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 22/26] perf annotate-data: Use generic hashmap for type_hist samples Ian Rogers
` (4 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert annotate_browser::type_hash and annotation_print_data::type_hash
in util/annotate.{c,h} and ui/browsers/annotate.c to
DECLARE_HASHMAP(type_hash_map, long, struct type_hash_entry *).
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/ui/browsers/annotate.c | 18 +++++++++---------
tools/perf/util/annotate.c | 5 -----
tools/perf/util/annotate.h | 8 +++++++-
3 files changed, 16 insertions(+), 15 deletions(-)
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c
index 8b0c933caf2e..7899c3e2b07a 100644
--- a/tools/perf/ui/browsers/annotate.c
+++ b/tools/perf/ui/browsers/annotate.c
@@ -39,7 +39,7 @@ struct annotate_browser {
struct hist_entry *he;
struct debuginfo *dbg;
struct evsel *evsel;
- struct hashmap *type_hash;
+ struct type_hash_map *type_hash;
bool searching_backwards;
char search_bf[128];
};
@@ -1131,8 +1131,8 @@ static int annotate_browser__run(struct annotate_browser *browser,
if (browser->dbg == NULL)
browser->dbg = dso__debuginfo(map__dso(ms->map));
if (browser->type_hash == NULL) {
- browser->type_hash = hashmap__new(type_hash, type_equal,
- /*ctx=*/NULL);
+ browser->type_hash = type_hash_map__new(type_hash, type_equal,
+ /*ctx=*/NULL);
}
annotate_browser__show(browser, title, help);
annotate_browser__debuginfo_warning(browser);
@@ -1233,7 +1233,7 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
if (annotate_opts.code_with_type) {
browser.dbg = dso__debuginfo(dso);
- browser.type_hash = hashmap__new(type_hash, type_equal, /*ctx=*/NULL);
+ browser.type_hash = type_hash_map__new(type_hash, type_equal, /*ctx=*/NULL);
}
browser.b.width = notes->src->widths.max_line_len +
@@ -1265,12 +1265,12 @@ int __hist_entry__tui_annotate(struct hist_entry *he, struct map_symbol *ms,
debuginfo__delete(browser.dbg);
if (browser.type_hash) {
- struct hashmap_entry *cur;
- size_t bkt;
+ long key;
+ struct type_hash_entry *val;
- hashmap__for_each_entry(browser.type_hash, cur, bkt)
- zfree(&cur->pvalue);
- hashmap__free(browser.type_hash);
+ hashmap__for_each(browser.type_hash, key, val)
+ free(val);
+ type_hash_map__free(browser.type_hash);
}
if (not_annotated && !notes->src->tried_source)
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index c6325040452f..ee47dcdc1f18 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -2075,11 +2075,6 @@ int annotation_br_cntr_entry(char **str, int br_cntr_nr,
return -ENOMEM;
}
-struct type_hash_entry {
- struct annotated_data_type *type;
- int offset;
-};
-
static int disasm_line__snprint_type_info(struct disasm_line *dl,
char *buf, int len,
struct annotation_print_data *apd)
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h
index 495c8602864c..2757fb07d4ec 100644
--- a/tools/perf/util/annotate.h
+++ b/tools/perf/util/annotate.h
@@ -202,13 +202,19 @@ struct annotation_write_ops {
void (*write_graph)(void *obj, int graph);
};
+struct type_hash_entry {
+ struct annotated_data_type *type;
+ int offset;
+};
+DECLARE_HASHMAP(type_hash_map, long, struct type_hash_entry *);
+
struct annotation_print_data {
struct hist_entry *he;
struct evsel *evsel;
const struct arch *arch;
struct debuginfo *dbg;
/* save data type info keyed by al->offset */
- struct hashmap *type_hash;
+ struct type_hash_map *type_hash;
/* It'll be set in hist_entry__annotate_printf() */
int addr_fmt_width;
};
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 22/26] perf annotate-data: Use generic hashmap for type_hist samples
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (20 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 21/26] perf annotate: Use generic hashmap for browser type_hash Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 23/26] perf inject: Use generic hashmap for aslr remap_addresses Ian Rogers
` (3 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert type_hist::samples in util/annotate-data.{c,h} and
ui/browsers/annotate-data.c to
DECLARE_HASHMAP(type_hist_map, int, struct type_hist_entry *).
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/annotate-data.c | 10 +++++-----
tools/perf/util/annotate-data.h | 5 ++++-
2 files changed, 9 insertions(+), 6 deletions(-)
diff --git a/tools/perf/util/annotate-data.c b/tools/perf/util/annotate-data.c
index 2332d465f975..dabbbb34fcd8 100644
--- a/tools/perf/util/annotate-data.c
+++ b/tools/perf/util/annotate-data.c
@@ -2059,12 +2059,12 @@ static int alloc_data_type_histograms(struct annotated_data_type *adt, int nr_en
static void delete_data_type_histograms(struct annotated_data_type *adt)
{
for (int i = 0; i < adt->nr_histograms; i++) {
- struct hashmap *map = &adt->histograms[i].samples;
- struct hashmap_entry *pos, *tmp;
- size_t bkt;
+ struct type_hist_map *map = &adt->histograms[i].samples;
+ int offset;
+ struct type_hist_entry *entry;
- hashmap__for_each_entry_safe(map, pos, tmp, bkt)
- free(pos->pvalue);
+ hashmap__for_each_safe(map, offset, entry)
+ free(entry);
hashmap__clear(map);
}
diff --git a/tools/perf/util/annotate-data.h b/tools/perf/util/annotate-data.h
index 184262c73562..b5b780d26004 100644
--- a/tools/perf/util/annotate-data.h
+++ b/tools/perf/util/annotate-data.h
@@ -8,6 +8,7 @@
#include <linux/types.h>
#include "dwarf-regs.h"
#include "annotate.h"
+#include "hashmap.h"
#ifdef HAVE_LIBDW_SUPPORT
#include "debuginfo.h"
@@ -81,6 +82,8 @@ struct type_hist_entry {
u64 period;
};
+DECLARE_HASHMAP(type_hist_map, int, struct type_hist_entry *);
+
/**
* struct type_hist - Type histogram for each event
* @nr_samples: Total number of samples in this data type
@@ -90,7 +93,7 @@ struct type_hist_entry {
struct type_hist {
u64 nr_samples;
u64 period;
- struct hashmap samples;
+ struct type_hist_map samples;
};
/**
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 23/26] perf inject: Use generic hashmap for aslr remap_addresses
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (21 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 22/26] perf annotate-data: Use generic hashmap for type_hist samples Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 24/26] perf inject: Use generic hashmap for aslr top_addresses Ian Rogers
` (2 subsequent siblings)
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert aslr_tool::remap_addresses in util/aslr.c to
DECLARE_HASHMAP(remap_addresses_map, struct remap_addresses_key *, u64 *).
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/aslr.c | 20 ++++++++++++--------
1 file changed, 12 insertions(+), 8 deletions(-)
diff --git a/tools/perf/util/aslr.c b/tools/perf/util/aslr.c
index e365f807b021..068e0250b151 100644
--- a/tools/perf/util/aslr.c
+++ b/tools/perf/util/aslr.c
@@ -6,6 +6,7 @@
#include "event.h"
#include "evsel.h"
#include "evlist.h"
+#include "hashmap.h"
#include "machine.h"
#include "map.h"
#include "thread.h"
@@ -69,6 +70,9 @@ static bool evsel_equal(long key1, long key2, void *ctx __maybe_unused)
struct process_top_address {
u64 remapped_max;
};
+
+DECLARE_HASHMAP(remap_addresses_map, struct remap_addresses_key *, u64 *);
+
struct aslr_tool {
/** @tool: The tool implemented here and a pointer to a delegate to process the data. */
struct delegate_tool tool;
@@ -77,7 +81,7 @@ struct aslr_tool {
/** @event_copy: Buffer used to create an event to pass to the delegate. */
char event_copy[PERF_SAMPLE_MAX_SIZE] __aligned(8);
/** @remap_addresses: mapping from remap_addresses_key to remapped address. */
- struct hashmap remap_addresses;
+ struct remap_addresses_map remap_addresses;
/** @top_addresses: mapping from process to max remapped address. */
struct hashmap top_addresses;
/**
@@ -1331,6 +1335,8 @@ void aslr_tool__delete(struct perf_tool *tool)
{
struct delegate_tool *del_tool;
struct aslr_tool *aslr;
+ struct remap_addresses_key *rkey;
+ u64 *rval;
struct hashmap_entry *cur;
size_t bkt;
struct rb_node *nd;
@@ -1341,13 +1347,11 @@ void aslr_tool__delete(struct perf_tool *tool)
del_tool = container_of(tool, struct delegate_tool, tool);
aslr = container_of(del_tool, struct aslr_tool, tool);
- hashmap__for_each_entry(&aslr->remap_addresses, cur, bkt) {
- struct remap_addresses_key *key = (struct remap_addresses_key *)cur->pkey;
-
- if (key)
- dso__put(key->dso);
- zfree(&cur->pkey);
- zfree(&cur->pvalue);
+ hashmap__for_each(&aslr->remap_addresses, rkey, rval) {
+ if (rkey)
+ dso__put(rkey->dso);
+ free(rkey);
+ free(rval);
}
hashmap__for_each_entry(&aslr->top_addresses, cur, bkt) {
zfree(&cur->pkey);
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 24/26] perf inject: Use generic hashmap for aslr top_addresses
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (22 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 23/26] perf inject: Use generic hashmap for aslr remap_addresses Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 25/26] perf inject: Use generic hashmap for aslr evsel_orig_attrs Ian Rogers
2026-11-13 21:48 ` [PATCH v1 26/26] perf hashmap: Remove untyped struct hashmap API Ian Rogers
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert aslr_tool::top_addresses in util/aslr.c to
DECLARE_HASHMAP(top_addresses_map, struct top_addresses_key *,
struct process_top_address *).
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/aslr.c | 16 ++++++++++------
1 file changed, 10 insertions(+), 6 deletions(-)
diff --git a/tools/perf/util/aslr.c b/tools/perf/util/aslr.c
index 068e0250b151..f9c961775e1d 100644
--- a/tools/perf/util/aslr.c
+++ b/tools/perf/util/aslr.c
@@ -71,7 +71,10 @@ struct process_top_address {
u64 remapped_max;
};
+struct top_addresses_key;
+
DECLARE_HASHMAP(remap_addresses_map, struct remap_addresses_key *, u64 *);
+DECLARE_HASHMAP(top_addresses_map, struct top_addresses_key *, struct process_top_address *);
struct aslr_tool {
/** @tool: The tool implemented here and a pointer to a delegate to process the data. */
@@ -83,7 +86,7 @@ struct aslr_tool {
/** @remap_addresses: mapping from remap_addresses_key to remapped address. */
struct remap_addresses_map remap_addresses;
/** @top_addresses: mapping from process to max remapped address. */
- struct hashmap top_addresses;
+ struct top_addresses_map top_addresses;
/**
* @evsel_orig_attrs: mapping from evsel pointer to its original
* unstripped sample_type and registers bitmasks.
@@ -383,8 +386,7 @@ static u64 aslr_tool__findnew_mapping(struct aslr_tool *aslr,
} else {
*tk = top_addr_key;
top_val->remapped_max = remap_addr + len;
- err = hashmap__insert(&aslr->top_addresses, tk, top_val,
- HASHMAP_ADD, NULL, NULL);
+ err = hashmap__add(&aslr->top_addresses, tk, top_val);
}
if (err) {
errno = -err;
@@ -1337,6 +1339,8 @@ void aslr_tool__delete(struct perf_tool *tool)
struct aslr_tool *aslr;
struct remap_addresses_key *rkey;
u64 *rval;
+ struct top_addresses_key *tkey;
+ struct process_top_address *tval;
struct hashmap_entry *cur;
size_t bkt;
struct rb_node *nd;
@@ -1353,9 +1357,9 @@ void aslr_tool__delete(struct perf_tool *tool)
free(rkey);
free(rval);
}
- hashmap__for_each_entry(&aslr->top_addresses, cur, bkt) {
- zfree(&cur->pkey);
- zfree(&cur->pvalue);
+ hashmap__for_each(&aslr->top_addresses, tkey, tval) {
+ free(tkey);
+ free(tval);
}
hashmap__for_each_entry(&aslr->evsel_orig_attrs, cur, bkt) {
zfree(&cur->pvalue);
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 25/26] perf inject: Use generic hashmap for aslr evsel_orig_attrs
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (23 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 24/26] perf inject: Use generic hashmap for aslr top_addresses Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
2026-11-13 21:48 ` [PATCH v1 26/26] perf hashmap: Remove untyped struct hashmap API Ian Rogers
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Convert aslr_tool::evsel_orig_attrs in util/aslr.c to
DECLARE_HASHMAP(evsel_orig_attrs_map, struct evsel *,
struct aslr_evsel_priv *).
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/aslr.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/tools/perf/util/aslr.c b/tools/perf/util/aslr.c
index f9c961775e1d..8aaa685f4203 100644
--- a/tools/perf/util/aslr.c
+++ b/tools/perf/util/aslr.c
@@ -75,6 +75,7 @@ struct top_addresses_key;
DECLARE_HASHMAP(remap_addresses_map, struct remap_addresses_key *, u64 *);
DECLARE_HASHMAP(top_addresses_map, struct top_addresses_key *, struct process_top_address *);
+DECLARE_HASHMAP(evsel_orig_attrs_map, struct evsel *, struct aslr_evsel_priv *);
struct aslr_tool {
/** @tool: The tool implemented here and a pointer to a delegate to process the data. */
@@ -91,7 +92,7 @@ struct aslr_tool {
* @evsel_orig_attrs: mapping from evsel pointer to its original
* unstripped sample_type and registers bitmasks.
*/
- struct hashmap evsel_orig_attrs;
+ struct evsel_orig_attrs_map evsel_orig_attrs;
};
static const pid_t kernel_pid = -1;
@@ -1341,8 +1342,8 @@ void aslr_tool__delete(struct perf_tool *tool)
u64 *rval;
struct top_addresses_key *tkey;
struct process_top_address *tval;
- struct hashmap_entry *cur;
- size_t bkt;
+ struct evsel *ekey;
+ struct aslr_evsel_priv *eval;
struct rb_node *nd;
if (!tool)
@@ -1361,9 +1362,8 @@ void aslr_tool__delete(struct perf_tool *tool)
free(tkey);
free(tval);
}
- hashmap__for_each_entry(&aslr->evsel_orig_attrs, cur, bkt) {
- zfree(&cur->pvalue);
- }
+ hashmap__for_each(&aslr->evsel_orig_attrs, ekey, eval)
+ free(eval);
hashmap__clear(&aslr->remap_addresses);
hashmap__clear(&aslr->top_addresses);
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread* [PATCH v1 26/26] perf hashmap: Remove untyped struct hashmap API
2026-11-13 21:48 [PATCH v1 00/26] perf hashmap: Fix undefined behavior, remove ERR_PTR, and add type-safe generic hashmaps Ian Rogers
` (24 preceding siblings ...)
2026-11-13 21:48 ` [PATCH v1 25/26] perf inject: Use generic hashmap for aslr evsel_orig_attrs Ian Rogers
@ 2026-11-13 21:48 ` Ian Rogers
25 siblings, 0 replies; 27+ messages in thread
From: Ian Rogers @ 2026-11-13 21:48 UTC (permalink / raw)
To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo, Namhyung Kim
Cc: Jiri Olsa, Ian Rogers, Adrian Hunter, James Clark,
linux-perf-users, linux-kernel
Now that all hashmap users in tools/perf have been migrated to
DECLARE_HASHMAP(), remove the legacy untyped struct hashmap wrapper,
hashmap__new(), __hashmap__new(), __hashmap__free(), the
untyped-hashmap compatibility branches in hashmap__insert/find/delete,
and the unused hashmap__for_each_entry* macros from util/hashmap.h, and
rename struct hashmap_entry to struct __hashmap_entry.
Signed-off-by: Ian Rogers <irogers@google.com>
---
tools/perf/util/hashmap.c | 75 +++++++----------
tools/perf/util/hashmap.h | 173 +++++++-------------------------------
2 files changed, 62 insertions(+), 186 deletions(-)
diff --git a/tools/perf/util/hashmap.c b/tools/perf/util/hashmap.c
index f6bfaeaee9d7..946105919197 100644
--- a/tools/perf/util/hashmap.c
+++ b/tools/perf/util/hashmap.c
@@ -21,15 +21,15 @@
/* start with 4 buckets */
#define HASHMAP_MIN_CAP_BITS 2
-static void __hashmap__add_entry(struct hashmap_entry **pprev,
- struct hashmap_entry *entry)
+static void __hashmap__add_entry(struct __hashmap_entry **pprev,
+ struct __hashmap_entry *entry)
{
entry->next = *pprev;
*pprev = entry;
}
-static void __hashmap__del_entry(struct hashmap_entry **pprev,
- struct hashmap_entry *entry)
+static void __hashmap__del_entry(struct __hashmap_entry **pprev,
+ struct __hashmap_entry *entry)
{
*pprev = entry->next;
entry->next = NULL;
@@ -48,42 +48,23 @@ void __hashmap__init(struct __hashmap *map, hashmap_hash_fn hash_fn,
map->sz = 0;
}
-struct __hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn,
- void *ctx)
-{
- struct __hashmap *map = malloc(sizeof(*map));
-
- if (!map) {
- errno = ENOMEM;
- return NULL;
- }
- __hashmap__init(map, hash_fn, equal_fn, ctx);
- return map;
-}
-
void __hashmap__clear(struct __hashmap *map)
{
- struct hashmap_entry *cur, *tmp;
- size_t bkt;
+ for (size_t bkt = 0; bkt < map->cap; bkt++) {
+ struct __hashmap_entry *cur = map->buckets[bkt];
+
+ while (cur) {
+ struct __hashmap_entry *next = cur->next;
- hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
- free(cur);
+ free(cur);
+ cur = next;
+ }
}
free(map->buckets);
map->buckets = NULL;
map->cap = map->cap_bits = map->sz = 0;
}
-void __hashmap__free(struct __hashmap *map)
-{
- if (!map)
- return;
-
- __hashmap__clear(map);
- free(map);
-}
-
size_t __hashmap__size(const struct __hashmap *map)
{
return map->sz;
@@ -102,10 +83,9 @@ static bool __hashmap__needs_to_grow(struct __hashmap *map)
static int __hashmap__grow(struct __hashmap *map)
{
- struct hashmap_entry **new_buckets;
- struct hashmap_entry *cur, *tmp;
+ struct __hashmap_entry **new_buckets;
size_t new_cap_bits, new_cap;
- size_t h, bkt;
+ size_t h;
new_cap_bits = map->cap_bits + 1;
if (new_cap_bits < HASHMAP_MIN_CAP_BITS)
@@ -116,9 +96,16 @@ static int __hashmap__grow(struct __hashmap *map)
if (!new_buckets)
return -ENOMEM;
- hashmap__for_each_entry_safe(map, cur, tmp, bkt) {
- h = hash_bits(map->hash_fn(cur->key, map->ctx), new_cap_bits);
- __hashmap__add_entry(&new_buckets[h], cur);
+ for (size_t bkt = 0; bkt < map->cap; bkt++) {
+ struct __hashmap_entry *cur = map->buckets[bkt];
+
+ while (cur) {
+ struct __hashmap_entry *next = cur->next;
+
+ h = hash_bits(map->hash_fn(cur->key, map->ctx), new_cap_bits);
+ __hashmap__add_entry(&new_buckets[h], cur);
+ cur = next;
+ }
}
map->cap = new_cap;
@@ -131,10 +118,10 @@ static int __hashmap__grow(struct __hashmap *map)
static bool __hashmap__find_entry(const struct __hashmap *map,
const long key, size_t hash,
- struct hashmap_entry ***pprev,
- struct hashmap_entry **entry)
+ struct __hashmap_entry ***pprev,
+ struct __hashmap_entry **entry)
{
- struct hashmap_entry *cur, **prev_ptr;
+ struct __hashmap_entry *cur, **prev_ptr;
if (!map->buckets)
return false;
@@ -157,7 +144,7 @@ int __hashmap__insert(struct __hashmap *map, long key, long value,
enum hashmap_insert_strategy strategy,
void *old_key, void *old_value)
{
- struct hashmap_entry *entry;
+ struct __hashmap_entry *entry;
size_t h;
int err;
@@ -193,7 +180,7 @@ int __hashmap__insert(struct __hashmap *map, long key, long value,
h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
}
- entry = malloc(sizeof(struct hashmap_entry));
+ entry = malloc(sizeof(struct __hashmap_entry));
if (!entry)
return -ENOMEM;
@@ -207,7 +194,7 @@ int __hashmap__insert(struct __hashmap *map, long key, long value,
bool __hashmap__find(const struct __hashmap *map, long key, void *value)
{
- struct hashmap_entry *entry;
+ struct __hashmap_entry *entry;
size_t h;
h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
@@ -222,7 +209,7 @@ bool __hashmap__find(const struct __hashmap *map, long key, void *value)
bool __hashmap__delete(struct __hashmap *map, long key,
void *old_key, void *old_value)
{
- struct hashmap_entry **pprev, *entry;
+ struct __hashmap_entry **pprev, *entry;
size_t h;
if (old_key)
diff --git a/tools/perf/util/hashmap.h b/tools/perf/util/hashmap.h
index bb85a65486be..e515443d3c70 100644
--- a/tools/perf/util/hashmap.h
+++ b/tools/perf/util/hashmap.h
@@ -92,16 +92,10 @@ typedef bool (*hashmap_equal_fn)(long key1, long key2, void *ctx);
* (e.g. bool, int, pid_t) are safe from buffer overflows, endianness bugs,
* and strict-aliasing violations.
*/
-struct hashmap_entry {
- union {
- long key;
- const void *pkey;
- };
- union {
- long value;
- void *pvalue;
- };
- struct hashmap_entry *next;
+struct __hashmap_entry {
+ long key;
+ long value;
+ struct __hashmap_entry *next;
};
struct __hashmap {
@@ -109,57 +103,19 @@ struct __hashmap {
hashmap_equal_fn equal_fn;
void *ctx;
- struct hashmap_entry **buckets;
+ struct __hashmap_entry **buckets;
size_t cap;
size_t cap_bits;
size_t sz;
};
-/* Legacy untyped hashmap, kept while migrating callers to DECLARE_HASHMAP. */
-struct hashmap {
- union {
- struct __hashmap map;
- struct {
- hashmap_hash_fn hash_fn;
- hashmap_equal_fn equal_fn;
- void *ctx;
-
- struct hashmap_entry **buckets;
- size_t cap;
- size_t cap_bits;
- size_t sz;
- };
- long *__key;
- long *__value;
- char __untyped;
- };
-};
-
void __hashmap__init(struct __hashmap *map, hashmap_hash_fn hash_fn,
hashmap_equal_fn equal_fn, void *ctx);
-struct __hashmap *__hashmap__new(hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn,
- void *ctx);
void __hashmap__clear(struct __hashmap *map);
-void __hashmap__free(struct __hashmap *map);
size_t __hashmap__size(const struct __hashmap *map);
size_t __hashmap__capacity(const struct __hashmap *map);
-static inline struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
- hashmap_equal_fn equal_fn,
- void *ctx)
-{
- struct hashmap *map = malloc(sizeof(*map));
-
- if (!map) {
- errno = ENOMEM;
- return NULL;
- }
- __hashmap__init(&map->map, hash_fn, equal_fn, ctx);
- return map;
-}
-
/**
* enum hashmap_insert_strategy - Strategy for hashmap__insert().
* @HASHMAP_ADD: Only add key/value if key doesn't exist yet; returns -EEXIST
@@ -195,8 +151,8 @@ bool __hashmap__find(const struct __hashmap *map, long key, void *value);
struct __hashmap_iter {
const struct __hashmap *map;
size_t bkt;
- struct hashmap_entry *cur;
- struct hashmap_entry *next;
+ struct __hashmap_entry *cur;
+ struct __hashmap_entry *next;
};
static inline struct __hashmap_iter
@@ -210,7 +166,7 @@ __hashmap_iter__init(const struct __hashmap *map)
};
}
-static inline struct hashmap_entry *
+static inline struct __hashmap_entry *
__hashmap_iter__advance(struct __hashmap_iter *it)
{
if (!it->map || !it->map->buckets)
@@ -232,14 +188,14 @@ __hashmap_iter__advance(struct __hashmap_iter *it)
struct __hashmap_key_iter {
const struct __hashmap *map;
long key;
- struct hashmap_entry *cur;
- struct hashmap_entry *next;
+ struct __hashmap_entry *cur;
+ struct __hashmap_entry *next;
};
static inline struct __hashmap_key_iter
__hashmap_key_iter__init(const struct __hashmap *map, long key)
{
- struct hashmap_entry *first = NULL;
+ struct __hashmap_entry *first = NULL;
if (map && map->buckets) {
size_t h = hash_bits(map->hash_fn(key, map->ctx), map->cap_bits);
@@ -254,11 +210,11 @@ __hashmap_key_iter__init(const struct __hashmap *map, long key)
};
}
-static inline struct hashmap_entry *
+static inline struct __hashmap_entry *
__hashmap_key_iter__advance(struct __hashmap_key_iter *it)
{
while (it->next) {
- struct hashmap_entry *entry = it->next;
+ struct __hashmap_entry *entry = it->next;
it->next = entry->next;
if (it->map->equal_fn(entry->key, it->key, it->map->ctx)) {
@@ -289,7 +245,6 @@ struct name { \
struct __hashmap map; \
key_type *__key; \
value_type *__value; \
- int __untyped; \
}; \
_Static_assert(sizeof(key_type) <= sizeof(long), \
#name " key_type exceeds sizeof(long)"); \
@@ -328,19 +283,6 @@ static inline __maybe_unused void name##__free(struct name *hmap) \
*(hm)->__key, (char *)NULL)) *)NULL, \
*(hm)->__key))
-#define ___hashmap__choose(hm, untyped_expr, typed_expr) \
- __builtin_choose_expr(sizeof((hm)->__untyped) == 1, \
- (untyped_expr), (typed_expr))
-
-#define ___hashmap__in_val(hm, expr) \
- ___hashmap__choose(hm, (long)(expr), (expr))
-
-#define ___hashmap__check_untyped_ptr(hm, ptr) \
- _Static_assert(sizeof((hm)->__untyped) != 1 || \
- (__builtin_constant_p((ptr)) ? (ptr) == NULL : 0) || \
- sizeof(*(ptr)) == sizeof(long), \
- #ptr " pointee should be a long-sized integer or a pointer")
-
/**
* hashmap__init() - Initialize an embedded/stack-allocated typed hashmap.
* @hmap: Pointer to a hashmap struct declared with DECLARE_HASHMAP().
@@ -397,25 +339,16 @@ do { \
__hashmap__capacity(&(hmap)->map)
#define ___hashmap__insert(hmap, key, value, strategy, old_key, old_value, \
- hm, k, v, okp, ovp, raw_ok, raw_ov, ok, ov, err) \
+ hm, k, v, okp, ovp, ok, ov, err) \
({ \
typeof(hmap) hm = (hmap); \
- typeof(*(hm)->__key) k = ___hashmap__in_val(hm, key); \
- typeof(*(hm)->__value) v = ___hashmap__in_val(hm, value); \
+ typeof(*(hm)->__key) k = (key); \
+ typeof(*(hm)->__value) v = (value); \
+ typeof((hm)->__key) okp = (old_key); \
+ typeof((hm)->__value) ovp = (old_value); \
long ok = 0, ov = 0; \
- typeof((hm)->__key) okp = \
- ___hashmap__choose(hm, (long *)NULL, (old_key)); \
- typeof((hm)->__value) ovp = \
- ___hashmap__choose(hm, (long *)NULL, (old_value)); \
- void *raw_ok = ___hashmap__choose(hm, (void *)(old_key), \
- (void *)(okp ? &ok : NULL)); \
- void *raw_ov = ___hashmap__choose(hm, (void *)(old_value), \
- (void *)(ovp ? &ov : NULL)); \
- int err; \
- ___hashmap__check_untyped_ptr(hm, old_key); \
- ___hashmap__check_untyped_ptr(hm, old_value); \
- err = __hashmap__insert(&(hm)->map, (long)k, (long)v, (strategy), \
- raw_ok, raw_ov); \
+ int err = __hashmap__insert(&(hm)->map, (long)k, (long)v, (strategy), \
+ okp ? &ok : NULL, ovp ? &ov : NULL); \
if (okp) \
*okp = (typeof(*(hm)->__key))ok; \
if (ovp) \
@@ -427,7 +360,6 @@ do { \
___hashmap__insert(hmap, key, value, strategy, old_key, old_value, \
__PASTE(__hm_, u), __PASTE(__k_, u), __PASTE(__v_, u), \
__PASTE(__okp_, u), __PASTE(__ovp_, u), \
- __PASTE(__rok_, u), __PASTE(__rov_, u), \
__PASTE(__ok_, u), __PASTE(__ov_, u), __PASTE(__err_, u))
/**
@@ -503,18 +435,13 @@ do { \
#define hashmap__append(hmap, key, value) \
hashmap__insert(hmap, key, value, HASHMAP_APPEND, NULL, NULL)
-#define ___hashmap__find(hmap, key, value_ptr, hm, k, vp, raw_vp, v, found) \
+#define ___hashmap__find(hmap, key, value_ptr, hm, k, vp, v, found) \
({ \
typeof(hmap) hm = (hmap); \
- ___hashmap__lookup_key_type(hm) k = ___hashmap__in_val(hm, key); \
+ ___hashmap__lookup_key_type(hm) k = (key); \
+ typeof((hm)->__value) vp = (value_ptr); \
long v = 0; \
- typeof((hm)->__value) vp = \
- ___hashmap__choose(hm, (long *)NULL, (value_ptr)); \
- void *raw_vp = ___hashmap__choose(hm, (void *)(value_ptr), \
- (void *)(vp ? &v : NULL)); \
- bool found; \
- ___hashmap__check_untyped_ptr(hm, value_ptr); \
- found = __hashmap__find(&(hm)->map, (long)k, raw_vp); \
+ bool found = __hashmap__find(&(hm)->map, (long)k, vp ? &v : NULL); \
if (found && vp) \
*vp = (typeof(*(hm)->__value))v; \
found; \
@@ -523,7 +450,7 @@ do { \
#define _hashmap__find(hmap, key, value_ptr, u) \
___hashmap__find(hmap, key, value_ptr, \
__PASTE(__hm_, u), __PASTE(__k_, u), __PASTE(__vp_, u), \
- __PASTE(__rvp_, u), __PASTE(__v_, u), __PASTE(__found_, u))
+ __PASTE(__v_, u), __PASTE(__found_, u))
/**
* hashmap__find() - Look up a key in a typed hashmap.
@@ -538,23 +465,15 @@ do { \
_hashmap__find(hmap, key, value_ptr, __COUNTER__)
#define ___hashmap__delete(hmap, key, old_key, old_value, \
- hm, k, okp, ovp, raw_ok, raw_ov, ok, ov, rem) \
+ hm, k, okp, ovp, ok, ov, rem) \
({ \
typeof(hmap) hm = (hmap); \
- ___hashmap__lookup_key_type(hm) k = ___hashmap__in_val(hm, key); \
+ ___hashmap__lookup_key_type(hm) k = (key); \
+ typeof((hm)->__key) okp = (old_key); \
+ typeof((hm)->__value) ovp = (old_value); \
long ok = 0, ov = 0; \
- typeof((hm)->__key) okp = \
- ___hashmap__choose(hm, (long *)NULL, (old_key)); \
- typeof((hm)->__value) ovp = \
- ___hashmap__choose(hm, (long *)NULL, (old_value)); \
- void *raw_ok = ___hashmap__choose(hm, (void *)(old_key), \
- (void *)(okp ? &ok : NULL)); \
- void *raw_ov = ___hashmap__choose(hm, (void *)(old_value), \
- (void *)(ovp ? &ov : NULL)); \
- bool rem; \
- ___hashmap__check_untyped_ptr(hm, old_key); \
- ___hashmap__check_untyped_ptr(hm, old_value); \
- rem = __hashmap__delete(&(hm)->map, (long)k, raw_ok, raw_ov); \
+ bool rem = __hashmap__delete(&(hm)->map, (long)k, \
+ okp ? &ok : NULL, ovp ? &ov : NULL); \
if (okp) \
*okp = (typeof(*(hm)->__key))ok; \
if (ovp) \
@@ -566,7 +485,6 @@ do { \
___hashmap__delete(hmap, key, old_key, old_value, \
__PASTE(__hm_, u), __PASTE(__k_, u), \
__PASTE(__okp_, u), __PASTE(__ovp_, u), \
- __PASTE(__rok_, u), __PASTE(__rov_, u), \
__PASTE(__ok_, u), __PASTE(__ov_, u), __PASTE(__rem_, u))
/**
@@ -660,33 +578,4 @@ do { \
#define hashmap__for_each_key_safe(hmap, key, val_var) \
hashmap__for_each_key(hmap, key, val_var)
-/*
- * Legacy entry iterators for unconverted struct hashmap callers.
- */
-#define hashmap__for_each_entry(map, cur, bkt) \
- for (bkt = 0; bkt < (map)->cap; bkt++) \
- for (cur = (map)->buckets[bkt]; cur; cur = cur->next)
-
-#define hashmap__for_each_entry_safe(map, cur, tmp, bkt) \
- for (bkt = 0; bkt < (map)->cap; bkt++) \
- for (cur = (map)->buckets[bkt]; \
- cur && ({tmp = cur->next; true; }); \
- cur = tmp)
-
-#define hashmap__for_each_key_entry(map, cur, _key) \
- for (cur = (map)->buckets \
- ? (map)->buckets[hash_bits((map)->hash_fn((_key), (map)->ctx), (map)->cap_bits)] \
- : NULL; \
- cur; \
- cur = cur->next) \
- if ((map)->equal_fn(cur->key, (_key), (map)->ctx))
-
-#define hashmap__for_each_key_entry_safe(map, cur, tmp, _key) \
- for (cur = (map)->buckets \
- ? (map)->buckets[hash_bits((map)->hash_fn((_key), (map)->ctx), (map)->cap_bits)] \
- : NULL; \
- cur && ({ tmp = cur->next; true; }); \
- cur = tmp) \
- if ((map)->equal_fn(cur->key, (_key), (map)->ctx))
-
#endif /* __PERF_UTIL_HASHMAP_H */
--
2.56.1.703.gf848ed807c-goog
^ permalink raw reply [flat|nested] 27+ messages in thread