<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2678798148555289975</id><updated>2012-02-22T19:00:51.027-08:00</updated><category term='90th percentile'/><category term='python json one-liner'/><category term='erlang sysadmin escript average'/><category term='scalability'/><category term='auto'/><category term='java'/><category term='erlang'/><category term='hotswap'/><category term='reload'/><category term='ssh'/><category term='load generation'/><category term='privacy'/><category term='os x applescript sudo gksudo psuedo'/><category term='osx'/><category term='compile'/><category term='test automation'/><category term='erlang sysadmin escript plot'/><category term='interface'/><category term='applescript'/><category term='localcommand'/><category term='location'/><category term='iPhone'/><category term='dns'/><category term='shell'/><category term='python'/><category term='erlang sysadmin escript compile beam'/><category term='performance'/><category term='statistics'/><category term='consolidated.db'/><category term='wireshark osx'/><category term='load test'/><title type='text'>Technical Musings</title><subtitle type='html'>Tools of a technical generalist</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>14</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-7078797073492103615</id><published>2012-02-06T19:47:00.000-08:00</published><updated>2012-02-22T19:00:51.035-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ssh'/><category scheme='http://www.blogger.com/atom/ns#' term='osx'/><category scheme='http://www.blogger.com/atom/ns#' term='applescript'/><category scheme='http://www.blogger.com/atom/ns#' term='localcommand'/><title type='text'>SSH LocalCommand / OSX Terminal Colors</title><content type='html'>If you're like me, you work in many different computing environments: home, test, QA, and production. &amp;nbsp;And it's REALLY important not to get them mixed up. &amp;nbsp;I've always used a color scheme in my ssh/console windows to help me differentiate each. &amp;nbsp; I've manually set the color of each console before I log in, and try not to reuse a console for different environments. &amp;nbsp;It's kind of a pain, and if I'm brain dead enough to not notice what host I'm logged into, chances are high that I'm not going to set the colors correctly either. &amp;nbsp;So I created an OSX (I'm on 10.7) AppleScript to automatically change the Terminal colors for each of my windows/tabs to match the environment that console is logged into.&lt;br /&gt;&lt;br /&gt;To trigger the AppleScript, I added it to an until-now-unknown to me ssh client setting: LocalCommand. &amp;nbsp;It will run a command locally each time you make an ssh connection.&lt;br /&gt;&lt;br /&gt;To setup the trigger, you must add the following lines into ~/.ssh/config:&lt;br /&gt;(Note: it's important to add the &amp;amp; at the end of LocalCommand to put the command in background so it can wait for Terminal to connect)&lt;br /&gt;&lt;pre style="background-image: URL(http://2.bp.blogspot.com/_z5ltvMQPaa8/SjJXr_U2YBI/AAAAAAAAAAM/46OqEP32CJ8/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt; Host *&lt;br /&gt;  &lt;br /&gt;   PermitLocalCommand yes&lt;br /&gt;  &lt;br /&gt;   LocalCommand /Users/MYNAME/applescript/term_tab_style.scpt &amp;amp;&lt;br /&gt;  &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;UPDATE:And this in /etc/ssh_config:&lt;br /&gt;&lt;pre style="background-image: URL(http://2.bp.blogspot.com/_z5ltvMQPaa8/SjJXr_U2YBI/AAAAAAAAAAM/46OqEP32CJ8/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;  PermitLocalCommand yes  &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The AppleScript goes through all open Terminal windows/tabs and checks the title. &amp;nbsp;This is normally set to the user@hostname, and I have a naming convention 'purpose'-'env'-'location'-'number'. &amp;nbsp;Applescript has very limited parsing capabilites; I found the functions I used &lt;a href="http://erikslab.com/2007/08/31/applescript-how-to-split-a-string/"&gt;here&lt;/a&gt; to split the hostname up and get the environment name. &amp;nbsp;You can then set the Terminal window/tab to whatever Profile you desire.&lt;br /&gt;&lt;br /&gt;The code for term_tab_style.scpt. &amp;nbsp;Copy this code into wherever you specified the LocalCommand in ~/.ssh/config and don't forget to run 'chmod u+x ~/term_tab_style.scpt'.&lt;br /&gt;&lt;br /&gt;&lt;pre style="background-image: URL(http://2.bp.blogspot.com/_z5ltvMQPaa8/SjJXr_U2YBI/AAAAAAAAAAM/46OqEP32CJ8/s320/codebg.gif); background: #f0f0f0; border: 1px dashed #CCCCCC; color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt; #!/usr/bin/osascript&lt;br /&gt;  &lt;br /&gt; on run argv&lt;br /&gt;&lt;br /&gt;   delay 1&lt;br /&gt;  &lt;br /&gt;   tell application "Terminal"&lt;br /&gt;     repeat with w from 1 to count windows&lt;br /&gt;       repeat with t from 1 to count tabs of window w&lt;br /&gt;         set title to custom title of tab t of window w  &lt;br /&gt;         try&lt;br /&gt;           set myArray to my theSplit(title, "@")&lt;br /&gt;           set userName to my getArrayValue(myArray, 1)&lt;br /&gt;           set restName to my getArrayValue(myArray, 2)&lt;br /&gt;           set myArray to my theSplit(restName, "-")&lt;br /&gt;           set envName to my getArrayValue(myArray, 2)&lt;br /&gt;           if envName is "pro" then&lt;br /&gt;             #display dialog ("" &amp;amp; envName)&lt;br /&gt;                set current settings of tab t of window w to (first settings set whose name is "Basic Blue")&lt;br /&gt;           else if envName is "qa" then&lt;br /&gt;                set current settings of tab t of window w to (first settings set whose name is "Basic")&lt;br /&gt;           end if&lt;br /&gt;         end try&lt;br /&gt;       end repeat&lt;br /&gt;     end repeat&lt;br /&gt;   end tell&lt;br /&gt;  &lt;br /&gt; end run&lt;br /&gt;  &lt;br /&gt; on theSplit(theString, theDelimiter) &lt;br /&gt;   -- save delimiters to restore old settings&lt;br /&gt;   set oldDelimiters to AppleScript's text item delimiters&lt;br /&gt;   -- set delimiters to delimiter to be used&lt;br /&gt;   set AppleScript's text item delimiters to theDelimiter&lt;br /&gt;   -- create the array&lt;br /&gt;   set theArray to every text item of theString  &lt;br /&gt;   -- restore the old setting  &lt;br /&gt;   set AppleScript's text item delimiters to oldDelimiters  &lt;br /&gt;   -- return the result  &lt;br /&gt;   return theArray &lt;br /&gt; end theSplit&lt;br /&gt;  &lt;br /&gt; on getArrayValue(array, location)  &lt;br /&gt;   -- very important -- The list index starts at 1 not 0  &lt;br /&gt;   return item location in array  &lt;br /&gt; end getArrayValue&lt;br /&gt;  &lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-7078797073492103615?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/7078797073492103615/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=7078797073492103615' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/7078797073492103615'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/7078797073492103615'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2012/02/ssh-localcommand-osx-terminal-colors.html' title='SSH LocalCommand / OSX Terminal Colors'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-3211323824654733789</id><published>2012-01-12T12:02:00.000-08:00</published><updated>2012-01-12T13:21:20.163-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python json one-liner'/><title type='text'>Python JSON one-liner</title><content type='html'>I've seen the python one-liner to parse and pretty-print json:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;cat test.json | python -mjson.tool&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;But I still have to grep/awk to get the value. &amp;nbsp;Here's a python one-liner to get a json value:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;curl -s -u user:password "http://test.json.output"  | python -c "import json,sys;input=json.load(sys.stdin);print (input.get('messages'))"&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;A reasonably short, pure python way to do this is with the &lt;a href="http://docs.python-requests.org/en/latest/index.html"&gt;requests&lt;/a&gt; library, but that's not built into python, and I find one-liners are only useful if they work across a lot of systems as-is:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;import json,requests&lt;br /&gt;r = requests.get("http://test.json.output/api/", auth=('user','pass'))&lt;br /&gt;o = json.loads(r.content)&lt;br /&gt;print (o.get('messages'))&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-3211323824654733789?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/3211323824654733789/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=3211323824654733789' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/3211323824654733789'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/3211323824654733789'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2012/01/ive-seen-python-one-liner-to-parse-and.html' title='Python JSON one-liner'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-1487843351826205703</id><published>2011-12-06T19:13:00.001-08:00</published><updated>2011-12-20T13:08:24.536-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wireshark osx'/><title type='text'>Wireshark on OS X</title><content type='html'>I run Wireshark (formerly Ethereal) on OSX, but by default only root has rights to the ethernet devices. &amp;nbsp;So either you run it as root, which is a security risk, or you give your own user the rights. &amp;nbsp;The devices (/dev/bf*) that are used are recreated every boot, so just 'chown'ing them won't do.&lt;br /&gt;&lt;br /&gt;First add your user to the wheel group:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;dscl . append /Groups/wheel GroupMembership 'username'&lt;/blockquote&gt;&lt;br /&gt;The run this command:&lt;br /&gt;&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;sudo chmod g+rw /dev/bpf*; open /Applications/Wireshark.app&lt;/blockquote&gt;&lt;br /&gt;I added that command to my .profile file as an alias for convenience:&lt;br /&gt;&lt;br /&gt;&lt;blockquote class="tr_bq"&gt;alias wireshark='sudo chmod g+rw /dev/bpf*; open /Applications/Wireshark.app'&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-1487843351826205703?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/1487843351826205703/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=1487843351826205703' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/1487843351826205703'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/1487843351826205703'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2011/12/wireshark-on-os-x.html' title='Wireshark on OS X'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-2272670743975321433</id><published>2011-12-06T18:47:00.001-08:00</published><updated>2011-12-06T19:12:35.403-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='os x applescript sudo gksudo psuedo'/><title type='text'>MSudo: Mac OSX Graphical SUDO</title><content type='html'>&lt;br /&gt;&lt;br /&gt;I develop a lot on OSX and there are times I need to run a GUI app as root, like a gui editor of a systems file, or running Wireshark.  I could just run it from a terminal using "sudo", but that's not very cool.  There is the &lt;a href="http://corz.org/osx/tut/pseudohow.php"&gt;Pseudo&lt;/a&gt; app, which is quite cool, and only $15, but I figured it wouldn't be too hard to script something together.  Well, Many Hours Later, I've got something!  An AppleScript droplet that can be added to your dock and when you drag another App on it, a GUI popup will ask for your password, and voila! &amp;nbsp;You have a GUI app running as root.&lt;br /&gt;&lt;br /&gt;Open Applications..Utilities..AppleScriptEditor, paste this below, and then save it as MSudo.app, as an Application. &amp;nbsp;Then drag it onto your Dock&lt;br /&gt;&lt;br /&gt;Open&lt;br /&gt;&lt;blockquote&gt;on open {filename}&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set p to POSIX path of filename&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set myArray to my theSplit(p, "/")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set numItems to (count myArray) - 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set AppNameFull to getArrayValue(myArray, numItems)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set myArray to my theSplit(AppNameFull, ".")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set numItems to (count myArray) - 1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set AppName to getArrayValue(myArray, 1)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;do shell script p &amp;amp; "/Contents/MacOS/" &amp;amp; AppName with administrator privileges&lt;br /&gt;end open&lt;br /&gt;on theSplit(theString, theDelimiter)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-- save delimiters to restore old settings&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set oldDelimiters to AppleScript's text item delimiters&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-- set delimiters to delimiter to be used&lt;br /&gt;set AppleScript's text item delimiters to theDelimiter&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-- create the array&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set theArray to every text item of theString&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-- restore the old setting&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;set AppleScript's text item delimiters to oldDelimiters&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-- return the result&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return theArray&lt;br /&gt;end theSplit&lt;br /&gt;on getArrayValue(array, location)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;-- very important -- The list index starts at 1 not 0&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return item location in array&lt;br /&gt;end getArrayValue&lt;/blockquote&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-2272670743975321433?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/2272670743975321433/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=2272670743975321433' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/2272670743975321433'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/2272670743975321433'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2011/12/msudo-mac-osx-graphical-sudo.html' title='MSudo: Mac OSX Graphical SUDO'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-1271643846551071066</id><published>2011-04-28T07:41:00.000-07:00</published><updated>2011-04-28T08:09:36.015-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang sysadmin escript compile beam'/><title type='text'>Compiling Erlang escript to .beam</title><content type='html'>As part of my experiments in command line Erlang escripts, I've been looking into compiling escripts into .beam files.  There is a bit of a wait on starting my .escript files, and command line tools should have as little delay on startup as possible since they are generally started and stopped often.&lt;br /&gt;&lt;br /&gt;First, I compiled my .escript files to .beam, I did this with this bash script:&lt;br /&gt;&lt;br /&gt;erlsee.sh (get it?):&lt;br /&gt;&lt;pre class="brush: bash"&gt;#!/usr/bin/bash&lt;br /&gt;test=$1&lt;br /&gt;echo $test&lt;br /&gt;&lt;br /&gt;args=("$@")&lt;br /&gt;file="$1"&lt;br /&gt;echo ${file}&lt;br /&gt;temp_file=$1.tmp&lt;br /&gt;echo $temp_file&lt;br /&gt;cp $file $temp_file&lt;br /&gt;file=`echo "$1" | sed s/.escript/.erl/g` &lt;br /&gt;sed -n '1!p' $temp_file &gt; $file&lt;br /&gt;/usr/bin/env erlc $file&lt;br /&gt;cp $temp_file $file&lt;br /&gt;rm $temp_file&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This allows you to compile an escript directly without having to remove the #! directives at the top or rename the file to have an .erl ending.&lt;br /&gt;&lt;br /&gt;Secondly, I added the same #! headers to the .beam files, and set execution permissions (`chmod u+x *.beam`).  Just open the .beam file with a text editor and paste the following lines at the top:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;#!/usr/bin/env escript&lt;br /&gt;%%! -smp disable&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Third, I tested the running times with a very small input file with the 'time' utility.  I figure this time should be dominated by the basic start up time, not the execution speeds.&lt;br /&gt;&lt;br /&gt;$ time cat test.txt | ./&lt;a href="http://technicalmusings.blogspot.com/2011/04/running-average-in-erlang-escript.html"&gt;runavg.escript&lt;/a&gt; -s 5 -i 5 | ./&lt;a href="http://technicalmusings.blogspot.com/2011/04/console-graphing-in-erlang.html"&gt;tgraph.escript&lt;/a&gt;&lt;br /&gt;************************************************************************24&lt;br /&gt;*********************************************15&lt;br /&gt;&lt;br /&gt;real    0m2.077s&lt;br /&gt;user    0m0.121s&lt;br /&gt;sys     0m0.045s&lt;br /&gt;&lt;br /&gt;$ time cat test.txt | ./runavg.beam -s 5 -i 5 | ./tgraph.beam&lt;br /&gt;************************************************************************24&lt;br /&gt;*********************************************15&lt;br /&gt;&lt;br /&gt;real    0m2.182s&lt;br /&gt;user    0m0.090s&lt;br /&gt;sys     0m0.138s&lt;br /&gt;&lt;br /&gt;I ran this multiple times, and the averages were almost equal; certainly not a big improvment.  So it's not worth the extra effort to compile your escript files to .beam to improve startup times.  &lt;br /&gt;&lt;br /&gt;Execution time is another matter; it is probably improved, but that doesn't really matter for programs that just output text.  They are fast enough as is.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-1271643846551071066?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/1271643846551071066/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=1271643846551071066' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/1271643846551071066'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/1271643846551071066'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2011/04/compiling-erlang-escript-to-beam.html' title='Compiling Erlang escript to .beam'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-1924566539039884749</id><published>2011-04-27T18:50:00.000-07:00</published><updated>2011-04-27T18:52:28.522-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang sysadmin escript average'/><title type='text'>Running average in Erlang (escript)</title><content type='html'>Another python util I've created a while back, and usually use in conjunction with &lt;a href="http://technicalmusings.blogspot.com/2011/04/console-graphing-in-erlang.html"&gt;tgraph&lt;/a&gt;, is a running (or moving) average util that can also be piped into.&amp;nbsp; So, as another exercise, I created an Erlang/escript version. &lt;br /&gt;&lt;br /&gt;The main use case is when I'm looking into a problem, and tailing a log.&amp;nbsp; Too much data.&amp;nbsp; Awk/perl/pyline the interesting numbers and tail that.&amp;nbsp; Numbers running too fast or changing too often to see a pattern.&amp;nbsp; So I then pipe the number stream through a running average and then to tgraph.&lt;br /&gt;&lt;br /&gt;tail -f some.log | awk -F" " '{print $10}' | ./runavg.escript | ./tgraph.escript&lt;br /&gt;&lt;br /&gt;Another similar case is to run a whole or portion of an existing log through runavg/tgraph to look for a pattern.&amp;nbsp; Make sure you set your terminal's buffer size to 2-3K before trying that.&lt;br /&gt;&lt;br /&gt;tail -10000 some.log | awk -F" " '{print $10}' | ./runavg.escript -s 10 -i 10 | ./tgraph.escript -t 1000&lt;br /&gt;&lt;br /&gt;runavg.escript&lt;br /&gt;&lt;pre class="brush: erlang"&gt;#!/usr/bin/env escript&lt;br /&gt;%% -*- erlang -*-&lt;br /&gt;%%! -smp disable&lt;br /&gt;%% Author: Drew Gulino&lt;br /&gt;-module(runavg).&lt;br /&gt;&lt;br /&gt;-export([main/1]).&lt;br /&gt;&lt;br /&gt;main(CmdLine) -&amp;gt;&lt;br /&gt;OptSpecList = option_spec_list(),&lt;br /&gt;case getopt:parse(OptSpecList, CmdLine) of&lt;br /&gt;&amp;nbsp; {ok, {Options, NonOptArgs}} -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; true;&lt;br /&gt;&amp;nbsp; {error, {Reason, Data}} -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Options = [],&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; NonOptArgs = [],&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; io:format("Error: ~s ~p~n~n", [Reason, Data]),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; version(),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; getopt:usage(OptSpecList, "runavg")&lt;br /&gt;end,&lt;br /&gt;SampleSize = get_opt_value(size,Options),&lt;br /&gt;OutputInterval = get_opt_value(interval,Options),&lt;br /&gt;&lt;br /&gt;case NonOptArgs of&lt;br /&gt;&amp;nbsp; [] -&amp;gt;&lt;br /&gt;&amp;nbsp; F = standard_io;&lt;br /&gt;&amp;nbsp; _ -&amp;gt;&lt;br /&gt;&amp;nbsp; {ok, F} = file:open(NonOptArgs, read)&lt;br /&gt;end,&lt;br /&gt;%io:format("~p,~p,~p~n",[F, SampleSize, OutputInterval]),&lt;br /&gt;proc_file(F, SampleSize, OutputInterval ).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;proc_file(F, SampleSize, OutputInterval) -&amp;gt;&lt;br /&gt;&amp;nbsp;%io:format("1"),&lt;br /&gt;&amp;nbsp;proc_file(F, SampleSize, OutputInterval, [], 0, 0).&lt;br /&gt;&lt;br /&gt;proc_file(F, SampleSize, OutputInterval, SampleAcc, SampleCount, IntervalCount) when IntervalCount &amp;gt;= OutputInterval -&amp;gt;&lt;br /&gt;&amp;nbsp; io:format("~.2f~n",[lists:sum(SampleAcc)/erlang:length(SampleAcc)]),&lt;br /&gt;&amp;nbsp; proc_file(F, SampleSize, OutputInterval, SampleAcc, SampleCount, 0);&lt;br /&gt;proc_file(F, SampleSize, OutputInterval, [_|T] , SampleCount, IntervalCount) when SampleCount &amp;gt;= SampleSize -&amp;gt;&lt;br /&gt;&amp;nbsp; %io:format("T: ~p~n",[T]),&lt;br /&gt;&amp;nbsp; proc_file(F, SampleSize, OutputInterval, T, SampleCount - 1, IntervalCount);&lt;br /&gt;proc_file(F, SampleSize, OutputInterval, SampleAcc, SampleCount, IntervalCount) -&amp;gt;&lt;br /&gt;%io:format("SampleAcc: ~p~n", [SampleAcc]),&lt;br /&gt;L = io:get_line(F, ''),&lt;br /&gt;case L of&lt;br /&gt;&amp;nbsp; eof -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ok;&lt;br /&gt;&amp;nbsp; "\n" -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; false;&lt;br /&gt;&amp;nbsp; Line -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Stripped = strip_newlines(Line),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Num = cast_to_integer(Stripped),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; proc_file(F, SampleSize, OutputInterval, [Num] ++ SampleAcc, SampleCount + 1, IntervalCount + 1)&lt;br /&gt;end.&lt;br /&gt;&lt;br /&gt;version() -&amp;gt;&lt;br /&gt;&amp;nbsp; io:format("Version: 1.0\n").&lt;br /&gt;&lt;br /&gt;get_opt_value(Key, Options) -&amp;gt;&lt;br /&gt;&amp;nbsp; case lists:keyfind(Key,1,Options) of&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {Key, Value} -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Value&lt;br /&gt;&amp;nbsp; end,&lt;br /&gt;&amp;nbsp; Value.&lt;br /&gt;&lt;br /&gt;option_spec_list() -&amp;gt;&lt;br /&gt;[&lt;br /&gt;%% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg}&lt;br /&gt;{help, $h, "help", undefined, "Show the program options"},&lt;br /&gt;{version, $v, "version", undefined, "Version"},&lt;br /&gt;{size, $s, "size", {integer, 5}, "Size of average sample, Default=5"},&lt;br /&gt;{interval, $i, "interval", {integer, 5}, "How many input entries before average is displayed, Default=5"}&lt;br /&gt;].&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;strip_newlines(String) -&amp;gt;&lt;br /&gt;string:strip(re:replace(String,"(.*)[\n\r]","\\1", [global,{return,list}])).&lt;br /&gt;&lt;br /&gt;cast_to_integer([]) -&amp;gt;&lt;br /&gt;[];&lt;br /&gt;cast_to_integer(Input) when is_integer(Input) -&amp;gt;&lt;br /&gt;Input;&lt;br /&gt;cast_to_integer(Input) when is_float(Input) -&amp;gt;&lt;br /&gt;erlang:round(Input);&lt;br /&gt;cast_to_integer(Input) when is_list(Input)-&amp;gt;&lt;br /&gt;case lists:member($., Input) of&lt;br /&gt;&amp;nbsp; true -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; erlang:round(erlang:list_to_float(Input));&lt;br /&gt;&amp;nbsp; false -&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; erlang:list_to_integer(Input)&lt;br /&gt;end. &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;A util I use to test numeric pipes is a random number streamer:&lt;br /&gt;randstream.escript:&lt;br /&gt;&lt;pre class="brush: erlang"&gt;#!/usr/bin/env escript&lt;br /&gt;%% -*- erlang -*-&lt;br /&gt;%%! -smp disable&lt;br /&gt;%% Author: Drew Gulino&lt;br /&gt;-module(randstream).&lt;br /&gt;&lt;br /&gt;-export([main/1]).&lt;br /&gt;&lt;br /&gt;main(_) -&amp;gt;&lt;br /&gt;{A1,A2,A3} = now(),&lt;br /&gt;random:seed(A1, A2, A3),&lt;br /&gt;rand().&lt;br /&gt;&lt;br /&gt;rand() -&amp;gt;&lt;br /&gt;Num = random:uniform(100),&lt;br /&gt;io:format("~B~n",[Num]),&lt;br /&gt;rand().&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;./randstream.escript | ./runavg.escript -s 100 -i 100&lt;br /&gt;&lt;br /&gt;should return a list of numbers right around 49 (average between 0 and 100).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-1924566539039884749?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/1924566539039884749/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=1924566539039884749' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/1924566539039884749'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/1924566539039884749'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2011/04/running-average-in-erlang-escript.html' title='Running average in Erlang (escript)'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-4215991061279813238</id><published>2011-04-25T21:26:00.000-07:00</published><updated>2011-07-28T14:35:24.172-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang sysadmin escript plot'/><title type='text'>Console plotting in Erlang</title><content type='html'>Years ago I created a simple Python script to plot a list of numbers in a simple ASCII line graph.  I use this script all the time; I extract (with awk,perl,&lt;a href="http://code.activestate.com/recipes/577075-pyliner-script-to-run-arbitrary-python-code-on-the/?in=user-4119417"&gt;pyliner&lt;/a&gt;,...) a column of numbers out of a log file and pipe it in to this script.  Very useful since even to today most admin work is done in a character based terminal.&lt;br /&gt;&lt;br /&gt;UPDATE (4/28/2011): Also check my other terminal console escript entries: &lt;a href="http://technicalmusings.blogspot.com/2011/04/running-average-in-erlang-escript.html"&gt;runavg.escript&lt;/a&gt;, &lt;a href="http://technicalmusings.blogspot.com/2011/04/compiling-erlang-escript-to-beam.html"&gt;escript to beam&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;All numbers are rounded to an integer.&amp;nbsp; It auto resizes new graph entries when a new max is set (default max is 0), and plots a new max line in &lt;b&gt;bold&lt;/b&gt;.&amp;nbsp; If you set a threshold, it outputs any line over that threshold in &lt;span style="color: #cc0000;"&gt;red&lt;/span&gt;.&amp;nbsp; Or &lt;b&gt;&lt;span style="color: #cc0000;"&gt;both&lt;/span&gt;&lt;/b&gt;. &lt;br /&gt;&lt;br /&gt;I created an Erlang escript version of it, just as an exercise.  It requires 'tput' to be in the path, this even works with the cygwin version.  It also uses &lt;a href="https://github.com/jcomellas/getopt#readme"&gt;An Erlang version of getopt&lt;/a&gt;.  I didn't bother to install it, I just copied &lt;a href="https://github.com/jcomellas/getopt/raw/master/src/getopt.erl"&gt;getopt.erl&lt;/a&gt; to my src dir and compiled it.  Also &lt;code&gt;chmod u+x&lt;/code&gt; tgraph.escript&lt;br /&gt;&lt;br /&gt;It's called as part of a pipe:&lt;br /&gt;&lt;code&gt;$ cat test.txt | ./tgraph.escript -t 40 -c 40&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;or with the file as a parameter:&lt;br /&gt;&lt;code&gt;./tgraph.escript test.txt -t 40 -c 40&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;test.txt:&lt;br /&gt;&lt;code&gt;1&lt;br /&gt;50&lt;br /&gt;20&lt;br /&gt;3&lt;br /&gt;45&lt;br /&gt;34.0&lt;br /&gt;12&lt;br /&gt;0&lt;br /&gt;1000&lt;br /&gt;0&lt;br /&gt;100&lt;br /&gt;34&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Output:&lt;br /&gt;&lt;code&gt;&lt;span style="background-color: black; color: white;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;b&gt;****************************************1&lt;/b&gt;&lt;br /&gt;&lt;span style="color: #cc0000;"&gt;****************************************50&lt;/span&gt;&lt;br /&gt;****************20&lt;br /&gt;**3&lt;br /&gt;&lt;span style="color: #cc0000;"&gt;************************************45&lt;/span&gt;&lt;br /&gt;***************************34&lt;br /&gt;**********12&lt;br /&gt;0&lt;br /&gt;&lt;b&gt;&lt;span style="color: #cc0000;"&gt;****************************************1000&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;0&lt;br /&gt;&lt;span style="color: #cc0000;"&gt;****100&lt;/span&gt;&lt;br /&gt;*34&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;tgraph.escript:&lt;br /&gt;&lt;pre class="brush: erlang"&gt;#!/usr/bin/env escript&lt;br /&gt;%% -*- erlang -*-&lt;br /&gt;%%! -smp disable&lt;br /&gt;%% Author: Drew Gulino&lt;br /&gt;-module(tgraph).&lt;br /&gt;&lt;br /&gt;-export([main/1]).&lt;br /&gt;&lt;br /&gt;main(CmdLine) -&amp;gt;&lt;br /&gt; OptSpecList = option_spec_list(),&lt;br /&gt; case getopt:parse(OptSpecList, CmdLine) of&lt;br /&gt;  {ok, {Options, NonOptArgs}} -&amp;gt;&lt;br /&gt;   true;&lt;br /&gt;  {error, {Reason, Data}} -&amp;gt;&lt;br /&gt;   Options = [],&lt;br /&gt;   NonOptArgs = [],&lt;br /&gt;   io:format("Error: ~s ~p~n~n", [Reason, Data]),&lt;br /&gt;   version(),&lt;br /&gt;   getopt:usage(OptSpecList, "tgraph")&lt;br /&gt; end,&lt;br /&gt; Symbol = get_opt_value(symbol,Options),&lt;br /&gt; Columns = get_opt_value(columns,Options),&lt;br /&gt; Display_number = get_opt_value(display_number,Options),&lt;br /&gt; Threshold = get_opt_value(threshold,Options),&lt;br /&gt; Maximum = get_opt_value(maximum,Options),&amp;nbsp;&lt;/pre&gt;&lt;pre class="brush: erlang"&gt;&amp;nbsp;&lt;/pre&gt;&lt;pre class="brush: erlang"&gt;Bold = strip_newlines(os:cmd("tput bold")),&lt;br /&gt; Init = strip_newlines(os:cmd("tput init")),&lt;br /&gt; Dim = strip_newlines(os:cmd("tput sgr0")),&lt;br /&gt; Red = strip_newlines(os:cmd("tput setaf 1")),&lt;br /&gt; %Green = strip_newlines(os:cmd("tput setaf 2")),&lt;br /&gt; %Yellow = strip_newlines(os:cmd("tput setaf 3")),&lt;br /&gt; %Blue = strip_newlines(os:cmd("tput setaf 4")),&lt;br /&gt; %Magenta = strip_newlines(os:cmd("tput setaf 5")),&lt;br /&gt;&lt;br /&gt; case NonOptArgs of&lt;br /&gt;  [] -&amp;gt; &lt;br /&gt;  F = standard_io;&lt;br /&gt;  _ -&amp;gt;&lt;br /&gt;  {ok, F} = file:open(NonOptArgs, read)&lt;br /&gt; end,&lt;br /&gt;  proc_file(F, {Symbol, Columns, Display_number, Threshold, Maximum} , {Bold, Init, Dim, Red}).&lt;br /&gt;&lt;br /&gt;version() -&amp;gt;&lt;br /&gt; io:format("Version: 1.1\n").&lt;br /&gt;&lt;br /&gt;get_opt_value(Key, Options) -&amp;gt;&lt;br /&gt; case lists:keyfind(Key,1,Options) of&lt;br /&gt;  {Key, Value} -&amp;gt;&lt;br /&gt;  Value&lt;br /&gt; end,&lt;br /&gt; Value.&lt;br /&gt;&lt;br /&gt;option_spec_list() -&amp;gt;&lt;br /&gt; %CurrentUser = os:getenv("USER"),&lt;br /&gt; [&lt;br /&gt; %% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg}&lt;br /&gt; {help, $h, "help", undefined, "Show the program options"},&lt;br /&gt; {version, $v, "version", undefined, "Version"},&lt;br /&gt; {display_number, $n, "display_number", {boolean, true}, "Display number w/graph"},&lt;br /&gt; {columns, $c, "columns", {integer, 72}, "Display columns (default = 72)"},&lt;br /&gt; {symbol, $s, "symbol", {string, "*"}, "Symbol to display (default = '*')"},&lt;br /&gt; {threshold, $t, "threshold", {integer, 0}, "Will color lines over this value"},&lt;br /&gt; {maximum, $m, "maximum", {integer, 0}, "Presets the scale for this maximum value (default = 0)"}&lt;br /&gt; ].&lt;br /&gt;&lt;br /&gt;proc_file(F, Options, Tput) -&amp;gt;&lt;/pre&gt;&lt;pre class="brush: erlang"&gt;{Symbol, Columns, Display_number, Threshold, Maximum} = Options,&amp;nbsp;&lt;/pre&gt;&lt;pre class="brush: erlang"&gt;{Bold, Init, Dim, Red} = Tput,&lt;/pre&gt;&lt;pre class="brush: erlang"&gt;%Columns = erlang:list_to_integer(os:cmd("tput cols")) - 8},&lt;/pre&gt;&lt;pre class="brush: erlang"&gt;L = io:get_line(F, ''),&lt;br /&gt; case L of&lt;br /&gt;  eof -&amp;gt;&lt;br /&gt;   ok;&lt;br /&gt;  "\n" -&amp;gt;&lt;br /&gt;   false;&lt;br /&gt;  Line -&amp;gt;&lt;br /&gt;   Stripped = strip_newlines(Line),&lt;br /&gt;   Num = cast_to_integer(Stripped),&lt;br /&gt;   io:put_chars(Init),&lt;br /&gt;   case Num &amp;gt; 0 of&lt;br /&gt;    true -&amp;gt;&lt;br /&gt;     case Num &amp;gt;= Maximum of&lt;br /&gt;      true -&amp;gt;&lt;br /&gt;       NewMax = Num,&lt;br /&gt;       io:put_chars(Bold);&lt;br /&gt;      false -&amp;gt;&lt;br /&gt;       NewMax = Maximum,    &lt;br /&gt;       io:put_chars(Dim)&lt;br /&gt;     end,&lt;br /&gt;     Scale = Columns / NewMax, &lt;br /&gt;     Graph = lists:map(fun(_) -&amp;gt; io_lib:format(Symbol,[]) end , lists:seq(1,erlang:round(Num * Scale))),&lt;br /&gt;     case Threshold of&lt;br /&gt;      0 -&amp;gt; &lt;br /&gt;       false;&lt;br /&gt;      _ -&amp;gt;&lt;br /&gt;       case Num &amp;gt;= Threshold of&lt;br /&gt;        true -&amp;gt;&lt;br /&gt;         io:put_chars(Red);&lt;br /&gt;        false -&amp;gt; &lt;br /&gt;         %io:put_chars(Init)&lt;br /&gt;         false&lt;br /&gt;       end&lt;br /&gt;     end,&lt;br /&gt;     case Display_number of&lt;br /&gt;      true -&amp;gt;&lt;br /&gt;       io:format("~s~p~n",[Graph,Num]);&lt;br /&gt;      false -&amp;gt;&lt;br /&gt;       io:format("~p~n",[Graph])&lt;br /&gt;     end,                                &lt;br /&gt;     NewOptions = {Symbol, Columns, Display_number, Threshold, NewMax},&lt;br /&gt;     proc_file(F,NewOptions, Tput);&lt;br /&gt;    false -&amp;gt;&lt;br /&gt;     io:put_chars(Dim),&lt;br /&gt;     io:put_chars(Init),&lt;br /&gt;     io:format("~p~n",[Num]),&lt;br /&gt;     proc_file(F,Options, Tput)&lt;br /&gt;   end&lt;br /&gt; end.&lt;br /&gt;&lt;br /&gt;strip_newlines(String) -&amp;gt;&lt;br /&gt; string:strip(re:replace(String,"(.*)[\n\r]","\\1", [global,{return,list}])).&lt;br /&gt;&lt;br /&gt;cast_to_integer([]) -&amp;gt;&lt;br /&gt; [];&lt;br /&gt;cast_to_integer(Input) when is_integer(Input) -&amp;gt;&lt;br /&gt; Input;&lt;br /&gt;cast_to_integer(Input) when is_float(Input) -&amp;gt;&lt;br /&gt; erlang:round(Input);&lt;br /&gt;cast_to_integer(Input) when is_list(Input)-&amp;gt;&lt;br /&gt; case lists:member($., Input) of&lt;br /&gt;  true -&amp;gt;&lt;br /&gt;   erlang:round(erlang:list_to_float(Input));&lt;br /&gt;  false -&amp;gt;      &lt;br /&gt;   erlang:list_to_integer(Input)&lt;br /&gt;end.&lt;/pre&gt;&lt;br /&gt;UPDATE (4/26/2001):&lt;br /&gt;Here's a link to the original python script: &lt;a href="http://code.activestate.com/recipes/577077-tgraph-simple-ascii-graphing-utility/"&gt;tgraph.py&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;UPDATE (4/27/2011):&lt;br /&gt;Version 1.1:&lt;br /&gt;1) Fixed bug where lines were never dimmed after being bolded in cygwin&lt;br /&gt;2) Changed the compiler flags to disable smp and not register the process name.&amp;nbsp; Both not needed.&amp;nbsp; One note: Couldn't get +Bc to work in Windows.&amp;nbsp; This should change the break key to Ctrl-C, but still stays Ctrl-Break.&lt;br /&gt;3) Moved tput initialization out of working loop, now runs quickly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-4215991061279813238?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/4215991061279813238/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=4215991061279813238' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/4215991061279813238'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/4215991061279813238'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2011/04/console-graphing-in-erlang.html' title='Console plotting in Erlang'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-4793981964200496650</id><published>2011-04-20T07:19:00.000-07:00</published><updated>2011-04-20T08:17:59.366-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='location'/><category scheme='http://www.blogger.com/atom/ns#' term='iPhone'/><category scheme='http://www.blogger.com/atom/ns#' term='consolidated.db'/><category scheme='http://www.blogger.com/atom/ns#' term='privacy'/><title type='text'>iOS consolidated.db workaround for hacked devices</title><content type='html'>Looks like Apple is tracking iOS devices an recording that info in clear text:&lt;br /&gt;http://radar.oreilly.com/2011/04/apple-location-tracking.html&lt;br /&gt;&lt;br /&gt;Here's a way to ensure this data is not recorded:&lt;br /&gt; &lt;br /&gt;You must have a hacked iOS device, and either Mobile Terminal or an SSH login.  You must also know the root password.  You first remove/move this file, and recreate it as a symbolic link to /dev/null like:&lt;br /&gt; &lt;br /&gt;su&lt;br /&gt;cd /System/Library/Frameworks/CoreLocation.framework/Support&lt;br /&gt;rm consolidated.db&lt;br /&gt;ln -s /dev/null consolidated.db&lt;br /&gt; &lt;br /&gt;Anything written to this 'file' is sent to /dev/null, so it is not saved on the file system.  I've done this on a hacked device, and Location Services continue to work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-4793981964200496650?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/4793981964200496650/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=4793981964200496650' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/4793981964200496650'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/4793981964200496650'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2011/04/ios-consolidateddb-workaround-for.html' title='iOS consolidated.db workaround for hacked devices'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-5120155853713405520</id><published>2011-04-17T17:37:00.000-07:00</published><updated>2011-04-17T17:37:02.600-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='statistics'/><category scheme='http://www.blogger.com/atom/ns#' term='load test'/><category scheme='http://www.blogger.com/atom/ns#' term='90th percentile'/><title type='text'>90th percentile done wrong</title><content type='html'>I'm no statistician, but I do load test a lot of systems and report upon them.  &lt;br /&gt;&lt;br /&gt;It's common practice to report upon a particular measure using the average of the 90th percentile of the data (throwing out the slowest 10%): response times, throughput, etc.  This is done to remove the outliers; the 2 days response time for a call that normally takes 100ms.&lt;br /&gt;&lt;br /&gt;I got to thinking; if the slowest 10% is obviously wrong, why isn't the fastest 10%?  Seems just taking the slow outliers is cheating.&lt;br /&gt;&lt;br /&gt;I think a good compromise between simplicity and accuracy would be to throw out the slowest 5% and the fastest 5%.&lt;br /&gt;&lt;br /&gt;A statistician would know better I'm sure.  But explaining 90th percentile to your boss/customer is generally hard enough.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-5120155853713405520?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/5120155853713405520/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=5120155853713405520' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/5120155853713405520'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/5120155853713405520'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2011/04/90th-percentile-done-wrong.html' title='90th percentile done wrong'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-8541192874079921037</id><published>2011-04-15T21:16:00.000-07:00</published><updated>2011-04-26T10:26:23.783-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='interface'/><category scheme='http://www.blogger.com/atom/ns#' term='hotswap'/><title type='text'>Erlang Hot Code Swapping - Interfaces</title><content type='html'>I read Raymond Tay's 2007 post &lt;a href="http://erlangraymondtay.blogspot.com/2007/10/erlang-hot-code-swapping.html"&gt;Erlang Hot Code Swapping&lt;/a&gt; a while back, and realized it was not quite right, or at least not complete.  The idea and code behind code swapping works, but the execution of the code in the blog didn't actually demonstrate that fact.  He changes the calling functions along with swapping the code; you shouldn't have to do that.&lt;br /&gt;&lt;br /&gt;Which brings me to interfaces.  EJBs (what he was trying to emulate) are all about interfaces.  You specifically have to have separate files for the interface and the implementation.  In fact, EJB 1.0 was way too heavy with the interfaces, and it was a pain to work with.&lt;br /&gt;&lt;br /&gt;But the general idea of an interface is awesome.  USB anyone?  Back in the bad old days there was the RS-232 serial interface, which was woefully under-standardized, so there was no guarantee that plugging two RS-232 devices together would work (usually the opposite).  You'd spend a lot of time trying different configurations to get things work (Remember 9600-N-8-1?).&lt;br /&gt;&lt;br /&gt;Interfaces dictate a list of methods (or functions) that can always be expected to be implemented by a piece of code.  This is great for long running code that must work with other systems.  The implementation changes over time, but the developer knows if he/she (ok, most likely he, but only statistically) is going to change the interface, they will break compatibility with other systems.&lt;br /&gt;&lt;br /&gt;The neat thing is that the interface is already defined in Erlang code that implements callbacks; they are just the exported functions that will make the callbacks.  But the Java idea of an Interface is a list of methods that are separate from the implementation that can be share across implementations.  Interfaces aren't a language feature of Erlang, but OTP behaviours are Interfaces (among other things):  They require a list of exported functions.&lt;br /&gt;&lt;br /&gt;What's the best way to implement an interface in Erlang without OTP?  The best way I've figured is this: Move the public functions from the private callback functions into separate modules, and then import the public functions.  This way two implementations that import the interface module will have to share the same interface.&lt;br /&gt;&lt;br /&gt;First, the generic container code:&lt;br /&gt;&lt;br /&gt;container.erl:&lt;br /&gt;&lt;pre class="brush: erlang"&gt;-module(container).&lt;br /&gt;-export([start/1, rpc/2, swap_code/1]).&lt;br /&gt;&lt;br /&gt;-include("callback.hrl").&lt;br /&gt;&lt;br /&gt;start(Mod) -&gt;&lt;br /&gt;register(?SERVERNAME, spawn(fun() -&gt; loop(?SERVERNAME, Mod, Mod:init()) end)).&lt;br /&gt;&lt;br /&gt;swap_code(Mod) -&gt; rpc(?SERVERNAME, {swap_code, Mod}).&lt;br /&gt;&lt;br /&gt;%&lt;br /&gt;% Standard code for abstracting the "RPC-call" layer&lt;br /&gt;%&lt;br /&gt;rpc(Name, Request) -&gt;&lt;br /&gt;    Name ! {self(), Request},&lt;br /&gt;    receive&lt;br /&gt;        {Name, Response} -&gt; Response&lt;br /&gt;    end.&lt;br /&gt;&lt;br /&gt;%&lt;br /&gt;% Standard code for looping and waiting for messages from clients&lt;br /&gt;%&lt;br /&gt;loop(Name, Mod, OldState) -&gt;&lt;br /&gt;    receive&lt;br /&gt;        {From, {swap_code, NewCallbackMod}} -&gt;&lt;br /&gt;            From ! {Name, ack},&lt;br /&gt;            loop(Name, NewCallbackMod, OldState);&lt;br /&gt;        {From, Request} -&gt;&lt;br /&gt;            {Response, NewState} = Mod:handle(Request,OldState),&lt;br /&gt;            From ! {Name, Response},&lt;br /&gt;            loop(Name, Mod, NewState)&lt;br /&gt;    end.&lt;br /&gt;&lt;/pre&gt;The registered server name is stored in an .hrl file included in the container and callback code:&lt;br /&gt;&lt;br /&gt;callback.hrl:&lt;br /&gt;&lt;pre class="brush: erlang"&gt;-define(SERVERNAME, moneyserver).&lt;br /&gt;&lt;/pre&gt;callback.erl:&lt;br /&gt;&lt;pre class="brush: erlang"&gt;-module(callback).&lt;br /&gt;-export([dollarToYen/1, yenToEuro/1]).&lt;br /&gt;-include("callback.hrl").&lt;br /&gt;&lt;br /&gt;-import(container, [rpc/2]).&lt;br /&gt;&lt;br /&gt;%% client routines&lt;br /&gt;dollarToYen(Dollars) -&gt; rpc(?SERVERNAME, {convertToYen, Dollars}).&lt;br /&gt;yenToEuro(Yen) -&gt; rpc(?SERVERNAME, {convertToEuro, Yen}).&lt;br /&gt;&lt;/pre&gt;callback_impl.erl:&lt;br /&gt;&lt;pre class="brush: erlang"&gt;-module(callback_impl).&lt;br /&gt;-export([init/0, handle/2]).&lt;br /&gt;-import(container, [rpc/2]).&lt;br /&gt;%% client routines&lt;br /&gt;-import(callback, [dollarToYen/1,yenToEuro/1]).&lt;br /&gt;&lt;br /&gt;%% callback routines&lt;br /&gt;init() -&gt; dict:new().&lt;br /&gt;&lt;br /&gt;handle({convertToYen, Dollars}, Dict) -&gt; { Dollars * 126, Dict};&lt;br /&gt;handle({convertToEuro, Yen}, Dict) -&gt; {Yen * 0.0077, Dict}.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;callback_impl2.erl:&lt;br /&gt;&lt;pre class="brush: erlang"&gt;-module(callback_impl2).&lt;br /&gt;-export([init/0, handle/2]).&lt;br /&gt;-import(container, [rpc/2]).&lt;br /&gt;%% client routines&lt;br /&gt;-import(callback, [dollarToYen/1,yenToEuro/1]).&lt;br /&gt;&lt;br /&gt;%% callback routines&lt;br /&gt;init() -&gt; dict:new().&lt;br /&gt;&lt;br /&gt;handle({convertToYen, Dollars}, Dict) -&gt; { Dollars * 126 * 126, Dict};&lt;br /&gt;handle({convertToEuro, Yen}, Dict) -&gt; {Yen * 0.0077 * 0.0077, Dict}.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;How this works:&lt;br /&gt;&lt;br /&gt;RUNTIME:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&gt; c(container).&lt;br /&gt;&gt; c(callback).&lt;br /&gt;&gt; c(callback_impl).&lt;br /&gt;&gt; c(callback_impl2).&lt;br /&gt;&lt;br /&gt;% our first implementation:&lt;br /&gt;&gt; container:start(callback_impl).&lt;br /&gt;&gt; callback:dollarToYen(1). &lt;br /&gt;126&lt;br /&gt;&gt; callback:yenToEuro(1).  &lt;br /&gt;0.0077&lt;br /&gt;&lt;br /&gt;&gt; container:swap_code(callback_impl2).&lt;br /&gt;ack&lt;br /&gt;&gt; callback:yenToEuro(1).              &lt;br /&gt;5.929e-5&lt;br /&gt;&gt; callback:dollarToYen(1).            &lt;br /&gt;15876&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Same interface, same exact call, but hot swapped implementations!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-8541192874079921037?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/8541192874079921037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=8541192874079921037' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/8541192874079921037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/8541192874079921037'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2011/04/erlang-hot-code-swapping-interfaces.html' title='Erlang Hot Code Swapping - Interfaces'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-7584359351393058111</id><published>2011-03-30T17:17:00.000-07:00</published><updated>2011-03-31T17:54:36.664-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='reload'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='compile'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><category scheme='http://www.blogger.com/atom/ns#' term='auto'/><title type='text'>Erlang auto reload  code in (w)erl shell</title><content type='html'>UPDATE: I found an existing, more mature implimentation of this idea:&lt;br /&gt;http://www.rustyrazorblade.com/2010/12/erlang-code-auto-reloader/.  Please disregard the rest.&lt;br /&gt;&lt;br /&gt;I develop most of my code in eclipse, because most of my professional work deals with Java, and I develop in many different languages, including erlang.&lt;br /&gt;&lt;br /&gt;One of the reasons I use and IDE instead of just and editor is auto compilation.  This doesn't work out that great with a erl or werl shell because they don't auto reload code that is compiled outside of the shell.&lt;br /&gt;&lt;br /&gt;I found a cool method to reload compiled code &lt;a href="http://amiest-devblog.blogspot.com/2008/01/reloading-all-code-from-erlang-shell.html"&gt;here&lt;/a&gt;, but it didn't auto reload; you still had to type a command to reload, so not much better than just running c().&lt;br /&gt;&lt;br /&gt;So, I figured, like many problems, Erlang could solve this with a process.  I was right!&lt;br /&gt;&lt;br /&gt;1) One thing to note is that you have to exclude recompiling itself, because that kills that running process.  And you'll have to update that code path manually.  You could also use a relative path to os:getenv("PWD") if that makes sense in your env.  &lt;br /&gt;&lt;br /&gt;2) Also note that moving reload.erl into a seperate directory than the path mentioned in the code will not stop it from being reloaded; erlang stores all paths added via -pa as relative to your main path.  Say you put reload.erl in ~/test and the rest of your code in ~/erlang, start erl in ~/test/src, and add ~/test/ebin to that code path:&lt;br /&gt;&lt;br /&gt;$/home/user/erlang: erl -pa ../../test/ebin&lt;br /&gt;&lt;br /&gt;the reload module would be loaded from:&lt;br /&gt;/home/user/erlang/ebin/../../test/ebin&lt;br /&gt;&lt;br /&gt;To start:&lt;br /&gt;reload:start() (defaults to reload every 1 second)&lt;br /&gt;or&lt;br /&gt;reload:start(N) (reloads every N milliseconds)&lt;br /&gt;&lt;br /&gt;To stop:&lt;br /&gt;reload:stop() (yes, just throws an exception, could be nicer)&lt;br /&gt;&lt;br /&gt;CODE:&lt;br /&gt;&lt;blockquote&gt;%% Author: Drew Gulino: drew dot gulino at gmail dot com&lt;br /&gt;%% Created: Mar 30, 2011&lt;br /&gt;%% Description: Process to auto reload compiled code in shell&lt;br /&gt;&lt;br /&gt;-module(reload).&lt;br /&gt;&lt;br /&gt;%%&lt;br /&gt;%% Exported Functions&lt;br /&gt;%%&lt;br /&gt;-export([start/0, start/1, stop/0, reloadAll/0]).&lt;br /&gt;&lt;br /&gt;%% API Functions&lt;br /&gt;start() -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; start(1000).&lt;br /&gt;&lt;br /&gt;start(Milliseconds) -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; register(reload, spawn_link(fun() -&amp;gt; loop(Milliseconds) end)).&lt;br /&gt;&lt;br /&gt;stop() -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; throw(reload_stopped).&lt;br /&gt;&lt;br /&gt;%% Local Functions&lt;br /&gt;loop(Milliseconds) -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; reloadAll(),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; timer:sleep(1000),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; loop(Milliseconds).&lt;br /&gt;&lt;br /&gt;reload(M) -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; code:purge(M),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; code:soft_purge(M),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {module, M} = code:load_file(M),&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {ok, M}.&lt;br /&gt;&lt;br /&gt;reloadAll() -&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Modules = [M || {M, P} &amp;lt;- code:all_loaded(), is_list(P)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; andalso M =/= reload&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; andalso string:str(P, "/home/drew/workspace/erlang/") &amp;gt; 0],&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; [reload(M) || M &amp;lt;- Modules].&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-7584359351393058111?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/7584359351393058111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=7584359351393058111' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/7584359351393058111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/7584359351393058111'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2011/03/erlang-auto-reload-code-in-werl-shell.html' title='Erlang auto reload  code in (w)erl shell'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-6959656881676175890</id><published>2007-08-14T23:09:00.000-07:00</published><updated>2011-03-31T17:55:15.492-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dns'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='load generation'/><title type='text'>Erlang to stress test DNS</title><content type='html'>I've been playing with Erlang and I thought it would be a great fit to create a highly scalable, distributed load generation tool.&lt;br /&gt;&lt;br /&gt;I found the dns interface code at http://www.trapexit.org/index.php/Lookup_MX_Records_In_DNS&lt;br /&gt;&lt;br /&gt;I've created a little program that will take a list of domains in a file, and a configurable number of processes.  It will then iterate through each domain in the file and select a random process it will send the domain to be resolved by.  Here's what I have as of yet:&lt;br /&gt;&lt;br /&gt;usage:&lt;br /&gt;create a list of domains in a file called queries.txt&lt;br /&gt;1&gt; Pids = dns:groups(N)&lt;br /&gt;2&gt; dns:group_lookup(Pids)&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;-module(dns).&lt;br /&gt;-export([start/0, start_load/0, lookup/2, group/1,group/2, group_lookup/1]).&lt;br /&gt;&lt;br /&gt;start() -&gt; &lt;br /&gt;spawn(fun loop/0).&lt;br /&gt;&lt;br /&gt;start_load() -&gt;&lt;br /&gt;Pids = group(10),&lt;br /&gt;group_lookup(Pids).&lt;br /&gt;&lt;br /&gt;lookup(Pid, What) -&gt;&lt;br /&gt;rpc(Pid,What).&lt;br /&gt;&lt;br /&gt;rpc(Pid, Request) -&gt;&lt;br /&gt;Pid ! {self(), Request},&lt;br /&gt;receive&lt;br /&gt;{Pid, Response} -&gt;&lt;br /&gt;Response&lt;br /&gt;end.&lt;br /&gt;&lt;br /&gt;loop() -&gt;&lt;br /&gt;receive&lt;br /&gt;{From, Domain} -&gt;&lt;br /&gt;dnslookup:init( [{192,168,1,1}] ),&lt;br /&gt;Addr2 = dnslookup:lookupa(lib:nonl(Domain)),&lt;br /&gt;io:format("~p~n",[Addr2]),&lt;br /&gt;From ! {self(), Addr2},&lt;br /&gt;loop()&lt;br /&gt;end.&lt;br /&gt;&lt;br /&gt;group(K) -&gt;&lt;br /&gt;group(K, []).&lt;br /&gt;&lt;br /&gt;group(0, Pids) -&gt; Pids;&lt;br /&gt;group(K, Pids) -&gt;&lt;br /&gt;group(K-1, [dns:start()| Pids]).&lt;br /&gt;&lt;br /&gt;group_lookup(Pids) -&gt;&lt;br /&gt;Domains = dnslookup:readlines("queries.txt"),&lt;br /&gt;dnslookup:init( [{192,168,1,1}] ),&lt;br /&gt;[dns:lookup( lists:nth( random:uniform( length(Pids) ), Pids), Domain ) || Domain &lt;- Domains].&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;-module(dnslookup).  &lt;br /&gt;-export([         readlines/1,         queries_from_list/1,         for_each_line_in_file/4,         queries_direct_from_file/0,         lookupmx/1,         lookupa/1,         lookupptr/1,         init/1,         findptr/1]).  -include_lib("kernel/src/inet_dns.hrl").  init([NS | T]) -&gt;&lt;br /&gt;ok = inet_db:add_ns(NS),&lt;br /&gt;init(T);&lt;br /&gt;init([]) -&gt; ok;&lt;br /&gt;init(Nameserver) -&gt;&lt;br /&gt;ok = inet_db:add_ns(Nameserver).&lt;br /&gt;&lt;br /&gt;print_list([]) -&gt;&lt;br /&gt;[];&lt;br /&gt;print_list([Head | Rest]) -&gt;&lt;br /&gt;io:format("~w~n",[Head]),&lt;br /&gt;print_list(Rest).&lt;br /&gt;&lt;br /&gt;findmx( [#dns_rr{ type=?S_A, data = Addr } | _] ) -&gt; Addr;&lt;br /&gt;findmx( [_ | T ]) -&gt; findmx(T);&lt;br /&gt;findmx( _Other ) -&gt; none.&lt;br /&gt;&lt;br /&gt;finda( [#dns_rr{ type=?S_A, data = Addr } | _] ) -&gt; Addr;&lt;br /&gt;finda( [_ | T ]) -&gt; finda(T);&lt;br /&gt;finda( _Other ) -&gt; none.&lt;br /&gt;&lt;br /&gt;findptr( [Head | _] ) -&gt;&lt;br /&gt;%% io:format("~s~n",[element(9,Head)]),&lt;br /&gt;%% io:formant("~s~n",[Head]),&lt;br /&gt;[element(9,Head)];&lt;br /&gt;%% findptr( [_ | T ]) -&gt; findptr(T);&lt;br /&gt;findptr( _Other ) -&gt; none.&lt;br /&gt;&lt;br /&gt;findheader( Head ) -&gt;&lt;br /&gt;io:format("~w~n",[is_tuple(Head)]),&lt;br /&gt;io:format("~w~n",[Head]).&lt;br /&gt;&lt;br /&gt;lookupmx(Domain) -&gt;&lt;br /&gt;case inet_res:nslookup(Domain,1,mx) of&lt;br /&gt;{ error, Reply } -&gt; Reply;&lt;br /&gt;{ ok, #dns_rec{ anlist = Ans } } -&gt;&lt;br /&gt;%% io:format("MX Ans ~w,~n", [Ans]),&lt;br /&gt;%% print_list(Ans),&lt;br /&gt;findmx( Ans )&lt;br /&gt;end.&lt;br /&gt;&lt;br /&gt;lookupa(Domain) -&gt;&lt;br /&gt;%init([{68,105,28,12}]),&lt;br /&gt;case inet_res:nslookup(Domain,1,a) of&lt;br /&gt;{ error, Reply } -&gt; Reply;&lt;br /&gt;{ ok, #dns_rec{ anlist = Ans } } -&gt;&lt;br /&gt;%% print_list(Ans),&lt;br /&gt;%%io:format("A Ans ~w,~n", [Ans]),&lt;br /&gt;finda( Ans )&lt;br /&gt;end.&lt;br /&gt;&lt;br /&gt;%% IP address to domain name resolution&lt;br /&gt;lookupptr(Domain) -&gt;&lt;br /&gt;case inet_res:nslookup(Domain,1,ptr) of&lt;br /&gt;{ error, Reply } -&gt; Reply;&lt;br /&gt;{ ok, #dns_rec{ anlist = Ans, arlist = Ar, qdlist = Qd, nslist = Ns, header = H  } } -&gt;&lt;br /&gt;%% print_list(Ans),&lt;br /&gt;%% io:format("PTR Ans ~w,~n", [Ans]),&lt;br /&gt;%% io:format("PTR Ar ~w,~n", [Ar]),&lt;br /&gt;%% io:format("PTR Qd ~w,~n", [Qd]),&lt;br /&gt;%% io:format("PTR Ns ~w,~n", [Ns]),&lt;br /&gt;%% io:format("PTR H ~w,~n", [H]),&lt;br /&gt;findptr(Ans)&lt;br /&gt;end.&lt;br /&gt;&lt;br /&gt;for_each_line_in_file(Name, Proc, Mode, Accum0) -&gt;&lt;br /&gt;{ok, Device} = file:open(Name, Mode),&lt;br /&gt;for_each_line(Device, Proc, Accum0).&lt;br /&gt;&lt;br /&gt;for_each_line(Device, Proc, Accum) -&gt;&lt;br /&gt;case io:get_line(Device, "") of&lt;br /&gt;eof  -&gt; file:close(Device), Accum;&lt;br /&gt;Line -&gt; NewAccum = Proc(Line, Accum),&lt;br /&gt;for_each_line(Device, Proc, NewAccum)&lt;br /&gt;end.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;queries_direct_from_file() -&gt;&lt;br /&gt;for_each_line_in_file("queries.txt",&lt;br /&gt;fun(X, Count) -&gt; io:format("~s~n",[lib:nonl(X)]),&lt;br /&gt;io:format("~s~n",[lookupa(lib:nonl(X))]),&lt;br /&gt;Count + 1 end, [read], 0).&lt;br /&gt;&lt;br /&gt;queries_from_list([]) -&gt;&lt;br /&gt;[];&lt;br /&gt;queries_from_list([Head | Rest]) -&gt;&lt;br /&gt;dnslookup:init( [{172,28,40,52}] ),&lt;br /&gt;io:format("~s sent~n",[lib:nonl(Head)]),&lt;br /&gt;A = dnslookup:lookupa(lib:nonl(Head)),&lt;br /&gt;queries_from_list(Rest).&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;readlines(FileName) -&gt;&lt;br /&gt;{ok, Device} = file:open(FileName, [read]),&lt;br /&gt;get_all_lines(Device, []).&lt;br /&gt;&lt;br /&gt;get_all_lines(Device, Accum) -&gt;&lt;br /&gt;case io:get_line(Device, "") of&lt;br /&gt;eof  -&gt; file:close(Device), lists:reverse(Accum);&lt;br /&gt;Line -&gt; get_all_lines(Device, [Line|Accum])&lt;br /&gt;end.&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-6959656881676175890?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/6959656881676175890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=6959656881676175890' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/6959656881676175890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/6959656881676175890'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2007/08/erlang-to-stress-test-dns.html' title='Erlang to stress test DNS'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-6076469643728999310</id><published>2007-07-29T14:21:00.000-07:00</published><updated>2011-03-31T17:55:31.458-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='test automation'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><title type='text'>Intro</title><content type='html'>Well, it's taken some time, but I finally started a Blog.&lt;br /&gt;&lt;br /&gt;Why?  Why do more documentation than I already do?&lt;br /&gt;&lt;br /&gt;One reason:  Went to OSCON and was asked 'do you have a blog' one too many time.&lt;br /&gt;&lt;br /&gt;I currently work as a System Engineer doing performance and scalability testing.  Not QA!  (Not that there's anything wrong with that).  Every test I do is automated and I'm often part of the solution to any problem I find.&lt;br /&gt;&lt;br /&gt;I work for a company with very strict SLAs and so we spend a lot of time and money ensuring our systems will perform at whole-web scales.&lt;br /&gt;&lt;br /&gt;Although everything I test is web-centric, almost none of it is web pages.  So many times I can't use test tools like LoadRunner to produce load.  I work with Open Source tools or just create my own.&lt;br /&gt;&lt;br /&gt;I work with a lot of different technologies and have access to a large amount of gear that other people probably don't have access to.&lt;br /&gt;&lt;br /&gt;So here is my Blog on what I've either played with or tested professionally.  It's sometimes hard to tell the difference.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-6076469643728999310?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/6076469643728999310/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=6076469643728999310' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/6076469643728999310'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/6076469643728999310'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2007/07/intro.html' title='Intro'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2678798148555289975.post-3588832233943289817</id><published>2007-07-25T10:39:00.000-07:00</published><updated>2011-03-31T17:55:46.728-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='dns'/><category scheme='http://www.blogger.com/atom/ns#' term='load test'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>OSCON 2007</title><content type='html'>I attended OSCON for the first time this year.&lt;br /&gt;&lt;br /&gt;I concentrated on Python and Java.&lt;br /&gt;Java:&lt;br /&gt;Grails and Tapestry show how they can do Rails on the JVM.&lt;br /&gt;&lt;br /&gt;Python:&lt;br /&gt;Django shows how it can do Rails w/Python.  Is there nothing else new under the sun?&lt;br /&gt;&lt;br /&gt;I had been looking into TurboGears, mainly because of Django's lack of a built AJAX support and the &lt;a href="http://www.turbogearsbook.com/"&gt;book&lt;/a&gt; out on it, but   Simon Willison showed how easy it was to add-on.  I had read about some issues w/the TurboGears team, but didn't want to believe it.  But I was impressed by Django's presense at OSCON and didn't hear a word on TurboGears, and that there will be book on it soon.&lt;br /&gt;&lt;br /&gt;- I also attended the Python 3000 (3.0) session w/Guido.  3.0 will not be completely backward compatible, and it looks like a bit of work for not a lot of gain; just a syntactic cleanup.  The main benefit is to add Java-like Collections, which is nice.&lt;br /&gt;&lt;br /&gt;I've been getting into &lt;a href="http://www.erlang.org/"&gt;Erlang&lt;/a&gt; lately, so I was really excited about the Erlang BoF:&lt;br /&gt;&lt;br /&gt;I was the only person other than the presenter with any experience with `lang.  Still I'm quite the nube.  Learned a few cool things.  I still have to repeat to myself "It a pattern match, not an assignment" over and over again when reading code.&lt;br /&gt;I heard that the new Erlang book &lt;a href="http://pragmaticprogrammer.com/titles/jaerlang/index.html"&gt;&lt;b&gt;Programming Erlang:&lt;/b&gt;&lt;br /&gt;Software for a Concurrent World&lt;/a&gt; was a real hit at the Powell's book stand.  I was going to order it, but took advantage no shipping and no Oregon sales tax.&lt;br /&gt;&lt;br /&gt;There was talk about how to redefining the Erlang standard library OTP from meaning "Open Telecom Platform" to something more web-centric.  I suggest renaming Erlang to URLang ;)  It's pronounced the same.&lt;br /&gt;&lt;br /&gt;I recently did some stress testing of 64bit BIND 9.x.  I had an existing Perl based testing suite, but it didn't meet all my needs: I needed incremental updates running while I ran a whole bunch'o queries.&lt;br /&gt;&lt;br /&gt;I looked around to see what libraries were available; I normally use either Java or Python.  I found good dns libs for each.&lt;br /&gt;&lt;br /&gt;I then tried out the same in Erlang.  I'll add a post about that soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2678798148555289975-3588832233943289817?l=technicalmusings.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://technicalmusings.blogspot.com/feeds/3588832233943289817/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=2678798148555289975&amp;postID=3588832233943289817' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/3588832233943289817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2678798148555289975/posts/default/3588832233943289817'/><link rel='alternate' type='text/html' href='http://technicalmusings.blogspot.com/2007/07/oscon-2007.html' title='OSCON 2007'/><author><name>Drew Gulino</name><uri>http://www.blogger.com/profile/09241510269905580739</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
