diff options
| author | Mac Mollison <mollison@cs.unc.edu> | 2009-03-03 20:16:52 -0500 |
|---|---|---|
| committer | Mac Mollison <mollison@cs.unc.edu> | 2009-03-03 20:16:52 -0500 |
| commit | ab1f1ca679d1ef327f9a3cac2eba48027ccd0cda (patch) | |
| tree | 3624b84e14ff7ccf7c7021e7b8aa77c97b199353 | |
| parent | b2d14e0e42b0f3983611128c1f6aae91892f29e7 (diff) | |
Re-implemented EDF test with new stragety. Not tested yet, but a lot of progress has been made.
| -rw-r--r-- | README | 19 | ||||
| -rwxr-xr-x | run.py | 5 | ||||
| -rwxr-xr-x | sta.py | 244 |
3 files changed, 105 insertions, 163 deletions
| @@ -10,21 +10,12 @@ The general idea it that you use run.py to put in the commands you want to do, | |||
| 10 | and then do something like ./run.py > out to write out the records. | 10 | and then do something like ./run.py > out to write out the records. |
| 11 | 11 | ||
| 12 | 12 | ||
| 13 | ############################ | ||
| 14 | Gotchas | ||
| 15 | ############################ | ||
| 16 | Remember that a StReleaseData record has 'release_time', not 'when', as a field. | ||
| 17 | |||
| 18 | |||
| 19 | ############################# | 13 | ############################# |
| 20 | Development Notes | 14 | Development Notes |
| 21 | ############################# | 15 | ############################# |
| 22 | 16 | ||
| 23 | EDF tester seems to be working, but does not account for this scenario: | 17 | Need to account for situation where there is a tie in deadline which spans the |
| 24 | -release | 18 | topN category. |
| 25 | -switch_to | 19 | |
| 26 | -switch_away | 20 | Might think about adding in hooks, so you do the trace, then add in the hook to |
| 27 | -switch_to | 21 | see the state at the place you're curious about. |
| 28 | Rather, it finds the release and the first switch_to, leaving the second | ||
| 29 | switch_to in the queue of switches, thus upsetting the order of switches. The | ||
| 30 | way to solve this is to add switch_aways into the release filter. | ||
| @@ -6,8 +6,8 @@ import sta | |||
| 6 | ###################################### | 6 | ###################################### |
| 7 | 7 | ||
| 8 | def main(): | 8 | def main(): |
| 9 | myEDF() | 9 | #myEDF() |
| 10 | #myTrace() | 10 | myTrace() |
| 11 | #switchToTrace() | 11 | #switchToTrace() |
| 12 | #releaseTrace() | 12 | #releaseTrace() |
| 13 | #oneCPU() | 13 | #oneCPU() |
| @@ -27,6 +27,7 @@ def myEDF(): | |||
| 27 | def myTrace(): | 27 | def myTrace(): |
| 28 | trace = sta.Trace(g6_list) | 28 | trace = sta.Trace(g6_list) |
| 29 | trace.sort('when',alt='release_time') | 29 | trace.sort('when',alt='release_time') |
| 30 | trace.filter('type==2') | ||
| 30 | trace.print_count(True) | 31 | trace.print_count(True) |
| 31 | trace.print_records() | 32 | trace.print_records() |
| 32 | 33 | ||
| @@ -146,168 +146,118 @@ class Trace: | |||
| 146 | ################## | 146 | ################## |
| 147 | 147 | ||
| 148 | class EDF: | 148 | class EDF: |
| 149 | """Object representing an EDF test | ||
| 150 | 149 | ||
| 151 | The strategy is to create a model of runnable and running tasks, which is | 150 | def __init__(self,trace_files, tolerance=150000, N=4): |
| 152 | updated and checked against an invariant every tick_size time units | ||
| 153 | |||
| 154 | The invariant is that every running task much have an earlier deadline | ||
| 155 | than every runnable task; an error message is not generated until the | ||
| 156 | invariant is false for more than a specified number of ticks (given by | ||
| 157 | 'tolerance')""" | ||
| 158 | |||
| 159 | def __init__(self,trace_files, tick_size=1000000, tolerance=1): | ||
| 160 | """Create an EDF test. | ||
| 161 | |||
| 162 | tick_size = check the invariant every tick_size units of time | ||
| 163 | |||
| 164 | tolerance = number of tick_size time units which can pass before | ||
| 165 | a false invariant triggers an error message""" | ||
| 166 | self.trace_files = trace_files | 151 | self.trace_files = trace_files |
| 167 | self.errors = 0 | ||
| 168 | self.runnables = [] | ||
| 169 | self.runnings = [] | ||
| 170 | self.time = None | ||
| 171 | self.tick_size = tick_size | ||
| 172 | self.tolerance = tolerance | 152 | self.tolerance = tolerance |
| 153 | self.jobs = [] | ||
| 154 | self.N = N #number of cores | ||
| 155 | self.now = 0 | ||
| 173 | 156 | ||
| 174 | def run_test(self): | 157 | def run_test(self): |
| 175 | """Run an EDF test""" | ||
| 176 | |||
| 177 | #Make the trace, in order | ||
| 178 | trace = Trace(self.trace_files) | 158 | trace = Trace(self.trace_files) |
| 179 | trace.sort('when',alt='release_time') | 159 | trace.sort('when',alt='release_time') |
| 180 | trace.iter = trace.iter.__iter__() | ||
| 181 | |||
| 182 | #Find the start time from the SysRelease record | ||
| 183 | while True: | ||
| 184 | record = trace.iter.__next__() | ||
| 185 | if record['type']==10: | ||
| 186 | self.time = record['when'] | ||
| 187 | print("Starting time: " + str(self.time)) | ||
| 188 | break | ||
| 189 | |||
| 190 | #Initialize variables needed for the algorithm | ||
| 191 | last_record = trace.iter.__next__() | ||
| 192 | finished = False | ||
| 193 | |||
| 194 | #Increment the time, update the model, and check the invariant | ||
| 195 | while not finished: | ||
| 196 | |||
| 197 | #Increment the time | ||
| 198 | self.time += self.tick_size | ||
| 199 | print("-"*50) | ||
| 200 | |||
| 201 | #Update the model with records that have passed the time | ||
| 202 | while Trace.getTime(last_record) < self.time: | ||
| 203 | last_record['overtime'] = 0 | ||
| 204 | self.update_model(last_record) | ||
| 205 | try: | ||
| 206 | last_record = trace.iter.__next__() | ||
| 207 | except StopIteration: | ||
| 208 | finished = True | ||
| 209 | break | ||
| 210 | |||
| 211 | #Check the invariant | ||
| 212 | self.check_invariant() | ||
| 213 | |||
| 214 | print('Errors : {0}'.format(self.errors)) | ||
| 215 | |||
| 216 | 160 | ||
| 217 | def check_invariant(self): | 161 | for record in trace.iter: |
| 218 | """Check the invariant: the earliest deadline in runnables should be | 162 | |
| 219 | later than the latest deadline in runnings""" | 163 | #Update now |
| 220 | 164 | if 'when' in record: | |
| 221 | #Find the max deadline in runnings | 165 | self.now = record['when'] |
| 222 | max_deadline = None | 166 | elif 'release_time' in record: |
| 223 | for record in self.runnings: | 167 | self.now = record['release_time'] |
| 224 | if max_deadline==None: | 168 | |
| 225 | max_deadline = record['deadline'] | 169 | #Process record |
| 170 | if record['type'] == 3: | ||
| 171 | if self.is_duplicate_release(record): continue | ||
| 172 | job = EDF.Job() | ||
| 173 | job.deadline = record['deadline'] | ||
| 174 | job.release_time = record['release_time'] | ||
| 175 | job.job = record['job'] | ||
| 176 | job.pid = record['pid'] | ||
| 177 | self.jobs.append(job) | ||
| 178 | print('{0}: {1} was released'.format(self.now,job.get_name())) | ||
| 179 | elif record['type'] == 5: | ||
| 180 | job = self.get_job(record['pid'],record['job']) | ||
| 181 | if job: | ||
| 182 | job.running = True | ||
| 183 | else: | ||
| 184 | continue | ||
| 185 | print('{0}: {1} was switched to' | ||
| 186 | .format(self.now,job.get_name())) | ||
| 187 | elif record['type'] == 6: | ||
| 188 | job = self.get_job(record['pid'],record['job']) | ||
| 189 | if job: | ||
| 190 | job.running = False | ||
| 191 | else: | ||
| 192 | continue | ||
| 193 | print('{0}: {1} was switched away' | ||
| 194 | .format(self.now,job.get_name())) | ||
| 195 | elif record['type'] == 7: | ||
| 196 | job = self.get_job(record['pid'],record['job']) | ||
| 197 | if job: | ||
| 198 | self.jobs.remove(job) | ||
| 199 | else: | ||
| 200 | continue | ||
| 201 | print('{0}: {1} completed' | ||
| 202 | .format(self.now,job.get_name())) | ||
| 203 | else: | ||
| 226 | continue | 204 | continue |
| 227 | elif record['deadline'] > max_deadline: | 205 | |
| 228 | max_deadline = record['deadline'] | 206 | #Sort jobs by deadline |
| 229 | 207 | self.jobs = sorted(self.jobs,key=lambda job: job.deadline) | |
| 230 | #Check against the deadlines in runnables | 208 | |
| 231 | if max_deadline: | 209 | #Check for inversions (and end-of inversions) in the top N jobs |
| 232 | for record in self.runnables: | 210 | for job in self.jobs[0:self.N]: |
| 233 | if record['deadline'] < max_deadline: | 211 | job.topN = True |
| 234 | record['overtime'] += 1 | 212 | if job.running == False: |
| 235 | if record['overtime'] > self.tolerance: | 213 | if job.inversion_start_time==0: |
| 236 | print("INVALID!") | 214 | job.inversion_start_time=self.now |
| 237 | self.print_runnables() | 215 | elif job.running == True: |
| 238 | self.print_runnings() | 216 | if job.inversion_start_time > 0: |
| 239 | record['overtime'] = -99999999999999999 | 217 | self.check_error(job) |
| 240 | self.errors += 1 | 218 | job.inversion_start_time = 0 |
| 241 | 219 | ||
| 242 | def update_model(self, record): | 220 | #Check for end-of-inversions in the other jobs |
| 243 | """Update our model of the system: running tasks ('self.runnings') and | 221 | for job in self.jobs[self.N:]: |
| 244 | runnable tasks ('self.runnable')""" | 222 | if job.topN == True: |
| 245 | 223 | job.topN = False | |
| 246 | #Releases | 224 | self.check_error(job) |
| 247 | if record['type'] == 3: | 225 | |
| 248 | if not self.check_duplicate_release(record): | 226 | def check_error(self, job): |
| 249 | self.runnables.append(record) | 227 | if job.inversion_start_time == 0: return |
| 250 | print("{0} became runnable" | 228 | if self.now - job.inversion_start_time > self.tolerance: |
| 251 | .format(Trace.getStr(record))) | 229 | print(' ' * 16 + job.get_name() + |
| 252 | 230 | ' was inverted for {0} time units,' | |
| 253 | #Switch Tos | 231 | .format(self.now - job.inversion_start_time)) |
| 254 | elif record['type'] == 5: | 232 | print(' '*26 + 'since {0}'.format(job.inversion_start_time)) |
| 255 | release_record = EDF.pop_job(record, self.runnables) | 233 | job.inversion_start_time = 0 |
| 256 | self.runnings.append(release_record) | 234 | |
| 257 | print("{0} became running".format(Trace.getStr(record))) | 235 | def get_job(self, pid, job): |
| 258 | 236 | for x in self.jobs: | |
| 259 | #Switch Aways | 237 | if x.pid == pid and x.job == job: |
| 260 | elif record['type'] == 6: | 238 | return x |
| 261 | release_record = EDF.pop_job(record, self.runnings) | 239 | |
| 262 | if release_record: | 240 | def is_duplicate_release(self, record): |
| 263 | self.runnables.append(release_record) | ||
| 264 | print('{0} went from running to runnable' | ||
| 265 | .format(Trace.getStr(record))) | ||
| 266 | |||
| 267 | #Completions | ||
| 268 | elif record['type'] == 7: | ||
| 269 | release_record = EDF.pop_job(record,self.runnings) | ||
| 270 | if not release_record: | ||
| 271 | release_record = EDF.pop_job(record,self.runnables) | ||
| 272 | print('{0} completed' | ||
| 273 | .format(Trace.getStr(record))) | ||
| 274 | |||
| 275 | def pop_job(record, record_list): | ||
| 276 | """Pop a job from a list of jobs""" | ||
| 277 | job = record['job'] | ||
| 278 | pid = record['pid'] | ||
| 279 | for record in record_list: | ||
| 280 | if record['job'] == job and record['pid'] == pid: | ||
| 281 | record_copy = copy.copy(record) | ||
| 282 | record_list.remove(record) | ||
| 283 | return record_copy | ||
| 284 | return None | ||
| 285 | |||
| 286 | def check_duplicate_release(self, record): | ||
| 287 | """Make sure we don't get duplicate releases""" | 241 | """Make sure we don't get duplicate releases""" |
| 288 | job = record['job'] | 242 | job = record['job'] |
| 289 | pid = record['pid'] | 243 | pid = record['pid'] |
| 290 | for record in self.runnables: | 244 | for x in self.jobs: |
| 291 | if record['job'] == job and record['pid'] == pid: | 245 | if x.job == job and x.pid == pid: |
| 292 | return True | 246 | return True |
| 293 | return False | 247 | return False |
| 294 | 248 | ||
| 295 | def print_runnables(self): | 249 | class Job: |
| 296 | """Print the runnable jobs""" | 250 | def __init__(self): |
| 297 | print("Runnable jobs:") | 251 | self.release_time = 0 |
| 298 | for record in self.runnables: | 252 | self.deadline = 0 |
| 299 | print('{0}, Deadline: {1}'.format(Trace.getStr(record, | 253 | self.job = 0 |
| 300 | omitTime=True), | 254 | self.pid = 0 |
| 301 | record['deadline'])) | 255 | self.running = False |
| 302 | 256 | self.topN = False | |
| 303 | def print_runnings(self): | 257 | self.inversion_start_time = 0 |
| 304 | """Print the runnable jobs""" | 258 | |
| 305 | print("Running jobs:") | 259 | def get_name(self): |
| 306 | for record in self.runnings: | 260 | return '({0},{1})'.format(self.pid,self.job) |
| 307 | print('{0}, Deadline: {1}'.format(Trace.getStr(record, | ||
| 308 | omitTime=True), | ||
| 309 | record['deadline'])) | ||
| 310 | |||
| 311 | 261 | ||
| 312 | #################################### | 262 | #################################### |
| 313 | # Types for binary data conversion # | 263 | # Types for binary data conversion # |
