Source Lines Of Code with Munin and SLOCCount

October 29, 2011

Pretty much exactly a year ago I wrote a plugin for Munin that scrapes the current temperature in Cambridge as recorded by the University Computer Laboratory.

The plugin is unlikely to be of much use to anyone else if used exactly as is, given that it only provides information for Cambridge. Posting the code here serves two purposes:

  1. It serves as a backup of the plugin for my own reference – full blown version control for snippets like this is overkill.
  2. Mutating something that already works to make it do what you want is often easier than writing something from scratch, so it may be of use to other Munin plugin developers just getting started.

It doesn’t take any configuration, and has no comments – the code should be self explanatory:

#!/bin/sh

case $1 in
config)
cat <<'EOM' graph_title Cambridge Weather graph_args --base 1000 -l 0 graph_vlabel Celsius graph_category sensors temperature.label Computer Laboratory EOM exit 0;; esac echo -n "temperature.value " wget http://www.cl.cam.ac.uk/research/dtg/weather/current-obs.txt \ -O - 2> /dev/null | awk '/Temperature/ {print $2}'

Installing this plugin is as simple as writing the above source to /usr/share/munin/plugins/weather and making a symbolic link to it in /etc/munin/plugins – not forgetting to restart munin-node having done so.

The temperature over the course of the last year, at the time of writing, follows:

If you do happen to look around at the rest of the graphs at the above link, you’ll notice that all the others are currently looking very bare, and that the weather graph is missing a chunk of the last week. There are two reasons for this:

  1. I’ve recently migrated away from a fixed-price-per-month VDS (it was slightly overpowered in terms of CPU and underproviding in terms of disk capacity) to an Amazon EC2 micro instance, which is more flexible in terms of cost (but also seems to be fairly erratic in terms of performance, to the point that I’m having second thoughts). It wouldn’t have been very meaningful to migrate the server-specific statistics of one server over to another.
  2. In the process of developing my SLOCCount plugin, I managed to make a bit of a mess of the freshly installed Munin configuration on the new EC2 instance, and had to revert to a backup of the RRD file that I’d copied over from the previous VDS.

Anyway, one year on and I’m just starting up a small side project for fun outside of work (which I will hopefully be making a post about soon), and as I don’t have a dissertation to write alongside it – which served well as a planning tool – I’m putting a bit of initial time in to setting up some tools like Trac for the project. As well as that, I thought it would be neat to have a graph of the number of lines of code over time as the project develops (this would have been interesting for my dissertation, but alas, I was – quite appropriately – focused only on the bits that really mattered; graphing lines of code is just a bit of fun).

So, before allowing myself to do any more development on the project itself, I hacked up a quick and dirty Munin plugin that shows lines of code by language as stacked graphs:

#!/bin/sh

case $1 in
config)
printf "graph_title $PROJECT_NAME\n"
printf "graph_args --base 1000 -l 0\n"
printf "graph_vlabel Lines of code\n"
printf "graph_category projects\n"
sloccount $PROJECT_PATH 2>/dev/null | awk \
'BEGIN{FS="[:]?[ ]*[(]?[%)]?"}; \
/Totals grouped by language/ {resultLine=NR}; \
/Total Physical Source Lines of Code/ {resultLine=0}; \
resultLine>0 && NF==5 {print $1 "_lines.label " $1; \
if(NR==resultLine+1) print $1 "_lines.draw AREA"; \
else print $1 "_lines.draw STACK"}'
exit 0;;
esac

sloccount $PROJECT_PATH 2>/dev/null | awk \
'BEGIN{FS="[:]?[ ]*[(]?[%)]?"}; \
/Totals grouped by language/ {resultLine=NR}; \
/Total Physical Source Lines of Code/ {resultLine=0}; \
resultLine>0 && NF==5 {print $1 "_lines.value " $2}'

Because you may wish to run multiple instances of the plugin – e.g. if you have several distinct projects that you want code analysis running on – I suggest you write the above code to /usr/share/munin/plugins/sloccount and then make symbolic links of the form /etc/munin/plugins/sloccount_projectname

The plugin does require configuration: the single entry I currently have in /etc/munin/plugin-conf.d/munin-node is:

[sloccount_cardiographer]
env.PROJECT_NAME Cardio Grapher
env.PROJECT_PATH /home/dhpiggott/Workspace/CardioGrapher
user dhpiggott

With this configuration, the config output at the time of writing is:

$ sudo munin-run sloccount_cardiographer config
graph_title Cardio Grapher
graph_args --base 1000 -l 0
graph_vlabel Lines of code
graph_category projects
java_lines.label java
java_lines.draw AREA
xml_lines.label xml
xml_lines.draw STACK

and the value output is:

java_lines.value 887
xml_lines.value 37

PROJECT_NAME and PROJECT_PATH do what they say on the tin. The user is a bit more interesting; sloccount tries to cache the results of its analysis to ~/.slocdata and fails if it can’t write this data. The directory it uses is configurable with --datadir but the point here is that Munin plugins by default run as the munin user and thus cannot write this slocdata. To make it work, configure the plugin to run as any user that will allow slocdata to be written to its home directory.

Of course, the user will also need permission to read PROJECT_PATH and you will need to have SLOCCount installed.

At the time of writing this post, the resulting graphs are pretty bare – as they would be. Here’s a snapshot anyway:

Cardio Grapher SLOCCount (day)

References

In creating this plugin I found the following resources useful. I won’t repeat the content to be found in them, except to say that if you’re struggling to grok awk, the key concept that everything else will follow from is the idea of patterns and actions (though I don’t claim to have grokked it!).