AC_SUBST(LIBXML2_CFLAGS)
AC_SUBST(LIBXML2_LIBS)
+# Check if system-monitor (plugin) support should be enabled.
+AC_ARG_ENABLE(system-monitor,
+ [ --enable-system-monitor enable system-monitor support],
+ [enable_sysmon=$enableval], [enable_sysmon=no])
+
+if test "$enable_sysmon" = "yes"; then
+ AC_MSG_NOTICE([System-monitor support is enabled.])
+ AC_DEFINE([SYSMON_ENABLED], 1, [Enable system-monitor support ?])
+else
+ AC_MSG_NOTICE([System-monitor support is disabled.])
+fi
+
+AM_CONDITIONAL(SYSMON_ENABLED, [test "$enable_sysmon" = "yes"])
+AC_SUBST(SYSMON_ENABLED)
+
+
# Check if dlog support was enabled.
AC_ARG_ENABLE(dlog,
[ --enable-dlog enable dlog support],
AM_CONDITIONAL(DISABLED_PLUGIN_RESOURCE_ASM, [check_if_disabled resource-asm])
AM_CONDITIONAL(DISABLED_PLUGIN_SYSTEMCTL, [test $enable_systemctl != yes])
+AM_CONDITIONAL(DISABLED_PLUGIN_SYSMON, [test $enable_sysmon != yes])
AM_CONDITIONAL(BUILTIN_PLUGIN_TEST, [check_if_internal test])
AM_CONDITIONAL(BUILTIN_PLUGIN_DBUS, [check_if_internal dbus])
AM_CONDITIONAL(BUILTIN_PLUGIN_TELEPHONY, [check_if_internal telephony])
AM_CONDITIONAL(BUILTIN_PLUGIN_RESOURCE_ASM, [check_if_internal resource-asm])
AM_CONDITIONAL(BUILTIN_PLUGIN_SYSTEMCTL, [check_if_internal system-controller])
+AM_CONDITIONAL(BUILTIN_PLUGIN_SYSMON, [check_if_internal system-monitor])
# Check for Check (unit test framework).
PKG_CHECK_MODULES(CHECK,
echo "systemd support: $enable_systemd"
echo "Telephony support: $enable_telephony"
echo "System controller support: $enable_systemctl"
+echo "System monitor support: $enable_sysmon"
echo "AIL support: $enable_ail"
echo "Plugins:"
echo " - linked-in:"
generate-linker-scripts: linker-script.system-controller
endif
+# system-monitor
+if SYSMON_ENABLED
+SYSMON_PLUGIN_SOURCES = \
+ plugins/system-monitor/plugin-system-monitor.c \
+ plugins/system-monitor/system-monitor.c \
+ plugins/system-monitor/cpu-watch.c \
+ plugins/system-monitor/cpu-sampler.c \
+ plugins/system-monitor/mem-watch.c \
+ plugins/system-monitor/mem-sampler.c
+
+
+SYSMON_PLUGIN_CFLAGS = -I$(top_builddir)/src/plugins/system-monitor
+SYSTEMCTL_PLUGIN_LIBS = -lm
+
+SYSMON_PLUGIN_LOADER = linkedin-system-monitor-loader.c
+
+if !DISABLED_PLUGIN_SYSMON
+if BUILTIN_PLUGIN_SYSMON
+LINKEDIN_PLUGINS += libmurphy-plugin-system-monitor.la
+lib_LTLIBRARIES += libmurphy-plugin-system-monitor.la
+SYSMON_PLUGIN_CFLAGS += $(BUILTIN_CFLAGS)
+
+
+libmurphy_plugin_system_monitor_ladir = \
+ $(includedir)/murphy/system-monitor
+
+libmurphy_plugin_system_monitor_la_SOURCES = \
+ $(SYSMON_PLUGIN_SOURCES) \
+ $(SYSMON_PLUGIN_LOADER)
+
+libmurphy_plugin_system_monitor_la_CFLAGS = \
+ $(SYSMON_PLUGIN_CFLAGS) \
+ $(AM_CFLAGS)
+
+libmurphy_plugin_system_monitor_la_LDFLAGS = \
+ -Wl,-version-script=linker-script.system-monitor \
+ -version-info @MURPHY_VERSION_INFO@
+
+libmurphy_plugin_system_monitor_la_LIBADD = \
+ libmurphy-core.la \
+ libmurphy-common.la \
+ $(SYSMON_PLUGIN_LIBS)
+
+libmurphy_plugin_system_monitor_la_DEPENDENCIES = \
+ linker-script.system-monitor \
+ $(filter %.la, $(libmurphy_plugin_system_monitor_la_LIBADD))
+else
+plugin_system_monitor_la_SOURCES = $(SYSMON_PLUGIN_SOURCES)
+plugin_system_monitor_la_CFLAGS = $(SYSMON_PLUGIN_CFLAGS) \
+ $(MURPHY_CFLAGS) $(AM_CFLAGS)
+plugin_system_monitor_la_LDFLAGS = -module -avoid-version
+plugin_system_monitor_la_LIBADD = $(SYSMON_PLUGIN_LIBS)
+plugin_LTLIBRARIES += plugin-system-monitor.la
+endif
+endif
+
+# linkedin system-monitor plugin linker script generation
+linker-script.system-monitor: $(SYSMON_PLUGIN_LOADER:%.c=%.h)
+ $(QUIET_GEN)$(top_builddir)/build-aux/gen-linker-script -q \
+ -c "$(libmurphy_plugin_system_monitor_la_CFLAGS)" -o $@ $^
+
+clean-linker-script::
+ -rm -f linker-script.system-monitor
+
+generate-linker-scripts: linker-script.system-monitor
+endif
+
# telephony plugin
TELEPHONY_PLUGIN_SOURCES = plugins/plugin-telephony.c \
plugins/telephony/telephony.c \
--- /dev/null
+ifneq ($(strip $(MAKECMDGOALS)),)
+%:
+ $(MAKE) -C .. $(MAKECMDGOALS)
+else
+all:
+ $(MAKE) -C .. all
+endif
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/debug.h>
+
+#include "cpu-sampler.h"
+
+/*
+ * CPU 'ticks' (entry for a single CPU in /proc/stat)
+ */
+
+typedef struct { /* time spent in various states */
+ uint64_t user; /* in user mode */
+ uint64_t nice; /* - || - with low priority */
+ uint64_t system; /* in system mode */
+ uint64_t idle; /* in the idle task */
+ uint64_t iowait; /* waiting for I/O completion */
+ uint64_t irq; /* servicing interrupts */
+ uint64_t softirq; /* servicing softirqs */
+ uint64_t steal; /* in other guests (if virtualized) */
+ uint64_t guest; /* running guests */
+ uint64_t guest_nice; /* - || - with low priority */
+} ticks_t;
+
+
+/*
+ * calculated CPU load
+ */
+
+typedef struct {
+ ticks_t samples[2]; /* tick sample buffer */
+ int current; /* current write index */
+} cpu_state_t;
+
+
+static int statfd = -1; /* fd for /proc/stat */
+static cpu_t *cpus; /* known CPUs */
+static int ncpu; /* number of CPUs */
+static uint32_t cpu_mask; /* mask of monitored CPUs */
+static cpu_state_t *states; /* CPU load/state tracking */
+
+
+static int enumerate_cpus(void)
+{
+ cpu_t *cpu;
+ char buf[4096], *p, *e;
+ int i, len;
+
+ if (statfd < 0) {
+ if ((statfd = open("/proc/stat", O_RDONLY)) < 0)
+ return -1;
+
+ fcntl(statfd, F_SETFD, FD_CLOEXEC);
+ }
+
+ if ((len = read(statfd, buf, sizeof(buf) - 1)) <= 0)
+ return -1;
+ else
+ buf[len] = '\0';
+
+ p = buf;
+ while (p[0] == 'c' && p[1] == 'p' && p[2] == 'u') {
+ for (e = p; *e != ' ' && *e; e++)
+ ;
+
+ if (!*e) {
+ errno = EILSEQ;
+ goto fail;
+ }
+
+ if (mrp_reallocz(cpus, ncpu, ncpu + 1) == NULL)
+ goto fail;
+ else
+ cpu = cpus + ncpu;
+
+ if ((cpu->name = mrp_datadup(p, e - p + 1)) == NULL)
+ goto fail;
+
+ cpu->name[e-p] = '\0';
+ cpu->id = ncpu++;
+
+ mrp_debug("CPU #%d is '%s'", cpu->id, cpu->name);
+
+ if ((p = strchr(p, '\n')) != NULL)
+ p++;
+ else
+ break;
+ }
+
+ if ((states = mrp_allocz(ncpu * sizeof(*states))) == NULL)
+ goto fail;
+
+ for (i = 0; i < ncpu; i++)
+ states[i].current = -1;
+
+ cpu_set_cpu_mask(-1);
+
+ return ncpu;
+
+ fail:
+ if (cpus != NULL) {
+ for (i = 0, cpu = cpus; i < ncpu; cpu++, i++)
+ mrp_free(cpu->name);
+
+ mrp_free(cpus);
+ cpus = NULL;
+ ncpu = 0;
+ }
+
+ return -1;
+}
+
+
+int cpu_get_id(const char *name)
+{
+ cpu_t *cpu;
+ int i;
+
+ if (cpus == NULL && enumerate_cpus() < 0)
+ return -1;
+
+ for (i = 0, cpu = cpus; i < ncpu; i++, cpu++)
+ if (!strcmp(cpu->name, name))
+ return cpu->id;
+
+ return -1;
+}
+
+
+const char *cpu_get_name(int id)
+{
+ cpu_t *cpu;
+ int i;
+
+ if (cpus == NULL && enumerate_cpus() < 0)
+ return NULL;
+
+ for (i = 0, cpu = cpus; i < ncpu; i++, cpu++)
+ if (cpu->id == id)
+ return cpu->name;
+
+ return NULL;
+}
+
+
+int cpu_get_cpus(const cpu_t **cpusptr)
+{
+ if (cpus == NULL && enumerate_cpus() < 0)
+ return -1;
+
+ if (cpusptr != NULL)
+ *cpusptr = cpus;
+
+ return ncpu;
+}
+
+
+int cpu_set_cpu_mask(uint32_t mask)
+{
+ if (cpus == NULL && enumerate_cpus() < 0)
+ return -1;
+
+ cpu_mask = (mask & ((1 << ncpu) - 1));
+
+ return 0;
+}
+
+
+static int sample_load(void)
+{
+ char buf[4096], *p;
+ int len;
+ uint32_t cmask, cid;
+ ticks_t *t;
+
+ lseek(statfd, 0, SEEK_SET);
+
+ if ((len = read(statfd, buf, sizeof(buf) - 1)) < 0)
+ return -1;
+
+ buf[len] = '\0';
+ p = buf;
+
+ cmask = cpu_mask;
+ cid = 0;
+
+ while (cmask) {
+ if (!(cmask & (1 << cid)))
+ goto next_cpu;
+
+ if (p[0] != 'c' || p[1] != 'p' || p[2] != 'u')
+ goto parse_error;
+
+ p += 3;
+ while ('0' <= *p && *p <= '9')
+ p++;
+
+ if (*p != ' ')
+ goto parse_error;
+ else
+ p++;
+
+ t = states[cid].samples + (states[cid].current & 0x1 ? 0 : 1);
+
+#define PARSE_FIELD(_f, _next) do { \
+ t->_f = strtoll(p, &p, 10); \
+ \
+ if (*p != _next) \
+ goto parse_error; \
+ } while (0)
+
+ PARSE_FIELD(user , ' ' );
+ PARSE_FIELD(nice , ' ' );
+ PARSE_FIELD(system , ' ' );
+ PARSE_FIELD(idle , ' ' );
+ PARSE_FIELD(iowait , ' ' );
+ PARSE_FIELD(irq , ' ' );
+ PARSE_FIELD(softirq , ' ' );
+ PARSE_FIELD(steal , ' ' );
+ PARSE_FIELD(guest , ' ' );
+ PARSE_FIELD(guest_nice, '\n');
+
+#undef PARSE_FIELD
+
+ cmask &= ~(1 << cid);
+
+ if (MRP_UNLIKELY(states[cid].current == -1)) {
+ states[cid].samples[1] = states[cid].samples[0];
+ states[cid].current = 0;
+ }
+ else
+ states[cid].current = !states[cid].current;
+
+ next_cpu:
+ cid++;
+ if (*p == '\n' || (p = strchr(p, '\n')) != NULL)
+ p++;
+ else {
+ if (cmask != 0) {
+ errno = ENODATA;
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+
+ parse_error:
+ errno = EILSEQ;
+ return -1;
+}
+
+
+int cpu_sample_load(void)
+{
+ return sample_load();
+}
+
+
+int cpu_get_sample(int cpu, cpu_sample_t sample)
+{
+ cpu_state_t *state;
+ uint64_t usr, nic, sys, idl, iow, irq, sirq, stl, gst, gstnc, total;
+
+ if (cpus == NULL && enumerate_cpus() < 0)
+ return -1;
+
+ if (cpu >= ncpu || !(cpu_mask & (1 << cpu))) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (sample < 0 || sample > CPU_SAMPLE_GUEST_LOAD) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ state = states + cpu;
+
+#define DIFF(_fld) \
+ state->samples[ state->current]._fld - \
+ state->samples[!state->current]._fld
+ total = (usr = DIFF(user));
+ total += (nic = DIFF(nice));
+ total += (sys = DIFF(system));
+ total += (idl = DIFF(idle));
+ total += (iow = DIFF(iowait));
+ total += (irq = DIFF(irq));
+ total += (sirq = DIFF(softirq));
+ total += (stl = DIFF(steal));
+ total += (gst = DIFF(guest));
+ total += (gstnc = DIFF(guest_nice));
+#undef DIFF
+
+ if (total == 0) {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ switch (sample) {
+#define PCNT(v) ((int)floor(0.5 + 100.0 * (v) / total))
+ case CPU_SAMPLE_USER: return PCNT(usr);
+ case CPU_SAMPLE_NICE: return PCNT(nic);
+ case CPU_SAMPLE_SYSTEM: return PCNT(sys);
+ case CPU_SAMPLE_IDLE: return PCNT(idl);
+ case CPU_SAMPLE_IOWAIT: return PCNT(iow);
+ case CPU_SAMPLE_IRQ: return PCNT(irq);
+ case CPU_SAMPLE_SOFTIRQ: return PCNT(sirq);
+ case CPU_SAMPLE_STEAL: return PCNT(stl);
+ case CPU_SAMPLE_GUEST: return PCNT(gst);
+ case CPU_SAMPLE_GUEST_NICE: return PCNT(gstnc);
+ case CPU_SAMPLE_LOAD: return PCNT(usr + nic + sys + iow);
+ case CPU_SAMPLE_INTERRUPT: return PCNT(irq + sirq);
+ case CPU_SAMPLE_GUEST_LOAD: return PCNT(gst + gstnc);
+ default: return -1;
+#undef PCNT
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SYSTEM_MONITOR_CPU_SAMPLER_H__
+#define __MURPHY_SYSTEM_MONITOR_CPU_SAMPLER_H__
+
+/*
+ * type of CPU load sampling
+ */
+
+typedef enum {
+ CPU_SAMPLE_INVALID = -1, /* invalid type */
+ /* these correspond directly to entries in /proc/stat */
+ CPU_SAMPLE_USER, /* time in user mode */
+ CPU_SAMPLE_NICE, /* - || - with low priority */
+ CPU_SAMPLE_SYSTEM, /* time in system mode */
+ CPU_SAMPLE_IDLE, /* time in the idle task */
+ CPU_SAMPLE_IOWAIT, /* time waiting for I/O completion */
+ CPU_SAMPLE_IRQ, /* time serving interrupts */
+ CPU_SAMPLE_SOFTIRQ, /* time serving softirqs */
+ CPU_SAMPLE_STEAL, /* time in other guests */
+ CPU_SAMPLE_GUEST, /* time running guests */
+ CPU_SAMPLE_GUEST_NICE, /* -||- with log priority */
+ /* these are calculated from the above */
+ CPU_SAMPLE_LOAD, /* user + nice + system + iowait */
+ CPU_SAMPLE_INTERRUPT, /* irq + softirq */
+ CPU_SAMPLE_GUEST_LOAD, /* guest + guest_nice */
+ /* these are not coming from /proc/stat at all */
+ CPU_SAMPLE_CGROUP, /* time running tasks in a cgroup */
+} cpu_sample_t;
+
+
+/*
+ * a CPU name and identifier
+ */
+
+typedef struct {
+ char *name; /* CPU name in /proc/stat */
+ int id; /* our identifier for it */
+} cpu_t;
+
+
+/*
+ * sampled CPU load
+ */
+
+typedef struct {
+ int user;
+ int nice;
+ int system;
+ int idle;
+ int iowait;
+ int irq;
+ int sofirq;
+ int steal;
+ int guest;
+ int guest_nice;
+ int load;
+ int interrupt;
+ int guest_load;
+#if 0
+ int *cgroup;
+#endif
+} cpu_load_t;
+
+
+/** Get the number, names and identifiers of all CPUs in the system. */
+int cpu_get_cpus(const cpu_t **cpus);
+
+/** Get the identifier for the given CPU name. */
+int cpu_get_id(const char *cpu);
+
+/** Get the CPU name for the given CPU id. */
+const char *cpu_get_name(int id);
+
+/** Set the mask of CPU ids to monitor. */
+int cpu_set_cpu_mask(uint32_t cpu_mask);
+
+/** Take another sample of the (various) CPU load(s) we track. */
+int cpu_sample_load(void);
+
+/** Get the last load for the given CPU and sample type. */
+int cpu_get_sample(int cpu, cpu_sample_t sample);
+
+#endif /* __MURPHY_SYSTEM_MONITOR_CPU_SAMPLER_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+#include "cpu-watch.h"
+
+/*
+ * CPU watch object
+ */
+
+#define CPU_WATCH_LUA_CLASS MRP_LUA_CLASS(cpu_watch, lua)
+#define RO MRP_LUA_CLASS_READONLY
+#define NOINIT MRP_LUA_CLASS_NOINIT
+#define NOFLAGS MRP_LUA_CLASS_NOFLAGS
+#define SETGET cpu_watch_setmember, cpu_watch_getmember
+#define setmember cpu_watch_setmember
+#define getmember cpu_watch_getmember
+
+static int cpu_watch_no_constructor(lua_State *L);
+static void cpu_watch_destroy(void *data);
+static void cpu_watch_changed(void *data, lua_State *L, int member);
+static ssize_t cpu_watch_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data);
+static int cpu_watch_setmember(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v);
+static int cpu_watch_getmember(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v);
+static int cpu_watch_delete(lua_State *L);
+
+MRP_LUA_METHOD_LIST_TABLE(cpu_watch_methods,
+ MRP_LUA_METHOD_CONSTRUCTOR(cpu_watch_no_constructor)
+ MRP_LUA_METHOD(delete, cpu_watch_delete));
+
+MRP_LUA_METHOD_LIST_TABLE(cpu_watch_overrides,
+ MRP_LUA_OVERRIDE_CALL(cpu_watch_no_constructor));
+
+MRP_LUA_MEMBER_LIST_TABLE(cpu_watch_members,
+ MRP_LUA_CLASS_STRING ("cpu" , 0, setmember, getmember, RO )
+ MRP_LUA_CLASS_STRING ("sample" , 0, setmember, getmember, RO )
+ MRP_LUA_CLASS_ANY ("limits" , 0, setmember, getmember, RO )
+ MRP_LUA_CLASS_INTEGER("polling" , 0, setmember, getmember, RO|NOINIT )
+ MRP_LUA_CLASS_INTEGER("window" , 0, setmember, getmember, NOFLAGS)
+ MRP_LUA_CLASS_INTEGER("raw" , 0, setmember, getmember, RO|NOINIT )
+ MRP_LUA_CLASS_INTEGER("value" , 0, setmember, getmember, RO|NOINIT )
+ MRP_LUA_CLASS_STRING ("current" , 0, setmember, getmember, RO|NOINIT )
+ MRP_LUA_CLASS_STRING ("previous", 0, setmember, getmember, RO|NOINIT )
+ MRP_LUA_CLASS_ANY ("notify" , 0, setmember, getmember, NOFLAGS)
+ MRP_LUA_CLASS_ANY ("update" , 0, setmember, getmember, NOFLAGS));
+
+MRP_LUA_DEFINE_CLASS(cpu_watch, lua, cpu_watch_lua_t, cpu_watch_destroy,
+ cpu_watch_methods, cpu_watch_overrides, cpu_watch_members, NULL,
+ cpu_watch_changed, cpu_watch_tostring , NULL,
+ MRP_LUA_CLASS_EXTENSIBLE | MRP_LUA_CLASS_PRIVREFS);
+
+MRP_LUA_CLASS_CHECKER(cpu_watch_lua_t, cpu_watch_lua, CPU_WATCH_LUA_CLASS);
+
+typedef enum {
+ CPU_WATCH_MEMBER_CPU,
+ CPU_WATCH_MEMBER_SAMPLE,
+ CPU_WATCH_MEMBER_LIMITS,
+ CPU_WATCH_MEMBER_POLLING,
+ CPU_WATCH_MEMBER_WINDOW,
+ CPU_WATCH_MEMBER_RAW,
+ CPU_WATCH_MEMBER_VALUE,
+ CPU_WATCH_MEMBER_CURRENT,
+ CPU_WATCH_MEMBER_PREVIOUS,
+ CPU_WATCH_MEMBER_NOTIFY,
+ CPU_WATCH_MEMBER_UPDATE
+} cpu_watch_member_t;
+
+static inline int get_cpu_id(const char *name);
+static inline const char *get_cpu_name(int id);
+static inline cpu_sample_t get_sample_id(const char *name);
+static inline const char *get_sample_name(cpu_sample_t id);
+static inline void recalc_smoothing(cpu_watch_lua_t *w);
+static int setup_limits(cpu_watch_lua_t *w, lua_State *L, int limref);
+static void cleanup_limits(cpu_watch_lua_t *w, lua_State *L,
+ cpu_limit_t *limits, int n, int limref);
+
+
+static int cpu_watch_no_constructor(lua_State *L)
+{
+ return luaL_error(L, "trying create a CPU watch via constructor.");
+}
+
+
+cpu_watch_lua_t *cpu_watch_create(sysmon_lua_t *sm, int polling, lua_State *L)
+{
+ cpu_watch_lua_t *w;
+ char e[256];
+
+ luaL_checktype(L, 2, LUA_TTABLE);
+
+ w = (cpu_watch_lua_t *)mrp_lua_create_object(L, CPU_WATCH_LUA_CLASS,
+ NULL, 0);
+
+ mrp_list_init(&w->hook);
+ w->sysmon = sm;
+ w->polling = polling;
+ w->limref = LUA_NOREF;
+ w->window = CPU_WATCH_WINDOW;
+
+ if (mrp_lua_init_members(w, L, 2, e, sizeof(e)) != 1) {
+ luaL_error(L, "failed to initialize CPU watch (error: %s)",
+ *e ? e : "<unknown error>");
+ return NULL;
+ }
+
+ if (w->sample == CPU_SAMPLE_IDLE)
+ w->value.S = 100;
+ else
+ w->value.S = 0;
+
+ recalc_smoothing(w);
+
+ return w;
+}
+
+
+static void cpu_watch_destroy(void *data)
+{
+ MRP_UNUSED(data);
+
+ mrp_debug("CPU watch %p destroyed", data);
+
+ return;
+}
+
+
+static int cpu_watch_delete(lua_State *L)
+{
+ cpu_watch_lua_t *w = cpu_watch_lua_check(L, 1);
+
+ mrp_list_delete(&w->hook);
+ mrp_list_init(&w->hook);
+
+ sysmon_del_cpu_watch(w->sysmon, w);
+
+ cleanup_limits(w, L, w->limits, -1, w->limref);
+ w->limits = NULL;
+ w->limref = LUA_NOREF;
+
+ mrp_funcbridge_unref(L, w->update);
+ mrp_funcbridge_unref(L, w->notify);
+ w->update = NULL;
+ w->notify = NULL;
+
+ return 0;
+}
+
+
+static void cpu_watch_changed(void *data, lua_State *L, int member)
+{
+ MRP_UNUSED(data);
+ MRP_UNUSED(L);
+ MRP_UNUSED(member);
+}
+
+
+static int cpu_watch_setmember(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v)
+{
+ cpu_watch_lua_t *w = (cpu_watch_lua_t *)data;
+ mrp_funcbridge_t *f, **fptr;
+
+ switch (member) {
+ case CPU_WATCH_MEMBER_CPU:
+ if ((w->cpu = get_cpu_id(v->str)) >= 0)
+ return 1;
+ else
+ mrp_log_error("Can't create watch for unknown CPU '%s'.", v->str);
+ return 0;
+
+ case CPU_WATCH_MEMBER_SAMPLE:
+ if ((w->sample = get_sample_id(v->str)) >= 0)
+ return 1;
+ else
+ mrp_log_error("Can't sample CPU for unknown type '%s'.", v->str);
+ return 0;
+
+ case CPU_WATCH_MEMBER_WINDOW:
+ cpu_watch_set_window(w, v->s32);
+ return 1;
+
+ case CPU_WATCH_MEMBER_NOTIFY: fptr = &w->notify; goto set_bridge;
+ case CPU_WATCH_MEMBER_UPDATE: fptr = &w->update;
+ set_bridge:
+ if (!mrp_lua_object_deref_value(w, L, v->any, false))
+ return 0;
+ switch (lua_type(L, -1)) {
+ case LUA_TFUNCTION:
+ f = *fptr = mrp_funcbridge_create_luafunc(L, -1);
+ break;
+ default:
+ f = NULL;
+ break;
+ }
+ lua_pop(L, 1);
+ mrp_lua_object_unref_value(w, L, v->any);
+
+ return (f != NULL ? 1 : 0);
+
+ case CPU_WATCH_MEMBER_LIMITS:
+ return setup_limits(w, L, v->any);
+
+ default:
+ mrp_log_error("Trying to set read-only CPU watch member #%d.", member);
+ return 0;
+ }
+}
+
+
+static int cpu_watch_getmember(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v)
+{
+ cpu_watch_lua_t *w = (cpu_watch_lua_t *)data;
+
+ MRP_UNUSED(L);
+
+ switch (member) {
+ case CPU_WATCH_MEMBER_CPU:
+ v->str = get_cpu_name(w->cpu);
+ return 1;
+
+ case CPU_WATCH_MEMBER_SAMPLE:
+ v->str = get_sample_name(w->sample);
+ return 1;
+
+ case CPU_WATCH_MEMBER_LIMITS:
+ v->any = w->limref;
+ return 1;
+
+ case CPU_WATCH_MEMBER_WINDOW:
+ v->s32 = w->window;
+ return 1;
+
+ case CPU_WATCH_MEMBER_POLLING:
+ v->s32 = w->polling;
+ return 1;
+
+ case CPU_WATCH_MEMBER_VALUE:
+ v->s32 = w->value.S;
+ return 1;
+
+ case CPU_WATCH_MEMBER_RAW:
+ v->s32 = w->value.sample;
+ return 1;
+
+ case CPU_WATCH_MEMBER_CURRENT:
+ v->str = w->curr ? w->curr->label : "<unknown limit>";
+ return 1;
+
+ case CPU_WATCH_MEMBER_PREVIOUS:
+ v->str = w->prev ? w->prev->label : "<unknown limit>";
+ return 1;
+
+ default:
+ v->any = LUA_REFNIL;
+ return 1;
+ }
+}
+
+
+static ssize_t cpu_watch_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data)
+{
+ cpu_watch_lua_t *w = (cpu_watch_lua_t *)data;
+
+ MRP_UNUSED(L);
+
+ switch (mode & MRP_LUA_TOSTR_MODEMASK) {
+ case MRP_LUA_TOSTR_LUA:
+ default:
+ return snprintf(buf, size, "{CPU watch %s/%s @ %d sec window}",
+ get_cpu_name(w->cpu), get_sample_name(w->sample),
+ w->window / 1000);
+ }
+
+}
+
+
+static inline double calculate_alpha(double L, double n)
+{
+ double x, diff, min_x, min_diff;
+
+ min_diff = 1.0;
+ min_x = 0.1;
+
+ for (x = 0.01; x < 1.0; x += 0.001) {
+ diff = pow(1 - x, n - 1) * x - L;
+ if (fabs(diff) < min_diff) {
+ min_x = x;
+ min_diff = fabs(diff);
+ }
+ }
+
+ return min_x;
+}
+
+
+static inline void recalc_smoothing(cpu_watch_lua_t *w)
+{
+ double alpha /*= 1 - exp(- (1.0 * w->polling) / (1.0 * w->window))*/;
+ double n;
+
+ if (w->window > w->polling) {
+ n = (1.0 * w->window) / (1.0 * w->polling);
+ alpha = calculate_alpha(0.0005, n);
+ }
+ else
+ alpha = 1;
+
+ ewma_init(&w->value, alpha, w->value.S);
+}
+
+
+void cpu_watch_set_polling(cpu_watch_lua_t *w, int polling)
+{
+ if (w->polling == polling)
+ return;
+
+ w->polling = polling;
+ recalc_smoothing(w);
+}
+
+
+void cpu_watch_set_window(cpu_watch_lua_t *w, int window)
+{
+ if (w->window == window)
+ return;
+
+ w->window = window;
+ recalc_smoothing(w);
+}
+
+
+int cpu_watch_update(cpu_watch_lua_t *w, lua_State *L)
+{
+ int sample = cpu_get_sample(w->cpu, w->sample);
+ int change;
+
+ if (sample < 0)
+ return FALSE;
+
+
+ if (w->update == NULL) {
+ double value = ewma_add(&w->value, sample);
+ cpu_limit_t *l;
+
+ mrp_debug("%s/%s sample=%d, estimate=%.2f", get_cpu_name(w->cpu),
+ get_sample_name(w->sample), sample, value);
+
+ change = FALSE;
+ for (l = w->limits; l->label != NULL; l++) {
+ if (value <= l->limit) {
+ if (w->curr != l) {
+ w->prev = w->curr;
+ w->curr = l;
+
+ change = TRUE;
+ }
+
+ break;
+ }
+ }
+ }
+ else {
+ mrp_funcbridge_value_t args[2], rv;
+ char rt;
+
+ mrp_debug("%s/%s sample=%d", get_cpu_name(w->cpu),
+ get_sample_name(w->sample), sample);
+
+ args[0].pointer = w;
+ args[1].integer = sample;
+
+ if (!mrp_funcbridge_call_from_c(L, w->update, "Od", &args[0], &rt,&rv)) {
+ mrp_log_error("Failed to invoke CPU watch update handler (%s).",
+ rv.string ? rv.string : "<unknown error>");
+ mrp_free((char *)rv.string);
+ change = FALSE;
+ }
+ else
+ change = ((rt == MRP_FUNCBRIDGE_BOOLEAN && rv.boolean) ||
+ (rt == MRP_FUNCBRIDGE_INTEGER && rv.integer));
+ }
+
+ return change;
+}
+
+
+void cpu_watch_notify(cpu_watch_lua_t *w, lua_State *L)
+{
+ mrp_funcbridge_value_t args[3], rv;
+ char rt;
+
+ MRP_UNUSED(L);
+
+ mrp_debug("CPU watch %s/%s: %s -> %s",
+ get_cpu_name(w->cpu), get_sample_name(w->sample),
+ w->prev ? w->prev->label : "<unknown>",
+ w->curr ? w->curr->label : "<unknown>");
+
+ if (w->notify == NULL)
+ return;
+
+ args[0].pointer = w;
+ args[1].string = w->prev ? w->prev->label : "<unknown>";
+ args[2].string = w->curr ? w->curr->label : "<unknown>";
+
+ if (!mrp_funcbridge_call_from_c(L, w->notify, "Oss", &args[0], &rt, &rv)) {
+ mrp_log_error("Failed to notify CPU watch %s/%s (%s).",
+ get_cpu_name(w->cpu), get_sample_name(w->sample),
+ rv.string ? rv.string : "<unknown error>");
+ mrp_free((char *)rv.string);
+ }
+}
+
+
+static inline int get_cpu_id(const char *name)
+{
+ return cpu_get_id(name);
+}
+
+
+static inline const char *get_cpu_name(int id)
+{
+ const char *name = cpu_get_name(id);
+
+ return name ? name : "<invalid CPU id>";
+}
+
+
+static inline cpu_sample_t get_sample_id(const char *name)
+{
+#define MAP(_name, _id) if (!strcmp(name, _name)) return _id
+ MAP("user" , CPU_SAMPLE_USER );
+ MAP("nice" , CPU_SAMPLE_NICE );
+ MAP("system" , CPU_SAMPLE_SYSTEM );
+ MAP("idle" , CPU_SAMPLE_IDLE );
+ MAP("iowait" , CPU_SAMPLE_IOWAIT );
+ MAP("irq" , CPU_SAMPLE_IRQ );
+ MAP("softirq" , CPU_SAMPLE_SOFTIRQ );
+ MAP("steal" , CPU_SAMPLE_STEAL );
+ MAP("guest" , CPU_SAMPLE_GUEST );
+ MAP("guest_nice", CPU_SAMPLE_GUEST_NICE);
+ MAP("load" , CPU_SAMPLE_LOAD );
+ MAP("interrupt" , CPU_SAMPLE_INTERRUPT );
+ MAP("guest_load", CPU_SAMPLE_GUEST_LOAD);
+#undef MAP
+
+ return CPU_SAMPLE_INVALID;
+}
+
+
+static inline const char *get_sample_name(cpu_sample_t id)
+{
+ const char *names[] = {
+#define MAP(_name, _id) [_id] = _name
+ MAP("user" , CPU_SAMPLE_USER ),
+ MAP("nice" , CPU_SAMPLE_NICE ),
+ MAP("system" , CPU_SAMPLE_SYSTEM ),
+ MAP("idle" , CPU_SAMPLE_IDLE ),
+ MAP("iowait" , CPU_SAMPLE_IOWAIT ),
+ MAP("irq" , CPU_SAMPLE_IRQ ),
+ MAP("softirq" , CPU_SAMPLE_SOFTIRQ ),
+ MAP("steal" , CPU_SAMPLE_STEAL ),
+ MAP("guest" , CPU_SAMPLE_GUEST ),
+ MAP("guest_nice", CPU_SAMPLE_GUEST_NICE),
+ MAP("load" , CPU_SAMPLE_LOAD ),
+ MAP("interrupt" , CPU_SAMPLE_INTERRUPT ),
+ MAP("guest_load", CPU_SAMPLE_GUEST_LOAD),
+#undef MAP
+ };
+
+ if (CPU_SAMPLE_USER <= id && id <= CPU_SAMPLE_GUEST_LOAD)
+ return names[id];
+ else
+ return "<invalid CPU sample type>";
+}
+
+
+static int cmp_limits(const void *ptr1, const void *ptr2)
+{
+ cpu_limit_t *l1 = (cpu_limit_t *)ptr1;
+ cpu_limit_t *l2 = (cpu_limit_t *)ptr2;
+
+ return l1->limit - l2->limit;
+}
+
+
+static int get_limit(lua_State *L, int idx, cpu_limit_t *l)
+{
+ if (lua_type(L, idx) != LUA_TTABLE)
+ return -1;
+
+ lua_getfield(L, idx, "label");
+
+ if (lua_type(L, -1) == LUA_TSTRING)
+ l->label = (char *)lua_tostring(L, -1);
+ else
+ l->label = NULL;
+
+ lua_pop(L, 1);
+
+ if (l->label == NULL)
+ return -1;
+
+ lua_getfield(L, idx, "limit");
+
+ switch (lua_type(L, -1)) {
+ case LUA_TNUMBER: l->limit = lua_tonumber(L, -1); break;
+ case LUA_TNIL: l->limit = 100; break;
+ default: l->limit = -1; break;
+ }
+
+ lua_pop(L, 1);
+
+ if (l->limit >= 0)
+ return 0;
+ else
+ return -1;
+}
+
+
+static int setup_limits(cpu_watch_lua_t *w, lua_State *L, int limref)
+{
+ int top = lua_gettop(L);
+ cpu_limit_t *limits, l;
+ int nlimit;
+ const char *kname;
+ int ktype, i;
+ size_t klen;
+
+ if (!mrp_lua_object_deref_value(w, L, limref, false)) {
+ mrp_log_error("Failed to dereference CPU watch limit table.");
+ return 0;
+ }
+
+ limits = NULL;
+ nlimit = 0;
+ MRP_LUA_FOREACH_ALL(L, i, top + 1, ktype, kname, klen) {
+ if (ktype != LUA_TNUMBER) {
+ mrp_log_error("Invalid CPU watch limits (non-numeric index).");
+ goto fail;
+ }
+
+ if (get_limit(L, -1, &l) != 0) {
+ mrp_log_error("Invalid CPU watch limit #%zd.", klen);
+ goto fail;
+ }
+
+ if (mrp_reallocz(limits, nlimit, nlimit + 1) == NULL) {
+ mrp_log_error("Failed to allocate CPU watch limits.");
+ goto fail;
+ }
+
+ limits[nlimit].label = mrp_strdup(l.label);
+ limits[nlimit].limit = l.limit;
+
+ if (limits[nlimit].label == NULL) {
+ mrp_log_error("CPU watch limit with no or invalid label.");
+ goto fail;
+ }
+ else
+ nlimit++;
+ }
+
+ if (mrp_reallocz(limits, nlimit, nlimit + 1) == NULL)
+ goto fail;
+
+ qsort(limits, nlimit, sizeof(limits[0]), cmp_limits);
+
+ cleanup_limits(w, L, w->limits, -1, w->limref);
+ w->limits = limits;
+ w->limref = limref;
+
+ lua_settop(L, top);
+ return 1;
+
+ fail:
+ cleanup_limits(w, L, limits, nlimit, LUA_NOREF);
+ lua_settop(L, top);
+ return 0;
+}
+
+
+static void cleanup_limits(cpu_watch_lua_t *w, lua_State *L,
+ cpu_limit_t *limits, int n, int limref)
+{
+ cpu_limit_t *l;
+ int i;
+
+ mrp_lua_object_unref_value(w, L, limref);
+
+ if (limits == NULL)
+ return;
+
+ for (i = 0, l = limits; (n > 0 && i < n) || (n < 0 && l->label); i++, l++)
+ mrp_free(l->label);
+
+ mrp_free(limits);
+}
+
+
+
+/*
+ * Uh... We misuse of the bindings registering macro by passing in
+ * (the empty) { NULL, NULL } for the bindings and use its optional
+ * class registering feature to register our class. Ugly..., we need
+ * to add a similar MURPHY_REGISTER_LUA_CLASSES macro and the necessary
+ * infra for it...
+ */
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, CPU_WATCH_LUA_CLASS, { NULL, NULL });
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_CPU_WATCH_H__
+#define __MURPHY_CPU_WATCH_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/list.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+
+#include "system-monitor.h"
+#include "cpu-sampler.h"
+#include "estimator.h"
+
+#define CPU_WATCH_WINDOW (60 * 1000) /* default estimating window */
+
+/*
+ * a CPU watch
+ */
+
+typedef struct {
+ char *label; /* label to use in notifications */
+ int limit; /* limit value in percentages */
+} cpu_limit_t;
+
+
+struct cpu_watch_lua_s {
+ mrp_list_hook_t hook; /* to list of CPU watches */
+ sysmon_lua_t *sysmon; /* system monitor */
+ int cpu; /* CPU to watch */
+ cpu_sample_t sample; /* sample to watch (what to measure) */
+ cpu_limit_t *limits; /* limits to notify about */
+ int limref; /* Lua reference to limit table */
+ int polling; /* polling interval */
+ int window; /* window to estimate over (msecs) */
+ ewma_t value; /* estimated value */
+ cpu_limit_t *curr; /* currently active limit */
+ cpu_limit_t *prev; /* previously active limit */
+ int watchref; /* self-ref system monitor */
+ mrp_funcbridge_t *notify; /* notification callback */
+ mrp_funcbridge_t *update; /* overridable update method */
+};
+
+/** Create a CPU watch. */
+cpu_watch_lua_t *cpu_watch_create(sysmon_lua_t *sm, int polling, lua_State *L);
+
+/** Let the CPU watch know the polling interval (in milliseconds). */
+void cpu_watch_set_polling(cpu_watch_lua_t *w, int polling);
+
+/** Set the CPU watch estimate window (in milliseconds). */
+void cpu_watch_set_window(cpu_watch_lua_t *w, int window);
+
+/** Update the CPU watch with the latest sampled value. */
+int cpu_watch_update(cpu_watch_lua_t *w, lua_State *L);
+
+/** Invoke the CPU watch state/limit change notification callback. */
+void cpu_watch_notify(cpu_watch_lua_t *w, lua_State *L);
+
+#endif /* __MURPHY_CPU_WATCH_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SYSCTL_ESTIMATOR_H__
+#define __MURPHY_SYSCTL_ESTIMATOR_H__
+
+#include <math.h>
+
+#include <murphy/common/mm.h>
+
+
+/*
+ * exponentially weighted moving average
+ */
+
+typedef struct {
+ double a; /* smoothing factor */
+ double sample; /* last sample fed in */
+ double S; /* current estimate */
+} ewma_t;
+
+
+/** Initialize the given EWMA estimator. */
+static inline void ewma_init(ewma_t *e, double alpha, double sample)
+{
+ e->S = e->sample = sample;
+ e->a = alpha;
+}
+
+
+/** Allocate and initialize a new EWMA estimator. */
+static inline ewma_t *ewma_create(double alpha, double sample)
+{
+ ewma_t *e;
+
+ if ((e = mrp_allocz(sizeof(*e))) != NULL)
+ ewma_init(e, alpha, sample);
+
+ return e;
+}
+
+
+/** Free an EWMA estimator created by ewma_create. */
+static inline void ewma_free(ewma_t *e)
+{
+ mrp_free(e);
+}
+
+
+/** Reset the given EWMA estimator. */
+#define ewma_reset(e, alpha, sample) ewma_init(e, alpha, sample)
+
+
+/** Push a new sample into the EWMA estimation buffer. */
+static inline double ewma_add(ewma_t *e, double sample)
+{
+ e->sample = sample;
+ e->S = e->a * sample + (1 - e->a) * e->S;
+
+ return e->S;
+}
+
+
+/** Read the current estimate from the EWMA estimation buffer. */
+static inline double ewma_value(ewma_t *e)
+{
+ return e->S;
+}
+
+
+/** Read the last sample fed in. */
+static inline double ewma_sample(ewma_t *e)
+{
+ return e->sample;
+}
+
+
+#endif /* __MURPHY_SYSCTL_ESTIMATOR_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <math.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+
+#include "mem-sampler.h"
+
+static mem_usage_t usage;
+
+/*
+ * mem_usage_t field descriptor
+ */
+typedef struct {
+ off_t offs; /* offset within mem_usage_t */
+ const char *tag; /* /proc/meminfo tag */
+ size_t len; /* pre-calculated tag length */
+} field_t;
+
+
+static int read_usage(mem_usage_t *m)
+{
+ /*
+ * fields to extract from /proc/meminfo
+ *
+ * Notes:
+ * Keep the order of the field definition in sync with
+ * the order of tags in /proc/meminfo. For efficiency,
+ * the extracting loop will make at most a single pass
+ * over the tags without ever backtracking. If you have
+ * fields out of order, the extractor will simply fail.
+ */
+#define FIELD(_fld, _tag) \
+ { .offs = MRP_OFFSET(mem_usage_t, _fld), .tag = _tag, .len = sizeof(_tag) - 1 }
+ static field_t fields[] = {
+ FIELD(mem_total , "MemTotal" ),
+ FIELD(mem_free , "MemFree" ),
+ FIELD(swap_total, "SwapTotal"),
+ FIELD(swap_free , "SwapFree" ),
+ FIELD(dirty , "Dirty" ),
+ FIELD(writeback , "Writeback"),
+ { (off_t)-1, NULL, 0 }
+ };
+ static int fd = -1;
+#undef FIELD
+
+ field_t *f;
+ uint64_t *v;
+ const char *t, *p;
+ char *n, buf[4096];
+ int l;
+
+ if (fd < 0) {
+ if ((fd = open("/proc/meminfo", O_RDONLY)) < 0)
+ return -1;
+
+ fcntl(fd, F_SETFD, FD_CLOEXEC);
+ }
+
+ lseek(fd, 0, SEEK_SET);
+
+ if ((l = read(fd, buf, sizeof(buf) - 1)) < 0)
+ return -1;
+
+ buf[l] = '\0';
+ p = buf;
+
+ for (f = fields; f->tag != NULL; f++) {
+ v = (uint64_t *)((char *)m + f->offs);
+ t = f->tag;
+ l = f->len;
+
+ while (p != NULL) {
+ if (!strncmp(p, t, l) && p[l] == ':') {
+ p += l + 1;
+ *v = strtoull(p, &n, 10);
+
+ if (*n == ' ' && n[1] == 'k')
+ *v *= 1024;
+
+ if ((n = strchr(n, '\n')) != NULL)
+ p = n + 1;
+ else
+ p = NULL;
+
+ goto next_field;
+ }
+ else {
+ if ((n = strchr(p, '\n')) != NULL)
+ p = n + 1;
+ else
+ p = NULL;
+ }
+ }
+
+ if (p == NULL)
+ return -1;
+
+ next_field:
+ ;
+ }
+
+ return 0;
+}
+
+
+static void dump_usage(char *msg, mem_usage_t *m)
+{
+ mrp_debug("%s: MemTotal=%llu, MemFree=%llu",
+ msg, m->mem_total, m->mem_free);
+ mrp_debug("%s: SwapTotal=%llu, SwapFree=%llu",
+ msg, m->swap_total, m->swap_free);
+ mrp_debug("%s: dirty=%llu, writeback=%llu",
+ msg, m->dirty, m->writeback);
+}
+
+
+int mem_sample_usage(void)
+{
+ if (read_usage(&usage) == 0) {
+ dump_usage("memory usage", &usage);
+ return 0;
+ }
+ else
+ return -1;
+}
+
+
+int mem_get_usage(mem_usage_t *m)
+{
+ if (read_usage(m) == 0) {
+ dump_usage("meminfo", m);
+ return 0;
+ }
+ else
+ return -1;
+}
+
+
+int64_t mem_get_sample(mem_sample_t sample)
+{
+ switch (sample) {
+ case MEM_SAMPLE_MEMFREE: return usage.mem_free; break;
+ case MEM_SAMPLE_SWAPFREE: return usage.swap_free; break;
+ case MEM_SAMPLE_DIRTY: return usage.dirty; break;
+ case MEM_SAMPLE_WRITEBACK: return usage.writeback; break;
+ default: return -1;
+ }
+}
+
+
+int64_t mem_get_memory(void)
+{
+ return usage.mem_total;
+}
+
+
+int64_t mem_get_swap(void)
+{
+ return usage.swap_total;
+}
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SYSTEM_MONITOR_MEM_SAMPLER_H__
+#define __MURPHY_SYSTEM_MONITOR_MEM_SAMPLER_H__
+
+#include <stdint.h>
+
+/*
+ * type of memory usage sampling
+ */
+
+typedef enum {
+ MEM_SAMPLE_INVALID = -1, /* invalid type */
+ MEM_SAMPLE_MEMFREE, /* free memory */
+ MEM_SAMPLE_SWAPFREE, /* free swap */
+ MEM_SAMPLE_DIRTY, /* to be written back to disk */
+ MEM_SAMPLE_WRITEBACK, /* actively being written back */
+} mem_sample_t;
+
+/*
+ * pieces of memory usage information extracted from /proc/meminfo
+ */
+typedef struct {
+ uint64_t mem_total; /* MemTotal */
+ uint64_t swap_total; /* SwapTotal */
+ uint64_t mem_free; /* MemFree */
+ uint64_t swap_free; /* SwapFree */
+ uint64_t dirty; /* Dirty */
+ uint64_t writeback; /* Writeback */
+} mem_usage_t;
+
+
+/** Sample current memory usage. */
+int mem_sample_usage(void);
+
+/** Get memory usage information. */
+int mem_get_usage(mem_usage_t *m);
+
+/** Get memory usage of the given type. */
+int64_t mem_get_sample(mem_sample_t sample);
+
+/** Get the total amount of memory. */
+int64_t mem_get_memory(void);
+
+/** Get the total amount of swap. */
+int64_t mem_get_swap(void);
+
+#endif /* __MURPHY_SYSTEM_MONITOR_MEM_SAMPLER_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+#include <murphy/core/lua-bindings/murphy.h>
+
+#include "mem-watch.h"
+
+/*
+ * memory watch object
+ */
+
+#define MEM_WATCH_LUA_CLASS MRP_LUA_CLASS(mem_watch, lua)
+#define RO MRP_LUA_CLASS_READONLY
+#define NOINIT MRP_LUA_CLASS_NOINIT
+#define NOFLAGS MRP_LUA_CLASS_NOFLAGS
+#define SETANDGET mem_watch_setmember, mem_watch_getmember
+#define setmember mem_watch_setmember
+#define getmember mem_watch_getmember
+
+static int mem_watch_no_constructor(lua_State *L);
+static void mem_watch_destroy(void *data);
+static void mem_watch_changed(void *data, lua_State *L, int member);
+static ssize_t mem_watch_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data);
+static int mem_watch_setmember(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v);
+static int mem_watch_getmember(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v);
+static int mem_watch_delete(lua_State *L);
+
+MRP_LUA_METHOD_LIST_TABLE(mem_watch_methods,
+ MRP_LUA_METHOD_CONSTRUCTOR(mem_watch_no_constructor)
+ MRP_LUA_METHOD(delete, mem_watch_delete));
+
+MRP_LUA_METHOD_LIST_TABLE(mem_watch_overrides,
+ MRP_LUA_OVERRIDE_CALL(mem_watch_no_constructor));
+
+MRP_LUA_MEMBER_LIST_TABLE(mem_watch_members,
+ MRP_LUA_CLASS_STRING ("sample" , 0, setmember, getmember, RO )
+ MRP_LUA_CLASS_ANY ("limits" , 0, setmember, getmember, RO )
+ MRP_LUA_CLASS_INTEGER("polling" , 0, setmember, getmember, RO|NOINIT )
+ MRP_LUA_CLASS_INTEGER("window" , 0, setmember, getmember, NOFLAGS)
+ MRP_LUA_CLASS_INTEGER("raw" , 0, setmember, getmember, RO|NOINIT )
+ MRP_LUA_CLASS_INTEGER("value" , 0, setmember, getmember, RO|NOINIT )
+ MRP_LUA_CLASS_STRING ("current" , 0, setmember, getmember, RO|NOINIT )
+ MRP_LUA_CLASS_STRING ("previous", 0, setmember, getmember, RO|NOINIT )
+ MRP_LUA_CLASS_ANY ("notify" , 0, setmember, getmember, NOFLAGS)
+ MRP_LUA_CLASS_ANY ("update" , 0, setmember, getmember, NOFLAGS));
+
+MRP_LUA_DEFINE_CLASS(mem_watch, lua, mem_watch_lua_t, mem_watch_destroy,
+ mem_watch_methods, mem_watch_overrides, mem_watch_members, NULL,
+ mem_watch_changed, mem_watch_tostring , NULL,
+ MRP_LUA_CLASS_EXTENSIBLE | MRP_LUA_CLASS_PRIVREFS);
+
+MRP_LUA_CLASS_CHECKER(mem_watch_lua_t, mem_watch_lua, MEM_WATCH_LUA_CLASS);
+
+typedef enum {
+ MEM_WATCH_MEMBER_SAMPLE,
+ MEM_WATCH_MEMBER_LIMITS,
+ MEM_WATCH_MEMBER_POLLING,
+ MEM_WATCH_MEMBER_WINDOW,
+ MEM_WATCH_MEMBER_RAW,
+ MEM_WATCH_MEMBER_VALUE,
+ MEM_WATCH_MEMBER_CURRENT,
+ MEM_WATCH_MEMBER_PREVIOUS,
+ MEM_WATCH_MEMBER_NOTIFY,
+ MEM_WATCH_MEMBER_UPDATE
+} mem_watch_member_t;
+
+static inline mem_sample_t get_sample_id(const char *name);
+static inline const char *get_sample_name(mem_sample_t id);
+static inline void recalc_smoothing(mem_watch_lua_t *w);
+static int setup_limits(mem_watch_lua_t *w, lua_State *L, int limref);
+static void cleanup_limits(mem_watch_lua_t *w, lua_State *L,
+ mem_limit_t *limits, int n, int limref);
+
+
+static int mem_watch_no_constructor(lua_State *L)
+{
+ return luaL_error(L, "trying create a memory watch via constructor.");
+}
+
+
+mem_watch_lua_t *mem_watch_create(sysmon_lua_t *sm, int polling, lua_State *L)
+{
+ mem_watch_lua_t *w;
+ char e[256];
+
+ luaL_checktype(L, 2, LUA_TTABLE);
+
+ w = (mem_watch_lua_t *)mrp_lua_create_object(L, MEM_WATCH_LUA_CLASS,
+ NULL, 0);
+
+ mrp_list_init(&w->hook);
+ w->sysmon = sm;
+ w->polling = polling;
+ w->limref = LUA_NOREF;
+ w->window = MEM_WATCH_WINDOW;
+
+ if (mrp_lua_init_members(w, L, 2, e, sizeof(e)) != 1) {
+ luaL_error(L, "failed to initialize memory watch (error: %s)",
+ *e ? e : "<unknown error>");
+ return NULL;
+ }
+
+ w->value.S = 0;
+
+ recalc_smoothing(w);
+
+ return w;
+}
+
+
+static void mem_watch_destroy(void *data)
+{
+ MRP_UNUSED(data);
+
+ mrp_debug("memory watch %p destroyed", data);
+
+ return;
+}
+
+
+static int mem_watch_delete(lua_State *L)
+{
+ mem_watch_lua_t *w = mem_watch_lua_check(L, 1);
+
+ mrp_list_delete(&w->hook);
+ mrp_list_init(&w->hook);
+
+ sysmon_del_mem_watch(w->sysmon, w);
+
+ cleanup_limits(w, L, w->limits, -1, w->limref);
+ w->limits = NULL;
+ w->limref = LUA_NOREF;
+
+ mrp_funcbridge_unref(L, w->update);
+ mrp_funcbridge_unref(L, w->notify);
+ w->update = NULL;
+ w->notify = NULL;
+
+ return 0;
+}
+
+
+static void mem_watch_changed(void *data, lua_State *L, int member)
+{
+ MRP_UNUSED(data);
+ MRP_UNUSED(L);
+ MRP_UNUSED(member);
+}
+
+
+static int mem_watch_setmember(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v)
+{
+ mem_watch_lua_t *w = (mem_watch_lua_t *)data;
+ mrp_funcbridge_t *f, **fptr;
+
+ switch (member) {
+ case MEM_WATCH_MEMBER_SAMPLE:
+ if ((w->sample = get_sample_id(v->str)) >= 0)
+ return 1;
+ else
+ mrp_log_error("Can't sample memory for unknown type '%s'.", v->str);
+ return 0;
+
+ case MEM_WATCH_MEMBER_WINDOW:
+ mem_watch_set_window(w, v->s32);
+ return 1;
+
+ case MEM_WATCH_MEMBER_NOTIFY: fptr = &w->notify; goto set_bridge;
+ case MEM_WATCH_MEMBER_UPDATE: fptr = &w->update;
+ set_bridge:
+ if (!mrp_lua_object_deref_value(w, L, v->any, false))
+ return 0;
+ switch (lua_type(L, -1)) {
+ case LUA_TFUNCTION:
+ f = *fptr = mrp_funcbridge_create_luafunc(L, -1);
+ break;
+ default:
+ f = NULL;
+ break;
+ }
+ lua_pop(L, 1);
+ mrp_lua_object_unref_value(w, L, v->any);
+
+ return (f != NULL ? 1 : 0);
+
+ case MEM_WATCH_MEMBER_LIMITS:
+ return setup_limits(w, L, v->any);
+
+ default:
+ mrp_log_error("Trying to set read-only memory watch member #%d.",
+ member);
+ return 0;
+ }
+}
+
+
+static int mem_watch_getmember(void *data, lua_State *L, int member,
+ mrp_lua_value_t *v)
+{
+ mem_watch_lua_t *w = (mem_watch_lua_t *)data;
+
+ MRP_UNUSED(L);
+
+ switch (member) {
+ case MEM_WATCH_MEMBER_SAMPLE:
+ v->str = get_sample_name(w->sample);
+ return 1;
+
+ case MEM_WATCH_MEMBER_LIMITS:
+ v->any = w->limref;
+ return 1;
+
+ case MEM_WATCH_MEMBER_WINDOW:
+ v->s32 = w->window;
+ return 1;
+
+ case MEM_WATCH_MEMBER_POLLING:
+ v->s32 = w->polling;
+ return 1;
+
+ case MEM_WATCH_MEMBER_VALUE:
+ v->s32 = w->value.S;
+ return 1;
+
+ case MEM_WATCH_MEMBER_RAW:
+ v->s32 = w->value.sample;
+ return 1;
+
+ case MEM_WATCH_MEMBER_CURRENT:
+ v->str = w->curr ? w->curr->label : "<unknown limit>";
+ return 1;
+
+ case MEM_WATCH_MEMBER_PREVIOUS:
+ v->str = w->prev ? w->prev->label : "<unknown limit>";
+ return 1;
+
+ default:
+ v->any = LUA_REFNIL;
+ return 1;
+ }
+}
+
+
+static ssize_t mem_watch_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data)
+{
+ mem_watch_lua_t *w = (mem_watch_lua_t *)data;
+
+ MRP_UNUSED(L);
+
+ switch (mode & MRP_LUA_TOSTR_MODEMASK) {
+ case MRP_LUA_TOSTR_LUA:
+ default:
+ return snprintf(buf, size, "{memory watch of %s @ %d sec window}",
+ get_sample_name(w->sample), w->window / 1000);
+ }
+
+}
+
+
+static inline double calculate_alpha(double L, double n)
+{
+ double x, diff, min_x, min_diff;
+
+ min_diff = 1.0;
+ min_x = 0.1;
+
+ for (x = 0.01; x < 1.0; x += 0.001) {
+ diff = pow(1 - x, n - 1) * x - L;
+ if (fabs(diff) < min_diff) {
+ min_x = x;
+ min_diff = fabs(diff);
+ }
+ }
+
+ return min_x;
+}
+
+
+static inline void recalc_smoothing(mem_watch_lua_t *w)
+{
+ double alpha /*= 1 - exp(- (1.0 * w->polling) / (1.0 * w->window))*/;
+ double n;
+
+ if (w->window > w->polling) {
+ n = (1.0 * w->window) / (1.0 * w->polling);
+ alpha = calculate_alpha(0.0005, n);
+ }
+ else
+ alpha = 1;
+
+ ewma_init(&w->value, alpha, w->value.S);
+}
+
+
+void mem_watch_set_polling(mem_watch_lua_t *w, int polling)
+{
+ if (w->polling == polling)
+ return;
+
+ w->polling = polling;
+ recalc_smoothing(w);
+}
+
+
+void mem_watch_set_window(mem_watch_lua_t *w, int window)
+{
+ if (w->window == window)
+ return;
+
+ w->window = window;
+ recalc_smoothing(w);
+}
+
+
+int mem_watch_update(mem_watch_lua_t *w, lua_State *L)
+{
+ int64_t sample = mem_get_sample(w->sample);
+ int change;
+
+ if (sample < 0)
+ return FALSE;
+
+
+ if (w->update == NULL) {
+ double value = ewma_add(&w->value, sample);
+ mem_limit_t *l;
+
+ mrp_debug("%s sample=%d, estimate=%.2f", get_sample_name(w->sample),
+ sample, value);
+
+ change = FALSE;
+ for (l = w->limits; l->label != NULL; l++) {
+ if (value <= l->limit) {
+ if (w->curr != l) {
+ w->prev = w->curr;
+ w->curr = l;
+
+ change = TRUE;
+ }
+
+ break;
+ }
+ }
+ }
+ else {
+ mrp_funcbridge_value_t args[2], rv;
+ char rt;
+
+ mrp_debug("%s sample=%d", get_sample_name(w->sample), sample);
+
+ args[0].pointer = w;
+ args[1].integer = sample;
+
+ if (!mrp_funcbridge_call_from_c(L, w->update, "Od", &args[0], &rt,&rv)) {
+ mrp_log_error("Failed to invoke memory watch update handler (%s).",
+ rv.string ? rv.string : "<unknown error>");
+ mrp_free((char *)rv.string);
+ change = FALSE;
+ }
+ else
+ change = ((rt == MRP_FUNCBRIDGE_BOOLEAN && rv.boolean) ||
+ (rt == MRP_FUNCBRIDGE_INTEGER && rv.integer));
+ }
+
+ return change;
+}
+
+
+void mem_watch_notify(mem_watch_lua_t *w, lua_State *L)
+{
+ mrp_funcbridge_value_t args[3], rv;
+ char rt;
+
+ MRP_UNUSED(L);
+
+ mrp_debug("memory watch %s: %s -> %s", get_sample_name(w->sample),
+ w->prev ? w->prev->label : "<unknown>",
+ w->curr ? w->curr->label : "<unknown>");
+
+ if (w->notify == NULL)
+ return;
+
+ args[0].pointer = w;
+ args[1].string = w->prev ? w->prev->label : "<unknown>";
+ args[2].string = w->curr ? w->curr->label : "<unknown>";
+
+ if (!mrp_funcbridge_call_from_c(L, w->notify, "Oss", &args[0], &rt, &rv)) {
+ mrp_log_error("Failed to notify memory watch %s (%s).",
+ get_sample_name(w->sample),
+ rv.string ? rv.string : "<unknown error>");
+ mrp_free((char *)rv.string);
+ }
+}
+
+
+static inline mem_sample_t get_sample_id(const char *name)
+{
+#define MAP(_name, _id) if (!strcmp(name, _name)) return _id
+ MAP("MemFree" , MEM_SAMPLE_MEMFREE );
+ MAP("SwapFree" , MEM_SAMPLE_SWAPFREE );
+ MAP("Dirty" , MEM_SAMPLE_DIRTY );
+ MAP("Writeback" , MEM_SAMPLE_WRITEBACK );
+#undef MAP
+
+ return MEM_SAMPLE_INVALID;
+}
+
+
+static inline const char *get_sample_name(mem_sample_t id)
+{
+ const char *names[] = {
+#define MAP(_name, _id) [_id] = _name
+ MAP("MemFree" , MEM_SAMPLE_MEMFREE ),
+ MAP("SwapFree" , MEM_SAMPLE_SWAPFREE ),
+ MAP("Dirty" , MEM_SAMPLE_DIRTY ),
+ MAP("Writeback" , MEM_SAMPLE_WRITEBACK),
+#undef MAP
+ };
+
+ if (MEM_SAMPLE_MEMFREE <= id && id <= MEM_SAMPLE_WRITEBACK)
+ return names[id];
+ else
+ return "<invalid memory sample type>";
+}
+
+
+static int cmp_limits(const void *ptr1, const void *ptr2)
+{
+ mem_limit_t *l1 = (mem_limit_t *)ptr1;
+ mem_limit_t *l2 = (mem_limit_t *)ptr2;
+
+ return l1->limit - l2->limit;
+}
+
+
+static int get_limit(lua_State *L, int idx, mem_limit_t *l)
+{
+ char *u;
+
+ if (lua_type(L, idx) != LUA_TTABLE)
+ return -1;
+
+ lua_getfield(L, idx, "label");
+
+ if (lua_type(L, -1) == LUA_TSTRING)
+ l->label = (char *)lua_tostring(L, -1);
+ else
+ l->label = NULL;
+
+ lua_pop(L, 1);
+
+ if (l->label == NULL)
+ return -1;
+
+ lua_getfield(L, idx, "limit");
+
+ switch (lua_type(L, -1)) {
+ case LUA_TNUMBER:
+ l->limit = lua_tonumber(L, -1);
+ break;
+ case LUA_TSTRING:
+ l->limit = strtoull(lua_tostring(L, -1), &u, 10);
+ if (u[0]) {
+ if (!u[1]) {
+ switch (u[0]) {
+ case 'k': l->limit *= 1024; break;
+ case 'M': l->limit *= 1024*1024; break;
+ case 'G': l->limit *= 1024*1024*1024; break;
+ default: goto invalid;
+ }
+ }
+ else
+ goto invalid;
+ }
+ break;
+ default:
+ invalid:
+ l->limit = -1;
+ break;
+ }
+
+ lua_pop(L, 1);
+
+ if (l->limit >= 0)
+ return 0;
+ else
+ return -1;
+}
+
+
+static int setup_limits(mem_watch_lua_t *w, lua_State *L, int limref)
+{
+ int top = lua_gettop(L);
+ mem_limit_t *limits, l;
+ int nlimit;
+ const char *kname;
+ int ktype, i;
+ size_t klen;
+
+ if (!mrp_lua_object_deref_value(w, L, limref, false)) {
+ mrp_log_error("Failed to dereference memory watch limit table.");
+ return 0;
+ }
+
+ limits = NULL;
+ nlimit = 0;
+ MRP_LUA_FOREACH_ALL(L, i, top + 1, ktype, kname, klen) {
+ if (ktype != LUA_TNUMBER) {
+ mrp_log_error("Invalid memory watch limits (non-numeric index).");
+ goto fail;
+ }
+
+ if (get_limit(L, -1, &l) != 0) {
+ mrp_log_error("Invalid memory watch limit #%zd.", klen);
+ goto fail;
+ }
+
+ if (mrp_reallocz(limits, nlimit, nlimit + 1) == NULL) {
+ mrp_log_error("Failed to allocate memory watch limits.");
+ goto fail;
+ }
+
+ limits[nlimit].label = mrp_strdup(l.label);
+ limits[nlimit].limit = l.limit;
+
+ if (limits[nlimit].label == NULL) {
+ mrp_log_error("memory watch limit with no or invalid label.");
+ goto fail;
+ }
+ else
+ nlimit++;
+ }
+
+ if (mrp_reallocz(limits, nlimit, nlimit + 1) == NULL)
+ goto fail;
+
+ qsort(limits, nlimit, sizeof(limits[0]), cmp_limits);
+
+ cleanup_limits(w, L, w->limits, -1, w->limref);
+ w->limits = limits;
+ w->limref = limref;
+
+ lua_settop(L, top);
+ return 1;
+
+ fail:
+ cleanup_limits(w, L, limits, nlimit, LUA_NOREF);
+ lua_settop(L, top);
+ return 0;
+}
+
+
+static void cleanup_limits(mem_watch_lua_t *w, lua_State *L,
+ mem_limit_t *limits, int n, int limref)
+{
+ mem_limit_t *l;
+ int i;
+
+ mrp_lua_object_unref_value(w, L, limref);
+
+ if (limits == NULL)
+ return;
+
+ for (i = 0, l = limits; (n > 0 && i < n) || (n < 0 && l->label); i++, l++)
+ mrp_free(l->label);
+
+ mrp_free(limits);
+}
+
+
+
+/*
+ * Uh... We misuse of the bindings registering macro by passing in
+ * (the empty) { NULL, NULL } for the bindings and use its optional
+ * class registering feature to register our class. Ugly..., we need
+ * to add a similar MURPHY_REGISTER_LUA_CLASSES macro and the necessary
+ * infra for it...
+ */
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, MEM_WATCH_LUA_CLASS, { NULL, NULL });
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_MEM_WATCH_H__
+#define __MURPHY_MEM_WATCH_H__
+
+#include <murphy/common/macros.h>
+#include <murphy/common/list.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+
+#include "system-monitor.h"
+#include "mem-sampler.h"
+#include "estimator.h"
+
+#define MEM_WATCH_WINDOW (15 * 1000) /* default estimating window */
+
+/*
+ * a memory watch
+ */
+
+typedef struct {
+ char *label; /* label to use in notifications */
+ int64_t limit; /* limit value in kilobytes */
+} mem_limit_t;
+
+
+struct mem_watch_lua_s {
+ mrp_list_hook_t hook; /* to list of CPU watches */
+ sysmon_lua_t *sysmon; /* system monitor */
+ mem_sample_t sample; /* sample to watch (what to measure) */
+ mem_limit_t *limits; /* limits to notify about */
+ int limref; /* Lua reference to limit table */
+ int polling; /* polling interval */
+ int window; /* window to estimate over (msecs) */
+ ewma_t value; /* estimated value */
+ mem_limit_t *curr; /* currently active limit */
+ mem_limit_t *prev; /* previously active limit */
+ int watchref; /* self-ref system monitor */
+ mrp_funcbridge_t *notify; /* notification callback */
+ mrp_funcbridge_t *update; /* overridable update method */
+};
+
+/** Create a memory watch. */
+mem_watch_lua_t *mem_watch_create(sysmon_lua_t *sm, int polling, lua_State *L);
+
+/** Let the memory watch know the polling interval (in milliseconds). */
+void mem_watch_set_polling(mem_watch_lua_t *w, int polling);
+
+/** Set the memory watch estimate window (in milliseconds). */
+void mem_watch_set_window(mem_watch_lua_t *w, int window);
+
+/** Update the memory watch with the latest sampled value. */
+int mem_watch_update(mem_watch_lua_t *w, lua_State *L);
+
+/** Invoke the memory watch state/limit change notification callback. */
+void mem_watch_notify(mem_watch_lua_t *w, lua_State *L);
+
+#endif /* __MURPHY_CPU_WATCH_H__ */
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/core/plugin.h>
+
+
+/*
+ * There's really nothing done here. All the interesting things
+ * are caused by the murphy Lua infra* registration macros and
+ * get automagically triggered when the plugin is dlopened.
+ */
+
+static int plugin_init(mrp_plugin_t *plugin)
+{
+ MRP_UNUSED(plugin);
+
+ return TRUE;
+}
+
+
+static void plugin_exit(mrp_plugin_t *plugin)
+{
+ MRP_UNUSED(plugin);
+}
+
+
+#define PLUGIN_DESCRIPTION "Murphy system monitor plugin."
+#define PLUGIN_HELP "Murphy system monitor plugin."
+#define PLUGIN_AUTHORS "Krisztian Litkey <kli@iki.fi>"
+#define PLUGIN_VERSION MRP_VERSION_INT(0, 0, 1)
+
+MURPHY_REGISTER_PLUGIN("system-monitor",
+ PLUGIN_VERSION, PLUGIN_DESCRIPTION, PLUGIN_AUTHORS,
+ PLUGIN_HELP, MRP_SINGLETON, plugin_init, plugin_exit,
+ NULL, 0,
+ NULL, 0,
+ NULL, 0,
+ NULL);
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <string.h>
+
+#include <murphy/common/macros.h>
+#include <murphy/common/debug.h>
+#include <murphy/common/log.h>
+#include <murphy/common/mm.h>
+#include <murphy/common/list.h>
+#include <murphy/common/mainloop.h>
+#include <murphy/core/lua-bindings/murphy.h>
+#include <murphy/core/lua-utils/object.h>
+#include <murphy/core/lua-utils/funcbridge.h>
+
+#include "system-monitor.h"
+#include "cpu-watch.h"
+#include "mem-watch.h"
+
+/*
+ * system monitor context (singleton) object
+ */
+
+#define SYSMON_LUA_CLASS MRP_LUA_CLASS(sysmon, lua)
+#define OFFSET(m) MRP_OFFSET(sysmon_lua_t, m)
+#define NOTIFY MRP_LUA_CLASS_NOTIFY
+
+struct sysmon_lua_s {
+ lua_State *L; /* Lua execution context */
+ mrp_list_hook_t cpu_watches; /* active CPU watches */
+ mrp_list_hook_t mem_watches; /* active memory watches */
+ int polling; /* polling interval (in msecs) */
+ mrp_context_t *ctx; /* murphy context */
+ mrp_timer_t *t; /* polling timer */
+ void *pending; /* pending notification */
+};
+
+static int sysmon_create(lua_State *L);
+static void sysmon_destroy(void *data);
+static void sysmon_notify(void *data, lua_State *L, int member);
+static ssize_t sysmon_tostring(mrp_lua_tostr_mode_t mode, char *buf, size_t size,
+ lua_State *L, void *data);
+
+static int sysmon_add_cpu_watch(lua_State *L);
+static int sysmon_add_mem_watch(lua_State *L);
+
+MRP_LUA_METHOD_LIST_TABLE(sysmon_methods,
+ MRP_LUA_METHOD_CONSTRUCTOR(sysmon_create)
+ MRP_LUA_METHOD(CpuWatch, sysmon_add_cpu_watch)
+ MRP_LUA_METHOD(MemWatch, sysmon_add_mem_watch));
+
+MRP_LUA_METHOD_LIST_TABLE(sysmon_overrides,
+ MRP_LUA_OVERRIDE_CALL(sysmon_create));
+
+MRP_LUA_MEMBER_LIST_TABLE(sysmon_members,
+ MRP_LUA_CLASS_INTEGER("polling", OFFSET(polling), NULL, NULL, NOTIFY));
+
+MRP_LUA_DEFINE_CLASS(sysmon, lua, sysmon_lua_t, sysmon_destroy,
+ sysmon_methods, sysmon_overrides, sysmon_members, NULL,
+ sysmon_notify , sysmon_tostring , NULL,
+ MRP_LUA_CLASS_EXTENSIBLE | MRP_LUA_CLASS_PRIVREFS);
+
+MRP_LUA_CLASS_CHECKER(sysmon_lua_t, sysmon_lua, SYSMON_LUA_CLASS);
+
+typedef enum {
+ SYSMON_MEMBER_POLLING, /* polling interval member */
+} sysmon_member_t;
+
+
+static sysmon_lua_t *singleton(lua_State *L)
+{
+ static sysmon_lua_t *sm = NULL;
+
+ if (L != NULL) {
+ if (sm == NULL) {
+ mrp_debug("creating system monitor singleton object");
+ sm = (sysmon_lua_t *)
+ mrp_lua_create_object(L, SYSMON_LUA_CLASS, NULL, 0);
+
+ mrp_list_init(&sm->cpu_watches);
+ mrp_list_init(&sm->mem_watches);
+ sm->L = L;
+ sm->polling = SYSMON_DEFAULT_POLLING;
+ sm->ctx = mrp_lua_get_murphy_context();
+ }
+ }
+ else {
+ mrp_debug("clearing system monitor singleton object");
+ sm = NULL;
+ }
+
+ return sm;
+}
+
+
+static int sysmon_create(lua_State *L)
+{
+ return mrp_lua_push_object(L, singleton(L));
+}
+
+
+static int sysmon_get(lua_State *L)
+{
+ return sysmon_create(L);
+}
+
+
+static void sysmon_destroy(void *data)
+{
+ sysmon_lua_t *sm = (sysmon_lua_t *)data;
+ mrp_list_hook_t *p, *n;
+
+ mrp_del_timer(sm->t);
+ sm->t = NULL;
+
+ mrp_list_foreach(&sm->cpu_watches, p, n) {
+ mrp_list_delete(p);
+ }
+
+ mrp_list_foreach(&sm->mem_watches, p, n) {
+ mrp_list_delete(p);
+ }
+
+ singleton(NULL);
+}
+
+
+static ssize_t sysmon_tostring(mrp_lua_tostr_mode_t mode, char *buf,
+ size_t size, lua_State *L, void *data)
+{
+ sysmon_lua_t *sm = (sysmon_lua_t *)data;
+
+ MRP_UNUSED(L);
+
+ switch (mode & MRP_LUA_TOSTR_MODEMASK) {
+ case MRP_LUA_TOSTR_LUA:
+ default:
+ return snprintf(buf, size, "{%s system-monitor @ %d msecs}",
+ sm->t ? "active" : "inactive", sm->polling);
+ }
+}
+
+
+static void update_polling(sysmon_lua_t *sm)
+{
+ mrp_list_hook_t *p, *n;
+ cpu_watch_lua_t *cw;
+ mem_watch_lua_t *mw;
+
+ mrp_list_foreach(&sm->cpu_watches, p, n) {
+ cw = mrp_list_entry(p, typeof(*cw), hook);
+
+ cpu_watch_set_polling(cw, sm->polling);
+ }
+
+ mrp_list_foreach(&sm->mem_watches, p, n) {
+ mw = mrp_list_entry(p, typeof(*mw), hook);
+
+ mem_watch_set_polling(mw, sm->polling);
+ }
+}
+
+
+static void sysmon_notify(void *data, lua_State *L, int member)
+{
+ sysmon_lua_t *sm = (sysmon_lua_t *)data;
+
+ MRP_UNUSED(L);
+
+ mrp_debug("system monitor member #%d changed", member);
+
+ switch (member) {
+ case SYSMON_MEMBER_POLLING:
+ if (sm->polling < SYSMON_MINIMUM_POLLING)
+ sm->polling = SYSMON_MINIMUM_POLLING;
+
+ if (sm->t != NULL)
+ mrp_mod_timer(sm->t, sm->polling);
+
+ update_polling(sm);
+
+ mrp_log_info("system monitor polling interval set to %d msecs.",
+ sm->polling);
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+static void sysmon_timer(mrp_timer_t *t, void *user_data)
+{
+ sysmon_lua_t *sm = (sysmon_lua_t *)user_data;
+ mrp_list_hook_t *p, *n;
+ cpu_watch_lua_t *cw;
+ mem_watch_lua_t *mw;
+
+ MRP_UNUSED(t);
+
+ mrp_debug("sampling CPU load");
+ cpu_sample_load();
+
+ mrp_list_foreach(&sm->cpu_watches, p, n) {
+ cw = mrp_list_entry(p, typeof(*cw), hook);
+
+ sm->pending = n;
+
+ if (cpu_watch_update(cw, sm->L))
+ cpu_watch_notify(cw, sm->L);
+
+ n = sm->pending;
+ }
+
+ mrp_debug("sampling memory usage");
+ mem_sample_usage();
+
+ mrp_list_foreach(&sm->mem_watches, p, n) {
+ mw = mrp_list_entry(p, typeof(*mw), hook);
+
+ sm->pending = n;
+
+ if (mem_watch_update(mw, sm->L))
+ mem_watch_notify(mw, sm->L);
+
+ n = sm->pending;
+ }
+
+}
+
+
+static int sysmon_add_cpu_watch(lua_State *L)
+{
+ sysmon_lua_t *sm;
+ cpu_watch_lua_t *w;
+
+ sm = sysmon_lua_check(L, 1);
+
+ if ((w = cpu_watch_create(sm, sm->polling, L)) == NULL)
+ return luaL_error(L, "failed to create CPU watch");
+
+ w->watchref = mrp_lua_object_ref_value(sm, L, -1);
+ mrp_list_append(&sm->cpu_watches, &w->hook);
+
+ if (sm->t == NULL)
+ sm->t = mrp_add_timer(sm->ctx->ml, sm->polling, sysmon_timer, sm);
+
+ return 1;
+}
+
+
+static int sysmon_add_mem_watch(lua_State *L)
+{
+ sysmon_lua_t *sm;
+ mem_watch_lua_t *w;
+
+ sm = sysmon_lua_check(L, 1);
+
+ if ((w = mem_watch_create(sm, sm->polling, L)) == NULL)
+ return luaL_error(L, "failed to create memory watch");
+
+ w->watchref = mrp_lua_object_ref_value(sm, L, -1);
+ mrp_list_append(&sm->mem_watches, &w->hook);
+
+ if (sm->t == NULL)
+ sm->t = mrp_add_timer(sm->ctx->ml, sm->polling, sysmon_timer, sm);
+
+ return 1;
+}
+
+
+int sysmon_del_cpu_watch(sysmon_lua_t *sm, cpu_watch_lua_t *w)
+{
+ if (sm->pending == &w->hook)
+ sm->pending = w->hook.next;
+
+ mrp_lua_object_unref_value(sm, sm->L, w->watchref);
+
+ if (mrp_list_empty(&sm->cpu_watches)) {
+ mrp_del_timer(sm->t);
+ sm->t = NULL;
+ }
+
+ return 0;
+}
+
+
+int sysmon_del_mem_watch(sysmon_lua_t *sm, mem_watch_lua_t *w)
+{
+ if (sm->pending == &w->hook)
+ sm->pending = w->hook.next;
+
+ mrp_lua_object_unref_value(sm, sm->L, w->watchref);
+
+ if (mrp_list_empty(&sm->mem_watches)) {
+ mrp_del_timer(sm->t);
+ sm->t = NULL;
+ }
+
+ return 0;
+}
+
+
+MURPHY_REGISTER_LUA_BINDINGS(murphy, SYSMON_LUA_CLASS,
+ { "get_system_monitor", sysmon_get });
--- /dev/null
+/*
+ * Copyright (c) 2012, 2013, Intel Corporation
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Intel Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __MURPHY_SYSTEM_MONITOR_H__
+#define __MURPHY_SYSTEM_MONITOR_H__
+
+#define SYSMON_MINIMUM_POLLING 1000 /* minimum polling interval */
+#define SYSMON_DEFAULT_POLLING 15000 /* default polling interval */
+
+typedef struct sysmon_lua_s sysmon_lua_t;
+typedef struct cpu_watch_lua_s cpu_watch_lua_t;
+typedef struct mem_watch_lua_s mem_watch_lua_t;
+
+/** Request deletion of the given CPU watch. */
+int sysmon_del_cpu_watch(sysmon_lua_t *sm, cpu_watch_lua_t *w);
+
+/** Request deletion of the given memory watch. */
+int sysmon_del_mem_watch(sysmon_lua_t *sm, mem_watch_lua_t *w);
+
+#endif /* __MURPHY_SYSTEM_MONITOR_H__ */