From 359405f39ebd8ba7e26295c57ee56264496e14ef Mon Sep 17 00:00:00 2001 From: Karol Lewandowski Date: Wed, 13 Nov 2019 16:41:49 +0100 Subject: [PATCH 01/16] Release 6.0.1 This version introduces plugin system, allowing to drop -light variant as all functionality can be enabled/disabled by providing or not respective modules. Change-Id: Ifb5b37652c622a7ea335ac4166b9c2e51be8032b --- packaging/resourced.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/resourced.spec b/packaging/resourced.spec index 13b7c39..c084577 100644 --- a/packaging/resourced.spec +++ b/packaging/resourced.spec @@ -14,7 +14,7 @@ Name: resourced Summary: Resource management daemon -Version: 5.5.3 +Version: 6.0.1 Release: 1 Group: System/Libraries License: Apache-2.0 -- 2.7.4 From bc2c2dba2d305a0c5399f611ecad44901754f094 Mon Sep 17 00:00:00 2001 From: INSUN PYO Date: Mon, 18 Nov 2019 19:57:37 +0900 Subject: [PATCH 02/16] Fix indent Change-Id: I38295ad9d738103ae2041db8c5a4eea08153d554 --- src/heart/heart-cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/heart/heart-cpu.c b/src/heart/heart-cpu.c index b26eaff..14af78e 100644 --- a/src/heart/heart-cpu.c +++ b/src/heart/heart-cpu.c @@ -700,9 +700,9 @@ void heart_cpu_update(struct logging_table_form *data, void *user_data) _E("pthread_mutex_lock() failed, %d", ret); return; } - table = - g_hash_table_lookup(cpu_usage_list, data->appid); + /* update */ + table = g_hash_table_lookup(cpu_usage_list, data->appid); if (table) { if (table->last_renew_time > data->time) -- 2.7.4 From 3b9052d2429cf3438f4d99463cb58425f15853fc Mon Sep 17 00:00:00 2001 From: Amandeep Chauhan Date: Mon, 3 Jun 2019 16:48:17 +0200 Subject: [PATCH 03/16] Remaining Useful Life (RUL) implementation RUL implementation for monitoring battery health and detecting when battery needs to be replaced Change-Id: Id286f292630ede1670be2859e4439f236551631f Signed-off-by: Amandeep Chauhan --- packaging/resourced.spec | 1 + src/CMakeLists.txt | 1 + src/heart/heart-battery.c | 288 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 290 insertions(+) diff --git a/packaging/resourced.spec b/packaging/resourced.spec index c084577..010e148 100644 --- a/packaging/resourced.spec +++ b/packaging/resourced.spec @@ -42,6 +42,7 @@ BuildRequires: pkgconfig(capi-system-info) BuildRequires: pkgconfig(libtzplatform-config) BuildRequires: pkgconfig(storage) BuildRequires: pkgconfig(libgum) +BuildRequires: pkgconfig(capi-system-device) #only for data types BuildRequires: pkgconfig(tapi) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 129472b..4c54ad6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -78,6 +78,7 @@ SET(REQUIRES_LIST ${REQUIRES_LIST} storage libgum libtzplatform-config + capi-system-device ) INCLUDE(FindPkgConfig) diff --git a/src/heart/heart-battery.c b/src/heart/heart-battery.c index c3d8a51..d1137b2 100644 --- a/src/heart/heart-battery.c +++ b/src/heart/heart-battery.c @@ -33,6 +33,8 @@ #include #include #include +#include +#include #include "proc-common.h" #include "notifier.h" @@ -112,6 +114,15 @@ #define HEART_BATTERY_DATA_FILE HEART_FILE_PATH"/.battery_data.dat" #define VCONFKEY_HEART_BATTERY_DEVICE_MODE "db/setting/psmode" +/* + * Remaining Useful Life prediction logic + */ +#define RUL_DATA_FILE HEART_FILE_PATH"/.rul_data.dat" +#define RUL_CHARGING_CYCLES_FILE HEART_FILE_PATH"/.charging_cycles.dat" +#define RUL_MIN_CAPACITY_DIFF 50 +#define RUL_DISPLAY_ON_CUTOFF_RATIO 0.1f +#define RUL_DATA_SIZE 32 + enum { TA = 0, /* prediction based on total data average */ PCB = 1, /* prediction with physiological behaviors */ @@ -142,6 +153,28 @@ enum { DEFAULT_VALUE_MAX = 3, }; + +/* Structure to calculate percentage time LCD on during charging */ +struct rul_lcd_data { + display_state_e state; + long on_time; + long on_start_time; + long on_stop_time; +}; + +/* Structure to store RUL related charging info */ +struct rul_info { + long charging_start_time; + long charging_stop_time; + float raw_capacity; /* capacity (mAh) accumulation during charging */ + int soc_start; /* SoC(%) value when charging starts */ + int soc_stop; /* SoC(%) value when charging stops */ + int charging_cycles; /* Number of charging cycles */ + int curr_soc; /* Current SoC %, compare with prev_soc to determine if charging */ + int prev_soc; /* Previous value of SoC % */ + struct rul_lcd_data lcd_data; /* LCD related data */ +}; + struct battery_used { time_t used_time_sec; /* seconds on battery */ time_t last_update_time; @@ -306,6 +339,22 @@ static bool first_level_change = FALSE; */ static char battery_header[BATTERY_LINE_MAX] = "BATTERY"; +/* + * Remaining Useful Life (RUL) prediction logic + */ +static int logic_rul; +struct rul_info rul; + +int heart_battery_get_capacity(void) +{ + return batt_stat.curr_capacity; +} + +enum charger_status_type heart_battery_get_charger_status(void) +{ + return batt_stat.curr_charger_status; +} + static inline void heart_battery_set_usage_reset_stime(int history, time_t start_time) { batt_stat.batt_reset_usage[history].start_time = start_time; @@ -1940,6 +1989,176 @@ static int heart_battery_add_capacity(int capacity) return RESOURCED_ERROR_NONE; } +static void rul_display_state_changed_cb(device_callback_e type, void *value, void *user_data) +{ + int display_state = (int) ((intptr_t) value), charger_status; + /* 0 : on, 1 : dim, 2: off */ + + /* + * If charger_status==CHARGING and raw capacity calculation ongoing, + * then, handle display_on time for RUL + */ + + charger_status = heart_battery_get_charger_status(); + if ((charger_status == CHARGING) && (rul.charging_start_time != 0)) { + if (type != DEVICE_CALLBACK_DISPLAY_STATE) + return; + + if (display_state == DISPLAY_STATE_NORMAL) { + _I("DISPLAY: NORMAL/DIM"); + rul.lcd_data.state = DISPLAY_STATE_NORMAL; + if (rul.lcd_data.on_start_time == 0) { + rul.lcd_data.on_start_time = logging_get_time(CLOCK_BOOTTIME); + } + } else if (display_state == DISPLAY_STATE_SCREEN_OFF) { + _I("DISPLAY: OFF"); + rul.lcd_data.state = DISPLAY_STATE_SCREEN_OFF; + if (rul.lcd_data.on_start_time > 0) { + rul.lcd_data.on_stop_time = logging_get_time(CLOCK_BOOTTIME); + rul.lcd_data.on_time += rul.lcd_data.on_stop_time - rul.lcd_data.on_start_time; + rul.lcd_data.on_start_time = 0; + rul.lcd_data.on_stop_time = 0; + } + } + } +} + +static void rul_write_data_to_file(void) +{ + int len = 0; + char buff[RUL_DATA_SIZE] = {0}; + _cleanup_fclose_ FILE *fp = NULL, *fp1 = NULL; + float fullcapnom = 0, fullcapnom_mAh = 0; + + /* Update count of Charging cycles*/ + rul.charging_cycles += 1; + + /* Calculate normalized capacity, i.e., extrapolate for 0% to 100% */ + fullcapnom = rul.raw_capacity / ((rul.soc_stop/100.0f) - (rul.soc_start/100.0f)); + fullcapnom_mAh = fullcapnom / 3.6f; + _I("rul : raw_capacity = %.4f As, fullcapnom = %.4f As = %.4f mAh", rul.raw_capacity, fullcapnom, fullcapnom_mAh); + + /* Update RUL data file */ + fp = fopen(RUL_DATA_FILE, "a"); + if (fp == NULL) { + _E("Could not open file - RUL_DATA_FILE"); + return; + } + + len = snprintf(buff, RUL_DATA_SIZE, "%d %.4f\n", rul.charging_cycles, fullcapnom_mAh); + + if (len < RUL_DATA_SIZE) + fputs(buff, fp); + + /* Write Charging cycles count to file */ + fp1 = fopen(RUL_CHARGING_CYCLES_FILE, "w"); + if (fp1 == NULL) { + _E("Could not open file for write - RUL_CHARGING_CYCLES_FILE"); + return; + } + fprintf(fp1, "%d", rul.charging_cycles); + +} + +static void rul_charging_cycle_end_work(void) +{ + int capacity; + long total_charging_time; + bool lcd_on_time_check; + float lcd_on_ratio = 1.0f; + + capacity = heart_battery_get_capacity(); + + rul.soc_stop = capacity; + rul.charging_stop_time = logging_get_time(CLOCK_BOOTTIME); + + /* If LCD was on before disconnecting charger, update display_on time */ + if (rul.lcd_data.on_start_time != 0) { + rul.lcd_data.on_stop_time = logging_get_time(CLOCK_BOOTTIME); + rul.lcd_data.on_time += rul.lcd_data.on_stop_time - rul.lcd_data.on_start_time; + } + + /* Max LCD ON Time ratio criteria met ? */ + total_charging_time = rul.charging_stop_time - rul.charging_start_time; + if (total_charging_time != 0) + lcd_on_ratio = (rul.lcd_data.on_time / (total_charging_time * 1.0f)); + lcd_on_time_check = lcd_on_ratio <= RUL_DISPLAY_ON_CUTOFF_RATIO; + + /* write data to file if condititons met*/ + if ((rul.charging_start_time != 0) && (lcd_on_time_check == true) && + (rul.soc_stop - rul.soc_start >= RUL_MIN_CAPACITY_DIFF)) + rul_write_data_to_file(); + + /* init for next charging cycle */ + rul.soc_start = 0; + rul.soc_stop = 0; + rul.charging_start_time = 0; + rul.charging_stop_time = 0; + rul.lcd_data.on_start_time = 0; + rul.lcd_data.on_stop_time = 0; + rul.lcd_data.on_time = 0; +} + +static void rul_calculate_raw_capacity(void) +{ + /* + * This function updates Raw Capacity every 1% SoC change. + * + * Upon first time 1% SoC change, initialize rul_info variables. + * + * Subsequently, Update Raw Capacity using time interval and + * current_now sys node values. + * + * If capacity = 100%, dump collected data + */ + + long rul_curr_time, rul_time_interval; + static long prev_charging_time; + int fg_curr_inst, capacity, ret; + float fg_curr_inst_f; + static float prev_fg_curr_inst_f = 0.0f; + + capacity = heart_battery_get_capacity(); + + if (rul.charging_start_time == 0) { + rul.charging_start_time = logging_get_time(CLOCK_BOOTTIME); + rul.soc_start = capacity; + rul.raw_capacity = 0.0f; + prev_charging_time = rul.charging_start_time; + prev_fg_curr_inst_f = 0.0f; + rul.lcd_data.on_start_time = 0; + rul.lcd_data.on_stop_time = 0; + rul.lcd_data.on_time = 0; + + /* Get display status at time of initiating RUL calculations */ + ret = device_display_get_state(&rul.lcd_data.state); + if (ret != DEVICE_ERROR_NONE) { + _E("Failed to get device display state with err = %d", ret); + return; + } + if ((rul.lcd_data.state == DISPLAY_STATE_NORMAL) || (rul.lcd_data.state == DISPLAY_STATE_SCREEN_DIM)) + rul.lcd_data.on_start_time = logging_get_time(CLOCK_BOOTTIME); + } else { + rul_curr_time = logging_get_time(CLOCK_BOOTTIME); + rul_time_interval = rul_curr_time - prev_charging_time; + prev_charging_time = rul_curr_time; + + ret = fread_int("/sys/class/power_supply/battery/current_now", &fg_curr_inst); + if (ret != RESOURCED_ERROR_NONE) { + _E("This device doesn't support FG CURRENT_NOW information. Disable LOGIC_RUL"); + return; + } + + fg_curr_inst_f = fg_curr_inst / 1000.0f; + rul.raw_capacity += ((fg_curr_inst_f + prev_fg_curr_inst_f) / 2.0f)*rul_time_interval; + prev_fg_curr_inst_f = fg_curr_inst_f; + } + + if (capacity == 100) { + rul_charging_cycle_end_work(); + } +} + /* ============================ DBUS -> DEVICED on demand ==================== */ static int heart_battery_direct_get_capacity(void) @@ -2015,6 +2234,20 @@ static void heart_battery_capacity_status(GVariant *params) first_level_change = FALSE; } } + if (logic_rul) { + /* Update prev_soc, curr_soc */ + rul.prev_soc = rul.curr_soc; + rul.curr_soc = capacity; + + /* + * If charger_status==CHARGING and SOC_INCREASING, start/continue raw capacity calculation. + * Else, if raw capacity calculation was ongoing, then dump collected data. + */ + if ((batt_stat.curr_charger_status == CHARGING) && (rul.curr_soc >= rul.prev_soc)) + rul_calculate_raw_capacity(); + else if (rul.charging_start_time != 0) + rul_charging_cycle_end_work(); + } } static void heart_battery_charger_status(GVariant *params) @@ -2081,6 +2314,14 @@ static void heart_battery_charger_status(GVariant *params) heart_battery_cal_charging_rem_time(); } } + if (logic_rul) { + /* + * If charger disconnected and raw capacity calculation was ongoing, + * then dump collected data. + */ + if ((charger_status == DISCHARGING) && (rul.charging_start_time != 0)) + rul_charging_cycle_end_work(); + } } /* ========================= DBUS -> DEVICED handler END ==================== */ @@ -2684,6 +2925,9 @@ static int heart_battery_config(struct parse_result *result, void *user_data) if (!strncmp(result->name, "LOGIC_V2", sizeof("LOGIC_V2") + 1)) { logic_v2 = atoi(result->value); _I("logic_v2 : %d", logic_v2); + } else if (!strncmp(result->name, "LOGIC_RUL", sizeof("LOGIC_RUL") + 1)) { + logic_rul = atoi(result->value); + _I("logic_rul : %d", logic_rul); } else if (!strncmp(result->name, "DISCHARGE_FAST", sizeof("DISCHARGE_FAST") + 1)) { discharge_fast = atoi(result->value); _I("discharge_fast: %d", discharge_fast); @@ -2772,6 +3016,47 @@ static void heart_read_battery_total_capacity(void) batt_capacity, battery_header); } +static void rul_data_init(void) +{ + _cleanup_fclose_ FILE *fp = NULL; + int ret; + + rul.charging_start_time = 0; + rul.charging_stop_time = 0; + rul.raw_capacity = 0; + rul.soc_start = 0; + rul.soc_stop = 0; + rul.curr_soc = heart_battery_direct_get_capacity(); + rul.prev_soc = rul.curr_soc; + + /* Init LCD related data */ + rul.lcd_data.on_start_time = 0; + rul.lcd_data.on_stop_time = 0; + rul.lcd_data.on_time = 0; + ret = device_display_get_state(&rul.lcd_data.state); + if (ret != DEVICE_ERROR_NONE) { + _E("Failed to get device display state with err = %d", ret); + return; + } + + /* Register callback for LCD state change */ + device_add_callback(DEVICE_CALLBACK_DISPLAY_STATE, rul_display_state_changed_cb, NULL); + + /* Read Charging cycles count from file */ + fp = fopen(RUL_CHARGING_CYCLES_FILE, "r"); + if (fp == NULL) { + _E("Can't open RUL_CHARGING_CYCLES_FILE"); + rul.charging_cycles = 0; + return; + } + + if (fscanf(fp, "%d", &rul.charging_cycles) != 1) { + _E("Unable to assign charging cycles from file"); + rul.charging_cycles = 0; + return; + } +} + static int heart_battery_init(void *data) { int ret; @@ -2805,6 +3090,9 @@ static int heart_battery_init(void *data) heart_battery_status_init(); + if (logic_rul) + rul_data_init(); + register_notifier(RESOURCED_NOTIFIER_LOW_BATTERY, low_battery_handler); register_notifier(RESOURCED_NOTIFIER_DATA_RESET, heart_battery_reset); -- 2.7.4 From 41d0b333942b35b39d5e6d6dbf46438f15966469 Mon Sep 17 00:00:00 2001 From: Michal Bloch Date: Mon, 7 Oct 2019 16:42:46 +0200 Subject: [PATCH 04/16] Port vmpressure-lowmem-handler.c Change-Id: I21fe594cd921aea9943f82bb968e3759d8226fcf --- src/common/memory-common.h | 10 + src/memory/memcontrol.c | 22 +++ src/memory/vmpressure-lowmem-handler.c | 348 +++++++++++++++++++++++++++------ 3 files changed, 315 insertions(+), 65 deletions(-) diff --git a/src/common/memory-common.h b/src/common/memory-common.h index 0781a82..872b87e 100644 --- a/src/common/memory-common.h +++ b/src/common/memory-common.h @@ -72,6 +72,11 @@ enum lowmem_control_type { #define MEMCG_DEFAULT_EVENT_LEVEL "low" #define MEMCG_DEFAULT_USE_HIERARCHY 0 +#define MEMCG_LOW_RATIO 0.8 +#define MEMCG_MEDIUM_RATIO 0.96 +#define MEMCG_FOREGROUND_LEAVE_RATIO 0.25 + + #define LOWMEM_ROOT_CGROUP "/sys/fs/cgroup/memory" #define LOWMEM_APPS_CGROUP LOWMEM_ROOT_CGROUP"/Apps" #define LOWMEM_BGLOCKED_CGROUP LOWMEM_ROOT_CGROUP"/Apps/BgLocked" @@ -88,6 +93,7 @@ enum lowmem_control_type { #define MEMCG_SWAPNESS_PATH "memory.swappiness" #define MEMPS_LOG_PATH "/var/log" +#define MEMPS_LOG_FILE MEMPS_LOG_PATH"/memps" struct memcg_info { /* name of memory cgroup */ @@ -105,6 +111,7 @@ struct memcg_info { unsigned int threshold[LOWMEM_MAX_LEVEL]; unsigned int threshold_leave; int evfd; + int swappiness; }; struct memcg { @@ -121,6 +128,9 @@ struct lowmem_control_data { int args[2]; }; +void memcg_info_set_limit(struct memcg_info *memcg_info, float ratio, + unsigned int totalram); +void memcg_info_set_swappiness(struct memcg_info *mi, int swappiness); void memcg_init(struct memcg *memcg); /** diff --git a/src/memory/memcontrol.c b/src/memory/memcontrol.c index 5a2d622..72eb99f 100644 --- a/src/memory/memcontrol.c +++ b/src/memory/memcontrol.c @@ -45,6 +45,28 @@ #define BUF_MAX 1023 +void memcg_info_set_limit(struct memcg_info *mi, float ratio, + unsigned int totalram) +{ + if (!mi) + return; + + mi->limit = (float)totalram * ratio; + mi->limit_ratio = ratio; + mi->threshold[LOWMEM_LOW] = (unsigned int)(mi->limit * MEMCG_LOW_RATIO); + mi->threshold[LOWMEM_MEDIUM] = (unsigned int)(mi->limit * MEMCG_MEDIUM_RATIO); + mi->threshold_leave = (float)mi->limit * MEMCG_FOREGROUND_LEAVE_RATIO; + mi->oomleave = mi->limit - mi->threshold_leave; +} + +void memcg_info_set_swappiness(struct memcg_info *mi, int swappiness) +{ + if (!mi) + return; + + mi->swappiness = swappiness; +} + void memcg_init(struct memcg *memcg) { memcg->use_hierarchy = MEMCG_DEFAULT_USE_HIERARCHY; diff --git a/src/memory/vmpressure-lowmem-handler.c b/src/memory/vmpressure-lowmem-handler.c index 12ab16f..9a7cfec 100644 --- a/src/memory/vmpressure-lowmem-handler.c +++ b/src/memory/vmpressure-lowmem-handler.c @@ -70,6 +70,7 @@ #define LOWMEM_NO_LIMIT 0 #define LOWMEM_THRES_INIT 0 +#define MEMPS_EXEC_PATH "usr/bin/memps" #define MEMCG_MOVE_CHARGE_PATH "memory.move_charge_at_immigrate" #define MEMCG_EVENTFD_MEMORY_PRESSURE "memory.pressure_level" #define MEM_CONF_FILE RD_CONFIG_FILE(memory) @@ -82,7 +83,9 @@ #define BUF_MAX 1024 #define MAX_VICTIMS_BETWEEN_CHECK 3 +#define MAX_PROACTIVE_LOW_VICTIMS 2 #define MAX_PROACTIVE_HIGH_VICTIMS 4 +#define FOREGROUND_VICTIMS 1 #define OOM_TIMER_INTERVAL 2 #define OOM_KILLER_PRIORITY -20 #define MAX_MEMPS_LOGS 50 @@ -264,8 +267,9 @@ static void medium_act(void); static size_t cur_mem_state = LOWMEM_NORMAL; static int num_max_victims = MAX_MEMORY_CGROUP_VICTIMS; static int num_vict_between_check = MAX_VICTIMS_BETWEEN_CHECK; +static int default_swappiness = -1; -static unsigned long long totalram; +static unsigned long totalram; static unsigned long ktotalram; static struct module_ops memory_modules_ops; @@ -274,6 +278,7 @@ static bool oom_popup_enable; static bool oom_popup; static bool memcg_swap_status; static bool bg_reclaim; +static int fragmentation_size; static struct memcg_info gmi[MEMCG_MAX] = { {LOWMEM_ROOT_CGROUP, "/", MEMCG_ROOT,}, @@ -332,18 +337,12 @@ static int lowmem_launch_oompopup(void) static inline void get_total_memory(void) { struct sysinfo si; - unsigned long long total_memory; - if (totalram) return; if (!sysinfo(&si)) { - total_memory = (unsigned long long)si.totalram * si.mem_unit; - totalram = total_memory; - - /* ktotalram can cover up to 4TB */ - total_memory = BYTE_TO_KBYTE(totalram); - ktotalram = (total_memory >= ULONG_MAX) ? ULONG_MAX : total_memory; + totalram = si.totalram; + ktotalram = BYTE_TO_KBYTE(totalram); } } @@ -415,6 +414,28 @@ static int memps_file_select(const struct dirent *entry) return strstr(entry->d_name, "memps") ? 1 : 0; } +static char *strrstr(const char *str, const char *token) +{ + int len = strlen(token); + const char *p = str + strlen(str); + + while (str <= --p) + if (p[0] == token[0] && strncmp(p, token, len) == 0) + return (char *)p; + + return NULL; +} + +static int timesort(const struct dirent **a, const struct dirent **b) +{ + long long time1, time2; + + time1 = atoll(strtok(strrstr((*a)->d_name, "_"), "_")); + time2 = atoll(strtok(strrstr((*b)->d_name, "_"), "_")); + + return (time1 - time2); +} + int clear_logs(void *data) { struct dirent **namelist; @@ -435,7 +456,7 @@ int clear_logs(void *data) return RESOURCED_ERROR_INVALID_PARAMETER; } - n = scandir(dir, &namelist, memps_file_select, alphasort); + n = scandir(dir, &namelist, memps_file_select, timesort); _D("num of log files %d", n); if (n < MAX_MEMPS_LOGS) { while (n--) @@ -470,11 +491,11 @@ int clear_logs(void *data) void make_memps_log(char *file, pid_t pid, char *victim_name) { - _cleanup_fclose_ FILE *f = NULL; time_t now; struct tm cur_tm; char new_log[BUF_MAX]; static pid_t old_pid; + int oom_score_adj = 0, ret; if (old_pid == pid) return; @@ -488,22 +509,34 @@ void make_memps_log(char *file, pid_t pid, char *victim_name) } snprintf(new_log, sizeof(new_log), - "%s_%s_%d_%.4d%.2d%.2d%.2d%.2d%.2d", file, victim_name, - pid, (1900 + cur_tm.tm_year), 1 + cur_tm.tm_mon, - cur_tm.tm_mday, cur_tm.tm_hour, cur_tm.tm_min, - cur_tm.tm_sec); - - f = fopen(new_log, "w"); - if (!f) { - _E("fail to create memps log %s", new_log); - return; - } + "%s_%s_%d_%.4d%.2d%.2d%.2d%.2d%.2d", file, victim_name, + pid, (1900 + cur_tm.tm_year), 1 + cur_tm.tm_mon, + cur_tm.tm_mday, cur_tm.tm_hour, cur_tm.tm_min, + cur_tm.tm_sec); + + ret = proc_get_oom_score_adj(pid, &oom_score_adj); + if (ret || oom_score_adj > OOMADJ_BACKGRD_LOCKED) { - proc_print_meninfo(f); + _cleanup_fclose_ FILE *f = NULL; + + f = fopen(new_log, "w"); + if (!f) { + _E("fail to create memps log %s", new_log); + return; + } + proc_print_meninfo(f); + + } else { + + const char *argv[4] = {"/usr/bin/memps", "-f", NULL, NULL}; + + argv[2] = new_log; + exec_cmd(ARRAY_SIZE(argv), argv); + } } static int lowmem_kill_victim(const struct task_info *tsk, - int flags, unsigned int *victim_size) + int flags, int memps_log, unsigned int *victim_size) { pid_t pid; int ret; @@ -520,21 +553,26 @@ static int lowmem_kill_victim(const struct task_info *tsk, if (ret == RESOURCED_ERROR_FAIL) return RESOURCED_ERROR_FAIL; - if (!strcmp("crash-worker", appname) || + if (!strcmp("memps", appname) || + !strcmp("crash-worker", appname) || !strcmp("system-syspopup", appname)) { _E("%s(%d) was selected, skip it", appname, pid); return RESOURCED_ERROR_FAIL; } + if (!memps_log) + make_memps_log(MEMPS_LOG_FILE, pid, appname); + + pai = find_app_info(pid); if (pai) { resourced_proc_status_change(PROC_CGROUP_SET_TERMINATE_REQUEST, pid, NULL, NULL, PROC_TYPE_NONE); - if (tsk->oom_score_adj < OOMADJ_BACKGRD_LOCKED) { + if (tsk->oom_score_lru <= OOMADJ_BACKGRD_LOCKED) { sigterm = 1; - } else if (tsk->oom_score_adj == OOMADJ_BACKGRD_LOCKED) { + } else if (tsk->oom_score_lru > OOMADJ_BACKGRD_LOCKED && tsk->oom_score_lru < OOMADJ_BACKGRD_UNLOCKED) { int app_flag = pai->flags; sigterm = app_flag & PROC_SIGTERM; } @@ -555,13 +593,15 @@ static int lowmem_kill_victim(const struct task_info *tsk, tsk->size, sigterm); *victim_size = tsk->size; - if (tsk->oom_score_adj > OOMADJ_FOREGRD_UNLOCKED) + if (tsk->oom_score_lru > OOMADJ_FOREGRD_UNLOCKED) return RESOURCED_ERROR_NONE; if (oom_popup_enable && !oom_popup) { lowmem_launch_oompopup(); oom_popup = true; } + if (memps_log) + make_memps_log(MEMPS_LOG_FILE, pid, appname); return RESOURCED_ERROR_NONE; } @@ -571,7 +611,11 @@ static int lowmem_check_kill_continued(struct task_info *tsk, int flags) { unsigned int available; - if (tsk->oom_score_adj > OOMADJ_BACKGRD_PERCEPTIBLE) + /* + * Processes with the priority higher than perceptible are killed + * only when the available memory is less than dynamic oom threshold. + */ + if (tsk->oom_score_lru > OOMADJ_BACKGRD_PERCEPTIBLE) return LOWMEM_RECLAIM_CONT; if ((flags & (OOM_FORCE|OOM_SINGLE_SHOT)) || !(flags & OOM_REVISE)) { @@ -597,8 +641,8 @@ static int compare_victims(const struct task_info *ta, const struct task_info *t assert(ta != NULL); assert(tb != NULL); - if (ta->oom_score_adj != tb->oom_score_adj) - return tb->oom_score_adj - ta->oom_score_adj; + if (ta->oom_score_lru != tb->oom_score_lru) + return tb->oom_score_lru - ta->oom_score_lru; /* * Get memory usage for tasks with the same oom score. @@ -612,6 +656,24 @@ static int compare_victims(const struct task_info *ta, const struct task_info *t return (int)(tb->size) - (int)(ta->size); } +static int compare_victims_point(const struct task_info *ta, const struct task_info *tb) +{ + unsigned int pa, pb; + + assert(ta != NULL); + assert(tb != NULL); + /* + * followed by kernel badness point calculation using heuristic. + * oom_score_adj is normalized by its unit, which varies -1000 ~ 1000. + * Since we only consider tasks with oom_score_adj larger than 0 + * as victim candidates, point always has positive value. + */ + pa = ta->oom_score_lru * (ktotalram / 1000) + ta->size; + pb = tb->oom_score_lru * (ktotalram / 1000) + tb->size; + + return pb - pa; +} + static void lowmem_free_task_info_array(GArray *array) { int i; @@ -627,14 +689,18 @@ static void lowmem_free_task_info_array(GArray *array) g_array_free(array, true); } -static unsigned int is_memory_recovered(unsigned int *avail, unsigned int *thres) +static inline int is_dynamic_process_killer(int flags) +{ + return (flags & OOM_FORCE) && !(flags & OOM_NOMEMORY_CHECK); +} + +static unsigned int is_memory_recovered(unsigned int *avail, unsigned int thres) { unsigned int available = proc_get_mem_available(); - unsigned int leave_threshold = memcg_root->threshold_leave; unsigned int should_be_freed = 0; - if (available < leave_threshold) - should_be_freed = leave_threshold - available; + if (available < thres) + should_be_freed = thres - available; /* * free THRESHOLD_MARGIN more than real should be freed, * because launching app is consuming up the memory. @@ -643,12 +709,70 @@ static unsigned int is_memory_recovered(unsigned int *avail, unsigned int *thres should_be_freed += THRESHOLD_MARGIN; *avail = available; - *thres = leave_threshold; _I("should_be_freed = %u MB", should_be_freed); return should_be_freed; } +static int lowmem_get_pids_proc(GArray *pids, bool add_app) +{ + DIR *dp; + struct dirent *dentry; + + dp = opendir("/proc"); + if (!dp) { + _E("fail to open /proc"); + return RESOURCED_ERROR_FAIL; + } + while ((dentry = readdir(dp)) != NULL) { + struct task_info tsk; + pid_t pid = 0, pgid = 0; + int oom = 0; + + if (!isdigit(dentry->d_name[0])) + continue; + + pid = (pid_t)atoi(dentry->d_name); + if (pid < 1) + /* skip invalid pids or kernel processes */ + continue; + + pgid = getpgid(pid); + if (pgid < 1) + continue; + + if (proc_get_oom_score_adj(pid, &oom) < 0) { + _D("pid(%d) was already terminated", pid); + continue; + } + + /* + * Check whether this array includes applications or not. + * If it doesn't require to get applications + * and pid has been already included in pai, + * skip to append. + */ + if (!add_app && find_app_info(pid)) + continue; + + /* + * Currently, for tasks in the memory cgroup, + * do not consider multiple tasks with one pgid. + */ + tsk.pid = pid; + tsk.pgid = pgid; + tsk.oom_score_adj = oom; + tsk.oom_score_lru = oom; + tsk.pids = NULL; + tsk.size = lowmem_get_task_mem_usage_rss(&tsk); + + g_array_append_val(pids, tsk); + } + + closedir(dp); + return RESOURCED_ERROR_NONE; +} + /** * @brief Terminate up to max_victims processes after finding them from pai. It depends on proc_app_info lists @@ -671,7 +795,6 @@ static int lowmem_kill_victims(int max_victims, int i, ret, victim = 0; unsigned int victim_size = 0; unsigned int total_victim_size = 0; - unsigned int total_task_size = 0; int status = LOWMEM_RECLAIM_NONE; GArray *candidates = NULL; GSList *iter, *iterchild; @@ -700,6 +823,17 @@ static int lowmem_kill_victims(int max_victims, ti.pgid = getpgid(ti.pid); ti.oom_score_adj = oom_score_adj; + /* + * Before oom_score_adj of favourite (oom_score = 270) applications is + * independent of lru_state, now we consider lru_state, while + * killing favourite process. + */ + + if (oom_score_adj == OOMADJ_FAVORITE && pai->lru_state >= PROC_BACKGROUND) + ti.oom_score_lru = OOMADJ_FAVORITE + OOMADJ_FAVORITE_APP_INCREASE * pai->lru_state; + else + ti.oom_score_lru = oom_score_adj; + if (pai->childs) { ti.pids = g_array_new(false, false, sizeof(pid_t)); g_array_append_val(ti.pids, ti.pid); @@ -710,9 +844,7 @@ static int lowmem_kill_victims(int max_victims, } else ti.pids = NULL; - ti.size = lowmem_get_task_mem_usage_rss(&ti); g_array_append_val(candidates, ti); - total_task_size += ti.size; } proc_app_list_close(); @@ -720,7 +852,26 @@ static int lowmem_kill_victims(int max_victims, if (!candidates->len) goto leave; - g_array_sort(candidates, (GCompareFunc)compare_victims); + for (i = 0; i < candidates->len; i++) { + struct task_info *tsk; + + tsk = &g_array_index(candidates, struct task_info, i); + tsk->size = lowmem_get_task_mem_usage_rss(tsk); + } + + /* + * In case of start_oom == OOMADJ_SU, + * we're going to try to kill some of processes in /proc + * to handle low memory situation. + * It can find malicious system process even though it has low oom score. + */ + if (start_oom == OOMADJ_SU) + lowmem_get_pids_proc(candidates, false); + + if (start_oom <= OOMADJ_BACKGRD_LOCKED || start_oom >= OOMADJ_BACKGRD_OLD) + g_array_sort(candidates, (GCompareFunc)compare_victims_point); + else + g_array_sort(candidates, (GCompareFunc)compare_victims); for (i = 0; i < candidates->len; i++) { struct task_info *tsk; @@ -757,7 +908,7 @@ static int lowmem_kill_victims(int max_victims, _I("select victims from proc_app_list pid(%d)\n", tsk->pid); - ret = lowmem_kill_victim(tsk, flags, &victim_size); + ret = lowmem_kill_victim(tsk, flags, i, &victim_size); if (ret != RESOURCED_ERROR_NONE) continue; victim++; @@ -796,7 +947,7 @@ static void lowmem_handle_request(struct lowmem_control *ctl) int start_oom, end_oom; int count = 0, victim_cnt = 0; int status = LOWMEM_RECLAIM_NONE; - unsigned int available = 0, leave_threshold = 0; + unsigned int available = 0; unsigned int total_size = 0; unsigned int current_size = 0; unsigned int reclaim_size, shortfall = 0; @@ -815,7 +966,7 @@ retry: /* Prepare LMK to start doing it's job. Check preconditions. */ calualate_range_of_oom(lmk_type, &start_oom, &end_oom); lmk_start_threshold = memcg_root->threshold[LOWMEM_MEDIUM]; - shortfall = is_memory_recovered(&available, &leave_threshold); + shortfall = is_memory_recovered(&available, ctl->size); if (!shortfall || !reclaim_size) { status = LOWMEM_RECLAIM_DONE; @@ -859,13 +1010,32 @@ retry: (Make sluggish or kill same victims continuously) Thus, otherwise, just return in first operation and wait some period. */ - if ((count < ctl->count) && (ctl->flags & OOM_IN_DEPTH)) { + if (count < ctl->count) { if (lmk_type == LMK_OLDEST) { - /* We tried in inactive processes and didn't succeed, try immediately in active */ - _D("Inactive wasn't enough, lets try in Active."); + _D("Oldest wasn't enough, lets try in RECENTLY USED."); + lmk_type = LMK_RECENTLY_USE; + goto retry; + } else if (lmk_type == LMK_RECENTLY_USE) { + /* We tried in inactive processes and didn't succed, try immediatly in active */ + _D("Recenlty used wasn't enough, lets try in Active."); lmk_type = LMK_ACTIVE; + + /* + * In case of force reclaim, leave threshold is harger than original threshold as margin. + * So it should be reduced when trying to kill applications in ACTIVE group + * in order to prevent aggresive killing of perceptible applications. + */ + if ((ctl->flags & OOM_FORCE) && + (ctl->count - count > MAX_PROACTIVE_LOW_VICTIMS)) + ctl->count = MAX_PROACTIVE_LOW_VICTIMS; + goto retry; + } else if ((lmk_type == LMK_ACTIVE) && (ctl->flags & OOM_IN_DEPTH)) { + /* We tried in inactive processes and didn't succed, try immediatly in active */ + _D("Recenlty used wasn't enough, lets try in foreground."); + lmk_type = LMK_FOREGROUND; + ctl->count = FOREGROUND_VICTIMS; goto retry; - } else if (lmk_type == LMK_ACTIVE) { + } else if ((lmk_type == LMK_FOREGROUND) && (ctl->flags & OOM_IN_DEPTH)) { /* * We tried in INACTIVE and ACTIVE but still didn't succeed * so it's time to try in /proc. Before doing it wait some time. @@ -879,9 +1049,8 @@ retry: done: _E("Done: killed %d processes reclaimed: %u remaining: %u shortfall: %u status: %d\n", count, total_size, reclaim_size, shortfall, status); - /* After we finish reclaiming it's worth to remove oldest memps logs */ - if (status == LOWMEM_RECLAIM_DONE) + if (count) request_helper_worker(CLEAR_LOGS, MEMPS_LOG_PATH, clear_logs, NULL); ctl->status = status; } @@ -1297,6 +1466,9 @@ static int set_memory_config(const char *section_name, const struct parse_result } else if (!strncmp(result->name, "ThresholdLeave", strlen("ThresholdLeave")+1)) { int value = atoi(result->value); lowmem_memcg_set_leave_threshold(MEMCG_MEMORY, value); + } else if (!strncmp(result->name, "ForegroundRatio", strlen("ForegroundRatio")+1)) { + float ratio = atof(result->value); + memcg_info_set_limit(memcg_tree[MEMCG_APPS]->info, ratio, totalram); } else if (!strncmp(result->name, "NumMaxVictims", strlen("NumMaxVictims")+1)) { int value = atoi(result->value); num_max_victims = value; @@ -1318,6 +1490,18 @@ static int set_memory_config(const char *section_name, const struct parse_result event_level = strdup(result->value); if (!event_level) return RESOURCED_ERROR_OUT_OF_MEMORY; + } else if (!strncmp(result->name, "SWAPPINESS", strlen("SWAPPINESS")+1)) { + int value = atoi(result->value); + default_swappiness = value; + memcg_info_set_swappiness(memcg_tree[MEMCG_MEMORY]->info, value); + } else if (!strncmp(result->name, "APPCG_SWAPPINESS", strlen("APPCG_SWAPPINESS")+1)) { + int value = atoi(result->value); + memcg_info_set_swappiness(memcg_tree[MEMCG_APPS]->info, value); + } else if (!strncmp(result->name, "SWAPCG_SWAPPINESS", strlen("SWAPCG_SWAPPINESS")+1)) { + int value = atoi(result->value); + memcg_info_set_swappiness(memcg_tree[MEMCG_SWAP]->info, value); + } else if (!strncmp(result->name, "NumFragSize", strlen("NumFragSize")+1)) { + fragmentation_size = atoi(result->value); } return RESOURCED_ERROR_NONE; @@ -1470,6 +1654,7 @@ static int write_params_memcg_info(struct memcg_info *mi) unsigned int limit = mi->limit; const char *name = mi->name; int ret = RESOURCED_ERROR_NONE; + int swappiness = -1; _I("write memcg param for %s", name); /* enable cgroup move */ ret = cgroup_write_node_uint32(name, @@ -1477,6 +1662,24 @@ static int write_params_memcg_info(struct memcg_info *mi) if (ret) return ret; + /* + * write swapness if it has a meaningful value. + * if it has own swappiness value, set it to memcg at first. + * otherwise, check default_swappiness value and use it. + */ + if (mi->swappiness >= 0) + swappiness = mi->swappiness; + else if (default_swappiness >= 0) + swappiness = default_swappiness; + + if (swappiness >= 0) { + ret = cgroup_write_node_uint32(name, + MEMCG_SWAPNESS_PATH, swappiness); + if (ret) + _I("failed to write %s %d to %s the", + MEMCG_SWAPNESS_PATH, swappiness, name); + } + if (mi->limit_ratio == LOWMEM_NO_LIMIT) return ret; @@ -1771,9 +1974,7 @@ static int lowmem_press_setup_eventfd(void) if (!memcg_tree[i]->use_hierarchy) continue; - const int r = lowmem_press_register_eventfd(memcg_tree[i]->info); - if (r < 0) - return RESOURCED_ERROR_FAIL; + lowmem_press_register_eventfd(memcg_tree[i]->info); } return RESOURCED_ERROR_NONE; } @@ -1825,7 +2026,14 @@ bool lowmem_fragmentated(void) if (ret < 0) return false; - if (bi.page[PAGE_32K] + bi.page[PAGE_64K] + bi.page[PAGE_128K] == 0) { + /* + * The fragmentation_size is the minimum count of order-2 pages in "Normal" zone. + * If total buddy pages is smaller than fragmentation_size, + * resourced will detect kernel memory is fragmented. + * Default value is zero in low memory device. + */ + if (bi.page[PAGE_32K] + (bi.page[PAGE_64K] << 1) + (bi.page[PAGE_128K] << 2) + + (bi.page[PAGE_256K] << 3) < fragmentation_size) { _I("fragmentation detected, need to execute proactive oom killer"); return true; } @@ -1839,13 +2047,18 @@ static void lowmem_proactive_oom_killer(int flags, char *appid) before = proc_get_mem_available(); - /* If low memory state, just return and kill in oom killer */ - if (before < memcg_root->threshold[LOWMEM_MEDIUM]) + /* If memory state is medium or normal, just return and kill in oom killer */ + if (before < memcg_root->threshold[LOWMEM_MEDIUM] || before > proactive_leave) return; victims = num_max_victims > MAX_PROACTIVE_HIGH_VICTIMS ? MAX_PROACTIVE_HIGH_VICTIMS : num_max_victims; +#ifdef HEART_SUPPORT + /* + * This branch is used only when HEART module is compiled in and + * it's MEMORY module must be enabled. Otherwise this is skipped. + */ struct heart_memory_data *md = heart_memory_get_memdata(appid, DATA_LATEST); if (md) { unsigned int rss, after, size; @@ -1873,13 +2086,7 @@ static void lowmem_proactive_oom_killer(int flags, char *appid) return; } - - /* - * run proactive oom killer only when available is larger than - * proactive threshold - */ - if (!proactive_threshold || before >= proactive_threshold) - return; +#endif /* * When there is no history data for the launching app, @@ -1887,9 +2094,20 @@ static void lowmem_proactive_oom_killer(int flags, char *appid) * So, resourced feels proactive LMK is required, run oom killer based on dynamic * threshold. */ - if (!lowmem_fragmentated() && !(flags & PROC_LARGEMEMORY)) + if (lowmem_fragmentated()) + goto reclaim; + + /* + * run proactive oom killer only when available is larger than + * dynamic process threshold + */ + if (!proactive_threshold || before >= proactive_threshold) return; + if (!(flags & PROC_LARGEMEMORY)) + return; + +reclaim: /* * free THRESHOLD_MARGIN more than real should be freed, * because launching app is consuming up the memory. @@ -1897,7 +2115,6 @@ static void lowmem_proactive_oom_killer(int flags, char *appid) _D("Run threshold based proactive LMK: memory level to reach: %u\n", proactive_leave + THRESHOLD_MARGIN); lowmem_trigger_reclaim(0, victims, LMK_OLDEST, proactive_leave + THRESHOLD_MARGIN); - return; } unsigned int lowmem_get_proactive_thres(void) @@ -1908,9 +2125,10 @@ unsigned int lowmem_get_proactive_thres(void) static int lowmem_prelaunch_handler(void *data) { struct proc_status *ps = (struct proc_status *)data; + struct proc_app_info *pai = ps->pai; - if (!ps->pai) - return RESOURCED_ERROR_NO_DATA; + if (!pai || CHECK_BIT(pai->flags, PROC_SERVICEAPP)) + return RESOURCED_ERROR_NONE; lowmem_proactive_oom_killer(ps->pai->flags, ps->pai->appid); return RESOURCED_ERROR_NONE; -- 2.7.4 From 5639c2149a7017190b00273f81168e6f968be033 Mon Sep 17 00:00:00 2001 From: Michal Bloch Date: Thu, 3 Oct 2019 18:33:36 +0200 Subject: [PATCH 05/16] Port heart-battery.c Change-Id: I232bdda38ab5c2cc73a93c5f75104f31fc49327f Signed-off-by: Michal Bloch --- src/common/heart-common.h | 30 +++ src/heart/heart-battery.c | 470 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 491 insertions(+), 9 deletions(-) diff --git a/src/common/heart-common.h b/src/common/heart-common.h index 8fcc818..2680baf 100644 --- a/src/common/heart-common.h +++ b/src/common/heart-common.h @@ -30,6 +30,13 @@ #include #include +#define HEART_BATTERY_STATS_MAX 1024 +#define QUERY_INSERT_BATTERYSTATS "INSERT INTO batterystats(resourceid, starttime, stoptime) VALUES('%d', '%ld', '%ld');" +#define QUERY_SELECT_BATTERYSTATS_BY_RESOURCEID "SELECT sum(min(stoptime - starttime, stoptime - %ld)) from batterystats where resourceid=%d and stoptime > %ld;" +#define BATTERY_STATS_DB_FILE_NAME "/opt/usr/dbspace/.battery_stats.db" +#define QUERY_CREATE_BATTERY_STATS "CREATE TABLE IF NOT EXISTS batterystats (resourceid INTEGER NOT NULL, starttime INTEGER, stoptime INTEGER);" +#define RESOURCEID_LENGTH_MAX 16 + /* period data types */ enum heart_data_period { DATA_LATEST, @@ -54,6 +61,17 @@ enum discharge_rate_level_type { BATTERY_DISCHARGE_MAX = 3, }; +enum resource_id { + WIFI_OFF = 100, + WIFI_UNCONNECTED = 110, + WIFI_CONNECTED = 120, + WIFI_TRANSFER = 130, + WIFI_TRANSFER_NONE = 131, + WIFI_TRANSFER_TX = 132, + WIFI_TRANSFER_RX = 133, + WIFI_TRANSFER_TXRX = 134, +}; + struct heart_cpu_data { char appid[MAX_APPID_LENGTH]; char pkgid[MAX_PKGNAME_LENGTH]; @@ -92,9 +110,21 @@ typedef int (*heart_cpu_get_appusage_list_t)(int uid, GHashTable *lists, int top extern heart_memory_get_data_t heart_memory_get_data_func; extern heart_cpu_get_appusage_list_t heart_cpu_get_appusage_list_func; +struct heart_calc_time { + time_t from; + time_t to; +}; + /* battery */ int heart_battery_get_capacity_history_latest(GArray *arrays, int charge, int max_size); int heart_battery_get_capacity_history(GArray *arrays, enum heart_data_period period); +int heart_battery_stats_execute_insert_db(char buf[]); +long heart_battery_stats_execute_select_total_db(char buf[]); + +/* wifi */ +double heart_battery_get_wifi_uptime_app(char *appid); +long heart_battery_get_wifi_uptime(struct heart_calc_time calc_time, enum resource_id res); +void heart_wifi_usage(struct heart_calc_time calc_time); /* cpu */ struct heart_cpu_data *heart_cpu_get_data(int uid, char *appid, enum heart_data_period period, struct heart_cpu_data *data); diff --git a/src/heart/heart-battery.c b/src/heart/heart-battery.c index d1137b2..d106e81 100644 --- a/src/heart/heart-battery.c +++ b/src/heart/heart-battery.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -97,9 +98,23 @@ #define BATTERY_LEVEL_GAP 5 /*previous 5 battery data*/ #define LONG_TIME_WEIGHT (0.7) /*weightage given for longer time discharge*/ #define SHORT_TIME_WEIGHT (1-LONG_TIME_WEIGHT) /*weightage given for short time discharge*/ +#define NEW_PIVOT_WEIGHTAGE(x) (x * 0.10) /* x/10th part */ +#define OLD_PIVOT_WEIGHTAGE(x) (1.0 - NEW_PIVOT_WEIGHTAGE(x)) +#define MIN_DISCHARGE_PERIOD 40 +#define LOW_CAPACITY 2 #define MIN_TIME_FOR_LVL_CHANGE 50 #define BATT_CHG_FLUSH_DATA 10 +/* + * Below 6 values are the co-efficient of the polynomial. These are devired from + * OCV and and battery percentage level based on experiment + */ +#define OCV_SOC_POLY_COEF_1 (3402.664) +#define OCV_SOC_POLY_COEF_2 (42.031) +#define OCV_SOC_POLY_COEF_3 (-1.76745) +#define OCV_SOC_POLY_COEF_4 (0.034798) +#define OCV_SOC_POLY_COEF_5 (-0.00030516) +#define OCV_SOC_POLY_COEF_6 (0.0000010116) #define UPS_FACTOR (2.88) #define DOUBLE_ZERO (0.000000) #define FULL_CAPACITY 100 @@ -108,11 +123,13 @@ * will be considered as 90 */ #define BATTERY_C_RATE 90 -#define SEC_TO_MIN(x) ((x)/60) +#define SEC_TO_MIN(x) ((x + 30)/60) /* 30 secs buffer to round off */ #define CAL_MIN(x, y) ((x < y) ? x : y) #define BATTERY_WINDOW_INDEX(x) (((x) + INDEX_WINDOW_SIZE) % INDEX_WINDOW_SIZE) #define HEART_BATTERY_DATA_FILE HEART_FILE_PATH"/.battery_data.dat" +#define HEART_BATTERY_PIVOT_INFO HEART_FILE_PATH"/.battery_pivot_data.dat" #define VCONFKEY_HEART_BATTERY_DEVICE_MODE "db/setting/psmode" +#define VCONFKEY_SETAPPL_MANAGE_BATTERY_TRIGGER_TIME "db/setting/manage_battery_trigger_time" /* * Remaining Useful Life prediction logic @@ -153,7 +170,6 @@ enum { DEFAULT_VALUE_MAX = 3, }; - /* Structure to calculate percentage time LCD on during charging */ struct rul_lcd_data { display_state_e state; @@ -193,6 +209,14 @@ struct battery_prediction { long time_pred_min[MAX_STRATEGY]; /* time prediction in minutes */ }; +struct battery_pivot_info { + long start_time_dischg; /* store the time from when discharge started */ + int pvt_data_ind; /* index to keep track of total_dischg_time value */ + int start_cap_dischg; /* capacity from when discharge started */ + int total_dischg_time[INDEX_WINDOW_SIZE]; /* total discharge period */ + time_t last_clock_tick; /* last tick stored */ +}; + struct battery_status { /* current battery status */ enum charger_status_type curr_charger_status; @@ -246,6 +270,9 @@ struct battery_status { /* calculated battery prediction */ struct battery_prediction prediction[MAX_CHARGER_STATE]; + + /* battery pivot information */ + struct battery_pivot_info pvt; }; static int default_sec_per_cap[MAX_CHARGER_STATE][DEFAULT_VALUE_MAX] = { @@ -265,6 +292,16 @@ static double default_mode_factor[POWER_MODE_MAX] = { 2.88 /* ULTRA_SAVING_MODE */ }; +struct heart_power_profile { + float wifi_on; + float wifi_active; + float wifi_scan; +}; + +static struct heart_power_profile power_profile; +#ifdef NETWORK_SUPPORT +static struct heart_calc_time calc_time; +#endif static struct battery_used batt_used; static struct battery_status batt_stat; static GSList *capacity_history_list = NULL; @@ -282,6 +319,9 @@ static bool data_avail_chg = false; /* 5 readings in charging mode */ * variable to hold the current device mode(NORMAL, PS & UPS) */ static int device_mode = POWER_NORMAL_MODE; + +static int heart_battery_direct_get_capacity(void); + /* * Given the 'SOC', remaining energy of the cell can be predicted. New battery * remaining time estimation logic will calculating the remaining energy as @@ -345,6 +385,8 @@ static char battery_header[BATTERY_LINE_MAX] = "BATTERY"; static int logic_rul; struct rul_info rul; +static int get_battery_remaining_time(int mode, enum charger_status_type status); + int heart_battery_get_capacity(void) { return batt_stat.curr_capacity; @@ -1204,6 +1246,14 @@ static void heart_battery_power_off_time_adjustment(void) * should not vary much */ first_level_change = TRUE; + + /* for pivot data */ + if (batt_stat.pvt.start_time_dischg != 0) { + time_diff = curr_time - batt_stat.pvt.start_time_dischg; + clock_tick_diff = logging_get_time(CLOCK_BOOTTIME) - batt_stat.pvt.last_clock_tick; + time_diff -= clock_tick_diff; + batt_stat.pvt.start_time_dischg += time_diff; + } } /* @@ -1307,6 +1357,178 @@ static void heart_battery_flush_charge_data(void) } /* + * store the discharge start time and capacity to calculte the + * time taken for the discharge period + */ +static void heart_battery_collect_discharge_data(void) +{ + batt_stat.pvt.start_cap_dischg = batt_stat.curr_capacity; + batt_stat.pvt.start_time_dischg = heart_battery_logging_get_time_sec_new(); + batt_stat.pvt.last_clock_tick = logging_get_time(CLOCK_BOOTTIME); +} + +/* + * Read all the stored pivot data from file and keep it in + * local variable batt_stat.pvt + */ +static void heart_battery_read_pivot_info_from_file(void) +{ + int i; + char buf[BATTERY_DATA_MAX] = {0, }; + char *p; + _cleanup_fclose_ FILE *fp = NULL; + + fp = fopen(HEART_BATTERY_PIVOT_INFO, "r"); + if (fp == NULL) { + _E("Could not open pivot info file"); + return; + } + + for (i = 0; i < INDEX_WINDOW_SIZE; i++) { + p = fgets(buf, BATTERY_DATA_MAX, fp); + if (p == NULL) + return; + + if (sscanf(buf, "%d", &batt_stat.pvt.total_dischg_time[i]) == -1) + return; + } + + p = fgets(buf, BATTERY_DATA_MAX, fp); + if (!p) + return; + if (sscanf(buf, "%d", &batt_stat.pvt.pvt_data_ind) == -1) + return; + +} + +/* + * Store all the local pivot data to a file + */ +static void heart_battery_save_pivot_info(void) +{ + int len = 0; + int i; + char buf[BATTERY_DATA_MAX] = {0, }; + _cleanup_fclose_ FILE *fp = NULL; + + fp = fopen(HEART_BATTERY_PIVOT_INFO, "w"); + if (fp == NULL) { + _E("Could not open pivot info file"); + return; + } + + for (i = 0; i < INDEX_WINDOW_SIZE; i++) { + len += snprintf(buf + len, BATTERY_DATA_MAX - len, "%d\n", + batt_stat.pvt.total_dischg_time[i]); + + if (BATTERY_DATA_MAX < len + BATTERY_LINE_MAX) { + fputs(buf, fp); + len = 0; + } + } + len += snprintf(buf + len, BATTERY_DATA_MAX - len, "%d", + batt_stat.pvt.pvt_data_ind); + + fputs(buf, fp); +} + +/* + * Calculate the remaining enery in battery based on current capacity + */ +static double heart_battery_get_energy(int capacity) +{ + int i; + double volt_intg = 0; + + for (i = 0; i < ocv_degree; i++) + volt_intg += (intg[i] /(i+1)) * pow(capacity , i+1); + + return (volt_intg /100000.0); +} + +/* + * Update the pivot based on device learning data + */ +static void heart_battery_update_pivot(void) +{ + int i; + int avg = 0; + + static int count = 1; + + for (i = 0; i < INDEX_WINDOW_SIZE; i++) { + if(batt_stat.pvt.total_dischg_time[i] == 0) + break; + avg += batt_stat.pvt.total_dischg_time[i]; + } + + if (avg == 0) + return; + if (i != 0) + avg = (avg / i); + + /* + * Total 10 discharge cycle data will be stored and based on those values + * new pivot will be calculted. Untill 10 cycles data are not available, take + * count/10 % from new pivot and 1-count/10 % from existing pivot. By + * doing this, sudden jump in pivot can be avoided + */ + if (count <= 10) { + avg = NEW_PIVOT_WEIGHTAGE(count) * avg + OLD_PIVOT_WEIGHTAGE(count) * pivot_nor; + count++; + } + + _I("Old pivot = %lf, New pivot =%d, count = %d", pivot_nor, avg, count); + + pivot_nor = avg; + pivot_ups = pivot_nor * UPS_FACTOR; +} + +/* + * This function will take the average of last 10 discharge time period and + * update the pivot + */ +static void heart_battery_collect_pivot_info(void) +{ + long curr_time; + int time_diff; + int cap_diff; + int total_time; + + double energy_dis; + double energy_chg; + double energy_total; + + if (batt_stat.pvt.start_time_dischg == 0) + return; + + curr_time = heart_battery_logging_get_time_sec_new(); + time_diff = curr_time - batt_stat.pvt.start_time_dischg; + cap_diff = batt_stat.pvt.start_cap_dischg - batt_stat.curr_capacity; + + /* discharge period is too less so do not consider this period */ + if (cap_diff < MIN_DISCHARGE_PERIOD) + return; + + energy_dis = heart_battery_get_energy(batt_stat.pvt.start_cap_dischg); + energy_chg = heart_battery_get_energy(batt_stat.curr_capacity); + energy_total = heart_battery_get_energy(FULL_CAPACITY); + + total_time = (energy_total * time_diff) /(energy_dis - energy_chg); + + _I("Energy discharge = %lf, Energy charge = %lf, Energy Total = %lf, Total time = %d", + energy_dis, energy_chg, energy_total, total_time); + + batt_stat.pvt.total_dischg_time[batt_stat.pvt.pvt_data_ind] = SEC_TO_MIN(total_time); + batt_stat.pvt.pvt_data_ind = (batt_stat.pvt.pvt_data_ind + 1) % INDEX_WINDOW_SIZE; + + /* update the pivot */ + heart_battery_update_pivot(); + /* save pivot info into file */ + heart_battery_save_pivot_info(); +} + +/* * This function will calculate the battery charging remaining time */ static void heart_battery_cal_charging_rem_time(void) @@ -2210,6 +2432,9 @@ static void heart_battery_capacity_status(GVariant *params) */ int capacity = -1; + int bat_trigger = -1; + int bat_ups_est = -1; + int bat_normal_est = -1; do_expr_unless_g_variant_get_typechecked(return, params, "(i)", &capacity); if (capacity < 0) { @@ -2232,6 +2457,12 @@ static void heart_battery_capacity_status(GVariant *params) batt_stat.last_capacity_chg < batt_stat.curr_capacity) { heart_battery_cal_charging_rem_time(); first_level_change = FALSE; + } else { + /* + * if battery is very very low then update the pivot before power off + */ + if (batt_stat.curr_capacity == LOW_CAPACITY) + heart_battery_collect_pivot_info(); } } if (logic_rul) { @@ -2248,6 +2479,33 @@ static void heart_battery_capacity_status(GVariant *params) else if (rul.charging_start_time != 0) rul_charging_cycle_end_work(); } + + /* Get the UPSM trigger value and remaining estimate */ + if (vconf_get_int(VCONFKEY_SETAPPL_MANAGE_BATTERY_TRIGGER_TIME, &bat_trigger)) { + _E("failed to get VCONFKEY_SETAPPL_MANAGE_BATTERY_TRIGGER_TIME\n"); + return; + } + if (bat_trigger > 0) { /* Battery planner is enabled*/ + bat_ups_est = get_battery_remaining_time(ULTRA_SAVING_MODE, DISCHARGING); + bat_normal_est = get_battery_remaining_time(POWER_NORMAL_MODE, DISCHARGING); + _I(" trigger time: %d, ups est:%d, normal est:%d", bat_trigger, bat_ups_est, bat_normal_est); + if (bat_ups_est > 0 && bat_trigger >= bat_ups_est) { + char *args[4] = { "_SYSPOPUP_CONTENT_", "manage_battery", + "_BAT_POPUP_TYPE_", "enable_mbp"}; + + d_bus_call_method_sync(SYSTEM_POPUP_BUS_NAME, + SYSTEM_POPUP_PATH_SYSTEM, SYSTEM_POPUP_IFACE_SYSTEM, + "PopupLaunch", "ssss", args); + } + if (bat_normal_est > 0 && bat_normal_est >= bat_trigger) { + char *args[4] = { "_SYSPOPUP_CONTENT_", "manage_battery",\ + "_BAT_POPUP_TYPE_", "disable_mbp"}; + + d_bus_call_method_sync(SYSTEM_POPUP_BUS_NAME, + SYSTEM_POPUP_PATH_SYSTEM, SYSTEM_POPUP_IFACE_SYSTEM, + "PopupLaunch", "ssss", args); + } + } } static void heart_battery_charger_status(GVariant *params) @@ -2308,10 +2566,15 @@ static void heart_battery_charger_status(GVariant *params) (batt_stat.last_capacity_chg - batt_stat.curr_capacity > BATT_CHG_FLUSH_DATA)) heart_battery_flush_charge_data(); + /* collect the data for pivot update */ + heart_battery_collect_pivot_info(); + batt_stat.last_wall_time_chg = 0; first_level_change = TRUE; heart_battery_print_prev_charge_data(); heart_battery_cal_charging_rem_time(); + } else { + heart_battery_collect_discharge_data(); } } if (logic_rul) { @@ -2701,8 +2964,23 @@ static void dbus_get_battery_remaining_time(GDBusMethodInvocation *invocation, G if (!battery_learning_mode) { _E("data is not enough to calculate prediction"); ret = BATTERY_USAGE_LEARNING; - } else - ret = get_battery_remaining_time(mode, DISCHARGING); + } else { + if (batt_stat.curr_capacity <= 0) { + _I("Last capacity read may not be proper so read again"); + batt_stat.curr_capacity = heart_battery_direct_get_capacity(); + if (batt_stat.curr_capacity <= 0) { + /* + * still not proper ? return learning mode + */ + ret = BATTERY_USAGE_LEARNING; + } else { + heart_battery_cal_discharge_rem_time(); + ret = get_battery_remaining_time(mode, DISCHARGING); + } + } else { + ret = get_battery_remaining_time(mode, DISCHARGING); + } + } _I("Remaining_time %d (mode: %d)", ret, mode); g_dbus_method_invocation_return_value(invocation, g_variant_new("(i)", ret)); @@ -2844,12 +3122,22 @@ static void heart_battery_status_init(void) batt_stat.index_chg = 0; batt_stat.remaining_time_chg = 0; batt_stat.last_clock_tick = logging_get_time(CLOCK_BOOTTIME); + for (i = 0; i < INDEX_WINDOW_SIZE; i++) + batt_stat.pvt.total_dischg_time[i] = 0; + batt_stat.pvt.pvt_data_ind = 0; + batt_stat.pvt.start_cap_dischg = 0; + batt_stat.pvt.start_time_dischg = 0; + batt_stat.pvt.last_clock_tick = 0; /* During the device boot-up, calculate energy for 100% battery and calcuate pivot(norma and UPS mode) for time remaining time estimation*/ heart_battery_calculate_pivot(); /*get all the previousely stored data from file to program variable*/ heart_battery_read_data_from_file(); + /* get all the previosly stored pivot info */ + heart_battery_read_pivot_info_from_file(); + /* if enough pivot data present then update the pivot */ + heart_battery_update_pivot(); /* * check whether was device power off for long time. If yes then adjust * our stored time values @@ -2871,12 +3159,22 @@ static void heart_battery_status_init(void) _E("Failed to read battery capacity data"); capacity = heart_battery_direct_get_capacity(); + status = heart_battery_direct_get_charger_status(); + + /* + * Consider power off charing mode + * If new capacity is higher than old capacity and it is full, reset heart data + */ + if (capacity > batt_stat.curr_capacity && status == DISCHARGING && capacity >= 90) + heart_battery_reset(NULL); + else + heart_battery_used_time_init(batt_stat.curr_charger_status); + if (capacity > 0) batt_stat.curr_capacity = capacity; - status = heart_battery_direct_get_charger_status(); if (status >= 0) batt_stat.curr_charger_status = status; - heart_battery_used_time_init(batt_stat.curr_charger_status); + heart_battery_calculate_prediction(batt_stat.curr_charger_status); batt_stat.last_event_wall_time = logging_get_time(CLOCK_BOOTTIME); batt_stat.discharge_rate_level = BATTERY_DISCHARGE_NONE; @@ -2887,6 +3185,8 @@ static void heart_battery_status_init(void) if (batt_stat.curr_charger_status == CHARGING) { heart_battery_cal_charging_rem_time(); first_level_change = TRUE; + } else { + heart_battery_collect_discharge_data(); } } } @@ -2900,6 +3200,7 @@ static int low_battery_handler(void *data) static int heart_battery_config(struct parse_result *result, void *user_data) { int val; + int battery_capacity = 0; if (!result) return -EINVAL; @@ -2928,6 +3229,14 @@ static int heart_battery_config(struct parse_result *result, void *user_data) } else if (!strncmp(result->name, "LOGIC_RUL", sizeof("LOGIC_RUL") + 1)) { logic_rul = atoi(result->value); _I("logic_rul : %d", logic_rul); + } else if (!strncmp(result->name, "BATTERY_CAPACITY", sizeof("BATTERY_CAPACITY") + 1)) { + battery_capacity = atoi(result->value); + _I("total battery capacity: %d", battery_capacity); + + if(total_battery_capacity == 0 && !battery_capacity) { + /* sys interface battery capacity path not found, read from conf file */ + total_battery_capacity = battery_capacity * 60; /* mAm */ + } } else if (!strncmp(result->name, "DISCHARGE_FAST", sizeof("DISCHARGE_FAST") + 1)) { discharge_fast = atoi(result->value); _I("discharge_fast: %d", discharge_fast); @@ -2965,6 +3274,14 @@ static int heart_battery_config(struct parse_result *result, void *user_data) } } } + } else if (!strncmp(result->section, "POWER_PROFILE", sizeof("POWER_PROFILE") + 1)) { + if (!strncmp(result->name, "WIFI_ACTIVE", sizeof("WIFI_ACTIVE") + 1)) { + power_profile.wifi_active = atof(result->value); + _D("HEART WIFI_ACTIVE: %f", power_profile.wifi_active); + } else if (!strncmp(result->name, "WIFI_ON", sizeof("WIFI_ON") + 1)) { + power_profile.wifi_on = atof(result->value); + _D("HEART WIFI_ON: %f", power_profile.wifi_on); + } } return RESOURCED_ERROR_NONE; } @@ -3016,6 +3333,134 @@ static void heart_read_battery_total_capacity(void) batt_capacity, battery_header); } +/* Database related operations for heart-battery tables */ + +static int heart_battery_stats_init_db(void) +{ + sqlite3 *battery_stats_db; + char *err_msg = NULL; + char buf[HEART_BATTERY_STATS_MAX] = {0, }; + int ret; + + ret = sqlite3_open(BATTERY_STATS_DB_FILE_NAME, &battery_stats_db); + if (ret != SQLITE_OK) { + _E("Can't open database %s: %s", BATTERY_STATS_DB_FILE_NAME, + sqlite3_errmsg(battery_stats_db)); + goto error_db_open; + } + + snprintf(buf, HEART_BATTERY_STATS_MAX, "%s", QUERY_CREATE_BATTERY_STATS); + ret = sqlite3_exec(battery_stats_db, buf, NULL, NULL, &err_msg); + if (ret != SQLITE_OK) { + _E("create failed: %s", err_msg); + sqlite3_free(err_msg); + goto error_db_open; + } + _I("Create db Success"); + +error_db_open: + sqlite3_close(battery_stats_db); + return ret; +} + +static int heart_battery_stats_db_open_transaction(sqlite3 *db) +{ + char *err_msg = NULL; + + if (sqlite3_exec(db, "PRAGMA journal_mode = PERSIST", NULL, NULL, &err_msg) != SQLITE_OK) { + _E("sqlite3_exec(\"PRAGMA journal_mode = PERSIST\") failed! -> %s", err_msg); + sqlite3_free(err_msg); + return RESOURCED_ERROR_DB_FAILED; + } + + if (sqlite3_exec(db, "BEGIN EXCLUSIVE", NULL, NULL, &err_msg) != SQLITE_OK) { + _E("sqlite3_exec(\"BEGIN EXCLUSIVE\") failed! -> %s", err_msg); + sqlite3_free(err_msg); + return RESOURCED_ERROR_DB_FAILED; + } + + return RESOURCED_ERROR_NONE; +} + +int heart_battery_stats_execute_insert_db(char buf[]) +{ + sqlite3 *battery_stats_db; + char *err_msg = NULL; + int ret; + + if (sqlite3_open(BATTERY_STATS_DB_FILE_NAME, &battery_stats_db) != SQLITE_OK) { + _E("Can't open database %s: %s", BATTERY_STATS_DB_FILE_NAME, + sqlite3_errmsg(battery_stats_db)); + return RESOURCED_ERROR_DB_FAILED; + } + ret = heart_battery_stats_db_open_transaction(battery_stats_db); + if (ret != RESOURCED_ERROR_NONE) + return ret; + + if (sqlite3_exec(battery_stats_db, buf, NULL, NULL, &err_msg) != SQLITE_OK) { + _E("sqlite3_exec(\"%s\") failed! -> %s", buf, err_msg); + sqlite3_free(err_msg); + goto error_db; + } + + if (sqlite3_exec(battery_stats_db, "COMMIT", NULL, NULL, NULL) != SQLITE_OK) { + _E("sqlite3_exec(\"COMMIT\") failed!"); + goto error_db; + } + _I("QUERY: %s SUCCESS", buf); + + sqlite3_close(battery_stats_db); + return RESOURCED_ERROR_NONE; + +error_db: + if (sqlite3_exec(battery_stats_db, "ROLLBACK", NULL, NULL, NULL) != SQLITE_OK) + _E("sqlite3_exec(\"ROLLBACK\") failed!"); + _I("QUERY: %s FAILED", buf); + sqlite3_close(battery_stats_db); + return RESOURCED_ERROR_DB_FAILED; +} + +long heart_battery_stats_execute_select_total_db(char buf[]) +{ + sqlite3 *battery_stats_db; + sqlite3_stmt *stmt; + long totaltime = 0; + int ret; + + if (sqlite3_open(BATTERY_STATS_DB_FILE_NAME, &battery_stats_db) != SQLITE_OK) { + _E("Can't open database %s: %s", BATTERY_STATS_DB_FILE_NAME, + sqlite3_errmsg(battery_stats_db)); + return 0; + } + + ret = sqlite3_prepare_v2(battery_stats_db, buf, -1, &stmt, NULL); + if (ret != SQLITE_OK) { + _E("Failed to prepare query %s", sqlite3_errmsg(battery_stats_db)); + return 0; + } + + ret = sqlite3_step(stmt); + switch (ret) { + case SQLITE_ROW: + _I("QUERY: %s SUCCESS", buf); + totaltime = (long)sqlite3_column_int(stmt, 0); + _D("totaltime: (%ld)", totaltime); + break; + case SQLITE_DONE: + _D("SQLITE_DONE"); + break; + case SQLITE_ERROR: + /* FALLTHROUGH */ + _E("sqlite3_step failed %s", sqlite3_errmsg(battery_stats_db)); + default: + _E("RESOURCED_ERROR_DB_FAILED"); + break; + } + sqlite3_finalize(stmt); + sqlite3_close(battery_stats_db); + return totaltime; +} + static void rul_data_init(void) { _cleanup_fclose_ FILE *fp = NULL; @@ -3061,10 +3506,9 @@ static int heart_battery_init(void *data) { int ret; - ret = logging_module_init(BATTERY_NAME, ONE_DAY, TEN_MINUTE, heart_battery_update, - HEART_BATTERY_UPDATE_INTERVAL, SYSTEM_DEFAULT); + ret = heart_battery_stats_init_db(); if (ret != RESOURCED_ERROR_NONE) { - _E("logging module init failed"); + _E("heart module init failed at db creation"); return RESOURCED_ERROR_FAIL; } @@ -3097,6 +3541,14 @@ static int heart_battery_init(void *data) register_notifier(RESOURCED_NOTIFIER_DATA_RESET, heart_battery_reset); heart_battery_set_file_commit_timestamp(logging_get_time(CLOCK_BOOTTIME)); + + ret = logging_module_init(BATTERY_NAME, ONE_DAY, TEN_MINUTE, heart_battery_update, + HEART_BATTERY_UPDATE_INTERVAL, SYSTEM_DEFAULT); + if (ret != RESOURCED_ERROR_NONE) { + _E("logging module init failed"); + return RESOURCED_ERROR_FAIL; + } + _D("heart battery init finished"); return RESOURCED_ERROR_NONE; } -- 2.7.4 From 132eb157bd7c6552c6cd2283014666740d37c8a5 Mon Sep 17 00:00:00 2001 From: ByungSoo Kim Date: Tue, 10 Jan 2017 16:57:48 +0900 Subject: [PATCH 06/16] proc: add new interface for getting ram size There are many requirements to get total ram size in such as smart manager or gear manager. Kernel and bootloader have already known ram size information and they wrote this to /proc/cmdline. 'mem=nn[KMG] [KNL,BOOT] Force usage of a specific amount of memory' Since Resourced can parse this information, it can avoid using the hard coded value in other modules. Change-Id: Ib1aca7c23414391b453b9e55c6cd3b393499f49b --- src/common/procfs.c | 39 +++++++++++++++++++++++++++++++++++++++ src/common/procfs.h | 28 ++++++++++++++++++++++++++++ src/memory/compaction.c | 10 ---------- src/proc-stat/proc-monitor.c | 17 +++++++++++++++++ 4 files changed, 84 insertions(+), 10 deletions(-) diff --git a/src/common/procfs.c b/src/common/procfs.c index 3e91146..c7916d7 100644 --- a/src/common/procfs.c +++ b/src/common/procfs.c @@ -689,6 +689,45 @@ int proc_get_meminfo(struct meminfo *mi, enum meminfo_mask mask) return RESOURCED_ERROR_NONE; } +static int parse_spanned_pages(const char *s, regmatch_t *match, + unsigned int parse_tag, void *data) +{ + char *e; + unsigned long v; + unsigned long *parse_v = (unsigned long *)data; + + v = strtoul(s + match[1].rm_so, &e, 0); + *parse_v += v; + + return 0; +} + +int proc_get_ram_total(unsigned int *total) +{ + unsigned long total_spanned = 0; + static unsigned int total_ram = 0; + int ret; + const struct parse_arg args[] = { + PARSE_TAG("spanned[[:blank:]]+([0-9]+)\n", + parse_spanned_pages, SPANNED), + PARSE_TAG_EMPTY(), + }; + + if (total_ram > 0) { + *total = total_ram; + return RESOURCED_ERROR_NONE; + } + + ret = proc_parse_zoneinfo(args, &total_spanned); + if (ret != RESOURCED_ERROR_NONE) + return RESOURCED_ERROR_NO_DATA; + + total_ram = (unsigned int)BYTE_TO_KBYTE(total_spanned << PAGE_SHIFT); + *total = total_ram; + + return RESOURCED_ERROR_NONE; +} + static inline char *proc_skip_blanks(const char *s) { while (s && (isblank(*s) || !isalnum(*s))) diff --git a/src/common/procfs.h b/src/common/procfs.h index e53be68..5f72ac1 100644 --- a/src/common/procfs.h +++ b/src/common/procfs.h @@ -214,6 +214,27 @@ struct parse_arg { unsigned int tag; }; +#define PARSE_TAG(exp, fn, id) \ +{ \ + .re_exp = exp, \ + .callback = fn, \ + .tag = PARSE_TAG_##id, \ +} + +#define PARSE_TAG_EMPTY() {0,} + +enum { + PARSE_TAG_ZONE = 1, + PARSE_TAG_PAGE_COUNT, + PARSE_TAG_WM_MIN, + PARSE_TAG_WM_LOW, + PARSE_TAG_WM_HIGH, + PARSE_TAG_MANAGED, + PARSE_TAG_SPANNED, + PARSE_TAG_MAX, +}; + + /** * @desc get command line from /proc/{pid}/cmdline * @return negative value if error @@ -350,6 +371,13 @@ int proc_sys_node_trigger(enum sys_node_id sys_node_id); int proc_get_uptime(unsigned long *uptime); /** + * @desc get total ram size including reserved memory + * @param[out] total Ram size in kilobytes + * @return negative value if error + */ +int proc_get_ram_total(unsigned int *total); + +/** * @brief print uss and memtotal for showing brief memory information */ void proc_print_meninfo(FILE *fp); diff --git a/src/memory/compaction.c b/src/memory/compaction.c index f42f53e..9749d02 100644 --- a/src/memory/compaction.c +++ b/src/memory/compaction.c @@ -74,16 +74,6 @@ #define PROC_COMPACT_ENTRY "/proc/sys/vm/compact_memory" #define MEM_CONF_FILE RD_CONFIG_FILE(memory) -enum { - PARSE_TAG_ZONE = 1, - PARSE_TAG_PAGE_COUNT, - PARSE_TAG_WM_MIN, - PARSE_TAG_WM_LOW, - PARSE_TAG_WM_HIGH, - PARSE_TAG_MANAGED, - PARSE_TAG_MAX, -}; - #define COMPACT_CONFIG_SECTION "Compaction" #define COMPACT_CONFIG_ENABLE "CompactEnable" #define COMPACT_CONFIG_FRAG "Fraglevel" diff --git a/src/proc-stat/proc-monitor.c b/src/proc-stat/proc-monitor.c index ba0149b..b6d6dab 100644 --- a/src/proc-stat/proc-monitor.c +++ b/src/proc-stat/proc-monitor.c @@ -53,6 +53,7 @@ #define TIZEN_DEBUG_MODE_FILE RD_SYS_ETC"/.debugmode" #define DEFAULT_APPID "NULL" +#define DEFAULT_RAMSIZE 512 #define INIT_PID 1 #define POWER_OFF_DIRECT 2 @@ -504,6 +505,18 @@ failure: D_BUS_REPLY_NULL(invocation); } +static void dbus_get_get_ram_size(GDBusMethodInvocation *invocation, GVariant *params) +{ + unsigned int total = MBYTE_TO_KBYTE(DEFAULT_RAMSIZE); + int ret; + + ret = proc_get_ram_total(&total); + if (ret) + _E("can't get ram total size. use default size"); + + g_dbus_method_invocation_return_value(invocation, g_variant_new("(u)", total)); +} + static void proc_dbus_exclude_signal_handler(GVariant *params) { const char *str = NULL; @@ -1062,6 +1075,9 @@ static const char dbus_methods_xml[] = " " " " " " +" " +" " +" " " " ""; @@ -1080,6 +1096,7 @@ static struct d_bus_method dbus_methods[] = { { "ProcMemoryUsage", dbus_proc_memory_usage }, { "ProcCpuUsage", dbus_proc_cpu_usage }, { "CheckAppStatus", dbus_get_checkappstatus }, + { "GetRamSize", dbus_get_get_ram_size }, /* Add methods here */ }; -- 2.7.4 From 41270a8a939a271e56abb26dd9fa26c1e35a612e Mon Sep 17 00:00:00 2001 From: ByungSoo Kim Date: Fri, 11 Aug 2017 22:50:56 +0900 Subject: [PATCH 07/16] proc: make new interface to change priority Systemd can support changing priority of system services but in case application, no one can change priority. It adds new interface in order to increase, decrease, reset priority of application with priviledge. Change-Id: I268c4ccb4faf8669e200f175691d3bfc8770c947 Signed-off-by: ByungSoo Kim --- src/common/dbus-names-local.h | 1 + src/proc-stat/proc-monitor.c | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/src/common/dbus-names-local.h b/src/common/dbus-names-local.h index a2baba8..6df39e3 100644 --- a/src/common/dbus-names-local.h +++ b/src/common/dbus-names-local.h @@ -52,6 +52,7 @@ #define SIGNAL_PROC_WATCHDOG "ProcWatchdog" #define SIGNAL_PROC_SYSTEMSERVICE "SystemService" #define SIGNAL_PROC_EXCLUDEAPPID "ProcExcludeByAppid" +#define SIGNAL_PROC_SET_PRIORITY "ProcSetPriority" #define SIGNAL_OOM_SET_THRESHOLD "SetThreshold" #define SIGNAL_OOM_SET_LEAVE_THRESHOLD "SetLeaveThreshold" diff --git a/src/proc-stat/proc-monitor.c b/src/proc-stat/proc-monitor.c index b6d6dab..08fcf8d 100644 --- a/src/proc-stat/proc-monitor.c +++ b/src/proc-stat/proc-monitor.c @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include "proc-main.h" #include "proc-monitor.h" @@ -622,6 +624,27 @@ static void proc_dbus_exclude_appid_signal_handler(GVariant *params) return; } +static void proc_dbus_set_priority_signal_handler(GVariant *params) +{ + char *str = NULL; + pid_t pid = -1; + + do_expr_unless_g_variant_get_typechecked(return, params, "(&si)", &str, &pid); + if (!str || pid < 0) { + _D("there is no message"); + return; + } + + _D("pid (%d) requested to change priority (%s)", pid, str); + if (!strncmp(str, "high", sizeof "high")) + setpriority(PRIO_PGRP, pid, -5); + else if (!strncmp(str, "default", sizeof "default")) + setpriority(PRIO_PGRP, pid, 0); + else if (!strncmp(str, "low", sizeof "low")) + setpriority(PRIO_PGRP, pid, 1); + else + _W("Warning: invalid set priority request (%s)!", str); +} static void proc_dbus_prelaunch_signal_handler(GVariant *params) { @@ -1116,6 +1139,8 @@ static const struct d_bus_signal dbus_signals[] = { SIGNAL_PROC_SYSTEMSERVICE, proc_dbus_systemservice_handler, NULL}, {RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS, SIGNAL_PROC_EXCLUDEAPPID, proc_dbus_exclude_appid_signal_handler, NULL}, + {RESOURCED_PATH_PROCESS, RESOURCED_INTERFACE_PROCESS, + SIGNAL_PROC_SET_PRIORITY, proc_dbus_set_priority_signal_handler, NULL}, /* DEVICED DBUS */ {DEVICED_PATH_DISPLAY, DEVICED_INTERFACE_DISPLAY, -- 2.7.4 From 2314c24e8986cd6ecbb418547a83652e4300ac94 Mon Sep 17 00:00:00 2001 From: Michal Bloch Date: Wed, 27 Nov 2019 11:52:01 +0100 Subject: [PATCH 08/16] Release 6.0.2 Adds some product related functionality. Change-Id: Ib967cc2f76454576a87714f3b9335a7778a3b345 --- packaging/resourced.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packaging/resourced.spec b/packaging/resourced.spec index 010e148..ea2f1e1 100644 --- a/packaging/resourced.spec +++ b/packaging/resourced.spec @@ -14,7 +14,7 @@ Name: resourced Summary: Resource management daemon -Version: 6.0.1 +Version: 6.0.2 Release: 1 Group: System/Libraries License: Apache-2.0 -- 2.7.4 From 4bdc7c0d5e2e622f7f48276f55478ce2f70d9b54 Mon Sep 17 00:00:00 2001 From: "sanghyeok.oh" Date: Tue, 3 Dec 2019 15:26:54 +0900 Subject: [PATCH 09/16] svace fix Change-Id: Ifc22558c277143c9620b38c9b2eb1552d37b652b Signed-off-by: sanghyeok.oh --- src/memory/vmpressure-lowmem-handler.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/memory/vmpressure-lowmem-handler.c b/src/memory/vmpressure-lowmem-handler.c index 9a7cfec..c7678f0 100644 --- a/src/memory/vmpressure-lowmem-handler.c +++ b/src/memory/vmpressure-lowmem-handler.c @@ -428,10 +428,17 @@ static char *strrstr(const char *str, const char *token) static int timesort(const struct dirent **a, const struct dirent **b) { - long long time1, time2; + long long time1 = 0; + long long time2 = 0; + char *ptr; - time1 = atoll(strtok(strrstr((*a)->d_name, "_"), "_")); - time2 = atoll(strtok(strrstr((*b)->d_name, "_"), "_")); + ptr = strrstr((*a)->d_name, "_"); + if (ptr && *++ptr) + time1 = atoll(ptr); + + ptr = strrstr((*b)->d_name, "_"); + if (ptr && *++ptr) + time2 = atoll(ptr); return (time1 - time2); } @@ -1850,13 +1857,11 @@ static int create_memcgs(void) return ret; } -static unsigned int lowmem_press_eventfd_read(int fd) +static int lowmem_press_eventfd_read(int fd) { - unsigned int ret; uint64_t dummy_state; - ret = read(fd, &dummy_state, sizeof(dummy_state)); - return ret; + return read(fd, &dummy_state, sizeof(dummy_state)); } static void lowmem_press_root_cgroup_handler(void) @@ -1902,7 +1907,8 @@ static bool lowmem_press_eventfd_handler(int fd, void *data) enum lmk_type lmk_type = LMK_MEMORY; // FIXME: probably shouldn't get ignored - (void)lowmem_press_eventfd_read(fd); + if (lowmem_press_eventfd_read(fd) < 0) + _E("Failed to read lowmem press event, %m\n"); for (i = 0; i < MEMCG_MAX; i++) { if (!memcg_tree[i] || !memcg_tree[i]->info) -- 2.7.4 From a37ca4514a124f02b00b7b597f65d13de26070fb Mon Sep 17 00:00:00 2001 From: =?utf8?q?=C5=81ukasz=20Stelmach?= Date: Wed, 28 Feb 2018 15:27:17 +0100 Subject: [PATCH 10/16] vip : rearrange logging to provide more debugging information MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Change-Id: I7ece8e8d7b83ff288457f8607eaa39f24b1bfa4a Signed-off-by: Łukasz Stelmach --- src/vip-agent/vip-release-agent.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vip-agent/vip-release-agent.c b/src/vip-agent/vip-release-agent.c index 50b642b..d233562 100644 --- a/src/vip-agent/vip-release-agent.c +++ b/src/vip-agent/vip-release-agent.c @@ -70,19 +70,21 @@ int main(int argc, char *argv[]) char *rebootargv[4] = {REBOOT_PATH, "silent", NULL, NULL}; DIR *dir = 0; + _I("Starting vip-release-agent: [%d:%s]", argc, argv[1]); + dir = opendir(VIP_CGROUP); if (!dir) { - _E("doesn't support cgroup release agent"); + _E("doesn't support cgroup release agent: %m"); return 0; } closedir(dir); - _E("call release agent : [%d:%s]\n", argc, argv[1]); - /* check previous process */ if (access(CHECK_RELEASE_PROGRESS, F_OK) == 0) return 0; + _D("No other vip-release-agent is running."); + /* make tmp file */ checkfd = creat(CHECK_RELEASE_PROGRESS, 0640); if (checkfd < 0) { @@ -91,7 +93,7 @@ int main(int argc, char *argv[]) } /* unmount cgroup for preventing launching another release-agent */ - _E("systemd service stop"); + _E("unmounting /sys/fs/cgroup"); umount2("/sys/fs/cgroup", MNT_FORCE |MNT_DETACH); /* check debug level */ @@ -107,6 +109,7 @@ int main(int argc, char *argv[]) sync(); + _E("rebooting"); run_exec(rebootargv); return 0; } -- 2.7.4 From db1efcddd46a10944e3dd9aa062deb133802f6dc Mon Sep 17 00:00:00 2001 From: Maciej Slodczyk Date: Tue, 3 Dec 2019 16:27:45 +0100 Subject: [PATCH 11/16] fix file-helper return value on premature EOF Change-Id: I022f22746db4a9285f50208fe53ec7c49063cb0d Signed-off-by: Maciej Slodczyk --- src/common/file-helper.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/common/file-helper.c b/src/common/file-helper.c index 15e3eac..e82a88b 100644 --- a/src/common/file-helper.c +++ b/src/common/file-helper.c @@ -46,8 +46,9 @@ int fwrite_str(const char *path, const char *str) ret_value_errno_msg_if(!f, -errno, "Fail to open file %s", path); + errno = 0; ret = fputs(str, f); - ret_value_errno_msg_if(ret == EOF, errno ? -errno : -EIO, + ret_value_errno_msg_if(ret == EOF, -(errno ?: EIO), "Fail to write file"); return RESOURCED_ERROR_NONE; @@ -115,9 +116,10 @@ int fread_nth_int(const char *path, size_t n, int32_t *number) ret_value_errno_msg_if(!f, -errno, "Fail to open %s file.", path); + errno = 0; for (i = 0; i <= n; i++) { ret = fscanf(f, "%d", &t); - ret_value_errno_msg_if(ret == EOF, -errno, + ret_value_errno_msg_if(ret == EOF, -(errno ?: ENOENT), "Fail to read file\n"); } @@ -136,9 +138,10 @@ int fread_nth_uint(const char *path, size_t n, u_int32_t *number) ret_value_errno_msg_if(!f, -errno, "Fail to open %s file.", path); + errno = 0; for (i = 0; i <= n; i++) { ret = fscanf(f, "%u", &t); - ret_value_errno_msg_if(ret == EOF, -errno, + ret_value_errno_msg_if(ret == EOF, -(errno ?: ENOENT), "Fail to read file\n"); } @@ -157,9 +160,10 @@ int fread_nth_ulong(const char *path, size_t n, unsigned long *number) ret_value_errno_msg_if(!f, -errno, "Fail to open %s file.", path); + errno = 0; for (i = 0; i <= n; i++) { ret = fscanf(f, "%lu", &t); - ret_value_errno_msg_if(ret == EOF, -errno, + ret_value_errno_msg_if(ret == EOF, -(errno ?: ENOENT), "Fail to read file\n"); } -- 2.7.4 From a53e41e31e3b0d4d007a321fd41b3ac0d0eee271 Mon Sep 17 00:00:00 2001 From: Maciej Slodczyk Date: Tue, 12 Nov 2019 14:16:41 +0100 Subject: [PATCH 12/16] add cmocka based unit testing framework to the build process Contains a notifier test as a working example. Change-Id: I545406edad50763d9687abf4c418c2e6b0f94b0d Signed-off-by: Maciej Slodczyk --- CMakeLists.txt | 1 + packaging/resourced.spec | 1 + src/CMakeLists.txt | 24 ++++++++ src/common/macro.h | 11 ++++ tests/CMakeLists.txt | 28 +++++++++ tests/cmocka-core.c | 146 +++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 211 insertions(+) create mode 100644 tests/cmocka-core.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 94e406a..3555cdd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,5 +55,6 @@ INSTALL(FILES ${CMAKE_SOURCE_DIR}/resourced.conf DESTINATION /etc/dbus-1/system. ADD_SUBDIRECTORY(src) IF(DEFINED RD_TESTS_PATH) + ENABLE_TESTING() ADD_SUBDIRECTORY(tests) ENDIF(DEFINED RD_TESTS_PATH) diff --git a/packaging/resourced.spec b/packaging/resourced.spec index ea2f1e1..7d9342d 100644 --- a/packaging/resourced.spec +++ b/packaging/resourced.spec @@ -43,6 +43,7 @@ BuildRequires: pkgconfig(libtzplatform-config) BuildRequires: pkgconfig(storage) BuildRequires: pkgconfig(libgum) BuildRequires: pkgconfig(capi-system-device) +BuildRequires: pkgconfig(cmocka) #only for data types BuildRequires: pkgconfig(tapi) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4c54ad6..07d0828 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -153,6 +153,30 @@ ADD_EXECUTABLE(${RD_BINARY_NAME} ${RESOURCED_SOURCE_DIR}/init.c ${RESOURCED_SOURCE_DIR}/main.c ${SOURCES}) + +SET(SAVE_EXTRA_CFLAGS ${EXTRA_CFLAGS}) +SET(SAVE_CMAKE_C_FLAGS ${CMAKE_C_FLAGS}) +SET(SAVE_CMAKE_C_FLAGS_RELEASE ${CMAKE_C_FLAGS_RELEASE}) +SET(EXTRA_CFLAGS "") +SET(EXTRA_CFLAGS_RELEASE "") +SET(CMAKE_C_FLAGS "") +SET(CMAKE_CXX_FLAGS "") +SET(CMAKE_CXX_FLAGS_RELEASE "") +SET(CMAKE_C_FLAGS_RELEASE "-D_UNIT_TEST -Wp,-U_FORTIFY_SOURCE -U_FORTIFY_SOURCE -O0 -g") +ADD_LIBRARY(resourced_shared_test STATIC + ${RESOURCED_INCLUDE_HEADERS} + ${RESOURCED_SHARED_SOURCES} + ${RESOURCED_SHARED_HEADERS} + ) + +SET_TARGET_PROPERTIES(resourced_shared_test + PROPERTIES COMPILE_FLAGS ${CMAKE_C_FLAGS_RELEASE}) +TARGET_LINK_LIBRARIES(resourced_shared_test ${RESOURCED_REQUIRE_PKGS_LDFLAGS}) + +SET(EXTRA_CFLAGS ${SAVE_EXTRA_CFLAGS}) +SET(CMAKE_C_FLAGS ${SAVE_CMAKE_C_FLAGS}) +SET(CMAKE_C_FLAGS_RELEASE ${SAVE_CMAKE_C_FLAGS_RELEASE}) + TARGET_LINK_LIBRARIES(${RD_BINARY_NAME} resourced-private-api ${RESOURCED_REQUIRE_PKGS_LDFLAGS} "-pie -ldl -lm -Wl,-rpath=${RD_PLUGIN_PATH}") SET_TARGET_PROPERTIES(${RD_BINARY_NAME} PROPERTIES COMPILE_FLAGS "-fvisibility=default") diff --git a/src/common/macro.h b/src/common/macro.h index f67a94d..c8e9a51 100644 --- a/src/common/macro.h +++ b/src/common/macro.h @@ -138,6 +138,7 @@ elem && ((node = elem->data) != NULL); \ elem = elem_next, elem_next = g_slist_next(elem), node = NULL) +#ifndef _UNIT_TEST #define MODULE_REGISTER(module) \ static void __attribute__ ((constructor)) module_init(void) \ { \ @@ -147,6 +148,16 @@ { \ remove_module(module); \ } +#else +/* module is declared in all modules as static struct and the resourced + * codebase is compiled with -Wall so we need to use 'module' pionter + * somehow to prevent from breaking the build process */ +#define MODULE_REGISTER(module) \ + static void __attribute__ ((constructor)) placeholder(void) \ + { \ + ((void)(module)); \ + } +#endif /* Overlap definition to consider three strings as a string */ #define STRING_FORMAT_SPECIFIER_WITH_MACRO(macro) "%"#macro"s" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f44c951..2576482 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -18,6 +18,34 @@ TARGET_LINK_LIBRARIES(watchdog-test "${GLIB2_LDFLAGS}" "${GIO2_LDFLAGS}") +# run unit tests autimatically whenever building resourced +ADD_CUSTOM_TARGET(do-test ALL make test + WORKING_DIRECTORY ./ + USES_TERMINAL) + +# build unit test +ADD_EXECUTABLE(cmocka-core cmocka-core.c) + +PKG_CHECK_MODULES(CMOCKA REQUIRED cmocka) + +SET(EXTRA_CFLAGS "") +SET(RESOURCED_REQUIRE_PKGS_LDFLAGS "") +SET(CMAKE_C_FLAGS "") +SET(CMAKE_C_FLAGS_RELEASE "") +SET(CMAKE_LDFLAGS "") +SET(CMAKE_LDFLAGS_RELEASE "") + +SET(UNIT_TESTS_CFLAGS "-O0 -D_UNIT_TEST -D_GNU_SOURCE") + +SET_TARGET_PROPERTIES(cmocka-core PROPERTIES COMPILE_FLAGS + "-I${COMMON_SOURCE_DIR} -I/usr/include/dlog -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include ${UNIT_TESTS_CFLAGS}") +TARGET_LINK_LIBRARIES(cmocka-core resourced_shared_test cmocka dlog gio-2.0 gobject-2.0 glib-2.0 + "-Wl,--wrap=malloc,--wrap=free,--wrap=g_slist_append,--wrap=g_slist_remove,--wrap=strdup,--wrap=strndup -O0") + +# add unit test to test target +ADD_TEST(core cmocka-core) +ADD_DEPENDENCIES(do-test cmocka-core) + INSTALL(TARGETS watchdog-test DESTINATION ${RD_TESTS_PATH}) INSTALL(FILES run_tests.sh diff --git a/tests/cmocka-core.c b/tests/cmocka-core.c new file mode 100644 index 0000000..0793cae --- /dev/null +++ b/tests/cmocka-core.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "notifier.h" +#include "config-parser.h" + +typedef int (*noti_cb)(void *data); + +void *__real_malloc(size_t size); +void __real_free(void *ptr); +GSList *__real_g_slist_append(GSList *list, gpointer data); +GSList *__real_g_slist_remove(GSList *list, gconstpointer data); + +struct resourced_notifier { + enum notifier_type status; + noti_cb func; +}; + +struct resourced_notifier *notifier; + +int test_notifier_cb(void *data) +{ + check_expected_ptr(data); + return 0; +} + +void __wrap_free(void *ptr) +{ + bool check = mock_type(bool); + if (check) + check_expected_ptr(ptr); + __real_free(ptr); +} + +void *__wrap_malloc(size_t size) +{ + bool fake = mock_type(bool); + if (fake) + return NULL; + return __real_malloc(size); +} + +GSList *__wrap_g_slist_append(GSList *list, gpointer data) +{ + assert(data); + bool wrap_append = mock_type(bool); + if (!wrap_append) + return __real_g_slist_append(list, data); + + notifier = (struct resourced_notifier *)data; + + if (!notifier->func) + return NULL; + int t = mock_type(int); + void *f = mock_ptr_type(void *); + + assert(t == (int) notifier->status); + assert(f == (void*) notifier->func); + + (void) t; + (void) f; + + GSList *l = (GSList *)calloc(1, sizeof (GSList)); + assert(l); + l->data = data; + + return l; +} + +GSList *__wrap_g_slist_remove(GSList *list, gconstpointer data) +{ + bool wrap_remove = mock_type(bool); + if (!wrap_remove) + return __real_g_slist_remove(list, data); + + if (!list) + return NULL; + + struct resourced_notifier *n = (struct resourced_notifier *)data; + assert(n == notifier); + __real_free(list); + + (void) n; + return NULL; +} + +static void test_register_notifier(void **state) +{ + (void) state; /* unused */ + + void *fptr = test_notifier_cb; + assert_int_equal(register_notifier(1, NULL), -EINVAL); + + will_return(__wrap_malloc, true); + assert_int_equal(register_notifier(1, fptr), -ENOMEM); + will_return_maybe(__wrap_malloc, false); + + will_return(__wrap_g_slist_append, true); + will_return(__wrap_g_slist_append, 1); + will_return(__wrap_g_slist_append, cast_ptr_to_largest_integral_type(fptr)); + assert_int_equal(register_notifier(1, fptr), 0); + assert_int_equal(register_notifier(1, fptr), -EINVAL); +} + +static void test_notify(void **state) +{ + (void) state; /* unused */ + const char *d = "xoxo"; + expect_string(test_notifier_cb, data, d); + resourced_notify(1, (void *)d); +} + +static void test_unregister_notifier(void **state) +{ + (void) state; /* unused */ + void *fptr = test_notifier_cb; + assert_int_equal(unregister_notifier(1, NULL), -EINVAL); + + will_return(__wrap_free, true); + expect_value(__wrap_free, ptr, cast_ptr_to_largest_integral_type(notifier)); + will_return(__wrap_g_slist_remove, true); + assert_int_equal(unregister_notifier(1, fptr), 0); +} + +static int test_setup(void **state) +{ + will_return_maybe(__wrap_malloc, false); + return 0; +} + +int main(int argc, char* argv[]) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_register_notifier), + cmocka_unit_test(test_notify), + cmocka_unit_test(test_unregister_notifier), + + }; + return cmocka_run_group_tests(tests, test_setup, NULL); +} -- 2.7.4 From a0a8a25bd80090e54ce8bb94d7492710e8be8a63 Mon Sep 17 00:00:00 2001 From: Maciej Slodczyk Date: Tue, 5 Nov 2019 17:58:21 +0100 Subject: [PATCH 13/16] unit tests: config parser Change-Id: If103e1b6a8a9d8ae647ea081fc5a9681c5767881 Signed-off-by: Maciej Slodczyk --- tests/cmocka-core.c | 152 +++++++++++++++++++++++++++++++++++++++- tests/config_parser_test_1.conf | 1 + tests/config_parser_test_2.conf | 2 + tests/config_parser_test_3.conf | 2 + tests/config_parser_test_4.conf | 140 ++++++++++++++++++++++++++++++++++++ tests/config_parser_test_5.conf | 8 +++ 6 files changed, 304 insertions(+), 1 deletion(-) create mode 100644 tests/config_parser_test_1.conf create mode 100644 tests/config_parser_test_2.conf create mode 100644 tests/config_parser_test_3.conf create mode 100644 tests/config_parser_test_4.conf create mode 100644 tests/config_parser_test_5.conf diff --git a/tests/cmocka-core.c b/tests/cmocka-core.c index 0793cae..01750ed 100644 --- a/tests/cmocka-core.c +++ b/tests/cmocka-core.c @@ -1,12 +1,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include "notifier.h" #include "config-parser.h" @@ -14,6 +16,8 @@ typedef int (*noti_cb)(void *data); void *__real_malloc(size_t size); void __real_free(void *ptr); +char *__real_strdup(const char *s); +char *__real_strndup(const char *s, size_t n); GSList *__real_g_slist_append(GSList *list, gpointer data); GSList *__real_g_slist_remove(GSList *list, gconstpointer data); @@ -30,6 +34,45 @@ int test_notifier_cb(void *data) return 0; } +int config_parser_cb(struct parse_result *result, void *user_data) +{ + int t = mock_type(int); + if (t == 1) + return -1; + check_expected_ptr(user_data); + + char *s = mock_ptr_type(char *); + char *k = mock_ptr_type(char *); + char *v = mock_ptr_type(char *); + + assert(!strcmp(s, result->section)); + assert(!strcmp(k, result->name)); + assert(!strcmp(v, result->value)); + + (void) s; + (void) k; + (void) v; + return 0; +} + +char *__wrap_strdup(const char *s) +{ + bool fake = mock_type(bool); + if (fake) + return NULL; + + return __real_strdup(s); +} + +char *__wrap_strndup(const char *s, size_t n) +{ + bool fake = mock_type(bool); + if (fake) + return NULL; + + return __real_strndup(s, n); +} + void __wrap_free(void *ptr) { bool check = mock_type(bool); @@ -128,6 +171,112 @@ static void test_unregister_notifier(void **state) assert_int_equal(unregister_notifier(1, fptr), 0); } +static void test_config_parse(void **state) +{ + (void) state; /* unused */ + const char *section = "section"; + const char *key = "key"; + const char *value = "value"; + void *ptr = (void*)0xD00D1E; + + /* null pointer test */ + assert_int_equal(config_parse(NULL, config_parser_cb, NULL), -EINVAL); + + /* null pointer test */ + assert_int_equal(config_parse("/dev/null", NULL, NULL), -EINVAL); + + /* non-existing file test */ + assert_int_equal(config_parse("the_x_files", config_parser_cb, NULL), -EIO); + + /* invalid section header test */ + assert_int_equal(config_parse("../../tests/config_parser_test_1.conf", config_parser_cb, NULL), -EBADMSG); + + /* key with no value test */ + assert_int_equal(config_parse("../../tests/config_parser_test_2.conf", config_parser_cb, NULL), -EBADMSG); + + /* callback return value test */ + will_return(config_parser_cb, 1); + assert_int_equal(config_parse("../../tests/config_parser_test_3.conf", config_parser_cb, NULL), -EBADMSG); + + /* positive test */ + will_return(config_parser_cb, 0); + expect_value(config_parser_cb, user_data, cast_ptr_to_largest_integral_type(ptr)); + will_return(config_parser_cb, cast_ptr_to_largest_integral_type(section)); + will_return(config_parser_cb, cast_ptr_to_largest_integral_type(key)); + will_return(config_parser_cb, cast_ptr_to_largest_integral_type(value)); + assert_int_equal(config_parse("../../tests/config_parser_test_3.conf", config_parser_cb, ptr), 0); +} + +static void test_config_parse_new(void **state) +{ + (void) state; /* unused */ + ConfigTableItem dummy_items[] = { + { NULL, NULL, NULL, 0, NULL } + }; + + char *str = NULL; + bool b = false; + int i = 0; + float f = 100.0; + int bytes = 1; + ConfigTableItem items[] = { + { "section", "key_s", config_parse_string, 0, &str }, + { "section", "key_b", config_parse_bool, 0, &b }, + { "section", "key_i", config_parse_int, 0, &i }, + { "section", "key_f", config_parse_float, 0, &f }, + { "section", "key_bytes", config_parse_bytes, 0, &bytes }, + { NULL, NULL, NULL, 0, NULL } + }; + + will_return_maybe(__wrap_free, false); + /* non-existing file test */ + assert_int_equal(config_parse_new("the_x_files", NULL), -ENOENT); + + /* invalid section header test */ + assert_int_equal(config_parse_new("../../tests/config_parser_test_1.conf", NULL), -EBADMSG); + + /* oom test 1 */ + will_return(__wrap_strndup, true); + assert_int_equal(config_parse_new("../../tests/config_parser_test_2.conf", NULL), -ENOMEM); + + /* oom test 2 */ + will_return(__wrap_strndup, false); + will_return(__wrap_strndup, true); + assert_int_equal(config_parse_new("../../tests/config_parser_test_3.conf", NULL), -ENOMEM); + + /* oom test 2 */ + will_return(__wrap_strndup, false); + will_return(__wrap_strndup, false); + will_return(__wrap_strdup, true); + assert_int_equal(config_parse_new("../../tests/config_parser_test_3.conf", NULL), -ENOMEM); + + /* too much data in a file test */ + will_return_count(__wrap_strndup, false, 131); + will_return_count(__wrap_strdup, false, 65); + assert_int_equal(config_parse_new("../../tests/config_parser_test_4.conf", dummy_items), -EOVERFLOW); + + /* lookup keys not present in config */ + will_return_count(__wrap_strndup, false, 2); + will_return_count(__wrap_strdup, false, 1); + assert_int_equal(config_parse_new("../../tests/config_parser_test_3.conf", items), 0); + assert(str == NULL); + assert(b == false); + assert (i == 0); + assert(f == 100.0); + assert(bytes = 1); + + /* positive test */ + will_return_count(__wrap_strndup, false, 8); + will_return_count(__wrap_strdup, false, 7); + assert_int_equal(config_parse_new("../../tests/config_parser_test_5.conf", items), 0); + assert(str); + assert(!strcmp(str, "xD")); + assert(b == true); + assert(i == 123); + assert(fabsf(f - 1.23) < 1e-6); + assert(bytes == 10485760); +} + static int test_setup(void **state) { will_return_maybe(__wrap_malloc, false); @@ -140,7 +289,8 @@ int main(int argc, char* argv[]) cmocka_unit_test(test_register_notifier), cmocka_unit_test(test_notify), cmocka_unit_test(test_unregister_notifier), - + cmocka_unit_test(test_config_parse), + cmocka_unit_test(test_config_parse_new), }; return cmocka_run_group_tests(tests, test_setup, NULL); } diff --git a/tests/config_parser_test_1.conf b/tests/config_parser_test_1.conf new file mode 100644 index 0000000..70a8368 --- /dev/null +++ b/tests/config_parser_test_1.conf @@ -0,0 +1 @@ +[section diff --git a/tests/config_parser_test_2.conf b/tests/config_parser_test_2.conf new file mode 100644 index 0000000..36fad3b --- /dev/null +++ b/tests/config_parser_test_2.conf @@ -0,0 +1,2 @@ +[section] +key diff --git a/tests/config_parser_test_3.conf b/tests/config_parser_test_3.conf new file mode 100644 index 0000000..877008f --- /dev/null +++ b/tests/config_parser_test_3.conf @@ -0,0 +1,2 @@ +[section] +key=value diff --git a/tests/config_parser_test_4.conf b/tests/config_parser_test_4.conf new file mode 100644 index 0000000..d422abc --- /dev/null +++ b/tests/config_parser_test_4.conf @@ -0,0 +1,140 @@ +[section0] +key=value +[section1] +key=value +[section2] +key=value +[section3] +key=value +[section4] +key=value +[section5] +key=value +[section6] +key=value +[section7] +key=value +[section8] +key=value +[section9] +key=value +[section10] +key=value +[section11] +key=value +[section12] +key=value +[section13] +key=value +[section14] +key=value +[section15] +key=value +[section16] +key=value +[section17] +key=value +[section18] +key=value +[section19] +key=value +[section20] +key=value +[section21] +key=value +[section22] +key=value +[section23] +key=value +[section24] +key=value +[section25] +key=value +[section26] +key=value +[section27] +key=value +[section28] +key=value +[section29] +key=value +[section30] +key=value +[section31] +key=value +[section32] +key=value +[section33] +key=value +[section34] +key=value +[section35] +key=value +[section36] +key=value +[section37] +key=value +[section38] +key=value +[section39] +key=value +[section30] +key=value +[section41] +key=value +[section42] +key=value +[section43] +key=value +[section44] +key=value +[section45] +key=value +[section46] +key=value +[section47] +key=value +[section48] +key=value +[section49] +key=value +[section50] +key=value +[section51] +key=value +[section52] +key=value +[section53] +key=value +[section54] +key=value +[section55] +key=value +[section56] +key=value +[section57] +key=value +[section58] +key=value +[section59] +key=value +[section60] +key=value +[section61] +key=value +[section62] +key=value +[section63] +key=value +[section64] +key=value +[section65] +key=value +[section66] +key=value +[section67] +key=value +[section68] +key=value +[section69] +key=value diff --git a/tests/config_parser_test_5.conf b/tests/config_parser_test_5.conf new file mode 100644 index 0000000..90671bb --- /dev/null +++ b/tests/config_parser_test_5.conf @@ -0,0 +1,8 @@ +[section] +key_s=xD +key_b=y +key_i=123 +key_f=1.23 +key_l=1230000 +key_bytes=10M + -- 2.7.4 From f7955964dc21665de08d12df651ef0e8f41095f0 Mon Sep 17 00:00:00 2001 From: Maciej Slodczyk Date: Thu, 14 Nov 2019 17:20:55 +0100 Subject: [PATCH 14/16] unit tests: fd-handler Change-Id: Id52044e532cc1873c6838f06e24c5f72229e107f Signed-off-by: Maciej Slodczyk --- tests/cmocka-core.c | 166 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 162 insertions(+), 4 deletions(-) diff --git a/tests/cmocka-core.c b/tests/cmocka-core.c index 01750ed..9cc2852 100644 --- a/tests/cmocka-core.c +++ b/tests/cmocka-core.c @@ -11,6 +11,7 @@ #include #include "notifier.h" #include "config-parser.h" +#include "fd-handler.h" typedef int (*noti_cb)(void *data); @@ -283,14 +284,171 @@ static int test_setup(void **state) return 0; } +struct test_fd_read_handler_ctx { + fd_handler_h h; + int fd[2]; + int flag; + GMainLoop *loop; +}; + +bool test_fd_read_handler_cb(int fd, void *data) +{ + struct test_fd_read_handler_ctx *c = (struct test_fd_read_handler_ctx *) data; + int check = mock_type(int); + + if (check == 1) { + check_expected(fd); + check_expected_ptr(data); + } + g_main_loop_quit(c->loop); + return TRUE; + +} + +static void test_fd_read_handler_release_cb(void *data) +{ + struct test_fd_read_handler_ctx *c = (struct test_fd_read_handler_ctx *) data; + int check = mock_type(int); + + if (check == 1) + check_expected_ptr(data); + c->flag++; +} + +static int test_fd_read_handler_setup(void **state) +{ + struct test_fd_read_handler_ctx *c = calloc(1, sizeof (struct test_fd_read_handler_ctx)); + + if (c == NULL) + return -1; + + if (pipe(c->fd) < 0) + return -1; + + c->loop = g_main_loop_new(NULL, FALSE); + + will_return_maybe(__wrap_malloc, false); + *state = c; + + return 0; +} + +static int test_fd_read_handler_teardown(void **state) +{ + struct test_fd_read_handler_ctx *c = (struct test_fd_read_handler_ctx *) *state; + assert(c); + assert(c->fd >= 0); + + close(c->fd[0]); + close(c->fd[1]); + + if (c->loop) + g_main_loop_unref(c->loop); + + will_return(__wrap_free, false); + free(c); + return 0; +} + +static void test_add_fd_read_handler(void **state) +{ + struct test_fd_read_handler_ctx *c = (struct test_fd_read_handler_ctx *) *state; + assert(c != NULL); + assert(c->fd >= 0); + + assert_int_equal(add_fd_read_handler(-1, NULL, NULL, NULL, &(c->h)), -EINVAL); + assert_int_equal(add_fd_read_handler(231, NULL, NULL, NULL, &(c->h)), -EINVAL); + assert_int_equal(add_fd_read_handler(c->fd[0], test_fd_read_handler_cb, c, test_fd_read_handler_release_cb, &(c->h)), 0); +} + +static gboolean test_fd_read_handler_start(gpointer data) +{ + struct test_fd_read_handler_ctx *c = (struct test_fd_read_handler_ctx *) data; + int r = write(c->fd[1], "hi", 2); + assert(r > 0); + + return FALSE; +} + +static gboolean test_fd_read_handler_terminate(gpointer data) +{ + struct test_fd_read_handler_ctx *c = (struct test_fd_read_handler_ctx *) data; + c->flag = 1; + g_main_loop_quit(c->loop); + return FALSE; +} + + +static void test_fd_read_handler(void **state) +{ + struct test_fd_read_handler_ctx *c = (struct test_fd_read_handler_ctx *) *state; + + assert(c != NULL); + assert(c->fd >= 0); + + will_return(test_fd_read_handler_cb, 1); + expect_value(test_fd_read_handler_cb, fd, c->fd[0]); + expect_value(test_fd_read_handler_cb, data, cast_ptr_to_largest_integral_type(c)); + + /* defer write to a pipe and run mainloop */ + g_timeout_add_seconds(0, test_fd_read_handler_start, c); + g_main_loop_run(c->loop); +} + + +static void test_remove_fd_read_handler(void **state) +{ + struct test_fd_read_handler_ctx *c = (struct test_fd_read_handler_ctx *) *state; + assert(c != NULL); + assert(c->fd >= 0); + + c->flag = 0; + assert_int_equal(remove_fd_read_handler(NULL), -EINVAL); + will_return(__wrap_free, false); + will_return(test_fd_read_handler_release_cb, 1); + expect_value(test_fd_read_handler_release_cb, data, cast_ptr_to_largest_integral_type(c)); + assert_int_equal(remove_fd_read_handler(&(c->h)), 0); + assert_int_equal(c->flag, 1); + + /* defer write to a pipe and run mainloop, the callback should not be called */ + g_timeout_add_seconds(0, test_fd_read_handler_start, c); + + /* flag should be changed in test_fd_read_handler_terminate() */ + c->flag = 0; + g_timeout_add_seconds(2, test_fd_read_handler_terminate, c); + g_main_loop_run(c->loop); + assert_int_equal(c->flag, 1); +} + int main(int argc, char* argv[]) { - const struct CMUnitTest tests[] = { + int r; + const struct CMUnitTest notifier_tests[] = { cmocka_unit_test(test_register_notifier), cmocka_unit_test(test_notify), - cmocka_unit_test(test_unregister_notifier), + cmocka_unit_test(test_unregister_notifier) + }; + const struct CMUnitTest config_parser_tests[] = { cmocka_unit_test(test_config_parse), - cmocka_unit_test(test_config_parse_new), + cmocka_unit_test(test_config_parse_new) }; - return cmocka_run_group_tests(tests, test_setup, NULL); + const struct CMUnitTest fd_read_handler_tests[] = { + cmocka_unit_test(test_add_fd_read_handler), + cmocka_unit_test(test_fd_read_handler), + cmocka_unit_test(test_remove_fd_read_handler), + }; + + r = cmocka_run_group_tests(notifier_tests, test_setup, NULL); + if (r != 0) + return r; + + r = cmocka_run_group_tests(config_parser_tests, test_setup, NULL); + if (r != 0) + return r; + + r = cmocka_run_group_tests(fd_read_handler_tests, test_fd_read_handler_setup, test_fd_read_handler_teardown); + if (r != 0) + return r; + + return r; } -- 2.7.4 From ffdd80579b4c686100e1b9546a8db99408cf0292 Mon Sep 17 00:00:00 2001 From: Michal Bloch Date: Wed, 4 Dec 2019 15:27:19 +0100 Subject: [PATCH 15/16] Fix `aarch64` builds Change-Id: I0636a683b342bd8e2460737260b7e6934d977d7e Signed-off-by: Michal Bloch --- tests/CMakeLists.txt | 2 +- tests/cmocka-core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2576482..aa27d09 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -38,7 +38,7 @@ SET(CMAKE_LDFLAGS_RELEASE "") SET(UNIT_TESTS_CFLAGS "-O0 -D_UNIT_TEST -D_GNU_SOURCE") SET_TARGET_PROPERTIES(cmocka-core PROPERTIES COMPILE_FLAGS - "-I${COMMON_SOURCE_DIR} -I/usr/include/dlog -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include ${UNIT_TESTS_CFLAGS}") + "-I${COMMON_SOURCE_DIR} -I/usr/include/dlog ${GLIB2_CFLAGS} ${UNIT_TESTS_CFLAGS}") TARGET_LINK_LIBRARIES(cmocka-core resourced_shared_test cmocka dlog gio-2.0 gobject-2.0 glib-2.0 "-Wl,--wrap=malloc,--wrap=free,--wrap=g_slist_append,--wrap=g_slist_remove,--wrap=strdup,--wrap=strndup -O0") diff --git a/tests/cmocka-core.c b/tests/cmocka-core.c index 9cc2852..3d3f132 100644 --- a/tests/cmocka-core.c +++ b/tests/cmocka-core.c @@ -219,7 +219,7 @@ static void test_config_parse_new(void **state) bool b = false; int i = 0; float f = 100.0; - int bytes = 1; + size_t bytes = 1; ConfigTableItem items[] = { { "section", "key_s", config_parse_string, 0, &str }, { "section", "key_b", config_parse_bool, 0, &b }, -- 2.7.4 From a5c04cc1c000d3c5030979f0db8b1ab8bd00a06d Mon Sep 17 00:00:00 2001 From: Maciej Slodczyk Date: Tue, 10 Dec 2019 11:08:46 +0100 Subject: [PATCH 16/16] remove duplicated code Change-Id: I0a9dfa4534eb87c8e665268ff08b2060762a99f4 Signed-off-by: Maciej Slodczyk --- src/common/config-parser.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/common/config-parser.c b/src/common/config-parser.c index 0e4e8fa..627fc0a 100644 --- a/src/common/config-parser.c +++ b/src/common/config-parser.c @@ -28,20 +28,6 @@ #define MAX_SECTION 64 -static inline char *trim_str(char *s) -{ - char *t; - /* left trim */ - s += strspn(s, WHITESPACE); - - /* right trim */ - for (t = strchr(s, 0); t > s; t--) - if (!strchr(WHITESPACE, t[-1])) - break; - *t = 0; - return s; -} - int config_parse(const char *file_name, int cb(struct parse_result *result, void *user_data), void *user_data) { @@ -72,7 +58,7 @@ int config_parse(const char *file_name, int cb(struct parse_result *result, start = line; truncate_nl(start); - start = trim_str(start); + start = strstrip(start); if (*start == COMMENT) { continue; @@ -95,12 +81,12 @@ int config_parse(const char *file_name, int cb(struct parse_result *result, goto error; } *end = '\0'; - name = trim_str(start); - value = trim_str(end + 1); + name = strstrip(start); + value = strstrip(end + 1); end = strchr(value, COMMENT); if (end && *end == COMMENT) { *end = '\0'; - value = trim_str(value); + value = strstrip(value); } result.section = section; -- 2.7.4