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