* Implement hash-table iterator

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

* PR feedback

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

* PR feedback

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

* PR feedback

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

* Switch iterator to match BPF_MAP_LOOKUP_BATCH syscall behavior

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

* Switch iterator to match BPF_MAP_LOOKUP_BATCH syscall behavior

Signed-off-by: Alan Jowett <alanjo@microsoft.com>

---------

Signed-off-by: Alan Jowett <alanjo@microsoft.com>
This commit is contained in:
Alan Jowett 2023-06-22 09:43:44 -07:00 коммит произвёл GitHub
Родитель 1955692077
Коммит c1fecf4e2d
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 320 добавлений и 175 удалений

Просмотреть файл

@ -7,6 +7,7 @@
#include "ebpf_bitmap.h"
#include "ebpf_epoch.h"
#include "ebpf_handle.h"
#include "ebpf_hash_table.h"
#include "ebpf_maps.h"
#include "ebpf_object.h"
#include "ebpf_program.h"

Просмотреть файл

@ -5,6 +5,7 @@
#include "ebpf_core.h"
#include "ebpf_handle.h"
#include "ebpf_hash_table.h"
#include "ebpf_native.h"
#include "ebpf_object.h"
#include "ebpf_program.h"

Просмотреть файл

@ -3,6 +3,7 @@
#include "ebpf_async.h"
#include "ebpf_epoch.h"
#include "ebpf_hash_table.h"
#include "ebpf_tracelog.h"
typedef struct _ebpf_async_tracker

Просмотреть файл

@ -2,7 +2,7 @@
// SPDX-License-Identifier: MIT
#include "ebpf_epoch.h"
#include "ebpf_platform.h"
#include "ebpf_hash_table.h"
// Buckets contain an array of pointers to value and keys.
// Buckets are immutable once inserted in to the hash-table and replaced when
@ -849,3 +849,59 @@ ebpf_hash_table_key_count(_In_ const ebpf_hash_table_t* hash_table)
{
return hash_table->entry_count;
}
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_iterate(
_In_ const ebpf_hash_table_t* hash_table,
_Inout_ size_t* bucket,
_Inout_ size_t* count,
_Out_writes_(*count) const uint8_t** keys,
_Out_writes_(*count) const uint8_t** values)
{
size_t bucket_index = *bucket;
size_t index = 0;
size_t remaining_space = *count;
size_t next_bucket_count = 0;
if (bucket_index >= hash_table->bucket_count) {
return EBPF_NO_MORE_KEYS;
}
while (remaining_space > 0) {
if (bucket_index >= hash_table->bucket_count) {
break;
}
ebpf_hash_bucket_header_t* bucket_header = hash_table->buckets[bucket_index].header;
// Check if the bucket is empty.
if (!bucket_header) {
bucket_index++;
continue;
}
// Check if the next bucket will fit in the remaining space.
next_bucket_count = bucket_header->count;
if (remaining_space < next_bucket_count) {
break;
}
// Copy the keys and values.
for (size_t i = 0; i < next_bucket_count; i++) {
ebpf_hash_bucket_entry_t* entry = _ebpf_hash_table_bucket_entry(hash_table->key_size, bucket_header, i);
if (!entry) {
return EBPF_INVALID_ARGUMENT;
}
keys[index] = entry->key;
values[index] = entry->data;
index++;
remaining_space--;
}
bucket_index++;
}
// If the bucket_index did not change, then there wasn't enough space to copy the next bucket.
if (*bucket == bucket_index) {
*count = next_bucket_count;
return EBPF_INSUFFICIENT_BUFFER;
}
*bucket = bucket_index;
*count = index;
return EBPF_SUCCESS;
}

Просмотреть файл

@ -0,0 +1,219 @@
// Copyright (c) Microsoft Corporation
// SPDX-License-Identifier: MIT
#pragma once
#include "ebpf_platform.h"
#ifdef __cplusplus
extern "C"
{
#endif
#define EBPF_HASH_TABLE_NO_LIMIT 0
#define EBPF_HASH_TABLE_DEFAULT_BUCKET_COUNT 64
typedef enum _ebpf_hash_table_operations
{
EBPF_HASH_TABLE_OPERATION_ANY = 0,
EBPF_HASH_TABLE_OPERATION_INSERT = 1,
EBPF_HASH_TABLE_OPERATION_REPLACE = 2,
} ebpf_hash_table_operations_t;
typedef struct _ebpf_hash_table ebpf_hash_table_t;
typedef enum _ebpf_hash_table_notification_type
{
EBPF_HASH_TABLE_NOTIFICATION_TYPE_ALLOCATE, //< A key + value have been allocated.
EBPF_HASH_TABLE_NOTIFICATION_TYPE_FREE, //< A key + value have been freed.
EBPF_HASH_TABLE_NOTIFICATION_TYPE_USE, //< A key + value have been used.
} ebpf_hash_table_notification_type_t;
typedef void (*ebpf_hash_table_notification_function)(
_Inout_ void* context,
_In_ ebpf_hash_table_notification_type_t type,
_In_ const uint8_t* key,
_Inout_ uint8_t* value);
typedef _Must_inspect_result_ _Ret_writes_maybenull_(size) void* (*ebpf_hash_table_allocate)(size_t size);
typedef void (*ebpf_hash_table_free)(_Frees_ptr_opt_ void* memory);
typedef void (*ebpf_hash_table_extract_function)(
_In_ const uint8_t* value,
_Outptr_result_buffer_((*length_in_bits + 7) / 8) const uint8_t** data,
_Out_ size_t* length_in_bits);
/**
* @brief Options to pass to ebpf_hash_table_create.
*
* Some fields are required, others are optional. If an optional field is
* not specified, a default value will be used.
*/
typedef struct _ebpf_hash_table_creation_options
{
// Required fields.
size_t key_size; //< Size of key in bytes.
size_t value_size; //< Size of value in bytes.
// Optional fields.
ebpf_hash_table_extract_function extract_function; //< Function to extract key from stored value.
ebpf_hash_table_allocate allocate; //< Function to allocate memory - defaults to ebpf_epoch_allocate.
ebpf_hash_table_free free; //< Function to free memory - defaults to ebpf_epoch_free.
size_t bucket_count; //< Number of buckets to use - defaults to EBPF_HASH_TABLE_DEFAULT_BUCKET_COUNT.
size_t max_entries; //< Maximum number of entries in the hash table - defaults to EBPF_HASH_TABLE_NO_LIMIT.
size_t supplemental_value_size; //< Size of supplemental value to store in each entry - defaults to 0.
void* notification_context; //< Context to pass to notification functions.
ebpf_hash_table_notification_function
notification_callback; //< Function to call when value storage is allocated or freed.
} ebpf_hash_table_creation_options_t;
/**
* @brief Allocate and initialize a hash table.
*
* @param[out] hash_table Pointer to memory that will contain hash table on
* success.
* @param[in] options Options to control hash table creation.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NO_MEMORY Unable to allocate resources for this
* hash table.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_create(
_Out_ ebpf_hash_table_t** hash_table, _In_ const ebpf_hash_table_creation_options_t* options);
/**
* @brief Remove all items from the hash table and release memory.
*
* @param[in] hash_table Hash-table to release.
*/
void
ebpf_hash_table_destroy(_In_opt_ _Post_ptr_invalid_ ebpf_hash_table_t* hash_table);
/**
* @brief Find an element in the hash table.
*
* @param[in] hash_table Hash-table to search.
* @param[in] key Key to find in hash table.
* @param[out] value Pointer to value if found.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NOT_FOUND Key not found in hash table.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_find(_In_ const ebpf_hash_table_t* hash_table, _In_ const uint8_t* key, _Outptr_ uint8_t** value);
/**
* @brief Insert or update an entry in the hash table.
*
* @param[in, out] hash_table Hash-table to update.
* @param[in] key Key to find and insert or update.
* @param[in] value Value to insert into hash table or NULL to insert zero entry.
* @param[in] operation One of ebpf_hash_table_operations_t operations.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NO_MEMORY Unable to allocate memory for this
* entry in the hash table.
* @retval EBPF_OUT_OF_SPACE Unable to insert this entry in the hash table.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_update(
_Inout_ ebpf_hash_table_t* hash_table,
_In_ const uint8_t* key,
_In_opt_ const uint8_t* value,
ebpf_hash_table_operations_t operation);
/**
* @brief Remove an entry from the hash table.
*
* @param[in, out] hash_table Hash-table to update.
* @param[in] key Key to find and remove.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NOT_FOUND Key not found in hash table.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_delete(_Inout_ ebpf_hash_table_t* hash_table, _In_ const uint8_t* key);
/**
* @brief Fetch pointers to keys and values from one or more buckets in the hash table. Whole buckets worth of keys
* and values are returned at a time, with *count being the number of keys and values returned. If *count is too
* small to hold all the keys and values in the next bucket, EBPF_INSUFFICIENT_BUFFER is returned.
*
* @param[in] hash_table Hash-table to iterate.
* @param[in,out] cookie Cookie to pass to the iterator or NULL to restart. Updated on return.
* @param[in,out] count On input, the number of keys and values that can be stored in the buffers. On output, the
* number of keys and values returned.
* @param[out] keys An array of pointers to keys in the hash table.
* @param[out] values An array of pointers to values in the hash table.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_INVALID_ARGUMENT An invalid argument was passed to this function.
* @retval EBPF_NO_MORE_KEYS No more keys.
* @retval EBPF_INSUFFICIENT_BUFFER The buffer is too small to hold all the keys and values in the bucket and *count
* has been updated to reflect the number of keys and values in the next bucket.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_iterate(
_In_ const ebpf_hash_table_t* hash_table,
_Inout_ size_t* bucket,
_Inout_ size_t* count,
_Out_writes_(*count) const uint8_t** keys,
_Out_writes_(*count) const uint8_t** values);
/**
* @brief Find the next key in the hash table.
*
* @param[in] hash_table Hash-table to query.
* @param[in] previous_key Previous key or NULL to restart.
* @param[out] next_key Next key if it exists.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NO_MORE_KEYS No keys exist in the hash table that
* are lexicographically after the specified key.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_next_key(
_In_ const ebpf_hash_table_t* hash_table, _In_opt_ const uint8_t* previous_key, _Out_ uint8_t* next_key);
/**
* @brief Returns the next (key, value) pair in the hash table.
*
* @param[in] hash_table Hash-table to query.
* @param[in] previous_key Previous key or NULL to restart.
* @param[out] next_key Next key if it exists.
* @param[out] next_value If non-NULL, returns the next value if it exists.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NO_MORE_KEYS No keys exist in the hash table that
* are lexicographically after the specified key.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_next_key_and_value(
_In_ const ebpf_hash_table_t* hash_table,
_In_opt_ const uint8_t* previous_key,
_Out_ uint8_t* next_key,
_Inout_opt_ uint8_t** next_value);
/**
* @brief Returns the next (key, value) pair in the hash table.
*
* @param[in] hash_table Hash-table to query.
* @param[in] previous_key Previous key or NULL to restart.
* @param[out] next_key_pointer Pointer to next key if one exists.
* @param[out] next_value If non-NULL, returns the next value if it exists.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NO_MORE_KEYS No keys exist in the hash table that
* are lexicographically after the specified key.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_next_key_pointer_and_value(
_In_ const ebpf_hash_table_t* hash_table,
_In_opt_ const uint8_t* previous_key,
_Outptr_ uint8_t** next_key_pointer,
_Outptr_opt_ uint8_t** next_value);
/**
* @brief Get the number of keys in the hash table
*
* @param[in] hash_table Hash-table to query.
* @return Count of entries in the hash table.
*/
size_t
ebpf_hash_table_key_count(_In_ const ebpf_hash_table_t* hash_table);
#ifdef __cplusplus
}
#endif

Просмотреть файл

@ -17,6 +17,7 @@
#define EBPF_FILE_ID EBPF_FILE_ID_PINNING_TABLE
#include "ebpf_core_structs.h"
#include "ebpf_hash_table.h"
#include "ebpf_object.h"
#include "ebpf_pinning_table.h"
#include "ebpf_tracelog.h"

Просмотреть файл

@ -36,9 +36,6 @@ extern "C"
#define EBPF_PAD_CACHE(X) ((X + EBPF_CACHE_LINE_SIZE - 1) & ~(EBPF_CACHE_LINE_SIZE - 1))
#define EBPF_PAD_8(X) ((X + 7) & ~7)
#define EBPF_HASH_TABLE_NO_LIMIT 0
#define EBPF_HASH_TABLE_DEFAULT_BUCKET_COUNT 64
#define EBPF_NS_PER_FILETIME 100
// Macro locally suppresses "Unreferenced variable" warning, which in 'Release' builds is treated as an error.
@ -106,13 +103,6 @@ extern "C"
extern bool ebpf_fuzzing_enabled;
typedef enum _ebpf_hash_table_operations
{
EBPF_HASH_TABLE_OPERATION_ANY = 0,
EBPF_HASH_TABLE_OPERATION_INSERT = 1,
EBPF_HASH_TABLE_OPERATION_REPLACE = 2,
} ebpf_hash_table_operations_t;
/**
* @brief Initialize the eBPF platform abstraction layer.
* @retval EBPF_SUCCESS The operation was successful.
@ -558,170 +548,6 @@ extern "C"
void
ebpf_free_timer_work_item(_Frees_ptr_opt_ ebpf_timer_work_item_t* timer);
typedef struct _ebpf_hash_table ebpf_hash_table_t;
typedef enum _ebpf_hash_table_notification_type
{
EBPF_HASH_TABLE_NOTIFICATION_TYPE_ALLOCATE, //< A key + value have been allocated.
EBPF_HASH_TABLE_NOTIFICATION_TYPE_FREE, //< A key + value have been freed.
EBPF_HASH_TABLE_NOTIFICATION_TYPE_USE, //< A key + value have been used.
} ebpf_hash_table_notification_type_t;
typedef void (*ebpf_hash_table_notification_function)(
_Inout_ void* context,
_In_ ebpf_hash_table_notification_type_t type,
_In_ const uint8_t* key,
_Inout_ uint8_t* value);
/**
* @brief Options to pass to ebpf_hash_table_create.
*
* Some fields are required, others are optional. If an optional field is
* not specified, a default value will be used.
*/
typedef struct _ebpf_hash_table_creation_options
{
// Required fields.
size_t key_size; //< Size of key in bytes.
size_t value_size; //< Size of value in bytes.
// Optional fields.
void (*extract_function)(
_In_ const uint8_t* value,
_Outptr_result_buffer_((*length_in_bits + 7) / 8) const uint8_t** data,
_Out_ size_t* length_in_bits); //< Function to extract key from stored value.
void* (*allocate)(size_t size); //< Function to allocate memory - defaults to ebpf_epoch_allocate.
void (*free)(void* memory); //< Function to free memory - defaults to ebpf_epoch_free.
size_t bucket_count; //< Number of buckets to use - defaults to EBPF_HASH_TABLE_DEFAULT_BUCKET_COUNT.
size_t max_entries; //< Maximum number of entries in the hash table - defaults to EBPF_HASH_TABLE_NO_LIMIT.
size_t supplemental_value_size; //< Size of supplemental value to store in each entry - defaults to 0.
void* notification_context; //< Context to pass to notification functions.
ebpf_hash_table_notification_function
notification_callback; //< Function to call when value storage is allocated or freed.
} ebpf_hash_table_creation_options_t;
/**
* @brief Allocate and initialize a hash table.
*
* @param[out] hash_table Pointer to memory that will contain hash table on
* success.
* @param[in] options Options to control hash table creation.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NO_MEMORY Unable to allocate resources for this
* hash table.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_create(
_Out_ ebpf_hash_table_t** hash_table, _In_ const ebpf_hash_table_creation_options_t* options);
/**
* @brief Remove all items from the hash table and release memory.
*
* @param[in] hash_table Hash-table to release.
*/
void
ebpf_hash_table_destroy(_In_opt_ _Post_ptr_invalid_ ebpf_hash_table_t* hash_table);
/**
* @brief Find an element in the hash table.
*
* @param[in] hash_table Hash-table to search.
* @param[in] key Key to find in hash table.
* @param[out] value Pointer to value if found.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NOT_FOUND Key not found in hash table.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_find(_In_ const ebpf_hash_table_t* hash_table, _In_ const uint8_t* key, _Outptr_ uint8_t** value);
/**
* @brief Insert or update an entry in the hash table.
*
* @param[in, out] hash_table Hash-table to update.
* @param[in] key Key to find and insert or update.
* @param[in] value Value to insert into hash table or NULL to insert zero entry.
* @param[in] operation One of ebpf_hash_table_operations_t operations.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NO_MEMORY Unable to allocate memory for this
* entry in the hash table.
* @retval EBPF_OUT_OF_SPACE Unable to insert this entry in the hash table.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_update(
_Inout_ ebpf_hash_table_t* hash_table,
_In_ const uint8_t* key,
_In_opt_ const uint8_t* value,
ebpf_hash_table_operations_t operation);
/**
* @brief Remove an entry from the hash table.
*
* @param[in, out] hash_table Hash-table to update.
* @param[in] key Key to find and remove.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NOT_FOUND Key not found in hash table.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_delete(_Inout_ ebpf_hash_table_t* hash_table, _In_ const uint8_t* key);
/**
* @brief Find the next key in the hash table.
*
* @param[in] hash_table Hash-table to query.
* @param[in] previous_key Previous key or NULL to restart.
* @param[out] next_key Next key if it exists.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NO_MORE_KEYS No keys exist in the hash table that
* are lexicographically after the specified key.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_next_key(
_In_ const ebpf_hash_table_t* hash_table, _In_opt_ const uint8_t* previous_key, _Out_ uint8_t* next_key);
/**
* @brief Returns the next (key, value) pair in the hash table.
*
* @param[in] hash_table Hash-table to query.
* @param[in] previous_key Previous key or NULL to restart.
* @param[out] next_key Next key if it exists.
* @param[out] next_value If non-NULL, returns the next value if it exists.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NO_MORE_KEYS No keys exist in the hash table that
* are lexicographically after the specified key.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_next_key_and_value(
_In_ const ebpf_hash_table_t* hash_table,
_In_opt_ const uint8_t* previous_key,
_Out_ uint8_t* next_key,
_Inout_opt_ uint8_t** next_value);
/**
* @brief Returns the next (key, value) pair in the hash table.
*
* @param[in] hash_table Hash-table to query.
* @param[in] previous_key Previous key or NULL to restart.
* @param[out] next_key_pointer Pointer to next key if one exists.
* @param[out] next_value If non-NULL, returns the next value if it exists.
* @retval EBPF_SUCCESS The operation was successful.
* @retval EBPF_NO_MORE_KEYS No keys exist in the hash table that
* are lexicographically after the specified key.
*/
_Must_inspect_result_ ebpf_result_t
ebpf_hash_table_next_key_pointer_and_value(
_In_ const ebpf_hash_table_t* hash_table,
_In_opt_ const uint8_t* previous_key,
_Outptr_ uint8_t** next_key_pointer,
_Outptr_opt_ uint8_t** next_value);
/**
* @brief Get the number of keys in the hash table
*
* @param[in] hash_table Hash-table to query.
* @return Count of entries in the hash table.
*/
size_t
ebpf_hash_table_key_count(_In_ const ebpf_hash_table_t* hash_table);
/**
* @brief Atomically increase the value of addend by 1 and return the new
* value.

Просмотреть файл

@ -2,6 +2,7 @@
// SPDX-License-Identifier: MIT
#include "ebpf_epoch.h"
#include "ebpf_hash_table.h"
#include "ebpf_state.h"
#include "ebpf_tracelog.h"

Просмотреть файл

@ -8,6 +8,7 @@
#include "ebpf_async.h"
#include "ebpf_bitmap.h"
#include "ebpf_epoch.h"
#include "ebpf_hash_table.h"
#include "ebpf_nethooks.h"
#include "ebpf_pinning_table.h"
#include "ebpf_platform.h"
@ -151,6 +152,43 @@ TEST_CASE("hash_table_test", "[platform]")
REQUIRE(ebpf_hash_table_update(table, key_3.data(), data_3.data(), EBPF_HASH_TABLE_OPERATION_ANY) == EBPF_SUCCESS);
REQUIRE(ebpf_hash_table_key_count(table) == 3);
// Iterate through all keys.
uint64_t cookie = 0;
uint8_t keys_found = 0;
std::vector<const uint8_t*> keys;
std::vector<const uint8_t*> values;
size_t count = 2;
keys.resize(count);
values.resize(count);
// Bucket contains 3 keys, but we only have space for 2.
// Should fail with insufficient buffer.
REQUIRE(ebpf_hash_table_iterate(table, &cookie, &count, keys.data(), values.data()) == EBPF_INSUFFICIENT_BUFFER);
REQUIRE(count == 3);
keys.resize(count);
values.resize(count);
// Bucket contains 3 keys, and we have space for 3.
// Should succeed.
REQUIRE(ebpf_hash_table_iterate(table, &cookie, &count, keys.data(), values.data()) == EBPF_SUCCESS);
// Verify that all keys are found.
for (size_t index = 0; index < 3; index++) {
if (memcmp(keys[index], key_1.data(), key_1.size()) == 0) {
REQUIRE(memcmp(values[index], data_1.data(), data_1.size()) == 0);
keys_found |= 1 << 0;
} else if (memcmp(keys[index], key_2.data(), key_2.size()) == 0) {
REQUIRE(memcmp(values[index], data_2.data(), data_2.size()) == 0);
keys_found |= 1 << 1;
} else if (memcmp(keys[index], key_3.data(), key_3.size()) == 0) {
REQUIRE(memcmp(values[index], data_3.data(), data_3.size()) == 0);
keys_found |= 1 << 2;
} else {
REQUIRE(false);
}
}
// Verify that there are no more keys.
REQUIRE(ebpf_hash_table_iterate(table, &cookie, &count, keys.data(), values.data()) == EBPF_NO_MORE_KEYS);
REQUIRE(keys_found == 0x7);
// Find the first
REQUIRE(ebpf_hash_table_find(table, key_1.data(), &returned_value) == EBPF_SUCCESS);
REQUIRE(memcmp(returned_value, data_1.data(), data_1.size()) == 0);

Просмотреть файл

@ -2,6 +2,7 @@
// SPDX-License-Identifier: MIT
#define TEST_AREA "platform"
#include "ebpf_hash_table.h"
#include "performance.h"
static void