#!/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 [ "`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). 
#  Handle only the -w option of sysctl.
do_sysctl()
{
	if [ x$1 != x"-w" ]; then
		echo "$0 unknown 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 [ ! -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
}


# extract the integer field N from --ctr[N]-xxxxxxx
extract_int()
{
	local val=`echo $1 | sed 's,--ctr\([0-9]*\)[-A-Za-z]*,\1,'`

	if test -z "$val"; then 
		echo "Invalid option \"$1\"" >&2
		exit 1
	fi
	if [ ! -d $MOUNT/$val ]; then
		echo "invalid argument $1: bad counter number" >&2
		exit 1
	fi
	echo $val
}


# verbose echo
vecho() {
	if [ "$VERBOSE" == "0" ]; then 
		return;
	fi
	echo $@
}

 
# print help message
do_help() { 
	echo "opcontrol: usage:
	--init			loads the oprofile module and oprofilefs
	--setup			give setup arguments
	--start-daemon          start daemon without starting profiling
	--start			start data collection
	--dump			flush the collected profiling data
	--stop			stop data collection
	--shutdown		stop data collection and remove daemon
	--reset			clears out data from current session
	--save=session_name	save data from current session to session_name
	--deinit		unload the oprofile module and oprofilefs

	--setup options
	  --buffer-size=num            number of samples in kernel buffer
	  --ctrN-event=[name|none]     symbolic event name for ctr N, or none to disable
	  --ctrN-count=val             number of events between samples for ctr N
	  --ctrN-unit-mask=val         unit mask for ctr N (e.g. --ctr0-unit-mask=0xf)
	  --ctrN-kernel=[0|1]          whether to count kernel events for ctr N
	  --ctrN-user=[0|1]            whether to count user events for ctr N
	Allowed counters for N are [$OP_COUNTERS]
	  --pid-filter=[pid|none]      Only profile process pid  (2.4 version)
	  --pgrp-filter=[pgrp|none]    Only profile process tty group pgrp (2.4 version)
	  --rtc-value=val	       Set sample rate to val per second (2.4 version)
	  --kernel-only=[0|1]	       profile only the kernel (2.4 version)
	  --note-table-size	       number of notes in kernel notes buffer (2.4 version)
	  --separate=[none,library,kernel,all]         separate samples as given
	  --vmlinux=file               vmlinux kernel image
	  --no-vmlinux                 no vmlinux image available (no kernel vmlinux image available)
	  --verbose                    be verbose in the daemon log
	  --kernel-range=start,end     kernel range vma address in hexadecimal

	General options
	  --list-events                list event types and unit masks
	  --help                       this message
	  --version                    show version" >&2
}


load_module_25() {
	grep oprofilefs /proc/filesystems >/dev/null
	if [ "$?" -ne 0 ]; then
		modprobe oprofile
		if [ "$?" != "0" ]; then
			# couldn't load a module
			return
		fi
		grep oprofile /proc/modules >/dev/null
		if [ "$?" != "0" ]; then
			# didn't find module
			return
		fi
	fi
	grep oprofilefs /proc/filesystems >/dev/null
	if [ "$?" -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 [ "$?" -ne 0 ]; then
		modprobe oprofile
		if [ "$?" != "0" ]; then
			# couldn't load a module
			return
		fi
		grep oprofile /proc/modules >/dev/null
		if [ "$?" != "0" ]; then
			# didn't find module
			return
		fi
	fi
	KERNEL_SUPPORT=no
	OPROFILE_AVAILABLE=yes
} 


load_module()
{
	OPROFILE_AVAILABLE=no
	load_module_25
	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=
	PID_FILTER=0
	PGRP_FILTER=0
	VERBOSE=0
	SEPARATE_LIB_SAMPLES=0
	SEPARATE_KERNEL_SAMPLES=0
	KERNEL_ONLY=0 # 2.2/2.4 kernel

	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/"
	if test "$KERNEL_SUPPORT" = "yes"; then
		MOUNT="/dev/oprofile"
		DEVICE_FILE="$MOUNT/buffer"
	else
		MOUNT="/proc/sys/dev/oprofile"
		SAMPLES_DIR="$DIR/samples/"
		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" " "`

	CPU_SPEED=`$OP_HELP --get-cpu-frequency`
	vecho "cpu speed (estimation) : $CPU_SPEED"

	IS_TIMER=0
	DEFAULT_EVENT=
	DEFAULT_UNIT_MASK=
	INT_CPU_SPEED=`echo $CPU_SPEED | awk -F. '{print $1}'`
	# ~ 2000 ints/second when CPU is busy (hopefully)
	DEFAULT_COUNT=`expr $INT_CPU_SPEED \* 500`
 
	case "$CPUTYPE" in
		0|1|2|i386/ppro|i386/pii|i386/piii)
			DEFAULT_EVENT=CPU_CLK_UNHALTED
			;;
		timer|4|5)
			IS_TIMER=1
			;;
		3|10|i386/athlon|x86-64/hammer)
			DEFAULT_EVENT=CPU_CLK_UNHALTED
			;;
		6|11|i386/p4*|x86-64/ia32e*)
			DEFAULT_EVENT=GLOBAL_POWER_EVENTS
			DEFAULT_UNIT_MASK=1
			;;
		7|8|9|ia64*)
			DEFAULT_EVENT=CPU_CYCLES
			;;
		12|13|14|15|16|alpha/*)
			DEFAULT_EVENT=CYCLES
			;;
		*)
			echo "Unknown CPU type $CPUTYPE, please fix me !" >&2
			exit 1
			;;
	esac

	if test "$IS_TIMER" -ne 1; then
		# we can now default define individual counter setup variable.
		for f in $OP_COUNTERS ; do
			CTR_USER[$f]=1
			CTR_KERNEL[$f]=1
			CTR_UM[$f]=0
		done
	fi
}


function create_dir {
	if [ ! -d "$1" ]; then
	       mkdir -p "$1"
	       if [ "$?" != "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 "$IS_TIMER" -ne 1; then
		for f in $OP_COUNTERS ; do
			echo "CTR_EVENT[${f}]=${CTR_EVENT[$f]}"  >> $SETUP_FILE
			echo "CTR_COUNT[${f}]=${CTR_COUNT[$f]}" >> $SETUP_FILE
			echo "CTR_KERNEL[${f}]=${CTR_KERNEL[$f]}" >> $SETUP_FILE
			echo "CTR_USER[${f}]=${CTR_USER[$f]}" >> $SETUP_FILE
			echo "CTR_UM[${f}]=${CTR_UM[$f]}" >> $SETUP_FILE
			echo "CTR_EVENT_VAL[${f}]=${CTR_EVENT_VAL[$f]}"  >> $SETUP_FILE
		done
	else
		if test "$KERNEL_SUPPORT" != "yes"; then
			echo "RTC_VALUE=$RTC_VALUE" >> $SETUP_FILE
		fi
	fi
 
	echo "one_enabled=$one_enabled" >> $SETUP_FILE
	echo "SEPARATE_LIB_SAMPLES=$SEPARATE_LIB_SAMPLES" >> $SETUP_FILE
	echo "SEPARATE_KERNEL_SAMPLES=$SEPARATE_KERNEL_SAMPLES" >> $SETUP_FILE
	echo "VMLINUX=$VMLINUX" >> $SETUP_FILE
	#write the actual information to file
	if [ "$BUF_SIZE" != 0 ]; then
		echo "BUF_SIZE=$BUF_SIZE" >> $SETUP_FILE
	fi;
	if test "$KERNEL_SUPPORT" != "yes"; then
		echo "KERNEL_ONLY=$KERNEL_ONLY" >> $SETUP_FILE
		echo "NOTE_SIZE=$NOTE_SIZE" >> $SETUP_FILE
		echo "PID_FILTER=$PID_FILTER" >> $SETUP_FILE
		echo "PGRP_FILTER=$PGRP_FILTER" >> $SETUP_FILE
	fi
}


#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

	if test "$one_enabled" != "1"; then
		if test -n "$DEFAULT_EVENT"; then
			one_enabled=1
			CTR_EVENT[0]=$DEFAULT_EVENT
			CTR_EVENT_VAL[0]=`$OP_HELP $DEFAULT_EVENT`
		fi
		if test -n "$DEFAULT_UNIT_MASK"; then
			CTR_UM[0]=$DEFAULT_UNIT_MASK
		fi
		if test -n "$DEFAULT_COUNT"; then
			CTR_COUNT[0]=$DEFAULT_COUNT
		fi
		do_save_setup
	fi

	vecho "Parameters used:"
	vecho "CPUTYPE $CPUTYPE"
	if [ $BUF_SIZE != 0 ]; then
		vecho "BUF_SIZE $BUF_SIZE"
	else
		vecho "BUF_SIZE default value"
	fi;
	if test "$IS_TIMER" -ne 1; then
		for f in $OP_COUNTERS ; do
			vecho "CTR_EVENT[${f}] ${CTR_EVENT[$f]}"
			vecho "CTR_COUNT[${f}] ${CTR_COUNT[$f]}"
			vecho "CTR_UM[${f}] ${CTR_UM[$f]}"
			vecho "CTR_USER[${f}] ${CTR_USER[$f]}"
			vecho "CTR_KERNEL[${f}] ${CTR_KERNEL[$f]}"
		done
	else
		vecho "RTC_VALUE $RTC_VALUE"
	fi

	vecho "KERNEL_ONLY $KERNEL_ONLY"
	vecho "SEPARATE_LIB_SAMPLES $SEPARATE_LIB_SAMPLES"
	vecho "SEPARATE_KERNEL_SAMPLES $SEPARATE_KERNEL_SAMPLES"
	vecho "VMLINUX $VMLINUX"
}


check_valid_args() {
	if [ "$one_enabled" = "0" ]; then
		echo "You must enable at least one event with --setup" >&2
		exit 1
	fi

	if [ -z "$VMLINUX" ]; then
		echo "No vmlinux file specified. You must specify the correct vmlinux file, e.g." >&2
		echo "opcontrol --setup --vmlinux=/path/to/vmlinux" >&2
		echo "If you do not have a vmlinux file, use " >&2
		echo "opcontrol --setup --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 | 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 | grep " .text " | awk  '{print $4}'`

	if test -z "$tmp1" -o -z "$tmp2"; then
		echo "Couldn't determine kernel start/end" >&2
		echo "Perhaps $VMLINUX is not a proper vmlinux file ?" >&2
		echo "found start as \"$tmp1\", end as \"$tmp2\"" >&2
		exit 1
	fi
	KERNEL_RANGE="`echo $tmp1`,`echo $tmp2`"
	vecho "KERNEL_RANGE $KERNEL_RANGE"
}
 
 
# 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
}


# 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
	while (($i < $#)); do
		shift
		case "$1" in
			library)
				SEPARATE_LIB_SAMPLES=1
				SEPARATE_KERNEL_SAMPLES=0
				;;
			kernel)
				# first implied by second
				SEPARATE_LIB_SAMPLES=1 
				SEPARATE_KERNEL_SAMPLES=1
				;;
			all)
				SEPARATE_LIB_SAMPLES=1
				SEPARATE_KERNEL_SAMPLES=1
				;;
			none)
				SEPARATE_LIB_SAMPLES=0
				SEPARATE_KERNEL_SAMPLES=0
				;;
			*)
				echo "invalid --separate= argument: $1"
				exit 1
		esac
	done
}


# 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
				;;

			--version)
				$OP_HELP --version | cut -d' ' -f2-
				exit 0
				;;
 
		esac
	done
}


# get and check specified options
do_options() {

	EXCLUSIVE_ARGC=0
	SETUP=no
	NEED_SETUP=no

	# load any default settings
	do_load_setup

	while [ "$#" -ne 0 ]
	do
		arg=`echo $1 | awk -F= '{print $1}'`
		val=`echo $1 | awk -F= '{print $2}'`
 
		if test "$IS_TIMER" -eq 1; then
			case "$arg" in
				--ctr*-unit-mask|--ctr*-event|--ctr*-count|--ctr*-user|--ctr*-kernel)
					echo "Cannot use option $arg in timer mode."
					exit 1;
					;;
			esac
		fi

		if test "$CPUTYPE" != "5" -a "$arg" = "--rtc-value"; then
			echo "Cannot use --rtc-value; RTC driver is not being used" >& 2
			exit 1
		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"
				;;

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

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

			--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"
				;;

			--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
				;;
			--ctr*-unit-mask)
				CTR_UM[`extract_int $arg`]=$val
				if (($? != 0)); then exit 1; fi
				DO_SETUP=yes
				;;
			--ctr*-event)
				if test "$val" = "none"; then
					CTR_EVENT[`extract_int $arg`]=
					if (($? != 0)); then exit 1; fi
					CTR_COUNT[`extract_int $arg`]=
					CTR_USER[`extract_int $arg`]=1
					CTR_KERNEL[`extract_int $arg`]=1
					CTR_UNIT_MASK[`extract_int $arg`]=0
					CTR_EVENT_VAL[`extract_int $arg`]=
					# one_enabled is fixed up below
					DO_SETUP=yes
				else
					CTR_EVENT[`extract_int $arg`]=$val
					if (($? != 0)); then exit 1; fi
					DO_SETUP=yes
				fi
				;;
			--ctr*-count)
				CTR_COUNT[`extract_int $arg`]=$val
				if (($? != 0)); then exit 1; fi
				DO_SETUP=yes
				;;
			--ctr*-user)
				CTR_USER[`extract_int $arg`]=$val
				if (($? != 0)); then exit 1; fi
				DO_SETUP=yes
				;;
			--ctr*-kernel)
				CTR_KERNEL[`extract_int $arg`]=$val
				if (($? != 0)); then exit 1; fi
				DO_SETUP=yes
				;;
			--rtc-value)
				if test "$KERNEL_SUPPORT" = "yes"; then
					echo "$arg unsupported. " >& 2
					exit 1
				fi
				RTC_VALUE=$val
				DO_SETUP=yes
				;;
			--separate)
				OLD_IFS=$IFS
				IFS=,
				validate_separate_args $arg $val
				IFS=$OLD_IFS
				DO_SETUP=yes
				;;
			--vmlinux)
				error_if_empty $arg $val
				VMLINUX=$val
				DO_SETUP=yes
				;;
			--no-vmlinux)
				VMLINUX=none
				DO_SETUP=yes
				;;
			--kernel-range)
				error_if_empty $arg $val
				KERNEL_RANGE=$val
				DO_SETUP=yes
				;;
			--pid-filter)
				if test "$KERNEL_SUPPORT" = "yes"; then
					echo "\"$arg\" ignored" >&2
				else
					if test "$val" = "none"; then
						val=0
					fi
					PID_FILTER=$val
				fi
				DO_SETUP=yes
				;;
			--pgrp-filter)
				if test "$KERNEL_SUPPORT" = "yes"; then
					echo "\"$arg\" ignored" >&2
				else
					if test "$val" = "none"; then
						val=0
					fi
					PGRP_FILTER=$val
				fi
				DO_SETUP=yes
				;;
			--kernel-only)
				if test $"KERNEL_SUPPORT" = "yes"; then
					echo "\"$arg\" ignored. " >& 2
				else
					if [ "$val" != "" ]; then
						KERNEL_ONLY=$val
					else
						KERNEL_ONLY=1
					fi
				fi
				DO_SETUP=yes
				;;
			--note-table-size)
				if test $"KERNEL_SUPPORT" = "yes"; then
					echo "\"$arg\" ignored. " >& 2
				else
					NOTE_SIZE=$val
				fi
				DO_SETUP=yes
				;;

			--verbose)
				VERBOSE=1
				;;

			--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
		shift
	done

	if test "$IS_TIMER" -ne 1; then
		one_enabled=0
		for f in $OP_COUNTERS ; do
			if [[ ${#CTR_EVENT[$f]} != 0 ]]; then
				CTR_EVENT_VAL[$f]=`$OP_HELP ${CTR_EVENT[$f]}`
				if [ "$?" != 0 ] || [ -z "${CTR_EVENT_VAL[$f]}" -a ! -z "${CTR_EVENT[$f]}" ]; then
					echo "Unknown event \"${CTR_EVENT[$f]}\"" >&2
					exit 1
				fi
				if [ -z "${CTR_COUNT[$f]}" ]; then
					echo "Event but no count specified for counter $f" >&2
					exit 1
				fi
				one_enabled=1
			else 
				if [ ! -z "${CTR_COUNT[$f]}" ]; then
					echo "Count but no event specified for counter $f" >&2
					exit 1
				fi
			fi
		done
	else
		one_enabled=1
	fi

	#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 "$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
}


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 `pidof oprofiled`
	do
		sleep 1 

		COUNT=`expr $COUNT + 1`
		if [ "$COUNT" -eq 15 ]; then
			echo "Daemon stuck shutting down; killing !"
			kill -9 `pidof oprofiled`
		fi
	done
	rm -f /var/lib/oprofile/lock
}


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


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


# 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 [ "$?" != "0" ]; then
		echo "Couldn't mknod $1" >&2
		exit 1
	fi
	chmod 700 "$1"
}


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 "$SAMPLES_DIR"
}


# initialise sysctl parameters
do_sysctl_setup_24() {
	if [ $BUF_SIZE != 0 ]; then
		$SYSCTL -w dev.oprofile.bufsize=$BUF_SIZE
	fi
	if [ $NOTE_SIZE != 0 ]; then
		$SYSCTL -w dev.oprofile.notesize=$NOTE_SIZE
	fi
	$SYSCTL -w dev.oprofile.kernel_only=$KERNEL_ONLY

	if test "$IS_TIMER" -ne 1; then
		# Necessary in this case :
		# op_start ctr0-on ctr1-on then op_start ctr0-on
		for f in $OP_COUNTERS ; do
			$SYSCTL -w dev.oprofile.$f.enabled=0 >/dev/null
			$SYSCTL -w dev.oprofile.$f.event=0 >/dev/null
		done

		for f in $OP_COUNTERS ; do
			if [ "${CTR_EVENT[$f]}" != "" ]; then
				$SYSCTL -w dev.oprofile.$f.enabled=1
				$SYSCTL -w dev.oprofile.$f.count=${CTR_COUNT[$f]}
				$SYSCTL -w dev.oprofile.$f.kernel=${CTR_KERNEL[$f]}
				$SYSCTL -w dev.oprofile.$f.user=${CTR_USER[$f]} 
				$SYSCTL -w dev.oprofile.$f.unit_mask=${CTR_UM[$f]} 
				$SYSCTL -w dev.oprofile.$f.event=${CTR_EVENT_VAL[$f]}
			fi
		done
	else
		$SYSCTL -w dev.oprofile.rtc_value=$RTC_VALUE
	fi
}


# initialise sysctl parameters
do_sysctl_setup_25() {
	if [ $BUF_SIZE != 0 ]; then
		echo $BUF_SIZE >$MOUNT/buffer_size
	fi

	if test "$IS_TIMER" -ne 1; then
		# Necessary in this case :
		# opcontrol ctr0-on ctr1-on then opcontrol ctr0-on
		for f in $OP_COUNTERS ; do
			echo 0 >$MOUNT/$f/enabled
			echo 0 >$MOUNT/$f/event
			echo 0 >$MOUNT/$f/count
		done

		for f in $OP_COUNTERS ; do
			if test "${CTR_EVENT[$f]}" != ""; then
				echo 1 >$MOUNT/$f/enabled
				echo ${CTR_COUNT[$f]} >$MOUNT/$f/count
				echo ${CTR_KERNEL[$f]} >$MOUNT/$f/kernel
				echo ${CTR_USER[$f]} >$MOUNT/$f/user
				echo ${CTR_UM[$f]} >$MOUNT/$f/unit_mask
				echo ${CTR_EVENT_VAL[$f]} >$MOUNT/$f/event
			fi
		done
	fi
}


do_sysctl_setup() {
	if test "$KERNEL_SUPPORT" = "yes"; then
		do_sysctl_setup_25
	else
		do_sysctl_setup_24
	fi
}

 
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_sysctl_setup

	OPD_ARGS=" \
		--separate-lib-samples=$SEPARATE_LIB_SAMPLES \
		--separate-kernel-samples=$SEPARATE_KERNEL_SAMPLES \
		--pid-filter=$PID_FILTER --pgrp-filter=$PGRP_FILTER"

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

	if [ "$VERBOSE" = "1" ]; 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 [ "$COUNT" -eq 30 ]; then
			echo "Couldn't start oprofiled." >&2
			echo "Check the log file \"$LOG_FILE\" and /var/log/messages" >&2
			exit 1
		fi
	done

	echo "Daemon started."
}


do_start() {
	if test "$KERNEL_SUPPORT" = "yes"; then
		echo 1 >$MOUNT/enable
	fi
	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`
		ps -p $OPROFILED_PID | grep $OPROFILED_PID > /dev/null
		if [ "$?" -ne 0 ]; 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 "Signalling daemon..."
		kill -HUP `cat $LOCK_FILE` 
	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
		
	#create directory
	mkdir -p "$SAVE_DIR"
	if [ "$?" != "0" ]; then
		echo "Couldn't mkdir -p $SAVE_DIR" >&2
		exit 1
	fi

	#move all the sample files to the directory
	SAMPLE_FILES=`ls $SAMPLES_DIR | tr ' ' '/'`
	for f in $SAMPLE_FILES; do
		fn="`echo ${f} | sed 's/\// /g'`"
		if test -f "${SAMPLES_DIR}${fn}"; then
			mv "${SAMPLES_DIR}${fn}" $SAVE_DIR
		fi
	done

	hup_daemon
}


#remove all the sample files, but leave the session directories alone
do_reset() {
	SAMPLE_FILES=`ls $SAMPLES_DIR | tr ' ' '/'`
	for f in $SAMPLE_FILES; do
		fn="`echo ${f} | sed 's/\// /g'`"
		if test -f "${SAMPLES_DIR}${fn}"; then
			rm "${SAMPLES_DIR}${fn}"
		fi
	done

	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
}
 
# 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 [ "$UID" != "0" ]; then
	echo "Must be root to use oprofile." >&2
	exit 1
fi

load_module
do_init
do_options $@
do_operations
