root/branches/atorf/personal playground/NXCProfiler/Analyzer/AnalyzeNXCProfile.py @ 857

Revision 857, 10.0 KB (checked in by atorf, 3 years ago)

NXCProfiler: First working version with very simple coverage report…

Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4"""
5
6    Author:  Linus Atorf
7    Date:    2009-12-08
8    License: GPLv3
9
10"""
11
12from __future__ import division
13
14from optparse import OptionParser
15
16import os
17import sys
18#import math
19#import platform
20#import time
21#import copy
22
23import HTMLSnippets
24
25
26__author__           = "Linus Atorf"
27__version__          = "0.1"
28__date__             = "2009-12-08"
29__copyright__        = "Copyright © 2009 Linus Atorf"
30__organizationName__ = "LA"
31__applicationName__  = "NXC Profile Analyzer"
32
33
34class StuffHolder(object):
35    pass
36
37
38Consts = StuffHolder()
39Vars   = StuffHolder()
40
41Vars.verbose = False
42
43
44Consts.CODENOTPROFILED = 0
45Consts.CODEDIDNTRUN    = 1
46Consts.CODEPROFILED    = 2
47
48verbosePrint = None  # this is gonne be a method!
49
50#Consts.
51
52
53
54def unflatten4(input):
55    out = ord(input[0]);
56    out = out + ord(input[1]) * 256
57    out = out + ord(input[2]) * 65536
58    out = out + ord(input[3]) * 16777216
59    return out
60#end def
61
62
63
64def printMethod(str):
65    print str
66   
67def emptyMethod(str):
68    pass
69
70
71
72def parseProfileFile(filename):
73    try:
74        f = file(filename, "rb")
75    except (IOError, OSError):
76        print "Could not find or open input file"
77        sys.exit(1)
78    #end try
79
80    out = StuffHolder()
81    try:
82        verbosePrint(" - Parsing firmware string")
83        out.FirmwareString = f.read(3)
84        verbosePrint(" - Parsing enhanced FW flag")
85        if f.read(1) == "1":
86            out.EnhancedFW = True
87        else:
88            out.EnhancedFW = False
89        #end if
90        verbosePrint(" - Parsing global stats")
91        out.TotalRunTime     = unflatten4(f.read(4))
92        out.ProfiledRunTime  = unflatten4(f.read(4))
93        out.MaxSections      = unflatten4(f.read(4))
94    except:
95        f.close()
96        print("Something went wrong parsing the first part of the input file")
97        sys.exit(1)
98    #end try
99
100    out.ExecutionCount   = []
101    out.ExecutionsSameMs = []
102    out.ElapsedTicks     = []
103
104    verbosePrint(" - Parsing section data")
105    try:
106        for i in range(1, out.MaxSections+1):
107            verbosePrint("   . Parsing %d of %d" % (i, out.MaxSections))
108            out.ExecutionCount.append(unflatten4(f.read(4)))
109            out.ExecutionsSameMs.append(unflatten4(f.read(4)))
110            out.ElapsedTicks.append(unflatten4(f.read(4)))
111        #end for
112    except:
113        f.close()
114        print("Something went wrong parsing the second part of the input file")
115        sys.exit(1)
116    #end try
117
118    f.close()
119   
120    return out
121
122#end def
123
124
125def readSourceFile(filename):
126    try:
127        f = file(filename, "r")
128        lines = f.readlines()
129    except (IOError, OSError):
130        print "Could not find, open or read input file"
131        f.close()
132        sys.exit(1)
133    #end try
134
135    f.close()
136    return lines
137#end def
138
139
140#def
141
142def writeStringToFile(filename, str):
143    try:
144        f = file(filename, "w")
145        f.write(str)
146    except (IOError, OSError):
147        print "Could not open output file for writing or write to it"
148        f.close()
149        sys.exit(1)
150    #end try
151
152    f.close()
153#end def
154
155
156
157def findProfilerCommands(lines):
158    out = [];
159   
160    verbosePrint(" - Looking for profiler commands")
161   
162    for i, l in enumerate(lines):
163        tmp = l.strip()
164        if len(tmp) > 0:
165            if tmp.startswith("PROFILER_START"):
166                out.append((i, "start"))
167                verbosePrint('   . found "start" in line %d' % i) 
168            elif tmp.startswith("PROFILER_STOP"):
169                out.append((i, "stop"))
170                verbosePrint('   . found "stop" in line %d' % i)
171            elif tmp.startswith("PROFILER_BEGINSECTION"):
172                val = int(tmp.partition("(")[2].partition(")")[0])
173                out.append((i, "beginsection", val))
174                verbosePrint('   . found "beginsection(%d)" in line %d' % (val, i))
175            elif tmp.startswith("PROFILER_ENDSECTION"):
176                val = int(tmp.partition("(")[2].partition(")")[0])
177                out.append((i, "endsection", val))
178                verbosePrint('   . found "endsection(%d)" in line %d' % (val, i))
179            #end if
180        #end if
181    #end for
182   
183    return out
184   
185#end def
186
187def findProfilerCommandLine(lines, cmd):
188    lineno = -1
189    for p in lines:
190        if p[1] == cmd:
191            lineno = p[0]
192            break
193        #end if
194    #end if
195    if lineno == -1:
196        print "Profiler command %s not found in source file." % cmd
197        sys.exit(1)
198    #end if
199   
200    return lineno
201#end def
202
203def generateCoverageLines(sourcelines, proflines, stats):
204    coverage = [Consts.CODENOTPROFILED for tmp in sourcelines];
205    # for all profiler commands
206    for (i, p) in enumerate(proflines):
207        # look for profiled sections
208        if p[1] == 'beginsection':
209            # find out if code was executed
210            verbosePrint('   . checking section %s, ExecutionCount = %d' % (p[2], stats.ExecutionCount[p[2]]))
211            if stats.ExecutionCount[p[2]] > 0:
212                val = Consts.CODEPROFILED
213            else:
214                val = Consts.CODEDIDNTRUN
215            #end if
216           
217            #NOTE BAD
218            endline = -1
219            for nextp in proflines:
220                if nextp[1] == 'endsection':
221                    if nextp[2] == p[2]:
222                        endline = nextp[0]
223                        break
224            if endline == -1:
225                print "Warning: Didn't find matching profiler command endsection for a certain startsection!"
226                       
227            #end if
228            # set all lines to the end...
229            for j in range(p[0], endline+1):
230                coverage[j] = val
231            #end for
232        #end if
233    return coverage
234
235def generateHTMLHeader(sourcefile):
236    # get everything until <body> (including), fill in title as
237    str = HTMLSnippets.getHTMLHead() % ("Profile results of " + sourcefile);
238    return str
239#end if
240
241def generateHTMLFileSummary(sourcefile, statsfile, stats):
242    isEnhanced = "yes" if stats.EnhancedFW else "no"
243    str = HTMLSnippets.getHTMLFileInfo() % (sourcefile, statsfile, stats.FirmwareString, isEnhanced)   
244    return str
245#end if
246
247def generateHTMLRuntimeSummary(stats):
248    str = HTMLSnippets.getHTMLRuntimeStats() % (stats.TotalRunTime/1000.0, stats.ProfiledRunTime/1000.0)   
249    return str
250
251
252
253
254
255def generateHTMLCodeTable(sourcelines, proflines, stats, coverage):
256    parts = [];
257   
258    startline = findProfilerCommandLine(proflines, "start")
259    stopline = findProfilerCommandLine(proflines, "stop")
260   
261   
262    parts.append(HTMLSnippets.getHTMLCodeStart())
263   
264    for (i, line) in enumerate(sourcelines):
265        escapedCode = line.rstrip().replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;').replace('"', '&quot;');
266        escapedCode = escapedCode.replace('\t', '    ')
267        escapedCode = escapedCode.replace(' ', '&nbsp;')
268        if i == startline or i == stopline:
269            tmp = '<span class="profilercmd">' +  escapedCode + "</span><br>\n"
270        else:
271            if coverage[i] == Consts.CODENOTPROFILED:
272                tmp = '<span class="codenotprofiled">' +  escapedCode + "</span><br>\n"
273            elif coverage[i] == Consts.CODEDIDNTRUN:
274                tmp = '<span class="codedidntrun">' +  escapedCode + "</span><br>\n"
275            else:
276                tmp = '<span class="codeprofiled">' +  escapedCode + "</span><br>\n"
277            #end if
278        #end if
279        parts.append(tmp)
280    #end for
281
282    parts.append("</td></tr>")
283   
284    return ''.join(parts)
285
286
287
288
289def doTheAnalysis(statsfile, sourcefile, outfile):
290    stats       = parseProfileFile(statsfile)
291    sourcelines = readSourceFile(sourcefile)
292    proflines   = findProfilerCommands(sourcelines)
293    coverage    = generateCoverageLines(sourcelines, proflines, stats)
294   
295    verbosePrint(" - Generating HTML output file")
296    HTMLparts = []; # build list of strings, join later...
297    HTMLparts.append(generateHTMLHeader(sourcefile))
298    HTMLparts.append(generateHTMLFileSummary(sourcefile, statsfile, stats))
299    HTMLparts.append(generateHTMLRuntimeSummary(stats))
300    HTMLparts.append(generateHTMLCodeTable(sourcelines, proflines, stats, coverage))
301    HTMLparts.append(HTMLSnippets.getHTMLFooter())
302   
303   
304    writeStringToFile(outfile, ''.join(HTMLparts))
305    verbosePrint(" - %s written." % outfile)
306   
307   
308#end def
309
310
311
312if __name__=='__main__':
313
314    # parse cmd line arguments...
315    parser = OptionParser()
316
317    parser.add_option("-p", "--profileinput",  dest="statsfile",  metavar="<FILE>",            help="Set <FILE> to binary .prf results file from NXT")
318    parser.add_option("-s", "--sourceinput",   dest="sourcefile", metavar="<FILE>",            help="Set <FILE> to NXC sourcecode file which was profiled")
319    parser.add_option("-o", "--output",        dest="outfile",    metavar="<FILE>",            help="Set <FILE> to HTML output file")
320    parser.add_option("-v", "--verbose", dest="verbose", default=False, action="store_true", help="Enable verbose output")
321
322    (options, args) = parser.parse_args()
323
324    Vars.verbose = options.verbose
325    if Vars.verbose:
326        verbosePrint = printMethod
327    else:
328        verbosePrint = emptyMethod
329    #end if
330
331
332    if options.statsfile is None:
333        print("No profiler input file specified")
334        print("Use --help, -? or /? too see available options")
335        sys.exit(1)
336    elif options.sourcefile is None:
337        print("No NXC sourcecode input file specified")
338        print("Use --help, -? or /? too see available options")
339        sys.exit(1)
340    elif options.outfile is None:
341        print("No HTML output file specified")
342        print("Use --help, -? or /? too see available options")
343        sys.exit(1)
344    else:
345        doTheAnalysis(options.statsfile, options.sourcefile, options.outfile)
346
347#end main
348
349
350
351
352
353
Note: See TracBrowser for help on using the browser.