# 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 class MyDB(object): def __init__(self, options): self.name = options.db self.host = options.host self.user = options.user self.passwd = options.passwd self.mydb = None self.cursor = None def admin(self): self.close() self.mydb = MySQLdb.connect(db='mysql', host=self.host, user=self.user, passwd=self.passwd) self.cursor = self.mydb.cursor() def connect(self): self.close() self.mydb = MySQLdb.connect(db=self.name, host=self.host, user=self.user, passwd=self.passwd) self.cursor = self.mydb.cursor() def close(self): if self.mydb is not None: self.mydb.close() self.cursor = None def query(self, sql): self.cursor.execute(sql) def drop(self): self.query('DROP DATABASE IF EXISTS %s' % self.name) def create(self): self.query('CREATE DATABASE %s' % self.name) def populate(self): # # Each run (or simulation) gets its own entry in the runs table to # group stats by where they were generated # # COLUMNS: # 'id' is a unique identifier for each run to be used in other # tables. # 'name' is the user designated name for the data generated. It is # configured in the simulator. # 'user' identifies the user that generated the data for the given # run. # 'project' another name to identify runs for a specific goal # 'date' is a timestamp for when the data was generated. It can be # used to easily expire data that was generated in the past. # 'expire' is a timestamp for when the data should be removed from # the database so we don't have years worth of junk. # # INDEXES: # 'run' is indexed so you can find out details of a run if the run # was retreived from the data table. # 'name' is indexed so that two all run names are forced to be unique # self.query(''' CREATE TABLE runs( rn_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, rn_name VARCHAR(200) NOT NULL, rn_sample VARCHAR(32) NOT NULL, rn_user VARCHAR(32) NOT NULL, rn_project VARCHAR(100) NOT NULL, rn_date TIMESTAMP NOT NULL, rn_expire TIMESTAMP NOT NULL, PRIMARY KEY (rn_id), UNIQUE (rn_name,rn_sample) ) TYPE=InnoDB''') # # The stat table gives us all of the data for a particular stat. # # COLUMNS: # 'stat' is a unique identifier for each stat to be used in other # tables for references. # 'name' is simply the simulator derived name for a given # statistic. # 'descr' is the description of the statistic and what it tells # you. # 'type' defines what the stat tells you. Types are: # SCALAR: A simple scalar statistic that holds one value # VECTOR: An array of statistic values. Such a something that # is generated per-thread. Vectors exist to give averages, # pdfs, cdfs, means, standard deviations, etc across the # stat values. # DIST: Is a distribution of data. When the statistic value is # sampled, its value is counted in a particular bucket. # Useful for keeping track of utilization of a resource. # (e.g. fraction of time it is 25% used vs. 50% vs. 100%) # VECTORDIST: Can be used when the distribution needs to be # factored out into a per-thread distribution of data for # example. It can still be summed across threads to find # the total distribution. # VECTOR2D: Can be used when you have a stat that is not only # per-thread, but it is per-something else. Like # per-message type. # FORMULA: This statistic is a formula, and its data must be # looked up in the formula table, for indicating how to # present its values. # 'subdata' is potentially used by any of the vector types to # give a specific name to all of the data elements within a # stat. # 'print' indicates whether this stat should be printed ever. # (Unnamed stats don't usually get printed) # 'prereq' only print the stat if the prereq is not zero. # 'prec' number of decimal places to print # 'nozero' don't print zero values # 'nonan' don't print NaN values # 'total' for vector type stats, print the total. # 'pdf' for vector type stats, print the pdf. # 'cdf' for vector type stats, print the cdf. # # The Following are for dist type stats: # 'min' is the minimum bucket value. Anything less is an underflow. # 'max' is the maximum bucket value. Anything more is an overflow. # 'bktsize' is the approximate number of entries in each bucket. # 'size' is the number of buckets. equal to (min/max)/bktsize. # # INDEXES: # 'stat' is indexed so that you can find out details about a stat # if the stat id was retrieved from the data table. # 'name' is indexed so that you can simply look up data about a # named stat. # self.query(''' CREATE TABLE stats( st_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, st_name VARCHAR(255) NOT NULL, st_descr TEXT NOT NULL, st_type ENUM("SCALAR", "VECTOR", "DIST", "VECTORDIST", "VECTOR2D", "FORMULA") NOT NULL, st_print BOOL NOT NULL, st_prereq SMALLINT UNSIGNED NOT NULL, st_prec TINYINT NOT NULL, st_nozero BOOL NOT NULL, st_nonan BOOL NOT NULL, st_total BOOL NOT NULL, st_pdf BOOL NOT NULL, st_cdf BOOL NOT NULL, st_min DOUBLE NOT NULL, st_max DOUBLE NOT NULL, st_bktsize DOUBLE NOT NULL, st_size SMALLINT UNSIGNED NOT NULL, PRIMARY KEY (st_id), UNIQUE (st_name) ) TYPE=InnoDB''') # # This is the main table of data for stats. # # COLUMNS: # 'stat' refers to the stat field given in the stat table. # # 'x' referrs to the first dimension of a multi-dimensional stat. For # a vector, x will start at 0 and increase for each vector # element. # For a distribution: # -1: sum (for calculating standard deviation) # -2: sum of squares (for calculating standard deviation) # -3: total number of samples taken (for calculating # standard deviation) # -4: minimum value # -5: maximum value # -6: underflow # -7: overflow # 'y' is used by a VECTORDIST and the VECTOR2D to describe the second # dimension. # 'run' is the run that the data was generated from. Details up in # the run table # 'tick' is a timestamp generated by the simulator. # 'data' is the actual stat value. # # INDEXES: # 'stat' is indexed so that a user can find all of the data for a # particular stat. It is not unique, because that specific stat # can be found in many runs and samples, in addition to # having entries for the mulidimensional cases. # 'run' is indexed to allow a user to remove all of the data for a # particular execution run. It can also be used to allow the # user to print out all of the data for a given run. # self.query(''' CREATE TABLE data( dt_stat SMALLINT UNSIGNED NOT NULL, dt_x SMALLINT NOT NULL, dt_y SMALLINT NOT NULL, dt_run SMALLINT UNSIGNED NOT NULL, dt_tick BIGINT UNSIGNED NOT NULL, dt_data DOUBLE NOT NULL, INDEX (dt_stat), INDEX (dt_run), UNIQUE (dt_stat,dt_x,dt_y,dt_run,dt_tick) ) TYPE=InnoDB;''') # # Names and descriptions for multi-dimensional stats (vectors, etc.) # are stored here instead of having their own entry in the statistics # table. This allows all parts of a single stat to easily share a # single id. # # COLUMNS: # 'stat' is the unique stat identifier from the stat table. # 'x' is the first dimension for multi-dimensional stats # corresponding to the data table above. # 'y' is the second dimension for multi-dimensional stats # corresponding to the data table above. # 'name' is the specific subname for the unique stat,x,y combination. # 'descr' is the specific description for the uniqe stat,x,y # combination. # # INDEXES: # 'stat' is indexed so you can get the subdata for a specific stat. # self.query(''' CREATE TABLE subdata( sd_stat SMALLINT UNSIGNED NOT NULL, sd_x SMALLINT NOT NULL, sd_y SMALLINT NOT NULL, sd_name VARCHAR(255) NOT NULL, sd_descr TEXT, UNIQUE (sd_stat,sd_x,sd_y) ) TYPE=InnoDB''') # # The formula table is maintained separately from the data table # because formula data, unlike other stat data cannot be represented # there. # # COLUMNS: # 'stat' refers to the stat field generated in the stat table. # 'formula' is the actual string representation of the formula # itself. # # INDEXES: # 'stat' is indexed so that you can just look up a formula. # self.query(''' CREATE TABLE formulas( fm_stat SMALLINT UNSIGNED NOT NULL, fm_formula BLOB NOT NULL, PRIMARY KEY(fm_stat) ) TYPE=InnoDB''') # # Each stat used in each formula is kept in this table. This way, if # you want to print out a particular formula, you can simply find out # which stats you need by looking in this table. Additionally, when # you remove a stat from the stats table and data table, you remove # any references to the formula in this table. When a formula is no # longer referred to, you remove its entry. # # COLUMNS: # 'stat' is the stat id from the stat table above. # 'child' is the stat id of a stat that is used for this formula. # There may be many children for any given 'stat' (formula) # # INDEXES: # 'stat' is indexed so you can look up all of the children for a # particular stat. # 'child' is indexed so that you can remove an entry when a stat is # removed. # self.query(''' CREATE TABLE formula_ref( fr_stat SMALLINT UNSIGNED NOT NULL, fr_run SMALLINT UNSIGNED NOT NULL, UNIQUE (fr_stat,fr_run), INDEX (fr_stat), INDEX (fr_run) ) TYPE=InnoDB''') # COLUMNS: # 'event' is the unique event id from the event_desc table # 'run' is simulation run id that this event took place in # 'tick' is the tick when the event happened # # INDEXES: # 'event' is indexed so you can look up all occurences of a # specific event # 'run' is indexed so you can find all events in a run # 'tick' is indexed because we want the unique thing anyway # 'event,run,tick' is unique combination self.query(''' CREATE TABLE events( ev_event SMALLINT UNSIGNED NOT NULL, ev_run SMALLINT UNSIGNED NOT NULL, ev_tick BIGINT UNSIGNED NOT NULL, INDEX(ev_event), INDEX(ev_run), INDEX(ev_tick), UNIQUE(ev_event,ev_run,ev_tick) ) TYPE=InnoDB''') # COLUMNS: # 'id' is the unique description id # 'name' is the name of the event that occurred # # INDEXES: # 'id' is indexed because it is the primary key and is what you use # to look up the descriptions # 'name' is indexed so one can find the event based on name # self.query(''' CREATE TABLE event_names( en_id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT, en_name VARCHAR(255) NOT NULL, PRIMARY KEY (en_id), UNIQUE (en_name) ) TYPE=InnoDB''') def clean(self): self.query(''' DELETE data FROM data LEFT JOIN runs ON dt_run=rn_id WHERE rn_id IS NULL''') self.query(''' DELETE formula_ref FROM formula_ref LEFT JOIN runs ON fr_run=rn_id WHERE rn_id IS NULL''') self.query(''' DELETE formulas FROM formulas LEFT JOIN formula_ref ON fm_stat=fr_stat WHERE fr_stat IS NULL''') self.query(''' DELETE stats FROM stats LEFT JOIN data ON st_id=dt_stat WHERE dt_stat IS NULL''') self.query(''' DELETE subdata FROM subdata LEFT JOIN data ON sd_stat=dt_stat WHERE dt_stat IS NULL''') self.query(''' DELETE events FROM events LEFT JOIN runs ON ev_run=rn_id WHERE rn_id IS NULL''') self.query(''' DELETE event_names FROM event_names LEFT JOIN events ON en_id=ev_event WHERE ev_event IS NULL''')