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 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