os/kernelhwsrv/kerneltest/e32utils/d_exc/printstk.pl
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     1 #! perl
     2 # Copyright (c) 2004-2009 Nokia Corporation and/or its subsidiary(-ies).
     3 # All rights reserved.
     4 # This component and the accompanying materials are made available
     5 # under the terms of the License "Eclipse Public License v1.0"
     6 # which accompanies this distribution, and is available
     7 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
     8 #
     9 # Initial Contributors:
    10 # Nokia Corporation - initial contribution.
    11 #
    12 # Contributors:
    13 #
    14 # Description:
    15 #
    16 
    17 if (@ARGV<1)
    18 	{
    19 #........1.........2.........3.........4.........5.........6.........7.....
    20 	print <<USAGE_EOF;
    21 
    22 Usage:
    23 	printstk.pl d_exc_nnn [romimage.symbol]
    24 
    25 Given the output of D_EXC, a file d_exc_nnn.txt and d_exc_nnn.stk, it 
    26 uses the other information to try to put symbolic information against 
    27 the stack image.
    28 
    29 USAGE_EOF
    30 	exit 1;
    31 	}
    32 
    33 sub add_object
    34 	{
    35 	my ($base, $max, $name) = @_;
    36 	$address{$base} = [ $base, $max, $name ];
    37 	my $key=$base>>20;
    38 	my $maxkey=$max>>20;
    39 	while ($key <= $maxkey)		# allowing for objects that span the boundary
    40 		{
    41 		push @{$addresslist{$key}}, $base;
    42 		$key+=1;
    43 		}
    44 	}
    45 
    46 my $RomBase = 0xF8000000;
    47 my $RomLimit = 0xFFF00000;
    48 add_object($RomBase,$RomLimit, "ROM");
    49 
    50 # Handle a MAKSYM.LOG file for a ROM
    51 #
    52 sub read_rom_symbols
    53 	{
    54 	my ($romimage)=@_;
    55 	open ROMSYMBOLS, $romimage or print "Can't open $romimage\n" and return;
    56 
    57 	my $a;
    58 	my $b;
    59 	while (my $line = <ROMSYMBOLS>)
    60 		{
    61 		if(!($line =~ /^[0-9A-Fa-f]{8}/))
    62 			{
    63 			next;
    64 			}
    65 		# 8 bytes for the address
    66 		
    67 		$a = substr $line,0,8;
    68 		if(!($a =~ /[0-9A-Fa-f]{8}/))
    69 			{
    70 			next;
    71 			}
    72 		# 4 bytes for the length
    73 		$b = substr $line,12,4;
    74 		if(!($b =~ /[0-9A-Fa-f]{4}/))
    75 			{
    76 			next;
    77 			}
    78 		# rest of line is symbol
    79 		my $symbol = substr $line,20;
    80 		chomp $symbol;
    81 
    82 		my $base=hex($a);
    83 		my $length=hex($b);
    84 		if ($base < 0x50000000) 
    85 			{
    86 			next;	# skip this line
    87 			}
    88 		if ($length==0xffffffff)
    89 			{
    90 			$length=100;	# MAKSYM bug? choose a rational length
    91 			}
    92 		add_object($base, $base+$length-1, $symbol);
    93 		}
    94 	print "ROM Symbols from $romimage\n";
    95 	}
    96 
    97 # Handle MAP file for a non execute-in-place binary
    98 #
    99 sub read_map_symbols
   100 	{
   101 	my ($binary, $binbase)=@_;
   102 	$binary =~ /([^\\]+)$/;
   103 	my $basename=$1;
   104 	if (not open MAPFILE, "$basename.map")
   105 		{
   106 		print "Can't open map file for \n$binary.map)\n";		
   107 		return;
   108 		}
   109 
   110 		
   111 	my @maplines;
   112 	while (<MAPFILE>) 
   113 		{
   114 		push @maplines, $_;
   115 		}
   116 	close MAPFILE;
   117 # See if we're dealing with the RVCT output
   118 	if ($maplines[0] =~ /^ARM Linker/) 
   119 		{
   120 		# scroll down to the global symbols
   121 		while ($_ = shift @maplines) 
   122 			{
   123 			if (/Global Symbols/) 
   124 				{
   125 				last;
   126 				}
   127 			}
   128 		# .text gets linked at 0x00008000		
   129 		$imgtext=hex(8000);#start of the text section during linking
   130 		
   131 		foreach (@maplines) 
   132 			{
   133 			# name address ignore size section
   134 			if (/^\s*(.+)\s*(0x\S+)\s+[^\d]*(\d+)\s+(.*)$/) 
   135 				{
   136 				my $symbol  = $1;
   137 				my $addr = hex($2);
   138 				my $size = $3;
   139 				if ($size > 0)#symbols of the 0 size contain some auxillary information, ignore them
   140 					{
   141 	            			add_object($addr-$imgtext+$binbase,#relocated address of the current symbol 
   142 						$addr-$imgtext+$binbase+$size,#relocated address of the current symbol + size of the current symbol
   143 						"$binary $symbol");
   144 					}
   145 				}
   146 			}			      
   147 		}
   148 	else 
   149 #we are dealing with GCC output
   150 		{
   151 		my $imgtext;
   152 		
   153 		# Find text section
   154 		while (($_ = shift @maplines) && !(/^\.text\s+/)) 
   155 			{
   156 			}
   157 
   158 		/^\.text\s+(\w+)\s+(\w+)/
   159 			or die "ERROR: Can't get .text section info for \"$file\"\n";
   160 		$imgtext=hex($1);#start of the text section during linking
   161 		$binbase-=$imgtext;
   162 		
   163 		foreach (@maplines) 
   164 			{
   165 			if (/___CTOR_LIST__/)
   166 				{
   167 				last;	# end of text section
   168 				}
   169 
   170 			if (/^\s(\.text)?\s+(0x\w+)\s+(0x\w+)\s+(.*)$/io) 
   171 				{		    
   172 				$textlimit = hex($2)+$binbase+hex($3)-1;			
   173 				next;
   174 				}
   175 					
   176 			if (/^\s+(\w+)\s\s+([a-zA-Z_].+)/o) 			
   177 				{				    
   178 				my $addr = hex($1);
   179 				my $symbol = $2;
   180 				add_object($addr+$binbase,#relocated address of the current symbol
   181 					$textlimit,#limit of the current object section
   182 					"$binary $symbol");
   183 				next;
   184 				}
   185 			}						
   186 		}
   187 #end of GCC output parsing		
   188 	}
   189 
   190 # Handle a matched pair of D_EXC output files (.txt and .stk)
   191 #
   192 sub read_d_exc
   193 	{
   194 	my ($name)=@_;
   195 
   196 	$stackbase = 0;
   197 	open D_EXC, "$name.txt" or die "Can't open $name.txt\n";
   198 
   199 	binmode D_EXC;
   200 	read D_EXC, $data, 16;
   201 	close D_EXC;
   202 
   203 	if ($data =~ /^(..)*.\0.\0/)
   204 		{
   205 		# Assuming Unicode
   206 		close D_EXC;
   207 
   208 		# Charconv won't convert STDIN or write to STDOUT
   209 		# so we generate an intermediate UTF8 file 
   210 		system "charconv -little -input=unicode $name.txt -output=utf8 $name.utf8.txt";
   211 
   212 		open D_EXC, "$name.utf8.txt" or die "Can't open $name.utf8.txt\n";
   213 		}
   214 	else
   215 		{
   216 		# Assuming ASCII
   217 		open D_EXC, "$name.txt" or die "Can't open $name.txt\n";
   218 		}
   219 
   220 	my $is_eka2_log = 0;
   221 
   222 	while (my $line = <D_EXC>)
   223 		{
   224 
   225 		if ($line =~ /^EKA2 USER CRASH LOG$/)
   226 			{
   227 			$is_eka2_log = 1;
   228 			next;
   229 			}
   230 	
   231 		# code=1 PC=500f7ff8 FAR=00000042 FSR=e8820013
   232 		
   233 		if ($line =~ /^code=\d PC=(.{8})/)
   234 			{
   235 			$is_exc = 1;
   236 			$fault_pc = hex($1);
   237 			next;
   238 			};
   239 
   240 		# R13svc=81719fc0 R14svc=50031da0 SPSRsvc=60000010
   241 	
   242 		if ($line =~ /^R13svc=(.{8}) R14svc=(.{8}) SPSRsvc=(.{8})/)
   243 			{
   244 			$fault_lr = hex($2);
   245 			next;
   246 			}
   247 
   248 		# r00=fffffff8 00000000 80000718 80000003
   249 
   250 		if ($line =~ /^r(\d\d)=(.{8}) (.{8}) (.{8}) (.{8})/)
   251 			{
   252 			$registers{$1} = $line;
   253 			if ($1 == 12)
   254 				{
   255 				$activesp = hex($3);
   256 				$user_pc = hex($5);
   257 				$user_lr = hex($4);
   258 				}
   259 			next;
   260 			}
   261 
   262 		# User Stack 03900000-03905ffb
   263 		# EKA1 format deliberately broken (was /^Stack.*/) to catch version problems
   264 
   265 		if ($line =~ /^User Stack (.{8})-(.{8})/)
   266 			{
   267 			$stackbase = hex($1);
   268 			add_object($stackbase,hex($2), "Stack");
   269 			next;
   270 			}
   271 
   272 		# fff00000-fff00fff C:\foo\bar.dll
   273 
   274 		if ($line =~ /^(.{8})-(.{8}) (.+)/)
   275 			{
   276 			next if ($RomBase <= hex($1) && hex($1) < $RomLimit); # skip ROM XIP binaries
   277 			add_object(hex($1), hex($2), $3);
   278 			read_map_symbols($3, hex($1));
   279 			}
   280 		}
   281 	close D_EXC;
   282 
   283 	die "$name.txt is not a valid EKA2 crash log" unless $is_eka2_log;
   284 
   285 	if ($stackbase == 0)
   286 		{
   287 		die "couldn't find stack information in $name.txt\n";
   288 		}
   289 
   290 	die "couldn't find stack pointer in  $name.txt\n" unless $activesp != 0;
   291 	$activesp -= $stackbase;
   292 
   293 	# Read in the binary dump of the stack
   294 
   295 	open STACK, "$name.stk" or die "Can't open $name.stk\n";
   296 	print "Stack Data from $name.stk\n";
   297 
   298 	binmode STACK;
   299 	while (read STACK, $data, 4)
   300 		{
   301 		unshift @stack, (unpack "V", $data);
   302 		}
   303 	$stackptr = 0;
   304 	}
   305 
   306 # Handle the captured text output from the Kernel debugger
   307 #
   308 sub read_debugger
   309 	{
   310 	my ($name)=@_;
   311 
   312 	open DEBUGFILE, "$name" or die "Can't open $name\n";
   313 	print "Kernel Debugger session from $name\n";
   314 
   315 	# stuff which should be inferred from "$name"
   316 
   317 	$stackbase = 0x81C00000;
   318 	$stackmax  = 0x81C01DC0;
   319 	$activesp = 0x81c01bc4-$stackbase;
   320 	add_object($stackbase,0x81C01FFF, "Stack");
   321 
   322 	while (my $line = <DEBUGFILE>)
   323 		{
   324 		if ($line =~ /^(\w{8}): ((\w\w ){16})/)
   325 			{
   326 			my $addr = hex($1);
   327 			if ($addr < $stackbase || $addr > $stackmax)
   328 				{
   329 				next;
   330 				}
   331 			if (@stack == 0)
   332 				{
   333 				if ($addr != $stackbase)
   334 					{
   335 					printf "Missing stack data for %x-%x - fill with 0x29\n", $stackbase, $addr-1;
   336 					@stack = (0x29292929) x (($addr-$stackbase)/4);
   337 					}
   338 				}
   339 			unshift @stack, reverse (unpack "V4", (pack "H2"x16, (split / /,$2)));
   340 			}
   341 		}
   342 		$stackptr = 0;
   343 	}
   344 
   345 read_d_exc(@ARGV[0]);
   346 if (@ARGV>1)
   347 	{
   348 	read_rom_symbols(@ARGV[1]);
   349 	}
   350 
   351 # We've accumulated the ranges of objects indexed by start address,
   352 # with a companion list of addresses subdivided by the leading byte
   353 # Now sort them numerically...
   354 
   355 sub numerically { $a <=> $b }
   356 foreach my $key (keys %addresslist)
   357 	{
   358 	@{$addresslist{$key}} = sort numerically @{$addresslist{$key}};
   359 	}
   360 
   361 # Off we go, reading the stack!
   362 
   363 sub skip_unused 
   364 	{
   365 	my $skipped=0;
   366 	while (@stack)
   367 		{
   368 		my $word=(pop @stack);
   369 		if ($word!=0x29292929)
   370 			{ 
   371 			push @stack, $word;
   372 			last;
   373 			}
   374 		$skipped += 4;
   375 		}
   376 	$stackptr += $skipped;
   377 	return $skipped;
   378 	}
   379 
   380 sub lookup_addr
   381 {
   382 	my ($word) = @_;
   383 
   384 	# Optimization - try looking up the address directly
   385 
   386 	my $base;
   387 	my $max;
   388 	my $name;
   389 	if(defined $address{$word}) {
   390 		($base, $max, $name) = @{$address{$word}};
   391 	}
   392 	if (!(defined $base))
   393 		{
   394 		my $key=$word>>20;
   395 		my $regionbase;
   396 		foreach $base (@{$addresslist{$key}})
   397 			{
   398 			if ($base <= $word)
   399 				{
   400 				$regionbase = $base;
   401 				next;
   402 				}
   403 			if ($base > $word)
   404 				{
   405 				last;
   406 				}
   407 			}
   408 		if(defined $regionbase)
   409 			{
   410 			($base, $max, $name) = @{$address{$regionbase}};
   411 			}
   412 		}
   413 	if (defined $base && defined $max && $base <= $word && $max >= $word)
   414 		{
   415 		my $data = pack "V", $word;
   416 		$data =~ tr [\040-\177]/./c;
   417 		return sprintf "%08x %4s  %s + 0x%x", $word, $data, $name, $word - $base;
   418 		}
   419 	return "";
   420 }
   421 
   422 sub match_addr
   423 #
   424 # Try matching one of the named areas in the addresslist
   425 #
   426 {
   427 	my $word = (pop @stack);
   428 
   429 	if ($word < 1024*1024)
   430 		{
   431 		push @stack, $word;
   432 		return 0;
   433 		}
   434 
   435 	my $result = lookup_addr($word);
   436 	if ($result ne "")
   437 		{
   438 		print "$result\n";
   439 		$stackptr+=4;
   440 		return 1;
   441 		}
   442 	push @stack, $word;
   443 	return 0;
   444 	}
   445 
   446 sub match_tbuf8
   447 #
   448 # Try matching a TBuf8
   449 #	0x3000LLLL 0x0000MMMM data
   450 #	
   451 	{
   452 	if (scalar @stack <3)
   453 		{
   454 		return 0;	# too short
   455 		}
   456 	my $word = (pop @stack);
   457 	my $maxlen = (pop @stack);
   458 	
   459 	my $len = $word & 0x0ffff;
   460 	my $type = ($word >> 16) & 0x0ffff;
   461 	if ( $type != 0x3000 || $maxlen <= $len || $maxlen > 4* scalar @stack 
   462 		|| ($stackptr < $activesp && $stackptr + $maxlen + 8 > $activesp))
   463 		{
   464 		push @stack, $maxlen;
   465 		push @stack, $word;
   466 		return 0;		# wrong type, or invalid looking sizes, or out of date
   467 		}
   468 
   469 	printf "TBuf8<%d>, length %d\n", $maxlen, $len;
   470 	$stackptr += 8;
   471 
   472 	my $string="";
   473 	while ($maxlen > 0)
   474 		{
   475 		$string .= pack "V", pop @stack;
   476 		$maxlen -= 4;
   477 		$stackptr += 4;
   478 		}
   479 	if ($len==0)
   480 		{
   481 		print "\n";
   482 		return 1;
   483 		}
   484 	my $line = substr($string,0,$len);
   485 	my @buf = unpack "C*", $line;
   486 	$line =~ tr [\040-\177]/./c;
   487 	printf "\n  %s", $line;
   488 	while ($len > 0)
   489 		{
   490 		my $datalen = 16;
   491 		if ($datalen > $len)
   492 			{
   493 			$datalen = $len;
   494 			}
   495 		$len -= $datalen;
   496 		printf "\n  ";
   497 		while ($datalen > 0)
   498 			{
   499 			my $char = shift @buf;
   500 			printf "%02x ", $char;
   501 			$datalen -= 1;
   502 			}
   503 		}
   504 	printf "\n\n";
   505 	return 1;
   506 	}
   507 
   508 # Skip the unused part of the stack
   509 
   510 skip_unused;
   511 printf "High watermark = %04x\n", $stackptr;
   512 
   513 # process the interesting bit!
   514 
   515 my $printed_current_sp = 0;
   516 while (@stack)
   517 	{
   518 	if (!$printed_current_sp && $stackptr >= $activesp)
   519 		{
   520 		printf "\n >>>> current user stack pointer >>>>\n\n";
   521 
   522 		print $registers{"00"};
   523 		print $registers{"04"};
   524 		print $registers{"08"};
   525 		print $registers{"12"};
   526 
   527 		if ($is_exc && $user_pc != $fault_pc)
   528 			{
   529 			print "\nWARNING: A kernel-side exception occured but this script\n";
   530 			print "is currently limited to user stack analysis. Sorry.\n";
   531 			my $result = lookup_addr($fault_pc);
   532 			if ($result ne "")
   533 				{
   534 				print "Kernel PC = $result\n";
   535 				}
   536 			$result = lookup_addr($fault_lr);
   537 			if ($result ne "")
   538 				{
   539 				print "Kernel LR = $result\n";
   540 				}
   541 			print "\n";
   542 			}
   543 
   544 		my $result = lookup_addr($user_pc);
   545 		if ($result ne "")
   546 			{
   547 			print "User PC = $result\n";
   548 			}
   549 		$result = lookup_addr($user_lr);
   550 		if ($result ne "")
   551 			{
   552 			print "User LR = $result\n";
   553 			}
   554 		printf "\n >>>> current user stack pointer >>>>\n\n";
   555 		$printed_current_sp = 1;
   556 		}
   557 
   558 	printf "%04x  ", $stackptr;
   559 
   560 	match_tbuf8() and next;
   561 	match_addr() and next;
   562 
   563 	$word = pop @stack;
   564 	$data = pack "V", $word;
   565 	$data =~ tr [\040-\177]/./c;
   566 	printf "%08x %4s  ", $word, $data;
   567 	$stackptr += 4;
   568 
   569 	if ($word == 0x29292929)
   570 		{
   571 		$skipped = skip_unused;
   572 		if ($skipped != 0)
   573 			{
   574 			printf "\n....";
   575 			}
   576 		printf "\n";
   577 		next;
   578 		}
   579 
   580 	# Try matching $word against the known addresses of things
   581 	printf "\n";
   582 	}
   583 
   584