monotone

monotone Mtn Source Tree

Root/contrib/monoprof.sh

  • Property mtn:execute set to true
1#!/bin/bash
2#Timothy Brownawell
3
4print_help()
5{
6cat <<EOF
7Arguments: [flags ...] [testname ...]
8 --build Rebuild monotone before profiling.
9 --update Run "monotone update" before building. Implies --build
10 --pull Run "monotone pull" before update. Implies --update, --build
11 --append x Append ".x" to the profile directory name
12 --list List available profile tests
13 --overwrite Allow results to be placed in an already existing directory
14 --datadir x Use x as base directory for files used, scratchwork, results
15 --help Print this message
16 --setup Set up most of the needed files (broken?)
17 --what x x is one of (time, mem); what to profile (defaults to time)
18 testname Only run selected profile tests
19EOF
20}
21
22#Bash script for profiling.
23#The user running this script must have sudo permission for opcontrol.
24#You are assumed to be using a debug version of libc, and to have a
25#version of libstdc++ compiled with both normal optimizations and debugging
26#symbols left in. (-O2 -g1 seems to be about right)
27#This script assumes that the debug libc is used by default.
28#This script probably assume a lot more, too.
29#
30#Files (any of these can be symlinks):
31#${DATADIR}/mt.dbdb holding net.venge.monotone
32#${DATADIR}/empty.dbdb holding a keypair
33#${DATADIR}/linux-2.6.11.tar.bz2
34#${DATADIR}/patch-2.6.11.7.bz2
35#${DATADIR}/monotone-src/dir with checked-out n.v.m
36#${DATADIR}/libstdc++.sooptimized debug stdc++ library
37#${DATADIR}/monotone-profiles/directory to store profiles in
38#created if nonexistent
39#${DATADIR}/hooks.luaoptional, hooks to use instead of
40#the default
41
42#The directory holding the files to test with
43#Can be overridden from the command line.
44DATADIR=/mnt/bigdisk
45
46#Some other variables depend on $DATADIR, which can be set on the command line.
47#So, the option handling needs to be done here or those vars might be wrong.
48BUILD=false
49UPDATE=false
50PULL=false
51HELP=false
52LIST=false
53APPEND=""
54RESTRICT=""
55OVERWRITE=false
56SETUP=false
57WHAT=""
58while ! [ $# -eq 0 ] ; do
59case "$1" in
60--build) BUILD=true;;
61--update) UPDATE=true; BUILD=true;;
62--pull) PULL=true; UPDATE=true; BUILD=true;;
63--append) shift; APPEND=".$1";;
64--list) LIST="true";;
65--overwrite) OVERWRITE="true";;
66--datadir) shift; DATADIR=$1;;
67--setup) SETUP=true;;
68--what) shift; WHAT=$1;;
69--help) HELP="true";;
70*) RESTRICT="${RESTRICT} $1";;
71esac
72shift
73done
74
75#Database holding net.venge.monotone
76MTDB=mt.db
77
78#A database holding only a keypair.
79EMPTYDB=empty.db
80
81#patch-${KPATCHVER}.bz2 is the "small patch" to add to the kernel.
82#file linux-${KVER}.tar.bz2 is the kernel tarball to use
83KPATCHVER=2.6.11.7
84KVER=2.6.11
85
86#Directory containing monotone sources
87BUILDDIR=${DATADIR}/monotone-src
88#Full path of the monotone binary to use.
89MONOTONE=${BUILDDIR}/monotone
90
91#sudo command, if not set you must be root
92#used to run opcontrol
93SUDO=/usr/bin/sudo
94
95#command line for valgrind
96VALGRIND="valgrind --tool=massif --depth=7"
97
98#Full path of the debug c++ library to use.
99#You probably have to build this yourself, since your distro's packaged
100#debug library probably isn't optimized (and so will give bogus profiles).
101#DBG_LIB=/usr/lib/debug/libstdc++.so
102DBG_LIB=${DATADIR}/libstdc++.so
103
104#Directory to store the generated profiles in.
105PROFDIR=${DATADIR}/monotone-profiles
106
107#Note: don't just use "time"; bash has a builtin by that name.
108#The real thing is better.
109TIME=/usr/bin/time
110
111VERSION=""
112[ -f ${MONOTONE} ] && VERSION=$(${MONOTONE} --version | sed 's/.*: \(.*\))/\1/')
113
114HOOKS=""
115[ -f ${DATADIR}/hooks.lua ] && HOOKS="--norc --rcfile=${DATADIR}/hooks.lua"
116
117server()
118{
119TIME_SERVER=$(tempfile)
120if [ "${WHAT}" = "mem" ] ; then
121${TIME} -o ${TIME_SERVER} \
122${VALGRIND} --log-file=srv-log \
123${MONOTONE} ${HOOKS} "$@" &
124else
125${TIME} -o ${TIME_SERVER} \
126${MONOTONE} ${HOOKS} "$@" &
127fi
128sleep 5
129}
130
131client()
132{
133TIME_CLIENT=$(tempfile)
134if [ "${WHAT}" = "mem" ] ; then
135${TIME} -o ${TIME_CLIENT} \
136${VALGRIND} --log-file=cli-log \
137${MONOTONE} ${HOOKS} "$@"
138else
139${TIME} -o ${TIME_CLIENT} \
140${MONOTONE} ${HOOKS} "$@"
141fi
142}
143
144mtn()
145{
146RUNTIME=$(tempfile)
147if [ "${WHAT}" = "mem" ] ; then
148${TIME} -o ${RUNTIME} \
149${VALGRIND} --log-file=mtn-log \
150${MONOTONE} ${HOOKS} "$@"
151else
152${TIME} -o ${RUNTIME} \
153${MONOTONE} ${HOOKS} "$@"
154fi
155}
156
157mtn_noprof()
158{
159${MONOTONE} ${HOOKS} "$@"
160}
161
162getsrvpid()
163{
164ps -Af|grep 'monotone.*serve\ localhost' | \
165grep -v time | awk '{print $2}'
166}
167
168killsrv()
169{
170kill -HUP $(getsrvpid)
171while ! [ "$(getsrvpid)" = "" ] ; do
172sleep 1
173done
174}
175
176
177#This picks the top 20 functions for execution time in the function,
178#and the top 20 for execution time in children of the function.
179#Function and template arguments are replaced with "...".
180hilights()
181{
182local F=$(tempfile)
183egrep -v '^( [[:digit:]]|-)' < $1 | \
184sed 's/([^()]*)/(...)/g' | \
185sed ':x ; s/<\(<\.\.\.>\|[^<>]\)*[^<>\.]>/<...>/g ; t x' > $F
186cat <(head -n1 $1) <(sort -rnk3 $F |head -n 20) <(echo) \
187<(sort -rnk1 $F | head -n 20)
188rm $F
189}
190
191profstart()
192{
193echo "${TESTNAME}..."
194if [ "${WHAT}" = "time" ] ; then
195${SUDO} opcontrol --reset
196${SUDO} opcontrol --start
197fi
198}
199
200profend()
201{
202if [ "${WHAT}" = "time" ] ; then
203opcontrol --dump
204opstack ${MONOTONE} > ${PDIR}/profile-${SHORTNAME}
205${SUDO} opcontrol --shutdown
206hilights ${PDIR}/profile-${SHORTNAME} > \
207${PDIR}/hilights-${SHORTNAME}
208fi
209
210local PID=""
211
212#Record standalone instance
213if [ -f "${RUNTIME}" ] ; then
214echo -e "\n${TESTNAME}:" >>${PDIR}/timing
215cat ${RUNTIME} >>${PDIR}/timing
216rm ${RUNTIME}
217if [ "${WHAT}" = "mem" ] ; then
218PID=$(echo mtn-log.pid*|sed 's/[^[:digit:]]//g')
219rm mtn-log.pid*
220mv massif.${PID}.txt ${PDIR}/memprof-${SHORTNAME}.txt
221mv massif.${PID}.ps ${PDIR}/memprof-${SHORTNAME}.ps
222fi
223fi
224
225#Record server instance
226if [ -f "${TIME_SERVER}" ] ; then
227echo -e "\n${SERVER_NAME}:" >>${PDIR}/timing
228cat ${TIME_SERVER} >>${PDIR}/timing
229rm ${TIME_SERVER}
230if [ "${WHAT}" = "mem" ] ; then
231PID=$(echo srv-log.pid*|sed 's/[^[:digit:]]//g')
232rm srv-log.pid*
233mv massif.${PID}.txt ${PDIR}/memprof-${SRVNAME}.txt
234mv massif.${PID}.ps ${PDIR}/memprof-${SRVNAME}.ps
235fi
236fi
237
238#Record client instance
239if [ -f "${TIME_CLIENT}" ] ; then
240echo -e "\n${CLIENT_NAME}:" >>${PDIR}/timing
241cat ${TIME_CLIENT} >>${PDIR}/timing
242rm ${TIME_CLIENT}
243if [ "${WHAT}" = "mem" ] ; then
244PID=$(echo cli-log.pid*|sed 's/[^[:digit:]]//g')
245rm cli-log.pid*
246mv massif.${PID}.txt ${PDIR}/memprof-${CLINAME}.txt
247mv massif.${PID}.ps ${PDIR}/memprof-${CLINAME}.ps
248fi
249fi
250}
251
252#Individual tests to run.
253#Each test should clean up after itself.
254#Since which tests to run can now be specified on the command line,
255#all tests should be independent.
256
257TESTS="${TESTS} test_netsync"
258test_netsync()
259{
260local SHORTNAME="netsync"
261local TESTNAME="Pull (and serve) net.venge.monotone"
262local SERVER_NAME="Serve net.venge.monotone"
263local SRVNAME="serve"
264local CLIENT_NAME="Pull net.venge.monotone"
265local CLINAME="pull"
266cp ${DATADIR}/${EMPTYDB} ${DATADIR}/test.db
267cp ${DATADIR}/${MTDB} ${DATADIR}/test-serve.db
268profstart
269server --db=${DATADIR}/test-serve.db \
270--ticker=none --quiet serve localhost \
271net.venge.monotone
272client --db=${DATADIR}/test.db \
273pull localhost net.venge.monotone
274killsrv
275profend
276
277rm ${DATADIR}/test.db ${DATADIR}/test-serve.db
278}
279
280TESTS="${TESTS} test_commit"
281test_commit()
282{
283local TESTNAME="Commit kernel ${KVER} to an empty database"
284local SHORTNAME="commitfirst"
285bzip2 -dc ${DATADIR}/linux-${KVER}.tar.bz2 | tar -C ${DATADIR} -xf -
286pushd ${DATADIR}/linux-${KVER}
287mtn_noprof setup .
288mtn_noprof --quiet add . # $(ls|grep -v '^MT')
289cp ${DATADIR}/${EMPTYDB} ${DATADIR}/test.db
290
291profstart
292mtn --branch=linux-kernel --db=${DATADIR}/test.db commit \
293--message="Commit message."
294profend
295
296TESTNAME="Commit a small patch (${KPATCHVER}) to the kernel"
297SHORTNAME="commitpatch"
298
299bzip2 -dc ${DATADIR}/patch-${KPATCHVER}.bz2 | patch -p1 >/dev/null
300profstart
301mtn --branch=linux-kernel --db=${DATADIR}/test.db commit \
302--message="Commit #2"
303profend
304
305TESTNAME="Recommit the kernel without changes"
306SHORTNAME="commitsame"
307
308profstart
309mtn --branch=linux-kernel --db=${DATADIR}/test.db commit \
310--message="no change"
311profend
312
313popd
314rm ${DATADIR}/test.db
315rm -rf ${DATADIR}/linux-${KVER}/
316}
317
318TESTS="${TESTS} test_lcad"
319test_lcad()
320{
321local TESTNAME="Find lcad of ebf14142 and 68fe12e6"
322local SHORTNAME="lcad"
323
324cp ${DATADIR}/${MTDB} ${DATADIR}/test.db
325profstart
326mtn --db=${DATADIR}/test.db \
327lcad ebf14142331667146d7a3aabb406945648ea00de \
328 68fe12e6f1de7d161eb9e27dd757e7d230049520
329
330profend
331rm ${DATADIR}/test.db
332}
333
334TESTS="${TESTS} test_bigfile"
335test_bigfile()
336{
337local TESTNAME=""#"Netsync a big file."
338local SHORTNAME=""#"bigfile"
339#setup:
340pushd ${DATADIR}
341cp ${EMPTYDB} test.db
342cp ${EMPTYDB} test2.db
343mtn_noprof --db=test.db setup testdir
344pushd testdir
345dd if=/dev/urandom of=largish bs=1M count=32
346mtn_noprof add largish
347
348TESTNAME="Commit a big file"
349SHORTNAME="bigfile-commit"
350profstart
351mtn commit --branch=bigfile --db=${DATADIR}/test.db \
352--message="log message"
353profend
354
355TESTNAME="Netsync a big file"
356SHORTNAME="bigfile-sync"
357local SERVER_NAME="Serve a big file"
358local SRVNAME="bigfile-serve"
359local CLIENT_NAME="Pull a big file"
360local CLINAME="bigfile-pull"
361profstart
362#run:
363server --db=${DATADIR}/test.db \
364--ticker=none --quiet serve localhost bigfile
365client --db=${DATADIR}/test2.db pull localhost bigfile
366killsrv
367
368profend
369#cleanup:
370popd
371rm -rf testdir/
372rm test.db test2.db
373popd
374}
375
376#TESTS="${TESTS} test_name"
377#test_name()
378#{
379#local TESTNAME=""
380#local SHORTNAME=""
381##setup:
382#
383#profstart
384##run:
385#
386#profend
387##cleanup:
388#
389#}
390
391run_tests()
392{
393local PDIR=${PROFDIR}/${VERSION}${APPEND}
394if [ -d ${PDIR} ] && [ ${OVERWRITE} = "false" ] ; then
395echo "Already profiled this version." >&2
396echo "If you've made changes since then use --append" >&2
397echo "to place the new results in a new directory," >&2
398echo "or specify --overwrite." >&2
399exit 1
400fi
401mkdir -p ${PDIR}
402export LD_PRELOAD=${DBG_LIB}
403echo "Profiling..."
404if [ "${WHAT}" = "time" ] ; then
405${SUDO} opcontrol --separate=lib --callgraph=10 \
406--image=${MONOTONE} --no-vmlinux
407fi
408for i in ${TESTS}; do
409$i
410done
411chmod -R a+rX ${PDIR}
412echo "Monotone version: ${VERSION}"
413cat <(echo -e "Timing for each run:") ${PROFDIR}/${VERSION}$1/timing
414}
415
416BEGINTIME=$(date +%s)
417
418if [ ${HELP} = "true" ] ; then
419print_help
420exit 0
421fi
422if [ ! -d ${DATADIR} ] ; then
423echo "datadir ${DATADIR} not found (perhaps try --datadir)"
424 print_help
425 exit 1
426fi
427if [ ${LIST} = "true" ] ; then
428for i in ${TESTS}; do
429echo -e "\t$i"
430done
431exit 0
432fi
433if [ ${SETUP} = "true" ] ; then
434pushd ${DATADIR}
435monotone --db=empty.db db init
436echo -e "xxx\nxxx\n" | monotone --db=empty.db genkey xxx
437 cat >hooks.lua <<EOF
438function get_passphrase(keypair_id) return "xxx" end
439function get_netsync_read_permitted(branch, keyid) return true end
440function get_netsync_write_permitted(keyid) return true end
441EOF
442cp empty.db mt.db
443monotone --db=mt.db pull off.net net.venge.monotone
444monotone --db=mt.db --branch=net.venge.monotone co monotone-src
445wget http://www.kernel.org/pub/linux/kernel/v2.6/patch-2.6.11.7.bz2
446wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.11.tar.bz2
447if ! [ -f ${DEBUG_LIB} ] ; then
448echo "You still need to build a debug stdlibc++"
449echo "and copy or symlink it to ${DEBUG_LIB}"
450fi
451popd
452exit 0
453fi
454
455pushd ${BUILDDIR} >/dev/null
456if [ ${PULL} = "true" ] ; then
457monotone pull
458fi
459if [ ${UPDATE} = "true" ] ; then
460monotone update
461fi
462if [ ${BUILD} = "true" ] ; then
463make || ( echo -e "Build failed.\nNot profiling." >&2 ; exit 1 )
464fi
465popd >/dev/null
466if ! [ "${RESTRICT}" = "" ] ; then
467TESTS="${RESTRICT}"
468fi
469
470if ! [ -f ${MONOTONE} ] ; then
471echo "Error: ${MONOTONE} does not exist." >&2
472exit 1
473fi
474
475TIME_OK=false
476if which opcontrol >/dev/null && which opstack >/dev/null; then
477TIME_OK=true
478fi
479
480MEM_OK=false
481if which valgrind >/dev/null; then
482MEM_OK=true
483fi
484
485if [ "${WHAT}" = "time" ] ; then
486if ! [ ${TIME_OK} = "true" ] ; then
487echo "Error: cannot find oprofile." >&2
488exit 1
489fi
490fi
491
492if [ "${WHAT}" = "mem" ] ; then
493if ! [ ${MEM_OK} = "true" ] ; then
494echo "Error: cannot find valgrind." >&2
495exit 1
496fi
497fi
498
499if [ "${WHAT}" = "" ] ; then
500if [ ${MEM_OK} = "true" ] ; then
501WHAT="mem"
502fi
503if [ ${TIME_OK} = "true" ] ; then
504WHAT="time"
505fi
506fi
507
508if [ "${WHAT}" = "" ] ; then
509echo "Error: cannot find a profiler." >&2
510exit 1
511fi
512
513run_tests
514TOTALTIME=$(($(date +%s)-${BEGINTIME}))
515ELAPSED=$((${TOTALTIME}/60)):$((${TOTALTIME}%60))
516echo -e "\nTime elapsed: ${ELAPSED}\n"

Archive Download this file

Branches

Tags

Quick Links:     www.monotone.ca    -     Downloads    -     Documentation    -     Wiki    -     Code Forge    -     Build Status