2 * Copyright (c) 2012, 2013, Intel Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of Intel Corporation nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33 #include <murphy/common/macros.h>
34 #include <murphy/common/debug.h>
35 #include <murphy/common/log.h>
36 #include <murphy/common/mm.h>
37 #include <murphy/core/lua-utils/object.h>
38 #include <murphy/core/lua-utils/funcbridge.h>
39 #include <murphy/core/lua-bindings/murphy.h>
41 #include "mem-watch.h"
47 #define MEM_WATCH_LUA_CLASS MRP_LUA_CLASS(mem_watch, lua)
48 #define RO MRP_LUA_CLASS_READONLY
49 #define NOINIT MRP_LUA_CLASS_NOINIT
50 #define NOFLAGS MRP_LUA_CLASS_NOFLAGS
51 #define SETANDGET mem_watch_setmember, mem_watch_getmember
52 #define setmember mem_watch_setmember
53 #define getmember mem_watch_getmember
55 static int mem_watch_no_constructor(lua_State *L);
56 static void mem_watch_destroy(void *data);
57 static void mem_watch_changed(void *data, lua_State *L, int member);
58 static ssize_t mem_watch_tostring(mrp_lua_tostr_mode_t mode, char *buf,
59 size_t size, lua_State *L, void *data);
60 static int mem_watch_setmember(void *data, lua_State *L, int member,
62 static int mem_watch_getmember(void *data, lua_State *L, int member,
64 static int mem_watch_delete(lua_State *L);
66 MRP_LUA_METHOD_LIST_TABLE(mem_watch_methods,
67 MRP_LUA_METHOD_CONSTRUCTOR(mem_watch_no_constructor)
68 MRP_LUA_METHOD(delete, mem_watch_delete));
70 MRP_LUA_METHOD_LIST_TABLE(mem_watch_overrides,
71 MRP_LUA_OVERRIDE_CALL(mem_watch_no_constructor));
73 MRP_LUA_MEMBER_LIST_TABLE(mem_watch_members,
74 MRP_LUA_CLASS_STRING ("sample" , 0, setmember, getmember, RO )
75 MRP_LUA_CLASS_ANY ("limits" , 0, setmember, getmember, RO )
76 MRP_LUA_CLASS_INTEGER("polling" , 0, setmember, getmember, RO|NOINIT )
77 MRP_LUA_CLASS_DOUBLE ("alpha" , 0, setmember, getmember, NOFLAGS)
78 MRP_LUA_CLASS_INTEGER("window" , 0, setmember, getmember, NOFLAGS)
79 MRP_LUA_CLASS_INTEGER("raw" , 0, setmember, getmember, RO|NOINIT )
80 MRP_LUA_CLASS_INTEGER("value" , 0, setmember, getmember, RO|NOINIT )
81 MRP_LUA_CLASS_STRING ("current" , 0, setmember, getmember, RO|NOINIT )
82 MRP_LUA_CLASS_STRING ("previous", 0, setmember, getmember, RO|NOINIT )
83 MRP_LUA_CLASS_ANY ("notify" , 0, setmember, getmember, NOFLAGS)
84 MRP_LUA_CLASS_ANY ("update" , 0, setmember, getmember, NOFLAGS));
86 MRP_LUA_DEFINE_CLASS(mem_watch, lua, mem_watch_lua_t, mem_watch_destroy,
87 mem_watch_methods, mem_watch_overrides, mem_watch_members, NULL,
88 mem_watch_changed, mem_watch_tostring , NULL,
89 MRP_LUA_CLASS_EXTENSIBLE);
91 MRP_LUA_CLASS_CHECKER(mem_watch_lua_t, mem_watch_lua, MEM_WATCH_LUA_CLASS);
94 MEM_WATCH_MEMBER_SAMPLE,
95 MEM_WATCH_MEMBER_LIMITS,
96 MEM_WATCH_MEMBER_POLLING,
97 MEM_WATCH_MEMBER_ALPHA,
98 MEM_WATCH_MEMBER_WINDOW,
100 MEM_WATCH_MEMBER_VALUE,
101 MEM_WATCH_MEMBER_CURRENT,
102 MEM_WATCH_MEMBER_PREVIOUS,
103 MEM_WATCH_MEMBER_NOTIFY,
104 MEM_WATCH_MEMBER_UPDATE
105 } mem_watch_member_t;
107 static inline mem_sample_t get_sample_id(const char *name);
108 static inline const char *get_sample_name(mem_sample_t id);
109 static inline void recalc_smoothing(mem_watch_lua_t *w);
110 static int setup_limits(mem_watch_lua_t *w, lua_State *L, int limref);
111 static void cleanup_limits(mem_watch_lua_t *w, lua_State *L,
112 mem_limit_t *limits, int n, int limref);
115 static int mem_watch_no_constructor(lua_State *L)
117 return luaL_error(L, "trying create a memory watch via constructor.");
121 mem_watch_lua_t *mem_watch_create(sysmon_lua_t *sm, int polling, lua_State *L)
126 luaL_checktype(L, 2, LUA_TTABLE);
128 w = (mem_watch_lua_t *)mrp_lua_create_object(L, MEM_WATCH_LUA_CLASS,
131 mrp_list_init(&w->hook);
133 w->polling = polling;
134 w->limref = LUA_NOREF;
138 if (mrp_lua_init_members(w, L, 2, e, sizeof(e)) != 1) {
139 luaL_error(L, "failed to initialize memory watch (error: %s)",
140 *e ? e : "<unknown error>");
144 if (w->window == -1 && w->value.a == -1)
145 w->window = MEM_WATCH_WINDOW;
154 static void mem_watch_destroy(void *data)
158 mrp_debug("memory watch %p destroyed", data);
164 static int mem_watch_delete(lua_State *L)
166 mem_watch_lua_t *w = mem_watch_lua_check(L, 1);
168 mrp_list_delete(&w->hook);
169 mrp_list_init(&w->hook);
171 sysmon_del_mem_watch(w->sysmon, w);
173 cleanup_limits(w, L, w->limits, -1, w->limref);
175 w->limref = LUA_NOREF;
177 mrp_funcbridge_unref(L, w->update);
178 mrp_funcbridge_unref(L, w->notify);
186 static void mem_watch_changed(void *data, lua_State *L, int member)
194 static int mem_watch_setmember(void *data, lua_State *L, int member,
197 mem_watch_lua_t *w = (mem_watch_lua_t *)data;
198 mrp_funcbridge_t *f, **fptr;
201 case MEM_WATCH_MEMBER_SAMPLE:
202 if ((w->sample = get_sample_id(v->str)) >= 0)
205 mrp_log_error("Can't sample memory for unknown type '%s'.", v->str);
208 case MEM_WATCH_MEMBER_WINDOW:
209 mem_watch_set_window(w, v->s32);
212 case MEM_WATCH_MEMBER_ALPHA:
213 if (w->window == -1) {
218 mrp_log_error("Can't set both window and alpha for memory watch.");
223 case MEM_WATCH_MEMBER_NOTIFY: fptr = &w->notify; goto set_bridge;
224 case MEM_WATCH_MEMBER_UPDATE: fptr = &w->update;
226 if (!mrp_lua_object_deref_value(w, L, v->any, false))
228 switch (lua_type(L, -1)) {
230 f = *fptr = mrp_funcbridge_create_luafunc(L, -1);
237 mrp_lua_object_unref_value(w, L, v->any);
239 return (f != NULL ? 1 : 0);
241 case MEM_WATCH_MEMBER_LIMITS:
242 return setup_limits(w, L, v->any);
245 mrp_log_error("Trying to set read-only memory watch member #%d.",
252 static int mem_watch_getmember(void *data, lua_State *L, int member,
255 mem_watch_lua_t *w = (mem_watch_lua_t *)data;
260 case MEM_WATCH_MEMBER_SAMPLE:
261 v->str = get_sample_name(w->sample);
264 case MEM_WATCH_MEMBER_LIMITS:
268 case MEM_WATCH_MEMBER_WINDOW:
272 case MEM_WATCH_MEMBER_ALPHA:
276 case MEM_WATCH_MEMBER_POLLING:
280 case MEM_WATCH_MEMBER_VALUE:
284 case MEM_WATCH_MEMBER_RAW:
285 v->s32 = w->value.sample;
288 case MEM_WATCH_MEMBER_CURRENT:
289 v->str = w->curr ? w->curr->label : "<unknown limit>";
292 case MEM_WATCH_MEMBER_PREVIOUS:
293 v->str = w->prev ? w->prev->label : "<unknown limit>";
303 static ssize_t mem_watch_tostring(mrp_lua_tostr_mode_t mode, char *buf,
304 size_t size, lua_State *L, void *data)
306 mem_watch_lua_t *w = (mem_watch_lua_t *)data;
310 switch (mode & MRP_LUA_TOSTR_MODEMASK) {
311 case MRP_LUA_TOSTR_LUA:
313 return snprintf(buf, size,
314 "{%s memory watch, %d/%d sec window/poll, %.2f alpha}",
315 get_sample_name(w->sample), w->window / 1000,
316 w->polling / 1000, w->value.a);
322 static inline double calculate_alpha(double L, double n)
324 double x, diff, min_x, min_diff;
329 for (x = 0.01; x < 1.0; x += 0.001) {
330 diff = pow(1 - x, n - 1) * x - L;
331 if (fabs(diff) < min_diff) {
333 min_diff = fabs(diff);
341 static inline void recalc_smoothing(mem_watch_lua_t *w)
343 double alpha /*= 1 - exp(- (1.0 * w->polling) / (1.0 * w->window))*/;
346 if (w->window > w->polling) {
347 n = (1.0 * w->window) / (1.0 * w->polling);
348 alpha = calculate_alpha(0.0005, n);
353 ewma_init(&w->value, alpha, w->value.S);
357 void mem_watch_set_polling(mem_watch_lua_t *w, int polling)
359 if (w->polling == polling)
362 if (w->window != -1) {
363 w->polling = polling;
369 void mem_watch_set_window(mem_watch_lua_t *w, int window)
371 if (w->window == window)
379 int mem_watch_update(mem_watch_lua_t *w, lua_State *L)
381 int64_t sample = mem_get_sample(w->sample);
388 if (w->update == NULL) {
389 double value = ewma_add(&w->value, sample);
392 mrp_debug("%s sample=%llu, estimate=%.2f", get_sample_name(w->sample),
393 (unsigned long long)sample, value);
396 for (l = w->limits; l->label != NULL; l++) {
397 if (value <= l->limit) {
410 mrp_funcbridge_value_t args[2], rv;
413 mrp_debug("%s sample=%llu", get_sample_name(w->sample),
414 (unsigned long long)sample);
417 args[1].integer = sample;
419 if (!mrp_funcbridge_call_from_c(L, w->update, "Od", &args[0], &rt,&rv)) {
420 mrp_log_error("Failed to invoke memory watch update handler (%s).",
421 rv.string ? rv.string : "<unknown error>");
422 mrp_free((char *)rv.string);
426 change = ((rt == MRP_FUNCBRIDGE_BOOLEAN && rv.boolean) ||
427 (rt == MRP_FUNCBRIDGE_INTEGER && rv.integer));
434 void mem_watch_notify(mem_watch_lua_t *w, lua_State *L)
436 mrp_funcbridge_value_t args[3], rv;
441 mrp_debug("memory watch %s: %s -> %s", get_sample_name(w->sample),
442 w->prev ? w->prev->label : "<unknown>",
443 w->curr ? w->curr->label : "<unknown>");
445 if (w->notify == NULL)
449 args[1].string = w->prev ? w->prev->label : "<unknown>";
450 args[2].string = w->curr ? w->curr->label : "<unknown>";
452 if (!mrp_funcbridge_call_from_c(L, w->notify, "Oss", &args[0], &rt, &rv)) {
453 mrp_log_error("Failed to notify memory watch %s (%s).",
454 get_sample_name(w->sample),
455 rv.string ? rv.string : "<unknown error>");
456 mrp_free((char *)rv.string);
461 static inline mem_sample_t get_sample_id(const char *name)
463 #define MAP(_name, _id) if (!strcmp(name, _name)) return _id
464 MAP("MemFree" , MEM_SAMPLE_MEMFREE );
465 MAP("SwapFree" , MEM_SAMPLE_SWAPFREE );
466 MAP("Dirty" , MEM_SAMPLE_DIRTY );
467 MAP("Writeback" , MEM_SAMPLE_WRITEBACK );
470 return MEM_SAMPLE_INVALID;
474 static inline const char *get_sample_name(mem_sample_t id)
476 const char *names[] = {
477 #define MAP(_name, _id) [_id] = _name
478 MAP("MemFree" , MEM_SAMPLE_MEMFREE ),
479 MAP("SwapFree" , MEM_SAMPLE_SWAPFREE ),
480 MAP("Dirty" , MEM_SAMPLE_DIRTY ),
481 MAP("Writeback" , MEM_SAMPLE_WRITEBACK),
485 if (MEM_SAMPLE_MEMFREE <= id && id <= MEM_SAMPLE_WRITEBACK)
488 return "<invalid memory sample type>";
492 static int cmp_limits(const void *ptr1, const void *ptr2)
494 mem_limit_t *l1 = (mem_limit_t *)ptr1;
495 mem_limit_t *l2 = (mem_limit_t *)ptr2;
497 return l1->limit - l2->limit;
501 static int get_limit(lua_State *L, int idx, mem_limit_t *l)
505 if (lua_type(L, idx) != LUA_TTABLE)
508 lua_getfield(L, idx, "label");
510 if (lua_type(L, -1) == LUA_TSTRING)
511 l->label = (char *)lua_tostring(L, -1);
517 if (l->label == NULL)
520 lua_getfield(L, idx, "limit");
522 switch (lua_type(L, -1)) {
524 l->limit = lua_tonumber(L, -1);
527 l->limit = strtoull(lua_tostring(L, -1), &u, 10);
531 case 'k': l->limit *= 1024; break;
532 case 'M': l->limit *= 1024*1024; break;
533 case 'G': l->limit *= 1024*1024*1024; break;
534 default: goto invalid;
542 l->limit = (int64_t)(((uint64_t)-1) >> 1);
559 static int setup_limits(mem_watch_lua_t *w, lua_State *L, int limref)
561 int top = lua_gettop(L);
562 mem_limit_t *limits, l;
568 if (!mrp_lua_object_deref_value(w, L, limref, false)) {
569 mrp_log_error("Failed to dereference memory watch limit table.");
575 MRP_LUA_FOREACH_ALL(L, i, top + 1, ktype, kname, klen) {
576 if (ktype != LUA_TNUMBER) {
577 mrp_log_error("Invalid memory watch limits (non-numeric index).");
581 if (get_limit(L, -1, &l) != 0) {
582 mrp_log_error("Invalid memory watch limit #%zd.", klen);
586 if (mrp_reallocz(limits, nlimit, nlimit + 1) == NULL) {
587 mrp_log_error("Failed to allocate memory watch limits.");
591 limits[nlimit].label = mrp_strdup(l.label);
592 limits[nlimit].limit = l.limit;
594 if (limits[nlimit].label == NULL) {
595 mrp_log_error("memory watch limit with no or invalid label.");
602 if (mrp_reallocz(limits, nlimit, nlimit + 1) == NULL)
605 qsort(limits, nlimit, sizeof(limits[0]), cmp_limits);
607 cleanup_limits(w, L, w->limits, -1, w->limref);
615 cleanup_limits(w, L, limits, nlimit, LUA_NOREF);
621 static void cleanup_limits(mem_watch_lua_t *w, lua_State *L,
622 mem_limit_t *limits, int n, int limref)
627 mrp_lua_object_unref_value(w, L, limref);
632 for (i = 0, l = limits; (n > 0 && i < n) || (n < 0 && l->label); i++, l++)
641 * Uh... We misuse of the bindings registering macro by passing in
642 * (the empty) { NULL, NULL } for the bindings and use its optional
643 * class registering feature to register our class. Ugly..., we need
644 * to add a similar MURPHY_REGISTER_LUA_CLASSES macro and the necessary
648 MURPHY_REGISTER_LUA_BINDINGS(murphy, MEM_WATCH_LUA_CLASS, { NULL, NULL });