Files
dockerized-swg/gameserver/utils/tools/coreMemoryReport.sh
2018-01-11 21:02:00 -05:00

221 lines
5.8 KiB
Bash

#!/bin/bash -f
if [ $# != 4 ]; then
echo Usage: coreMemoryReport.sh exename corename dumpname reportname
exit 1
fi
checkTool()
{
which $1 >& /dev/null
if [ $? = 1 ]; then
echo "Could not find $1 in $PATH."
exit 1
fi
}
# check for needed tools
checkTool grep
checkTool sed
checkTool sort
checkTool uniq
checkTool gawk
checkTool objdump
checkTool addr2line
checkTool CoreMemWalker
# check gawk version, need >= 3.1 for coprocesses
awk_major=`gawk --version |grep '^GNU Awk' |sed -e '/^GNU Awk /s///' -e '/\..*$/s///'`
awk_minor=`gawk --version |grep '^GNU Awk' |sed -e '/^GNU Awk [0-9]*\./s///' -e '/\..*$/s///'`
if [ $awk_major -le 3 ]; then
if [ $awk_major -lt 3 ] || [ $awk_minor -lt 1 ]; then
echo 'GNU Awk version >= 3.1 is required.'
exit 1
fi
fi
EXE=$1
CORE=$2
DUMPNAME=$3
REPORTNAME=$4
# load the memory map for a specified object file and section type
loadMemoryMap()
{
objdump -h $1 |grep $2 >.tmpmem
ao_c=0
while read -a LINE ; do
let s=0x${LINE[3]}
let e=$s+0x${LINE[2]}
let o=0x${LINE[5]}-$s
ao_s[$ao_c]=$s
ao_e[$ao_c]=$e
ao_o[$ao_c]=$o
let ao_c=$ao_c+1
done <.tmpmem
}
# determine the offset in the file associated with the current memory map of the memory address passed in
getAddrOffset()
{
result=0
let t=$1
i=0
while [ $i -lt $ao_c ]; do
if [ $t -ge ${ao_s[$i]} ] && [ $t -lt ${ao_e[$i]} ] ; then
let result=${ao_o[$i]}+$t
break
fi
let i=$i+1
done
echo $result
}
# determine the object to search for memory manager info
MEMOBJ=`ldd $EXE |grep libsharedMemoryManager.so |sed 's/[()]//g' |gawk '{print $3}'`
if [ "$MEMOBJ" = "" ]; then
MEMOBJ=$EXE
MEMOFS=0
else
LINE=(`ldd $EXE |sed 's/[()]//g' |sort -k 4,4 |nl |gawk '{print $1, $4}' |grep libsharedMemoryManager.so`)
MEMOFS=0x`objdump -h $CORE |grep -B1 'READONLY, CODE' |grep '00000000 4' |nl |grep "^ *${LINE[0]} " |gawk '{print $5}'`
fi
# grab the mappings of memory regions in the memory manager object
echo Getting memory manager object memory map...
loadMemoryMap $MEMOBJ rodata
# determine the number of entries in the memory block call stacks
echo Determining memory block owner call stack size...
LINE=(`objdump -t $MEMOBJ |grep ' _[^G]*22MemoryManagerNamespace.*cms_allocatedBlockSize'`)
offset=`getAddrOffset 0x${LINE[0]}`
LINE=(`od -t x4 -j$offset -N4 $MEMOBJ`)
let own_size=(0x${LINE[1]}-12)/4
# grab the mappings of memory regions in the core file
echo Getting core memory map...
loadMemoryMap $CORE load
# get the address of the first memory block
echo Finding first memory block...
LINE=(`objdump -t $MEMOBJ |grep '22MemoryManagerNamespace.*ms_firstSystemAllocation'`)
let adjustedOfs=0x${LINE[0]}+$MEMOFS
offset=`getAddrOffset $adjustedOfs`
LINE=(`od -t x4 -j$offset -N4 $CORE`)
let firstAddr=0x${LINE[1]}
rm -f $DUMPNAME
echo Dumping allocated block info...
CoreMemWalker $CORE .tmpmem $firstAddr $own_size >$DUMPNAME
rm -f .tmpmem
echo Processing memory report...
cat $DUMPNAME |sed 's/^0x[0-9a-f]* //' |sort -n |uniq -c | \
gawk '
{
printf "%d %d", $1*$2, $1
for (i = 3; i <= NF; ++i)
printf " %s", $i
printf "\n"
}' | sort -n -r >.tmpdmp
rm -f .tmpresolve .tmplib*
# Determine all binaries associated with $EXE, and store their names and
# base addresses in the lib_name and lib_addr arrays respectively.
echo Determining address spaces...
lib_count=1
lib_addr[0]=0
lib_name[0]=$EXE
ldd $EXE |sed 's/[()]//g' |sort -k 4,4 |nl |gawk '{print $1, $4}' >.tmplibs
while read -a LINE ; do
lib_name[$lib_count]=${LINE[1]}
let lib_addr[$lib_count]=0x`objdump -h $CORE |grep -B1 'READONLY, CODE' |grep '00000000 4' |nl |grep "^ *${LINE[0]} " |gawk '{print $5}'`
let lib_count=$lib_count+1
done <.tmplibs
# Determine which library in the lib_name/lib_addr arrays the passed in
# address belongs to. Return the index in the array.
getLibNumber()
{
result=0
let t=$1
j=1
while [ $j -lt $lib_count ]; do
if [ ${lib_addr[$j]} -gt $t ] ; then
break
fi
let j=$j+1
done
let result=$j-1
echo $result
}
# Run through all referenced addresses, sorting them by the binary they
# belong to, and saving a file of addresses and a separate file of
# addresses relative to the binary's start address. Also fill in '??:0'
# in the resolved address cache for each address, so that every referenced
# address has an entry, and use that to prevent trying to resolve an
# address multiple times (by outputting duplicates to the per-binary
# address list files).
echo Sorting addresses by lib...
while read -a LINE ; do
# run through $LINE[2..] accumulating addresses per lib
i=2
while [ $i -lt ${#LINE[*]} ]; do
addr=${LINE[$i]}
if [ "${addr_cache[$addr]}" != "??:0" ] ; then
lib_number=`getLibNumber $addr`
let offset=addr-${lib_addr[$lib_number]}
echo $addr >> .tmplib_addr_$lib_number
echo -n "0x" >> .tmplib_ofs_$lib_number
echo "obase=16; $offset" |bc >> .tmplib_ofs_$lib_number
addr_cache[$addr]="??:0"
fi
let i=$i+1
done
done <.tmpdmp
# Resolve all addresses referenced in the report, batched by which binary
# the address belongs to.
echo Resolving Addresses...
i=0
while [ $i -lt $lib_count ]; do
if [ -e .tmplib_ofs_$i ]; then
addr2line -s -e ${lib_name[$i]} `cat .tmplib_ofs_$i` >.tmpresolve
j=0
while read -a LINE ; do
resolved[$j]=${LINE[*]}
let j=$j+1
done <.tmpresolve
j=0
while read -a LINE ; do
addr_cache[${LINE[*]}]=${resolved[$j]}
let j=$j+1
done <.tmplib_addr_$i
fi
let i=$i+1
done
# Generate the final report file, by looking up all but the first 2 fields of
# everything in .tmpdmp in the resolved address cache.
echo Generating Report...
rm -f $REPORTNAME
while read -a LINE ; do
echo -n "${LINE[0]} ${LINE[1]}" >> $REPORTNAME
i=2
while [ $i -lt ${#LINE[*]} ]; do
addr=${LINE[$i]}
echo -n " ${addr_cache[$addr]}($addr)" >> $REPORTNAME
let i=$i+1
done
echo >> $REPORTNAME
done <.tmpdmp
rm -f .tmpdmp .tmpresolve .tmplib*
echo Done!