Poor man's profiler - proper stack folder that handles generic C++ types

This commit is contained in:
Pavel Kirienko 2015-01-17 02:25:26 +03:00 committed by Lorenz Meier
parent 1898b51c74
commit d1abf9c133
1 changed files with 113 additions and 24 deletions

View File

@ -84,7 +84,7 @@ gdberrfile=/tmp/pmpn-gdberr.log
# #
cd $root cd $root
if [[ $nsamples > 0 && "$taskname" != "" ]] if [[ $nsamples > 0 ]]
then then
[[ $append = 0 ]] && (rm -f $stacksfile; echo "Old stacks removed") [[ $append = 0 ]] && (rm -f $stacksfile; echo "Old stacks removed")
@ -92,12 +92,20 @@ then
for x in $(seq 1 $nsamples) for x in $(seq 1 $nsamples)
do do
arm-none-eabi-gdb $elf --batch -ex "set print asm-demangle on" \ if [[ "$taskname" = "" ]]
-ex "source $root/Debug/Nuttx.py" \ then
-ex "show mybt $taskname" \ arm-none-eabi-gdb $elf --batch -ex "set print asm-demangle on" -ex bt \
2> $gdberrfile \ 2> $gdberrfile \
| sed -n 's/0\.0:\(#.*\)/\1/p' \ | sed -n 's/\(#.*\)/\1/p' \
>> $stacksfile >> $stacksfile
else
arm-none-eabi-gdb $elf --batch -ex "set print asm-demangle on" \
-ex "source $root/Debug/Nuttx.py" \
-ex "show mybt $taskname" \
2> $gdberrfile \
| sed -n 's/0\.0:\(#.*\)/\1/p' \
>> $stacksfile
fi
echo -e '\n\n' >> $stacksfile echo -e '\n\n' >> $stacksfile
echo -ne "\r$x/$nsamples" echo -ne "\r$x/$nsamples"
sleep $sleeptime sleep $sleeptime
@ -106,7 +114,7 @@ then
echo echo
echo "Stacks saved to $stacksfile" echo "Stacks saved to $stacksfile"
else else
echo "Sampling skipped - set 'nsamples' and 'taskname' to re-sample." echo "Sampling skipped - set 'nsamples' to re-sample."
fi fi
# #
@ -114,22 +122,103 @@ fi
# #
[ -f $stacksfile ] || die "Where are the stack samples?" [ -f $stacksfile ] || die "Where are the stack samples?"
cat $stacksfile | perl -e 'use File::Basename; cat $stacksfile | python -c "
my $current = ""; #
my %stacks; # This stack folder correctly handles C++ types.
while(<>) { #
if(m/^#[0-9]*\s*0x[a-zA-Z0-9]*\s*in (.*) at (.*)/) {
my $x = $1 eq "None" ? basename($2) : ("$1 at " . basename($2)); from __future__ import print_function, division
if ($current eq "") { $current = $x; } import fileinput, collections, os
else { $current = $x . ";" . $current; }
} elsif(!($current eq "")) { def enforce(x, msg='Invalid input'):
$stacks{$current} += 1; if not x:
$current = ""; raise Exception(msg)
}
} def split_first_part_with_parens(line):
foreach my $k (sort { $a cmp $b } keys %stacks) { LBRACES = {'(':'()', '<':'<>', '[':'[]', '{':'{}'}
print "$k $stacks{$k}\n"; RBRACES = {')':'()', '>':'<>', ']':'[]', '}':'{}'}
}' > $foldfile braces = collections.defaultdict(int)
out = ''
for ch in line:
out += ch
# special cases
if out.endswith('operator>') or out.endswith('operator->'): # gotta love c++
braces['<>'] += 1
if out.endswith('operator<'):
braces['<>'] -= 1
# counting parens
if ch in LBRACES.keys():
braces[LBRACES[ch]] += 1
if ch in RBRACES.keys():
braces[RBRACES[ch]] -= 1
# sanity check
for v in braces.values():
enforce(v >= 0, 'Unaligned braces: ' + str(dict(braces)))
# termination condition
if ch == ' ' and sum(braces.values()) == 0:
break
out = out.strip()
return out, line[len(out):]
def parse(line):
def take_path(line, output):
line = line.strip()
if line.startswith('at '):
line = line[3:].strip()
if line:
output['file_full_path'] = line.rsplit(':', 1)[0].strip()
output['file_base_name'] = os.path.basename(output['file_full_path'])
output['line'] = int(line.rsplit(':', 1)[1])
return output
def take_args(line, output):
line = line.lstrip()
if line[0] == '(':
output['args'], line = split_first_part_with_parens(line)
return take_path(line.lstrip(), output)
def take_function(line, output):
output['function'], line = split_first_part_with_parens(line.lstrip())
return take_args(line.lstrip(), output)
def take_mem_loc(line, output):
line = line.lstrip()
if line.startswith('0x'):
end = line.find(' ')
num = line[:end]
output['memloc'] = int(num, 16)
line = line[end:].lstrip()
end = line.find(' ')
enforce(line[:end] == 'in')
line = line[end:].lstrip()
return take_function(line, output)
def take_frame_num(line, output):
line = line.lstrip()
enforce(line[0] == '#')
end = line.find(' ')
num = line[1:end]
output['frame_num'] = int(num)
return take_mem_loc(line[end:], output)
return take_frame_num(line, {})
stacks = collections.defaultdict(int)
current = ''
for line in fileinput.input():
line = line.strip()
if line:
inf = parse(line)
fun = inf['function']
current = (fun + ';' + current) if current else fun
elif current:
stacks[current] += 1
current = ''
for s, f in sorted(stacks.items(), key=lambda (s, f): s):
print(s, f)
" > $foldfile
echo "Folded stacks saved to $foldfile" echo "Folded stacks saved to $foldfile"