From adaea7d80f57dfacf6c9556d88d6eaa1aa7f8f93 Mon Sep 17 00:00:00 2001 From: summerxu <1032755103@qq.com> Date: Fri, 4 Dec 2020 20:58:21 +0800 Subject: [PATCH 1/8] Add files via upload add perl scripts --- armstack.pl | 334 ++++++++++++++++++++++++++++++++++++++++++++++++++++ avstack.pl | 251 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 585 insertions(+) create mode 100644 armstack.pl create mode 100644 avstack.pl diff --git a/armstack.pl b/armstack.pl new file mode 100644 index 0000000..eb0c30b --- /dev/null +++ b/armstack.pl @@ -0,0 +1,334 @@ +#!/usr/bin/perl -w +# avstack.pl: AVR stack checker +# Copyright (C) 2013 Daniel Beer +# +# Permission to use, copy, modify, and/or distribute this software for +# any purpose with or without fee is hereby granted, provided that the +# above copyright notice and this permission notice appear in all +# copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +# +# Usage +# ----- +# +# This script requires that you compile your code with -fstack-usage. +# This results in GCC generating a .su file for each .o file. Once you +# have these, do: +# +# ./avstack.pl +# +# This will disassemble .o files to construct a call graph, and read +# frame size information from .su. The call graph is traced to find, for +# each function: +# +# - Call height: the maximum call height of any callee, plus 1 +# (defined to be 1 for any function which has no callees). +# +# - Inherited frame: the maximum *inherited* frame of any callee, plus +# the GCC-calculated frame size of the function in question. +# +# Using these two pieces of information, we calculate a cost (estimated +# peak stack usage) for calling the function. Functions are then listed +# on stdout in decreasing order of cost. +# +# Functions which are recursive are marked with an 'R' to the left of +# them. Their cost is calculated for a single level of recursion. +# +# The peak stack usage of your entire program can usually be estimated +# as the stack cost of "main", plus the maximum stack cost of any +# interrupt handler which might execute. +# +# +# Note by summerxu +# ---------------- +# armstack.pl is partially rewritten by summerxu(). +# it's mainly based on avstack.pl and max call chains can be shown now. + +use strict; +use Data::Dumper; +use List::MoreUtils 'uniq'; + +# Configuration: set these as appropriate for your architecture/project. + +#my $objdump = "avr-objdump"; +#my $call_cost = 4; +my $objdump = "/opt/arm/gcc-arm-none-eabi-9/bin/arm-none-eabi-objdump"; +my $call_cost = 0; + +# First, we need to read all object and corresponding .su files. We're +# gathering a mapping of functions to callees and functions to frame +# sizes. We're just parsing at this stage -- callee name resolution +# comes later. + +my %frame_size; # "func@file" -> size +my %call_graph; # "func@file" -> {callees} +my %addresses; # "addr@file" -> "func@file" + +my %global_name; # "func" -> "func@file" +my %ambiguous; # "func" -> 1 + +my %call_chain; + +foreach (@ARGV) { + # Disassemble this object file to obtain a callees. Sources in the + # call graph are named "func@file". Targets in the call graph are + # named either "offset@file" or "funcname". We also keep a list of + # the addresses and names of each function we encounter. + my $objfile = $_; + my $source; + + open(DISASSEMBLY, "$objdump -dr $objfile|") || die "Can't disassemble $objfile"; + while () { + chomp; + + if (/^([0-9a-fA-F]+) <(.*)>:/) { + my $a = $1; + my $name = $2; + #printf("### address %s, function name %s\n", $a, $name); + $source = "$name\@$objfile"; + $call_graph{$source} = {}; + @{$call_chain{$source}} = []; + $ambiguous{$name} = 1 if defined($global_name{$name}); + $global_name{$name} = "$name\@$objfile"; + + $a =~ s/^0*//; + $addresses{"$a\@$objfile"} = "$name\@$objfile"; + } + + if (/: R_[A-Za-z0-9_]+_CALL[ \t]+(.*)/ || /: R_[A-Za-z0-9_]+_JUMP.*[ \t]+(.*)/) { + my $t = $1; + if ($t eq ".text") { + $t = "\@$objfile"; + } elsif ($t =~ /^\.text\+0x(.*)$/) { + $t = "$1\@$objfile"; + } + #printf("### callee %s\n", $t); + $call_graph{$source}->{$t} = 1; + #print Dumper(\%call_graph); + } + } + close(DISASSEMBLY); + + # Extract frame sizes from the corresponding .su file. + if ($objfile =~ /^(.*).o$/) { + my $sufile = "$1.su"; + + open(SUFILE, "<$sufile") || die "Can't open $sufile"; + while () { + if (/^.*:([^\t ]+)[ \t]+([0-9]+)/) { + $frame_size{"$1\@$objfile"} = $2 + $call_cost; + #printf("### function %s, stack %s\n", $1, $2); + } + } + close(SUFILE); + } +} + +# In this step, we enumerate each list of callees in the call graph and +# try to resolve the symbols. We omit ones we can't resolve, but keep a +# set of them anyway. + +my %unresolved; + +foreach (keys %call_graph) { + my $from = $_; + my $callees = $call_graph{$from}; + my %resolved; + + foreach (keys %$callees) { + my $t = $_; + + if (defined($addresses{$t})) { + $resolved{$addresses{$t}} = 1; + } elsif (defined($global_name{$t})) { + $resolved{$global_name{$t}} = 1; + warn "Ambiguous resolution: $t" if defined ($ambiguous{$t}); + } elsif (defined($call_graph{$t})) { + $resolved{$t} = 1; + } else { + $unresolved{$t} = 1; + } + } + + $call_graph{$from} = \%resolved; +} + +#print Dumper(\%call_graph); + +# Create fake edges and nodes to account for dynamic behaviour. +$call_graph{"INTERRUPT"} = {}; + +foreach (keys %call_graph) { + $call_graph{"INTERRUPT"}->{$_} = 1 if /^__vector_/; +} + +# Trace the call graph and calculate, for each function: +# +# - inherited frames: maximum inherited frame of callees, plus own +# frame size. +# - height: maximum height of callees, plus one. +# - recursion: is the function called recursively (including indirect +# recursion)? + +my %has_caller; +my %visited; +my %total_cost; +my %call_depth; +my @visited_path; + +sub trace { + my $f = shift; + #print " shift: $f\n"; + my @chain; + my $max_depth = 0; + my $max_frame = 0; + my @inner_chain; + + push @visited_path, $f; + my @path_uniq = uniq @visited_path; + if (scalar(@visited_path) != scalar(@path_uniq)) { + #print "$f Recursive!!!\n"; + $visited{$f} = "R"; + pop @visited_path; + return @chain; + } + + if ($visited{$f}) { + #print "$f visited before, return chain:\n"; + #print Dumper(\@{$call_chain{$f}}); + #print "\n\n"; + pop @visited_path; + return @{$call_chain{$f}}; + } + + my $targets = $call_graph{$f} || die "Unknown function: $f"; + + #print "outer from file $f\n"; + #print "its callees: \n"; + #print Dumper(\%$targets); + + if (defined($targets)) { + foreach (keys %$targets) { + my $t = $_; + $has_caller{$t} = 1; + @inner_chain = trace($t); + + #print "Inner from $t : "; + #print Dumper(\reverse @inner_chain); + #print "\n\n"; + + my $is = $total_cost{$t}; + my $d = $call_depth{$t}; + + if (defined $is && $is > $max_frame) { + $max_frame = $is; + @chain = @inner_chain; + } + if (defined $d && $d > $max_depth) { + $max_depth = $d; + } + } + } + $visited{$f} = "?" if ((defined $visited{$f} && $visited{$f} ne "R") || !defined $visited{$f}); + push @chain, $f; + $call_depth{$f} = $max_depth + 1; + $total_cost{$f} = $max_frame + ($frame_size{$f} || 0); + #print "add $f stack size $frame_size{$f}, total $total_cost{$f}\n"; + #print "visited path @visited_path \n\n"; + pop @visited_path; + #print "chain from $f: "; + #print Dumper(\reverse @chain); + #print "\n\n"; + @{$call_chain{$f}} = @chain; + return @chain; +} + +foreach (keys %call_graph) { + trace($_); + #print "call chain:\n"; + #print Dumper(\%call_chain); +}; + +# Now, print results in a nice table. +printf " %-42s %8s %8s %8s\n", + "Func", "Cost", "Frame", "Height"; +print "------------------------------------"; +print "------------------------------------\n"; + +my $max_iv = 0; +my $main = 0; + +#print "visited: \n"; +#print Dumper(\%visited); +foreach (keys %visited) { + $visited{$_} = " " if $visited{$_} eq "?"; +} + +foreach (sort { $total_cost{$b} <=> $total_cost{$a} } keys %visited) { + my $name = $_; + + if (/^(.*)@(.*)$/) { + $name = $1 unless $ambiguous{$name}; + } + + my $tag = $visited{$_}; + my $cost = $total_cost{$_}; + + $name = $_ if $ambiguous{$name}; + $tag = ">" unless $has_caller{$_}; + + if (/^__vector_/) { + $max_iv = $cost if $cost > $max_iv; + } elsif (/^main@/) { + $main = $cost; + } + + if ($ambiguous{$name}) { $name = $_; } + + printf "%s %-42s %8d %8d %8d\n", $tag, $name, $cost, + $frame_size{$_} || 0, $call_depth{$_}; +} + +print "\n\n"; + +#print Dumper(\%call_chain); +foreach (sort { $total_cost{$b} <=> $total_cost{$a} } keys %visited) { + my @chain = reverse @{$call_chain{$_}}; + my $level = 2; + foreach (@chain) { + my $name = $_; + if (/^(.*)@(.*)$/) { + $name = $1 unless $ambiguous{$name}; + } + if ($level == 2) { + print "## Chain from $name, Cost ($total_cost{$_})\n"; + print "------------------------------------"; + print "------------------------------------\n"; + } + print ' ' x $level, ">", "$name ($frame_size{$_})", "\n" if defined $frame_size{$_}; + $level = $level + 4; + } + print "\n"; +} + +printf "Peak execution estimate (main + worst-case IV):\n main = %d, worst IV = %d, total = %d\n", + $total_cost{$global_name{"main"}}, + $total_cost{"INTERRUPT"}, + $total_cost{$global_name{"main"}} + $total_cost{"INTERRUPT"} + if defined $global_name{"main"} && defined $total_cost{"INTERRUPT"}; + +print "\n"; + +print "The following functions were not resolved:\n"; +foreach (keys %unresolved) { print " $_\n"; } +#!/usr/bin/perl -w +# avstack.pl: AVR stack checker +# Copyright (C) 2013 Daniel Beer diff --git a/avstack.pl b/avstack.pl new file mode 100644 index 0000000..b00fce1 --- /dev/null +++ b/avstack.pl @@ -0,0 +1,251 @@ +#!/usr/bin/perl -w +# avstack.pl: AVR stack checker +# Copyright (C) 2013 Daniel Beer +# +# Permission to use, copy, modify, and/or distribute this software for +# any purpose with or without fee is hereby granted, provided that the +# above copyright notice and this permission notice appear in all +# copies. +# +# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL +# WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE +# AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL +# DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR +# PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER +# TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +# PERFORMANCE OF THIS SOFTWARE. +# +# Usage +# ----- +# +# This script requires that you compile your code with -fstack-usage. +# This results in GCC generating a .su file for each .o file. Once you +# have these, do: +# +# ./avstack.pl +# +# This will disassemble .o files to construct a call graph, and read +# frame size information from .su. The call graph is traced to find, for +# each function: +# +# - Call height: the maximum call height of any callee, plus 1 +# (defined to be 1 for any function which has no callees). +# +# - Inherited frame: the maximum *inherited* frame of any callee, plus +# the GCC-calculated frame size of the function in question. +# +# Using these two pieces of information, we calculate a cost (estimated +# peak stack usage) for calling the function. Functions are then listed +# on stdout in decreasing order of cost. +# +# Functions which are recursive are marked with an 'R' to the left of +# them. Their cost is calculated for a single level of recursion. +# +# The peak stack usage of your entire program can usually be estimated +# as the stack cost of "main", plus the maximum stack cost of any +# interrupt handler which might execute. + +use strict; + +# Configuration: set these as appropriate for your architecture/project. + +my $objdump = "avr-objdump"; +my $call_cost = 4; + +# First, we need to read all object and corresponding .su files. We're +# gathering a mapping of functions to callees and functions to frame +# sizes. We're just parsing at this stage -- callee name resolution +# comes later. + +my %frame_size; # "func@file" -> size +my %call_graph; # "func@file" -> {callees} +my %addresses; # "addr@file" -> "func@file" + +my %global_name; # "func" -> "func@file" +my %ambiguous; # "func" -> 1 + +foreach (@ARGV) { + # Disassemble this object file to obtain a callees. Sources in the + # call graph are named "func@file". Targets in the call graph are + # named either "offset@file" or "funcname". We also keep a list of + # the addresses and names of each function we encounter. + my $objfile = $_; + my $source; + + open(DISASSEMBLY, "$objdump -dr $objfile|") || + die "Can't disassemble $objfile"; + while () { + chomp; + + if (/^([0-9a-fA-F]+) <(.*)>:/) { + my $a = $1; + my $name = $2; + + $source = "$name\@$objfile"; + $call_graph{$source} = {}; + $ambiguous{$name} = 1 if defined($global_name{$name}); + $global_name{$name} = "$name\@$objfile"; + + $a =~ s/^0*//; + $addresses{"$a\@$objfile"} = "$name\@$objfile"; + } + + if (/: R_[A-Za-z0-9_]+_CALL[ \t]+(.*)/) { + my $t = $1; + + if ($t eq ".text") { + $t = "\@$objfile"; + } elsif ($t =~ /^\.text\+0x(.*)$/) { + $t = "$1\@$objfile"; + } + + $call_graph{$source}->{$t} = 1; + } + } + close(DISASSEMBLY); + + # Extract frame sizes from the corresponding .su file. + if ($objfile =~ /^(.*).o$/) { + my $sufile = "$1.su"; + + open(SUFILE, "<$sufile") || die "Can't open $sufile"; + while () { + $frame_size{"$1\@$objfile"} = $2 + $call_cost + if /^.*:([^\t ]+)[ \t]+([0-9]+)/; + } + close(SUFILE); + } +} + +# In this step, we enumerate each list of callees in the call graph and +# try to resolve the symbols. We omit ones we can't resolve, but keep a +# set of them anyway. + +my %unresolved; + +foreach (keys %call_graph) { + my $from = $_; + my $callees = $call_graph{$from}; + my %resolved; + + foreach (keys %$callees) { + my $t = $_; + + if (defined($addresses{$t})) { + $resolved{$addresses{$t}} = 1; + } elsif (defined($global_name{$t})) { + $resolved{$global_name{$t}} = 1; + warn "Ambiguous resolution: $t" if defined ($ambiguous{$t}); + } elsif (defined($call_graph{$t})) { + $resolved{$t} = 1; + } else { + $unresolved{$t} = 1; + } + } + + $call_graph{$from} = \%resolved; +} + +# Create fake edges and nodes to account for dynamic behaviour. +$call_graph{"INTERRUPT"} = {}; + +foreach (keys %call_graph) { + $call_graph{"INTERRUPT"}->{$_} = 1 if /^__vector_/; +} + +# Trace the call graph and calculate, for each function: +# +# - inherited frames: maximum inherited frame of callees, plus own +# frame size. +# - height: maximum height of callees, plus one. +# - recursion: is the function called recursively (including indirect +# recursion)? + +my %has_caller; +my %visited; +my %total_cost; +my %call_depth; + +sub trace { + my $f = shift; + + if ($visited{$f}) { + $visited{$f} = "R" if $visited{$f} eq "?"; + return; + } + + $visited{$f} = "?"; + + my $max_depth = 0; + my $max_frame = 0; + + my $targets = $call_graph{$f} || die "Unknown function: $f"; + if (defined($targets)) { + foreach (keys %$targets) { + my $t = $_; + + $has_caller{$t} = 1; + trace($t); + + my $is = $total_cost{$t}; + my $d = $call_depth{$t}; + + $max_frame = $is if $is > $max_frame; + $max_depth = $d if $d > $max_depth; + } + } + + $call_depth{$f} = $max_depth + 1; + $total_cost{$f} = $max_frame + ($frame_size{$f} || 0); + $visited{$f} = " " if $visited{$f} eq "?"; +} + +foreach (keys %call_graph) { trace $_; } + +# Now, print results in a nice table. +printf " %-30s %8s %8s %8s\n", + "Func", "Cost", "Frame", "Height"; +print "------------------------------------"; +print "------------------------------------\n"; + +my $max_iv = 0; +my $main = 0; + +foreach (sort { $total_cost{$b} <=> $total_cost{$a} } keys %visited) { + my $name = $_; + + if (/^(.*)@(.*)$/) { + $name = $1 unless $ambiguous{$name}; + } + + my $tag = $visited{$_}; + my $cost = $total_cost{$_}; + + $name = $_ if $ambiguous{$name}; + $tag = ">" unless $has_caller{$_}; + + if (/^__vector_/) { + $max_iv = $cost if $cost > $max_iv; + } elsif (/^main@/) { + $main = $cost; + } + + if ($ambiguous{$name}) { $name = $_; } + + printf "%s %-30s %8d %8d %8d\n", $tag, $name, $cost, + $frame_size{$_} || 0, $call_depth{$_}; +} + +print "\n"; + +print "Peak execution estimate (main + worst-case IV):\n"; +printf " main = %d, worst IV = %d, total = %d\n", + $total_cost{$global_name{"main"}}, + $total_cost{"INTERRUPT"}, + $total_cost{$global_name{"main"}} + $total_cost{"INTERRUPT"}; + +print "\n"; + +print "The following functions were not resolved:\n"; +foreach (keys %unresolved) { print " $_\n"; } From 040b5ab1dc783cf2393306ada3b1fc013a9d2561 Mon Sep 17 00:00:00 2001 From: summerxu <1032755103@qq.com> Date: Fri, 4 Dec 2020 21:05:44 +0800 Subject: [PATCH 2/8] Update README.md add sample output --- README.md | 314 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 313 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index b267510..99a4493 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,314 @@ # max-callstack-analysis -an easy to use max stack usage analysis script written in Perl +an easy to use max stack usage analysis script written in Perl, improved version of avstack.pl. +origin version written by Daniel Beer https://dlbeer.co.nz/downloads/avstack.pl + +# sample output + + Func Cost Frame Height +------------------------------------------------------------------------ +> pb_dec_submessage 344 40 9 +> pb_decode_delimited 336 32 9 +> pb_decode_delimited_noinit 320 32 8 + pb_decode 304 16 8 +> pb_decode_nullterminated 304 0 9 + pb_decode_noinit 288 104 7 + decode_field 184 80 6 +R pb_release_single_field 104 56 4 + initialize_pointer_field.isra.0 104 16 5 +R pb_message_set_to_defaults 88 32 4 +> pb_dec_svarint 88 24 4 +> pb_dec_fixed_length_bytes 72 32 4 + pb_decode_tag 72 32 3 +> pb_dec_varint 72 32 3 + pb_make_string_substream 64 24 4 + pb_decode_svarint 64 24 3 +> pb_dec_string 64 24 4 +> pb_dec_uvarint 64 24 3 +> pb_dec_bytes 64 24 4 + pb_field_set_to_default 56 40 3 +> pb_decode_bool 56 0 5 + pb_dec_bool 56 16 4 + pb_skip_field 56 16 4 + pb_decode_fixed32 48 16 2 + pb_decode_fixed64 48 16 2 +> pb_dec_fixed64 48 0 3 + pb_release 48 32 3 + pb_close_string_substream 48 16 2 +> pb_dec_fixed32 48 0 3 + pb_decode_varint32 40 0 3 + pb_decode_varint 40 32 2 + pb_decode_varint32_eof 40 32 2 + pb_field_iter_find 32 16 3 +R pb_read 32 32 1 + iter_from_extension 16 16 2 + pb_field_iter_next 16 16 2 + allocate_field 16 16 1 +> pb_istream_from_buffer 8 8 1 + pb_readbyte 8 8 1 +> buf_read 8 8 1 + pb_field_iter_begin 0 0 1 +> INTERRUPT 0 0 1 + + +## Chain from pb_dec_submessage, Cost (344) +------------------------------------------------------------------------ + >pb_dec_submessage (40) + >pb_decode (16) + >pb_decode_noinit (104) + >decode_field (80) + >pb_release_single_field (56) + >pb_release (32) + >pb_field_iter_next (16) + +## Chain from pb_decode_delimited, Cost (336) +------------------------------------------------------------------------ + >pb_decode_delimited (32) + >pb_decode (16) + >pb_decode_noinit (104) + >decode_field (80) + >pb_release_single_field (56) + >pb_release (32) + >pb_field_iter_next (16) + +## Chain from pb_decode_delimited_noinit, Cost (320) +------------------------------------------------------------------------ + >pb_decode_delimited_noinit (32) + >pb_decode_noinit (104) + >decode_field (80) + >pb_release_single_field (56) + >pb_release (32) + >pb_field_iter_next (16) + +## Chain from pb_decode, Cost (304) +------------------------------------------------------------------------ + >pb_decode (16) + >pb_decode_noinit (104) + >decode_field (80) + >pb_release_single_field (56) + >pb_release (32) + >pb_field_iter_next (16) + +## Chain from pb_decode_nullterminated, Cost (304) +------------------------------------------------------------------------ + >pb_decode_nullterminated (0) + >pb_decode (16) + >pb_decode_noinit (104) + >decode_field (80) + >pb_release_single_field (56) + >pb_release (32) + >pb_field_iter_next (16) + +## Chain from pb_decode_noinit, Cost (288) +------------------------------------------------------------------------ + >pb_decode_noinit (104) + >decode_field (80) + >pb_release_single_field (56) + >pb_release (32) + >pb_field_iter_next (16) + +## Chain from decode_field, Cost (184) +------------------------------------------------------------------------ + >decode_field (80) + >pb_release_single_field (56) + >pb_release (32) + >pb_field_iter_next (16) + +## Chain from pb_release_single_field, Cost (104) +------------------------------------------------------------------------ + >pb_release_single_field (56) + >pb_release (32) + >pb_field_iter_next (16) + +## Chain from initialize_pointer_field.isra.0, Cost (104) +------------------------------------------------------------------------ + >initialize_pointer_field.isra.0 (16) + >pb_message_set_to_defaults (32) + >pb_field_set_to_default (40) + >iter_from_extension (16) + +## Chain from pb_message_set_to_defaults, Cost (88) +------------------------------------------------------------------------ + >pb_message_set_to_defaults (32) + >pb_field_set_to_default (40) + >iter_from_extension (16) + +## Chain from pb_dec_svarint, Cost (88) +------------------------------------------------------------------------ + >pb_dec_svarint (24) + >pb_decode_svarint (24) + >pb_decode_varint (32) + >pb_readbyte (8) + +## Chain from pb_dec_fixed_length_bytes, Cost (72) +------------------------------------------------------------------------ + >pb_dec_fixed_length_bytes (32) + >pb_decode_varint32 (0) + >pb_decode_varint32_eof (32) + >pb_readbyte (8) + +## Chain from pb_decode_tag, Cost (72) +------------------------------------------------------------------------ + >pb_decode_tag (32) + >pb_decode_varint32_eof (32) + >pb_readbyte (8) + +## Chain from pb_dec_varint, Cost (72) +------------------------------------------------------------------------ + >pb_dec_varint (32) + >pb_decode_varint (32) + >pb_readbyte (8) + +## Chain from pb_make_string_substream, Cost (64) +------------------------------------------------------------------------ + >pb_make_string_substream (24) + >pb_decode_varint32 (0) + >pb_decode_varint32_eof (32) + >pb_readbyte (8) + +## Chain from pb_decode_svarint, Cost (64) +------------------------------------------------------------------------ + >pb_decode_svarint (24) + >pb_decode_varint (32) + >pb_readbyte (8) + +## Chain from pb_dec_string, Cost (64) +------------------------------------------------------------------------ + >pb_dec_string (24) + >pb_decode_varint32 (0) + >pb_decode_varint32_eof (32) + >pb_readbyte (8) + +## Chain from pb_dec_uvarint, Cost (64) +------------------------------------------------------------------------ + >pb_dec_uvarint (24) + >pb_decode_varint (32) + >pb_readbyte (8) + +## Chain from pb_dec_bytes, Cost (64) +------------------------------------------------------------------------ + >pb_dec_bytes (24) + >pb_decode_varint32 (0) + >pb_decode_varint32_eof (32) + >pb_readbyte (8) + +## Chain from pb_field_set_to_default, Cost (56) +------------------------------------------------------------------------ + >pb_field_set_to_default (40) + >iter_from_extension (16) + +## Chain from pb_decode_bool, Cost (56) +------------------------------------------------------------------------ + >pb_decode_bool (0) + >pb_dec_bool (16) + >pb_decode_varint32 (0) + >pb_decode_varint32_eof (32) + >pb_readbyte (8) + +## Chain from pb_dec_bool, Cost (56) +------------------------------------------------------------------------ + >pb_dec_bool (16) + >pb_decode_varint32 (0) + >pb_decode_varint32_eof (32) + >pb_readbyte (8) + +## Chain from pb_skip_field, Cost (56) +------------------------------------------------------------------------ + >pb_skip_field (16) + >pb_decode_varint32 (0) + >pb_decode_varint32_eof (32) + >pb_readbyte (8) + +## Chain from pb_decode_fixed32, Cost (48) +------------------------------------------------------------------------ + >pb_decode_fixed32 (16) + >pb_read (32) + +## Chain from pb_decode_fixed64, Cost (48) +------------------------------------------------------------------------ + >pb_decode_fixed64 (16) + >pb_read (32) + +## Chain from pb_dec_fixed64, Cost (48) +------------------------------------------------------------------------ + >pb_dec_fixed64 (0) + >pb_decode_fixed64 (16) + >pb_read (32) + +## Chain from pb_release, Cost (48) +------------------------------------------------------------------------ + >pb_release (32) + >pb_field_iter_next (16) + +## Chain from pb_close_string_substream, Cost (48) +------------------------------------------------------------------------ + >pb_close_string_substream (16) + >pb_read (32) + +## Chain from pb_dec_fixed32, Cost (48) +------------------------------------------------------------------------ + >pb_dec_fixed32 (0) + >pb_decode_fixed32 (16) + >pb_read (32) + +## Chain from pb_decode_varint32, Cost (40) +------------------------------------------------------------------------ + >pb_decode_varint32 (0) + >pb_decode_varint32_eof (32) + >pb_readbyte (8) + +## Chain from pb_decode_varint, Cost (40) +------------------------------------------------------------------------ + >pb_decode_varint (32) + >pb_readbyte (8) + +## Chain from pb_decode_varint32_eof, Cost (40) +------------------------------------------------------------------------ + >pb_decode_varint32_eof (32) + >pb_readbyte (8) + +## Chain from pb_field_iter_find, Cost (32) +------------------------------------------------------------------------ + >pb_field_iter_find (16) + >pb_field_iter_next (16) + +## Chain from pb_read, Cost (32) +------------------------------------------------------------------------ + >pb_read (32) + +## Chain from iter_from_extension, Cost (16) +------------------------------------------------------------------------ + >iter_from_extension (16) + +## Chain from pb_field_iter_next, Cost (16) +------------------------------------------------------------------------ + >pb_field_iter_next (16) + +## Chain from allocate_field, Cost (16) +------------------------------------------------------------------------ + >allocate_field (16) + +## Chain from pb_istream_from_buffer, Cost (8) +------------------------------------------------------------------------ + >pb_istream_from_buffer (8) + +## Chain from pb_readbyte, Cost (8) +------------------------------------------------------------------------ + >pb_readbyte (8) + +## Chain from buf_read, Cost (8) +------------------------------------------------------------------------ + >buf_read (8) + +## Chain from pb_field_iter_begin, Cost (0) +------------------------------------------------------------------------ + >pb_field_iter_begin (0) + +## Chain from INTERRUPT, Cost (0) +------------------------------------------------------------------------ + + +The following functions were not resolved: + memcpy + realloc + free + memset From 8d7bdb6df4a3891200ff0a9fce52c30d8d55eacf Mon Sep 17 00:00:00 2001 From: summerxu <1032755103@qq.com> Date: Fri, 4 Dec 2020 21:06:30 +0800 Subject: [PATCH 3/8] Update README.md minor change --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 99a4493..f34290f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # max-callstack-analysis an easy to use max stack usage analysis script written in Perl, improved version of avstack.pl. -origin version written by Daniel Beer https://dlbeer.co.nz/downloads/avstack.pl +origin version written by Daniel Beer ##dlbeer@gmail.com https://dlbeer.co.nz/downloads/avstack.pl # sample output From d8c33cab65d04f863d0163d31ce70129dc7a007f Mon Sep 17 00:00:00 2001 From: summerxu <1032755103@qq.com> Date: Fri, 4 Dec 2020 21:08:29 +0800 Subject: [PATCH 4/8] Update README.md format minor change --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f34290f..464b3ad 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # max-callstack-analysis -an easy to use max stack usage analysis script written in Perl, improved version of avstack.pl. -origin version written by Daniel Beer ##dlbeer@gmail.com https://dlbeer.co.nz/downloads/avstack.pl +an easy to use max stack usage analysis script written in Perl, an improved version of avstack.pl. + +origin version written by Daniel Beer #dlbeer@gmail.com: https://dlbeer.co.nz/downloads/avstack.pl # sample output From ebcc1194be235df081366194bb7e1fa44b3a9411 Mon Sep 17 00:00:00 2001 From: summerxu <1032755103@qq.com> Date: Fri, 4 Dec 2020 21:22:52 +0800 Subject: [PATCH 5/8] Update README.md format --- README.md | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 464b3ad..130f137 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,19 @@ -# max-callstack-analysis -an easy to use max stack usage analysis script written in Perl, an improved version of avstack.pl. +# maximum call stack analysis -origin version written by Daniel Beer #dlbeer@gmail.com: https://dlbeer.co.nz/downloads/avstack.pl +an easy to use maximum stack usage analysis script written in Perl, an improved version of avstack.pl. -# sample output +origin version written by Daniel Beer https://dlbeer.co.nz/downloads/avstack.pl + +## improved features + +- show maximum **call stack chains** in readable format +- more **accurate** stack size +- add parsing of relocation R_ARM_THM_JUMP* + +## sample output + +``` +./armstack.pl pb_decode.c.o pb_common.c.o Func Cost Frame Height ------------------------------------------------------------------------ @@ -313,3 +323,6 @@ The following functions were not resolved: realloc free memset + +``` + From acaf05c173dbbefecb0867d9c979a301561f7ef1 Mon Sep 17 00:00:00 2001 From: summerxu <1032755103@qq.com> Date: Tue, 8 Dec 2020 14:48:09 +0800 Subject: [PATCH 6/8] Update README.md remove inaccurate description. as for max stack size, avstack.pl already did a good job. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 130f137..a1b2d71 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ origin version written by Daniel Beer https://dlbeer.co.nz/downloads/avstack.pl ## improved features - show maximum **call stack chains** in readable format -- more **accurate** stack size - add parsing of relocation R_ARM_THM_JUMP* ## sample output From 93008cb5ad0f0a6ebd07444b78266518b7f8ecfd Mon Sep 17 00:00:00 2001 From: xuwenjie Date: Tue, 8 Dec 2020 14:51:02 +0800 Subject: [PATCH 7/8] add example --- call_stack_test/Makefile | 40 ++++++++++++ call_stack_test/call_stack_testcase.c | 92 +++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 call_stack_test/Makefile create mode 100644 call_stack_test/call_stack_testcase.c diff --git a/call_stack_test/Makefile b/call_stack_test/Makefile new file mode 100644 index 0000000..b2d7ef1 --- /dev/null +++ b/call_stack_test/Makefile @@ -0,0 +1,40 @@ +VERBOSE ?= 0 +# echo suspend +ifeq ($(VERBOSE),1) + NO_ECHO := +else + NO_ECHO := @ +endif +# $1 path +GNU_PREFIX := arm-none-eabi +# https://developer.arm.com/tools-and-software/open-source-software/developer-tools/gnu-toolchain/gnu-rm/downloads +GNU_INSTALL_PATH := /opt/arm/gcc-arm-none-eabi-9/bin/ +# Toolchain commands +CC := $(GNU_INSTALL_PATH)$(GNU_PREFIX)-gcc +OBJDUMP := $(GNU_INSTALL_PATH)$(GNU_PREFIX)-objdump +#$(info $$CC is $(CC)) +#$(info $$OBJDUMP is $(OBJDUMP)) +# Optimization flags +OPT = -O0 -g0 +# C flags common to all targets +CFLAGS += $(OPT) +CFLAGS += -mcpu=cortex-m4 +CFLAGS += -mthumb -mabi=aapcs +CFLAGS += -Wall -Werror -fstack-usage +CFLAGS += -mfloat-abi=soft +# keep every function in a separate section, this allows linker to discard unused ones +CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing +CFLAGS += -fno-builtin -fshort-enums -Wno-unused-function -Wno-unused-variable +.PHONY: clean default +# Default target - first one defined +SOURCES=$(wildcard *.c) +OBJECTS=$(SOURCES:.c=.o) +ASSEMBLY=$(OBJECTS:.o=.S) +default: $(OBJECTS) $(ASSEMBLY) +$(ASSEMBLY): $(OBJECTS) +$(OBJECTS): $(SOURCES) + $(NO_ECHO)$(CC) -std=c99 $(CFLAGS) -c -o $@ $< +$(ASSEMBLY): $(OBJECTS) + $(NO_ECHO)$(OBJDUMP) -dr $< > $@ +clean: + $(NO_ECHO)rm -f *.o *.S *.su diff --git a/call_stack_test/call_stack_testcase.c b/call_stack_test/call_stack_testcase.c new file mode 100644 index 0000000..5c36d71 --- /dev/null +++ b/call_stack_test/call_stack_testcase.c @@ -0,0 +1,92 @@ +#include +#include +#include +#include +#include + +void A(void); +void B(void); +void C(void); +void D(void); +void M(void); +void N(void); +void O(void); +void P(void); +void X(void); +void Y(void); +void Z(void); + +void A(void) +{ + volatile uint8_t buf[10] = {0}; + B(); + X(); +} +void B(void) +{ + volatile uint8_t buf[20] = {0}; + buf[0] = buf[0]; + C(); +} +void C(void) +{ + volatile uint8_t buf[30] = {0}; + buf[0] = buf[0]; + D(); + M(); +} +void D(void) +{ + volatile uint8_t buf[40] = {0}; + buf[0] = buf[0]; + C(); +} +void M(void) +{ + volatile uint8_t buf[50] = {0}; + buf[0] = buf[0]; + N(); +} +void N(void) +{ + volatile uint8_t buf[60] = {0}; + buf[0] = buf[0]; + O(); +} +void O(void) +{ + volatile uint8_t buf[70] = {0}; + buf[0] = buf[0]; + P(); +} +void P(void) +{ + volatile uint8_t buf[80] = {0}; + buf[0] = buf[0]; +} +void X(void) +{ + volatile uint8_t buf[15] = {0}; + buf[0] = buf[0]; + Y(); +} +void Y(void) +{ + volatile uint8_t buf[25] = {0}; + buf[0] = buf[0]; + Z(); +} +void Z(void) +{ + volatile uint8_t buf[35] = {0}; + buf[0] = buf[0]; + C(); +} + +int START(void) +{ + A(); + + return 0; +} + From e9d843b5d8126a6be573291c6d460d130c35865f Mon Sep 17 00:00:00 2001 From: summerxu <1032755103@qq.com> Date: Tue, 8 Dec 2020 15:25:01 +0800 Subject: [PATCH 8/8] update readme based on test file --- README.md | 468 ++++++++++++++++++++++-------------------------------- 1 file changed, 192 insertions(+), 276 deletions(-) diff --git a/README.md b/README.md index a1b2d71..0bc22fd 100644 --- a/README.md +++ b/README.md @@ -11,316 +11,232 @@ origin version written by Daniel Beer https://dlbeer.co.nz/downloads/avstack.pl ## sample output +```c +#include +#include +#include +#include +#include + +void A(void); +void B(void); +void C(void); +void D(void); +void M(void); +void N(void); +void O(void); +void P(void); +void X(void); +void Y(void); +void Z(void); + +void A(void) +{ + volatile uint8_t buf[10] = {0}; + B(); + X(); +} +void B(void) +{ + volatile uint8_t buf[20] = {0}; + buf[0] = buf[0]; + C(); +} +void C(void) +{ + volatile uint8_t buf[30] = {0}; + buf[0] = buf[0]; + D(); + M(); +} +void D(void) +{ + volatile uint8_t buf[40] = {0}; + buf[0] = buf[0]; + C(); +} +void M(void) +{ + volatile uint8_t buf[50] = {0}; + buf[0] = buf[0]; + N(); +} +void N(void) +{ + volatile uint8_t buf[60] = {0}; + buf[0] = buf[0]; + O(); +} +void O(void) +{ + volatile uint8_t buf[70] = {0}; + buf[0] = buf[0]; + P(); +} +void P(void) +{ + volatile uint8_t buf[80] = {0}; + buf[0] = buf[0]; +} +void X(void) +{ + volatile uint8_t buf[15] = {0}; + buf[0] = buf[0]; + Y(); +} +void Y(void) +{ + volatile uint8_t buf[25] = {0}; + buf[0] = buf[0]; + Z(); +} +void Z(void) +{ + volatile uint8_t buf[35] = {0}; + buf[0] = buf[0]; + C(); +} + +int START(void) +{ + A(); + + return 0; +} + ``` -./armstack.pl pb_decode.c.o pb_common.c.o +```shell +$ cd call_stack_test && make +$ ls +call_stack_testcase.c call_stack_testcase.o call_stack_testcase.S call_stack_testcase.su Makefile +$ chmod +x ../armstack.pl ../avstack.pl +$ ../armstack.pl call_stack_testcase.o Func Cost Frame Height ------------------------------------------------------------------------ -> pb_dec_submessage 344 40 9 -> pb_decode_delimited 336 32 9 -> pb_decode_delimited_noinit 320 32 8 - pb_decode 304 16 8 -> pb_decode_nullterminated 304 0 9 - pb_decode_noinit 288 104 7 - decode_field 184 80 6 -R pb_release_single_field 104 56 4 - initialize_pointer_field.isra.0 104 16 5 -R pb_message_set_to_defaults 88 32 4 -> pb_dec_svarint 88 24 4 -> pb_dec_fixed_length_bytes 72 32 4 - pb_decode_tag 72 32 3 -> pb_dec_varint 72 32 3 - pb_make_string_substream 64 24 4 - pb_decode_svarint 64 24 3 -> pb_dec_string 64 24 4 -> pb_dec_uvarint 64 24 3 -> pb_dec_bytes 64 24 4 - pb_field_set_to_default 56 40 3 -> pb_decode_bool 56 0 5 - pb_dec_bool 56 16 4 - pb_skip_field 56 16 4 - pb_decode_fixed32 48 16 2 - pb_decode_fixed64 48 16 2 -> pb_dec_fixed64 48 0 3 - pb_release 48 32 3 - pb_close_string_substream 48 16 2 -> pb_dec_fixed32 48 0 3 - pb_decode_varint32 40 0 3 - pb_decode_varint 40 32 2 - pb_decode_varint32_eof 40 32 2 - pb_field_iter_find 32 16 3 -R pb_read 32 32 1 - iter_from_extension 16 16 2 - pb_field_iter_next 16 16 2 - allocate_field 16 16 1 -> pb_istream_from_buffer 8 8 1 - pb_readbyte 8 8 1 -> buf_read 8 8 1 - pb_field_iter_begin 0 0 1 +> START 488 8 10 + A 480 24 9 + X 456 24 8 + Y 432 40 7 +R D 392 48 6 + Z 392 48 6 + B 376 32 6 + C 344 40 5 + M 304 64 4 + N 240 72 3 + O 168 80 2 + P 88 88 1 > INTERRUPT 0 0 1 -## Chain from pb_dec_submessage, Cost (344) ------------------------------------------------------------------------- - >pb_dec_submessage (40) - >pb_decode (16) - >pb_decode_noinit (104) - >decode_field (80) - >pb_release_single_field (56) - >pb_release (32) - >pb_field_iter_next (16) - -## Chain from pb_decode_delimited, Cost (336) ------------------------------------------------------------------------- - >pb_decode_delimited (32) - >pb_decode (16) - >pb_decode_noinit (104) - >decode_field (80) - >pb_release_single_field (56) - >pb_release (32) - >pb_field_iter_next (16) - -## Chain from pb_decode_delimited_noinit, Cost (320) ------------------------------------------------------------------------- - >pb_decode_delimited_noinit (32) - >pb_decode_noinit (104) - >decode_field (80) - >pb_release_single_field (56) - >pb_release (32) - >pb_field_iter_next (16) - -## Chain from pb_decode, Cost (304) ------------------------------------------------------------------------- - >pb_decode (16) - >pb_decode_noinit (104) - >decode_field (80) - >pb_release_single_field (56) - >pb_release (32) - >pb_field_iter_next (16) - -## Chain from pb_decode_nullterminated, Cost (304) ------------------------------------------------------------------------- - >pb_decode_nullterminated (0) - >pb_decode (16) - >pb_decode_noinit (104) - >decode_field (80) - >pb_release_single_field (56) - >pb_release (32) - >pb_field_iter_next (16) - -## Chain from pb_decode_noinit, Cost (288) ------------------------------------------------------------------------- - >pb_decode_noinit (104) - >decode_field (80) - >pb_release_single_field (56) - >pb_release (32) - >pb_field_iter_next (16) - -## Chain from decode_field, Cost (184) ------------------------------------------------------------------------- - >decode_field (80) - >pb_release_single_field (56) - >pb_release (32) - >pb_field_iter_next (16) - -## Chain from pb_release_single_field, Cost (104) ------------------------------------------------------------------------- - >pb_release_single_field (56) - >pb_release (32) - >pb_field_iter_next (16) +## Chain from START, Cost (488) +------------------------------------------------------------------------ + >START (8) + >A (24) + >X (24) + >Y (40) + >Z (48) + >C (40) + >M (64) + >N (72) + >O (80) + >P (88) -## Chain from initialize_pointer_field.isra.0, Cost (104) +## Chain from A, Cost (480) ------------------------------------------------------------------------ - >initialize_pointer_field.isra.0 (16) - >pb_message_set_to_defaults (32) - >pb_field_set_to_default (40) - >iter_from_extension (16) + >A (24) + >X (24) + >Y (40) + >Z (48) + >C (40) + >M (64) + >N (72) + >O (80) + >P (88) -## Chain from pb_message_set_to_defaults, Cost (88) +## Chain from X, Cost (456) ------------------------------------------------------------------------ - >pb_message_set_to_defaults (32) - >pb_field_set_to_default (40) - >iter_from_extension (16) + >X (24) + >Y (40) + >Z (48) + >C (40) + >M (64) + >N (72) + >O (80) + >P (88) -## Chain from pb_dec_svarint, Cost (88) +## Chain from Y, Cost (432) ------------------------------------------------------------------------ - >pb_dec_svarint (24) - >pb_decode_svarint (24) - >pb_decode_varint (32) - >pb_readbyte (8) + >Y (40) + >Z (48) + >C (40) + >M (64) + >N (72) + >O (80) + >P (88) -## Chain from pb_dec_fixed_length_bytes, Cost (72) +## Chain from D, Cost (392) ------------------------------------------------------------------------ - >pb_dec_fixed_length_bytes (32) - >pb_decode_varint32 (0) - >pb_decode_varint32_eof (32) - >pb_readbyte (8) + >D (48) + >C (40) + >M (64) + >N (72) + >O (80) + >P (88) -## Chain from pb_decode_tag, Cost (72) +## Chain from Z, Cost (392) ------------------------------------------------------------------------ - >pb_decode_tag (32) - >pb_decode_varint32_eof (32) - >pb_readbyte (8) + >Z (48) + >C (40) + >M (64) + >N (72) + >O (80) + >P (88) -## Chain from pb_dec_varint, Cost (72) +## Chain from B, Cost (376) ------------------------------------------------------------------------ - >pb_dec_varint (32) - >pb_decode_varint (32) - >pb_readbyte (8) + >B (32) + >C (40) + >M (64) + >N (72) + >O (80) + >P (88) -## Chain from pb_make_string_substream, Cost (64) +## Chain from C, Cost (344) ------------------------------------------------------------------------ - >pb_make_string_substream (24) - >pb_decode_varint32 (0) - >pb_decode_varint32_eof (32) - >pb_readbyte (8) + >C (40) + >M (64) + >N (72) + >O (80) + >P (88) -## Chain from pb_decode_svarint, Cost (64) +## Chain from M, Cost (304) ------------------------------------------------------------------------ - >pb_decode_svarint (24) - >pb_decode_varint (32) - >pb_readbyte (8) + >M (64) + >N (72) + >O (80) + >P (88) -## Chain from pb_dec_string, Cost (64) +## Chain from N, Cost (240) ------------------------------------------------------------------------ - >pb_dec_string (24) - >pb_decode_varint32 (0) - >pb_decode_varint32_eof (32) - >pb_readbyte (8) + >N (72) + >O (80) + >P (88) -## Chain from pb_dec_uvarint, Cost (64) +## Chain from O, Cost (168) ------------------------------------------------------------------------ - >pb_dec_uvarint (24) - >pb_decode_varint (32) - >pb_readbyte (8) + >O (80) + >P (88) -## Chain from pb_dec_bytes, Cost (64) ------------------------------------------------------------------------- - >pb_dec_bytes (24) - >pb_decode_varint32 (0) - >pb_decode_varint32_eof (32) - >pb_readbyte (8) - -## Chain from pb_field_set_to_default, Cost (56) ------------------------------------------------------------------------- - >pb_field_set_to_default (40) - >iter_from_extension (16) - -## Chain from pb_decode_bool, Cost (56) ------------------------------------------------------------------------- - >pb_decode_bool (0) - >pb_dec_bool (16) - >pb_decode_varint32 (0) - >pb_decode_varint32_eof (32) - >pb_readbyte (8) - -## Chain from pb_dec_bool, Cost (56) ------------------------------------------------------------------------- - >pb_dec_bool (16) - >pb_decode_varint32 (0) - >pb_decode_varint32_eof (32) - >pb_readbyte (8) - -## Chain from pb_skip_field, Cost (56) ------------------------------------------------------------------------- - >pb_skip_field (16) - >pb_decode_varint32 (0) - >pb_decode_varint32_eof (32) - >pb_readbyte (8) - -## Chain from pb_decode_fixed32, Cost (48) ------------------------------------------------------------------------- - >pb_decode_fixed32 (16) - >pb_read (32) - -## Chain from pb_decode_fixed64, Cost (48) ------------------------------------------------------------------------- - >pb_decode_fixed64 (16) - >pb_read (32) - -## Chain from pb_dec_fixed64, Cost (48) ------------------------------------------------------------------------- - >pb_dec_fixed64 (0) - >pb_decode_fixed64 (16) - >pb_read (32) - -## Chain from pb_release, Cost (48) ------------------------------------------------------------------------- - >pb_release (32) - >pb_field_iter_next (16) - -## Chain from pb_close_string_substream, Cost (48) ------------------------------------------------------------------------- - >pb_close_string_substream (16) - >pb_read (32) - -## Chain from pb_dec_fixed32, Cost (48) ------------------------------------------------------------------------- - >pb_dec_fixed32 (0) - >pb_decode_fixed32 (16) - >pb_read (32) - -## Chain from pb_decode_varint32, Cost (40) ------------------------------------------------------------------------- - >pb_decode_varint32 (0) - >pb_decode_varint32_eof (32) - >pb_readbyte (8) - -## Chain from pb_decode_varint, Cost (40) ------------------------------------------------------------------------- - >pb_decode_varint (32) - >pb_readbyte (8) - -## Chain from pb_decode_varint32_eof, Cost (40) ------------------------------------------------------------------------- - >pb_decode_varint32_eof (32) - >pb_readbyte (8) - -## Chain from pb_field_iter_find, Cost (32) ------------------------------------------------------------------------- - >pb_field_iter_find (16) - >pb_field_iter_next (16) - -## Chain from pb_read, Cost (32) ------------------------------------------------------------------------- - >pb_read (32) - -## Chain from iter_from_extension, Cost (16) ------------------------------------------------------------------------- - >iter_from_extension (16) - -## Chain from pb_field_iter_next, Cost (16) ------------------------------------------------------------------------- - >pb_field_iter_next (16) - -## Chain from allocate_field, Cost (16) ------------------------------------------------------------------------- - >allocate_field (16) - -## Chain from pb_istream_from_buffer, Cost (8) ------------------------------------------------------------------------- - >pb_istream_from_buffer (8) - -## Chain from pb_readbyte, Cost (8) ------------------------------------------------------------------------- - >pb_readbyte (8) - -## Chain from buf_read, Cost (8) ------------------------------------------------------------------------- - >buf_read (8) - -## Chain from pb_field_iter_begin, Cost (0) ------------------------------------------------------------------------- - >pb_field_iter_begin (0) +## Chain from P, Cost (88) +------------------------------------------------------------------------ + >P (88) ## Chain from INTERRUPT, Cost (0) ------------------------------------------------------------------------ The following functions were not resolved: - memcpy - realloc - free memset ```