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)
#include "tvheadend.h"
#include "channels.h"
#include "access.h"
+#include "memoryinfo.h"
#include "api.h"
#include "config.h"
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 )
{
{ "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 },
};
* 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)
{
#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
*/
{
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
+}
#include "htsbuf.h"
#include "bouquet.h"
#include "intlconv.h"
+#include "memoryinfo.h"
#define CHANNEL_BLANK_NAME "{name-not-set}"
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
+};
/**
*
char *s;
RB_INIT(&channels);
+ memoryinfo_register(&channels_memoryinfo);
/* Tags */
channel_tag_init();
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();
}
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
*/
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) {
/*
- * 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
#include "epg.h"
#include "epggrab.h"
#include "config.h"
+#include "memoryinfo.h"
#define EPG_DB_VERSION 2
#define EPG_DB_ALLOC_STEP (1024*1024)
}
}
+/*
+ * 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
*/
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);
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);
}
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
*/
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;
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);
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
* *************************************************************************/
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);
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);
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);
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 );
#include "profile.h"
#include "bouquet.h"
#include "tvhtime.h"
+#include "packet.h"
+#include "memoryinfo.h"
#ifdef PLATFORM_LINUX
#include <sys/prctl.h>
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)
{
tasklet_t *tsk = calloc(1, sizeof(*tsk));
if (tsk) {
+ memoryinfo_alloc(&tasklet_memoryinfo, sizeof(*tsk));
tsk->tsk_allocated = 1;
tasklet_arm(tsk, callback, opaque);
}
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);
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);
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
*/
--- /dev/null
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#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,
+ },
+ {}
+ }
+};
--- /dev/null
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 */
{
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];
#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));
+ }
}
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;
+}
+
+
/**
*
*/
{
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));
+ }
}
}
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));
+ }
}
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));
+ }
}
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;
}
*
*/
+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);
}
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;
}
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;
}
#ifndef PACKET_H_
#define PACKET_H_
+struct memoryinfo;
+
/**
* Packet buffer
*/
} th_pktref_t;
+/**
+ *
+ */
+extern struct memoryinfo pkt_memoryinfo;
+extern struct memoryinfo pktbuf_memoryinfo;
+extern struct memoryinfo pktref_memoryinfo;
+
/**
*
*/
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);
/*
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);
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);
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);
{ "u16", PT_U16 },
{ "u32", PT_U32 },
{ "s64", PT_S64 },
+ { "s64", PT_S64_ATOMIC },
{ "dbl", PT_DBL },
{ "time", PT_TIME },
{ "langstr", PT_LANGSTR },
PROP_UPDATE(s64, int64_t);
break;
}
+ case PT_S64_ATOMIC:
+ break;
case PT_DBL: {
if (htsmsg_field_get_dbl(f, &dbl))
continue;
} 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))
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:
PT_U16,
PT_U32,
PT_S64,
+ PT_S64_ATOMIC,
PT_DBL,
PT_TIME,
PT_LANGSTR,
#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);
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
*/
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;
*/
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);
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);
}
int (*s_satip_source)(struct service *t);
+ void (*s_memoryinfo)(struct service *t, int64_t *size);
+
/**
* Channel info
*/
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__
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;
}
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 */
/* 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;
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 {
/*
* 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);
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)
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);
tvheadend.idnode_simple(panel, {
url: 'api/tvhlog/config',
- title: _('Debugging'),
+ title: _('Configuration'),
iconCls: 'debug',
tabIndex: index,
comet: 'tvhlog_conf',
});
};
+
+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');
+ }
+ });
+};