--- /dev/null
+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
--- /dev/null
+{
+ "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,
+ },
+}
--- /dev/null
+{
+ "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,
+ },
+}
--- /dev/null
+[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
--- /dev/null
+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
--- /dev/null
+/*
+ * 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");
--- /dev/null
+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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+#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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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 */