{"id":146,"date":"2009-07-27T18:10:28","date_gmt":"2009-07-27T07:10:28","guid":{"rendered":"http:\/\/blog.mozilla.org\/nnethercote\/?p=146"},"modified":"2009-07-27T18:10:28","modified_gmt":"2009-07-27T07:10:28","slug":"how-i-work-on-tracemonkey","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/nnethercote\/2009\/07\/27\/how-i-work-on-tracemonkey\/","title":{"rendered":"How I Work on Tracemonkey"},"content":{"rendered":"<p>After six months of working on Tracemonkey, I&#8217;ve built up a particular workflow &#8212; how I use Mercurial, arrange my workspaces, run tests, and commit code.\u00a0 I thought it would be worth describing this in case it helps other developers improve their workflow, or perhaps so they can give me ideas on how to improve my own workflow.<\/p>\n<h3>Workspace Structure<\/h3>\n<p>I have two machines, an Ubuntu Linux desktop and a Mac laptop.\u00a0 For both machines I use the same workspace structure.\u00a0 All my Mozilla work is in a directory ~\/moz\/.\u00a0 At any one time I have up to 10 workspaces.\u00a0 ~\/moz\/ws0\/ always contains an unmodified clone of the tracemonkey repository, created like so:<\/p>\n<pre>hg clone http:\/\/hg.mozilla.org\/tracemonkey\/ ~\/moz\/ws0<\/pre>\n<p>Workspaces ~\/moz\/ws1 through to ~\/moz\/ws9 are local clones of ~\/moz\/ws0\/ in which I make modifications.\u00a0 I create these workspaces like this:<\/p>\n<pre>hg clone ~\/moz\/ws0 ~\/moz\/wsN<\/pre>\n<p>Local hg clones are much cheaper than ones done over the network.\u00a0 On my Linux box it takes about 45 seconds, on my Mac somewhere over 2 minutes;\u00a0 it seems that laptops have slower hard disks than desktops.\u00a0 In comparison, cloning hg.mozilla.org\/tracemonkey\/ can take anywhere from 5 to 30 minutes or more (I don&#8217;t know why there&#8217;s so much variation there).<\/p>\n<p>I mostly work with the Javascript shell, called &#8216;js&#8217;, so I do most of my work in ~\/moz\/wsN\/js\/src\/.\u00a0 There are three ways I commonly build &#8216;js&#8217;.<\/p>\n<ul>\n<li>Debug builds go in ~\/moz\/wsN\/js\/src\/debug\/.\u00a0 I use these for most of my development and testing.<\/li>\n<li>Optimised builds go in ~\/moz\/wsN\/js\/src\/opt\/.\u00a0 I use these for measuring performance.<\/li>\n<li>Optimised builds with symbols go in ~\/moz\/wsN\/js\/src\/optg\/.\u00a0 I use these with Cachegrind, which needs optimised code with symbols to be useful.<\/li>\n<\/ul>\n<p>I have a number of bash aliases I use to move around these directories:<\/p>\n<pre>alias m=\"cd ~\/moz\/\"\r\nalias m0=\"cd ~\/moz\/ws0\/\"\r\nalias j0=\"cd ~\/moz\/ws0\/js\/src\/\"\r\nalias j0d=\"cd ~\/moz\/ws0\/js\/src\/debug\/\"\r\nalias j0o=\"cd ~\/moz\/ws0\/js\/src\/opt\/\"<\/pre>\n<p>and so on for the remaining workspaces ws1 through ws9.\u00a0 I have a common bash config file that I use on both my machines;\u00a0 whenever I change it I copy it to the other machine.\u00a0 This is a manual process, which is not ideal, but in practice it works well enough.<\/p>\n<p>I find nine workspaces for making changes is enough to cover everything I&#8217;m doing;\u00a0 if I find myself needing more it&#8217;s because some of the existing ones have stagnated and I need to do some cleaning up.<\/p>\n<h3>Building &#8216;js&#8217;<\/h3>\n<p>I have three scripts, js_conf_debug, js_conf_opt, js_conf_optg, which configure and build from scratch.\u00a0 Here is js_conf_debug, the others are similar:<\/p>\n<pre>#! \/bin\/sh\r\n\r\nif [ -z $1 ] ; then\r\n    echo \"usage: $0 &lt;dirname&gt;\"\r\nelif [ -d $1 ] ; then\r\n    echo \"directory $1 already exists\"\r\nelse\r\n    autoconf2.13\r\n    mkdir $1\r\n    cd $1\r\n    CC='gcc -m32' CXX='g++ -m32' AR=ar ..\/configure \\\r\n        --enable-debug --disable-optimize --target=i686-pc-linux-gnu\r\n    make --quiet -j 2\r\nfi<\/pre>\n<p>These are scripts rather than bash aliases or functions because they are quite different on the Linux machine and the Mac.<\/p>\n<p>I also have this alias for incremental builds:<\/p>\n<pre>alias mq=\"make --quiet -j 2\"<\/pre>\n<h3>Testing &#8216;js&#8217;<\/h3>\n<p>The program I run most is trace-test.js.\u00a0 So much so that I have more aliases for it:<\/p>\n<pre>alias jsott=\"time opt\/js -j trace-test.js\"\r\nalias jsdtt=\"time debug\/js -j trace-test.js\"<\/pre>\n<p>I don&#8217;t need an alias for the optg build because that&#8217;s only used with Cachegrind, which I run in a different way (see below).<\/p>\n<p>I run the JS unit test with the following script:<\/p>\n<pre>function js_regtest\r\n{\r\n    x=$1\r\n    y=$2\r\n    if [ -z $x ] || [ -z $y ] ; then\r\n        echo \"usage: js_regtest &lt;ws-number-1&gt; &lt;ws-number-2&gt;\"\r\n    else\r\n        xdir=$HOME\/moz\/ws$x\/js\/src\/debug\r\n        ydir=$HOME\/moz\/ws$y\/js\/src\/debug\r\n        echo \"############################\"\r\n        echo \"## COMPILING $xdir\"\r\n        echo \"############################\"\r\n        cd $xdir &amp;&amp; mq\r\n        echo \"############################\"\r\n        echo \"## COMPILING $ydir\"\r\n        echo \"############################\"\r\n        cd $ydir &amp;&amp; mq\r\n        cd $ydir\/..\/..\/tests\r\n        echo \"############################\"\r\n        echo \"## TESTING $xdir\"\r\n        echo \"############################\"\r\n        time jsDriver.pl \\\r\n            -k \\\r\n            -e smdebug \\\r\n            --opt '-j' \\\r\n            -L spidermonkey-n.tests slow-n.tests \\\r\n            -f base.html \\\r\n            -s $xdir\/js &amp;&amp; \\\r\n        echo \"############################\"\r\n        echo \"## TESTING $ydir\"\r\n        echo \"############################\"\r\n        time jsDriver.pl \\\r\n             -k \\\r\n             -e smdebug \\\r\n             --opt '-j' \\\r\n             -L spidermonkey-n.tests slow-n.tests \\\r\n             -L base-failures.txt \\\r\n             -s $ydir\/js\r\n    fi\r\n}<\/pre>\n<p>An example invocation would be:<\/p>\n<pre>js_regtest 0 3<\/pre>\n<p>The above invocation first ensures a debug &#8216;js&#8217; is built in workspaces 0 and 3.\u00a0 Then it runs ~\/moz\/ws0\/js\/src\/debug\/js in order to get the baseline failures, which are put in base-failures.txt.\u00a0 Then it runs ~\/moz\/ws3\/js\/src\/debug\/js and compares the results against the baseline.\u00a0 The -L lines skip the tests that are really slow;\u00a0 without them it takes hours to run.\u00a0 I time each invocation just so I always know roughly how long it takes;\u00a0 it&#8217;s a bit over 10 minutes to do both runs.\u00a0 It assumes that workspace 0 and 3 correspond to the same hg revision;\u00a0 perhaps I could automate that to guarantee it but I haven&#8217;t (knowingly) got that wrong yet so haven&#8217;t bothered to do so.<\/p>\n<h3>Timing &#8216;js&#8217;<\/h3>\n<p>I time &#8216;js&#8217; by running SunSpider.\u00a0 I obtained it like so:<\/p>\n<pre>svn http:\/\/svn.webkit.org\/repository\/webkit\/trunk\/SunSpider ~\/moz\/SunSpider<\/pre>\n<p>I haven&#8217;t updated it in a while, I hope it hasn&#8217;t changed recently!<\/p>\n<p>I run it with this bash function:<\/p>\n<pre>function my_sunspider\r\n{\r\n    x=$1\r\n    y=$2\r\n    n=$3\r\n    if [ -z $x ] || [ -z $y ] || [ -z $n ] ; then\r\n        echo \"usage: my_sunspider &lt;ws-number-1&gt; &lt;ws-number-2&gt; &lt;number-of-runs&gt;\"\r\n    else\r\n        for i in $x $y ; do\r\n            dir = $HOME\/moz\/ws$i\/js\/src\/opt\r\n            cd $dir || exit 1\r\n            make --quiet || exit 1\r\n            cd ~\/moz\/SunSpider\r\n            echo \"############################\"\r\n            echo \"####### TESTING ws$i #######\"\r\n            echo \"############################\"\r\n            time sunspider --runs=$n --args='-j' --shell $dir\/js &gt; opt$i\r\n         done\r\n\r\n         my_sunspider_compare_results $x $y\r\n    fi\r\n}\r\n\r\nfunction my_sunspider_compare_results\r\n{\r\n    x=$1\r\n    y=$2\r\n    if [ -z $x ] || [ -z $y ] ; then\r\n        echo \"usage: my_sunspider_compare_results &lt;ws-number-1&gt; &lt;ws-number-2&gt;\"\r\n    else\r\n        sunspider-compare-results \\\r\n            --shell $HOME\/moz\/ws$x\/js\/src\/opt\/js opt$x opt$y\r\n    fi\r\n}<\/pre>\n<p>An invocation like this:<\/p>\n<pre>my_sunspider 0 3 100<\/pre>\n<p>will ensure that optimised builds in both workspaces are present, and then compare them by doing SunSpider 100 runs.\u00a0 That usually gives what SunSpider claims as +\/-0.1% variation (I don&#8217;t believe it, though).\u00a0 On my Mac this takes about 3.5 minutes, and 100 runs is enough that the results are fairly reliable, certainly more so than the default of 10 runs.\u00a0 But when testing a performance-affecting change I like to do some timings, wait until a few more patches have landed in the tree, then update and rerun the timings &#8212; on my Mac I see variations of 5-10ms regularly due to minor code differences.\u00a0 Timing multiple versions like this gives me a better idea of whether a timing difference is real or not.\u00a0 Even then, it&#8217;s still not easy to know for sure, and this can be frustrating when trying to work out if an optimisation I applied is really giving a 5ms speed-up or not.<\/p>\n<p>On my Linux box, I have to use 1000 runs to get +\/-0.1% variation.\u00a0 This takes about 25 minutes, so I rarely do performance-related work on this machine.\u00a0 I don&#8217;t know why Linux causes greater timing variation.<\/p>\n<h3>Profiling &#8216;js&#8217; with Cachegrind<\/h3>\n<p>I run Cachegrind on &#8216;js&#8217; running SunSpider with this bash function:<\/p>\n<pre>function cg_sunspider\r\n{\r\n    x=$1\r\n    y=$2\r\n    if [ -z $x ] || [ -z $y ] ; then\r\n        echo \"usage: cg_sunspider &lt;ws-number-1&gt; &lt;ws-number-2&gt;\"\r\n    else\r\n        for i in $x $y ; do\r\n            dir = $HOME\/moz\/ws$i\/js\/src\/optg\r\n            cd $dir || exit 1\r\n            make --quiet || exit 1\r\n            cd ~\/moz\/SunSpider\r\n            time valgrind --tool=cachegrind --branch-sim=yes --smc-check=all \\\r\n                --cachegrind-out-file=cachegrind.out.optg$i \\\r\n                --auto-run-dsymutil=yes \\\r\n                $dir\/js `cat ss0-args.txt`\r\n            cg_annotate --auto=yes cachegrind.out.optg$i &gt; ann-optg$i\r\n        done\r\n    fi\r\n}<\/pre>\n<p>ss0-args.txt contains this text:<\/p>\n<pre>-j -f tmp\/sunspider-test-prefix.js -f resources\/sunspider-standalone-driver.js<\/pre>\n<p>What this does is run just the main SunSpider program, once, avoiding all the start-up processes and all that.\u00a0 This is important for Cachegrind &#8212; it means that I can safely use &#8211;cachegrind-out-file to name a specific file, which is not safe if running Cachegrind on a program involving multiple processes.\u00a0\u00a0 (I think this is slightly dangerous&#8230; if you run &#8216;sunspider &#8211;ubench&#8217; it seems to change one of the above .js files and you have to rerun SunSpider normally to get them back to normal.)\u00a0 I use &#8211;branch-sim=yes because I often find it to be useful; at least twice recently it has helped me identify performance problems.<\/p>\n<p>If I want to focus on a particular Cachegrind statistic, e.g. D2mr (level 2 data read misses) or Bim (indirect branch mispredictions) then I rerun cg_annotate like this:<\/p>\n<pre>cg_annotate --auto=yes --show=I2mr --sort=I2mr cachegrind.out.optgN &gt; ann-optgN-I2mr<\/pre>\n<h3>Profiling &#8216;js&#8217; with Shark<\/h3>\n<p>To profile &#8216;js&#8217; with Shark, I use SunSpider&#8217;s &#8211;shark20 and &#8211;test options.\u00a0 I don&#8217;t have this automated yet, I probably should.<\/p>\n<h3>Managing Changes with Mercurial<\/h3>\n<p>Most of my changes are not that large, so I leave them uncommitted in a workspace.\u00a0 This is primitive, but has one really nice feature:\u00a0 when pulling and updating, hg merges the changes and marks conflicts in the nice &#8220;&lt;&lt;&lt;&#8221; &#8220;&gt;&gt;&gt;&#8221; way.<\/p>\n<p>In comparison, with Mercurial queues (which I tried for a while) you have to pop your patches, update, then push them, and it uses &#8216;patch&#8217; to do the merging.\u00a0 And I hate &#8216;patch&#8217; because conflicted areas tend to be larger, and because they go in a separate reject file rather than being inserted inline.<\/p>\n<p>I also avoid doing local commits unless I&#8217;m working on something really large just because the subsequent merging is difficult (at least, I think it&#8217;s difficult;\u00a0 my Mercurial knowledge still isn&#8217;t great).\u00a0 In that case I do local commits until the change is finished, then apply the patch (using &#8216;hg diff&#8217; and &#8216;patch&#8217;) in a single hit to a newly cloned tree &#8212; given Mozilla&#8217;s use of Bugzilla, the change will have to be a single patch anyway so this aggregation step has to happen at some point.<\/p>\n<h3>Pre-push Checklist<\/h3>\n<p>Before landing any patch, I do my best to work through the following check-list.\u00a0 I created this list recently after having to back out several commits due to missing one of the above steps;\u00a0 I give examples of breakage I&#8217;ve caused in square brackets.<\/p>\n<ul>\n<li>Ensure there are no new compiler warnings for &#8216;js&#8217; for optimised and debug builds.\u00a0 [I managed to introduce some warnings on an optimised build recently for what was supposedly a whitespace-only change!]<\/li>\n<li>Ensure &#8216;js&#8217; runs trace-test.js without failures, for optimised builds, debug builds, debug builds with TMFLAGS=full (to test the verbose output) under Valgrind (to test for memory errors).\u00a0 [I&#8217;ve had to back out several patches due to breaking TMFLAGS=full]<\/li>\n<li>Ensure lirasm builds and passes its tests for both optimised and debug builds.\u00a0 [I&#8217;ve forgotten this numerous times, leaving lirasm in a broken state, which is why I created <a href=\"http:\/\/https:\/\/bugzilla.mozilla.org\/show_bug.cgi?id=503449\">bug 503449<\/a>].<\/li>\n<li>Ensure unit tests pass with a debug build.\u00a0 [Amusingly enough, I don&#8217;t think I&#8217;ve ever caused breakage by forgetting this step!]<\/li>\n<li>(For any commit that might affect performance) Check SunSpider timings with an optimised build.<\/li>\n<li>(For complex changes) Check the patch on the try servers. (Nb: they run optimised builds, so will miss assertion failures among other things)<\/li>\n<li>(For changes affecting the ARM backend) Check the patch builds and runs trace-test.js (using a debug build) on my virtual qemu+ARM\/Linux machine.<\/li>\n<li>Check tinderbox to make sure the tree is open for commits.\u00a0 [When the tree is closed, there&#8217;s no mechanism that actually prevents you from committing.\u00a0 I had to back-out a patch during a tinderbox upgrade because of this.]<\/li>\n<\/ul>\n<p>It&#8217;s quite a list, and I don&#8217;t usually do anything with a browser build, when I probably should, so that would make it even longer.\u00a0 And there are other things to get wrong&#8230; for example, I never test the &#8211;disable-jit configuration and I broke it once.<\/p>\n<h3>Pushing<\/h3>\n<p>When I&#8217;m ready to push a change, I make sure my workspaces are up-to-date with respect to the Mozilla repo.\u00a0 I then commit the change to my modified repo, then push it from there into ~\/moz\/ws0\/, then check &#8216;hg outgoing -p&#8217; on that repo to make sure it looks ok, and then push to the Mozilla repo from there.\u00a0 I try to do this quickly so that no-one else lands something in the meantime;\u00a0 this has only happened to me once and I tried to use &#8216;hg rollback&#8217; to undo my local changes which I think should have worked but seemingly didn&#8217;t.<\/p>\n<h3>Post-push Checklist<\/h3>\n<p>After committing, I do these steps:<\/p>\n<ul>\n<li>Mark the bug&#8217;s &#8220;whiteboard&#8221; field as &#8220;fixed-in-tracemonkey&#8221;.<\/li>\n<li>Put a link to the commit in a comment for the bug, of the form http:\/\/hg.mozilla.org\/tracemonkey\/rev\/&lt;revhash&gt;\/.\u00a0 I always test the link before submitting the comment.<\/li>\n<\/ul>\n<h3>Conclusions<\/h3>\n<p>That&#8217;s a lot of stuff.\u00a0 Two of my more notable conclusions are:<\/p>\n<ul>\n<li>Automation is a wonderful thing.\u00a0 In particular, having scripts for the complicated tasks (e.g. running the unit tests, running sunspider, running sunspider under Cachegrind) has saved me lots of time and typing (and lots of head-scratching and re-running when I realised I forgot some command line option somewhere).\u00a0 And this automation was made much easier once I settled on a standard workspace+build layout.<\/li>\n<li>The pre-push checklist is both disconcertingly long and disconcertingly incomplete.\u00a0 And I had to work it out almost entirely by myself &#8212; I&#8217;m not aware of any such check-list documented anywhere else.\u00a0 Having lots of possible configurations really hurts testability.\u00a0 I&#8217;m not sure how to improve this.<\/li>\n<\/ul>\n<p>If you made it this far, congratulations!\u00a0 That was pretty dry, especially if you&#8217;re not a Tracemonkey developer.\u00a0 I&#8217;d love to hear suggestions for improving what I&#8217;m doing.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>After six months of working on Tracemonkey, I&#8217;ve built up a particular workflow &#8212; how I use Mercurial, arrange my workspaces, run tests, and commit code.\u00a0 I thought it would be worth describing this in case it helps other developers improve their workflow, or perhaps so they can give me ideas on how to improve [&hellip;]<\/p>\n","protected":false},"author":139,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[289,467,649],"tags":[],"_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/posts\/146"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/users\/139"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/comments?post=146"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/posts\/146\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/media?parent=146"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/categories?post=146"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/nnethercote\/wp-json\/wp\/v2\/tags?post=146"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}