两个不同版本的perl产生不同的结果是否正常?

时间:2018-10-16 07:23:19

标签: eclipse perl microcontroller post-build

我正在尝试按照here中描述的步骤在MCU上执行一些堆栈分析。然后,该站点链接到一个Perl script,我通过一个简单的批处理文件的含义将其作为构建后操作启动。 基于Eclipse的IDEA在以下路径上使用Perl可执行文件:

C:\..\S32DS_ARM_v2018.R1\utils\msys32\usr\bin\perl.exe

perl.exe -v提供:

This is perl 5, version 22, subversion 1 (v5.22.1) built for i686-msys-thread-multi-64int 

操作系统(windows)在

处安装了perl
C:\Perl64\bin\perl.exe

perl.exe -v提供:

This is perl 5, version 24, subversion 3 (v5.24.3) built for MSWin32-x64-multi-thread
(with 1 registered patch, see perl -V for more detail)

我可以确认avstak.pl(我在上面引用的perl脚本行)与前者或后者产生不同的结果。 为什么发生这种情况,目前不在我的专业知识范围内。

我想了解的是

  1. 了解发生这种情况的原因;
  2. 了解哪个perl提供正确的输出(肯定我认为5.24.3是正确的输出);
  3. 了解如何在将来使用perl时预防该问题。

感谢和问候,

编辑:具有两个不同perl版本的脚本的结果(为便于阅读而减少了输出): 这是result_5.22.1

  Func                               Cost    Frame   Height
------------------------------------------------------------------------
> I2C_MasterGetTransferStatus         292      292        1
> FLEXIO_I2C_DRV_MasterStartTransfer      236      236        1
> CLOCK_DRV_Init                      172      172        1
> CLOCK_SYS_SetConfiguration          172      172        1
> EDMA_DRV_ConfigScatterGatherTransfer      132      132        1
> CLOCK_SYS_SetSystemClockConfig       76       76        1
> FLEXIO_I2C_DRV_MasterInit            60       60        1
> EDMA_DRV_ConfigSingleBlockTransfer       60       60        1
> main                                 52       52        1
> LPI2C_DRV_MasterSetBaudRate          52       52        1
> LPI2C_DRV_MasterStartDmaTransfer       52       52        1
> FLEXIO_DRV_InitDriver                52       52        1
> I2C_MasterInit                       44       44        1
> LPI2C_DRV_SlaveStartDmaTransfer       44       44        1
> CLOCK_SYS_UpdateConfiguration        44       44        1
> CLOCK_DRV_SetClockSource             44       44        1
> LPI2C_DRV_SlaveInit                  44       44        1
> EDMA_DRV_Init                        44       44        1
> EDMA_DRV_Deinit                      36       36        1
> CLOCK_SYS_ConfigureSOSC              36       36        1
> CLOCK_SYS_ConfigureFIRC              36       36        1

vs result_5.24.3

 Func                               Cost    Frame   Height
------------------------------------------------------------------------
> main                                536       52        9
  I2C_MasterSendDataBlocking          484       28        8
> I2C_MasterReceiveDataBlocking       484       28        8
> I2C_MasterReceiveData               468       20        7
> I2C_MasterSendData                  468       20        7
  FLEXIO_I2C_DRV_MasterReceiveDataBlocking      456       28        7
  FLEXIO_I2C_DRV_MasterSendDataBlocking      456       28        7
  FLEXIO_I2C_DRV_MasterSendData       448       20        5
  FLEXIO_I2C_DRV_MasterReceiveData      448       20        5
  FLEXIO_I2C_DRV_MasterStartTransfer      428      236        4
> I2C_MasterGetTransferStatus         408      292        6
  CLOCK_SYS_UpdateConfiguration       336       44        6
  CLOCK_SYS_SetConfiguration          292      172        5
> CLOCK_DRV_Init                      292      172        5
  LPI2C_DRV_MasterReceiveDataBlocking      256       20        7
> I2C_SlaveReceiveDataBlocking        252       12        8
> I2C_SlaveSendDataBlocking           252       12        8

如您所见,第一个版本中的最高数字没有增加(并且应该增加)。 成本和框架遭受了我猜想的相同问题。

脚本在这里:

#!/usr/bin/perl -w
# avstack.pl: AVR stack checker
# Copyright (C) 2013 Daniel Beer <dlbeer@gmail.com>
#
# 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 <object files>
#
# 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 = "arm-none-eabi-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 (<DISASSEMBLY>) {
    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 (<SUFILE>) {
        $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"; }

Edit2: 正如Amon建议检查的那样,脚本在同一数据集上的后续迭代不会产生相同的输出。值(成本/框架/高度)始终相同,但报告功能的顺序不同。

0 个答案:

没有答案