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 | |||
