fbs/fontandbitmapserver/utils/fbsbitmap_memory.pl
branchRCL_3
changeset 209 5c40347c6f16
parent 186 1bc91eb0b8ae
equal deleted inserted replaced
186:1bc91eb0b8ae 209:5c40347c6f16
     1 #!/usr/local/bin/perl
       
     2 #
       
     3 # Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies).
       
     4 # All rights reserved.
       
     5 # This component and the accompanying materials are made available
       
     6 # under the terms of "Eclipse Public License v1.0"
       
     7 # which accompanies this distribution, and is available
       
     8 # at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     9 #
       
    10 # Initial Contributors:
       
    11 # Nokia Corporation - initial contribution.
       
    12 #
       
    13 # Contributors:
       
    14 #
       
    15 # Description:
       
    16 #  This script parses trace data produced by OST from FBS, using the the FBSCLI, 
       
    17 #  FBSERV and Symbian BTrace Hooks OST dictionaries, to produce a CSV output of
       
    18 #  the amount of bitmap memory used per-thread over a user-definable time
       
    19 #  granularity, since the start of the trace.
       
    20 # 
       
    21 #  To use, enable SYMBIAN_KERNEL_THREAD_IDENTIFICATION trace group in Symbian
       
    22 #  BTrace Hooks OST dictionary, GRAPHICS_RESOURCE_MANAGEMENT_SEMANTICS in FBSERV
       
    23 #  OST dictionary, and GRAPHICS_RESOURCE_MANAGEMENT_SEMANTICS, 
       
    24 #  GRAPHICS_RESOURCE_MANAGEMENT_FUNCTIONS and GRAPHICS_CONTROL_FUNCTIONS in
       
    25 #  FBSCLI OST dictionary. Once tracing is gathered, save trace output as ascii 
       
    26 #  and run this script against it. The resulting file can then be imported into
       
    27 #  a spreadsheet application to be visually processed.
       
    28 #  
       
    29 
       
    30 use strict;
       
    31 
       
    32 # Sanity checking of the command line parameters...
       
    33 if ($#ARGV == -1 || $ARGV[0] eq "help" || $ARGV[0] eq "/?")
       
    34 {
       
    35    print "\nusage: $0 filename [-h]\n";
       
    36    print "where\n";
       
    37    print " -h : Specifies the heartbeat in millisecs (default=10000)\n";
       
    38    print " -d : Ignore duplicated handles\n";
       
    39    exit;
       
    40 }
       
    41 
       
    42 
       
    43 ## Modifiable constants...
       
    44 my $CSV_DELIMITER = ',';
       
    45 
       
    46 # Time after start to take first snapshot, in millisecs
       
    47 my $firstHeartBeatTimeMS = 1000;
       
    48 
       
    49 # Default heartbeat in millisecs if none specified.
       
    50 my $heartBeatMS = 10000;
       
    51 
       
    52 
       
    53 ##
       
    54 ## Internal structures...
       
    55 ##
       
    56 my $heartBeatCount = 0;
       
    57 my $nextHeartBeatMS = -1;
       
    58 my $logLastLineTimeMS = 0;
       
    59 
       
    60 # A hash of thread names to the amount of bitmap memory they 
       
    61 # have used since the start of the trace.
       
    62 my %bmpMemoryPerThread = ();
       
    63 
       
    64 # A hash of bitmaps fully qualified by the session they belong to,
       
    65 # and their local handle (because bitmaps can have the same local
       
    66 # handle in different threads), mapped to their size in bytes.
       
    67 my %bmpMemoryByServerHandle = ();
       
    68 
       
    69 # Hash of FbsSessions to thread IDs.
       
    70 my %SessionThreadMap = ();
       
    71 
       
    72 # Array of the above hashes, one hash per heartbeat.
       
    73 my @arrayOfSnapshots;
       
    74 
       
    75 # Count duplicated handles as memory used by a thread/process. 
       
    76 my $countDuplicates = 1;
       
    77 
       
    78 # Hashes of thread and process names to IDs.
       
    79 my %ThreadNames;
       
    80 my %ProcessNames;
       
    81 
       
    82 
       
    83 ##
       
    84 ## Command line options parsing...
       
    85 ## First arg is assumed to be the filename.
       
    86 ##
       
    87 for my $i (1..$#ARGV)
       
    88 {
       
    89    my $cma = $ARGV[$i];
       
    90    if ($cma =~ m/-h(\d*)/)
       
    91    {
       
    92       $heartBeatMS = $1;
       
    93    }
       
    94    elsif ($cma =~ m/-d/)
       
    95    {
       
    96       $countDuplicates = 0;
       
    97    }
       
    98    else
       
    99    {
       
   100       print "Unrecognised parameter: $cma , ignoring...\n";
       
   101    }
       
   102 }
       
   103 
       
   104 ## Read from the file.
       
   105 ## Read the log into an array line by line.
       
   106 my $TRACE_FILENAME = $ARGV[0];
       
   107 open(INPUT_FILE, '<', $TRACE_FILENAME) or die $!;
       
   108 binmode(INPUT_FILE);
       
   109 
       
   110 ##
       
   111 ## Parse each line sequentially...
       
   112 ##
       
   113 while ( my $line = <INPUT_FILE> )
       
   114 {
       
   115    my $timeFromMidnightMS;
       
   116 
       
   117    ## 
       
   118    ## If this line is about a new process, make a note of the name and the
       
   119    ## associated process id, so that FbsSessions can be mapped to their 
       
   120    ## thread by name.
       
   121    ##
       
   122    if ($line =~ /^.*Thread:Process name assigned;NThread:(.*);DProcess:(.*);Name:(.*)$/i)
       
   123    {
       
   124       my $threadId  = $1;
       
   125       my $processId = $2;
       
   126       my $processName = $3;
       
   127       $ProcessNames{$processId} = $processName ;
       
   128    }
       
   129 
       
   130    ## 
       
   131    ## If this line is about a new process, make a note of the name and the
       
   132    ## associated process id, so that FbsSessions can be mapped to their 
       
   133    ## thread by name when the csv is generated.
       
   134    ##
       
   135    if (($line =~ /^.*Thread:Thread created;NThread:(.*);DProcess:(.*);Name:(.*)$/i) ||
       
   136       ($line =~ /^.*Thread:Thread name assigned;NThread:(.*);DProcess:(.*);Name:(.*)$/i))
       
   137       {
       
   138       my $threadId  = $1;
       
   139       my $processId = $2;
       
   140       my $threadName = $3;
       
   141       my $fullThreadName = $ProcessNames{$processId} . ":" . $threadName;
       
   142       $ThreadNames{$threadId} = $fullThreadName;
       
   143    }
       
   144 
       
   145    ##
       
   146    ## Determine timestamp. If this time is beyond the heartbeat, 
       
   147    ## take a snapshot and 
       
   148    ##
       
   149    if ($line =~ /^(\d\d):(\d\d):(\d\d)\.(\d{3})/)
       
   150    {
       
   151       $timeFromMidnightMS = ((($1 * 3600) + ($2 * 60) + $3) * 1000) + $4;
       
   152       if ($nextHeartBeatMS == -1) 
       
   153       {
       
   154          $nextHeartBeatMS = $firstHeartBeatTimeMS;
       
   155          $logLastLineTimeMS = $timeFromMidnightMS;
       
   156       }
       
   157       ## We have wrapped around midnight!
       
   158       ## Add a 1000ms cushion to the comparison to avoid wrapping around 
       
   159       ## midnight if a trace is buffered too slowly.
       
   160       if (($timeFromMidnightMS+1000) < $logLastLineTimeMS)
       
   161       {
       
   162          $timeFromMidnightMS += 86400000;
       
   163       }
       
   164       $nextHeartBeatMS -= ($timeFromMidnightMS - $logLastLineTimeMS);
       
   165       $logLastLineTimeMS = $timeFromMidnightMS;
       
   166 
       
   167       ##
       
   168       ## If heartbeat reached, take snapshot of bmp memory per thread
       
   169       ## and set next heartbeat time.
       
   170       ##
       
   171       while ($nextHeartBeatMS <= 0)
       
   172       {
       
   173          $nextHeartBeatMS += $heartBeatMS;
       
   174          # take a snapshot of the current bitmap memory usage per thread
       
   175          while ((my $thread, my $bmpMemory) = each(%bmpMemoryPerThread))
       
   176          {
       
   177               $arrayOfSnapshots[$heartBeatCount]{$thread} = $bmpMemory;
       
   178          }
       
   179          $heartBeatCount++;
       
   180       }
       
   181    }
       
   182 
       
   183    ## FBS Client-side traces.
       
   184    if ($line =~ m/\tFBSCLI: /)
       
   185    {
       
   186       ##
       
   187       ## If this line is an FBSCLI trace, and it contains iSSH then
       
   188       ## it gives a chance to map a client thread ID to a session handle.
       
   189       ## 
       
   190       if ( $line =~ m/iSSH=(\w*);.*Thread ID:(.*)$/)
       
   191       {
       
   192          my $ServerSessionHandle = $1;
       
   193          my $thread = $2;
       
   194          if ($thread ne "0x00000000")
       
   195          {
       
   196             $SessionThreadMap{$ServerSessionHandle} = $thread;
       
   197          }
       
   198       }
       
   199    }
       
   200 
       
   201    ## 
       
   202    ## FBS Server-side traces.
       
   203    ##
       
   204    if ($line =~ m/\tFBSERV: /)
       
   205    {
       
   206       ## The line must have a s= parameter to be useful - the session server handle.
       
   207       ## Any FBSERV line without this is not considered for parsing.
       
   208       if ($line =~ m/; iSSH=(\w*);/)
       
   209       {
       
   210          my $FbsSessionHandle = $1;
       
   211          my $thread = "Unknown Thread [Session=$FbsSessionHandle]";
       
   212          if (defined($SessionThreadMap{$FbsSessionHandle}))
       
   213          {
       
   214             $thread = $SessionThreadMap{$FbsSessionHandle};
       
   215          }
       
   216          if ($line =~ m/# Server resource destroyed; .*iH=(\w*);/)
       
   217          {
       
   218             my $bmpHandle = $1;
       
   219             my $bmpIdentifier = "$FbsSessionHandle:$bmpHandle";
       
   220             if (defined($bmpMemoryByServerHandle{$bmpIdentifier}))
       
   221             {
       
   222                $bmpMemoryPerThread{$thread} -= $bmpMemoryByServerHandle{$bmpIdentifier};
       
   223                delete $bmpMemoryByServerHandle{$bmpIdentifier};
       
   224             }
       
   225          }
       
   226          if ($line =~ m/# Server bitmap resized; .*iOldH=(\w*); iNewH=(\w*); newbytes=(\d*);/)
       
   227          {
       
   228             # When a bitmap is resized, the amount of memory held by the bitmap may change
       
   229             # and the bitmap localhandle will change.
       
   230             my $oldBmpHandle = $1;
       
   231             my $newBmpHandle = $2;
       
   232             my $newBmpBytes = $3;
       
   233             my $oldBmpIdentifier = "$FbsSessionHandle:$oldBmpHandle";
       
   234             my $newBmpIdentifier = "$FbsSessionHandle:$newBmpHandle";
       
   235             if (defined($bmpMemoryByServerHandle{$oldBmpIdentifier}))
       
   236             {
       
   237                $bmpMemoryPerThread{$thread} -= $bmpMemoryByServerHandle{$oldBmpIdentifier};
       
   238                delete $bmpMemoryByServerHandle{$oldBmpIdentifier};
       
   239             }
       
   240             $bmpMemoryPerThread{$thread} += $newBmpBytes;
       
   241             $bmpMemoryByServerHandle{$newBmpIdentifier} = $newBmpBytes;           
       
   242          }
       
   243          elsif ($line =~ m/#.*iOldH=(\w*); iNewH=(\w*);/)
       
   244          {
       
   245             # When a bitmap is compressed, cleaned or resized, the bitmap local handle changes
       
   246             my $oldBmpHandle = $1;
       
   247             my $newBmpHandle = $2;
       
   248             my $oldBmpIdentifier = "$FbsSessionHandle:$oldBmpHandle";
       
   249             my $newBmpIdentifier = "$FbsSessionHandle:$newBmpHandle";
       
   250             if (defined($bmpMemoryByServerHandle{$oldBmpIdentifier}))
       
   251             {
       
   252                my $bytes = $bmpMemoryByServerHandle{$oldBmpIdentifier};
       
   253                delete $bmpMemoryByServerHandle{$oldBmpIdentifier};
       
   254                $bmpMemoryByServerHandle{$newBmpIdentifier} = $bytes;
       
   255             }
       
   256          }
       
   257          elsif ($line =~ m/# Server bitmap duplicated.*iH=(\w*);.*bytes=(\d+);/)
       
   258          {
       
   259             if ($countDuplicates == 1)
       
   260             {
       
   261                # When a bitmap is duplicated, the memory is 'owned' by all
       
   262                # threads that duplicate it.
       
   263                my $bmpHandle = $1;
       
   264                my $bmpBytes = $2;
       
   265                my $bmpIdentifier = "$FbsSessionHandle:$bmpHandle";
       
   266                $bmpMemoryPerThread{$thread} += $bmpBytes;
       
   267                $bmpMemoryByServerHandle{$bmpIdentifier} = $bmpBytes;
       
   268             }
       
   269          }
       
   270          elsif ($line =~ m/#.*iH=(\w*);.*bytes=(\d+);/)
       
   271          {
       
   272             my $bmpHandle = $1;
       
   273             my $bmpBytes = $2;
       
   274             my $bmpIdentifier = "$FbsSessionHandle:$bmpHandle";
       
   275             $bmpMemoryPerThread{$thread} += $bmpBytes;
       
   276             $bmpMemoryByServerHandle{$bmpIdentifier} = $bmpBytes;
       
   277          }
       
   278       }
       
   279    }
       
   280 }
       
   281 
       
   282 close (INPUT_FILE);
       
   283 
       
   284 
       
   285 ##
       
   286 ## Make a map of unique threads across all snapshots
       
   287 ## This is so only one occurrence of each thread will appear
       
   288 ## in the csv file.
       
   289 ##
       
   290 my %uniqueThreads = ();
       
   291 for my $i (0..$#arrayOfSnapshots)
       
   292 {
       
   293    for my $thread (keys %{$arrayOfSnapshots[$i]})
       
   294    {
       
   295       $uniqueThreads{$thread} = 1;
       
   296    }
       
   297 }
       
   298 
       
   299 ##
       
   300 ## Start writing to file.
       
   301 ## First row, which contains the heartbeat number column headings...
       
   302 ##
       
   303 my $OUTPUT_FILENAME = sprintf("%s.csv", $TRACE_FILENAME);
       
   304 open(OUTPUT_FILE,">$OUTPUT_FILENAME") or die $!;
       
   305 print OUTPUT_FILE "Session$CSV_DELIMITER";
       
   306 for my $i (0..$heartBeatCount)
       
   307 {
       
   308     print OUTPUT_FILE "$i$CSV_DELIMITER";
       
   309 }
       
   310 
       
   311 ##
       
   312 ## For each subsequent row, print the first thread and the
       
   313 ## memory at each snapshot...
       
   314 ##
       
   315 print OUTPUT_FILE "\n";
       
   316 while ((my $thread, my $dummy) = each(%uniqueThreads))
       
   317 {
       
   318     # Resolve the thread to its full name...
       
   319     print OUTPUT_FILE "$thread";
       
   320     if (defined($ThreadNames{$thread}) )
       
   321     {
       
   322        my $threadName = $ThreadNames{$thread};
       
   323        print OUTPUT_FILE ":$threadName";
       
   324     }
       
   325     print OUTPUT_FILE "$CSV_DELIMITER";
       
   326 
       
   327     # print the memory use per thread, for each snapshot...
       
   328     for my $i (0..$#arrayOfSnapshots)
       
   329     {
       
   330        my %snapshot = %{$arrayOfSnapshots[$i]};
       
   331        while ((my $snapshotThread, my $bmpMemory) = each(%snapshot))
       
   332        {
       
   333            if ($snapshotThread eq $thread) 
       
   334            {
       
   335               print OUTPUT_FILE "$bmpMemory";
       
   336            }
       
   337        }
       
   338        print OUTPUT_FILE "$CSV_DELIMITER";
       
   339     }
       
   340     print OUTPUT_FILE "\n";
       
   341 }
       
   342 close (OUTPUT_FILE);
       
   343