From f0049514e1c67e2565c96b660286fada64f36b00 Mon Sep 17 00:00:00 2001 From: Tom Hromatka Date: Thu, 17 Jul 2025 10:55:19 -0600 Subject: [PATCH 1/2] github: Disable cgroup v1 Github Actions Cgroup v1 (Ubuntu 20) boxes have been retired in Github Actions. Delete all Github Actions jobs that require these machines Signed-off-by: Tom Hromatka Reviewed-by: Sidhartha Kumar --- .github/workflows/continuous-integration.yml | 51 +------------------- 1 file changed, 1 insertion(+), 50 deletions(-) diff --git a/.github/workflows/continuous-integration.yml b/.github/workflows/continuous-integration.yml index 4bb7624..a266986 100644 --- a/.github/workflows/continuous-integration.yml +++ b/.github/workflows/continuous-integration.yml @@ -85,24 +85,6 @@ jobs: env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - distcheck_cgv1: - name: Adaptived make distcheck v1 - runs-on: ubuntu-20.04 - - steps: - - uses: actions/checkout@v3 - with: - submodules: false - - name: Initialize the directory - uses: ./.github/actions/setup-adaptived - - name: Run make distcheck - run: cd adaptived && make distcheck - - name: Display test logs - if: ${{ failure() }} - run: | - cat adaptived/adaptived-*/_build/sub/tests/ftests/ftests-wrapper.sh.log - cat adaptived/adaptived-*/_build/sub/tests/ftests/ftests-c-wrapper.sh.log - distcheck_cgv2: name: Adaptived make distcheck v2 runs-on: ubuntu-latest @@ -121,37 +103,6 @@ jobs: cat adaptived/adaptived-*/_build/sub/tests/ftests/ftests-wrapper.sh.log cat adaptived/adaptived-*/_build/sub/tests/ftests/ftests-c-wrapper.sh.log - functional_tests_cgv1: - name: Adaptived Functional Tests v1 - runs-on: ubuntu-20.04 - - steps: - - uses: actions/checkout@v3 - with: - submodules: false - - name: Fail on all warnings - run: | - CFLAGS=-Werror - export CFLAGS - - name: Initialize the directory - uses: ./.github/actions/setup-adaptived - - name: Run functional tests - run: cd adaptived && make check - - name: Display test logs - if: ${{ always() }} - run: | - cat adaptived/tests/ftests/ftests-wrapper.sh.log - cat adaptived/tests/ftests/ftests-c-wrapper.sh.log - - name: Collate code coverage results - uses: ./.github/actions/code-coverage - - name: Upload code coverage results - uses: coverallsapp/github-action@master - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - path-to-lcov: ./lcov.total - flag-name: "Adaptived Functional Tests - cgv1" - parallel: True - functional_tests_cgv2: name: Adaptived Functional Tests v2 runs-on: ubuntu-latest @@ -186,7 +137,7 @@ jobs: finalize: name: Finalize the test run if: ${{ always() }} - needs: [functional_tests_cgv1, functional_tests_cgv2] + needs: [functional_tests_cgv2] runs-on: ubuntu-latest steps: - name: Finalize code coverage results From 877469390c09ca95d550fd623361ba0df04047a4 Mon Sep 17 00:00:00 2001 From: Tom Hromatka Date: Thu, 17 Jul 2025 10:43:57 -0600 Subject: [PATCH 2/2] adaptived: Add shared data support Add support for sharing data between a cause and the effect(s) that are in the same rule. It is intended to share data unidirectionally from the cause to the effect(s), but by using the persist flag, data can also be shared from the effect(s) to the cause. (The persist flag can also be used by the cause to retain data across adaptived_loop() loops.) Sharing data in any direction makes tight coupling between causes and effects. Use caution when sharing data, and clearly document data sharing expectations. Signed-off-by: Tom Hromatka Reviewed-by: Sidhartha Kumar --- adaptived/include/adaptived.h | 88 +++ adaptived/src/Makefile.am | 2 + adaptived/src/cause.h | 6 + adaptived/src/main.c | 21 +- adaptived/src/shared_data.c | 236 ++++++++ adaptived/src/shared_data.h | 56 ++ adaptived/tests/gunit/012-shared_data.cpp | 637 ++++++++++++++++++++++ adaptived/tests/gunit/Makefile.am | 3 +- 8 files changed, 1047 insertions(+), 2 deletions(-) create mode 100644 adaptived/src/shared_data.c create mode 100644 adaptived/src/shared_data.h create mode 100644 adaptived/tests/gunit/012-shared_data.cpp diff --git a/adaptived/include/adaptived.h b/adaptived/include/adaptived.h index 16b987d..d2afb4f 100644 --- a/adaptived/include/adaptived.h +++ b/adaptived/include/adaptived.h @@ -63,6 +63,29 @@ enum adaptived_attr { ADAPTIVED_ATTR_CNT }; +enum adaptived_sdata_type { + /* + * Custom data. Can be used by registered functions without forcing a recompile + * of adaptived + */ + ADAPTIVED_SDATA_CUSTOM = 0, + ADAPTIVED_SDATA_STR, + ADAPTIVED_SDATA_CGROUP, + ADAPTIVED_SDATA_NAME_VALUE, + + ADAPTIVED_SDATA_CNT +}; + +enum adaptived_sdata_flags { + ADAPTIVED_SDATAF_PERSIST = 0x1 +}; + +/** + * Function to free a custom shared data structure. Not needed for any other + * shared data type, as adaptived knows how to free built-in data types. + */ +typedef void (*adaptived_sdata_free)(void * const data); + enum adaptived_cgroup_value_type { ADAPTIVED_CGVAL_STR = 0, ADAPTIVED_CGVAL_LONG_LONG, @@ -89,6 +112,11 @@ struct adaptived_cgroup_value { } value; }; +struct adaptived_name_and_value { + char *name; + struct adaptived_cgroup_value *value; +}; + struct adaptived_rule_stats { int cause_cnt; int effect_cnt; @@ -459,6 +487,66 @@ int adaptived_load_rule(struct adaptived_ctx * const ctx, struct adaptived_rule */ int adaptived_unload_rule(struct adaptived_ctx * const ctx, const char * const name); +/** + * Method to write shared data to a cause. Data can be used by downstream effect(s) + * @param cse adaptived cause structure + * @param type shared data enumeration describing the type of data being store + * @param data pointer to the data to be stored + * @param free_fn Function to free the shared data. Only needed for custom data types + * @param flags See enum adaptived_sdata_flags + * + * @note - shared data is deleted/freed at the end of each adaptive main loop unless + * the persist flag is set + * @note - freeing of data is done by free(), so the *data pointer must be created + * via malloc. It would be possible to avoid this by creating a new type, + * e.g. ADAPTIVED_SDATA_INT, and adding handling for it in free_shared_data(). + * @note - persist can be used to share data bidirectionally between a cause and some + * effect(s). Use with caution, as this tightly couples these causes and + * effect(s). + */ +int adaptived_write_shared_data(struct adaptived_cause * const cse, + enum adaptived_sdata_type type, void *data, + adaptived_sdata_free free_fn, + uint32_t flags); + +/** + * Update shared data + * @param cse adaptived cause structure + * @param index shared data object index + * @param type shared data enumeration describing the type of data being store + * @param data pointer to the data to be stored + * @param flags See enum adaptived_sdata_flags + * + * @note - If the data pointer is changed, it's up to the caller of this function + * to ensure that the previous *data pointer is freed. + */ +int adaptived_update_shared_data(struct adaptived_cause * const cse, int index, + enum adaptived_sdata_type type, void *data, + uint32_t flags); + +/** + * Get the number of shared data objects in this cause + * @param cse adaptived cause + * + * @return number of shared data objects + */ +int adaptived_get_shared_data_cnt(const struct adaptived_cause * const cse); + +/** + * Retrieve one shared data object from the cause + * @param cse adaptived cause + * @param index shared data object index + * @param type Output parameter that tells what type of data is stored in this object + * @param data Output parameter that contains the stored data + * @param flags See enum adaptived_sdata_flags + * + * @note You do not need to free the data in the shared data object. adaptived will + * automatically do that at the end of each main processing loop + */ +int adaptived_get_shared_data(const struct adaptived_cause * const cse, int index, + enum adaptived_sdata_type * const type, void **data, + uint32_t * const flags); + #ifdef __cplusplus } /* extern "C" */ #endif diff --git a/adaptived/src/Makefile.am b/adaptived/src/Makefile.am index 6deb740..c7e391c 100644 --- a/adaptived/src/Makefile.am +++ b/adaptived/src/Makefile.am @@ -38,6 +38,8 @@ SOURCES = \ parse.c \ pressure.h \ rule.c \ + shared_data.c \ + shared_data.h \ utils/cgroup_utils.c \ utils/sd_bus_utils.c \ utils/file_utils.c \ diff --git a/adaptived/src/cause.h b/adaptived/src/cause.h index ba42bd2..413ee6b 100644 --- a/adaptived/src/cause.h +++ b/adaptived/src/cause.h @@ -75,6 +75,12 @@ struct adaptived_cause { struct json_object *json; /* only used when building a rule at runtime */ struct adaptived_cause *next; + /* + * Data that can be shared between causes and effects. It is freed/deleted + * at the end of each adaptived_loop() loop + */ + struct shared_data *sdata; + /* private data store for each cause plugin */ void *data; }; diff --git a/adaptived/src/main.c b/adaptived/src/main.c index 98bf0b2..a0a24d8 100644 --- a/adaptived/src/main.c +++ b/adaptived/src/main.c @@ -39,6 +39,7 @@ #include #include "adaptived-internal.h" +#include "shared_data.h" #include "defines.h" void cleanup(struct adaptived_ctx *ctx); @@ -426,6 +427,18 @@ API int adaptived_register_injection_function(struct adaptived_ctx * const ctx, return 0; } +static void free_rule_shared_data(struct adaptived_rule * const rule, bool force_delete) +{ + struct adaptived_cause *cse; + + cse = rule->causes; + + while(cse) { + free_shared_data(cse, force_delete); + cse = cse->next; + } +} + API int adaptived_loop(struct adaptived_ctx * const ctx, bool parse) { struct adaptived_effect *eff; @@ -534,7 +547,7 @@ API int adaptived_loop(struct adaptived_ctx * const ctx, bool parse) } } - + free_rule_shared_data(rule, false); rule = rule->next; } @@ -567,6 +580,12 @@ API int adaptived_loop(struct adaptived_ctx * const ctx, bool parse) } out: + rule = ctx->rules; + while (rule) { + free_rule_shared_data(rule, true); + rule = rule->next; + } + pthread_mutex_unlock(&ctx->ctx_mutex); return ret; diff --git a/adaptived/src/shared_data.c b/adaptived/src/shared_data.c new file mode 100644 index 0000000..c20bc09 --- /dev/null +++ b/adaptived/src/shared_data.c @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + * adaptived file for sharing data between causes and effects + */ + +#include +#include +#include + +#include "adaptived-internal.h" +#include "shared_data.h" +#include "cause.h" + +/* + * Method for a cause to share data with effect(s) in the same rule. + * + * Note that the shared data is deleted at the end of each run of the + * adaptived main loop + */ +API int adaptived_write_shared_data(struct adaptived_cause * const cse, + enum adaptived_sdata_type type, void *data, + adaptived_sdata_free free_fn, + uint32_t flags) +{ + struct shared_data *sdata, *prev; + + if (!cse || !data) + return -EINVAL; + + if (type < 0) + return -EINVAL; + if (type >= ADAPTIVED_SDATA_CNT) + return -EINVAL; + if (type == ADAPTIVED_SDATA_CUSTOM && free_fn == NULL) + return -EINVAL; + if (type != ADAPTIVED_SDATA_CUSTOM && free_fn != NULL) + return -EINVAL; + + sdata = malloc(sizeof(struct shared_data)); + if (!sdata) + return -ENOMEM; + + sdata->type = type; + sdata->data = data; + sdata->free_fn = free_fn; + sdata->flags = flags; + sdata->next = NULL; + + if (cse->sdata == NULL) { + cse->sdata = sdata; + } else { + prev = cse->sdata; + + while (prev != NULL) { + if (prev->next == NULL) + break; + + prev = prev->next; + } + + prev->next = sdata; + } + + return 0; +} + +API int adaptived_update_shared_data(struct adaptived_cause * const cse, int index, + enum adaptived_sdata_type type, void *data, + uint32_t flags) +{ + struct shared_data *sdata; + + if (cse == NULL || data == NULL) + return -EINVAL; + + if (index < 0) + return -EINVAL; + + sdata = cse->sdata; + + if (sdata == NULL) + return -ERANGE; + + while (index > 0) { + if (sdata->next == NULL) + return -ERANGE; + + sdata = sdata->next; + index--; + } + + if (sdata->type != type) + /* Don't allow the changing of the data type */ + return -EINVAL; + + /* + * It's up to the user to ensure that the old data field is properly freed and not + * leaked + */ + sdata->data = data; + sdata->flags = flags; + + return 0; +} + +API int adaptived_get_shared_data_cnt(const struct adaptived_cause * const cse) +{ + struct shared_data *sdata = NULL; + int cnt = 0; + + if (cse == NULL) + return 0; + + sdata = cse->sdata; + + while (sdata) { + cnt++; + sdata = sdata->next; + } + + return cnt; +} + +API int adaptived_get_shared_data(const struct adaptived_cause * const cse, int index, + enum adaptived_sdata_type * const type, void **data, + uint32_t * const flags) +{ + struct shared_data *sdata; + + if (cse == NULL || type == NULL || data == NULL || flags == NULL) + return -EINVAL; + + if (index < 0) + return -EINVAL; + + sdata = cse->sdata; + + if (sdata == NULL) + return -ERANGE; + + while (index > 0) { + if (sdata->next == NULL) + return -ERANGE; + + sdata = sdata->next; + index--; + } + + *type = sdata->type; + *data = sdata->data; + *flags = sdata->flags; + + return 0; +} + +API void free_shared_data(struct adaptived_cause * const cse, bool force_delete) +{ + struct shared_data *cur, *next, *prev_valid = NULL, *first_valid = NULL; + bool do_free, persist; + + if (cse == NULL) + return; + + if (cse->sdata == NULL) + return; + + cur = cse->sdata; + + while (cur != NULL) { + next = cur->next; + + persist = (bool)(cur->flags & ADAPTIVED_SDATAF_PERSIST); + + do_free = force_delete || !persist; + + if (!do_free) { + if (!first_valid) + first_valid = cur; + + if (prev_valid) + prev_valid->next = cur; + + prev_valid = cur; + cur = next; + continue; + } + + switch(cur->type) { + case ADAPTIVED_SDATA_CUSTOM: + (*cur->free_fn)(cur->data); + break; + case ADAPTIVED_SDATA_CGROUP: + adaptived_free_cgroup_value(cur->data); + free(cur->data); + break; + case ADAPTIVED_SDATA_NAME_VALUE: + struct adaptived_name_and_value *name_value; + + name_value = (struct adaptived_name_and_value *)cur->data; + + free(name_value->name); + adaptived_free_cgroup_value(name_value->value); + free(cur->data); + break; + default: + free(cur->data); + break; + } + + free(cur); + cur = next; + } + + cse->sdata = first_valid; +} diff --git a/adaptived/src/shared_data.h b/adaptived/src/shared_data.h new file mode 100644 index 0000000..e1bd7df --- /dev/null +++ b/adaptived/src/shared_data.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + * adaptived header file for shared data + */ + +#ifndef __ADAPTIVED_SHARED_DATA_H +#define __ADAPTIVED_SHARED_DATA_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +struct shared_data { + enum adaptived_sdata_type type; + void *data; + adaptived_sdata_free free_fn; + + uint32_t flags; + struct shared_data *next; +}; + +/* + * If force_delete is true, then the shared data will be deleted (regardless + * of the value of the persist structure member. + */ +void free_shared_data(struct adaptived_cause * const cse, bool force_delete); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* __ADAPTIVED_SHARED_DATA_H */ diff --git a/adaptived/tests/gunit/012-shared_data.cpp b/adaptived/tests/gunit/012-shared_data.cpp new file mode 100644 index 0000000..7e29f95 --- /dev/null +++ b/adaptived/tests/gunit/012-shared_data.cpp @@ -0,0 +1,637 @@ +/* + * Copyright (c) 2025, Oracle and/or its affiliates. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +/** + * adaptived googletest for shared_data + */ + +#include +#include + +#include "gtest/gtest.h" +#include "shared_data.h" +#include "cause.h" + +class SharedDataTest : public ::testing::Test { +}; + +static void populate_cause(struct adaptived_cause * const cse, int idx) +{ + char *name; + + name = (char *)malloc(sizeof(char) * 16); + ASSERT_NE(name, nullptr); + + sprintf(name, "test012-%d", idx); + + cse->idx = (enum cause_enum)idx; + cse->name = name; + cse->fns = NULL; + cse->sdata = NULL; + cse->data = NULL; +} + +static void free_sdata(void * const data) +{ + return; +} + +TEST_F(SharedDataTest, InvalidShare) +{ + enum adaptived_sdata_type type; + struct adaptived_cause cse; + uint32_t flags; + void *vdata; + char *data; + int ret; + + data = (char *)malloc(sizeof(char) * 16); + ASSERT_NE(data, nullptr); + + sprintf(data, "data string"); + + ret = adaptived_write_shared_data(NULL, ADAPTIVED_SDATA_STR, data, &free_sdata, 0); + ASSERT_EQ(ret, -EINVAL); + + populate_cause(&cse, 0); + + ret = adaptived_get_shared_data(&cse, 0, &type, &vdata, &flags); + ASSERT_EQ(ret, -ERANGE); + + ret = adaptived_write_shared_data(&cse, (enum adaptived_sdata_type)-1, data, + &free_sdata, 0); + ASSERT_EQ(ret, -EINVAL); + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_CNT, data, &free_sdata, 0); + ASSERT_EQ(ret, -EINVAL); + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_STR, NULL, &free_sdata, + ADAPTIVED_SDATAF_PERSIST); + ASSERT_EQ(ret, -EINVAL); + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_STR, data, &free_sdata, 0); + ASSERT_EQ(ret, -EINVAL); + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_CUSTOM, data, NULL, 0); + ASSERT_EQ(ret, -EINVAL); + + ret = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(ret, 0); + + ret = adaptived_get_shared_data_cnt(NULL); + ASSERT_EQ(ret, 0); + + free_shared_data(NULL, true); + + free(cse.name); + free(data); +} + +static void free_sdata2(void * const data) +{ + char ** cdata; + + cdata = (char **)data; + + free(cdata[0]); + free(cdata[1]); + free(cdata[2]); + free(cdata[3]); + free(cdata); +} + +TEST_F(SharedDataTest, CustomDataShare) +{ + char **custom_data, **read_data_ptr; + enum adaptived_sdata_type type; + struct adaptived_cause cse; + uint32_t flags = 0; + void *read_data; + int ret, cnt; + + populate_cause(&cse, 0); + + ret = adaptived_update_shared_data(&cse, 0, type, &read_data, 0); + ASSERT_EQ(ret, -ERANGE); + + custom_data = (char **)malloc(sizeof(char *) * 4); + ASSERT_NE(custom_data, nullptr); + custom_data[0] = (char *)malloc(sizeof(char) * 32); + ASSERT_NE(custom_data[0], nullptr); + sprintf(custom_data[0], "This is string 0"); + custom_data[1] = (char *)malloc(sizeof(char) * 64); + ASSERT_NE(custom_data[1], nullptr); + sprintf(custom_data[1], "This is string 1. It's twice as long as string 0"); + custom_data[2] = (char *)malloc(sizeof(char) * 12); + ASSERT_NE(custom_data[2], nullptr); + sprintf(custom_data[2], "String 2"); + custom_data[3] = (char *)malloc(sizeof(char) * 32); + ASSERT_NE(custom_data[3], nullptr); + sprintf(custom_data[3], "This is string 3"); + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_CUSTOM, custom_data, free_sdata2, + flags); + ASSERT_EQ(ret, 0); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 1); + + ret = adaptived_get_shared_data(&cse, 0, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(flags, (uint32_t)0); + ASSERT_EQ(type, ADAPTIVED_SDATA_CUSTOM); + ASSERT_EQ(read_data, custom_data); + + read_data_ptr = (char **)read_data; + + ASSERT_STREQ(read_data_ptr[0], "This is string 0"); + ASSERT_STREQ(read_data_ptr[1], "This is string 1. It's twice as long as string 0"); + ASSERT_STREQ(read_data_ptr[2], "String 2"); + ASSERT_STREQ(read_data_ptr[3], "This is string 3"); + + /* + * Do some invalid testing on get_shared_data() now that we have + * populated the shared_data field in the cse structure + */ + ret = adaptived_get_shared_data(&cse, -1, &type, &read_data, &flags); + ASSERT_EQ(ret, -EINVAL); + + ret = adaptived_get_shared_data(&cse, 2, &type, &read_data, &flags); + ASSERT_EQ(ret, -ERANGE); + + ret = adaptived_get_shared_data(&cse, 0, NULL, &read_data, &flags); + ASSERT_EQ(ret, -EINVAL); + + ret = adaptived_get_shared_data(&cse, 0, &type, NULL, &flags); + ASSERT_EQ(ret, -EINVAL); + + ret = adaptived_get_shared_data(&cse, 0, &type, &read_data, NULL); + ASSERT_EQ(ret, -EINVAL); + + /* + * Do some invalid testing on update_shared_data() + */ + type = ADAPTIVED_SDATA_CUSTOM; + ret = adaptived_update_shared_data(&cse, 0, type, NULL, 0); + ASSERT_EQ(ret, -EINVAL); + + ret = adaptived_update_shared_data(NULL, 0, type, &read_data, 0); + ASSERT_EQ(ret, -EINVAL); + + ret = adaptived_update_shared_data(&cse, -1, type, &read_data, 0); + ASSERT_EQ(ret, -EINVAL); + + ret = adaptived_update_shared_data(&cse, 4, type, &read_data, 0); + ASSERT_EQ(ret, -ERANGE); + + type = ADAPTIVED_SDATA_STR; + ret = adaptived_update_shared_data(&cse, 0, type, &read_data, 0); + ASSERT_EQ(ret, -EINVAL); + + free_shared_data(&cse, false); + free(cse.name); +} + +TEST_F(SharedDataTest, StringDataShare) +{ + enum adaptived_sdata_type type; + struct adaptived_cause cse; + char *src, *read; + void *read_data; + uint32_t flags; + int ret, cnt; + + populate_cause(&cse, 0); + + src = (char *)malloc(sizeof(char) * 12); + ASSERT_NE(src, nullptr); + sprintf(src, "test data"); + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_STR, src, NULL, + ADAPTIVED_SDATAF_PERSIST); + ASSERT_EQ(ret, 0); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 1); + + ret = adaptived_get_shared_data(&cse, 0, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_STR); + ASSERT_EQ(read_data, src); + ASSERT_EQ(flags, ADAPTIVED_SDATAF_PERSIST); + + read = (char *)read_data; + + ASSERT_STREQ(read, "test data"); + + free_shared_data(&cse, false); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 1); + + free_shared_data(&cse, true); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 0); + + free(cse.name); +} + +TEST_F(SharedDataTest, CgroupFloatDataShare) +{ + struct adaptived_cgroup_value *src, *dst; + enum adaptived_sdata_type type; + struct adaptived_cause cse; + void *read_data; + uint32_t flags; + int ret, cnt; + + populate_cause(&cse, 0); + + src = (struct adaptived_cgroup_value *)malloc(sizeof(struct adaptived_cgroup_value)); + ASSERT_NE(src, nullptr); + src->type = ADAPTIVED_CGVAL_FLOAT; + src->value.float_value = 123.4; + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_CGROUP, src, NULL, 0); + ASSERT_EQ(ret, 0); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 1); + + ret = adaptived_get_shared_data(&cse, 0, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_CGROUP); + ASSERT_EQ(read_data, src); + ASSERT_EQ(flags, (uint32_t)0); + + dst = (struct adaptived_cgroup_value *)read_data; + ASSERT_EQ(dst->type, ADAPTIVED_CGVAL_FLOAT); + ASSERT_FLOAT_EQ(dst->value.float_value, 123.4); + + free_shared_data(&cse, false); + free(cse.name); +} + +TEST_F(SharedDataTest, CgroupStringDataShare) +{ + struct adaptived_cgroup_value *src, *dst; + enum adaptived_sdata_type type; + struct adaptived_cause cse; + void *read_data; + uint32_t flags; + int ret, cnt; + char *cstr; + + populate_cause(&cse, 0); + + src = (struct adaptived_cgroup_value *)malloc(sizeof(struct adaptived_cgroup_value)); + ASSERT_NE(src, nullptr); + + cstr = (char *)malloc(sizeof(char) * 128); + ASSERT_NE(cstr, nullptr); + sprintf(cstr, "/sys/fs/cgroup/test012.slice/"); + + src->type = ADAPTIVED_CGVAL_STR; + src->value.str_value = cstr; + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_CGROUP, src, NULL, 0); + ASSERT_EQ(ret, 0); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 1); + + ret = adaptived_get_shared_data(&cse, 0, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_CGROUP); + ASSERT_EQ(read_data, src); + ASSERT_EQ(flags, (uint32_t)0); + + dst = (struct adaptived_cgroup_value *)read_data; + ASSERT_EQ(dst->type, ADAPTIVED_CGVAL_STR); + ASSERT_STREQ(dst->value.str_value, "/sys/fs/cgroup/test012.slice/"); + + free_shared_data(&cse, false); + free(cse.name); +} + +TEST_F(SharedDataTest, NameValueDataShare) +{ + struct adaptived_name_and_value *src, *dst; + struct adaptived_cgroup_value *cg; + enum adaptived_sdata_type type; + struct adaptived_cause cse; + void *read_data; + uint32_t flags; + int ret, cnt; + char *name; + + populate_cause(&cse, 0); + + src = (struct adaptived_name_and_value *)malloc(sizeof(struct adaptived_name_and_value)); + ASSERT_NE(src, nullptr); + + cg = (struct adaptived_cgroup_value *)malloc(sizeof(struct adaptived_cgroup_value)); + ASSERT_NE(cg, nullptr); + + name = (char *)malloc(sizeof(char) * 32); + ASSERT_NE(name, nullptr); + sprintf(name, "database13.scope"); + + cg->type = ADAPTIVED_CGVAL_LONG_LONG; + cg->value.ll_value = 135798642; + + src->name = name; + src->value = cg; + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_NAME_VALUE, src, NULL, 0); + ASSERT_EQ(ret, 0); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 1); + + ret = adaptived_get_shared_data(&cse, 0, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_NAME_VALUE); + ASSERT_EQ(read_data, src); + ASSERT_EQ(flags, (uint32_t)0); + + dst = (struct adaptived_name_and_value *)read_data; + ASSERT_EQ(dst->value->type, ADAPTIVED_CGVAL_LONG_LONG); + ASSERT_EQ(dst->value->value.ll_value, 135798642); + + free_shared_data(&cse, false); + free(cse.name); +} + +static void free_sdata3(void * const data) +{ + free(data); +} + +TEST_F(SharedDataTest, MultipleDataShares) +{ + struct adaptived_cgroup_value *src_cgstr, *dst_cgstr; + struct adaptived_cgroup_value *src_cgflt, *dst_cgflt; + char *src_str, *dst_str, *cgstr; + enum adaptived_sdata_type type; + struct adaptived_cause cse; + int *src_custom, *dst_custom; + void *read_data; + uint32_t flags; + int ret, cnt; + + populate_cause(&cse, 0); + + src_custom = (int *)malloc(sizeof(int) * 4); + ASSERT_NE(src_custom, nullptr); + src_custom[0] = 111; + src_custom[1] = 2222; + src_custom[2] = 33333; + src_custom[3] = 444444; + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_CUSTOM, src_custom, + &free_sdata3, ADAPTIVED_SDATAF_PERSIST); + ASSERT_EQ(ret, 0); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 1); + + src_str = (char *)malloc(sizeof(char) * 12); + ASSERT_NE(src_str, nullptr); + sprintf(src_str, "abc123"); + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_STR, src_str, NULL, 0); + ASSERT_EQ(ret, 0); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 2); + + src_cgstr = (struct adaptived_cgroup_value *)malloc( + sizeof(struct adaptived_cgroup_value)); + ASSERT_NE(src_cgstr, nullptr); + + cgstr = (char *)malloc(sizeof(char) * 128); + sprintf(cgstr, "/sys/fs/cgroup/database.slice/test012.scope"); + + src_cgstr->type = ADAPTIVED_CGVAL_STR; + src_cgstr->value.str_value = cgstr; + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_CGROUP, src_cgstr, NULL, 0); + ASSERT_EQ(ret, 0); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 3); + + src_cgflt = (struct adaptived_cgroup_value *)malloc( + sizeof(struct adaptived_cgroup_value)); + ASSERT_NE(src_cgflt, nullptr); + src_cgflt->type = ADAPTIVED_CGVAL_FLOAT; + src_cgflt->value.float_value = 56789.1234; + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_CGROUP, src_cgflt, + NULL, ADAPTIVED_SDATAF_PERSIST); + ASSERT_EQ(ret, 0); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 4); + + ret = adaptived_get_shared_data(&cse, 0, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_CUSTOM); + ASSERT_EQ(read_data, src_custom); + ASSERT_EQ(flags, ADAPTIVED_SDATAF_PERSIST); + + dst_custom = (int *)read_data; + + ASSERT_EQ(dst_custom[0], 111); + ASSERT_EQ(dst_custom[1], 2222); + ASSERT_EQ(dst_custom[2], 33333); + ASSERT_EQ(dst_custom[3], 444444); + + ret = adaptived_get_shared_data(&cse, 1, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_STR); + ASSERT_EQ(read_data, src_str); + ASSERT_EQ(flags, (uint32_t)0); + + dst_str = (char *)read_data; + ASSERT_STREQ(dst_str, "abc123"); + + ret = adaptived_get_shared_data(&cse, 2, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_CGROUP); + ASSERT_EQ(read_data, src_cgstr); + ASSERT_EQ(flags, (uint32_t)0); + + dst_cgstr = (struct adaptived_cgroup_value *)read_data; + ASSERT_EQ(dst_cgstr->type, ADAPTIVED_CGVAL_STR); + ASSERT_STREQ(dst_cgstr->value.str_value, "/sys/fs/cgroup/database.slice/test012.scope"); + + ret = adaptived_get_shared_data(&cse, 3, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_CGROUP); + ASSERT_EQ(read_data, src_cgflt); + ASSERT_EQ(flags, ADAPTIVED_SDATAF_PERSIST); + + dst_cgflt = (struct adaptived_cgroup_value *)read_data; + ASSERT_EQ(dst_cgflt->type, ADAPTIVED_CGVAL_FLOAT); + ASSERT_FLOAT_EQ(dst_cgflt->value.float_value, 56789.1234); + + ret = adaptived_get_shared_data(&cse, 4, &type, &read_data, &flags); + ASSERT_EQ(ret, -ERANGE); + + free_shared_data(&cse, false); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 2); + + ret = adaptived_get_shared_data(&cse, 0, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_CUSTOM); + ASSERT_EQ(read_data, src_custom); + ASSERT_EQ(flags, ADAPTIVED_SDATAF_PERSIST); + + ret = adaptived_get_shared_data(&cse, 1, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_CGROUP); + ASSERT_EQ(read_data, src_cgflt); + ASSERT_EQ(flags, ADAPTIVED_SDATAF_PERSIST); + + dst_cgflt = (struct adaptived_cgroup_value *)read_data; + ASSERT_EQ(dst_cgflt->type, ADAPTIVED_CGVAL_FLOAT); + ASSERT_FLOAT_EQ(dst_cgflt->value.float_value, 56789.1234); + + free_shared_data(&cse, true); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 0); + + free(cse.name); +} + +TEST_F(SharedDataTest, UpdateSharedData) +{ + struct adaptived_cgroup_value *src, *dst; + enum adaptived_sdata_type type; + struct adaptived_cause cse; + char *cstr, *src_str; + void *read_data; + uint32_t flags; + int ret, cnt; + + populate_cause(&cse, 0); + + src = (struct adaptived_cgroup_value *)malloc(sizeof(struct adaptived_cgroup_value)); + + cstr = (char *)malloc(sizeof(char) * 128); + ASSERT_NE(cstr, nullptr); + sprintf(cstr, "/sys/fs/cgroup/test012.slice/"); + + src->type = ADAPTIVED_CGVAL_STR; + src->value.str_value = cstr; + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_CGROUP, src, NULL, 0); + ASSERT_EQ(ret, 0); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 1); + + src_str = (char *)malloc(sizeof(char) * 16); + ASSERT_NE(src_str, nullptr); + sprintf(src_str, "test012.slice"); + + ret = adaptived_write_shared_data(&cse, ADAPTIVED_SDATA_STR, src_str, NULL, 0); + ASSERT_EQ(ret, 0); + + ret = adaptived_get_shared_data(&cse, 0, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_CGROUP); + ASSERT_EQ(read_data, src); + ASSERT_EQ(flags, (uint32_t)0); + + dst = (struct adaptived_cgroup_value *)read_data; + ASSERT_EQ(dst->type, ADAPTIVED_CGVAL_STR); + ASSERT_STREQ(dst->value.str_value, "/sys/fs/cgroup/test012.slice/"); + + /* free the string stored in the cgroup struct */ + adaptived_free_cgroup_value(dst); + /* free the entire cgroup struct */ + free(dst); + + /* create and populate a new cgroup struct */ + src = (struct adaptived_cgroup_value *)malloc(sizeof(struct adaptived_cgroup_value)); + + cstr = (char *)malloc(sizeof(char) * 128); + ASSERT_NE(cstr, nullptr); + sprintf(cstr, "/sys/fs/cgroup/test012.slice/database.scope"); + + src->type = ADAPTIVED_CGVAL_STR; + src->value.str_value = cstr; + + ret = adaptived_update_shared_data(&cse, 0, ADAPTIVED_SDATA_CGROUP, src, 0); + ASSERT_EQ(ret, 0); + + ret = adaptived_get_shared_data(&cse, 0, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_CGROUP); + ASSERT_EQ(read_data, src); + ASSERT_EQ(flags, (uint32_t)0); + + dst = (struct adaptived_cgroup_value *)read_data; + ASSERT_EQ(dst->type, ADAPTIVED_CGVAL_STR); + ASSERT_STREQ(dst->value.str_value, "/sys/fs/cgroup/test012.slice/database.scope"); + + ret = adaptived_get_shared_data(&cse, 1, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_STR); + ASSERT_EQ(read_data, src_str); + ASSERT_EQ(flags, (uint32_t)0); + + free(read_data); + + src_str = (char *)malloc(sizeof(char) * 40); + ASSERT_NE(src_str, nullptr); + sprintf(src_str, "This is an even longer string :)"); + + ret = adaptived_update_shared_data(&cse, 1, ADAPTIVED_SDATA_STR, src_str, + ADAPTIVED_SDATAF_PERSIST); + ASSERT_EQ(ret, 0); + + free_shared_data(&cse, false); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 1); + + ret = adaptived_get_shared_data(&cse, 0, &type, &read_data, &flags); + ASSERT_EQ(ret, 0); + ASSERT_EQ(type, ADAPTIVED_SDATA_STR); + ASSERT_EQ(read_data, src_str); + ASSERT_EQ(flags, ADAPTIVED_SDATAF_PERSIST); + + free_shared_data(&cse, true); + + cnt = adaptived_get_shared_data_cnt(&cse); + ASSERT_EQ(cnt, 0); + + free(cse.name); +} diff --git a/adaptived/tests/gunit/Makefile.am b/adaptived/tests/gunit/Makefile.am index 99b9855..577beaf 100644 --- a/adaptived/tests/gunit/Makefile.am +++ b/adaptived/tests/gunit/Makefile.am @@ -26,7 +26,8 @@ gtest_SOURCES = gtest.cpp \ 008-cgroup_get_procs.cpp \ 009-cgroup_detect.cpp \ 010-adaptived_get_schedstats.cpp \ - 011-kill_processes_sort.cpp + 011-kill_processes_sort.cpp \ + 012-shared_data.cpp gtest_LDFLAGS = -L$(top_srcdir)/googletest/googletest -l:libgtest.so \ -rpath $(abs_top_srcdir)/googletest/googletest