core: Add cache_media_frames debugging option.

The media frame cache gets in the way of finding use after free errors of
media frames.  Tools like valgrind and MALLOC_DEBUG don't know when a
frame is released because it gets put into the cache instead of being
freed.

* Added the "cache_media_frames" option to asterisk.conf.  Disabling the
option helps track down media frame mismanagement when using valgrind or
MALLOC_DEBUG.  The cache gets in the way of determining if the frame is
used after free and who freed it.  NOTE: This option has no effect when
Asterisk is compiled with the LOW_MEMORY compile time option enabled
because the cache code does not exist.

To disable the media frame cache simply disable the cache_media_frames
option in asterisk.conf and restart Asterisk.

Sample asterisk.conf setting:
[options]
cache_media_frames=no

ASTERISK-27413

Change-Id: I0ab2ce0f4547cccf2eb214901835c2d951b78c00
pull/9/head
Richard Mudgett 8 years ago
parent 6f1910d6ec
commit 90bb0a3e10

@ -30,6 +30,15 @@ chan_sip
--- Functionality changes from Asterisk 15.1.0 to Asterisk 15.2.0 ------------
------------------------------------------------------------------------------
Core
------------------
* Added the "cache_media_frames" option to asterisk.conf. Disabling the option
helps track down media frame mismanagement when using valgrind or
MALLOC_DEBUG. The cache gets in the way of determining if the frame is
used after free and who freed it. NOTE: This option has no effect when
Asterisk is compiled with the LOW_MEMORY compile time option enabled because
the cache code does not exist.
res_rtp_asterisk
------------------
* The X.509 certificate used for DTLS negotation can now be automatically

@ -1296,7 +1296,9 @@ void iax_frame_free(struct iax_frame *fr)
ast_atomic_fetchadd_int(&frames, -1);
#if !defined(LOW_MEMORY)
if (!fr->cacheable || !(iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) {
if (!fr->cacheable
|| !ast_opt_cache_media_frames
|| !(iax_frames = ast_threadstorage_get(&frame_cache, sizeof(*iax_frames)))) {
ast_free(fr);
return;
}

@ -44,6 +44,15 @@ astsbindir => /usr/sbin
;minmemfree = 1 ; In MBs, Asterisk stops accepting new calls if
; the amount of free memory falls below this
; watermark.
;cache_media_frames = yes ; Cache media frames for performance
; Disable this option to help track down media frame
; mismanagement when using valgrind or MALLOC_DEBUG.
; The cache gets in the way of determining if the
; frame is used after being freed and who freed it.
; NOTE: This option has no effect when Asterisk is
; compiled with the LOW_MEMORY compile time option
; enabled because the cache code does not exist.
; Default yes
;cache_record_files = yes ; Cache recorded sound files to another
; directory during recording.
;record_cache_dir = /tmp ; Specify cache directory (used in conjunction

@ -66,6 +66,8 @@ enum ast_option_flags {
AST_OPT_FLAG_CACHE_RECORD_FILES = (1 << 13),
/*! Display timestamp in CLI verbose output */
AST_OPT_FLAG_TIMESTAMP = (1 << 14),
/*! Cache media frames for performance */
AST_OPT_FLAG_CACHE_MEDIA_FRAMES = (1 << 15),
/*! Reconnect */
AST_OPT_FLAG_RECONNECT = (1 << 16),
/*! Transmit Silence during Record() and DTMF Generation */
@ -99,7 +101,7 @@ enum ast_option_flags {
};
/*! These are the options that set by default when Asterisk starts */
#define AST_DEFAULT_OPTIONS AST_OPT_FLAG_TRANSCODE_VIA_SLIN
#define AST_DEFAULT_OPTIONS (AST_OPT_FLAG_TRANSCODE_VIA_SLIN | AST_OPT_FLAG_CACHE_MEDIA_FRAMES)
#define ast_opt_exec_includes ast_test_flag(&ast_options, AST_OPT_FLAG_EXEC_INCLUDES)
#define ast_opt_no_fork ast_test_flag(&ast_options, AST_OPT_FLAG_NO_FORK)
@ -116,6 +118,7 @@ enum ast_option_flags {
#define ast_opt_stdexten_macro ast_test_flag(&ast_options, AST_OPT_FLAG_STDEXTEN_MACRO)
#define ast_opt_dump_core ast_test_flag(&ast_options, AST_OPT_FLAG_DUMP_CORE)
#define ast_opt_cache_record_files ast_test_flag(&ast_options, AST_OPT_FLAG_CACHE_RECORD_FILES)
#define ast_opt_cache_media_frames ast_test_flag(&ast_options, AST_OPT_FLAG_CACHE_MEDIA_FRAMES)
#define ast_opt_timestamp ast_test_flag(&ast_options, AST_OPT_FLAG_TIMESTAMP)
#define ast_opt_reconnect ast_test_flag(&ast_options, AST_OPT_FLAG_RECONNECT)
#define ast_opt_transmit_silence ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSMIT_SILENCE)

@ -607,6 +607,9 @@ static char *handle_show_settings(struct ast_cli_entry *e, int cmd, struct ast_c
ast_cli(a->fd, " Transmit silence during rec: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_TRANSMIT_SILENCE) ? "Enabled" : "Disabled");
ast_cli(a->fd, " Generic PLC: %s\n", ast_test_flag(&ast_options, AST_OPT_FLAG_GENERIC_PLC) ? "Enabled" : "Disabled");
ast_cli(a->fd, " Min DTMF duration:: %u\n", option_dtmfminduration);
#if !defined(LOW_MEMORY)
ast_cli(a->fd, " Cache media frames: %s\n", ast_opt_cache_media_frames ? "Enabled" : "Disabled");
#endif
ast_cli(a->fd, " RTP use dynamic payloads: %u\n", ast_option_rtpusedynamic);
if (ast_option_rtpptdynamic == AST_RTP_PT_LAST_REASSIGN) {
@ -3714,6 +3717,11 @@ static void ast_readconfig(void)
/* Cache recorded sound files to another directory during recording */
} else if (!strcasecmp(v->name, "cache_record_files")) {
ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CACHE_RECORD_FILES);
#if !defined(LOW_MEMORY)
/* Cache media frames for performance */
} else if (!strcasecmp(v->name, "cache_media_frames")) {
ast_set2_flag(&ast_options, ast_true(v->value), AST_OPT_FLAG_CACHE_MEDIA_FRAMES);
#endif
/* Specify cache directory */
} else if (!strcasecmp(v->name, "record_cache_dir")) {
ast_copy_string(record_cache_dir, v->value, AST_CACHE_DIR_LEN);

@ -120,14 +120,18 @@ static void __frame_free(struct ast_frame *fr, int cache)
return;
#if !defined(LOW_MEMORY)
if (cache && fr->mallocd == AST_MALLOCD_HDR) {
if (fr->mallocd == AST_MALLOCD_HDR
&& cache
&& ast_opt_cache_media_frames) {
/* Cool, only the header is malloc'd, let's just cache those for now
* to keep things simple... */
struct ast_frame_cache *frames;
if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames))) &&
(frames->size < FRAME_CACHE_MAX_SIZE)) {
if ((fr->frametype == AST_FRAME_VOICE) || (fr->frametype == AST_FRAME_VIDEO) ||
(fr->frametype == AST_FRAME_IMAGE)) {
frames = ast_threadstorage_get(&frame_cache, sizeof(*frames));
if (frames && frames->size < FRAME_CACHE_MAX_SIZE) {
if (fr->frametype == AST_FRAME_VOICE
|| fr->frametype == AST_FRAME_VIDEO
|| fr->frametype == AST_FRAME_IMAGE) {
ao2_cleanup(fr->subclass.format);
}
@ -147,8 +151,9 @@ static void __frame_free(struct ast_frame *fr, int cache)
ast_free((void *) fr->src);
}
if (fr->mallocd & AST_MALLOCD_HDR) {
if ((fr->frametype == AST_FRAME_VOICE) || (fr->frametype == AST_FRAME_VIDEO) ||
(fr->frametype == AST_FRAME_IMAGE)) {
if (fr->frametype == AST_FRAME_VOICE
|| fr->frametype == AST_FRAME_VIDEO
|| fr->frametype == AST_FRAME_IMAGE) {
ao2_cleanup(fr->subclass.format);
}

Loading…
Cancel
Save