[0d0e60]: utils / opcontrol Maximize Restore History

Download this file

opcontrol    1193 lines (1001 with data), 24.5 kB

#!/bin/bash
#
# opcontrol is a script to control OProfile
# opcontrol --help and opcontrol --list-events have info
#
# Copyright 2002
# Read the file COPYING
#
# Authors: John Levon, Philippe Elie, Will Cohen

# ensure bash2
if test "`echo $BASH_VERSION | cut -b1`" -lt 2; then
	exec /bin/bash2 $0 $@
fi
 

SYSCTL=do_sysctl

# A replacement function for the sysctl (procps package) utility which is
# missing on some distribution (e.g. slack 7.0). 
# Handles only the -w option of sysctl.
do_sysctl()
{
	if test "$1" != "-w"; then
		echo "$0 unknown sysctl option" >&2
		exit 1
	fi

	shift

	arg=`echo $1 | awk -F= '{print $1}'`
	val=`echo $1 | awk -F= '{print $2}'`

	dev_name=`echo $arg | tr . /`

	if test ! -f /proc/sys/$dev_name; then
		echo "/proc/sys/$dev_name does not exist or is not a regular file" >&2
		exit 1
	fi
	echo $val > /proc/sys/$dev_name
}


# check value is set
error_if_empty()
{
	if test -z "$2"; then
		echo "No value given for option $1" >&2
		do_help 
		exit 1
	fi
}


# rm_device arguments $1=file_name
function rm_device
{
	if test -c "$1"; then
		vecho "Removing $1"
		rm "$1"
	fi
}


# create_device arguments $1=file_name $2=MAJOR_NR $3=MINOR_NR
function create_device {
	vecho "Doing mknod $1"
	mknod "$1" c $2 $3
	if test "$?" != "0"; then
		echo "Couldn't mknod $1" >&2
		exit 1
	fi
	chmod 700 "$1"
}


move_and_remove()
{
	if test -e $1; then
		mv $1 $SAMPLES_DIR/.tmp_reset.$$
		rm -rf $SAMPLES_DIR/.tmp_reset.$$
	fi
}


# verbose echo
vecho()
{
	if test "$VERBOSE" != "yes"; then 
		return;
	fi
	echo $@
}

 
# print help message
do_help()
{ 
   echo "opcontrol: usage:
   -l/--list-events list event types and unit masks
   -?/--help        this message
   -v/--version     show version
   --init           loads the oprofile module and oprofilefs
   --setup          give setup arguments (may be omitted)
   --start-daemon   start daemon without starting profiling
   -s/--start       start data collection
   -d/--dump        flush the collected profiling data
   -t/--stop        stop data collection
   -h/--shutdown    stop data collection and kill daemon
   -V/--verbose     be verbose in the daemon log
   --reset          clears out data from current session
   --save=name      save data from current session to session_name
   --deinit         unload the oprofile module and oprofilefs

   -e/--event=eventspec 

      Choose an event. May be specified multiple times. Of the form
      "default" or "name:count:unitmask:kernel:user", where :

      name:     event name, e.g. CPU_CLK_UNHALTED or RTC_INTERRUPTS
      count:    reset counter value e.g. 100000
      unitmask: hardware unit mask e.g. 0x0f
      kernel:   whether to profile kernel: 0 or 1
      user:     whether to profile userspace: 0 or 1

   -p/--separate=type,[types]

       Separate profiles as follows :

       none:     no profile separation
       library:  separate shared library profiles per-application
       kernel:   same as library, plus kernel profiles
       thread:   per-thread/process profiles
       cpu:      per CPU profiles
       all:      all of the above

   -c/--callgraph=#depth         enable callgraph sample collection with a maximum depth. Use 0 to disable callgraph. This option is ignored with 2.2/2.4 or 2.6 kernel w/o the callgraph support patch (x86 only).

   -i/--image=name[,names]       list of binaries to profile (default is "all")
   --vmlinux=file                vmlinux kernel image
   --no-vmlinux                  no kernel image (vmlinux) available
   --kernel-range=start,end      kernel range vma address in hexadecimal
   --buffer-size=num             kernel buffer size in sample units
   --note-table-size             kernel notes buffer size in notes units (2.4 only)
" >&2
}


# load the module and mount oprofilefs
load_module_26()
{
	grep oprofilefs /proc/filesystems >/dev/null
	if test "$?" -ne 0; then
		modprobe oprofile
		if test "$?" != "0"; then
			# couldn't load a module
			return
		fi
		grep oprofile /proc/modules >/dev/null
		if test "$?" != "0"; then
			# didn't find module
			return
		fi
	fi
	grep oprofilefs /proc/filesystems >/dev/null
	if test "$?" -ne 0; then
		# filesystem still not around
		return
	fi
	mkdir /dev/oprofile >/dev/null 2>&1
	grep oprofilefs /etc/mtab >/dev/null
	if test "$?" -ne 0; then
		mount -t oprofilefs nodev /dev/oprofile >/dev/null
	fi
	KERNEL_SUPPORT=yes
	OPROFILE_AVAILABLE=yes
}


load_module_24()
{
	grep oprof /proc/devices >/dev/null
	if test "$?" -ne 0; then
		modprobe oprofile
		if test "$?" != "0"; then
			# couldn't load a module
			return
		fi
		grep oprofile /proc/modules >/dev/null
		if test "$?" != "0"; then
			# didn't find module
			return
		fi
	fi
	KERNEL_SUPPORT=no
	OPROFILE_AVAILABLE=yes
}


load_module()
{
	OPROFILE_AVAILABLE=no
	load_module_26
	if test "$OPROFILE_AVAILABLE" != "yes"; then
		load_module_24
	fi
	if test "$OPROFILE_AVAILABLE" != "yes"; then
		echo "Kernel doesn't support oprofile" >&2
		exit 1
	fi
}


# initialise parameters
do_init()
{
	# for these three buffer size == 0 means use the default value
	# hard-coded in op_user.h
	BUF_SIZE=0
	NOTE_SIZE=0
	VMLINUX=
	VERBOSE=no
	SEPARATE_LIB=0
	SEPARATE_KERNEL=0
	SEPARATE_THREAD=0
	SEPARATE_CPU=0
	CALLGRAPH=0

	OPROFILED="$OPDIR/oprofiled"

	# location for daemon setup information
	SETUP_DIR="/root/.oprofile"
	SETUP_FILE="$SETUP_DIR/daemonrc"
 
	# as in op_user.h
	DIR="/var/lib/oprofile"
	LOCK_FILE="/var/lib/oprofile/lock"
	LOG_FILE="$DIR/oprofiled.log"
	SAMPLES_DIR="$DIR/samples"
	CURRENT_SAMPLES_DIR=${SAMPLES_DIR}/current
	if test "$KERNEL_SUPPORT" = "yes"; then
		MOUNT="/dev/oprofile"
		DEVICE_FILE="$MOUNT/buffer"
	else
		MOUNT="/proc/sys/dev/oprofile"
		DEVICE_FILE="$DIR/opdev"
		NOTE_DEVICE_FILE="$DIR/opnotedev"
		HASH_MAP_DEVICE_FILE="$DIR/ophashmapdev"
	fi

	CPUTYPE=`cat $MOUNT/cpu_type`
	OP_COUNTERS=`ls $MOUNT/ | grep "^[0-9]\+\$" | tr "\n" " "`
	NR_CHOSEN=0

	DEFAULT_EVENT=`$OP_HELP --get-default-event`
 
	IS_TIMER=0
	IS_PERFMON=0
	if test "$CPUTYPE" = "timer"; then
		IS_TIMER=1
	else
		case "$CPUTYPE" in
			ia64/*)
				IS_PERFMON=$KERNEL_SUPPORT
				;;
		esac
	fi
}


create_dir()
{
	if test ! -d "$1"; then
	       mkdir -p "$1"
	       if test "$?" != "0"; then
		       echo "Couldn't mkdir -p $1" >&2
		       exit 1
	       fi
	       chmod 755 "$1"
	fi
}


# save all the setup related information
do_save_setup()
{
	create_dir "$SETUP_DIR"

	touch $SETUP_FILE
	chmod 644 $SETUP_FILE
	>$SETUP_FILE

	if test "$NR_CHOSEN" != 0; then
		for f in `seq 0 $((NR_CHOSEN - 1))`; do
			echo "CHOSEN_EVENTS[${f}]=${CHOSEN_EVENTS[$f]}" >>$SETUP_FILE
		done
	fi
 
	echo "NR_CHOSEN=$NR_CHOSEN" >>$SETUP_FILE

	echo "SEPARATE_LIB=$SEPARATE_LIB" >> $SETUP_FILE
	echo "SEPARATE_KERNEL=$SEPARATE_KERNEL" >> $SETUP_FILE
	echo "SEPARATE_THREAD=$SEPARATE_THREAD" >> $SETUP_FILE
	echo "SEPARATE_CPU=$SEPARATE_CPU" >> $SETUP_FILE
	echo "VMLINUX=$VMLINUX" >> $SETUP_FILE
	echo "IMAGE_FILTER=$IMAGE_FILTER" >> $SETUP_FILE
	# write the actual information to file
	if test "$BUF_SIZE" != 0; then
		echo "BUF_SIZE=$BUF_SIZE" >> $SETUP_FILE
	fi;
	if test "$KERNEL_SUPPORT" != "yes"; then
		echo "NOTE_SIZE=$NOTE_SIZE" >> $SETUP_FILE
	fi
	echo "CALLGRAPH=$CALLGRAPH" >> $SETUP_FILE
}


# reload all the setup-related information
do_load_setup()
{
	if test -f "$SETUP_FILE"; then
		# load the actual information from file
		# FIXME this is insecure, arbitrary commands could be added to
		# $SETUP_FILE and be executed as root
		source $SETUP_FILE
	fi

	vecho "Parameters used:"
	vecho "CPUTYPE $CPUTYPE"
	if test $BUF_SIZE != 0; then
		vecho "BUF_SIZE $BUF_SIZE"
	else
		vecho "BUF_SIZE default value"
	fi;

	vecho "SEPARATE_LIB $SEPARATE_LIB"
	vecho "SEPARATE_KERNEL $SEPARATE_KERNEL"
	vecho "SEPARATE_THREAD $SEPARATE_THREAD"
	vecho "SEPARATE_CPU $SEPARATE_CPU"
	vecho "CALLGRAPH $CALLGRAPH"
	vecho "VMLINUX $VMLINUX"
}


check_valid_args()
{
	if test -z "$VMLINUX"; then
		echo "No vmlinux file specified. You must specify the correct vmlinux file, e.g." >&2
		echo "opcontrol --vmlinux=/path/to/vmlinux" >&2
		echo "If you do not have a vmlinux file, use " >&2
		echo "opcontrol --no-vmlinux" >&2
		echo "Enter opcontrol --help for full options" >&2
		exit 1
	fi

	if test -f "$VMLINUX"; then
		return
	fi

	if test "$VMLINUX" = "none"; then
		return
	fi

	echo "The specified vmlinux file \"$VMLINUX\" doesn't exist." >&2
	exit 1
}


# get start and end points of the kernel
get_kernel_range()
{
	if test ! -z "$KERNEL_RANGE"; then
		return;
	fi

	if test "$VMLINUX" = "none"; then
		return;
	fi

	range_info=`objdump -h $VMLINUX 2>/dev/null | grep " .text "`
	tmp1=`echo $range_info | awk '{print $4}'`
	tmp_length=`echo $range_info | awk  '{print $3}'`
	tmp2=`objdump -h $VMLINUX --adjust-vma=0x$tmp_length 2>/dev/null | grep " .text " | awk  '{print $4}'`

	if test -z "$tmp1" -o -z "$tmp2"; then
		echo "The specified file $VMLINUX does not seem to be valid" >&2
		echo "Make sure you are using vmlinux not vmlinuz" >&2
		vecho "found start as \"$tmp1\", end as \"$tmp2\"" >&2
		exit 1
	fi
	KERNEL_RANGE="`echo $tmp1`,`echo $tmp2`"
	vecho "KERNEL_RANGE $KERNEL_RANGE"
}
 
 
# validate --separate= parameters. This function is called with IFS=,
# so on each argument is splitted
validate_separate_args()
{
	error_if_empty $1 $2	# we need at least one argument
	local i=1
	SEPARATE_LIB=0
	SEPARATE_KERNEL=0
	SEPARATE_THREAD=0
	SEPARATE_CPU=0
	while (($i < $#)); do
		shift
		case "$1" in
			library)
				SEPARATE_LIB=1
				;;
			kernel)
				# first implied by second
				SEPARATE_LIB=1 
				SEPARATE_KERNEL=1
				;;
			thread)
				SEPARATE_THREAD=1
				;;
			cpu)
				SEPARATE_CPU=1
				;;
			all)
				SEPARATE_LIB=1
				SEPARATE_KERNEL=1
				SEPARATE_THREAD=1
				SEPARATE_CPU=1
				;;
			none)
				SEPARATE_LIB=0
				SEPARATE_KERNEL=0
				SEPARATE_THREAD=0
				SEPARATE_CPU=0
				;;
			*)
				echo "invalid --separate= argument: $1"
				exit 1
		esac
	done
}


# check the counters make sense, and resolve the hardware allocation
verify_counters()
{
	if test "$IS_TIMER" = 1; then
		if test "$NR_CHOSEN" != 0; then
	 		echo "You cannot specify any performance counter events" >&2
			echo "because OProfile is in timer mode." >&2
			exit 1
		fi
		return
	fi

	OP_HELP_ARGS=

	if test "$NR_CHOSEN" != 0; then
		for f in `seq 0 $((NR_CHOSEN - 1))`; do
			if test "${CHOSEN_EVENTS[$f]}" != ""; then
				OP_HELP_ARGS="$OP_HELP_ARGS ${CHOSEN_EVENTS[$f]}"
			fi
		done

		HW_CTRS=`$OP_HELP --check-events $OP_HELP_ARGS`
		if test "$?" != 0; then
			exit 1
		fi
	fi
}


# get and check specified options
do_options()
{
	EXCLUSIVE_ARGC=0
	SETUP=no
	NEED_SETUP=no
	SEEN_EVENT=0

	# load any default settings
	do_load_setup

	while [ "$#" -ne 0 ]
	do
		arg=`printf %s $1 | awk -F= '{print $1}'`
		val=`printf %s $1 | awk -F= '{print $2}'`
		shift
		if test -z "$val"; then
			local possibleval=$1
			printf %s $1 "$possibleval" | grep ^- >/dev/null 2>&1
			if test "$?" != "0"; then
				val=$possibleval
				shift
			fi
		fi
 
		case "$arg" in

			--init)
				# this is already done in load_module
				# because need to know the processor type
				# and number of registers
				INIT=yes;
				EXCLUSIVE_ARGC=`expr $EXCLUSIVE_ARGC + 1`
				EXCLUSIVE_ARGV="$arg"
				;;

			--setup)
				SETUP=yes
				;;

			--start-daemon)
				if test "$KERNEL_SUPPORT" != "yes"; then
					echo "$arg unsupported. use \"--start\"" >& 2
					exit 1
				fi
				START_DAEMON=yes
				EXCLUSIVE_ARGC=`expr $EXCLUSIVE_ARGC + 1`
				EXCLUSIVE_ARGV="$arg"
				;;

			-s|--start)
				START=yes
				EXCLUSIVE_ARGC=`expr $EXCLUSIVE_ARGC + 1`
				EXCLUSIVE_ARGV="$arg"
				;;

			-d|--dump)
				DUMP=yes
				ONLY_DUMP=yes
				EXCLUSIVE_ARGC=`expr $EXCLUSIVE_ARGC + 1`
				EXCLUSIVE_ARGV="$arg"
				;;

			-t|--stop)
				if test "$KERNEL_SUPPORT" != "yes"; then
					echo "$arg unsupported. use \"--shutdown\"" >& 2
					exit 1
				fi
				DUMP=yes
				STOP=yes
				EXCLUSIVE_ARGC=`expr $EXCLUSIVE_ARGC + 1`
				EXCLUSIVE_ARGV="$arg"
				;;

			-h|--shutdown)
				DUMP=yes
				STOP=yes
				KILL_DAEMON=yes
				EXCLUSIVE_ARGC=`expr $EXCLUSIVE_ARGC + 1`
				EXCLUSIVE_ARGV="$arg"
				;;

			--reset)
				DUMP=yes
				RESET=yes
				EXCLUSIVE_ARGC=`expr $EXCLUSIVE_ARGC + 1`
				EXCLUSIVE_ARGV="$arg"
				;;

			--save)
				error_if_empty $arg $val
				DUMP=yes
				SAVE_SESSION=yes
				SAVE_NAME=$val
				EXCLUSIVE_ARGC=`expr $EXCLUSIVE_ARGC + 1`
				EXCLUSIVE_ARGV="$arg"
				;;

			--deinit)
				DUMP=yes
				STOP=yes
				KILL_DAEMON=yes
				DEINIT=yes
				EXCLUSIVE_ARGC=`expr $EXCLUSIVE_ARGC + 1`
				EXCLUSIVE_ARGV="$arg"
				;;

			# --setup options

			--buffer-size)
				error_if_empty $arg $val
				BUF_SIZE=$val
				DO_SETUP=yes
				;;
			-e|--event)
				error_if_empty $arg $val
				# reset any read-in defaults from daemonrc
				if test "$SEEN_EVENT" = "0"; then
					NR_CHOSEN=0
					SEEN_EVENT=1
				fi
				if test "$val" = "default"; then
					val=$DEFAULT_EVENT
				fi
				CHOSEN_EVENTS[$NR_CHOSEN]=$val
				NR_CHOSEN=`expr $NR_CHOSEN + 1`
				DO_SETUP=yes
				;;
			-p|--separate)
				OLD_IFS=$IFS
				IFS=,
				validate_separate_args $arg $val
				IFS=$OLD_IFS
				DO_SETUP=yes
				;;
			-c|--callgraph)
				error_if_empty $arg $val
				CALLGRAPH=$val
				DO_SETUP=yes
				;;
			--vmlinux)
				error_if_empty $arg $val
				VMLINUX=$val
				DO_SETUP=yes
				# check validity
				get_kernel_range
				;;
			--no-vmlinux)
				VMLINUX=none
				DO_SETUP=yes
				;;
			--kernel-range)
				error_if_empty $arg $val
				KERNEL_RANGE=$val
				DO_SETUP=yes
				;;
			--note-table-size)
				error_if_empty $arg $val
				if test $"KERNEL_SUPPORT" = "yes"; then
					echo "\"$arg\" ignored. " >& 2
				else
					NOTE_SIZE=$val
				fi
				DO_SETUP=yes
				;;
			-i|--image)
				error_if_empty $arg $val
				if test "$val" = "all"; then
					IMAGE_FILTER=
				else
					IMAGE_FILTER=$val
				fi
				DO_SETUP=yes
				;;

			-V|--verbose)
				VERBOSE=yes
				;;

			-l|--list-events)
				EXCLUSIVE_ARGC=`expr $EXCLUSIVE_ARGC + 1`
				EXCLUSIVE_ARGV="$arg"
				exec $OP_HELP
				;;
				
			*)
				echo "Unknown option \"$arg\". See opcontrol --help" >&2
				exit 1
				;; 
		esac
	done

	verify_counters

	# error checking to make sure options make sense
	if test "$EXCLUSIVE_ARGC" -gt 1; then
		echo "Option \"$EXCLUSIVE_ARGV\" not valid with other options." >&2
		exit 1
	fi

	if test "$SETUP" = "yes" -a "$DO_SETUP" != "yes"; then
		echo "No options specified for --setup." >&2
		exit 1
	fi

	if test "$VERBOSE" = "yes"; then
		if test "$START" != "yes" -a "$START_DAEMON" != "yes"; then
			echo "Option --verbose may only be used with --start or --start-daemon" >&2
			exit 1
		fi
	fi

	if test "$DO_SETUP" = "yes"; then
		SETUP="$DO_SETUP"
	fi

	if test "$EXCLUSIVE_ARGC" -eq 1 -a "$SETUP" = "yes"; then
		if test "$EXCLUSIVE_ARGV" != "--start-daemon" -a "$EXCLUSIVE_ARGV" != "--start"; then
			echo "Option \"--setup\" not valid with \"$EXCLUSIVE_ARGV\"." >&2
			exit 1
		fi
	fi
}


# stop any existing daemon
do_stop()
{
	if test ! -f "$LOCK_FILE"; then
		echo "Daemon not running" >&2
		return
	fi

	kill -s 0 `cat $LOCK_FILE` 2>/dev/null
	if test "$?" -ne 0; then
		echo "Detected stale lock file. Removing." >&2
		rm -f "$LOCK_FILE"
		return
	fi

	if test $KERNEL_SUPPORT = "yes"; then
		echo "Stopping profiling."
		echo 0 >/dev/oprofile/enable
	fi
	kill -s USR2 `cat $LOCK_FILE` 2>/dev/null
}


# kill the daemon process(es)
do_kill_daemon()
{
	if test ! -f "$LOCK_FILE"; then
		# no error message, do_kill_deamon imply stop and stop already
		# output "Daemon not running"
		return
	fi

	kill -s 0 `cat $LOCK_FILE` 2>/dev/null
	if test "$?" -ne 0; then
		echo "Detected stale lock file. Removing." >&2
		rm -f "$LOCK_FILE"
		return
	fi

	echo "Killing daemon."

	if test $KERNEL_SUPPORT = "yes"; then
		kill -TERM `cat $LOCK_FILE`
	else
		echo 1 >/proc/sys/dev/oprofile/dump_stop
	fi

	COUNT=0
	while test -n "`pidof oprofiled`"
	do
		sleep 1 

		# because oprofiled only sets a variable inside the
		# signal handler itself, it's possible to miss a
		# signal just before it goes to sleep waiting for
		# data from the kernel that never arrives. So we
		# remind it it needs to die - this works because
		# the signal will bring oprofiled out of the kernel
		# back into userspace
		if test $KERNEL_SUPPORT = "yes"; then
			pid=`cat $LOCK_FILE 2>/dev/null`
			kill -TERM "$pid" 2>/dev/null
		fi

		COUNT=`expr $COUNT + 1`
		if test "$COUNT" -eq 15; then
			echo "Daemon stuck shutting down; killing !"
			kill -9 `cat $LOCK_FILE`
		fi
	done

	# already removed unless we forced the kill
	rm -f /var/lib/oprofile/lock
}


rm_devices_24()
{
	rm_device "$DEVICE_FILE"
	rm_device "$NOTE_DEVICE_FILE"
	rm_device "$HASH_MAP_DEVICE_FILE"
}


create_devices_24() 
{
	MAJOR_NR=`grep oprof /proc/devices | awk '{print $1}'`

	create_device $DEVICE_FILE $MAJOR_NR 0
	create_device $NOTE_DEVICE_FILE $MAJOR_NR 2
	create_device $HASH_MAP_DEVICE_FILE $MAJOR_NR 1
}


# setup and start module
do_setup()
{
	create_dir "$DIR"

	>$LOG_FILE

	if test "$KERNEL_SUPPORT" != "yes"; then
		rm_devices_24
		create_devices_24
	fi

	create_dir "$CURRENT_SAMPLES_DIR"
}


# set a sysctl/oprofilefs parameter
set_param()
{
	if test "$KERNEL_SUPPORT" = "yes"; then
		echo $2 >$MOUNT/$1
	else
		$SYSCTL -w dev.oprofile.$1=$2
	fi
}


# set a sysctl/oprofilefs counter parameter
set_ctr_param()
{
	# no such thing for perfmon
	if test "$IS_PERFMON" = "yes"; then
		return
	fi

	if test "$KERNEL_SUPPORT" = "yes"; then
		echo $3 >$MOUNT/$1/$2
	else
		$SYSCTL -w dev.oprofile.$1.$2=$3
	fi
}


do_param_setup()
{
	# different names
	if test $BUF_SIZE != 0; then
		if test "$KERNEL_SUPPORT" = "yes"; then
			echo $BUF_SIZE >$MOUNT/buffer_size
		else
			$SYSCTL -w dev.oprofile.bufsize=$BUF_SIZE
		fi
	fi

	if test $NOTE_SIZE != 0; then
		set_param notesize $NOTE_SIZE
	fi

	if test "$KERNEL_SUPPORT" = "yes" -a -f $MOUNT/backtrace_depth; then
		set_param backtrace_depth $CALLGRAPH
	elif test "$CALLGRAPH" != "0"; then
		echo "CALLGRAPH not supported with this kernel"
	fi

	if test "$IS_TIMER" = 1; then
		return
	fi

	# use the default setup if none set
	if test "$NR_CHOSEN" = 0; then
		CHOSEN_EVENTS[0]=$DEFAULT_EVENT
		NR_CHOSEN=1
		HW_CTRS=`$OP_HELP --check-events $DEFAULT_EVENT`
		echo "Using default event: ${CHOSEN_EVENTS[0]}"
	fi

	# Necessary in this case :
	# opcontrol ctr0-on ctr1-on then opcontrol ctr0-on
	for f in $OP_COUNTERS ; do
		set_ctr_param $f enabled 0
		set_ctr_param $f event 0
		set_ctr_param $f count 0
	done

	verify_counters

	OPROFILED_EVENTS=
	for f in `seq 0 $((NR_CHOSEN - 1))`; do
		if test "${CHOSEN_EVENTS[$f]}" != ""; then
			EVENT=`echo ${CHOSEN_EVENTS[$f]} | awk -F: '{print $1}'`
			EVENT_VAL=`$OP_HELP $EVENT`
			COUNT=`echo ${CHOSEN_EVENTS[$f]} | awk -F: '{print $2}'`
			UNIT_MASK=`echo ${CHOSEN_EVENTS[$f]} | awk -F: '{print $3}'`
			KERNEL=`echo ${CHOSEN_EVENTS[$f]} | awk -F: '{print $4}'`
			USER=`echo ${CHOSEN_EVENTS[$f]} | awk -F: '{print $5}'`
			CTR=`echo $HW_CTRS | awk "{print \\$$((f + 1))}"`
			if test -z "$UNIT_MASK"; then
				TMPEVENT="$EVENT:$COUNT"
				UNIT_MASK=`$OP_HELP --unit-mask $TMPEVENT`
			fi
			if test -z "$KERNEL"; then
				KERNEL=1
			fi
			if test -z "$USER"; then
				USER=1
			fi
			if test "$EVENT" = "RTC_INTERRUPTS"; then
				set_param rtc_value $COUNT
				$SYSCTL -w dev.oprofile.rtc_value=$COUNT
			else
				set_ctr_param $CTR enabled 1
				set_ctr_param $CTR event $EVENT_VAL
				set_ctr_param $CTR count $COUNT
				set_ctr_param $CTR kernel $KERNEL
				set_ctr_param $CTR user $USER
				set_ctr_param $CTR unit_mask $UNIT_MASK
			fi
			OPROFILED_EVENTS=${OPROFILED_EVENTS}$EVENT:$EVENT_VAL:
			OPROFILED_EVENTS=${OPROFILED_EVENTS}$CTR:$COUNT:$UNIT_MASK:
			OPROFILED_EVENTS=${OPROFILED_EVENTS}$KERNEL:$USER,
		fi
	done
}


do_start_daemon()
{
 
	if test -f "$LOCK_FILE"; then
		kill -s 0 `cat $LOCK_FILE` 2>/dev/null
		if test "$?" -eq 0; then
			return;
		else
			echo "Detected stale lock file. Removing." >&2
			rm -f "$LOCK_FILE"
		fi
	fi
 
	do_setup
	do_load_setup
	check_valid_args
	get_kernel_range
	do_param_setup

	OPD_ARGS=" \
		--separate-lib=$SEPARATE_LIB \
		--separate-kernel=$SEPARATE_KERNEL \
		--separate-thread=$SEPARATE_THREAD \
		--separate-cpu=$SEPARATE_CPU"

	OPD_ARGS="$OPD_ARGS --events=$OPROFILED_EVENTS"

	if test "$VMLINUX" = "none"; then
		OPD_ARGS="$OPD_ARGS --no-vmlinux"
	else
		OPD_ARGS="$OPD_ARGS --vmlinux=$VMLINUX --kernel-range=$KERNEL_RANGE"
	fi

	if ! test -z "$IMAGE_FILTER"; then
		OPD_ARGS="$OPD_ARGS --image=$IMAGE_FILTER"
	fi

	if test "$VERBOSE" = "yes"; then
		OPD_ARGS="$OPD_ARGS --verbose"
	fi

	vecho "executing oprofiled $OPD_ARGS"

	$OPROFILED $OPD_ARGS
 
	COUNT=0
	while ! test -f "$DIR/lock"
	do
		sleep 1
		COUNT=`expr $COUNT + 1`
		if test "$COUNT" -eq 10; then
			echo "Couldn't start oprofiled." >&2
			echo "Check the log file \"$LOG_FILE\" and kernel syslog" >&2
			exit 1
		fi
	done

	echo "Daemon started."
}


do_start()
{
	if test "$KERNEL_SUPPORT" = "yes"; then
		echo 1 >$MOUNT/enable
	fi
	kill -s USR1 `cat $LOCK_FILE` 2>/dev/null
	echo "Profiler running."
}


# do_dump
# returns 0 if successful
# returns 1 if the daemon is not running
do_dump()
{
	# make sure that the daemon is running
	if test -e "$DIR/lock"; then
		OPROFILED_PID=`cat $DIR/lock`
		if test ! -d "/proc/$OPROFILED_PID"; then
			return 1;
		fi
	else
		return 1;
	fi

	if test "$KERNEL_SUPPORT" = "yes"; then
		# find current time
		TMPFILE=`mktemp /tmp/oprofile.XXXXXX` || exit 1
		echo 1 > $MOUNT/dump
		# loop until there is a file to check
		while [ ! -e "$DIR/complete_dump" ]
		do
			sleep 1;
		done
		# loop until modification data of $MOUNT/dump after TMPFILE
		while [ "$TMPFILE" -nt "$DIR/complete_dump" ]
		do
			sleep 1;
		done
		rm $TMPFILE
	else
		echo 1 > $MOUNT/dump
		# HACK !
		sleep 2
	fi
	return 0;
}


# tell daemon to re-open the sample files
hup_daemon()
{
	if test -f "$LOCK_FILE"; then
		echo -n "Signalling daemon... "
		kill -HUP `cat $LOCK_FILE` 
		echo "done"
	fi
}
 

# move all the sample files to a sample directory
do_save_session()
{
	SAVE_DIR="${SAMPLES_DIR}/${SAVE_NAME}"

	if test -e "$SAVE_DIR"; then
		echo "session $SAVE_DIR already exists" >&2
		exit 1
	fi

	if ! test -e $CURRENT_SAMPLES_DIR; then
		echo "$CURRENT_SAMPLES_DIR doesn't exist: nothing to save" >&2
		exit 0
	fi

	# FIXME: I don't think it's worth checking for empty current directory

	mv $CURRENT_SAMPLES_DIR $SAVE_DIR
	if test "$?" != "0"; then
		echo "Couldn't move $CURRENT_SAMPLES_DIR to $SAVE_DIR" >&2
		exit 1
	fi

	hup_daemon
}


# remove all the sample files
do_reset()
{
	if test -z "$SAMPLES_DIR"; then
		echo "opcontrol:do_reset() SAMPLES_DIR is empty!"
		exit 1;
	fi

	# daemon use {kern} and {root} subdir, it's not a typo to not use ${}
	move_and_remove $SAMPLES_DIR/current/{kern}
	move_and_remove $SAMPLES_DIR/current/{root}

	hup_daemon
}


do_deinit()
{
	# unmount /dev/oprofile if it is mounted
	OPROF_FS=`grep /dev/oprofile /etc/mtab`
	if test -n "$OPROF_FS"; then
		umount /dev/oprofile
	fi
	# unload the oprofile module if it is around
	OPROF_MOD=`lsmod | grep oprofile`
	if test -n "$OPROF_MOD"; then
		echo "Unloading oprofile module" >& 2
		rmmod oprofile
	fi
}


# The function that calls the appropriate operations
do_operations()
{
	# INIT always done by load_module to get access to cputype
	# thus INIT is a noop

	if test "$SETUP" = "yes"; then
		check_valid_args
		do_save_setup
	fi

	if test "$START_DAEMON" = "yes"; then
		do_start_daemon
	fi

	if test "$START" = "yes"; then
		do_start_daemon
		do_start
	fi

	if test "$DUMP" = "yes"; then
		do_dump
		if test $? -ne 0 -a "$ONLY_DUMP" = "yes"; then
			echo "No daemon running" >& 2
			exit 1;
		fi
	fi

	if test "$SAVE_SESSION" = "yes"; then
		do_save_session
	fi

	if test "$STOP" = "yes"; then
		do_stop
	fi

	if test "$KILL_DAEMON" = "yes"; then
		do_kill_daemon
	fi

	if test "$RESET" = "yes"; then
		do_reset
	fi

	if test "$DEINIT" = "yes"; then
		do_deinit
	fi
}
 
# early check for --version, --help
check_version_help()
{

	OP_HELP="$OPDIR/op_help"

	for i in $@; do
		case "$i" in
			-\?|--help)
				do_help
				exit 0
				;;

			-v|--version)
				echo -n "`basename $0`: "
				$OP_HELP --version | cut -d' ' -f2-
				exit 0
				;;
 
		esac
	done
}


# main

# determine the location of opcontrol and related programs
OPCONTROL=`which $0`
OPDIR=`dirname $OPCONTROL`

PATH=/usr/local/bin:/usr/local/sbin:/bin:/sbin:/usr/bin:/usr/sbin

check_version_help $@

if test -z "$1"; then
	do_help
	exit 0
fi

if test "$UID" != "0"; then
	echo "Must be root to use oprofile." >&2
	exit 1
fi

load_module
do_init
do_options $@
do_operations