Asterisk - The Open Source Telephony Project  21.4.1
func_frame_trace.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2010, Digium, Inc.
5  *
6  * David Vossel <dvossel@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief Trace internal ast_frames on a channel.
22  *
23  * \author David Vossel <dvossel@digium.com>
24  *
25  * \ingroup functions
26  */
27 
28 /*** MODULEINFO
29  <support_level>extended</support_level>
30  ***/
31 
32 #include "asterisk.h"
33 
34 #include "asterisk/module.h"
35 #include "asterisk/channel.h"
36 #include "asterisk/pbx.h"
37 #include "asterisk/framehook.h"
38 #include "asterisk/cli.h"
39 
40 /*** DOCUMENTATION
41  <function name="FRAME_TRACE" language="en_US">
42  <synopsis>
43  View internal ast_frames as they are read and written on a channel.
44  </synopsis>
45  <syntax>
46  <parameter name="filter list type" required="true">
47  <para>A filter can be applied to the trace to limit what frames are viewed. This
48  filter can either be a <literal>white</literal> or <literal>black</literal> list
49  of frame types. When no filter type is present, <literal>white</literal> is
50  used. If no arguments are provided at all, all frames will be output.
51  </para>
52 
53  <para>Below are the different types of frames that can be filtered.</para>
54  <enumlist>
55  <enum name = "DTMF_BEGIN" />
56  <enum name = "DTMF_END" />
57  <enum name = "VOICE" />
58  <enum name = "VIDEO" />
59  <enum name = "CONTROL" />
60  <enum name = "NULL" />
61  <enum name = "IAX" />
62  <enum name = "TEXT" />
63  <enum name = "TEXT_DATA" />
64  <enum name = "IMAGE" />
65  <enum name = "HTML" />
66  <enum name = "CNG" />
67  <enum name = "MODEM" />
68  </enumlist>
69  </parameter>
70  </syntax>
71  <description>
72  <para>Examples:</para>
73  <example title="View only DTMF frames">
74  exten => 1,1,Set(FRAME_TRACE(white)=DTMF_BEGIN,DTMF_END)
75  </example>
76  <example title="View only DTMF frames">
77  exten => 1,1,Set(FRAME_TRACE()=DTMF_BEGIN,DTMF_END)
78  </example>
79  <example title="View everything except DTMF frames">
80  exten => 1,1,Set(FRAME_TRACE(black)=DTMF_BEGIN,DTMF_END)
81  </example>
82  </description>
83  </function>
84  ***/
85 
86 static void print_frame(struct ast_frame *frame);
87 static struct {
88  enum ast_frame_type type;
89  const char *str;
90 } frametype2str[] = {
91  { AST_FRAME_DTMF_BEGIN, "DTMF_BEGIN" },
92  { AST_FRAME_DTMF_END, "DTMF_END" },
93  { AST_FRAME_VOICE, "VOICE" },
94  { AST_FRAME_VIDEO, "VIDEO" },
95  { AST_FRAME_CONTROL, "CONTROL" },
96  { AST_FRAME_NULL, "NULL" },
97  { AST_FRAME_IAX, "IAX" },
98  { AST_FRAME_TEXT, "TEXT" },
99  { AST_FRAME_TEXT_DATA, "TEXT_DATA" },
100  { AST_FRAME_IMAGE, "IMAGE" },
101  { AST_FRAME_HTML, "HTML" },
102  { AST_FRAME_CNG, "CNG" },
103  { AST_FRAME_MODEM, "MODEM" },
104 };
105 
107  int list_type; /* 0 = white, 1 = black */
108  int values[ARRAY_LEN(frametype2str)];
109 };
110 
111 static void datastore_destroy_cb(void *data) {
112  ast_free(data);
113 }
114 
115 static const struct ast_datastore_info frame_trace_datastore = {
116  .type = "frametrace",
117  .destroy = datastore_destroy_cb
118 };
119 
120 static void hook_destroy_cb(void *framedata)
121 {
122  ast_free(framedata);
123 }
124 
125 static struct ast_frame *hook_event_cb(struct ast_channel *chan, struct ast_frame *frame, enum ast_framehook_event event, void *data)
126 {
127  int i;
128  int show_frame = 0;
129  struct frame_trace_data *framedata = data;
130  if (!frame) {
131  return frame;
132  }
133 
134  if ((event != AST_FRAMEHOOK_EVENT_WRITE) && (event != AST_FRAMEHOOK_EVENT_READ)) {
135  return frame;
136  }
137 
138  for (i = 0; i < ARRAY_LEN(frametype2str); i++) {
139  if (frame->frametype == frametype2str[i].type) {
140  if ((framedata->list_type == 0) && (framedata->values[i])) { /* white list */
141  show_frame = 1;
142  } else if ((framedata->list_type == 1) && (!framedata->values[i])){ /* black list */
143  show_frame = 1;
144  }
145  break;
146  }
147  }
148 
149  if (show_frame) {
150  ast_verbose("%s on Channel %s\n", event == AST_FRAMEHOOK_EVENT_READ ? "<--Read" : "--> Write", ast_channel_name(chan));
151  print_frame(frame);
152  }
153  return frame;
154 }
155 
156 static int frame_trace_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value)
157 {
158  struct frame_trace_data *framedata;
159  struct ast_datastore *datastore = NULL;
160  struct ast_framehook_interface interface = {
161  .version = AST_FRAMEHOOK_INTERFACE_VERSION,
162  .event_cb = hook_event_cb,
163  .destroy_cb = hook_destroy_cb,
164  };
165  int i = 0;
166 
167  if (!chan) {
168  ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);
169  return -1;
170  }
171 
172  if (!(framedata = ast_calloc(1, sizeof(*framedata)))) {
173  return 0;
174  }
175 
176  interface.data = framedata;
177 
178  if (!strcasecmp(data, "black")) {
179  framedata->list_type = 1;
180  }
181  for (i = 0; i < ARRAY_LEN(frametype2str); i++) {
182  if (strcasestr(value, frametype2str[i].str)) {
183  framedata->values[i] = 1;
184  }
185  }
186 
187  ast_channel_lock(chan);
188  i = ast_framehook_attach(chan, &interface);
189  if (i >= 0) {
190  int *id;
191  if ((datastore = ast_channel_datastore_find(chan, &frame_trace_datastore, NULL))) {
192  id = datastore->data;
193  ast_framehook_detach(chan, *id);
194  ast_channel_datastore_remove(chan, datastore);
195  ast_datastore_free(datastore);
196  }
197 
198  if (!(datastore = ast_datastore_alloc(&frame_trace_datastore, NULL))) {
199  ast_framehook_detach(chan, i);
200  ast_channel_unlock(chan);
201  return 0;
202  }
203 
204  if (!(id = ast_calloc(1, sizeof(int)))) {
205  ast_datastore_free(datastore);
206  ast_framehook_detach(chan, i);
207  ast_channel_unlock(chan);
208  return 0;
209  }
210 
211  *id = i; /* Store off the id. The channel is still locked so it is safe to access this ptr. */
212  datastore->data = id;
213  ast_channel_datastore_add(chan, datastore);
214  }
215  ast_channel_unlock(chan);
216 
217  return 0;
218 }
219 
220 static void print_frame(struct ast_frame *frame)
221 {
222  switch (frame->frametype) {
223  case AST_FRAME_DTMF_END:
224  ast_verbose("FrameType: DTMF END\n");
225  ast_verbose("Digit: 0x%02X '%c'\n", (unsigned)frame->subclass.integer,
226  frame->subclass.integer < ' ' ? ' ' : frame->subclass.integer);
227  break;
228  case AST_FRAME_VOICE:
229  ast_verbose("FrameType: VOICE\n");
230  ast_verbose("Codec: %s\n", ast_format_get_name(frame->subclass.format));
231  ast_verbose("MS: %ld\n", frame->len);
232  ast_verbose("Samples: %d\n", frame->samples);
233  ast_verbose("Bytes: %d\n", frame->datalen);
234  break;
235  case AST_FRAME_VIDEO:
236  ast_verbose("FrameType: VIDEO\n");
237  ast_verbose("Codec: %s\n", ast_format_get_name(frame->subclass.format));
238  ast_verbose("MS: %ld\n", frame->len);
239  ast_verbose("Samples: %d\n", frame->samples);
240  ast_verbose("Bytes: %d\n", frame->datalen);
241  break;
242  case AST_FRAME_CONTROL:
243  ast_verbose("FrameType: CONTROL\n");
244  switch ((enum ast_control_frame_type) frame->subclass.integer) {
245  case AST_CONTROL_HANGUP:
246  ast_verbose("SubClass: HANGUP\n");
247  break;
248  case AST_CONTROL_RING:
249  ast_verbose("SubClass: RING\n");
250  break;
251  case AST_CONTROL_RINGING:
252  ast_verbose("SubClass: RINGING\n");
253  break;
254  case AST_CONTROL_ANSWER:
255  ast_verbose("SubClass: ANSWER\n");
256  break;
257  case AST_CONTROL_BUSY:
258  ast_verbose("SubClass: BUSY\n");
259  break;
261  ast_verbose("SubClass: TAKEOFFHOOK\n");
262  break;
263  case AST_CONTROL_OFFHOOK:
264  ast_verbose("SubClass: OFFHOOK\n");
265  break;
267  ast_verbose("SubClass: CONGESTION\n");
268  break;
269  case AST_CONTROL_FLASH:
270  ast_verbose("SubClass: FLASH\n");
271  break;
272  case AST_CONTROL_WINK:
273  ast_verbose("SubClass: WINK\n");
274  break;
275  case AST_CONTROL_OPTION:
276  ast_verbose("SubClass: OPTION\n");
277  break;
279  ast_verbose("SubClass: RADIO KEY\n");
280  break;
282  ast_verbose("SubClass: RADIO UNKEY\n");
283  break;
285  ast_verbose("SubClass: PROGRESS\n");
286  break;
288  ast_verbose("SubClass: PROCEEDING\n");
289  break;
290  case AST_CONTROL_HOLD:
291  ast_verbose("SubClass: HOLD\n");
292  break;
293  case AST_CONTROL_UNHOLD:
294  ast_verbose("SubClass: UNHOLD\n");
295  break;
297  ast_verbose("SubClass: VIDUPDATE\n");
298  break;
300  ast_verbose("SubClass: XXX T38\n");
301  break;
303  ast_verbose("SubClass: SRCUPDATE\n");
304  break;
306  ast_verbose("SubClass: TRANSFER\n");
307  break;
309  ast_verbose("SubClass: CONNECTED LINE\n");
310  break;
312  ast_verbose("SubClass: REDIRECTING\n");
313  break;
315  ast_verbose("SubClass: T38 PARAMETERS\n");
316  break;
317  case AST_CONTROL_CC:
318  ast_verbose("SubClass: CC\n");
319  break;
321  ast_verbose("SubClass: SRCCHANGE\n");
322  break;
324  ast_verbose("SubClass: READ ACTION\n");
325  break;
326  case AST_CONTROL_AOC:
327  ast_verbose("SubClass: AOC\n");
328  break;
329  case AST_CONTROL_MCID:
330  ast_verbose("SubClass: MCID\n");
331  break;
333  ast_verbose("SubClass: INCOMPLETE\n");
334  break;
336  ast_verbose("SubClass: END_OF_Q\n");
337  break;
339  ast_verbose("SubClass: UPDATE_RTP_PEER\n");
340  break;
342  ast_verbose("SubClass: PVT_CAUSE_CODE\n");
343  break;
345  ast_verbose("SubClass: MASQUERADE_NOTIFY\n");
346  break;
348  ast_verbose("SubClass: STREAM_TOPOLOGY_REQUEST_CHANGE\n");
349  break;
351  ast_verbose("SubClass: STREAM_TOPOLOGY_CHANGED\n");
352  break;
354  ast_verbose("SubClass: STREAM_TOPOLOGY_SOURCE_CHANGED\n");
355  break;
357  ast_verbose("SubClass: STREAM_STOP\n");
358  break;
360  ast_verbose("SubClass: STREAM_SUSPEND\n");
361  break;
363  ast_verbose("SubClass: STREAM_RESTART\n");
364  break;
366  ast_verbose("SubClass: STREAM_REVERSE\n");
367  break;
369  ast_verbose("SubClass: STREAM_FORWARD\n");
370  break;
372  ast_verbose("SubClass: RECORD_CANCEL\n");
373  break;
375  ast_verbose("SubClass: RECORD_STOP\n");
376  break;
378  ast_verbose("SubClass: RECORD_SUSPEND\n");
379  break;
381  ast_verbose("SubClass: RECORD_MUTE\n");
382  break;
383  }
384 
385  if (frame->subclass.integer == -1) {
386  ast_verbose("SubClass: %d\n", frame->subclass.integer);
387  }
388  ast_verbose("Bytes: %d\n", frame->datalen);
389  break;
390  case AST_FRAME_RTCP:
391  ast_verbose("FrameType: RTCP\n");
392  break;
393  case AST_FRAME_NULL:
394  ast_verbose("FrameType: NULL\n");
395  break;
396  case AST_FRAME_IAX:
397  ast_verbose("FrameType: IAX\n");
398  break;
399  case AST_FRAME_TEXT:
400  ast_verbose("FrameType: TXT\n");
401  ast_verbose("Text: %.*s\n", frame->datalen, (char*) frame->data.ptr);
402  break;
403  case AST_FRAME_TEXT_DATA:
404  ast_verbose("FrameType: TXT_DATA\n");
405  break;
406  case AST_FRAME_IMAGE:
407  ast_verbose("FrameType: IMAGE\n");
408  break;
409  case AST_FRAME_HTML:
410  ast_verbose("FrameType: HTML\n");
411  break;
412  case AST_FRAME_CNG:
413  ast_verbose("FrameType: CNG\n");
414  break;
415  case AST_FRAME_MODEM:
416  ast_verbose("FrameType: MODEM\n");
417  break;
419  ast_verbose("FrameType: DTMF BEGIN\n");
420  ast_verbose("Digit: 0x%02X '%c'\n", (unsigned)frame->subclass.integer,
421  frame->subclass.integer < ' ' ? ' ' : frame->subclass.integer);
422  break;
424  ast_verbose("FrameType: Bridge\n");
425  ast_verbose("SubClass: %d\n", frame->subclass.integer);
426  break;
428  ast_verbose("Frametype: Synchronous Bridge\n");
429  ast_verbose("Subclass: %d\n", frame->subclass.integer);
430  break;
431  }
432 
433  ast_verbose("Src: %s\n", ast_strlen_zero(frame->src) ? "NOT PRESENT" : frame->src);
434  ast_verbose("\n");
435 }
436 
437 static struct ast_custom_function frame_trace_function = {
438  .name = "FRAME_TRACE",
439  .write = frame_trace_helper,
440 };
441 
442 static char *handle_dump_frames(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
443 {
444  struct ast_channel *chan;
445  struct ast_frame *f;
446  int c = 1;
447 
448  switch (cmd) {
449  case CLI_INIT:
450  e->command = "channel dump frames";
451  e->usage =
452  "Usage: channel dump frames <channel>\n"
453  " List all frames queued to a channel.\n";
454  return NULL;
455  case CLI_GENERATE:
456  return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
457  }
458 
459  if (a->argc != 4) {
460  return CLI_SHOWUSAGE;
461  }
462 
463  chan = ast_channel_get_by_name(a->argv[3]);
464  if (!chan) {
465  ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
466  return CLI_SUCCESS;
467  }
468 
469  ast_channel_lock(chan);
470 
471  ast_cli(a->fd, "== Frame list for %s ==\n", ast_channel_name(chan));
472  ast_cli(a->fd, "%5s %6s %6s %-15s (%-20s) - %s\n", "#", "Seqno", "Stream", "Frame Type", "Frame Subclass", "Src");
473  AST_LIST_TRAVERSE(ast_channel_readq(chan), f, frame_list) {
474  char type[64];
475  char subclass[64];
476  ast_frame_type2str(f->frametype, type, sizeof(type));
477  ast_frame_subclass2str(f, subclass, sizeof(subclass), NULL, 0);
478  ast_cli(a->fd, "%5d %6d %6d %-15s (%-20s) - %s\n", c++, f->seqno, f->stream_num, type, subclass, S_OR(f->src, ""));
479  }
480 
481  ast_channel_unlock(chan);
482  ast_channel_unref(chan);
483  return CLI_SUCCESS;
484 }
485 
486 static struct ast_cli_entry cli_frames[] = {
487  AST_CLI_DEFINE(handle_dump_frames, "Display frames queued on a specific channel")
488 };
489 
490 static int unload_module(void)
491 {
492  ast_cli_unregister_multiple(cli_frames, ARRAY_LEN(cli_frames));
493  return ast_custom_function_unregister(&frame_trace_function);
494 }
495 
496 static int load_module(void)
497 {
498  int res = ast_custom_function_register(&frame_trace_function);
499  res |= ast_cli_register_multiple(cli_frames, ARRAY_LEN(cli_frames));
501 }
502 
503 AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Frame Trace for internal ast_frame debugging.");
const char * name
Definition: pbx.h:119
const char * type
Definition: datastore.h:32
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
descriptor for a cli entry.
Definition: cli.h:171
ast_framehook_event
These are the types of events that the framehook's event callback can receive.
Definition: framehook.h:151
int ast_framehook_detach(struct ast_channel *chan, int framehook_id)
Detach an framehook from a channel.
Definition: framehook.c:177
Structure for a data store type.
Definition: datastore.h:31
Definition: astman.c:222
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
ast_control_frame_type
Internal control frame subtype field values.
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition: format.c:334
Structure for a data store object.
Definition: datastore.h:64
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition: channel.c:2399
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:68
struct ast_frame_subclass subclass
int ast_framehook_attach(struct ast_channel *chan, struct ast_framehook_interface *i)
Attach an framehook onto a channel for frame interception.
Definition: framehook.c:132
General Asterisk PBX channel definitions.
const char * src
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
char * ast_frame_subclass2str(struct ast_frame *f, char *subclass, size_t slen, char *moreinfo, size_t mlen)
Copy the discription of a frame's subclass into the provided string.
Definition: main/frame.c:406
Core PBX routines and definitions.
ast_frame_type
Frame types.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
union ast_frame::@224 data
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
char * ast_frame_type2str(enum ast_frame_type frame_type, char *ftype, size_t len)
Copy the discription of a frame type into the provided string.
Definition: main/frame.c:671
const char * usage
Definition: cli.h:177
FrameHook Architecture.
void * data
Definition: datastore.h:66
Standard Command Line Interface.
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
char * ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
Command completion for the list of active channels.
Definition: main/cli.c:1865
Data structure associated with a single frame of data.
enum ast_frame_type frametype
struct ast_format * format
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1454
Asterisk module definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2385
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2394
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558