A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 245 lines 7.2 kB view raw
1#!/usr/bin/perl 2 3sub error { 4 print STDERR ("Error: @_\n"); 5 exit(1); 6} 7 8sub warning { 9 print STDERR ("Warning: @_\n"); 10} 11 12# string (filename.map) 13# return hash(string:hash(string:number)) 14sub read_map { 15 open(MAP_FILE,$_[0]) || error("Couldn't open a map $_[0]"); 16 my %retval; 17 while (<MAP_FILE>) { 18 chomp; 19 my @parts = split(/[[:space:]]+/); 20 if (@parts != 5) { 21 next; 22 } 23 if ($parts[1] =~ m/\.(text|data|rodata|bss|icode|idata|irodata|ibss)/) { 24 my $region = $parts[1]; 25 my $number = $parts[2]; 26 @parts = split(/\//,$parts[4]); 27 @parts = split(/[\(\)]/,$parts[$#parts]); 28 my $library = $retval{$parts[0]}; 29 my %library = %$library; 30 my $object = $parts[$#parts]; 31 $library{$object . $region} = $number; 32 $retval{$parts[0]} = \%library; 33 } 34 } 35 close(MAP_FILE); 36 return %retval; 37} 38 39# string (filename.(a|o|elf)), hash(number:string), string(objdump_tool) 40# return hash(number:string) 41sub read_library { 42 open(OBJECT_FILE,"$_[2] -t $_[0] |") || 43 error("Couldn't pipe objdump for $_[0]\nCommand was: $_[2] -t $_[0]"); 44 my $library = $_[1]; 45 my %library = %$library; 46 my %retval; 47 my $object; 48 while (<OBJECT_FILE>) { 49 chomp; 50 my @parts = split(/[[:space:]]+/); 51 if ($parts[0] =~ m/:$/) { 52 $object = $parts[0]; 53 $object =~ s/:$//; 54 next; 55 } 56 if (@parts != 6) { 57 next; 58 } 59 if ($parts[0] eq "") { 60 next; 61 } 62 if ($parts[3] eq $parts[5]) { 63 next; 64 } 65 if ($parts[3] =~ m/\.(text|data|rodata|bss|icode|idata|irodata|ibss|iram)/) { 66 my $region = $parts[3]; 67 my $symbolOffset = hex("0x" . $parts[0]); 68 my $sectionOffset = hex($library{$object . $region}); 69 my $location = $symbolOffset + $sectionOffset; 70 $retval{$location} = $parts[5] . "(" . $object . ")"; 71 } 72 } 73 close(OBJECT_FILE); 74 return %retval; 75} 76 77# string (0xFFFFFFFF), hash(number:string) 78# return string 79sub get_name { 80 my $location = hex($_[0]); 81 my $offsets = $_[1]; 82 my %offsets = %$offsets; 83 if (exists $offsets{$location}) { 84 return $offsets{$location}; 85 } else { 86 my $retval = $_[0]; 87 $retval =~ y/[A-Z]/[a-z]/; 88 warning("No symbol found for $retval"); 89 return $retval; 90 } 91} 92 93# string (filename), hash(number:string) 94# return array(array(number,number,string)) 95sub create_list { 96 open(PROFILE_FILE,$_[0]) || 97 error("Could not open profile file: $profile_file"); 98 my $offsets = $_[1]; 99 my $started = 0; 100 my %pfds; 101 while (<PROFILE_FILE>) { 102 if ($started == 0) { 103 if (m/^0x/) { 104 $started = 1; 105 } else { 106 next; 107 } 108 } 109 my @parts = split(/[[:space:]]+/); 110 if ($parts[0] =~ m/^0x/) { 111 my $callName = get_name($parts[0],$offsets); 112 my $calls = $parts[1]; 113 my $ticks = $parts[2]; 114 my @pfd = ($calls,$ticks,$callName); 115 if (exists $pfds{$callName}) { 116 my $old_pfd = $pfds{$callName}; 117 $pfd[0]+=@$old_pfd[0]; 118 $pfd[1]+=@$old_pfd[1]; 119 } 120 $pfds{$callName} = \@pfd; 121 } else { 122 last; 123 } 124 125 } 126 close(PROFILE_FILE); 127# print("FUNCTIONS\tTOTAL_CALLS\tTOTAL_TICKS\n"); 128# printf(" %4d\t %8d\t %8d\n",$pfds,$totalCalls,$totalTicks); 129 return values(%pfds); 130} 131 132# array(array(number,number,string)), number (sort element) 133sub print_sorted { 134 my $pfds = $_[0]; 135 my @pfds = @$pfds; 136 my $sort_index = $_[1]; 137 my $percent = $_[2]; 138 my %elements; 139 my $totalCalls = 0; 140 my $totalTicks = 0; 141 $pfds = 0; 142 143 # we use a key sort, which means numerical fields need to be 144 # numerically sortable by an alphanumeric sort - we can simply 145 # do this by giving the numeric keys trailing zeros. Note that 146 # simple string concatenation (what this used to do) would not do this 147 foreach $element(@pfds) { 148 $ne = $element; 149 @$ne[0] = sprintf( "%08d", @$element[0]); 150 @$ne[1] = sprintf( "%08d", @$element[1]); 151 $elements{@$ne[$sort_index] . @$element[2]} = $element; 152 $pfds++; 153 $totalCalls += @$element[0]; 154 $totalTicks += @$element[1]; 155 } 156 my @keys = sort(keys(%elements)); 157 print("FUNCTIONS\tTOTAL_CALLS\tTOTAL_TICKS\n"); 158 printf(" %4d\t %8d\t %8d\n",$pfds,$totalCalls,$totalTicks); 159 foreach $key(@keys) { 160 my $element = $elements{$key}; 161 if ($percent) { 162 printf("Calls: %7.2f%% Ticks: %7.2f%% Symbol: %s\n", 163 @$element[0]/$totalCalls*100, 164 @$element[1]/$totalTicks*100, 165 @$element[2]); 166 } else { 167 printf("Calls: %08d Ticks: %08d Symbol: %s\n", 168 @$element); 169 } 170 } 171} 172 173# merges two hashes 174sub merge_hashes { 175 my $hash1 = $_[0]; 176 my $hash2 = $_[1]; 177 return (%$hash1,%$hash2); 178} 179 180sub usage { 181 if (@_) { 182 print STDERR ("Error: @_\n"); 183 } 184 print STDERR ("USAGE:\n"); 185 print STDERR ("$0 profile.out objdump_tool map obj[...] [map obj[...]...] sort[...]\n"); 186 print STDERR 187 ("\tprofile.out output from the profiler, extension is .out\n"); 188 print STDERR 189 ("\tobjdump_tool name of objdump executable for this platform\n"); 190 print STDERR 191 ("\t e.g. arm-elf-objdump\n"); 192 print STDERR 193 ("\tmap map file, extension is .map\n"); 194 print STDERR 195 ("\tobj library or object file, extension is .a or .o or .elf\n"); 196 print STDERR 197 ("\tformat 0-2[_p] 0: by calls, 1: by ticks, 2: by name\n"); 198 print STDERR 199 ("\t _p shows percents instead of counts\n"); 200 print STDERR ("NOTES:\n"); 201 print STDERR 202 ("\tmaps and objects come in sets, one map then many objects\n"); 203 exit(1); 204} 205 206 207if ($ARGV[0] =~ m/-(h|help|-help)/) { 208 usage(); 209} 210if (@ARGV < 3) { 211 usage("Requires at least 3 arguments"); 212} 213if ($ARGV[0] !~ m/\.out$/) { 214 usage("Profile file must end in .out"); 215} 216my $i = 2; 217my %symbols; 218{ 219 my %map; 220 for (; $i < @ARGV; $i++) { 221 my $file = $ARGV[$i]; 222 if ($file =~ m/\.map$/) { 223 %map = read_map($file); 224 } elsif ($file =~ m/\.(a|o|elf)$/) { 225 if (!%map) { 226 usage("No map file found before first object file"); 227 } 228 my @parts = split(/\//,$file); 229 my %new_symbols = read_library($file,$map{$parts[$#parts]},$ARGV[1]); 230 %symbols = merge_hashes(\%symbols,\%new_symbols); 231 } else { 232 last; 233 } 234 } 235} 236if (!%symbols) { 237 warning("No symbols found"); 238} 239if ($i >= @ARGV) { 240 error("You forgot to specify any sort ordering on output (e.g. 0, 1_p, 2)"); 241} 242my @pfds = create_list($ARGV[0],\%symbols); 243for (; $i < @ARGV; $i++) { 244 print_sorted(\@pfds,split("_",$ARGV[$i])); 245}