system-monitor: fix a few incorrect mrp_debug usages.
[profile/ivi/murphy.git] / src / plugins / system-monitor / mem-watch.c
1 /*
2  * Copyright (c) 2012, 2013, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
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.
16  *
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.
28  */
29
30 #include <errno.h>
31 #include <string.h>
32
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>
40
41 #include "mem-watch.h"
42
43 /*
44  * memory watch object
45  */
46
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
54
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,
61                                mrp_lua_value_t *v);
62 static int mem_watch_getmember(void *data, lua_State *L, int member,
63                                mrp_lua_value_t *v);
64 static int mem_watch_delete(lua_State *L);
65
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));
69
70 MRP_LUA_METHOD_LIST_TABLE(mem_watch_overrides,
71     MRP_LUA_OVERRIDE_CALL(mem_watch_no_constructor));
72
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));
85
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);
90
91 MRP_LUA_CLASS_CHECKER(mem_watch_lua_t, mem_watch_lua, MEM_WATCH_LUA_CLASS);
92
93 typedef enum {
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,
99     MEM_WATCH_MEMBER_RAW,
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;
106
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);
113
114
115 static int mem_watch_no_constructor(lua_State *L)
116 {
117     return luaL_error(L, "trying create a memory watch via constructor.");
118 }
119
120
121 mem_watch_lua_t *mem_watch_create(sysmon_lua_t *sm, int polling, lua_State *L)
122 {
123     mem_watch_lua_t *w;
124     char             e[256];
125
126     luaL_checktype(L, 2, LUA_TTABLE);
127
128     w = (mem_watch_lua_t *)mrp_lua_create_object(L, MEM_WATCH_LUA_CLASS,
129                                                  NULL, 0);
130
131     mrp_list_init(&w->hook);
132     w->sysmon  = sm;
133     w->polling = polling;
134     w->limref  = LUA_NOREF;
135     w->window  = -1;
136     w->value.a = -1;
137
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>");
141         return NULL;
142     }
143
144     if (w->window == -1 && w->value.a == -1)
145         w->window = MEM_WATCH_WINDOW;
146
147     if (w->window != -1)
148         recalc_smoothing(w);
149
150     return w;
151 }
152
153
154 static void mem_watch_destroy(void *data)
155 {
156     MRP_UNUSED(data);
157
158     mrp_debug("memory watch %p destroyed", data);
159
160     return;
161 }
162
163
164 static int mem_watch_delete(lua_State *L)
165 {
166     mem_watch_lua_t *w = mem_watch_lua_check(L, 1);
167
168     mrp_list_delete(&w->hook);
169     mrp_list_init(&w->hook);
170
171     sysmon_del_mem_watch(w->sysmon, w);
172
173     cleanup_limits(w, L, w->limits, -1, w->limref);
174     w->limits = NULL;
175     w->limref = LUA_NOREF;
176
177     mrp_funcbridge_unref(L, w->update);
178     mrp_funcbridge_unref(L, w->notify);
179     w->update = NULL;
180     w->notify = NULL;
181
182     return 0;
183 }
184
185
186 static void mem_watch_changed(void *data, lua_State *L, int member)
187 {
188     MRP_UNUSED(data);
189     MRP_UNUSED(L);
190     MRP_UNUSED(member);
191 }
192
193
194 static int mem_watch_setmember(void *data, lua_State *L, int member,
195                                mrp_lua_value_t *v)
196 {
197     mem_watch_lua_t  *w = (mem_watch_lua_t *)data;
198     mrp_funcbridge_t *f, **fptr;
199
200     switch (member) {
201     case MEM_WATCH_MEMBER_SAMPLE:
202         if ((w->sample = get_sample_id(v->str)) >= 0)
203             return 1;
204         else
205             mrp_log_error("Can't sample memory for unknown type '%s'.", v->str);
206         return 0;
207
208     case MEM_WATCH_MEMBER_WINDOW:
209         mem_watch_set_window(w, v->s32);
210         return 1;
211
212     case MEM_WATCH_MEMBER_ALPHA:
213         if (w->window == -1) {
214             w->value.a = v->dbl;
215             return 1;
216         }
217         else {
218             mrp_log_error("Can't set both window and alpha for memory watch.");
219             return 0;
220         }
221         break;
222
223     case MEM_WATCH_MEMBER_NOTIFY: fptr = &w->notify; goto set_bridge;
224     case MEM_WATCH_MEMBER_UPDATE: fptr = &w->update;
225     set_bridge:
226         if (!mrp_lua_object_deref_value(w, L, v->any, false))
227             return 0;
228         switch (lua_type(L, -1)) {
229         case LUA_TFUNCTION:
230             f = *fptr = mrp_funcbridge_create_luafunc(L, -1);
231             break;
232         default:
233             f = NULL;
234             break;
235         }
236         lua_pop(L, 1);
237         mrp_lua_object_unref_value(w, L, v->any);
238
239         return (f != NULL ? 1 : 0);
240
241     case MEM_WATCH_MEMBER_LIMITS:
242         return setup_limits(w, L, v->any);
243
244     default:
245         mrp_log_error("Trying to set read-only memory watch member #%d.",
246                       member);
247         return 0;
248     }
249 }
250
251
252 static int mem_watch_getmember(void *data, lua_State *L, int member,
253                                mrp_lua_value_t *v)
254 {
255     mem_watch_lua_t *w  = (mem_watch_lua_t *)data;
256
257     MRP_UNUSED(L);
258
259     switch (member) {
260     case MEM_WATCH_MEMBER_SAMPLE:
261         v->str = get_sample_name(w->sample);
262         return 1;
263
264     case MEM_WATCH_MEMBER_LIMITS:
265         v->any = w->limref;
266         return 1;
267
268     case MEM_WATCH_MEMBER_WINDOW:
269         v->s32 = w->window;
270         return 1;
271
272     case MEM_WATCH_MEMBER_ALPHA:
273         v->dbl = w->value.a;
274         return 1;
275
276     case MEM_WATCH_MEMBER_POLLING:
277         v->s32 = w->polling;
278         return 1;
279
280     case MEM_WATCH_MEMBER_VALUE:
281         v->s32 = w->value.S;
282         return 1;
283
284     case MEM_WATCH_MEMBER_RAW:
285         v->s32 = w->value.sample;
286         return 1;
287
288     case MEM_WATCH_MEMBER_CURRENT:
289         v->str = w->curr ? w->curr->label : "<unknown limit>";
290         return 1;
291
292     case MEM_WATCH_MEMBER_PREVIOUS:
293         v->str = w->prev ? w->prev->label : "<unknown limit>";
294         return 1;
295
296     default:
297         v->any = LUA_REFNIL;
298         return 1;
299     }
300 }
301
302
303 static ssize_t mem_watch_tostring(mrp_lua_tostr_mode_t mode, char *buf,
304                                   size_t size, lua_State *L, void *data)
305 {
306     mem_watch_lua_t *w = (mem_watch_lua_t *)data;
307
308     MRP_UNUSED(L);
309
310     switch (mode & MRP_LUA_TOSTR_MODEMASK) {
311     case MRP_LUA_TOSTR_LUA:
312     default:
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);
317     }
318
319 }
320
321
322 static inline double calculate_alpha(double L, double n)
323 {
324     double x, diff, min_x, min_diff;
325
326     min_diff = 1.0;
327     min_x    = 0.1;
328
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) {
332             min_x    = x;
333             min_diff = fabs(diff);
334         }
335     }
336
337     return min_x;
338 }
339
340
341 static inline void recalc_smoothing(mem_watch_lua_t *w)
342 {
343     double alpha /*= 1 - exp(- (1.0 * w->polling) / (1.0 * w->window))*/;
344     double n;
345
346     if (w->window > w->polling) {
347         n = (1.0 * w->window) / (1.0 * w->polling);
348         alpha = calculate_alpha(0.0005, n);
349     }
350     else
351         alpha = 1;
352
353     ewma_init(&w->value, alpha, w->value.S);
354 }
355
356
357 void mem_watch_set_polling(mem_watch_lua_t *w, int polling)
358 {
359     if (w->polling == polling)
360         return;
361
362     if (w->window != -1) {
363         w->polling = polling;
364         recalc_smoothing(w);
365     }
366 }
367
368
369 void mem_watch_set_window(mem_watch_lua_t *w, int window)
370 {
371     if (w->window == window)
372         return;
373
374     w->window = window;
375     recalc_smoothing(w);
376 }
377
378
379 int mem_watch_update(mem_watch_lua_t *w, lua_State *L)
380 {
381     int64_t sample = mem_get_sample(w->sample);
382     int     change;
383
384     if (sample < 0)
385         return FALSE;
386
387
388     if (w->update == NULL) {
389         double       value = ewma_add(&w->value, sample);
390         mem_limit_t *l;
391
392         mrp_debug("%s sample=%llu, estimate=%.2f", get_sample_name(w->sample),
393                   (unsigned long long)sample, value);
394
395         change = FALSE;
396         for (l = w->limits; l->label != NULL; l++) {
397             if (value <= l->limit) {
398                 if (w->curr != l) {
399                     w->prev = w->curr;
400                     w->curr = l;
401
402                     change = TRUE;
403                 }
404
405                 break;
406             }
407         }
408     }
409     else {
410         mrp_funcbridge_value_t args[2], rv;
411         char                            rt;
412
413         mrp_debug("%s sample=%llu", get_sample_name(w->sample),
414                   (unsigned long long)sample);
415
416         args[0].pointer = w;
417         args[1].integer = sample;
418
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);
423             change = FALSE;
424         }
425         else
426             change = ((rt == MRP_FUNCBRIDGE_BOOLEAN && rv.boolean) ||
427                       (rt == MRP_FUNCBRIDGE_INTEGER && rv.integer));
428     }
429
430     return change;
431 }
432
433
434 void mem_watch_notify(mem_watch_lua_t *w, lua_State *L)
435 {
436     mrp_funcbridge_value_t args[3], rv;
437     char                            rt;
438
439     MRP_UNUSED(L);
440
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>");
444
445     if (w->notify == NULL)
446         return;
447
448     args[0].pointer = w;
449     args[1].string  = w->prev ? w->prev->label : "<unknown>";
450     args[2].string  = w->curr ? w->curr->label : "<unknown>";
451
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);
457     }
458 }
459
460
461 static inline mem_sample_t get_sample_id(const char *name)
462 {
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 );
468 #undef MAP
469
470     return MEM_SAMPLE_INVALID;
471 }
472
473
474 static inline const char *get_sample_name(mem_sample_t id)
475 {
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),
482 #undef MAP
483     };
484
485     if (MEM_SAMPLE_MEMFREE <= id && id <= MEM_SAMPLE_WRITEBACK)
486         return names[id];
487     else
488         return "<invalid memory sample type>";
489 }
490
491
492 static int cmp_limits(const void *ptr1, const void *ptr2)
493 {
494     mem_limit_t *l1 = (mem_limit_t *)ptr1;
495     mem_limit_t *l2 = (mem_limit_t *)ptr2;
496
497     return l1->limit - l2->limit;
498 }
499
500
501 static int get_limit(lua_State *L, int idx, mem_limit_t *l)
502 {
503     char *u;
504
505     if (lua_type(L, idx) != LUA_TTABLE)
506         return -1;
507
508     lua_getfield(L, idx, "label");
509
510     if (lua_type(L, -1) == LUA_TSTRING)
511         l->label = (char *)lua_tostring(L, -1);
512     else
513         l->label = NULL;
514
515     lua_pop(L, 1);
516
517     if (l->label == NULL)
518         return -1;
519
520     lua_getfield(L, idx, "limit");
521
522     switch (lua_type(L, -1)) {
523     case LUA_TNUMBER:
524         l->limit = lua_tonumber(L, -1);
525         break;
526     case LUA_TSTRING:
527         l->limit = strtoull(lua_tostring(L, -1), &u, 10);
528         if (u[0]) {
529             if (!u[1]) {
530                 switch (u[0]) {
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;
535                 }
536             }
537             else
538                 goto invalid;
539         }
540         break;
541     case LUA_TNIL:
542         l->limit = (int64_t)(((uint64_t)-1) >> 1);
543         break;
544     default:
545     invalid:
546         l->limit = -1;
547         break;
548     }
549
550     lua_pop(L, 1);
551
552     if (l->limit >= 0)
553         return 0;
554     else
555         return -1;
556 }
557
558
559 static int setup_limits(mem_watch_lua_t *w, lua_State *L, int limref)
560 {
561     int          top = lua_gettop(L);
562     mem_limit_t *limits, l;
563     int          nlimit;
564     const char  *kname;
565     int          ktype, i;
566     size_t       klen;
567
568     if (!mrp_lua_object_deref_value(w, L, limref, false)) {
569         mrp_log_error("Failed to dereference memory watch limit table.");
570         return 0;
571     }
572
573     limits = NULL;
574     nlimit = 0;
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).");
578             goto fail;
579         }
580
581         if (get_limit(L, -1, &l) != 0) {
582             mrp_log_error("Invalid memory watch limit #%zd.", klen);
583             goto fail;
584         }
585
586         if (mrp_reallocz(limits, nlimit, nlimit + 1) == NULL) {
587             mrp_log_error("Failed to allocate memory watch limits.");
588             goto fail;
589         }
590
591         limits[nlimit].label = mrp_strdup(l.label);
592         limits[nlimit].limit = l.limit;
593
594         if (limits[nlimit].label == NULL) {
595             mrp_log_error("memory watch limit with no or invalid label.");
596             goto fail;
597         }
598         else
599             nlimit++;
600     }
601
602     if (mrp_reallocz(limits, nlimit, nlimit + 1) == NULL)
603         goto fail;
604
605     qsort(limits, nlimit, sizeof(limits[0]), cmp_limits);
606
607     cleanup_limits(w, L, w->limits, -1, w->limref);
608     w->limits = limits;
609     w->limref = limref;
610
611     lua_settop(L, top);
612     return 1;
613
614  fail:
615     cleanup_limits(w, L, limits, nlimit, LUA_NOREF);
616     lua_settop(L, top);
617     return 0;
618 }
619
620
621 static void cleanup_limits(mem_watch_lua_t *w, lua_State *L,
622                            mem_limit_t *limits, int n, int limref)
623 {
624     mem_limit_t *l;
625     int          i;
626
627     mrp_lua_object_unref_value(w, L, limref);
628
629     if (limits == NULL)
630         return;
631
632     for (i = 0, l = limits; (n > 0 && i < n) || (n < 0 && l->label); i++, l++)
633         mrp_free(l->label);
634
635     mrp_free(limits);
636 }
637
638
639
640 /*
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
645  * infra for it...
646  */
647
648 MURPHY_REGISTER_LUA_BINDINGS(murphy, MEM_WATCH_LUA_CLASS, { NULL, NULL });