Whamcloud - gitweb
LU-13090 utils: fix lfs_migrate -p for file with pool
[fs/lustre-release.git] / lustre-iokit / stats-collect / iokit-lstats
1 #!/bin/bash
2
3 #
4 # very short example:
5 #
6 # to start collection:
7 #   VMSTAT_INTERVAL=0 SERVICE_INTERVAL=2 SDIO_INTERVAL=0 iokit-lstats start
8 #
9 # where value of interval means:
10 #   0 - gather stats at start and stop only
11 #   N - gather stats every N seconds
12 # if some XXX_INTERVAL isn't specified, related stats won't be collected
13 # XXX can be: VMSTAT, SERVICE, BRW, SDIO, MBALLOC, IO
14 #
15 # to stop collection:
16 #   iokit-lstats stop
17 #
18 # to fetch collected stats:
19 #   iokit-lstats fetch >file
20 # in file you'll get a tarbal containing directory with stats
21 # directory's name consists of hostname and date,
22 # like: stats-bzzz-2007-05-13-22.52.31
23 #
24
25 #
26 # TODO
27 #  - close all file descriptors, otherwise sshd can't finish session
28 #  - for sd_iostats convert partition to whole device
29 #
30
31 # configuration variables
32 TMP=${TMP:-/tmp}
33 PREFIX=${PREFIX:-${TMP}/lstats.}
34 PIDFILE=${PREFIX}pid
35 STATPIDS=${PREFIX}pids
36 OUTPREFIX=${OUTPREFIX:-${PREFIX}out.}
37 STIMEPREFIX=${STIMEPREFIX:-${PREFIX}time.}
38
39
40 function ls_grab_control()
41 {
42         OCOMM=$(ps -p $$ -o comm=)
43         if [ "$OCOMM" == "" ]; then
44                 echo "Can't fetch process name"
45                 exit
46         fi
47
48         # check for running master first
49         PID=$(cat $PIDFILE 2>/dev/null)
50 #echo "check master $PID"
51         if [ "x$PID" != "x" ]; then
52                 COMM=$(ps -p $PID -o comm=)
53                 if [ "$COMM" == "$OCOMM" ]; then
54                         echo "Master is already running by $PID"
55                         return 1
56                 fi
57         fi
58
59         # XXX: race -- two process can do this at same time, use rename instead
60         echo $$ >${PIDFILE}.$$
61         mv ${PIDFILE}.$$ ${PIDFILE}
62         a=$(cat ${PIDFILE})
63         if [ "$$" != "$a" ]; then
64                 echo "Some one $a won the race"
65                 return 1
66         fi
67
68         HAS_CONTROL="yes"
69 #echo "We've got control"
70
71         return 0
72
73 }
74
75 function ls_release_control()
76 {
77 #echo "Release control"
78
79         rm -f $PIDFILE
80 }
81
82 trap ls_atexit EXIT
83 function ls_atexit()
84 {
85         if [ "$HAS_CONTROL" != "" ]; then
86                 ls_release_control
87         fi
88 }
89
90
91 function usr1signal()
92 {
93         stop_collector=1
94 }
95
96 function idle_collector()
97 {
98         while [ "$stop_collector" != "1" ]; do
99                 sleep 100;
100         done
101 }
102
103 #
104 # args:
105 # - type
106 # - collector function
107 # - collector arguments
108 function run_collector()
109 {
110         local pid
111         local stime
112         local ctype=$1
113         local cfunc=$2
114         shift
115         shift
116
117         read pid NN </proc/self/stat
118         stime=$(ps -p $pid -o bsdstart=)
119         echo -n "$pid " >>$STATPIDS
120         echo -n "$stime" >>${STIMEPREFIX}${pid}
121
122         trap "usr1signal" SIGUSR1
123
124 #       echo "$pid: new collector $ctype $cfunc"
125         $cfunc $@ </dev/null >&${OUTPREFIX}${ctype}.${pid}
126
127 }
128
129 #
130 # vmstat collector
131 #
132 # VMSTAT_INTERVAL:
133 # - 0       - collect at start and stop only
134 # - N       - collect each N seconds
135 function vmstat_collector()
136 {
137         echo "vmstat " $(date)
138
139         if let "VMSTAT_INTERVAL==0"; then
140                 date
141                 vmstat
142                 idle_collector
143                 date
144                 vmstat
145         elif let "VMSTAT_INTERVAL>0"; then
146                 vmstat $VMSTAT_INTERVAL
147         else
148                 echo "Invalid VMSTAT_INTERVAL=$VMSTAT_INTERVAL"
149                 idle_collector
150         fi
151 }
152
153 function vmstat_start()
154 {
155         if [ "$VMSTAT_INTERVAL" == "" ]; then
156                 return;
157         fi
158
159         run_collector "vmstat" vmstat_collector &
160 }
161
162 #
163 # brw_stats collector
164 #
165 # BRW_INVERVAL:
166 # - 0 - collect at start and stop only
167 # - N - collect each N seconds
168 #
169 function brw_collector()
170 {
171         local filter=$1
172
173         echo "brw_* for $filter " $(date)
174
175         # clear old stats
176         lctl set_param -n obdfilter.${filter}.brw_*=0
177
178         if let "BRW_INTERVAL==0"; then
179                 lctl get_param -n obdfilter.${filter}.brw_*
180                 idle_collector
181                 lctl get_param -n obdfilter.${filter}.brw_*
182         elif let "BRW_INTERVAL>0"; then
183                 while [ "$stop_collector" != "1" ]; do
184                         lctl get_param -n obdfilter.${filter}.brw_*
185                         sleep $BRW_INTERVAL
186                 done
187         else
188                 echo "Invalid BRW_INTERVAL=$BRW_INTERVAL"
189                 idle_collector
190         fi
191 }
192
193 function brw_start()
194 {
195         if [ "$BRW_INTERVAL" == "" ]; then
196                 return;
197         fi
198
199         # find all obdfilters
200         for i in $(lctl list_param obdfilter.*); do
201                 filter=$(echo "$i" | awk -F"." '{print $2}')
202                 if [ "$filter" == "num_refs" ]; then
203                         continue;
204                 fi
205                 run_collector "brw" brw_collector $filter &
206         done
207 }
208
209 #
210 # service_stats collector
211 #
212 # SERVICE_INVERVAL:
213 # - 0 - collect at start and stop only
214 # - N - collect each N seconds
215 #
216 function service_collector()
217 {
218         local file=$1
219         local target=$2
220         local srv=$3
221
222         echo "service stats for ${target}/${srv} " $(date)
223
224         # clear old stats
225         lctl set_param -n $file=0
226
227         if let "SERVICE_INTERVAL==0"; then
228                 lctl get_param -n $file | grep -v "^[^ ]*[^0-9]*0 samples"
229                 idle_collector
230                 lctl get_param -n $file | grep -v "^[^ ]*[^0-9]*0 samples"
231         elif let "SERVICE_INTERVAL>0"; then
232                 while [ "$stop_collector" != "1" ]; do
233                         lctl get_param -n $file | grep -v "^[^ ]*[^0-9]*0 samples"
234                         sleep $SERVICE_INTERVAL
235                 done
236         else
237                 echo "Invalid SERVICE_INTERVAL=$SERVICE_INTERVAL"
238                 idle_collector
239         fi
240 }
241
242 function service_start()
243 {
244         if [ "$SERVICE_INTERVAL" == "" ]; then
245                 return;
246         fi
247
248         # find all OSTs and MDTs
249         for i in $(lctl list_param ost.* mdt.*); do
250                 target=$(echo "$i" | awk -F"." '{print $2}')
251                 if [ "$target" == "num_refs" ]; then
252                         continue;
253                 fi
254                 for j in $(lctl list_param ${i}.*); do
255                         srv=$(echo "$j" | awk -F"." '{print $3}')
256                         if [ "$srv" == "uuid" ]; then
257                                 continue;
258                         fi
259                         run_collector "service-${srv}" service_collector \
260                                 ${j}.stats $target $srv &
261                 done
262         done
263
264         # find all LDLM services
265         for i in $(lctl list_param ldlm.services.*); do
266                 srv=$(echo "$i" | awk -F"." '{print $3}')
267                 run_collector "service" service_collector ${i}.stats "ldlm" $srv &
268         done
269
270 }
271
272 #
273 # client_stats collector
274 #
275 # CLIENT_INTERVAL:
276 # - 0 - collect at start and stop only
277 # - N - collect each N seconds
278 #
279 function client_collector()
280 {
281         local file=$1
282         local target=$2
283         local srv=$3
284
285         echo "client stats for ${target}/${srv} " $(date)
286
287         # clear old stats
288         echo 0 >$file
289
290         if let "CLIENT_INTERVAL==0"; then
291                 grep -v "^[^ ]*[^0-9]*0 samples" $file
292                 idle_collector
293                 grep -v "^[^ ]*[^0-9]*0 samples" $file
294         elif let "CLIENT_INTERVAL>0"; then
295                 while [ "$stop_collector" != "1" ]; do
296                         grep -v "^[^ ]*[^0-9]*0 samples" $file
297                         sleep $CLIENT_INTERVAL
298                 done
299         else
300                 echo "Invalid CLIENT_INTERVAL=$CLIENT_INTERVAL"
301                 idle_collector
302         fi
303 }
304
305 function client_start()
306 {
307         if [ "$CLIENT_INTERVAL" == "" ]; then
308                 return;
309         fi
310
311         # find all osc
312         for i in $(lctl list_param osc.*); do
313                 target=$(echo "$i" | awk -F"." '{print $2}')
314                 if [ "$target" == "num_refs" ]; then
315                         continue;
316                 fi
317                 i=$(echo "$i" |awk '{gsub(/\./,"/");print}')
318                 for j in ${i}/*; do
319                         local stats=$(basename $j)
320                         if [ "$stats" == "stats" -o "$stats" == "rpc_stats" ]; then
321                                 run_collector "osc-${stats}" client_collector \
322                                         ${j} $target $stats &
323                         fi
324                 done
325         done
326         # find all llite stats
327         for i in $(lctl list_param llite.*); do
328                 target=$(echo "$i" | awk -F"." '{print $2}')
329                 i=$(echo "$i" |awk '{gsub(/\./,"/");print}')
330                 for j in ${i}/*; do
331                         stats=$(basename $j)
332                         if [ "$stats" == "stats" -o "$stats" == "vfs_ops_stats" ]; then
333                                 run_collector "llite-${stats}" client_collector \
334                                         ${j} $target ${stats} &
335                         fi
336                 done
337         done
338 }
339
340 #
341 # sdio_stats collector
342 #
343 # SDIO_INVERVAL:
344 # - 0 - collect at start and stop only
345 # - N - collect each N seconds
346 #
347 function sdio_collector()
348 {
349         local obd=$1
350         local uuid=$(lctl get_param -n obd.uuid 2>&1)
351         local tmp=$(lctl get_param -n obd.mntdev 2>&1)
352         local disk=$(basename $tmp)
353         local file="/proc/scsi/sd_iostats/${disk}"
354
355         echo "sd_iostats for ${uuid}/${disk} " $(date)
356
357         # clear old stats
358         echo 0 >$file
359
360         if let "SDIO_INTERVAL==0"; then
361                 cat $file
362                 idle_collector
363                 cat $file
364         elif let "SDIO_INTERVAL>0"; then
365                 while [ "$stop_collector" != "1" ]; do
366                         cat $file
367                         sleep $SDIO_INTERVAL
368                 done
369         else
370                 echo "Invalid SDIO_INTERVAL=$SDIO_INTERVAL"
371                 idle_collector
372         fi
373 }
374
375 function sdio_start()
376 {
377         if [ "$SDIO_INTERVAL" == "" ]; then
378                 return;
379         fi
380
381         # find all obdfilters and MDSs
382         for i in $(lctl list_param obdfilter.* mds.*); do
383                 obd=$(echo "$i" | awk -F"." '{print $2}')
384                 if [ "$obd" == "num_refs" ]; then
385                         continue;
386                 fi
387                 tmp=$(lctl get_param -n ${i}.mntdev 2>&1)
388                 if [ $? != 0 ]; then
389                         continue;
390                 fi
391                 local disk=$(basename $tmp)
392                 if [ ! -f /proc/scsi/sd_iostats/${disk} ]; then
393                         continue;
394                 fi
395                 run_collector "sdio" sdio_collector ${i} &
396         done
397 }
398
399 #
400 # mballoc_stats collector
401 #
402 # MBALLOC_INVERVAL:
403 # - 0 - collect at start and stop only
404 # - N - isn't implemented yet, works as with 0
405 #
406 function mballoc_collector()
407 {
408         local obd=$1
409         local uuid=$(lctl get_param -n obd.uuid 2>&1)
410         local tmp=$(lctl get_param -n obd.mntdev 2>&1)
411         local disk=$(basename $tmp)
412         local file="/proc/fs/ldiskfs*/${disk}/mb_history"
413
414         echo "mballoc history for ${uuid}/${disk} " $(date)
415
416         # log allocations only
417         for i in $file; do
418                 echo 3 >$i
419         done
420
421         if let "MBALLOC_INTERVAL==0"; then
422                 idle_collector
423                 cat $file
424         elif let "MBALLOC_INTERVAL>0"; then
425                 idle_collector
426                 cat $file
427         else
428                 echo "Invalid MBALLOC_INTERVAL=$MBALLOC_INTERVAL"
429                 idle_collector
430         fi
431 }
432
433 function mballoc_start()
434 {
435         if [ "$MBALLOC_INTERVAL" == "" ]; then
436                 return;
437         fi
438
439         # find all obdfilters and MDSs
440         for i in $(lctl list_param obdfilter.* mds.*); do
441                 obd=$(echo "$i" | awk -F"." '{print $2}')
442                 if [ "$obd" == "num_refs" ]; then
443                         continue;
444                 fi
445                 tmp=$(lctl get_param -n ${i}.mntdev 2>&1)
446                 if [ $? != 0 ]; then
447                         continue;
448                 fi
449                 disk=$(basename $tmp)
450                 if [ ! -f /proc/fs/ldiskfs*/${disk}/mb_history ]; then
451                         continue;
452                 fi
453                 run_collector "mballoc" mballoc_collector ${i} &
454         done
455 }
456
457 #
458 # io_stats collector
459 #
460 # IO_INVERVAL:
461 # - 0 - collect at start and stop only
462 # - N - collect each N seconds
463 #
464 function io_collector()
465 {
466         local obd=$1
467         local uuid=$(lctl get_param -n obd.uuid 2>&1)
468         local tmp=$(lctl get_param -n obd.mntdev 2>&1)
469         local disk=$(basename $tmp)
470         local file="/sys/block/${disk}/stat"
471
472         echo "iostats for ${uuid}/${disk} " $(date)
473
474         if let "IO_INTERVAL==0"; then
475                 cat $file
476                 idle_collector
477                 cat $file
478         elif let "IO_INTERVAL>0"; then
479                 while [ "$stop_collector" != "1" ]; do
480                         cat $file
481                         sleep $IO_INTERVAL
482                 done
483         else
484                 echo "Invalid IO_INTERVAL=$IO_INTERVAL"
485                 idle_collector
486         fi
487 }
488
489 function io_start()
490 {
491         if [ "$IO_INTERVAL" == "" ]; then
492                 return;
493         fi
494
495         # find all obdfilters and MDSs
496         for i in $(lctl list_param obdfilter.* mds.*); do
497                 obd=$(echo "$i" | awk -F"." '{print $2}')
498                 if [ "$obd" == "num_refs" ]; then
499                         continue;
500                 fi
501                 local tmp=$(lctl get_param -n ${i}.mntdev 2>&1)
502                 if [ $? != 0 ]; then
503                         continue;
504                 fi
505                 local disk=$(basename $tmp)
506                 if [ ! -f /sys/block/${disk}/stat ]; then
507                         continue;
508                 fi
509                 run_collector "io" io_collector ${i} &
510         done
511 }
512
513 #
514 # start entry point
515 #
516 function ls_start()
517 {
518         if ! ls_grab_control; then
519                 exit
520         fi
521
522         local PID=$(cat $STATPIDS 2>/dev/null)
523         if [ "x$PID" != "x" ]; then
524                 for i in $PID; do
525                         local i=$(echo $i | sed 's/^[^:]*://')
526                         local TO=$(cat ${STIMEPREFIX}$i)
527                         local TN=$(ps -p $i -o bsdstart=)
528                         if [ "$TO" != "" -a "$TO" == "$TN" ]; then
529                                 echo "Some slave is already running by $i"
530                                 exit
531                         fi
532                 done
533         fi
534
535         # clean all all stuff
536         rm -rf ${STATPIDS}* ${OUTPREFIX}* ${STIMEPREFIX}
537
538         vmstat_start
539         brw_start
540         service_start
541         sdio_start
542         mballoc_start
543         io_start
544         client_start
545 }
546
547 #
548 # stop entry point
549 #
550 # should stop collection, gather all collected data
551 #
552 function ls_stop()
553 {
554         if ! ls_grab_control; then
555                 exit
556         fi
557
558         local PID=$(cat $STATPIDS 2>/dev/null)
559         if [ "x$PID" != "x" ]; then
560                 local pids2wait=""
561                 for i in $PID; do
562                         local i=$(echo $i | sed 's/^[^:]*://')
563                         local TO=$(cat ${STIMEPREFIX}$i 2>/dev/null)
564                         local TN=$(ps -p $i -o bsdstart=)
565                         if [ "$TO" == "" -o "$TO" != "$TN" ]; then
566                                 echo "No collector with $i found"
567                                 continue
568                         fi
569                         /bin/kill -s USR1 -- -${i}
570                         pids2wait="$pids2wait $i"
571                 done
572 #echo "XXX: wait collectors $pids2wait"
573                 for i in $pids2wait; do
574                         TO=$(cat ${STIMEPREFIX}$i 2>/dev/null)
575                         TN=$(ps -p $i -o bsdstart=)
576                         while [ "$TO" != "" -a "$TO" == "$TN" ]; do
577                                 sleep 1
578                                 TN=$(ps -p $i -o bsdstart=)
579                         done
580                 done
581         fi
582         rm -f $STATPIDS ${STIMEPREFIX}*
583 }
584
585 #
586 # fetch entry point
587 #
588 # creates tarball of all collected stats
589 # current version is silly - just finds all *out* files in $TMP
590 ls_fetch()
591 {
592         if [ "X${GLOBAL_TIMESTAMP}" = "X" ]; then
593                 local date=$(date +%F-%H.%M.%S)
594         else
595                 date=${GLOBAL_TIMESTAMP}
596         fi
597
598         local hostname=$(hostname -s)
599         local name="stats-$hostname-$date"
600
601         stats=${OUTPREFIX}*
602         if ! mkdir ${TMP}/${name}; then
603                 echo "Can't create ${TMP}/${name}"
604                 exit
605         fi
606
607         let found=0
608         for i in ${OUTPREFIX}*; do
609                 mv $i ${TMP}/${name}/
610                 let "found++"
611         done
612
613         if let "found > 0"; then
614                 (cd ${TMP}; tar -zcf "./${name}.tar.gz" "./${name}")
615                 cat ${TMP}/${name}.tar.gz
616         else
617                 echo "No stats found"
618         fi
619         rm -rf ${TMP}/${name}*
620 }
621
622 #
623 # abort entry point
624 #
625 # should kill all running collections
626 #
627 function ls_abort()
628 {
629         echo "Abort isn't implemented yet"
630 }
631
632 #########
633 #  main
634 #########
635
636 # required to put all background processes into different process groups
637 # so that we can manage whole groups sending them a single signal
638 set -m
639
640 case $1 in
641         start) ls_start ;;
642         stop)  ls_stop ;;
643         fetch) ls_fetch ;;
644         abort)  ls_abort ;;
645         *) echo "Unknown command"
646 esac
647