summaryrefslogtreecommitdiffstats
path: root/unit_trace/gedf_test.py
diff options
context:
space:
mode:
Diffstat (limited to 'unit_trace/gedf_test.py')
-rw-r--r--unit_trace/gedf_test.py163
1 files changed, 163 insertions, 0 deletions
diff --git a/unit_trace/gedf_test.py b/unit_trace/gedf_test.py
new file mode 100644
index 0000000..8457901
--- /dev/null
+++ b/unit_trace/gedf_test.py
@@ -0,0 +1,163 @@
1###############################################################################
2# Description
3###############################################################################
4
5# G-EDF Test
6
7###############################################################################
8# Imports
9###############################################################################
10
11import copy
12
13
14###############################################################################
15# Public Functions
16###############################################################################
17
18def gedf_test(stream):
19
20 # Two lists to model the system: tasks occupying a CPU and tasks eligible
21 # to do so. Also, m = the number of CPUs.
22 eligible = []
23 on_cpu = []
24 m = None
25
26 # Time of the last record we saw. Only run the G-EDF test when the time
27 # is updated.
28 last_time = None
29
30 for record in stream:
31 if record.record_type != "event":
32 if record.record_type == "meta" and record.type_name == "num_cpus":
33 m = record.num_cpus
34 continue
35
36 # Check for inversion starts and ends and yield them.
37 # Only to the check when time has moved forward.
38 # (It is common to have records with simultaneous timestamps.)
39 if last_time is not None and last_time != record.when:
40 errors = _gedf_check(eligible,on_cpu,record.when,m)
41 for error in errors:
42 yield error
43
44 # Add a newly-released Job to the eligible queue
45 if record.type_name == 'release':
46 eligible.append(Job(record))
47
48 # Move a Job from the eligible queue to on_cpu
49 elif record.type_name == 'switch_to':
50 pos = _find_job(record,eligible)
51 job = eligible[pos]
52 del eligible[pos]
53 on_cpu.append(job)
54
55 # Mark a Job as completed.
56 # The only time a Job completes when it is not on a
57 # CPU is when it is the last job of the task.
58 elif record.type_name == 'completion':
59 pos = _find_job(record,on_cpu)
60 if pos is not None:
61 on_cpu[pos].is_complete = True
62 else:
63 pos = _find_job(record,eligible)
64 del eligible[pos]
65
66 # A job is switched away from a CPU. If it has
67 # been marked as complete, remove it from the model.
68 elif record.type_name == 'switch_away':
69 pos = _find_job(record,on_cpu)
70 job = on_cpu[pos]
71 del on_cpu[pos]
72 if job.is_complete == False:
73 eligible.append(job)
74
75 last_time = record.when
76 yield record
77
78###############################################################################
79# Private Functions
80###############################################################################
81
82# Internal representation of a Job
83class Job(object):
84 def __init__(self, record):
85 self.pid = record.pid
86 self.job = record.job
87 self.deadline = record.deadline
88 self.is_complete = False
89 self.inversion_start = None
90 self.inversion_end = None
91 def __str__(self):
92 return "(%d.%d:%d)" % (self.pid,self.job,self.deadline)
93
94# G-EDF errors: the start or end of an inversion
95class Error(object):
96 def __init__(self, job, eligible, on_cpu):
97 self.job = copy.copy(job)
98 self.eligible = copy.copy(eligible)
99 self.on_cpu = copy.copy(on_cpu)
100 self.record_type = 'error'
101 if job.inversion_end is None:
102 self.type_name = 'inversion_start'
103 else:
104 self.type_name = 'inversion_end'
105
106# Returns the position of a Job in a list, or None
107def _find_job(record,list):
108 for i in range(0,len(list)):
109 if list[i].pid == record.pid and list[i].job == record.job:
110 return i
111 return None
112
113# Return records for any inversion_starts and inversion_ends
114def _gedf_check(eligible,on_cpu,when,m):
115
116 # List of error records to be returned
117 errors = []
118
119 # List of all jobs that are not complete
120 all = []
121 for x in on_cpu:
122 if x.is_complete is not True:
123 all.append(x)
124 all += eligible
125
126 # Sort by on_cpu and then by deadline. sort() is guaranteed to be stable.
127 # Thus, this gives us jobs ordered by deadline with preference to those
128 # actually running.
129 all.sort(key=lambda x: 0 if (x in on_cpu) else 1)
130 all.sort(key=lambda x: x.deadline)
131
132 # Check those that actually should be running
133 for x in range(0,min(m,len(all))):
134 job = all[x]
135
136 # It's not running and an inversion_start has not been recorded
137 if job not in on_cpu and job.inversion_start is None:
138 job.inversion_start = when
139 errors.append(Error(job, eligible, on_cpu))
140
141 # It is running and an inversion_start exists (i.e. it it still
142 # marked as being inverted)
143 elif job in on_cpu and job.inversion_start is not None:
144 job.inversion_end = when
145 errors.append(Error(job, eligible, on_cpu))
146 job.inversion_start = None
147 job.inversion_end = None
148
149 # Check those that actually should not be running
150 for x in range(m,len(all)):
151 job = all[x]
152
153 # It actually is running. We don't care.
154
155 # It isn't running, but an inversion_start exists (i.e. it is still
156 # marked as being inverted)
157 if job not in on_cpu and job.inversion_start is not None:
158 job.inversion_end = when
159 errors.append(Error(job, eligible, on_cpu))
160 job.inversion_start = None
161 job.inversion_end = None
162
163 return errors