From 011a42650dd1795500f0415364144f750437db6a Mon Sep 17 00:00:00 2001 From: Richard Fuchs Date: Wed, 30 Apr 2014 13:22:59 -0400 Subject: [PATCH] restore query/stats output Fixes #3 Pending documentation --- daemon/call.c | 65 +--------- daemon/call.h | 4 +- daemon/call_interfaces.c | 258 ++++++++++++++++++++++++++------------- daemon/call_interfaces.h | 4 +- utils/ng-client | 8 ++ 5 files changed, 191 insertions(+), 148 deletions(-) diff --git a/daemon/call.c b/daemon/call.c index e2f0080b8..4c07229f5 100644 --- a/daemon/call.c +++ b/daemon/call.c @@ -2093,6 +2093,8 @@ static void __call_free(void *p) { struct endpoint_map *em; GList *it; + __C_DBG("freeing call struct"); + call_buffer_free(&c->buffer); mutex_destroy(&c->buffer_lock); rwlock_destroy(&c->master_lock); @@ -2391,7 +2393,7 @@ int call_delete_branch(struct callmaster *m, const str *callid, const str *branc } if (output) - ng_call_stats(c, fromtag, totag, output); + ng_call_stats(c, fromtag, totag, output, NULL); /* if (branch && branch->len) { @@ -2439,67 +2441,6 @@ out: } -#define SSUM(x) \ - stats->totals[0].x += stream->stats.x; -/* call->master_lock must be held in W */ -/* XXX possibly eliminate W lock, should work with R only */ -void stats_query(struct call *call, const str *fromtag, const str *totag, struct call_stats *stats, - void (*cb)(struct packet_stream *, void *), void *arg) -{ - const str *match_tag; - struct call_monologue *ml; - struct call_media *media; - GList *l, *k; - GSList *ml_l = NULL; - struct packet_stream *stream; - - ZERO(*stats); - - match_tag = (totag && totag->s && totag->len) ? totag : fromtag; - if (!match_tag) { - ml_l = call->monologues; - if (!ml_l) - goto out; - ml = ml_l->data; - } - else - ml = g_hash_table_lookup(call->tags, match_tag); - - while (ml) { - l = ml->medias.head; - for (l = ml->medias.head; l; l = l->next) { - media = l->data; - - for (k = media->streams.head; k; k = k->next) { - stream = k->data; - - if (stream->last_packet > stats->newest) - stats->newest = stream->last_packet; - - if (cb) - cb(stream, arg); - - SSUM(packets); - SSUM(bytes); - SSUM(errors); - - /* XXX more meaningful stats */ - } - } - - if (!ml_l) - break; - ml_l = ml_l->next; - if (!ml_l) - break; - ml = ml_l->data; - } - -out: - ; -} - - static void callmaster_get_all_calls_interator(void *key, void *val, void *ptr) { GQueue *q = ptr; g_queue_push_tail(q, obj_get_o(val)); diff --git a/daemon/call.h b/daemon/call.h index be50663d5..506484ce0 100644 --- a/daemon/call.h +++ b/daemon/call.h @@ -335,7 +335,7 @@ struct callmaster { }; struct call_stats { - time_t newest; + time_t last_packet; struct stats totals[4]; /* rtp in, rtcp in, rtp out, rtcp out */ }; @@ -376,8 +376,6 @@ struct call_monologue *call_get_mono_dialogue(struct call *call, const str *from int monologue_offer_answer(struct call_monologue *monologue, GQueue *streams, const struct sdp_ng_flags *flags); int call_delete_branch(struct callmaster *m, const str *callid, const str *branch, const str *fromtag, const str *totag, bencode_item_t *output); -void stats_query(struct call *call, const str *fromtag, const str *totag, struct call_stats *stats, - void (*cb)(struct packet_stream *, void *), void *arg); void kernelize(struct packet_stream *); int call_stream_address_alt(char *, struct packet_stream *, enum stream_address_format, int *); diff --git a/daemon/call_interfaces.c b/daemon/call_interfaces.c index 1c6bbb287..c0ca3001a 100644 --- a/daemon/call_interfaces.c +++ b/daemon/call_interfaces.c @@ -328,12 +328,12 @@ str *call_query_udp(char **out, struct callmaster *m) { goto err; } - stats_query(c, &fromtag, &totag, &stats, NULL, NULL); + ng_call_stats(c, &fromtag, &totag, NULL, &stats); rwlock_unlock_w(&c->master_lock); ret = str_sprintf("%s %lld "UINT64F" "UINT64F" "UINT64F" "UINT64F"\n", out[RE_UDP_COOKIE], - (long long int) m->conf.silent_timeout - (poller_now - stats.newest), + (long long int) m->conf.silent_timeout - (poller_now - stats.last_packet), stats.totals[0].packets, stats.totals[1].packets, stats.totals[2].packets, stats.totals[3].packets); goto out; @@ -408,6 +408,20 @@ void calls_status_tcp(struct callmaster *m, struct control_stream *s) { +static void call_release_ref(void *p) { + struct call *c = p; + obj_put(c); +} +INLINE void call_bencode_hold_ref(struct call *c, bencode_item_t *bi) { + /* We cannot guarantee that the "call" structures are still around at the time + * when the bencode reply is finally read and sent out. Since we use scatter/gather + * to avoid duplication of strings and stuff, we reserve a reference to the call + * structs and have it released when the bencode buffer is destroyed. This is + * necessary every time the bencode response may reference strings contained + * within the call structs. */ + bencode_buffer_destroy_add(bi->buffer, call_release_ref, obj_get(c)); +} + static void call_ng_process_flags(struct sdp_ng_flags *out, bencode_item_t *input) { bencode_item_t *list, *it; int diridx; @@ -528,6 +542,10 @@ static const char *call_offer_answer_ng(bencode_item_t *input, struct callmaster if (!call) goto out; + /* At least the random ICE strings are contained within the call struct, so we + * need to hold a ref until we're done sending the reply */ + call_bencode_hold_ref(call, output); + monologue = call_get_mono_dialogue(call, &fromtag, &totag); chopper = sdp_chopper_new(&sdp); @@ -594,114 +612,189 @@ const char *call_delete_ng(bencode_item_t *input, struct callmaster *m, bencode_ return NULL; } -#if 0 -static bencode_item_t *peer_address(bencode_buffer_t *b, struct stream *s) { - bencode_item_t *d; +static void ng_stats(bencode_item_t *d, const struct stats *s, struct stats *totals) { + bencode_dictionary_add_integer(d, "packets", s->packets); + bencode_dictionary_add_integer(d, "bytes", s->bytes); + bencode_dictionary_add_integer(d, "errors", s->errors); + if (!totals) + return; + totals->packets += s->packets; + totals->bytes += s->bytes; + totals->errors += s->errors; +} + +static void ng_stats_endpoint(bencode_item_t *dict, const struct endpoint *ep) { char buf[64]; - d = bencode_dictionary(b); - if (IN6_IS_ADDR_V4MAPPED(&s->ip46)) { - bencode_dictionary_add_string(d, "family", "IPv4"); - inet_ntop(AF_INET, &(s->ip46.s6_addr32[3]), buf, sizeof(buf)); + if (IN6_IS_ADDR_V4MAPPED(&ep->ip46)) { + bencode_dictionary_add_string(dict, "family", "IPv4"); + inet_ntop(AF_INET, &(ep->ip46.s6_addr32[3]), buf, sizeof(buf)); } else { - bencode_dictionary_add_string(d, "family", "IPv6"); - inet_ntop(AF_INET6, &s->ip46, buf, sizeof(buf)); + bencode_dictionary_add_string(dict, "family", "IPv6"); + inet_ntop(AF_INET6, &ep->ip46, buf, sizeof(buf)); } - bencode_dictionary_add_string_dup(d, "address", buf); - bencode_dictionary_add_integer(d, "port", s->port); - - return d; + bencode_dictionary_add_string_dup(dict, "address", buf); + bencode_dictionary_add_integer(dict, "port", ep->port); } -#endif -#if 0 -static bencode_item_t *stats_encode(bencode_buffer_t *b, struct stats *s) { - bencode_item_t *d; +#define BF_PS(k, f) if (PS_ISSET(ps, f)) bencode_list_add_string(flags, k) - d = bencode_dictionary(b); - bencode_dictionary_add_integer(d, "packets", s->packets); - bencode_dictionary_add_integer(d, "bytes", s->bytes); - bencode_dictionary_add_integer(d, "errors", s->errors); - return d; -} -#endif +static void ng_stats_stream(bencode_item_t *list, const struct packet_stream *ps, + struct call_stats *totals) +{ + bencode_item_t *dict = NULL, *flags; + struct stats *s; -#if 0 -static bencode_item_t *streamrelay_stats(bencode_buffer_t *b, struct packet_stream *ps) { - bencode_item_t *d; + if (!list) + goto stats; - d = bencode_dictionary(b); + dict = bencode_list_add_dictionary(list); - // XXX - //bencode_dictionary_add(d, "counters", stats_encode(b, &r->stats)); - //bencode_dictionary_add(d, "peer address", peer_address(b, &r->peer)); - //bencode_dictionary_add(d, "advertised peer address", peer_address(b, &r->peer_advertised)); + if (ps->sfd) + bencode_dictionary_add_integer(dict, "local port", ps->sfd->fd.localport); + ng_stats_endpoint(bencode_dictionary_add_dictionary(dict, "endpoint"), &ps->endpoint); + ng_stats_endpoint(bencode_dictionary_add_dictionary(dict, "advertised endpoint"), + &ps->advertised_endpoint); + if (ps->crypto.params.crypto_suite) + bencode_dictionary_add_string(dict, "crypto suite", + ps->crypto.params.crypto_suite->name); + bencode_dictionary_add_integer(dict, "last packet", ps->last_packet); - bencode_dictionary_add_integer(d, "local port", ps->fd.localport); + flags = bencode_dictionary_add_list(dict, "flags"); - return d; -} -#endif + BF_PS("RTP", RTP); + BF_PS("RTCP", RTCP); + BF_PS("fallback RTCP", FALLBACK_RTCP); + BF_PS("filled", FILLED); + BF_PS("confirmed", CONFIRMED); + BF_PS("kernelized", KERNELIZED); + BF_PS("no kernel support", NO_KERNEL_SUPPORT); + +stats: + if (totals->last_packet < ps->last_packet) + totals->last_packet = ps->last_packet; -#if 0 -static bencode_item_t *rtp_rtcp_stats(bencode_buffer_t *b, struct stats *rtp, struct stats *rtcp) { - bencode_item_t *s; - s = bencode_dictionary(b); - bencode_dictionary_add(s, "rtp", stats_encode(b, rtp)); - bencode_dictionary_add(s, "rtcp", stats_encode(b, rtcp)); - return s; + /* XXX distinguish between input and output */ + s = &totals->totals[0]; + if (!PS_ISSET(ps, RTP)) + s = &totals->totals[1]; + ng_stats(bencode_dictionary_add_dictionary(dict, "stats"), &ps->stats, s); } -#endif -#if 0 -XXX -static bencode_item_t *peer_stats(bencode_buffer_t *b, struct peer *p) { - bencode_item_t *d, *s; +#define BF_M(k, f) if (MEDIA_ISSET(m, f)) bencode_list_add_string(flags, k) - d = bencode_dictionary(b); +static void ng_stats_media(bencode_item_t *list, const struct call_media *m, + struct call_stats *totals) +{ + bencode_item_t *dict, *streams = NULL, *flags; + GList *l; + struct packet_stream *ps; - bencode_dictionary_add_str_dup(d, "tag", &p->tag); - if (p->codec) - bencode_dictionary_add_string(d, "codec", p->codec); - if (p->kernelized) - bencode_dictionary_add_string(d, "status", "in kernel"); - else if (p->confirmed) - bencode_dictionary_add_string(d, "status", "confirmed peer address"); - else if (p->filled) - bencode_dictionary_add_string(d, "status", "known but unconfirmed peer address"); - else - bencode_dictionary_add_string(d, "status", "unknown peer address"); + if (!list) + goto stats; + + dict = bencode_list_add_dictionary(list); - s = bencode_dictionary_add_dictionary(d, "stats"); - bencode_dictionary_add(s, "rtp", streamrelay_stats(b, &p->rtps[0])); - bencode_dictionary_add(s, "rtcp", streamrelay_stats(b, &p->rtps[1])); + bencode_dictionary_add_integer(dict, "index", m->index); + bencode_dictionary_add_str(dict, "type", &m->type); + if (m->protocol) + bencode_dictionary_add_string(dict, "protocol", m->protocol->name); - return d; + streams = bencode_dictionary_add_list(dict, "streams"); + + flags = bencode_dictionary_add_list(dict, "flags"); + + BF_M("initialized", INITIALIZED); + BF_M("rtcp-mux", RTCP_MUX); + BF_M("DTLS-SRTP", DTLS); + BF_M("SDES", SDES); + BF_M("passthrough", PASSTHRU); + BF_M("ICE", ICE); + +stats: + for (l = m->streams.head; l; l = l->next) { + ps = l->data; + ng_stats_stream(streams, ps, totals); + } } -static void ng_stats_cb(struct peer *p, struct peer *px, void *streams) { - bencode_item_t *stream; +static void ng_stats_monologue(bencode_item_t *dict, const struct call_monologue *ml, + struct call_stats *totals) +{ + bencode_item_t *sub, *medias = NULL; + GList *l; + struct call_media *m; + + if (!ml) + return; - stream = bencode_list_add_list(streams); - bencode_list_add(stream, peer_stats(stream->buffer, p)); - bencode_list_add(stream, peer_stats(stream->buffer, px)); + if (!dict) + goto stats; + + sub = bencode_dictionary_add_dictionary(dict, ml->tag.s); + + bencode_dictionary_add_str(sub, "tag", &ml->tag); + bencode_dictionary_add_integer(sub, "created", ml->created); + if (ml->active_dialogue) + bencode_dictionary_add_str(sub, "in dialogue with", &ml->active_dialogue->tag); + + medias = bencode_dictionary_add_list(sub, "medias"); + +stats: + for (l = ml->medias.head; l; l = l->next) { + m = l->data; + ng_stats_media(medias, m, totals); + } } -#endif /* call must be locked */ -void ng_call_stats(struct call *call, const str *fromtag, const str *totag, bencode_item_t *output) { - //bencode_item_t *streams, *dict; -// struct call_stats stats; +void ng_call_stats(struct call *call, const str *fromtag, const str *totag, bencode_item_t *output, + struct call_stats *totals) +{ + bencode_item_t *tags = NULL, *dict; + const str *match_tag; + GSList *l; + struct call_monologue *ml; + struct call_stats t_b; -// bencode_dictionary_add_integer(output, "created", call->created); + if (!totals) + totals = &t_b; + ZERO(*totals); - //streams = bencode_dictionary_add_list(output, "streams"); - //stats_query(call, fromtag, totag, &stats, ng_stats_cb, streams); XXX + if (!output) + goto stats; -// dict = bencode_dictionary_add_dictionary(output, "totals"); -// bencode_dictionary_add(dict, "input", rtp_rtcp_stats(output->buffer, &stats.totals[0], &stats.totals[1])); -// bencode_dictionary_add(dict, "output", rtp_rtcp_stats(output->buffer, &stats.totals[2], &stats.totals[3])); + call_bencode_hold_ref(call, output); + + bencode_dictionary_add_integer(output, "created", call->created); + bencode_dictionary_add_integer(output, "last_signal", call->last_signal); + + tags = bencode_dictionary_add_dictionary(output, "tags"); + +stats: + match_tag = (totag && totag->s && totag->len) ? totag : fromtag; + + if (!match_tag) { + for (l = call->monologues; l; l = l->next) { + ml = l->data; + ng_stats_monologue(tags, ml, totals); + } + } + else { + ml = g_hash_table_lookup(call->tags, match_tag); + if (ml) { + ng_stats_monologue(tags, ml, totals); + ng_stats_monologue(tags, ml->active_dialogue, totals); + } + } + + if (!output) + return; + + dict = bencode_dictionary_add_dictionary(output, "totals"); + ng_stats(bencode_dictionary_add_dictionary(dict, "RTP"), &totals->totals[0], NULL); + ng_stats(bencode_dictionary_add_dictionary(dict, "RTCP"), &totals->totals[1], NULL); } const char *call_query_ng(bencode_item_t *input, struct callmaster *m, bencode_item_t *output) { @@ -717,8 +810,9 @@ const char *call_query_ng(bencode_item_t *input, struct callmaster *m, bencode_i bencode_dictionary_get_str(input, "to-tag", &totag); bencode_dictionary_add_string(output, "result", "ok"); - ng_call_stats(call, &fromtag, &totag, output); + ng_call_stats(call, &fromtag, &totag, output, NULL); rwlock_unlock_w(&call->master_lock); + obj_put(call); return NULL; } diff --git a/daemon/call_interfaces.h b/daemon/call_interfaces.h index 85bba9a47..768fa9e28 100644 --- a/daemon/call_interfaces.h +++ b/daemon/call_interfaces.h @@ -9,10 +9,12 @@ struct call; +struct call_stats; -void ng_call_stats(struct call *call, const str *fromtag, const str *totag, bencode_item_t *output); +void ng_call_stats(struct call *call, const str *fromtag, const str *totag, bencode_item_t *output, + struct call_stats *totals); #endif diff --git a/utils/ng-client b/utils/ng-client index 95c1cb444..6c41531bb 100755 --- a/utils/ng-client +++ b/utils/ng-client @@ -104,3 +104,11 @@ if (defined($$resp{sdp})) { print("New SDP:\n-----8<-----8<-----8<-----8<-----8<-----\n$$resp{sdp}\n". "----->8----->8----->8----->8----->8-----\n"); } +else { + local $Data::Dumper::Indent = 1; + local $Data::Dumper::Terse = 1; + local $Data::Dumper::Quotekeys = 0; + print("Result dictionary:\n-----8<-----8<-----8<-----8<-----8<-----\n" + . Dumper($resp) + . "----->8----->8----->8----->8----->8-----\n"); +}