report-generator: implement top report generator 48/183848/9
authorLukasz Stanislawski <l.stanislaws@samsung.com>
Wed, 11 Jul 2018 18:02:29 +0000 (20:02 +0200)
committerLukasz Stanislawski <l.stanislaws@samsung.com>
Mon, 16 Jul 2018 14:32:28 +0000 (16:32 +0200)
This patch adds additional objects:
proc-scanner - for scanning whole /proc/ dir
process - to gather information from /proc/<pid>/*

Change-Id: Ic0ad58fb9a0ae59e9390b3d109440d91d25e90da

20 files changed:
src/CMakeLists.txt
src/appinfo-provider.c
src/appinfo-provider.h
src/config-deserializer.c
src/config.h
src/json-schema-defs.h
src/proc-scanner.c [new file with mode: 0644]
src/proc-scanner.h [new file with mode: 0644]
src/process.c [new file with mode: 0644]
src/process.h [new file with mode: 0644]
src/procfs.c
src/procfs.h
src/report-generator.c
src/report-generator.h
src/report-json-serializer.c
src/report-json-serializer.h
src/stats.c
src/stats.h
src/task-factory.c
src/task.h

index 81b5b5e..422cfed 100644 (file)
@@ -29,6 +29,8 @@ SET(SRCS
        stats.c
        clock.c
        ipc.c
+       process.c
+       proc-scanner.c
 )
 ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS})
 
index b950925..aaea86b 100644 (file)
@@ -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;
+}
index 0cbd48f..ac1c0be 100644 (file)
@@ -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.
  */
index a7afe08..0dc8290 100644 (file)
@@ -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;
+}
index 55514b7..35e4734 100644 (file)
@@ -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
index f36126b..838d92f 100644 (file)
 #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 (file)
index 0000000..4e72753
--- /dev/null
@@ -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 <stdlib.h>
+
+#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 (file)
index 0000000..3759c3e
--- /dev/null
@@ -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 <stdbool.h>
+
+#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 (file)
index 0000000..8f352b7
--- /dev/null
@@ -0,0 +1,107 @@
+#include <string.h>
+#include <stdlib.h>
+
+#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 (file)
index 0000000..7d13938
--- /dev/null
@@ -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 <stdbool.h>
+
+#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
index 2e64900..1296809 100644 (file)
@@ -19,6 +19,7 @@
 #include <dirent.h>
 #include <stdlib.h>
 #include <unistd.h>
+#include <ctype.h>
 
 #include "procfs.h"
 #include "log.h"
 #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)
index 5cda830..5de7707 100644 (file)
@@ -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
index 05f16d6..71b3798 100644 (file)
@@ -18,6 +18,7 @@
 #include <string.h>
 #include <stdbool.h>
 #include <unistd.h>
+#include <math.h>
 
 #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;
+}
index aaec19c..c79d1e8 100644 (file)
@@ -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
index 619cbdd..a835e06 100644 (file)
@@ -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);
index f335b37..90b80fd 100644 (file)
@@ -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
index 275e860..c5b006e 100644 (file)
  * limitations under the License.
  */
 
+#include <unistd.h>
+
 #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;
 }
index 0382cf8..8053d7e 100644 (file)
@@ -17,6 +17,8 @@
 #ifndef _STATS_H_
 #define _STATS_H_
 
+#include <time.h>
+
 /**
  * @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
 
 
index 9b5375d..c4ea70d 100644 (file)
 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);
+}
index 81f03a7..22f8823 100644 (file)
@@ -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.