Thursday, October 21, 2010

Capturing stdio for a (AGI) Perl process.

I found myself debugging a weird issue the other day with an AGI script that caused me to believe Asterisk::AGI was being confused (as in not doing it properly) about blocking on STDIN for the responses to "GET DATA" requests. Which in turn reminded me that I needed to start capturing a large set of AGI protocol data to write tests around once I have a functioning version of the "Asterisk::MAGI" package I intend on starting soon.

Now multiplexing the input and output of a FileHandle in Perl isn't a difficult thing to accomplish when you have complete control of the FileHandle you want to multiplex. This however is not the case in some (maybe a lot) of Perl packages. They like to do evil things like interacting directly and statically to STDIN, STDOUT, STDERR. Sometimes they even give you the glimmer of hope by allowing you to pass a FileHandle to the constructor or a method but almost willfully ignore the idea of persisting any FileHandle you would like to operate on; spaghetti monster forbid you want to use an "old school" Perl package with a Socket or an IO::Handle object (in memory FileHandles for testing anyone?).

At first when trying to tackle this problem I fooled around with manipulating IO layers using PerlIO (which is neat in it's own right) but found that marshaling read data from an input FileHandle to write's on another FileHandle a bit tricky. It seems that things like the multplexing PerlIO utility layer "tee" worked well on FileHandles being written to (STDOUT) but not for reads. Then, I started hacking around this by trying to insert a "filter" CODE reference hook as a layer for my input FileHandle using PerlIO::code - but this seemed kludgey and a lot of code for a seemingly simple task.

Frustrated and suffering my lack of knowledge regarding PerlIO in general I turned to IRC at which point Matt Trout gave me a perfectly simple and elegant solution to my problem: strace

Why bother with a whole slew of code to do "magic" with FileHandles when I could just trace the system calls for the process in question and capture read and write calls to the file descriptors in question.

The result was this little snippet that I modified from his example:

Which will take input like this:

And gives you this:

Since the whole point of this little exercise was to allow me to capture a lot of IO data between Asterisk and a   Perl AGI script I decided that a system() call to strace would be good as calling strace from a dialplan is a bit of a pain:

Just do the above in a BEGIN block and you are set as the -s flag tells strace to keep a large string buffer and the -ff option tells strace to name it's -o output file by pid.

No comments:

Post a Comment