| #!/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 |