From: Jaroslav Kysela Date: Sat, 12 Mar 2016 16:43:22 +0000 (+0100) Subject: memoryinfo: initial work X-Git-Tag: v4.2.1~874 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=41b5def2a26ce628fcb2b58adcb5b955214541cd;p=thirdparty%2Ftvheadend.git memoryinfo: initial work --- diff --git a/Makefile b/Makefile index ec95b5ea1..1e12a6084 100644 --- a/Makefile +++ b/Makefile @@ -213,7 +213,8 @@ SRCS-1 = \ src/profile.c \ src/bouquet.c \ src/lock.c \ - src/wizard.c + src/wizard.c \ + src/memoryinfo.c SRCS = $(SRCS-1) I18N-C = $(SRCS-1) diff --git a/src/api/api_config.c b/src/api/api_config.c index 6a85622cd..45a38843b 100644 --- a/src/api/api_config.c +++ b/src/api/api_config.c @@ -20,6 +20,7 @@ #include "tvheadend.h" #include "channels.h" #include "access.h" +#include "memoryinfo.h" #include "api.h" #include "config.h" @@ -31,6 +32,19 @@ api_config_capabilities(access_t *perm, void *opaque, const char *op, return 0; } +static void +api_memoryinfo_grid + ( access_t *perm, idnode_set_t *ins, api_idnode_grid_conf_t *conf, htsmsg_t *args ) +{ + memoryinfo_t *my; + + LIST_FOREACH(my, &memoryinfo_entries, my_link) { + if (my->my_update) + my->my_update(my); + idnode_set_add(ins, (idnode_t*)my, &conf->filter, perm->aa_lang_ui); + } +} + void api_config_init ( void ) { @@ -40,6 +54,8 @@ api_config_init ( void ) { "config/save", ACCESS_ADMIN, api_idnode_save_simple, &config }, { "tvhlog/config/load", ACCESS_ADMIN, api_idnode_load_simple, &tvhlog_conf }, { "tvhlog/config/save", ACCESS_ADMIN, api_idnode_save_simple, &tvhlog_conf }, + { "memoryinfo/class", ACCESS_ADMIN, api_idnode_class, (void *)&memoryinfo_class }, + { "memoryinfo/grid", ACCESS_ADMIN, api_idnode_grid, api_memoryinfo_grid }, { NULL }, }; diff --git a/src/atomic.h b/src/atomic.h index aba947618..42b839ce3 100644 --- a/src/atomic.h +++ b/src/atomic.h @@ -82,6 +82,21 @@ atomic_add_time_t(volatile time_t *ptr, time_t incr) * Atomic ADD and FETCH operation */ +static inline int64_t +atomic_pre_add_s64(volatile int64_t *ptr, int64_t incr) +{ +#if ENABLE_ATOMIC64 + return __sync_add_and_fetch(ptr, incr); +#else + int64_t ret; + pthread_mutex_lock(&atomic_lock); + *ptr += incr; + ret = *ptr; + pthread_mutex_unlock(&atomic_lock); + return ret; +#endif +} + static inline uint64_t atomic_pre_add_u64(volatile uint64_t *ptr, uint64_t incr) { @@ -97,6 +112,31 @@ atomic_pre_add_u64(volatile uint64_t *ptr, uint64_t incr) #endif } +/* + * Atomic ADD and FETCH operation with PEAK (MAX) + */ + +static inline int64_t +atomic_pre_add_s64_peak(volatile int64_t *ptr, int64_t incr, + volatile int64_t *peak) +{ +#if ENABLE_ATOMIC64 + int64_t ret = __sync_add_and_fetch(ptr, incr); + if (__sync_fetch_and_add(peak, 0) < ret) + __sync_lock_test_and_set(peak, ret); + return ret; +#else + int64_t ret; + pthread_mutex_lock(&atomic_lock); + *ptr += incr; + ret = *ptr; + if (*peak < ret) + *peak = ret; + pthread_mutex_unlock(&atomic_lock); + return ret; +#endif +} + /* * Atomic DEC operation */ @@ -247,3 +287,25 @@ atomic_set_time_t(volatile time_t *ptr, time_t val) { return atomic_exchange_time_t(ptr, val); } + +/* + * Atomic set operation + peak (MAX) + */ + +static inline int64_t +atomic_set_s64_peak(volatile int64_t *ptr, int64_t val, volatile int64_t *peak) +{ +#if ENABLE_ATOMIC64 + int64_t ret = atomic_exchange_s64(ptr, val); + if (val > atomic_get_s64(peak)) + atomic_set_s64(peak, val); + return ret; +#else + pthread_mutex_lock(&atomic_lock); + *ptr = val; + if (val > *peak) + *peak = val; + pthread_mutex_unlock(&atomic_lock); + return ret; +#endif +} diff --git a/src/channels.c b/src/channels.c index 6365c353c..a29bcc203 100644 --- a/src/channels.c +++ b/src/channels.c @@ -44,6 +44,7 @@ #include "htsbuf.h" #include "bouquet.h" #include "intlconv.h" +#include "memoryinfo.h" #define CHANNEL_BLANK_NAME "{name-not-set}" @@ -1064,7 +1065,30 @@ channel_delete ( channel_t *ch, int delconf ) free(ch); } +/** + * + */ +static void channels_memoryinfo_update(memoryinfo_t *my) +{ + channel_t *ch; + int64_t size = 0, count = 0; + + lock_assert(&global_lock); + CHANNEL_FOREACH(ch) { + size += sizeof(*ch); + size += tvh_strlen(ch->ch_epg_parent); + size += tvh_strlen(ch->ch_name); + size += tvh_strlen(ch->ch_icon); + count++; + } + memoryinfo_update(my, size, count); +} + +static memoryinfo_t channels_memoryinfo = { + .my_name = "Channels", + .my_update = channels_memoryinfo_update +}; /** * @@ -1078,6 +1102,7 @@ channel_init ( void ) char *s; RB_INIT(&channels); + memoryinfo_register(&channels_memoryinfo); /* Tags */ channel_tag_init(); @@ -1116,6 +1141,7 @@ channel_done ( void ) pthread_mutex_lock(&global_lock); while ((ch = RB_FIRST(&channels)) != NULL) channel_delete(ch, 0); + memoryinfo_unregister(&channels_memoryinfo); pthread_mutex_unlock(&global_lock); channel_tag_done(); } @@ -1485,6 +1511,31 @@ channel_tag_find_by_identifier(uint32_t id) { return NULL; } +/** + * + */ + +static void channel_tags_memoryinfo_update(memoryinfo_t *my) +{ + channel_tag_t *ct; + int64_t size = 0, count = 0; + + lock_assert(&global_lock); + TAILQ_FOREACH(ct, &channel_tags, ct_link) { + size += sizeof(*ct); + size += tvh_strlen(ct->ct_name); + size += tvh_strlen(ct->ct_comment); + size += tvh_strlen(ct->ct_icon); + count++; + } + memoryinfo_update(my, size, count); +} + +static memoryinfo_t channel_tags_memoryinfo = { + .my_name = "Channel tags", + .my_update = channel_tags_memoryinfo_update +}; + /** * Init / Done */ @@ -1495,6 +1546,7 @@ channel_tag_init ( void ) htsmsg_t *c, *m; htsmsg_field_t *f; + memoryinfo_register(&channel_tags_memoryinfo); TAILQ_INIT(&channel_tags); if ((c = hts_settings_load("channel/tag")) != NULL) { HTSMSG_FOREACH(f, c) { diff --git a/src/clock.h b/src/clock.h index 7d914e469..3035bae4d 100644 --- a/src/clock.h +++ b/src/clock.h @@ -1,6 +1,6 @@ /* - * Tvheadend - structures - * Copyright (C) 2007 Andreas Öman + * Tvheadend - clock support + * Copyright (C) 2016 Jaroslav Kysela * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/src/epgdb.c b/src/epgdb.c index 111960f24..aebcbc462 100644 --- a/src/epgdb.c +++ b/src/epgdb.c @@ -32,6 +32,7 @@ #include "epg.h" #include "epggrab.h" #include "config.h" +#include "memoryinfo.h" #define EPG_DB_VERSION 2 #define EPG_DB_ALLOC_STEP (1024*1024) @@ -146,6 +147,125 @@ _epgdb_v2_process( char **sect, htsmsg_t *m, epggrab_stats_t *stats ) } } +/* + * Memoryinfo + */ + +static void epg_memoryinfo_brands_update(memoryinfo_t *my) +{ + epg_object_t *eo; + epg_brand_t *eb; + int64_t size = 0, count = 0; + + RB_FOREACH(eo, &epg_brands, uri_link) { + eb = (epg_brand_t *)eo; + size += sizeof(*eb); + size += tvh_strlen(eb->uri); + size += lang_str_size(eb->title); + size += lang_str_size(eb->summary); + size += tvh_strlen(eb->image); + count++; + } + memoryinfo_update(my, size, count); +} + +static memoryinfo_t epg_memoryinfo_brands = { + .my_name = "EPG Brands", + .my_update = epg_memoryinfo_brands_update +}; + +static void epg_memoryinfo_seasons_update(memoryinfo_t *my) +{ + epg_object_t *eo; + epg_season_t *es; + int64_t size = 0, count = 0; + + RB_FOREACH(eo, &epg_seasons, uri_link) { + es = (epg_season_t *)eo; + size += sizeof(*es); + size += tvh_strlen(es->uri); + size += lang_str_size(es->summary); + size += tvh_strlen(es->image); + count++; + } + memoryinfo_update(my, size, count); +} + +static memoryinfo_t epg_memoryinfo_seasons = { + .my_name = "EPG Seasons", + .my_update = epg_memoryinfo_seasons_update +}; + +static void epg_memoryinfo_episodes_update(memoryinfo_t *my) +{ + epg_object_t *eo; + epg_episode_t *ee; + int64_t size = 0, count = 0; + + RB_FOREACH(eo, &epg_episodes, uri_link) { + ee = (epg_episode_t *)eo; + size += sizeof(*ee); + size += tvh_strlen(ee->uri); + size += lang_str_size(ee->title); + size += lang_str_size(ee->subtitle); + size += lang_str_size(ee->summary); + size += lang_str_size(ee->description); + size += tvh_strlen(ee->image); + size += tvh_strlen(ee->epnum.text); + count++; + } + memoryinfo_update(my, size, count); +} + +static memoryinfo_t epg_memoryinfo_episodes = { + .my_name = "EPG Episodes", + .my_update = epg_memoryinfo_episodes_update +}; + +static void epg_memoryinfo_serieslinks_update(memoryinfo_t *my) +{ + epg_object_t *eo; + epg_serieslink_t *es; + int64_t size = 0, count = 0; + + RB_FOREACH(eo, &epg_serieslinks, uri_link) { + es = (epg_serieslink_t *)eo; + size += sizeof(*es); + size += tvh_strlen(es->uri); + count++; + } + memoryinfo_update(my, size, count); +} + +static memoryinfo_t epg_memoryinfo_serieslinks = { + .my_name = "EPG Series Links", + .my_update = epg_memoryinfo_serieslinks_update +}; + +static void epg_memoryinfo_broadcasts_update(memoryinfo_t *my) +{ + channel_t *ch; + epg_broadcast_t *ebc; + int64_t size = 0, count = 0; + + CHANNEL_FOREACH(ch) { + if (ch->ch_epg_parent) continue; + RB_FOREACH(ebc, &ch->ch_epg_schedule, sched_link) { + size += sizeof(*ebc); + size += tvh_strlen(ebc->uri); + size += lang_str_size(ebc->summary); + size += lang_str_size(ebc->description); + count++; + } + } + memoryinfo_update(my, size, count); +} + +static memoryinfo_t epg_memoryinfo_broadcasts = { + .my_name = "EPG Broadcasts", + .my_update = epg_memoryinfo_broadcasts_update +}; + /* * Recovery */ @@ -170,6 +290,12 @@ void epg_init ( void ) struct sigaction act, oldact; char *sect = NULL; + memoryinfo_register(&epg_memoryinfo_brands); + memoryinfo_register(&epg_memoryinfo_seasons); + memoryinfo_register(&epg_memoryinfo_episodes); + memoryinfo_register(&epg_memoryinfo_serieslinks); + memoryinfo_register(&epg_memoryinfo_broadcasts); + /* Find the right file (and version) */ while (fd < 0 && ver > 0) { fd = hts_settings_open_file(0, "epgdb.v%d", ver); @@ -298,6 +424,11 @@ void epg_done ( void ) CHANNEL_FOREACH(ch) epg_channel_unlink(ch); epg_skel_done(); + memoryinfo_unregister(&epg_memoryinfo_brands); + memoryinfo_unregister(&epg_memoryinfo_seasons); + memoryinfo_unregister(&epg_memoryinfo_episodes); + memoryinfo_unregister(&epg_memoryinfo_serieslinks); + memoryinfo_unregister(&epg_memoryinfo_broadcasts); pthread_mutex_unlock(&global_lock); } diff --git a/src/idnode.c b/src/idnode.c index 90b9a01a8..c7b4fa2b1 100644 --- a/src/idnode.c +++ b/src/idnode.c @@ -434,6 +434,33 @@ idnode_get_s64 return 1; } +/* + * Get field as signed 64-bit int + */ +int +idnode_get_s64_atomic + ( idnode_t *self, const char *key, int64_t *s64 ) +{ + const property_t *p = idnode_find_prop(self, key); + if (p) { + const void *ptr; + if (p->islist) + return 1; + else if (p->get) + ptr = p->get(self); + else + ptr = ((void*)self) + p->off; + switch (p->type) { + case PT_S64_ATOMIC: + *s64 = atomic_get_s64((int64_t*)ptr); + return 0; + default: + break; + } + } + return 1; +} + /* * Get field as double */ @@ -733,6 +760,17 @@ idnode_cmp_sort return safecmp(s64b, s64a); } break; + case PT_S64_ATOMIC: + { + int64_t s64a = 0, s64b = 0; + idnode_get_s64_atomic(ina, sort->key, &s64a); + idnode_get_s64_atomic(inb, sort->key, &s64b); + if (sort->dir == IS_ASC) + return safecmp(s64a, s64b); + else + return safecmp(s64b, s64a); + } + break; case PT_DBL: { double dbla = 0, dblb = 0; diff --git a/src/idnode.h b/src/idnode.h index 380cfc850..f859fbb92 100644 --- a/src/idnode.h +++ b/src/idnode.h @@ -269,6 +269,7 @@ int idnode_list_set2 ( idnode_t *in2, idnode_list_head_t *in2_list, const char *idnode_get_str (idnode_t *self, const char *key ); int idnode_get_u32 (idnode_t *self, const char *key, uint32_t *u32); int idnode_get_s64 (idnode_t *self, const char *key, int64_t *s64); +int idnode_get_s64_atomic (idnode_t *self, const char *key, int64_t *s64); int idnode_get_dbl (idnode_t *self, const char *key, double *dbl); int idnode_get_bool(idnode_t *self, const char *key, int *b); int idnode_get_time(idnode_t *self, const char *key, time_t *tm); diff --git a/src/input/mpegts/mpegts_service.c b/src/input/mpegts/mpegts_service.c index 4bdc09592..db7fd4aa9 100644 --- a/src/input/mpegts/mpegts_service.c +++ b/src/input/mpegts/mpegts_service.c @@ -696,6 +696,18 @@ mpegts_service_satip_source ( service_t *t ) return mn ? mn->mn_satip_source : -1; } +static void +mpegts_service_memoryinfo ( service_t *t, int64_t *size ) +{ + mpegts_service_t *ms = (mpegts_service_t*)t; + *size += sizeof(*ms); + *size += tvh_strlen(ms->s_nicename); + *size += tvh_strlen(ms->s_dvb_svcname); + *size += tvh_strlen(ms->s_dvb_provider); + *size += tvh_strlen(ms->s_dvb_cridauth); + *size += tvh_strlen(ms->s_dvb_charset); +} + /* ************************************************************************** * Creation/Location * *************************************************************************/ @@ -749,6 +761,7 @@ mpegts_service_create0 s->s_channel_icon = mpegts_service_channel_icon; s->s_mapped = mpegts_service_mapped; s->s_satip_source = mpegts_service_satip_source; + s->s_memoryinfo = mpegts_service_memoryinfo; pthread_mutex_lock(&s->s_stream_mutex); service_make_nicename((service_t*)s); @@ -1019,6 +1032,7 @@ mpegts_service_create_raw ( mpegts_mux_t *mm ) s->s_link = mpegts_service_link; s->s_unlink = mpegts_service_unlink; s->s_satip_source = mpegts_service_satip_source; + s->s_memoryinfo = mpegts_service_memoryinfo; pthread_mutex_lock(&s->s_stream_mutex); free(s->s_nicename); diff --git a/src/lang_str.c b/src/lang_str.c index eb040de50..fd9cf0b0c 100644 --- a/src/lang_str.c +++ b/src/lang_str.c @@ -311,6 +311,20 @@ int lang_str_empty(lang_str_t* str) { return strempty(lang_str_get(str, NULL)); } +size_t lang_str_size(const lang_str_t *ls) +{ + lang_str_ele_t *e; + size_t size; + if (!ls) return 0; + size = sizeof(*ls); + RB_FOREACH(e, ls, link) { + size += sizeof(*e); + size += tvh_strlen(e->str); + size += tvh_strlen(e->lang); + } + return size; +} + void lang_str_done( void ) { SKEL_FREE(lang_str_ele_skel); diff --git a/src/lang_str.h b/src/lang_str.h index d8e3b5f6c..d31dbddfd 100644 --- a/src/lang_str.h +++ b/src/lang_str.h @@ -68,6 +68,9 @@ int lang_str_compare ( const lang_str_t *ls1, const lang_str_t *ls2 int strempty(const char* c); int lang_str_empty(lang_str_t* str); +/* Size in bytes */ +size_t lang_str_size ( const lang_str_t *ls ); + /* Init/Done */ void lang_str_done( void ); diff --git a/src/main.c b/src/main.c index 3ab20345e..7c191a969 100644 --- a/src/main.c +++ b/src/main.c @@ -71,6 +71,8 @@ #include "profile.h" #include "bouquet.h" #include "tvhtime.h" +#include "packet.h" +#include "memoryinfo.h" #ifdef PLATFORM_LINUX #include @@ -186,6 +188,7 @@ static pthread_cond_t gtimer_cond; static TAILQ_HEAD(, tasklet) tasklets; static tvh_cond_t tasklet_cond; static pthread_t tasklet_tid; +static memoryinfo_t tasklet_memoryinfo = { .my_name = "Tasklet" }; static void handle_sigpipe(int x) @@ -364,6 +367,7 @@ tasklet_arm_alloc(tsk_callback_t *callback, void *opaque) { tasklet_t *tsk = calloc(1, sizeof(*tsk)); if (tsk) { + memoryinfo_alloc(&tasklet_memoryinfo, sizeof(*tsk)); tsk->tsk_allocated = 1; tasklet_arm(tsk, callback, opaque); } @@ -422,8 +426,10 @@ tasklet_flush() TAILQ_REMOVE(&tasklets, tsk, tsk_link); tsk->tsk_callback(tsk->tsk_opaque, 1); tsk->tsk_callback = NULL; - if (tsk->tsk_allocated) + if (tsk->tsk_allocated) { + memoryinfo_free(&tasklet_memoryinfo, sizeof(*tsk)); free(tsk); + } } pthread_mutex_unlock(&tasklet_lock); @@ -453,8 +459,10 @@ tasklet_thread ( void *aux ) tsk_cb = tsk->tsk_callback; opaque = tsk->tsk_opaque; tsk->tsk_callback = NULL; - if (tsk->tsk_allocated) + if (tsk->tsk_allocated) { + memoryinfo_free(&tasklet_memoryinfo, sizeof(*tsk)); free(tsk); + } /* now, the callback can be safely called */ if (tsk_cb) { pthread_mutex_unlock(&tasklet_lock); @@ -1125,6 +1133,12 @@ main(int argc, char **argv) spawn_init(); config_init(opt_nobackup == 0); + /* Memoryinfo */ + memoryinfo_register(&tasklet_memoryinfo); + memoryinfo_register(&pkt_memoryinfo); + memoryinfo_register(&pktbuf_memoryinfo); + memoryinfo_register(&pktref_memoryinfo); + /** * Initialize subsystems */ diff --git a/src/memoryinfo.c b/src/memoryinfo.c new file mode 100644 index 000000000..d76a914e4 --- /dev/null +++ b/src/memoryinfo.c @@ -0,0 +1,76 @@ +/* + * Tvheadend - memory info support + * Copyright (C) 2016 Jaroslav Kysela + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "tvheadend.h" +#include "idnode.h" +#include "access.h" +#include "memoryinfo.h" + +struct memoryinfo_list memoryinfo_entries; + +static const char * +service_class_get_title ( idnode_t *self, const char *lang ) +{ + return ((memoryinfo_t *)self)->my_name; +} + +const idclass_t memoryinfo_class = { + .ic_class = "memoryinfo", + .ic_caption = N_("Memory information"), + .ic_event = "memoryinfo", + .ic_perm_def = ACCESS_ADMIN, + .ic_get_title = service_class_get_title, + .ic_properties = (const property_t[]){ + { + .type = PT_STR, + .id = "name", + .name = N_("Name"), + .off = offsetof(memoryinfo_t, my_name), + .opts = PO_RDONLY | PO_NOSAVE, + }, + { + .type = PT_S64_ATOMIC, + .id = "size", + .name = N_("Size"), + .off = offsetof(memoryinfo_t, my_size), + .opts = PO_RDONLY | PO_NOSAVE, + }, + { + .type = PT_S64_ATOMIC, + .id = "peak_size", + .name = N_("Peak size"), + .off = offsetof(memoryinfo_t, my_peak_size), + .opts = PO_RDONLY | PO_NOSAVE, + }, + { + .type = PT_S64_ATOMIC, + .id = "count", + .name = N_("Counf of objects"), + .off = offsetof(memoryinfo_t, my_count), + .opts = PO_RDONLY | PO_NOSAVE, + }, + { + .type = PT_S64_ATOMIC, + .id = "peak_count", + .name = N_("Peak counf of objects"), + .off = offsetof(memoryinfo_t, my_peak_count), + .opts = PO_RDONLY | PO_NOSAVE, + }, + {} + } +}; diff --git a/src/memoryinfo.h b/src/memoryinfo.h new file mode 100644 index 000000000..44d127f6b --- /dev/null +++ b/src/memoryinfo.h @@ -0,0 +1,79 @@ +/* + * Tvheadend - memory info support + * Copyright (C) 2016 Jaroslav Kysela + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef TVHEADEND_MEMORYINFO_H +#define TVHEADEND_MEMORYINFO_H + +#include "idnode.h" + +struct memoryinfo; + +typedef void (*memoryinfo_cb_t)(struct memoryinfo *my); + +typedef struct memoryinfo { + idnode_t my_idnode; + LIST_ENTRY(memoryinfo) my_link; + const char *my_name; + void *my_opaque; + memoryinfo_cb_t my_update; + int64_t my_size; + int64_t my_peak_size; + int64_t my_count; + int64_t my_peak_count; +} memoryinfo_t; + +extern struct memoryinfo_list memoryinfo_entries; +extern const idclass_t memoryinfo_class; + +static inline void memoryinfo_register(memoryinfo_t *my) +{ + LIST_INSERT_HEAD(&memoryinfo_entries, my, my_link); + idnode_insert(&my->my_idnode, NULL, &memoryinfo_class, 0); +} + +static inline void memoryinfo_unregister(memoryinfo_t *my) +{ + LIST_REMOVE(my, my_link); + idnode_unlink(&my->my_idnode); +} + +static inline void memoryinfo_update(memoryinfo_t *my, int64_t size, int64_t count) +{ + atomic_set_s64_peak(&my->my_size, size, &my->my_peak_size); + atomic_set_s64_peak(&my->my_count, count, &my->my_peak_count); +} + +static inline void memoryinfo_alloc(memoryinfo_t *my, int64_t size) +{ + atomic_pre_add_s64_peak(&my->my_size, size, &my->my_peak_size); + atomic_pre_add_s64_peak(&my->my_count, 1, &my->my_peak_count); +} + +static inline void memoryinfo_append(memoryinfo_t *my, int64_t size) +{ + atomic_pre_add_s64_peak(&my->my_size, size, &my->my_peak_size); +} + +static inline void memoryinfo_free(memoryinfo_t *my, int64_t size) +{ + atomic_dec_s64(&my->my_size, size); + atomic_dec_s64(&my->my_count, 1); +} + + +#endif /* TVHEADEND_MEMORYINFO_H */ diff --git a/src/muxer/muxer_pass.c b/src/muxer/muxer_pass.c index 87a97cf7b..c569bd81e 100644 --- a/src/muxer/muxer_pass.c +++ b/src/muxer/muxer_pass.c @@ -436,14 +436,14 @@ pass_muxer_write_ts(muxer_t *m, pktbuf_t *pb) { pass_muxer_t *pm = (pass_muxer_t*)m; int l, pid; - uint8_t *tsb, *pkt = pb->pb_data; - size_t len = pb->pb_size, len2; + uint8_t *tsb, *pkt = pktbuf_ptr(pb); + size_t len = pktbuf_len(pb), len2; /* Rewrite PAT/PMT in operation */ if (pm->m_config.m_rewrite_pat || pm->m_config.m_rewrite_pmt || pm->pm_rewrite_sdt || pm->pm_rewrite_eit) { - for (tsb = pb->pb_data, len2 = pb->pb_size, len = 0; + for (tsb = pktbuf_ptr(pb), len2 = pktbuf_len(pb), len = 0; len2 > 0; tsb += l, len2 -= l) { pid = (tsb[1] & 0x1f) << 8 | tsb[2]; diff --git a/src/packet.c b/src/packet.c index 917550663..a73663ec9 100644 --- a/src/packet.c +++ b/src/packet.c @@ -21,21 +21,29 @@ #include "packet.h" #include "string.h" #include "atomic.h" +#include "memoryinfo.h" #ifndef PKTBUF_DATA_ALIGN #define PKTBUF_DATA_ALIGN 64 #endif +memoryinfo_t pkt_memoryinfo = { .my_name = "Packets" }; +memoryinfo_t pktbuf_memoryinfo = { .my_name = "Packet buffers" }; +memoryinfo_t pktref_memoryinfo = { .my_name = "Packet references" }; + /* * */ static void pkt_destroy(th_pkt_t *pkt) { - pktbuf_ref_dec(pkt->pkt_payload); - pktbuf_ref_dec(pkt->pkt_meta); + if (pkt) { + pktbuf_ref_dec(pkt->pkt_payload); + pktbuf_ref_dec(pkt->pkt_meta); - free(pkt); + free(pkt); + memoryinfo_free(&pkt_memoryinfo, sizeof(*pkt)); + } } @@ -49,14 +57,63 @@ pkt_alloc(const void *data, size_t datalen, int64_t pts, int64_t dts) th_pkt_t *pkt; pkt = calloc(1, sizeof(th_pkt_t)); - if(datalen) - pkt->pkt_payload = pktbuf_alloc(data, datalen); - pkt->pkt_dts = dts; - pkt->pkt_pts = pts; - pkt->pkt_refcount = 1; + if (pkt) { + if(datalen) + pkt->pkt_payload = pktbuf_alloc(data, datalen); + pkt->pkt_dts = dts; + pkt->pkt_pts = pts; + pkt->pkt_refcount = 1; + memoryinfo_alloc(&pkt_memoryinfo, sizeof(*pkt)); + } return pkt; } + +/** + * + */ +th_pkt_t * +pkt_copy_shallow(th_pkt_t *pkt) +{ + th_pkt_t *n = malloc(sizeof(th_pkt_t)); + + if (n) { + *n = *pkt; + + n->pkt_refcount = 1; + + pktbuf_ref_inc(n->pkt_meta); + pktbuf_ref_inc(n->pkt_payload); + + memoryinfo_alloc(&pkt_memoryinfo, sizeof(*pkt)); + } + + return n; +} + + +/** + * + */ +th_pkt_t * +pkt_copy_nodata(th_pkt_t *pkt) +{ + th_pkt_t *n = malloc(sizeof(th_pkt_t)); + + if (n) { + *n = *pkt; + + n->pkt_refcount = 1; + + n->pkt_meta = n->pkt_payload = NULL; + + memoryinfo_alloc(&pkt_memoryinfo, sizeof(*pkt)); + } + + return n; +} + + /** * */ @@ -95,10 +152,13 @@ pktref_clear_queue(struct th_pktref_queue *q) { th_pktref_t *pr; - while((pr = TAILQ_FIRST(q)) != NULL) { - TAILQ_REMOVE(q, pr, pr_link); - pkt_ref_dec(pr->pr_pkt); - free(pr); + if (q) { + while((pr = TAILQ_FIRST(q)) != NULL) { + TAILQ_REMOVE(q, pr, pr_link); + pkt_ref_dec(pr->pr_pkt); + free(pr); + memoryinfo_free(&pktref_memoryinfo, sizeof(*pr)); + } } } @@ -110,8 +170,11 @@ void pktref_enqueue(struct th_pktref_queue *q, th_pkt_t *pkt) { th_pktref_t *pr = malloc(sizeof(th_pktref_t)); - pr->pr_pkt = pkt; - TAILQ_INSERT_TAIL(q, pr, pr_link); + if (pr) { + pr->pr_pkt = pkt; + TAILQ_INSERT_TAIL(q, pr, pr_link); + memoryinfo_alloc(&pktref_memoryinfo, sizeof(*pr)); + } } @@ -121,27 +184,13 @@ pktref_enqueue(struct th_pktref_queue *q, th_pkt_t *pkt) void pktref_remove(struct th_pktref_queue *q, th_pktref_t *pr) { - TAILQ_REMOVE(q, pr, pr_link); - pkt_ref_dec(pr->pr_pkt); - free(pr); -} - - -/** - * - */ -th_pkt_t * -pkt_copy_shallow(th_pkt_t *pkt) -{ - th_pkt_t *n = malloc(sizeof(th_pkt_t)); - *n = *pkt; - - n->pkt_refcount = 1; - - pktbuf_ref_inc(n->pkt_meta); - pktbuf_ref_inc(n->pkt_payload); - - return n; + if (pr) { + if (q) + TAILQ_REMOVE(q, pr, pr_link); + pkt_ref_dec(pr->pr_pkt); + free(pr); + memoryinfo_free(&pktref_memoryinfo, sizeof(*pr)); + } } @@ -152,7 +201,10 @@ th_pktref_t * pktref_create(th_pkt_t *pkt) { th_pktref_t *pr = malloc(sizeof(th_pktref_t)); - pr->pr_pkt = pkt; + if (pr) { + pr->pr_pkt = pkt; + memoryinfo_alloc(&pktref_memoryinfo, sizeof(*pr)); + } return pr; } @@ -160,11 +212,22 @@ pktref_create(th_pkt_t *pkt) * */ +void +pktbuf_destroy(pktbuf_t *pb) +{ + if (pb) { + memoryinfo_free(&pktbuf_memoryinfo, sizeof(*pb) + pb->pb_size); + free(pb->pb_data); + free(pb); + } +} + void pktbuf_ref_dec(pktbuf_t *pb) { if (pb) { if((atomic_add(&pb->pb_refcount, -1)) == 1) { + memoryinfo_free(&pktbuf_memoryinfo, sizeof(*pb) + pb->pb_size); free(pb->pb_data); free(pb); } @@ -185,17 +248,23 @@ pktbuf_t * pktbuf_alloc(const void *data, size_t size) { pktbuf_t *pb = malloc(sizeof(pktbuf_t)); + + if (pb == NULL) return NULL; pb->pb_refcount = 1; pb->pb_size = size; pb->pb_err = 0; - if(size > 0) { pb->pb_data = malloc(size); - if(data != NULL) - memcpy(pb->pb_data, data, size); + if (pb->pb_data != NULL) { + if (data != NULL) + memcpy(pb->pb_data, data, size); + } else { + pb->pb_size = 0; + } } else { pb->pb_data = NULL; } + memoryinfo_alloc(&pktbuf_memoryinfo, sizeof(*pb) + pb->pb_size); return pb; } @@ -203,19 +272,27 @@ pktbuf_t * pktbuf_make(void *data, size_t size) { pktbuf_t *pb = malloc(sizeof(pktbuf_t)); - pb->pb_refcount = 1; - pb->pb_size = size; - pb->pb_data = data; + if (pb) { + pb->pb_refcount = 1; + pb->pb_size = size; + pb->pb_data = data; + memoryinfo_alloc(&pktbuf_memoryinfo, sizeof(*pb) + pb->pb_size); + } return pb; } pktbuf_t * pktbuf_append(pktbuf_t *pb, const void *data, size_t size) { + void *ndata; if (pb == NULL) return pktbuf_alloc(data, size); - pb->pb_data = realloc(pb->pb_data, pb->pb_size + size); - memcpy(pb->pb_data + pb->pb_size, data, size); - pb->pb_size += size; + ndata = realloc(pb->pb_data, pb->pb_size + size); + if (ndata) { + pb->pb_data = ndata; + memcpy(ndata + pb->pb_size, data, size); + pb->pb_size += size; + memoryinfo_append(&pktbuf_memoryinfo, size); + } return pb; } diff --git a/src/packet.h b/src/packet.h index 982518043..c30d434e7 100644 --- a/src/packet.h +++ b/src/packet.h @@ -19,6 +19,8 @@ #ifndef PACKET_H_ #define PACKET_H_ +struct memoryinfo; + /** * Packet buffer */ @@ -80,6 +82,13 @@ typedef struct th_pktref { } th_pktref_t; +/** + * + */ +extern struct memoryinfo pkt_memoryinfo; +extern struct memoryinfo pktbuf_memoryinfo; +extern struct memoryinfo pktref_memoryinfo; + /** * */ @@ -101,6 +110,8 @@ th_pkt_t *pkt_alloc(const void *data, size_t datalen, int64_t pts, int64_t dts); th_pkt_t *pkt_copy_shallow(th_pkt_t *pkt); +th_pkt_t *pkt_copy_nodata(th_pkt_t *pkt); + th_pktref_t *pktref_create(th_pkt_t *pkt); /* @@ -109,6 +120,8 @@ th_pktref_t *pktref_create(th_pkt_t *pkt); void pktbuf_ref_dec(pktbuf_t *pb); +void pktbuf_destroy(pktbuf_t *pb); + pktbuf_t *pktbuf_ref_inc(pktbuf_t *pb); pktbuf_t *pktbuf_alloc(const void *data, size_t size); diff --git a/src/parsers/parser_avc.c b/src/parsers/parser_avc.c index 94fc1cd8b..10ed33c3d 100644 --- a/src/parsers/parser_avc.c +++ b/src/parsers/parser_avc.c @@ -189,11 +189,7 @@ th_pkt_t * avc_convert_pkt(th_pkt_t *src) { sbuf_t payload; - th_pkt_t *pkt = malloc(sizeof(*pkt)); - - *pkt = *src; - pkt->pkt_refcount = 1; - pkt->pkt_meta = NULL; + th_pkt_t *pkt = pkt_copy_nodata(src); sbuf_init(&payload); diff --git a/src/parsers/parser_hevc.c b/src/parsers/parser_hevc.c index eb1b8b703..cc212b0ec 100644 --- a/src/parsers/parser_hevc.c +++ b/src/parsers/parser_hevc.c @@ -1178,11 +1178,7 @@ th_pkt_t * hevc_convert_pkt(th_pkt_t *src) { sbuf_t payload; - th_pkt_t *pkt = malloc(sizeof(*pkt)); - - *pkt = *src; - pkt->pkt_refcount = 1; - pkt->pkt_meta = NULL; + th_pkt_t *pkt = pkt_copy_nodata(src); sbuf_init(&payload); diff --git a/src/prop.c b/src/prop.c index 64c0be815..7ffb5821c 100644 --- a/src/prop.c +++ b/src/prop.c @@ -42,6 +42,7 @@ const static struct strtab typetab[] = { { "u16", PT_U16 }, { "u32", PT_U32 }, { "s64", PT_S64 }, + { "s64", PT_S64_ATOMIC }, { "dbl", PT_DBL }, { "time", PT_TIME }, { "langstr", PT_LANGSTR }, @@ -165,6 +166,8 @@ prop_write_values PROP_UPDATE(s64, int64_t); break; } + case PT_S64_ATOMIC: + break; case PT_DBL: { if (htsmsg_field_get_dbl(f, &dbl)) continue; @@ -310,6 +313,9 @@ prop_read_value } else htsmsg_add_s64(m, name, *(int64_t *)val); break; + case PT_S64_ATOMIC: + htsmsg_add_s64(m, name, atomic_get_s64((int64_t *)val)); + break; case PT_STR: if (optmask & PO_LOCALE) { if ((s = *(const char **)val)) @@ -438,6 +444,7 @@ prop_serialize_value htsmsg_add_u32(m, "default", pl->def.u32); break; case PT_S64: + case PT_S64_ATOMIC: htsmsg_add_s64(m, "default", pl->def.s64); break; case PT_DBL: diff --git a/src/prop.h b/src/prop.h index a3f95f47c..404b51de4 100644 --- a/src/prop.h +++ b/src/prop.h @@ -35,6 +35,7 @@ typedef enum { PT_U16, PT_U32, PT_S64, + PT_S64_ATOMIC, PT_DBL, PT_TIME, PT_LANGSTR, diff --git a/src/service.c b/src/service.c index bc4b59ec1..f8557a2d4 100644 --- a/src/service.c +++ b/src/service.c @@ -48,6 +48,7 @@ #include "access.h" #include "esfilter.h" #include "bouquet.h" +#include "memoryinfo.h" static void service_data_timeout(void *aux); static void service_class_delete(struct idnode *self); @@ -936,6 +937,13 @@ service_provider_name ( service_t *s ) return NULL; } +void +service_memoryinfo ( service_t *s, int64_t *size ) +{ + *size += sizeof(*s); + *size += tvh_strlen(s->s_nicename); +} + /** * Create and initialize a new service struct */ @@ -968,6 +976,7 @@ service_create0 t->s_channel_number = service_channel_number; t->s_channel_name = service_channel_name; t->s_provider_name = service_provider_name; + t->s_memoryinfo = service_memoryinfo; TAILQ_INIT(&t->s_components); TAILQ_INIT(&t->s_filt_components); t->s_last_pid = -1; @@ -1483,9 +1492,36 @@ service_saver(void *aux) */ pthread_t service_saver_tid; +static void services_memoryinfo_update(memoryinfo_t *my) +{ + service_t *t; + int64_t size = 0, count = 0; + + lock_assert(&global_lock); + TAILQ_FOREACH(t, &service_all, s_all_link) { + t->s_memoryinfo(t, &size); + count++; + } + TAILQ_FOREACH(t, &service_raw_all, s_all_link) { + t->s_memoryinfo(t, &size); + count++; + } + TAILQ_FOREACH(t, &service_raw_remove, s_all_link) { + t->s_memoryinfo(t, &size); + count++; + } + memoryinfo_update(my, size, count); +} + +static memoryinfo_t services_memoryinfo = { + .my_name = "Services", + .my_update = services_memoryinfo_update +}; + void service_init(void) { + memoryinfo_register(&services_memoryinfo); TAILQ_INIT(&pending_save_queue); TAILQ_INIT(&service_all); TAILQ_INIT(&service_raw_all); @@ -1508,6 +1544,7 @@ service_done(void) pthread_mutex_lock(&global_lock); while ((t = TAILQ_FIRST(&service_raw_remove)) != NULL) service_destroy(t, 0); + memoryinfo_unregister(&services_memoryinfo); pthread_mutex_unlock(&global_lock); } diff --git a/src/service.h b/src/service.h index 47ea5cda8..32bff5c6b 100644 --- a/src/service.h +++ b/src/service.h @@ -330,6 +330,8 @@ typedef struct service { int (*s_satip_source)(struct service *t); + void (*s_memoryinfo)(struct service *t, int64_t *size); + /** * Channel info */ @@ -623,6 +625,8 @@ int64_t service_get_channel_number (service_t *s); const char *service_get_channel_icon (service_t *s); const char *service_get_channel_epgid (service_t *s); +void service_memoryinfo (service_t *s, int64_t *size); + void service_mapped (service_t *s); #endif // SERVICE_H__ diff --git a/src/streaming.c b/src/streaming.c index d5273b60a..e6925091a 100644 --- a/src/streaming.c +++ b/src/streaming.c @@ -55,11 +55,11 @@ streaming_message_data_size(streaming_message_t *sm) if (sm->sm_type == SMT_PACKET) { th_pkt_t *pkt = sm->sm_data; if (pkt && pkt->pkt_payload) - return pkt->pkt_payload->pb_size; + return pktbuf_len(pkt->pkt_payload); } else if (sm->sm_type == SMT_MPEGTS) { pktbuf_t *pkt_payload = sm->sm_data; if (pkt_payload) - return pkt_payload->pb_size; + return pktbuf_len(pkt_payload); } return 0; } diff --git a/src/subscriptions.c b/src/subscriptions.c index 6780ba338..a889bd35c 100644 --- a/src/subscriptions.c +++ b/src/subscriptions.c @@ -485,11 +485,11 @@ subscription_input_direct(void *opauqe, streaming_message_t *sm) th_pkt_t *pkt = sm->sm_data; atomic_add(&s->ths_total_err, pkt->pkt_err); if (pkt->pkt_payload) - subscription_add_bytes_in(s, pkt->pkt_payload->pb_size); + subscription_add_bytes_in(s, pktbuf_len(pkt->pkt_payload)); } else if(sm->sm_type == SMT_MPEGTS) { pktbuf_t *pb = sm->sm_data; atomic_add(&s->ths_total_err, pb->pb_err); - subscription_add_bytes_in(s, pb->pb_size); + subscription_add_bytes_in(s, pktbuf_len(pb)); } /* Pass to output */ diff --git a/src/timeshift/timeshift_reader.c b/src/timeshift/timeshift_reader.c index ca9663a25..9c25c3bc5 100644 --- a/src/timeshift/timeshift_reader.c +++ b/src/timeshift/timeshift_reader.c @@ -129,10 +129,10 @@ static ssize_t _read_pktbuf ( timeshift_file_t *tsf, int fd, pktbuf_t **pktbuf ) /* Data */ *pktbuf = pktbuf_alloc(NULL, sz); - r = _read_buf(tsf, fd, (*pktbuf)->pb_data, sz); + r = _read_buf(tsf, fd, pktbuf_ptr(*pktbuf), sz); if (r != sz) { - free((*pktbuf)->pb_data); - free(*pktbuf); + pktbuf_destroy(*pktbuf); + *pktbuf = NULL; return r < 0 ? -1 : 0; } cnt += r; diff --git a/src/timeshift/timeshift_writer.c b/src/timeshift/timeshift_writer.c index 37eaa0d9f..9cd6221eb 100644 --- a/src/timeshift/timeshift_writer.c +++ b/src/timeshift/timeshift_writer.c @@ -143,7 +143,7 @@ static int _write_pktbuf ( timeshift_file_t *tsf, pktbuf_t *pktbuf ) if (pktbuf) { ret = err = _write(tsf, &pktbuf->pb_size, sizeof(pktbuf->pb_size)); if (err < 0) return err; - err = _write(tsf, pktbuf->pb_data, pktbuf->pb_size); + err = _write(tsf, pktbuf_ptr(pktbuf), pktbuf_len(pktbuf)); if (err < 0) return err; ret += err; } else { diff --git a/src/tvheadend.h b/src/tvheadend.h index 20ee1f60a..8c067ed27 100644 --- a/src/tvheadend.h +++ b/src/tvheadend.h @@ -255,6 +255,7 @@ void tasklet_disarm(tasklet_t *gti); /* * List / Queue header declarations */ +LIST_HEAD(memoryinfo_list, memoryinfo); LIST_HEAD(access_entry_list, access_entry); LIST_HEAD(th_subscription_list, th_subscription); LIST_HEAD(dvr_vfs_list, dvr_vfs); @@ -671,6 +672,8 @@ extern void scopedunlock(pthread_mutex_t **mtxp); char *tvh_b = alloca(tvh_l + 1); \ memcpy(tvh_b, n, tvh_l + 1); }) +#define tvh_strlen(s) ((s) ? strlen(s) : 0) + #define tvh_strlcatf(buf, size, ptr, fmt...) \ do { int __r = snprintf((buf) + ptr, (size) - ptr, fmt); \ ptr = __r >= (size) - ptr ? (size) - 1 : ptr + __r; } while (0) diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index d11bcdb54..9cb4db8a4 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -580,7 +580,18 @@ function accessUpdate(o) { tvheadend.caclient(cp, 6); /* Debug */ - tvheadend.tvhlog(cp, 7); + var dbg = new Ext.TabPanel({ + tabIndex: 7, + activeTab: 0, + autoScroll: true, + title: _('Debugging'), + iconCls: 'debug', + items: [] + }); + tvheadend.tvhlog(dbg, 0); + tvheadend.memoryinfo(dbg, 1); + + cp.add(dbg); /* Finish */ tvheadend.rootTabPanel.add(cp); diff --git a/src/webui/static/app/tvhlog.js b/src/webui/static/app/tvhlog.js index ac9d01f70..6fc7ce3a6 100644 --- a/src/webui/static/app/tvhlog.js +++ b/src/webui/static/app/tvhlog.js @@ -14,7 +14,7 @@ tvheadend.tvhlog = function(panel, index) { tvheadend.idnode_simple(panel, { url: 'api/tvhlog/config', - title: _('Debugging'), + title: _('Configuration'), iconCls: 'debug', tabIndex: index, comet: 'tvhlog_conf', @@ -30,3 +30,18 @@ tvheadend.tvhlog = function(panel, index) { }); }; + +tvheadend.memoryinfo = function(panel, index) +{ + tvheadend.idnode_grid(panel, { + url: 'api/memoryinfo', + titleS: _('Memory info entry'), + titleP: _('Memory info entries'), + iconCls: 'exclamation', + tabIndex: index, + readonly: true, + help: function() { + new tvheadend.help(_('Memory information entries'), 'config_memoryinfo.html'); + } + }); +};