blob: cbb4226c186eda961bb3029bf02fba6bca7c52dc [file] [log] [blame]
#!/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 -n "$VERBOSE"; then
echo $@
fi
}
# 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)
--status show configuration
--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[=all,sfile,arcs,samples,module,misc] 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 profiling.
-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
--cpu-buffer-size=num per-cpu buffer size in units (2.6 only)
--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
}
# setup variables related to daemon
do_init_daemon_vars()
{
# 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
}
# pick the appropriate locations device for oprofile based on kernel
decide_oprofile_device()
{
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
}
# initialise parameters
do_init()
{
# for these three buffer size == 0 means use the default value
# hard-coded in op_user.h
BUF_SIZE=0
CPU_BUF_SIZE=0
NOTE_SIZE=0
VMLINUX=
VERBOSE=""
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"
CPUTYPE=`cat $MOUNT/cpu_type`
OP_COUNTERS=`ls $MOUNT/ | grep "^[0-9]\+\$" | tr "\n" " "`
NR_CHOSEN=0
DEFAULT_EVENT=`$OPHELP --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 "CPU_BUF_SIZE=$CPU_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;
if test "$KERNEL_SUPPORT" = "yes"; then
if test "$CPU_BUF_SIZE" != "0"; then
vecho "CPU_BUF_SIZE $CPU_BUF_SIZE"
else
vecho "CPU_BUF_SIZE default value"
fi;
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
# start at the start of .text and then continue to the end
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
lib|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
OPHELP_ARGS=
if test "$NR_CHOSEN" != 0; then
for f in `seq 0 $((NR_CHOSEN - 1))`; do
if test "${CHOSEN_EVENTS[$f]}" != ""; then
OPHELP_ARGS="$OPHELP_ARGS ${CHOSEN_EVENTS[$f]}"
fi
done
HW_CTRS=`$OPHELP --check-events $OPHELP_ARGS --callgraph=$CALLGRAPH`
if test "$?" != 0; then
exit 1
fi
fi
}
# setup any needed default value in chosen events
normalise_events()
{
if test "$NR_CHOSEN" -le 0; then
return
fi
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=`$OPHELP $EVENT`
if test "$?" != 0; then
exit 1
fi
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}'`
if test -z "$UNIT_MASK"; then
TMPEVENT="$EVENT:$COUNT"
UNIT_MASK=`$OPHELP --unit-mask $TMPEVENT`
if test "$?" != 0; then
exit 1
fi
fi
if test -z "$KERNEL"; then
KERNEL=1
fi
if test -z "$USER"; then
USER=1
fi
CHOSEN_EVENTS[$f]=$EVENT:$COUNT:$UNIT_MASK:$KERNEL:$USER
fi
done
}
# 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"
;;
--status)
STATUS=yes
;;
--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
;;
--cpu-buffer-size)
if test "$KERNEL_SUPPORT" != "yes"; then
echo "$arg unsupported for this kernel version"
exit 1
fi
error_if_empty $arg $val
CPU_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
if test ! -f $MOUNT/backtrace_depth; then
echo "Call-graph profiling unsupported on this kernel/hardware" >&2
exit 1
fi
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\" meaningless on this kernel" >&2
exit 1
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)
if test -z "$val"; then
VERBOSE="all"
else
VERBOSE=$val
fi
;;
-l|--list-events)
EXCLUSIVE_ARGC=`expr $EXCLUSIVE_ARGC + 1`
EXCLUSIVE_ARGV="$arg"
exec $OPHELP
;;
*)
echo "Unknown option \"$arg\". See opcontrol --help" >&2
exit 1
;;
esac
done
normalise_events
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 -n "$VERBOSE"; 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
}
# If EVENT_STR has more than one token in it (i.e., the event ID),
# this implies some additional information is present that is associated
# with the event. For example, for PPC64 architectures, the values
# required to program the MMCRs for the given event are returned along
# with the event. This function will perform any needed validation
# of such event mapping data.
check_event_mapping_data()
{
if [ "$CPUTYPE" = "ppc64/power4" -o "$CPUTYPE" = "ppc64/power5" -o "$CPUTYPE" = "ppc64/970" ]; then
MMCR0=`echo $EVENT_STR | awk '{print $2}'`
MMCR1=`echo $EVENT_STR | awk '{print $3}'`
MMCRA=`echo $EVENT_STR | awk '{print $4}'`
MMCR0_VAL=`echo $MMCR0 | awk -F: '{print $2}'`
MMCR1_VAL=`echo $MMCR1 | awk -F: '{print $2}'`
MMCRA_VAL=`echo $MMCRA | awk -F: '{print $2}'`
## mmcr0, mmcr1, mmcra are for all ppc64 counters
# Save first event mmcr settings to compare with additional
# events. All events must have the same mmcrx values i.e. be in
# the same group. Only one event is assigned per counter,
# hence there will not be a conflict on the counters
if [ "$MMCR0_CK_VAL" = "" ] ; then
MMCR0_CK_VAL=$MMCR0_VAL
MMCR1_CK_VAL=$MMCR1_VAL
MMCRA_CK_VAL=$MMCRA_VAL
else
# make sure all events are from the same group
if test $MMCR0_CK_VAL != $MMCR0_VAL \
-o $MMCR1_CK_VAL != $MMCR1_VAL \
-o $MMCRA_CK_VAL != $MMCRA_VAL ; then
echo "ERROR: The specified events are not from the same group."
echo " Use 'opcontrol --list-events' to see event groupings."
exit 1
fi
fi
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 $CPU_BUF_SIZE != 0; then
if test "$KERNEL_SUPPORT" = "yes"; then
echo $CPU_BUF_SIZE >$MOUNT/cpu_buffer_size
else
echo "cpu-buffer-size not supported - ignored" >&2
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 "Call-graph profiling not supported - ignored" >&2
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=`$OPHELP --check-events $DEFAULT_EVENT --callgraph=$CALLGRAPH`
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_STR=`$OPHELP $EVENT`
# For some architectures, additional information associated with given
# event is returned in EVENT_STR, so we validate this information if necessary
check_event_mapping_data
EVENT_VAL=`echo $EVENT_STR | awk '{print $1}'`
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 "$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
let loop_count=1
for i in ${EVENT_STR}; do
#Skip first argument of EVENT_STR (event val) since we've already
#processed that value.
if test "$loop_count" -gt 1; then
KEY=`echo $i | awk -F: '{print $1}'`
VAL=`echo $i | awk -F: '{print $2}'`
set_ctr_param "" $KEY $VAL
fi
let loop_count="$loop_count"+1
done
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 -n "$VERBOSE"; then
OPD_ARGS="$OPD_ARGS --verbose=$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."
}
# print status
do_status()
{
OPROFILED_PID=`cat $DIR/lock 2>/dev/null`
if test -n "$OPROFILED_PID" -a -d "/proc/$OPROFILED_PID"; then
echo "Daemon running: pid $OPROFILED_PID"
else
echo "Daemon not running"
fi
if test "$NR_CHOSEN" != "0"; then
for f in `seq 0 $((NR_CHOSEN - 1))`; do
echo "Event 0: ${CHOSEN_EVENTS[$f]}"
done
fi
SEPARATE=""
if test "$SEPARATE_LIB" = "1"; then
SEPARATE="library";
fi
if test "$SEPARATE_KERNEL" = "1"; then
SEPARATE="$SEPARATE kernel";
fi
if test "$SEPARATE_THREAD" = "1"; then
SEPARATE="$SEPARATE thread";
fi
if test "$SEPARATE_CPU" = "1"; then
SEPARATE="$SEPARATE cpu";
fi
if test -z "$SEPARATE"; then
SEPARATE=none
fi
echo "Separate options: $SEPARATE"
echo "vmlinux file: $VMLINUX"
if test -z "$IMAGE_FILTER"; then
echo "Image filter: none"
else
echo "Image filter: $IMAGE_FILTER"
fi
echo "Call-graph depth: $CALLGRAPH"
if test "$BUF_SIZE" != "0"; then
echo "Buffer size: $BUF_SIZE"
fi;
if test "$KERNEL_SUPPORT" != "yes"; then
if test "$NOTE_SIZE" != "0"; then
echo "Note buffer size: $NOTE_SIZE"
fi
else
if test "$CPU_BUF_SIZE" != "0"; then
echo "CPU buffer size: $CPU_BUF_SIZE"
fi
fi
exit 0
}
# do_dump_data
# returns 0 if successful
# returns 1 if the daemon is unable to dump data
# exit 1 if we need to be root to dump
do_dump_data()
{
# make sure that the daemon is not dead and gone
if test -e "$DIR/lock"; then
OPROFILED_PID=`cat $DIR/lock`
if test ! -d "/proc/$OPROFILED_PID"; then
echo "dump fail: daemon died during last run ?" >&2
return 1;
fi
else
return 1;
fi
if test "$KERNEL_SUPPORT" = "yes"; then
# find current time
TMPFILE=`mktemp /tmp/oprofile.XXXXXX` || exit 1
if ! test -w $MOUNT/dump; then
if test "$UID" != "0"; then
echo "You must be root to dump with this kernel version"
exit 1
fi
fi
echo 1 > $MOUNT/dump
# loop until there is a file to check and
# the modification data of $MOUNT/dump after TMPFILE
while [ \( ! -e "$DIR/complete_dump" \) -o \( "$TMPFILE" -nt "$DIR/complete_dump" \) ]
do
if test ! -d "/proc/$OPROFILED_PID"; then
rm $TMPFILE
echo "dump fail: either daemon died during last run or dies during dump" >&2
return 1
fi
sleep 1;
done
rm $TMPFILE
else
echo 1 > $MOUNT/dump
# HACK !
sleep 2
fi
return 0;
}
# do_dump
# returns 0 if successful
# exits if unsuccessful
do_dump()
{
do_dump_data
if test $? -ne 0 -a "$ONLY_DUMP" = "yes"; then
echo "Unable to complete dump of oprofile data: is the oprofile daemon running?" >& 2
exit 1;
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 "$STATUS" = "yes"; then
do_status
fi
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
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()
{
OPHELP="$OPDIR/ophelp"
for i in $@; do
case "$i" in
-\?|--help)
do_help
exit 0
;;
-v|--version)
echo -n "`basename $0`: "
$OPHELP --version | cut -d' ' -f2-
exit 0
;;
esac
done
}
#determine which module is loaded
check_version()
{
OPROFILE_AVAILABLE=no
grep oprofilefs /etc/mtab >/dev/null
if test "$?" -eq 0; then
# need to have oprofilefs mounted for this to work on 2.6
KERNEL_SUPPORT=yes
OPROFILE_AVAILABLE=yes
return
fi
# need to have /proc/oprof available for this to work on 2.4
grep oprof /proc/devices >/dev/null
if test "$?" -eq 0; then
KERNEL_SUPPORT=no
OPROFILE_AVAILABLE=yes
return
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 test -z "$1"; then
do_help
exit 0
fi
if test "$UID" = "0"; then
load_module
fi
check_version
do_init_daemon_vars
decide_oprofile_device
if test "$UID" != "0"; then
if test "$1" = "--dump" -a -z "$2"; then
ONLY_DUMP=yes
do_dump
exit 0;
elif test "$1" = "--list-events" -a -z "$2"; then
exec $OPHELP
else
echo "Normal users are limited to either '--dump' or '--list-events'." >&2
exit 1
fi
fi
do_init
do_options $@
do_operations