-
Notifications
You must be signed in to change notification settings - Fork 1
/
Assignment_3_Code_Template.c
435 lines (363 loc) · 13.9 KB
/
Assignment_3_Code_Template.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
/* Created by: Muhammad Junaid Aslam
* Teaching Assistant, Real-Time Systems Course IN4343, 2020
* PhD Candidate Technical University of Delft, Netherlands.
*/
// Header File inclusion
#define _GNU_SOURCE
#include <sys/fcntl.h>
#include <stdio.h>
#include <stdarg.h>
#include <pthread.h>
#include <sched.h>
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <linux/unistd.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
// Global Defines
#define TRUE 1
#define FALSE 0
#define NUM_THREADS 4
#define PERIOD 5 // define how many times do you want the tasks to be called
#define HIGHEST_PRIORITY 99
#define handle_error_en(en, msg) \
do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)
#define POL_TO_STR(x) \
x==SCHED_FIFO?"FIFO":x==SCHED_RR?"RR":x==SCHED_OTHER?"OTHER":"INVALID"
#define DEFAULT_BUSY_WAIT_TIME 5000 // 5 Milliseconds
#define DEFAULT_RR_LOOP_TIME 1000 // 1 Millisecond
#define MICRO_SECOND_MULTIPLIER 1000000 // 1 = 1 microsecond
// Global data and structures
static int trace_fd = -1;
static int inputpolicy = 0;
struct thread_args {
unsigned int thread_period;
unsigned int thread_priority;
unsigned int thread_policy;
unsigned int thread_affinity;
unsigned int thread_number;
struct timespec thread_start_time;
struct timespec thread_end_time;
unsigned long long thread_end_timestamp;
double thread_exectime;
long long thread_deadline;
long long thread_response_time;
};
static struct thread_args results[NUM_THREADS];
typedef void* (*thread_func_prototype)(void*);
/*<======== Do not change anything in this function =========>*/
static void timespec_add_us(struct timespec *t, long us)
{
// add microseconds to timespecs nanosecond counter
t->tv_nsec += us*1000;
// if wrapping nanosecond counter, increment second counter
if (t->tv_nsec > 1000000000)
{
t->tv_nsec = t->tv_nsec - 1000000000;
t->tv_sec += 1;
}
}
/*<======== Do not change anything in this function =========>*/
static inline int timespec_cmp(struct timespec *now, struct timespec *next)
{
int diff = now->tv_sec - next->tv_sec;
return diff ? diff : now->tv_nsec - next->tv_nsec;
}
/*<======== Do not change anything in this function =========>*/
static unsigned long long getTimeStampMicroSeconds(void)
{
struct timeval currentTime;
gettimeofday(¤tTime, NULL);
return currentTime.tv_sec * 1000000 + currentTime.tv_usec;
}
/*<======== Do not change anything in this function =========>*/
static long clock_diff(struct timespec start, struct timespec end)
{
/* It returns difference in time in nano-seconds */
struct timespec temp;
if ((end.tv_nsec-start.tv_nsec)<0)
{
temp.tv_sec = end.tv_sec-start.tv_sec-1;
temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec;
}
else
{
temp.tv_sec = end.tv_sec-start.tv_sec;
temp.tv_nsec = end.tv_nsec-start.tv_nsec;
}
return temp.tv_nsec;
}
/*<======== Do not change anything in this function =========>*/
static int set_attributes(int *priorities, int num_thr, int *Affinities, int *Policies, pthread_attr_t *attributes)
{
cpu_set_t cpuset;
for(int i=0; i<num_thr; i++)
{
if((pthread_attr_init(&attributes[i])!=0)) {
printf("%d - %s %d\n", __LINE__, "Error Initializing Attributes for Thread:", i+1);
return FALSE;
}
struct sched_param param;
param.sched_priority = priorities[i];
CPU_ZERO(&cpuset);
// CPU_SET: This macro adds cpu to the CPU set set.
CPU_SET(Affinities[i], &cpuset);
if((pthread_attr_setschedpolicy(&attributes[i], Policies[i])!=0))
{
printf("%s %d\n", "Error Setting Scheduling Policy for Thread:", i);
return FALSE;
}
if((pthread_attr_setschedparam(&attributes[i], ¶m) != 0))
{
printf("%s %d\n", "Error Setting Scheduling Parameters for Thread:", i);
return FALSE;
}
if((pthread_attr_setaffinity_np(&attributes[i], sizeof(cpu_set_t), &cpuset)!=0))
{
printf("%s %d\n", "Error Setting CPU Affinities for Thread:", i);
return FALSE;
}
if((pthread_attr_setinheritsched(&attributes[i], PTHREAD_EXPLICIT_SCHED)!=0))
{
printf("%s %d\n", "Error Setting CPU Attributes for Thread:", i);
return FALSE;
}
}
return TRUE;
}
/*<======== Do not change anything in this function =========>*/
static void trace_write(const char *fmt, ...)
{
va_list ap;
char buf[256];
int n;
if (trace_fd < 0){return;}
va_start(ap, fmt);
n = vsnprintf(buf, 256, fmt, ap);
va_end(ap);
write(trace_fd, buf, n);
}
/*<======== Do not change anything in this function =========>*/
static void workload(int tid)
{
struct timespec lvTimeVal;
if(clock_gettime(CLOCK_THREAD_CPUTIME_ID, &lvTimeVal) != 0){
fprintf(stderr, "%s\n", "Error Fetching Clock Start Time."); return;
}
struct timespec lvTimer = lvTimeVal, lvTimer2 = lvTimeVal;
unsigned int counter = 1;
while(1)
{
if(clock_gettime(CLOCK_THREAD_CPUTIME_ID, &lvTimeVal) != 0){
fprintf(stderr, "%s\n", "Error Fetching Clock Start Time."); return;
}
if (
(
(lvTimeVal.tv_sec*MICRO_SECOND_MULTIPLIER+lvTimeVal.tv_nsec/1000) -
(lvTimer.tv_sec*MICRO_SECOND_MULTIPLIER+lvTimer.tv_nsec/1000)
)
>= DEFAULT_BUSY_WAIT_TIME
) {break;}
if (
(inputpolicy == SCHED_RR) &&
(
(
(lvTimeVal.tv_sec*MICRO_SECOND_MULTIPLIER+lvTimeVal.tv_nsec/1000) -
(lvTimer2.tv_sec*MICRO_SECOND_MULTIPLIER+lvTimer2.tv_nsec/1000)
)
>= DEFAULT_RR_LOOP_TIME
)
)
{
lvTimer2.tv_sec = lvTimeVal.tv_sec;
lvTimer2.tv_nsec = lvTimeVal.tv_nsec;
trace_write("RTS_Thread_%d Loop ... %d", tid, counter++);
}
}
}
/*<======== This is where you have to work =========>*/
static void* Thread(void *inArgs)
{
long long thread_response_time = 0;
int task_count = 0; // counter for number of tasks executed
/* <==================== ADD CODE BELOW =======================>*/
/* Follow the instruction manual for creating a periodic task here
* The given code snippet of this thread is one shot, which means that
* it will execute once and exit. However, in real-time systems, a thread
* is normally periodic. You can also check the deadline miss of a thread
* following the same tutorial. Use the given clock_gettime() examples to
* find out the response_time of the threads. For calculating the next
* period of the thread use the period value given in thread_args struct.
*/
struct thread_args *args = (struct thread_args*)inArgs; /*This fetches the thread control block (thread_args)
passed to the thread at the time of creation*/
int tid = args->thread_number; /* tid is just the number of the executing thread; Note that,
pthread_t specified thread id is no the same as this thread id as this depicts only the sequence number of this thread.*/
trace_write("RTS_Thread_%d Policy:%s Priority:%d\n", /*This is an example trace message which appears at the start of the thread in KernelShark */
args->thread_number, POL_TO_STR(args->thread_policy), args->thread_priority);
clock_gettime(CLOCK_REALTIME, &results[tid].thread_start_time); // This fetches the timespec structure through which can get current time.
struct timespec next;
struct timespec now;
clock_gettime(CLOCK_REALTIME, &next);
while (1) {
workload(args->thread_number); // This produces a busy wait loop of ~5+/-100us milliseconds
/* In order to change the execution time (busy wait loop) of this thread
* from ~5+/-100us milliseconds to XX milliseconds, you have to change the value of
* DEFAULT_BUSY_WAIT_TIME macro at the top of this file.
*/
printf("Thread %d performing task\n", tid);
timespec_add_us(&next, args->thread_period);
clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next, NULL);
task_count++;
}
clock_gettime(CLOCK_REALTIME, &results[tid].thread_end_time);
/* Following sequence of commented instructions should be filled at the end of each periodic iteration*/
// results[tid].thread_deadline = <Fill with the next calculated deadline>;
// results[tid].thread_response_time = <Fill with the response_time>;
/* Do not change the below sequence of instructions.*/
results[tid].thread_number = args->thread_number;
results[tid].thread_policy = args->thread_policy;
results[tid].thread_affinity = sched_getcpu();
results[tid].thread_priority = args->thread_priority;
results[tid].thread_end_timestamp = getTimeStampMicroSeconds();
trace_write("RTS_Thread_%d Terminated ... ResponseTime:%lld Deadline:%lld", args->thread_number, args->thread_response_time, args->thread_deadline);
/* <==================== ADD CODE ABOVE =======================>*/
}
/*<======== Do not change anything in this function =========>*/
static void help(void)
{
printf("Error... Provide One of the Following Scheduling Policies\n");
printf("1 - FIFO\n");
printf("2 - RR\n");
printf("4 - OTHER\n");
}
/*<======== Do not change anything in this function =========>*/
static int comparator(const void* p, const void* q)
{
if (((struct thread_args*)p)->thread_end_timestamp < ((struct thread_args*)q)->thread_end_timestamp) {return -1;}
else if (((struct thread_args*)p)->thread_end_timestamp > ((struct thread_args*)q)->thread_end_timestamp) {return 1;}
else {return 0;}
}
int main(int argc, char **argv)
{
/*<======== You have to change the following three data structures whose values will be assigned to threads in sequence =========>*/
/* NOTE: Do not forget to change the value of macro NUM_THREADS when you want to change the number of threads,
* and then fill the following arrays accordingly. Default value of NUM_THREADS is 4.
* -------------------------------------------------------------------------------------------------------------------------------
* Warning: If you run the code without filling in priorities, you will get runtime stack error.
* If you do not have a multicore computer, then change the value of affinities[i] from 1 to 0.
*/
int priorities[NUM_THREADS]; /* Priorities are in the range from 1 to 99 when it comes to SCHED_FIFO and SCHED_RR, and for
SCHED_OTHER, the priority is always 0 */
int periods[NUM_THREADS]; // Used in calculation of next period value in a periodic task / thread.
// for (int i = 0; i < NUM_THREADS; i++)
// {
// priorities[i] = i+1;
// periods[i] = 1024;
// }
priorities[0] = 1;
periods[0] = 8000*10;
priorities[1] = 3;
periods[1] = 2000*10;
priorities[2] = 2;
periods[2] = 4000*10;
priorities[3] = 4;
periods[3] = 1000*10;
/*<======== Do not change anything below unless you have to change value of affinities[i] below =========>*/
thread_func_prototype thread_functions[NUM_THREADS] = {Thread};
pthread_t thread_id[NUM_THREADS];
pthread_attr_t attributes[NUM_THREADS];
int affinities[NUM_THREADS];
int policies[NUM_THREADS];
struct thread_args lvargs[NUM_THREADS] = {0};
srand((unsigned) time(NULL));
if(argc <= 1)
{
help();
return FALSE;
} else {
if(strcmp(argv[1], "RR") == 0) {
printf("Round Robin Scheduling\n");
for(int i = 0; i < NUM_THREADS; i++) {
policies[i] = SCHED_RR;
inputpolicy = SCHED_RR;
affinities[i] = 1;
}
} else if(strcmp(argv[1], "FIFO") == 0) {
printf("First in First out Scheduling\n");
for(int i = 0; i < NUM_THREADS; i++) {
policies[i] = SCHED_FIFO;
inputpolicy = SCHED_FIFO;
affinities[i] = 1;
}
} else if(strcmp(argv[1], "OTHER") == 0) {
printf("Completely Fair Scheduling\n");
for(int i = 0; i < NUM_THREADS; i++) {
policies[i] = SCHED_OTHER;
inputpolicy = SCHED_OTHER;
affinities[i] = 1;
}
} else {
help();
return FALSE;
}
}
printf("Assignment_2 Threads:%d Scheduling Policy:%s\n", NUM_THREADS, POL_TO_STR(inputpolicy));
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(0, &cpuset);
// For Main Thread Affinity is set to CPU 0
if(sched_setaffinity(0, sizeof(cpuset), &cpuset) != 0) {printf("%d Main Error Setting CPU Affinity\n", __LINE__);}
trace_fd = open("/sys/kernel/debug/tracing/trace_marker", O_WRONLY);
if(trace_fd < 0) {
printf("Error Opening trace marker, try running with sudo\n");
} else{
printf("Successfully opened trace_marker\n");
}
if((set_attributes(priorities, NUM_THREADS, affinities, policies, attributes) == FALSE))
{
printf("Error Setting Attributes of all threads...\n");
}
for(int i = 0; i < NUM_THREADS; i++)
{
lvargs[i].thread_number = i;
lvargs[i].thread_policy = policies[i];
lvargs[i].thread_affinity = affinities[i];
lvargs[i].thread_period = periods[i];
lvargs[i].thread_priority = priorities[i];
if(pthread_create(&thread_id[i], &attributes[i], Thread, (void*)&lvargs[i]) != 0)
{
printf("%d - %s %d\n", __LINE__, "Error Spawning Thread:", i);
}
else
{
trace_write("RTS_Spawned Thread_%d\n", i);
}
}
for(int i=0; i<NUM_THREADS; i++) {
pthread_join(thread_id[i], NULL);
}
qsort(results, NUM_THREADS, sizeof(struct thread_args), comparator);
for(int i=0;i<NUM_THREADS;i++)
{
results[i].thread_exectime = (clock_diff(results[i].thread_start_time, results[i].thread_end_time)/1.0e6);
printf(
"--- >>> TimeStamp:%llu Thread:%d Policy:%s Affinity:%d Priority:%d ExecutionTime:%f Responsetime:%lldms Deadline:%lld\n",
results[i].thread_end_timestamp, results[i].thread_number, POL_TO_STR(results[i].thread_policy),
results[i].thread_affinity, results[i].thread_priority, results[i].thread_exectime,
results[i].thread_response_time, results[i].thread_deadline
);
}
if(close(trace_fd) == 0)
{
printf("Successfully closed the trace file\n");
}
return 0;
}