stats.py revision 2002
112855Sgabeblack@google.com#!/usr/bin/env python
212855Sgabeblack@google.com
312855Sgabeblack@google.com# Copyright (c) 2003-2004 The Regents of The University of Michigan
412855Sgabeblack@google.com# All rights reserved.
512855Sgabeblack@google.com#
612855Sgabeblack@google.com# Redistribution and use in source and binary forms, with or without
712855Sgabeblack@google.com# modification, are permitted provided that the following conditions are
812855Sgabeblack@google.com# met: redistributions of source code must retain the above copyright
912855Sgabeblack@google.com# notice, this list of conditions and the following disclaimer;
1012855Sgabeblack@google.com# redistributions in binary form must reproduce the above copyright
1112855Sgabeblack@google.com# notice, this list of conditions and the following disclaimer in the
1212855Sgabeblack@google.com# documentation and/or other materials provided with the distribution;
1312855Sgabeblack@google.com# neither the name of the copyright holders nor the names of its
1412855Sgabeblack@google.com# contributors may be used to endorse or promote products derived from
1512855Sgabeblack@google.com# this software without specific prior written permission.
1612855Sgabeblack@google.com#
1712855Sgabeblack@google.com# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1812855Sgabeblack@google.com# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1912855Sgabeblack@google.com# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2012855Sgabeblack@google.com# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2112855Sgabeblack@google.com# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2212855Sgabeblack@google.com# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2312855Sgabeblack@google.com# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2412855Sgabeblack@google.com# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2512855Sgabeblack@google.com# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2612855Sgabeblack@google.com# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2712855Sgabeblack@google.com# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2812855Sgabeblack@google.com
2912855Sgabeblack@google.comfrom __future__ import division
3012855Sgabeblack@google.comimport re, sys, math
3112855Sgabeblack@google.com
3212855Sgabeblack@google.comdef usage():
3312855Sgabeblack@google.com    print '''\
3412855Sgabeblack@google.comUsage: %s [-E] [-F] [ -G <get> ] [-d <db> ] [-g <graphdir> ] [-h <host>] [-p]
3512855Sgabeblack@google.com       [-s <system>] [-r <runs> ] [-T <samples>] [-u <username>]
3612855Sgabeblack@google.com       <command> [command args]
3712855Sgabeblack@google.com
3812855Sgabeblack@google.com       commands    extra parameters   description
3912855Sgabeblack@google.com       ----------- ------------------ ---------------------------------------
4012855Sgabeblack@google.com       bins        [regex]            List bins (only matching regex)
4112855Sgabeblack@google.com       formula     <formula>          Evaluated formula specified
4212855Sgabeblack@google.com       formulas    [regex]            List formulas (only matching regex)
4312855Sgabeblack@google.com       runs        none               List all runs in database
4412855Sgabeblack@google.com       samples     none               List samples present in database
4512855Sgabeblack@google.com       stability   <pairnum> <stats>  Calculated statistical info about stats
4612855Sgabeblack@google.com       stat        <regex>            Show stat data (only matching regex)
4712855Sgabeblack@google.com       stats       [regex]            List all stats (only matching regex)
4812855Sgabeblack@google.com
4912855Sgabeblack@google.com       database    <command>          Where command is drop, init, or clean
5012855Sgabeblack@google.com
5112855Sgabeblack@google.com''' % sys.argv[0]
5212855Sgabeblack@google.com    sys.exit(1)
5312855Sgabeblack@google.com
5412855Sgabeblack@google.comdef getopts(list, flags):
5512855Sgabeblack@google.com    import getopt
5612855Sgabeblack@google.com    try:
5712855Sgabeblack@google.com        opts, args = getopt.getopt(list, flags)
5812855Sgabeblack@google.com    except getopt.GetoptError:
5912855Sgabeblack@google.com        usage()
6012855Sgabeblack@google.com
6112855Sgabeblack@google.com    return opts, args
6212855Sgabeblack@google.com
6312855Sgabeblack@google.comclass CommandException(Exception):
6412855Sgabeblack@google.com    pass
6512855Sgabeblack@google.com
6612855Sgabeblack@google.comdef commands(options, command, args):
6712855Sgabeblack@google.com    if command == 'database':
6812855Sgabeblack@google.com        if len(args) == 0: raise CommandException
6912855Sgabeblack@google.com
7012855Sgabeblack@google.com        import dbinit
7112855Sgabeblack@google.com        mydb = dbinit.MyDB(options)
7212855Sgabeblack@google.com
7312855Sgabeblack@google.com        if args[0] == 'drop':
7412855Sgabeblack@google.com            if len(args) > 2: raise CommandException
7512855Sgabeblack@google.com            mydb.admin()
7612855Sgabeblack@google.com            mydb.drop()
7712855Sgabeblack@google.com            if len(args) == 2 and args[1] == 'init':
7812855Sgabeblack@google.com                mydb.create()
7912855Sgabeblack@google.com                mydb.connect()
8012855Sgabeblack@google.com                mydb.populate()
8112855Sgabeblack@google.com            mydb.close()
8212855Sgabeblack@google.com            return
8312855Sgabeblack@google.com
8412855Sgabeblack@google.com        if args[0] == 'init':
8512855Sgabeblack@google.com            if len(args) > 1: raise CommandException
8612855Sgabeblack@google.com            mydb.admin()
8712855Sgabeblack@google.com            mydb.create()
8812855Sgabeblack@google.com            mydb.connect()
8912855Sgabeblack@google.com            mydb.populate()
9012855Sgabeblack@google.com            mydb.close()
9112855Sgabeblack@google.com            return
9212855Sgabeblack@google.com
9312855Sgabeblack@google.com        if args[0] == 'clean':
9412855Sgabeblack@google.com            if len(args) > 1: raise CommandException
9512855Sgabeblack@google.com            mydb.connect()
9612855Sgabeblack@google.com            mydb.clean()
9712855Sgabeblack@google.com            return
9812855Sgabeblack@google.com
9912855Sgabeblack@google.com        raise CommandException
10012855Sgabeblack@google.com
10112855Sgabeblack@google.com    import db
10212855Sgabeblack@google.com    source = db.Database()
10312855Sgabeblack@google.com    source.host = options.host
10412855Sgabeblack@google.com    source.db = options.db
10512855Sgabeblack@google.com    source.passwd = options.passwd
10612855Sgabeblack@google.com    source.user = options.user
10712855Sgabeblack@google.com    source.connect()
10812855Sgabeblack@google.com    #source.update_dict(globals())
10912855Sgabeblack@google.com
11012855Sgabeblack@google.com    if type(options.method) is str:
11112855Sgabeblack@google.com        source.method = options.method
11212855Sgabeblack@google.com
11312855Sgabeblack@google.com    if options.runs is None:
11412855Sgabeblack@google.com        runs = source.allRuns
11512855Sgabeblack@google.com    else:
11612855Sgabeblack@google.com        rx = re.compile(options.runs)
11712855Sgabeblack@google.com        runs = []
11812855Sgabeblack@google.com        for run in source.allRuns:
11912855Sgabeblack@google.com            if rx.match(run.name):
12012855Sgabeblack@google.com                runs.append(run)
12112855Sgabeblack@google.com
12212855Sgabeblack@google.com    if command == 'runs':
12312855Sgabeblack@google.com        user = None
12412855Sgabeblack@google.com        opts, args = getopts(args, '-u')
12512855Sgabeblack@google.com        if len(args):
12612855Sgabeblack@google.com            raise CommandException
12712855Sgabeblack@google.com        for o,a in opts:
12812855Sgabeblack@google.com            if o == '-u':
12912855Sgabeblack@google.com                user = a
13012855Sgabeblack@google.com        source.listRuns(user)
13112855Sgabeblack@google.com        return
13212855Sgabeblack@google.com
13312855Sgabeblack@google.com    if command == 'stats':
13412855Sgabeblack@google.com        if len(args) == 0:
13512855Sgabeblack@google.com            source.listStats()
13612855Sgabeblack@google.com        elif len(args) == 1:
13712855Sgabeblack@google.com            source.listStats(args[0])
13812855Sgabeblack@google.com        else:
13912855Sgabeblack@google.com            raise CommandException
140
141        return
142
143    if command == 'bins':
144        if len(args) == 0:
145            source.listBins()
146        elif len(args) == 1:
147            source.listBins(args[0])
148        else:
149            raise CommandException
150
151        return
152
153    if command == 'formulas':
154        if len(args) == 0:
155            source.listFormulas()
156        elif len(args) == 1:
157            source.listFormulas(args[0])
158        else:
159            raise CommandException
160
161        return
162
163    if command == 'samples':
164        if len(args):
165            raise CommandException
166
167        source.listTicks(runs)
168        return
169
170    if command == 'stability':
171        if len(args) < 2:
172            raise CommandException
173
174        try:
175            merge = int(args[0])
176        except ValueError:
177            usage()
178        stats = source.getStat(args[1])
179        source.method = 'sum'
180
181        def disp(*args):
182            print "%-35s %12s %12s %4s %5s %5s %5s %10s" % args
183
184        # temporary variable containing a bunch of dashes
185        d = '-' * 100
186
187        #loop through all the stats selected
188        for stat in stats:
189            print "%s:" % stat.name
190            disp("run name", "average", "stdev", ">10%", ">1SDV", ">2SDV",
191                 "SAMP", "CV")
192            disp(d[:35], d[:12], d[:12], d[:4], d[:5], d[:5], d[:5], d[:10])
193
194            #loop through all the selected runs
195            for run in runs:
196                runTicks = source.retTicks([ run ])
197                #throw away the first one, it's 0
198                runTicks.pop(0)
199                source.ticks = runTicks
200                avg = 0
201                stdev = 0
202                numoutsideavg  = 0
203                numoutside1std = 0
204                numoutside2std = 0
205                pairRunTicks = []
206                if value(stat, run.run) == 1e300*1e300:
207                    continue
208                for t in range(0, len(runTicks)-(merge-1), merge):
209                    tempPair = []
210                    for p in range(0,merge):
211                        tempPair.append(runTicks[t+p])
212                    pairRunTicks.append(tempPair)
213                #loop through all the various ticks for each run
214                for tick in pairRunTicks:
215                    source.ticks = tick
216                    avg += value(stat, run.run)
217                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', 'rxbps', 'txbps', 'bpt',
254                'misses', 'mpkb',
255                'ipkb',
256                'pps', 'bpp', 'txbpp', 'rxbpp',
257                'rtp', 'rtb' ]
258        for command in all:
259            commands(options, command, args)
260
261    if options.ticks:
262        if not options.graph:
263            print 'only displaying sample %s' % options.ticks
264        source.ticks = [ int(x) for x in options.ticks.split() ]
265
266    import output
267
268    def display():
269        if options.graph:
270            output.graph(options.graphdir)
271        else:
272            output.display(options.binned, options.printmode)
273
274
275    if command == 'stat' or command == 'formula':
276        if len(args) != 1:
277            raise CommandException
278
279        if command == 'stat':
280            stats = source.getStat(args[0])
281        if command == 'formula':
282            stats = eval(args[0])
283
284        for stat in stats:
285            output = output.StatOutput(stat.name, options.jobfile, source)
286            output.stat = stat
287            output.label = stat.name
288            display()
289
290        return
291
292    if len(args):
293        raise CommandException
294
295    system = source.__dict__[options.system]
296    from info import ProxyGroup
297    sim_seconds = source['sim_seconds']
298    proxy = ProxyGroup(system = source[options.system])
299    system = proxy.system
300
301    etherdev = system.tsunami.etherdev0
302    bytes = etherdev.rxBytes + etherdev.txBytes
303    kbytes = bytes / 1024
304    packets = etherdev.rxPackets + etherdev.txPackets
305    bps = etherdev.rxBandwidth + etherdev.txBandwidth
306
307    output = output.StatOutput(command, options.jobfile, source)
308
309    if command == 'usertime':
310        import copy
311        user = copy.copy(system.run0.numCycles)
312        user.bins = 'user'
313
314        output.stat = user / system.run0.numCycles
315        output.label = 'User Fraction'
316
317        display()
318        return
319
320    if command == 'ticks':
321        output.stat = system.run0.numCycles
322        output.binstats = [ system.run0.numCycles ]
323
324        display()
325        return
326
327    if command == 'bytes':
328        output.stat = bytes
329        display()
330        return
331
332    if command == 'packets':
333        output.stat = packets
334        display()
335        return
336
337    if command == 'ppt' or command == 'tpp':
338        output.stat = packets / system.run0.numCycles
339        output.invert = command == 'tpp'
340        display()
341        return
342
343    if command == 'pps':
344        output.stat = packets / sim_seconds
345        output.label = 'Packets/s'
346        display()
347        return
348
349    if command == 'bpt' or command == 'tpb':
350        output.stat = bytes / system.run0.numCycles * 8
351        output.label = 'bps / Hz'
352        output.invert = command == 'tpb'
353        display()
354        return
355
356    if command in ('rxbps', 'txbps', 'bps'):
357        if command == 'rxbps':
358            output.stat = etherdev.rxBandwidth / 1e9
359        if command == 'txbps':
360            output.stat = etherdev.txBandwidth / 1e9
361        if command == 'bps':
362            output.stat = bps / 1e9
363
364        output.label = 'Bandwidth (Gbps)'
365        display()
366        return
367
368    if command == 'bpp':
369        output.stat = bytes / packets
370        output.label = 'Bytes / Packet'
371        display()
372        return
373
374    if command == 'rxbpp':
375        output.stat = etherdev.rxBytes / etherdev.rxPackets
376        output.label = 'Receive Bytes / Packet'
377        display()
378        return
379
380    if command == 'txbpp':
381        output.stat = etherdev.txBytes / etherdev.txPackets
382        output.label = 'Transmit Bytes / Packet'
383        display()
384        return
385
386    if command == 'rtp':
387        output.stat = etherdev.rxPackets / etherdev.txPackets
388        output.label = 'rxPackets / txPackets'
389        display()
390        return
391
392    if command == 'rtb':
393        output.stat = etherdev.rxBytes / etherdev.txBytes
394        output.label = 'rxBytes / txBytes'
395        display()
396        return
397
398    misses = system.l2.overall_mshr_misses
399
400    if command == 'misses':
401        output.stat = misses
402        output.label = 'Overall MSHR Misses'
403        display()
404        return
405
406    if command == 'mpkb':
407        output.stat = misses / (bytes / 1024)
408        output.binstats = [ misses ]
409        output.label = 'Misses / KB'
410        display()
411        return
412
413    if command == 'ipkb':
414        interrupts = system.run0.kern.faults[4]
415        output.stat = interrupts / kbytes
416        output.binstats = [ interrupts ]
417        output.label = 'Interrupts / KB'
418        display()
419        return
420
421    if command == 'execute':
422        output.stat = system.run0.ISSUE__count
423        display()
424        return
425
426    if command == 'commit':
427        output.stat = system.run0.COM__count
428        display()
429        return
430
431    if command == 'fetch':
432        output.stat = system.run0.FETCH__count
433        display()
434        return
435
436    raise CommandException
437
438
439class Options: pass
440
441if __name__ == '__main__':
442    import getpass
443    from jobfile import JobFile
444
445    options = Options()
446    options.host = None
447    options.db = None
448    options.passwd = ''
449    options.user = getpass.getuser()
450    options.runs = None
451    options.system = 'client'
452    options.method = None
453    options.binned = False
454    options.graph = False
455    options.ticks = False
456    options.printmode = 'G'
457    jobfilename = 'Test.py'
458    options.jobfile = None
459    options.all = False
460
461    opts, args = getopts(sys.argv[1:], '-BEFJad:g:h:j:m:pr:s:u:T:')
462    for o,a in opts:
463        if o == '-B':
464            options.binned = True
465        if o == '-E':
466            options.printmode = 'E'
467        if o == '-F':
468            options.printmode = 'F'
469        if o == '-a':
470            options.all = True
471        if o == '-d':
472            options.db = a
473        if o == '-g':
474            options.graph = True;
475            options.graphdir = a
476        if o == '-h':
477            options.host = a
478        if o == '-J':
479            jobfilename = None
480        if o == '-j':
481            jobfilename = a
482        if o == '-m':
483            options.method = a
484        if o == '-p':
485            options.passwd = getpass.getpass()
486        if o == '-r':
487            options.runs = a
488        if o == '-u':
489            options.user = a
490        if o == '-s':
491            options.system = a
492        if o == '-T':
493            options.ticks = a
494
495    if jobfilename:
496        options.jobfile = JobFile(jobfilename)
497        if not options.host:
498            options.host = options.jobfile.dbhost
499        if not options.db:
500            options.db = options.jobfile.statdb
501
502    if not options.host:
503        sys.exit('Database server must be provided from a jobfile or -h')
504
505    if not options.db:
506        sys.exit('Database name must be provided from a jobfile or -d')
507
508    if len(args) == 0:
509        usage()
510
511    command = args[0]
512    args = args[1:]
513
514    try:
515        commands(options, command, args)
516    except CommandException:
517        usage()
518