report-generator: reafactor process generator
[apps/native/ttsd-worker-task.git] / src / report-generator.c
index 40e1858..9869bcb 100644 (file)
  */
 
 #include <stdlib.h>
-#include <assert.h>
 #include <string.h>
 #include <stdbool.h>
 #include <unistd.h>
+#include <math.h>
 
 #include "report-generator.h"
-#include "procfs.h"
 #include "log.h"
 #include "err-check.h"
-
-struct report_generator_system
-{
-       /** previous read from procfs */
-       struct procfs_system_cpu_usage_info previous;
-};
-
-struct process_ticks_snapshot
-{
-       unsigned long long system_ticks;
-       unsigned long long process_ticks;
+#include "appinfo-provider.h"
+#include "sys-stats.h"
+#include "clock.h"
+#include "proc-scanner.h"
+#include "process.h"
+#include "json-schema-defs.h"
+
+struct report_generator_system {
+       /** system cpu usage statistics */
+       struct sys_stats stats;
 };
 
 struct report_generator_process
 {
-       /** process pid */
-       int pid;
-       /** previous process stats */
-       struct process_ticks_snapshot previous;
+       proc_scanner_t *scanner;
+       report_generator_filter_cb filter;
+       void *user_data;
+       JsonBuilder *current_builder;
 };
 
-static int _report_generator_read_process_ticks(int pid, struct process_ticks_snapshot *ticks);
-
-static struct timespec clock_get_monotonic()
+struct report_generator_top
 {
-       struct timespec ret = {0,};
+       struct sys_stats sys_stats;
+       proc_scanner_t *scanner;
+       report_generator_top_type_e type;
+       int limit;
+};
 
-       if (clock_gettime(CLOCK_MONOTONIC, &ret) != 0) {
-               ERR("Platform do not support monotonic clock type");
-               //TODO consider adding first init function to evaluate
-               //if clock_gettime can be used, so calling module could
-               //handle cases without monotonic clock gracefully.
-               abort();
-       }
+struct report_generator_top_closure {
+       struct sys_stats sys_stats;
+       int current_index; int max_index;
+       struct process_usage_report *usage_report;
+};
 
-       return ret;
-}
+static int _report_generator_top_report_generator_scan(report_generator_top_t *generator);
 
 report_generator_system_t *report_generator_new_system_report_generator()
 {
@@ -68,8 +65,8 @@ report_generator_system_t *report_generator_new_system_report_generator()
        if (!ret)
                return NULL;
 
-       if (procfs_read_system_cpu_usage(&ret->previous) != 0) {
-               ERR("procfs_read_system_cpu_usage failed");
+       if (sys_stats_update(&ret->stats) != 0) {
+               ERR("stats_update_system_stats failed");
                free(ret);
                return NULL;
        }
@@ -85,276 +82,376 @@ void report_generator_free_system_generator(report_generator_system_t *generator
        free(generator);
 }
 
-report_generator_process_t *report_generator_new_process_report_generator(int pid)
+report_generator_process_t *report_generator_new_process_report_generator()
 {
-       ON_TRUE_RETURN_VAL(pid <= 0, NULL);
-
        report_generator_process_t *ret = calloc(1, sizeof(struct report_generator_process));
        if (!ret)
                return NULL;
 
-       if (_report_generator_read_process_ticks(pid, &ret->previous) != 0) {
-               ERR("_report_generator_read_process_ticks failed.");
-               free(ret);
-               return NULL;
-       };
+       ret->scanner = proc_scanner_new();
+       if (!ret->scanner) goto err;
 
-       ret->pid = pid;
+       if (proc_scanner_scan(ret->scanner) != 0) {
+               ERR("proc_scanner_scan failed");
+               goto err;
+       }
 
        return ret;
+
+err:
+       proc_scanner_free(ret->scanner);
+       free(ret);
+       return NULL;
 }
 
 void report_generator_free_process_generator(report_generator_process_t *generator)
 {
-       if (!generator)
-               return;
-
+       if (!generator) return;
+       proc_scanner_free(generator->scanner);
        free(generator);
 }
 
-report_generator_apps_t *report_generator_new_apps_report_generator(const char *app_id)
+static bool _match_proc_and_append(struct process *proc, void *data)
 {
-       //TODO implement
-       return NULL;
-}
+       report_generator_process_t *gen = data;
 
-void report_generator_free_apps_generator(report_generator_apps_t *generator)
-{
-       //TODO implement
+       if (gen->filter && gen->filter(proc, gen->user_data)) {
+               process_serialize(proc, gen->current_builder);
+       }
+
+       return true;
 }
 
-static void _calculate_system_cpu_usage(
-               struct procfs_system_cpu_usage_info *previous,
-               struct procfs_system_cpu_usage_info *current,
-               float *usage)
+int report_generator_generate_process_report(
+               report_generator_process_t *generator,
+               JsonBuilder *builder)
 {
-       struct procfs_system_cpu_usage_info diff_ticks = { 0,};
+       ON_NULL_RETURN_VAL(generator, -1);
+       ON_NULL_RETURN_VAL(builder, -1);
+
+       // refresh data
+       if (proc_scanner_scan(generator->scanner) != 0) {
+               ERR("proc_scanner_scan failed");
+               return -1;
+       }
 
-       diff_ticks.user = current->user > previous->user ? current->user - previous->user : 0;
-       diff_ticks.system = current->system > previous->system ? current->system - previous->system : 0;
-       diff_ticks.nice = current->nice > previous->nice ? current->nice - previous->nice : 0;
-       diff_ticks.idle = current->idle > previous->idle ? current->idle - previous->idle : 0;
-       diff_ticks.iowait = current->iowait > previous->iowait ? current->iowait - previous->iowait : 0;
-       diff_ticks.irq = current->irq > previous->irq ? current->irq - previous->irq : 0;
-       diff_ticks.softirq = current->softirq > previous->softirq ? current->softirq - previous->softirq : 0;
+       generator->current_builder = builder;
+       json_builder_begin_object(builder);
 
-       unsigned long long total = diff_ticks.user + diff_ticks.system + diff_ticks.nice +
-               diff_ticks.idle + diff_ticks.iowait + diff_ticks.irq + diff_ticks.softirq;
+       json_builder_set_member_name(builder, SCHEMA_TYPE);
+       json_builder_add_string_value(builder, SCHEMA_TYPE_PROCESS);
 
-       unsigned long long busy = diff_ticks.user + diff_ticks.system + diff_ticks.nice +
-               diff_ticks.irq + diff_ticks.softirq;
+       json_builder_set_member_name(builder, "time");
+       json_builder_add_int_value(builder, clock_realtime_get());
 
-       if (total == 0) {
-               *usage = 0;
-       } else {
-               *usage = (float)busy / total;
-       }
+       json_builder_set_member_name(builder, SCHEMA_RESULT_DATA_PROCESS);
+       json_builder_begin_array(builder);
+
+       proc_scanner_foreach_process(generator->scanner, _match_proc_and_append, generator);
+       json_builder_end_array(builder);
+
+       json_builder_end_object(builder); // end report
+
+       generator->current_builder = NULL;
+       return 0;
+}
+
+void report_generator_set_filter(
+               report_generator_process_t *generator,
+               report_generator_filter_cb cb, const void *user_data)
+{
+       ON_NULL_RETURN(generator);
+       ON_NULL_RETURN(cb);
+
+       generator->filter = cb;
+       generator->user_data = (void*)user_data;
 }
 
-int report_generator_generate_system_cpu_usage_report(
+int report_generator_generate_system_report(
                report_generator_system_t *generator,
-               int interval,
-               struct system_cpu_usage_report *report)
+               struct system_report *report)
 {
        ON_NULL_RETURN_VAL(generator, -1);
-       ON_TRUE_RETURN_VAL(interval < 0, -1);
        ON_NULL_RETURN_VAL(report, -1);
 
-       struct procfs_system_cpu_usage_info current = {0,};
-       float usage;
-       bool block = interval > 0;
-
-       if (block) {
-               if (procfs_read_system_cpu_usage(&generator->previous) != 0) {
-                       ERR("procfs_read_system_cpu_usage failed.");
-                       return -1;
-               }
-               sleep(interval);
+       if (sys_stats_update(&generator->stats) != 0) {
+               ERR("stats_update_system_stats failed.");
+               return -1;
        }
 
-       if (procfs_read_system_cpu_usage(&current) != 0) {
-               ERR("procfs_read_system_cpu_usage failed.");
+       if (sys_stats_get_cpu_usage_percentage(&generator->stats, &report->cpu))
+       {
+               ERR("stats_get_system_cpu_usage_average failed");
                return -1;
        }
 
-       _calculate_system_cpu_usage(&generator->previous, &current, &usage);
-
-       report->usage = usage;
-       report->time = clock_get_monotonic().tv_sec;
+       if (sys_stats_get_memory_usage_percentage(&generator->stats, &report->memory) != 0) {
+               ERR("stats_get_system_memory_usage failed.");
+               return -1;
+       }
 
-       generator->previous = current;
+       report->time = clock_realtime_get();
 
        return 0;
 }
 
-int report_generator_generate_system_memory_usage_report(
-               report_generator_system_t *generator,
-               struct system_memory_usage_report *report) {
-       ON_NULL_RETURN_VAL(generator, -1);
+int report_generator_generate_load_average_report(struct system_load_average_report *report)
+{
        ON_NULL_RETURN_VAL(report, -1);
 
-       struct procfs_system_memory_usage_info mem_usage;
+       float a1, a5, a15;
 
-       if (procfs_read_system_memory_usage(&mem_usage) != 0) {
-               ERR("procfs_read_system_memory_usage failed.");
+       if (sys_stats_get_load_averages(&a1, &a5, &a15) != 0) {
+               ERR("stats_get_load_averages failed.");
                return -1;
        }
 
-       report->time = clock_get_monotonic().tv_sec;
-       report->usage = (float)mem_usage.used / mem_usage.total;
+       report->time = clock_realtime_get();
+       report->one_min_avg = a1;
+       report->five_min_avg = a5;
+       report->fifteen_min_avg = a15;
 
        return 0;
 }
 
-/**
- * Calculates system average cpu usage between previous and current
- * snapshots.
- */
-static void _calculate_process_cpu_usage(struct process_ticks_snapshot *previous,
-               struct process_ticks_snapshot *current, int ncpus, float *usage)
+report_generator_top_t *report_generator_new_top_report_generator(report_generator_top_type_e type, int limit)
 {
-       struct process_ticks_snapshot diff;
+       report_generator_top_t *gen = calloc(1, sizeof(report_generator_top_t));
+       if (!gen) {
+               ERR("calloc failed.");
+               return NULL;
+       }
 
-       diff.process_ticks = current->process_ticks > previous->process_ticks ? current->process_ticks - previous->process_ticks: 0;
-       diff.system_ticks = current->system_ticks > previous->system_ticks ? current->system_ticks - previous->system_ticks : 0;
+       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 (sys_stats_update(&gen->sys_stats) != 0) {
+               report_generator_free_top_generator(gen);
+               return NULL;
+       }
 
-       if (diff.system_ticks == 0)
-               *usage = 0;
-       else
-               *usage = (float)diff.process_ticks / diff.system_ticks * ncpus;
+       return gen;
 }
 
-static unsigned long long
-_system_total_time(const struct procfs_system_cpu_usage_info *info)
+void report_generator_free_top_generator(report_generator_top_t *generator)
 {
-       return info->user + info->system + info->nice + info->idle;
+       if (!generator) return;
+       proc_scanner_free(generator->scanner);
+       free(generator);
 }
 
-static int
-_report_generator_read_process_ticks(int pid, struct process_ticks_snapshot *ticks)
+static bool _append_to_mem_report(struct process *proc, void *data)
 {
-       struct procfs_system_cpu_usage_info system_ticks;
-       struct procfs_process_cpu_usage_info process_ticks;
+       struct report_generator_top_closure *closure = data;
+       struct process_usage_report report = {0,};
+       const char *appid;
+       int mem;
 
-       if (procfs_read_process_cpu_usage(pid, &process_ticks) != 0) {
-               ERR("procfs_read_process_cpu_usage failed.");
-               return -1;
+       if (closure->current_index >= closure->max_index)
+               return false;
+
+       if (process_get_memory_usage(proc, &mem) != 0) {
+               report.usage = NAN;
+       } else {
+               report.usage = (float)mem / closure->sys_stats.total_memory;
        }
 
-       if (procfs_read_system_cpu_usage(&system_ticks) != 0) {
-               ERR("procfs_read_system_cpu_usage failed.");
-               return -1;
+       appid = process_get_appid(proc);
+       if (appid) strncpy(report.app_id, appid, sizeof(report.app_id));
+       report.pid = process_get_pid(proc);
+       report.time = clock_realtime_get();
+       closure->usage_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 process_usage_report report = {0,};
+       const char *appid;
+
+       if (closure->current_index >= closure->max_index)
+               return false;
+
+       if (process_get_cpu_usage_percentage(proc, &report.usage) != 0) {
+               report.usage = NAN;
        }
 
-       ticks->process_ticks = process_ticks.stime + process_ticks.utime;
-       ticks->system_ticks = _system_total_time(&system_ticks);
+       appid = process_get_appid(proc);
+       if (appid) strncpy(report.app_id, appid, sizeof(report.app_id));
+       report.pid = process_get_pid(proc);
+       report.time = clock_realtime_get();
+       closure->usage_report[closure->current_index++] = report;
 
-       return 0;
+       return true;
 }
 
-int report_generator_generate_process_cpu_usage_report(
-               report_generator_process_t *generator,
-               int interval,
-               struct process_cpu_usage_report *report)
+static int _sort_by_cpu_usage(struct process *proc1, struct process *proc2)
 {
-       ON_NULL_RETURN_VAL(generator, -1);
-       ON_TRUE_RETURN_VAL(interval < 0, -1);
-       ON_NULL_RETURN_VAL(report, -1);
+       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;
+}
 
-       struct process_ticks_snapshot current;
-       bool block = interval > 0;
-       float usage;
-       int ncpus;
+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 (block) {
-               if (_report_generator_read_process_ticks(generator->pid, &generator->previous) != 0) {
-                       ERR("Unable to update process cpu ticks.");
-                       return -1;
-               }
-               sleep(interval);
+       if (!iter) {
+               return -1;
        }
 
-       if (_report_generator_read_process_ticks(generator->pid, &current) != 0) {
-               ERR("Unable to update process cpu ticks.");
+       int count = app_info_iterator_get_count(iter);
+       if (count < 1) {
+               app_info_iterator_free(iter);
                return -1;
        }
 
-       if (procfs_read_cpu_count(&ncpus) != 0) {
-               ERR("procfs_read_cpu_count failed.");
+       int *pids = calloc(count, sizeof(int));
+       if (!pids) {
+               app_info_iterator_free(iter);
                return -1;
        }
 
-       _calculate_process_cpu_usage(&generator->previous, &current, ncpus, &usage);
+       do {
+               pids[i++] = app_info_iterator_get_pid(iter);
+       }
+       while (app_info_iterator_next(iter));
 
-       report->time = clock_get_monotonic().tv_sec;
-       report->pid = generator->pid;
-       report->usage = usage;
+       app_info_iterator_free(iter);
 
-       generator->previous = current;
+       int ret = proc_scanner_scan_pids(generator->scanner, pids, i);
+       free(pids);
+       return ret;
+}
 
-       return 0;
+static int _report_generator_top_report_generator_scan_all(
+               report_generator_top_t *generator)
+{
+       return proc_scanner_scan(generator->scanner);
 }
 
-int report_generator_generate_proccess_memory_usage_report(
-               report_generator_process_t *generator,
-               struct process_memory_usage_report *report)
+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 process_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 procfs_process_memory_usage_info mem_info;
-       struct procfs_system_memory_usage_info sys_info;
+       struct report_generator_top_closure closure = {0,};
 
-       if (procfs_read_process_memory_usage(generator->pid, &mem_info) != 0) {
-               ERR("procfs_read_process_memory_usage failed.");
+       if (sys_stats_update(&generator->sys_stats) != 0) {
                return -1;
        }
 
-       if (procfs_read_system_memory_usage(&sys_info) != 0) {
-               ERR("procfs_read_system_memory_usage failed.");
+       if (_report_generator_top_report_generator_scan(generator) != 0) {
                return -1;
        }
 
-       report->time = clock_get_monotonic().tv_sec;
-       report->usage = sys_info.total > 0 ? (float)mem_info.rss / sys_info.total : 0;
+       if (proc_scanner_sort_processes(generator->scanner, _sort_by_cpu_usage) != 0) {
+               return -1;
+       }
 
-       return 0;
-}
+       closure.max_index = generator->limit;
+       closure.sys_stats = generator->sys_stats;
+       closure.usage_report = calloc(generator->limit, sizeof(struct process_usage_report));
+       if (!closure.usage_report) {
+               return -1;
+       }
 
-int report_generator_generate_apps_cpu_usage_report(
-               report_generator_apps_t *generator,
-               int interval,
-               struct app_cpu_usage_report **reports)
-{
-       //TODO implement
-       return -1;
-}
+       if (proc_scanner_foreach_process(generator->scanner, _append_to_cpu_report, &closure) != 0) {
+               free(closure.usage_report);
+               return -1;
+       }
 
+       *report = closure.usage_report;
+       *n_reports = closure.current_index;
 
-int report_generator_generate_apps_memory_usage_report(
-               report_generator_apps_t *generator,
-               struct app_memory_usage_report **report)
-{
-       //TODO implement
-       return -1;
+       return 0;
 }
 
-int report_generator_generate_load_average_report(struct system_load_average_report *report)
+int report_generator_generate_top_memory_report(
+               report_generator_top_t *generator,
+               struct process_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 procfs_load_average_info info = {0,};
+       struct report_generator_top_closure closure = {0,};
+
+       if (sys_stats_update(&generator->sys_stats) != 0) {
+               return -1;
+       }
 
-       if (procfs_read_system_load_average(&info) != 0) {
-               ERR("procfs_read_system_load_average failed.");
+       if (_report_generator_top_report_generator_scan(generator) != 0) {
                return -1;
        }
 
-       report->time = clock_get_monotonic().tv_sec;
-       report->one_min_avg = info.one_min_avg;
-       report->five_min_avg = info.five_min_avg;
-       report->fifteen_min_avg = info.fifteen_min_avg;
+       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.usage_report = calloc(generator->limit, sizeof(struct process_usage_report));
+       if (!closure.usage_report) {
+               return -1;
+       }
+
+       if (proc_scanner_foreach_process(generator->scanner, _append_to_mem_report, &closure) != 0) {
+               free(closure.usage_report);
+               return -1;
+       }
+
+       *report = closure.usage_report;
+       *n_reports = closure.current_index;
 
        return 0;
 }
-