1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
|
#!/bin/sh
#
# Bootchart logger script
# Ziga Mahkovec <ziga.mahkovec@klika.si>
#
# This script is used for data collection for the bootchart
# boot performance visualization tool (http://www.bootchart.org).
#
# To profile the boot process, bootchartd should be called instead of
# /sbin/init. Modify the kernel command line to include:
# init=/sbin/bootchartd
#
# bootchartd will then start itself in background and exec /sbin/init
# (or an alternative init process if specified using bootchart_init=)
#
# To profile a running system, run:
# $ /sbin/bootchartd start; sleep 30; /sbin/bootchartd stop
#
PATH="/sbin:/bin:/usr/sbin:/usr/bin:$PATH"
VERSION="0.8"
# Lock file
BOOTLOG_LOCK=".lock"
# Read configuration.
CONF="/etc/bootchartd.conf"
if [ -f $PWD/bootchartd.conf ]; then
. $PWD/bootchartd.conf
elif [ -f $CONF ]; then
. $CONF
else
echo "$CONF missing"
exit 1
fi
# Start the boot logger.
start()
{
# Mount the temporary file system for log file storage. If possible,
# a temporary directory is created. In most cases though (i.e. during
# boot), a tmpfs is mounted in /mnt. The mount point is immediately
# released using a lazy umount, so the script must never leave that
# directory.
LOG_DIR="$( mktemp -d /tmp/bootchart.XXXXXX 2>/dev/null )"
if [ -z "$LOG_DIR" ]; then
LOG_DIR="/mnt"
LAZY_UMOUNT="yes"
mount -n -t tmpfs -o size=$TMPFS_SIZE none "$LOG_DIR" >/dev/null 2>&1
fi
cd "$LOG_DIR"
> "$BOOTLOG_LOCK"
[ -n "$LAZY_UMOUNT" ] && umount -nfl "$LOG_DIR"
# Enable process accounting if configured
if [ "$PROCESS_ACCOUNTING" = "yes" ]; then
> kernel_pacct
accton kernel_pacct
fi
#
# Run loggers in background
#
log_output "cat /proc/stat" proc_stat.log &
# /proc/diskstats is available in 2.6 kernels
log_output "cat /proc/diskstats" proc_diskstats.log &
log_output "cat /proc/*/stat" proc_ps.log &
#log_output "ps -eww -o pid,ppid,s,pcpu,comm,cmd" ps.log &
# Uncomment this line for diskless stations
#log_output "cat /proc/net/dev" proc_netdev.log &
if [ -n "$IN_INIT" ]; then
if [ "$AUTO_STOP_LOGGER" = "yes" ]; then
# If we were called during init, wait for the boot process to end
wait_boot &
fi
elif [ "$#" -gt 0 ]; then
# If a command was passed, run it
# (used for profiling specific applications)
echo "profile.process = $( basename $1 )" >> header
$@
stop
else
echo "profile.process = (all)" >> header
fi
}
# Run the command ($1) every $SAMPLE_PERIOD seconds and append output to
# file ($2). Mostly pure bash, so we avoid creating an excessive number of
# processes (thus polluting the process tree).
log_output()
{
# Set the stop() trap.
trap trapstop USR1
local cmd="$1"
local logfile="$2"
while [ -f "$BOOTLOG_LOCK" ]; do
# Write the time (in jiffies).
read uptime < /proc/uptime
uptime=${uptime%% [0-9]*}
uptime=${uptime%.*}${uptime#*.}
echo $uptime
# Log the command output
eval $cmd 2>/dev/null
echo
sleep $SAMPLE_PERIOD
done >> "$logfile" || stop
}
# Wait for the boot process to end.
wait_boot()
{
local runlevel=$( sed -n 's/.*:\(.*\):initdefault:.*/\1/gp' /etc/inittab )
# The processes we have to wait for
local exit_proc="gdmgreeter gdm-binary kdm_greet kdm"
# early_login in FC4 starts gdm early, so fall back to mingetty
local early_login="no"
grep -q early_login /proc/cmdline && early_login="yes"
if [ "$runlevel" -eq "2" -o "$runlevel" -eq "3" -o "$early_login" = "yes" ]; then
exit_proc="mingetty agetty rungetty getty fgetty"
fi
while [ -f "$BOOTLOG_LOCK" ]; do
if [ -n "$( pidof $exit_proc )" ]; then
# Give the exit process some time to start
sleep 5
# Flush the log files
stop
return
fi
sleep 2
done;
}
trapstop()
{
stop
}
# Stop the boot logger. The lock file is removed to force the loggers in
# background to exit. Some final log files are created and then all log files
# from the tmpfs are packaged and stored in $BOOTLOG_DEST.
stop()
{
if [ ! -f "$BOOTLOG_LOCK" ]; then
echo "${0##*/} not running"
return
fi
# Prevent concurrent stop() calls
rm -f "$BOOTLOG_LOCK" || return
sleep $SAMPLE_PERIOD
sleep $SAMPLE_PERIOD
# Stop process accounting if configured
local pacct=
if [ "$PROCESS_ACCOUNTING" = "yes" ]; then
accton
pacct=kernel_pacct
fi
# Write system information
log_header
# Package log files
tar -zcf "$BOOTLOG_DEST" header $pacct *.log
if [ -z "$LAZY_UMOUNT" ]; then
rm "$LOG_DIR"/*
rmdir "$LOG_DIR"
fi
# Render the chart if configured (and the renderer is installed)
[ "$AUTO_RENDER" = "yes" -a -x /usr/bin/bootchart ] && \
/usr/bin/bootchart -o "$AUTO_RENDER_DIR" -f $AUTO_RENDER_FORMAT "$BOOTLOG_DEST"
}
# Log some basic information about the system.
log_header()
{
(
echo "version = $VERSION"
echo "title = Boot chart for $( hostname | sed q ) ($( date ))"
echo "system.uname = $( uname -srvm | sed q )"
if [ -f /etc/gentoo-release ]; then
echo "system.release = $( sed q /etc/gentoo-release )"
elif [ -f /etc/SuSE-release ]; then
echo "system.release = $( sed q /etc/SuSE-release )"
elif [ -f /etc/debian_version ]; then
echo "system.release = Debian GNU/$( uname -s ) $( cat /etc/debian_version )"
elif [ -f /etc/frugalware-release ]; then
echo "system.release = $( sed q /etc/frugalware-release )"
elif [ -f /etc/pardus-release ]; then
echo "system.release = $( sed q /etc/pardus-release )"
else
echo "system.release = $( sed 's/\\.//g;q' /etc/issue )"
fi
# Get CPU count
local cpucount=$(grep -c '^processor' /proc/cpuinfo)
if [ $cpucount -gt 1 -a -n "$(grep 'sibling.*2' /proc/cpuinfo)" ]; then
# Hyper-Threading enabled
cpucount=$(( $cpucount / 2 ))
fi
if grep -q '^model name' /proc/cpuinfo; then
echo "system.cpu = $( grep '^model name' /proc/cpuinfo | sed q )"\
"($cpucount)"
else
echo "system.cpu = $( grep '^cpu' /proc/cpuinfo | sed q )"\
"($cpucount)"
fi
echo "system.kernel.options = $( sed q /proc/cmdline )"
) >> header
}
if [ $$ -eq 1 ]; then
# Started by the kernel. Start the logger in background and exec
# init(1).
IN_INIT="yes"
echo "Starting bootchart logging"
start
# Optionally, an alternative init(1) process may be specified using
# the kernel command line (e.g. "bootchart_init=/sbin/initng")
init="${bootchart_init}"
if [ -z "$init" ]; then
init="/sbin/init"
fi
exec $init $*
fi
case "$1" in
"init")
# Started by the init script
IN_INIT="yes"
echo "Starting bootchart logging"
start &
;;
"start")
# Started by the user
shift
start $@
;;
"stop")
# We get the signal, too. But we ignore it.
trap : USR1
# Signal all background processes to stop logging
killall --exact -USR1 bootchartd
;;
*)
echo "Usage: $0 {init|start|stop}"
;;
esac
|