stats.py revision 2343:a2b4a6ccee56
1#!/usr/bin/env python
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
29from __future__ import division
30import re, sys, math
31
32def usage():
33    print '''\
34Usage: %s [-E] [-F] [ -G <get> ] [-d <db> ] [-g <graphdir> ] [-h <host>] [-p]
35       [-s <system>] [-r <runs> ] [-T <samples>] [-u <username>]
36       <command> [command args]
37
38       commands    extra parameters   description
39       ----------- ------------------ ---------------------------------------
40       formula     <formula>          Evaluated formula specified
41       formulas    [regex]            List formulas (only matching regex)
42       runs        none               List all runs in database
43       samples     none               List samples present in database
44       stability   <pairnum> <stats>  Calculated statistical info about stats
45       stat        <regex>            Show stat data (only matching regex)
46       stats       [regex]            List all stats (only matching regex)
47
48       database    <command>          Where command is drop, init, or clean
49
50''' % sys.argv[0]
51    sys.exit(1)
52
53def getopts(list, flags):
54    import getopt
55    try:
56        opts, args = getopt.getopt(list, flags)
57    except getopt.GetoptError:
58        usage()
59
60    return opts, args
61
62class CommandException(Exception):
63    pass
64
65def commands(options, command, args):
66    if command == 'database':
67        if len(args) == 0: raise CommandException
68
69        import dbinit
70        mydb = dbinit.MyDB(options)
71
72        if args[0] == 'drop':
73            if len(args) > 2: raise CommandException
74            mydb.admin()
75            mydb.drop()
76            if len(args) == 2 and args[1] == 'init':
77                mydb.create()
78                mydb.connect()
79                mydb.populate()
80            mydb.close()
81            return
82
83        if args[0] == 'init':
84            if len(args) > 1: raise CommandException
85            mydb.admin()
86            mydb.create()
87            mydb.connect()
88            mydb.populate()
89            mydb.close()
90            return
91
92        if args[0] == 'clean':
93            if len(args) > 1: raise CommandException
94            mydb.connect()
95            mydb.clean()
96            return
97
98        raise CommandException
99
100    import db
101    source = db.Database()
102    source.host = options.host
103    source.db = options.db
104    source.passwd = options.passwd
105    source.user = options.user
106    source.connect()
107    #source.update_dict(globals())
108
109    if type(options.method) is str:
110        source.method = options.method
111
112    if options.runs is None:
113        runs = source.allRuns
114    else:
115        rx = re.compile(options.runs)
116        runs = []
117        for run in source.allRuns:
118            if rx.match(run.name):
119                runs.append(run)
120
121    if command == 'runs':
122        user = None
123        opts, args = getopts(args, '-u')
124        if len(args):
125            raise CommandException
126        for o,a in opts:
127            if o == '-u':
128                user = a
129        source.listRuns(user)
130        return
131
132    if command == 'stats':
133        if len(args) == 0:
134            source.listStats()
135        elif len(args) == 1:
136            source.listStats(args[0])
137        else:
138            raise CommandException
139
140        return
141
142    if command == 'formulas':
143        if len(args) == 0:
144            source.listFormulas()
145        elif len(args) == 1:
146            source.listFormulas(args[0])
147        else:
148            raise CommandException
149
150        return
151
152    if command == 'samples':
153        if len(args):
154            raise CommandException
155
156        source.listTicks(runs)
157        return
158
159    if command == 'stability':
160        if len(args) < 2:
161            raise CommandException
162
163        try:
164            merge = int(args[0])
165        except ValueError:
166            usage()
167        stats = source.getStat(args[1])
168        source.method = 'sum'
169
170        def disp(*args):
171            print "%-35s %12s %12s %4s %5s %5s %5s %10s" % args
172
173        # temporary variable containing a bunch of dashes
174        d = '-' * 100
175
176        #loop through all the stats selected
177        for stat in stats:
178            print "%s:" % stat.name
179            disp("run name", "average", "stdev", ">10%", ">1SDV", ">2SDV",
180                 "SAMP", "CV")
181            disp(d[:35], d[:12], d[:12], d[:4], d[:5], d[:5], d[:5], d[:10])
182
183            #loop through all the selected runs
184            for run in runs:
185                runTicks = source.retTicks([ run ])
186                #throw away the first one, it's 0
187                runTicks.pop(0)
188                source.ticks = runTicks
189                avg = 0
190                stdev = 0
191                numoutsideavg  = 0
192                numoutside1std = 0
193                numoutside2std = 0
194                pairRunTicks = []
195                if value(stat, run.run) == 1e300*1e300:
196                    continue
197                for t in range(0, len(runTicks)-(merge-1), merge):
198                    tempPair = []
199                    for p in range(0,merge):
200                        tempPair.append(runTicks[t+p])
201                    pairRunTicks.append(tempPair)
202                #loop through all the various ticks for each run
203                for tick in pairRunTicks:
204                    source.ticks = tick
205                    avg += value(stat, run.run)
206                avg /= len(pairRunTicks)
207                for tick in pairRunTicks:
208                    source.ticks = tick
209                    val = value(stat, run.run)
210                    stdev += pow((val-avg),2)
211                stdev = math.sqrt(stdev / len(pairRunTicks))
212                for tick in pairRunTicks:
213                    source.ticks = tick
214                    val = value(stat, run.run)
215                    if (val < (avg * .9)) or (val > (avg * 1.1)):
216                        numoutsideavg += 1
217                    if (val < (avg - stdev)) or (val > (avg + stdev)):
218                        numoutside1std += 1
219                    if (val < (avg - (2*stdev))) or (val > (avg + (2*stdev))):
220                        numoutside2std += 1
221                if avg > 1000:
222                    disp(run.name, "%.1f" % avg, "%.1f" % stdev,
223                         "%d" % numoutsideavg, "%d" % numoutside1std,
224                         "%d" % numoutside2std, "%d" % len(pairRunTicks),
225                         "%.3f" % (stdev/avg*100))
226                elif avg > 100:
227                    disp(run.name, "%.1f" % avg, "%.1f" % stdev,
228                         "%d" % numoutsideavg, "%d" % numoutside1std,
229                         "%d" % numoutside2std, "%d" % len(pairRunTicks),
230                         "%.5f" % (stdev/avg*100))
231                else:
232                    disp(run.name, "%.5f" % avg, "%.5f" % stdev,
233                         "%d" % numoutsideavg, "%d" % numoutside1std,
234                         "%d" % numoutside2std, "%d" % len(pairRunTicks),
235                         "%.7f" % (stdev/avg*100))
236        return
237
238    if command == 'all':
239        if len(args):
240            raise CommandException
241
242        all = [ 'bps', 'misses', 'mpkb', 'ipkb', 'pps', 'bpt' ]
243        for command in all:
244            commands(options, command, args)
245
246    if options.ticks:
247        if not options.graph:
248            print 'only displaying sample %s' % options.ticks
249        source.ticks = [ int(x) for x in options.ticks.split() ]
250
251    from output import StatOutput
252    output = StatOutput(options.jobfile, source)
253    output.xlabel = 'System Configuration'
254    output.colormap = 'RdYlGn'
255
256    if command == 'stat' or command == 'formula':
257        if len(args) != 1:
258            raise CommandException
259
260        if command == 'stat':
261            stats = source.getStat(args[0])
262        if command == 'formula':
263            stats = eval(args[0])
264
265        for stat in stats:
266            output.stat = stat
267            output.ylabel = stat.name
268            if options.graph:
269                output.graph(stat.name, options.graphdir)
270            else:
271                output.display(stat.name, options.printmode)
272
273        return
274
275    if len(args):
276        raise CommandException
277
278    from info import ProxyGroup
279    proxy = ProxyGroup(system = source[options.system])
280    system = proxy.system
281
282    etherdev = system.tsunami.etherdev0
283    bytes = etherdev.rxBytes + etherdev.txBytes
284    kbytes = bytes / 1024
285    packets = etherdev.rxPackets + etherdev.txPackets
286
287    def display():
288        if options.graph:
289            output.graph(command, options.graphdir, proxy)
290        else:
291            output.display(command, options.printmode)
292
293    if command == 'ticks':
294        output.stat = system.run0.numCycles
295
296        display()
297        return
298
299    if command == 'bytes':
300        output.stat = bytes
301        display()
302        return
303
304    if command == 'packets':
305        output.stat = packets
306        display()
307        return
308
309    if command == 'ppt' or command == 'tpp':
310        output.stat = packets / system.run0.numCycles
311        output.invert = command == 'tpp'
312        display()
313        return
314
315    if command == 'pps':
316        output.stat = packets / source['sim_seconds']
317        output.ylabel = 'Packets/s'
318        display()
319        return
320
321    if command == 'bpt' or command == 'tpb':
322        output.stat = bytes / system.run0.numCycles * 8
323        output.ylabel = 'bps / Hz'
324        output.invert = command == 'tpb'
325        display()
326        return
327
328    if command in ('rxbps', 'txbps', 'bps'):
329        if command == 'rxbps':
330            output.stat = etherdev.rxBandwidth / 1e9
331        if command == 'txbps':
332            output.stat = etherdev.txBandwidth / 1e9
333        if command == 'bps':
334            output.stat = (etherdev.rxBandwidth + etherdev.txBandwidth) / 1e9
335
336        output.ylabel = 'Bandwidth (Gbps)'
337        output.ylim = [ 0.0, 10.0 ]
338        display()
339        return
340
341    if command == 'bpp':
342        output.stat = bytes / packets
343        output.ylabel = 'Bytes / Packet'
344        display()
345        return
346
347    if command == 'rxbpp':
348        output.stat = etherdev.rxBytes / etherdev.rxPackets
349        output.ylabel = 'Receive Bytes / Packet'
350        display()
351        return
352
353    if command == 'txbpp':
354        output.stat = etherdev.txBytes / etherdev.txPackets
355        output.ylabel = 'Transmit Bytes / Packet'
356        display()
357        return
358
359    if command == 'rtp':
360        output.stat = etherdev.rxPackets / etherdev.txPackets
361        output.ylabel = 'rxPackets / txPackets'
362        display()
363        return
364
365    if command == 'rtb':
366        output.stat = etherdev.rxBytes / etherdev.txBytes
367        output.ylabel = 'rxBytes / txBytes'
368        display()
369        return
370
371    misses = system.l2.overall_mshr_misses
372
373    if command == 'misses':
374        output.stat = misses
375        output.ylabel = 'Overall MSHR Misses'
376        display()
377        return
378
379    if command == 'mpkb':
380        output.stat = misses / (bytes / 1024)
381        output.ylabel = 'Misses / KB'
382        display()
383        return
384
385    if command == 'ipkb':
386        interrupts = system.run0.kern.faults[4]
387        output.stat = interrupts / kbytes
388        output.ylabel = 'Interrupts / KB'
389        display()
390        return
391
392    if command == 'execute':
393        output.stat = system.run0.ISSUE__count
394        display()
395        return
396
397    if command == 'commit':
398        output.stat = system.run0.COM__count
399        display()
400        return
401
402    if command == 'fetch':
403        output.stat = system.run0.FETCH__count
404        display()
405        return
406
407    raise CommandException
408
409
410class Options: pass
411
412if __name__ == '__main__':
413    import getpass
414    from jobfile import JobFile
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 = 'Test.py'
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        options.jobfile = JobFile(jobfilename)
465        if not options.host:
466            options.host = options.jobfile.dbhost
467        if not options.db:
468            options.db = options.jobfile.statdb
469
470    if not options.host:
471        sys.exit('Database server must be provided from a jobfile or -h')
472
473    if not options.db:
474        sys.exit('Database name must be provided from a jobfile or -d')
475
476    if len(args) == 0:
477        usage()
478
479    command = args[0]
480    args = args[1:]
481
482    try:
483        commands(options, command, args)
484    except CommandException:
485        usage()
486