kerneltest/e32utils/trace/btracevw.pl
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kerneltest/e32utils/trace/btracevw.pl	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,1952 @@
+#
+# Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
+# All rights reserved.
+# This component and the accompanying materials are made available
+# under the terms of the License "Eclipse Public License v1.0"
+# which accompanies this distribution, and is available
+# at the URL "http://www.eclipse.org/legal/epl-v10.html".
+#
+# Initial Contributors:
+# Nokia Corporation - initial contribution.
+#
+# Contributors:
+#
+# Description:
+#
+
+#!/usr/bin/perl
+
+use File::Find;
+use File::Spec::Functions;
+
+
+	my $TraceFileName;
+	
+	my $PrintFlagFilePos = 0;
+	my $PrintFlagHdrLen = 0;
+	my $PrintFlagHdrFlags = 0;
+	my $PrintFlagFormatString = 0;
+	my $VerboseMode = 0;
+	my $RawMode = 0;
+	my $FormatIdIsSubCategory = 0;
+	my $OutputSawDictionaryMode = 0;
+	
+	# for the category range 0-191, the format string is indexed by the category & subcategory
+	%FormatTables = 
+		(
+		0 => 			# ERDebugPrintf
+			{
+			0 => "ThreadId %h, %s",
+			},
+	
+		1 => 			# ERKernPrintf
+			{
+			0 => "ThreadId %h, %s",
+			},
+
+		3 =>			# EThreadIdentification
+			{	
+			0 => "ENanoThreadCreate, NThread %x",
+			1 => "ENanoThreadDestroy, NThread %x",
+			2 => "EThreadCreate, NThread %x, DProcess %x, name %s",
+			3 => "EThreadDestroy, NThread %x, DProcess %x, Id %x",
+			4 => "EThreadName, NThread %x, DProcess %x, name %s",
+			5 => "EProcessName, NThread %x, DProcess %x, name %s",
+			6 => "EThreadId, NThread %x, DProcess %x, Id %x",
+			7 => "EProcessCreate, DProcess %x",
+			8 => "EProcessDestroy, DProcess %x",
+			},
+		);
+
+	my @typedefs;
+	my @members;
+	my %values	= (
+#		UTF::KInitialClientFormat		=>	{type=>"TFormatId", size=>2, value=>512}
+		KMaxTUint8						=> {type=>"TUint8", size=>1, value=>255},
+		KMaxTUint16						=> {type=>"TUint16", size=>2, value=>65535}
+	);
+	my %macros;
+	my @classes;
+	my @enums;
+	my %formatStrings;		# each enum may have it's own format string
+	my %formatCategories;	# each enum may have it's own format category
+	
+	my %filescope;
+	$filescope{file}=1;
+	undef $filescope{name};	
+
+	$filescope{typedefs}=\@typedefs;
+	$filescope{members}=\@members;
+	$filescope{values}=\%values;
+	$filescope{macros} = \%macros;
+	$filescope{FormatTables} = \%FormatTables;
+	
+	$filescope{classes} = \@classes;
+	$filescope{enums} = \@enums;
+
+	$filescope{formatStrings} =\%formatStrings;
+	$filescope{formatCategories} = \%formatCategories;
+	
+		
+		
+	if (@ARGV == 0)
+  		{
+  		print "BTraceVw.pl \n";
+  		print "An unsupported utility which extracts UTrace-style format-strings\n";
+  		print "from header files & uses these to decode a BTrace output file\n";
+  		print "Syntax : BTraceVw.pl [-v] [-r] [-sd] [-i <IncFilePath>] [<BTrace file>]\n";
+  		print "where  : -v  = verbose mode\n";
+  		print "       : -r  = raw output mode\n";
+  		print "       : -sd = produce SAW trace viewer dictionary file\n";
+  		print "       :       this file then needs to be merged into the 'com.symbian.analysis.trace.ui.prefs' file\n";
+  		print "       :       located under the carbide workspace directory\n";
+		print "\n";
+  		
+		print "e.g. (this decodes a trace file & produces a comma-separated output file) : \n";
+		print "btracevw.pl -i /os/kernelhwsrv/userlibandfileserver/fileserver/inc/f32tracedef.h -i /os/kernelhwsrv/userlibandfileserver/fileserver/inc/utraceefsrv.h -i /os/kernelhwsrv/userlibandfileserver/fileserver/inc/utraceefile.h trace.utf >trace.csv\n";
+		print "\n";
+		print "e.g. (this overwrites the SAW dictioany file) : \n";
+		print "btracevw.pl -sd -i /os/kernelhwsrv/userlibandfileserver/fileserver/inc/f32tracedef.h -i /os/kernelhwsrv/userlibandfileserver/fileserver/inc/utraceefsrv.h -i /os/kernelhwsrv/userlibandfileserver/fileserver/inc/utraceefile.h >com.symbian.analysis.trace.ui.prefs\n";
+  		
+		exit;
+		}
+
+	while (@ARGV > 0)
+		{
+		
+		if ($ARGV[0] eq "-i")
+	        {
+	        shift @ARGV;
+		    ($FilePath) = @ARGV;
+	        shift @ARGV;
+
+	        undef @incFiles;
+		    @incFiles;
+		
+		    find sub { push @incFiles, $File::Find::name if m/\.h$/i;}, $FilePath ;
+		    foreach $incFile (@incFiles)
+		        {
+				H2Trace($incFile, \%filescope);
+		        }
+	        }
+		elsif ($ARGV[0] eq "-r")
+	        {
+		    $RawMode = 1;
+   	        shift @ARGV;
+	        }
+		elsif ($ARGV[0] eq "-sd")
+	        {
+		    $OutputSawDictionaryMode = 1;
+   	        shift @ARGV;
+	        }
+		elsif ($ARGV[0] eq "-v")
+	        {
+		    $VerboseMode = 1;
+   	        shift @ARGV;
+	        }
+	    else
+	    	{
+			$TraceFileName = "$ARGV[0]";
+	        shift @ARGV;
+	    	}
+        }
+		
+	if ($VerboseMode)
+		{
+		dump_scope(\%filescope);
+		PrintFormatTables(\%FormatTables);
+		}
+	if ($OutputSawDictionaryMode)
+		{
+		OutputSawDictionary(\%FormatTables);
+		}
+
+    if (defined ($TraceFileName))
+        {
+        ReadTraceFile($RawMode);
+        }
+
+        
+        
+        
+sub ReadTraceFile($)
+    {
+	(my $RawMode) = @_;
+#	print "Trace file is $TraceFileName, RawMode $RawMode, VerboseMode $VerboseMode\n\n";
+
+	open (LOGFILE, "<$TraceFileName") or die "Can't open $TraceFileName: $!\n";
+	binmode (LOGFILE);
+
+	my $val = 0;
+
+
+	# enum TFlags from e32btrace.h
+	$EHeader2Present	= 1<<0;
+	$ETimestampPresent	= 1<<1;
+	$ETimestamp2Present	= 1<<2;
+	$EContextIdPresent	= 1<<3;
+	$EPcPresent			= 1<<4;
+	$EExtraPresent		= 1<<5;
+	$ERecordTruncated	= 1<<6;
+	$EMissingRecord		= 1<<7;
+	
+	# enum TFlags2 from e32btrace.h
+	$EMultipartFlagMask	= 3<<0;
+	$ECpuIdMask			= 0xfff<<20;
+
+	# enum TMultiPart from e32btrace.h
+	$EMultipartFirst	= 1;
+	$EMultipartMiddle	= 2;
+	$EMultipartLast		= 3;
+	
+	$EMaxBTraceDataArray = 80;
+	
+	# enum TCategory from e32btrace.h
+	$EThreadIdentification = 3;
+	
+	# enum TThreadIdentification from e32btrace.h
+	$EThreadCreate = 2;
+	$EThreadName = 4;
+	$EProcessName = 5;
+	$EThreadId = 6;
+	
+	# Context Id bits from e32btrace.h
+	$EContextIdMask = 0x00000003;
+	$EContextIdThread = 0;
+	$EContextIdFIQ = 0x1;
+	$EContextIdIRQ = 0x2;
+	$EContextIdIDFC = 0x3;
+
+	# enum TClassificationRange from e32btraceu.h
+	$EAllRangeFirst = 192;
+	$EAllRangeLast = 222;
+
+	%TCategoryIdToString = 
+		(
+		0 => "ERDebugPrintf",
+		1 => "EKernPrintf",
+		2 => "EPlatsecPrintf",
+		3 => "EThreadIdentification",
+		4 => "ECpuUsage",
+        5 => "EKernPerfLog",
+        6 => "EClientServer",
+        7 => "ERequests",
+        8 => "EChunks",
+        9 => "ECodeSegs",
+		10 => "EPaging",
+		11 => "EThreadPriority",
+		12 => "EPagingMedia",
+		13 => "EKernelMemory",
+		14 => "EHeap",
+		15 => "EMetaTrace",
+		16 => "ERamAllocator",
+		17 => "EFastMutex",
+		18 => "EProfiling", 
+        19 => "EResourceManager",
+        20 => "EResourceManagerUs",
+		21 => "ERawEvent ",
+		128 => "EPlatformSpecificFirst",
+		191 => "EPlatformSpecificLast",
+		192 => "ESymbianExtentionsFirst",
+
+		# UTrace "ALL" range 
+		192 => "EPanic",
+		193 => "EError",
+		194 => "EWarning", 
+		195 => "EBorder", 
+		196 => "EState", 
+		197 => "EInternals", 
+		198 => "EDump", 
+		199 => "EFlow", 
+		200 => "ESystemCharacteristicMetrics", 
+		201 => "EAdhoc",
+
+		253 => "ESymbianExtentionsLast",
+		254 => "ETest1",
+		255 => "ETest2",
+		);
+
+
+	%ProcessNames;
+	%ThreadNames;
+	%ThreadIds;
+	
+	
+	# print column titles
+	if ($PrintFlagFilePos) {printf "FilePos, ";}	# col #0
+	if ($PrintFlagHdrLen) {	printf "Len, ";}		# col #1
+	if ($PrintFlagHdrFlags) {printf "Flags, "; }	# col #2
+	printf "Category, ";			# col #3
+	printf "TimeStamp, ";			# col #4
+	printf "Delta, ";				# col #5
+	printf "context Id, ";			# col #6
+	printf "PC, ";					# col #7
+	printf "UID, ";					# col #8
+	if ($PrintFlagFormatString){printf "Format string, ";}	# col #9
+	printf "Formatted text, ";		# col #10
+	print "\n\n";
+
+	
+	while (1)
+		{
+		my $pos = tell (LOGFILE);
+		
+		# print file pos (col #0)
+		if ($PrintFlagFilePos){	printf ("0x%08X, ", $pos);}
+		
+		my $category;
+		my $subCategory;
+		my $multipartFlags = 0;
+		my $recordData = "";
+		my $recordLen;
+		my $recordPos = 0;
+		
+		$recordLen = ReadRecord(LOGFILE, \$pos, \$recordData, \$category, \$subCategory, \$multipartFlags, $RawMode);
+		if ($recordLen == -1)
+			{last;}
+
+			
+		if (!$RawMode && ($multipartFlags == $EMultipartMiddle || $multipartFlags == $EMultipartLast))
+			{next;}
+					
+#		print record contents
+#		my $buf;
+#					for (my $i=0; $i < $recordLen; $i+=4)
+#						{
+#		$buf.= sprintf ("%08X ", unpack("V", substr($recordData, $recordPos+$i, 4)));
+#						}
+#		printf "\n[$buf\n]";				
+
+
+		# for UTrace "ALL" range, read UID 
+		if ($category >= $EAllRangeFirst && $category <= $EAllRangeLast && 
+			(!$RawMode) && $multipartFlags != $EMultipartMiddle && $multipartFlags != $EMultipartLast)
+			{
+			$uid = unpack("V", substr($recordData, $recordPos, 4));
+			$recordPos+= 4;	
+
+			# then read formatID			
+			$FormatIdIsSubCategory = ($subCategory != 0) ? 1 : 0;
+			if ($FormatIdIsSubCategory)
+				{
+				$formatId = $subCategory
+				}
+			else				
+				{
+				$formatId = unpack("V", substr($recordData, $recordPos, 4));
+  				$recordPos+= 4;
+				}
+			}
+		
+					
+		# print UID (col #8)
+		printf "0x%08X, ", $uid;
+
+			
+		my $formatTable;
+		my $formatString;
+		if ($category >= $EAllRangeFirst && $category <= $EAllRangeLast)
+			{
+			$formatString = $FormatTables{$uid}{$formatId};
+			}
+		else
+			{
+			$formatString = $FormatTables{$category}{$subCategory};
+			}
+
+
+		# Get thread names
+		if ($category == $EThreadIdentification)
+			{
+			if ($subCategory == $EProcessName)
+				{
+				my $process = unpack("V", substr($recordData, 4, 4));
+				my $processName = substr($recordData, 8, $recordLen - 8);	
+#				printf ("\nprocess [%08X] processName [$processName]\n", $process);
+				$ProcessNames{$process} = $processName;
+				}
+			elsif ($subCategory == $EThreadCreate || $subCategory == $EThreadName)
+				{
+				my $thread = unpack("V", substr($recordData, 0, 4));
+				my $process = unpack("V", substr($recordData, 4, 4));
+				my $threadName = substr($recordData, 8, $recordLen - 8);	
+#				printf ("\nprocess [%08X] thread [%08X] threadName [$threadName]\n", $process, $thread, $threadName);
+				$ThreadNames{$thread} = $ProcessNames{$process} . "::" . $threadName;
+				}
+			elsif ($subCategory == $EThreadId)
+				{
+				my $thread = unpack("V", substr($recordData, 0, 4));
+				my $process = unpack("V", substr($recordData, 4, 4));
+				my $threadId = unpack("V", substr($recordData, 8, 4));
+#				printf ("\nprocess [%08X] thread [%08X] threadId [%08X]\n", $process, $thread, $threadId);
+				$ThreadIds{$thread} = $threadId;
+				}
+			}
+			
+			
+		# print Format string (col #9)
+		if ($PrintFlagFormatString)
+			{
+			my $formatStringWithoutCommas = $formatString;
+			$formatStringWithoutCommas=~ s/,/ /g;
+			printf "%s, ", $formatStringWithoutCommas;
+			}
+
+		my $formattedText;
+		
+		my $lenFormatString = length($formatString);
+		if ($lenFormatString && !$RawMode && $multipartFlags != $EMultipartMiddle && $multipartFlags != $EMultipartLast)
+			{
+			for (my $i=0; $i<$lenFormatString; $i++)
+				{
+				my $c = (substr ($formatString, $i, 1));
+#				printf "$c\n";
+				if ($c eq "%")
+					{
+					undef my $fieldLen;
+					$i++;
+	        		$c = (substr ($formatString, $i, 1));
+					if ($c eq "%")
+						{
+						$formattedText.= substr ($formatString, $i, 1);
+						next;
+						}
+					if ($c eq "*")	## take length from buffer
+						{
+						$fieldLen = unpack("V", substr($recordData, $recordPos, 4));
+						if ($fieldLen > $recordLen-$recordPos)
+							{
+							$formattedText.= "*** Invalid field length ***";
+							last;
+							}
+						$recordPos+= 4;
+						$i++;
+		        		$c = (substr ($formatString, $i, 1));
+						}
+					if (lc $c eq "x" || $c eq "h")
+						{
+						if (defined $fieldLen)
+							{
+							if (($fieldLen & 3) == 0)
+								{
+								for (my $i=0; $i< $fieldLen; $i+= 4)
+									{
+									$formattedText.= sprintf ("%08X ", unpack("V", substr($recordData, $recordPos, 4)));
+									$recordPos+= 4;
+									}
+								}
+							else
+								{
+								for (my $i=0; $i< $fieldLen; $i++)
+									{
+									$formattedText.= sprintf ("%02X ", unpack("C", substr($recordData, $recordPos, 1)));
+									$recordPos++;
+									}
+								}
+							}
+						else
+							{
+							$formattedText.= sprintf ("0x%08X", unpack("V", substr($recordData, $recordPos, 4)));
+							$recordPos+= 4;
+							}
+						$recordPos = ($recordPos + 3) & ~3;
+						next;
+						}
+					# display "%ld" as hex for now as don't know how to get perl to use or display a 64 decimal value
+					elsif (lc $c eq "l" && substr ($formatString, $i+1, 1) eq "d")
+						{
+						$i++;
+						my $loWord = unpack("V", substr($recordData, $recordPos, 4));
+						$recordPos+= 4;
+						my $hiWord = unpack("V", substr($recordData, $recordPos, 4));
+						$recordPos+= 4;
+						$formattedText.= sprintf ("0x%X:%08X", $hiWord, $loWord);
+						}
+					elsif (lc $c eq "l" && substr ($formatString, $i+1, 1) eq "x")
+						{
+						$i++;
+						my $loWord = unpack("V", substr($recordData, $recordPos, 4));
+						$recordPos+= 4;
+						my $hiWord = unpack("V", substr($recordData, $recordPos, 4));
+						$recordPos+= 4;
+						$formattedText.= sprintf ("0x%X:%08X", $hiWord, $loWord);
+						}
+					elsif (lc $c eq "d")
+						{
+						$formattedText.= sprintf ("%d", unpack("V", substr($recordData, $recordPos, 4)));
+						$recordPos+= 4;
+						$recordPos = ($recordPos + 3) & ~3;
+						next;
+						}
+					elsif ($c eq "s")
+						{
+						if (!defined $fieldLen) 
+							{$fieldLen = $recordLen - $recordPos;}
+						$formattedText.= substr($recordData, $recordPos, $fieldLen);
+						$recordPos+= $fieldLen; 
+						$recordPos = ($recordPos + 3) & ~3;
+						next;
+						}
+					elsif ($c eq "S")
+						{
+						if (!defined $fieldLen) 
+							{$fieldLen = $recordLen-$recordPos;}
+						for (my $j=0; $j < $fieldLen; $j+=2)
+							{
+					        my $byte = unpack("c", substr ($recordData, $recordPos+$j, 1));
+ 							$formattedText.= sprintf ("%c", $byte);
+							}
+						$recordPos+= $fieldLen; 
+						$recordPos = ($recordPos + 3) & ~3;
+						next;
+						}
+					elsif ($c eq "c")
+						{
+				        my $byte = unpack("c", substr ($recordData, $recordPos, 1));
+						$formattedText.= sprintf ("%c", $byte);
+						}
+					}
+				else
+					{
+					$formattedText.= $c;
+					}
+				}
+			}
+		else	# no format string : print as hex
+			{
+			for (my $i=0; $i < $recordLen; $i+=4)
+				{
+				$formattedText.= sprintf ("%08X ", unpack("V", substr($recordData, $i, 4)));
+				}
+			$recordPos+= $recordLen; $recordLen = 0;
+			
+			}
+		
+
+		# print Formatted text (col #10)
+		$formattedText=~ s/,/;/g;
+		$formattedText=~ s/\r//g;
+		$formattedText=~ s/\n/,/g;
+		printf "%s", $formattedText;
+
+		printf("\n");
+
+		if ($len < 0 || $recordLen < 0)	{die "truncated file";}
+  
+
+		$pos+= ($len +3) & ~3;
+		seek (LOGFILE, $pos, SEEK_SET) or die "truncated file";
+		$i++;
+		}
+
+	close (LOGFILE);
+
+	if ($VerboseMode)
+		{
+		print "*** Processes ***\n";
+		for $id ( keys %ProcessNames )
+			{
+			printf ("process %08X ProcessName %s\n", $id, $ProcessNames{$id});
+			}
+		print "*** Thread ***\n";
+		for $id ( keys %ThreadNames )
+			{
+			printf ("thread %08X ThreadName %s::%X\n", $id, $ThreadNames{$id}, $ThreadIds{$id});
+			}
+		}
+
+    }
+
+    
+sub ReadSingleRecord
+	{
+	($fh, $data, $dataLen, $recordLen, $category, $subCategory, $multipartFlags, $extraN, $totalLen, $offset, $RawMode) = @_;	
+	
+	my $hdr;
+	my $flags;
+	my $header2;
+	my $timestamp;
+	my $timestamp2;
+	my $contextId;
+	my $programConter;	
+	
+	my $recordOffset = 0;
+	
+	$timestampLast;	
+	my $timestampDelta = 0;	
+	
+	my $bytesRead = read($fh, $hdr, 4);
+	
+	
+	if ($bytesRead < 4)	
+		{return -1;}
+
+	($$recordLen,$flags,$$category,$$subCategory) = unpack("CCCC", $hdr);
+	$$dataLen = $$recordLen-4;
+	
+	if ($flags & $EHeader2Present)
+		{$$multipartFlags = (ReadDword($fh) & $EMultipartFlagMask); $$dataLen-= 4}
+	else
+		{$$multipartFlags = 0;}
+	if ($flags & $ETimestampPresent)
+		{$timestamp = ReadDword($fh); $$dataLen-= 4;}
+	if ($flags & $ETimestamp2Present)
+		{$timestamp2 = ReadDword($fh); $$dataLen-= 4;}
+	if ($flags & $EContextIdPresent)
+		{$contextId = ReadDword($fh); $$dataLen-= 4;}
+	if ($flags & $EPcPresent)
+		{$programConter = ReadDword($fh); $$dataLen-= 4;}
+	if ($flags & $EExtraPresent)
+		{$$extraN = ReadDword($fh); $$dataLen-= 4;}
+	if ($$multipartFlags != 0)
+		{
+		$$totalLen = ReadDword($fh);  $$dataLen-= 4;
+		if ($$multipartFlags == $EMultipartMiddle || $$multipartFlags == $EMultipartLast)
+			{$$offset = ReadDword($fh);  $$totalLen-= 4; $$dataLen-= 4;}
+		}				
+
+	$timestampDelta = $timestamp - $timestampLast;
+	$timestampLast = $timestamp;
+
+	read($fh, $$data, ($$dataLen + 3) & ~3);
+
+
+	if ($RawMode || $$multipartFlags == $EMultipartFirst || $$multipartFlags == 0)
+		{
+		# print header len (col #1)
+		if ($PrintFlagHdrLen){printf ("0x%02X, ", $$recordLen);}
+	
+		# print header flags (col #2)
+		if ($PrintFlagHdrFlags)
+			{
+			printf ("%02X ", $flags);
+			if ($flags & $EHeader2Present) {printf "EHeader2Present ";}
+			if ($flags & $ETimestampPresent) {printf "ETimestampPresent ";}
+			if ($flags & $ETimestamp2Present) {printf "ETimestamp2Present ";}
+			if ($flags & $EContextIdPresent) {printf "EContextIdPresent ";}
+			if ($flags & $EPcPresent) {printf "EPcPresent ";}
+			if ($$multipartFlags != 0)
+				{
+				printf "EExtraPresent ";
+				if ($$multipartFlags == $EMultipartFirst) {print "EMultipartFirst ";}
+				elsif ($$multipartFlags == $EMultipartMiddle) {print "EMultipartMiddle ";}
+				elsif ($$multipartFlags == $EMultipartLast) {print "EMultipartLast ";}
+				printf ("ExtraN(0x%08X) ", $$extraN);
+				}
+			if ($flags & $ERecordTruncated) {printf "ERecordTruncated ";}
+			if ($flags & $EMissingRecord) {printf "EMissingRecord ";}
+			print ",";
+			}
+				
+		# print category (col #3)
+		printf "(%d;%d) $categoryString  , ", $$category, $$subCategory;
+	
+		# print timestamp(s) (col #4)
+		printf "0x";
+		if (defined $timestamp2) {printf "%08X : ", $timestamp2;}
+		printf "%08X", $timestamp;
+		printf ", ";;
+	
+		# print timestamp delta (col #5)
+		printf "0x%08X, ", $timestampDelta;
+
+		# print context Id (col #6)
+		if (!$RawMode && defined $ThreadNames{$contextId})
+			{
+			printf ("%s::%X, ", $ThreadNames{$contextId}, $ThreadIds{$contextId});
+			}
+		else			
+			{
+			if ((($contextId & $EContextIdMask) == $EContextIdThread) || $RawMode)
+				{printf "0x%08X, ", $contextId;}
+			elsif (($contextId & $EContextIdMask) == $EContextIdFIQ)
+				{printf "FIQ, ";}
+			elsif (($contextId & $EContextIdMask) == $EContextIdIRQ)
+				{printf "IRQ, ";}
+			elsif (($contextId & $EContextIdMask) == $EContextIdIDFC)
+				{printf "IDFC, ";}
+			}
+	
+		# print Program Counter (col #7)
+		printf "0x%08X, ", $programConter;
+		}
+
+		
+	
+	
+#########################################################
+#	my $hex;
+#	for (my $i=0; $i < $$dataLen; $i+=4)
+#		{
+#		$hex.= sprintf ("%08X ", unpack("V", substr($$data, $i, 4)));
+#		}
+#	printf "\nadding [$hex]\n";
+#########################################################
+	return $bytesRead
+	}
+
+	      
+sub ReadRecord 
+	{
+	($fh, $recordPos, $recordData, $category, $subCategory, $multipartFlags, $RawMode) = @_;
+#	printf "CurrentPos %08X\n", $pos;
+
+
+
+	seek ($fh, $$recordPos, SEEK_SET) or die "truncated file";
+	my $recordLen;
+	my $extraN;
+	my $totalLen;
+	my $offset;
+	my $dataLen;
+	my $data;
+	my $bytesRead;
+	
+	
+	$bytesRead = ReadSingleRecord($fh,  \$data, \$dataLen, \$recordLen, \$$category, \$$subCategory, \$$multipartFlags, \$extraN, \$totalLen, \$offset, $RawMode);
+
+	if ($bytesRead == -1)	# eof ?
+		{return -1; }
+	$$recordPos+= ($recordLen +3) & ~3;
+	
+	$$recordData = $data;
+    $offset = $dataLen;
+
+	$offset-= 4;		# subtract 4 bytes for UID ?????????
+    
+    if ($RawMode || $$multipartFlags != $EMultipartFirst)
+    	{return $dataLen;}
+
+    $pos = $$recordPos;
+
+	while (1)
+		{
+		
+		# find next record, i.e. look for a record which matches $extraN 
+		
+		seek ($fh, $pos, SEEK_SET) or die "truncated file";
+
+		my $recordLen;
+		
+		my $category;
+		my $subCategory;
+		my $multipartFlags;
+		my $currentExtraN;
+		my $currentOffset;
+		
+		my $totalLen;
+		my $currentDataLen;
+		my $data;
+		$bytesRead = ReadSingleRecord($fh, \$data, \$currentDataLen, \$recordLen, \$category, \$subCategory, \$multipartFlags, \$currentExtraN, \$totalLen, \$currentOffset, $RawMode);
+		if ($bytesRead == -1)	# eof ?
+			{return -1; }
+		$pos+= ($recordLen +3) & ~3;
+		
+#		printf "\npos %08X, Seaching for (extra %08X, offset %08X), found (extra %08X, offset %08X)\n",
+#			$pos, $extraN, $offset, $currentExtraN, $currentOffset;
+
+		if ($currentExtraN == $extraN && $currentOffset == $offset)
+			{
+			$$recordData.= $data;
+			$offset+= $currentDataLen;
+			$dataLen+= $currentDataLen;
+			}
+			
+		if ($multipartFlags == $EMultipartLast)
+			{last;}
+		}
+	
+	return $dataLen;
+	}	
+
+sub ReadDword {
+	(my $fh) = @_;
+	my $buffer;
+
+	$bytesRead = read($fh, $buffer, 4);
+	if ($bytesRead < 4) 	{die "truncated file";}
+
+	my $dword = unpack("V", $buffer);
+
+	return $dword
+	};
+
+sub ReadByte {
+	(my $fh) = @_;
+	my $buffer;
+
+	$bytesRead = read($fh, $buffer, 1);
+	if ($bytesRead < 1) 	{die "truncated file";}
+
+	my $byte = unpack("C", $buffer);
+
+	return $byte
+	};
+
+    
+	
+sub PrintFormatTables($)
+	{
+	my ($formatTables) = @_;
+		
+	for $tableIndex ( sort keys %$formatTables )
+		{
+		printf ("SYMTraceFormatCategory %08X:\n", $tableIndex);
+		for $formatId (sort keys %{ $$formatTables{$tableIndex} } )
+			{
+			printf ("%08X => %s\n", $formatId, $$formatTables{$tableIndex}{$formatId});
+			}
+			print "\n";
+		}
+	}
+        
+
+
+sub OutputSawDictionary($)
+	{
+	my ($formatTables) = @_;
+
+
+	# SAW enums
+	$EFieldTypeHexDump = 0;
+	$EFieldTypeHex = 1;
+	$EFieldTypeDecimal = 2;
+	$EFieldTypeStringToEnd = 3;
+	$EFieldTypeNullTerminatedString = 4;
+	$EFieldTypeHexDumpToEnd = 5;
+	$EFieldTypeUnicodeToEnd = 6;
+	$EFieldTypeNullTerminatedUnicode = 7;
+	$EFieldTypeCountedUnicode = 8;
+	$EFieldTypeCountedHexDump = 9;
+	$EFieldTypeCountedString = 10;
+
+	my $moduleIds;	# string containg all UIDs separared by semi-colons
+		
+	for $tableIndex ( sort keys %$formatTables )
+		{
+		if ($tableIndex < 256)
+			{
+			next;
+			}
+		$moduleIds.= sprintf ("%08X;", $tableIndex);
+		
+		printf ("MODULEID_%08X_DESC=\n", $tableIndex);
+		printf ("MODULEID_%08X_NAME=%08X\n", $tableIndex, $tableIndex);
+		
+		my $formatIds;
+		$formatIds = sprintf ("MODULEID_%08X_FORMATIDS=", $tableIndex);
+		
+		for $formatId  (sort keys %{ $$formatTables{$tableIndex} } )
+			{
+			$formatIds.= sprintf ("%d;", $formatId);
+			}
+		printf ("$formatIds\n");
+		
+		
+		for $formatId (sort keys %{ $$formatTables{$tableIndex} } )
+			{
+			my $fieldCount = 0;
+			my $formatString = $$formatTables{$tableIndex}{$formatId};
+			
+#printf ("formatString = (%s)\n", $formatString);
+
+			# format name is the first format string up until the first space or '%' character or end-of line ...
+			$formatString=~ m/^[^%\s]*/;
+			my $formatName = $&;
+			
+			# thow the format name away
+			$formatString = $';
+			
+			# strip the leading space
+			$formatString=~ s/\s*//;
+
+			printf ("MODULEID_%08X_FORMATID_%d_NAME=%s\n", $tableIndex, $formatId, $formatName);
+#printf ("MODULEID_%08X_FORMATID_%d_DESC=\n", $tableIndex, $formatId);
+
+			my $lenFormatString = length($formatString);
+			
+			my $formattedText;
+			my $fieldType = $EFieldTypeHex;
+			my $fieldLen = 0;
+			while (length($formatString))
+				{
+				my $c = (substr ($formatString, 0, 1));
+#print ("[$formatString][$c]\n");				
+				$formatString=~ s/.//;	# strip the leading space
+				if ($c eq "%")
+					{
+#print "found %\n";							
+					my $fieldLenSpecified = 0;
+	        		$c = (substr ($formatString, 0, 1));
+					$formatString=~ s/.//;	# discard char
+#print "c2=$c\n";							
+					if ($c eq "%")
+						{
+						$formattedText.= substr ($formatString, 0, 1);
+						next;
+						}
+					if ($c eq "*")	## take length from buffer
+						{
+						$fieldLenSpecified = 1;
+		        		$c = (substr ($formatString, 0, 1));
+						$formatString=~ s/.//;	# discard char
+						}
+					if (lc $c eq "x" || $c eq "h")
+						{
+						## deal wilth $fieldLenSpecified
+						if ($fieldLenSpecified)
+							{
+							$fieldType = $EFieldTypeCountedHexDump;
+							$fieldLen = 0;
+							}
+						else
+							{
+							$fieldType = $EFieldTypeHex;
+							$fieldLen = 4;
+							}
+						}
+					elsif (lc $c eq "l" && substr ($formatString, 0, 1) eq "d")
+						{
+						$formatString=~ s/.//;	# discard char
+						$fieldType = $EFieldTypeDecimal;
+						$fieldLen = 8;
+						}
+					elsif (lc $c eq "l" && substr ($formatString, 0, 1) eq "x")
+						{
+						$formatString=~ s/.//;	# discard char
+						$fieldType = $EFieldTypeHex;
+						$fieldLen = 8;
+						}
+					elsif (lc $c eq "d")
+						{
+						$fieldType = $EFieldTypeDecimal;
+						$fieldLen = 4;
+						}
+					elsif ($c eq "s")
+						{
+						## deal wilth $fieldLenSpecified
+						if ($fieldLenSpecified)
+							{
+							$fieldType = $EFieldTypeCountedString;
+							$fieldLen = 0;
+							}
+						else
+							{
+							$fieldType = $EFieldTypeStringToEnd;
+							$fieldLen = 0;
+							}
+						}
+					elsif ($c eq "S")
+						{
+						## deal wilth $fieldLenSpecified
+						if ($fieldLenSpecified)
+							{
+							$fieldType = $EFieldTypeCountedUnicode;
+							$fieldLen = 0;
+							}
+						else
+							{
+							$fieldType = EFieldTypeUnicodeToEnd;
+							$fieldLen = 0;
+							}
+						}
+					elsif ($c eq "c")
+						{
+						$fieldType = $EFieldTypeHex;
+						$fieldLen = 1;
+						}
+					printf ("MODULEID_%08X_FORMATID_%d_FIELD_%d_NAME=%s\n", $tableIndex, $formatId, $fieldCount, $formattedText);
+					printf ("MODULEID_%08X_FORMATID_%d_FIELD_%d_TYPE=%s\n", $tableIndex, $formatId, $fieldCount, $fieldType);
+					if ($fieldLen > 0)
+						{printf ("MODULEID_%08X_FORMATID_%d_FIELD_%d_LENGTH=%s\n", $tableIndex, $formatId, $fieldCount, $fieldLen);}
+					$fieldCount++;
+					$formattedText="";
+					
+					$formatString=~ s/\s//;	# strip the leading space
+					}
+				else
+					{
+#					if ($c eq ":") {$formattedText.= '\\'; }
+					$formattedText.= $c;
+					}
+				}
+			printf ("MODULEID_%08X_FORMATID_%d_FIELDS=%d\n", $tableIndex, $formatId, $fieldCount);
+			
+			}
+		print "MODULEIDS=$moduleIds\n";
+		}
+	}
+	
+	
+	
+	
+	
+	
+	        
+        
+sub H2Trace($$)
+{
+	%basictypes = (
+		TInt8		=>	1,
+		TUint8		=>	1,
+		TInt16		=>	2,
+		TUint16		=>	2,
+		TInt32		=>	4,
+		TUint32		=>	4,
+		TInt		=>	4,
+		TUint		=>	4,
+		TBool		=>	4,
+		TInt64		=>	8,
+		TUint64		=>	8,
+		TLinAddr	=>	4,
+		TVersion	=>	4,
+		TPde		=>	4,
+		TPte		=>	4,
+		TProcessPriority => 4,
+		TFormatId	=>  2,
+	);
+	
+	if (scalar(@_)!= 2) {
+		die "perl h2trace.pl <input.h>\n";
+	}
+	my ($infile, $filescope) = @_;
+	
+	if ($VerboseMode)
+		{print "\nOpening $infile\n";}
+	
+	open IN, $infile or die "Can't open $infile for input\n";
+	my $in;
+	while (<IN>) {
+		$in.=$_;
+	}
+	close IN;
+	
+	# First remove any backslash-newline combinations
+	$in =~ s/\\\n//gms;
+	
+	# Remove any character constants
+	$in =~  s/\'(.?(${0})*?)\'//gms;
+	
+	# Strip comments beginning with //
+	$in =~ s/\/\/(.*?)\n/\n/gms;    #//(.*?)\n
+	
+	# Strip comments (/* */) but leave doxygen comments (/** */)
+	$in =~ s/\/\*[^*](.*?)\*\//\n/gms;  #/*(.*?)*/
+	
+	
+	# Collapse whitespace into a single space or newline
+	$in =~ s/\t/\ /gms;
+	$in =~ s/\r/\ /gms;
+	
+	# Tokenize on non-identifier characters
+	my @tokens0 = split(/(\W)/,$in);
+	my @tokens;
+	my $inString = 0;
+	my $inComment = 0;
+	my $string;
+	foreach $t (@tokens0) {
+		next if ($t eq "");
+		next if (!$inString && ($t eq " " or $t eq ""));
+		if ($inComment == 0) 
+			{
+			if ($t eq "/")
+				{$inComment = 1;}
+			}
+		elsif ($inComment == 1) 
+			{
+			if ($t eq "*")
+				{$inComment = 2;}
+			else
+				{$inComment = 0;}
+			}
+		elsif ($inComment == 2) 
+			{
+			if ($t eq "*")
+				{$inComment = 3;}
+			}
+		elsif ($inComment == 3) 
+			{
+			if ($t eq "/")
+				{
+				$inComment = 0;
+		        # if we were in a string, need to push previous '*'
+		        if ($inString)
+		          {
+		          push @tokens, "*";
+		          }
+				$inString = 0;	# end of comment aborts a string
+				$string = "";
+				}
+			else
+				{$inComment = 2;}
+			}
+			
+		if ($t eq "\"")
+			{
+			if (!$inString) 
+				{
+				$inString=1;
+				next;
+				}
+			else
+				{
+				$inString=0;
+				$t = $string;
+				$string = "";
+#				if ($VerboseMode) {print "string : [$t]\n";	}
+				}
+			}
+			
+		if ($inString)
+			{
+			$string.= $t;
+			next;
+			}
+		push @tokens, $t;
+	}
+	
+	my $CurrentTraceFormatString;
+	my $CurrentTraceFormatCategory;
+	# format Key as specified by the @TraceFormatCategory tag is either the current category 
+	# or the current UID
+	my $CurrentFormatTableKey;	
+	
+	
+	my $line=1;
+	parse_scope($filescope, \@tokens, \$line);
+
+	#print $in;
+	#print join (" ", @tokens);
+}	# end of     H2Trace
+	
+
+
+	sub parse_scope($$$) {
+		my ($scope, $tokens, $line) = @_;
+		my $state = 1;
+		
+		my @classes;
+		my $curr_offset=0;
+		my $overall_align=0;
+#		print ">parse_scope $scope->{name}\n";
+		
+		while (scalar(@$tokens))
+			{
+			my $t = shift @$tokens;
+#			printf "t: [$t] [$$line]\n";
+	    	if (!defined ($t)) {
+	      		printf "undefined !";
+	      		next;
+	      	}
+			if ($state>=-1 and $t eq "\n") {
+				++$$line;
+				$state=1;
+				next;
+			} elsif ($state==-1 and $t ne "\n") {
+				next;
+			} elsif ($state==-2 and $t ne ';') {
+				next;
+			}
+			
+			if ($state>0 and $t eq '#') {
+				$t = shift @$tokens;
+				if ($t eq 'define') {
+					my $ident = shift @$tokens;
+					my $defn = shift @$tokens;
+					if ($defn ne '(') {	# don't do macros with parameters
+#					print "MACRO: $ident :== $defn\n";
+					$macros{$ident} = $defn;
+					}
+				}
+				$state=-1;	# skip to next line
+				next;
+			}
+			
+			
+			if (parse_doxygen($scope,$tokens, $line, $t) == 1)
+				{next;}
+	
+			if ($t eq "namespace" ) {
+				$state=0;
+				my %cl;
+				$cl{specifier}=$t;
+				$cl{scope}=$scope;
+				$cl{values}=$scope->{values};
+				$cl{members}=\$scope->{members};
+				$cl{typedefs}=\$scope->{typedefs};
+				$cl{FormatTables}=$scope->{FormatTables};
+				$cl{formatStrings} =$scope->{formatStrings};
+				$cl{formatCategories} =$scope->{formatCategories};
+				
+				my $new_namespace = \%cl;
+				my $n = get_token($scope,$tokens,$line);
+				if ($n !~ /\w+/) {
+					warn "Unnamed $t not supported at line $$line\n";
+					return;
+				}
+				$new_namespace->{name}=$n;
+				my @class_match = grep {$_->{name} eq $n} @classes;
+				my $exists = scalar(@class_match);
+				my $b = get_token($scope,$tokens,$line);
+				if ($b eq ':') {
+					die "Inheritance not supported at line $$line\n";
+				} elsif ($b eq ';') {
+					# forward declaration
+					push @classes, $new_namespace unless ($exists);
+					next;
+				} elsif ($b ne '{') {
+					warn "Syntax error#1 at line $$line\n";
+					return;
+				}
+				if ($exists) {
+					$new_namespace = $class_match[0];
+					if ($new_namespace->{complete}) {
+						warn "Duplicate definition of $cl{specifier} $n\n";
+					}
+				}
+				push @classes, $new_namespace unless ($exists);
+				parse_scope($new_namespace, $tokens, $line);
+				next;
+			}
+			
+			if ($t eq "struct" or $t eq "class" or $t eq "NONSHARABLE_CLASS") {
+				next if ($state==0);
+				$state=0;
+				my %cl;
+				$cl{specifier}=$t;
+				$cl{scope}=$scope;
+				my @members;
+				my @typedefs;
+				$cl{members}=\@members;
+				$cl{typedefs}=\@typedefs;
+				$cl{FormatTables}=$scope->{FormatTables};
+				my $new_class = \%cl;
+				my $n;
+
+				if ($t eq "NONSHARABLE_CLASS")
+					{
+					my $b = get_token($scope,$tokens,$line);
+					if ($b !~ /\(/) {die "Syntax error at line $$line\n";}
+					$n = get_token($scope,$tokens,$line);
+  				$b = get_token($scope,$tokens,$line);
+					if ($b !~ /\)/) {die "Syntax error at line $$line\n";}
+					}
+				else					
+					{
+					$n = get_token($scope,$tokens,$line);
+					}
+								
+				
+				if ($n !~ /\w+/) {
+					warn "Unnamed $t not supported at line $$line\n";
+					return;
+				}
+				$new_class->{name}=$n;
+				my @class_match = grep {$_->{name} eq $n} @classes;
+				my $exists = scalar(@class_match);
+				my $b = get_token($scope,$tokens,$line);
+				#skip inheritance etc until we get to a '{' or \ ';'
+				while ($b ne '{' && $b ne ';')
+					{
+			        $b = get_token($scope,$tokens,$line);
+			        die "Syntax error#2 at line $$line\n" if  (!defined $b);
+					}
+				if ($b eq ';') {
+					# forward declaration
+					push @classes, $new_class unless ($exists);
+					next;
+				} 
+				if ($exists) {
+					$new_class = $class_match[0];
+					if ($new_class->{complete}) {
+						warn "Duplicate definition of $cl{specifier} $n\n";
+					}
+				}
+				push @classes, $new_class unless ($exists);
+				parse_scope($new_class, $tokens, $line);
+				next;
+			} elsif ($t eq "enum") {
+				$state=0;
+				my $n = get_token($scope,$tokens,$line);
+				my $name="";
+				if ($n =~ /\w+/) {
+					$name = $n;
+					$n = get_token($scope,$tokens,$line);
+				}
+				push @enums, $name;
+				if ($n ne '{') {
+					die "Syntax error#4 at line $$line\n";
+				}
+				parse_enum($scope, $tokens, $line, $name);
+				next;
+			} elsif ($t eq '}') {
+				$state=0;
+				if ($scope->{scope}) {
+			        if ($scope->{specifier} eq "namespace")
+			        	{
+						$scope->{complete}=1;
+#						print "Scope completed\n";
+						last;
+						}
+					$t = get_token($scope,$tokens,$line);
+					# skip to next ';'
+					while (defined ($t) and $t ne ';')
+						{$t = get_token($scope,$tokens,$line);}
+					die "Syntax error#5 at line $$line\n" if ($t ne ';');
+					$scope->{complete}=1;
+#					print "Scope completed\n";
+					last;
+				}
+				warn "Syntax error#5 at line $$line\n";
+				return;
+			}
+			$state=0;
+			if ($scope->{scope}) {
+				if ($t eq "public" or $t eq "private" or $t eq "protected") {
+					if (shift (@$tokens) eq ':') {
+						next;	# ignore access specifiers
+					}
+				die "Syntax error#6 at line $$line\n";
+				}
+			}
+			unshift @$tokens, $t;
+			
+			my @currdecl = parse_decl_def($scope, $tokens, $line);
+#			print scalar (@currdecl), "\n";
+			if ($t eq 'static') {
+				next;	# skip static members
+			}
+			my $typedef;
+			if ($t eq 'typedef') {
+#			print "TYPEDEF\n";
+				$typedef = 1;
+				$t = shift @currdecl;
+				$t = $currdecl[0];
+			} else {
+#			print "NOT TYPEDEF\n";
+				$typedef = 0;
+			}
+#			print "$currdecl[0]\n";
+			next if (scalar(@currdecl)==0);
+			
+			if ($t eq "const") {
+				# check for constant declaration
+#				print "CONST $currdecl[1] $currdecl[2] $currdecl[3]\n";
+				my $ctype = lookup_type($scope, $currdecl[1]);
+#				print "$ctype->{basic}    $ctype->{size}\n";
+				if ($ctype->{basic} and $currdecl[2]=~/^\w+$/ and $currdecl[3] eq '=') {
+					if ($typedef!=0) {
+						die "Syntax error#7 at line $$line\n";
+					}
+					shift @currdecl;
+					shift @currdecl;
+					my $type = $ctype->{name};
+					my $name;		#### = shift @currdecl;
+
+					if ($scope->{name})
+						{	
+						$name = $scope->{name} . "::" . shift @currdecl;
+						}
+					else
+						{
+						$name = shift @currdecl;
+						}
+#					printf "[$name,$scope->{name}]";
+					my $size = $ctype->{size};
+					shift @currdecl;
+					my $value = get_constant_expr($scope,\@currdecl,$line);
+					$values{$name} = {type=>$type, size=>$size, value=>$value};
+					next;
+				}
+			}
+			
+			
+			
+		}
+	}
+	
+	sub get_token($$$) {
+		my ($scope,$tokenlist,$line) = @_;
+		while (scalar(@$tokenlist)) {
+			my $t = shift @$tokenlist;
+			return $t if (!defined($t));
+			if (parse_doxygen($scope,$tokenlist, $line, $t) == 1)
+				{next;}
+			if ($t !~ /^[\s]*$/)
+				{
+				if ($$tokenlist[0] eq ":" and $$tokenlist[1] eq ":")
+					{
+					$t.= shift @$tokenlist;
+					$t.= shift @$tokenlist;
+					$t.= shift @$tokenlist;
+#					print "Colon-separated token";
+					}
+				return $t
+				}
+			++$$line;
+		}
+  		return undef;
+	}
+	
+	sub skip_qualifiers($) {
+		my ($tokens) = @_;
+		my $f=0;
+		my %quals = (
+			EXPORT_C => 1,
+			IMPORT_C => 1,
+			inline => 1,
+			virtual => 0,
+			const => 0,
+			volatile => 0,
+			static => 0,
+			extern => 0,
+			LOCAL_C => 0,
+			LOCAL_D => 0,
+			GLDEF_C => 0,
+			GLREF_C => 0,
+			GLDEF_D => 0,
+			GLREF_D => 0
+			);
+		for (;;) {
+			my $t = $$tokens[0];
+			my $q = $quals{$t};
+			last unless (defined ($q));
+			$f |= $q;
+			shift @$tokens;
+		}
+		return $f;
+	}
+	
+	sub parse_indirection($) {
+		my ($tokens) = @_;
+		my $level = 0;
+		for (;;) {
+			my $t = $$tokens[0];
+			if ($t eq '*') {
+				++$level;
+				shift @$tokens;
+				next;
+			}
+			last if ($t ne "const" and $t ne "volatile");
+			shift @$tokens;
+		}
+		return $level;
+	}
+	
+	sub get_operand($$$) {
+		my ($scope,$tokens,$line) = @_;
+		my $t = get_token($scope,$tokens,$line);
+		if ($t eq '-') {
+			my $x = get_operand($scope,$tokens,$line);
+			return -$x;
+		} elsif ($t eq '+') {
+			my $x = get_operand($scope,$tokens,$line);
+			return $x;
+		} elsif ($t eq '~') {
+			my $x = get_operand($scope,$tokens,$line);
+			return ~$x;
+		} elsif ($t eq '!') {
+			my $x = get_operand($scope,$tokens,$line);
+			return $x ? 0 : 1;
+		} elsif ($t eq '(') {
+			my $x = get_constant_expr($scope,$tokens,$line);
+			my $t = get_token($scope,$tokens,$line);
+			if ($t ne ')') {
+				warn "Missing ) at line $$line\n";
+				return undefined;
+			}
+			return $x;
+		} elsif ($t eq "sizeof") {
+			my $ident = get_token($scope,$tokens,$line);
+			if ($ident eq '(') {
+				$ident = get_token($scope,$tokens,$line);
+				my $cb = get_token($scope,$tokens,$line);
+				if ($cb ne ')') {
+					warn "Bad sizeof() syntax at line $$line\n";
+					return undefined;
+				}
+			}
+			$ident = look_through_macros($ident);
+			if ($ident !~ /^\w+$/) {
+				warn "Bad sizeof() syntax at line $$line\n";
+				return undefined;
+			}
+			my $type = lookup_type($scope, $ident);
+			if (!defined $type) {
+				warn "Unrecognised type $ident at line $$line\n";
+				return undefined;
+			}
+			if ($type->{basic}) {
+				return $type->{size};
+			} elsif ($type->{enum}) {
+				return 4;
+			} elsif ($type->{ptr}) {
+				return 4;
+			} elsif ($type->{fptr}) {
+				return 4;
+			}
+			my $al = $type->{class}->{align};
+			my $sz = $type->{class}->{size};
+			return ($sz+$al-1)&~($al-1);
+		}
+		$t = look_through_macros($t);
+		if ($t =~ /^0x/i) {
+			return oct($t);
+		} elsif ($t =~ /^\d/) {
+			return $t;
+		} elsif ($t =~ /^\w+$/) {
+			my $x = lookup_value($scope,$t);
+#			die "Unrecognised identifier '$t' at line $$line\n" unless defined($x);
+			if (!defined($x)) {
+				print "Unrecognised identifier '$t' at line $$line\n" ;
+			}
+			return $x;
+		} elsif ($t =~ /^\w+::\w+$/) {
+			my $x = lookup_value($scope,$t);
+#			die "Unrecognised identifier '$t' at line $$line\n" unless defined($x);
+			if (!defined($x)) {
+				print "Unrecognised identifier '$t' at line $$line\n" ;
+			}
+			return $x;
+		} else {
+			warn "Syntax error#10 at line $$line\n";
+			return undefined;
+		}
+	}
+	
+	sub look_through_macros($) {
+		my ($ident) = @_;
+		while ($ident and $macros{$ident}) {
+			$ident = $macros{$ident};
+		}
+		return $ident;
+	}
+	
+	sub lookup_value($$) {
+		my ($scope,$ident) = @_;
+		while ($scope) {
+			my $vl = $scope->{values};
+			if (defined($vl->{$ident})) {
+				return $vl->{$ident}->{value};
+			}
+			$scope = $scope->{scope};
+		}
+		return undef();
+	}
+	
+	sub lookup_type($$) {
+		my ($scope,$ident) = @_;
+		if ($basictypes{$ident}) {
+			return {scope=>$scope, basic=>1, name=>$ident, size=>$basictypes{$ident} };
+		}
+		while ($scope) {
+			if ($basictypes{$ident}) {
+				return {scope=>$scope, basic=>1, name=>$ident, size=>$basictypes{$ident} };
+			}
+			my $el = $scope->{enums};
+			my $cl = $scope->{classes};
+			my $td = $scope->{typedefs};
+			if (grep {$_ eq $ident} @$el) {
+				return {scope=>$scope, enum=>1, name=>$ident, size=>4 };
+			}
+			my @match_class = (grep {$_->{name} eq $ident} @$cl);
+			if (scalar(@match_class)) {
+				return {scope=>$scope, class=>$match_class[0]};
+			}
+			my @match_td = (grep {$_->{name} eq $ident} @$td);
+			if (scalar(@match_td)) {
+				my $tdr = $match_td[0];
+				my $cat = $tdr->{category};
+				if ($cat eq 'basic' or $cat eq 'enum' or $cat eq 'class') {
+					$ident = $tdr->{alias};
+					next;
+				} else {
+					return { scope=>$scope, $cat=>1, $size=>$tdr->{size} };
+				}
+			}
+			$scope = $scope->{scope};
+		}
+		return undef();
+	}
+	
+	sub get_mult_expr($$$) {
+		my ($scope,$tokens,$line) = @_;
+		my $x = get_operand($scope,$tokens,$line);
+		my $t;
+		for (;;) {
+			$t = get_token($scope,$tokens,$line);
+			if ($t eq '*') {
+				my $y = get_operand($scope,$tokens,$line);
+				$x = $x * $y;
+			} elsif ($t eq '/') {
+				my $y = get_operand($scope,$tokens,$line);
+				if ($y != 0)
+					{$x = int($x / $y);}
+			} elsif ($t eq '%') {
+				my $y = get_operand($scope,$tokens,$line);
+				if ($y != 0)
+					{$x = int($x % $y);}
+			} else {
+				last;
+			}
+		}
+		unshift @$tokens, $t;
+		return $x;
+	}
+	
+	sub get_add_expr($$$) {
+		my ($scope,$tokens,$line) = @_;
+		my $x = get_mult_expr($scope,$tokens,$line);
+		my $t;
+		for (;;) {
+			$t = get_token($scope,$tokens,$line);
+			if ($t eq '+') {
+				my $y = get_mult_expr($scope,$tokens,$line);
+				$x = $x + $y;
+			} elsif ($t eq '-') {
+				my $y = get_mult_expr($scope,$tokens,$line);
+				$x = $x - $y;
+			} else {
+				last;
+			}
+		}
+		unshift @$tokens, $t;
+		return $x;
+	}
+	
+	sub get_shift_expr($$$) {
+		my ($scope,$tokens,$line) = @_;
+		my $x = get_add_expr($scope,$tokens,$line);
+		my $t, $t2;
+		for (;;) {
+			$t = get_token($scope,$tokens,$line);
+			if ($t eq '<' or $t eq '>') {
+				$t2 = get_token($scope,$tokens,$line);
+				if ($t2 ne $t) {
+					unshift @$tokens, $t2;
+					last;
+				}
+			}
+			if ($t eq '<') {
+				my $y = get_add_expr($scope,$tokens,$line);
+				$x = $x << $y;
+			} elsif ($t eq '>') {
+				my $y = get_add_expr($scope,$tokens,$line);
+				$x = $x >> $y;
+			} else {
+				last;
+			}
+		}
+		unshift @$tokens, $t;
+		return $x;
+	}
+	
+	sub get_and_expr($$$) {
+		my ($scope,$tokens,$line) = @_;
+		my $x = get_shift_expr($scope,$tokens,$line);
+		my $t;
+		for (;;) {
+			$t = get_token($scope,$tokens,$line);
+			if ($t eq '&') {
+				my $y = get_shift_expr($scope,$tokens,$line);
+				$x = $x & $y;
+			} else {
+				last;
+			}
+		}
+		unshift @$tokens, $t;
+		return $x;
+	}
+	
+	sub get_xor_expr($$$) {
+		my ($scope,$tokens,$line) = @_;
+		my $x = get_and_expr($scope,$tokens,$line);
+		my $t;
+		for (;;) {
+			$t = get_token($scope,$tokens,$line);
+			if ($t eq '^') {
+				my $y = get_and_expr($scope,$tokens,$line);
+				$x = $x ^ $y;
+			} else {
+				last;
+			}
+		}
+		unshift @$tokens, $t;
+		return $x;
+	}
+	
+	sub get_ior_expr($$$) {
+		my ($scope,$tokens,$line) = @_;
+		my $x = get_xor_expr($scope,$tokens,$line);
+		my $t;
+		for (;;) {
+			$t = get_token($scope,$tokens,$line);
+			if ($t eq '|') {
+				my $y = get_xor_expr($scope,$tokens,$line);
+				$x = $x | $y;
+			} else {
+				last;
+			}
+		}
+		unshift @$tokens, $t;
+		return $x;
+	}
+	
+	sub get_constant_expr($$$) {
+		my ($scope,$tokens,$line) = @_;
+		my $x = get_ior_expr($scope,$tokens,$line);
+		return $x;
+	}
+	
+	sub parse_enum($$$$) {
+		my ($scope,$tokens,$line,$enum_name) = @_;
+		my $vl = $scope->{values};
+		my $fstr = $scope->{formatStrings};
+		my $fcat = $scope->{formatCategories};
+		my $fmtTable = $scope->{FormatTables};
+		
+		my $x = 0;
+		for (;;) {
+			my $t = get_token($scope,$tokens,$line);
+			last if ($t eq '}');
+			if (!defined($t)) {
+				die "Unexpected end of file #2 at line $$line\n";
+			}
+			
+			if ($t eq '#') {
+				next;
+				}
+			
+			if ($t !~ /^\w+$/) {
+				warn "Syntax error#11 at line $$line\n";
+				next;
+			}
+
+			if ($scope->{name})
+				{	
+				$t = $scope->{name} . "::" . $t;
+				}
+
+			if (defined($vl->{$t})) {
+				warn "Duplicate identifier [$t] at line $$line\n";
+			}
+			my $t2 = get_token($scope,$tokens,$line);
+			if ($t2 eq ',') {
+				$vl->{$t} = {type=>$enum_name, size=>4, value=>$x, enum=>1};
+				$fstr->{$t} = $CurrentTraceFormatString; 
+				$fcat->{$t} = $CurrentTraceFormatCategory; 
+				if (defined $CurrentTraceFormatCategory && defined $CurrentTraceFormatString)
+					{ $fmtTable->{$CurrentTraceFormatCategory}{$x} = $CurrentTraceFormatString; }
+				undef $CurrentTraceFormatString;
+				++$x;
+			} elsif ($t2 eq '}') {
+				$vl->{$t} = {type=>$enum_name, size=>4, value=>$x, enum=>1};
+				$fstr->{$t} = $CurrentTraceFormatString; 
+				$fcat->{$t} = $CurrentTraceFormatCategory; 
+				if (defined $CurrentTraceFormatCategory && defined $CurrentTraceFormatString)
+					{ $fmtTable->{$CurrentTraceFormatCategory}{$x} = $CurrentTraceFormatString; }
+				undef $CurrentTraceFormatString;
+				++$x;
+				last;
+			} elsif ($t2 eq '=') {
+				$x = get_constant_expr($scope, $tokens, $line);
+				$vl->{$t} = {type=>$enum_name, size=>4, value=>$x, enum=>1};
+				$fstr->{$t} = $CurrentTraceFormatString; 
+				$fcat->{$t} = $CurrentTraceFormatCategory;
+				if (defined $CurrentTraceFormatCategory && defined $CurrentTraceFormatString)
+					{ $fmtTable->{$CurrentTraceFormatCategory}{$x} = $CurrentTraceFormatString; }
+				undef $CurrentTraceFormatString; 
+				++$x;
+				$t2 = get_token($scope,$tokens,$line);
+				last if ($t2 eq '}');
+				next if ($t2 eq ',');
+				warn "Syntax error#12 at line $$line\n";
+			} else {
+				unshift @$tokens, $t2;
+			}
+		}
+		my $t = get_token($scope,$tokens,$line);
+		if ($t ne ';') {
+			warn "Missing ; at line $$line\n";
+		}
+	}
+	
+	
+	sub  parse_decl_def($$$) {
+		my ($scope,$tokens,$line) = @_;
+		my $level=0;
+		my @decl;
+		while ( scalar(@$tokens) ) {
+			my $t = get_token($scope,$tokens, $line);
+			if ( (!defined ($t) || $t eq ';') and ($level==0)) {
+				return @decl;
+			}
+	
+			if ($t eq "static")
+				{
+				next;
+				}
+	
+			push @decl, $t;
+			if ($t eq '{') {
+				++$level;
+			}
+			if ($t eq '}') {
+				if ($level==0) {
+					warn "Syntax error#13 at line $$line\n";
+					unshift @$tokens, $t;
+					return @decl;
+					
+				}
+				if (--$level==0) {
+					return ();	# end of function definition reached
+				}
+			}
+		}
+		die "Unexpected end of file #3 at line $$line\n";
+	}
+	
+	sub dump_scope($) {
+		my ($scope) = @_;
+		my $el = $scope->{enums};
+		my $cl = $scope->{classes};
+		my $vl = $scope->{values};
+		my $fstr = $scope->{formatStrings};
+		my $fcat = $scope->{formatCategories};
+		print "SCOPE: $scope->{name}\n";
+		if (scalar(@$el)) {
+			print "\tenums:\n";
+			foreach (@$el) {
+				print "\t\t$_\n";
+			}
+		}
+		if (scalar(keys(%$vl))) {
+			print "\tvalues:\n";
+			foreach $vname (keys(%$vl)) {
+				my $v = $vl->{$vname};
+				my $x = $v->{value};
+				my $t = $v->{type};
+				my $sz = $v->{size};
+				my $fstring = $fstr->{$vname};
+				my $fcategory = $fcat->{$vname};
+				if ($v->{enum}) {
+					printf ("\t\t$vname\=$x (enum $t) size=$sz fcat=[0x%x] fstr=[%s]\n", $fcategory,$fstring);
+				} else {
+					printf ("\t\t$vname\=$x (type $t) size=$sz fcat=[0x%x] fstr=[%s]\n", $fcategory, $fstring);
+				}
+			}
+		}
+		if ($scope->{scope}) {
+			my $members = $scope->{members};
+			foreach (@$members) {
+				my $n = $_->{name};
+				my $sz = $_->{size};
+				my $off = $_->{offset};
+				my $spc = $_->{spacing};
+				if (defined $spc) {
+					print "\t$n\[\]\: spacing $spc size $sz offset $off\n";
+				} else {
+					print "\t$n\: size $sz offset $off\n";
+				}
+			}
+			print "\tOverall size : $scope->{size}\n";
+			print "\tOverall align: $scope->{align}\n";
+		}
+		foreach $s (@$cl) {
+			dump_scope($s);
+		}
+	}
+	
+	
+	
+		
+	sub parse_doxygen($$$$) {
+		my ($scope,$tokens,$line,$t) = @_;
+	
+		if ($t ne "/")
+			{
+			return 0;	# not a doxygen comment
+			}
+		if ($t eq "/") {
+			$state=0;
+			my $t2 = shift @$tokens;
+			my $t3 = shift @$tokens;
+	
+			if ($t2 ne "*" || $t3 ne "*")
+				{
+				unshift @$tokens, $t3;
+				unshift @$tokens, $t2;
+				return 0;	# not a doxygen comment
+				}
+		}
+#		printf "doxygen start on line %d\n", $$line;
+		for (;;) {
+			my $t = shift @$tokens;
+			if (!defined($t)) 
+					{
+					warn "Unexpected end of file #4 at line $$line\n";	
+					return
+					}
+			
+			if ($t eq "\n"){++$$line };
+			
+			if ($t eq '*')
+				{
+				my $t2 = shift @$tokens;
+				last if ($t2 eq '/');
+				unshift @$tokens, $t2;
+				}
+			
+			if ($t eq '@')
+				{
+				my $t2 = shift @$tokens;
+				if ($t2 eq 'SYMTraceFormatString')
+					{
+					my $t3 = shift @$tokens;
+#					if ($VerboseMode){print "SYMTraceFormatString = [$t3]\n";}
+					$CurrentTraceFormatString = $t3;
+					}
+				if ($t2 eq 'SYMTraceFormatCategory')
+					{
+					$CurrentTraceFormatCategory = get_operand($scope,$tokens,$line);
+#					if ($VerboseMode){printf ("SYMTraceFormatCategory = 0x%x\n", $CurrentTraceFormatCategory);}
+					}
+				else
+					{
+					unshift @$tokens, $t2;
+					}
+				}
+	
+		}
+#		printf ("doxygen end  on line %d\n", $$line);
+		return 1;	# is a doxygen comment
+	}
+	
+
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+        
+