diff options
author | Mac Mollison <mollison@cs.unc.edu> | 2010-02-08 00:24:40 -0500 |
---|---|---|
committer | Mac Mollison <mollison@cs.unc.edu> | 2010-02-08 00:27:11 -0500 |
commit | 1016a3e271faebb899766f5d18468dd88b4d84b7 (patch) | |
tree | 387ab36e8ee886d8d6741d5820c178c974f46252 | |
parent | cd6e43f37856f7fe6b60e0e2ae45f864a4bd6d64 (diff) |
This is the beginning of a very major refactoring of the tool.
Current features:
- Create a record stream from trace files
- Print the record stream to standard out
- TODO file
- README file
- run.py file to set up and execute the testing pipeline
-rw-r--r-- | README | 1 | ||||
-rw-r--r-- | TODO | 3 | ||||
-rwxr-xr-x | run.py | 34 | ||||
-rw-r--r-- | test_driver/make_devices | 18 | ||||
-rw-r--r-- | test_driver/sample_test_case.py | 9 | ||||
-rwxr-xr-x | test_driver/test.py | 147 | ||||
-rw-r--r-- | text_print.py | 23 | ||||
-rw-r--r-- | trace.py | 138 | ||||
-rw-r--r-- | trace_analyzer/README | 21 | ||||
-rwxr-xr-x | trace_analyzer/run.py | 99 | ||||
-rwxr-xr-x | trace_analyzer/sta.py | 365 |
11 files changed, 199 insertions, 659 deletions
@@ -0,0 +1 @@ | |||
See the LITMUS Wiki page for an explanation of this tool. | |||
@@ -0,0 +1,3 @@ | |||
1 | - Currently, trace.py reads all records from the first trace file, then all | ||
2 | records from the second, etc. Instead it should progress through all | ||
3 | files simultaneously, producing records in order based on timestamp. | ||
@@ -0,0 +1,34 @@ | |||
1 | #!/usr/bin/env python3 | ||
2 | |||
3 | ############################################################################### | ||
4 | # Description | ||
5 | ############################################################################### | ||
6 | |||
7 | # Use this file to set up and execute a testing pipeline | ||
8 | # Sample pipeline provided | ||
9 | |||
10 | |||
11 | ############################################################################### | ||
12 | # Imports | ||
13 | ############################################################################### | ||
14 | |||
15 | import trace | ||
16 | import text_print | ||
17 | |||
18 | ############################################################################### | ||
19 | # Trace files | ||
20 | ############################################################################### | ||
21 | |||
22 | g4 = [ | ||
23 | '/home/mollison/old/sta/traces/st-g4-0.bin', | ||
24 | '/home/mollison/old/sta/traces/st-g4-1.bin', | ||
25 | '/home/mollison/old/sta/traces/st-g4-2.bin', | ||
26 | '/home/mollison/old/sta/traces/st-g4-3.bin' | ||
27 | ] | ||
28 | |||
29 | ############################################################################### | ||
30 | # Pipeline | ||
31 | ############################################################################### | ||
32 | |||
33 | stream = trace.get_trace_record_stream(g4) | ||
34 | text_print.print_stream(stream) | ||
diff --git a/test_driver/make_devices b/test_driver/make_devices deleted file mode 100644 index 4d4554b..0000000 --- a/test_driver/make_devices +++ /dev/null | |||
@@ -1,18 +0,0 @@ | |||
1 | #!/bin/bash | ||
2 | |||
3 | #Creates device drivers for traces (litmus_log, ft_trace and sched_trace) | ||
4 | #Taken from http://www.cs.unc.edu/~anderson/litmus-rt/doc/tracing.html | ||
5 | |||
6 | LITMUS_LOG_MAJOR=`grep litmus_log /proc/devices | awk '{print $1}'` | ||
7 | FT_TRACE_MAJOR=`grep ft_trace /proc/devices | awk '{print $1}'` | ||
8 | SCHED_TRACE_MAJOR=`grep sched_trace /proc/devices | awk '{print $1}'` | ||
9 | |||
10 | mknod litmus_log c $LITMUS_LOG_MAJOR 0 | ||
11 | mknod ft_trace c $FT_TRACE_MAJOR 0 | ||
12 | |||
13 | NUM_PROCS=$((`grep 'processor' /proc/cpuinfo | wc -l` - 1)) | ||
14 | |||
15 | for P in `seq 0 $NUM_PROCS` | ||
16 | do | ||
17 | mknod "sched_trace$P" c $SCHED_TRACE_MAJOR $P | ||
18 | done | ||
diff --git a/test_driver/sample_test_case.py b/test_driver/sample_test_case.py deleted file mode 100644 index 6cb886e..0000000 --- a/test_driver/sample_test_case.py +++ /dev/null | |||
@@ -1,9 +0,0 @@ | |||
1 | #Sample test case for the test driver. | ||
2 | |||
3 | #Each element of the test_case list is a real-time executable task, with parameters. | ||
4 | #For each task, we have a list: [location of executable,wcet,period,duration]. | ||
5 | |||
6 | test_case = [ | ||
7 | ['/root/liblitmus/rtspin',10,100,1000] | ||
8 | ['/root/liblitmus/rtspin',10,100,1000] | ||
9 | ] | ||
diff --git a/test_driver/test.py b/test_driver/test.py deleted file mode 100755 index 87038a0..0000000 --- a/test_driver/test.py +++ /dev/null | |||
@@ -1,147 +0,0 @@ | |||
1 | #!/usr/bin/env python3 | ||
2 | |||
3 | ########## | ||
4 | #Settings# | ||
5 | ########## | ||
6 | |||
7 | #liblitmus directory (You need to put the compiled liblitmus here) | ||
8 | liblitmus_dir = '/root/liblitmus/' | ||
9 | |||
10 | #ft_tools directory (You need to but the compiled ft_tools here) | ||
11 | ft_tools_dir = '/root/ft_tools/' | ||
12 | |||
13 | #device files directory (This script will set up the device files here) | ||
14 | device_dir = '/root/device_files/' | ||
15 | |||
16 | #recorded trace directory (This script will place recorded traces here) | ||
17 | trace_dir = '/root/traces/' | ||
18 | |||
19 | #desired scheduling policy, to be set by this script | ||
20 | #options listed in /proc/litmus/plugins | ||
21 | policy = 'GSN-EDF' | ||
22 | |||
23 | #desired test case | ||
24 | test_case_file = 'sample_test_case' | ||
25 | |||
26 | #name of traces | ||
27 | trace_name = 'driver' | ||
28 | |||
29 | #Additional instructions: | ||
30 | # -Make sure you've chmod +x'd the make_devices file and kept it in the same | ||
31 | # dir as test.py | ||
32 | # -Make sure you've set up a test case | ||
33 | |||
34 | ########### | ||
35 | # Imports # | ||
36 | ########### | ||
37 | |||
38 | import subprocess | ||
39 | import os | ||
40 | import time | ||
41 | exec('from ' + test_case_file + ' import test_case') | ||
42 | |||
43 | ################# | ||
44 | # Run the tests # | ||
45 | ################# | ||
46 | |||
47 | def main(): | ||
48 | |||
49 | #Determine directory of test.py | ||
50 | test_dir = os.getcwd() | ||
51 | |||
52 | #Set scheduling policy | ||
53 | print("Setting scheduling policy... ",end='') | ||
54 | ret = subprocess.getstatusoutput( | ||
55 | 'echo ' + policy + ' > ' + '/proc/litmus/active_plugin') | ||
56 | if ret[0] == 0: | ||
57 | print("[SUCCESS]") | ||
58 | else: | ||
59 | print("[FAILURE], error was:") | ||
60 | print(str(ret[0]) + ': ' + ret[1]) | ||
61 | exit() | ||
62 | |||
63 | #Check for execute permission on the make_devices script | ||
64 | print("Checking for permission to run make_devices... ",end='') | ||
65 | if not os.access('./make_devices',os.X_OK): | ||
66 | print("\nAttempting to chmod +x make_devices... ",end='') | ||
67 | ret = subprocess.getstatusoutput('chmod +x ./make_devices') | ||
68 | if ret[0]!=1: | ||
69 | print("[FAILURE], error was:") | ||
70 | print(str(ret[0]) + ': ' + ret[1]) | ||
71 | exit() | ||
72 | print("[SUCCESS]") | ||
73 | |||
74 | #Setup the device files for the traces | ||
75 | print("Creating device files. OK if they already exist.") | ||
76 | subprocess.Popen(test_dir + '/make_devices',cwd=device_dir) | ||
77 | time.sleep(1) | ||
78 | |||
79 | #Set up environment variables needed for st_trace script | ||
80 | print("Setting up environment variables... ",end='') | ||
81 | os.putenv('FTCAT', ft_tools_dir + 'ftcat') | ||
82 | os.putenv('FTDEV',device_dir + 'sched_trace') | ||
83 | print("[SUCCESS]") | ||
84 | |||
85 | #See if we can access and run st_trace | ||
86 | print("Checking for permission to run st_trace... ",end='') | ||
87 | if not os.access(ft_tools_dir + 'st_trace',os.X_OK): | ||
88 | print("\nAttempting to chmod +x st_trace... ",end='') | ||
89 | ret = subprocess.getstatusoutput('chmod +x ' + | ||
90 | ft_tools_dir + 'st_trace') | ||
91 | if ret[0]!=1: | ||
92 | print("[FAILURE], error was:") | ||
93 | print(str(ret[0]) + ': ' + ret[1]) | ||
94 | exit() | ||
95 | print("[SUCCESS]") | ||
96 | |||
97 | #Start sched_trace | ||
98 | print("Starting st_trace trace recorder... ",end='') | ||
99 | st_trace = subprocess.Popen([ft_tools_dir + 'st_trace',trace_name], | ||
100 | cwd=trace_dir) | ||
101 | print("[SUCCESS]") | ||
102 | |||
103 | #We will get an error there, if the test.py program terminates. | ||
104 | #time.sleep(7) | ||
105 | |||
106 | #Start tasks in test_case | ||
107 | print("Launching tasks... ",end='') | ||
108 | for task in test_case: | ||
109 | subprocess.Popen([task[0],'-w',str(task[1]),str(task[2]),str(task[3])]) | ||
110 | print("[SUCCESS]") | ||
111 | |||
112 | #Wait for tasks to suspend themselves | ||
113 | print("Waiting for tasks to suspend... ",end='') | ||
114 | time.sleep(3) | ||
115 | print("[SUCCESS]") | ||
116 | |||
117 | #Release the tasks | ||
118 | print("Releasing the tasks... ",end='') | ||
119 | ret = subprocess.getstatusoutput(liblitmus_dir + 'release_ts') | ||
120 | if ret[0] != 0: | ||
121 | print("[FAILURE], error was:") | ||
122 | print(str(ret[0]) + ': ' + ret[1]) | ||
123 | exit() | ||
124 | print("[SUCCESS]") | ||
125 | |||
126 | #Wait for the tasks to complete. I still need to implement this. | ||
127 | |||
128 | |||
129 | #Current steps... | ||
130 | #Run st_trace (must tell it location of sched_trace device files exactly) | ||
131 | #Let it get set up, then suspend | ||
132 | #let it run in background with bg <job> | ||
133 | #Run rtspin 10 100 1000 twice; suspend, run in background | ||
134 | #Bring rtspins to foreground and stop them. | ||
135 | #Bring st_trace to foreground, hit enter to complete it. | ||
136 | |||
137 | #Need to try to do the above with rt_launch and then release_ts | ||
138 | |||
139 | |||
140 | |||
141 | ############## | ||
142 | # start main # | ||
143 | ############## | ||
144 | |||
145 | if __name__=='__main__': | ||
146 | main() | ||
147 | |||
diff --git a/text_print.py b/text_print.py new file mode 100644 index 0000000..7254b51 --- /dev/null +++ b/text_print.py | |||
@@ -0,0 +1,23 @@ | |||
1 | ############################################################################### | ||
2 | # Description | ||
3 | ############################################################################### | ||
4 | |||
5 | # Prints records to standard out | ||
6 | |||
7 | ############################################################################### | ||
8 | # Public functions | ||
9 | ############################################################################### | ||
10 | |||
11 | def print_stream(stream): | ||
12 | for record in stream: | ||
13 | if record.record_type == "event": | ||
14 | _print_event(record) | ||
15 | print("") | ||
16 | |||
17 | ############################################################################### | ||
18 | # Private functions | ||
19 | ############################################################################### | ||
20 | |||
21 | def _print_event(record): | ||
22 | print("Job: {}.{}".format(record.pid,record.job)) | ||
23 | print("Type: {}".format(record.type_name)) | ||
diff --git a/trace.py b/trace.py new file mode 100644 index 0000000..1ceccae --- /dev/null +++ b/trace.py | |||
@@ -0,0 +1,138 @@ | |||
1 | ############################################################################### | ||
2 | # Description | ||
3 | ############################################################################### | ||
4 | |||
5 | # get_trace_record_stream(files) returns an iterator which produces records | ||
6 | # in order from the files given. (the param is a list of files.) | ||
7 | |||
8 | |||
9 | ############################################################################### | ||
10 | # Imports | ||
11 | ############################################################################### | ||
12 | |||
13 | import struct | ||
14 | |||
15 | |||
16 | ############################################################################### | ||
17 | # Public functions | ||
18 | ############################################################################### | ||
19 | |||
20 | # Generator function returning an iterable over records in a trace file. | ||
21 | def get_trace_record_stream(files): | ||
22 | for file in files: | ||
23 | with open(file,'rb') as f: | ||
24 | while True: | ||
25 | data = f.read(24) | ||
26 | try: | ||
27 | type_num = struct.unpack_from('b',data)[0] | ||
28 | except struct.error: | ||
29 | break #We read to the end of the file | ||
30 | type = _get_type(type_num) | ||
31 | try: | ||
32 | values = struct.unpack_from(StHeader.format + | ||
33 | type.format,data) | ||
34 | record_dict = dict(zip(type.keys,values)) | ||
35 | except struct.error: | ||
36 | f.close() | ||
37 | print("Invalid record detected, stopping.") | ||
38 | exit() | ||
39 | Record = _dict2obj(record_dict) | ||
40 | Record.type_name = _get_type_name(type_num) | ||
41 | Record.record_type = "event" | ||
42 | yield Record | ||
43 | |||
44 | ############################################################################### | ||
45 | # Private functions | ||
46 | ############################################################################### | ||
47 | |||
48 | # Convert a dict into an object | ||
49 | def _dict2obj(d): | ||
50 | class Obj: pass | ||
51 | o = Obj() | ||
52 | for key in d.keys(): | ||
53 | o.__dict__[key] = d[key] | ||
54 | return o | ||
55 | |||
56 | ############################################################################### | ||
57 | # Trace record data types and accessor functions | ||
58 | ############################################################################### | ||
59 | |||
60 | class StHeader: | ||
61 | format = '<bbhi' | ||
62 | formatStr = struct.Struct(format) | ||
63 | keys = ['type','cpu','pid','job'] | ||
64 | message = 'The header.' | ||
65 | |||
66 | class StNameData: | ||
67 | format = '16s' | ||
68 | formatStr = struct.Struct(StHeader.format + format) | ||
69 | keys = StHeader.keys + ['name'] | ||
70 | message = 'The name of the executable of this process.' | ||
71 | |||
72 | class StParamData: | ||
73 | format = 'IIIc' | ||
74 | formatStr = struct.Struct(StHeader.format + format) | ||
75 | keys = StHeader.keys + ['wcet','period','phase','partition'] | ||
76 | message = 'Regular parameters.' | ||
77 | |||
78 | class StReleaseData: | ||
79 | format = 'QQ' | ||
80 | formatStr = struct.Struct(StHeader.format + format) | ||
81 | keys = StHeader.keys + ['release_time','deadline'] | ||
82 | message = 'A job was/is going to be released.' | ||
83 | |||
84 | #Not yet used by Sched Trace | ||
85 | class StAssignedData: | ||
86 | format = 'Qc' | ||
87 | formatStr = struct.Struct(StHeader.format + format) | ||
88 | keys = StHeader.keys + ['when','target'] | ||
89 | message = 'A job was assigned to a CPU.' | ||
90 | |||
91 | class StSwitchToData: | ||
92 | format = 'QI' | ||
93 | formatStr = struct.Struct(StHeader.format + format) | ||
94 | keys = StHeader.keys + ['when','exec_time'] | ||
95 | message = 'A process was switched to on a given CPU.' | ||
96 | |||
97 | class StSwitchAwayData: | ||
98 | format = 'QI' | ||
99 | formatStr = struct.Struct(StHeader.format + format) | ||
100 | keys = StHeader.keys + ['when','exec_time'] | ||
101 | message = 'A process was switched away on a given CPU.' | ||
102 | |||
103 | class StCompletionData: | ||
104 | format = 'Q3x?c' | ||
105 | formatStr = struct.Struct(StHeader.format + format) | ||
106 | keys = StHeader.keys + ['when','forced?','flags'] | ||
107 | message = 'A job completed.' | ||
108 | |||
109 | class StBlockData: | ||
110 | format = 'Q' | ||
111 | formatStr = struct.Struct(StHeader.format + format) | ||
112 | keys = StHeader.keys + ['when'] | ||
113 | message = 'A task blocks.' | ||
114 | |||
115 | class StResumeData: | ||
116 | format = 'Q' | ||
117 | formatStr = struct.Struct(StHeader.format + format) | ||
118 | keys = StHeader.keys + ['when'] | ||
119 | message = 'A task resumes.' | ||
120 | |||
121 | class StSysReleaseData: | ||
122 | format = 'QQ' | ||
123 | formatStr = struct.Struct(StHeader.format + format) | ||
124 | keys = StHeader.keys + ['when','release'] | ||
125 | message = 'All tasks have checked in, task system released by user' | ||
126 | |||
127 | # Return the binary data type, given the type_num | ||
128 | def _get_type(type_num): | ||
129 | types = [None,StNameData,StParamData,StReleaseData,StAssignedData, | ||
130 | StSwitchToData,StSwitchAwayData,StCompletionData,StBlockData, | ||
131 | StResumeData,StSysReleaseData] | ||
132 | return types[type_num] | ||
133 | |||
134 | # Return the type name, given the type_num | ||
135 | def _get_type_name(type_num): | ||
136 | type_names = [None,"name","params","release","assign","switch_to","switch_away", | ||
137 | "completion","block","resume","sys_release"] | ||
138 | return type_names[type_num] | ||
diff --git a/trace_analyzer/README b/trace_analyzer/README deleted file mode 100644 index 16450df..0000000 --- a/trace_analyzer/README +++ /dev/null | |||
@@ -1,21 +0,0 @@ | |||
1 | Sched Trace Analyzer | ||
2 | |||
3 | ############################ | ||
4 | General Information | ||
5 | ############################ | ||
6 | |||
7 | Examples of how to use this can be found in run.py. | ||
8 | |||
9 | 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. | ||
11 | |||
12 | |||
13 | ############################# | ||
14 | Development Notes | ||
15 | ############################# | ||
16 | |||
17 | Need to account for situation where there is a tie in deadline which spans the | ||
18 | topN category. | ||
19 | |||
20 | Might think about adding in hooks, so you do the trace, then add in the hook to | ||
21 | see the state at the place you're curious about. | ||
diff --git a/trace_analyzer/run.py b/trace_analyzer/run.py deleted file mode 100755 index 80ae257..0000000 --- a/trace_analyzer/run.py +++ /dev/null | |||
@@ -1,99 +0,0 @@ | |||
1 | #!/usr/bin/env python3 | ||
2 | import sta | ||
3 | |||
4 | ###################################### | ||
5 | # Sched Trace Analyzer LaunchPad # | ||
6 | ###################################### | ||
7 | |||
8 | def main(): | ||
9 | #myEDF() | ||
10 | macTrace() | ||
11 | #myTrace() | ||
12 | #switchToTrace() | ||
13 | #releaseTrace() | ||
14 | #oneCPU() | ||
15 | |||
16 | def oneCPU(): | ||
17 | trace = sta.Trace(g6_list) | ||
18 | trace.filter('cpu==0') | ||
19 | trace.sort('when') | ||
20 | trace.print_count(True) | ||
21 | trace.print_records() | ||
22 | |||
23 | def myEDF(): | ||
24 | test = sta.EDF(g6_list,hooks=[37917786934190]) | ||
25 | #test = sta.EDF(g6_list) | ||
26 | test.run_test() | ||
27 | |||
28 | def myTrace(): | ||
29 | trace = sta.Trace(g6_list) | ||
30 | trace.sort('when',alt='release_time') | ||
31 | trace.filter('type==2') | ||
32 | trace.print_count(True) | ||
33 | trace.print_records() | ||
34 | |||
35 | def macTrace(): | ||
36 | trace = sta.Trace(mac2_list) | ||
37 | trace.sort('when',alt='release_time') | ||
38 | trace.print_count(True) | ||
39 | trace.print_records() | ||
40 | |||
41 | def switchToTrace(): | ||
42 | events_trace = sta.Trace(g6_list) | ||
43 | events_trace.filter('type==5') | ||
44 | events_trace.sort('when') | ||
45 | events_trace.print_count(True) | ||
46 | events_trace.print_records() | ||
47 | |||
48 | def releaseTrace(): | ||
49 | events_trace = sta.Trace(g6_list) | ||
50 | events_trace.filter('type==3') | ||
51 | events_trace.sort('release_time') | ||
52 | events_trace.print_count(True) | ||
53 | events_trace.print_records() | ||
54 | |||
55 | def sampleTrace(): | ||
56 | """A sample trace""" | ||
57 | trace.filter('pid==4129') | ||
58 | trace.filter('when>1323753839') | ||
59 | trace.filter('when<1331677799') | ||
60 | trace.sort('when') | ||
61 | trace.print_count(True) | ||
62 | trace.print_records() | ||
63 | |||
64 | |||
65 | ###################################### | ||
66 | # Put lists of your trace files here # | ||
67 | ###################################### | ||
68 | |||
69 | path = '/home/mollison/sta/traces/' | ||
70 | |||
71 | g6_list = [ | ||
72 | path + 'st-g6-0.bin', | ||
73 | path + 'st-g6-1.bin', | ||
74 | path + 'st-g6-2.bin', | ||
75 | path + 'st-g6-3.bin'] | ||
76 | |||
77 | g5_list = [ | ||
78 | path + 'st-g5-0.bin', | ||
79 | path + 'st-g5-1.bin', | ||
80 | path + 'st-g5-2.bin', | ||
81 | path + 'st-g5-3.bin'] | ||
82 | |||
83 | x19_list = [ | ||
84 | path + 'st-x19-0.bin', | ||
85 | path + 'st-x19-1.bin', | ||
86 | path + 'st-x19-2.bin', | ||
87 | path + 'st-x19-3.bin'] | ||
88 | |||
89 | mac2_list = [ | ||
90 | path + 'st-mac2-0.bin'] | ||
91 | |||
92 | |||
93 | |||
94 | ############## | ||
95 | # start main # | ||
96 | ############## | ||
97 | |||
98 | if __name__=='__main__': | ||
99 | main() | ||
diff --git a/trace_analyzer/sta.py b/trace_analyzer/sta.py deleted file mode 100755 index 4269037..0000000 --- a/trace_analyzer/sta.py +++ /dev/null | |||
@@ -1,365 +0,0 @@ | |||
1 | #!/usr/bin/env python3 | ||
2 | |||
3 | |||
4 | ################## | ||
5 | # Imports # | ||
6 | ################## | ||
7 | |||
8 | import sys | ||
9 | import struct | ||
10 | import copy | ||
11 | |||
12 | ################## | ||
13 | # Trace class # | ||
14 | ################## | ||
15 | class Trace: | ||
16 | """Object representing a trace (i.e. set of records)""" | ||
17 | |||
18 | def __init__(self,files): | ||
19 | """Initialize the Trace object with an iterator""" | ||
20 | self.iter = self.make_iter(files) | ||
21 | |||
22 | def make_iter(self, files): | ||
23 | """Make the iterator for this Trace object""" | ||
24 | for file in files: | ||
25 | f = open(file,'rb') | ||
26 | while True: | ||
27 | data = f.read(24) | ||
28 | try: | ||
29 | typenum = struct.unpack_from('b',data)[0] | ||
30 | except struct.error: | ||
31 | #We read to the end of the file | ||
32 | f.close() | ||
33 | break | ||
34 | type = get_type(typenum) | ||
35 | try: | ||
36 | values = struct.unpack_from(StHeader.format + | ||
37 | type.format,data) | ||
38 | record = dict(zip(type.keys,values)) | ||
39 | except struct.error: | ||
40 | f.close() | ||
41 | print("Invalid record detected, stopping.") | ||
42 | exit() | ||
43 | yield record | ||
44 | |||
45 | def filter(self, criteria): | ||
46 | """Apply a filter to the trace""" | ||
47 | |||
48 | #Determine if they want to filter by >, <, !=, or == | ||
49 | comp = None | ||
50 | seps = ['>','<','==','!='] | ||
51 | for sep in seps: | ||
52 | if criteria.find(sep) > 0: | ||
53 | comp = sep | ||
54 | if not comp: | ||
55 | print("Your filter is invalid: " + criteria) | ||
56 | exit() | ||
57 | |||
58 | #Apply the filter | ||
59 | field, comp, value = criteria.partition(comp) | ||
60 | test = "rec['" + field + "']" + comp + "int(" + value + ")" | ||
61 | def func(rec): | ||
62 | try: | ||
63 | if eval(test): | ||
64 | return True | ||
65 | else: | ||
66 | return False | ||
67 | except KeyError: | ||
68 | return False | ||
69 | self.iter = filter(func, self.iter) | ||
70 | |||
71 | def sort(self, key, key2=None, alt=None): | ||
72 | """Return the records in sorted order. | ||
73 | key = the key to sort by. | ||
74 | key2 = secondary value to sort by, optional | ||
75 | alt = alternate key if 'key' does not exist. | ||
76 | """ | ||
77 | def sortfunc(record): | ||
78 | score = 0 | ||
79 | if key in record: | ||
80 | score += record[key] | ||
81 | if key2 in record: | ||
82 | score = float(str(score) + '.' + str(record[key2])) | ||
83 | if score==0 and alt in record: | ||
84 | score = record[alt] | ||
85 | return score | ||
86 | self.iter = sorted(self.iter, key=sortfunc) | ||
87 | |||
88 | def slice(self, start, end): | ||
89 | """Slice the trace iterator""" | ||
90 | self.iter = list(self.iter)[start:end] | ||
91 | |||
92 | def remove_side_effects(self, type1, type2): | ||
93 | """Remove records of type2 immediately following records of type1""" | ||
94 | self.iter = list(self.iter) | ||
95 | i = 0 | ||
96 | while i < len(self.iter) - 1: | ||
97 | if self.iter[i]['type'] == type1 and self.iter[i+1]['type']==type2: | ||
98 | del self.iter[i+1] | ||
99 | i += 1 | ||
100 | |||
101 | def print_records(self): | ||
102 | """Prints all records in the trace""" | ||
103 | for record in self.iter: | ||
104 | print(50*'=') | ||
105 | print(get_type(record['type']).message) | ||
106 | for k,v in record.items(): | ||
107 | print(k +":", v) | ||
108 | |||
109 | def print_count(self,verbose=False): | ||
110 | """Prints the number of records in the trace.""" | ||
111 | self.iter = list(self.iter) | ||
112 | print("Total Count: " + str(len(self.iter))) | ||
113 | if verbose: | ||
114 | counts = {1:0,2:0,3:0,4:0,5:0,6:0,7:0,8:0,9:0,10:0} | ||
115 | for record in list(self.iter): | ||
116 | type = record['type'] | ||
117 | counts[type] += 1 | ||
118 | for key in counts.keys(): | ||
119 | print(get_type(key).__name__ + | ||
120 | ': ' + str(counts[key])) | ||
121 | print("") | ||
122 | |||
123 | def getStr(record,omitTime=False): | ||
124 | """Return the representation of a record in the format | ||
125 | time: (task, job)""" | ||
126 | time = None | ||
127 | if 'when' in record: | ||
128 | time = record['when'] | ||
129 | else: | ||
130 | time = record['release_time'] | ||
131 | if not omitTime: | ||
132 | return "{0}: ({1},{2})".format(time,record['pid'],record['job']) | ||
133 | else: | ||
134 | return "({0},{1})".format(record['pid'],record['job']) | ||
135 | |||
136 | def getTime(record): | ||
137 | """Return the time value from a record -- when, or release-time""" | ||
138 | if 'when' in record.keys(): | ||
139 | return record['when'] | ||
140 | else: | ||
141 | return record['release_time'] | ||
142 | |||
143 | |||
144 | ################## | ||
145 | # EDF class # | ||
146 | ################## | ||
147 | |||
148 | class EDF: | ||
149 | |||
150 | def __init__(self,trace_files, tolerance=150000, N=4, hooks=[]): | ||
151 | self.trace_files = trace_files | ||
152 | self.tolerance = tolerance | ||
153 | self.jobs = [] | ||
154 | self.N = N #number of cores | ||
155 | self.now = 0 | ||
156 | self.hooks = hooks | ||
157 | |||
158 | def run_test(self): | ||
159 | trace = Trace(self.trace_files) | ||
160 | trace.sort('when',alt='release_time') | ||
161 | |||
162 | for record in trace.iter: | ||
163 | |||
164 | #Update now | ||
165 | if 'when' in record: | ||
166 | self.now = record['when'] | ||
167 | elif 'release_time' in record: | ||
168 | self.now = record['release_time'] | ||
169 | |||
170 | if self.now in self.hooks: | ||
171 | self.print_jobs() | ||
172 | |||
173 | #Process record | ||
174 | if record['type'] == 3: | ||
175 | if self.is_duplicate_release(record): continue | ||
176 | job = EDF.Job() | ||
177 | job.deadline = record['deadline'] | ||
178 | job.release_time = record['release_time'] | ||
179 | job.job = record['job'] | ||
180 | job.pid = record['pid'] | ||
181 | self.jobs.append(job) | ||
182 | print('{0}: {1} was released'.format(self.now,job.get_name())) | ||
183 | elif record['type'] == 5: | ||
184 | job = self.get_job(record['pid'],record['job']) | ||
185 | if job: | ||
186 | job.running = True | ||
187 | else: | ||
188 | continue | ||
189 | print('{0}: {1} was switched to' | ||
190 | .format(self.now,job.get_name())) | ||
191 | elif record['type'] == 6: | ||
192 | job = self.get_job(record['pid'],record['job']) | ||
193 | if job: | ||
194 | job.running = False | ||
195 | else: | ||
196 | continue | ||
197 | print('{0}: {1} was switched away' | ||
198 | .format(self.now,job.get_name())) | ||
199 | elif record['type'] == 7: | ||
200 | job = self.get_job(record['pid'],record['job']) | ||
201 | if job: | ||
202 | self.jobs.remove(job) | ||
203 | else: | ||
204 | continue | ||
205 | print('{0}: {1} completed' | ||
206 | .format(self.now,job.get_name())) | ||
207 | else: | ||
208 | continue | ||
209 | |||
210 | #Sort jobs by deadline | ||
211 | self.jobs = sorted(self.jobs,key=lambda job: job.deadline) | ||
212 | |||
213 | if self.now in self.hooks: | ||
214 | self.print_jobs() | ||
215 | |||
216 | #Check for inversions (and end-of inversions) in the top N jobs | ||
217 | for job in self.jobs[0:self.N]: | ||
218 | job.topN = True | ||
219 | if job.running == False: | ||
220 | if job.inversion_start_time==0: | ||
221 | job.inversion_start_time=self.now | ||
222 | if self.check_tie(job): | ||
223 | job.inversion_start_time=0 | ||
224 | elif job.running == True: | ||
225 | if job.inversion_start_time > 0: | ||
226 | self.check_error(job) | ||
227 | job.inversion_start_time = 0 | ||
228 | |||
229 | #Check for end-of-inversions in the other jobs | ||
230 | for job in self.jobs[self.N:]: | ||
231 | if job.topN == True: | ||
232 | job.topN = False | ||
233 | self.check_error(job) | ||
234 | job.inversion_start_time = 0 | ||
235 | |||
236 | def check_error(self, job): | ||
237 | if job.inversion_start_time == 0: return | ||
238 | if self.now - job.inversion_start_time > self.tolerance: | ||
239 | print(' ' * 16 + job.get_name() + | ||
240 | ' was inverted for {0} time units,' | ||
241 | .format(self.now - job.inversion_start_time)) | ||
242 | print(' '*26 + 'since {0}'.format(job.inversion_start_time)) | ||
243 | |||
244 | def get_job(self, pid, job): | ||
245 | for x in self.jobs: | ||
246 | if x.pid == pid and x.job == job: | ||
247 | return x | ||
248 | |||
249 | def is_duplicate_release(self, record): | ||
250 | """Make sure we don't get duplicate releases""" | ||
251 | job = record['job'] | ||
252 | pid = record['pid'] | ||
253 | for x in self.jobs: | ||
254 | if x.job == job and x.pid == pid: | ||
255 | return True | ||
256 | return False | ||
257 | |||
258 | def check_tie(self, job): | ||
259 | """Returns True if there is another job with the same deadline that is | ||
260 | running""" | ||
261 | for x in self.jobs: | ||
262 | if x.deadline == job.deadline and x.running == True: | ||
263 | return True | ||
264 | return False | ||
265 | |||
266 | class Job: | ||
267 | def __init__(self): | ||
268 | self.release_time = 0 | ||
269 | self.deadline = 0 | ||
270 | self.job = 0 | ||
271 | self.pid = 0 | ||
272 | self.running = False | ||
273 | self.topN = False | ||
274 | self.inversion_start_time = 0 | ||
275 | |||
276 | def get_name(self): | ||
277 | return '({0},{1})'.format(self.pid,self.job) | ||
278 | |||
279 | def print_jobs(self): | ||
280 | print('---Jobs---') | ||
281 | for job in self.jobs: | ||
282 | print(job.get_name() + '{0} {1}'.format(job.running, job.deadline)) | ||
283 | print('----------') | ||
284 | |||
285 | #################################### | ||
286 | # Types for binary data conversion # | ||
287 | #################################### | ||
288 | |||
289 | import struct | ||
290 | |||
291 | class StHeader: | ||
292 | format = '<bbhi' | ||
293 | formatStr = struct.Struct(format) | ||
294 | keys = ['type','cpu','pid','job'] | ||
295 | message = 'The header.' | ||
296 | |||
297 | class StNameData: | ||
298 | format = '16s' | ||
299 | formatStr = struct.Struct(StHeader.format + format) | ||
300 | keys = StHeader.keys + ['name'] | ||
301 | message = 'The name of the executable of this process.' | ||
302 | |||
303 | class StParamData: | ||
304 | format = 'IIIc' | ||
305 | #format = 'ccccc' | ||
306 | formatStr = struct.Struct(StHeader.format + format) | ||
307 | keys = StHeader.keys + ['wcet','period','phase','partition'] | ||
308 | message = 'Regular parameters.' | ||
309 | |||
310 | class StReleaseData: | ||
311 | format = 'QQ' | ||
312 | formatStr = struct.Struct(StHeader.format + format) | ||
313 | keys = StHeader.keys + ['release_time','deadline'] | ||
314 | message = 'A job was/is going to be released.' | ||
315 | |||
316 | #Not yet used by Sched Trace | ||
317 | class StAssignedData: | ||
318 | format = 'Qc' | ||
319 | formatStr = struct.Struct(StHeader.format + format) | ||
320 | keys = StHeader.keys + ['when','target'] | ||
321 | message = 'A job was assigned to a CPU.' | ||
322 | |||
323 | class StSwitchToData: | ||
324 | format = 'QI' | ||
325 | formatStr = struct.Struct(StHeader.format + format) | ||
326 | keys = StHeader.keys + ['when','exec_time'] | ||
327 | message = 'A process was switched to on a given CPU.' | ||
328 | |||
329 | class StSwitchAwayData: | ||
330 | format = 'QI' | ||
331 | formatStr = struct.Struct(StHeader.format + format) | ||
332 | keys = StHeader.keys + ['when','exec_time'] | ||
333 | message = 'A process was switched away on a given CPU.' | ||
334 | |||
335 | class StCompletionData: | ||
336 | format = 'Q3x?c' | ||
337 | formatStr = struct.Struct(StHeader.format + format) | ||
338 | keys = StHeader.keys + ['when','forced?','flags'] | ||
339 | message = 'A job completed.' | ||
340 | |||
341 | class StBlockData: | ||
342 | format = 'Q' | ||
343 | formatStr = struct.Struct(StHeader.format + format) | ||
344 | keys = StHeader.keys + ['when'] | ||
345 | message = 'A task blocks.' | ||
346 | |||
347 | class StResumeData: | ||
348 | format = 'Q' | ||
349 | formatStr = struct.Struct(StHeader.format + format) | ||
350 | keys = StHeader.keys + ['when'] | ||
351 | message = 'A task resumes.' | ||
352 | |||
353 | class StSysReleaseData: | ||
354 | format = 'QQ' | ||
355 | formatStr = struct.Struct(StHeader.format + format) | ||
356 | keys = StHeader.keys + ['when','release'] | ||
357 | message = 'All tasks have checked in, task system released by user' | ||
358 | |||
359 | def get_type(type_num): | ||
360 | """Return the binary data type, given the type_num""" | ||
361 | types = [None,StNameData,StParamData,StReleaseData,StAssignedData, | ||
362 | StSwitchToData,StSwitchAwayData,StCompletionData,StBlockData, | ||
363 | StResumeData,StSysReleaseData] | ||
364 | return types[type_num] | ||
365 | |||