aboutsummaryrefslogtreecommitdiffstats
path: root/jitter/jitter.c
blob: 24bc3bdccccfca599cb1df3986c4a0a411475de0 (plain)
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
/**
 * Copyright (c) 2017 Intel Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *   Original Creation Date : Sept 1, 2012
 *   Last Modified : Dec 17, 2017
 *   Author: Shrikant M. Shah
 *     Contributor: Patrick Lu
 *
 *   Program for measuring software jitter
 *
 *   This program is meant for measuring the "jitter" in the execution time caused by OS and/or the underlying architecture.
 *   The program does the following:
 *		- Calls a dummy function which consists of a small piece of code block being executed in a loop. The loop count is user defined (default 80,000).
 *		- Measures the Execution time for this function in Core cycles
 *		- Compares the Execution time with Instantaneous Minimum and Maximum execution time values tracked during the display update interval. These values are adjusted based on the comparison.
 *		- Compares the execution time with Absolute Minimum and maximum execution values since the program started. These values are adjusted based on the comparison.
 *		- Repeats above operation.
 *      - Updates the display with the statistics once after the dummy function is executed certain number of times (default 20,000).
 *			The display update interval is approximately set to 1 second. At this time, Instantaneous Minimum and Maximum execution time are initialized.
 *		- The statistics include Instantaneous Minimum and Maximum execution times measured during the display interval and jitter.
 *   The most important statistic to watch is Inst_jitter, the difference between instantaneous Minimum and Maximum execution times
 *   Display update interval is also user programmable.
 *   Note that Absolute Minimum and Maximum execution times keep track of variations in the execution time over a long period of time.
 *   The Absolute Min and Max values can be reset using User signal.
 *
 */

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <signal.h>
#include <string.h>
#define __USE_GNU
#include <sched.h>

#define DEFAULT_CORE 1

union timestampclock
{
	uint64_t timestamp64;
	struct
	{
		uint32_t low;
		uint32_t high;
	};
}a;

#define MINVAL 0xfffffffL

int resetStats=0;
unsigned int loopcount = 80000;
unsigned int displayUpdate = 20000;
#ifdef PROCESSOR_TRACE
unsigned int jitter_threshold = 15000;
#endif
unsigned int iterations = 200;

char *title = "   Inst_Min   Inst_Max   Inst_jitter last_Exec  Abs_min    Abs_max      tmp       Interval     Sample No\n";

int dummyop(unsigned int loops, unsigned startval)
{
	register unsigned int i,a , k;
	a = startval ;
	k = loops;
	for(i=0; i < k; i++)
	{
		a += (3*i + k);
		a &=0x7f0f0000;
	}

	return a;
}

inline uint64_t TimeStampCounter()
{
	union timestampclock tscount;
	uint32_t eax,edx;

	__asm__ volatile("mfence");

	__asm__ __volatile__("rdtsc" : "=a" (eax), "=d" (edx));
	tscount.low = eax;
	tscount.high = edx;
	return tscount.timestamp64;
}

inline uint64_t TimeStampCounterEnd()
{
	union timestampclock tscount;
	uint32_t eax,edx;

	__asm__ __volatile__("rdtscp" : "=a" (eax), "=d" (edx)::"%rcx");
	tscount.low = eax;
	tscount.high = edx;
	__asm__ __volatile__("rdtscp" : "=a" (eax), "=d" (edx)::"%rcx");
	return tscount.timestamp64;
}

uint64_t clockdiff(uint64_t starttime,uint64_t  endtime)
{

	uint64_t a ;
	if (starttime > endtime)
		a = starttime - endtime;
	else
		a = endtime - starttime;

	return a ;
}

void signalHandler(int signalno)
{
	if (signalno == SIGUSR1)
	{
		printf("Resettings Absolute Min and Max counts\n");
		resetStats = 1;
	}
}

void showhelp()
{
	printf("usage:\n");
	printf("./jitter [-c <core id>] [-l] [r] [-h] [-p $(pgrep perf)] [-t]\n");

	printf("	-c : pin jitter to core_id. Default core_id is %d\n", DEFAULT_CORE);
	printf("	-r : Display update rate. Default is %d\n", displayUpdate);
	printf("	-l : Loop count for code block. Default is %d\n", loopcount);
	printf("	-p : perf_pid [run: perf record -S -C$CORENO -e intel_pt// -v on another window]\n");
#ifdef PROCESSOR_TRACE
	printf("	-t : jitter threshold for perf trigger. Default is > %d cycles\n", jitter_threshold);
#endif
	printf("	-i : Sample counts after which program terminates. Default count is %u\n", iterations);
	printf("For resetting statistics use:  pkill -USR1 jitter\n");
	printf("For elevating the priority of this program try:  chrt -r -p 99 processId\n");

}

void displayInfo()
{
	printf("Timings are in CPU Core cycles\n");
	printf("Inst_Min:    Minimum Excution time during the display update interval(default is ~1 second)\n");
	printf("Inst_Max:    Maximum Excution time during the display update interval(default is ~1 second)\n");
	printf("Inst_jitter: Jitter in the Excution time during rhe display update interval. This is the value of interest\n");
	printf("last_Exec:   The Excution time of last iteration just before the display update\n");
	printf("Abs_Min:     Absolute Minimum Excution time since the program started or statistics were reset\n");
	printf("Abs_Max:     Absolute Maximum Excution time since the program started or statistics were reset\n");
	printf("tmp:         Cumulative value calcualted by the dummy function\n");
	printf("Interval:    Time interval between the display updates in Core Cycles\n");
	printf("Sample No:   Sample number\n\n");
}

int main(int argc, char* argv[])
{

	register unsigned int tref=0;
	register unsigned int SampleNo=0;
	register long currentExectime;
	uint64_t startTime,endTime,seedtime, lasttime;
	uint64_t absoluteMin=MINVAL, absoluteMax=0, TransientMax=0, TransientMin=MINVAL;
	unsigned int tmp=0;
	int userinput;
	uint32_t core_id = DEFAULT_CORE;
	cpu_set_t cpuset;
#ifdef PROCESSOR_TRACE
	pid_t perf_pid = -1;
	int skip = 5, signaled = 0;
#endif

	printf("Linux Jitter testing program version 1.9\n");

	while ((userinput = getopt(argc, argv, "c:l:r:hp:t:i:")) != EOF)
	{
		switch(userinput)
		{
			case 'c' : core_id = strtoul(optarg, (char**)NULL, 0);
				   break;
			case 'h' : showhelp();
				   exit(0);
			case 'l' : loopcount = strtoul(optarg, (char**)NULL, 0);
				   break;
			case 'r' : displayUpdate = strtoul(optarg, (char**)NULL, 0);
				   break;
			case 'p' :
#ifdef PROCESSOR_TRACE
				   perf_pid = strtol(optarg, (char**)NULL, 0);
				   printf("Perf PID=%d\n", perf_pid);
#else
				   printf("jitter program did not compile with -DPROCESSOR_TRACE, cannot use Linux perf with Intel Processor Trace to record jitter source\n");
#endif
				   break;

			case 'i' :
				   iterations = strtoul(optarg, (char**)NULL, 0);
				   printf("Iterations=%u\n", iterations);
				   break;

			case 't' :
#ifdef PROCESSOR_TRACE
				   jitter_threshold = strtoul(optarg, (char**)NULL, 0);
				   printf("Update jitter threshold to %u\n", jitter_threshold);
#else
				   printf("jitter program did not compile with -DPROCESSOR_TRACE, cannot use Linux perf with Intel Processor Trace to record jitter source\n");
#endif
				   break;
		}
	}

	printf("The pragram will execute a dummy function %u times\n",loopcount);
	printf("Display is updated every %u displayUpdate intervals\n",displayUpdate);
	printf("Thread affinity will be set to core_id:%u\n", core_id);

	displayInfo();

	// register signal handler for resetting Absoulte statistics
	if (signal(SIGUSR1, signalHandler) == SIG_ERR)
	{
		printf("**** Error: Signal Handler is somehow not registered. Sorry, you cannot reset statistics using USR1 signal\n");

	}

	// set Thread affinity
	CPU_ZERO(&cpuset);
	CPU_SET(core_id, &cpuset);
	sched_setaffinity(0, sizeof(cpuset), &cpuset);

	printf("%s",title);

	seedtime = TimeStampCounter();	// get a random value from timestamp

	// prime the cache by executing the code a few times
	for(SampleNo =0;SampleNo < 1000; SampleNo++)
	{
		tmp += dummyop(loopcount, (uint32_t) seedtime);
	}

	SampleNo = 0;
	tref =0;

	do	// run the loop for a long time
	{

		startTime = TimeStampCounter();	// timestamp start
		tmp += dummyop(loopcount, (uint32_t) seedtime);	// run the compute function 'loopcount' times
		endTime = TimeStampCounterEnd();	// timestamp end

		currentExectime = clockdiff(startTime, endTime); // calculate differnce in clocks

		// calcualte absolute min and max
		if (currentExectime < absoluteMin)		// calculate absolute min
			absoluteMin = currentExectime;
		if (currentExectime>= absoluteMax)        // calculate absolute max
			absoluteMax = currentExectime;

		// calcualte Max execution time within the Display displayUpdate interval
		if (currentExectime < TransientMin)
			TransientMin = currentExectime;          // temporary  min
		if (currentExectime >= TransientMax)
			TransientMax = currentExectime;          // temporary max

#ifdef PROCESSOR_TRACE
		if ((perf_pid != -1) && (SampleNo > skip) && !(signaled) && (currentExectime - TransientMin) > jitter_threshold) {
			kill(perf_pid, SIGUSR2);
			signaled = 1;
		}
#endif

		if(tref++ >= displayUpdate) // update the stats on screen/memory at displayUpdate interval
		{
			tref=1;
			if(SampleNo > 0)
			{
				printf("%10lu %10lu %10lu %10lu %10lu %10lu %13u %10lu %10u\n",
						TransientMin,
						TransientMax,
						TransientMax-TransientMin,
						currentExectime, absoluteMin,
						absoluteMax,tmp,
						(endTime-lasttime),
						SampleNo);
				fflush(stdout);
			}
			else
			{
				absoluteMax = 0;
				absoluteMin = MINVAL;
			}

			SampleNo++;
			lasttime = endTime;

			TransientMax = 0;		// reset TransientMax to zero
			TransientMin = MINVAL;

			if (resetStats !=0)		// the varibale is set if user issues 'pkill USR1 jitter' signal
			{
				absoluteMax = 0;
				absoluteMin = MINVAL;
				resetStats = 0;
				SampleNo = 1 ;
#ifdef PROCESSOR_TRACE
				signaled = 0;
#endif
			}


			if (!(SampleNo % 40))	// print the title every 40 lines
					printf("%s",title);

		}

	} while(SampleNo <= iterations);

}