stats.py revision 2005
13560SN/A#!/usr/bin/env python
23560SN/A
33560SN/A# Copyright (c) 2003-2004 The Regents of The University of Michigan
43560SN/A# All rights reserved.
53560SN/A#
63560SN/A# Redistribution and use in source and binary forms, with or without
73560SN/A# modification, are permitted provided that the following conditions are
83560SN/A# met: redistributions of source code must retain the above copyright
93560SN/A# notice, this list of conditions and the following disclaimer;
103560SN/A# redistributions in binary form must reproduce the above copyright
113560SN/A# notice, this list of conditions and the following disclaimer in the
123560SN/A# documentation and/or other materials provided with the distribution;
133560SN/A# neither the name of the copyright holders nor the names of its
143560SN/A# contributors may be used to endorse or promote products derived from
153560SN/A# this software without specific prior written permission.
163560SN/A#
173560SN/A# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
183560SN/A# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
193560SN/A# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
203560SN/A# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
213560SN/A# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
223560SN/A# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
233560SN/A# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
243560SN/A# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
253560SN/A# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
263560SN/A# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
273560SN/A# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
283560SN/A
293560SN/Afrom __future__ import division
303560SN/Aimport re, sys, math
313560SN/A
323560SN/Adef usage():
333560SN/A    print '''\
343560SN/AUsage: %s [-E] [-F] [ -G <get> ] [-d <db> ] [-g <graphdir> ] [-h <host>] [-p]
353560SN/A       [-s <system>] [-r <runs> ] [-T <samples>] [-u <username>]
365191Ssaidi@eecs.umich.edu       <command> [command args]
373565Sgblack@eecs.umich.edu
383560SN/A       commands    extra parameters   description
393560SN/A       ----------- ------------------ ---------------------------------------
403560SN/A       bins        [regex]            List bins (only matching regex)
413560SN/A       formula     <formula>          Evaluated formula specified
423560SN/A       formulas    [regex]            List formulas (only matching regex)
433560SN/A       runs        none               List all runs in database
443560SN/A       samples     none               List samples present in database
453560SN/A       stability   <pairnum> <stats>  Calculated statistical info about stats
463560SN/A       stat        <regex>            Show stat data (only matching regex)
473560SN/A       stats       [regex]            List all stats (only matching regex)
483560SN/A
493560SN/A       database    <command>          Where command is drop, init, or clean
503560SN/A
513560SN/A''' % sys.argv[0]
523560SN/A    sys.exit(1)
533560SN/A
543560SN/Adef getopts(list, flags):
553560SN/A    import getopt
563560SN/A    try:
573560SN/A        opts, args = getopt.getopt(list, flags)
583560SN/A    except getopt.GetoptError:
593560SN/A        usage()
603560SN/A
613560SN/A    return opts, args
623560SN/A
633560SN/Aclass CommandException(Exception):
643560SN/A    pass
653560SN/A
663560SN/Adef commands(options, command, args):
673560SN/A    if command == 'database':
683560SN/A        if len(args) == 0: raise CommandException
693560SN/A
703560SN/A        import dbinit
713560SN/A        mydb = dbinit.MyDB(options)
723560SN/A
733560SN/A        if args[0] == 'drop':
743560SN/A            if len(args) > 2: raise CommandException
753560SN/A            mydb.admin()
763560SN/A            mydb.drop()
773560SN/A            if len(args) == 2 and args[1] == 'init':
783560SN/A                mydb.create()
793560SN/A                mydb.connect()
803560SN/A                mydb.populate()
813560SN/A            mydb.close()
823560SN/A            return
833560SN/A
843560SN/A        if args[0] == 'init':
853560SN/A            if len(args) > 1: raise CommandException
863560SN/A            mydb.admin()
873560SN/A            mydb.create()
883560SN/A            mydb.connect()
893560SN/A            mydb.populate()
903560SN/A            mydb.close()
913560SN/A            return
923560SN/A
933560SN/A        if args[0] == 'clean':
943560SN/A            if len(args) > 1: raise CommandException
953560SN/A            mydb.connect()
963560SN/A            mydb.clean()
973560SN/A            return
983560SN/A
993560SN/A        raise CommandException
1003560SN/A
1013560SN/A    import db
1023560SN/A    source = db.Database()
1033560SN/A    source.host = options.host
1043560SN/A    source.db = options.db
1053560SN/A    source.passwd = options.passwd
1063560SN/A    source.user = options.user
1073560SN/A    source.connect()
1083560SN/A    #source.update_dict(globals())
1093560SN/A
1103560SN/A    if type(options.method) is str:
1113560SN/A        source.method = options.method
1123560SN/A
1133560SN/A    if options.runs is None:
1143560SN/A        runs = source.allRuns
1153560SN/A    else:
1163560SN/A        rx = re.compile(options.runs)
1173560SN/A        runs = []
1183560SN/A        for run in source.allRuns:
1193560SN/A            if rx.match(run.name):
1203560SN/A                runs.append(run)
1213560SN/A
1223560SN/A    if command == 'runs':
1233560SN/A        user = None
1243560SN/A        opts, args = getopts(args, '-u')
1253560SN/A        if len(args):
1263560SN/A            raise CommandException
1273560SN/A        for o,a in opts:
1283560SN/A            if o == '-u':
1293560SN/A                user = a
1303560SN/A        source.listRuns(user)
1313560SN/A        return
1323560SN/A
1333560SN/A    if command == 'stats':
1343560SN/A        if len(args) == 0:
1353560SN/A            source.listStats()
1363560SN/A        elif len(args) == 1:
1373560SN/A            source.listStats(args[0])
1383560SN/A        else:
1393560SN/A            raise CommandException
1403560SN/A
1415191Ssaidi@eecs.umich.edu        return
1425191Ssaidi@eecs.umich.edu
1435191Ssaidi@eecs.umich.edu    if command == 'bins':
1443560SN/A        if len(args) == 0:
1453560SN/A            source.listBins()
1467823Ssteve.reinhardt@amd.com        elif len(args) == 1:
1473560SN/A            source.listBins(args[0])
1487823Ssteve.reinhardt@amd.com        else:
1493560SN/A            raise CommandException
1503560SN/A
1513560SN/A        return
1523560SN/A
1533560SN/A    if command == 'formulas':
1543560SN/A        if len(args) == 0:
1555568Snate@binkert.org            source.listFormulas()
1563560SN/A        elif len(args) == 1:
1573560SN/A            source.listFormulas(args[0])
1583560SN/A        else:
1593560SN/A            raise CommandException
1603560SN/A
1613560SN/A        return
1623560SN/A
1633560SN/A    if command == 'samples':
1643560SN/A        if len(args):
1653560SN/A            raise CommandException
1663560SN/A
1673560SN/A        source.listTicks(runs)
1683560SN/A        return
1693560SN/A
1705191Ssaidi@eecs.umich.edu    if command == 'stability':
1715191Ssaidi@eecs.umich.edu        if len(args) < 2:
1725191Ssaidi@eecs.umich.edu            raise CommandException
1735191Ssaidi@eecs.umich.edu
1743560SN/A        try:
1753560SN/A            merge = int(args[0])
1763560SN/A        except ValueError:
1773560SN/A            usage()
1783560SN/A        stats = source.getStat(args[1])
1793560SN/A        source.method = 'sum'
1803560SN/A
1813560SN/A        def disp(*args):
1823560SN/A            print "%-35s %12s %12s %4s %5s %5s %5s %10s" % args
1833560SN/A
1843560SN/A        # temporary variable containing a bunch of dashes
1853560SN/A        d = '-' * 100
1863560SN/A
1873560SN/A        #loop through all the stats selected
1883560SN/A        for stat in stats:
1893560SN/A            print "%s:" % stat.name
1903560SN/A            disp("run name", "average", "stdev", ">10%", ">1SDV", ">2SDV",
1913560SN/A                 "SAMP", "CV")
1923560SN/A            disp(d[:35], d[:12], d[:12], d[:4], d[:5], d[:5], d[:5], d[:10])
1933560SN/A
1943560SN/A            #loop through all the selected runs
1953560SN/A            for run in runs:
1963560SN/A                runTicks = source.retTicks([ run ])
1973560SN/A                #throw away the first one, it's 0
1983560SN/A                runTicks.pop(0)
1993560SN/A                source.ticks = runTicks
2003560SN/A                avg = 0
2013560SN/A                stdev = 0
2023560SN/A                numoutsideavg  = 0
2033560SN/A                numoutside1std = 0
2043560SN/A                numoutside2std = 0
2053560SN/A                pairRunTicks = []
2063560SN/A                if value(stat, run.run) == 1e300*1e300:
2073560SN/A                    continue
2083560SN/A                for t in range(0, len(runTicks)-(merge-1), merge):
2093560SN/A                    tempPair = []
2103560SN/A                    for p in range(0,merge):
2113560SN/A                        tempPair.append(runTicks[t+p])
2123560SN/A                    pairRunTicks.append(tempPair)
2133560SN/A                #loop through all the various ticks for each run
2143560SN/A                for tick in pairRunTicks:
2153560SN/A                    source.ticks = tick
2165568Snate@binkert.org                    avg += value(stat, run.run)
2175568Snate@binkert.org                avg /= len(pairRunTicks)
218                for tick in pairRunTicks:
219                    source.ticks = tick
220                    val = value(stat, run.run)
221                    stdev += pow((val-avg),2)
222                stdev = math.sqrt(stdev / len(pairRunTicks))
223                for tick in pairRunTicks:
224                    source.ticks = tick
225                    val = value(stat, run.run)
226                    if (val < (avg * .9)) or (val > (avg * 1.1)):
227                        numoutsideavg += 1
228                    if (val < (avg - stdev)) or (val > (avg + stdev)):
229                        numoutside1std += 1
230                    if (val < (avg - (2*stdev))) or (val > (avg + (2*stdev))):
231                        numoutside2std += 1
232                if avg > 1000:
233                    disp(run.name, "%.1f" % avg, "%.1f" % stdev,
234                         "%d" % numoutsideavg, "%d" % numoutside1std,
235                         "%d" % numoutside2std, "%d" % len(pairRunTicks),
236                         "%.3f" % (stdev/avg*100))
237                elif avg > 100:
238                    disp(run.name, "%.1f" % avg, "%.1f" % stdev,
239                         "%d" % numoutsideavg, "%d" % numoutside1std,
240                         "%d" % numoutside2std, "%d" % len(pairRunTicks),
241                         "%.5f" % (stdev/avg*100))
242                else:
243                    disp(run.name, "%.5f" % avg, "%.5f" % stdev,
244                         "%d" % numoutsideavg, "%d" % numoutside1std,
245                         "%d" % numoutside2std, "%d" % len(pairRunTicks),
246                         "%.7f" % (stdev/avg*100))
247        return
248
249    if command == 'all':
250        if len(args):
251            raise CommandException
252
253        all = [ 'bps', 'misses', 'mpkb', 'ipkb', 'pps', 'bpt' ]
254        for command in all:
255            commands(options, command, args)
256
257    if options.ticks:
258        if not options.graph:
259            print 'only displaying sample %s' % options.ticks
260        source.ticks = [ int(x) for x in options.ticks.split() ]
261
262    import output
263
264    def display():
265        if options.graph:
266            output.graph(options.graphdir)
267        else:
268            output.display(options.binned, options.printmode)
269
270
271    if command == 'stat' or command == 'formula':
272        if len(args) != 1:
273            raise CommandException
274
275        if command == 'stat':
276            stats = source.getStat(args[0])
277        if command == 'formula':
278            stats = eval(args[0])
279
280        for stat in stats:
281            output = output.StatOutput(stat.name, options.jobfile, source)
282            output.stat = stat
283            output.label = stat.name
284            display()
285
286        return
287
288    if len(args):
289        raise CommandException
290
291    system = source.__dict__[options.system]
292    from info import ProxyGroup
293    sim_seconds = source['sim_seconds']
294    proxy = ProxyGroup(system = source[options.system])
295    system = proxy.system
296
297    etherdev = system.tsunami.etherdev0
298    bytes = etherdev.rxBytes + etherdev.txBytes
299    kbytes = bytes / 1024
300    packets = etherdev.rxPackets + etherdev.txPackets
301    bps = etherdev.rxBandwidth + etherdev.txBandwidth
302
303    output = output.StatOutput(command, options.jobfile, source)
304
305    if command == 'usertime':
306        import copy
307        user = copy.copy(system.run0.numCycles)
308        user.bins = 'user'
309
310        output.stat = user / system.run0.numCycles
311        output.label = 'User Fraction'
312
313        display()
314        return
315
316    if command == 'ticks':
317        output.stat = system.run0.numCycles
318        output.binstats = [ system.run0.numCycles ]
319
320        display()
321        return
322
323    if command == 'bytes':
324        output.stat = bytes
325        display()
326        return
327
328    if command == 'packets':
329        output.stat = packets
330        display()
331        return
332
333    if command == 'ppt' or command == 'tpp':
334        output.stat = packets / system.run0.numCycles
335        output.invert = command == 'tpp'
336        display()
337        return
338
339    if command == 'pps':
340        output.stat = packets / sim_seconds
341        output.label = 'Packets/s'
342        display()
343        return
344
345    if command == 'bpt' or command == 'tpb':
346        output.stat = bytes / system.run0.numCycles * 8
347        output.label = 'bps / Hz'
348        output.invert = command == 'tpb'
349        display()
350        return
351
352    if command in ('rxbps', 'txbps', 'bps'):
353        if command == 'rxbps':
354            output.stat = etherdev.rxBandwidth / 1e9
355        if command == 'txbps':
356            output.stat = etherdev.txBandwidth / 1e9
357        if command == 'bps':
358            output.stat = bps / 1e9
359
360        output.label = 'Bandwidth (Gbps)'
361        display()
362        return
363
364    if command == 'bpp':
365        output.stat = bytes / packets
366        output.label = 'Bytes / Packet'
367        display()
368        return
369
370    if command == 'rxbpp':
371        output.stat = etherdev.rxBytes / etherdev.rxPackets
372        output.label = 'Receive Bytes / Packet'
373        display()
374        return
375
376    if command == 'txbpp':
377        output.stat = etherdev.txBytes / etherdev.txPackets
378        output.label = 'Transmit Bytes / Packet'
379        display()
380        return
381
382    if command == 'rtp':
383        output.stat = etherdev.rxPackets / etherdev.txPackets
384        output.label = 'rxPackets / txPackets'
385        display()
386        return
387
388    if command == 'rtb':
389        output.stat = etherdev.rxBytes / etherdev.txBytes
390        output.label = 'rxBytes / txBytes'
391        display()
392        return
393
394    misses = system.l2.overall_mshr_misses
395
396    if command == 'misses':
397        output.stat = misses
398        output.label = 'Overall MSHR Misses'
399        display()
400        return
401
402    if command == 'mpkb':
403        output.stat = misses / (bytes / 1024)
404        output.binstats = [ misses ]
405        output.label = 'Misses / KB'
406        display()
407        return
408
409    if command == 'ipkb':
410        interrupts = system.run0.kern.faults[4]
411        output.stat = interrupts / kbytes
412        output.binstats = [ interrupts ]
413        output.label = 'Interrupts / KB'
414        display()
415        return
416
417    if command == 'execute':
418        output.stat = system.run0.ISSUE__count
419        display()
420        return
421
422    if command == 'commit':
423        output.stat = system.run0.COM__count
424        display()
425        return
426
427    if command == 'fetch':
428        output.stat = system.run0.FETCH__count
429        display()
430        return
431
432    raise CommandException
433
434
435class Options: pass
436
437if __name__ == '__main__':
438    import getpass
439    from jobfile import JobFile
440
441    options = Options()
442    options.host = None
443    options.db = None
444    options.passwd = ''
445    options.user = getpass.getuser()
446    options.runs = None
447    options.system = 'client'
448    options.method = None
449    options.binned = False
450    options.graph = False
451    options.ticks = False
452    options.printmode = 'G'
453    jobfilename = 'Test.py'
454    options.jobfile = None
455    options.all = False
456
457    opts, args = getopts(sys.argv[1:], '-BEFJad:g:h:j:m:pr:s:u:T:')
458    for o,a in opts:
459        if o == '-B':
460            options.binned = True
461        if o == '-E':
462            options.printmode = 'E'
463        if o == '-F':
464            options.printmode = 'F'
465        if o == '-a':
466            options.all = True
467        if o == '-d':
468            options.db = a
469        if o == '-g':
470            options.graph = True;
471            options.graphdir = a
472        if o == '-h':
473            options.host = a
474        if o == '-J':
475            jobfilename = None
476        if o == '-j':
477            jobfilename = a
478        if o == '-m':
479            options.method = a
480        if o == '-p':
481            options.passwd = getpass.getpass()
482        if o == '-r':
483            options.runs = a
484        if o == '-u':
485            options.user = a
486        if o == '-s':
487            options.system = a
488        if o == '-T':
489            options.ticks = a
490
491    if jobfilename:
492        options.jobfile = JobFile(jobfilename)
493        if not options.host:
494            options.host = options.jobfile.dbhost
495        if not options.db:
496            options.db = options.jobfile.statdb
497
498    if not options.host:
499        sys.exit('Database server must be provided from a jobfile or -h')
500
501    if not options.db:
502        sys.exit('Database name must be provided from a jobfile or -d')
503
504    if len(args) == 0:
505        usage()
506
507    command = args[0]
508    args = args[1:]
509
510    try:
511        commands(options, command, args)
512    except CommandException:
513        usage()
514