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)
%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
-DMEMORY_MODULE=%{memory_module} \
-DWEARABLE_NOTI=%{wearable_noti} \
-DBLOCK_MODULE=%{block_module} \
+ -DTESTS_MODULE=%{tests_module}
make %{?jobs:-j%jobs}
%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
#network part
%{_libdir}/libresourced.so
%{_includedir}/system/data_usage.h
-
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+</manifest>
+
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()
+
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);
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();
}
}
--- /dev/null
+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)
--- /dev/null
+/*
+ * 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.
--- /dev/null
+/*
+ * 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
--- /dev/null
+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.
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+ }
+ }
+}
+
--- /dev/null
+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.
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+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.
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}