| # Copyright (c) 2003-2004 The Regents of The University of Michigan | |
| # All rights reserved. | |
| # | |
| # Redistribution and use in source and binary forms, with or without | |
| # modification, are permitted provided that the following conditions are | |
| # met: redistributions of source code must retain the above copyright | |
| # notice, this list of conditions and the following disclaimer; | |
| # redistributions in binary form must reproduce the above copyright | |
| # notice, this list of conditions and the following disclaimer in the | |
| # documentation and/or other materials provided with the distribution; | |
| # neither the name of the copyright holders nor the names of its | |
| # contributors may be used to endorse or promote products derived from | |
| # this software without specific prior written permission. | |
| # | |
| # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| # | |
| # Authors: Nathan Binkert | |
| import MySQLdb, re, string | |
| def statcmp(a, b): | |
| v1 = a.split('.') | |
| v2 = b.split('.') | |
| last = min(len(v1), len(v2)) - 1 | |
| for i,j in zip(v1[0:last], v2[0:last]): | |
| if i != j: | |
| return cmp(i, j) | |
| # Special compare for last element. | |
| if len(v1) == len(v2): | |
| return cmp(v1[last], v2[last]) | |
| else: | |
| return cmp(len(v1), len(v2)) | |
| class RunData: | |
| def __init__(self, row): | |
| self.run = int(row[0]) | |
| self.name = row[1] | |
| self.user = row[2] | |
| self.project = row[3] | |
| class SubData: | |
| def __init__(self, row): | |
| self.stat = int(row[0]) | |
| self.x = int(row[1]) | |
| self.y = int(row[2]) | |
| self.name = row[3] | |
| self.descr = row[4] | |
| class Data: | |
| def __init__(self, row): | |
| if len(row) != 5: | |
| raise 'stat db error' | |
| self.stat = int(row[0]) | |
| self.run = int(row[1]) | |
| self.x = int(row[2]) | |
| self.y = int(row[3]) | |
| self.data = float(row[4]) | |
| def __repr__(self): | |
| return '''Data(['%d', '%d', '%d', '%d', '%f'])''' % ( self.stat, | |
| self.run, self.x, self.y, self.data) | |
| class StatData(object): | |
| def __init__(self, row): | |
| self.stat = int(row[0]) | |
| self.name = row[1] | |
| self.desc = row[2] | |
| self.type = row[3] | |
| self.prereq = int(row[5]) | |
| self.precision = int(row[6]) | |
| import flags | |
| self.flags = 0 | |
| if int(row[4]): self.flags |= flags.printable | |
| if int(row[7]): self.flags |= flags.nozero | |
| if int(row[8]): self.flags |= flags.nonan | |
| if int(row[9]): self.flags |= flags.total | |
| if int(row[10]): self.flags |= flags.pdf | |
| if int(row[11]): self.flags |= flags.cdf | |
| if self.type == 'DIST' or self.type == 'VECTORDIST': | |
| self.min = float(row[12]) | |
| self.max = float(row[13]) | |
| self.bktsize = float(row[14]) | |
| self.size = int(row[15]) | |
| if self.type == 'FORMULA': | |
| self.formula = self.db.allFormulas[self.stat] | |
| class Node(object): | |
| def __init__(self, name): | |
| self.name = name | |
| def __str__(self): | |
| return self.name | |
| class Result(object): | |
| def __init__(self, x, y): | |
| self.data = {} | |
| self.x = x | |
| self.y = y | |
| def __contains__(self, run): | |
| return run in self.data | |
| def __getitem__(self, run): | |
| if run not in self.data: | |
| self.data[run] = [ [ 0.0 ] * self.y for i in xrange(self.x) ] | |
| return self.data[run] | |
| class Database(object): | |
| def __init__(self): | |
| self.host = 'zizzer.pool' | |
| self.user = '' | |
| self.passwd = '' | |
| self.db = 'm5stats' | |
| self.cursor = None | |
| self.allStats = [] | |
| self.allStatIds = {} | |
| self.allStatNames = {} | |
| self.allSubData = {} | |
| self.allRuns = [] | |
| self.allRunIds = {} | |
| self.allRunNames = {} | |
| self.allFormulas = {} | |
| self.stattop = {} | |
| self.statdict = {} | |
| self.statlist = [] | |
| self.mode = 'sum'; | |
| self.runs = None | |
| self.ticks = None | |
| self.method = 'sum' | |
| self._method = type(self).sum | |
| def get(self, job, stat, system=None): | |
| run = self.allRunNames.get(str(job), None) | |
| if run is None: | |
| return None | |
| from info import ProxyError, scalar, vector, value, values, total, len | |
| if system is None and hasattr(job, 'system'): | |
| system = job.system | |
| if system is not None: | |
| stat.system = self[system] | |
| try: | |
| if scalar(stat): | |
| return value(stat, run.run) | |
| if vector(stat): | |
| return values(stat, run.run) | |
| except ProxyError: | |
| return None | |
| return None | |
| def query(self, sql): | |
| self.cursor.execute(sql) | |
| def update_dict(self, dict): | |
| dict.update(self.stattop) | |
| def append(self, stat): | |
| statname = re.sub(':', '__', stat.name) | |
| path = string.split(statname, '.') | |
| pathtop = path[0] | |
| fullname = '' | |
| x = self | |
| while len(path) > 1: | |
| name = path.pop(0) | |
| if not x.__dict__.has_key(name): | |
| x.__dict__[name] = Node(fullname + name) | |
| x = x.__dict__[name] | |
| fullname = '%s%s.' % (fullname, name) | |
| name = path.pop(0) | |
| x.__dict__[name] = stat | |
| self.stattop[pathtop] = self.__dict__[pathtop] | |
| self.statdict[statname] = stat | |
| self.statlist.append(statname) | |
| def connect(self): | |
| # connect | |
| self.thedb = MySQLdb.connect(db=self.db, | |
| host=self.host, | |
| user=self.user, | |
| passwd=self.passwd) | |
| # create a cursor | |
| self.cursor = self.thedb.cursor() | |
| self.query('''select rn_id,rn_name,rn_sample,rn_user,rn_project | |
| from runs''') | |
| for result in self.cursor.fetchall(): | |
| run = RunData(result); | |
| self.allRuns.append(run) | |
| self.allRunIds[run.run] = run | |
| self.allRunNames[run.name] = run | |
| self.query('select sd_stat,sd_x,sd_y,sd_name,sd_descr from subdata') | |
| for result in self.cursor.fetchall(): | |
| subdata = SubData(result) | |
| if self.allSubData.has_key(subdata.stat): | |
| self.allSubData[subdata.stat].append(subdata) | |
| else: | |
| self.allSubData[subdata.stat] = [ subdata ] | |
| self.query('select * from formulas') | |
| for id,formula in self.cursor.fetchall(): | |
| self.allFormulas[int(id)] = formula.tostring() | |
| StatData.db = self | |
| self.query('select * from stats') | |
| import info | |
| for result in self.cursor.fetchall(): | |
| stat = info.NewStat(self, StatData(result)) | |
| self.append(stat) | |
| self.allStats.append(stat) | |
| self.allStatIds[stat.stat] = stat | |
| self.allStatNames[stat.name] = stat | |
| # Name: listruns | |
| # Desc: Prints all runs matching a given user, if no argument | |
| # is given all runs are returned | |
| def listRuns(self, user=None): | |
| print '%-40s %-10s %-5s' % ('run name', 'user', 'id') | |
| print '-' * 62 | |
| for run in self.allRuns: | |
| if user == None or user == run.user: | |
| print '%-40s %-10s %-10d' % (run.name, run.user, run.run) | |
| # Name: listTicks | |
| # Desc: Prints all samples for a given run | |
| def listTicks(self, runs=None): | |
| print "tick" | |
| print "----------------------------------------" | |
| sql = 'select distinct dt_tick from data where dt_stat=1180 and (' | |
| if runs != None: | |
| first = True | |
| for run in runs: | |
| if first: | |
| # sql += ' where' | |
| first = False | |
| else: | |
| sql += ' or' | |
| sql += ' dt_run=%s' % run.run | |
| sql += ')' | |
| self.query(sql) | |
| for r in self.cursor.fetchall(): | |
| print r[0] | |
| # Name: retTicks | |
| # Desc: Prints all samples for a given run | |
| def retTicks(self, runs=None): | |
| sql = 'select distinct dt_tick from data where dt_stat=1180 and (' | |
| if runs != None: | |
| first = True | |
| for run in runs: | |
| if first: | |
| first = False | |
| else: | |
| sql += ' or' | |
| sql += ' dt_run=%s' % run.run | |
| sql += ')' | |
| self.query(sql) | |
| ret = [] | |
| for r in self.cursor.fetchall(): | |
| ret.append(r[0]) | |
| return ret | |
| # Name: liststats | |
| # Desc: Prints all statistics that appear in the database, | |
| # the optional argument is a regular expression that can | |
| # be used to prune the result set | |
| def listStats(self, regex=None): | |
| print '%-60s %-8s %-10s' % ('stat name', 'id', 'type') | |
| print '-' * 80 | |
| rx = None | |
| if regex != None: | |
| rx = re.compile(regex) | |
| stats = [ stat.name for stat in self.allStats ] | |
| stats.sort(statcmp) | |
| for stat in stats: | |
| stat = self.allStatNames[stat] | |
| if rx == None or rx.match(stat.name): | |
| print '%-60s %-8s %-10s' % (stat.name, stat.stat, stat.type) | |
| # Name: liststats | |
| # Desc: Prints all statistics that appear in the database, | |
| # the optional argument is a regular expression that can | |
| # be used to prune the result set | |
| def listFormulas(self, regex=None): | |
| print '%-60s %s' % ('formula name', 'formula') | |
| print '-' * 80 | |
| rx = None | |
| if regex != None: | |
| rx = re.compile(regex) | |
| stats = [ stat.name for stat in self.allStats ] | |
| stats.sort(statcmp) | |
| for stat in stats: | |
| stat = self.allStatNames[stat] | |
| if stat.type == 'FORMULA' and (rx == None or rx.match(stat.name)): | |
| print '%-60s %s' % (stat.name, self.allFormulas[stat.stat]) | |
| def getStat(self, stats): | |
| if type(stats) is not list: | |
| stats = [ stats ] | |
| ret = [] | |
| for stat in stats: | |
| if type(stat) is int: | |
| ret.append(self.allStatIds[stat]) | |
| if type(stat) is str: | |
| rx = re.compile(stat) | |
| for stat in self.allStats: | |
| if rx.match(stat.name): | |
| ret.append(stat) | |
| return ret | |
| ######################################### | |
| # get the data | |
| # | |
| def query(self, op, stat, ticks, group=False): | |
| sql = 'select ' | |
| sql += 'dt_stat as stat, ' | |
| sql += 'dt_run as run, ' | |
| sql += 'dt_x as x, ' | |
| sql += 'dt_y as y, ' | |
| if group: | |
| sql += 'dt_tick as tick, ' | |
| sql += '%s(dt_data) as data ' % op | |
| sql += 'from data ' | |
| sql += 'where ' | |
| if isinstance(stat, list): | |
| val = ' or '.join([ 'dt_stat=%d' % s.stat for s in stat ]) | |
| sql += ' (%s)' % val | |
| else: | |
| sql += ' dt_stat=%d' % stat.stat | |
| if self.runs != None and len(self.runs): | |
| val = ' or '.join([ 'dt_run=%d' % r for r in self.runs ]) | |
| sql += ' and (%s)' % val | |
| if ticks != None and len(ticks): | |
| val = ' or '.join([ 'dt_tick=%d' % s for s in ticks ]) | |
| sql += ' and (%s)' % val | |
| sql += ' group by dt_stat,dt_run,dt_x,dt_y' | |
| if group: | |
| sql += ',dt_tick' | |
| return sql | |
| # Name: sum | |
| # Desc: given a run, a stat and an array of samples, total the samples | |
| def sum(self, *args, **kwargs): | |
| return self.query('sum', *args, **kwargs) | |
| # Name: avg | |
| # Desc: given a run, a stat and an array of samples, average the samples | |
| def avg(self, stat, ticks): | |
| return self.query('avg', *args, **kwargs) | |
| # Name: stdev | |
| # Desc: given a run, a stat and an array of samples, get the standard | |
| # deviation | |
| def stdev(self, stat, ticks): | |
| return self.query('stddev', *args, **kwargs) | |
| def __setattr__(self, attr, value): | |
| super(Database, self).__setattr__(attr, value) | |
| if attr != 'method': | |
| return | |
| if value == 'sum': | |
| self._method = self.sum | |
| elif value == 'avg': | |
| self._method = self.avg | |
| elif value == 'stdev': | |
| self._method = self.stdev | |
| else: | |
| raise AttributeError, "can only set get to: sum | avg | stdev" | |
| def data(self, stat, ticks=None): | |
| if ticks is None: | |
| ticks = self.ticks | |
| sql = self._method(self, stat, ticks) | |
| self.query(sql) | |
| runs = {} | |
| xmax = 0 | |
| ymax = 0 | |
| for x in self.cursor.fetchall(): | |
| data = Data(x) | |
| if not runs.has_key(data.run): | |
| runs[data.run] = {} | |
| if not runs[data.run].has_key(data.x): | |
| runs[data.run][data.x] = {} | |
| xmax = max(xmax, data.x) | |
| ymax = max(ymax, data.y) | |
| runs[data.run][data.x][data.y] = data.data | |
| results = Result(xmax + 1, ymax + 1) | |
| for run,data in runs.iteritems(): | |
| result = results[run] | |
| for x,ydata in data.iteritems(): | |
| for y,data in ydata.iteritems(): | |
| result[x][y] = data | |
| return results | |
| def __getitem__(self, key): | |
| return self.stattop[key] |