Jaroslav Kysela wrote: >On Wed, 12 Dec 2001, Tim Goetze wrote: > >> Abramo Bagnara wrote: >> >> >Tim Goetze wrote: >> >> >> >> >>but sometimes i also get inexplicable corruption within ordinary >> >> >>dynamically allocated memory of the process before it exits. >> >> >> >> >> >>i have spent considerable time on verifying that these are indeed >> >> >>caused by changing the period_size, and not by my own code. >> >> > >> >> >i would be too certain of this conclusion. i spent many a long night >> >> >and day and night tracking down places in the audioengine code that >> >> >had overrun the mmapped areas. i was only ever to track things down by >> >> >generating huge log files that detailed every byte i wrote, and then >> >> >use perl and awk to digest them. >> >> > >> >> >i'm not saying its not a problem in ALSA, only that its unlikely at >> >> >this point. what have you done to ensure that its not your code? do >> >> >you completely avoid writing data to the mapped areas? >> >> > >> >> >> >> yes, exactly that. i just did >> >> n * (open, configure, m * (poll, mmap_begin, mmap_commit), close) >> >> and nothing more. >> > >> >You're missing avail_update: it's *mandatory*. >> > >> >> i ran this twice, one time with the usual configure, and one time >> >> with f/c hardcoded to 64 in the hw setup so my code in all places believes >> >> it's going 2048, 1024, 512, ... -- not that it matters because i don't >> >> process any data. the one that's doing the 'real configure' crashes. >> >> yes, i did avail_update. sorry for not quoting all the 12111 lines of code >> involved. >> >> and before i receive another message like this: i also did snd_pcm_link, >> _unlink, _start and _drop. > >I think that it would be best for us to see some C code invoking the bad >behaviour to debug the alsa-lib's code.
so thinks me too. i've isolated the audio stream code and attached it. compile with 'gcc -o a.out main.cc -lasound -L/usr/lib', and run with for example $ ./a.out hw:0,0 2048 it will try to configure both streams on hw:0,0 for period_size of 2048, 1024 ... 64. on the awe it crashes when reaching 512. this code does not even link, start, drop or unlink - just configure the streams. it segfaults here before exit() even if i only configured them one time. tim
#include <unistd.h> #include <stdio.h> #include <alsa/asoundlib.h> #include <exception> #define D_SAMPLE_TO_INT(in,out) \ __asm__ __volatile__ ("fistpl %0" : "=m" (out) : "t" (in) : "st"); typedef float d_sample; typedef char int8; typedef short int16; typedef int int32; typedef long long int64; class blewit : public std::exception { public: char excuse [200]; blewit (const char * s = ""); blewit & operator << (const char * s); blewit & operator << (int i); blewit & operator << (double d); blewit & operator << (void * v); virtual const char * what() const { return excuse; } }; #define msg() _msg (__FILE__, __LINE__) class _msg { public: static FILE * file; FILE * f; static void init (const char * path = 0); _msg (FILE * _f); _msg (char * __file__ = 0, int __line__ = -1); ~_msg() { if (f == file) fputc ('\n', f); fflush (f); } operator FILE * () { return f; } _msg & operator << (const char * s) { if (!s) s = "(char *) 0"; fputs (s, f); return *this; } _msg & operator << (unsigned long long ll) { fprintf (f, "%llu ", ll); return *this; } _msg & operator << (long l) { fprintf (f, "%ld ", l); return *this; } _msg & operator << (unsigned long l) { fprintf (f, "%lu ", l); return *this; } _msg & operator << (int i) { fprintf (f, "%d ", i); return *this; } _msg & operator << (unsigned int i) { fprintf (f, "%u ", i); return *this; } _msg & operator << (const void * p) { fprintf (f, "<%p> ", p); return *this; } _msg & operator << (double d) { fprintf (f, "%.5f ", d); return *this; } }; FILE * _msg::file = stderr; _msg::_msg (FILE * _f) { f = _f; } _msg::_msg (char * __file__ = 0, int __line__ = -1) { f = file; if (__file__ && __line__ >= 0) fprintf (f, "%s:%d:", __file__, __line__); } void _msg::init (const char * path) { file = path ? fopen (path, "w") : 0; if (!file) file = stderr; else if (0) fcntl ( fileno (file), F_SETFL, fcntl (fileno (file), F_GETFL) | O_NONBLOCK ); } blewit::blewit (const char * s) { strcpy (excuse, s); } blewit & blewit::operator << (const char * s) { strcpy (excuse + strlen (excuse), s); return *this; } blewit & blewit::operator << (int i) { sprintf (excuse + strlen (excuse), "%d", i); return *this; } blewit & blewit::operator << (double d) { sprintf (excuse + strlen (excuse), "%.4f", d); return *this; } blewit & blewit::operator << (void * v) { sprintf (excuse + strlen (excuse), "<%p>", v); return *this; } enum { Input = 0, Output = 1 }; class AudioStream { public: snd_pcm_t * handle; snd_pcm_hw_params_t * hw_params; snd_pcm_sw_params_t * sw_params; const snd_pcm_channel_area_t * areas; public: snd_pcm_sframes_t available, delay; snd_pcm_uframes_t offset, frames; int frames_per_cycle, sample_rate; int type; /* Port::Input || Port::Output */ bool interleaved, configured; uint bytes_per_sample; int channels; AudioStream(); ~AudioStream(); void open (char * _name, int _type); /* Plugin::* */ virtual void configure (uint sample_rate, uint frames_per_cycle); virtual void process (uint _samples); private: void get_pollfd (struct pollfd * pfd) { snd_pcm_poll_descriptors (handle, pfd, 1); } bool link (AudioStream *); bool unlink(); bool prepare(); bool start(); bool stop(); void complete_silence (uint frames); /* returns negative on xrun. */ int frames_available(); /* returns number of frames accessible */ uint begin_cycle (uint _frames); /* commits */ bool end_cycle (uint _frames); protected: void silence (int channel); void stream_in (int channel, d_sample *); void stream_out (int channel, d_sample *); /* called by configure to initialize the ports. */ void setup_ports(); }; AudioStream::AudioStream() { type = -1; frames = 0; handle = 0; int error = snd_pcm_hw_params_malloc (&hw_params) | snd_pcm_sw_params_malloc (&sw_params); if (error) throw blewit ("cannot create a pcm_device_stream since we're out of memory."); } AudioStream::~AudioStream() { if (handle) snd_pcm_close (handle); snd_pcm_hw_params_free (hw_params); snd_pcm_sw_params_free (sw_params); } void AudioStream::open (char * _name, int _type) { type = _type; int error; if (type == Input) error = snd_pcm_open (&handle, _name, SND_PCM_STREAM_CAPTURE, 0); else error = snd_pcm_open (&handle, _name, SND_PCM_STREAM_PLAYBACK, 0); if (error) { msg() << "cannot open " << _name << " " << -error << ":" << snd_strerror (error) << "."; throw blewit() << "snd_pcm_open failed."; } } bool AudioStream::link (AudioStream * other) { int error = snd_pcm_link (handle, other->handle); if (error == 0) return true; msg() << " cannot link because " << -error << ":" << snd_strerror (error) << "."; /* already means that we haven't failed in a way. */ return error == -EALREADY || false; } bool AudioStream::unlink() { int error = snd_pcm_unlink (handle); if (error == 0) return true; msg() << " cannot unlink because " << -error << ":" << snd_strerror (error) << "."; /* already means that we haven't failed in a way. */ return error == -EALREADY || false; } bool AudioStream::prepare() { if (!configured) { msg() << " is not configured yet."; return false; } int error = snd_pcm_prepare (handle); if (error == 0) return true; msg() << " cannot prepare because " << -error << ":" << snd_strerror (error) << "."; return false; } bool AudioStream::start() { int error = snd_pcm_start (handle); if (error == 0) return true; msg() << " could not be started because " << -error << ":" << snd_strerror (error) << "."; return false; } bool AudioStream::stop() { int error = snd_pcm_drop (handle); if (error == 0) return true; msg() << "could not stop " << "because " << -error << ":" << snd_strerror (error) << "."; return false; } int AudioStream::frames_available() { snd_pcm_sframes_t a = snd_pcm_avail_update (handle); snd_pcm_delay (handle, (snd_pcm_sframes_t *) &delay); if (a >= 0) return (int) (available = a); int xrun = abs (delay); msg() << xrun << "samples " << (char *) (type == Input ? "over" : "under") << "run."; return -xrun; } uint AudioStream::begin_cycle (uint _frames) { frames = _frames; areas = 0; int error = snd_pcm_mmap_begin ( handle, &areas, (snd_pcm_uframes_t *) &offset, (snd_pcm_uframes_t *) &frames ); // msg() << "::begin_cycle = " << frames; if (error == 0) return frames; msg() << "mmap_begin failed" << " because " << snd_strerror (error); return 0; } bool AudioStream::end_cycle (uint _frames) { int error = snd_pcm_mmap_commit (handle, offset, _frames); // msg() << "::end_cycle = " << _frames; /* remember that the cycle has ended. */ frames = 0; areas = 0; /* the ALSA doc says it returns 0 on success, but >= 0 is more like it. */ if (error >= 0) return true; msg() << "cannot mmap_commit" << " because " << snd_strerror (error); return false; } /* Plugin::process for an AudioStream. Note that we inherently assume * that uint samples is the same as our available frames count. */ void AudioStream::process (uint samples) { if (samples != frames) throw blewit() << "number of frames to process mismatch."; end_cycle (frames); } /* silences *all* channels. could be optimized by checking for adjacency * of channel buffers. later. */ void AudioStream::complete_silence (uint frames) { for (int i = 0; i < channels; i++) silence (i); } /* silences this channel. */ void AudioStream::silence (int channel) { if (!frames) throw blewit() << "ringbuffer not open."; const snd_pcm_channel_area_t & area = areas[channel]; int8 * ptr = (int8 *) area.addr + ((area.first + area.step * offset) >> 3); uint to_process = frames; int step = area.step >> 3; if (bytes_per_sample == 4) { while (to_process--) { *((int32 *) ptr) = 0; ptr += step; } } else if (bytes_per_sample == 2) { while (to_process--) { *((int16 *) ptr) = 0; ptr += step; } } else if (bytes_per_sample == 1) { while (to_process--) { *ptr = 0; ptr += step; } } } /* streams audio data into the mmap buffer */ void AudioStream::stream_in (int channel, d_sample * buffer) { if (!frames) throw blewit() << "ringbuffer not open."; const snd_pcm_channel_area_t & area = areas[channel]; int8 * ptr = (int8 *) area.addr + ((area.first + area.step * offset) >> 3); int step = area.step >> 3; uint to_process = frames; if (bytes_per_sample == 4) { while (to_process--) { D_SAMPLE_TO_INT (0x7FFFFFFF * (*buffer), *((int32 *) ptr)); buffer++; ptr += step; } } else if (bytes_per_sample == 2) { while (to_process--) { *((int16 *) ptr) = (int16) (0x7FFF * (*buffer)); buffer++; ptr += step; } } else if (bytes_per_sample == 1) { while (to_process--) { *ptr = (int8) (0x7F * (*buffer)); buffer++; ptr += step; } } } /* streams audio data from the mmap buffer */ void AudioStream::stream_out (int channel, d_sample * buffer) { if (!frames) throw blewit() << "ringbuffer not open."; const snd_pcm_channel_area_t & area = areas[channel]; int8 * ptr = (int8 *) area.addr + ((area.first + area.step * offset) >> 3); uint step = area.step >> 3; uint to_process = frames; if (bytes_per_sample == 4) { while (to_process--) { *buffer = (d_sample) *((int32 *) ptr) / (d_sample) 0x7FFFFFFF; buffer++; ptr += step; } } else if (bytes_per_sample == 2) { while (to_process--) { *buffer = (d_sample) *((int16 *) ptr) / (d_sample) 32767.; buffer++; ptr += step; } } else if (bytes_per_sample == 1) { while (to_process--) { *buffer = (d_sample) *ptr / 127.; buffer++; ptr += step; } } } /* after configuring the stream, this creates the ports that the stream * presents as a Plugin. */ void AudioStream::setup_ports() { } /* the _big_ configure. */ #define CONFIGURE(what,ifnot) \ { \ int error = what; \ if (error) \ { \ msg() << ifnot << " in AudioStream" \ << "::configure (line " << __LINE__ << "): " \ << snd_strerror (error); \ throw blewit() << ifnot; \ } \ } void AudioStream::configure (uint _sample_rate, uint _frames_per_cycle) { if (configured) if (sample_rate == _sample_rate) if (frames_per_cycle == _frames_per_cycle) return; configured = false; sample_rate = _sample_rate; frames_per_cycle = _frames_per_cycle; CONFIGURE (snd_pcm_hw_params_any (handle, hw_params), "no 'any' configuration to start with."); CONFIGURE (snd_pcm_hw_params_set_periods_integer (handle, hw_params), "no integer period size."); int error = snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); if (error == 0) interleaved = false; else { CONFIGURE (snd_pcm_hw_params_set_access (handle, hw_params, SND_PCM_ACCESS_MMAP_INTERLEAVED), "memory-mapped access method refused."); interleaved = true; } /* determine sample format */ snd_pcm_format_t formats [3] = { SND_PCM_FORMAT_S32, SND_PCM_FORMAT_S16, SND_PCM_FORMAT_S8 }; bytes_per_sample = ~0; for (int i = 0; i < 3; i++) { error = snd_pcm_hw_params_set_format (handle, hw_params, formats[i]); if (error == 0) { bytes_per_sample = 4 >> i; break; } } if (bytes_per_sample > 4) throw blewit() << "none of (32, 16, 8) bit sample sizes available."; CONFIGURE (snd_pcm_hw_params_set_rate (handle, hw_params, sample_rate, 0), "unsupported sample rate."); channels = snd_pcm_hw_params_get_channels_max (hw_params); error = snd_pcm_hw_params_set_channels (handle, hw_params, channels); if (error) { msg() << " does not want to give us all its " << channels << " channels."; throw blewit() << "no channels available."; } CONFIGURE (snd_pcm_hw_params_set_period_size (handle, hw_params, (uint) frames_per_cycle, 0), /* XXX */ "hardware refuses this frames_per_cycle setting."); CONFIGURE (snd_pcm_hw_params_set_periods (handle, hw_params, 2, 0), "hardware refuses 2 cycles per buffer."); CONFIGURE (snd_pcm_hw_params_set_buffer_size (handle, hw_params, 2 * frames_per_cycle), /* XXX */ "hardare refuses a buffer size of 2 * frames_per_cycle."); CONFIGURE (snd_pcm_hw_params (handle, hw_params), "hardware setup failed."); /* sw params */ snd_pcm_sw_params_current (handle, sw_params); CONFIGURE (snd_pcm_sw_params_set_start_threshold (handle, sw_params, ~0u), "cannot turn off start threshold."); CONFIGURE (snd_pcm_sw_params_set_stop_threshold (handle, sw_params, ~0u), "cannot turn off stop threshold."); CONFIGURE (snd_pcm_sw_params_set_silence_threshold (handle, sw_params, 0), "cannot set 0 silence threshold."); CONFIGURE (snd_pcm_sw_params_set_silence_size (handle, sw_params, 0), "cannot set 0 silence size."); uint avail_min = frames_per_cycle; CONFIGURE (snd_pcm_sw_params_set_avail_min (handle, sw_params, avail_min), "software setup for minimum available frames failed."); CONFIGURE (snd_pcm_sw_params (handle, sw_params), "software setup failed."); setup_ports(); configured = true; msg() << " configured for " << sample_rate << frames_per_cycle << (8 * bytes_per_sample) << "bit " << (char *) (interleaved ? "interleaved" : "non-interleaved"); } int main (int argc, char ** argv) { if (argc != 3) { _msg() << argv[0] << " <device> <initial period_size>"; return 1; } int fpc; if (sscanf (argv[2], "%d", &fpc) != 1) { _msg() << argv[2] << " is not a valid period size."; return 2; } try { while (fpc >= 64) { AudioStream playback, capture; capture.open (argv[1], Input); playback.open (argv[1], Output); capture.configure (44100, fpc); playback.configure (44100, fpc); sleep (1); fpc >>= 1; } } catch (exception & e) { _msg() << e.what(); return 3; } return 0; }