From d9cdf0617f87029e9525479388a13b1a844b488f Mon Sep 17 00:00:00 2001 From: Lukasz Stanislawski Date: Wed, 11 Jul 2018 20:02:29 +0200 Subject: [PATCH] report-generator: implement top report generator This patch adds additional objects: proc-scanner - for scanning whole /proc/ dir process - to gather information from /proc//* Change-Id: Ic0ad58fb9a0ae59e9390b3d109440d91d25e90da --- src/CMakeLists.txt | 2 + src/appinfo-provider.c | 9 +- src/appinfo-provider.h | 8 ++ src/config-deserializer.c | 20 +++- src/config.h | 15 ++- src/json-schema-defs.h | 4 + src/proc-scanner.c | 218 +++++++++++++++++++++++++++++++++++ src/proc-scanner.h | 108 +++++++++++++++++ src/process.c | 107 +++++++++++++++++ src/process.h | 125 ++++++++++++++++++++ src/procfs.c | 79 ++++--------- src/procfs.h | 37 ++---- src/report-generator.c | 268 ++++++++++++++++++++++++++++++++++++++++++- src/report-generator.h | 57 +++++++++ src/report-json-serializer.c | 32 +++--- src/report-json-serializer.h | 4 +- src/stats.c | 42 ++++--- src/stats.h | 21 +++- src/task-factory.c | 87 ++++++++++++++ src/task.h | 8 ++ 20 files changed, 1123 insertions(+), 128 deletions(-) create mode 100644 src/proc-scanner.c create mode 100644 src/proc-scanner.h create mode 100644 src/process.c create mode 100644 src/process.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 81b5b5e..422cfed 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -29,6 +29,8 @@ SET(SRCS stats.c clock.c ipc.c + process.c + proc-scanner.c ) ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS}) diff --git a/src/appinfo-provider.c b/src/appinfo-provider.c index b950925..aaea86b 100644 --- a/src/appinfo-provider.c +++ b/src/appinfo-provider.c @@ -311,4 +311,11 @@ static void app_terminated(GVariant *parameters) } remove_app_by_pid(pid); -} \ No newline at end of file +} + +int app_info_iterator_get_count(app_info_iterator_t *iter) +{ + ON_NULL_RETURN_VAL(iter, -1); + + return iter->data->len; +} diff --git a/src/appinfo-provider.h b/src/appinfo-provider.h index 0cbd48f..ac1c0be 100644 --- a/src/appinfo-provider.h +++ b/src/appinfo-provider.h @@ -44,6 +44,14 @@ const char *app_info_iterator_get_app_id(app_info_iterator_t *iter); int app_info_iterator_get_pid(app_info_iterator_t *iter); /** + * @brief Gets total number of apps in iterator + * @param[in] iter Iterator struct. + * + * @return number of apps. + */ +int app_info_iterator_get_count(app_info_iterator_t *iter); + +/** * @brief Frees resource used by iterator. * @param[in] iter Iterator struct. */ diff --git a/src/config-deserializer.c b/src/config-deserializer.c index a7afe08..0dc8290 100644 --- a/src/config-deserializer.c +++ b/src/config-deserializer.c @@ -27,6 +27,7 @@ static JsonNode *parse_string(JsonParser *parser, const char *config_json); static void config_array_iterate_func(JsonArray *array, guint index, JsonNode *element, gpointer user_data); static config_options_e config_parse_options(const char *option); static int calculate_task_counter(int frequency); +static config_top_subject_e config_parse_top_subject(const char *option); static struct _cfg_data { @@ -99,6 +100,9 @@ static void config_array_iterate_func(JsonArray *array, guint index, JsonNode *e const gchar *target = json_object_get_string_member(entry, SCHEMA_TARGET); configs[index].data.top.options = config_parse_options(target); + const gchar *subject = json_object_get_string_member(entry, SCHEMA_SUBJECT); + configs[index].data.top.options = config_parse_top_subject(subject); + gint64 top = json_object_get_int_member(entry, SCHEMA_TOP); configs[index].data.top.top = top; } @@ -142,4 +146,18 @@ static config_options_e config_parse_options(const char *option) static int calculate_task_counter(int frequency) { return cfg_data.total_duration / frequency; -} \ No newline at end of file +} + +static config_top_subject_e config_parse_top_subject(const char *option) +{ + if (g_strcmp0(option, SCHEMA_SUBJECT_APPS) == 0) + { + return TOP_SUBJECT_APPS; + } + else if (g_strcmp0(option, SCHEMA_SUBJECT_ALL) == 0) + { + return TOP_SUBJECT_ALL; + } + + return -1; +} diff --git a/src/config.h b/src/config.h index 55514b7..35e4734 100644 --- a/src/config.h +++ b/src/config.h @@ -58,8 +58,7 @@ typedef enum config_options /** * @brief Observe memory usage. */ - OBSERVE_MEMORY -} config_options_e; + OBSERVE_MEMORY } config_options_e; /** * @brief Config app data structure. @@ -77,6 +76,11 @@ typedef struct config_data_app char app_id[APP_ID_REGEX_MAX_LEN+1]; } config_data_app_t; +typedef enum config_top_subject { + TOP_SUBJECT_APPS, + TOP_SUBJECT_ALL, +} config_top_subject_e; + /** * @brief Config top data structure. */ @@ -91,6 +95,11 @@ typedef struct config_data_top * @brief Length of top. */ long top; + + /** + * @brief top Subject + */ + config_top_subject_e subject; } config_data_top_t; /** @@ -133,4 +142,4 @@ typedef struct config } data; } config_t; -#endif \ No newline at end of file +#endif diff --git a/src/json-schema-defs.h b/src/json-schema-defs.h index f36126b..838d92f 100644 --- a/src/json-schema-defs.h +++ b/src/json-schema-defs.h @@ -30,6 +30,10 @@ #define SCHEMA_TARGET_CPU "CPU" #define SCHEMA_TARGET_MEMORY "MEMORY" +#define SCHEMA_SUBJECT "subject" +#define SCHEMA_SUBJECT_APPS "APPS" +#define SCHEMA_SUBJECT_ALL "ALL" + #define SCHEMA_FREQUENCY "frequency" #define SCHEMA_TOP "top" #define SCHEMA_ID "id" diff --git a/src/proc-scanner.c b/src/proc-scanner.c new file mode 100644 index 0000000..4e72753 --- /dev/null +++ b/src/proc-scanner.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * + * Licensed under the Flora License, Version 1.1 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +#include "proc-scanner.h" +#include "procfs.h" +#include "process.h" +#include "err-check.h" +#include "clock.h" + +#define SWAP(a, b, type) do { type tmp = (a); (a) = (b); (b) = tmp; } while(0); +#define PROCTAB_INITIAL_LEN 64 + +struct proctab { + struct process *processes; + int size; + int max; +}; + +struct proc_scanner { + struct proctab current; + struct proctab history; + int last_history_index; +}; + +void proctab_free(struct proctab *tab) +{ + if (!tab) return; + for (int i = 0; i < tab->size; i++) + process_shutdown(&tab->processes[i]); + if (tab->processes) free(tab->processes); +} + +void proctab_resize(struct proctab *tab, int new_max) +{ + tab->processes = realloc(tab->processes, new_max * sizeof(struct process)); + if (!tab->processes) abort(); + tab->max = new_max; +} + +void proctab_append(struct proctab *tab, struct process *proc) +{ + if (tab->size >= tab->max) + proctab_resize(tab, tab->max *= 2); + tab->processes[tab->size++] = *proc; +} + +void proctab_reset(struct proctab *tab) +{ + for (int i = 0; i < tab->size; i++) + process_shutdown(&tab->processes[i]); + tab->size = 0; +} + +proc_scanner_t *proc_scanner_new() +{ + proc_scanner_t *ret = calloc(1, sizeof(proc_scanner_t)); + if (!ret) { + return NULL; + } + proctab_resize(&ret->current, PROCTAB_INITIAL_LEN); + proctab_resize(&ret->history, PROCTAB_INITIAL_LEN); + return ret; +} + +void proc_scanner_free(proc_scanner_t *scanner) +{ + if (!scanner) return; + proctab_free(&scanner->current); + proctab_free(&scanner->history); + free(scanner); +} + +static int _sort_processes_by_pid(const void *a, const void *b) +{ + const struct process *proc1 = a; + const struct process *proc2 = b; + return process_get_pid(proc1) - process_get_pid(proc2); +} + +static int _search_for_pid_key(const void *a, const void *b) +{ + const int *key = a; + const struct process *proc = b; + return *key - proc->pid; +} + +static int _sort_pids(const void *a, const void *b) +{ + const int *pid1 = a; + const int *pid2 = a; + return pid1 - pid2; +} + +// Search for process entry in history +static struct process *_proc_scanner_find_process_in_history(proc_scanner_t *scanner, int pid) +{ + if (scanner->history.size == 0) + return NULL; + + // the scanner->history_last_history_index helps for quickly find + // history entry matching given pid. We assume that history is sorted ascending by pid + // and searching for pid is done in same order. + // the speedup gain is about ~15% compared to bsearch only. + while (scanner->last_history_index < scanner->history.size) + { + if (scanner->history.processes[scanner->last_history_index].pid < pid) + { + scanner->last_history_index++; + continue; + } + if (scanner->history.processes[scanner->last_history_index].pid == pid) { + return &scanner->history.processes[scanner->last_history_index++]; + } + break; + } + + return bsearch(&pid, scanner->history.processes, scanner->history.size, sizeof(struct process), _search_for_pid_key); +} + +static bool _proc_scanner_read_pid(int pid, void *user_data) +{ + proc_scanner_t *scanner = user_data; + struct process proc_new; + + struct process *proc = _proc_scanner_find_process_in_history(scanner, pid); + + if (!proc) { + process_init(pid, &proc_new); + proc = &proc_new; + } else { + process_move(&proc_new, proc); + } + + if (process_update(&proc_new) != 0) { + process_shutdown(&proc_new); + return true; + } + proctab_append(&scanner->current, &proc_new); + + return true; +} + +static int _proc_scanner_iterate_array(int *pids, int n_pids, procfs_pid_iterator_cb cb, void *user_data) +{ + qsort(pids, n_pids, sizeof(int), _sort_pids); + + for (int i = 0; i < n_pids; ++i) { + if (!cb(pids[i], user_data)) + break; + } + return 0; +} + +static int _proc_scanner_scan_internal(proc_scanner_t *scanner, int *pids, int n_pids) +{ + ON_NULL_RETURN_VAL(scanner, -1); + + // swap processes and history + SWAP(scanner->current, scanner->history, struct proctab); + + // reset current tab, so we can start adding results of current scan + proctab_reset(&scanner->current); + + // prepare history + scanner->last_history_index = 0; + qsort(scanner->history.processes, scanner->history.size, sizeof(struct process), _sort_processes_by_pid); + + if (pids) + return _proc_scanner_iterate_array(pids, n_pids, _proc_scanner_read_pid, scanner); + else + return procfs_iterate_pids(_proc_scanner_read_pid, scanner); +} + +int proc_scanner_scan(proc_scanner_t *scanner) +{ + return _proc_scanner_scan_internal(scanner, NULL, -1); +} +int proc_scanner_scan_pids(proc_scanner_t *scanner, int *pids, int n_pids) +{ + return _proc_scanner_scan_internal(scanner, pids, n_pids); +} + +int proc_scanner_foreach_process(proc_scanner_t *scanner, foreach_t cb, void *data) +{ + ON_NULL_RETURN_VAL(scanner, -1); + ON_NULL_RETURN_VAL(cb, -1); + + for (int i = 0; i < scanner->current.size; ++i) { + if (!cb(&scanner->current.processes[i], data)) + break; + } + return 0; +} + +int proc_scanner_sort_processes(proc_scanner_t *scanner, sort_t cb) +{ + ON_NULL_RETURN_VAL(scanner, -1); + ON_NULL_RETURN_VAL(cb, -1); + + qsort(scanner->current.processes, scanner->current.size, sizeof(struct process), (__compar_fn_t)cb); + + return 0; +} diff --git a/src/proc-scanner.h b/src/proc-scanner.h new file mode 100644 index 0000000..3759c3e --- /dev/null +++ b/src/proc-scanner.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * + * Licensed under the Flora License, Version 1.1 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PROC_SCANNER_H +#define __PROC_SCANNER_H + +#include + +#include "process.h" + +/** + * @brief Scanner object + */ +typedef struct proc_scanner proc_scanner_t; + +/** + * @brief Iterator callback + * + * @param[in]: proc the proccess structure + * @param[in]: data passed to @proc_scanner_foreach_process + * + * @return: true to continue iterating, false otherwise. +*/ +typedef bool (*foreach_t)(struct process *proc, void *data); + +/** + * @brief Sort callback + * + * @param[in]: proc the process + * @param[in]: proc the process + * + * @return value < 0 if @proc1 goes before @proc2, value > 0 othersize, 0 if + * equal + */ +typedef int (*sort_t)(struct process *proc1, struct process *proc2); + +/** + * @brief Creates new proc_scanner object + * + * @return scanner object poiter or NULL in case of error + * + * @note the scanner should be released with @proc_scanner_free + */ +proc_scanner_t *proc_scanner_new(); + +/** + * @brief Releases resources allocated for scanner_object + * + * @param[in] scanner + */ +void proc_scanner_free(proc_scanner_t *scanner); + +/** + * @brief Performs scan of processes scanners contained in /proc/ scannerectory + * + * @param[in] scanner + * + * @return 0 on success, other value on failure. + */ +int proc_scanner_scan(proc_scanner_t *scanner); + +/** + * @brief Performs scan of processes scanners contained in /proc/ scannerectory + * + * @param[in] scanner + * + * @return 0 on success, other value on failure. + */ +int proc_scanner_scan_pids(proc_scanner_t *scanner, int *pids, int n_pids); + +/** + * @brief Iterate over processes scanned by @proc_scanner_scan_pids or + * @proc_scanner_scan methods. + * + * @param[in] scanner + * @param[in] cb iterator callback + * @param[in] data user data passed to callback @cb + * + * @return 0 on success, other value on failure. + */ +int proc_scanner_foreach_process(proc_scanner_t *scanner, foreach_t cb, void *data); + +/** + * @brief Sort processes scanned by @proc_scanner_scan_pids or + * @proc_scanner_scan methods. The sorting will affect process iteration + * with @proc_scanner_foreach_process. + * + * @param[in] scanner + * @param[in] cb sort callback + * + * @return 0 on success, other value on failure. + */ +int proc_scanner_sort_processes(proc_scanner_t *scanner, sort_t cb); + +#endif diff --git a/src/process.c b/src/process.c new file mode 100644 index 0000000..8f352b7 --- /dev/null +++ b/src/process.c @@ -0,0 +1,107 @@ +#include +#include + +#include "process.h" +#include "err-check.h" +#include "procfs.h" + +static int _process_update_cpu_usage(struct process *proc) +{ + struct procfs_process_cpu_usage_info info; + unsigned long long ticks; + + if (procfs_read_process_cpu_usage(proc->pid, &info) != 0) { + return -1; + } + ticks = info.stime + info.utime; + proc->frame_ticks_used = ticks - proc->total_ticks_used; + proc->total_ticks_used = ticks; + return 0; +} + +static int _process_update_memory_usage(struct process *proc) +{ + struct procfs_process_memory_usage_info info; + + if (procfs_read_process_memory_usage(proc->pid, &info) != 0) { + return -1; + } + proc->memory_used = info.rss; + return 0; +} + +int process_get_memory_usage(struct process *proc, int *usage) +{ + ON_NULL_RETURN_VAL(proc, -1); + ON_NULL_RETURN_VAL(usage, -1); + + *usage = proc->memory_used; + return 0; +} + +int process_get_cpu_usage(struct process *proc, unsigned long long *usage) +{ + ON_NULL_RETURN_VAL(proc, -1); + ON_NULL_RETURN_VAL(usage, -1); + + *usage = proc->frame_ticks_used; + return 0; +} + +int process_update(struct process *proc) +{ + if (_process_update_cpu_usage(proc) != 0) { + return -1; + } + if (_process_update_memory_usage(proc) != 0) { + return -1; + } + return 0; +} + +void process_init(int pid, struct process *proc) +{ + memset(proc, 0x0, sizeof(struct process)); + proc->pid = pid; +} + +int process_get_pid(const struct process *proc) +{ + ON_NULL_RETURN_VAL(proc, -1); + return proc->pid; +} + +const char *process_get_appid(struct process *proc) +{ + ON_NULL_RETURN_VAL(proc, NULL); + + if (!proc->appid) { + } + return proc->appid; +} + +const char *process_get_exe(struct process *proc) +{ + ON_NULL_RETURN_VAL(proc, NULL); + + if (!proc->exe) { + if (procfs_read_exe(proc->pid, &proc->exe) != 0) + return NULL; + } + return proc->exe; +} + +void process_shutdown(struct process *proc) +{ + if (!proc) return; + if (proc->appid) free(proc->appid); + if (proc->exe) free(proc->exe); +} + +void process_move(struct process *dst, struct process *src) +{ + process_shutdown(dst); + memcpy(dst, src, sizeof(struct process)); + src->appid = NULL; + src->exe = NULL; +} diff --git a/src/process.h b/src/process.h new file mode 100644 index 0000000..7d13938 --- /dev/null +++ b/src/process.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * + * Licensed under the Flora License, Version 1.1 (the License); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://floralicense.org/license/ + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __PROCESS_H +#define __PROCESS_H + +#include + +#include "stats.h" + +/** + * @brief The process structure. + * + * @note Do not read fields directly, the struct + * is only available publicly to allow easy copy semantic + */ +struct process +{ + int pid; + char *appid; + char *exe; + unsigned long long total_ticks_used; + unsigned long long frame_ticks_used; + int memory_used; +}; + +/** + * @brief Gets last read process memory usage + * + * @param[in]: proc process + * @param[out]: used the memory usage in KiB + * + * @return 0 on success, other value on error. + */ +int process_get_memory_usage(struct process *proc, int *used); + +/** + * @brief Gets last read process CPU usage + * + * @param[in]: proc process + * @param[out]: usage CPU usage in clock ticks + * + * @return 0 on success, other value on error. + */ +int process_get_cpu_usage(struct process *proc, unsigned long long *usage); + +/** + * @brief Updates process memory & CPU usage data. + * + * @param[in]: proc process + * + * @return 0 on success, other value on error. + */ +int process_update(struct process *proc); + +/** + * @brief Initializes process structure + * + * @param[in]: pid + * @param[in]: proc process + * + * @return 0 on success, other value on error. + */ +void process_init(int pid, struct process *proc); + +/** + * @brief Shutdown process structure + * + * @param[in]: proc process + * + * @return 0 on success, other value on error. + * + * @note the structure should be initialized with @process_init, @process_move + */ +void process_shutdown(struct process *proc); + +/** + * @brief Get process pid + * + * @param[in]: proc process + * + * @return: pid + */ +int process_get_pid(const struct process *proc); + +/** + * @brief Get process application id. + * + * @param[in]: proc process + * + * @return: appid or NULL if process is not an Tizen Application. + */ +const char *process_get_appid(struct process *proc); + +/** + * @brief Pathname of executed command. + * + * @param[in]: proc process + * + * @return: exe file path or NULL in case of error. + */ +const char *process_get_exe(struct process *proc); + +/** + * @brief Moves process data from one struct to another. + * + * @param[in]: dst destination process + * @param[in]: src source process + */ +void process_move(struct process *dst, struct process *src); + +#endif diff --git a/src/procfs.c b/src/procfs.c index 2e64900..1296809 100644 --- a/src/procfs.c +++ b/src/procfs.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "procfs.h" #include "log.h" @@ -35,12 +36,6 @@ #define PROC_PID_EXE_PATH "/proc/%d/exe" #define PROC_PID_CMDLINE_PATH "/proc/%d/cmdline" -struct procfs_pid_iterator -{ - DIR *dir; - int current_pid; -}; - int procfs_read_system_load_average(struct procfs_load_average_info *info) { float a1, a5, a15; @@ -227,6 +222,7 @@ int procfs_read_process_cpu_usage(int pid, struct procfs_process_cpu_usage_info usage->utime = utime; usage->stime = stime; + fclose(stat_fp); return 0; } @@ -283,6 +279,9 @@ bool _procfs_dirname_parse_pid(const char *dirname, int *pid) { int parsed_pid; + if (!isdigit(*dirname)) + return false; + if (sscanf(dirname, "%d", &parsed_pid) != 1) { return false; } @@ -290,68 +289,34 @@ bool _procfs_dirname_parse_pid(const char *dirname, int *pid) return true; } -/** - * @brief returns true if pid_iterator could successfully read - * next pid from /proc directory, false otherwise - */ -bool _procfs_pid_iterator_next_internal(procfs_pid_iterator_t *iter) +int procfs_iterate_pids(procfs_pid_iterator_cb iterator, void *user_data) { + ON_NULL_RETURN_VAL(iterator, -1); + struct dirent *entry; int pid; - bool ret = false; + + DIR *dir = opendir(PROC_DIR_PATH); + if (!dir) { + ERR("opendir failed."); + return -1; + } // According to POSIX docs readdir is not-thread safe. // however in glib recent implementations readdir // is thread safe, so we can avoid using locks here. - while ((entry = readdir(iter->dir)) != NULL) { - if (_procfs_dirname_parse_pid(entry->d_name, &pid)) { - iter->current_pid = pid; - ret = true; - break; - } else { + while ((entry = readdir(dir)) != NULL) { + if (!_procfs_dirname_parse_pid(entry->d_name, &pid)) continue; - } - } - - return ret; -} -procfs_pid_iterator_t *procfs_get_pid_iterator() -{ - procfs_pid_iterator_t *ret = calloc(1, sizeof(struct procfs_pid_iterator)); - if (!ret) { - ERR("calloc failed."); - return NULL; - } - ret->dir = opendir(PROC_DIR_PATH); - if (!ret->dir) { - ERR("opendir failed."); - procfs_pid_iterator_free(ret); - return NULL; - } - if (!_procfs_pid_iterator_next_internal(ret)) { - ERR("_procfs_pid_iterator_next_internal failed"); - procfs_pid_iterator_free(ret); - return NULL; + if (iterator(pid, user_data)) + continue; + else + break;; } - return ret; -} - -bool procfs_pid_iterator_next(procfs_pid_iterator_t *iterator) -{ - return _procfs_pid_iterator_next_internal(iterator); -} -int procfs_pid_iterator_get_pid(procfs_pid_iterator_t *iterator) -{ - return iterator->current_pid; -} - -void procfs_pid_iterator_free(procfs_pid_iterator_t *iterator) -{ - if (!iterator) return; - if (iterator->dir) closedir(iterator->dir); - free(iterator); + closedir(dir); + return 0; } int procfs_read_exe(int pid, char **exe) diff --git a/src/procfs.h b/src/procfs.h index 5cda830..5de7707 100644 --- a/src/procfs.h +++ b/src/procfs.h @@ -149,44 +149,21 @@ int procfs_read_uptime(unsigned long *uptime); int procfs_read_cpu_count(int *cpu_count); /** - * @brief Iterator over pids in /proc/ directory. - */ -typedef struct procfs_pid_iterator procfs_pid_iterator_t; - -/** - * @brief Move iterator to next entry. - * - * @param[in]: itearator - * - * @return returns true if there are more entries available, false otherwise - */ -bool procfs_pid_iterator_next(procfs_pid_iterator_t *iterator); - -/** - * @brief Gets current pid from iterator - * - * @param[in]: itearator - * - * @not User should always check validity of pid, since it not guaranteed that - * returned pid is still valid after function returns. - */ -int procfs_pid_iterator_get_pid(procfs_pid_iterator_t *iterator); - -/** - * @brief Frees procfs_pid_iterator_t + * @brief Callback func for @procfs_iterate_pids * - * @param[in]: itearator + * @return true if continue interation, false otherwise */ -void procfs_pid_iterator_free(procfs_pid_iterator_t *iterator); +typedef bool (*procfs_pid_iterator_cb)(int pid, void *user_data); /** * @brief Gets pid iterator * - * @return new procfs_pid_iterator_t or NULL or error or no pids is available. + * @param[in] iterator interator function + * @param[in] user_data data passed to @iterator * - * @note the returned value should be released wiht procfs_pid_iterator_free + * @return 0 on success, other value of failure */ -procfs_pid_iterator_t *procfs_get_pid_iterator(); +int procfs_iterate_pids(procfs_pid_iterator_cb iterator, void *user_data); /** * @brief Reads cmdline file for given pid diff --git a/src/report-generator.c b/src/report-generator.c index 05f16d6..71b3798 100644 --- a/src/report-generator.c +++ b/src/report-generator.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "report-generator.h" #include "log.h" @@ -25,6 +26,7 @@ #include "appinfo-provider.h" #include "stats.h" #include "clock.h" +#include "proc-scanner.h" struct report_generator_system { /** system cpu usage statistics */ @@ -45,7 +47,24 @@ struct report_generator_app report_generator_process_t *process_gen; }; +struct report_generator_top +{ + struct stats_system sys_stats; + proc_scanner_t *scanner; + report_generator_top_type_e type; + int limit; +}; + +struct report_generator_top_closure { + struct stats_system sys_stats; + int current_index; + int max_index; + struct app_cpu_usage_report *cpu_report; + struct app_memory_usage_report *mem_report; +}; + int _app_report_generator_setup_process_generator(report_generator_app_t *generator); +static int _report_generator_top_report_generator_scan(report_generator_top_t *generator); report_generator_system_t *report_generator_new_system_report_generator() { @@ -168,7 +187,12 @@ int report_generator_generate_system_memory_usage_report( float usage; - if (stats_get_system_memory_usage(&usage) != 0) { + if (stats_update_system_stats(&generator->previous) != 0) { + ERR("stats_update_system_stats failed."); + return -1; + } + + if (stats_get_system_memory_usage(&generator->previous, &usage) != 0) { ERR("stats_get_system_memory_usage failed."); return -1; } @@ -325,3 +349,245 @@ int report_generator_generate_load_average_report(struct system_load_average_rep return 0; } +report_generator_top_t *report_generator_new_top_report_generator(report_generator_top_type_e type, int limit) +{ + report_generator_top_t *gen = calloc(1, sizeof(report_generator_top_t)); + if (!gen) { + ERR("calloc failed."); + return NULL; + } + + gen->type = type; + gen->limit = limit; + gen->scanner = proc_scanner_new(); + if (!gen->scanner) { + report_generator_free_top_generator(gen); + return NULL; + } + // run initial scan, so other next report will have valid data. + if (_report_generator_top_report_generator_scan(gen) != 0) { + report_generator_free_top_generator(gen); + return NULL; + } + if (stats_update_system_stats(&gen->sys_stats) != 0) { + report_generator_free_top_generator(gen); + return NULL; + } + + return gen; +} + +void report_generator_free_top_generator(report_generator_top_t *generator) +{ + if (!generator) return; + proc_scanner_free(generator->scanner); + free(generator); +} + +static bool _append_to_mem_report(struct process *proc, void *data) +{ + struct report_generator_top_closure *closure = data; + struct app_memory_usage_report report = {0,}; + const char *appid; + int mem; + + if (closure->current_index >= closure->max_index) + return false; + + if (process_get_memory_usage(proc, &mem) != 0) { + report.process_report.usage = NAN; + } else { + report.process_report.usage = (float)mem / closure->sys_stats.total_memory; + } + + appid = process_get_appid(proc); + if (appid) strncpy(report.app_id, appid, sizeof(report.app_id)); + report.process_report.pid = process_get_pid(proc); + report.process_report.time = clock_realtime_get().tv_sec; + closure->mem_report[closure->current_index++] = report; + + return true; +} + +static bool _append_to_cpu_report(struct process *proc, void *data) +{ + struct report_generator_top_closure *closure = data; + struct app_cpu_usage_report report = {0,}; + const char *appid; + unsigned long long ticks = 0; + + if (closure->current_index >= closure->max_index) + return false; + + if (process_get_cpu_usage(proc, &ticks) != 0) { + report.process_report.usage = NAN; + } else { + report.process_report.usage = stats_get_cpu_usage_percentage(ticks, closure->sys_stats.frame_time); + } + + appid = process_get_appid(proc); + if (appid) strncpy(report.app_id, appid, sizeof(report.app_id)); + report.process_report.pid = process_get_pid(proc); + report.process_report.time = clock_realtime_get().tv_sec; + closure->cpu_report[closure->current_index++] = report; + + return true; +} + +static int _sort_by_cpu_usage(struct process *proc1, struct process *proc2) +{ + unsigned long long usage1, usage2; + if (process_get_cpu_usage(proc1, &usage1) != 0) { + return 1; + } + if (process_get_cpu_usage(proc2, &usage2) != 0) { + return -1; + } + return usage2 - usage1; +} + +static int _sort_by_memory_usage(struct process *proc1, struct process *proc2) +{ + int usage1, usage2; + if (process_get_memory_usage(proc1, &usage1) != 0) { + return 1; + } + if (process_get_memory_usage(proc2, &usage2) != 0) { + return -1; + } + return usage2 - usage1; +} + +static int _report_generator_top_report_generator_scan_apps( + report_generator_top_t *generator) +{ + app_info_iterator_t *iter = app_info_provider_get_running_applications(); + int i = 0; + + if (!iter) { + return -1; + } + + int count = app_info_iterator_get_count(iter); + if (count < 1) { + app_info_iterator_free(iter); + return -1; + } + + int *pids = calloc(count, sizeof(int)); + if (!pids) { + app_info_iterator_free(iter); + return -1; + } + + do { + pids[i++] = app_info_iterator_get_pid(iter); + } + while (app_info_iterator_next(iter)); + + app_info_iterator_free(iter); + + return proc_scanner_scan_pids(generator->scanner, pids, count); +} + +static int _report_generator_top_report_generator_scan_all( + report_generator_top_t *generator) +{ + return proc_scanner_scan(generator->scanner); +} + +static int _report_generator_top_report_generator_scan( + report_generator_top_t *generator) +{ + switch (generator->type) { + case REPORT_GENERATOR_TOP_TYPE_APPS: + return _report_generator_top_report_generator_scan_apps(generator); + case REPORT_GENERATOR_TOP_TYPE_ALL: + return _report_generator_top_report_generator_scan_all(generator); + break; + } + + return -1; +} + +int report_generator_generate_top_cpu_report( + report_generator_top_t *generator, + struct app_cpu_usage_report **report, + int *n_reports) +{ + ON_NULL_RETURN_VAL(generator, -1); + ON_NULL_RETURN_VAL(report, -1); + ON_NULL_RETURN_VAL(n_reports, -1); + + struct report_generator_top_closure closure = {0,}; + + if (stats_update_system_stats(&generator->sys_stats) != 0) { + return -1; + } + + if (_report_generator_top_report_generator_scan(generator) != 0) { + return -1; + } + + if (proc_scanner_sort_processes(generator->scanner, _sort_by_cpu_usage) != 0) { + return -1; + } + + closure.max_index = generator->limit; + closure.sys_stats = generator->sys_stats; + closure.cpu_report = calloc(generator->limit, sizeof(struct app_cpu_usage_report)); + if (!closure.cpu_report) { + return -1; + } + + if (proc_scanner_foreach_process(generator->scanner, _append_to_cpu_report, &closure) != 0) { + free(closure.cpu_report); + return -1; + } + + *report = closure.cpu_report; + *n_reports = closure.current_index; + + return 0; +} + +int report_generator_generate_top_memory_report( + report_generator_top_t *generator, + struct app_memory_usage_report **report, + int *n_reports) +{ + ON_NULL_RETURN_VAL(generator, -1); + ON_NULL_RETURN_VAL(report, -1); + ON_NULL_RETURN_VAL(n_reports, -1); + + struct report_generator_top_closure closure = {0,}; + + if (stats_update_system_stats(&generator->sys_stats) != 0) { + return -1; + } + + if (_report_generator_top_report_generator_scan(generator) != 0) { + return -1; + } + + if (proc_scanner_sort_processes(generator->scanner, _sort_by_memory_usage) != 0) { + return -1; + } + + closure.max_index = generator->limit; + closure.sys_stats = generator->sys_stats; + closure.mem_report = calloc(generator->limit, sizeof(struct app_memory_usage_report)); + if (!closure.mem_report) { + return -1; + } + + if (proc_scanner_foreach_process(generator->scanner, _append_to_mem_report, &closure) != 0) { + free(closure.mem_report); + return -1; + } + + *report = closure.mem_report; + *n_reports = closure.current_index; + + return 0; +} diff --git a/src/report-generator.h b/src/report-generator.h index aaec19c..c79d1e8 100644 --- a/src/report-generator.h +++ b/src/report-generator.h @@ -28,6 +28,16 @@ typedef struct report_generator_process report_generator_process_t; /** Generator for app report */ typedef struct report_generator_app report_generator_app_t; +/** Top report generator yt*/ +typedef enum report_generator_top_type +{ + REPORT_GENERATOR_TOP_TYPE_APPS, + REPORT_GENERATOR_TOP_TYPE_ALL, +} report_generator_top_type_e; + +/** Generator for top report */ +typedef struct report_generator_top report_generator_top_t; + /** * @brief Creates new instance of report_generator_system_t * @return new report_generator_system_t object, or NULL on error @@ -78,6 +88,25 @@ report_generator_app_t *report_generator_new_app_report_generator(const char *ap void report_generator_free_app_generator(report_generator_app_t *generator); /** + * @brief Creates new instance of report_generator_top_h + * + * @param[in] type Type of apps to track in generator. + * @param[in] limit the maximum number of apps to return in report. + * + * @return New report_generator_top_h object, or NULL on error + * + * @remark return value should be released with + * @report_generator_free_top_generator + */ +report_generator_top_t *report_generator_new_top_report_generator(report_generator_top_type_e type, int limit); + +/** + * @brief Release report_generator_top_h created with + * @report_generator_new_top_report_generator + */ +void report_generator_free_top_generator(report_generator_top_t *generator); + +/** * @brief Fills system_cpu_usage_report. * * When interval > 0 the function will block for interval seconds and return report with @@ -198,4 +227,32 @@ int report_generator_generate_app_memory_usage_report( int report_generator_generate_load_average_report( struct system_load_average_report *report); +/** + * @brief Creates new CPU top report for apps + * + * @param[in] generator top generator + * @param[out] report + * @param[out] number of entries in report + * + * @note the report should be released with @free + */ +int report_generator_generate_top_cpu_report( + report_generator_top_t *generator, + struct app_cpu_usage_report **report, + int *n_reports); + +/** + * @brief Creates new memory top report for apps + * + * @param[in] generator top generator + * @param[out] report + * @param[out] number of entries in report + * + * @note the report should be released with @free + */ +int report_generator_generate_top_memory_report( + report_generator_top_t *generator, + struct app_memory_usage_report **report, + int *n_reports); + #endif diff --git a/src/report-json-serializer.c b/src/report-json-serializer.c index 619cbdd..a835e06 100644 --- a/src/report-json-serializer.c +++ b/src/report-json-serializer.c @@ -48,11 +48,11 @@ func_name(param_type *report) \ #define IMPLEMENT_ARRAY_SERIALIZER_FUNC(func_name, param_type, serializer) \ char* \ -func_name(param_type **reports) \ +func_name(param_type *reports, int len) \ {\ JsonBuilder *builder = json_builder_new(); \ if (!builder) return NULL; \ - serializer(builder, reports); \ + serializer(builder, reports, len); \ char *ret = _serialize(builder); \ g_object_unref(builder); \ return ret; \ @@ -127,7 +127,7 @@ static void system_cpu_usage_report_to_json_object(JsonBuilder *builder, struct json_builder_end_object(builder); } -static void top_cpu_usage_reports_to_json_object(JsonBuilder *builder, struct app_cpu_usage_report **reports) +static void top_cpu_usage_reports_to_json_object(JsonBuilder *builder, struct app_cpu_usage_report *reports, int len) { json_builder_begin_object(builder); @@ -140,36 +140,34 @@ static void top_cpu_usage_reports_to_json_object(JsonBuilder *builder, struct ap json_builder_set_member_name(builder, SCHEMA_RESULT_DATA_TOP); json_builder_begin_array(builder); - int i = 0; - while(reports[i]) + for (int i = 0; i < len; ++i) { json_builder_begin_object(builder); json_builder_set_member_name(builder, SCHEMA_ID); - json_builder_add_string_value(builder, reports[i]->app_id); + json_builder_add_string_value(builder, reports[i].app_id); json_builder_set_member_name(builder, SCHEMA_RESULT_PID); - json_builder_add_double_value(builder, reports[i]->process_report.pid); + json_builder_add_double_value(builder, reports[i].process_report.pid); json_builder_set_member_name(builder, SCHEMA_RESULT_RESULT); json_builder_begin_object(builder); json_builder_set_member_name(builder, SCHEMA_RESULT_TIME); - json_builder_add_double_value(builder, reports[i]->process_report.time); + json_builder_add_double_value(builder, reports[i].process_report.time); json_builder_set_member_name(builder, SCHEMA_RESULT_USAGE); - json_builder_add_double_value(builder, reports[i]->process_report.usage); + json_builder_add_double_value(builder, reports[i].process_report.usage); json_builder_end_object(builder); json_builder_end_object(builder); - i++; } json_builder_end_array(builder); json_builder_end_object(builder); } -static void top_memory_usage_reports_to_json_object(JsonBuilder *builder, struct app_memory_usage_report **reports) +static void top_memory_usage_reports_to_json_object(JsonBuilder *builder, struct app_memory_usage_report *reports, int len) { json_builder_begin_object(builder); @@ -182,29 +180,27 @@ static void top_memory_usage_reports_to_json_object(JsonBuilder *builder, struct json_builder_set_member_name(builder, SCHEMA_RESULT_DATA_TOP); json_builder_begin_array(builder); - int i = 0; - while(reports[i]) + for (int i = 0; i < len; ++i) { json_builder_begin_object(builder); json_builder_set_member_name(builder, SCHEMA_ID); - json_builder_add_string_value(builder, reports[i]->app_id); + json_builder_add_string_value(builder, reports[i].app_id); json_builder_set_member_name(builder, SCHEMA_RESULT_PID); - json_builder_add_double_value(builder, reports[i]->process_report.pid); + json_builder_add_double_value(builder, reports[i].process_report.pid); json_builder_set_member_name(builder, SCHEMA_RESULT_RESULT); json_builder_begin_object(builder); json_builder_set_member_name(builder, SCHEMA_RESULT_TIME); - json_builder_add_double_value(builder, reports[i]->process_report.time); + json_builder_add_double_value(builder, reports[i].process_report.time); json_builder_set_member_name(builder, SCHEMA_RESULT_USAGE); - json_builder_add_double_value(builder, reports[i]->process_report.usage); + json_builder_add_double_value(builder, reports[i].process_report.usage); json_builder_end_object(builder); json_builder_end_object(builder); - i++; } json_builder_end_array(builder); diff --git a/src/report-json-serializer.h b/src/report-json-serializer.h index f335b37..90b80fd 100644 --- a/src/report-json-serializer.h +++ b/src/report-json-serializer.h @@ -107,7 +107,7 @@ char *report_json_serializer_serialize_apps_memory_usage_report(struct app_memor * @return dynamically allocated string on NULL on error. * @remark returned value should be released with @free */ -char *report_json_serializer_serialize_top_cpu_usage_reports(struct app_cpu_usage_report **reports); +char *report_json_serializer_serialize_top_cpu_usage_reports(struct app_cpu_usage_report *reports, int len); /** * @brief Serializes app_memory_usage_reports to json string @@ -117,6 +117,6 @@ char *report_json_serializer_serialize_top_cpu_usage_reports(struct app_cpu_usag * @return dynamically allocated string on NULL on error. * @remark returned value should be released with @free */ -char *report_json_serializer_serialize_top_memory_usage_reports(struct app_memory_usage_report **reports); +char *report_json_serializer_serialize_top_memory_usage_reports(struct app_memory_usage_report *reports, int len); #endif diff --git a/src/stats.c b/src/stats.c index 275e860..c5b006e 100644 --- a/src/stats.c +++ b/src/stats.c @@ -14,11 +14,15 @@ * limitations under the License. */ +#include + #include "stats.h" #include "procfs.h" #include "err-check.h" +#include "clock.h" static int ncpus; +static float timescale; int stats_get_system_cpu_usage_average(struct stats_system *previous, struct stats_system *current, float *usage) { @@ -41,30 +45,35 @@ int stats_update_system_stats(struct stats_system *sys) { ON_NULL_RETURN_VAL(sys, -1); - struct procfs_system_cpu_usage_info info; + struct procfs_system_cpu_usage_info cpu_info; + struct procfs_system_memory_usage_info mem_info; + + if (procfs_read_system_cpu_usage(&cpu_info) != 0) { + return -1; + } - if (procfs_read_system_cpu_usage(&info) != 0) { + if (procfs_read_system_memory_usage(&mem_info) != 0) { + ERR("procfs_read_system_memory_usage failed."); return -1; } - sys->busy_ticks = info.user + info.system + info.nice + info.irq + info.softirq; - sys->total_ticks = info.user + info.system + info.nice + info.irq + info.softirq + info.idle + info.iowait; + sys->busy_ticks = cpu_info.user + cpu_info.system + cpu_info.nice + cpu_info.irq + cpu_info.softirq; + sys->total_ticks = cpu_info.user + cpu_info.system + cpu_info.nice + cpu_info.irq + cpu_info.softirq + cpu_info.idle + cpu_info.iowait; + sys->memory_used = mem_info.used; + sys->total_memory = mem_info.total; + + struct timespec now = clock_monotonic_get(); + sys->frame_time = 1.0f / (now.tv_sec - sys->update_time.tv_sec + ((float)now.tv_nsec - sys->update_time.tv_nsec) / 1000000000.0f); + sys->update_time = now; return 0; } -int stats_get_system_memory_usage(float *usage) +int stats_get_system_memory_usage(struct stats_system *sys, float *usage) { ON_NULL_RETURN_VAL(usage, -1); - struct procfs_system_memory_usage_info info; - - if (procfs_read_system_memory_usage(&info) != 0) { - ERR("procfs_read_system_memory_usage failed."); - return -1; - } - - *usage = (float)info.used / info.total; + *usage = (float)sys->memory_used / sys->total_memory; return 0; } @@ -90,7 +99,6 @@ int stats_update_process_stats(int pid, struct stats_process *stats) stats->process_ticks = proc_info.stime + proc_info.utime; stats->system_ticks = sys_info.user + sys_info.system + sys_info.nice + sys_info.idle; - return 0; } @@ -148,11 +156,17 @@ int stats_get_load_averages(float *a1, float *a5, float *a15) return 0; } +float stats_get_cpu_usage_percentage(unsigned long long ticks_delta, float ticks_time_inverted) +{ + return (float)ticks_delta * ticks_time_inverted * timescale; +} + int stats_init() { if (procfs_read_cpu_count(&ncpus) != 0) { ERR("procfs_read_cpu_count failed."); return -1; } + timescale = 1.0f / (float)sysconf(_SC_CLK_TCK); return 0; } diff --git a/src/stats.h b/src/stats.h index 0382cf8..8053d7e 100644 --- a/src/stats.h +++ b/src/stats.h @@ -17,6 +17,8 @@ #ifndef _STATS_H_ #define _STATS_H_ +#include + /** * @brief System's statistics snapshot */ @@ -24,6 +26,10 @@ struct stats_system { unsigned long long busy_ticks; unsigned long long total_ticks; + unsigned long long memory_used; + unsigned long long total_memory; + struct timespec update_time; + float frame_time; }; /** @@ -72,7 +78,7 @@ int stats_update_system_stats(struct stats_system *stats); * * @return: 0 on success, other value on error */ -int stats_get_system_memory_usage(float *usage); +int stats_get_system_memory_usage(struct stats_system *stats, float *usage); /** * @brief Takes process statistics snapshot. @@ -120,6 +126,19 @@ int stats_get_process_memory_usage(int pid, float *usage); */ int stats_get_load_averages(float *a1, float *a5, float *a15); +/** + * @brief Gets CPU usage percentage + * + * @param[in] ticks_delta the amount of clock ticks + * @param[in] ticks_time_inverted the time of ticks_delete inverted ^-1 + * + * @return: CPU usage perecentage + * + * @note in case when process has 2 threads which runs tight loop, the function + * will report 200% usage (2.0f). + */ +float stats_get_cpu_usage_percentage(unsigned long long ticks_delta, float ticks_time_inverted); + #endif diff --git a/src/task-factory.c b/src/task-factory.c index 9b5375d..c4ea70d 100644 --- a/src/task-factory.c +++ b/src/task-factory.c @@ -28,15 +28,19 @@ static task_t *create_system_report_task(config_options_e options); static task_t *create_load_avg_report_task(); static task_t *create_app_report_task(config_options_e options, const char *regex); +static task_t *create_top_report_task(struct config_data_top options); static void execute_scan_system_memory(task_t *task); static void execute_scan_system_cpu(task_t *task); static void execute_scan_load_avg(task_t *task); static void execute_scan_app_memory(task_t *task); static void execute_scan_apps_cpu(task_t *task); +static void execute_scan_top_cpu(task_t *task); +static void execute_scan_top_memory(task_t *task); static void release_system_task(task_t *task); static void release_app_task(task_t *task); +static void release_top_task(task_t *task); task_t *task_factory_create_task(const config_t *config) { @@ -49,6 +53,7 @@ task_t *task_factory_create_task(const config_t *config) case APPS: return create_app_report_task(config->data.apps.options, config->data.apps.app_id); case TOP: + return create_top_report_task(config->data.top); default: return NULL; } @@ -207,3 +212,85 @@ static void release_app_task(task_t *task) report_generator_free_app_generator(_app_task->report_generator); g_free(_app_task); } + +static task_t *create_top_report_task(struct config_data_top options) +{ + top_task_t *_top_task; + report_generator_top_type_e type; + task_execute_cb execute; + + switch (options.subject) { + case TOP_SUBJECT_APPS: + type = REPORT_GENERATOR_TOP_TYPE_APPS; + break; + case TOP_SUBJECT_ALL: + type = REPORT_GENERATOR_TOP_TYPE_ALL; + break; + default: + return NULL; + } + + switch (options.options) { + case OBSERVE_CPU: + execute = execute_scan_top_cpu; + break; + case OBSERVE_MEMORY: + execute = execute_scan_top_memory; + break; + default: + return NULL; + } + + _top_task = (top_task_t *)g_malloc(sizeof(top_task_t)); + _top_task->task.execute = execute; + _top_task->task.release = release_top_task; + _top_task->report_generator = report_generator_new_top_report_generator(type, options.top); + if (!_top_task->report_generator) { + g_free(_top_task); + return NULL; + } + + return &_top_task->task; +} + +static void release_top_task(task_t *task) +{ + ON_NULL_RETURN(task); + + top_task_t *_top_task = container_of(task, top_task_t, task); + + report_generator_free_top_generator(_top_task->report_generator); + g_free(_top_task); +} + +static void execute_scan_top_cpu(task_t *task) +{ + ON_NULL_RETURN(task); + top_task_t *_top_task = container_of(task, top_task_t, task); + + struct app_cpu_usage_report *reports; + int n_reports; + + report_generator_generate_top_cpu_report(_top_task->report_generator, &reports, &n_reports); + + char *json_report = report_json_serializer_serialize_top_cpu_usage_reports(reports, n_reports); + ipc_send_report(json_report); + g_free(json_report); + free(reports); +} + +static void execute_scan_top_memory(task_t *task) +{ + ON_NULL_RETURN(task); + top_task_t *_top_task = container_of(task, top_task_t, task); + + struct app_memory_usage_report *reports; + int n_reports; + + report_generator_generate_top_memory_report(_top_task->report_generator, &reports, &n_reports); + + char *json_report = report_json_serializer_serialize_top_memory_usage_reports(reports, n_reports); + ipc_send_report(json_report); + g_free(json_report); + free(reports); +} diff --git a/src/task.h b/src/task.h index 81f03a7..22f8823 100644 --- a/src/task.h +++ b/src/task.h @@ -71,6 +71,14 @@ typedef struct process_task report_generator_process_t *report_generator; } process_task_t; +/** + * @brief Top task structure. + */ +typedef struct top_task +{ + task_t task; + report_generator_top_t *report_generator; +} top_task_t; /** * @brief Releases the task. -- 2.7.4