tests: added tests package
authorPrajwal A N <an.prajwal@samsung.com>
Fri, 31 Jul 2015 11:42:57 +0000 (20:42 +0900)
committerPrajwal A N <an.prajwal@samsung.com>
Tue, 15 Dec 2015 00:28:55 +0000 (09:28 +0900)
* added code to test lowmem module in resourced
* added util programs and scripts

Change-Id: Idb8914af71febb32e34082bfd224fc6213fe827e
Signed-off-by: Prajwal A N <an.prajwal@samsung.com>
20 files changed:
CMakeLists.txt
packaging/resourced.spec
resourced_tests.manifest [new file with mode: 0644]
src/CMakeLists.txt
src/memory/vmpressure-lowmem-handler.c
src/tests/CMakeLists.txt [new file with mode: 0644]
src/tests/README [new file with mode: 0644]
src/tests/include/resourced_tests.h [new file with mode: 0644]
src/tests/memory/Notes [new file with mode: 0644]
src/tests/memory/include/resourced_memory_test.h [new file with mode: 0644]
src/tests/memory/include/utils.h [new file with mode: 0644]
src/tests/memory/resourced_memory_test.c [new file with mode: 0644]
src/tests/memory/resourced_memory_test_helper.c [new file with mode: 0644]
src/tests/memory/utils.c [new file with mode: 0644]
src/tests/scripts/Notes [new file with mode: 0644]
src/tests/scripts/resourced_dbus_sender.sh [new file with mode: 0755]
src/tests/scripts/resourced_util_memory_test.sh [new file with mode: 0644]
src/tests/util/Notes [new file with mode: 0644]
src/tests/util/resourced_dummy_process.c [new file with mode: 0644]
src/tests/util/resourced_hogger_memory.c [new file with mode: 0644]

index 4fc6f409ae2f8320ae6583de6b2e4fe52c189963..75a47c369a70436abdf1e033c18a7762cc62daa0 100644 (file)
@@ -76,6 +76,7 @@ SET(VIP_SOURCE_DIR              ${SOURCE_DIR}/vip-agent)
 SET(TIMER_SOURCE_DIR            ${SOURCE_DIR}/timer-slack)
 SET(BLOCK_SOURCE_DIR            ${SOURCE_DIR}/block)
 SET(RESOURCED_DBUS_SOURCE_DIR   ${SOURCE_DIR}/resourced-dbus)
+SET(TESTS_SOURCE_DIR           ${SOURCE_DIR}/tests)
 
 INSTALL(FILES ${CMAKE_SOURCE_DIR}/lib${RESOURCED}.pc DESTINATION lib/pkgconfig)
 INSTALL(FILES ${CMAKE_SOURCE_DIR}/resourced.conf DESTINATION /etc/dbus-1/system.d)
index fea10538c71d4e8003d36e7809c0ce7319065638..c3463d868f795ba221f1f17fc64267d01876a978 100644 (file)
@@ -19,6 +19,8 @@ Source2:    resourced-cpucgroup.service
 %define network_state OFF
 %define memory_eng ON
 
+%define tests_module ON
+
 %if "%{?tizen_profile_name}" == "mobile"
        %define swap_module ON
        %define freezer_module ON
@@ -149,6 +151,7 @@ export FFLAGS="$FFLAGS -DTIZEN_DEBUG_ENABLE"
         -DMEMORY_MODULE=%{memory_module} \
         -DWEARABLE_NOTI=%{wearable_noti} \
         -DBLOCK_MODULE=%{block_module} \
+        -DTESTS_MODULE=%{tests_module}
 
 make %{?jobs:-j%jobs}
 
@@ -264,6 +267,16 @@ fi
 %attr(-,root, root) %{_bindir}/mem-stress
 %{_libdir}/systemd/system/mem-stress.service
 %{_libdir}/systemd/system/graphical.target.wants/mem-stress.service
+%if %{?tests_module} == ON
+%defattr(-,root,root,-)
+%{_bindir}/resourced_memory_test
+%defattr(-,root,root,-)
+%{_bindir}/resourced_dummy_process
+%defattr(-,root,root,-)
+%{_bindir}/resourced_hogger_memory
+%{_bindir}/resourced_dbus_sender.sh
+%{_bindir}/resourced_util_memory_test.sh
+%endif
 
 %files -n libresourced
 %manifest libresourced.manifest
@@ -284,4 +297,3 @@ fi
 #network part
 %{_libdir}/libresourced.so
 %{_includedir}/system/data_usage.h
-
diff --git a/resourced_tests.manifest b/resourced_tests.manifest
new file mode 100644 (file)
index 0000000..79f9a2f
--- /dev/null
@@ -0,0 +1,6 @@
+<manifest>
+       <request>
+               <domain name="_"/>
+       </request>
+</manifest>
+
index 6156d9750ccd6cf5aab8a0289a0f1229d094c499..38772b6bd3a6bfc7ce8e72d131a5180e91d66fac 100644 (file)
@@ -339,3 +339,7 @@ ADD_SUBDIRECTORY(mem-stress)
 ADD_SUBDIRECTORY(${PROC-STAT_SOURCE_DIR})
 ADD_SUBDIRECTORY(${MEMPS_SOURCE_DIR})
 ADD_SUBDIRECTORY(${NETWORK_SOURCE_DIR})
+IF("${TESTS_MODULE}" STREQUAL "ON")
+  ADD_SUBDIRECTORY(${TESTS_SOURCE_DIR})
+ENDIF()
+
index 816b1254a7c22ba8fab20a6fed22709a2e905e0d..e9d796a08adda590e1c94b8d1ffeed69dac706dc 100644 (file)
@@ -1092,8 +1092,11 @@ static void change_lowmem_state(unsigned int mem_state)
        if (cur_mem_state == mem_state)
                return;
 
-       _I("[LOW MEM STATE] %s ==> %s", convert_memstate_to_str(cur_mem_state),
-               convert_memstate_to_str(mem_state));
+       _I("[LOW MEM STATE] %s ==> %s, changed available = %d MB",
+                       convert_memstate_to_str(cur_mem_state),
+                       convert_memstate_to_str(mem_state),
+                       proc_get_mem_available());
+
        cur_mem_state = mem_state;
 
        adjust_dynamic_threshold(MEMCG_BACKGROUND);
@@ -1856,9 +1859,10 @@ static void lowmem_press_root_cgroup_handler(void)
        for (i = 0; i < ARRAY_SIZE(lpe); i++) {
                if ((cur_mem_state == lpe[i].cur_mem_state)
                                && (mem_state == lpe[i].new_mem_state)) {
-                       _D("cur_mem_state = %s, new_mem_state = %s",
+                       _D("cur_mem_state = %s, new_mem_state = %s, available = %d",
                                        convert_memstate_to_str(cur_mem_state),
-                                       convert_memstate_to_str(mem_state));
+                                       convert_memstate_to_str(mem_state),
+                                       available);
                        lpe[i].action();
                }
        }
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..84ab56b
--- /dev/null
@@ -0,0 +1,40 @@
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -Wall -g")
+
+#Dependencies
+SET(requires libsystemd)
+
+#Include directories
+INCLUDE_DIRECTORIES(
+       ${TESTS_SOURCE_DIR}/include
+       ${TESTS_SOURCE_DIR}/memory/include
+)
+
+#Setting sources
+SET(SRC_MEMORY_TEST ${TESTS_SOURCE_DIR}/memory/resourced_memory_test.c
+                       ${TESTS_SOURCE_DIR}/memory/resourced_memory_test_helper.c
+                       ${TESTS_SOURCE_DIR}/memory/utils.c)
+
+SET(SRC_DUMMY_PROCESS ${TESTS_SOURCE_DIR}/util/resourced_dummy_process.c)
+SET(SRC_MEMORY_HOG ${TESTS_SOURCE_DIR}/util/resourced_hogger_memory.c)
+
+#Linking flags
+INCLUDE(FindPkgConfig)
+pkg_check_modules(daemon_pkgs REQUIRED ${requires})
+FOREACH(flag ${daemon_pkgs_CFLAGS})
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${EXTRA_CFLAGS}")
+
+#Adding executables
+ADD_EXECUTABLE (resourced_memory_test ${SRC_MEMORY_TEST})
+TARGET_LINK_LIBRARIES(resourced_memory_test ${daemon_pkgs_LDFLAGS})
+INSTALL(TARGETS resourced_memory_test DESTINATION bin)
+
+ADD_EXECUTABLE(resourced_dummy_process ${SRC_DUMMY_PROCESS})
+INSTALL(TARGETS resourced_dummy_process DESTINATION bin)
+
+ADD_EXECUTABLE(resourced_hogger_memory ${SRC_MEMORY_HOG})
+INSTALL(TARGETS resourced_hogger_memory DESTINATION bin)
+
+INSTALL(PROGRAMS ${TESTS_SOURCE_DIR}/scripts/resourced_dbus_sender.sh DESTINATION bin)
+INSTALL(PROGRAMS ${TESTS_SOURCE_DIR}/scripts/resourced_util_memory_test.sh DESTINATION bin)
diff --git a/src/tests/README b/src/tests/README
new file mode 100644 (file)
index 0000000..3edf625
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @file  README
+ * @desc  Brief detail of the package and structure of the package
+ * @author Prajwal A N (an.prajwal@samsung.com)
+ **/
+
+Resourced tests package
+======================
+This package provides test programs to test different modules of resourced
+
+Directory structure
+===================
+There subdirectories are named according to the module in resourced they test.
+src/tests/
+       | memory        (tests the memory module of resourced)
+       | util          (provides some utilities to use while testing)
+       | scripts       (provides some scripts that need to used when executing the tests)
+
+Important subdirectories have Notes file which details the developer notes.
+The code is well documented and this can be used to understand the programs in this package.
diff --git a/src/tests/include/resourced_tests.h b/src/tests/include/resourced_tests.h
new file mode 100644 (file)
index 0000000..cb31dd3
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @file  resourced_tests.h
+ * @desc  common definitions for test package
+ **/
+
+#ifndef __RESOURCED_TESTS_H__
+#define __RESOURCED_TESTS_H__
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <systemd/sd-journal.h>
+
+#define STRING_MAX 256
+
+#define _E(fmt, arg...) sd_journal_print(LOG_ERR, "[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg)
+#define _D(fmt, arg...) sd_journal_print(LOG_DEBUG, "[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg)
+#define _I(fmt, arg...) sd_journal_print(LOG_INFO, "[%s,%d] "fmt, __FUNCTION__, __LINE__, ##arg)
+
+/* resourced_tests package error codes */
+enum {
+       ERROR_NONE = 0,
+       ERROR_INVALID_INPUT = -1,
+       ERROR_IO = -2,
+       ERROR_MEMORY = -3,
+       ERROR_FAIL = -4,
+};
+
+#endif
diff --git a/src/tests/memory/Notes b/src/tests/memory/Notes
new file mode 100644 (file)
index 0000000..c0773d9
--- /dev/null
@@ -0,0 +1,90 @@
+Resourced memory test
+=====================
+Tests the memory module of resourced. The following tests are offered:
+* Proactive oom killer: Tests the proactive oom killer called by the prelaunch dbus signal handler
+* OOM trigger: Tests the OOM trigger dbus signal handler
+* Memory pressure handler (normal): Tests the memory pressure eventfd interface of resourced (without callback)
+* Memory pressure handler (callback): Tests the memory pressure eventfd interface of resourced (with callback)
+
+Usage
+=====
+This test tool can be used as follows:
+<cmd-prompt>$ resourced_memory_test <test-name>
+       Supported test names are: proactive, oom_trigger and vmpressure
+
+* Executing this tool in the above way results in logging happening in journalctl.
+* The tool is interactive and gives instructions on what to do.
+* To use different tests consecutively, it is preferred to kill the processes started in the previous test.
+  This is preferred as it becomes difficult to create the base usage scenario of the next test with less available memory.
+  This can be done by executing the following command.
+       <cmd-prompt>$ killall resourced_memory_test
+* Along with this tool, the user can run journalctl to see the logs of the test tool.
+  The memory test provides its PID to enable following only this process in journalctl as follows:
+       $ journalctl -f _PID=<pid-of-memory-test>
+* Also, to see the working of the resourced in response to the actions of the test tool, the user can run dlog in 2 ways:
+       $ dlogutil RESOURCED -v time                    (to see all the logs in resourced)
+       $ dlogutil RESOURCED -v time | grep mem         (to see only memory module logs in resourced)
+
+Internal working
+================
+The basis of all memory tests are going to be the same.
+* First create a base usage scenario (i.e. create processes in different cgroups according
+  to the test) so as to prep the target to trigger the needed interface in resourced
+* Next run the test function corresponding to the input test.
+  This test function triggers the interface in resourced and then checks for working of the
+  trigger interface by checking for kills in the cgroups according to the conditions of the test
+* Depending on the success of the checks, the test success status is determined.
+
+Tests details
+=============
+* Proactive oom killer: Tests the proactive oom killer called by the prelaunch dbus signal handler
+       # The proactive oom killer is called before launching new apps.
+       # The purpose of this killer is to check if enough memory is available before launching a new app
+       # We trigger this killer by calling the prelaunch signal handler using the dbus sender script
+               (see the script on details of how to use the Prelaunch_OOM option)
+       # The proactive oom killer kills from the swap and background cgroups only until the available memory
+         reaches proactive threshold leave. It does not kill processes with negative oom and also kills only
+         num_max_victims # of processes (this value is set by the memory.conf file in resourced).
+       # We create processes throughout all the cgroups with more processes in the memory and background cgroup.
+       # Some of these processes are with -ve oom and some are with +ve oom.
+       # After sending the dbus signal, the test function checks if the correct processes are killed/not killed.
+
+* OOM trigger: Tests the OOM trigger dbus signal handler
+       # OOM trigger function is called when resourced receives the respective dbus signal
+       # The purpose of this killer is to clear all possible memory in swap and background cgroup
+         upon receiving the dbus oom signal
+       # We trigger this interface by calling the oom trigger handler using the dbus sender script
+               (see the script for details on how to use the OOM_Trigger option)
+       # The oom trigger killer kills from the swap and background cgroup without any restrictions on
+         recovery target or the number of kills. Only processes with -ve oom are not killed.
+       # We create processes throughout all the cgroups with more processes in the memory and background cgroup.
+       # Some of the processes are with -ve oom, but most are with +ve oom.
+       # After sending the dbus signal, the test function checks if the processes in the cgroup are correctly killed/not killed.
+
+* Memory pressure handler (normal): Tests the memory pressure eventfd interface of resourced (without callback)
+       # This interface is activated when the usage of the root memcg exceeds the medium pressure level
+       # The purpose of this killer is to free enough memory after the pressure level exceeds medium
+       # We trigger this interface by using more than medium threshold of memory when creating the base usage scenario
+       # Kernel notifies resourced of the crossing of the medium pressure level and the interface gets activated.
+       # We wait for some time before the interface is activated and then check for kills.
+       # This killer kills processes in each cgroup starting from the swap cgroup proceeding till the root cgroup.
+       # This killing continues until the available memory reaches threshold leave of the memory configuration/max victims
+         are killed in a cgroup.
+       # We create processes throughout all the cgroups with more processes in the background and foreground cgroups
+       # Most of these processes have +ve oom scores (to ensure that enough memory is freed in a single iteration of the killer)
+       # After waiting for activation of the interface, we check if processes were killed correctly.
+
+* Memory pressure handler (callback): Tests the memory pressure eventfd interface of resourced (with callback)
+       # This interface is a follow up killer called if the first run of the mempressure killer does not free enough memory
+       # We trigger this killer in the same way as mentioned in the normal test above
+       # Since we dont want the first run of the killer to free enough memory, most processes created in this test
+         have -ve oom. This results in the first run of the vmpressure killer not killing them and calling the callback.
+       # We then change the oom of the processes to a +ve value just in time for the callback to kill them and
+         thus allowing us to check for the correctness of the callback killer.
+       # We accomplish this by making the user of the test to let us know as soon as the first run of the oom killer thread
+         starts. They can see this by tracking the dlog of resourced memory module.
+       # Once they signal this, we check if the processes were killed by the first run. And then we change the oom before
+         the callback is called and check after the finish of the callback if the processes were killed correctly.
+       # ** To make this test work correctly, we need to change the OOM_TIMER_INTERVAL constant in vmpressure module to
+         around 15. This is because, it takes some time for the user to notify the test and if the callback is called
+         by this time, we cannot change the oom scores in time to test the working of the callback.
diff --git a/src/tests/memory/include/resourced_memory_test.h b/src/tests/memory/include/resourced_memory_test.h
new file mode 100644 (file)
index 0000000..56531a1
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @file  resourced_memory_tests.h
+ * @desc  common definitions for memory tests program
+ **/
+
+#ifndef __RESOURCED_MEMORY_TEST_H__
+#define __RESOURCED_MEMORY_TEST_H__
+
+#include "resourced_tests.h"
+
+/* Memory margins which are used to define lowmem boundaries (in resourced) */
+#define RESOURCED_THRESHOLD_MARGIN 10
+
+/* Maximum kills limit in resourced */
+#define RESOURCED_MAX_VICTIMS 10
+
+#define MEMCG_ROOT "/sys/fs/cgroup/memory"
+
+/* Memory cgroups as defined in resourced
+ * Any addition of deletion to this enum list should
+ * be accompanied by appropriate changes to the memcg_*
+ * arrays defined below
+ */
+enum {
+       MEMCG_MEMORY,
+       MEMCG_PLATFORM,
+       MEMCG_FOREGROUND,
+       MEMCG_PREVIOUS,
+       MEMCG_FAVORITE,
+       MEMCG_BACKGROUND,
+       MEMCG_SWAP,
+       MEMCG_MAX,
+};
+
+/* Resourced memory tests available in the resourced_memory_test program
+ * Any addition of deletion to this enum list should be accompanied by
+ * appropriate changes to the test_name and memcg_* arrays defined below
+ */
+enum {
+       TEST_PROACTIVE_KILLER,
+       TEST_OOM_DBUS_TRIGGER,
+       TEST_VMPRESSURE_ROOT,
+       TEST_VMPRESSURE_ROOT_CB,
+       TEST_VMPRESSURE_CGROUP,
+       TEST_MAX,
+};
+
+/* Name of each test enumerated above (used for input and debug purposes) */
+extern char *test_name[TEST_MAX];
+
+/* Limits (associated with the target) present in the memory module of resourced
+ * These limits are used to create the needed scenario to activate the appropriate
+ * interface of the vmpressure module of resourced
+ * Any addition of deletion to this enum list should be accompanied by appropriate
+ * changes to the memcg_* arrays defined below
+ */
+enum {
+       LIMIT_TOTAL_MEMORY,
+       LIMIT_THRESHOLD_SWAP,
+       LIMIT_THRESHOLD_LOW,
+       LIMIT_THRESHOLD_MEDIUM,
+       LIMIT_THRESHOLD_LEAVE,
+       LIMIT_DYNAMIC_THRESHOLD,
+       LIMIT_DYNAMIC_THRESH_LEAVE,
+       LIMIT_MAX_VICTIMS,
+       LIMIT_MAX,
+};
+
+/* Target memory configurations supported by resourced
+ * Currently the tests package only tests the 750MB configuration (Z1)
+ * Any addition of deletion to this enum list should be accompanied by
+ * appropriate changes to the vmpressure_* arrays defined in resourced_memory_test.c
+ */
+enum {
+       MEMCONF_768,
+       MEMCONF_MAX,
+};
+
+/* Name of the memcg subcgroups as defined by resourced */
+extern char *memcg_name[MEMCG_MAX];
+
+/* Defines the fraction of memory to be allocated to each
+ * cgroup (in a particular test) when creating the base usage
+ * scenario of that test
+ */
+extern double memcg_base_usage_ratio[TEST_MAX][MEMCG_MAX];
+
+/* Defines the number of processes to be started in each
+ * cgroup (in a particular test) when creating the base usage
+ * scenario of that test. All processes started in a cgroup
+ * use the same amount of memory (memory allocated to the cgroup
+ * is equally divided among the processes of the cgroup).
+ */
+extern int memcg_base_process_num[TEST_MAX][MEMCG_MAX];
+
+/* Defines the oom adj score of each process (of the number
+ * of processes defined in memcg_base_process_num) started in each
+ * cgroup (for a particular test)
+ */
+extern int memcg_base_process_oom[TEST_MAX][MEMCG_MAX][RESOURCED_MAX_VICTIMS];
+
+/* Process IDs of each process (of the number of processes defined in
+ * memcg_base_process_num) started in each cgroup (for a particular test)
+ */
+extern int pid_list[][RESOURCED_MAX_VICTIMS];
+
+extern int pid_memory_list[][RESOURCED_MAX_VICTIMS];
+
+int get_memconf(int total);
+int launch_memory_hogger(int memory, int oom, char *cgroup_path);
+void populate_cgroup(int test, int memcg_index, int target);
+int check_cgroup_kill_status(int test, int memcg_index, int kill_flag,
+                               int recovery_target, int *recovered,
+                               int num_max_victims, int *num_victims);
+
+#endif
diff --git a/src/tests/memory/include/utils.h b/src/tests/memory/include/utils.h
new file mode 100644 (file)
index 0000000..f6ee476
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @file  utils.h
+ * @desc  file IO and proc fs functions
+ **/
+
+#ifndef __RESOURCED_TESTS_UTILS_H__
+#define __RESOURCED_TESTS_UTILS_H__
+
+#include "resourced_tests.h"
+
+/* Memory size conversion macros */
+#define kBtoMB(val) (int)((val*1000) >> 20)
+#define KBtoB(val) (int)(val << 10)
+#define MBtoB(val) (int)(val << 20)
+
+/* File write abstract functions */
+int fwrite_str(char *path, char *str);
+int fwrite_int(char *path, int num);
+
+/* Proc fs util functions */
+unsigned int procfs_get_available(void);
+unsigned int procfs_get_total(void);
+int procfs_set_oom_score_adj(int pid, int oom);
+
+int pid_exists(int pid);
+
+#endif
diff --git a/src/tests/memory/resourced_memory_test.c b/src/tests/memory/resourced_memory_test.c
new file mode 100644 (file)
index 0000000..634b87c
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @file  resourced_memory_test.c
+ * @desc  test program for memory module of resourced
+ **/
+
+#include <time.h>
+#include "resourced_memory_test.h"
+#include "utils.h"
+
+#define PROACTIVE_SLEEP 15
+#define VMPRESSURE_ROOT_SLEEP 5
+#define VMPRESSURE_ROOT_CB_SLEEP 32
+
+char *test_name[TEST_MAX] = {
+       "proactive",
+       "oom_trigger",
+       "vmpressure_root",
+       "vmpressure_root_cb",
+       "vmpressure_cgroup",
+};
+
+char *memcg_name[MEMCG_MAX] = {
+       "root",
+       "platform",
+       "foreground",
+       "previous",
+       "favorite",
+       "background",
+       "swap"
+};
+
+double memcg_base_usage_ratio[TEST_MAX][MEMCG_MAX] = {
+       {0.2, 0, 0.2, 0, 0, 0.4, 0.2},          /* Proactive killer */
+       {0, 0, 0.2, 0, 0, 0.6, 0.2},            /* OOM dbus trigger */
+       {0.05, 0, 0.4, 0.05, 0, 0.4, 0.15},     /* VmPressure on root cgroup with no callback */
+       {0.1, 0, 0.25, 0.05, 0.05, 0.4, 0.15},  /* VmPressure on root cgroup with callback */
+       {0, 0, 1, 1, 1, 1, 1},                  /* VmPressure on other cgroups */
+};
+
+int memcg_base_process_num[TEST_MAX][MEMCG_MAX] = {
+       {1, 0, 1, 0, 0, 5, 3},                  /* Proactive killer */
+       {0, 0, 1, 0, 0, 6, 3},                  /* OOM dbus trigger */
+       {2, 0, 5, 1, 0, 6, 3},                  /* VmPressure on root cgroup with no callback */
+       {2, 0, 3, 1, 1, 5, 3},                  /* VmPressure on root cgroup with callback */
+       {1, 0, 1, 0, 0, 5, 3},                  /* VmPressure on other cgroups */
+};
+
+int memcg_base_process_oom[TEST_MAX][MEMCG_MAX][RESOURCED_MAX_VICTIMS] = {
+       /* Proactive killer */
+       {
+               {-900},
+               {},
+               {150},
+               {},
+               {},
+               {-900, -900, 300, 300, 350},
+               {-900, 350, 350}
+       },
+       /* OOM dbus trigger */
+       {
+               {},
+               {},
+               {150},
+               {},
+               {},
+               {-900, -900, 300, 300, 300, 350},
+               {-900, 350, 350}
+       },
+       /* VmPressure on root cgroup with no callback */
+       {
+               {-900, 200},
+               {},
+               {-900, 150, 150, 170, 200},
+               {230},
+               {},
+               {-900, -900, 300, 300, 350, 400},
+               {-900, 350, 350}
+       },
+       /* VmPressure on root cgroup with callback */
+       {
+               {-900, -900},
+               {},
+               {-900, -900, 150},
+               {150},
+               {-900},
+               {-900, -900, -900, -900, 350},
+               {-900, -900, 350}
+       },
+       /* VmPressure on different cgroups */
+       {
+               {-900},
+               {},
+               {150},
+               {},
+               {},
+               {-900, -900, 300, 300, 350},
+               {-900, 350, 350}
+       },
+};
+
+/* The values of the limit specified in the LIMIT_* enum list
+ * for each memory configuration in the MEMCONF_* enum list
+ */
+static int vmpressure_limits[MEMCONF_MAX][LIMIT_MAX] = {
+       {768, 300, 200, 100, 150, 150, 230, 5}
+};
+
+/* Different memory margins defined for each memory configuration */
+enum {
+       MEMORY_MARGIN_LOW,
+       MEMORY_MARGIN_MEDIUM,
+       MEMORY_MARGIN_HIGH,
+       MEMORY_MARGIN_MAX,
+};
+
+/* Memory margins to be used for different memory configurations
+ * These values of these margins are set according to the values of the limits
+ * (in resourced) in the LIMITS_* list. These margins help in managing the
+ * internal memory state of the memory module of resourced
+ */
+static int memory_margin[MEMCONF_MAX][MEMORY_MARGIN_MAX] = {
+       {10, 25, 40}
+};
+
+int pid_list[MEMCG_MAX][RESOURCED_MAX_VICTIMS];
+int pid_memory_list[MEMCG_MAX][RESOURCED_MAX_VICTIMS];
+
+/* Test for proactive oom killer (called by the prelaunch dbus signal handler) */
+int proactive_oom_killer(void)
+{
+       int available, prev_available, target;
+       int to_populate, recovered, recovery_target;
+       int memconf;
+       int pid_bck, pid_swap;
+       int num_max_victims, num_victims;
+       int memcg_index;
+       int ret, ret_val, kill_flag;
+       char bckground_path[STRING_MAX];
+       char swap_path[STRING_MAX];
+       char inp_str[STRING_MAX];
+       struct timespec req, rem;
+
+       available = kBtoMB(procfs_get_available());
+       memconf = get_memconf(kBtoMB(procfs_get_total()));
+
+       /* Populating memory till dynamic threshold */
+       to_populate = available - vmpressure_limits[memconf][LIMIT_DYNAMIC_THRESHOLD];
+       to_populate += memory_margin[memconf][MEMORY_MARGIN_MEDIUM];
+       if (to_populate < 0) {
+               _E("Not able to test proactive oom killer. Not enough memory");
+               return ERROR_MEMORY;
+       }
+
+       /* Add a process each to the background and the swap cgroups (with sizes in ratio 0.4/0.6 of to_populate) */
+       snprintf(bckground_path, sizeof(bckground_path), "%s/%s/cgroup.procs", MEMCG_ROOT, memcg_name[MEMCG_BACKGROUND]);
+       snprintf(swap_path, sizeof(swap_path), "%s/%s/cgroup.procs", MEMCG_ROOT, memcg_name[MEMCG_SWAP]);
+       pid_bck = launch_memory_hogger((int)(to_populate*0.6), 300, bckground_path);
+       pid_swap = launch_memory_hogger((int)(to_populate*0.4), 350, swap_path);
+       if (pid_bck < 0 || pid_swap < 0) {
+               _E("error in creating processes");
+               return ERROR_MEMORY;
+       }
+       prev_available = available = kBtoMB(procfs_get_available());
+       _D("Added %d MB to raise to dynamic threshold (%d MB). current available %d MB",
+                       to_populate, vmpressure_limits[memconf][LIMIT_DYNAMIC_THRESHOLD], available);
+
+       /* Expecting input after the needed dbus call to prelaunch handler is made
+        * Will sleep for PROACTIVE_SLEEP seconds before checking for results
+        * This is to allow time for proactive killer in resourced to finish its operation
+        */
+       printf("Enter input (to continue) after triggering dbus call to prelaunch handler: ");
+       ret = scanf("%s", inp_str);
+       _D("going to sleep for %d seconds before testing output scenario\n\n\n\n", PROACTIVE_SLEEP);
+       req.tv_sec = PROACTIVE_SLEEP;
+       req.tv_nsec = 0;
+       nanosleep(&req, &rem);
+
+       /* Check if the dynamic threshold has been finally reached */
+       available = kBtoMB(procfs_get_available());
+       target = vmpressure_limits[memconf][LIMIT_DYNAMIC_THRESH_LEAVE] + memory_margin[memconf][MEMORY_MARGIN_LOW];
+       if (available >= target)
+               _D("proactive killer reached dynamic threshold leave as expected");
+       else
+               _E("proactive killer did not reach dynamic threshold leave (%d / %d)",
+                               available, vmpressure_limits[memconf][LIMIT_DYNAMIC_THRESH_LEAVE]);
+
+       /* Start checking for kills/no-kills accordingly
+        * Keep track of recovered memory and # of kills for each encountered kill
+        * (Note that this calculation is just approximate and not accurate which is why memory margins are used)
+        */
+       ret_val = ERROR_NONE;
+       recovery_target = vmpressure_limits[memconf][LIMIT_DYNAMIC_THRESH_LEAVE] - prev_available;
+       recovery_target += memory_margin[memconf][MEMORY_MARGIN_LOW];
+       recovered = 0;
+       num_max_victims = vmpressure_limits[memconf][LIMIT_MAX_VICTIMS];
+       num_victims = 0;
+       _D("current available %d MB, recovery target: %d MB", available, recovery_target);
+
+       /* Starting from swap cgroup proceed till the foreground cgroup checking according to the cgroup */
+       for (memcg_index = MEMCG_SWAP; memcg_index >= MEMCG_FOREGROUND; --memcg_index) {
+               _D("checking %s cgroup....", memcg_name[memcg_index]);
+
+               /* kill_flag keeps track of if kills are expected in the current cgroup or not
+                * Swap and background cgroups expect kills (nature of proactive killer)
+                * the other cgroups should not have any kills
+                */
+               kill_flag = 1;
+               switch (memcg_index) {
+               case MEMCG_SWAP:
+                       /* Check if the pid_swap process started above has been killed (expected) */
+                       if (pid_exists(pid_swap))
+                               _E("process %d expected to be killed in swap! (%d / %d)",
+                                               pid_swap, recovered, recovery_target);
+                       else {
+                               _D("process %d killed as expected in swap (%d / %d)",
+                                               pid_swap, recovered, recovery_target);
+                               recovered += (int)(to_populate*0.4);
+                               num_victims++;
+                       }
+                       break;
+               case MEMCG_BACKGROUND:
+                       /* Check if the pid_bck process started above has been killed (expected) */
+                       if (pid_exists(pid_bck)) {
+                               if (recovered < recovery_target && num_victims < num_max_victims)
+                                       _E("process %d expected to be killed in backgrd! (%d / %d)",
+                                                       pid_bck, recovered, recovery_target);
+                       } else {
+                               if (recovered >= recovery_target || num_victims >= num_max_victims)
+                                       _E("process %d should not be killed (%d / %d)",
+                                                       pid_bck, recovered, recovery_target);
+                               _D("process %d killed in background cgroup", pid_bck);
+                               recovered += (int)(to_populate*0.4);
+                               num_victims++;
+                       }
+                       break;
+               default:
+                       /* Disable kill_flag for the remaining cgroups */
+                       kill_flag = 0;
+               }
+
+               ret = check_cgroup_kill_status(TEST_PROACTIVE_KILLER, memcg_index, kill_flag,
+                                               recovery_target, &recovered,
+                                               num_max_victims, &num_victims);
+               if (ret != ERROR_NONE)
+                       ret_val = ret;
+       }
+       return ret_val;
+}
+
+/* Test function for the oom dbus trigger interface of resourced */
+int oom_dbus_trigger(void)
+{
+       int num_max_victims, num_victims;
+       int recovered, recovery_target;
+       int memcg_index, memconf;
+       int ret, ret_val, kill_flag;
+       struct timespec req, rem;
+       char inp_str[STRING_MAX];
+
+       memconf = get_memconf(kBtoMB(procfs_get_total()));
+
+       /* Wait for input signalling call to oom trigger dbus handler */
+       printf("Enter input (to continue) after sending oom trigger dbus signal to resourced: ");
+       ret = scanf("%s", inp_str);
+       _D("going to sleep for %d seconds before testing output scenario\n\n\n\n", PROACTIVE_SLEEP);
+       req.tv_sec = PROACTIVE_SLEEP;
+       req.tv_nsec = 0;
+       nanosleep(&req, &rem);
+
+       /* There is no target for recovery. All eligible processes in the swap
+        * and background cgroups are expected to be killed irrespective of recovered
+        * memory. Thus recovery_target is set to the total available memory on the target.
+        */
+       ret_val = ERROR_NONE;
+       recovery_target = vmpressure_limits[memconf][LIMIT_TOTAL_MEMORY];
+       recovered = 0;
+       num_max_victims = RESOURCED_MAX_VICTIMS;
+       num_victims = 0;
+       for (memcg_index = MEMCG_MAX-1; memcg_index >= MEMCG_FOREGROUND; --memcg_index) {
+               _D("checking %s cgroup", memcg_name[memcg_index]);
+
+               /* kill flag is enabled only for swap and background cgroups */
+               switch (memcg_index) {
+               case MEMCG_SWAP:
+               case MEMCG_BACKGROUND:
+                       kill_flag = 1;
+                       break;
+               default:
+                       kill_flag = 0;
+                       break;
+               }
+
+               ret = check_cgroup_kill_status(TEST_OOM_DBUS_TRIGGER, memcg_index, kill_flag,
+                                               recovery_target, &recovered,
+                                               num_max_victims, &num_victims);
+               if (ret != ERROR_NONE)
+                       ret_val = ret;
+       }
+       return ret_val;
+}
+
+/* Test function to test the memory pressure interface of resourced */
+int vmpressure_root(int test)
+{
+       int ret, ret_val;
+       struct timespec req, rem;
+       int available, prev_available, memconf;
+       int recovery_target, recovered;
+       int num_max_victims, num_victims, kill_flag;
+       int memcg_index;
+       char inp_str[STRING_MAX];
+
+       memconf = get_memconf(kBtoMB(procfs_get_total()));
+
+       /* Instructions to trigger the memory pressure eventfd */
+       _D("open an app on the target now. this would start the callback for medium pressure");
+       _D("ToDo: check why the eventfd on memory pressure is not triggered earlier");
+
+       /* If the test is to check the working at the first call (without callback) */
+       if (test == TEST_VMPRESSURE_ROOT) {
+               ret_val = ERROR_NONE;
+
+               /* Input as soon as you see oom killer thread related messages on the dlog
+                * of resourced. We wait for VMPRESSURE_ROOT_SLEEP seconds to ensure that the
+                * thread has completed.
+                */
+               printf("Enter input (to continue) after oom killer has run once: ");
+               ret = scanf("%s", inp_str);
+               _D("going to sleep for %d seconds before testing output scenario\n\n\n\n", VMPRESSURE_ROOT_SLEEP);
+               req.tv_sec = VMPRESSURE_ROOT_SLEEP;
+               req.tv_nsec = 0;
+               nanosleep(&req, &rem);
+
+               /* Check if the target (threshold leave) has been reached, and if not
+                * set the recovery target according to the available memory before the killer ran
+                * and the threshold leave for the memory configuration
+                */
+               available = kBtoMB(procfs_get_available());
+               if (available >= vmpressure_limits[memconf][LIMIT_THRESHOLD_LEAVE])
+                       _D("threshold leave %dMB reached as expected, available %dMB",
+                                       vmpressure_limits[memconf][LIMIT_THRESHOLD_LEAVE],
+                                       available);
+               else
+                       _E("threshold leave %dMB not reached, available %dMB",
+                                       vmpressure_limits[memconf][LIMIT_THRESHOLD_LEAVE],
+                                       available);
+
+               prev_available = vmpressure_limits[memconf][LIMIT_THRESHOLD_MEDIUM] - memory_margin[memconf][MEMORY_MARGIN_HIGH];
+               recovery_target = vmpressure_limits[memconf][LIMIT_THRESHOLD_LEAVE] - prev_available;
+               recovery_target += memory_margin[memconf][MEMORY_MARGIN_MEDIUM];
+               recovered = 0;
+               for (memcg_index = MEMCG_MAX-1; memcg_index >= MEMCG_MEMORY; --memcg_index) {
+                       _D("checking %s cgroup", memcg_name[memcg_index]);
+
+                       /* OOM killer kills from all cgroups, until the recovery target is met
+                        * The max victims limit is reset for each cgroup (kills from one cgroup
+                        * do not propogate to the count of another cgroup)
+                        */
+                       kill_flag = 1;
+                       num_victims = 0;
+                       num_max_victims = vmpressure_limits[memconf][LIMIT_MAX_VICTIMS];
+                       ret = check_cgroup_kill_status(test, memcg_index, kill_flag,
+                                               recovery_target, &recovered,
+                                               num_max_victims, &num_victims);
+                       if (ret != ERROR_NONE)
+                               ret_val = ret;
+               }
+       } else {
+               int i, pid_list_num;
+               int oom;
+
+               ret_val = ERROR_NONE;
+
+               /* Input after you see that the oom killer thread has started to run
+                * We let the first run go through and check for reach of the target
+                */
+               printf("Enter input (to continue) after the oom killer has started running: ");
+               ret = scanf("%s", inp_str);
+
+               available = kBtoMB(procfs_get_available());
+               if (available >= vmpressure_limits[memconf][LIMIT_THRESHOLD_LEAVE])
+                       _E("threshold leave %dMB should not yet be reached, available %dMB",
+                                       vmpressure_limits[memconf][LIMIT_THRESHOLD_LEAVE],
+                                       available);
+               else
+                       _D("threshold leave %dMB not reached as expected, available %dMB",
+                                       vmpressure_limits[memconf][LIMIT_THRESHOLD_LEAVE],
+                                       available);
+               recovery_target = vmpressure_limits[memconf][LIMIT_THRESHOLD_LEAVE] - available;
+               recovery_target += memory_margin[memconf][MEMORY_MARGIN_MEDIUM];
+               recovered = 0;
+               for (memcg_index = MEMCG_MAX-1; memcg_index >= MEMCG_MEMORY; --memcg_index) {
+                       _D("checking %s cgroup", memcg_name[memcg_index]);
+
+                       /* OOM killer kills from all cgroups until the recovery target is met
+                        * The max # of victims is reset for each cgroup
+                        * Due to the oom scores of the processes created in base usage for this test
+                        * there should not be major kills and thus the target is not going to be reached.
+                        */
+                       kill_flag = 1;
+                       num_victims = 0;
+                       num_max_victims = vmpressure_limits[memconf][LIMIT_MAX_VICTIMS];
+                       ret = check_cgroup_kill_status(test, memcg_index, kill_flag,
+                                       recovery_target, &recovered,
+                                       num_max_victims, &num_victims);
+               }
+
+               /* Since the target has not been reached, we now test the working of the callback.
+                * We change the oom scores of the processes launched in create_base_usage to +ve
+                * values and check if the callback kills these processes until the conditions are met
+                */
+               _D("changing oom score adj of processes appropriately");
+               for (memcg_index = MEMCG_MAX-1; memcg_index >= MEMCG_MEMORY; --memcg_index) {
+                       pid_list_num = memcg_base_process_num[test][memcg_index];
+                       if (!pid_list_num)
+                               continue;
+
+                       /* If the max oom of the cgroup is -ve then continue without changing
+                        * (case of important cgroup)
+                        */
+                       oom = memcg_base_process_oom[test][memcg_index][pid_list_num-1];
+                       if (oom < 0)
+                               continue;
+                       /* Else change the oom scores of all processes in the cgroup to the max value */
+                       for (i = pid_list_num-2; i >= 0; --i) {
+                               if (memcg_base_process_oom[test][memcg_index][i] > 0)
+                                       continue;
+                               else {
+                                       ret = procfs_set_oom_score_adj(pid_list[memcg_index][i], oom);
+                                       if (ret == ERROR_NONE)
+                                               memcg_base_process_oom[test][memcg_index][i] = oom;
+                               }
+                       }
+               }
+
+               /* Wait for some time to get the medium pressure callback to do its work */
+               _D("going to sleep for %d seconds before testing output scenario\n\n\n\n", VMPRESSURE_ROOT_CB_SLEEP);
+               req.tv_sec = VMPRESSURE_ROOT_CB_SLEEP;
+               req.tv_nsec = 0;
+               nanosleep(&req, &rem);
+
+               /* Now recheck all the cgroups for proper kills */
+               recovered = 0;
+               for (memcg_index = MEMCG_MAX-1; memcg_index >= MEMCG_MEMORY; --memcg_index) {
+                       _D("checking %s cgroup", memcg_name[memcg_index]);
+
+                       kill_flag = 1;
+                       num_victims = 0;
+                       num_max_victims = vmpressure_limits[memconf][LIMIT_MAX_VICTIMS];
+                       ret = check_cgroup_kill_status(test, memcg_index, kill_flag,
+                                       recovery_target, &recovered,
+                                       num_max_victims, &num_victims);
+                       if (ret != ERROR_NONE)
+                               ret_val = ret;
+               }
+       }
+       return ret_val;
+}
+
+/* Creates base usage scenario (i.e. creates processes taking up
+ * certain amounts of memory) before the respective test function
+ * proceeds to test for the actions in resourced memory module
+ * This way helps to create usage on the target and simultaneously keep
+ * track of the status of the processes (created in this scenario)
+ */
+int create_base_usage(int test)
+{
+       int available, limit;
+       int memconf;
+       int i, to_populate;
+       struct timespec req, rem;
+
+       /* Get memory configuration and current available memory
+        * Base usage creation is dependent on memory configuration
+        */
+       memconf = get_memconf(kBtoMB(procfs_get_total()));
+       _I("%s: Memory configuration is %d", test_name[test], memconf);
+
+       available = kBtoMB(procfs_get_available());
+
+       /* Find out the memory size to be populated accordingly for the test
+        * and then usage populate_cgroup to create processes and populate the
+        * cgroups. These processes are going to be tracked in the test functions
+        * The return value is dependent on successful creation of the processes
+        */
+       switch (test) {
+       case TEST_PROACTIVE_KILLER:
+               to_populate = available - vmpressure_limits[memconf][LIMIT_THRESHOLD_LOW];
+               to_populate += memory_margin[memconf][MEMORY_MARGIN_LOW];
+               limit = vmpressure_limits[memconf][LIMIT_THRESHOLD_LOW];
+
+               break;
+       case TEST_OOM_DBUS_TRIGGER:
+               to_populate = available - vmpressure_limits[memconf][LIMIT_THRESHOLD_MEDIUM];
+               to_populate -= memory_margin[memconf][MEMORY_MARGIN_HIGH];
+               limit = vmpressure_limits[memconf][LIMIT_THRESHOLD_MEDIUM];
+
+               break;
+       case TEST_VMPRESSURE_ROOT:
+       case TEST_VMPRESSURE_ROOT_CB:
+               to_populate = available - vmpressure_limits[memconf][LIMIT_THRESHOLD_MEDIUM];
+               to_populate += memory_margin[memconf][MEMORY_MARGIN_HIGH];
+               limit = vmpressure_limits[memconf][LIMIT_THRESHOLD_MEDIUM];
+
+               break;
+       default:
+               _E("Invalid input");
+               return ERROR_INVALID_INPUT;
+       }
+
+       if (to_populate < 0) {
+               _E("%s: Base usage cannot be created. Not enough memory (%d/%d MB)",
+                               test_name[test], to_populate, available);
+               return ERROR_MEMORY;
+       } else
+               _D("%s: Available %d MB, Base usage to populate %d MB",
+                               test_name[test], available, to_populate);
+
+       for (i = 0; i < MEMCG_MAX; ++i)
+               populate_cgroup(test, i, to_populate);
+
+       req.tv_sec = 3;
+       req.tv_nsec = 0;
+       nanosleep(&req, &rem);
+
+       available = kBtoMB(procfs_get_available());
+
+       _I("%s: revised available memory is: %d MB, limit is: %d MB",
+                       test_name[test], available, limit);
+
+       return ERROR_NONE;
+}
+
+/* Runs the needed sequence of tests for the input test
+ * Refer to the README in the memory submodule to understand the
+ * checks conducted in each test
+ */
+int run_test(int test)
+{
+       int  ret;
+
+       /* Create the base usage scenario before proceeding to test for
+        * correct working of memory module in resourced
+        */
+       ret = create_base_usage(test);
+       if (ret != ERROR_NONE) {
+               _E("%s: Not able to create base usage. Error %d", test_name[test], ret);
+               return ret;
+       }
+
+       /* After base usage scenario was created successfully, test
+        * the working of memory module by calling the appropriate test function
+        */
+       switch (test) {
+       case TEST_PROACTIVE_KILLER:
+               ret = proactive_oom_killer();
+               break;
+       case TEST_OOM_DBUS_TRIGGER:
+               ret = oom_dbus_trigger();
+               break;
+       case TEST_VMPRESSURE_ROOT:
+       case TEST_VMPRESSURE_ROOT_CB:
+               ret = vmpressure_root(test);
+               break;
+       default:
+               _E("Invalid input");
+               return ERROR_INVALID_INPUT;
+       }
+       if (ret != ERROR_NONE)
+               _E("%s: Error running test", test_name[test]);
+       else
+               _I("%s: Test successfully completed", test_name[test]);
+
+       return ERROR_NONE;
+}
+
+/* Usage function */
+void usage(void)
+{
+       printf("Usage: resourced_memory_test <test>\n");
+       printf("\tSupported tests:\n");
+       printf("\t\tproactive : tests proactive oom killer (called before launching new apps)\n");
+       printf("\t\toom_trigger : tests oom trigger dbus signal handler\n");
+       printf("\t\tvmpressure_root : tests oom killer when pressure level builds up\n");
+       printf("\t\tvmpressure_root_cb : tests oom killer callback when pressure level builds up\n");
+       printf("\t\tvmpressure_cgroup : tests oom killer when pressure level of cgroup builds up\n");
+}
+
+/* Test program for memory module of resourced.
+ * Usage given in usage() function
+ */
+int main(int argc, char *argv[])
+{
+       int i;
+       char inp_str[STRING_MAX];
+
+       /* This is done so as to provide an opportunity to start a journalctl
+        * session following only this pid
+        */
+       printf("Running as pid %d\n", getpid());
+       printf("Enter input after starting journalctl: ");
+       i = scanf("%s", inp_str);
+
+       /* Invalid argument */
+       if (argc < 2) {
+               _E("Usage not correct!");
+               usage();
+               return 0;
+       }
+
+       /* Find out the test mentioned as input and run the test (calling run_test) */
+       for (i = 0; i < TEST_MAX; ++i) {
+               if (!strncmp(argv[1], test_name[i], strlen(argv[1]))) {
+                       printf("%s selected. Shifting to journalctl for messages\n", test_name[i]);
+                       _I("%s selected", test_name[i]);
+                       run_test(i);
+                       break;
+               }
+       }
+
+       /* Invalid argument */
+       if (i == TEST_MAX) {
+               _E("Usage not correct!");
+               usage();
+       }
+
+       return 0;
+}
diff --git a/src/tests/memory/resourced_memory_test_helper.c b/src/tests/memory/resourced_memory_test_helper.c
new file mode 100644 (file)
index 0000000..cb5276f
--- /dev/null
@@ -0,0 +1,231 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @file  resourced_memory_test_helper.c
+ * @desc  helper functions for memory test program
+ **/
+
+#include "resourced_memory_test.h"
+#include "utils.h"
+
+#define DEFAULT_PAGE_SIZE 4
+#define DEFAULT_SLEEP_TIME 4000
+
+/* Get the memory configuration (in terms of the MEMCONF_* enum)
+ * on the basis of the total memory of the system (given by input
+ * arg total_memory).
+ * Currently on 750MB configuration is supported (MEMCONF_768)
+ */
+int get_memconf(int total_memory)
+{
+       return MEMCONF_768;
+}
+
+/* Launches memory hogging processes of size memory, with oom
+ * adj score as oom and charged to the cgroup with path cgroup_path
+ */
+int launch_memory_hogger(int memory, int oom, char *cgroup_path)
+{
+       pid_t pid;
+
+       pid = fork();
+
+       /* If error return pid immediately */
+       if (pid < 0)
+               return pid;
+       else if (!pid) {
+               /* Memory hogger process */
+               int ret;
+               char *mem_alloc;
+               char oom_path[STRING_MAX];
+               int page_size = KBtoB(DEFAULT_PAGE_SIZE);
+               unsigned long memory_size = MBtoB(memory);
+               unsigned long memory_alloted = 0;
+
+               /* Make this process the head of its own process group */
+               ret = setpgid(getpid(), 0);
+               if (ret)
+                       _E("failed to separate current process into its own process group");
+
+               /* Writing the oom score to the oom_score_adj file of the process
+                * If this fails, then the process finishes immediately
+                */
+               snprintf(oom_path, sizeof(oom_path), "/proc/%d/oom_score_adj", getpid());
+               ret = fwrite_int(oom_path, oom);
+               if (ret != ERROR_NONE) {
+                       _E("IO: Error writing oom %d of pid %d", oom, pid);
+                       return 0;
+               }
+
+               /* Allocate memory upto target size in pages
+                * Tinker with these pages so that the memory is actually allocated
+                * Sleep for DEFAULT_SLEEP_TIME seconds (to simulate app use time lifecycle)
+                */
+               while (memory_alloted < memory_size) {
+                       mem_alloc = (char *)malloc(page_size);
+                       if (!mem_alloc) {
+                               _E("IO: Process %d not able to allocate memory after %d Bytes (target %d)",
+                                               getpid(), (int)memory_alloted, (int)memory_size);
+                               break;
+                       }
+
+                       /* Random arithmetic */
+                       mem_alloc[0] = memory_alloted % 128;
+                       mem_alloc[page_size-1] = memory_alloted % 123;
+
+                       memory_alloted += page_size;
+               }
+               sleep(DEFAULT_SLEEP_TIME);
+
+               return 0;
+       } else {
+               /* Resourced memory tests process */
+               int ret;
+
+               /* Writing pid to the cgroup. Error returned if this fails. */
+               ret = fwrite_int(cgroup_path, (int)pid);
+               if (ret != ERROR_NONE) {
+                       _E("IO: Not able to write %d to %s", pid, cgroup_path);
+                       return ret;
+               }
+
+               return pid;
+       }
+}
+
+/* Starts processes in the memory cgroup (specified by memcg_index) for
+ * the memory test (specified by test) as specified by the memcg_* arrays.
+ * The memory to be allocated (specified by target and the memcg_base_usage_ratio
+ * array) to the cgroup is equally distributed among all the processes started.
+ */
+void populate_cgroup(int test, int memcg_index, int target)
+{
+       int i;
+       int proc_num, proc_oom, proc_memory;
+       int pid;
+       int charged_memory;
+       char cgroup_path[STRING_MAX];
+
+       /* proc_num number of processes will be started */
+       proc_num = memcg_base_process_num[test][memcg_index];
+       if (!proc_num)
+               return;
+
+       /* charged_memory MB of memory will be distributed among proc_num processes */
+       charged_memory = (int)((double)target * memcg_base_usage_ratio[test][memcg_index]);
+       /* ToDo: Make process memory calculation variable */
+       proc_memory = charged_memory/proc_num;
+
+       if (!memcg_index)
+               snprintf(cgroup_path, sizeof(cgroup_path), "%s/cgroup.procs", MEMCG_ROOT);
+       else
+               snprintf(cgroup_path, sizeof(cgroup_path), "%s/%s/cgroup.procs", MEMCG_ROOT, memcg_name[memcg_index]);
+
+       /* Launch proc_num processes */
+       for (i = 0; i < proc_num; ++i) {
+               proc_oom = memcg_base_process_oom[test][memcg_index][i];
+               pid = launch_memory_hogger(proc_memory, proc_oom, cgroup_path);
+
+               /* If an error is encountered while launching the processes, log it
+                * But the pid is still stored in the pid_list array (to keep track
+                * of which processes to track in the output scenario)
+                */
+               if (pid < 0)
+                       _E("%s: Failed to launch %d process (cgroup: %s ; oom:%d ; memory:%dMB)",
+                                       test_name[test], i, memcg_name[memcg_index], proc_oom, proc_memory);
+               else
+                       _D("%s: Launched process %d (cgroup: %s ; oom: %d ; memory: %dMB)",
+                                       test_name[test], pid, memcg_name[memcg_index], proc_oom, proc_memory);
+               pid_list[memcg_index][i] = pid;
+               pid_memory_list[memcg_index][i] = proc_memory;
+       }
+}
+
+/* Checks for status of kills in the input cgroup (memcg_index), depending on the test,
+ * kill flag of the cgroup (kill_flag), target for recovery (recovery_target),
+ * amount of recovered memory till now (recovered), # of victims killed (num_victims)
+ * against the max # of victims allowed to be killed (num_max_victims)
+ */
+int check_cgroup_kill_status(int test, int memcg_index, int kill_flag,
+                               int recovery_target, int *recovered,
+                               int num_max_victims, int *num_victims)
+{
+       int i, ret, ret_val;
+       int curr_pid, pid_list_num, oom;
+
+       /* For all processes start in create_base_usage for this cgroup
+        * Check if they are killed/not killed and output error/debug messages
+        * according to the condition (code and log msgs are self-explanatory)
+        */
+       ret_val = ERROR_NONE;
+       pid_list_num = memcg_base_process_num[test][memcg_index];
+       for (i = pid_list_num-1; i >= 0; --i) {
+               curr_pid = pid_list[memcg_index][i];
+               if (curr_pid >= 0) {
+                       ret = pid_exists(curr_pid);
+                       oom = memcg_base_process_oom[test][memcg_index][i];
+                       if (!ret) {
+                               ret = ERROR_FAIL;
+                               if (!kill_flag)
+                                       _E("process %d (oom: %d) should not be killed (%d / %d ; killflag)",
+                                                       curr_pid, oom, *recovered, recovery_target);
+                               else if (oom < 0)
+                                       _E("process %d (oom: %d) should not be killed (%d / %d ; oom)",
+                                                       curr_pid, oom, *recovered, recovery_target);
+                               else if (*recovered >= recovery_target)
+                                       _E("process %d (oom: %d) should not be killed (%d / %d ; target met)",
+                                                       curr_pid, oom, *recovered, recovery_target);
+                               else if (*num_victims >= num_max_victims)
+                                       _E("process %d (oom: %d) should not be killed (%d / %d ; max victims)",
+                                                       curr_pid, oom, *recovered, recovery_target);
+                               else {
+                                       _D("process %d (oom: %d) killed as expected (%d / %d)",
+                                                       curr_pid, oom, *recovered, recovery_target);
+                                       ret = ERROR_NONE;
+                               }
+
+                               *recovered = *recovered + pid_memory_list[memcg_index][i];
+                               *num_victims = *num_victims + 1;
+                       } else {
+                               ret = ERROR_NONE;
+                               if (!kill_flag)
+                                       _D("process %d (oom: %d) not killed as expected (%d / %d ; killflag)",
+                                                       curr_pid, oom, *recovered, recovery_target);
+                               else if (oom < 0)
+                                       _D("process %d (oom: %d) not killed as expected (%d / %d ; oom)",
+                                                       curr_pid, oom, *recovered, recovery_target);
+                               else if (*recovered >= recovery_target)
+                                       _D("process %d (oom: %d) not killed as expected (%d / %d ; target met)",
+                                                       curr_pid, oom, *recovered, recovery_target);
+                               else if (*num_victims >= num_max_victims)
+                                       _D("process %d (oom: %d) not killed as expected (%d / %d ; max victims)",
+                                                       curr_pid, oom, *recovered, recovery_target);
+                               else {
+                                       _E("process %d (oom: %d) expected to be killed (%d / %d)",
+                                                       curr_pid, oom, *recovered, recovery_target);
+                                       ret = ERROR_FAIL;
+                               }
+                       }
+                       if (ret != ERROR_NONE)
+                               ret_val = ret;
+               }
+       }
+       return ret_val;
+}
diff --git a/src/tests/memory/utils.c b/src/tests/memory/utils.c
new file mode 100644 (file)
index 0000000..ebf5737
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @file  utils.c
+ * @desc  file IO and proc fs related functions
+ **/
+
+#include <errno.h>
+#include "utils.h"
+
+#define MEM_AVAILABLE "MemAvailable"
+#define MEM_TOTAL "MemTotal"
+#define MEM_FREE "MemFree"
+#define MEM_CACHED "Cached"
+
+/* File IO abstract function
+ * Writes the string (in str) to the file (with path as path)
+ */
+int fwrite_str(char *path, char *str)
+{
+       int ret;
+       FILE *file;
+
+       file = fopen(path, "w");
+       if (!file) {
+               _E("IO: Error opening file %s", path);
+               return ERROR_IO;
+       }
+
+       ret = fputs(str, file);
+       fclose(file);
+
+       if (ret < 0)
+               return ERROR_IO;
+       else
+               return ERROR_NONE;
+}
+
+/* File IO abstract function
+ * Writes the integer (in num) to the file (with path as path)
+ * Uses fwrite_str to accomplish the task
+ */
+int fwrite_int(char *path, int num)
+{
+       char content_str[STRING_MAX];
+
+       snprintf(content_str, sizeof(content_str), "%d", num);
+       return fwrite_str(path, content_str);
+}
+
+/* Proc fs util function to get the available (usable) memory in the system
+ * Scans the /proc/meminfo file and returns the value of the field MemAvailable
+ * The value returned is in kB (1000 bytes)
+ */
+unsigned int procfs_get_available(void)
+{
+       char buf[STRING_MAX];
+       FILE *fp;
+       unsigned int available = 0;
+       unsigned int free, cached;
+
+       free = cached = 0;
+
+       fp = fopen("/proc/meminfo", "r");
+
+       if (!fp) {
+               _E("IO: Failed to open /proc/meminfo");
+               return available;
+       }
+
+       while (fgets(buf, STRING_MAX, fp) != NULL) {
+               if (!strncmp(buf, MEM_FREE, strlen(MEM_FREE))) {
+                       if (sscanf(buf, "%*s %d kB", &free) != 1) {
+                               _E("IO: Failed to get free memory from /proc/meminfo");
+                               free = 0;
+                       }
+               } else if (!strncmp(buf, MEM_CACHED, strlen(MEM_CACHED))) {
+                       if (sscanf(buf, "%*s %d kB", &cached) != 1) {
+                               _E("IO: Failed to get cached memory from /proc/meminfo");
+                               cached = 0;
+                       }
+               } else if (!strncmp(buf, MEM_AVAILABLE, strlen(MEM_AVAILABLE))) {
+                       if (sscanf(buf, "%*s %d kB", &available) != 1) {
+                               _E("IO: Failed to get available memory from /proc/meminfo");
+                               available = 0;
+                       }
+                       break;
+               }
+       }
+
+       if (!available && (free && cached))
+               available = free + cached;
+
+       fclose(fp);
+
+       return available;
+}
+
+/* Proc fs util function to get the total memory in the system
+ * Scans the /proc/meminfo file and returns the value of the field MemTotal.
+ * The value returned is in kB (1000 bytes)
+ */
+unsigned int procfs_get_total(void)
+{
+       char buf[STRING_MAX];
+       FILE *fp;
+       unsigned int total = 0;
+
+       fp = fopen("/proc/meminfo", "r");
+
+       if (!fp) {
+               _E("IO: Failed to open /proc/meminfo");
+               return total;
+       }
+
+       while (fgets(buf, STRING_MAX, fp) != NULL) {
+               if (!strncmp(buf, MEM_TOTAL, strlen(MEM_TOTAL))) {
+                       if (sscanf(buf, "%*s %d kB", &total) != 1) {
+                               _E("IO: Failed to get total memory from /proc/meminfo");
+                               total = 0;
+                       }
+                       break;
+               }
+       }
+       fclose(fp);
+
+       return total;
+}
+
+/* Proc fs util function to set oom score adj (given by oom) of
+ * the process (with id pid)
+ */
+int procfs_set_oom_score_adj(int pid, int oom)
+{
+       int ret;
+       char name[STRING_MAX];
+
+       snprintf(name, sizeof(name), "/proc/%d/oom_score_adj", pid);
+       ret = fwrite_int(name, oom);
+       if (ret != ERROR_NONE)
+               _E("IO: Not able to change oom score of process %d", pid);
+       return ret;
+}
+
+/* Finds out if the process with input pid is still running.
+ * Uses the existence of the respective /proc/<pid>/ directory
+ * to find if the process is running.
+ */
+int pid_exists(int pid)
+{
+       char name[STRING_MAX];
+
+       /* If the file /proc/pid/cmdline cannot be accessed, the process does not exist */
+       snprintf(name, sizeof(name), "/proc/%d/stat", pid);
+       if (access(name, F_OK)) {
+               return 0;
+       } else {
+               /* Zombie processes which are not accounted for by the parent processes
+                * still retain their /proc/pid/ directory until the parent processes dies
+                * So we check the process status field of the stat file to see if the process
+                * is either in Z (zombie) or X/x (killed) state
+                * If the process is in either of these states, then it is not running.
+                * Otherwise the process exists
+                */
+               FILE *stat_fp;
+               int ret;
+               char proc_stat;
+
+               stat_fp = fopen(name, "r");
+               if (!stat_fp)
+                       return 0;
+               else {
+                       while (fgets(name, sizeof(name), stat_fp) != NULL) {
+                               if (sscanf(name, "%*d %*s %c", &proc_stat) != 1)
+                                       ret = 0;
+                               else if (proc_stat == 'Z' || proc_stat == 'X' || proc_stat == 'x')
+                                       ret = 0;
+                               else
+                                       ret = 1;
+                       }
+                       fclose(stat_fp);
+                       return ret;
+               }
+       }
+}
+
diff --git a/src/tests/scripts/Notes b/src/tests/scripts/Notes
new file mode 100644 (file)
index 0000000..4d7a070
--- /dev/null
@@ -0,0 +1,11 @@
+Resourced tests scripts
+=======================
+Provides scripts needed to interact with resourced during tests
+
+Scripts
+=======
+* resourced_dbus_sender.sh: Provides command line way of sending dbus method calls/signals to resourced
+                               Uses dbus-send utility to accomplish the same. Look at the usage message
+                               to understand the signals/methods supported.
+* resourced_util_memory_test.sh: Provides functions to gather info about the memory usage and memcg info
+                                       Look at the usage message to understand the options supported.
diff --git a/src/tests/scripts/resourced_dbus_sender.sh b/src/tests/scripts/resourced_dbus_sender.sh
new file mode 100755 (executable)
index 0000000..36deb5f
--- /dev/null
@@ -0,0 +1,118 @@
+#!/bin/bash
+
+usage() {
+       echo "resourced_dbus_sender [method_call/signal_name] [method options]"
+       if [ "$1" == "default" ]
+               then
+               echo "  Supported Method calls:"
+               echo "          Memory_Usage <smack enable/disable> <array of pid>: Call to the Proc_Method_Usage method of proc-usage-stats-api"
+               echo "          CPU_Usage <smack enable/disable> <array of pid>: Call to the Proc_CPU_Usage method of proc-usage-stats-api"
+               echo "          AUL_Launch <pid> <app name> <pkg type>: Call to the AppLaunch signal of org.tizen.aul.AppStatus"
+               echo "          Prelaunch_OOM [appid] [pkgid]: Call to the prelaunch handler in resourced"
+               echo "          OOM_Trigger: Sends signal to the oom trigger dbus handler in resourced"
+               echo "          Help <Method_Call>: Details about that method call"
+       fi
+       if [ "$1" == "Memory_Usage" ]
+               then
+               echo "          Memory_Usage <smack enable/disable> <array of pid>: Call to the Proc_Method_Usage method of proc-usage-stats-api"
+       fi
+       if [ "$1" == "CPU_Usage" ]
+               then
+               echo "          CPU_Usage <smack enable/disable> <array of pid>: Call to the Proc_CPU_Usage method of proc-usage-stats-api"
+       fi
+       if [ "$1" == "AUL_Launch" ]
+               then
+               echo "          AUL_Launch <pid> <app name> <pkg type>: Call to the AppLaunch signal of org.tizen.aul.AppStatus"
+               echo "                  pkg type: svc, ui, widget, watch"
+       fi
+       if [ "$1" == "Prelaunch_OOM" ]
+               then
+               echo "          Prelaunch_OOM [appid] [pkgid]: Call to the prelaunch handler in resourced"
+               echo "                  If there are not arguments after Prelaunch_OOM:"
+               echo "                          sends default appid and pkgid"
+               echo "                  Else:"
+               echo "                          set: sends input appid and pkgid; need 2nd and 3rd argument to script"
+       fi
+       if [ "$1" == "OOM_Trigger" ]
+               then
+               echo "          OOM_Trigger: Sends signal to the oom trigger dbus handler in resourced"
+       fi
+       exit -1
+}
+
+if [ $# -lt 1 ]
+       then
+       usage default
+fi
+
+method=$1
+
+case "$method" in
+       Memory_Usage )
+               if [ $# -lt 3 ]
+                       then
+                       usage Memory_Usage
+               fi
+               smack_label=$2
+               arr=$3
+               if [ "$smack_label" == "enable" ]
+                       then
+                       echo "Smack labeling enabled"
+                       echo "resourced:systeminfo" > /proc/self/attr/current
+               fi
+               dbus-send --system --type=method_call --print-reply --reply-timeout=120000 --dest=org.tizen.resourced /Org/Tizen/ResourceD/Process org.tizen.resourced.process.ProcMemoryUsage array:int32:$arr
+               ;;
+       CPU_Usage )
+               if [ $# -lt 3 ]
+                       then
+                       usage CPU_Usage
+               fi
+               smack_label=$2
+               arr=$3
+               if [ "$smack_label" == "enable" ]
+                       then
+                       echo "Smack labeling enabled"
+                       echo "resourced:systeminfo" > /proc/self/attr/current
+               fi
+               dbus-send --system --type=method_call --print-reply --reply-timeout=120000 --dest=org.tizen.resourced /Org/Tizen/ResourceD/Process org.tizen.resourced.process.ProcCpuUsage array:int32:$arr
+               ;;
+       AUL_Launch )
+               if [ $# -lt 4 ]
+                       then
+                       usage AUL_Launch
+               fi
+               pid=$2
+               appid=$3
+               pkgtype=$4
+               dbus-send --system --dest=org.tizen.aul.AppStatus /Org/Tizen/Aul/AppStatus org.tizen.aul.AppStatus.AppLaunch int32:$pid string:'$appid' string:'$pkgtype'
+               ;;
+       Prelaunch_OOM )
+               if [ $# -lt 2 ]
+                       then
+                       appid="org.prelaunch"
+                       pkgid="org.pkg_prelaunch"
+               else
+                       if [ $# -lt 3 ]
+                               then
+                               usage Prelaunch_OOM
+                       else
+                               appid=$2
+                               pkgid=$3
+                       fi
+               fi
+               flags=1         #Set only PROC_LARGEMEMORY flag
+               dbus-send --system --dest=org.tizen.resourced /Org/Tizen/ResourceD/Process org.tizen.resourced.process.ProcPrelaunch string:$appid string:$pkgid int32:$flags
+               ;;
+       OOM_Trigger )
+               dbus-send --system --dest=org.tizen.resourced /Org/Tizen/ResourceD/Oom org.tizen.resourced.oom.Trigger
+               ;;
+       Help )
+               if [ $# -lt 2 ]
+                       then
+                       usage default
+               fi
+               usage $2
+               ;;
+       *)
+               usage default
+esac
diff --git a/src/tests/scripts/resourced_util_memory_test.sh b/src/tests/scripts/resourced_util_memory_test.sh
new file mode 100644 (file)
index 0000000..7949e5f
--- /dev/null
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+MEMCG_ROOT="/sys/fs/cgroup/memory"
+
+function usage() {
+       echo "resourced_memory_tests_util [action] [action options]"
+       if [ "$1" == "default" ]
+               then
+               echo "  Supported actions:"
+               echo "          Memory_probe: Displays memory usage info and memory cgroup info"
+               echo "          Memcg_probe: Displays the cgroup.procs of all the memory subcgroups"
+               echo "          MemUsage_probe: Displays memory usage info"
+               echo "          Help <Action>: Details about that action"
+       fi
+       if [ "$1" == "Memory_probe" ]
+               then
+               echo "          Memory_probe: Displays memory usage info and memory cgroup info"
+       fi
+       if [ "$1" == "Memcg_probe" ]
+               then
+               echo "          Memcg_probe: Displays the cgroup.procs of all the memory subcgroups"
+       fi
+       if [ "$1" == "MemUsage_probe" ]
+               then
+               echo "          MemUsage_probe: Displays memory usage info"
+       fi
+       exit -1
+}
+
+if [ $# -lt 1 ]
+       then
+       usage default
+fi
+
+function memcg_probe() {
+       memcg_names=("foreground" "service" "favorite" "background" "swap")
+       echo "Format: pid, oom score adj"
+       for memcg in "${memcg_names[@]}"
+       do
+               echo "$memcg memcg"
+               echo "================"
+               cat $MEMCG_ROOT/$memcg/cgroup.procs | while read pid
+               do
+                       oom=`cat /proc/$pid/oom_score_adj`
+                       echo "$pid, $oom"
+               done
+       done
+}
+
+function memusage_probe() {
+       memusage_fields=("MemTotal" "MemAvailable")
+       for usage_field in "${memusage_fields[@]}"
+       do
+               cat /proc/meminfo | grep "$usage_field"
+       done
+}
+
+action=$1
+
+case "$action" in
+       Memory_probe )
+               echo "Memory probe"
+               echo "=================="
+               memusage_probe
+               echo "-------------------"
+               memcg_probe
+               ;;
+       Memcg_probe )
+               echo "Memory cgroup probe"
+               echo "==================="
+               memcg_probe
+               ;;
+       MemUsage_probe )
+               echo "Memory usage probe"
+               echo "=================="
+               memusage_probe
+               ;;
+       Help )
+               if [ $# -lt 2 ]
+                       then
+                       usage default
+               fi
+               usage $2
+               ;;
+       *)
+               usage default
+esac
diff --git a/src/tests/util/Notes b/src/tests/util/Notes
new file mode 100644 (file)
index 0000000..4e0cd86
--- /dev/null
@@ -0,0 +1,11 @@
+Resourced tests utilities
+=========================
+This folder provides some utilities that can be used to assist testing of resourced
+
+Utilities
+=========
+Look at the source code of the utilities to check the usage.
+* resourced_dummy_process: Creates a dummy process with oom score set to input value and runs for infinite time.
+                          Can be used as the victim process
+* resourced_hogger_memory: Creates a memory hogging process as big as the size mentioned in input and runs for a given time.
+                          Can be used to trigger oom situations.
diff --git a/src/tests/util/resourced_dummy_process.c b/src/tests/util/resourced_dummy_process.c
new file mode 100644 (file)
index 0000000..50318d3
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @file  resourced_dummy_process.c
+ * @desc  util program to run dummy process for a infinite time
+ **/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#define MEMORY_BAIT -1
+#define ACTIVE_PROCESS 1
+
+#define MBtoB(bytes) (bytes << 20)
+
+/* Utility to start a dummy process (does nothing) which runs
+ * for infinite time (sleeps for 5 seconds on each iteration
+ * of an infinite loop)
+ * Usage: resourced_dummy_process [oom_score_adj]
+ *             oom_score_adj: optional argument
+ *                     OOM score of the started process is set
+ *                     to the value provided (if provided)
+ */
+int main(int argc, char *argv[])
+{
+       int oom_score_adj, oom_score_flag;
+       FILE *oom_fp;
+       char buf[256];
+
+       if (argc < 1)
+               oom_score_flag = 0;
+       else {
+               oom_score_flag = 1;
+               oom_score_adj = atoi(argv[1]);
+       }
+
+       printf("Resourced tester: dummy process\n\n");
+
+       /* Set oom score adj if it is provided as argument */
+       if (oom_score_flag) {
+               snprintf(buf, sizeof(buf), "/proc/%d/oom_score_adj", getpid());
+               oom_fp = fopen(buf, "w");
+               if (oom_fp) {
+                       printf("not able to write oom_score_adj (%d). please write it manually\n", oom_score_adj);
+               } else {
+                       printf("writing oom_score_adj(%d) for dummy process", oom_score_adj);
+                       fprintf(oom_fp, "%d\n", oom_score_adj);
+                       fclose(oom_fp);
+               }
+       } else
+               printf("oom_score_adj is not being written into. no args provided\n");
+
+       printf("resourced_tester: dummy process %d running on empty loop\n", getpid());
+       while (1)
+               sleep(5);
+       return 0;
+}
diff --git a/src/tests/util/resourced_hogger_memory.c b/src/tests/util/resourced_hogger_memory.c
new file mode 100644 (file)
index 0000000..3205e66
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * resourced
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+/**
+ * @file  resourced_memory_hog.c
+ * @desc  util program to run memory hogging process for a given time
+ **/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#define MBtoB(mb) (mb << 20)
+#define KBtoB(kb) (kb << 10)
+
+/* Utility to start memory hogging process which runs for a given time
+ * The oom score adj of the process started is -900 (important process)
+ * Can be used to create memory pressure situations on the target
+ * Usage: resourced_memory_hog <mem-size> <sleep-time>
+ *             mem-size: Needed argument.
+ *                     Process allocates this memory (in MB)
+ *             sleep-time: Needed argument.
+ *                     Process sleeps for this time (in seconds)
+ *                     before terminating
+ */
+int main(int argc, char *argv[])
+{
+       int memory_size, memory_alloted, page_size, sleep_time;
+       char *memory_bait;
+       FILE *fp;
+       char buf[256];
+
+       printf("Resourced tester: dummy process\n\n");
+
+       if (argc < 2) {
+               printf("resourced_tester: Memory hog process needs 2 arguments memory-size(in MB) and the time to sleep in seconds\n");
+               return 0;
+       }
+
+       snprintf(buf, sizeof(buf), "/proc/%d/oom_adj", getpid());
+       fp = fopen(buf, "w");
+       if (!fp) {
+               printf("resourced_tester: error in opening oom_score file\n");
+               return 0;
+       }
+       fprintf(fp, "-17\n");
+       fclose(fp);
+
+       /* Set oom score adj to -900 to make it important process */
+       snprintf(buf, sizeof(buf), "/proc/%d/oom_score_adj", getpid());
+       fp = fopen(buf, "w");
+       if (!fp) {
+               printf("resourced_tester: error in opening oom_score_adj file\n");
+               return 0;
+       }
+       fprintf(fp, "-900\n");
+       fclose(fp);
+
+
+       memory_size = MBtoB(atoi(argv[1]));
+       sleep_time = atoi(argv[2]);
+       page_size = KBtoB(4);
+       memory_alloted = 0;
+       printf("resourced_tester: dummy process %d running as memory hogger, taking %dB\n", getpid(), memory_size);
+
+       /* Allocate memory in pages (till target size is reached)
+        * Use this memory so that kmalloc is actually called (pages are
+        * actually allocated). Sleep for given time after that.
+        */
+       while (memory_alloted < memory_size) {
+               memory_bait = (char *)malloc(page_size);
+               memory_alloted += page_size;
+               memory_bait[0] = memory_alloted%128;
+       }
+       sleep(sleep_time);
+       return 0;
+}