Add initial version of stability-monitor
authorKonrad Kuchciak <k.kuchciak@samsung.com>
Fri, 29 Mar 2019 10:41:15 +0000 (11:41 +0100)
committerKonrad Kuchciak <k.kuchciak@samsung.com>
Thu, 27 Jun 2019 11:38:13 +0000 (13:38 +0200)
Change-Id: Ifdde9fcfce1eeea1debe5720b751061f82f4cb33

37 files changed:
Makefile [new file with mode: 0644]
config/10-example.conf [new file with mode: 0644]
config/default.conf [new file with mode: 0644]
config/stability-monitor.service [new file with mode: 0644]
kernel/Makefile [new file with mode: 0644]
kernel/proc-tsm.c [new file with mode: 0644]
packaging/stability-monitor.spec [new file with mode: 0644]
src/action.c [new file with mode: 0644]
src/action.h [new file with mode: 0644]
src/aul.c [new file with mode: 0644]
src/aul.h [new file with mode: 0644]
src/config.c [new file with mode: 0644]
src/config.h [new file with mode: 0644]
src/data_source.c [new file with mode: 0644]
src/data_source.h [new file with mode: 0644]
src/data_sources/tsm_cpu.c [new file with mode: 0644]
src/data_sources/tsm_fd.c [new file with mode: 0644]
src/data_sources/tsm_io.c [new file with mode: 0644]
src/data_sources/tsm_mem.c [new file with mode: 0644]
src/dbus.c [new file with mode: 0644]
src/dbus.h [new file with mode: 0644]
src/limit.c [new file with mode: 0644]
src/limit.h [new file with mode: 0644]
src/list.h [new file with mode: 0644]
src/log.c [new file with mode: 0644]
src/log.h [new file with mode: 0644]
src/process.c [new file with mode: 0644]
src/process.h [new file with mode: 0644]
src/raw_data.c [new file with mode: 0644]
src/raw_data.h [new file with mode: 0644]
src/raw_data_providers/proc_tsm.c [new file with mode: 0644]
src/raw_data_providers/proc_tsm.h [new file with mode: 0644]
src/sample.c [new file with mode: 0644]
src/sample.h [new file with mode: 0644]
src/stability-monitor.c [new file with mode: 0644]
src/utils.c [new file with mode: 0644]
src/utils.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..bde1162
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,38 @@
+srcdir = ./src
+src = \
+       src/stability-monitor.c \
+       src/config.c \
+       src/aul.c \
+       src/process.c \
+       src/sample.c \
+       src/utils.c \
+       src/log.c \
+       src/dbus.c \
+       src/limit.c \
+       src/action.c \
+       src/raw_data.c \
+       src/raw_data_providers/proc_tsm.c \
+       src/data_source.c \
+       src/data_sources/tsm_cpu.c \
+       src/data_sources/tsm_mem.c \
+       src/data_sources/tsm_io.c \
+       src/data_sources/tsm_fd.c
+
+obj = $(src:.c=.o)
+
+libs = systemd json-c aul gio-2.0 glib-2.0 pkgmgr-info
+
+CFLAGS = \
+       -Wall \
+       -I$(srcdir) \
+       `pkg-config --cflags $(libs)` \
+       -DKMOD_PATH=\"$(KMOD_PATH)\"
+
+LDFLAGS = `pkg-config --libs $(libs)`
+
+
+stability-monitor: $(obj)
+       $(CC) -o $@ $^ $(LDFLAGS)
+
+install:
+       install -D stability-monitor $(INSTALL_PREFIX)/stability-monitor
diff --git a/config/10-example.conf b/config/10-example.conf
new file mode 100644 (file)
index 0000000..9306636
--- /dev/null
@@ -0,0 +1,51 @@
+{
+    "global":{
+        "report": 1,
+    },
+
+
+    "foreground_native_app":{
+        "cpu_limit_avg": 0.6,
+    },
+
+    "background_native_app":{
+        "cpu_limit_avg": 0.5,
+    },
+
+
+    "foreground_web_app":{
+    },
+
+    "background_web_app":{
+    },
+
+
+    "foreground_csharp_app":{
+    },
+
+    "background_csharp_app":{
+    },
+
+
+    "foreground_ui_widget":{
+    },
+
+    "background_ui_widget":{
+    },
+
+
+    "native_service":{
+    },
+
+    "other_service":{
+        "cpu_limit_avg": 0.7,
+    },
+
+    "com.samsung.voice-trigger":{
+        "monitor": 0,
+    },
+
+    "voice-trigger":{
+        "monitor": 0,
+    },
+}
diff --git a/config/default.conf b/config/default.conf
new file mode 100644 (file)
index 0000000..0c4c01e
--- /dev/null
@@ -0,0 +1,32 @@
+{
+    "global":{
+        "monitor": 1,
+        "print_current": 0,
+        "kill": 0,
+        "report": 0,
+        "sampling_rate": 1,
+
+        "cpu_limit_avg": 0.5,
+        "cpu_avg_period": 10,
+        "cpu_limit_peak": 0.95,
+
+        "mem_limit_avg": 0.3,
+        "mem_avg_period": 10,
+        "mem_limit_peak": 0.3,
+
+        "io_limit_avg": 30.0,
+        "io_avg_period": 10,
+        "io_limit_peak": 60.0,
+
+        "fd_limit": 300,
+    },
+
+    "stability-monitor":{
+        "monitor": 0,
+    },
+
+    "crash-manager": {
+        "monitor": 0,
+        "apply_to_children": 1,
+    },
+}
diff --git a/config/stability-monitor.service b/config/stability-monitor.service
new file mode 100644 (file)
index 0000000..a67b7ca
--- /dev/null
@@ -0,0 +1,12 @@
+[Unit]
+Description=Stability monitoring daemon
+
+[Service]
+Type=simple
+SmackProcessLabel=System
+ExecStart=/usr/sbin/stability-monitor
+Restart=on-failure
+RestartSec=0
+
+[Install]
+WantedBy=multi-user.target
diff --git a/kernel/Makefile b/kernel/Makefile
new file mode 100644 (file)
index 0000000..2060cda
--- /dev/null
@@ -0,0 +1,12 @@
+KERNELDIR = /boot/kernel_header/debug/linux/
+PWD := $(shell pwd)
+LD = ld
+
+obj-m += proc-tsm.o
+proc-tsm_SOURCES = proc-tsm.c
+
+all:
+       make -C $(KERNELDIR) M=$(PWD) modules
+
+clean:
+       make -C $(KERNELDIR) M=$(PWD) clean
diff --git a/kernel/proc-tsm.c b/kernel/proc-tsm.c
new file mode 100644 (file)
index 0000000..5dad1aa
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/seq_file.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/pid_namespace.h>
+#include <asm/uaccess.h>
+#include <linux/time.h>
+#include <linux/posix-timers.h>
+#include <linux/kernel_stat.h>
+#include <linux/fdtable.h>
+#include <linux/math64.h>
+
+#define DEBUG 0
+#define PAGE_TO_KB(x) ((x) << (PAGE_SHIFT - 10))
+
+static struct proc_dir_entry* proc_file;
+
+
+static u64 time_now(void)
+{
+    return div64_ul(ktime_get_boot_ns(), NSEC_PER_USEC);
+}
+
+static int stability_monitor_show(struct seq_file *m, void *v)
+{
+    struct sysinfo info;
+    struct task_struct *task;
+    struct pid *pid = NULL;
+    pid_t ppid = 0;
+    cputime_t utime, stime;
+    unsigned long long vm_rss, total_ram, task_io;
+    unsigned int open_fds;
+    u64 uptime;
+
+
+    rcu_read_lock();
+
+    /* Uptime in us */
+    uptime = time_now();
+
+    /* Memory info */
+    si_meminfo(&info);
+    total_ram = PAGE_TO_KB(info.totalram);
+
+    seq_put_decimal_ull(m, 0, uptime);
+    seq_put_decimal_ull(m, ' ', total_ram);
+    seq_printf(m, "\n");
+
+    for_each_process(task) {
+        ppid = task->real_parent->pid;
+        pid = task_pgrp(task);
+        if (!pid)
+            continue;
+
+        task_lock(task);
+
+        if (!task->mm) {
+            task_unlock(task);
+            continue;
+        }
+
+        if (task->flags & (PF_DUMPCORE|PF_SIGNALED|PF_EXITING)) {
+            task_unlock(task);
+            continue;
+        }
+
+
+        /* Memory */
+        vm_rss = get_mm_rss(task->mm);
+
+        /* Open FDs - this needs CONFIG_KDEBUGD_FD_DEBUG */
+        open_fds = task->files ? files_fdtable(task->files)->cur_fdn : 0;
+
+        /* CPU time */
+        thread_group_cputime_adjusted(task, &utime, &stime);
+
+        /* IO */
+        task_io = (unsigned long long)task->ioac.read_bytes + (unsigned long long)task->ioac.write_bytes;
+
+        task_unlock(task);
+
+        seq_put_decimal_ll(m, 0, task->pid);
+        seq_put_decimal_ll(m, ' ', ppid);
+        seq_put_decimal_ull(m, ' ', cputime_to_usecs(utime + stime));
+        seq_put_decimal_ull(m, ' ', PAGE_TO_KB(vm_rss)); // kB
+        seq_put_decimal_ll(m, ' ', open_fds);
+        seq_put_decimal_ull(m, ' ', task_io);
+        seq_printf(m, "\n");
+    }
+
+    rcu_read_unlock();
+
+#if DEBUG
+    printk(KERN_DEBUG "stability_monitor_show() took %llu us\n", time_now() - uptime);
+#endif
+
+    return 0;
+}
+
+static int stability_monitor_open(struct inode *inode, struct file *file)
+{
+    return single_open_size(file, stability_monitor_show, NULL, 8192);
+}
+
+static const struct file_operations stability_monitor_fops = {
+    .owner     = THIS_MODULE,
+    .open      = stability_monitor_open,
+    .read      = seq_read,
+    .llseek  = seq_lseek,
+    .release = single_release,
+};
+
+static int __init stability_monitor_init(void)
+{
+    proc_file = proc_create("tsm", 0, NULL, &stability_monitor_fops);
+    if (!proc_file)
+        return -ENOMEM;
+
+    return 0;
+}
+
+static void __exit stability_monitor_exit(void)
+{
+    remove_proc_entry("tsm", NULL);
+}
+
+module_init(stability_monitor_init);
+module_exit(stability_monitor_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/packaging/stability-monitor.spec b/packaging/stability-monitor.spec
new file mode 100644 (file)
index 0000000..0107538
--- /dev/null
@@ -0,0 +1,46 @@
+Name:       stability-monitor
+Version:    0.0.1
+Release:    0
+License:    Apache-2.0
+Source0:    %{name}-%{version}.tar.xz
+Summary:    Stability monitoring daemon
+Group:      System/Monitoring
+BuildRequires: pkgconfig(libsystemd)
+BuildRequires: pkgconfig(json-c)
+BuildRequires: pkgconfig(aul)
+BuildRequires: pkgconfig(glib-2.0)
+BuildRequires: pkgconfig(gio-2.0)
+BuildRequires: pkgconfig(pkgmgr-info)
+BuildRequires: linux-kantm-devel
+
+%description
+This package provides stability monitoring daemon.
+
+%prep
+%setup -q
+%define KMOD_PATH /%{_libdir}/modules/linux/kernel/drivers/misc/stability-monitor/proc-tsm.ko
+
+%build
+make stability-monitor KMOD_PATH=%KMOD_PATH
+
+cd kernel
+make clean
+make all
+
+%install
+make install INSTALL_PREFIX=%{buildroot}/%{_sbindir}
+install -D config/default.conf %{buildroot}/%{_libdir}/stability-monitor/default.conf
+install -D config/10-example.conf %{buildroot}/%{_sysconfdir}/stability-monitor.d/10-example.conf
+install -D kernel/proc-tsm.ko %{buildroot}/%KMOD_PATH
+
+install -D config/stability-monitor.service %{buildroot}/%{_unitdir}/stability-monitor.service
+mkdir -p %{buildroot}/%{_unitdir}/multi-user.target.wants
+ln -s ../stability-monitor.service %{buildroot}/%{_unitdir}/multi-user.target.wants/stability-monitor.service
+
+%files
+%{_sbindir}/stability-monitor
+%{_libdir}/stability-monitor/default.conf
+%{_sysconfdir}/stability-monitor.d/10-example.conf
+%KMOD_PATH
+%{_unitdir}/stability-monitor.service
+%{_unitdir}/multi-user.target.wants/stability-monitor.service
diff --git a/src/action.c b/src/action.c
new file mode 100644 (file)
index 0000000..045ba38
--- /dev/null
@@ -0,0 +1,237 @@
+/*
+ * This file is part of stability-monitor
+ *
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <sys/types.h>
+#include <unistd.h>
+#include <signal.h>
+#include <limits.h>
+#include <glib.h>
+#include <glib-unix.h>
+#include <gio/gio.h>
+#include <fcntl.h>
+#include <string.h>
+
+#include "utils.h"
+#include "log.h"
+#include "action.h"
+#include "dbus.h"
+#include "limit.h"
+
+
+struct action_data {
+    int stdout_fd;
+    int stderr_fd;
+
+    struct data_source *ds;
+    enum limit_type lt;
+    GVariant *actual_value;
+    GVariant *allowed_value;
+
+    char *report_path;
+};
+
+static void action_finish(struct action_data *ad)
+{
+    char objpath[PATH_MAX];
+    char interface[PATH_MAX];
+    char format[20];
+    GVariantBuilder *builder;
+    GVariant *signal_params;
+
+    snprintf(format, 20, "(s@%s@%s@a{sv})", g_variant_get_type_string(ad->actual_value),
+                                             g_variant_get_type_string(ad->allowed_value));
+
+    snprintf(objpath, PATH_MAX, "%s/%s", TSM_DBUS_PATH, ad->ds->name);
+    snprintf(interface, PATH_MAX, "%s.%s", TSM_DBUS_INTERFACE, ad->ds->param_name);
+
+
+    builder = g_variant_builder_new(G_VARIANT_TYPE("a{sv}"));
+    g_variant_builder_add(builder, "{sv}",
+                          "report_path",
+                          g_variant_new_string(ad->report_path ? ad->report_path : ""));
+
+    g_variant_builder_add(builder, "{sv}",
+                          "killed",
+                          g_variant_new_boolean(ad->ds->process->kill));
+
+    signal_params = g_variant_new(format,
+                                  limit_type_to_string(ad->lt),
+                                  ad->actual_value,
+                                  ad->allowed_value,
+                                  g_variant_builder_end(builder));
+
+    g_variant_builder_unref(builder);
+
+    /* Kill the process */
+    if (ad->ds->process->kill) {
+        _D_PROC(ad->ds->process, "Killing process");
+        if (kill(ad->ds->process->pid, SIGKILL))
+            _E("Unable to kill process %s(%d): %m",
+               ad->ds->process->name,
+               ad->ds->process->pid);
+    }
+
+    /* Send signal */
+    dbus_send_signal(objpath, interface, "AbnormalityDetected", signal_params);
+
+    _D_PROC(ad->ds->process, "Sent D-Bus signal (%s, %s)",
+                             limit_type_to_string(ad->lt),
+                             ad->ds->param_name);
+
+    ad->ds->process->action_in_progress = 0;
+
+    close(ad->stdout_fd);
+    close(ad->stderr_fd);
+    free(ad->report_path);
+    free(ad);
+}
+
+static void crash_manager_exit_cb(GPid pid, gint status, gpointer user_data)
+{
+    struct action_data *ad = user_data;
+    FILE *stream = NULL;
+    char *line = NULL;
+    size_t len = 0;
+    ssize_t n;
+
+    if (!g_spawn_check_exit_status (status, NULL)) {
+        _E("Crash-manager failed with code: %d", status);
+
+        stream = fdopen(ad->stderr_fd, "r");
+        if (!stream) {
+            _E("Unable to open stderr stream: %m");
+            goto finish;
+        }
+
+        while ((n = getline(&line, &len, stream)) != -1)
+            _D("%s", line);
+
+        goto finish;
+    }
+
+    stream = fdopen(ad->stdout_fd, "r");
+    if (!stream) {
+        _E("Unable to open stdout stream: %m");
+        goto finish;
+    }
+
+    /* Get report path from stdout */
+    while ((n = getline(&line, &len, stream)) != -1) {
+        if (strncmp(line, "REPORT_PATH=", 12) == 0) {
+            line[n-1] = 0;
+
+            ad->report_path = strndup(line + 12, n - 12);
+            if (!ad->report_path)
+                _E("Unable to allocate memory");
+            else
+                _D_PROC(ad->ds->process, "Received report: %s", ad->report_path);
+
+            goto finish;
+        }
+    }
+
+    _E("Crash-worker ended without error but didn't provide report path");
+
+finish:
+    action_finish(ad);
+
+    free(line);
+    if (stream)
+        fclose(stream);
+
+    g_spawn_close_pid(pid);
+}
+
+static GPid spawn_crash_manager(struct action_data *ad)
+{
+    GPid child_pid = 0;
+    GError *error = NULL;
+    gchar **argv;
+
+    argv = g_new(gchar *, 5);
+    argv[0] = g_strdup(CRASH_MANAGER_BIN);
+    argv[1] = g_strdup_printf("-p%d", ad->ds->process->pid);
+    argv[2] = g_strdup("-l");
+    argv[3] = g_strdup("-r");
+    argv[4] = NULL;
+
+    _D_PROC(ad->ds->process, "Generating report...");
+
+    /* Spawn child */
+    if (!g_spawn_async_with_pipes(NULL, argv, NULL,
+                                  G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH, NULL,
+                                  NULL, &child_pid, NULL, &ad->stdout_fd, &ad->stderr_fd, &error)) {
+
+        _E("Unable to spawn child process: %s", error->message);
+        g_error_free(error);
+    }
+
+    g_strfreev(argv);
+
+    return child_pid;
+}
+
+static gboolean action_run(gpointer data)
+{
+    struct action_data *ad = data;
+    GPid child_pid;
+
+    if (ad->ds->process->report) {
+        child_pid = spawn_crash_manager(ad);
+        if (child_pid != 0)
+            g_child_watch_add(child_pid, crash_manager_exit_cb, ad);
+    } else {
+        action_finish(ad);
+    }
+
+    return FALSE;
+}
+
+int action_run_default(struct data_source *ds, enum limit_type lt)
+{
+    struct action_data *ad;
+    struct smpl_container *sc = process_is_foreground(ds->process) ? &ds->data_fg : &ds->data_bg;
+
+    if (ds->process->action_in_progress)
+        return 0;
+    ds->process->action_in_progress = 1;
+
+    _D_PROC(ds->process, "Running default action");
+
+    ad = calloc(1, sizeof(*ad));
+    if (!ad) {
+        _E("Unable to allocate memory");
+        return -ENOMEM;
+    }
+
+    ad->ds = ds;
+    ad->report_path = NULL;
+    ad->lt = lt;
+
+    if (lt == LIMIT_TYPE_AVG) {
+        ad->actual_value = g_variant_new_double(sc->average);
+        ad->allowed_value = g_variant_new_double(ds->limit.avg);
+    } else {
+        ad->actual_value = sample_to_gvariant(list_first_entry(&sc->samples, struct sample_s, node));
+        ad->allowed_value = sample_to_gvariant(&ds->limit.peak);
+    }
+
+    g_timeout_add(0, action_run, ad);
+
+    return 0;
+}
diff --git a/src/action.h b/src/action.h
new file mode 100644 (file)
index 0000000..87a2aa8
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 ACTION_H
+#define ACTION_H
+
+#include "data_source.h"
+#include "limit.h"
+
+#define CRASH_MANAGER_BIN "crash-manager"
+
+int action_run_default(struct data_source *ds, enum limit_type lt);
+
+#endif /* ACTION_H */
\ No newline at end of file
diff --git a/src/aul.c b/src/aul.c
new file mode 100644 (file)
index 0000000..c169c24
--- /dev/null
+++ b/src/aul.c
@@ -0,0 +1,82 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <aul/aul.h>
+#include <systemd/sd-event.h>
+#include <string.h>
+
+#include "log.h"
+#include "process.h"
+#include "config.h"
+#include "dbus.h"
+
+#include "aul.h"
+
+
+static void app_status_change_handler(GDBusConnection *connection,
+                                       const gchar *sender_name,
+                                       const gchar *object_path,
+                                       const gchar *interface_name,
+                                       const gchar *signal_name,
+                                       GVariant *parameters,
+                                       gpointer user_data)
+{
+    const char *appid, *pkgid, *status, *type;
+    int pid, aul_app_status;
+    struct proc_info *process;
+    unsigned int old_class;
+    int ret;
+
+    g_variant_get(parameters, "(issss)", &pid, &appid, &pkgid, &status, &type);
+
+    process = process_get_by_pid(pid);
+    if (process) {
+        old_class = process->class;
+
+        if (0 == strcmp(status, "fg"))
+            aul_app_status = STATUS_VISIBLE;
+        else
+            aul_app_status = STATUS_BG;
+
+        ret = process_update_info(process, &aul_app_status);
+        if (ret)
+            return;
+
+        if (process->class != old_class)
+            _D_PROC(process, "Class changed");
+
+    } else {
+        _W("Received AUL status update for PID %d, but it wasn't found in internal state - ignoring.", pid);
+    }
+}
+
+int aul_listener_init(void)
+{
+    int ret;
+
+    ret = dbus_subscribe("/Org/Tizen/Aul/AppStatus",
+                         "org.tizen.aul.AppStatus",
+                         "AppStatusChange",
+                         app_status_change_handler);
+    if (ret) {
+        _E("Unable to subscribe to signal \"AppStatusChange\"");
+        return -1;
+    }
+
+    return 0;
+}
diff --git a/src/aul.h b/src/aul.h
new file mode 100644 (file)
index 0000000..cad7419
--- /dev/null
+++ b/src/aul.h
@@ -0,0 +1,24 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 AUL_H
+#define AUL_H
+
+int aul_listener_init(void);
+
+#endif /* AUL_H */
\ No newline at end of file
diff --git a/src/config.c b/src/config.c
new file mode 100644 (file)
index 0000000..d792c17
--- /dev/null
@@ -0,0 +1,190 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <json-c/json.h>
+#include <errno.h>
+#include <string.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <limits.h>
+
+#include "log.h"
+#include "process.h"
+#include "utils.h"
+#include "data_source.h"
+#include "config.h"
+
+static struct json_object *config = NULL;
+
+
+int config_is_empty(void) {
+    return config == NULL;
+}
+
+struct json_object *config_get(void)
+{
+    return config;
+}
+
+int config_load(const char *config_file)
+{
+    config = json_object_from_file(config_file);
+    if (!config)
+        return -EINVAL;
+
+    _D("Loaded config %s\n", config_file);
+
+    return 0;
+}
+
+static int config_load_merge(const char *config_file)
+{
+    struct json_object *obj;
+    int ret;
+
+    obj = json_object_from_file(config_file);
+    if (!obj) {
+        _E("Unable to parse config %s", config_file);
+        return -EINVAL;
+    }
+
+    json_object_object_foreach(obj, key, val) {
+        ret = _json_object_object_merge(config, key, val);
+        if (ret) {
+            json_object_put(obj);
+            return ret;
+        }
+    }
+
+    _D("Loaded config %s\n", config_file);
+
+    return 0;
+}
+
+static int is_regular_file(const char *path)
+{
+    struct stat st;
+
+    stat(path, &st);
+
+    return S_ISREG(st.st_mode);
+}
+
+int config_load_dir(const char *config_dir)
+{
+    int n;
+    struct dirent **filelist;
+    char full_path[PATH_MAX];
+
+    n = scandir(config_dir, &filelist, NULL, alphasort);
+    if (n < 0) {
+        _W("Unable to load user config from %s: %m", config_dir);
+        return n;
+    }
+
+    while (n--) {
+        snprintf(full_path, PATH_MAX, "%s/%s", config_dir, filelist[n]->d_name);
+
+        if (is_regular_file(full_path))
+            config_load_merge(full_path);
+
+        free(filelist[n]);
+    }
+
+    free(filelist);
+    return 0;
+}
+
+void config_print(void)
+{
+    _D("Config tree:\n\n%s\n",
+        json_object_to_json_string_ext(config, JSON_C_TO_STRING_PRETTY));
+}
+
+static void _read_params(json_object *obj, struct proc_info *process)
+{
+    struct data_source *ds;
+
+    json_object_object_foreach(obj, key, val) {
+        if (strcmp(key, "monitor") == 0) {
+            process->monitor = json_object_get_int(val) ? 1 : 0;
+            continue;
+        }
+
+        if (strcmp(key, "kill") == 0) {
+            process->kill = json_object_get_int(val) ? 1 : 0;
+            continue;
+        }
+
+        if (strcmp(key, "report") == 0) {
+            process->report = json_object_get_int(val) ? 1 : 0;
+            continue;
+        }
+
+        if (strcmp(key, "print_current") == 0) {
+            process->print_current = json_object_get_int(val) ? 1 : 0;
+            continue;
+        }
+
+        if (strcmp(key, "sampling_rate") == 0) {
+            process->sampling_rate = json_object_get_double(val);
+            continue;
+        }
+
+        if (strcmp(key, "apply_to_children") == 0) {
+            process->apply_to_children = json_object_get_int(val) ? 1 : 0;
+            continue;
+        }
+
+        for (int i = 0; i < data_sources_get_n(); i++) {
+            ds = &(process->data_sources[i]);
+            if (ds->apply_config(ds, key, val) == 0)
+                break;
+        }
+    }
+}
+
+int config_read_per_process(struct proc_info *process)
+{
+    const char *class_str = process_class_to_string(process->class);
+    struct json_object *obj;
+
+    if (!config) {
+        _E("There is no config");
+        return -EINVAL;
+    }
+
+    /* Global */
+    if (!json_object_object_get_ex(config, "global", &obj))
+        _E("There is no 'global' section in config");
+    else
+        _read_params(obj, process);
+
+    /* Class related config */
+    if (!json_object_object_get_ex(config, class_str, &obj))
+        _W("There is no '%s' section in config", class_str);
+    else
+        _read_params(obj, process);
+
+    /* Process by name */
+    if (json_object_object_get_ex(config, process->name, &obj)) {
+        _read_params(obj, process);
+    }
+
+    return 0;
+}
\ No newline at end of file
diff --git a/src/config.h b/src/config.h
new file mode 100644 (file)
index 0000000..99b9d79
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 CONFIG_H
+#define CONFIG_H
+
+#include <json-c/json.h>
+#include "process.h"
+
+
+int config_is_empty(void);
+struct json_object *config_get(void);
+int config_load(const char *config_file);
+int config_load_dir(const char *config_dir);
+void config_print(void);
+int config_read_per_process(struct proc_info *process);
+
+#endif /* CONFIG_H */
\ No newline at end of file
diff --git a/src/data_source.c b/src/data_source.c
new file mode 100644 (file)
index 0000000..e34ece6
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <errno.h>
+#include <assert.h>
+
+#include "list.h"
+#include "log.h"
+
+#include "data_source.h"
+
+static struct data_source_mod **data_sources = NULL;
+static int data_sources_n = 0;
+
+
+int data_source_add_sample(struct data_source *ds, enum sample_type type,
+                           void *value, unsigned long long measure_time)
+{
+    struct sample_s *last_sample, *sample, *prev;
+    struct smpl_container *sc = process_is_foreground(ds->process) ? &ds->data_fg : &ds->data_bg;
+
+    last_sample = sample_add(type, value, &sc->samples, measure_time);
+    if (!last_sample){
+        _E("Unable to add sample");
+        return -ENOMEM;
+    }
+
+    if (last_sample->type == ST_INT)
+        sc->average = (sc->average * sc->samples_n + last_sample->value.i) / (sc->samples_n + 1);
+    else if (last_sample->type == ST_DOUBLE)
+        sc->average = (sc->average * sc->samples_n + last_sample->value.d) / (sc->samples_n + 1);
+    else if (last_sample->type == ST_ULL)
+        sc->average = (sc->average * sc->samples_n + last_sample->value.ull) / (sc->samples_n + 1);
+
+    sc->samples_n++;
+
+    /* Sample retency */
+
+    if (ds->limit.avg_period == 0) {
+    /* Don't count average, leave only last sample */
+
+        sample_remove_all_but_n(1, &sc->samples);
+        sc->average = 0;
+        sc->samples_n = 0;
+
+    } else {
+    /* Remove old samples, no longer needed */
+        list_for_each_entry_reverse_safe(sample, prev, &sc->samples, node) {
+            if ((long long) sample->measure_time >= (long long) (last_sample->measure_time - (USEC_PER_SEC * ds->limit.avg_period)))
+                break;
+
+            assert(sc->samples_n > 1);
+
+            /* Recalculate average */
+            if (sample->type == ST_INT)
+                sc->average = (sc->average * sc->samples_n - sample->value.i) / (sc->samples_n - 1);
+            else if (sample->type == ST_DOUBLE)
+                sc->average = (sc->average * sc->samples_n - sample->value.d) / (sc->samples_n - 1);
+            else if (sample->type == ST_ULL)
+                sc->average = (sc->average * sc->samples_n - sample->value.ull) / (sc->samples_n - 1);
+
+            sc->samples_n--;
+
+            list_del(&sample->node);
+            free(sample);
+        }
+    }
+
+    return 0;
+}
+
+int data_source_register(struct data_source_mod *mod)
+{
+    if (!mod->name) {
+        _E("Data source name must not be empty");
+        goto error;
+    }
+
+    data_sources = realloc(data_sources, (data_sources_n + 1) * sizeof(struct data_source_mod *));
+    if (!data_sources) {
+        _E("Unable to allocate memory");
+        goto error;
+    }
+
+    data_sources[data_sources_n++] = mod;
+    _D("Registered data source: %s\n", mod->name);
+
+    return 0;
+
+error:
+    _E("Unable to register data source: %s", mod->name);
+    return -1;
+}
+
+void data_source_unregister(struct data_source_mod *mod)
+{
+    if (--data_sources_n == 0)
+        free(data_sources);
+}
+
+struct data_source_mod **data_sources_get(void)
+{
+    return data_sources;
+}
+
+int data_sources_get_n(void)
+{
+    return data_sources_n;
+}
diff --git a/src/data_source.h b/src/data_source.h
new file mode 100644 (file)
index 0000000..be9a323
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 DATA_SOURCE_H
+#define DATA_SOURCE_H
+
+#include <json-c/json.h>
+
+#include "list.h"
+#include "limit.h"
+#include "process.h"
+#include "sample.h"
+#include "raw_data.h"
+
+struct smpl_container {
+    struct list_head samples;
+    int samples_n;
+    double average;
+};
+
+struct data_source {
+    char                  *name;
+    char                  *param_name;
+
+    struct limit_s        limit;
+    struct smpl_container data_fg;
+    struct smpl_container data_bg;
+    void                  *extra_data;
+
+    int (*update)(struct data_source *ds,
+                  struct raw_data_item *rd_global,
+                  struct raw_data_item *rd_pid);
+    int (*apply_config)(struct data_source *ds,
+                        char *key, struct json_object *val);
+    int (*action)(struct data_source *ds, enum limit_type lt);
+    void (*cleanup)(struct data_source *ds);
+
+    struct proc_info *process;
+};
+
+struct data_source_mod {
+    char *name;
+
+    int (*init)(struct data_source *ds);
+};
+
+int data_source_add_sample(struct data_source *ds, enum sample_type type,
+                           void *value, unsigned long long measure_time);
+
+int data_source_register(struct data_source_mod *mod);
+void data_source_unregister(struct data_source_mod *mod);
+
+struct data_source_mod **data_sources_get(void);
+int data_sources_get_n(void);
+
+#define DATA_SOURCE_REGISTER(mod)                                \
+    static void __attribute__((constructor)) _ds_register(void)  \
+    {                                                            \
+        data_source_register(mod);                               \
+    }                                                            \
+                                                                 \
+    static void __attribute__((destructor)) _ds_unregister(void) \
+    {                                                            \
+        data_source_unregister(mod);                             \
+    }
+
+#endif /* DATA_SOURCE_H */
\ No newline at end of file
diff --git a/src/data_sources/tsm_cpu.c b/src/data_sources/tsm_cpu.c
new file mode 100644 (file)
index 0000000..528a218
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <errno.h>
+#include <malloc.h>
+#include <json-c/json.h>
+#include <string.h>
+
+#include "log.h"
+#include "data_source.h"
+#include "utils.h"
+#include "raw_data.h"
+#include "limit.h"
+#include "action.h"
+#include "raw_data_providers/proc_tsm.h"
+
+#define TSM_CPU_MODULE_NAME "tsm_cpu"
+#define TSM_CPU_PARAM_NAME  "cpu.relative"
+
+
+struct cpu_extra_data {
+    unsigned long long cpu_time, cpu_time_last;
+    unsigned long long measure_time, measure_time_last;
+    int next_run;
+};
+
+static int update(struct data_source *ds,
+                  struct raw_data_item *rd_global,
+                  struct raw_data_item *rd_pid)
+{
+    struct cpu_extra_data *ced = (struct cpu_extra_data *) ds->extra_data;
+    double cpu_usage;
+    int ret;
+
+    ced->cpu_time_last = ced->cpu_time;
+    ced->measure_time_last = ced->measure_time;
+
+    ced->cpu_time = rd_pid->v[ITEM_CPU];
+    ced->measure_time = rd_global->v[ITEM_UPTIME];
+
+    if (!ced->next_run) {
+        /*
+         * End here if this is the first run of this function,
+         * because we need at least two values of cpu_time to calculate the cpu usage
+         */
+        ced->next_run = 1;
+        return 0;
+    }
+
+    cpu_usage = (double) (ced->cpu_time - ced->cpu_time_last) / (ced->measure_time - ced->measure_time_last);
+
+    ret = data_source_add_sample(ds, ST_DOUBLE, &cpu_usage, rd_global->v[0]);
+    if (ret) {
+        _E("Unable to add sample: %d", ret);
+        return ret;
+    }
+
+    return 0;
+}
+
+static int apply_config(struct data_source *ds, char *key, struct json_object *val)
+{
+    if (strcmp(key, "cpu_limit_avg") == 0) {
+        ds->limit.avg = json_object_get_double(val);
+        return 0;
+    }
+
+    if (strcmp(key, "cpu_avg_period") == 0) {
+        ds->limit.avg_period = json_object_get_int(val);
+        return 0;
+    }
+
+    if (strcmp(key, "cpu_limit_peak") == 0) {
+        ds->limit.peak.value.d = json_object_get_double(val);
+        ds->limit.peak.type = ST_DOUBLE;
+        return 0;
+    }
+
+    return -1;
+}
+
+static int action(struct data_source *ds, enum limit_type lt)
+{
+    return action_run_default(ds, lt);
+}
+
+static void cleanup(struct data_source *ds)
+{
+    struct cpu_extra_data *ced = (struct cpu_extra_data*) ds->extra_data;
+
+    free(ced);
+}
+
+static int init(struct data_source *ds)
+{
+    struct cpu_extra_data *ced;
+
+    ds->name = TSM_CPU_MODULE_NAME;
+    ds->param_name = TSM_CPU_PARAM_NAME;
+
+    ds->update = update;
+    ds->apply_config = apply_config;
+    ds->action = action;
+    ds->cleanup = cleanup;
+
+    /* Extra data - for cpu usage calculation */
+    ced = calloc(1, sizeof(*ced));
+    if (!ced) {
+        _E("Unable to allocate memory");
+        return -ENOMEM;
+    }
+
+    ds->extra_data = ced;
+
+    return 0;
+}
+
+struct data_source_mod cpu_module = {
+    .name = TSM_CPU_MODULE_NAME,
+    .init = init,
+};
+
+DATA_SOURCE_REGISTER(&cpu_module);
diff --git a/src/data_sources/tsm_fd.c b/src/data_sources/tsm_fd.c
new file mode 100644 (file)
index 0000000..665b587
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <errno.h>
+#include <json-c/json.h>
+#include <string.h>
+
+#include "log.h"
+#include "data_source.h"
+#include "utils.h"
+#include "raw_data.h"
+#include "limit.h"
+#include "action.h"
+#include "raw_data_providers/proc_tsm.h"
+
+#define TSM_FD_MODULE_NAME "tsm_fd"
+#define TSM_FD_PARAM_NAME  "fd.absolute"
+
+
+static int update(struct data_source *ds,
+                  struct raw_data_item *rd_global,
+                  struct raw_data_item *rd_pid)
+{
+    int ret;
+
+    ret = data_source_add_sample(ds, ST_INT, &rd_pid->v[ITEM_FDS], rd_global->v[ITEM_UPTIME]);
+    if (ret) {
+        _E("Unable to add sample: %d", ret);
+        return ret;
+    }
+
+    return 0;
+}
+
+static int apply_config(struct data_source *ds, char *key, struct json_object *val)
+{
+    if (strcmp(key, "fd_limit") == 0) {
+        ds->limit.peak.value.i = json_object_get_int(val);
+        ds->limit.peak.type = ST_INT;
+        return 0;
+    }
+
+    return -1;
+}
+
+static int action(struct data_source *ds, enum limit_type lt)
+{
+    return action_run_default(ds, lt);
+}
+
+static int init(struct data_source *ds)
+{
+    ds->name = TSM_FD_MODULE_NAME;
+    ds->param_name = TSM_FD_PARAM_NAME;
+
+    ds->update = update;
+    ds->apply_config = apply_config;
+    ds->action = action;
+    ds->cleanup = NULL;
+
+    return 0;
+}
+
+struct data_source_mod fd_module = {
+    .name = TSM_FD_MODULE_NAME,
+    .init = init,
+};
+
+DATA_SOURCE_REGISTER(&fd_module);
diff --git a/src/data_sources/tsm_io.c b/src/data_sources/tsm_io.c
new file mode 100644 (file)
index 0000000..827add6
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <errno.h>
+#include <malloc.h>
+#include <json-c/json.h>
+#include <string.h>
+
+#include "log.h"
+#include "data_source.h"
+#include "utils.h"
+#include "raw_data.h"
+#include "limit.h"
+#include "action.h"
+#include "raw_data_providers/proc_tsm.h"
+
+#define TSM_IO_MODULE_NAME "tsm_io"
+#define TSM_IO_PARAM_NAME  "io.absolute"
+
+
+struct io_extra_data {
+    unsigned long long io_bytes, io_bytes_last;
+    unsigned long long measure_time, measure_time_last;
+    int next_run;
+};
+
+static int update(struct data_source *ds,
+                  struct raw_data_item *rd_global,
+                  struct raw_data_item *rd_pid)
+{
+    struct io_extra_data *ied = (struct io_extra_data *) ds->extra_data;
+    double io_rate;
+    int ret;
+
+    ied->io_bytes_last = ied->io_bytes;
+    ied->measure_time_last = ied->measure_time;
+
+    ied->io_bytes = rd_pid->v[ITEM_IO];
+    ied->measure_time = rd_global->v[ITEM_UPTIME];
+
+    if (!ied->next_run) {
+        ied->next_run = 1;
+        return 0;
+    }
+
+    io_rate = (double) (ied->io_bytes - ied->io_bytes_last) /
+              (ied->measure_time - ied->measure_time_last);
+
+    ret = data_source_add_sample(ds, ST_DOUBLE, &io_rate, rd_global->v[0]);
+    if (ret) {
+        _E("Unable to add sample: %d", ret);
+        return ret;
+    }
+
+    return 0;
+}
+
+static int apply_config(struct data_source *ds, char *key, struct json_object *val)
+{
+    if (strcmp(key, "io_limit_avg") == 0) {
+        ds->limit.avg = json_object_get_double(val);
+        return 0;
+    }
+
+    if (strcmp(key, "io_avg_period") == 0) {
+        ds->limit.avg_period = json_object_get_int(val);
+        return 0;
+    }
+
+    if (strcmp(key, "io_limit_peak") == 0) {
+        ds->limit.peak.value.d = json_object_get_double(val);
+        ds->limit.peak.type = ST_DOUBLE;
+        return 0;
+    }
+
+    return -1;
+}
+
+static int action(struct data_source *ds, enum limit_type lt)
+{
+    return action_run_default(ds, lt);
+}
+
+static void cleanup(struct data_source *ds)
+{
+    struct io_extra_data *ied = (struct io_extra_data*) ds->extra_data;
+
+    free(ied);
+}
+
+static int init(struct data_source *ds)
+{
+    struct io_extra_data *ied;
+
+    ds->name = TSM_IO_MODULE_NAME;
+    ds->param_name = TSM_IO_PARAM_NAME;
+
+    ds->update = update;
+    ds->apply_config = apply_config;
+    ds->action = action;
+    ds->cleanup = cleanup;
+
+    /* Extra data - for io_rate calculation */
+    ied = calloc(1, sizeof(*ied));
+    if (!ied) {
+        _E("Unable to allocate memory");
+        return -ENOMEM;
+    }
+
+    ds->extra_data = ied;
+
+    return 0;
+}
+
+struct data_source_mod io_module = {
+    .name = TSM_IO_MODULE_NAME,
+    .init = init,
+};
+
+DATA_SOURCE_REGISTER(&io_module);
diff --git a/src/data_sources/tsm_mem.c b/src/data_sources/tsm_mem.c
new file mode 100644 (file)
index 0000000..287d23f
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <errno.h>
+#include <json-c/json.h>
+#include <string.h>
+
+#include "log.h"
+#include "data_source.h"
+#include "utils.h"
+#include "raw_data.h"
+#include "limit.h"
+#include "action.h"
+#include "raw_data_providers/proc_tsm.h"
+
+#define TSM_MEM_MODULE_NAME "tsm_mem"
+#define TSM_MEM_PARAM_NAME  "mem.relative"
+
+
+static int update(struct data_source *ds,
+                  struct raw_data_item *rd_global,
+                  struct raw_data_item *rd_pid)
+{
+    double mem_usage;
+    int ret;
+
+    mem_usage = (double) rd_pid->v[ITEM_RSS] / rd_global->v[ITEM_TOTAL_MEM];
+
+    ret = data_source_add_sample(ds, ST_DOUBLE, &mem_usage, rd_global->v[ITEM_UPTIME]);
+    if (ret) {
+        _E("Unable to add sample: %d", ret);
+        return ret;
+    }
+
+    return 0;
+}
+
+static int apply_config(struct data_source *ds, char *key, struct json_object *val)
+{
+    if (strcmp(key, "mem_limit_avg") == 0) {
+        ds->limit.avg = json_object_get_double(val);
+        return 0;
+    }
+
+    if (strcmp(key, "mem_avg_period") == 0) {
+        ds->limit.avg_period = json_object_get_int(val);
+        return 0;
+    }
+
+    if (strcmp(key, "mem_limit_peak") == 0) {
+        ds->limit.peak.value.d = json_object_get_double(val);
+        ds->limit.peak.type = ST_DOUBLE;
+        return 0;
+    }
+
+    return -1;
+}
+
+static int action(struct data_source *ds, enum limit_type lt)
+{
+    return action_run_default(ds, lt);
+}
+
+static int init(struct data_source *ds)
+{
+    ds->name = TSM_MEM_MODULE_NAME;
+    ds->param_name = TSM_MEM_PARAM_NAME;
+
+    ds->update = update;
+    ds->apply_config = apply_config;
+    ds->action = action;
+    ds->cleanup = NULL;
+
+    return 0;
+}
+
+struct data_source_mod mem_module = {
+    .name = TSM_MEM_MODULE_NAME,
+    .init = init,
+};
+
+DATA_SOURCE_REGISTER(&mem_module);
diff --git a/src/dbus.c b/src/dbus.c
new file mode 100644 (file)
index 0000000..4eb0036
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <gio/gio.h>
+#include <glib.h>
+
+#include "log.h"
+#include "dbus.h"
+
+static GDBusConnection *system_bus = NULL;
+
+
+static int dbus_connect()
+{
+    GError *err = NULL;
+
+    if (system_bus)
+        return 0;
+
+    system_bus = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
+    if (!system_bus) {
+        _E("Unable to connect to system bus: %s", err->message);
+        g_error_free(err);
+        return -1;
+    }
+
+    return 0;
+}
+
+int dbus_send_signal(const char *object_path,
+                     const char *interface_name,
+                     const char *signal_name,
+                     GVariant *parameters)
+{
+    GError *err = NULL;
+    gchar *parameters_str = g_variant_print(parameters, TRUE);
+    int ret;
+
+    ret = dbus_connect();
+    if (ret)
+        goto cleanup;
+
+    ret = g_dbus_connection_emit_signal(system_bus,                 /* connection */
+                                        NULL,                       /* destination_bus_name */
+                                        object_path,                /* object_path */
+                                        interface_name,             /* interface_name */
+                                        signal_name,                /* signal_name */
+                                        parameters,                 /* parameters */
+                                        &err);                      /* error */
+    if (ret == FALSE) {
+        _E("Unable to emit dbus signal: %s", err->message);
+        g_error_free(err);
+        ret = -1;
+        goto cleanup;
+    }
+
+    _DD("Emitted signal\n"
+        "    objpath: %s\n"
+        "    interface: %s\n"
+        "    member: %s\n"
+        "    parameters: %s\n",
+        object_path, interface_name, signal_name, parameters_str);
+
+cleanup:
+    g_free(parameters_str);
+    return ret;
+}
+
+int dbus_subscribe(const char *object_path,
+                   const char *interface_name,
+                   const char *signal_name,
+                   void (*handler)(GDBusConnection *,
+                                   const gchar *,
+                                   const gchar *,
+                                   const gchar *,
+                                   const gchar *,
+                                   GVariant *,
+                                   gpointer))
+{
+    int ret;
+
+    ret = dbus_connect();
+    if (ret)
+        return ret;
+
+    ret = g_dbus_connection_signal_subscribe(system_bus,
+                    NULL,
+                    interface_name,
+                    signal_name,
+                    object_path,
+                    NULL,
+                    G_DBUS_SIGNAL_FLAGS_NONE,
+                    handler,
+                    NULL,
+                    NULL);
+    if (ret == FALSE) {
+        _E("g_dbus_connection_signal_subscribe() failed");
+        return -1;
+    }
+
+    return 0;
+}
\ No newline at end of file
diff --git a/src/dbus.h b/src/dbus.h
new file mode 100644 (file)
index 0000000..23969a9
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 DBUS_H
+#define DBUS_H
+
+#include <gio/gio.h>
+#include <glib.h>
+
+#define TSM_DBUS_PATH       "/Org/Tizen/StabilityMonitor"
+#define TSM_DBUS_INTERFACE  "org.tizen.abnormality"
+
+int dbus_send_signal(const char *object_path,
+                     const char *interface_name,
+                     const char *signal_name,
+                     GVariant *parameters);
+
+int dbus_subscribe(const char *object_path,
+                   const char *interface_name,
+                   const char *signal_name,
+                   void (*handler)(GDBusConnection *,
+                                   const gchar *,
+                                   const gchar *,
+                                   const gchar *,
+                                   const gchar *,
+                                   GVariant *,
+                                   gpointer));
+#endif /* DBUS_H */
diff --git a/src/limit.c b/src/limit.c
new file mode 100644 (file)
index 0000000..d0a11f4
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <assert.h>
+
+#include "limit.h"
+#include "log.h"
+
+
+const char *limit_type_to_string(enum limit_type lt)
+{
+    assert(lt == LIMIT_TYPE_PEAK || lt == LIMIT_TYPE_AVG);
+
+    switch (lt) {
+    case LIMIT_TYPE_PEAK:
+        return "peak";
+    case LIMIT_TYPE_AVG:
+        return "avg";
+    }
+
+    return NULL;
+}
diff --git a/src/limit.h b/src/limit.h
new file mode 100644 (file)
index 0000000..de44e79
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 LIMIT_H
+#define LIMIT_H
+
+#include "list.h"
+#include "sample.h"
+
+enum limit_type {
+    LIMIT_TYPE_PEAK,
+    LIMIT_TYPE_AVG,
+};
+
+struct limit_s {
+    double avg;
+    int avg_period;         /* seconds */
+    struct sample_s peak;
+};
+
+const char *limit_type_to_string(enum limit_type);
+
+#endif /* LIMIT_H */
\ No newline at end of file
diff --git a/src/list.h b/src/list.h
new file mode 100644 (file)
index 0000000..861ee30
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * This file is based on list.h from faultd.
+ * https://review.tizen.org/gerrit/a/platform/core/system/faultd.git
+ *
+ * Copyright © 2017,2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 LIST_H
+#define LIST_H
+
+
+#ifndef offsetof
+#define offsetof(type, member)  __builtin_offsetof(type, member)
+#endif /* offsetof */
+
+#ifndef container_of
+#define container_of(ptr, type, field) ({             \
+       const typeof(((type *)0)->field) *member = (ptr); \
+       (type *)((char *)member - offsetof(type, field)); \
+})
+#endif /* container_of */
+
+
+/*
+ * Simple doubly linked list based on list from linux kernel
+ */
+struct list_head {
+       struct list_head *next;
+       struct list_head *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+static inline void INIT_LIST_HEAD(struct list_head *head)
+{
+       head->next = head;
+       head->prev = head;
+}
+
+static inline void list_add_helper(struct list_head *new,
+                                   struct list_head *prev,
+                                   struct list_head *next)
+{
+       new->prev = prev;
+       new->next = next;
+       next->prev = new;
+       prev->next = new;
+}
+
+/*
+ * Add a new entry after the specified head
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+       list_add_helper(new, head, head->next);
+}
+
+/*
+ * Add a new entry before the specified head
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+       list_add_helper(new, head->prev, head);
+}
+
+/*
+ * Remove given element from the list
+ */
+static inline void list_del(struct list_head *entry)
+{
+       entry->next->prev = entry->prev;
+       entry->prev->next = entry->next;
+}
+
+/*
+ * Remove given element from the list and reinitialize it
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+       list_del(entry);
+       INIT_LIST_HEAD(entry);
+}
+
+static inline int list_empty(const struct list_head *head)
+{
+       return head->next == head;
+}
+
+static inline int list_is_singular(const struct list_head *head)
+{
+       return (!list_empty(head) && head->prev == head->next);
+}
+
+#define list_entry(ptr, type, member) \
+       container_of((ptr), type, member)
+
+#define list_first_entry(ptr, type, member) \
+       list_entry((ptr)->next, type, member)
+
+#define list_first_entry_or_null(ptr, type, member) \
+       (list_empty(ptr) ? NULL : list_first_entry((ptr), type, member))
+
+#define list_last_entry(ptr, type, member) \
+       list_entry((ptr)->prev, type, member)
+
+#define list_next_entry(pos, member) \
+       list_entry((pos)->member.next, typeof(*pos), member)
+
+#define list_prev_entry(pos, member) \
+       list_entry((pos)->member.prev, typeof(*pos), member)
+
+#define list_for_each(pos, head) for ( \
+       (pos) = (head)->next; \
+       (pos) != (head); \
+       (pos) = (pos)->next)
+
+#define list_for_each_safe(pos, n, head) for ( \
+       (pos) = (head)->next, (n) = (pos)->next; \
+       (pos) != (head); \
+       (pos) = (n), (n) = (pos)->next)
+
+#define list_for_each_entry(pos, head, member) for ( \
+       (pos) = list_first_entry((head), typeof(*pos), member); \
+       &(pos)->member != (head); \
+       (pos) = list_next_entry((pos), member))
+
+#define list_for_each_entry_reverse(pos, head, member) for ( \
+       (pos) = list_last_entry((head), typeof(*pos), member); \
+       &(pos)->member != (head); \
+       (pos) = list_prev_entry((pos), member))
+
+#define list_for_each_entry_safe(pos, n, head, member) for ( \
+       (pos) = list_first_entry((head), typeof(*pos), member), \
+               (n) = list_next_entry((pos), member); \
+       &(pos)->member != (head); \
+       (pos) = (n), \
+               (n) = list_next_entry((pos), member))
+
+#define list_for_each_entry_reverse_safe(pos, n, head, member) for ( \
+       (pos) = list_last_entry((head), typeof(*pos), member), \
+               (n) = list_prev_entry((pos), member); \
+       &(pos)->member != (head); \
+       (pos) = (n), \
+               (n) = list_prev_entry((pos), member))
+
+#endif /* LIST_H */
diff --git a/src/log.c b/src/log.c
new file mode 100644 (file)
index 0000000..6e256af
--- /dev/null
+++ b/src/log.c
@@ -0,0 +1,13 @@
+#include "log.h"
+
+static unsigned long long msg_count = 0;
+
+void log_inc_msg_count()
+{
+    msg_count++;
+}
+
+unsigned long long log_get_msg_count()
+{
+    return msg_count;
+}
\ No newline at end of file
diff --git a/src/log.h b/src/log.h
new file mode 100644 (file)
index 0000000..016245b
--- /dev/null
+++ b/src/log.h
@@ -0,0 +1,57 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 LOG_H
+#define LOG_H
+
+#include <stdio.h>
+
+#include "utils.h"
+#include "process.h"
+
+#define print(fmt, arg...)      \
+    printf(fmt, ##arg);         \
+    log_inc_msg_count()
+
+#define _E(fmt, arg...) do {    \
+    print("ERROR: %s::%s(%d): " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##arg); \
+} while (0)
+
+#define _W(fmt, arg...) do {    \
+    print("WARN: %s::%s(%d): " fmt "\n", __FILE__, __FUNCTION__, __LINE__, ##arg);  \
+} while (0)
+
+#define _D(fmt, arg...) do {    \
+    print(fmt, ##arg);          \
+} while (0)
+
+#define _DD(fmt, arg...) do {   \
+    /* TODO */                  \
+} while (0)
+
+#define _D_PROC(process, fmt, arg...)           \
+    _D("(%d)\t[%-25.25s | %-21s ] " fmt "\n",   \
+       process->pid,                            \
+       process->name,                           \
+       process_class_to_string(process->class), \
+       ##arg)
+
+void log_inc_msg_count();
+unsigned long long log_get_msg_count();
+
+#endif /* LOG_H */
diff --git a/src/process.c b/src/process.c
new file mode 100644 (file)
index 0000000..a9c5bdb
--- /dev/null
@@ -0,0 +1,548 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <errno.h>
+#include <malloc.h>
+#include <string.h>
+#include <aul/aul.h>
+#include <limits.h>
+#include <pkgmgr-info.h>
+
+#include "utils.h"
+#include "config.h"
+#include "log.h"
+#include "sample.h"
+#include "dbus.h"
+#include "data_source.h"
+
+#include "process.h"
+
+#define HASH_TABLE_SIZE 200
+
+/* List of all processes and their current state */
+static struct list_head process_table[HASH_TABLE_SIZE];
+
+
+static int hash(int pid)
+{
+    return pid % HASH_TABLE_SIZE;
+}
+
+void process_table_init()
+{
+    for (int i = 0; i < HASH_TABLE_SIZE; i++)
+        INIT_LIST_HEAD(&process_table[i]);
+}
+
+static void process_table_add(struct proc_info *process)
+{
+    list_add(&process->node, &process_table[hash(process->pid)]);
+}
+
+int process_create_info(int pid, int ppid, struct proc_info **process)
+{
+    struct proc_info *p;
+    struct data_source_mod *ds_mod;
+    struct data_source *ds;
+    int ret;
+
+    p = calloc(1, sizeof(struct proc_info));
+    if (!p) {
+        _E("Unable to allocate memory");
+        return -ENOMEM;
+    }
+
+    /* Init and set default values in case of not being specified in config */
+    p->pid = pid;
+    p->ppid = ppid;
+    p->parent = process_get_by_pid(ppid);
+    p->name = NULL;
+    p->appid = NULL;
+    p->apptype = NULL;
+    p->component = -1;
+    p->status = -1;
+
+    p->monitor = 1;
+    p->kill = 0;
+    p->print_current = 0;
+    p->sampling_rate = 1;
+    p->apply_to_children = 0;
+
+    /* Create data sources */
+    p->data_sources = calloc(data_sources_get_n(), sizeof(struct data_source));
+    if (!p->data_sources) {
+        _E("Unable to allocate memory");
+        return -ENOMEM;
+    }
+
+    for (int i = 0; i < data_sources_get_n(); i++) {
+        ds_mod = data_sources_get()[i];
+        ds = &(p->data_sources[i]);
+
+        ds->process = p;
+        INIT_LIST_HEAD(&ds->data_fg.samples);
+        INIT_LIST_HEAD(&ds->data_bg.samples);
+
+        ret = ds_mod->init(ds);
+        if (ret) {
+            _E("Unable to initialize new %s data source: %d", ds_mod->name, ret);
+            return ret;
+        }
+    }
+
+    INIT_LIST_HEAD(&p->node);
+
+    ret = process_update_info(p, NULL);
+    if (ret)
+        return ret;
+
+    process_table_add(p);
+    *process = p;
+
+    if (p->monitor)
+        _D_PROC(p, "Created process");
+
+    return 0;
+}
+
+static int process_get_appid(struct proc_info *process)
+{
+    char buf[NAME_MAX];
+    int ret;
+
+    free(process->appid);
+    process->appid = NULL;
+
+    ret = aul_app_get_appid_bypid(process->pid, buf, sizeof(buf));
+    if (ret != AUL_R_OK) {
+        /* process is not an application */
+        process->appid = NULL;
+        return -1;
+    }
+
+    process->appid = strndup(buf, NAME_MAX);
+    if (!process->appid) {
+        _E("Unable to allocate memory");
+        return -ENOMEM;
+    }
+
+    return 0;
+}
+
+static int process_get_exe(struct proc_info *process)
+{
+    char buf[NAME_MAX];
+    int ret;
+
+    free(process->exe);
+    process->exe = NULL;
+
+    ret = pid_to_basename(process->pid, buf);
+    if (ret) {
+        _E("Unable to get process basename: %d", ret);
+        return -1;
+    }
+
+    process->exe = strndup(buf, NAME_MAX);
+    if (!process->exe) {
+        _E("Unable to allocate memory");
+        return -ENOMEM;
+    }
+
+    return 0;
+}
+
+static int process_get_component(struct proc_info *process)
+{
+    pkgmgrinfo_appinfo_h handle;
+    pkgmgrinfo_app_component component;
+    int ret;
+
+    process->component = -1;
+
+    ret = pkgmgrinfo_appinfo_get_appinfo(process->appid, &handle);
+    if (ret != PMINFO_R_OK) {
+        _E("pkgmgrinfo_appinfo_get_appinfo() failed: %d", ret);
+        return -1;
+    }
+
+    ret = pkgmgrinfo_appinfo_get_component(handle, &component);
+    if (ret != PMINFO_R_OK) {
+        _E("pkgmgrinfo_appinfo_get_component() failed: %d", ret);
+        pkgmgrinfo_appinfo_destroy_appinfo(handle);
+        return -1;
+    }
+
+    process->component = component;
+
+    pkgmgrinfo_appinfo_destroy_appinfo(handle);
+    return 0;
+}
+
+static int process_get_apptype(struct proc_info *process)
+{
+    pkgmgrinfo_appinfo_h handle;
+    char *apptype = NULL;
+    int ret;
+
+    if (process->apptype) {
+        free(process->apptype);
+        process->apptype = NULL;
+    }
+
+    ret = pkgmgrinfo_appinfo_get_appinfo(process->appid, &handle);
+    if (ret != PMINFO_R_OK) {
+        _E("pkgmgrinfo_appinfo_get_appinfo() failed: %d", ret);
+        return -1;
+    }
+
+    ret = pkgmgrinfo_appinfo_get_apptype(handle, &apptype);
+    if (ret != PMINFO_R_OK) {
+        _E("pkgmgrinfo_appinfo_get_apptype() failed: %d", ret);
+        pkgmgrinfo_appinfo_destroy_appinfo(handle);
+        return -1;
+    }
+
+    process->apptype = strdup(apptype);
+    if (!process->apptype) {
+        _E("Unable to allocate memory");
+        return -ENOMEM;
+    }
+
+    pkgmgrinfo_appinfo_destroy_appinfo(handle);
+    return 0;
+}
+
+int process_is_foreground(struct proc_info *process)
+{
+    return (process->status == STATUS_FOCUS ||
+            process->status == STATUS_VISIBLE ||
+            process->status == STATUS_HOME);
+}
+
+static void process_classify(struct proc_info *process)
+{
+    int webapp, dotnet;
+
+    if (process->component == PMINFO_UI_APP) {
+        webapp = (0 == strcmp(process->apptype, "webapp"));
+        dotnet = (0 == strcmp(process->apptype, "dotnet"));
+
+        if (process_is_foreground(process)) {
+            if (webapp)
+                process->class = PROC_CLASS_FG_WEB_APP;
+            else if (dotnet)
+                process->class = PROC_CLASS_FG_CSHARP_APP;
+            else
+                process->class = PROC_CLASS_FG_NATIVE_APP;
+        } else {
+            if (webapp)
+                process->class = PROC_CLASS_BG_WEB_APP;
+            else if (dotnet)
+                process->class = PROC_CLASS_BG_CSHARP_APP;
+            else
+                process->class = PROC_CLASS_BG_NATIVE_APP;
+        }
+
+        return;
+    }
+
+    if (process->component == PMINFO_WIDGET_APP) {
+        if (process_is_foreground(process))
+            process->class = PROC_CLASS_FG_UI_WIDGET;
+        else
+            process->class = PROC_CLASS_BG_UI_WIDGET;
+        return;
+    }
+
+    if (process->component == PMINFO_SVC_APP) {
+        process->class = PROC_CLASS_NATIVE_SERVICE;
+        return;
+    }
+
+    process->class = PROC_CLASS_OTHER_SERVICE;
+}
+
+int process_update_info(struct proc_info *process, int *status)
+{
+    int ret;
+
+    if (!process->monitor)
+        return 0;
+
+    ret = process_get_appid(process);
+    if (ret == -ENOMEM) {
+        goto cleanup;
+    }
+
+    ret = process_get_exe(process);
+    if (ret) {
+        goto cleanup;
+    }
+
+    if (process->appid) {
+        process->name = process->appid;
+
+        ret = process_get_apptype(process);
+        if (ret) {
+            _E("Unable to get process' app type");
+            goto cleanup;
+        }
+
+        ret = process_get_component(process);
+        if (ret) {
+            _E("Unable to get process' component type");
+            goto cleanup;
+        }
+
+    } else {
+        process->name = process->exe;
+    }
+
+    process->status = status ? *status : aul_app_get_status_bypid(process->pid);
+
+    process_classify(process);
+
+    if (process->parent && process->parent->apply_to_children) {
+        process_copy_settings(process->parent, process);
+    } else {
+        ret = config_read_per_process(process);
+        if (ret) {
+            _E("Unable to read limits for process");
+            goto cleanup;
+        }
+    }
+
+    return 0;
+
+cleanup:
+    process_cleanup(process);
+    return ret;
+}
+
+void process_copy_settings(struct proc_info *src, struct proc_info *dst)
+{
+    dst->monitor = src->monitor;
+    dst->kill = src->kill;
+    dst->sampling_rate = src->sampling_rate;
+    dst->apply_to_children = src->apply_to_children;
+
+    for (int i = 0; i < data_sources_get_n(); i++) {
+        dst->data_sources[i].limit = src->data_sources[i].limit;
+    }
+}
+
+struct proc_info *process_get_by_pid(int pid)
+{
+    struct proc_info *process;
+    int h = hash(pid);
+
+    list_for_each_entry(process, &process_table[h], node)
+        if (process->pid == pid)
+            return process;
+
+    return NULL;
+}
+
+const char *process_class_to_string(int class)
+{
+    switch (class) {
+    case PROC_CLASS_FG_NATIVE_APP:
+        return "foreground_native_app";
+    case PROC_CLASS_BG_NATIVE_APP:
+        return "background_native_app";
+    case PROC_CLASS_FG_WEB_APP:
+        return "foreground_web_app";
+    case PROC_CLASS_BG_WEB_APP:
+        return "background_web_app";
+    case PROC_CLASS_FG_CSHARP_APP:
+        return "foreground_csharp_app";
+    case PROC_CLASS_BG_CSHARP_APP:
+        return "background_csharp_app";
+    case PROC_CLASS_FG_UI_WIDGET:
+        return "foreground_ui_widget";
+    case PROC_CLASS_BG_UI_WIDGET:
+        return "background_ui_widget";
+    case PROC_CLASS_NATIVE_SERVICE:
+        return "native_service";
+    case PROC_CLASS_OTHER_SERVICE:
+        return "other_service";
+    default:
+        return "unknown class";
+    }
+}
+
+void process_check_limits(struct proc_info *process)
+{
+    struct data_source *ds;
+    struct smpl_container *sc;
+    struct sample_s *last_sample;
+
+    for (int i = 0; i < data_sources_get_n(); i++) {
+        ds = &(process->data_sources[i]);
+
+        sc = process_is_foreground(ds->process) ? &ds->data_fg : &ds->data_bg;
+        if (list_empty(&sc->samples))
+            continue;
+
+        last_sample = list_first_entry(&sc->samples, struct sample_s, node);
+
+        /* Peak limit */
+        if (sample_is_greater(last_sample, &ds->limit.peak)) {
+            _D_PROC(process, "%s: PEAK limit exceeded (%s)",
+                             ds->name, smpl2str(last_sample));
+
+            ds->action(ds, LIMIT_TYPE_PEAK);
+        }
+
+        /* Avg limit */
+        if (sc->average > ds->limit.avg) {
+            _D_PROC(process, "%s: AVG limit exceeded (%2.3lf)",
+                             ds->name, sc->average);
+
+            ds->action(ds, LIMIT_TYPE_AVG);
+        }
+    }
+}
+
+static void _print_header(struct proc_info *process)
+{
+    struct data_source *ds;
+    int ds_n = data_sources_get_n();
+    int header_lines = 2;
+    gchar **header_items = g_new(gchar *, (ds_n + 1) * header_lines);
+    gchar *str = NULL;
+
+    for (int i = 0; i < ds_n; i++) {
+        ds = &(process->data_sources[i]);
+        header_items[(ds_n + 1) * 0 + i] = g_strdup_printf("%s%-20s", i > 0 ? " | " : "", ds->param_name);
+        header_items[(ds_n + 1) * 1 + i] = g_strdup_printf("%s%-10s%-10s", i > 0 ? " | " : "", "peak ---- ", "avg ------");
+    }
+
+    /* Print the header line by line */
+    for (int i = 0; i < header_lines; i++) {
+        header_items[(ds_n + 1) * i + ds_n] = NULL;
+        str = g_strjoinv(NULL, &header_items[(ds_n + 1) * i]);
+        _D_PROC(process, "%s", str);
+        g_free(str);
+    }
+
+    g_strfreev(header_items);
+}
+
+void process_print_current_state(struct proc_info *process)
+{
+    struct data_source *ds;
+    struct smpl_container *sc;
+    struct sample_s *last_sample;
+    int ds_n = data_sources_get_n();
+    gchar **state_items = g_new(gchar *, ds_n);
+    gchar *avg_item;
+    gchar *state_msg = NULL;
+    static unsigned long long last_msg_count = 0;
+
+    /* Print header every n log messages to keep it always visible */
+    if (log_get_msg_count() - last_msg_count > STATE_HEADER_EVERY_N_MSG) {
+        last_msg_count = log_get_msg_count();
+        _print_header(process);
+    }
+
+    for (int i = 0; i < ds_n; i++) {
+        ds = &(process->data_sources[i]);
+        sc = process_is_foreground(ds->process) ? &ds->data_fg : &ds->data_bg;
+
+        last_sample = list_empty(&sc->samples) ?
+                      NULL : list_first_entry(&sc->samples, struct sample_s, node);
+
+        avg_item = g_strdup_printf("%2.3lf", sc->average);
+        state_items[i] = g_strdup_printf("%s%-10s%-10s",
+                                         i > 0 ? " | " : "",
+                                         last_sample ? smpl2str(last_sample) : "n/a",
+                                         ds->limit.avg_period > 0 ? avg_item : "n/a");
+        g_free(avg_item);
+    }
+
+    state_items[ds_n] = NULL;
+
+    state_msg = g_strjoinv(NULL, state_items);
+    _D_PROC(process, "%s", state_msg);
+
+    g_strfreev(state_items);
+    g_free(state_msg);
+}
+
+void process_remove_deads(void)
+{
+    struct proc_info *process, *next;
+
+    for (int i = 0; i < HASH_TABLE_SIZE; i++) {
+        list_for_each_entry_safe(process, next, &process_table[i], node) {
+            if (!process->still_alive && !process->action_in_progress) {
+                process_cleanup(process);
+                continue;
+            }
+
+            process->still_alive = 0;
+        }
+    }
+}
+
+void process_cleanup(struct proc_info *process)
+{
+    struct data_source *ds;
+    struct sample_s *sample, *sample_next;
+
+    if (process->monitor)
+        _D_PROC(process, "Removed process");
+
+    free(process->exe);
+    free(process->appid);
+    free(process->apptype);
+
+    /* Remove data sources */
+    for (int i = 0; i < data_sources_get_n(); i++) {
+        ds = &(process->data_sources[i]);
+
+        /* Remove samples */
+        list_for_each_entry_safe(sample, sample_next, &ds->data_fg.samples, node) {
+            list_del(&sample->node);
+            free(sample);
+        }
+        list_for_each_entry_safe(sample, sample_next, &ds->data_bg.samples, node) {
+            list_del(&sample->node);
+            free(sample);
+        }
+
+        if (ds->cleanup)
+            ds->cleanup(ds);
+    }
+    free(process->data_sources);
+
+    list_del(&process->node);
+    free(process);
+}
+
+void process_cleanup_all(void)
+{
+    struct proc_info *process, *next;
+
+    for (int i = 0; i < HASH_TABLE_SIZE; i++) {
+        list_for_each_entry_safe(process, next, &process_table[i], node)
+            process_cleanup(process);
+    }
+}
diff --git a/src/process.h b/src/process.h
new file mode 100644 (file)
index 0000000..a0a160c
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <stdint.h>
+
+#include "list.h"
+#include "limit.h"
+
+#define STATE_HEADER_EVERY_N_MSG 20
+
+enum proc_class {
+    PROC_CLASS_FG_NATIVE_APP,
+    PROC_CLASS_BG_NATIVE_APP,
+    PROC_CLASS_FG_WEB_APP,
+    PROC_CLASS_BG_WEB_APP,
+    PROC_CLASS_FG_CSHARP_APP,
+    PROC_CLASS_BG_CSHARP_APP,
+    PROC_CLASS_FG_UI_WIDGET,
+    PROC_CLASS_BG_UI_WIDGET,
+    PROC_CLASS_NATIVE_SERVICE,
+    PROC_CLASS_OTHER_SERVICE,
+};
+
+/* Main structure storing current process state */
+struct proc_info {
+    int                 pid;
+    int                 ppid;               /* pid of the parent */
+    struct proc_info    *parent;            /* pointer to the parent */
+    char*               name;               /* either exe or appid */
+    char*               exe;                /* basename of /proc/pid/exe */
+    char*               appid;              /* from aul */
+    char*               apptype;            /* from pkgmgrinfo */
+    int                 component;          /* pkgmgrinfo_app_component */
+    int                 status;             /* aul_app_status */
+    enum proc_class     class;
+
+    int                 still_alive;
+    int                 action_in_progress;
+
+    int                 monitor;
+    int                 kill;
+    int                 report;
+    int                 print_current;
+    double              sampling_rate;
+    unsigned long long  last_update;
+    int                 apply_to_children;
+
+    struct data_source  *data_sources;
+
+    struct list_head    node;
+};
+
+void process_table_init();
+int process_create_info(int pid, int ppid, struct proc_info **process);
+int process_update_info(struct proc_info *process, int *status);
+void process_copy_settings(struct proc_info *src, struct proc_info *dst);
+struct proc_info *process_get_by_pid(int pid);
+int process_is_foreground(struct proc_info *process);
+const char *process_class_to_string(int class);
+void process_check_limits(struct proc_info *process);
+void process_print_current_state(struct proc_info *process);
+void process_remove_deads(void);
+void process_cleanup(struct proc_info *process);
+void process_cleanup_all(void);
+
+
+#endif /* PROCESS_H */
\ No newline at end of file
diff --git a/src/raw_data.c b/src/raw_data.c
new file mode 100644 (file)
index 0000000..9e8d455
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <errno.h>
+
+#include "list.h"
+#include "log.h"
+
+#include "raw_data.h"
+
+static struct raw_data_provider *rdp = NULL;
+
+int raw_data_new_item(struct list_head *rd, struct raw_data_item **rd_i)
+{
+    *rd_i = calloc(1, sizeof(struct raw_data_item));
+    if (!*rd_i) {
+        _E("Unable to allocate memory");
+        return -ENOMEM;
+    }
+
+    INIT_LIST_HEAD(&(*rd_i)->node);
+    list_add_tail(&(*rd_i)->node, rd);
+
+    return 0;
+}
+
+void raw_data_cleanup(struct list_head *rd)
+{
+    struct raw_data_item *rd_i, *next;
+
+    list_for_each_entry_safe(rd_i, next, rd, node) {
+        list_del(&rd_i->node);
+        free(rd_i);
+    }
+}
+
+int raw_data_provider_register(struct raw_data_provider *mod)
+{
+    /* TODO: Check name */
+
+    rdp = mod;
+    _D("Registered raw data provider: %s\n", mod->name);
+
+    return 0;
+}
+
+struct raw_data_provider *raw_data_provider_get(void)
+{
+    return rdp;
+}
\ No newline at end of file
diff --git a/src/raw_data.h b/src/raw_data.h
new file mode 100644 (file)
index 0000000..e54f115
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 RAW_DATA_H
+#define RAW_DATA_H
+
+#include "list.h"
+
+#define MAX_PARAMS_PER_LINE 10
+
+
+struct raw_data_item {
+    unsigned long long v[MAX_PARAMS_PER_LINE];
+    struct list_head node;
+};
+
+struct raw_data_provider {
+    char *name;
+
+    int (*init)(void);
+    int (*get_raw_data)(struct list_head *rd);
+    void (*cleanup)(void);
+};
+
+int raw_data_new_item(struct list_head *rd, struct raw_data_item **rd_i);
+void raw_data_cleanup(struct list_head *rd);
+
+int raw_data_provider_register(struct raw_data_provider *mod);
+struct raw_data_provider *raw_data_provider_get(void);
+
+#define RAW_DATA_PROVIDER_REGISTER(mod)                           \
+    static void __attribute__((constructor)) _rdp_register(void)  \
+    {                                                             \
+        raw_data_provider_register(mod);                          \
+    }
+
+#endif /* RAW_DATA_H */
diff --git a/src/raw_data_providers/proc_tsm.c b/src/raw_data_providers/proc_tsm.c
new file mode 100644 (file)
index 0000000..7656564
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "log.h"
+#include "raw_data.h"
+
+#define PROC_TSM_MODULE_NAME "proc_tsm"
+#define PROC_TSM_PATH "/proc/tsm"
+#define MAX_LINE 100
+#define finit_module(fd, params, flags) syscall(__NR_finit_module, fd, params, flags)
+
+
+static int str_to_ullarray(char *str, unsigned long long *array)
+{
+    char *ptr = str;
+    int n = 0;
+
+    while (*ptr != '\n' && *ptr != 0)
+        if (n < MAX_PARAMS_PER_LINE) {
+            array[n++] = strtoull(ptr, &ptr, 10);
+        } else {
+            _W("Reached maximum number of parameters per line (%d), discarding others",
+               MAX_PARAMS_PER_LINE);
+            break;
+        }
+
+    return n;
+}
+
+/* Read data prepared by kernel module */
+static int get_raw_data(struct list_head *rd)
+{
+    char buf[MAX_LINE] = {0,};
+    struct raw_data_item *rd_i;
+    int ret;
+    FILE *fp;
+
+    fp = fopen(PROC_TSM_PATH, "r");
+    if (!fp) {
+        _E("Unable to open " PROC_TSM_PATH ": %m");
+        return -EINVAL;
+    }
+
+    while (fgets(buf, MAX_LINE, fp) != NULL) {
+        ret = raw_data_new_item(rd, &rd_i);
+        if (ret)
+            goto error;
+
+        ret = str_to_ullarray(buf, rd_i->v);
+        if (ret <= 0) {
+            _E("Unable to parse raw data from kernel: \"%s\"\n", buf);
+            goto error;
+        }
+    }
+
+    fclose(fp);
+    return 0;
+
+error:
+    fclose(fp);
+    raw_data_cleanup(rd);
+    return ret;
+}
+
+/* Load kernel module if not loaded */
+static int init(void)
+{
+    int fd, ret = 0;
+
+    if (access(PROC_TSM_PATH, O_RDONLY) == 0) {
+        _D("Kernel module already loaded\n");
+        return 0;
+    }
+
+    fd = open(KMOD_PATH, O_RDONLY);
+    if (fd == -1) {
+        _E("Unable to open file descriptor");
+        return -1;
+    }
+
+    ret = finit_module(fd, "", 0);
+    close(fd);
+    if (ret) {
+        _E("Unable to load kernel module: %m");
+        return ret;
+    }
+
+    return 0;
+}
+
+static void cleanup(void)
+{
+    return;
+}
+
+struct raw_data_provider proc_tsm_module = {
+    .name = PROC_TSM_MODULE_NAME,
+    .init = init,
+    .get_raw_data = get_raw_data,
+    .cleanup = cleanup,
+};
+
+RAW_DATA_PROVIDER_REGISTER(&proc_tsm_module);
diff --git a/src/raw_data_providers/proc_tsm.h b/src/raw_data_providers/proc_tsm.h
new file mode 100644 (file)
index 0000000..7a642cf
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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_TSM
+#define PROC_TSM
+
+enum proc_tsm_glob_items {
+    ITEM_UPTIME,
+    ITEM_TOTAL_MEM,
+};
+
+enum proc_tsm_pid_items {
+    ITEM_PID,
+    ITEM_PPID,
+    ITEM_CPU,
+    ITEM_RSS,
+    ITEM_FDS,
+    ITEM_IO,
+};
+
+#endif /* PROC_TSM */
\ No newline at end of file
diff --git a/src/sample.c b/src/sample.c
new file mode 100644 (file)
index 0000000..f4a9213
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <malloc.h>
+#include <errno.h>
+
+#include "log.h"
+#include "list.h"
+#include "sample.h"
+
+
+struct sample_s *sample_add(enum sample_type type, void *value,
+                            struct list_head *list, unsigned long long measure_time)
+{
+    struct sample_s *sample;
+
+    sample = calloc(1, sizeof(*sample));
+    if (!sample)
+        return NULL;
+
+    INIT_LIST_HEAD(&sample->node);
+    sample->type = type;
+    sample->measure_time = measure_time;
+
+    if (type == ST_INT)
+        sample->value.i = *(int*) value;
+    else if (type == ST_DOUBLE)
+        sample->value.d = *(double*) value;
+    else if (type == ST_ULL)
+        sample->value.ull = *(unsigned long long*) value;
+
+    list_add(&sample->node, list);
+
+    return sample;
+}
+
+void sample_remove_older_than(unsigned long long time, struct list_head *list)
+{
+    struct sample_s *sample, *prev;
+
+    list_for_each_entry_reverse_safe(sample, prev, list, node) {
+        if (sample->measure_time >= time)
+            break;
+
+        list_del(&sample->node);
+        free(sample);
+    }
+}
+
+void sample_remove_all_but_n(int n, struct list_head *list)
+{
+    struct sample_s *sample, *next;
+
+    /* Skip n samples */
+    sample = list_first_entry(list, struct sample_s, node);
+    for (int i = 0; i < n; i++) {
+        sample = list_next_entry(sample, node);
+        if (&sample->node == list)
+            return;
+    }
+
+    /* Remove the rest */
+    for (next = list_next_entry(sample, node);
+         &sample->node != list;
+         sample = next, next = list_next_entry(sample, node)) {
+
+        list_del(&sample->node);
+        free(sample);
+    }
+}
+
+void sample_print_list(struct list_head *list)
+{
+    struct sample_s *sample;
+
+    list_for_each_entry(sample, list, node) {
+        _D("sample %p\n", sample);
+    }
+}
+
+int sample_is_greater(struct sample_s *s1, struct sample_s *s2)
+{
+    if (s1->type != s2->type)
+        _W("Comparing samples of different types");
+
+    if (s1->type == ST_INT)
+        return s1->value.i > s2->value.i;
+    else if (s1->type == ST_DOUBLE)
+        return s1->value.d > s2->value.d;
+    else if (s1->type == ST_ULL)
+        return s1->value.ull > s2->value.ull;
+
+    return 0;
+}
+
+GVariant *sample_to_gvariant(struct sample_s *s)
+{
+    if (s->type == ST_INT)
+        return g_variant_new_int32(s->value.i);
+    else if (s->type == ST_DOUBLE)
+        return g_variant_new_double(s->value.d);
+    else if (s->type == ST_ULL)
+        return g_variant_new_uint64(s->value.ull);
+
+    return NULL;
+}
diff --git a/src/sample.h b/src/sample.h
new file mode 100644 (file)
index 0000000..a28e9a6
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 SAMPLE_H
+#define SAMPLE_H
+
+#include <stdint.h>
+#include <glib.h>
+
+#include "list.h"
+
+
+enum sample_type {
+    ST_INT,
+    ST_ULL,
+    ST_DOUBLE,
+};
+
+/* Single sample of data */
+struct sample_s {
+    union {
+        int i;
+        double d;
+        unsigned long long ull;
+    } value;
+
+    enum sample_type type;
+    unsigned long long measure_time;
+
+    struct list_head node;
+};
+
+struct sample_s *sample_add(enum sample_type type, void *value,
+                            struct list_head *list, unsigned long long measure_time);
+
+void sample_remove_older_than(unsigned long long time, struct list_head *list);
+void sample_remove_all_but_n(int n, struct list_head *list);
+void sample_print_list(struct list_head *list);
+int sample_is_greater(struct sample_s *s1, struct sample_s *s2);
+GVariant *sample_to_gvariant(struct sample_s *s);
+
+#endif /* SAMPLE_H */
\ No newline at end of file
diff --git a/src/stability-monitor.c b/src/stability-monitor.c
new file mode 100644 (file)
index 0000000..3fe0f46
--- /dev/null
@@ -0,0 +1,317 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <errno.h>
+#include <getopt.h>
+#include <signal.h>
+#include <string.h>
+#include <glib.h>
+#include <glib-unix.h>
+#include <gio/gio.h>
+
+#include "log.h"
+#include "config.h"
+#include "aul.h"
+#include "process.h"
+#include "dbus.h"
+#include "raw_data.h"
+#include "data_source.h"
+
+#define DEFAULT_CONFIG_FILE "/usr/lib/stability-monitor/default.conf"
+#define USER_CONFIG_DIR  "/etc/stability-monitor.d"
+
+
+static void print_help(const char *name)
+{
+    printf("Usage: %s [OPTIONS]\n"
+           "Options: \n"
+           "  -c, --config <file>       Read configuration only from <file>\n"
+           "  -h, --help                Display this help and exit\n",
+           name);
+}
+
+static int parse_argv(int argc, char *argv[])
+{
+    static struct option long_options[] = {
+        {"config", required_argument, NULL, 'c'},
+        {"help", no_argument, NULL, 'h'},
+        {}
+    };
+    int c, ret;
+
+
+    while ((c = getopt_long(argc, argv, "c:h", long_options, NULL)) >= 0) {
+        switch (c) {
+        case 'c':
+            ret = config_load(optarg);
+            if (ret) {
+                _E("Unable to parse config: %d", ret);
+                return -1;
+            }
+            break;
+
+        case 'h':
+            print_help(argv[0]);
+            exit(0);
+            break;
+
+        default:
+            print_help(argv[0]);
+            return -EINVAL;
+        }
+    }
+
+    return 0;
+}
+
+static int load_config(void)
+{
+    int ret;
+
+    /* Return if custom config is already loaded */
+    if (!config_is_empty())
+        return 0;
+
+    /* Load main config */
+    ret = config_load(DEFAULT_CONFIG_FILE);
+    if (ret) {
+        _E("Unable to load config: %d", ret);
+        return -1;
+    }
+
+    /* Load additional configs */
+    config_load_dir(USER_CONFIG_DIR);
+
+    return 0;
+}
+
+static gboolean signal_handler(GMainLoop *loop, const char *sig_name)
+{
+    _D("\nReceived %s, exit\n", sig_name);
+    g_main_loop_quit(loop);
+    return FALSE;
+}
+static gboolean sigterm_handler(gpointer data)
+{
+    return signal_handler(data, "SIGTERM");
+}
+
+static gboolean sigint_handler(gpointer data)
+{
+    return signal_handler(data, "SIGINT");
+}
+
+static int get_global_sampling_rate(double *sr)
+{
+    struct json_object *config, *obj;
+    int ret;
+
+    config = config_get();
+
+    ret = json_object_object_get_ex(config, "global", &obj);
+    if (!ret) {
+        _E("There is no 'global' section in config");
+        return -ENOENT;
+    }
+
+    ret = json_object_object_get_ex(obj, "sampling_rate", &obj);
+    if (!ret) {
+        _E("There is no 'sampling_rate' parameter in global config");
+        return -ENOENT;
+    }
+
+    *sr = json_object_get_double(obj);
+    if (*sr == 0) {
+        _E("Sampling rate should be of type double and greater than 0");
+        return -EINVAL;
+    }
+
+    return 0;
+}
+
+static gboolean run(gpointer data)
+{
+    struct list_head rd;
+    struct raw_data_item *rd_i, *next, *rd_global;
+    struct proc_info *process;
+    struct data_source *ds;
+    unsigned long long current_time;
+    // unsigned long long _t = time_now();
+    int ret, first_item = 1;
+
+    INIT_LIST_HEAD(&rd);
+
+    ret = raw_data_provider_get()->get_raw_data(&rd);
+    if (ret) {
+        _E("Unable to get raw data");
+        goto cleanup;
+    }
+
+    list_for_each_entry_safe(rd_i, next, &rd, node) {
+        if (first_item) {
+            rd_global = rd_i;
+            current_time = rd_global->v[0];
+
+            first_item = 0;
+            continue;
+        }
+
+        /* Get proc info */
+        process = process_get_by_pid(rd_i->v[0]);
+        if (!process) {
+            /* Create process info */
+            ret = process_create_info(rd_i->v[0], rd_i->v[1], &process);
+            if (ret) {
+                _E("Unable to create process info: %d", ret);
+                continue;
+            }
+        }
+
+        process->still_alive = 1;
+
+        /* End here if not monitoring the process */
+        if (!process->monitor)
+            continue;
+
+        if (process->sampling_rate != 0 &&
+            current_time - process->last_update >= USEC_PER_SEC / process->sampling_rate) {
+
+            /* Update process state */
+            process->last_update = current_time;
+            for (int i = 0; i < data_sources_get_n(); i++) {
+                ds = &(process->data_sources[i]);
+
+                ret = ds->update(ds, rd_global, rd_i);
+                if (ret) {
+                    _E("Update() failed in data source %s", ds->name);
+                    continue;
+                }
+            }
+
+            /* Check whether limits are exceeded */
+            process_check_limits(process);
+
+            if (process->print_current)
+                process_print_current_state(process);
+        }
+    }
+
+    /* Remove processes that were not present in raw data */
+    process_remove_deads();
+
+cleanup:
+    raw_data_cleanup(&rd);
+
+    // _D("Run took %lf s\n", (double) 1. * (time_now() - _t) / USEC_PER_SEC);
+    return TRUE;
+}
+
+static int setup(GMainLoop *loop)
+{
+    struct raw_data_provider *rdp = raw_data_provider_get();
+    double sr;
+    int ret;
+
+    /* Register signal handlers */
+    ret = g_unix_signal_add(SIGINT, sigint_handler, loop);
+    if (ret < 0) {
+        _E("Failed to register SIGINT handler: %d", ret);
+        return -1;
+    }
+
+    ret = g_unix_signal_add(SIGTERM, sigterm_handler, loop);
+    if (ret < 0) {
+        _E("Failed to register SIGTERM handler: %d", ret);
+        return -1;
+    }
+
+    /* Init raw data provider */
+    if (!rdp) {
+        _E("Unable to find any raw data providers");
+        return -1;
+    }
+
+    ret = rdp->init();
+    if (ret) {
+        _E("Unable to initialize %s raw data provider: %d", rdp->name, ret);
+        return -1;
+    }
+
+    /* Init other modules */
+    ret = aul_listener_init();
+    if (ret) {
+        _E("Unable to initialize aul listener: %d", ret);
+        return -1;
+    }
+
+    process_table_init();
+
+    /* Schedule periodic processing */
+    ret = get_global_sampling_rate(&sr);
+    if (ret)
+        return ret;
+
+    g_timeout_add(MSEC_PER_SEC / sr, run, NULL);
+    run(NULL);
+
+    return 0;
+}
+
+static void cleanup(void)
+{
+    struct raw_data_provider *rdp = raw_data_provider_get();
+
+    if (rdp)
+        rdp->cleanup();
+
+    process_cleanup_all();
+}
+
+int main(int argc, char *argv[])
+{
+    GMainLoop *loop = NULL;
+    int ret;
+
+    ret = parse_argv(argc, argv);
+    if (ret)
+        return -1;
+
+    ret = load_config();
+    if (ret)
+        return -1;
+
+    /* Create event loop */
+    loop = g_main_loop_new(NULL, FALSE);
+    if (!loop) {
+        _E("Failed to create an event loop");
+        return -1;
+    }
+
+    ret = setup(loop);
+    if (ret)
+        return -1;
+
+
+    /* Run main loop */
+    g_main_loop_run(loop);
+    g_main_loop_unref(loop);
+
+    cleanup();
+
+    return 0;
+}
\ No newline at end of file
diff --git a/src/utils.c b/src/utils.c
new file mode 100644 (file)
index 0000000..25cde1b
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <time.h>
+#include <limits.h>
+#include <errno.h>
+
+#include "log.h"
+#include "utils.h"
+
+
+unsigned long long time_now(void)
+{
+    struct timespec ts;
+
+    clock_gettime(CLOCK_BOOTTIME, &ts);
+    return ts.tv_sec * USEC_PER_SEC + ts.tv_nsec / NSEC_PER_USEC; // us
+}
+
+char *get_basename(char *path)
+{
+    char *base = strrchr(path, '/');
+    return base ? base+1 : path;
+}
+
+int pid_to_basename(int pid, char *basename)
+{
+    char path[PATH_MAX];
+    char link_name[NAME_MAX];
+    char *basename_ptr;
+    int basename_len;
+    int ret;
+
+    /* Assembly /proc/pid/exe */
+    ret = snprintf(link_name, sizeof(link_name), PROC_EXE_PATH, pid);
+    if (ret <= 0) {
+        return -1;
+    }
+
+    /* Readlink /proc/pid/exe */
+    ret = readlink(link_name, path, PATH_MAX);
+    if (ret <= 0) {
+        _E("readlink() failed, probably the process(%d) has just died: %d", pid, ret);
+        return -1;
+    }
+
+    path[ret] = 0;
+
+    /* Return basename */
+    basename_ptr = get_basename(path);
+    basename_len = strlen(basename_ptr);
+
+    strncpy(basename, basename_ptr, basename_len);
+    basename[basename_len] = 0;
+
+    return 0;
+}
+
+const char *smpl2str(struct sample_s *s)
+{
+    static char str[MAX_SAMPLE_STR];
+
+    if (s->type == ST_DOUBLE)
+        snprintf(str, MAX_SAMPLE_STR, "%2.3lf", s->value.d);
+    else if (s->type == ST_INT)
+        snprintf(str, MAX_SAMPLE_STR, "%d", s->value.i);
+    else if (s->type == ST_ULL)
+        snprintf(str, MAX_SAMPLE_STR, "%llu", s->value.ull);
+
+    return str;
+}
+
+int _json_object_object_merge(json_object *obj1, char *key, json_object *obj2_node)
+{
+       struct json_object *obj1_node = NULL;
+       int ret;
+
+       ret = json_object_object_get_ex(obj1, key, &obj1_node);
+
+       /* if there is no such object in obj1 - add it */
+       if (ret == 0)
+               goto json_add;
+
+       if (obj1_node == NULL) {
+               _E("Unable to get json object");
+               return -ENOMEM;
+       }
+
+       /* if there is, but obj1 or obj2 is not json_type_object - replace it */
+       if (!json_object_is_type(obj1_node, json_type_object) ||
+               !json_object_is_type(obj2_node, json_type_object)) {
+
+               json_object_object_del(obj1, key);
+               goto json_add;
+       }
+
+       /* if obj1 and obj2 is json_object_type */
+       json_object_object_foreach(obj2_node, k, v) {
+               ret = _json_object_object_merge(obj1_node, k, v);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+
+json_add:
+       json_object_get(obj2_node);
+       json_object_object_add(obj1, key, obj2_node);
+       return 0;
+}
diff --git a/src/utils.h b/src/utils.h
new file mode 100644 (file)
index 0000000..59d2470
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * This file is part of stability-monitor
+ * 
+ * Copyright © 2019 Samsung Electronics
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * 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 UTILS_H
+#define UTILS_H
+
+#include <stdint.h>
+#include <json-c/json.h>
+
+#include "sample.h"
+
+#define PROC_EXE_PATH "/proc/%d/exe"
+
+#define MSEC_PER_SEC   1000ULL
+#define USEC_PER_MSEC  1000ULL
+#define NSEC_PER_USEC  1000ULL
+#define NSEC_PER_MSEC  1000000ULL
+#define USEC_PER_SEC   1000000ULL
+#define NSEC_PER_SEC   1000000000ULL
+#define FSEC_PER_SEC   1000000000000000ULL
+
+#define MAX_SAMPLE_STR 20
+
+
+unsigned long long time_now(void);
+char *get_basename(char *path);
+int pid_to_basename(int pid, char *basename);
+const char *smpl2str(struct sample_s *s);
+
+int _json_object_object_merge(json_object *obj1, char *key, json_object *obj2_node);
+
+#endif /* UTILS_H */