stats.py revision 13540
1#!/usr/bin/env python2.7
2
3# Copyright (c) 2003-2004 The Regents of The University of Michigan
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted provided that the following conditions are
8# met: redistributions of source code must retain the above copyright
9# notice, this list of conditions and the following disclaimer;
10# redistributions in binary form must reproduce the above copyright
11# notice, this list of conditions and the following disclaimer in the
12# documentation and/or other materials provided with the distribution;
13# neither the name of the copyright holders nor the names of its
14# contributors may be used to endorse or promote products derived from
15# this software without specific prior written permission.
16#
17# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28#
29# Authors: Nathan Binkert
30
31import re, sys, math
32
33def usage():
34    print '''\
35Usage: %s [-E] [-F] [ -G <get> ] [-d <db> ] [-g <graphdir> ] [-h <host>] [-p]
36       [-s <system>] [-r <runs> ] [-T <samples>] [-u <username>]
37       <command> [command args]
38
39       commands    extra parameters   description
40       ----------- ------------------ ---------------------------------------
41       formula     <formula>          Evaluated formula specified
42       formulas    [regex]            List formulas (only matching regex)
43       runs        none               List all runs in database
44       samples     none               List samples present in database
45       stability   <pairnum> <stats>  Calculated statistical info about stats
46       stat        <regex>            Show stat data (only matching regex)
47       stats       [regex]            List all stats (only matching regex)
48
49       database    <command>          Where command is drop, init, or clean
50
51''' % sys.argv[0]
52    sys.exit(1)
53
54def getopts(list, flags):
55    import getopt
56    try:
57        opts, args = getopt.getopt(list, flags)
58    except getopt.GetoptError:
59        usage()
60
61    return opts, args
62
63class CommandException(Exception):
64    pass
65
66def commands(options, command, args):
67    if command == 'database':
68        if len(args) == 0: raise CommandException
69
70        import dbinit
71        mydb = dbinit.MyDB(options)
72
73        if args[0] == 'drop':
74            if len(args) > 2: raise CommandException
75            mydb.admin()
76            mydb.drop()
77            if len(args) == 2 and args[1] == 'init':
78                mydb.create()
79                mydb.connect()
80                mydb.populate()
81            mydb.close()
82            return
83
84        if args[0] == 'init':
85            if len(args) > 1: raise CommandException
86            mydb.admin()
87            mydb.create()
88            mydb.connect()
89            mydb.populate()
90            mydb.close()
91            return
92
93        if args[0] == 'clean':
94            if len(args) > 1: raise CommandException
95            mydb.connect()
96            mydb.clean()
97            return
98
99        raise CommandException
100
101    import db
102    source = db.Database()
103    source.host = options.host
104    source.db = options.db
105    source.passwd = options.passwd
106    source.user = options.user
107    source.connect()
108    #source.update_dict(globals())
109
110    if type(options.method) is str:
111        source.method = options.method
112
113    if options.runs is None:
114        runs = source.allRuns
115    else:
116        rx = re.compile(options.runs)
117        runs = []
118        for run in source.allRuns:
119            if rx.match(run.name):
120                runs.append(run)
121
122    if command == 'runs':
123        user = None
124        opts, args = getopts(args, '-u')
125        if len(args):
126            raise CommandException
127        for o,a in opts:
128            if o == '-u':
129                user = a
130        source.listRuns(user)
131        return
132
133    if command == 'stats':
134        if len(args) == 0:
135            source.listStats()
136        elif len(args) == 1:
137            source.listStats(args[0])
138        else:
139            raise CommandException
140
141        return
142
143    if command == 'formulas':
144        if len(args) == 0:
145            source.listFormulas()
146        elif len(args) == 1:
147            source.listFormulas(args[0])
148        else:
149            raise CommandException
150
151        return
152
153    if command == 'samples':
154        if len(args):
155            raise CommandException
156
157        source.listTicks(runs)
158        return
159
160    if command == 'stability':
161        if len(args) < 2:
162            raise CommandException
163
164        try:
165            merge = int(args[0])
166        except ValueError:
167            usage()
168        stats = source.getStat(args[1])
169        source.method = 'sum'
170
171        def disp(*args):
172            print "%-35s %12s %12s %4s %5s %5s %5s %10s" % args
173
174        # temporary variable containing a bunch of dashes
175        d = '-' * 100
176
177        #loop through all the stats selected
178        for stat in stats:
179            print "%s:" % stat.name
180            disp("run name", "average", "stdev", ">10%", ">1SDV", ">2SDV",
181                 "SAMP", "CV")
182            disp(d[:35], d[:12], d[:12], d[:4], d[:5], d[:5], d[:5], d[:10])
183
184            #loop through all the selected runs
185            for run in runs:
186                runTicks = source.retTicks([ run ])
187                #throw away the first one, it's 0
188                runTicks.pop(0)
189                source.ticks = runTicks
190                avg = 0
191                stdev = 0
192                numoutsideavg  = 0
193                numoutside1std = 0
194                numoutside2std = 0
195                pairRunTicks = []
196                if value(stat, run.run) == 1e300*1e300:
197                    continue
198                for t in range(0, len(runTicks)-(merge-1), merge):
199                    tempPair = []
200                    for p in range(0,merge):
201                        tempPair.append(runTicks[t+p])
202                    pairRunTicks.append(tempPair)
203                #loop through all the various ticks for each run
204                for tick in pairRunTicks:
205                    source.ticks = tick
206                    avg += value(stat, run.run)
207                avg /= len(pairRunTicks)
208                for tick in pairRunTicks:
209                    source.ticks = tick
210                    val = value(stat, run.run)
211                    stdev += pow((val-avg),2)
212                stdev = math.sqrt(stdev / len(pairRunTicks))
213                for tick in pairRunTicks:
214                    source.ticks = tick
215                    val = value(stat, run.run)
216                    if (val < (avg * .9)) or (val > (avg * 1.1)):
217                        numoutsideavg += 1
218                    if (val < (avg - stdev)) or (val > (avg + stdev)):
219                        numoutside1std += 1
220                    if (val < (avg - (2*stdev))) or (val > (avg + (2*stdev))):
221                        numoutside2std += 1
222                if avg > 1000:
223                    disp(run.name, "%.1f" % avg, "%.1f" % stdev,
224                         "%d" % numoutsideavg, "%d" % numoutside1std,
225                         "%d" % numoutside2std, "%d" % len(pairRunTicks),
226                         "%.3f" % (stdev/avg*100))
227                elif avg > 100:
228                    disp(run.name, "%.1f" % avg, "%.1f" % stdev,
229                         "%d" % numoutsideavg, "%d" % numoutside1std,
230                         "%d" % numoutside2std, "%d" % len(pairRunTicks),
231                         "%.5f" % (stdev/avg*100))
232                else:
233                    disp(run.name, "%.5f" % avg, "%.5f" % stdev,
234                         "%d" % numoutsideavg, "%d" % numoutside1std,
235                         "%d" % numoutside2std, "%d" % len(pairRunTicks),
236                         "%.7f" % (stdev/avg*100))
237        return
238
239    if command == 'all':
240        if len(args):
241            raise CommandException
242
243        all = [ 'bps', 'misses', 'mpkb', 'ipkb', 'pps', 'bpt' ]
244        for command in all:
245            commands(options, command, args)
246
247    if options.ticks:
248        if not options.graph:
249            print 'only displaying sample %s' % options.ticks
250        source.ticks = [ int(x) for x in options.ticks.split() ]
251
252    from output import StatOutput
253    output = StatOutput(options.jobfile, source)
254    output.xlabel = 'System Configuration'
255    output.colormap = 'RdYlGn'
256
257    if command == 'stat' or command == 'formula':
258        if len(args) != 1:
259            raise CommandException
260
261        if command == 'stat':
262            stats = source.getStat(args[0])
263        if command == 'formula':
264            stats = eval(args[0])
265
266        for stat in stats:
267            output.stat = stat
268            output.ylabel = stat.name
269            if options.graph:
270                output.graph(stat.name, options.graphdir)
271            else:
272                output.display(stat.name, options.printmode)
273
274        return
275
276    if len(args):
277        raise CommandException
278
279    from info import ProxyGroup
280    proxy = ProxyGroup(system = source[options.system])
281    system = proxy.system
282
283    etherdev = system.tsunami.etherdev0
284    bytes = etherdev.rxBytes + etherdev.txBytes
285    kbytes = bytes / 1024
286    packets = etherdev.rxPackets + etherdev.txPackets
287
288    def display():
289        if options.graph:
290            output.graph(command, options.graphdir, proxy)
291        else:
292            output.display(command, options.printmode)
293
294    if command == 'ticks':
295        output.stat = system.run0.numCycles
296
297        display()
298        return
299
300    if command == 'bytes':
301        output.stat = bytes
302        display()
303        return
304
305    if command == 'packets':
306        output.stat = packets
307        display()
308        return
309
310    if command == 'ppt' or command == 'tpp':
311        output.stat = packets / system.run0.numCycles
312        output.invert = command == 'tpp'
313        display()
314        return
315
316    if command == 'pps':
317        output.stat = packets / source['sim_seconds']
318        output.ylabel = 'Packets/s'
319        display()
320        return
321
322    if command == 'bpt' or command == 'tpb':
323        output.stat = bytes / system.run0.numCycles * 8
324        output.ylabel = 'bps / Hz'
325        output.invert = command == 'tpb'
326        display()
327        return
328
329    if command in ('rxbps', 'txbps', 'bps'):
330        if command == 'rxbps':
331            output.stat = etherdev.rxBandwidth / 1e9
332        if command == 'txbps':
333            output.stat = etherdev.txBandwidth / 1e9
334        if command == 'bps':
335            output.stat = (etherdev.rxBandwidth + etherdev.txBandwidth) / 1e9
336
337        output.ylabel = 'Bandwidth (Gbps)'
338        output.ylim = [ 0.0, 10.0 ]
339        display()
340        return
341
342    if command == 'bpp':
343        output.stat = bytes / packets
344        output.ylabel = 'Bytes / Packet'
345        display()
346        return
347
348    if command == 'rxbpp':
349        output.stat = etherdev.rxBytes / etherdev.rxPackets
350        output.ylabel = 'Receive Bytes / Packet'
351        display()
352        return
353
354    if command == 'txbpp':
355        output.stat = etherdev.txBytes / etherdev.txPackets
356        output.ylabel = 'Transmit Bytes / Packet'
357        display()
358        return
359
360    if command == 'rtp':
361        output.stat = etherdev.rxPackets / etherdev.txPackets
362        output.ylabel = 'rxPackets / txPackets'
363        display()
364        return
365
366    if command == 'rtb':
367        output.stat = etherdev.rxBytes / etherdev.txBytes
368        output.ylabel = 'rxBytes / txBytes'
369        display()
370        return
371
372    misses = system.l2.overall_mshr_misses
373
374    if command == 'misses':
375        output.stat = misses
376        output.ylabel = 'Overall MSHR Misses'
377        display()
378        return
379
380    if command == 'mpkb':
381        output.stat = misses / (bytes / 1024)
382        output.ylabel = 'Misses / KB'
383        display()
384        return
385
386    if command == 'ipkb':
387        interrupts = system.run0.kern.faults[4]
388        output.stat = interrupts / kbytes
389        output.ylabel = 'Interrupts / KB'
390        display()
391        return
392
393    if command == 'execute':
394        output.stat = system.run0.ISSUE__count
395        display()
396        return
397
398    if command == 'commit':
399        output.stat = system.run0.COM__count
400        display()
401        return
402
403    if command == 'fetch':
404        output.stat = system.run0.FETCH__count
405        display()
406        return
407
408    raise CommandException
409
410
411class Options: pass
412
413if __name__ == '__main__':
414    import getpass
415
416    options = Options()
417    options.host = None
418    options.db = None
419    options.passwd = ''
420    options.user = getpass.getuser()
421    options.runs = None
422    options.system = 'client'
423    options.method = None
424    options.graph = False
425    options.ticks = False
426    options.printmode = 'G'
427    jobfilename = None
428    options.jobfile = None
429    options.all = False
430
431    opts, args = getopts(sys.argv[1:], '-EFJad:g:h:j:m:pr:s:u:T:')
432    for o,a in opts:
433        if o == '-E':
434            options.printmode = 'E'
435        if o == '-F':
436            options.printmode = 'F'
437        if o == '-a':
438            options.all = True
439        if o == '-d':
440            options.db = a
441        if o == '-g':
442            options.graph = True;
443            options.graphdir = a
444        if o == '-h':
445            options.host = a
446        if o == '-J':
447            jobfilename = None
448        if o == '-j':
449            jobfilename = a
450        if o == '-m':
451            options.method = a
452        if o == '-p':
453            options.passwd = getpass.getpass()
454        if o == '-r':
455            options.runs = a
456        if o == '-u':
457            options.user = a
458        if o == '-s':
459            options.system = a
460        if o == '-T':
461            options.ticks = a
462
463    if jobfilename:
464        from jobfile import JobFile
465        options.jobfile = JobFile(jobfilename)
466        if not options.host:
467            options.host = options.jobfile.dbhost
468        if not options.db:
469            options.db = options.jobfile.statdb
470
471    if not options.host:
472        sys.exit('Database server must be provided from a jobfile or -h')
473
474    if not options.db:
475        sys.exit('Database name must be provided from a jobfile or -d')
476
477    if len(args) == 0:
478        usage()
479
480    command = args[0]
481    args = args[1:]
482
483    try:
484        commands(options, command, args)
485    except CommandException:
486        usage()
487