[go: up one dir, main page]

File: fut_record.c

package info (click to toggle)
fxt 0.3.0-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie, jessie-kfreebsd
  • size: 3,208 kB
  • ctags: 1,970
  • sloc: sh: 11,340; ansic: 7,915; perl: 3,268; asm: 598; makefile: 202
file content (360 lines) | stat: -rw-r--r-- 8,365 bytes parent folder | download
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
#include "../config.h"

#ifdef MARCEL
#  define MARCEL_INTERNAL_INCLUDE
#  include "marcel.h"
#else
#  ifdef USE_GETTID
#    include <unistd.h>
#    include <sys/syscall.h>	/* For SYS_xxx definitions */
#  endif
#endif

#undef NDEBUG
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>

#ifdef HAVE_CLOCK_GETTIME
#include <time.h>
#else
/* to get an efficient implementation of the TBX_GET_TICK on various archs */
#include "tbx_timing.h"
#endif

#ifdef __MINGW32__
#include <windows.h>
#endif

#define tbx_unlikely(x) __builtin_expect(!!(x), 0)

#include "fxt.h"
#include "fxt_internal.h"
#include "fut.h"

extern int record_tid_activated;

#ifdef PROFILE_NEW_FORMAT

/* pseudo fonction pour trouver o crire un vnement dans les buffers */

/*
 * Structure du buffer de trace
 *
 * ADDR (a<b<c...)
 * ||
 * \/
 * 
 * a /--------------- 1re zone
 *     struct record
 *        last_event = c
 *        start = a
 *        end = e
 *
 * b   ev1
 *
 *     ev2
 *
 *     ...
 *
 * c   evn
 *
 * d   FREE SPACE
 * 
 * e /--------------- 2ime zone
 *     struct record
 *        last_event = g
 *        start = e
 *        end = i
 *        
 * f   ev1
 *
 *     ...
 *
 * g   evn
 *
 * h   FREE SPACE
 *
 * i /--------------- 3ime zone
 *   ...
 * 
 * */


// Variables globales :
void *start_buffer;
void *pos_buffer;
void *end_buffer;

FXT_PERTHREAD_DEFINE(struct fxt_perthread, fxt_perthread_data)

#define SIZE_BUFF 200 /* au hazard */

int __attribute__((no_instrument_function)) 
fut_header (unsigned long code, ... ) 
{
	long size=code && 0xFF;
  
	th_buff old_th, new_th;
  
  restart:
 	/* lecture atomique des 2 valeurs (8 octets) */
 	read8(th_buff, old_th);

	new_th = old_th;
	new_th.pos += size;

	if (new_th.pos > new_th.infos->end) {
     		/* les infos ne tiennent pas dans la place du buffer 
		 * On cherche un nouveau buffer/record */
		void* old_pos = pos_buffer;
		
		void* new_pos = old_pos + SIZE_BUFF;

		if (new_pos > end_buffer) {
			/* On stoppe tout comme actuellement */
			...

			return -E_NOSPACELEFT;
		}
		if (!lock_cmpxchg(pos_buffer, old_pos, new_pos)) {
			/* Des choses ont changs, peut-tre nous qui avons t
			 * interrompu et avons alors dj rexcut ce code
			 * (donc donn une nouvelle zone  ce thread */
			goto restart;
		}
		/* Ok, on est propritaire de la nouvelle rgion qui va de
		 * old_pos  new_pos */
		
		/* On prvoit de la place pour :
		 * 1) les donnes de controle (struct record)
		 * 2) notre vnement
		 * */
		new_th.pos=old_pos+sizeof(record_t)+size;
		
		*((struct record*)old_pos) = {
			.start=old_pos;
			.end=new_pos;
			.last_event=old_pos;
		};
		
		/* Sur ia32, on a cmpxchg8,
		 * mais sur ia64, on a seulement cmp4xchg8 (en fait cmp8xchg16
		 * vu qu'on est en 64 bits)
		 * J'cris donc l'algo avec le plus contraignant
		 *
		 * cmp4xchg8 : compare 4 octets, mais en cas d'galit on en crit 8
		 * */
		if (!cmp4xchg8(th_buff.pos, old_th.pos, new_th)) {
			/* Arghhh, on a t interrompu par une autre mesure qui
			 * a mis en place un nouveau buffer
			 * */
			if (lock_cmpxchg(pos_buffer, new_pos, old_pos)) {
				/* On redonne la zone qu'on vient d'allouer si
				 * c'est encore possible */
				goto restart;
			}
			/* Bon, ben on a une zone avec une mesure et pas grand
			 * chose d'autre  mettre dedans...
			 * Tant pis
			 *
			 * cas (*) (voir TODO)
			 * */
		}
	}
	/* criture des donnes  l'adresse :
	 * new_th.pos - size
	 * */
	...
  restart_update:
	/* Et mise  jour du pointeur sur la dernire criture de la zone */
	int old_last_ev=new_th.infos->last_event;
	if (new_th.pos > old_last_ev) {
		if (!cmpxchg(new_th.infos->last_event, old_last_ev, new_th.pos)) {
			goto restart_update;
		}
	}
	return 0;
}




#else /* PROFILE_NEW_FORMAT */

#ifndef MARCEL
#ifdef __MINGW32__
#ifdef __i386__
  #define ma_cmpxchg(ptr,o,n) \
	InterlockedCompareExchange((ptr),(n),(o))
#elif defined (__x86_64__)
  #define ma_cmpxchg(ptr,o,n) \
	InterlockedCompareExchange64((ptr),(n),(o))
#else
#error TODO test the long size
#endif
#else
/* poor man's cmpxchg,  supprimer lorsque tbx implmentera cmpxchg lui-mme */
#define ma_cmpxchg(ptr,o,n) \
	__sync_val_compare_and_swap((ptr), (o), (n))
#define add_return(ptr,s) \
	__sync_fetch_and_add((ptr), (s))
#endif
#endif

#define i386_sync() __asm__ __volatile__("lock; addl $0,0(%%esp)" ::: "memory")
#define x86_64_sync() __asm__ __volatile__("lock; addl $0,0(%%rsp)" ::: "memory")
#define ppc_sync() __asm__ __volatile__("sync" ::: "memory")

#if defined(__i386__) || defined(__KNC__) || defined(__KNF__)
#define mb() i386_sync()
#define rmb() i386_sync()
#define wmb() i386_sync()
#elif defined(__x86_64__)
#define mb() x86_64_sync()
#define rmb() __asm__ __volatile__("lfence" ::: "memory")
#define wmb() __asm__ __volatile__("sfence" ::: "memory")
#elif defined(__ppc__) || defined(__ppc64__)
#define mb() ppc_sync()
#define rmb() ppc_sync()
#define wmb() ppc_sync()
#else
#define mb() __sync_synchronize()
#define rmb() __sync_synchronize()
#define wmb() __sync_synchronize()
#endif

static unsigned long trash_buffer[8];
extern int allow_fut_flush;
extern pthread_mutex_t fut_flush_lock;

#ifdef __GNUC__
__attribute__((weak))
#endif
uint64_t fut_getstamp(void)
{
	unsigned long long tick;
#ifdef HAVE_CLOCK_GETTIME
	struct timespec tp;
#  ifdef CLOCK_MONOTONIC_RAW
	/* avoid NTP mess */
	clock_gettime(CLOCK_MONOTONIC_RAW, &tp);
#  else
	clock_gettime(CLOCK_MONOTONIC, &tp);
#  endif
	tick = 1000000000ULL*tp.tv_sec+ tp.tv_nsec;
#else
	TBX_GET_TICK(tick);
#endif
	return tick;
}

unsigned long* __fut_record_event(fxt_trace_user_raw_t * rec,
					 uint64_t stamp,
					 unsigned long code)
{
	rec->tick = stamp;
#ifdef MA__FUT_RECORD_TID
	rec->tid=(unsigned long)MARCEL_SELF;
#else
	if (record_tid_activated) {
#  ifdef USE_GETTID
		rec->tid = syscall(SYS_gettid);
#  else
#ifdef __MINGW32__
		rec->tid = GetCurrentThreadId();
#else
		rec->tid = pthread_self();
#endif
#  endif
	} else {
		rec->tid = 0;
	}
#endif
	rec->code = code;
	return (unsigned long*)(rec->args);
}

unsigned long* fut_getstampedbuffer(unsigned long code, 
				    int size)
{
	unsigned long *prev_slot, *next_slot;
	uint64_t stamp;
 retry:
#if ! USE_SPINLOCKS
	/* compare and swap method */
	do {
		/* Wait for flusher to finish flushing */
		do {
			rmb();
			prev_slot=(unsigned long*)fut_next_slot;
		} while (prev_slot > fut_last_slot);
		next_slot=(unsigned long*)((unsigned long)prev_slot+size);
		/* Note: we don't want to use add_return here, as we want to
		 * make sure we are not reserving beyond fut_last_slot */
	} while (prev_slot != ma_cmpxchg(&fut_next_slot, 
					 prev_slot, next_slot));
	stamp = fut_getstamp();
#else
	/* spin lock method: guarantees coherency between stamp and trace
	 * order, but is much less scalable. */
	pthread_spin_lock(&fut_slot_lock);
	stamp = fut_getstamp();
	prev_slot = fut_next_slot;
	fut_next_slot = next_slot = prev_slot + size;
	pthread_spin_unlock(&fut_slot_lock);
#endif

	if (tbx_unlikely(next_slot > fut_last_slot)) {
	  if(allow_fut_flush) {
	    /* our record doesn't fit, flush the buffer to disk */

	    if (prev_slot > fut_last_slot)
	      /* another thread is going to flush the buffer */
	      goto retry;

	    pthread_mutex_lock(&fut_flush_lock);

	    /* Wait for all threads to commit their writes */
	    while (fut_filled_slot < (void*) prev_slot - (void*) fut_first_slot)
	      rmb();
	    /* Make sure others have finished writing */
	    rmb();
	    assert(fut_filled_slot == (void*) prev_slot - (void*) fut_first_slot);
	    fut_flush(NULL, prev_slot, 1);
	    /* synchronize between the flush and other threads */
	    mb();
	    __fut_reset_pointers();

	    pthread_mutex_unlock(&fut_flush_lock);

	    goto retry;
	  } else {
	    /* stop recording events */
	    fut_active=0;
	    /* Pourquoi on restaure ici ? Pas de race condition possible ? */
	    fut_next_slot = prev_slot;
	    return trash_buffer;
	  }
	}

	fxt_trace_user_raw_t *rec=(fxt_trace_user_raw_t *)prev_slot;

	return __fut_record_event(rec, stamp, code);
}

void fut_commitstampedbuffer(int size)
{
#if !USE_SPINLOCKS
	/* Make sure flusher sees our writes */
	wmb();
	add_return(&fut_filled_slot, size);
#else
	pthread_spin_lock(&fut_slot_lock);
	fut_filled_slot += size;
	pthread_spin_unlock(&fut_slot_lock);
#endif
}

#endif /* PROFILE_NEW_FORMAT */