* limitations under the License.
*/
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <stdbool.h>
+#include <unistd.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;
+};
+
+struct report_generator_process
+{
+ /** process pid */
+ int pid;
+ /** previous process stats */
+ struct process_ticks_snapshot previous;
+};
+
+static int _report_generator_read_process_ticks(int pid, struct process_ticks_snapshot *ticks);
+
+static struct timespec clock_get_monotonic()
+{
+ struct timespec ret = {0,};
+
+ 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();
+ }
+
+ return ret;
+}
report_generator_system_t *report_generator_new_system_report_generator()
{
- return NULL;
+ report_generator_system_t *ret = calloc(1, sizeof(struct report_generator_system));
+ if (!ret)
+ return NULL;
+
+ if (procfs_read_system_cpu_usage(&ret->previous) != 0) {
+ ERR("procfs_read_system_cpu_usage failed");
+ free(ret);
+ return NULL;
+ }
+
+ return ret;
}
void report_generator_free_system_generator(report_generator_system_t *generator)
{
+ if (!generator)
+ return;
+
+ free(generator);
}
report_generator_process_t *report_generator_new_process_report_generator(int pid)
{
- return NULL;
+ 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->pid = pid;
+
+ return ret;
}
void report_generator_free_process_generator(report_generator_process_t *generator)
{
+ if (!generator)
+ return;
+
+ free(generator);
+}
+
+static void _calculate_system_cpu_usage(
+ struct procfs_system_cpu_usage_info *previous,
+ struct procfs_system_cpu_usage_info *current,
+ float *usage)
+{
+ struct procfs_system_cpu_usage_info diff_ticks = { 0,};
+
+ 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;
+
+ 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;
+
+ unsigned long long busy = diff_ticks.user + diff_ticks.system + diff_ticks.nice +
+ diff_ticks.irq + diff_ticks.softirq;
+
+ if (total == 0) {
+ *usage = 0;
+ } else {
+ *usage = (float)busy / total;
+ }
}
int report_generator_generate_system_cpu_usage_report(
int interval,
struct system_cpu_usage_report *report)
{
- return -1;
+ 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 (procfs_read_system_cpu_usage(¤t) != 0) {
+ ERR("procfs_read_system_cpu_usage failed.");
+ return -1;
+ }
+
+ _calculate_system_cpu_usage(&generator->previous, ¤t, &usage);
+
+ report->usage = usage;
+ report->time = clock_get_monotonic().tv_sec;
+
+ generator->previous = current;
+
+ return 0;
}
-int report_generator_generate_percpu_usage_report(
+int report_generator_generate_system_memory_usage_report(
report_generator_system_t *generator,
- int interval,
- struct system_percpu_usage_report **report)
+ struct system_memory_usage_report *report) {
+ ON_NULL_RETURN_VAL(generator, -1);
+ ON_NULL_RETURN_VAL(report, -1);
+
+ struct procfs_system_memory_usage_info mem_usage;
+
+ if (procfs_read_system_memory_usage(&mem_usage) != 0) {
+ ERR("procfs_read_system_memory_usage failed.");
+ return -1;
+ }
+
+ report->time = clock_get_monotonic().tv_sec;
+ report->total = mem_usage.total;
+ report->used = mem_usage.used;
+ report->free = mem_usage.free;
+
+ 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)
+{
+ struct process_ticks_snapshot diff;
+
+ 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;
+
+ if (diff.system_ticks == 0)
+ *usage = 0;
+ else
+ *usage = (float)diff.process_ticks / diff.system_ticks * ncpus;
+}
+
+static unsigned long long
+_system_total_time(const struct procfs_system_cpu_usage_info *info)
{
- return -1;
+ return info->user + info->system + info->nice + info->idle;
}
-int report_generator_generate_system_memory_usage_report(
- report_generator_system_t *generator,
- struct system_memory_usage_report *report)
+static int
+_report_generator_read_process_ticks(int pid, struct process_ticks_snapshot *ticks)
{
- return -1;
+ struct procfs_system_cpu_usage_info system_ticks;
+ struct procfs_process_cpu_usage_info process_ticks;
+
+ if (procfs_read_process_cpu_usage(pid, &process_ticks) != 0) {
+ ERR("procfs_read_process_cpu_usage failed.");
+ return -1;
+ }
+
+ if (procfs_read_system_cpu_usage(&system_ticks) != 0) {
+ ERR("procfs_read_system_cpu_usage failed.");
+ return -1;
+ }
+
+ ticks->process_ticks = process_ticks.stime + process_ticks.utime;
+ ticks->system_ticks = _system_total_time(&system_ticks);
+
+ return 0;
}
int report_generator_generate_process_cpu_usage_report(
int interval,
struct process_cpu_usage_report *report)
{
- return -1;
+ ON_NULL_RETURN_VAL(generator, -1);
+ ON_TRUE_RETURN_VAL(interval < 0, -1);
+ ON_NULL_RETURN_VAL(report, -1);
+
+ struct process_ticks_snapshot current;
+ bool block = interval > 0;
+ float usage;
+ int ncpus;
+
+ 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 (_report_generator_read_process_ticks(generator->pid, ¤t) != 0) {
+ ERR("Unable to update process cpu ticks.");
+ return -1;
+ }
+
+ if (procfs_read_cpu_count(&ncpus) != 0) {
+ ERR("procfs_read_cpu_count failed.");
+ return -1;
+ }
+
+ _calculate_process_cpu_usage(&generator->previous, ¤t, ncpus, &usage);
+
+ report->time = clock_get_monotonic().tv_sec;
+ report->pid = generator->pid;
+ report->usage = usage;
+
+ generator->previous = current;
+
+ return 0;
}
int report_generator_generate_proccess_memory_usage_report(
report_generator_process_t *generator,
struct process_memory_usage_report *report)
{
- return -1;
+ ON_NULL_RETURN_VAL(generator, -1);
+ ON_NULL_RETURN_VAL(report, -1);
+
+ struct procfs_process_memory_usage_info mem_info;
+ struct procfs_system_memory_usage_info sys_info;
+
+ if (procfs_read_process_memory_usage(generator->pid, &mem_info) != 0) {
+ ERR("procfs_read_process_memory_usage failed.");
+ return -1;
+ }
+
+ if (procfs_read_system_memory_usage(&sys_info) != 0) {
+ ERR("procfs_read_system_memory_usage failed.");
+ return -1;
+ }
+
+ report->time = clock_get_monotonic().tv_sec;
+ report->usage = sys_info.total > 0 ? (float)mem_info.rss / sys_info.total : 0;
+
+ return 0;
}
int report_generator_generate_load_average_report(struct system_load_average_report *report)
{
- return -1;
+ ON_NULL_RETURN_VAL(report, -1);
+
+ struct procfs_load_average_info info = {0,};
+
+ if (procfs_read_system_load_average(&info) != 0) {
+ ERR("procfs_read_system_load_average failed.");
+ 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;
+
+ return 0;
}